ftp://ftp.demon.nl/disk1/redhat-contrib/libc5/SRPMS/xscreensaver-2.14-1.src.rpm
[xscreensaver] / utils / grabscreen.c
1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997
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   }
250 #endif /* DEBUG */
251
252   if (!root_p)
253     XSetWindowBackgroundPixmap (dpy, window, None);
254
255   if (grab_mouse_p)
256     {
257       /* prevent random viewer of the screen saver (locker) from messing
258          with windows.   We don't check whether it succeeded, because what
259          are our options, really... */
260       XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
261                     GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
262       XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
263                      CurrentTime);
264     }
265
266   if (unmap_time > 0)
267     {
268       XUnmapWindow (dpy, window);
269       install_screen_colormaps (screen);
270       XSync (dpy, True);
271       usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
272     }
273
274   XGetWindowAttributes (dpy, window, &xgwa);
275
276   if (!root_p)
277     {
278 #ifdef HAVE_READ_DISPLAY_EXTENSION
279       if (! read_display(screen, window, 0, saver_p))
280 #endif /* HAVE_READ_DISPLAY_EXTENSION */
281         {
282 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
283           fprintf(stderr, "%s: read_display() failed\n", progname);
284 #endif /* DEBUG */
285           copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
286           raise_window(dpy, window, saver_p);
287         }
288     }
289   else  /* root_p */
290     {
291       Pixmap pixmap;
292       XWindowAttributes xgwa;
293       XGetWindowAttributes(dpy, window, &xgwa);
294       pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
295
296 #ifdef HAVE_READ_DISPLAY_EXTENSION
297       if (! read_display(screen, window, pixmap, True))
298 #endif
299         {
300           Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */
301           XGCValues gcv;
302           GC gc;
303
304 #if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG)
305           fprintf(stderr, "%s: read_display() failed\n", progname);
306 #endif /* DEBUG */
307
308           copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
309
310           gcv.function = GXcopy;
311           gcv.subwindow_mode = IncludeInferiors;
312           gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
313           XCopyArea (dpy, real_root, pixmap, gc,
314                      xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
315           XFreeGC (dpy, gc);
316         }
317       XSetWindowBackgroundPixmap (dpy, window, pixmap);
318       XFreePixmap (dpy, pixmap);
319     }
320
321   if (grab_mouse_p)
322     {
323       XUngrabPointer (dpy, CurrentTime);
324       XUngrabKeyboard (dpy, CurrentTime);
325     }
326
327   XSync (dpy, True);
328 }
329
330 void
331 grab_screen_image (Screen *screen, Window window)
332 {
333 #ifdef HAVE_SGI_VIDEO
334   char c, *s = get_string_resource("grabVideoProbability", "Float");
335   double prob = -1;
336   if (!s ||
337       (1 != sscanf (s, " %lf %c", &prob, &c)) ||
338       prob < 0 ||
339       prob > 1)
340     prob = 0.5;
341
342   if ((random() % 100) < ((int) (100 * prob)))
343     {
344       XWindowAttributes xgwa;
345       Display *dpy = DisplayOfScreen (screen);
346       XGetWindowAttributes (dpy, window, &xgwa);
347 # ifdef DEBUG
348       fprintf(stderr, "%s: trying to grab from video...\n", progname);
349 # endif /* DEBUG */
350       if (grab_video_frame (screen, xgwa.visual, window))
351         {
352           if (xgwa.depth < 24)
353             {
354               int class = visual_class (screen, xgwa.visual);
355               if (class == PseudoColor || class == DirectColor)
356                 make_cubic_colormap (screen, window, xgwa.visual);
357             }
358           return;
359         }
360     }
361 #endif /* HAVE_SGI_VIDEO */
362
363   grab_screen_image_1 (screen, window);
364 }
365
366
367 /* When we are grabbing and manipulating a screen image, it's important that
368    we use the same colormap it originally had.  So, if the screensaver was
369    started with -install, we need to copy the contents of the default colormap
370    into the screensaver's colormap.
371  */
372 static void
373 copy_default_colormap_contents (Screen *screen,
374                                 Colormap to_cmap,
375                                 Visual *to_visual)
376 {
377   Display *dpy = DisplayOfScreen (screen);
378   Visual *from_visual = DefaultVisualOfScreen (screen);
379   Colormap from_cmap = XDefaultColormapOfScreen (screen);
380
381   XColor *old_colors, *new_colors;
382   unsigned long *pixels;
383   XVisualInfo vi_in, *vi_out;
384   int out_count;
385   int from_cells, to_cells, max_cells, got_cells;
386   int i;
387
388   if (from_cmap == to_cmap)
389     return;
390
391   vi_in.screen = XScreenNumberOfScreen (screen);
392   vi_in.visualid = XVisualIDFromVisual (from_visual);
393   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
394                            &vi_in, &out_count);
395   if (! vi_out) abort ();
396   from_cells = vi_out [0].colormap_size;
397   XFree ((char *) vi_out);
398
399   vi_in.screen = XScreenNumberOfScreen (screen);
400   vi_in.visualid = XVisualIDFromVisual (to_visual);
401   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
402                            &vi_in, &out_count);
403   if (! vi_out) abort ();
404   to_cells = vi_out [0].colormap_size;
405   XFree ((char *) vi_out);
406
407   max_cells = (from_cells > to_cells ? to_cells : from_cells);
408
409   old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
410   new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
411   pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
412   for (i = 0; i < max_cells; i++)
413     old_colors[i].pixel = i;
414   XQueryColors (dpy, from_cmap, old_colors, max_cells);
415
416   got_cells = max_cells;
417   allocate_writable_colors (dpy, to_cmap, pixels, &got_cells);
418
419 #ifdef DEBUG
420   if (got_cells != max_cells)
421     fprintf(stderr, "%s: got only %d of %d cells\n", progname,
422             got_cells, max_cells);
423 #endif /* DEBUG */
424
425   if (got_cells <= 0)                                    /* we're screwed */
426     ;
427   else if (got_cells == max_cells &&                     /* we're golden */
428            from_cells == to_cells)
429     XStoreColors (dpy, to_cmap, old_colors, got_cells);
430   else                                                   /* try to cope... */
431     {
432       for (i = 0; i < got_cells; i++)
433         {
434           XColor *c = old_colors + i;
435           int j;
436           for (j = 0; j < got_cells; j++)
437             if (pixels[j] == c->pixel)
438               {
439                 /* only store this color value if this is one of the pixels
440                    we were able to allocate. */
441                 XStoreColors (dpy, to_cmap, c, 1);
442                 break;
443               }
444         }
445     }
446
447
448 #ifdef DEBUG
449   fprintf(stderr, "%s: installing copy of default colormap\n", progname);
450 #endif /* DEBUG */
451
452   free (old_colors);
453   free (new_colors);
454   free (pixels);
455 }
456
457
458 \f
459 /* The SGI ReadDisplay extension.
460    This extension lets you get back a 24-bit image of the screen, taking into
461    account the colors with which all windows are *currently* displayed, even
462    if those windows have different visuals.  Without this extension, presence
463    of windows with different visuals or colormaps will result in technicolor
464    when one tries to grab the screen image.
465  */
466
467 #ifdef HAVE_READ_DISPLAY_EXTENSION
468
469 static Bool
470 read_display (Screen *screen, Window window, Pixmap into_pixmap,
471               Bool dont_wait)
472 {
473   Display *dpy = DisplayOfScreen (screen);
474   XWindowAttributes xgwa;
475   int rd_event_base = 0;
476   int rd_error_base = 0;
477   unsigned long hints = 0;
478   XImage *image = 0;
479   XGCValues gcv;
480   int class;
481   GC gc;
482   Bool install_p = False;
483
484   /* Check to see if the server supports the extension, and bug out if not.
485    */
486   if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
487     return False;
488
489   /* If this isn't a visual we know how to handle, bug out.  We handle:
490       = TrueColor in depths 8, 12, 16, and 32;
491       = PseudoColor and DirectColor in depths 8 and 12.
492    */
493   XGetWindowAttributes(dpy, window, &xgwa);
494   class = visual_class (screen, xgwa.visual);
495   if (class == TrueColor)
496     {
497       if (xgwa.depth != 8  && xgwa.depth != 12 && xgwa.depth != 16 &&
498           xgwa.depth != 24 && xgwa.depth != 32)
499         return False;
500     }
501   else if (class == PseudoColor || class == DirectColor)
502     {
503       if (xgwa.depth != 8 && xgwa.depth != 12)
504         return False;
505       else
506         /* Install a colormap that makes this visual behave like
507            a TrueColor visual of the same depth. */
508         install_p = True;
509     }
510
511
512   /* Try and read the screen.
513    */
514   hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
515   image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
516                         hints, &hints);
517   if (!image)
518     return False;
519   if (!image->data)
520     {
521       XDestroyImage(image);
522       return False;
523     }
524
525   /* Uh, this can't be right, can it?  But it's necessary.  X sucks.
526      If the visual is of depth 24, but the image came back as depth 32,
527      hack it to be 24 lest we get a BadMatch from XPutImage.  (I presume
528      I'm expected to look at the server's pixmap formats or some such
529      nonsense... but fuck it.)
530    */
531   if (xgwa.depth == 24 && image->depth == 32)
532     image->depth = 24;
533
534   /* If the visual of the window/pixmap into which we're going to draw is
535      less deep than the screen itself, then we need to convert the grabbed bits
536      to match the depth by clipping off the less significant bit-planes of each
537      color component.
538    */
539   if (image->depth > xgwa.depth)
540     {
541       int x, y;
542       /* We use the same image->data in both images -- that's ok, because
543          since we're reading from B and writing to A, and B uses more bytes
544          per pixel than A, the write pointer won't overrun the read pointer.
545        */
546       XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
547                                      ZPixmap, 0, image->data,
548                                      xgwa.width, xgwa.height,
549                                      8, 0);
550       if (!image2)
551         return False;
552
553 #ifdef DEBUG
554       fprintf(stderr, "%s: converting from depth %d to depth %d\n",
555               progname, image->depth, xgwa.depth);
556 #endif /* DEBUG */
557
558       for (y = 0; y < image->height; y++)
559         for (x = 0; x < image->width; x++)
560           {
561             /* #### really these shift values should be determined from the
562                mask values -- but that's a pain in the ass, and anyway,
563                this is an SGI-specific extension so hardcoding assumptions
564                about the SGI server's behavior isn't *too* heinous... */
565             unsigned long pixel = XGetPixel(image, x, y);
566             unsigned int r = (pixel & image->red_mask);
567             unsigned int g = (pixel & image->green_mask) >> 8;
568             unsigned int b = (pixel & image->blue_mask) >> 16;
569
570             if (xgwa.depth == 8)
571               pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
572             else if (xgwa.depth == 12)
573               pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
574             else if (xgwa.depth == 16)
575               pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
576             else
577               abort();
578
579             XPutPixel(image2, x, y, pixel);
580           }
581       image->data = 0;
582       XDestroyImage(image);
583       image = image2;
584     }
585
586
587   /* Now actually put the bits into the window or pixmap -- note the design
588      bogosity of this extension, where we've been forced to take 24 bit data
589      from the server to the client, and then push it back from the client to
590      the server, *without alteration*.  We should have just been able to tell
591      the server, "put a screen image in this drawable", instead of having to
592      go through the intermediate step of converting it to an Image.  Geez.
593      (Assuming that the window is of screen depth; we happen to handle less
594      deep windows, but that's beside the point.)
595    */
596   gcv.function = GXcopy;
597   gc = XCreateGC (dpy, window, GCFunction, &gcv);
598
599   if (into_pixmap)
600     {
601       gcv.function = GXcopy;
602       gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
603       XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
604                  xgwa.width, xgwa.height);
605     }
606   else
607     {
608       gcv.function = GXcopy;
609       gc = XCreateGC (dpy, window, GCFunction, &gcv);
610
611       /* Ok, now we'll be needing that window on the screen... */
612       raise_window(dpy, window, dont_wait);
613
614       /* Plop down the bits... */
615       XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
616     }
617   XFreeGC (dpy, gc);
618
619   if (image->data)
620     {
621       free(image->data);
622       image->data = 0;
623     }
624   XDestroyImage(image);
625
626   if (install_p)
627     make_cubic_colormap (screen, window, xgwa.visual);
628
629   return True;
630 }
631 #endif /* HAVE_READ_DISPLAY_EXTENSION */
632
633
634 #if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
635
636 /* Makes and installs a colormap that makes a PseudoColor or DirectColor
637    visual behave like a TrueColor visual of the same depth.
638  */
639 static void
640 make_cubic_colormap (Screen *screen, Window window, Visual *visual)
641 {
642   Display *dpy = DisplayOfScreen (screen);
643   Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
644   int nr, ng, nb, cells;
645   int r, g, b;
646   int depth;
647   XColor colors[4097];
648   int i;
649
650   depth = visual_depth(screen, visual);
651   switch (depth)
652     {
653     case 8:  nr = 3; ng = 3; nb = 2; cells = 256;  break;
654     case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
655     default: abort(); break;
656     }
657
658   memset(colors, 0, sizeof(colors));
659   for (i = 0; i < cells; i++)
660     {
661       colors[i].flags = DoRed|DoGreen|DoBlue;
662       colors[i].red = colors[i].green = colors[i].blue = 0;
663     }
664
665   for (r = 0; r < (1 << nr); r++)
666     for (g = 0; g < (1 << ng); g++)
667       for (b = 0; b < (1 << nb); b++)
668         {
669           i = (r | (g << nr) | (b << (nr + ng)));
670           colors[i].pixel = i;
671           if (depth == 8)
672             {
673               colors[i].red   = ((r << 13) | (r << 10) | (r << 7) |
674                                  (r <<  4) | (r <<  1));
675               colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
676                                  (g <<  4) | (g <<  1));
677               colors[i].blue  = ((b << 14) | (b << 12) | (b << 10) |
678                                  (b <<  8) | (b <<  6) | (b <<  4) |
679                                  (b <<  2) | b);
680             }
681           else
682             {
683               colors[i].red   = (r << 12) | (r << 8) | (r << 4) | r;
684               colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
685               colors[i].blue  = (b << 12) | (b << 8) | (b << 4) | b;
686             }
687         }
688
689 #ifdef DEBUG
690   fprintf(stderr, "%s: installing cubic colormap\n", progname);
691 #endif /* DEBUG */
692
693   XStoreColors (dpy, cmap, colors, cells);
694   XSetWindowColormap (dpy, window, cmap);
695
696   /* Gag, install the colormap.
697      This is definitely right in the `if xscreensaver_window_p' case, since
698      it will never get installed otherwise.  But, if we don't do it
699      unconditionally, then the new colormap won't get installed until the
700      window (re-)gains focus.  It's generally very antisocial to install
701      the colormap of a non-OverrideRedirect window (that task belongs to
702      the WM) and if we were being kosher, we would only install this cmap
703      if the old cmap was already installed (or perhaps, if the window had
704      focus.)  But, since this extension only exists on SGIs, and since SGIs
705      can handle four colormaps at once, let's go ahead and install it all
706      the time, so that even if the window pops up and has never had focus,
707      it will still display in the proper colors.
708    */
709   XInstallColormap (dpy, cmap);
710 }
711
712 #endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */