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