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