1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998
2 * Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* This file contains code for grabbing an image of the screen to hack its
14 bits. This is a little tricky, since doing this involves the need to tell
15 the difference between drawing on the actual root window, and on the fake
16 root window used by the screensaver, since at this level the illusion
23 #include <X11/Xatom.h>
24 #include <X11/Xutil.h>
28 # include <X11/Xmu/WinUtil.h>
30 # include <Xmu/WinUtil.h>
36 #include "grabscreen.h"
38 #include "resources.h"
41 #undef RootWindowOfScreen
43 #undef DefaultRootWindow
46 #ifdef HAVE_READ_DISPLAY_EXTENSION
47 # include <X11/extensions/readdisplay.h>
48 static Bool read_display (Screen *, Window, Pixmap, Bool);
49 #endif /* HAVE_READ_DISPLAY_EXTENSION */
52 static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
54 #ifdef HAVE_READ_DISPLAY_EXTENSION
55 static void allocate_cubic_colormap (Screen *, Window, Visual *);
56 void remap_image (Screen *, Window, Colormap, XImage *);
61 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
63 return (event->xany.type == MapNotify &&
64 event->xvisibility.window == (Window) window);
67 extern char *progname;
68 Bool grab_verbose_p = False;
71 grabscreen_verbose(void)
73 grab_verbose_p = True;
78 raise_window(Display *dpy, Window window, Bool dont_wait)
81 fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
82 progname, (unsigned long) window,
83 (dont_wait ? "not waiting" : "waiting"));
87 XWindowAttributes xgwa;
90 memset(&hints, 0, sizeof(hints));
91 XGetWMNormalHints(dpy, window, &hints, &supplied);
92 XGetWindowAttributes (dpy, window, &xgwa);
95 hints.width = xgwa.width;
96 hints.height = xgwa.height;
97 hints.flags |= (PPosition|USPosition|PSize|USSize);
98 XSetWMNormalHints(dpy, window, &hints);
100 XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask));
103 XMapRaised(dpy, window);
108 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
115 xscreensaver_window_p (Display *dpy, Window window)
119 unsigned long nitems, bytesafter;
121 if (XGetWindowProperty (dpy, window,
122 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
123 0, 1, False, XA_STRING,
124 &type, &format, &nitems, &bytesafter,
125 (unsigned char **) &version)
134 /* Whether the given window is:
135 - the real root window;
136 - the virtual root window;
137 - a direct child of the root window;
138 - a direct child of the window manager's decorations.
141 top_level_window_p (Screen *screen, Window window)
143 Display *dpy = DisplayOfScreen (screen);
144 Window root, parent, *kids;
145 Window vroot = VirtualRootWindowOfScreen(screen);
151 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
157 /* If our direct parent is the root (or *a* root), then yes. */
158 if (parent == root || parent == vroot)
164 unsigned long nitems, bytesafter;
167 /* If our direct parent has the WM_STATE property, then it is a
168 window manager decoration -- yes.
170 if (XGetWindowProperty (dpy, window,
171 XInternAtom (dpy, "WM_STATE", True),
172 0, 0, False, AnyPropertyType,
173 &type, &format, &nitems, &bytesafter,
174 (unsigned char **) &data)
180 /* Else, no. We're deep in a tree somewhere.
187 static XErrorHandler old_ehandler = 0;
189 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
191 if (error->error_code == BadWindow || error->error_code == BadDrawable)
193 else if (!old_ehandler)
199 return (*old_ehandler) (dpy, error);
203 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
204 on a window whose depth is not the maximal depth of the screen? Or
205 something. Anyway, things don't work unless we: use SubwindowMode for
206 the real root window (or a legitimate virtual root window); but do not
207 use SubwindowMode for the xscreensaver window. I make no attempt to
211 use_subwindow_mode_p(Screen *screen, Window window)
213 if (window != VirtualRootWindowOfScreen(screen))
215 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
222 /* Install the colormaps of all visible windows, deepest first.
223 This should leave the colormaps of the topmost windows installed
224 (if only N colormaps can be installed at a time, then only the
225 topmost N windows will be shown in the right colors.)
228 install_screen_colormaps (Screen *screen)
231 Display *dpy = DisplayOfScreen (screen);
232 Window vroot, real_root;
233 Window parent, *kids = 0;
234 unsigned int nkids = 0;
237 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
239 vroot = VirtualRootWindowOfScreen (screen);
240 if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
241 for (i = 0; i < nkids; i++)
243 XWindowAttributes xgwa;
246 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
247 if (! (client = XmuClientWindow (dpy, kids[i])))
251 XGetWindowAttributes (dpy, client, &xgwa);
252 if (xgwa.colormap && xgwa.map_state == IsViewable)
253 XInstallColormap (dpy, xgwa.colormap);
255 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
257 XSetErrorHandler (old_ehandler);
261 XFree ((char *) kids);
266 grab_screen_image (Screen *screen, Window window)
268 Display *dpy = DisplayOfScreen (screen);
269 XWindowAttributes xgwa;
273 Bool grab_mouse_p = False;
276 real_root = XRootWindowOfScreen (screen); /* not vroot */
277 root_p = (window == real_root);
278 saver_p = xscreensaver_window_p (dpy, window);
280 XGetWindowAttributes (dpy, window, &xgwa);
281 screen = xgwa.screen;
284 /* I think this is redundant, but just to be safe... */
288 /* The only time grabbing the mouse is important is if this program
289 is being run while the saver is locking the screen. */
297 unmap = get_float_resource("grabRootDelay", "Seconds");
298 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
302 unmap = get_float_resource("grabWindowDelay", "Seconds");
303 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
305 unmap_time = unmap * 100000;
311 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
312 progname, (unsigned long) window,
313 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
315 fprintf(stderr, "%s: ", progname);
316 describe_visual(stderr, screen, xgwa.visual, False);
317 fprintf (stderr, "\n");
321 if (!root_p && !top_level_window_p (screen, window))
324 fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
325 progname, (unsigned long) window);
331 XSetWindowBackgroundPixmap (dpy, window, None);
335 /* prevent random viewer of the screen saver (locker) from messing
336 with windows. We don't check whether it succeeded, because what
337 are our options, really... */
338 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
339 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
340 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
346 XUnmapWindow (dpy, window);
347 install_screen_colormaps (screen);
349 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
354 #ifdef HAVE_READ_DISPLAY_EXTENSION
355 if (! read_display(screen, window, 0, saver_p))
356 #endif /* HAVE_READ_DISPLAY_EXTENSION */
358 #ifdef HAVE_READ_DISPLAY_EXTENSION
360 fprintf(stderr, "%s: read_display() failed\n", progname);
361 #endif /* HAVE_READ_DISPLAY_EXTENSION */
363 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
364 raise_window(dpy, window, saver_p);
366 /* Generally it's bad news to call XInstallColormap() explicitly,
367 but this file does a lot of sleazy stuff already... This is to
368 make sure that the window's colormap is installed, even in the
369 case where the window is OverrideRedirect. */
370 if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
377 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
379 #ifdef HAVE_READ_DISPLAY_EXTENSION
380 if (! read_display(screen, window, pixmap, True))
383 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
387 #ifdef HAVE_READ_DISPLAY_EXTENSION
389 fprintf(stderr, "%s: read_display() failed\n", progname);
390 #endif /* HAVE_READ_DISPLAY_EXTENSION */
392 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
394 gcv.function = GXcopy;
395 gcv.subwindow_mode = IncludeInferiors;
396 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
397 XCopyArea (dpy, real_root, pixmap, gc,
398 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
401 XSetWindowBackgroundPixmap (dpy, window, pixmap);
402 XFreePixmap (dpy, pixmap);
406 fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
407 progname, xgwa.depth,
408 (root_p ? "real root " : ""));
412 XUngrabPointer (dpy, CurrentTime);
413 XUngrabKeyboard (dpy, CurrentTime);
420 /* When we are grabbing and manipulating a screen image, it's important that
421 we use the same colormap it originally had. So, if the screensaver was
422 started with -install, we need to copy the contents of the default colormap
423 into the screensaver's colormap.
426 copy_default_colormap_contents (Screen *screen,
430 Display *dpy = DisplayOfScreen (screen);
431 Visual *from_visual = DefaultVisualOfScreen (screen);
432 Colormap from_cmap = XDefaultColormapOfScreen (screen);
434 XColor *old_colors, *new_colors;
435 unsigned long *pixels;
436 XVisualInfo vi_in, *vi_out;
438 int from_cells, to_cells, max_cells, got_cells;
441 if (from_cmap == to_cmap)
444 vi_in.screen = XScreenNumberOfScreen (screen);
445 vi_in.visualid = XVisualIDFromVisual (from_visual);
446 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
448 if (! vi_out) abort ();
449 from_cells = vi_out [0].colormap_size;
450 XFree ((char *) vi_out);
452 vi_in.screen = XScreenNumberOfScreen (screen);
453 vi_in.visualid = XVisualIDFromVisual (to_visual);
454 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
456 if (! vi_out) abort ();
457 to_cells = vi_out [0].colormap_size;
458 XFree ((char *) vi_out);
460 max_cells = (from_cells > to_cells ? to_cells : from_cells);
462 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
463 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
464 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
465 for (i = 0; i < max_cells; i++)
466 old_colors[i].pixel = i;
467 XQueryColors (dpy, from_cmap, old_colors, max_cells);
469 got_cells = max_cells;
470 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
472 if (grab_verbose_p && got_cells != max_cells)
473 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
474 got_cells, max_cells);
476 if (got_cells <= 0) /* we're screwed */
478 else if (got_cells == max_cells && /* we're golden */
479 from_cells == to_cells)
480 XStoreColors (dpy, to_cmap, old_colors, got_cells);
481 else /* try to cope... */
483 for (i = 0; i < got_cells; i++)
485 XColor *c = old_colors + i;
487 for (j = 0; j < got_cells; j++)
488 if (pixels[j] == c->pixel)
490 /* only store this color value if this is one of the pixels
491 we were able to allocate. */
492 XStoreColors (dpy, to_cmap, c, 1);
500 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
509 /* The SGI ReadDisplay extension.
510 This extension lets you get back a 24-bit image of the screen, taking into
511 account the colors with which all windows are *currently* displayed, even
512 if those windows have different visuals. Without this extension, presence
513 of windows with different visuals or colormaps will result in technicolor
514 when one tries to grab the screen image.
517 #ifdef HAVE_READ_DISPLAY_EXTENSION
520 read_display (Screen *screen, Window window, Pixmap into_pixmap,
523 Display *dpy = DisplayOfScreen (screen);
524 XWindowAttributes xgwa;
525 int rd_event_base = 0;
526 int rd_error_base = 0;
527 unsigned long hints = 0;
532 Bool remap_p = False;
534 /* Check to see if the server supports the extension, and bug out if not.
536 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
539 fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
543 /* If this isn't a visual we know how to handle, bug out. We handle:
544 = TrueColor in depths 8, 12, 15, 16, and 32;
545 = PseudoColor and DirectColor in depths 8 and 12.
547 XGetWindowAttributes(dpy, window, &xgwa);
548 class = visual_class (screen, xgwa.visual);
549 if (class == TrueColor)
551 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
552 xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
555 fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
556 progname, xgwa.depth);
560 else if (class == PseudoColor || class == DirectColor)
562 if (xgwa.depth != 8 && xgwa.depth != 12)
565 fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
566 progname, xgwa.depth);
570 /* Allocate a TrueColor-like spread of colors for the image. */
575 /* Try and read the screen.
577 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
578 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
583 fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
589 fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
590 XDestroyImage(image);
594 /* XReadDisplay tends to LIE about the depth of the image it read.
595 It is returning an XImage which has `depth' and `bits_per_pixel'
598 That is, on a 24-bit display, where all visuals claim depth 24, and
599 where XGetImage would return an XImage with depth 24, and where
600 XPutImage will get a BadMatch with images that are not depth 24,
601 XReadDisplay is returning images with depth 32! Fuckwits!
603 So if the visual is of depth 24, but the image came back as depth 32,
604 hack it to be 24 lest we get a BadMatch from XPutImage.
606 I wonder what happens on an 8-bit SGI... Probably it still returns
607 an image claiming depth 32? Certainly it can't be 8. So, let's just
610 if (image->depth == 32 /* && xgwa.depth == 24 */ )
613 /* If the visual of the window/pixmap into which we're going to draw is
614 less deep than the screen itself, then we need to convert the grabbed bits
615 to match the depth by clipping off the less significant bit-planes of each
618 if (image->depth > xgwa.depth)
621 /* We use the same image->data in both images -- that's ok, because
622 since we're reading from B and writing to A, and B uses more bytes
623 per pixel than A, the write pointer won't overrun the read pointer.
625 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
626 ZPixmap, 0, image->data,
627 xgwa.width, xgwa.height,
632 fprintf(stderr, "%s: out of memory?\n", progname);
637 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
638 progname, image->depth, xgwa.depth);
640 for (y = 0; y < image->height; y++)
641 for (x = 0; x < image->width; x++)
643 /* #### really these shift values should be determined from the
644 mask values -- but that's a pain in the ass, and anyway,
645 this is an SGI-specific extension so hardcoding assumptions
646 about the SGI server's behavior isn't *too* heinous... */
647 unsigned long pixel = XGetPixel(image, x, y);
648 unsigned int r = (pixel & image->red_mask);
649 unsigned int g = (pixel & image->green_mask) >> 8;
650 unsigned int b = (pixel & image->blue_mask) >> 16;
653 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
654 else if (xgwa.depth == 12)
655 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
656 else if (xgwa.depth == 16 || xgwa.depth == 15)
657 /* Gah! I don't understand why these are in the other order. */
658 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
662 XPutPixel(image2, x, y, pixel);
665 XDestroyImage(image);
671 allocate_cubic_colormap (screen, window, xgwa.visual);
672 remap_image (screen, window, xgwa.colormap, image);
675 /* Now actually put the bits into the window or pixmap -- note the design
676 bogosity of this extension, where we've been forced to take 24 bit data
677 from the server to the client, and then push it back from the client to
678 the server, *without alteration*. We should have just been able to tell
679 the server, "put a screen image in this drawable", instead of having to
680 go through the intermediate step of converting it to an Image. Geez.
681 (Assuming that the window is of screen depth; we happen to handle less
682 deep windows, but that's beside the point.)
684 gcv.function = GXcopy;
685 gc = XCreateGC (dpy, window, GCFunction, &gcv);
689 gcv.function = GXcopy;
690 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
691 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
692 xgwa.width, xgwa.height);
696 gcv.function = GXcopy;
697 gc = XCreateGC (dpy, window, GCFunction, &gcv);
699 /* Ok, now we'll be needing that window on the screen... */
700 raise_window(dpy, window, dont_wait);
702 /* Plop down the bits... */
703 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
712 XDestroyImage(image);
716 #endif /* HAVE_READ_DISPLAY_EXTENSION */
719 #ifdef HAVE_READ_DISPLAY_EXTENSION
721 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
722 visual behave like a TrueColor visual of the same depth.
725 allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
727 Display *dpy = DisplayOfScreen (screen);
728 XWindowAttributes xgwa;
730 int nr, ng, nb, cells;
736 XGetWindowAttributes (dpy, window, &xgwa);
737 cmap = xgwa.colormap;
738 depth = visual_depth(screen, visual);
742 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
743 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
744 default: abort(); break;
747 memset(colors, 0, sizeof(colors));
748 for (r = 0; r < (1 << nr); r++)
749 for (g = 0; g < (1 << ng); g++)
750 for (b = 0; b < (1 << nb); b++)
752 i = (r | (g << nr) | (b << (nr + ng)));
754 colors[i].flags = DoRed|DoGreen|DoBlue;
757 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
758 (r << 4) | (r << 1));
759 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
760 (g << 4) | (g << 1));
761 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
762 (b << 8) | (b << 6) | (b << 4) |
767 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
768 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
769 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
776 int interleave = cells / 8; /* skip around, rather than allocating in
777 order, so that we get better coverage if
778 we can't allocated all of them. */
779 for (j = 0; j < interleave; j++)
780 for (i = 0; i < cells; i += interleave)
781 if (XAllocColor (dpy, cmap, &colors[i + j]))
785 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
786 progname, allocated, cells);
791 find_closest_pixel (XColor *colors, int ncolors,
792 unsigned long r, unsigned long g, unsigned long b)
794 unsigned long distance = ~0;
799 for (i = 0; i < ncolors; i++)
804 rd = r - colors[i].red;
805 gd = g - colors[i].green;
806 bd = b - colors[i].blue;
807 if (rd < 0) rd = -rd;
808 if (gd < 0) gd = -gd;
809 if (bd < 0) bd = -bd;
810 d = (rd << 1) + (gd << 2) + bd;
826 remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
828 Display *dpy = DisplayOfScreen (screen);
829 unsigned long map[4097];
834 if (image->depth == 8)
836 else if (image->depth == 12)
841 memset(map, -1, sizeof(*map));
842 memset(colors, -1, sizeof(*colors));
844 for (i = 0; i < cells; i++)
846 XQueryColors (dpy, cmap, colors, cells);
849 fprintf(stderr, "%s: building table for %d bit image\n",
850 progname, image->depth);
852 for (i = 0; i < cells; i++)
854 unsigned short r, g, b;
858 /* "RRR GGG BB" In an 8 bit map. Convert that to
859 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
865 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
866 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
867 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
868 (b << 6) | (b << 4) | (b << 2) | b);
872 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
873 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
876 g = (i & 0x0F0) >> 4;
877 b = (i & 0xF00) >> 8;
879 r = (r << 12) | (r << 8) | (r << 4) | r;
880 g = (g << 12) | (g << 8) | (g << 4) | g;
881 b = (b << 12) | (b << 8) | (b << 4) | b;
884 map[i] = find_closest_pixel (colors, cells, r, g, b);
888 fprintf(stderr, "%s: remapping colors in %d bit image\n",
889 progname, image->depth);
891 for (y = 0; y < image->height; y++)
892 for (x = 0; x < image->width; x++)
894 unsigned long pixel = XGetPixel(image, x, y);
895 if (pixel >= cells) abort();
896 XPutPixel(image, x, y, map[pixel]);
901 #endif /* HAVE_READ_DISPLAY_EXTENSION */