1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998
2 * Jamie Zawinski <jwz@netscape.com>
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"
39 #include "resources.h"
42 #undef RootWindowOfScreen
44 #undef DefaultRootWindow
47 #ifdef HAVE_READ_DISPLAY_EXTENSION
48 # include <X11/extensions/readdisplay.h>
49 static Bool read_display (Screen *, Window, Pixmap, Bool);
50 #endif /* HAVE_READ_DISPLAY_EXTENSION */
53 static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
55 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
56 static void make_cubic_colormap (Screen *, Window, Visual *);
61 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
63 return (event->xany.type == MapNotify &&
64 event->xvisibility.window == (Window) window);
68 extern char *progname;
73 raise_window(Display *dpy, Window window, Bool dont_wait)
76 fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
77 progname, (unsigned long) window,
78 (dont_wait ? "not waiting" : "waiting"));
83 XWindowAttributes xgwa;
86 memset(&hints, 0, sizeof(hints));
87 XGetWMNormalHints(dpy, window, &hints, &supplied);
88 XGetWindowAttributes (dpy, window, &xgwa);
91 hints.width = xgwa.width;
92 hints.height = xgwa.height;
93 hints.flags |= (PPosition|USPosition|PSize|USSize);
94 XSetWMNormalHints(dpy, window, &hints);
97 XMapRaised(dpy, window);
102 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
109 xscreensaver_window_p (Display *dpy, Window window)
113 unsigned long nitems, bytesafter;
115 if (XGetWindowProperty (dpy, window,
116 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
117 0, 1, False, XA_STRING,
118 &type, &format, &nitems, &bytesafter,
119 (unsigned char **) &version)
128 static XErrorHandler old_ehandler = 0;
130 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
132 if (error->error_code == BadWindow || error->error_code == BadDrawable)
134 else if (!old_ehandler)
137 return (*old_ehandler) (dpy, error);
141 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
142 on a window whose depth is not the maximal depth of the screen? Or
143 something. Anyway, things don't work unless we: use SubwindowMode for
144 the real root window (or a legitimate virtual root window); but do not
145 use SubwindowMode for the xscreensaver window. I make no attempt to
149 use_subwindow_mode_p(Screen *screen, Window window)
151 if (window != VirtualRootWindowOfScreen(screen))
153 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
160 /* Install the colormaps of all visible windows, deepest first.
161 This should leave the colormaps of the topmost windows installed
162 (if only N colormaps can be installed at a time, then only the
163 topmost N windows will be shown in the right colors.)
166 install_screen_colormaps (Screen *screen)
169 Display *dpy = DisplayOfScreen (screen);
170 Window vroot, real_root;
171 Window parent, *kids = 0;
172 unsigned int nkids = 0;
175 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
177 vroot = VirtualRootWindowOfScreen (screen);
178 if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
179 for (i = 0; i < nkids; i++)
181 XWindowAttributes xgwa;
184 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
185 if (! (client = XmuClientWindow (dpy, kids[i])))
189 XGetWindowAttributes (dpy, client, &xgwa);
190 if (xgwa.colormap && xgwa.map_state == IsViewable)
191 XInstallColormap (dpy, xgwa.colormap);
193 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
195 XSetErrorHandler (old_ehandler);
199 XFree ((char *) kids);
204 grab_screen_image_1 (Screen *screen, Window window)
206 Display *dpy = DisplayOfScreen (screen);
207 XWindowAttributes xgwa;
208 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
209 Bool root_p = (window == real_root);
210 Bool saver_p = xscreensaver_window_p (dpy, window);
211 Bool grab_mouse_p = False;
215 /* I think this is redundant, but just to be safe... */
219 /* The only time grabbing the mouse is important is if this program
220 is being run while the saver is locking the screen. */
228 unmap = get_float_resource("grabRootDelay", "Seconds");
229 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
233 unmap = get_float_resource("grabWindowDelay", "Seconds");
234 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
236 unmap_time = unmap * 100000;
241 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
242 progname, (unsigned long) window,
243 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
245 XWindowAttributes xgwa2;
246 XGetWindowAttributes (dpy, window, &xgwa2);
247 fprintf(stderr, "%s: ", progname);
248 describe_visual(stderr, screen, xgwa2.visual);
253 XSetWindowBackgroundPixmap (dpy, window, None);
257 /* prevent random viewer of the screen saver (locker) from messing
258 with windows. We don't check whether it succeeded, because what
259 are our options, really... */
260 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
261 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
262 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
268 XUnmapWindow (dpy, window);
269 install_screen_colormaps (screen);
271 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
274 XGetWindowAttributes (dpy, window, &xgwa);
278 #ifdef HAVE_READ_DISPLAY_EXTENSION
279 if (! read_display(screen, window, 0, saver_p))
280 #endif /* HAVE_READ_DISPLAY_EXTENSION */
282 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
283 fprintf(stderr, "%s: read_display() failed\n", progname);
285 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
286 raise_window(dpy, window, saver_p);
292 XWindowAttributes xgwa;
293 XGetWindowAttributes(dpy, window, &xgwa);
294 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
296 #ifdef HAVE_READ_DISPLAY_EXTENSION
297 if (! read_display(screen, window, pixmap, True))
300 Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
304 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
305 fprintf(stderr, "%s: read_display() failed\n", progname);
308 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
310 gcv.function = GXcopy;
311 gcv.subwindow_mode = IncludeInferiors;
312 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
313 XCopyArea (dpy, real_root, pixmap, gc,
314 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
317 XSetWindowBackgroundPixmap (dpy, window, pixmap);
318 XFreePixmap (dpy, pixmap);
323 XUngrabPointer (dpy, CurrentTime);
324 XUngrabKeyboard (dpy, CurrentTime);
331 grab_screen_image (Screen *screen, Window window)
333 #ifdef HAVE_SGI_VIDEO
334 char c, *s = get_string_resource("grabVideoProbability", "Float");
337 (1 != sscanf (s, " %lf %c", &prob, &c)) ||
342 if ((random() % 100) < ((int) (100 * prob)))
344 XWindowAttributes xgwa;
345 Display *dpy = DisplayOfScreen (screen);
346 XGetWindowAttributes (dpy, window, &xgwa);
348 fprintf(stderr, "%s: trying to grab from video...\n", progname);
350 if (grab_video_frame (screen, xgwa.visual, window))
354 int class = visual_class (screen, xgwa.visual);
355 if (class == PseudoColor || class == DirectColor)
356 make_cubic_colormap (screen, window, xgwa.visual);
361 #endif /* HAVE_SGI_VIDEO */
363 grab_screen_image_1 (screen, window);
367 /* When we are grabbing and manipulating a screen image, it's important that
368 we use the same colormap it originally had. So, if the screensaver was
369 started with -install, we need to copy the contents of the default colormap
370 into the screensaver's colormap.
373 copy_default_colormap_contents (Screen *screen,
377 Display *dpy = DisplayOfScreen (screen);
378 Visual *from_visual = DefaultVisualOfScreen (screen);
379 Colormap from_cmap = XDefaultColormapOfScreen (screen);
381 XColor *old_colors, *new_colors;
382 unsigned long *pixels;
383 XVisualInfo vi_in, *vi_out;
385 int from_cells, to_cells, max_cells, got_cells;
388 if (from_cmap == to_cmap)
391 vi_in.screen = XScreenNumberOfScreen (screen);
392 vi_in.visualid = XVisualIDFromVisual (from_visual);
393 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
395 if (! vi_out) abort ();
396 from_cells = vi_out [0].colormap_size;
397 XFree ((char *) vi_out);
399 vi_in.screen = XScreenNumberOfScreen (screen);
400 vi_in.visualid = XVisualIDFromVisual (to_visual);
401 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
403 if (! vi_out) abort ();
404 to_cells = vi_out [0].colormap_size;
405 XFree ((char *) vi_out);
407 max_cells = (from_cells > to_cells ? to_cells : from_cells);
409 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
410 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
411 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
412 for (i = 0; i < max_cells; i++)
413 old_colors[i].pixel = i;
414 XQueryColors (dpy, from_cmap, old_colors, max_cells);
416 got_cells = max_cells;
417 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
420 if (got_cells != max_cells)
421 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
422 got_cells, max_cells);
425 if (got_cells <= 0) /* we're screwed */
427 else if (got_cells == max_cells && /* we're golden */
428 from_cells == to_cells)
429 XStoreColors (dpy, to_cmap, old_colors, got_cells);
430 else /* try to cope... */
432 for (i = 0; i < got_cells; i++)
434 XColor *c = old_colors + i;
436 for (j = 0; j < got_cells; j++)
437 if (pixels[j] == c->pixel)
439 /* only store this color value if this is one of the pixels
440 we were able to allocate. */
441 XStoreColors (dpy, to_cmap, c, 1);
449 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
459 /* The SGI ReadDisplay extension.
460 This extension lets you get back a 24-bit image of the screen, taking into
461 account the colors with which all windows are *currently* displayed, even
462 if those windows have different visuals. Without this extension, presence
463 of windows with different visuals or colormaps will result in technicolor
464 when one tries to grab the screen image.
467 #ifdef HAVE_READ_DISPLAY_EXTENSION
470 read_display (Screen *screen, Window window, Pixmap into_pixmap,
473 Display *dpy = DisplayOfScreen (screen);
474 XWindowAttributes xgwa;
475 int rd_event_base = 0;
476 int rd_error_base = 0;
477 unsigned long hints = 0;
482 Bool install_p = False;
484 /* Check to see if the server supports the extension, and bug out if not.
486 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
489 /* If this isn't a visual we know how to handle, bug out. We handle:
490 = TrueColor in depths 8, 12, 16, and 32;
491 = PseudoColor and DirectColor in depths 8 and 12.
493 XGetWindowAttributes(dpy, window, &xgwa);
494 class = visual_class (screen, xgwa.visual);
495 if (class == TrueColor)
497 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 16 &&
498 xgwa.depth != 24 && xgwa.depth != 32)
501 else if (class == PseudoColor || class == DirectColor)
503 if (xgwa.depth != 8 && xgwa.depth != 12)
506 /* Install a colormap that makes this visual behave like
507 a TrueColor visual of the same depth. */
512 /* Try and read the screen.
514 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
515 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
521 XDestroyImage(image);
525 /* XReadDisplay tends to LIE about the depth of the image it read.
526 It is returning an XImage which has `depth' and `bits_per_pixel'
529 That is, on a 24-bit display, where all visuals claim depth 24, and
530 where XGetImage would return an XImage with depth 24, and where
531 XPutImage will get a BadMatch with images that are not depth 24,
532 XReadDisplay is returning images with depth 32! Fuckwits!
534 So if the visual is of depth 24, but the image came back as depth 32,
535 hack it to be 24 lest we get a BadMatch from XPutImage.
537 I wonder what happens on an 8-bit SGI... Probably it still returns
538 an image claiming depth 32? Certainly it can't be 8. So, let's just
541 if (image->depth == 32 /* && xgwa.depth == 24 */ )
544 /* If the visual of the window/pixmap into which we're going to draw is
545 less deep than the screen itself, then we need to convert the grabbed bits
546 to match the depth by clipping off the less significant bit-planes of each
549 if (image->depth > xgwa.depth)
552 /* We use the same image->data in both images -- that's ok, because
553 since we're reading from B and writing to A, and B uses more bytes
554 per pixel than A, the write pointer won't overrun the read pointer.
556 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
557 ZPixmap, 0, image->data,
558 xgwa.width, xgwa.height,
564 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
565 progname, image->depth, xgwa.depth);
568 for (y = 0; y < image->height; y++)
569 for (x = 0; x < image->width; x++)
571 /* #### really these shift values should be determined from the
572 mask values -- but that's a pain in the ass, and anyway,
573 this is an SGI-specific extension so hardcoding assumptions
574 about the SGI server's behavior isn't *too* heinous... */
575 unsigned long pixel = XGetPixel(image, x, y);
576 unsigned int r = (pixel & image->red_mask);
577 unsigned int g = (pixel & image->green_mask) >> 8;
578 unsigned int b = (pixel & image->blue_mask) >> 16;
581 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
582 else if (xgwa.depth == 12)
583 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
584 else if (xgwa.depth == 16)
585 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
589 XPutPixel(image2, x, y, pixel);
592 XDestroyImage(image);
597 /* Now actually put the bits into the window or pixmap -- note the design
598 bogosity of this extension, where we've been forced to take 24 bit data
599 from the server to the client, and then push it back from the client to
600 the server, *without alteration*. We should have just been able to tell
601 the server, "put a screen image in this drawable", instead of having to
602 go through the intermediate step of converting it to an Image. Geez.
603 (Assuming that the window is of screen depth; we happen to handle less
604 deep windows, but that's beside the point.)
606 gcv.function = GXcopy;
607 gc = XCreateGC (dpy, window, GCFunction, &gcv);
611 gcv.function = GXcopy;
612 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
613 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
614 xgwa.width, xgwa.height);
618 gcv.function = GXcopy;
619 gc = XCreateGC (dpy, window, GCFunction, &gcv);
621 /* Ok, now we'll be needing that window on the screen... */
622 raise_window(dpy, window, dont_wait);
624 /* Plop down the bits... */
625 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
634 XDestroyImage(image);
637 make_cubic_colormap (screen, window, xgwa.visual);
641 #endif /* HAVE_READ_DISPLAY_EXTENSION */
644 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
646 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
647 visual behave like a TrueColor visual of the same depth.
650 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
652 Display *dpy = DisplayOfScreen (screen);
653 Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
654 int nr, ng, nb, cells;
660 depth = visual_depth(screen, visual);
663 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
664 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
665 default: abort(); break;
668 memset(colors, 0, sizeof(colors));
669 for (i = 0; i < cells; i++)
671 colors[i].flags = DoRed|DoGreen|DoBlue;
672 colors[i].red = colors[i].green = colors[i].blue = 0;
675 for (r = 0; r < (1 << nr); r++)
676 for (g = 0; g < (1 << ng); g++)
677 for (b = 0; b < (1 << nb); b++)
679 i = (r | (g << nr) | (b << (nr + ng)));
683 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
684 (r << 4) | (r << 1));
685 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
686 (g << 4) | (g << 1));
687 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
688 (b << 8) | (b << 6) | (b << 4) |
693 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
694 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
695 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
700 fprintf(stderr, "%s: installing cubic colormap\n", progname);
703 XStoreColors (dpy, cmap, colors, cells);
704 XSetWindowColormap (dpy, window, cmap);
706 /* Gag, install the colormap.
707 This is definitely right in the `if xscreensaver_window_p' case, since
708 it will never get installed otherwise. But, if we don't do it
709 unconditionally, then the new colormap won't get installed until the
710 window (re-)gains focus. It's generally very antisocial to install
711 the colormap of a non-OverrideRedirect window (that task belongs to
712 the WM) and if we were being kosher, we would only install this cmap
713 if the old cmap was already installed (or perhaps, if the window had
714 focus.) But, since this extension only exists on SGIs, and since SGIs
715 can handle four colormaps at once, let's go ahead and install it all
716 the time, so that even if the window pops up and has never had focus,
717 it will still display in the proper colors.
719 XInstallColormap (dpy, cmap);
722 #endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */