http://www.mirrorservice.org/sites/master.us.finkmirrors.net/distfiles/md5/fa43fdd68d...
[xscreensaver] / utils / grabscreen.c
1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998, 2003
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
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 
10  * implied warranty.
11  */
12
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 
17    breaks down...
18  */
19
20 #include "utils.h"
21 #include "yarandom.h"
22
23 #include <X11/Xatom.h>
24 #include <X11/Xutil.h>
25
26 #ifdef HAVE_XMU
27 # ifndef VMS
28 #  include <X11/Xmu/WinUtil.h>
29 # else  /* VMS */
30 #  include <Xmu/WinUtil.h>
31 # endif /* VMS */
32 #endif
33
34 #include "usleep.h"
35 #include "colors.h"
36 #include "grabscreen.h"
37 #include "visual.h"
38 #include "resources.h"
39
40 #include "vroot.h"
41 #undef RootWindowOfScreen
42 #undef RootWindow
43 #undef DefaultRootWindow
44
45
46 #ifdef HAVE_READ_DISPLAY_EXTENSION
47 # include <X11/extensions/readdisplay.h>
48   static Bool read_display (Screen *, Window, Pixmap, Bool);
49 #endif /* HAVE_READ_DISPLAY_EXTENSION */
50
51
52 static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
53
54 #ifdef HAVE_READ_DISPLAY_EXTENSION
55 static void allocate_cubic_colormap (Screen *, Window, Visual *);
56 void remap_image (Screen *, Window, Colormap, XImage *);
57 #endif
58
59
60 static Bool
61 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
62 {
63   return (event->xany.type == MapNotify &&
64           event->xvisibility.window == (Window) window);
65 }
66
67 extern char *progname;
68 Bool grab_verbose_p = False;
69
70 void
71 grabscreen_verbose(void)
72 {
73   grab_verbose_p = True;
74 }
75
76
77 static void
78 raise_window(Display *dpy, Window window, Bool dont_wait)
79 {
80   if (grab_verbose_p)
81     fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
82             progname, (unsigned long) window,
83             (dont_wait ? "not waiting" : "waiting"));
84
85   if (! dont_wait)
86     {
87       XWindowAttributes xgwa;
88       XSizeHints hints;
89       long supplied = 0;
90       memset(&hints, 0, sizeof(hints));
91       XGetWMNormalHints(dpy, window, &hints, &supplied);
92       XGetWindowAttributes (dpy, window, &xgwa);
93       hints.x = xgwa.x;
94       hints.y = xgwa.y;
95       hints.width = xgwa.width;
96       hints.height = xgwa.height;
97       hints.flags |= (PPosition|USPosition|PSize|USSize);
98       XSetWMNormalHints(dpy, window, &hints);
99
100       XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask));
101     }
102
103   XMapRaised(dpy, window);
104
105   if (! dont_wait)
106     {
107       XEvent event;
108       XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
109       XSync (dpy, True);
110     }
111 }
112
113
114 static Bool
115 xscreensaver_window_p (Display *dpy, Window window)
116 {
117   Atom type;
118   int format;
119   unsigned long nitems, bytesafter;
120   char *version;
121   if (XGetWindowProperty (dpy, window,
122                           XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
123                           0, 1, False, XA_STRING,
124                           &type, &format, &nitems, &bytesafter,
125                           (unsigned char **) &version)
126       == Success
127       && type != None)
128     return True;
129   return False;
130 }
131
132
133
134 /* Whether the given window is:
135    - the real root window;
136    - the virtual root window;
137    - a direct child of the root window;
138    - a direct child of the window manager's decorations.
139  */
140 Bool
141 top_level_window_p (Screen *screen, Window window)
142 {
143   Display *dpy = DisplayOfScreen (screen);
144   Window root, parent, *kids;
145   Window vroot = VirtualRootWindowOfScreen(screen);
146   unsigned int nkids;
147
148   if (window == vroot)
149     return True;
150
151   if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
152     return False;
153
154   if (window == root)
155     return True;
156
157   /* If our direct parent is the root (or *a* root), then yes. */
158   if (parent == root || parent == vroot)
159     return True;
160   else
161     {
162       Atom type = None;
163       int format;
164       unsigned long nitems, bytesafter;
165       unsigned char *data;
166
167       /* If our direct parent has the WM_STATE property, then it is a
168          window manager decoration -- yes.
169       */
170       if (XGetWindowProperty (dpy, window,
171                               XInternAtom (dpy, "WM_STATE", True),
172                               0, 0, False, AnyPropertyType,
173                               &type, &format, &nitems, &bytesafter,
174                               (unsigned char **) &data)
175           == Success
176           && type != None)
177         return True;
178     }
179
180   /* Else, no.  We're deep in a tree somewhere.
181    */
182   return False;
183 }
184
185
186
187 static Bool error_handler_hit_p = False;
188 static XErrorHandler old_ehandler = 0;
189 static int
190 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
191 {
192   error_handler_hit_p = True;
193   if (error->error_code == BadWindow || error->error_code == BadDrawable)
194     return 0;
195   else if (!old_ehandler)
196     {
197       abort();
198       return 0;
199     }
200   else
201     return (*old_ehandler) (dpy, error);
202 }
203
204
205 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
206    on a window whose depth is not the maximal depth of the screen?  Or
207    something.  Anyway, things don't work unless we: use SubwindowMode for
208    the real root window (or a legitimate virtual root window); but do not
209    use SubwindowMode for the xscreensaver window.  I make no attempt to
210    explain.
211  */
212 Bool
213 use_subwindow_mode_p(Screen *screen, Window window)
214 {
215   if (window != VirtualRootWindowOfScreen(screen))
216     return False;
217   else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
218     return False;
219   else
220     return True;
221 }
222
223
224 /* Install the colormaps of all visible windows, deepest first.
225    This should leave the colormaps of the topmost windows installed
226    (if only N colormaps can be installed at a time, then only the
227    topmost N windows will be shown in the right colors.)
228  */
229 static void
230 install_screen_colormaps (Screen *screen)
231 {
232   int i;
233   Display *dpy = DisplayOfScreen (screen);
234   Window vroot, real_root;
235   Window parent, *kids = 0;
236   unsigned int nkids = 0;
237
238   XSync (dpy, False);
239   old_ehandler = XSetErrorHandler (BadWindow_ehandler);
240   error_handler_hit_p = False;
241
242   vroot = VirtualRootWindowOfScreen (screen);
243   if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
244     for (i = 0; i < nkids; i++)
245       {
246         XWindowAttributes xgwa;
247         Window client;
248 #ifdef HAVE_XMU
249         /* #### need to put XmuClientWindow() in xmu.c, sigh... */
250         if (! (client = XmuClientWindow (dpy, kids[i])))
251 #endif
252           client = kids[i];
253         xgwa.colormap = 0;
254         XGetWindowAttributes (dpy, client, &xgwa);
255         if (xgwa.colormap && xgwa.map_state == IsViewable)
256           XInstallColormap (dpy, xgwa.colormap);
257       }
258   XInstallColormap (dpy, DefaultColormapOfScreen (screen));
259   XSync (dpy, False);
260   XSetErrorHandler (old_ehandler);
261   XSync (dpy, False);
262
263   if (kids)
264     XFree ((char *) kids);
265 }
266
267
268 void
269 grab_screen_image (Screen *screen, Window window)
270 {
271   Display *dpy = DisplayOfScreen (screen);
272   XWindowAttributes xgwa;
273   Window real_root;
274   Bool root_p;
275   Bool saver_p;
276   Bool grab_mouse_p = False;
277   int unmap_time = 0;
278
279   real_root = XRootWindowOfScreen (screen);  /* not vroot */
280   root_p = (window == real_root);
281   saver_p = xscreensaver_window_p (dpy, window);
282
283   XGetWindowAttributes (dpy, window, &xgwa);
284   screen = xgwa.screen;
285
286   if (saver_p)
287     /* I think this is redundant, but just to be safe... */
288     root_p = False;
289
290   if (saver_p)
291     /* The only time grabbing the mouse is important is if this program
292        is being run while the saver is locking the screen. */
293     grab_mouse_p = True;
294
295   if (!root_p)
296     {
297       double unmap = 0;
298       if (saver_p)
299         {
300           unmap = get_float_resource("grabRootDelay", "Seconds");
301           if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
302         }
303       else
304         {
305           unmap = get_float_resource("grabWindowDelay", "Seconds");
306           if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
307         }
308       unmap_time = unmap * 100000;
309     }
310
311   if (grab_verbose_p)
312     {
313       fprintf(stderr,
314               "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
315               progname, (unsigned long) window,
316               root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
317
318       fprintf(stderr, "%s: ", progname);
319       describe_visual(stderr, screen, xgwa.visual, False);
320       fprintf (stderr, "\n");
321     }
322
323
324   if (!root_p && !top_level_window_p (screen, window))
325     {
326       if (grab_verbose_p)
327         fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
328                  progname, (unsigned long) window);
329       return;
330     }
331
332
333   if (!root_p)
334     XSetWindowBackgroundPixmap (dpy, window, None);
335
336   if (grab_mouse_p)
337     {
338       /* prevent random viewer of the screen saver (locker) from messing
339          with windows.   We don't check whether it succeeded, because what
340          are our options, really... */
341       XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
342                     GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
343       XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
344                      CurrentTime);
345     }
346
347   if (unmap_time > 0)
348     {
349       XUnmapWindow (dpy, window);
350       install_screen_colormaps (screen);
351       XSync (dpy, True);
352       usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
353     }
354
355   if (!root_p)
356     {
357 #ifdef HAVE_READ_DISPLAY_EXTENSION
358       if (! read_display(screen, window, 0, saver_p))
359 #endif /* HAVE_READ_DISPLAY_EXTENSION */
360         {
361 #ifdef HAVE_READ_DISPLAY_EXTENSION
362           if (grab_verbose_p)
363             fprintf(stderr, "%s: read_display() failed\n", progname);
364 #endif /* HAVE_READ_DISPLAY_EXTENSION */
365
366           copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
367           raise_window(dpy, window, saver_p);
368
369           /* Generally it's bad news to call XInstallColormap() explicitly,
370              but this file does a lot of sleazy stuff already...  This is to
371              make sure that the window's colormap is installed, even in the
372              case where the window is OverrideRedirect. */
373           if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
374           XSync (dpy, False);
375         }
376     }
377   else  /* root_p */
378     {
379       Pixmap pixmap;
380       pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
381
382 #ifdef HAVE_READ_DISPLAY_EXTENSION
383       if (! read_display(screen, window, pixmap, True))
384 #endif
385         {
386           Window real_root = XRootWindowOfScreen (screen); /* not vroot */
387           XGCValues gcv;
388           GC gc;
389
390 #ifdef HAVE_READ_DISPLAY_EXTENSION
391           if  (grab_verbose_p)
392             fprintf(stderr, "%s: read_display() failed\n", progname);
393 #endif /* HAVE_READ_DISPLAY_EXTENSION */
394
395           copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
396
397           gcv.function = GXcopy;
398           gcv.subwindow_mode = IncludeInferiors;
399           gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
400           XCopyArea (dpy, real_root, pixmap, gc,
401                      xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
402           XFreeGC (dpy, gc);
403         }
404       XSetWindowBackgroundPixmap (dpy, window, pixmap);
405       XFreePixmap (dpy, pixmap);
406     }
407
408   if (grab_verbose_p)
409     fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
410              progname, xgwa.depth,
411              (root_p ? "real root " : ""));
412
413   if (grab_mouse_p)
414     {
415       XUngrabPointer (dpy, CurrentTime);
416       XUngrabKeyboard (dpy, CurrentTime);
417     }
418
419   XSync (dpy, True);
420 }
421
422
423 /* When we are grabbing and manipulating a screen image, it's important that
424    we use the same colormap it originally had.  So, if the screensaver was
425    started with -install, we need to copy the contents of the default colormap
426    into the screensaver's colormap.
427  */
428 static void
429 copy_default_colormap_contents (Screen *screen,
430                                 Colormap to_cmap,
431                                 Visual *to_visual)
432 {
433   Display *dpy = DisplayOfScreen (screen);
434   Visual *from_visual = DefaultVisualOfScreen (screen);
435   Colormap from_cmap = XDefaultColormapOfScreen (screen);
436
437   XColor *old_colors, *new_colors;
438   unsigned long *pixels;
439   XVisualInfo vi_in, *vi_out;
440   int out_count;
441   int from_cells, to_cells, max_cells, got_cells;
442   int i;
443
444   if (from_cmap == to_cmap)
445     return;
446
447   vi_in.screen = XScreenNumberOfScreen (screen);
448   vi_in.visualid = XVisualIDFromVisual (from_visual);
449   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
450                            &vi_in, &out_count);
451   if (! vi_out) abort ();
452   from_cells = vi_out [0].colormap_size;
453   XFree ((char *) vi_out);
454
455   vi_in.screen = XScreenNumberOfScreen (screen);
456   vi_in.visualid = XVisualIDFromVisual (to_visual);
457   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
458                            &vi_in, &out_count);
459   if (! vi_out) abort ();
460   to_cells = vi_out [0].colormap_size;
461   XFree ((char *) vi_out);
462
463   max_cells = (from_cells > to_cells ? to_cells : from_cells);
464
465   old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
466   new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
467   pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
468   for (i = 0; i < max_cells; i++)
469     old_colors[i].pixel = i;
470   XQueryColors (dpy, from_cmap, old_colors, max_cells);
471
472   got_cells = max_cells;
473   allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
474
475   if (grab_verbose_p && got_cells != max_cells)
476     fprintf(stderr, "%s: got only %d of %d cells\n", progname,
477             got_cells, max_cells);
478
479   if (got_cells <= 0)                                    /* we're screwed */
480     ;
481   else if (got_cells == max_cells &&                     /* we're golden */
482            from_cells == to_cells)
483     XStoreColors (dpy, to_cmap, old_colors, got_cells);
484   else                                                   /* try to cope... */
485     {
486       for (i = 0; i < got_cells; i++)
487         {
488           XColor *c = old_colors + i;
489           int j;
490           for (j = 0; j < got_cells; j++)
491             if (pixels[j] == c->pixel)
492               {
493                 /* only store this color value if this is one of the pixels
494                    we were able to allocate. */
495                 XStoreColors (dpy, to_cmap, c, 1);
496                 break;
497               }
498         }
499     }
500
501
502   if (grab_verbose_p)
503     fprintf(stderr, "%s: installing copy of default colormap\n", progname);
504
505   free (old_colors);
506   free (new_colors);
507   free (pixels);
508 }
509
510
511 \f
512 /* The SGI ReadDisplay extension.
513    This extension lets you get back a 24-bit image of the screen, taking into
514    account the colors with which all windows are *currently* displayed, even
515    if those windows have different visuals.  Without this extension, presence
516    of windows with different visuals or colormaps will result in technicolor
517    when one tries to grab the screen image.
518  */
519
520 #ifdef HAVE_READ_DISPLAY_EXTENSION
521
522 static Bool
523 read_display (Screen *screen, Window window, Pixmap into_pixmap,
524               Bool dont_wait)
525 {
526   Display *dpy = DisplayOfScreen (screen);
527   XWindowAttributes xgwa;
528   int rd_event_base = 0;
529   int rd_error_base = 0;
530   unsigned long hints = 0;
531   XImage *image = 0;
532   XGCValues gcv;
533   int class;
534   GC gc;
535   Bool remap_p = False;
536
537   /* Check to see if the server supports the extension, and bug out if not.
538    */
539   if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
540     {
541       if (grab_verbose_p)
542         fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
543       return False;
544     }
545
546   /* If this isn't a visual we know how to handle, bug out.  We handle:
547       = TrueColor in depths 8, 12, 15, 16, and 32;
548       = PseudoColor and DirectColor in depths 8 and 12.
549    */
550   XGetWindowAttributes(dpy, window, &xgwa);
551   class = visual_class (screen, xgwa.visual);
552   if (class == TrueColor)
553     {
554       if (xgwa.depth != 8  && xgwa.depth != 12 && xgwa.depth != 15 &&
555           xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
556         {
557           if (grab_verbose_p)
558             fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
559                     progname, xgwa.depth);
560           return False;
561         }
562     }
563   else if (class == PseudoColor || class == DirectColor)
564     {
565       if (xgwa.depth != 8 && xgwa.depth != 12)
566         {
567           if (grab_verbose_p)
568             fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
569                     progname, xgwa.depth);
570           return False;
571         }
572       else
573         /* Allocate a TrueColor-like spread of colors for the image. */
574         remap_p = True;
575     }
576
577
578   /* Try and read the screen.
579    */
580   hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
581   image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
582                         hints, &hints);
583   if (!image)
584     {
585       if (grab_verbose_p)
586         fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
587       return False;
588     }
589   if (!image->data)
590     {
591       if (grab_verbose_p)
592         fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
593       XDestroyImage(image);
594       return False;
595     }
596
597   /* XReadDisplay tends to LIE about the depth of the image it read.
598      It is returning an XImage which has `depth' and `bits_per_pixel'
599      confused!
600
601      That is, on a 24-bit display, where all visuals claim depth 24, and
602      where XGetImage would return an XImage with depth 24, and where
603      XPutImage will get a BadMatch with images that are not depth 24,
604      XReadDisplay is returning images with depth 32!  Fuckwits!
605
606      So if the visual is of depth 24, but the image came back as depth 32,
607      hack it to be 24 lest we get a BadMatch from XPutImage.
608
609      I wonder what happens on an 8-bit SGI...  Probably it still returns
610      an image claiming depth 32?  Certainly it can't be 8.  So, let's just
611      smash it to 32...
612    */
613   if (image->depth == 32 /* && xgwa.depth == 24 */ )
614     image->depth = 24;
615
616   /* If the visual of the window/pixmap into which we're going to draw is
617      less deep than the screen itself, then we need to convert the grabbed bits
618      to match the depth by clipping off the less significant bit-planes of each
619      color component.
620    */
621   if (image->depth > xgwa.depth)
622     {
623       int x, y;
624       /* We use the same image->data in both images -- that's ok, because
625          since we're reading from B and writing to A, and B uses more bytes
626          per pixel than A, the write pointer won't overrun the read pointer.
627        */
628       XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
629                                      ZPixmap, 0, image->data,
630                                      xgwa.width, xgwa.height,
631                                      8, 0);
632       if (!image2)
633         {
634           if (grab_verbose_p)
635             fprintf(stderr, "%s: out of memory?\n", progname);
636           return False;
637         }
638
639       if (grab_verbose_p)
640         fprintf(stderr, "%s: converting from depth %d to depth %d\n",
641                 progname, image->depth, xgwa.depth);
642
643       for (y = 0; y < image->height; y++)
644         for (x = 0; x < image->width; x++)
645           {
646             /* #### really these shift values should be determined from the
647                mask values -- but that's a pain in the ass, and anyway,
648                this is an SGI-specific extension so hardcoding assumptions
649                about the SGI server's behavior isn't *too* heinous... */
650             unsigned long pixel = XGetPixel(image, x, y);
651             unsigned int r = (pixel & image->red_mask);
652             unsigned int g = (pixel & image->green_mask) >> 8;
653             unsigned int b = (pixel & image->blue_mask) >> 16;
654
655             if (xgwa.depth == 8)
656               pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
657             else if (xgwa.depth == 12)
658               pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
659             else if (xgwa.depth == 16 || xgwa.depth == 15)
660               /* Gah! I don't understand why these are in the other order. */
661               pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
662             else
663               abort();
664
665             XPutPixel(image2, x, y, pixel);
666           }
667       image->data = 0;
668       XDestroyImage(image);
669       image = image2;
670     }
671
672   if (remap_p)
673     {
674       allocate_cubic_colormap (screen, window, xgwa.visual);
675       remap_image (screen, window, xgwa.colormap, image);
676     }
677
678   /* Now actually put the bits into the window or pixmap -- note the design
679      bogosity of this extension, where we've been forced to take 24 bit data
680      from the server to the client, and then push it back from the client to
681      the server, *without alteration*.  We should have just been able to tell
682      the server, "put a screen image in this drawable", instead of having to
683      go through the intermediate step of converting it to an Image.  Geez.
684      (Assuming that the window is of screen depth; we happen to handle less
685      deep windows, but that's beside the point.)
686    */
687   gcv.function = GXcopy;
688   gc = XCreateGC (dpy, window, GCFunction, &gcv);
689
690   if (into_pixmap)
691     {
692       gcv.function = GXcopy;
693       gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
694       XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
695                  xgwa.width, xgwa.height);
696     }
697   else
698     {
699       gcv.function = GXcopy;
700       gc = XCreateGC (dpy, window, GCFunction, &gcv);
701
702       /* Ok, now we'll be needing that window on the screen... */
703       raise_window(dpy, window, dont_wait);
704
705       /* Plop down the bits... */
706       XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
707     }
708   XFreeGC (dpy, gc);
709
710   if (image->data)
711     {
712       free(image->data);
713       image->data = 0;
714     }
715   XDestroyImage(image);
716
717   return True;
718 }
719 #endif /* HAVE_READ_DISPLAY_EXTENSION */
720
721
722 #ifdef HAVE_READ_DISPLAY_EXTENSION
723
724 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
725    visual behave like a TrueColor visual of the same depth.
726  */
727 static void
728 allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
729 {
730   Display *dpy = DisplayOfScreen (screen);
731   XWindowAttributes xgwa;
732   Colormap cmap;
733   int nr, ng, nb, cells;
734   int r, g, b;
735   int depth;
736   XColor colors[4097];
737   int i;
738
739   XGetWindowAttributes (dpy, window, &xgwa);
740   cmap = xgwa.colormap;
741   depth = visual_depth(screen, visual);
742
743   switch (depth)
744     {
745     case 8:  nr = 3; ng = 3; nb = 2; cells = 256;  break;
746     case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
747     default: abort(); break;
748     }
749
750   memset(colors, 0, sizeof(colors));
751   for (r = 0; r < (1 << nr); r++)
752     for (g = 0; g < (1 << ng); g++)
753       for (b = 0; b < (1 << nb); b++)
754         {
755           i = (r | (g << nr) | (b << (nr + ng)));
756           colors[i].pixel = i;
757           colors[i].flags = DoRed|DoGreen|DoBlue;
758           if (depth == 8)
759             {
760               colors[i].red   = ((r << 13) | (r << 10) | (r << 7) |
761                                  (r <<  4) | (r <<  1));
762               colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
763                                  (g <<  4) | (g <<  1));
764               colors[i].blue  = ((b << 14) | (b << 12) | (b << 10) |
765                                  (b <<  8) | (b <<  6) | (b <<  4) |
766                                  (b <<  2) | b);
767             }
768           else
769             {
770               colors[i].red   = (r << 12) | (r << 8) | (r << 4) | r;
771               colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
772               colors[i].blue  = (b << 12) | (b << 8) | (b << 4) | b;
773             }
774         }
775
776   {
777     int j;
778     int allocated = 0;
779     int interleave = cells / 8;  /* skip around, rather than allocating in
780                                     order, so that we get better coverage if
781                                     we can't allocated all of them. */
782     for (j = 0; j < interleave; j++)
783       for (i = 0; i < cells; i += interleave)
784         if (XAllocColor (dpy, cmap, &colors[i + j]))
785           allocated++;
786
787     if (grab_verbose_p)
788       fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
789                progname, allocated, cells);
790   }
791 }
792
793 static unsigned long
794 find_closest_pixel (XColor *colors, int ncolors,
795                     unsigned long r, unsigned long g, unsigned long b)
796 {
797   unsigned long distance = ~0;
798   int i, found = 0;
799
800   if (ncolors == 0)
801     abort();
802   for (i = 0; i < ncolors; i++)
803     {
804       unsigned long d;
805       int rd, gd, bd;
806
807       rd = r - colors[i].red;
808       gd = g - colors[i].green;
809       bd = b - colors[i].blue;
810       if (rd < 0) rd = -rd;
811       if (gd < 0) gd = -gd;
812       if (bd < 0) bd = -bd;
813       d = (rd << 1) + (gd << 2) + bd;
814       
815       if (d < distance)
816         {
817           distance = d;
818           found = i;
819           if (distance == 0)
820               break;
821         }
822     }
823
824   return found;
825 }
826
827
828 void
829 remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
830 {
831   Display *dpy = DisplayOfScreen (screen);
832   unsigned long map[4097];
833   int x, y, i;
834   int cells;
835   XColor colors[4097];
836
837   if (image->depth == 8)
838     cells = 256;
839   else if (image->depth == 12)
840     cells = 4096;
841   else
842     abort();
843
844   memset(map,    -1, sizeof(*map));
845   memset(colors, -1, sizeof(*colors));
846
847   for (i = 0; i < cells; i++)
848     colors[i].pixel = i;
849   XQueryColors (dpy, cmap, colors, cells);
850
851   if (grab_verbose_p)
852     fprintf(stderr, "%s: building table for %d bit image\n",
853             progname, image->depth);
854
855   for (i = 0; i < cells; i++)
856     {
857       unsigned short r, g, b;
858
859       if (cells == 256)
860         {
861           /* "RRR GGG BB" In an 8 bit map.  Convert that to
862              "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
863              an even spread. */
864           r = (i & 0x07);
865           g = (i & 0x38) >> 3;
866           b = (i & 0xC0) >> 6;
867
868           r = ((r << 13) | (r << 10) | (r << 7) | (r <<  4) | (r <<  1));
869           g = ((g << 13) | (g << 10) | (g << 7) | (g <<  4) | (g <<  1));
870           b = ((b << 14) | (b << 12) | (b << 10) | (b <<  8) |
871                (b <<  6) | (b <<  4) | (b <<  2) | b);
872         }
873       else
874         {
875           /* "RRRR GGGG BBBB" In a 12 bit map.  Convert that to
876              "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
877              spread. */
878           r = (i & 0x00F);
879           g = (i & 0x0F0) >> 4;
880           b = (i & 0xF00) >> 8;
881
882           r = (r << 12) | (r << 8) | (r << 4) | r;
883           g = (g << 12) | (g << 8) | (g << 4) | g;
884           b = (b << 12) | (b << 8) | (b << 4) | b;
885         }
886
887       map[i] = find_closest_pixel (colors, cells, r, g, b);
888     }
889
890   if (grab_verbose_p)
891     fprintf(stderr, "%s: remapping colors in %d bit image\n",
892             progname, image->depth);
893
894   for (y = 0; y < image->height; y++)
895     for (x = 0; x < image->width; x++)
896       {
897         unsigned long pixel = XGetPixel(image, x, y);
898         if (pixel >= cells) abort();
899         XPutPixel(image, x, y, map[pixel]);
900       }
901 }
902
903
904 #endif /* HAVE_READ_DISPLAY_EXTENSION */