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