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