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
24 #include <X11/Xatom.h>
25 #include <X11/Xutil.h>
29 # include <X11/Xmu/WinUtil.h>
31 # include <Xmu/WinUtil.h>
35 #include "grabscreen.h"
38 #undef RootWindowOfScreen
40 #undef DefaultRootWindow
43 #ifdef HAVE_READ_DISPLAY_EXTENSION
44 # include <X11/extensions/readdisplay.h>
45 static Bool read_display (Screen *, Window, Pixmap, Bool);
46 #endif /* HAVE_READ_DISPLAY_EXTENSION */
49 static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
51 #ifdef HAVE_READ_DISPLAY_EXTENSION
52 static void allocate_cubic_colormap (Screen *, Window, Visual *);
53 void remap_image (Screen *, Window, Colormap, XImage *);
58 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
60 return (event->xany.type == MapNotify &&
61 event->xvisibility.window == (Window) window);
66 grabscreen_verbose(void)
72 raise_window(Display *dpy, Window window, Bool dont_wait)
77 XWindowAttributes xgwa;
80 memset(&hints, 0, sizeof(hints));
81 XGetWMNormalHints(dpy, window, &hints, &supplied);
82 XGetWindowAttributes (dpy, window, &xgwa);
85 hints.width = xgwa.width;
86 hints.height = xgwa.height;
87 hints.flags |= (PPosition|USPosition|PSize|USSize);
88 XSetWMNormalHints(dpy, window, &hints);
90 XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask));
93 XMapRaised(dpy, window);
98 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
105 xscreensaver_window_p (Display *dpy, Window window)
109 unsigned long nitems, bytesafter;
111 if (XGetWindowProperty (dpy, window,
112 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
113 0, 1, False, XA_STRING,
114 &type, &format, &nitems, &bytesafter,
115 (unsigned char **) &version)
124 /* Whether the given window is:
125 - the real root window;
126 - the virtual root window;
127 - a direct child of the root window;
128 - a direct child of the window manager's decorations.
131 top_level_window_p (Screen *screen, Window window)
133 Display *dpy = DisplayOfScreen (screen);
134 Window root, parent, *kids;
135 Window vroot = VirtualRootWindowOfScreen(screen);
141 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
147 /* If our direct parent is the root (or *a* root), then yes. */
148 if (parent == root || parent == vroot)
154 unsigned long nitems, bytesafter;
157 /* If our direct parent has the WM_STATE property, then it is a
158 window manager decoration -- yes.
160 if (XGetWindowProperty (dpy, window,
161 XInternAtom (dpy, "WM_STATE", True),
162 0, 0, False, AnyPropertyType,
163 &type, &format, &nitems, &bytesafter,
164 (unsigned char **) &data)
170 /* Else, no. We're deep in a tree somewhere.
177 static XErrorHandler old_ehandler = 0;
179 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
181 if (error->error_code == BadWindow || error->error_code == BadDrawable)
183 else if (!old_ehandler)
189 return (*old_ehandler) (dpy, error);
193 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
194 on a window whose depth is not the maximal depth of the screen? Or
195 something. Anyway, things don't work unless we: use SubwindowMode for
196 the real root window (or a legitimate virtual root window); but do not
197 use SubwindowMode for the xscreensaver window. I make no attempt to
201 use_subwindow_mode_p(Screen *screen, Window window)
203 if (window != VirtualRootWindowOfScreen(screen))
205 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
212 /* Install the colormaps of all visible windows, deepest first.
213 This should leave the colormaps of the topmost windows installed
214 (if only N colormaps can be installed at a time, then only the
215 topmost N windows will be shown in the right colors.)
218 install_screen_colormaps (Screen *screen)
221 Display *dpy = DisplayOfScreen (screen);
222 Window vroot, real_root;
223 Window parent, *kids = 0;
224 unsigned int nkids = 0;
227 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
229 vroot = VirtualRootWindowOfScreen (screen);
230 if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
231 for (i = 0; i < nkids; i++)
233 XWindowAttributes xgwa;
236 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
237 if (! (client = XmuClientWindow (dpy, kids[i])))
241 XGetWindowAttributes (dpy, client, &xgwa);
242 if (xgwa.colormap && xgwa.map_state == IsViewable)
243 XInstallColormap (dpy, xgwa.colormap);
245 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
247 XSetErrorHandler (old_ehandler);
251 XFree ((char *) kids);
256 grab_screen_image (Screen *screen, Window window)
258 Display *dpy = DisplayOfScreen (screen);
259 XWindowAttributes xgwa;
263 Bool grab_mouse_p = False;
266 real_root = XRootWindowOfScreen (screen); /* not vroot */
267 root_p = (window == real_root);
268 saver_p = xscreensaver_window_p (dpy, window);
270 XGetWindowAttributes (dpy, window, &xgwa);
271 screen = xgwa.screen;
274 /* I think this is redundant, but just to be safe... */
278 /* The only time grabbing the mouse is important is if this program
279 is being run while the saver is locking the screen. */
293 unmap_time = unmap * 100000;
298 if (!root_p && !top_level_window_p (screen, window))
305 XSetWindowBackgroundPixmap (dpy, window, None);
309 /* prevent random viewer of the screen saver (locker) from messing
310 with windows. We don't check whether it succeeded, because what
311 are our options, really... */
312 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
313 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
314 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
320 XUnmapWindow (dpy, window);
321 install_screen_colormaps (screen);
323 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
328 #ifdef HAVE_READ_DISPLAY_EXTENSION
329 if (! read_display(screen, window, 0, saver_p))
330 #endif /* HAVE_READ_DISPLAY_EXTENSION */
333 // copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
334 raise_window(dpy, window, saver_p);
336 /* Generally it's bad news to call XInstallColormap() explicitly,
337 but this file does a lot of sleazy stuff already... This is to
338 make sure that the window's colormap is installed, even in the
339 case where the window is OverrideRedirect. */
340 if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
347 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
349 #ifdef HAVE_READ_DISPLAY_EXTENSION
350 if (! read_display(screen, window, pixmap, True))
353 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
357 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
359 gcv.function = GXcopy;
360 gcv.subwindow_mode = IncludeInferiors;
361 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
362 XCopyArea (dpy, real_root, pixmap, gc,
363 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
366 XSetWindowBackgroundPixmap (dpy, window, pixmap);
367 XFreePixmap (dpy, pixmap);
372 XUngrabPointer (dpy, CurrentTime);
373 XUngrabKeyboard (dpy, CurrentTime);
380 /* When we are grabbing and manipulating a screen image, it's important that
381 we use the same colormap it originally had. So, if the screensaver was
382 started with -install, we need to copy the contents of the default colormap
383 into the screensaver's colormap.
386 copy_default_colormap_contents (Screen *screen,
390 Display *dpy = DisplayOfScreen (screen);
391 Visual *from_visual = DefaultVisualOfScreen (screen);
392 Colormap from_cmap = XDefaultColormapOfScreen (screen);
394 XColor *old_colors, *new_colors;
395 unsigned long *pixels;
396 XVisualInfo vi_in, *vi_out;
398 int from_cells, to_cells, max_cells, got_cells;
401 if (from_cmap == to_cmap)
404 vi_in.screen = XScreenNumberOfScreen (screen);
405 vi_in.visualid = XVisualIDFromVisual (from_visual);
406 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
408 if (! vi_out) abort ();
409 from_cells = vi_out [0].colormap_size;
410 XFree ((char *) vi_out);
412 vi_in.screen = XScreenNumberOfScreen (screen);
413 vi_in.visualid = XVisualIDFromVisual (to_visual);
414 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
416 if (! vi_out) abort ();
417 to_cells = vi_out [0].colormap_size;
418 XFree ((char *) vi_out);
420 max_cells = (from_cells > to_cells ? to_cells : from_cells);
422 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
423 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
424 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
425 for (i = 0; i < max_cells; i++)
426 old_colors[i].pixel = i;
427 XQueryColors (dpy, from_cmap, old_colors, max_cells);
429 got_cells = max_cells;
430 //allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
433 if (got_cells <= 0) /* we're screwed */
435 else if (got_cells == max_cells && /* we're golden */
436 from_cells == to_cells)
437 XStoreColors (dpy, to_cmap, old_colors, got_cells);
438 else /* try to cope... */
440 for (i = 0; i < got_cells; i++)
442 XColor *c = old_colors + i;
444 for (j = 0; j < got_cells; j++)
445 if (pixels[j] == c->pixel)
447 /* only store this color value if this is one of the pixels
448 we were able to allocate. */
449 XStoreColors (dpy, to_cmap, c, 1);
464 /* The SGI ReadDisplay extension.
465 This extension lets you get back a 24-bit image of the screen, taking into
466 account the colors with which all windows are *currently* displayed, even
467 if those windows have different visuals. Without this extension, presence
468 of windows with different visuals or colormaps will result in technicolor
469 when one tries to grab the screen image.
472 #ifdef HAVE_READ_DISPLAY_EXTENSION
475 read_display (Screen *screen, Window window, Pixmap into_pixmap,
478 Display *dpy = DisplayOfScreen (screen);
479 XWindowAttributes xgwa;
480 int rd_event_base = 0;
481 int rd_error_base = 0;
482 unsigned long hints = 0;
487 Bool remap_p = False;
489 /* Check to see if the server supports the extension, and bug out if not.
491 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
496 /* If this isn't a visual we know how to handle, bug out. We handle:
497 = TrueColor in depths 8, 12, 15, 16, and 32;
498 = PseudoColor and DirectColor in depths 8 and 12.
500 XGetWindowAttributes(dpy, window, &xgwa);
501 class = visual_class (screen, xgwa.visual);
502 if (class == TrueColor)
504 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
505 xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
510 else if (class == PseudoColor || class == DirectColor)
512 if (xgwa.depth != 8 && xgwa.depth != 12)
517 /* Allocate a TrueColor-like spread of colors for the image. */
522 /* Try and read the screen.
524 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
525 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
533 XDestroyImage(image);
537 /* XReadDisplay tends to LIE about the depth of the image it read.
538 It is returning an XImage which has `depth' and `bits_per_pixel'
541 That is, on a 24-bit display, where all visuals claim depth 24, and
542 where XGetImage would return an XImage with depth 24, and where
543 XPutImage will get a BadMatch with images that are not depth 24,
544 XReadDisplay is returning images with depth 32! Fuckwits!
546 So if the visual is of depth 24, but the image came back as depth 32,
547 hack it to be 24 lest we get a BadMatch from XPutImage.
549 I wonder what happens on an 8-bit SGI... Probably it still returns
550 an image claiming depth 32? Certainly it can't be 8. So, let's just
553 if (image->depth == 32 /* && xgwa.depth == 24 */ )
556 /* If the visual of the window/pixmap into which we're going to draw is
557 less deep than the screen itself, then we need to convert the grabbed bits
558 to match the depth by clipping off the less significant bit-planes of each
561 if (image->depth > xgwa.depth)
564 /* We use the same image->data in both images -- that's ok, because
565 since we're reading from B and writing to A, and B uses more bytes
566 per pixel than A, the write pointer won't overrun the read pointer.
568 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
569 ZPixmap, 0, image->data,
570 xgwa.width, xgwa.height,
578 for (y = 0; y < image->height; y++)
579 for (x = 0; x < image->width; x++)
581 /* #### really these shift values should be determined from the
582 mask values -- but that's a pain in the ass, and anyway,
583 this is an SGI-specific extension so hardcoding assumptions
584 about the SGI server's behavior isn't *too* heinous... */
585 unsigned long pixel = XGetPixel(image, x, y);
586 unsigned int r = (pixel & image->red_mask);
587 unsigned int g = (pixel & image->green_mask) >> 8;
588 unsigned int b = (pixel & image->blue_mask) >> 16;
591 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
592 else if (xgwa.depth == 12)
593 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
594 else if (xgwa.depth == 16 || xgwa.depth == 15)
595 /* Gah! I don't understand why these are in the other order. */
596 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
600 XPutPixel(image2, x, y, pixel);
603 XDestroyImage(image);
609 allocate_cubic_colormap (screen, window, xgwa.visual);
610 remap_image (screen, window, xgwa.colormap, image);
613 /* Now actually put the bits into the window or pixmap -- note the design
614 bogosity of this extension, where we've been forced to take 24 bit data
615 from the server to the client, and then push it back from the client to
616 the server, *without alteration*. We should have just been able to tell
617 the server, "put a screen image in this drawable", instead of having to
618 go through the intermediate step of converting it to an Image. Geez.
619 (Assuming that the window is of screen depth; we happen to handle less
620 deep windows, but that's beside the point.)
622 gcv.function = GXcopy;
623 gc = XCreateGC (dpy, window, GCFunction, &gcv);
627 gcv.function = GXcopy;
628 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
629 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
630 xgwa.width, xgwa.height);
634 gcv.function = GXcopy;
635 gc = XCreateGC (dpy, window, GCFunction, &gcv);
637 /* Ok, now we'll be needing that window on the screen... */
638 raise_window(dpy, window, dont_wait);
640 /* Plop down the bits... */
641 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
650 XDestroyImage(image);
654 #endif /* HAVE_READ_DISPLAY_EXTENSION */
657 #ifdef HAVE_READ_DISPLAY_EXTENSION
659 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
660 visual behave like a TrueColor visual of the same depth.
663 allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
665 Display *dpy = DisplayOfScreen (screen);
666 XWindowAttributes xgwa;
668 int nr, ng, nb, cells;
674 XGetWindowAttributes (dpy, window, &xgwa);
675 cmap = xgwa.colormap;
676 depth = visual_depth(screen, visual);
680 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
681 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
682 default: abort(); break;
685 memset(colors, 0, sizeof(colors));
686 for (r = 0; r < (1 << nr); r++)
687 for (g = 0; g < (1 << ng); g++)
688 for (b = 0; b < (1 << nb); b++)
690 i = (r | (g << nr) | (b << (nr + ng)));
692 colors[i].flags = DoRed|DoGreen|DoBlue;
695 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
696 (r << 4) | (r << 1));
697 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
698 (g << 4) | (g << 1));
699 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
700 (b << 8) | (b << 6) | (b << 4) |
705 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
706 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
707 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
714 int interleave = cells / 8; /* skip around, rather than allocating in
715 order, so that we get better coverage if
716 we can't allocated all of them. */
717 for (j = 0; j < interleave; j++)
718 for (i = 0; i < cells; i += interleave)
719 if (XAllocColor (dpy, cmap, &colors[i + j]))
726 find_closest_pixel (XColor *colors, int ncolors,
727 unsigned long r, unsigned long g, unsigned long b)
729 unsigned long distance = ~0;
734 for (i = 0; i < ncolors; i++)
739 rd = r - colors[i].red;
740 gd = g - colors[i].green;
741 bd = b - colors[i].blue;
742 if (rd < 0) rd = -rd;
743 if (gd < 0) gd = -gd;
744 if (bd < 0) bd = -bd;
745 d = (rd << 1) + (gd << 2) + bd;
761 remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
763 Display *dpy = DisplayOfScreen (screen);
764 unsigned long map[4097];
769 if (image->depth == 8)
771 else if (image->depth == 12)
776 memset(map, -1, sizeof(*map));
777 memset(colors, -1, sizeof(*colors));
779 for (i = 0; i < cells; i++)
781 XQueryColors (dpy, cmap, colors, cells);
784 for (i = 0; i < cells; i++)
786 unsigned short r, g, b;
790 /* "RRR GGG BB" In an 8 bit map. Convert that to
791 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
797 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
798 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
799 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
800 (b << 6) | (b << 4) | (b << 2) | b);
804 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
805 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
808 g = (i & 0x0F0) >> 4;
809 b = (i & 0xF00) >> 8;
811 r = (r << 12) | (r << 8) | (r << 4) | r;
812 g = (g << 12) | (g << 8) | (g << 4) | g;
813 b = (b << 12) | (b << 8) | (b << 4) | b;
816 map[i] = find_closest_pixel (colors, cells, r, g, b);
820 for (y = 0; y < image->height; y++)
821 for (x = 0; x < image->width; x++)
823 unsigned long pixel = XGetPixel(image, x, y);
824 if (pixel >= cells) abort();
825 XPutPixel(image, x, y, map[pixel]);
830 #endif /* HAVE_READ_DISPLAY_EXTENSION */