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);
296 XWindowAttributes xgwa;
297 XGetWindowAttributes(dpy, window, &xgwa);
298 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
300 #ifdef HAVE_READ_DISPLAY_EXTENSION
301 if (! read_display(screen, window, pixmap, True))
304 Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
308 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
309 fprintf(stderr, "%s: read_display() failed\n", progname);
312 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
314 gcv.function = GXcopy;
315 gcv.subwindow_mode = IncludeInferiors;
316 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
317 XCopyArea (dpy, real_root, pixmap, gc,
318 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
321 XSetWindowBackgroundPixmap (dpy, window, pixmap);
322 XFreePixmap (dpy, pixmap);
327 XUngrabPointer (dpy, CurrentTime);
328 XUngrabKeyboard (dpy, CurrentTime);
335 grab_screen_image (Screen *screen, Window window)
337 #ifdef HAVE_SGI_VIDEO
338 char c, *s = get_string_resource("grabVideoProbability", "Float");
341 (1 != sscanf (s, " %lf %c", &prob, &c)) ||
346 if ((random() % 100) < ((int) (100 * prob)))
348 XWindowAttributes xgwa;
349 Display *dpy = DisplayOfScreen (screen);
350 XGetWindowAttributes (dpy, window, &xgwa);
352 fprintf(stderr, "%s: trying to grab from video...\n", progname);
354 if (grab_video_frame (screen, xgwa.visual, window))
358 int class = visual_class (screen, xgwa.visual);
359 if (class == PseudoColor || class == DirectColor)
360 make_cubic_colormap (screen, window, xgwa.visual);
365 #endif /* HAVE_SGI_VIDEO */
367 grab_screen_image_1 (screen, window);
371 /* When we are grabbing and manipulating a screen image, it's important that
372 we use the same colormap it originally had. So, if the screensaver was
373 started with -install, we need to copy the contents of the default colormap
374 into the screensaver's colormap.
377 copy_default_colormap_contents (Screen *screen,
381 Display *dpy = DisplayOfScreen (screen);
382 Visual *from_visual = DefaultVisualOfScreen (screen);
383 Colormap from_cmap = XDefaultColormapOfScreen (screen);
385 XColor *old_colors, *new_colors;
386 unsigned long *pixels;
387 XVisualInfo vi_in, *vi_out;
389 int from_cells, to_cells, max_cells, got_cells;
392 if (from_cmap == to_cmap)
395 vi_in.screen = XScreenNumberOfScreen (screen);
396 vi_in.visualid = XVisualIDFromVisual (from_visual);
397 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
399 if (! vi_out) abort ();
400 from_cells = vi_out [0].colormap_size;
401 XFree ((char *) vi_out);
403 vi_in.screen = XScreenNumberOfScreen (screen);
404 vi_in.visualid = XVisualIDFromVisual (to_visual);
405 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
407 if (! vi_out) abort ();
408 to_cells = vi_out [0].colormap_size;
409 XFree ((char *) vi_out);
411 max_cells = (from_cells > to_cells ? to_cells : from_cells);
413 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
414 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
415 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
416 for (i = 0; i < max_cells; i++)
417 old_colors[i].pixel = i;
418 XQueryColors (dpy, from_cmap, old_colors, max_cells);
420 got_cells = max_cells;
421 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
424 if (got_cells != max_cells)
425 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
426 got_cells, max_cells);
429 if (got_cells <= 0) /* we're screwed */
431 else if (got_cells == max_cells && /* we're golden */
432 from_cells == to_cells)
433 XStoreColors (dpy, to_cmap, old_colors, got_cells);
434 else /* try to cope... */
436 for (i = 0; i < got_cells; i++)
438 XColor *c = old_colors + i;
440 for (j = 0; j < got_cells; j++)
441 if (pixels[j] == c->pixel)
443 /* only store this color value if this is one of the pixels
444 we were able to allocate. */
445 XStoreColors (dpy, to_cmap, c, 1);
453 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
463 /* The SGI ReadDisplay extension.
464 This extension lets you get back a 24-bit image of the screen, taking into
465 account the colors with which all windows are *currently* displayed, even
466 if those windows have different visuals. Without this extension, presence
467 of windows with different visuals or colormaps will result in technicolor
468 when one tries to grab the screen image.
471 #ifdef HAVE_READ_DISPLAY_EXTENSION
474 read_display (Screen *screen, Window window, Pixmap into_pixmap,
477 Display *dpy = DisplayOfScreen (screen);
478 XWindowAttributes xgwa;
479 int rd_event_base = 0;
480 int rd_error_base = 0;
481 unsigned long hints = 0;
486 Bool install_p = False;
488 /* Check to see if the server supports the extension, and bug out if not.
490 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
493 /* If this isn't a visual we know how to handle, bug out. We handle:
494 = TrueColor in depths 8, 12, 16, and 32;
495 = PseudoColor and DirectColor in depths 8 and 12.
497 XGetWindowAttributes(dpy, window, &xgwa);
498 class = visual_class (screen, xgwa.visual);
499 if (class == TrueColor)
501 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 16 &&
502 xgwa.depth != 24 && xgwa.depth != 32)
505 else if (class == PseudoColor || class == DirectColor)
507 if (xgwa.depth != 8 && xgwa.depth != 12)
510 /* Install a colormap that makes this visual behave like
511 a TrueColor visual of the same depth. */
516 /* Try and read the screen.
518 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
519 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
525 XDestroyImage(image);
529 /* XReadDisplay tends to LIE about the depth of the image it read.
530 It is returning an XImage which has `depth' and `bits_per_pixel'
533 That is, on a 24-bit display, where all visuals claim depth 24, and
534 where XGetImage would return an XImage with depth 24, and where
535 XPutImage will get a BadMatch with images that are not depth 24,
536 XReadDisplay is returning images with depth 32! Fuckwits!
538 So if the visual is of depth 24, but the image came back as depth 32,
539 hack it to be 24 lest we get a BadMatch from XPutImage.
541 I wonder what happens on an 8-bit SGI... Probably it still returns
542 an image claiming depth 32? Certainly it can't be 8. So, let's just
545 if (image->depth == 32 /* && xgwa.depth == 24 */ )
548 /* If the visual of the window/pixmap into which we're going to draw is
549 less deep than the screen itself, then we need to convert the grabbed bits
550 to match the depth by clipping off the less significant bit-planes of each
553 if (image->depth > xgwa.depth)
556 /* We use the same image->data in both images -- that's ok, because
557 since we're reading from B and writing to A, and B uses more bytes
558 per pixel than A, the write pointer won't overrun the read pointer.
560 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
561 ZPixmap, 0, image->data,
562 xgwa.width, xgwa.height,
568 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
569 progname, image->depth, xgwa.depth);
572 for (y = 0; y < image->height; y++)
573 for (x = 0; x < image->width; x++)
575 /* #### really these shift values should be determined from the
576 mask values -- but that's a pain in the ass, and anyway,
577 this is an SGI-specific extension so hardcoding assumptions
578 about the SGI server's behavior isn't *too* heinous... */
579 unsigned long pixel = XGetPixel(image, x, y);
580 unsigned int r = (pixel & image->red_mask);
581 unsigned int g = (pixel & image->green_mask) >> 8;
582 unsigned int b = (pixel & image->blue_mask) >> 16;
585 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
586 else if (xgwa.depth == 12)
587 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
588 else if (xgwa.depth == 16)
589 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
593 XPutPixel(image2, x, y, pixel);
596 XDestroyImage(image);
601 /* Now actually put the bits into the window or pixmap -- note the design
602 bogosity of this extension, where we've been forced to take 24 bit data
603 from the server to the client, and then push it back from the client to
604 the server, *without alteration*. We should have just been able to tell
605 the server, "put a screen image in this drawable", instead of having to
606 go through the intermediate step of converting it to an Image. Geez.
607 (Assuming that the window is of screen depth; we happen to handle less
608 deep windows, but that's beside the point.)
610 gcv.function = GXcopy;
611 gc = XCreateGC (dpy, window, GCFunction, &gcv);
615 gcv.function = GXcopy;
616 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
617 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
618 xgwa.width, xgwa.height);
622 gcv.function = GXcopy;
623 gc = XCreateGC (dpy, window, GCFunction, &gcv);
625 /* Ok, now we'll be needing that window on the screen... */
626 raise_window(dpy, window, dont_wait);
628 /* Plop down the bits... */
629 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
638 XDestroyImage(image);
641 make_cubic_colormap (screen, window, xgwa.visual);
645 #endif /* HAVE_READ_DISPLAY_EXTENSION */
648 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
650 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
651 visual behave like a TrueColor visual of the same depth.
654 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
656 Display *dpy = DisplayOfScreen (screen);
657 Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
658 int nr, ng, nb, cells;
664 depth = visual_depth(screen, visual);
667 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
668 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
669 default: abort(); break;
672 memset(colors, 0, sizeof(colors));
673 for (i = 0; i < cells; i++)
675 colors[i].flags = DoRed|DoGreen|DoBlue;
676 colors[i].red = colors[i].green = colors[i].blue = 0;
679 for (r = 0; r < (1 << nr); r++)
680 for (g = 0; g < (1 << ng); g++)
681 for (b = 0; b < (1 << nb); b++)
683 i = (r | (g << nr) | (b << (nr + ng)));
687 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
688 (r << 4) | (r << 1));
689 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
690 (g << 4) | (g << 1));
691 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
692 (b << 8) | (b << 6) | (b << 4) |
697 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
698 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
699 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
704 fprintf(stderr, "%s: installing cubic colormap\n", progname);
707 XStoreColors (dpy, cmap, colors, cells);
708 XSetWindowColormap (dpy, window, cmap);
710 /* Gag, install the colormap.
711 This is definitely right in the `if xscreensaver_window_p' case, since
712 it will never get installed otherwise. But, if we don't do it
713 unconditionally, then the new colormap won't get installed until the
714 window (re-)gains focus. It's generally very antisocial to install
715 the colormap of a non-OverrideRedirect window (that task belongs to
716 the WM) and if we were being kosher, we would only install this cmap
717 if the old cmap was already installed (or perhaps, if the window had
718 focus.) But, since this extension only exists on SGIs, and since SGIs
719 can handle four colormaps at once, let's go ahead and install it all
720 the time, so that even if the window pops up and has never had focus,
721 it will still display in the proper colors.
723 XInstallColormap (dpy, cmap);
726 #endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */