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, ####);
249 fprintf (stderr, "\n");
254 XSetWindowBackgroundPixmap (dpy, window, None);
258 /* prevent random viewer of the screen saver (locker) from messing
259 with windows. We don't check whether it succeeded, because what
260 are our options, really... */
261 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
262 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
263 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
269 XUnmapWindow (dpy, window);
270 install_screen_colormaps (screen);
272 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
275 XGetWindowAttributes (dpy, window, &xgwa);
279 #ifdef HAVE_READ_DISPLAY_EXTENSION
280 if (! read_display(screen, window, 0, saver_p))
281 #endif /* HAVE_READ_DISPLAY_EXTENSION */
283 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
284 fprintf(stderr, "%s: read_display() failed\n", progname);
286 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
287 raise_window(dpy, window, saver_p);
293 XWindowAttributes xgwa;
294 XGetWindowAttributes(dpy, window, &xgwa);
295 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
297 #ifdef HAVE_READ_DISPLAY_EXTENSION
298 if (! read_display(screen, window, pixmap, True))
301 Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
305 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
306 fprintf(stderr, "%s: read_display() failed\n", progname);
309 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
311 gcv.function = GXcopy;
312 gcv.subwindow_mode = IncludeInferiors;
313 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
314 XCopyArea (dpy, real_root, pixmap, gc,
315 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
318 XSetWindowBackgroundPixmap (dpy, window, pixmap);
319 XFreePixmap (dpy, pixmap);
324 XUngrabPointer (dpy, CurrentTime);
325 XUngrabKeyboard (dpy, CurrentTime);
332 grab_screen_image (Screen *screen, Window window)
334 #ifdef HAVE_SGI_VIDEO
335 char c, *s = get_string_resource("grabVideoProbability", "Float");
338 (1 != sscanf (s, " %lf %c", &prob, &c)) ||
343 if ((random() % 100) < ((int) (100 * prob)))
345 XWindowAttributes xgwa;
346 Display *dpy = DisplayOfScreen (screen);
347 XGetWindowAttributes (dpy, window, &xgwa);
349 fprintf(stderr, "%s: trying to grab from video...\n", progname);
351 if (grab_video_frame (screen, xgwa.visual, window))
355 int class = visual_class (screen, xgwa.visual);
356 if (class == PseudoColor || class == DirectColor)
357 make_cubic_colormap (screen, window, xgwa.visual);
362 #endif /* HAVE_SGI_VIDEO */
364 grab_screen_image_1 (screen, window);
368 /* When we are grabbing and manipulating a screen image, it's important that
369 we use the same colormap it originally had. So, if the screensaver was
370 started with -install, we need to copy the contents of the default colormap
371 into the screensaver's colormap.
374 copy_default_colormap_contents (Screen *screen,
378 Display *dpy = DisplayOfScreen (screen);
379 Visual *from_visual = DefaultVisualOfScreen (screen);
380 Colormap from_cmap = XDefaultColormapOfScreen (screen);
382 XColor *old_colors, *new_colors;
383 unsigned long *pixels;
384 XVisualInfo vi_in, *vi_out;
386 int from_cells, to_cells, max_cells, got_cells;
389 if (from_cmap == to_cmap)
392 vi_in.screen = XScreenNumberOfScreen (screen);
393 vi_in.visualid = XVisualIDFromVisual (from_visual);
394 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
396 if (! vi_out) abort ();
397 from_cells = vi_out [0].colormap_size;
398 XFree ((char *) vi_out);
400 vi_in.screen = XScreenNumberOfScreen (screen);
401 vi_in.visualid = XVisualIDFromVisual (to_visual);
402 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
404 if (! vi_out) abort ();
405 to_cells = vi_out [0].colormap_size;
406 XFree ((char *) vi_out);
408 max_cells = (from_cells > to_cells ? to_cells : from_cells);
410 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
411 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
412 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
413 for (i = 0; i < max_cells; i++)
414 old_colors[i].pixel = i;
415 XQueryColors (dpy, from_cmap, old_colors, max_cells);
417 got_cells = max_cells;
418 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
421 if (got_cells != max_cells)
422 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
423 got_cells, max_cells);
426 if (got_cells <= 0) /* we're screwed */
428 else if (got_cells == max_cells && /* we're golden */
429 from_cells == to_cells)
430 XStoreColors (dpy, to_cmap, old_colors, got_cells);
431 else /* try to cope... */
433 for (i = 0; i < got_cells; i++)
435 XColor *c = old_colors + i;
437 for (j = 0; j < got_cells; j++)
438 if (pixels[j] == c->pixel)
440 /* only store this color value if this is one of the pixels
441 we were able to allocate. */
442 XStoreColors (dpy, to_cmap, c, 1);
450 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
460 /* The SGI ReadDisplay extension.
461 This extension lets you get back a 24-bit image of the screen, taking into
462 account the colors with which all windows are *currently* displayed, even
463 if those windows have different visuals. Without this extension, presence
464 of windows with different visuals or colormaps will result in technicolor
465 when one tries to grab the screen image.
468 #ifdef HAVE_READ_DISPLAY_EXTENSION
471 read_display (Screen *screen, Window window, Pixmap into_pixmap,
474 Display *dpy = DisplayOfScreen (screen);
475 XWindowAttributes xgwa;
476 int rd_event_base = 0;
477 int rd_error_base = 0;
478 unsigned long hints = 0;
483 Bool install_p = False;
485 /* Check to see if the server supports the extension, and bug out if not.
487 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
490 /* If this isn't a visual we know how to handle, bug out. We handle:
491 = TrueColor in depths 8, 12, 16, and 32;
492 = PseudoColor and DirectColor in depths 8 and 12.
494 XGetWindowAttributes(dpy, window, &xgwa);
495 class = visual_class (screen, xgwa.visual);
496 if (class == TrueColor)
498 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 16 &&
499 xgwa.depth != 24 && xgwa.depth != 32)
502 else if (class == PseudoColor || class == DirectColor)
504 if (xgwa.depth != 8 && xgwa.depth != 12)
507 /* Install a colormap that makes this visual behave like
508 a TrueColor visual of the same depth. */
513 /* Try and read the screen.
515 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
516 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
522 XDestroyImage(image);
526 /* XReadDisplay tends to LIE about the depth of the image it read.
527 It is returning an XImage which has `depth' and `bits_per_pixel'
530 That is, on a 24-bit display, where all visuals claim depth 24, and
531 where XGetImage would return an XImage with depth 24, and where
532 XPutImage will get a BadMatch with images that are not depth 24,
533 XReadDisplay is returning images with depth 32! Fuckwits!
535 So if the visual is of depth 24, but the image came back as depth 32,
536 hack it to be 24 lest we get a BadMatch from XPutImage.
538 I wonder what happens on an 8-bit SGI... Probably it still returns
539 an image claiming depth 32? Certainly it can't be 8. So, let's just
542 if (image->depth == 32 /* && xgwa.depth == 24 */ )
545 /* If the visual of the window/pixmap into which we're going to draw is
546 less deep than the screen itself, then we need to convert the grabbed bits
547 to match the depth by clipping off the less significant bit-planes of each
550 if (image->depth > xgwa.depth)
553 /* We use the same image->data in both images -- that's ok, because
554 since we're reading from B and writing to A, and B uses more bytes
555 per pixel than A, the write pointer won't overrun the read pointer.
557 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
558 ZPixmap, 0, image->data,
559 xgwa.width, xgwa.height,
565 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
566 progname, image->depth, xgwa.depth);
569 for (y = 0; y < image->height; y++)
570 for (x = 0; x < image->width; x++)
572 /* #### really these shift values should be determined from the
573 mask values -- but that's a pain in the ass, and anyway,
574 this is an SGI-specific extension so hardcoding assumptions
575 about the SGI server's behavior isn't *too* heinous... */
576 unsigned long pixel = XGetPixel(image, x, y);
577 unsigned int r = (pixel & image->red_mask);
578 unsigned int g = (pixel & image->green_mask) >> 8;
579 unsigned int b = (pixel & image->blue_mask) >> 16;
582 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
583 else if (xgwa.depth == 12)
584 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
585 else if (xgwa.depth == 16)
586 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
590 XPutPixel(image2, x, y, pixel);
593 XDestroyImage(image);
598 /* Now actually put the bits into the window or pixmap -- note the design
599 bogosity of this extension, where we've been forced to take 24 bit data
600 from the server to the client, and then push it back from the client to
601 the server, *without alteration*. We should have just been able to tell
602 the server, "put a screen image in this drawable", instead of having to
603 go through the intermediate step of converting it to an Image. Geez.
604 (Assuming that the window is of screen depth; we happen to handle less
605 deep windows, but that's beside the point.)
607 gcv.function = GXcopy;
608 gc = XCreateGC (dpy, window, GCFunction, &gcv);
612 gcv.function = GXcopy;
613 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
614 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
615 xgwa.width, xgwa.height);
619 gcv.function = GXcopy;
620 gc = XCreateGC (dpy, window, GCFunction, &gcv);
622 /* Ok, now we'll be needing that window on the screen... */
623 raise_window(dpy, window, dont_wait);
625 /* Plop down the bits... */
626 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
635 XDestroyImage(image);
638 make_cubic_colormap (screen, window, xgwa.visual);
642 #endif /* HAVE_READ_DISPLAY_EXTENSION */
645 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
647 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
648 visual behave like a TrueColor visual of the same depth.
651 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
653 Display *dpy = DisplayOfScreen (screen);
654 Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
655 int nr, ng, nb, cells;
661 depth = visual_depth(screen, visual);
664 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
665 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
666 default: abort(); break;
669 memset(colors, 0, sizeof(colors));
670 for (i = 0; i < cells; i++)
672 colors[i].flags = DoRed|DoGreen|DoBlue;
673 colors[i].red = colors[i].green = colors[i].blue = 0;
676 for (r = 0; r < (1 << nr); r++)
677 for (g = 0; g < (1 << ng); g++)
678 for (b = 0; b < (1 << nb); b++)
680 i = (r | (g << nr) | (b << (nr + ng)));
684 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
685 (r << 4) | (r << 1));
686 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
687 (g << 4) | (g << 1));
688 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
689 (b << 8) | (b << 6) | (b << 4) |
694 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
695 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
696 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
701 fprintf(stderr, "%s: installing cubic colormap\n", progname);
704 XStoreColors (dpy, cmap, colors, cells);
705 XSetWindowColormap (dpy, window, cmap);
707 /* Gag, install the colormap.
708 This is definitely right in the `if xscreensaver_window_p' case, since
709 it will never get installed otherwise. But, if we don't do it
710 unconditionally, then the new colormap won't get installed until the
711 window (re-)gains focus. It's generally very antisocial to install
712 the colormap of a non-OverrideRedirect window (that task belongs to
713 the WM) and if we were being kosher, we would only install this cmap
714 if the old cmap was already installed (or perhaps, if the window had
715 focus.) But, since this extension only exists on SGIs, and since SGIs
716 can handle four colormaps at once, let's go ahead and install it all
717 the time, so that even if the window pops up and has never had focus,
718 it will still display in the proper colors.
720 XInstallColormap (dpy, cmap);
723 #endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */