1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997
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 /* Uh, this can't be right, can it? But it's necessary. X sucks.
526 If the visual is of depth 24, but the image came back as depth 32,
527 hack it to be 24 lest we get a BadMatch from XPutImage. (I presume
528 I'm expected to look at the server's pixmap formats or some such
529 nonsense... but fuck it.)
531 if (xgwa.depth == 24 && image->depth == 32)
534 /* If the visual of the window/pixmap into which we're going to draw is
535 less deep than the screen itself, then we need to convert the grabbed bits
536 to match the depth by clipping off the less significant bit-planes of each
539 if (image->depth > xgwa.depth)
542 /* We use the same image->data in both images -- that's ok, because
543 since we're reading from B and writing to A, and B uses more bytes
544 per pixel than A, the write pointer won't overrun the read pointer.
546 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
547 ZPixmap, 0, image->data,
548 xgwa.width, xgwa.height,
554 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
555 progname, image->depth, xgwa.depth);
558 for (y = 0; y < image->height; y++)
559 for (x = 0; x < image->width; x++)
561 /* #### really these shift values should be determined from the
562 mask values -- but that's a pain in the ass, and anyway,
563 this is an SGI-specific extension so hardcoding assumptions
564 about the SGI server's behavior isn't *too* heinous... */
565 unsigned long pixel = XGetPixel(image, x, y);
566 unsigned int r = (pixel & image->red_mask);
567 unsigned int g = (pixel & image->green_mask) >> 8;
568 unsigned int b = (pixel & image->blue_mask) >> 16;
571 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
572 else if (xgwa.depth == 12)
573 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
574 else if (xgwa.depth == 16)
575 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
579 XPutPixel(image2, x, y, pixel);
582 XDestroyImage(image);
587 /* Now actually put the bits into the window or pixmap -- note the design
588 bogosity of this extension, where we've been forced to take 24 bit data
589 from the server to the client, and then push it back from the client to
590 the server, *without alteration*. We should have just been able to tell
591 the server, "put a screen image in this drawable", instead of having to
592 go through the intermediate step of converting it to an Image. Geez.
593 (Assuming that the window is of screen depth; we happen to handle less
594 deep windows, but that's beside the point.)
596 gcv.function = GXcopy;
597 gc = XCreateGC (dpy, window, GCFunction, &gcv);
601 gcv.function = GXcopy;
602 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
603 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
604 xgwa.width, xgwa.height);
608 gcv.function = GXcopy;
609 gc = XCreateGC (dpy, window, GCFunction, &gcv);
611 /* Ok, now we'll be needing that window on the screen... */
612 raise_window(dpy, window, dont_wait);
614 /* Plop down the bits... */
615 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
624 XDestroyImage(image);
627 make_cubic_colormap (screen, window, xgwa.visual);
631 #endif /* HAVE_READ_DISPLAY_EXTENSION */
634 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
636 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
637 visual behave like a TrueColor visual of the same depth.
640 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
642 Display *dpy = DisplayOfScreen (screen);
643 Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
644 int nr, ng, nb, cells;
650 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 (i = 0; i < cells; i++)
661 colors[i].flags = DoRed|DoGreen|DoBlue;
662 colors[i].red = colors[i].green = colors[i].blue = 0;
665 for (r = 0; r < (1 << nr); r++)
666 for (g = 0; g < (1 << ng); g++)
667 for (b = 0; b < (1 << nb); b++)
669 i = (r | (g << nr) | (b << (nr + ng)));
673 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
674 (r << 4) | (r << 1));
675 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
676 (g << 4) | (g << 1));
677 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
678 (b << 8) | (b << 6) | (b << 4) |
683 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
684 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
685 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
690 fprintf(stderr, "%s: installing cubic colormap\n", progname);
693 XStoreColors (dpy, cmap, colors, cells);
694 XSetWindowColormap (dpy, window, cmap);
696 /* Gag, install the colormap.
697 This is definitely right in the `if xscreensaver_window_p' case, since
698 it will never get installed otherwise. But, if we don't do it
699 unconditionally, then the new colormap won't get installed until the
700 window (re-)gains focus. It's generally very antisocial to install
701 the colormap of a non-OverrideRedirect window (that task belongs to
702 the WM) and if we were being kosher, we would only install this cmap
703 if the old cmap was already installed (or perhaps, if the window had
704 focus.) But, since this extension only exists on SGIs, and since SGIs
705 can handle four colormaps at once, let's go ahead and install it all
706 the time, so that even if the window pops up and has never had focus,
707 it will still display in the proper colors.
709 XInstallColormap (dpy, cmap);
712 #endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */