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
22 #include <X11/Xatom.h>
23 #include <X11/Xutil.h>
27 # include <X11/Xmu/WinUtil.h>
29 # include <Xmu/WinUtil.h>
35 #include "grabscreen.h"
38 #undef RootWindowOfScreen
40 #undef DefaultRootWindow
43 #ifdef HAVE_READ_DISPLAY_EXTENSION
45 # include <X11/extensions/readdisplay.h>
46 static Bool read_display (Screen *, Window, Pixmap, Bool);
47 #endif /* HAVE_READ_DISPLAY_EXTENSION */
50 static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
54 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
56 return (event->xany.type == MapNotify &&
57 event->xvisibility.window == (Window) window);
61 extern char *progname;
66 raise_window(Display *dpy, Window window, Bool dont_wait)
69 fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
70 progname, (unsigned long) window,
71 (dont_wait ? "not waiting" : "waiting"));
76 XWindowAttributes xgwa;
79 memset(&hints, 0, sizeof(hints));
80 XGetWMNormalHints(dpy, window, &hints, &supplied);
81 XGetWindowAttributes (dpy, window, &xgwa);
84 hints.width = xgwa.width;
85 hints.height = xgwa.height;
86 hints.flags |= (PPosition|USPosition|PSize|USSize);
87 XSetWMNormalHints(dpy, window, &hints);
90 XMapRaised(dpy, window);
95 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
102 xscreensaver_window_p (Display *dpy, Window window)
106 unsigned long nitems, bytesafter;
108 if (XGetWindowProperty (dpy, window,
109 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
110 0, 1, False, XA_STRING,
111 &type, &format, &nitems, &bytesafter,
112 (unsigned char **) &version)
121 static XErrorHandler old_ehandler = 0;
123 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
125 if (error->error_code == BadWindow || error->error_code == BadDrawable)
127 else if (!old_ehandler)
130 return (*old_ehandler) (dpy, error);
134 /* Install the colormaps of all visible windows, deepest first.
135 This should leave the colormaps of the topmost windows installed
136 (if only N colormaps can be installed at a time, then only the
137 topmost N windows will be shown in the right colors.)
140 install_screen_colormaps (Screen *screen)
143 Display *dpy = DisplayOfScreen (screen);
144 Window vroot, real_root;
145 Window parent, *kids = 0;
146 unsigned int nkids = 0;
149 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
151 vroot = VirtualRootWindowOfScreen (screen);
152 if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
153 for (i = 0; i < nkids; i++)
155 XWindowAttributes xgwa;
158 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
159 if (! (client = XmuClientWindow (dpy, kids[i])))
163 XGetWindowAttributes (dpy, client, &xgwa);
164 if (xgwa.colormap && xgwa.map_state == IsViewable)
165 XInstallColormap (dpy, xgwa.colormap);
167 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
169 XSetErrorHandler (old_ehandler);
173 XFree ((char *) kids);
178 grab_screen_image (Screen *screen, Window window)
180 Display *dpy = DisplayOfScreen (screen);
181 XWindowAttributes xgwa;
182 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
183 Bool root_p = (window == real_root);
184 Bool saver_p = xscreensaver_window_p (dpy, window);
185 Bool grab_mouse_p = False;
189 /* I think this is redundant, but just to be safe... */
193 /* The only time grabbing the mouse is important is if this program
194 is being run while the saver is locking the screen. */
200 unmap_time = 2500000; /* 2 1/2 seconds */
202 unmap_time = 660000; /* 2/3rd second */
207 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
208 progname, (unsigned long) window,
209 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
211 XWindowAttributes xgwa2;
212 XGetWindowAttributes (dpy, window, &xgwa2);
213 fprintf(stderr, "%s: ", progname);
214 describe_visual(stderr, screen, xgwa2.visual);
219 XSetWindowBackgroundPixmap (dpy, window, None);
223 /* prevent random viewer of the screen saver (locker) from messing
224 with windows. We don't check whether it succeeded, because what
225 are our options, really... */
226 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
227 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
228 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
234 XUnmapWindow (dpy, window);
235 install_screen_colormaps (screen);
237 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
240 XGetWindowAttributes (dpy, window, &xgwa);
244 #ifdef HAVE_READ_DISPLAY_EXTENSION
245 if (! read_display(screen, window, 0, saver_p))
246 #endif /* HAVE_READ_DISPLAY_EXTENSION */
248 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
249 fprintf(stderr, "%s: read_display() failed\n", progname);
251 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
252 raise_window(dpy, window, saver_p);
258 XWindowAttributes xgwa;
259 XGetWindowAttributes(dpy, window, &xgwa);
260 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
262 #ifdef HAVE_READ_DISPLAY_EXTENSION
263 if (! read_display(screen, window, pixmap, True))
266 Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
270 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
271 fprintf(stderr, "%s: read_display() failed\n", progname);
274 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
276 gcv.function = GXcopy;
277 gcv.subwindow_mode = IncludeInferiors;
278 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
279 XCopyArea (dpy, real_root, pixmap, gc,
280 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
283 XSetWindowBackgroundPixmap (dpy, window, pixmap);
284 XFreePixmap (dpy, pixmap);
289 XUngrabPointer (dpy, CurrentTime);
290 XUngrabKeyboard (dpy, CurrentTime);
297 /* When we are grabbing and manipulating a screen image, it's important that
298 we use the same colormap it originally had. So, if the screensaver was
299 started with -install, we need to copy the contents of the default colormap
300 into the screensaver's colormap.
303 copy_default_colormap_contents (Screen *screen,
307 Display *dpy = DisplayOfScreen (screen);
308 Visual *from_visual = DefaultVisualOfScreen (screen);
309 Colormap from_cmap = XDefaultColormapOfScreen (screen);
311 XColor *old_colors, *new_colors;
312 unsigned long *pixels;
313 XVisualInfo vi_in, *vi_out;
315 int from_cells, to_cells, max_cells, got_cells;
318 if (from_cmap == to_cmap)
321 vi_in.screen = XScreenNumberOfScreen (screen);
322 vi_in.visualid = XVisualIDFromVisual (from_visual);
323 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
325 if (! vi_out) abort ();
326 from_cells = vi_out [0].colormap_size;
327 XFree ((char *) vi_out);
329 vi_in.screen = XScreenNumberOfScreen (screen);
330 vi_in.visualid = XVisualIDFromVisual (to_visual);
331 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
333 if (! vi_out) abort ();
334 to_cells = vi_out [0].colormap_size;
335 XFree ((char *) vi_out);
337 max_cells = (from_cells > to_cells ? to_cells : from_cells);
339 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
340 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
341 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
342 for (i = 0; i < max_cells; i++)
343 old_colors[i].pixel = i;
344 XQueryColors (dpy, from_cmap, old_colors, max_cells);
346 got_cells = max_cells;
347 allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
348 XStoreColors (dpy, to_cmap, old_colors, got_cells);
351 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
361 /* The SGI ReadDisplay extension.
362 This extension lets you get back a 24-bit image of the screen, taking into
363 account the colors with which all windows are *currently* displayed, even
364 if those windows have different visuals. Without this extension, presence
365 of windows with different visuals or colormaps will result in technicolor
366 when one tries to grab the screen image.
369 #ifdef HAVE_READ_DISPLAY_EXTENSION
371 static void make_cubic_colormap (Screen *, Window, Visual *);
374 read_display (Screen *screen, Window window, Pixmap into_pixmap,
377 Display *dpy = DisplayOfScreen (screen);
378 XWindowAttributes xgwa;
379 int rd_event_base = 0;
380 int rd_error_base = 0;
381 unsigned long hints = 0;
386 Bool install_p = False;
388 /* Check to see if the server supports the extension, and bug out if not.
390 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
393 /* If this isn't a visual we know how to handle, bug out. We handle:
394 = TrueColor in depths 8, 12, 16, and 32;
395 = PseudoColor and DirectColor in depths 8 and 12.
397 XGetWindowAttributes(dpy, window, &xgwa);
398 class = visual_class (screen, xgwa.visual);
399 if (class == TrueColor)
401 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 16 &&
402 xgwa.depth != 24 && xgwa.depth != 32)
405 else if (class == PseudoColor || class == DirectColor)
407 if (xgwa.depth != 8 && xgwa.depth != 12)
410 /* Install a colormap that makes this visual behave like
411 a TrueColor visual of the same depth. */
416 /* Try and read the screen.
418 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
419 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
425 XDestroyImage(image);
429 /* Uh, this can't be right, can it? But it's necessary. X sucks.
430 If the visual is of depth 24, but the image came back as depth 32,
431 hack it to be 24 lest we get a BadMatch from XPutImage. (I presume
432 I'm expected to look at the server's pixmap formats or some such
433 nonsense... but fuck it.
435 if (xgwa.depth == 24 && image->depth == 32)
438 /* If the visual of the window/pixmap into which we're going to draw is
439 less deep than the screen itself, then we need to convert the grabbed bits
440 to match the depth by clipping off the less significant bit-planes of each
443 if (image->depth > xgwa.depth)
446 /* We use the same image->data in both images -- that's ok, because
447 since we're reading from B and writing to A, and B uses more bytes
448 per pixel than A, the write pointer won't overrun the read pointer.
450 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
451 ZPixmap, 0, image->data,
452 xgwa.width, xgwa.height,
458 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
459 progname, image->depth, xgwa.depth);
462 for (y = 0; y < image->height; y++)
463 for (x = 0; x < image->width; x++)
465 /* #### really these shift values should be determined from the
466 mask values -- but that's a pain in the ass, and anyway,
467 this is an SGI-specific extension so hardcoding assumptions
468 about the SGI server's behavior isn't *too* heinous... */
469 unsigned long pixel = XGetPixel(image, x, y);
470 unsigned int r = (pixel & image->red_mask);
471 unsigned int g = (pixel & image->green_mask) >> 8;
472 unsigned int b = (pixel & image->blue_mask) >> 16;
475 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
476 else if (xgwa.depth == 12)
477 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
478 else if (xgwa.depth == 16)
479 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
483 XPutPixel(image2, x, y, pixel);
486 XDestroyImage(image);
491 /* Now actually put the bits into the window or pixmap -- note the design
492 bogosity of this extension, where we've been forced to take 24 bit data
493 from the server to the client, and then push it back from the client to
494 the server, *without alteration*. We should have just been able to tell
495 the server, "put a screen image in this drawable", instead of having to
496 go through the intermediate step of converting it to an Image. Geez.
497 (Assuming that the window is of screen depth; we happen to handle less
498 deep windows, but that's beside the point.)
500 gcv.function = GXcopy;
501 gc = XCreateGC (dpy, window, GCFunction, &gcv);
505 gcv.function = GXcopy;
506 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
507 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
508 xgwa.width, xgwa.height);
512 gcv.function = GXcopy;
513 gc = XCreateGC (dpy, window, GCFunction, &gcv);
515 /* Ok, now we'll be needing that window on the screen... */
516 raise_window(dpy, window, dont_wait);
518 /* Plop down the bits... */
519 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
528 XDestroyImage(image);
531 make_cubic_colormap (screen, window, xgwa.visual);
537 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
539 Display *dpy = DisplayOfScreen (screen);
540 Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
541 int nr, ng, nb, cells;
547 depth = visual_depth(screen, visual);
550 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
551 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
552 default: abort(); break;
555 memset(colors, 0, sizeof(colors));
556 for (i = 0; i < cells; i++)
558 colors[i].flags = DoRed|DoGreen|DoBlue;
559 colors[i].red = colors[i].green = colors[i].blue = 0;
562 for (r = 0; r < (1 << nr); r++)
563 for (g = 0; g < (1 << ng); g++)
564 for (b = 0; b < (1 << nb); b++)
566 i = (r | (g << nr) | (b << (nr + ng)));
570 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
571 (r << 4) | (r << 1));
572 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
573 (g << 4) | (g << 1));
574 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
575 (b << 8) | (b << 6) | (b << 4) |
580 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
581 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
582 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
587 fprintf(stderr, "%s: installing cubic colormap\n", progname);
590 XStoreColors (dpy, cmap, colors, cells);
591 XSetWindowColormap (dpy, window, cmap);
593 /* Gag, install the colormap.
594 This is definitely right in the `if xscreensaver_window_p' case, since
595 it will never get installed otherwise. But, if we don't do it
596 unconditionally, then the new colormap won't get installed until the
597 window (re-)gains focus. It's generally very antisocial to install
598 the colormap of a non-OverrideRedirect window (that task belongs to
599 the WM) and if we were being kosher, we would only install this cmap
600 if the old cmap was already installed (or perhaps, if the window had
601 focus.) But, since this extension only exists on SGIs, and since SGIs
602 can handle four colormaps at once, let's go ahead and install it all
603 the time, so that even if the window pops up and has never had focus,
604 it will still display in the proper colors.
606 XInstallColormap (dpy, cmap);
610 #endif /* HAVE_READ_DISPLAY_EXTENSION */