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;
270 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
271 Bool root_p = (window == real_root);
272 Bool saver_p = xscreensaver_window_p (dpy, window);
273 Bool grab_mouse_p = False;
277 /* I think this is redundant, but just to be safe... */
281 /* The only time grabbing the mouse is important is if this program
282 is being run while the saver is locking the screen. */
290 unmap = get_float_resource("grabRootDelay", "Seconds");
291 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
295 unmap = get_float_resource("grabWindowDelay", "Seconds");
296 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
298 unmap_time = unmap * 100000;
303 XWindowAttributes xgwa2;
305 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
306 progname, (unsigned long) window,
307 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
309 XGetWindowAttributes (dpy, window, &xgwa2);
310 fprintf(stderr, "%s: ", progname);
311 describe_visual(stderr, screen, xgwa2.visual, False);
312 fprintf (stderr, "\n");
316 if (!root_p && !top_level_window_p (screen, window))
319 fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
320 progname, (unsigned long) window);
326 XSetWindowBackgroundPixmap (dpy, window, None);
330 /* prevent random viewer of the screen saver (locker) from messing
331 with windows. We don't check whether it succeeded, because what
332 are our options, really... */
333 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
334 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
335 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
341 XUnmapWindow (dpy, window);
342 install_screen_colormaps (screen);
344 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
347 XGetWindowAttributes (dpy, window, &xgwa);
351 #ifdef HAVE_READ_DISPLAY_EXTENSION
352 if (! read_display(screen, window, 0, saver_p))
353 #endif /* HAVE_READ_DISPLAY_EXTENSION */
355 #ifdef HAVE_READ_DISPLAY_EXTENSION
357 fprintf(stderr, "%s: read_display() failed\n", progname);
358 #endif /* HAVE_READ_DISPLAY_EXTENSION */
360 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
361 raise_window(dpy, window, saver_p);
363 /* Generally it's bad news to call XInstallColormap() explicitly,
364 but this file does a lot of sleazy stuff already... This is to
365 make sure that the window's colormap is installed, even in the
366 case where the window is OverrideRedirect. */
367 if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
374 XWindowAttributes xgwa;
375 XGetWindowAttributes(dpy, window, &xgwa);
376 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
378 #ifdef HAVE_READ_DISPLAY_EXTENSION
379 if (! read_display(screen, window, pixmap, True))
382 Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
386 #ifdef HAVE_READ_DISPLAY_EXTENSION
388 fprintf(stderr, "%s: read_display() failed\n", progname);
389 #endif /* HAVE_READ_DISPLAY_EXTENSION */
391 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
393 gcv.function = GXcopy;
394 gcv.subwindow_mode = IncludeInferiors;
395 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
396 XCopyArea (dpy, real_root, pixmap, gc,
397 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
400 XSetWindowBackgroundPixmap (dpy, window, pixmap);
401 XFreePixmap (dpy, pixmap);
405 fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
406 progname, xgwa.depth,
407 (root_p ? "real root " : ""));
411 XUngrabPointer (dpy, CurrentTime);
412 XUngrabKeyboard (dpy, CurrentTime);
419 /* When we are grabbing and manipulating a screen image, it's important that
420 we use the same colormap it originally had. So, if the screensaver was
421 started with -install, we need to copy the contents of the default colormap
422 into the screensaver's colormap.
425 copy_default_colormap_contents (Screen *screen,
429 Display *dpy = DisplayOfScreen (screen);
430 Visual *from_visual = DefaultVisualOfScreen (screen);
431 Colormap from_cmap = XDefaultColormapOfScreen (screen);
433 XColor *old_colors, *new_colors;
434 unsigned long *pixels;
435 XVisualInfo vi_in, *vi_out;
437 int from_cells, to_cells, max_cells, got_cells;
440 if (from_cmap == to_cmap)
443 vi_in.screen = XScreenNumberOfScreen (screen);
444 vi_in.visualid = XVisualIDFromVisual (from_visual);
445 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
447 if (! vi_out) abort ();
448 from_cells = vi_out [0].colormap_size;
449 XFree ((char *) vi_out);
451 vi_in.screen = XScreenNumberOfScreen (screen);
452 vi_in.visualid = XVisualIDFromVisual (to_visual);
453 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
455 if (! vi_out) abort ();
456 to_cells = vi_out [0].colormap_size;
457 XFree ((char *) vi_out);
459 max_cells = (from_cells > to_cells ? to_cells : from_cells);
461 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
462 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
463 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
464 for (i = 0; i < max_cells; i++)
465 old_colors[i].pixel = i;
466 XQueryColors (dpy, from_cmap, old_colors, max_cells);
468 got_cells = max_cells;
469 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
471 if (grab_verbose_p && got_cells != max_cells)
472 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
473 got_cells, max_cells);
475 if (got_cells <= 0) /* we're screwed */
477 else if (got_cells == max_cells && /* we're golden */
478 from_cells == to_cells)
479 XStoreColors (dpy, to_cmap, old_colors, got_cells);
480 else /* try to cope... */
482 for (i = 0; i < got_cells; i++)
484 XColor *c = old_colors + i;
486 for (j = 0; j < got_cells; j++)
487 if (pixels[j] == c->pixel)
489 /* only store this color value if this is one of the pixels
490 we were able to allocate. */
491 XStoreColors (dpy, to_cmap, c, 1);
499 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
508 /* The SGI ReadDisplay extension.
509 This extension lets you get back a 24-bit image of the screen, taking into
510 account the colors with which all windows are *currently* displayed, even
511 if those windows have different visuals. Without this extension, presence
512 of windows with different visuals or colormaps will result in technicolor
513 when one tries to grab the screen image.
516 #ifdef HAVE_READ_DISPLAY_EXTENSION
519 read_display (Screen *screen, Window window, Pixmap into_pixmap,
522 Display *dpy = DisplayOfScreen (screen);
523 XWindowAttributes xgwa;
524 int rd_event_base = 0;
525 int rd_error_base = 0;
526 unsigned long hints = 0;
531 Bool remap_p = False;
533 /* Check to see if the server supports the extension, and bug out if not.
535 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
538 fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
542 /* If this isn't a visual we know how to handle, bug out. We handle:
543 = TrueColor in depths 8, 12, 15, 16, and 32;
544 = PseudoColor and DirectColor in depths 8 and 12.
546 XGetWindowAttributes(dpy, window, &xgwa);
547 class = visual_class (screen, xgwa.visual);
548 if (class == TrueColor)
550 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
551 xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
554 fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
555 progname, xgwa.depth);
559 else if (class == PseudoColor || class == DirectColor)
561 if (xgwa.depth != 8 && xgwa.depth != 12)
564 fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
565 progname, xgwa.depth);
569 /* Allocate a TrueColor-like spread of colors for the image. */
574 /* Try and read the screen.
576 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
577 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
582 fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
588 fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
589 XDestroyImage(image);
593 /* XReadDisplay tends to LIE about the depth of the image it read.
594 It is returning an XImage which has `depth' and `bits_per_pixel'
597 That is, on a 24-bit display, where all visuals claim depth 24, and
598 where XGetImage would return an XImage with depth 24, and where
599 XPutImage will get a BadMatch with images that are not depth 24,
600 XReadDisplay is returning images with depth 32! Fuckwits!
602 So if the visual is of depth 24, but the image came back as depth 32,
603 hack it to be 24 lest we get a BadMatch from XPutImage.
605 I wonder what happens on an 8-bit SGI... Probably it still returns
606 an image claiming depth 32? Certainly it can't be 8. So, let's just
609 if (image->depth == 32 /* && xgwa.depth == 24 */ )
612 /* If the visual of the window/pixmap into which we're going to draw is
613 less deep than the screen itself, then we need to convert the grabbed bits
614 to match the depth by clipping off the less significant bit-planes of each
617 if (image->depth > xgwa.depth)
620 /* We use the same image->data in both images -- that's ok, because
621 since we're reading from B and writing to A, and B uses more bytes
622 per pixel than A, the write pointer won't overrun the read pointer.
624 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
625 ZPixmap, 0, image->data,
626 xgwa.width, xgwa.height,
631 fprintf(stderr, "%s: out of memory?\n", progname);
636 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
637 progname, image->depth, xgwa.depth);
639 for (y = 0; y < image->height; y++)
640 for (x = 0; x < image->width; x++)
642 /* #### really these shift values should be determined from the
643 mask values -- but that's a pain in the ass, and anyway,
644 this is an SGI-specific extension so hardcoding assumptions
645 about the SGI server's behavior isn't *too* heinous... */
646 unsigned long pixel = XGetPixel(image, x, y);
647 unsigned int r = (pixel & image->red_mask);
648 unsigned int g = (pixel & image->green_mask) >> 8;
649 unsigned int b = (pixel & image->blue_mask) >> 16;
652 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
653 else if (xgwa.depth == 12)
654 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
655 else if (xgwa.depth == 16 || xgwa.depth == 15)
656 /* Gah! I don't understand why these are in the other order. */
657 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
661 XPutPixel(image2, x, y, pixel);
664 XDestroyImage(image);
670 allocate_cubic_colormap (screen, window, xgwa.visual);
671 remap_image (screen, window, xgwa.colormap, image);
674 /* Now actually put the bits into the window or pixmap -- note the design
675 bogosity of this extension, where we've been forced to take 24 bit data
676 from the server to the client, and then push it back from the client to
677 the server, *without alteration*. We should have just been able to tell
678 the server, "put a screen image in this drawable", instead of having to
679 go through the intermediate step of converting it to an Image. Geez.
680 (Assuming that the window is of screen depth; we happen to handle less
681 deep windows, but that's beside the point.)
683 gcv.function = GXcopy;
684 gc = XCreateGC (dpy, window, GCFunction, &gcv);
688 gcv.function = GXcopy;
689 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
690 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
691 xgwa.width, xgwa.height);
695 gcv.function = GXcopy;
696 gc = XCreateGC (dpy, window, GCFunction, &gcv);
698 /* Ok, now we'll be needing that window on the screen... */
699 raise_window(dpy, window, dont_wait);
701 /* Plop down the bits... */
702 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
711 XDestroyImage(image);
715 #endif /* HAVE_READ_DISPLAY_EXTENSION */
718 #ifdef HAVE_READ_DISPLAY_EXTENSION
720 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
721 visual behave like a TrueColor visual of the same depth.
724 allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
726 Display *dpy = DisplayOfScreen (screen);
727 XWindowAttributes xgwa;
729 int nr, ng, nb, cells;
735 XGetWindowAttributes (dpy, window, &xgwa);
736 cmap = xgwa.colormap;
737 depth = visual_depth(screen, visual);
741 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
742 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
743 default: abort(); break;
746 memset(colors, 0, sizeof(colors));
747 for (r = 0; r < (1 << nr); r++)
748 for (g = 0; g < (1 << ng); g++)
749 for (b = 0; b < (1 << nb); b++)
751 i = (r | (g << nr) | (b << (nr + ng)));
753 colors[i].flags = DoRed|DoGreen|DoBlue;
756 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
757 (r << 4) | (r << 1));
758 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
759 (g << 4) | (g << 1));
760 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
761 (b << 8) | (b << 6) | (b << 4) |
766 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
767 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
768 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
775 int interleave = cells / 8; /* skip around, rather than allocating in
776 order, so that we get better coverage if
777 we can't allocated all of them. */
778 for (j = 0; j < interleave; j++)
779 for (i = 0; i < cells; i += interleave)
780 if (XAllocColor (dpy, cmap, &colors[i + j]))
784 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
785 progname, allocated, cells);
790 find_closest_pixel (XColor *colors, int ncolors,
791 unsigned long r, unsigned long g, unsigned long b)
793 unsigned long distance = ~0;
798 for (i = 0; i < ncolors; i++)
803 rd = r - colors[i].red;
804 gd = g - colors[i].green;
805 bd = b - colors[i].blue;
806 if (rd < 0) rd = -rd;
807 if (gd < 0) gd = -gd;
808 if (bd < 0) bd = -bd;
809 d = (rd << 1) + (gd << 2) + bd;
825 remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
827 Display *dpy = DisplayOfScreen (screen);
828 unsigned long map[4097];
833 if (image->depth == 8)
835 else if (image->depth == 12)
840 memset(map, -1, sizeof(*map));
841 memset(colors, -1, sizeof(*colors));
843 for (i = 0; i < cells; i++)
845 XQueryColors (dpy, cmap, colors, cells);
848 fprintf(stderr, "%s: building table for %d bit image\n",
849 progname, image->depth);
851 for (i = 0; i < cells; i++)
853 unsigned short r, g, b;
857 /* "RRR GGG BB" In an 8 bit map. Convert that to
858 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
864 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
865 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
866 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
867 (b << 6) | (b << 4) | (b << 2) | b);
871 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
872 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
875 g = (i & 0x0F0) >> 4;
876 b = (i & 0xF00) >> 8;
878 r = (r << 12) | (r << 8) | (r << 4) | r;
879 g = (g << 12) | (g << 8) | (g << 4) | g;
880 b = (b << 12) | (b << 8) | (b << 4) | b;
883 map[i] = find_closest_pixel (colors, cells, r, g, b);
887 fprintf(stderr, "%s: remapping colors in %d bit image\n",
888 progname, image->depth);
890 for (y = 0; y < image->height; y++)
891 for (x = 0; x < image->width; x++)
893 unsigned long pixel = XGetPixel(image, x, y);
894 if (pixel >= cells) abort();
895 XPutPixel(image, x, y, map[pixel]);
900 #endif /* HAVE_READ_DISPLAY_EXTENSION */