http://ftp.x.org/contrib/applications/xscreensaver-2.24.tar.gz
[xscreensaver] / utils / grabscreen.c
1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998
2  *  Jamie Zawinski <jwz@netscape.com>
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 "sgivideo.h"
38 #include "visual.h"
39 #include "resources.h"
40
41 #include "vroot.h"
42 #undef RootWindowOfScreen
43 #undef RootWindow
44 #undef DefaultRootWindow
45
46
47 #ifdef HAVE_READ_DISPLAY_EXTENSION
48 # include <X11/extensions/readdisplay.h>
49   static Bool read_display (Screen *, Window, Pixmap, Bool);
50 #endif /* HAVE_READ_DISPLAY_EXTENSION */
51
52
53 static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
54
55 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
56 static void make_cubic_colormap (Screen *, Window, Visual *);
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 #ifdef DEBUG
68 extern char *progname;
69 #endif /* DEBUG */
70
71
72 static void
73 raise_window(Display *dpy, Window window, Bool dont_wait)
74 {
75 #ifdef DEBUG
76   fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
77           progname, (unsigned long) window,
78           (dont_wait ? "not waiting" : "waiting"));
79 #endif /* DEBUG */
80
81   if (! dont_wait)
82     {
83       XWindowAttributes xgwa;
84       XSizeHints hints;
85       long supplied = 0;
86       memset(&hints, 0, sizeof(hints));
87       XGetWMNormalHints(dpy, window, &hints, &supplied);
88       XGetWindowAttributes (dpy, window, &xgwa);
89       hints.x = xgwa.x;
90       hints.y = xgwa.y;
91       hints.width = xgwa.width;
92       hints.height = xgwa.height;
93       hints.flags |= (PPosition|USPosition|PSize|USSize);
94       XSetWMNormalHints(dpy, window, &hints);
95     }
96
97   XMapRaised(dpy, window);
98
99   if (! dont_wait)
100     {
101       XEvent event;
102       XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
103       XSync (dpy, True);
104     }
105 }
106
107
108 static Bool
109 xscreensaver_window_p (Display *dpy, Window window)
110 {
111   Atom type;
112   int format;
113   unsigned long nitems, bytesafter;
114   char *version;
115   if (XGetWindowProperty (dpy, window,
116                           XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
117                           0, 1, False, XA_STRING,
118                           &type, &format, &nitems, &bytesafter,
119                           (unsigned char **) &version)
120       == Success
121       && type != None)
122     return True;
123   return False;
124 }
125
126
127
128 static XErrorHandler old_ehandler = 0;
129 static int
130 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
131 {
132   if (error->error_code == BadWindow || error->error_code == BadDrawable)
133     return 0;
134   else if (!old_ehandler)
135     abort();
136   else
137     return (*old_ehandler) (dpy, error);
138 }
139
140
141 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
142    on a window whose depth is not the maximal depth of the screen?  Or
143    something.  Anyway, things don't work unless we: use SubwindowMode for
144    the real root window (or a legitimate virtual root window); but do not
145    use SubwindowMode for the xscreensaver window.  I make no attempt to
146    explain.
147  */
148 Bool
149 use_subwindow_mode_p(Screen *screen, Window window)
150 {
151   if (window != VirtualRootWindowOfScreen(screen))
152     return False;
153   else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
154     return False;
155   else
156     return True;
157 }
158
159
160 /* Install the colormaps of all visible windows, deepest first.
161    This should leave the colormaps of the topmost windows installed
162    (if only N colormaps can be installed at a time, then only the
163    topmost N windows will be shown in the right colors.)
164  */
165 static void
166 install_screen_colormaps (Screen *screen)
167 {
168   int i;
169   Display *dpy = DisplayOfScreen (screen);
170   Window vroot, real_root;
171   Window parent, *kids = 0;
172   unsigned int nkids = 0;
173
174   XSync (dpy, False);
175   old_ehandler = XSetErrorHandler (BadWindow_ehandler);
176
177   vroot = VirtualRootWindowOfScreen (screen);
178   if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids))
179     for (i = 0; i < nkids; i++)
180       {
181         XWindowAttributes xgwa;
182         Window client;
183 #ifdef HAVE_XMU
184         /* #### need to put XmuClientWindow() in xmu.c, sigh... */
185         if (! (client = XmuClientWindow (dpy, kids[i])))
186 #endif
187           client = kids[i];
188         xgwa.colormap = 0;
189         XGetWindowAttributes (dpy, client, &xgwa);
190         if (xgwa.colormap && xgwa.map_state == IsViewable)
191           XInstallColormap (dpy, xgwa.colormap);
192       }
193   XInstallColormap (dpy, DefaultColormapOfScreen (screen));
194   XSync (dpy, False);
195   XSetErrorHandler (old_ehandler);
196   XSync (dpy, False);
197
198   if (kids)
199     XFree ((char *) kids);
200 }
201
202
203 static void
204 grab_screen_image_1 (Screen *screen, Window window)
205 {
206   Display *dpy = DisplayOfScreen (screen);
207   XWindowAttributes xgwa;
208   Window real_root = XRootWindowOfScreen (screen);  /* not vroot */
209   Bool root_p = (window == real_root);
210   Bool saver_p = xscreensaver_window_p (dpy, window);
211   Bool grab_mouse_p = False;
212   int unmap_time = 0;
213
214   if (saver_p)
215     /* I think this is redundant, but just to be safe... */
216     root_p = False;
217
218   if (saver_p)
219     /* The only time grabbing the mouse is important is if this program
220        is being run while the saver is locking the screen. */
221     grab_mouse_p = True;
222
223   if (!root_p)
224     {
225       double unmap = 0;
226       if (saver_p)
227         {
228           unmap = get_float_resource("grabRootDelay", "Seconds");
229           if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
230         }
231       else
232         {
233           unmap = get_float_resource("grabWindowDelay", "Seconds");
234           if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
235         }
236       unmap_time = unmap * 100000;
237     }
238
239 #ifdef DEBUG
240   fprintf(stderr,
241           "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
242           progname, (unsigned long) window,
243           root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
244   {
245     XWindowAttributes xgwa2;
246     XGetWindowAttributes (dpy, window, &xgwa2);
247     fprintf(stderr, "%s: ", progname);
248     describe_visual(stderr, screen, xgwa2.visual, ####);
249     fprintf (stderr, "\n");
250   }
251 #endif /* DEBUG */
252
253   if (!root_p)
254     XSetWindowBackgroundPixmap (dpy, window, None);
255
256   if (grab_mouse_p)
257     {
258       /* prevent random viewer of the screen saver (locker) from messing
259          with windows.   We don't check whether it succeeded, because what
260          are our options, really... */
261       XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
262                     GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
263       XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
264                      CurrentTime);
265     }
266
267   if (unmap_time > 0)
268     {
269       XUnmapWindow (dpy, window);
270       install_screen_colormaps (screen);
271       XSync (dpy, True);
272       usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
273     }
274
275   XGetWindowAttributes (dpy, window, &xgwa);
276
277   if (!root_p)
278     {
279 #ifdef HAVE_READ_DISPLAY_EXTENSION
280       if (! read_display(screen, window, 0, saver_p))
281 #endif /* HAVE_READ_DISPLAY_EXTENSION */
282         {
283 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
284           fprintf(stderr, "%s: read_display() failed\n", progname);
285 #endif /* DEBUG */
286           copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
287           raise_window(dpy, window, saver_p);
288         }
289     }
290   else  /* root_p */
291     {
292       Pixmap pixmap;
293       XWindowAttributes xgwa;
294       XGetWindowAttributes(dpy, window, &xgwa);
295       pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
296
297 #ifdef HAVE_READ_DISPLAY_EXTENSION
298       if (! read_display(screen, window, pixmap, True))
299 #endif
300         {
301           Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
302           XGCValues gcv;
303           GC gc;
304
305 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
306           fprintf(stderr, "%s: read_display() failed\n", progname);
307 #endif /* DEBUG */
308
309           copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
310
311           gcv.function = GXcopy;
312           gcv.subwindow_mode = IncludeInferiors;
313           gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
314           XCopyArea (dpy, real_root, pixmap, gc,
315                      xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
316           XFreeGC (dpy, gc);
317         }
318       XSetWindowBackgroundPixmap (dpy, window, pixmap);
319       XFreePixmap (dpy, pixmap);
320     }
321
322   if (grab_mouse_p)
323     {
324       XUngrabPointer (dpy, CurrentTime);
325       XUngrabKeyboard (dpy, CurrentTime);
326     }
327
328   XSync (dpy, True);
329 }
330
331 void
332 grab_screen_image (Screen *screen, Window window)
333 {
334 #ifdef HAVE_SGI_VIDEO
335   char c, *s = get_string_resource("grabVideoProbability", "Float");
336   double prob = -1;
337   if (!s ||
338       (1 != sscanf (s, " %lf %c", &prob, &c)) ||
339       prob < 0 ||
340       prob > 1)
341     prob = 0.5;
342
343   if ((random() % 100) < ((int) (100 * prob)))
344     {
345       XWindowAttributes xgwa;
346       Display *dpy = DisplayOfScreen (screen);
347       XGetWindowAttributes (dpy, window, &xgwa);
348 # ifdef DEBUG
349       fprintf(stderr, "%s: trying to grab from video...\n", progname);
350 # endif /* DEBUG */
351       if (grab_video_frame (screen, xgwa.visual, window))
352         {
353           if (xgwa.depth < 24)
354             {
355               int class = visual_class (screen, xgwa.visual);
356               if (class == PseudoColor || class == DirectColor)
357                 make_cubic_colormap (screen, window, xgwa.visual);
358             }
359           return;
360         }
361     }
362 #endif /* HAVE_SGI_VIDEO */
363
364   grab_screen_image_1 (screen, window);
365 }
366
367
368 /* When we are grabbing and manipulating a screen image, it's important that
369    we use the same colormap it originally had.  So, if the screensaver was
370    started with -install, we need to copy the contents of the default colormap
371    into the screensaver's colormap.
372  */
373 static void
374 copy_default_colormap_contents (Screen *screen,
375                                 Colormap to_cmap,
376                                 Visual *to_visual)
377 {
378   Display *dpy = DisplayOfScreen (screen);
379   Visual *from_visual = DefaultVisualOfScreen (screen);
380   Colormap from_cmap = XDefaultColormapOfScreen (screen);
381
382   XColor *old_colors, *new_colors;
383   unsigned long *pixels;
384   XVisualInfo vi_in, *vi_out;
385   int out_count;
386   int from_cells, to_cells, max_cells, got_cells;
387   int i;
388
389   if (from_cmap == to_cmap)
390     return;
391
392   vi_in.screen = XScreenNumberOfScreen (screen);
393   vi_in.visualid = XVisualIDFromVisual (from_visual);
394   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
395                            &vi_in, &out_count);
396   if (! vi_out) abort ();
397   from_cells = vi_out [0].colormap_size;
398   XFree ((char *) vi_out);
399
400   vi_in.screen = XScreenNumberOfScreen (screen);
401   vi_in.visualid = XVisualIDFromVisual (to_visual);
402   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
403                            &vi_in, &out_count);
404   if (! vi_out) abort ();
405   to_cells = vi_out [0].colormap_size;
406   XFree ((char *) vi_out);
407
408   max_cells = (from_cells > to_cells ? to_cells : from_cells);
409
410   old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
411   new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
412   pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
413   for (i = 0; i < max_cells; i++)
414     old_colors[i].pixel = i;
415   XQueryColors (dpy, from_cmap, old_colors, max_cells);
416
417   got_cells = max_cells;
418   allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
419
420 #ifdef DEBUG
421   if (got_cells != max_cells)
422     fprintf(stderr, "%s: got only %d of %d cells\n", progname,
423             got_cells, max_cells);
424 #endif /* DEBUG */
425
426   if (got_cells <= 0)                                    /* we're screwed */
427     ;
428   else if (got_cells == max_cells &&                     /* we're golden */
429            from_cells == to_cells)
430     XStoreColors (dpy, to_cmap, old_colors, got_cells);
431   else                                                   /* try to cope... */
432     {
433       for (i = 0; i < got_cells; i++)
434         {
435           XColor *c = old_colors + i;
436           int j;
437           for (j = 0; j < got_cells; j++)
438             if (pixels[j] == c->pixel)
439               {
440                 /* only store this color value if this is one of the pixels
441                    we were able to allocate. */
442                 XStoreColors (dpy, to_cmap, c, 1);
443                 break;
444               }
445         }
446     }
447
448
449 #ifdef DEBUG
450   fprintf(stderr, "%s: installing copy of default colormap\n", progname);
451 #endif /* DEBUG */
452
453   free (old_colors);
454   free (new_colors);
455   free (pixels);
456 }
457
458
459 \f
460 /* The SGI ReadDisplay extension.
461    This extension lets you get back a 24-bit image of the screen, taking into
462    account the colors with which all windows are *currently* displayed, even
463    if those windows have different visuals.  Without this extension, presence
464    of windows with different visuals or colormaps will result in technicolor
465    when one tries to grab the screen image.
466  */
467
468 #ifdef HAVE_READ_DISPLAY_EXTENSION
469
470 static Bool
471 read_display (Screen *screen, Window window, Pixmap into_pixmap,
472               Bool dont_wait)
473 {
474   Display *dpy = DisplayOfScreen (screen);
475   XWindowAttributes xgwa;
476   int rd_event_base = 0;
477   int rd_error_base = 0;
478   unsigned long hints = 0;
479   XImage *image = 0;
480   XGCValues gcv;
481   int class;
482   GC gc;
483   Bool install_p = False;
484
485   /* Check to see if the server supports the extension, and bug out if not.
486    */
487   if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
488     return False;
489
490   /* If this isn't a visual we know how to handle, bug out.  We handle:
491       = TrueColor in depths 8, 12, 16, and 32;
492       = PseudoColor and DirectColor in depths 8 and 12.
493    */
494   XGetWindowAttributes(dpy, window, &xgwa);
495   class = visual_class (screen, xgwa.visual);
496   if (class == TrueColor)
497     {
498       if (xgwa.depth != 8  && xgwa.depth != 12 && xgwa.depth != 16 &&
499           xgwa.depth != 24 && xgwa.depth != 32)
500         return False;
501     }
502   else if (class == PseudoColor || class == DirectColor)
503     {
504       if (xgwa.depth != 8 && xgwa.depth != 12)
505         return False;
506       else
507         /* Install a colormap that makes this visual behave like
508            a TrueColor visual of the same depth. */
509         install_p = True;
510     }
511
512
513   /* Try and read the screen.
514    */
515   hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
516   image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
517                         hints, &hints);
518   if (!image)
519     return False;
520   if (!image->data)
521     {
522       XDestroyImage(image);
523       return False;
524     }
525
526   /* XReadDisplay tends to LIE about the depth of the image it read.
527      It is returning an XImage which has `depth' and `bits_per_pixel'
528      confused!
529
530      That is, on a 24-bit display, where all visuals claim depth 24, and
531      where XGetImage would return an XImage with depth 24, and where
532      XPutImage will get a BadMatch with images that are not depth 24,
533      XReadDisplay is returning images with depth 32!  Fuckwits!
534
535      So if the visual is of depth 24, but the image came back as depth 32,
536      hack it to be 24 lest we get a BadMatch from XPutImage.
537
538      I wonder what happens on an 8-bit SGI...  Probably it still returns
539      an image claiming depth 32?  Certainly it can't be 8.  So, let's just
540      smash it to 32...
541    */
542   if (image->depth == 32 /* && xgwa.depth == 24 */ )
543     image->depth = 24;
544
545   /* If the visual of the window/pixmap into which we're going to draw is
546      less deep than the screen itself, then we need to convert the grabbed bits
547      to match the depth by clipping off the less significant bit-planes of each
548      color component.
549    */
550   if (image->depth > xgwa.depth)
551     {
552       int x, y;
553       /* We use the same image->data in both images -- that's ok, because
554          since we're reading from B and writing to A, and B uses more bytes
555          per pixel than A, the write pointer won't overrun the read pointer.
556        */
557       XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
558                                      ZPixmap, 0, image->data,
559                                      xgwa.width, xgwa.height,
560                                      8, 0);
561       if (!image2)
562         return False;
563
564 #ifdef DEBUG
565       fprintf(stderr, "%s: converting from depth %d to depth %d\n",
566               progname, image->depth, xgwa.depth);
567 #endif /* DEBUG */
568
569       for (y = 0; y < image->height; y++)
570         for (x = 0; x < image->width; x++)
571           {
572             /* #### really these shift values should be determined from the
573                mask values -- but that's a pain in the ass, and anyway,
574                this is an SGI-specific extension so hardcoding assumptions
575                about the SGI server's behavior isn't *too* heinous... */
576             unsigned long pixel = XGetPixel(image, x, y);
577             unsigned int r = (pixel & image->red_mask);
578             unsigned int g = (pixel & image->green_mask) >> 8;
579             unsigned int b = (pixel & image->blue_mask) >> 16;
580
581             if (xgwa.depth == 8)
582               pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
583             else if (xgwa.depth == 12)
584               pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
585             else if (xgwa.depth == 16)
586               pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
587             else
588               abort();
589
590             XPutPixel(image2, x, y, pixel);
591           }
592       image->data = 0;
593       XDestroyImage(image);
594       image = image2;
595     }
596
597
598   /* Now actually put the bits into the window or pixmap -- note the design
599      bogosity of this extension, where we've been forced to take 24 bit data
600      from the server to the client, and then push it back from the client to
601      the server, *without alteration*.  We should have just been able to tell
602      the server, "put a screen image in this drawable", instead of having to
603      go through the intermediate step of converting it to an Image.  Geez.
604      (Assuming that the window is of screen depth; we happen to handle less
605      deep windows, but that's beside the point.)
606    */
607   gcv.function = GXcopy;
608   gc = XCreateGC (dpy, window, GCFunction, &gcv);
609
610   if (into_pixmap)
611     {
612       gcv.function = GXcopy;
613       gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
614       XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
615                  xgwa.width, xgwa.height);
616     }
617   else
618     {
619       gcv.function = GXcopy;
620       gc = XCreateGC (dpy, window, GCFunction, &gcv);
621
622       /* Ok, now we'll be needing that window on the screen... */
623       raise_window(dpy, window, dont_wait);
624
625       /* Plop down the bits... */
626       XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
627     }
628   XFreeGC (dpy, gc);
629
630   if (image->data)
631     {
632       free(image->data);
633       image->data = 0;
634     }
635   XDestroyImage(image);
636
637   if (install_p)
638     make_cubic_colormap (screen, window, xgwa.visual);
639
640   return True;
641 }
642 #endif /* HAVE_READ_DISPLAY_EXTENSION */
643
644
645 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
646
647 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
648    visual behave like a TrueColor visual of the same depth.
649  */
650 static void
651 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
652 {
653   Display *dpy = DisplayOfScreen (screen);
654   Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
655   int nr, ng, nb, cells;
656   int r, g, b;
657   int depth;
658   XColor colors[4097];
659   int i;
660
661   depth = visual_depth(screen, visual);
662   switch (depth)
663     {
664     case 8:  nr = 3; ng = 3; nb = 2; cells = 256;  break;
665     case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
666     default: abort(); break;
667     }
668
669   memset(colors, 0, sizeof(colors));
670   for (i = 0; i < cells; i++)
671     {
672       colors[i].flags = DoRed|DoGreen|DoBlue;
673       colors[i].red = colors[i].green = colors[i].blue = 0;
674     }
675
676   for (r = 0; r < (1 << nr); r++)
677     for (g = 0; g < (1 << ng); g++)
678       for (b = 0; b < (1 << nb); b++)
679         {
680           i = (r | (g << nr) | (b << (nr + ng)));
681           colors[i].pixel = i;
682           if (depth == 8)
683             {
684               colors[i].red   = ((r << 13) | (r << 10) | (r << 7) |
685                                  (r <<  4) | (r <<  1));
686               colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
687                                  (g <<  4) | (g <<  1));
688               colors[i].blue  = ((b << 14) | (b << 12) | (b << 10) |
689                                  (b <<  8) | (b <<  6) | (b <<  4) |
690                                  (b <<  2) | b);
691             }
692           else
693             {
694               colors[i].red   = (r << 12) | (r << 8) | (r << 4) | r;
695               colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
696               colors[i].blue  = (b << 12) | (b << 8) | (b << 4) | b;
697             }
698         }
699
700 #ifdef DEBUG
701   fprintf(stderr, "%s: installing cubic colormap\n", progname);
702 #endif /* DEBUG */
703
704   XStoreColors (dpy, cmap, colors, cells);
705   XSetWindowColormap (dpy, window, cmap);
706
707   /* Gag, install the colormap.
708      This is definitely right in the `if xscreensaver_window_p' case, since
709      it will never get installed otherwise.  But, if we don't do it
710      unconditionally, then the new colormap won't get installed until the
711      window (re-)gains focus.  It's generally very antisocial to install
712      the colormap of a non-OverrideRedirect window (that task belongs to
713      the WM) and if we were being kosher, we would only install this cmap
714      if the old cmap was already installed (or perhaps, if the window had
715      focus.)  But, since this extension only exists on SGIs, and since SGIs
716      can handle four colormaps at once, let's go ahead and install it all
717      the time, so that even if the window pops up and has never had focus,
718      it will still display in the proper colors.
719    */
720   XInstallColormap (dpy, cmap);
721 }
722
723 #endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */