1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998, 2003, 2004
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;
120 unsigned char *version;
121 if (XGetWindowProperty (dpy, window,
122 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
123 0, 1, False, XA_STRING,
124 &type, &format, &nitems, &bytesafter,
134 /* Whether the given window is:
135 - the real root window;
136 - a direct child of the root window;
137 - a direct child of the window manager's decorations.
140 top_level_window_p (Screen *screen, Window window)
142 Display *dpy = DisplayOfScreen (screen);
143 Window root, parent, *kids;
146 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
152 /* If our direct parent is the real root window, then yes. */
159 unsigned long nitems, bytesafter;
162 /* If our direct parent has the WM_STATE property, then it is a
163 window manager decoration -- yes.
165 if (XGetWindowProperty (dpy, window,
166 XInternAtom (dpy, "WM_STATE", True),
167 0, 0, False, AnyPropertyType,
168 &type, &format, &nitems, &bytesafter,
169 (unsigned char **) &data)
175 /* Else, no. We're deep in a tree somewhere.
181 static Bool error_handler_hit_p = False;
182 static XErrorHandler old_ehandler = 0;
184 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
186 error_handler_hit_p = True;
187 if (error->error_code == BadWindow || error->error_code == BadDrawable)
189 else if (!old_ehandler)
195 return (*old_ehandler) (dpy, error);
199 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
200 on a window whose depth is not the maximal depth of the screen? Or
201 something. Anyway, things don't work unless we: use SubwindowMode for
202 the real root window (or a legitimate virtual root window); but do not
203 use SubwindowMode for the xscreensaver window. I make no attempt to
207 use_subwindow_mode_p(Screen *screen, Window window)
209 if (window != VirtualRootWindowOfScreen(screen))
211 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
218 /* Install the colormaps of all visible windows, deepest first.
219 This should leave the colormaps of the topmost windows installed
220 (if only N colormaps can be installed at a time, then only the
221 topmost N windows will be shown in the right colors.)
224 install_screen_colormaps (Screen *screen)
227 Display *dpy = DisplayOfScreen (screen);
229 Window parent, *kids = 0;
230 unsigned int nkids = 0;
233 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
234 error_handler_hit_p = False;
236 real_root = XRootWindowOfScreen (screen); /* not vroot */
237 if (XQueryTree (dpy, real_root, &real_root, &parent, &kids, &nkids))
238 for (i = 0; i < nkids; i++)
240 XWindowAttributes xgwa;
243 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
244 if (! (client = XmuClientWindow (dpy, kids[i])))
248 XGetWindowAttributes (dpy, client, &xgwa);
249 if (xgwa.colormap && xgwa.map_state == IsViewable)
250 XInstallColormap (dpy, xgwa.colormap);
252 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
254 XSetErrorHandler (old_ehandler);
258 XFree ((char *) kids);
263 grab_screen_image_internal (Screen *screen, Window window)
265 Display *dpy = DisplayOfScreen (screen);
266 XWindowAttributes xgwa;
270 Bool grab_mouse_p = False;
273 real_root = XRootWindowOfScreen (screen); /* not vroot */
274 root_p = (window == real_root);
275 saver_p = xscreensaver_window_p (dpy, window);
277 XGetWindowAttributes (dpy, window, &xgwa);
278 screen = xgwa.screen;
281 /* I think this is redundant, but just to be safe... */
285 /* The only time grabbing the mouse is important is if this program
286 is being run while the saver is locking the screen. */
294 unmap = get_float_resource("grabRootDelay", "Seconds");
295 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
299 unmap = get_float_resource("grabWindowDelay", "Seconds");
300 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
302 unmap_time = unmap * 100000;
308 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
309 progname, (unsigned long) window,
310 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
312 fprintf(stderr, "%s: ", progname);
313 describe_visual(stderr, screen, xgwa.visual, False);
314 fprintf (stderr, "\n");
318 if (!root_p && !top_level_window_p (screen, window))
321 fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
322 progname, (unsigned long) window);
328 XSetWindowBackgroundPixmap (dpy, window, None);
332 /* prevent random viewer of the screen saver (locker) from messing
333 with windows. We don't check whether it succeeded, because what
334 are our options, really... */
335 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
336 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
337 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
343 XUnmapWindow (dpy, window);
344 install_screen_colormaps (screen);
346 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
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 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
376 #ifdef HAVE_READ_DISPLAY_EXTENSION
377 if (! read_display(screen, window, pixmap, True))
380 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
384 #ifdef HAVE_READ_DISPLAY_EXTENSION
386 fprintf(stderr, "%s: read_display() failed\n", progname);
387 #endif /* HAVE_READ_DISPLAY_EXTENSION */
389 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
391 gcv.function = GXcopy;
392 gcv.subwindow_mode = IncludeInferiors;
393 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
394 XCopyArea (dpy, real_root, pixmap, gc,
395 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
398 XSetWindowBackgroundPixmap (dpy, window, pixmap);
399 XFreePixmap (dpy, pixmap);
403 fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
404 progname, xgwa.depth,
405 (root_p ? "real root " : ""));
409 XUngrabPointer (dpy, CurrentTime);
410 XUngrabKeyboard (dpy, CurrentTime);
417 /* When we are grabbing and manipulating a screen image, it's important that
418 we use the same colormap it originally had. So, if the screensaver was
419 started with -install, we need to copy the contents of the default colormap
420 into the screensaver's colormap.
423 copy_default_colormap_contents (Screen *screen,
427 Display *dpy = DisplayOfScreen (screen);
428 Visual *from_visual = DefaultVisualOfScreen (screen);
429 Colormap from_cmap = XDefaultColormapOfScreen (screen);
431 XColor *old_colors, *new_colors;
432 unsigned long *pixels;
433 XVisualInfo vi_in, *vi_out;
435 int from_cells, to_cells, max_cells, got_cells;
438 if (from_cmap == to_cmap)
441 vi_in.screen = XScreenNumberOfScreen (screen);
442 vi_in.visualid = XVisualIDFromVisual (from_visual);
443 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
445 if (! vi_out) abort ();
446 from_cells = vi_out [0].colormap_size;
447 XFree ((char *) vi_out);
449 vi_in.screen = XScreenNumberOfScreen (screen);
450 vi_in.visualid = XVisualIDFromVisual (to_visual);
451 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
453 if (! vi_out) abort ();
454 to_cells = vi_out [0].colormap_size;
455 XFree ((char *) vi_out);
457 max_cells = (from_cells > to_cells ? to_cells : from_cells);
459 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
460 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
461 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
462 for (i = 0; i < max_cells; i++)
463 old_colors[i].pixel = i;
464 XQueryColors (dpy, from_cmap, old_colors, max_cells);
466 got_cells = max_cells;
467 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
469 if (grab_verbose_p && got_cells != max_cells)
470 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
471 got_cells, max_cells);
473 if (got_cells <= 0) /* we're screwed */
475 else if (got_cells == max_cells && /* we're golden */
476 from_cells == to_cells)
477 XStoreColors (dpy, to_cmap, old_colors, got_cells);
478 else /* try to cope... */
480 for (i = 0; i < got_cells; i++)
482 XColor *c = old_colors + i;
484 for (j = 0; j < got_cells; j++)
485 if (pixels[j] == c->pixel)
487 /* only store this color value if this is one of the pixels
488 we were able to allocate. */
489 XStoreColors (dpy, to_cmap, c, 1);
497 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
506 /* The SGI ReadDisplay extension.
507 This extension lets you get back a 24-bit image of the screen, taking into
508 account the colors with which all windows are *currently* displayed, even
509 if those windows have different visuals. Without this extension, presence
510 of windows with different visuals or colormaps will result in technicolor
511 when one tries to grab the screen image.
514 #ifdef HAVE_READ_DISPLAY_EXTENSION
517 read_display (Screen *screen, Window window, Pixmap into_pixmap,
520 Display *dpy = DisplayOfScreen (screen);
521 XWindowAttributes xgwa;
522 int rd_event_base = 0;
523 int rd_error_base = 0;
524 unsigned long hints = 0;
529 Bool remap_p = False;
531 /* Check to see if the server supports the extension, and bug out if not.
533 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
536 fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
540 /* If this isn't a visual we know how to handle, bug out. We handle:
541 = TrueColor in depths 8, 12, 15, 16, and 32;
542 = PseudoColor and DirectColor in depths 8 and 12.
544 XGetWindowAttributes(dpy, window, &xgwa);
545 class = visual_class (screen, xgwa.visual);
546 if (class == TrueColor)
548 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
549 xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
552 fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
553 progname, xgwa.depth);
557 else if (class == PseudoColor || class == DirectColor)
559 if (xgwa.depth != 8 && xgwa.depth != 12)
562 fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
563 progname, xgwa.depth);
567 /* Allocate a TrueColor-like spread of colors for the image. */
572 /* Try and read the screen.
574 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
575 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
580 fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
586 fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
587 XDestroyImage(image);
591 /* XReadDisplay tends to LIE about the depth of the image it read.
592 It is returning an XImage which has `depth' and `bits_per_pixel'
595 That is, on a 24-bit display, where all visuals claim depth 24, and
596 where XGetImage would return an XImage with depth 24, and where
597 XPutImage will get a BadMatch with images that are not depth 24,
598 XReadDisplay is returning images with depth 32! Fuckwits!
600 So if the visual is of depth 24, but the image came back as depth 32,
601 hack it to be 24 lest we get a BadMatch from XPutImage.
603 I wonder what happens on an 8-bit SGI... Probably it still returns
604 an image claiming depth 32? Certainly it can't be 8. So, let's just
607 if (image->depth == 32 /* && xgwa.depth == 24 */ )
610 /* If the visual of the window/pixmap into which we're going to draw is
611 less deep than the screen itself, then we need to convert the grabbed bits
612 to match the depth by clipping off the less significant bit-planes of each
615 if (image->depth > xgwa.depth)
618 /* We use the same image->data in both images -- that's ok, because
619 since we're reading from B and writing to A, and B uses more bytes
620 per pixel than A, the write pointer won't overrun the read pointer.
622 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
623 ZPixmap, 0, image->data,
624 xgwa.width, xgwa.height,
629 fprintf(stderr, "%s: out of memory?\n", progname);
634 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
635 progname, image->depth, xgwa.depth);
637 for (y = 0; y < image->height; y++)
638 for (x = 0; x < image->width; x++)
640 /* #### really these shift values should be determined from the
641 mask values -- but that's a pain in the ass, and anyway,
642 this is an SGI-specific extension so hardcoding assumptions
643 about the SGI server's behavior isn't *too* heinous... */
644 unsigned long pixel = XGetPixel(image, x, y);
645 unsigned int r = (pixel & image->red_mask);
646 unsigned int g = (pixel & image->green_mask) >> 8;
647 unsigned int b = (pixel & image->blue_mask) >> 16;
650 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
651 else if (xgwa.depth == 12)
652 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
653 else if (xgwa.depth == 16 || xgwa.depth == 15)
654 /* Gah! I don't understand why these are in the other order. */
655 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
659 XPutPixel(image2, x, y, pixel);
662 XDestroyImage(image);
668 allocate_cubic_colormap (screen, window, xgwa.visual);
669 remap_image (screen, window, xgwa.colormap, image);
672 /* Now actually put the bits into the window or pixmap -- note the design
673 bogosity of this extension, where we've been forced to take 24 bit data
674 from the server to the client, and then push it back from the client to
675 the server, *without alteration*. We should have just been able to tell
676 the server, "put a screen image in this drawable", instead of having to
677 go through the intermediate step of converting it to an Image. Geez.
678 (Assuming that the window is of screen depth; we happen to handle less
679 deep windows, but that's beside the point.)
681 gcv.function = GXcopy;
682 gc = XCreateGC (dpy, window, GCFunction, &gcv);
686 gcv.function = GXcopy;
687 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
688 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
689 xgwa.width, xgwa.height);
693 gcv.function = GXcopy;
694 gc = XCreateGC (dpy, window, GCFunction, &gcv);
696 /* Ok, now we'll be needing that window on the screen... */
697 raise_window(dpy, window, dont_wait);
699 /* Plop down the bits... */
700 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
709 XDestroyImage(image);
713 #endif /* HAVE_READ_DISPLAY_EXTENSION */
716 #ifdef HAVE_READ_DISPLAY_EXTENSION
718 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
719 visual behave like a TrueColor visual of the same depth.
722 allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
724 Display *dpy = DisplayOfScreen (screen);
725 XWindowAttributes xgwa;
727 int nr, ng, nb, cells;
733 XGetWindowAttributes (dpy, window, &xgwa);
734 cmap = xgwa.colormap;
735 depth = visual_depth(screen, visual);
739 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
740 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
741 default: abort(); break;
744 memset(colors, 0, sizeof(colors));
745 for (r = 0; r < (1 << nr); r++)
746 for (g = 0; g < (1 << ng); g++)
747 for (b = 0; b < (1 << nb); b++)
749 i = (r | (g << nr) | (b << (nr + ng)));
751 colors[i].flags = DoRed|DoGreen|DoBlue;
754 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
755 (r << 4) | (r << 1));
756 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
757 (g << 4) | (g << 1));
758 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
759 (b << 8) | (b << 6) | (b << 4) |
764 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
765 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
766 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
773 int interleave = cells / 8; /* skip around, rather than allocating in
774 order, so that we get better coverage if
775 we can't allocated all of them. */
776 for (j = 0; j < interleave; j++)
777 for (i = 0; i < cells; i += interleave)
778 if (XAllocColor (dpy, cmap, &colors[i + j]))
782 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
783 progname, allocated, cells);
788 find_closest_pixel (XColor *colors, int ncolors,
789 unsigned long r, unsigned long g, unsigned long b)
791 unsigned long distance = ~0;
796 for (i = 0; i < ncolors; i++)
801 rd = r - colors[i].red;
802 gd = g - colors[i].green;
803 bd = b - colors[i].blue;
804 if (rd < 0) rd = -rd;
805 if (gd < 0) gd = -gd;
806 if (bd < 0) bd = -bd;
807 d = (rd << 1) + (gd << 2) + bd;
823 remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
825 Display *dpy = DisplayOfScreen (screen);
826 unsigned long map[4097];
831 if (image->depth == 8)
833 else if (image->depth == 12)
838 memset(map, -1, sizeof(*map));
839 memset(colors, -1, sizeof(*colors));
841 for (i = 0; i < cells; i++)
843 XQueryColors (dpy, cmap, colors, cells);
846 fprintf(stderr, "%s: building table for %d bit image\n",
847 progname, image->depth);
849 for (i = 0; i < cells; i++)
851 unsigned short r, g, b;
855 /* "RRR GGG BB" In an 8 bit map. Convert that to
856 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
862 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
863 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
864 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
865 (b << 6) | (b << 4) | (b << 2) | b);
869 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
870 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
873 g = (i & 0x0F0) >> 4;
874 b = (i & 0xF00) >> 8;
876 r = (r << 12) | (r << 8) | (r << 4) | r;
877 g = (g << 12) | (g << 8) | (g << 4) | g;
878 b = (b << 12) | (b << 8) | (b << 4) | b;
881 map[i] = find_closest_pixel (colors, cells, r, g, b);
885 fprintf(stderr, "%s: remapping colors in %d bit image\n",
886 progname, image->depth);
888 for (y = 0; y < image->height; y++)
889 for (x = 0; x < image->width; x++)
891 unsigned long pixel = XGetPixel(image, x, y);
892 if (pixel >= cells) abort();
893 XPutPixel(image, x, y, map[pixel]);
898 #endif /* HAVE_READ_DISPLAY_EXTENSION */