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 static XErrorHandler old_ehandler = 0;
136 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
138 if (error->error_code == BadWindow || error->error_code == BadDrawable)
140 else if (!old_ehandler)
146 return (*old_ehandler) (dpy, error);
150 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
151 on a window whose depth is not the maximal depth of the screen? Or
152 something. Anyway, things don't work unless we: use SubwindowMode for
153 the real root window (or a legitimate virtual root window); but do not
154 use SubwindowMode for the xscreensaver window. I make no attempt to
158 use_subwindow_mode_p(Screen *screen, Window window)
160 if (window != VirtualRootWindowOfScreen(screen))
162 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
169 /* Install the colormaps of all visible windows, deepest first.
170 This should leave the colormaps of the topmost windows installed
171 (if only N colormaps can be installed at a time, then only the
172 topmost N windows will be shown in the right colors.)
175 install_screen_colormaps (Screen *screen)
178 Display *dpy = DisplayOfScreen (screen);
179 Window vroot, real_root;
180 Window parent, *kids = 0;
181 unsigned int nkids = 0;
184 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
186 vroot = VirtualRootWindowOfScreen (screen);
187 if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
188 for (i = 0; i < nkids; i++)
190 XWindowAttributes xgwa;
193 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
194 if (! (client = XmuClientWindow (dpy, kids[i])))
198 XGetWindowAttributes (dpy, client, &xgwa);
199 if (xgwa.colormap && xgwa.map_state == IsViewable)
200 XInstallColormap (dpy, xgwa.colormap);
202 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
204 XSetErrorHandler (old_ehandler);
208 XFree ((char *) kids);
213 grab_screen_image (Screen *screen, Window window)
215 Display *dpy = DisplayOfScreen (screen);
216 XWindowAttributes xgwa;
217 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
218 Bool root_p = (window == real_root);
219 Bool saver_p = xscreensaver_window_p (dpy, window);
220 Bool grab_mouse_p = False;
224 /* I think this is redundant, but just to be safe... */
228 /* The only time grabbing the mouse is important is if this program
229 is being run while the saver is locking the screen. */
237 unmap = get_float_resource("grabRootDelay", "Seconds");
238 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
242 unmap = get_float_resource("grabWindowDelay", "Seconds");
243 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
245 unmap_time = unmap * 100000;
250 XWindowAttributes xgwa2;
252 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
253 progname, (unsigned long) window,
254 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
256 XGetWindowAttributes (dpy, window, &xgwa2);
257 fprintf(stderr, "%s: ", progname);
258 describe_visual(stderr, screen, xgwa2.visual, False);
259 fprintf (stderr, "\n");
263 XSetWindowBackgroundPixmap (dpy, window, None);
267 /* prevent random viewer of the screen saver (locker) from messing
268 with windows. We don't check whether it succeeded, because what
269 are our options, really... */
270 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
271 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
272 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
278 XUnmapWindow (dpy, window);
279 install_screen_colormaps (screen);
281 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
284 XGetWindowAttributes (dpy, window, &xgwa);
288 #ifdef HAVE_READ_DISPLAY_EXTENSION
289 if (! read_display(screen, window, 0, saver_p))
290 #endif /* HAVE_READ_DISPLAY_EXTENSION */
292 #ifdef HAVE_READ_DISPLAY_EXTENSION
294 fprintf(stderr, "%s: read_display() failed\n", progname);
295 #endif /* HAVE_READ_DISPLAY_EXTENSION */
297 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
298 raise_window(dpy, window, saver_p);
300 /* Generally it's bad news to call XInstallColormap() explicitly,
301 but this file does a lot of sleazy stuff already... This is to
302 make sure that the window's colormap is installed, even in the
303 case where the window is OverrideRedirect. */
304 if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
311 XWindowAttributes xgwa;
312 XGetWindowAttributes(dpy, window, &xgwa);
313 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
315 #ifdef HAVE_READ_DISPLAY_EXTENSION
316 if (! read_display(screen, window, pixmap, True))
319 Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
323 #ifdef HAVE_READ_DISPLAY_EXTENSION
325 fprintf(stderr, "%s: read_display() failed\n", progname);
326 #endif /* HAVE_READ_DISPLAY_EXTENSION */
328 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
330 gcv.function = GXcopy;
331 gcv.subwindow_mode = IncludeInferiors;
332 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
333 XCopyArea (dpy, real_root, pixmap, gc,
334 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
337 XSetWindowBackgroundPixmap (dpy, window, pixmap);
338 XFreePixmap (dpy, pixmap);
342 fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
343 progname, xgwa.depth,
344 (root_p ? "real root " : ""));
348 XUngrabPointer (dpy, CurrentTime);
349 XUngrabKeyboard (dpy, CurrentTime);
356 /* When we are grabbing and manipulating a screen image, it's important that
357 we use the same colormap it originally had. So, if the screensaver was
358 started with -install, we need to copy the contents of the default colormap
359 into the screensaver's colormap.
362 copy_default_colormap_contents (Screen *screen,
366 Display *dpy = DisplayOfScreen (screen);
367 Visual *from_visual = DefaultVisualOfScreen (screen);
368 Colormap from_cmap = XDefaultColormapOfScreen (screen);
370 XColor *old_colors, *new_colors;
371 unsigned long *pixels;
372 XVisualInfo vi_in, *vi_out;
374 int from_cells, to_cells, max_cells, got_cells;
377 if (from_cmap == to_cmap)
380 vi_in.screen = XScreenNumberOfScreen (screen);
381 vi_in.visualid = XVisualIDFromVisual (from_visual);
382 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
384 if (! vi_out) abort ();
385 from_cells = vi_out [0].colormap_size;
386 XFree ((char *) vi_out);
388 vi_in.screen = XScreenNumberOfScreen (screen);
389 vi_in.visualid = XVisualIDFromVisual (to_visual);
390 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
392 if (! vi_out) abort ();
393 to_cells = vi_out [0].colormap_size;
394 XFree ((char *) vi_out);
396 max_cells = (from_cells > to_cells ? to_cells : from_cells);
398 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
399 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
400 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
401 for (i = 0; i < max_cells; i++)
402 old_colors[i].pixel = i;
403 XQueryColors (dpy, from_cmap, old_colors, max_cells);
405 got_cells = max_cells;
406 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
408 if (grab_verbose_p && got_cells != max_cells)
409 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
410 got_cells, max_cells);
412 if (got_cells <= 0) /* we're screwed */
414 else if (got_cells == max_cells && /* we're golden */
415 from_cells == to_cells)
416 XStoreColors (dpy, to_cmap, old_colors, got_cells);
417 else /* try to cope... */
419 for (i = 0; i < got_cells; i++)
421 XColor *c = old_colors + i;
423 for (j = 0; j < got_cells; j++)
424 if (pixels[j] == c->pixel)
426 /* only store this color value if this is one of the pixels
427 we were able to allocate. */
428 XStoreColors (dpy, to_cmap, c, 1);
436 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
445 /* The SGI ReadDisplay extension.
446 This extension lets you get back a 24-bit image of the screen, taking into
447 account the colors with which all windows are *currently* displayed, even
448 if those windows have different visuals. Without this extension, presence
449 of windows with different visuals or colormaps will result in technicolor
450 when one tries to grab the screen image.
453 #ifdef HAVE_READ_DISPLAY_EXTENSION
456 read_display (Screen *screen, Window window, Pixmap into_pixmap,
459 Display *dpy = DisplayOfScreen (screen);
460 XWindowAttributes xgwa;
461 int rd_event_base = 0;
462 int rd_error_base = 0;
463 unsigned long hints = 0;
468 Bool remap_p = False;
470 /* Check to see if the server supports the extension, and bug out if not.
472 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
475 /* If this isn't a visual we know how to handle, bug out. We handle:
476 = TrueColor in depths 8, 12, 16, and 32;
477 = PseudoColor and DirectColor in depths 8 and 12.
479 XGetWindowAttributes(dpy, window, &xgwa);
480 class = visual_class (screen, xgwa.visual);
481 if (class == TrueColor)
483 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 16 &&
484 xgwa.depth != 24 && xgwa.depth != 32)
487 else if (class == PseudoColor || class == DirectColor)
489 if (xgwa.depth != 8 && xgwa.depth != 12)
492 /* Allocate a TrueColor-like spread of colors for the image. */
497 /* Try and read the screen.
499 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
500 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
506 XDestroyImage(image);
510 /* XReadDisplay tends to LIE about the depth of the image it read.
511 It is returning an XImage which has `depth' and `bits_per_pixel'
514 That is, on a 24-bit display, where all visuals claim depth 24, and
515 where XGetImage would return an XImage with depth 24, and where
516 XPutImage will get a BadMatch with images that are not depth 24,
517 XReadDisplay is returning images with depth 32! Fuckwits!
519 So if the visual is of depth 24, but the image came back as depth 32,
520 hack it to be 24 lest we get a BadMatch from XPutImage.
522 I wonder what happens on an 8-bit SGI... Probably it still returns
523 an image claiming depth 32? Certainly it can't be 8. So, let's just
526 if (image->depth == 32 /* && xgwa.depth == 24 */ )
529 /* If the visual of the window/pixmap into which we're going to draw is
530 less deep than the screen itself, then we need to convert the grabbed bits
531 to match the depth by clipping off the less significant bit-planes of each
534 if (image->depth > xgwa.depth)
537 /* We use the same image->data in both images -- that's ok, because
538 since we're reading from B and writing to A, and B uses more bytes
539 per pixel than A, the write pointer won't overrun the read pointer.
541 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
542 ZPixmap, 0, image->data,
543 xgwa.width, xgwa.height,
549 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
550 progname, image->depth, xgwa.depth);
552 for (y = 0; y < image->height; y++)
553 for (x = 0; x < image->width; x++)
555 /* #### really these shift values should be determined from the
556 mask values -- but that's a pain in the ass, and anyway,
557 this is an SGI-specific extension so hardcoding assumptions
558 about the SGI server's behavior isn't *too* heinous... */
559 unsigned long pixel = XGetPixel(image, x, y);
560 unsigned int r = (pixel & image->red_mask);
561 unsigned int g = (pixel & image->green_mask) >> 8;
562 unsigned int b = (pixel & image->blue_mask) >> 16;
565 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
566 else if (xgwa.depth == 12)
567 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
568 else if (xgwa.depth == 16)
569 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
573 XPutPixel(image2, x, y, pixel);
576 XDestroyImage(image);
582 allocate_cubic_colormap (screen, window, xgwa.visual);
583 remap_image (screen, window, xgwa.colormap, image);
586 /* Now actually put the bits into the window or pixmap -- note the design
587 bogosity of this extension, where we've been forced to take 24 bit data
588 from the server to the client, and then push it back from the client to
589 the server, *without alteration*. We should have just been able to tell
590 the server, "put a screen image in this drawable", instead of having to
591 go through the intermediate step of converting it to an Image. Geez.
592 (Assuming that the window is of screen depth; we happen to handle less
593 deep windows, but that's beside the point.)
595 gcv.function = GXcopy;
596 gc = XCreateGC (dpy, window, GCFunction, &gcv);
600 gcv.function = GXcopy;
601 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
602 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
603 xgwa.width, xgwa.height);
607 gcv.function = GXcopy;
608 gc = XCreateGC (dpy, window, GCFunction, &gcv);
610 /* Ok, now we'll be needing that window on the screen... */
611 raise_window(dpy, window, dont_wait);
613 /* Plop down the bits... */
614 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
623 XDestroyImage(image);
627 #endif /* HAVE_READ_DISPLAY_EXTENSION */
630 #ifdef HAVE_READ_DISPLAY_EXTENSION
632 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
633 visual behave like a TrueColor visual of the same depth.
636 allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
638 Display *dpy = DisplayOfScreen (screen);
639 XWindowAttributes xgwa;
641 int nr, ng, nb, cells;
647 XGetWindowAttributes (dpy, window, &xgwa);
648 cmap = xgwa.colormap;
649 depth = visual_depth(screen, visual);
653 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
654 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
655 default: abort(); break;
658 memset(colors, 0, sizeof(colors));
659 for (r = 0; r < (1 << nr); r++)
660 for (g = 0; g < (1 << ng); g++)
661 for (b = 0; b < (1 << nb); b++)
663 i = (r | (g << nr) | (b << (nr + ng)));
665 colors[i].flags = DoRed|DoGreen|DoBlue;
668 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
669 (r << 4) | (r << 1));
670 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
671 (g << 4) | (g << 1));
672 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
673 (b << 8) | (b << 6) | (b << 4) |
678 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
679 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
680 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
687 int interleave = cells / 8; /* skip around, rather than allocating in
688 order, so that we get better coverage if
689 we can't allocated all of them. */
690 for (j = 0; j < interleave; j++)
691 for (i = 0; i < cells; i += interleave)
692 if (XAllocColor (dpy, cmap, &colors[i + j]))
696 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
697 progname, allocated, cells);
702 find_closest_pixel (XColor *colors, int ncolors,
703 unsigned long r, unsigned long g, unsigned long b)
705 unsigned long distance = ~0;
710 for (i = 0; i < ncolors; i++)
715 rd = r - colors[i].red;
716 gd = g - colors[i].green;
717 bd = b - colors[i].blue;
718 if (rd < 0) rd = -rd;
719 if (gd < 0) gd = -gd;
720 if (bd < 0) bd = -bd;
721 d = (rd << 1) + (gd << 2) + bd;
737 remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
739 Display *dpy = DisplayOfScreen (screen);
740 unsigned long map[4097];
745 if (image->depth == 8)
747 else if (image->depth == 12)
752 memset(map, -1, sizeof(*map));
753 memset(colors, -1, sizeof(*colors));
755 for (i = 0; i < cells; i++)
757 XQueryColors (dpy, cmap, colors, cells);
760 fprintf(stderr, "%s: building table for %d bit image\n",
761 progname, image->depth);
763 for (i = 0; i < cells; i++)
765 unsigned short r, g, b;
769 /* "RRR GGG BB" In an 8 bit map. Convert that to
770 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
776 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
777 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
778 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
779 (b << 6) | (b << 4) | (b << 2) | b);
783 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
784 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
787 g = (i & 0x0F0) >> 4;
788 b = (i & 0xF00) >> 8;
790 r = (r << 12) | (r << 8) | (r << 4) | r;
791 g = (g << 12) | (g << 8) | (g << 4) | g;
792 b = (b << 12) | (b << 8) | (b << 4) | b;
795 map[i] = find_closest_pixel (colors, cells, r, g, b);
799 fprintf(stderr, "%s: remapping colors in %d bit image\n",
800 progname, image->depth);
802 for (y = 0; y < image->height; y++)
803 for (x = 0; x < image->width; x++)
805 unsigned long pixel = XGetPixel(image, x, y);
806 if (pixel >= cells) abort();
807 XPutPixel(image, x, y, map[pixel]);
812 #endif /* HAVE_READ_DISPLAY_EXTENSION */