summaryrefslogtreecommitdiffstats
path: root/utils/grabscreen.c
diff options
context:
space:
mode:
authorSimon Rettberg2024-09-06 14:42:37 +0200
committerSimon Rettberg2024-09-06 14:42:37 +0200
commitbadef32037f52f79abc1f1440b786cd71afdf270 (patch)
tree412b792d4cab4a7a110db82fcf74fe8a1ac55ec1 /utils/grabscreen.c
parentDelete pre-6.00 files (diff)
downloadxscreensaver-master.tar.gz
xscreensaver-master.tar.xz
xscreensaver-master.zip
Diffstat (limited to 'utils/grabscreen.c')
-rw-r--r--utils/grabscreen.c924
1 files changed, 0 insertions, 924 deletions
diff --git a/utils/grabscreen.c b/utils/grabscreen.c
deleted file mode 100644
index e2742bf..0000000
--- a/utils/grabscreen.c
+++ /dev/null
@@ -1,924 +0,0 @@
-/* xscreensaver, Copyright (c) 1992-2016 Jamie Zawinski <jwz@jwz.org>
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation. No representations are made about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- */
-
-/* This file contains code for grabbing an image of the screen to hack its
- bits. This is a little tricky, since doing this involves the need to tell
- the difference between drawing on the actual root window, and on the fake
- root window used by the screensaver, since at this level the illusion
- breaks down...
-
- The hacks themselves use utils/grabclient.c to invoke the
- "xscreensaver-getimage" program as a sub-process.
-
- On "real" X11 systems:
-
- "driver/xscreensaver-getimage" runs the code in this file to grab
- the X11 root window image as a Pixmap.
-
- On MacOS systems running X11, which nobody does any more:
-
- "driver/xscreensaver-getimage" runs "/usr/sbin/screencapture" to
- get the Mac desktop image as a PNG.
-
- On MacOS systems running the native Cocoa build, or on iOS or Android
- systems:
-
- "driver/xscreensaver-getimage" is not used. Instead, each saver's
- "utils/grabclient.c" links against "OSX/grabclient-osx.m",
- "OSX/grabclient-ios.m" or "jwxyz/jwxyz-android.c" to grab
- screenshots directly without invoking a sub-process to do it.
-
- See the comment at the top of utils/grabclient.c for a more detailed
- explanation.
- */
-
-#include "utils.h"
-#include "yarandom.h"
-
-#include <X11/Xatom.h>
-#include <X11/Xutil.h>
-
-#include "usleep.h"
-#include "colors.h"
-#include "grabscreen.h"
-#include "visual.h"
-#include "resources.h"
-
-#include "vroot.h"
-#undef RootWindowOfScreen
-#undef RootWindow
-#undef DefaultRootWindow
-
-
-#ifdef HAVE_READ_DISPLAY_EXTENSION
-# include <X11/extensions/readdisplay.h>
- static Bool read_display (Screen *, Window, Pixmap, Bool);
-#endif /* HAVE_READ_DISPLAY_EXTENSION */
-
-
-static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
-
-#ifdef HAVE_READ_DISPLAY_EXTENSION
-static void allocate_cubic_colormap (Screen *, Window, Visual *);
-void remap_image (Screen *, Window, Colormap, XImage *);
-#endif
-
-
-static Bool
-MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
-{
- return (event->xany.type == MapNotify &&
- event->xvisibility.window == (Window) window);
-}
-
-extern char *progname;
-Bool grab_verbose_p = False;
-
-void
-grabscreen_verbose(void)
-{
- grab_verbose_p = True;
-}
-
-
-static void
-raise_window(Display *dpy, Window window, Bool dont_wait)
-{
- if (grab_verbose_p)
- fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
- progname, (unsigned long) window,
- (dont_wait ? "not waiting" : "waiting"));
-
- if (! dont_wait)
- {
- XWindowAttributes xgwa;
- XSizeHints hints;
- long supplied = 0;
- memset(&hints, 0, sizeof(hints));
- XGetWMNormalHints(dpy, window, &hints, &supplied);
- XGetWindowAttributes (dpy, window, &xgwa);
- hints.x = xgwa.x;
- hints.y = xgwa.y;
- hints.width = xgwa.width;
- hints.height = xgwa.height;
- hints.flags |= (PPosition|USPosition|PSize|USSize);
- XSetWMNormalHints(dpy, window, &hints);
-
- XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask));
- }
-
- XMapRaised(dpy, window);
-
- if (! dont_wait)
- {
- XEvent event;
- XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
- XSync (dpy, True);
- }
-}
-
-
-static Bool
-xscreensaver_window_p (Display *dpy, Window window)
-{
- Atom type;
- int format;
- unsigned long nitems, bytesafter;
- unsigned char *version;
- if (XGetWindowProperty (dpy, window,
- XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
- 0, 1, False, XA_STRING,
- &type, &format, &nitems, &bytesafter,
- &version)
- == Success
- && type != None)
- return True;
- return False;
-}
-
-
-
-/* Whether the given window is:
- - the real root window;
- - a direct child of the root window;
- - a direct child of the window manager's decorations.
- */
-Bool
-top_level_window_p (Screen *screen, Window window)
-{
- Display *dpy = DisplayOfScreen (screen);
- Window root, parent, *kids;
- unsigned int nkids;
-
- if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
- return False;
-
- if (window == root)
- return True;
-
- /* If our direct parent is the real root window, then yes. */
- if (parent == root)
- return True;
- else
- {
- Atom type = None;
- int format;
- unsigned long nitems, bytesafter;
- unsigned char *data;
-
- /* If our direct parent has the WM_STATE property, then it is a
- window manager decoration -- yes.
- */
- if (XGetWindowProperty (dpy, window,
- XInternAtom (dpy, "WM_STATE", True),
- 0, 0, False, AnyPropertyType,
- &type, &format, &nitems, &bytesafter,
- (unsigned char **) &data)
- == Success
- && type != None)
- return True;
- }
-
- /* Else, no. We're deep in a tree somewhere.
- */
- return False;
-}
-
-
-static Bool error_handler_hit_p = False;
-static XErrorHandler old_ehandler = 0;
-static int
-BadWindow_ehandler (Display *dpy, XErrorEvent *error)
-{
- error_handler_hit_p = True;
- if (error->error_code == BadWindow || error->error_code == BadDrawable)
- return 0;
- else if (!old_ehandler)
- {
- abort();
- return 0;
- }
- else
- return (*old_ehandler) (dpy, error);
-}
-
-
-/* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
- on a window whose depth is not the maximal depth of the screen? Or
- something. Anyway, things don't work unless we: use SubwindowMode for
- the real root window (or a legitimate virtual root window); but do not
- use SubwindowMode for the xscreensaver window. I make no attempt to
- explain.
- */
-Bool
-use_subwindow_mode_p(Screen *screen, Window window)
-{
- if (window != VirtualRootWindowOfScreen(screen))
- return False;
- else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
- return False;
- else
- return True;
-}
-
-
-/* Install the colormaps of all visible windows, deepest first.
- This should leave the colormaps of the topmost windows installed
- (if only N colormaps can be installed at a time, then only the
- topmost N windows will be shown in the right colors.)
- */
-static void
-install_screen_colormaps (Screen *screen)
-{
- unsigned int i;
- Display *dpy = DisplayOfScreen (screen);
- Window real_root;
- Window parent, *kids = 0;
- unsigned int nkids = 0;
-
- XSync (dpy, False);
- old_ehandler = XSetErrorHandler (BadWindow_ehandler);
- error_handler_hit_p = False;
-
- real_root = XRootWindowOfScreen (screen); /* not vroot */
- if (XQueryTree (dpy, real_root, &real_root, &parent, &kids, &nkids))
- for (i = 0; i < nkids; i++)
- {
- XWindowAttributes xgwa;
- Window client;
- /* if (! (client = XmuClientWindow (dpy, kids[i]))) */
- client = kids[i];
- xgwa.colormap = 0;
- XGetWindowAttributes (dpy, client, &xgwa);
- if (xgwa.colormap && xgwa.map_state == IsViewable)
- XInstallColormap (dpy, xgwa.colormap);
- }
- XInstallColormap (dpy, DefaultColormapOfScreen (screen));
- XSync (dpy, False);
- XSetErrorHandler (old_ehandler);
- XSync (dpy, False);
-
- if (kids)
- XFree ((char *) kids);
-}
-
-
-void
-grab_screen_image_internal (Screen *screen, Window window)
-{
- Display *dpy = DisplayOfScreen (screen);
- XWindowAttributes xgwa;
- Window real_root;
- Bool root_p;
- Bool saver_p;
- Bool grab_mouse_p = False;
- int unmap_time = 0;
-
- real_root = XRootWindowOfScreen (screen); /* not vroot */
- root_p = (window == real_root);
- saver_p = xscreensaver_window_p (dpy, window);
-
- XGetWindowAttributes (dpy, window, &xgwa);
- screen = xgwa.screen;
-
- if (saver_p)
- /* I think this is redundant, but just to be safe... */
- root_p = False;
-
- if (saver_p)
- /* The only time grabbing the mouse is important is if this program
- is being run while the saver is locking the screen. */
- grab_mouse_p = True;
-
- if (!root_p)
- {
- double unmap = 0;
- if (saver_p)
- {
- unmap = get_float_resource(dpy, "grabRootDelay", "Seconds");
- if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
- }
- else
- {
- unmap = get_float_resource(dpy, "grabWindowDelay", "Seconds");
- if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
- }
- unmap_time = unmap * 100000;
- }
-
- if (grab_verbose_p)
- {
- fprintf(stderr,
- "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
- progname, (unsigned long) window,
- root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
-
- fprintf(stderr, "%s: ", progname);
- describe_visual(stderr, screen, xgwa.visual, False);
- fprintf (stderr, "\n");
- }
-
-
- if (!root_p && !top_level_window_p (screen, window))
- {
- if (grab_verbose_p)
- fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
- progname, (unsigned long) window);
- return;
- }
-
-
- if (!root_p)
- XSetWindowBackgroundPixmap (dpy, window, None);
-
- if (grab_mouse_p)
- {
- /* prevent random viewer of the screen saver (locker) from messing
- with windows. We don't check whether it succeeded, because what
- are our options, really... */
- XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
- GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
- XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
- CurrentTime);
- }
-
- if (unmap_time > 0)
- {
- XUnmapWindow (dpy, window);
- install_screen_colormaps (screen);
- XSync (dpy, True);
- usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
- }
-
- if (!root_p)
- {
-#ifdef HAVE_READ_DISPLAY_EXTENSION
- if (! read_display(screen, window, 0, saver_p))
-#endif /* HAVE_READ_DISPLAY_EXTENSION */
- {
-#ifdef HAVE_READ_DISPLAY_EXTENSION
- if (grab_verbose_p)
- fprintf(stderr, "%s: read_display() failed\n", progname);
-#endif /* HAVE_READ_DISPLAY_EXTENSION */
-
- copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
- raise_window(dpy, window, saver_p);
-
- /* Generally it's bad news to call XInstallColormap() explicitly,
- but this file does a lot of sleazy stuff already... This is to
- make sure that the window's colormap is installed, even in the
- case where the window is OverrideRedirect. */
- if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
- XSync (dpy, False);
- }
- }
- else /* root_p */
- {
- Pixmap pixmap;
- pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
-
-#ifdef HAVE_READ_DISPLAY_EXTENSION
- if (! read_display(screen, window, pixmap, True))
-#endif
- {
- Window real_root = XRootWindowOfScreen (screen); /* not vroot */
- XGCValues gcv;
- GC gc;
-
-#ifdef HAVE_READ_DISPLAY_EXTENSION
- if (grab_verbose_p)
- fprintf(stderr, "%s: read_display() failed\n", progname);
-#endif /* HAVE_READ_DISPLAY_EXTENSION */
-
- copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
-
- gcv.function = GXcopy;
- gcv.subwindow_mode = IncludeInferiors;
- gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
- XCopyArea (dpy, real_root, pixmap, gc,
- xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
- XFreeGC (dpy, gc);
- }
- XSetWindowBackgroundPixmap (dpy, window, pixmap);
- XFreePixmap (dpy, pixmap);
- }
-
- if (grab_verbose_p)
- fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
- progname, xgwa.depth,
- (root_p ? "real root " : ""));
-
- if (grab_mouse_p)
- {
- XUngrabPointer (dpy, CurrentTime);
- XUngrabKeyboard (dpy, CurrentTime);
- }
-
- XSync (dpy, True);
-}
-
-
-/* When we are grabbing and manipulating a screen image, it's important that
- we use the same colormap it originally had. So, if the screensaver was
- started with -install, we need to copy the contents of the default colormap
- into the screensaver's colormap.
- */
-static void
-copy_default_colormap_contents (Screen *screen,
- Colormap to_cmap,
- Visual *to_visual)
-{
- Display *dpy = DisplayOfScreen (screen);
- Visual *from_visual = DefaultVisualOfScreen (screen);
- Colormap from_cmap = XDefaultColormapOfScreen (screen);
-
- XColor *old_colors, *new_colors;
- unsigned long *pixels;
- XVisualInfo vi_in, *vi_out;
- int out_count;
- int from_cells, to_cells, max_cells, got_cells;
- int i;
-
- if (from_cmap == to_cmap)
- return;
-
- vi_in.screen = XScreenNumberOfScreen (screen);
- vi_in.visualid = XVisualIDFromVisual (from_visual);
- vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
- &vi_in, &out_count);
- if (! vi_out) abort ();
- from_cells = vi_out [0].colormap_size;
- XFree ((char *) vi_out);
-
- vi_in.screen = XScreenNumberOfScreen (screen);
- vi_in.visualid = XVisualIDFromVisual (to_visual);
- vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
- &vi_in, &out_count);
- if (! vi_out) abort ();
- to_cells = vi_out [0].colormap_size;
- XFree ((char *) vi_out);
-
- max_cells = (from_cells > to_cells ? to_cells : from_cells);
-
- old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
- new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
- pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
- for (i = 0; i < max_cells; i++)
- old_colors[i].pixel = i;
- XQueryColors (dpy, from_cmap, old_colors, max_cells);
-
- got_cells = max_cells;
- allocate_writable_colors (screen, to_cmap, pixels, &got_cells);
-
- if (grab_verbose_p && got_cells != max_cells)
- fprintf(stderr, "%s: got only %d of %d cells\n", progname,
- got_cells, max_cells);
-
- if (got_cells <= 0) /* we're screwed */
- ;
- else if (got_cells == max_cells && /* we're golden */
- from_cells == to_cells)
- XStoreColors (dpy, to_cmap, old_colors, got_cells);
- else /* try to cope... */
- {
- for (i = 0; i < got_cells; i++)
- {
- XColor *c = old_colors + i;
- int j;
- for (j = 0; j < got_cells; j++)
- if (pixels[j] == c->pixel)
- {
- /* only store this color value if this is one of the pixels
- we were able to allocate. */
- XStoreColors (dpy, to_cmap, c, 1);
- break;
- }
- }
- }
-
-
- if (grab_verbose_p)
- fprintf(stderr, "%s: installing copy of default colormap\n", progname);
-
- free (old_colors);
- free (new_colors);
- free (pixels);
-}
-
-
-
-/* The SGI ReadDisplay extension.
- This extension lets you get back a 24-bit image of the screen, taking into
- account the colors with which all windows are *currently* displayed, even
- if those windows have different visuals. Without this extension, presence
- of windows with different visuals or colormaps will result in technicolor
- when one tries to grab the screen image.
- */
-
-#ifdef HAVE_READ_DISPLAY_EXTENSION
-
-static Bool
-read_display (Screen *screen, Window window, Pixmap into_pixmap,
- Bool dont_wait)
-{
- Display *dpy = DisplayOfScreen (screen);
- XWindowAttributes xgwa;
- int rd_event_base = 0;
- int rd_error_base = 0;
- unsigned long hints = 0;
- XImage *image = 0;
- XGCValues gcv;
- int class;
- GC gc;
- Bool remap_p = False;
-
- /* Check to see if the server supports the extension, and bug out if not.
- */
- if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
- {
- if (grab_verbose_p)
- fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
- return False;
- }
-
- /* If this isn't a visual we know how to handle, bug out. We handle:
- = TrueColor in depths 8, 12, 15, 16, and 32;
- = PseudoColor and DirectColor in depths 8 and 12.
- */
- XGetWindowAttributes(dpy, window, &xgwa);
- class = visual_class (screen, xgwa.visual);
- if (class == TrueColor)
- {
- if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
- xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
- {
- if (grab_verbose_p)
- fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
- progname, xgwa.depth);
- return False;
- }
- }
- else if (class == PseudoColor || class == DirectColor)
- {
- if (xgwa.depth != 8 && xgwa.depth != 12)
- {
- if (grab_verbose_p)
- fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
- progname, xgwa.depth);
- return False;
- }
- else
- /* Allocate a TrueColor-like spread of colors for the image. */
- remap_p = True;
- }
-
-
- /* Try and read the screen.
- */
- hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
- image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
- hints, &hints);
- if (!image)
- {
- if (grab_verbose_p)
- fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
- return False;
- }
- if (!image->data)
- {
- if (grab_verbose_p)
- fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
- XDestroyImage(image);
- return False;
- }
-
- /* XReadDisplay tends to LIE about the depth of the image it read.
- It is returning an XImage which has `depth' and `bits_per_pixel'
- confused!
-
- That is, on a 24-bit display, where all visuals claim depth 24, and
- where XGetImage would return an XImage with depth 24, and where
- XPutImage will get a BadMatch with images that are not depth 24,
- XReadDisplay is returning images with depth 32! Fuckwits!
-
- So if the visual is of depth 24, but the image came back as depth 32,
- hack it to be 24 lest we get a BadMatch from XPutImage.
-
- I wonder what happens on an 8-bit SGI... Probably it still returns
- an image claiming depth 32? Certainly it can't be 8. So, let's just
- smash it to 32...
- */
- if (image->depth == 32 /* && xgwa.depth == 24 */ )
- image->depth = 24;
-
- /* If the visual of the window/pixmap into which we're going to draw is
- less deep than the screen itself, then we need to convert the grabbed bits
- to match the depth by clipping off the less significant bit-planes of each
- color component.
- */
- if (image->depth > xgwa.depth)
- {
- int x, y;
- /* We use the same image->data in both images -- that's ok, because
- since we're reading from B and writing to A, and B uses more bytes
- per pixel than A, the write pointer won't overrun the read pointer.
- */
- XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
- ZPixmap, 0, image->data,
- xgwa.width, xgwa.height,
- 8, 0);
- if (!image2)
- {
- if (grab_verbose_p)
- fprintf(stderr, "%s: out of memory?\n", progname);
- return False;
- }
-
- if (grab_verbose_p)
- fprintf(stderr, "%s: converting from depth %d to depth %d\n",
- progname, image->depth, xgwa.depth);
-
- for (y = 0; y < image->height; y++)
- for (x = 0; x < image->width; x++)
- {
- /* #### really these shift values should be determined from the
- mask values -- but that's a pain in the ass, and anyway,
- this is an SGI-specific extension so hardcoding assumptions
- about the SGI server's behavior isn't *too* heinous... */
- unsigned long pixel = XGetPixel(image, x, y);
- unsigned int r = (pixel & image->red_mask);
- unsigned int g = (pixel & image->green_mask) >> 8;
- unsigned int b = (pixel & image->blue_mask) >> 16;
-
- if (xgwa.depth == 8)
- pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
- else if (xgwa.depth == 12)
- pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
- else if (xgwa.depth == 16 || xgwa.depth == 15)
- /* Gah! I don't understand why these are in the other order. */
- pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
- else
- abort();
-
- XPutPixel(image2, x, y, pixel);
- }
- image->data = 0;
- XDestroyImage(image);
- image = image2;
- }
-
- if (remap_p)
- {
- allocate_cubic_colormap (screen, window, xgwa.visual);
- remap_image (screen, window, xgwa.colormap, image);
- }
-
- /* Now actually put the bits into the window or pixmap -- note the design
- bogosity of this extension, where we've been forced to take 24 bit data
- from the server to the client, and then push it back from the client to
- the server, *without alteration*. We should have just been able to tell
- the server, "put a screen image in this drawable", instead of having to
- go through the intermediate step of converting it to an Image. Geez.
- (Assuming that the window is of screen depth; we happen to handle less
- deep windows, but that's beside the point.)
- */
- gcv.function = GXcopy;
- gc = XCreateGC (dpy, window, GCFunction, &gcv);
-
- if (into_pixmap)
- {
- gcv.function = GXcopy;
- gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
- XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
- xgwa.width, xgwa.height);
- }
- else
- {
- gcv.function = GXcopy;
- gc = XCreateGC (dpy, window, GCFunction, &gcv);
-
- /* Ok, now we'll be needing that window on the screen... */
- raise_window(dpy, window, dont_wait);
-
- /* Plop down the bits... */
- XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
- }
- XFreeGC (dpy, gc);
-
- if (image->data)
- {
- free(image->data);
- image->data = 0;
- }
- XDestroyImage(image);
-
- return True;
-}
-#endif /* HAVE_READ_DISPLAY_EXTENSION */
-
-
-#ifdef HAVE_READ_DISPLAY_EXTENSION
-
-/* Makes and installs a colormap that makes a PseudoColor or DirectColor
- visual behave like a TrueColor visual of the same depth.
-
- #### Duplicated in driver/xscreensaver-getimage.c
- */
-static void
-allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
-{
- Display *dpy = DisplayOfScreen (screen);
- XWindowAttributes xgwa;
- Colormap cmap;
- int nr, ng, nb, cells;
- int r, g, b;
- int depth;
- XColor colors[4097];
- int i;
-
- XGetWindowAttributes (dpy, window, &xgwa);
- cmap = xgwa.colormap;
- depth = visual_depth (screen, visual);
-
- switch (depth)
- {
- case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
- case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
- default: abort(); break;
- }
-
- memset(colors, 0, sizeof(colors));
- for (r = 0; r < (1 << nr); r++)
- for (g = 0; g < (1 << ng); g++)
- for (b = 0; b < (1 << nb); b++)
- {
- i = (r | (g << nr) | (b << (nr + ng)));
- colors[i].pixel = i;
- colors[i].flags = DoRed|DoGreen|DoBlue;
- if (depth == 8)
- {
- colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
- (r << 4) | (r << 1));
- colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
- (g << 4) | (g << 1));
- colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
- (b << 8) | (b << 6) | (b << 4) |
- (b << 2) | b);
- }
- else
- {
- colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
- colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
- colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
- }
- }
-
- {
- int j;
- int allocated = 0;
- int interleave = cells / 8; /* skip around, rather than allocating in
- order, so that we get better coverage if
- we can't allocated all of them. */
- for (j = 0; j < interleave; j++)
- for (i = 0; i < cells; i += interleave)
- if (XAllocColor (dpy, cmap, &colors[i + j]))
- allocated++;
-
- if (grab_verbose_p)
- fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
- progname, allocated, cells);
- }
-}
-
-/* Find the pixel index that is closest to the given color
- (using linear distance in RGB space -- which is far from the best way.)
-
- #### Duplicated in driver/xscreensaver-getimage.c
- */
-static unsigned long
-find_closest_pixel (XColor *colors, int ncolors,
- unsigned long r, unsigned long g, unsigned long b)
-{
- unsigned long distance = ~0;
- int i, found = 0;
-
- if (ncolors == 0)
- abort();
- for (i = 0; i < ncolors; i++)
- {
- unsigned long d;
- int rd, gd, bd;
-
- rd = r - colors[i].red;
- gd = g - colors[i].green;
- bd = b - colors[i].blue;
- if (rd < 0) rd = -rd;
- if (gd < 0) gd = -gd;
- if (bd < 0) bd = -bd;
- d = (rd << 1) + (gd << 2) + bd;
-
- if (d < distance)
- {
- distance = d;
- found = i;
- if (distance == 0)
- break;
- }
- }
-
- return found;
-}
-
-
-/* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
- displayable with the given X colormap. The farther from a perfect
- color cube the contents of the colormap are, the lossier the
- transformation will be. No dithering is done.
-
- #### Duplicated in driver/xscreensaver-getimage.c
- */
-void
-remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
-{
- Display *dpy = DisplayOfScreen (screen);
- unsigned long map[4097];
- int x, y, i;
- int cells;
- XColor colors[4097];
-
- if (image->depth == 8)
- cells = 256;
- else if (image->depth == 12)
- cells = 4096;
- else
- abort();
-
- memset(map, -1, sizeof(*map));
- memset(colors, -1, sizeof(*colors));
-
- for (i = 0; i < cells; i++)
- colors[i].pixel = i;
- XQueryColors (dpy, cmap, colors, cells);
-
- if (grab_verbose_p)
- fprintf(stderr, "%s: building table for %d bit image\n",
- progname, image->depth);
-
- for (i = 0; i < cells; i++)
- {
- unsigned short r, g, b;
-
- if (cells == 256)
- {
- /* "RRR GGG BB" In an 8 bit map. Convert that to
- "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
- an even spread. */
- r = (i & 0x07);
- g = (i & 0x38) >> 3;
- b = (i & 0xC0) >> 6;
-
- r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
- g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
- b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
- (b << 6) | (b << 4) | (b << 2) | b);
- }
- else
- {
- /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
- "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
- spread. */
- r = (i & 0x00F);
- g = (i & 0x0F0) >> 4;
- b = (i & 0xF00) >> 8;
-
- r = (r << 12) | (r << 8) | (r << 4) | r;
- g = (g << 12) | (g << 8) | (g << 4) | g;
- b = (b << 12) | (b << 8) | (b << 4) | b;
- }
-
- map[i] = find_closest_pixel (colors, cells, r, g, b);
- }
-
- if (grab_verbose_p)
- fprintf(stderr, "%s: remapping colors in %d bit image\n",
- progname, image->depth);
-
- for (y = 0; y < image->height; y++)
- for (x = 0; x < image->width; x++)
- {
- unsigned long pixel = XGetPixel(image, x, y);
- if (pixel >= cells) abort();
- XPutPixel(image, x, y, map[pixel]);
- }
-}
-
-
-#endif /* HAVE_READ_DISPLAY_EXTENSION */