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