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"
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)
140 return (*old_ehandler) (dpy, error);
144 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
145 on a window whose depth is not the maximal depth of the screen? Or
146 something. Anyway, things don't work unless we: use SubwindowMode for
147 the real root window (or a legitimate virtual root window); but do not
148 use SubwindowMode for the xscreensaver window. I make no attempt to
152 use_subwindow_mode_p(Screen *screen, Window window)
154 if (window != VirtualRootWindowOfScreen(screen))
156 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
163 /* Install the colormaps of all visible windows, deepest first.
164 This should leave the colormaps of the topmost windows installed
165 (if only N colormaps can be installed at a time, then only the
166 topmost N windows will be shown in the right colors.)
169 install_screen_colormaps (Screen *screen)
172 Display *dpy = DisplayOfScreen (screen);
173 Window vroot, real_root;
174 Window parent, *kids = 0;
175 unsigned int nkids = 0;
178 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
180 vroot = VirtualRootWindowOfScreen (screen);
181 if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
182 for (i = 0; i < nkids; i++)
184 XWindowAttributes xgwa;
187 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
188 if (! (client = XmuClientWindow (dpy, kids[i])))
192 XGetWindowAttributes (dpy, client, &xgwa);
193 if (xgwa.colormap && xgwa.map_state == IsViewable)
194 XInstallColormap (dpy, xgwa.colormap);
196 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
198 XSetErrorHandler (old_ehandler);
202 XFree ((char *) kids);
207 grab_screen_image_1 (Screen *screen, Window window)
209 Display *dpy = DisplayOfScreen (screen);
210 XWindowAttributes xgwa;
211 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
212 Bool root_p = (window == real_root);
213 Bool saver_p = xscreensaver_window_p (dpy, window);
214 Bool grab_mouse_p = False;
218 /* I think this is redundant, but just to be safe... */
222 /* The only time grabbing the mouse is important is if this program
223 is being run while the saver is locking the screen. */
231 unmap = get_float_resource("grabRootDelay", "Seconds");
232 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
236 unmap = get_float_resource("grabWindowDelay", "Seconds");
237 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
239 unmap_time = unmap * 100000;
244 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
245 progname, (unsigned long) window,
246 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
248 XWindowAttributes xgwa2;
249 XGetWindowAttributes (dpy, window, &xgwa2);
250 fprintf(stderr, "%s: ", progname);
251 describe_visual(stderr, screen, xgwa2.visual, ####);
252 fprintf (stderr, "\n");
257 XSetWindowBackgroundPixmap (dpy, window, None);
261 /* prevent random viewer of the screen saver (locker) from messing
262 with windows. We don't check whether it succeeded, because what
263 are our options, really... */
264 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
265 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
266 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
272 XUnmapWindow (dpy, window);
273 install_screen_colormaps (screen);
275 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
278 XGetWindowAttributes (dpy, window, &xgwa);
282 #ifdef HAVE_READ_DISPLAY_EXTENSION
283 if (! read_display(screen, window, 0, saver_p))
284 #endif /* HAVE_READ_DISPLAY_EXTENSION */
286 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
287 fprintf(stderr, "%s: read_display() failed\n", progname);
289 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
290 raise_window(dpy, window, saver_p);
292 /* Generally it's bad news to call XInstallColormap() explicitly,
293 but this file does a lot of sleazy stuff already... This is to
294 make sure that the window's colormap is installed, even in the
295 case where the window is OverrideRedirect. */
296 if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
303 XWindowAttributes xgwa;
304 XGetWindowAttributes(dpy, window, &xgwa);
305 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
307 #ifdef HAVE_READ_DISPLAY_EXTENSION
308 if (! read_display(screen, window, pixmap, True))
311 Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
315 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
316 fprintf(stderr, "%s: read_display() failed\n", progname);
319 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
321 gcv.function = GXcopy;
322 gcv.subwindow_mode = IncludeInferiors;
323 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
324 XCopyArea (dpy, real_root, pixmap, gc,
325 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
328 XSetWindowBackgroundPixmap (dpy, window, pixmap);
329 XFreePixmap (dpy, pixmap);
334 XUngrabPointer (dpy, CurrentTime);
335 XUngrabKeyboard (dpy, CurrentTime);
342 grab_screen_image (Screen *screen, Window window)
344 #ifdef HAVE_SGI_VIDEO
345 char c, *s = get_string_resource("grabVideoProbability", "Float");
348 (1 != sscanf (s, " %lf %c", &prob, &c)) ||
353 if ((random() % 100) < ((int) (100 * prob)))
355 XWindowAttributes xgwa;
356 Display *dpy = DisplayOfScreen (screen);
357 XGetWindowAttributes (dpy, window, &xgwa);
359 fprintf(stderr, "%s: trying to grab from video...\n", progname);
361 if (grab_video_frame (screen, xgwa.visual, window))
365 int class = visual_class (screen, xgwa.visual);
366 if (class == PseudoColor || class == DirectColor)
367 make_cubic_colormap (screen, window, xgwa.visual);
372 #endif /* HAVE_SGI_VIDEO */
374 grab_screen_image_1 (screen, window);
378 /* When we are grabbing and manipulating a screen image, it's important that
379 we use the same colormap it originally had. So, if the screensaver was
380 started with -install, we need to copy the contents of the default colormap
381 into the screensaver's colormap.
384 copy_default_colormap_contents (Screen *screen,
388 Display *dpy = DisplayOfScreen (screen);
389 Visual *from_visual = DefaultVisualOfScreen (screen);
390 Colormap from_cmap = XDefaultColormapOfScreen (screen);
392 XColor *old_colors, *new_colors;
393 unsigned long *pixels;
394 XVisualInfo vi_in, *vi_out;
396 int from_cells, to_cells, max_cells, got_cells;
399 if (from_cmap == to_cmap)
402 vi_in.screen = XScreenNumberOfScreen (screen);
403 vi_in.visualid = XVisualIDFromVisual (from_visual);
404 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
406 if (! vi_out) abort ();
407 from_cells = vi_out [0].colormap_size;
408 XFree ((char *) vi_out);
410 vi_in.screen = XScreenNumberOfScreen (screen);
411 vi_in.visualid = XVisualIDFromVisual (to_visual);
412 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
414 if (! vi_out) abort ();
415 to_cells = vi_out [0].colormap_size;
416 XFree ((char *) vi_out);
418 max_cells = (from_cells > to_cells ? to_cells : from_cells);
420 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
421 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
422 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
423 for (i = 0; i < max_cells; i++)
424 old_colors[i].pixel = i;
425 XQueryColors (dpy, from_cmap, old_colors, max_cells);
427 got_cells = max_cells;
428 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
431 if (got_cells != max_cells)
432 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
433 got_cells, max_cells);
436 if (got_cells <= 0) /* we're screwed */
438 else if (got_cells == max_cells && /* we're golden */
439 from_cells == to_cells)
440 XStoreColors (dpy, to_cmap, old_colors, got_cells);
441 else /* try to cope... */
443 for (i = 0; i < got_cells; i++)
445 XColor *c = old_colors + i;
447 for (j = 0; j < got_cells; j++)
448 if (pixels[j] == c->pixel)
450 /* only store this color value if this is one of the pixels
451 we were able to allocate. */
452 XStoreColors (dpy, to_cmap, c, 1);
460 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
470 /* The SGI ReadDisplay extension.
471 This extension lets you get back a 24-bit image of the screen, taking into
472 account the colors with which all windows are *currently* displayed, even
473 if those windows have different visuals. Without this extension, presence
474 of windows with different visuals or colormaps will result in technicolor
475 when one tries to grab the screen image.
478 #ifdef HAVE_READ_DISPLAY_EXTENSION
481 read_display (Screen *screen, Window window, Pixmap into_pixmap,
484 Display *dpy = DisplayOfScreen (screen);
485 XWindowAttributes xgwa;
486 int rd_event_base = 0;
487 int rd_error_base = 0;
488 unsigned long hints = 0;
493 Bool install_p = False;
495 /* Check to see if the server supports the extension, and bug out if not.
497 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
500 /* If this isn't a visual we know how to handle, bug out. We handle:
501 = TrueColor in depths 8, 12, 16, and 32;
502 = PseudoColor and DirectColor in depths 8 and 12.
504 XGetWindowAttributes(dpy, window, &xgwa);
505 class = visual_class (screen, xgwa.visual);
506 if (class == TrueColor)
508 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 16 &&
509 xgwa.depth != 24 && xgwa.depth != 32)
512 else if (class == PseudoColor || class == DirectColor)
514 if (xgwa.depth != 8 && xgwa.depth != 12)
517 /* Install a colormap that makes this visual behave like
518 a TrueColor visual of the same depth. */
523 /* Try and read the screen.
525 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
526 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
532 XDestroyImage(image);
536 /* XReadDisplay tends to LIE about the depth of the image it read.
537 It is returning an XImage which has `depth' and `bits_per_pixel'
540 That is, on a 24-bit display, where all visuals claim depth 24, and
541 where XGetImage would return an XImage with depth 24, and where
542 XPutImage will get a BadMatch with images that are not depth 24,
543 XReadDisplay is returning images with depth 32! Fuckwits!
545 So if the visual is of depth 24, but the image came back as depth 32,
546 hack it to be 24 lest we get a BadMatch from XPutImage.
548 I wonder what happens on an 8-bit SGI... Probably it still returns
549 an image claiming depth 32? Certainly it can't be 8. So, let's just
552 if (image->depth == 32 /* && xgwa.depth == 24 */ )
555 /* If the visual of the window/pixmap into which we're going to draw is
556 less deep than the screen itself, then we need to convert the grabbed bits
557 to match the depth by clipping off the less significant bit-planes of each
560 if (image->depth > xgwa.depth)
563 /* We use the same image->data in both images -- that's ok, because
564 since we're reading from B and writing to A, and B uses more bytes
565 per pixel than A, the write pointer won't overrun the read pointer.
567 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
568 ZPixmap, 0, image->data,
569 xgwa.width, xgwa.height,
575 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
576 progname, image->depth, xgwa.depth);
579 for (y = 0; y < image->height; y++)
580 for (x = 0; x < image->width; x++)
582 /* #### really these shift values should be determined from the
583 mask values -- but that's a pain in the ass, and anyway,
584 this is an SGI-specific extension so hardcoding assumptions
585 about the SGI server's behavior isn't *too* heinous... */
586 unsigned long pixel = XGetPixel(image, x, y);
587 unsigned int r = (pixel & image->red_mask);
588 unsigned int g = (pixel & image->green_mask) >> 8;
589 unsigned int b = (pixel & image->blue_mask) >> 16;
592 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
593 else if (xgwa.depth == 12)
594 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
595 else if (xgwa.depth == 16)
596 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
600 XPutPixel(image2, x, y, pixel);
603 XDestroyImage(image);
608 /* Now actually put the bits into the window or pixmap -- note the design
609 bogosity of this extension, where we've been forced to take 24 bit data
610 from the server to the client, and then push it back from the client to
611 the server, *without alteration*. We should have just been able to tell
612 the server, "put a screen image in this drawable", instead of having to
613 go through the intermediate step of converting it to an Image. Geez.
614 (Assuming that the window is of screen depth; we happen to handle less
615 deep windows, but that's beside the point.)
617 gcv.function = GXcopy;
618 gc = XCreateGC (dpy, window, GCFunction, &gcv);
622 gcv.function = GXcopy;
623 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
624 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
625 xgwa.width, xgwa.height);
629 gcv.function = GXcopy;
630 gc = XCreateGC (dpy, window, GCFunction, &gcv);
632 /* Ok, now we'll be needing that window on the screen... */
633 raise_window(dpy, window, dont_wait);
635 /* Plop down the bits... */
636 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
645 XDestroyImage(image);
648 make_cubic_colormap (screen, window, xgwa.visual);
652 #endif /* HAVE_READ_DISPLAY_EXTENSION */
655 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
657 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
658 visual behave like a TrueColor visual of the same depth.
661 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
663 Display *dpy = DisplayOfScreen (screen);
664 Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
665 int nr, ng, nb, cells;
671 depth = visual_depth(screen, visual);
674 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
675 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
676 default: abort(); break;
679 memset(colors, 0, sizeof(colors));
680 for (i = 0; i < cells; i++)
682 colors[i].flags = DoRed|DoGreen|DoBlue;
683 colors[i].red = colors[i].green = colors[i].blue = 0;
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)));
694 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
695 (r << 4) | (r << 1));
696 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
697 (g << 4) | (g << 1));
698 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
699 (b << 8) | (b << 6) | (b << 4) |
704 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
705 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
706 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
711 fprintf(stderr, "%s: installing cubic colormap\n", progname);
714 XStoreColors (dpy, cmap, colors, cells);
715 XSetWindowColormap (dpy, window, cmap);
717 /* Gag, install the colormap.
718 This is definitely right in the `if xscreensaver_window_p' case, since
719 it will never get installed otherwise. But, if we don't do it
720 unconditionally, then the new colormap won't get installed until the
721 window (re-)gains focus. It's generally very antisocial to install
722 the colormap of a non-OverrideRedirect window (that task belongs to
723 the WM) and if we were being kosher, we would only install this cmap
724 if the old cmap was already installed (or perhaps, if the window had
725 focus.) But, since this extension only exists on SGIs, and since SGIs
726 can handle four colormaps at once, let's go ahead and install it all
727 the time, so that even if the window pops up and has never had focus,
728 it will still display in the proper colors.
730 XInstallColormap (dpy, cmap);
733 #endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */