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