+
+
+\f
+/* The SGI ReadDisplay extension.
+ This extension lets you get back a 24-bit image of the screen, taking into
+ account the colors with which all windows are *currently* displayed, even
+ if those windows have different visuals. Without this extension, presence
+ of windows with different visuals or colormaps will result in technicolor
+ when one tries to grab the screen image.
+ */
+
+#ifdef HAVE_READ_DISPLAY_EXTENSION
+
+static Bool
+read_display (Screen *screen, Window window, Pixmap into_pixmap,
+ Bool dont_wait)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ int rd_event_base = 0;
+ int rd_error_base = 0;
+ unsigned long hints = 0;
+ XImage *image = 0;
+ XGCValues gcv;
+ int class;
+ GC gc;
+ Bool install_p = False;
+
+ /* Check to see if the server supports the extension, and bug out if not.
+ */
+ if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
+ return False;
+
+ /* If this isn't a visual we know how to handle, bug out. We handle:
+ = TrueColor in depths 8, 12, 16, and 32;
+ = PseudoColor and DirectColor in depths 8 and 12.
+ */
+ XGetWindowAttributes(dpy, window, &xgwa);
+ class = visual_class (screen, xgwa.visual);
+ if (class == TrueColor)
+ {
+ if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 16 &&
+ xgwa.depth != 24 && xgwa.depth != 32)
+ return False;
+ }
+ else if (class == PseudoColor || class == DirectColor)
+ {
+ if (xgwa.depth != 8 && xgwa.depth != 12)
+ return False;
+ else
+ /* Install a colormap that makes this visual behave like
+ a TrueColor visual of the same depth. */
+ install_p = True;
+ }
+
+
+ /* Try and read the screen.
+ */
+ hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
+ image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
+ hints, &hints);
+ if (!image)
+ return False;
+ if (!image->data)
+ {
+ XDestroyImage(image);
+ return False;
+ }
+
+ /* XReadDisplay tends to LIE about the depth of the image it read.
+ It is returning an XImage which has `depth' and `bits_per_pixel'
+ confused!
+
+ That is, on a 24-bit display, where all visuals claim depth 24, and
+ where XGetImage would return an XImage with depth 24, and where
+ XPutImage will get a BadMatch with images that are not depth 24,
+ XReadDisplay is returning images with depth 32! Fuckwits!
+
+ So if the visual is of depth 24, but the image came back as depth 32,
+ hack it to be 24 lest we get a BadMatch from XPutImage.
+
+ I wonder what happens on an 8-bit SGI... Probably it still returns
+ an image claiming depth 32? Certainly it can't be 8. So, let's just
+ smash it to 32...
+ */
+ if (image->depth == 32 /* && xgwa.depth == 24 */ )
+ image->depth = 24;
+
+ /* If the visual of the window/pixmap into which we're going to draw is
+ less deep than the screen itself, then we need to convert the grabbed bits
+ to match the depth by clipping off the less significant bit-planes of each
+ color component.
+ */
+ if (image->depth > xgwa.depth)
+ {
+ int x, y;
+ /* We use the same image->data in both images -- that's ok, because
+ since we're reading from B and writing to A, and B uses more bytes
+ per pixel than A, the write pointer won't overrun the read pointer.
+ */
+ XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
+ ZPixmap, 0, image->data,
+ xgwa.width, xgwa.height,
+ 8, 0);
+ if (!image2)
+ return False;
+
+#ifdef DEBUG
+ fprintf(stderr, "%s: converting from depth %d to depth %d\n",
+ progname, image->depth, xgwa.depth);
+#endif /* DEBUG */
+
+ for (y = 0; y < image->height; y++)
+ for (x = 0; x < image->width; x++)
+ {
+ /* #### really these shift values should be determined from the
+ mask values -- but that's a pain in the ass, and anyway,
+ this is an SGI-specific extension so hardcoding assumptions
+ about the SGI server's behavior isn't *too* heinous... */
+ unsigned long pixel = XGetPixel(image, x, y);
+ unsigned int r = (pixel & image->red_mask);
+ unsigned int g = (pixel & image->green_mask) >> 8;
+ unsigned int b = (pixel & image->blue_mask) >> 16;
+
+ if (xgwa.depth == 8)
+ pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
+ else if (xgwa.depth == 12)
+ pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
+ else if (xgwa.depth == 16)
+ pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
+ else
+ abort();
+
+ XPutPixel(image2, x, y, pixel);
+ }
+ image->data = 0;
+ XDestroyImage(image);
+ image = image2;
+ }
+
+
+ /* Now actually put the bits into the window or pixmap -- note the design
+ bogosity of this extension, where we've been forced to take 24 bit data
+ from the server to the client, and then push it back from the client to
+ the server, *without alteration*. We should have just been able to tell
+ the server, "put a screen image in this drawable", instead of having to
+ go through the intermediate step of converting it to an Image. Geez.
+ (Assuming that the window is of screen depth; we happen to handle less
+ deep windows, but that's beside the point.)
+ */
+ gcv.function = GXcopy;
+ gc = XCreateGC (dpy, window, GCFunction, &gcv);
+
+ if (into_pixmap)
+ {
+ gcv.function = GXcopy;
+ gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
+ XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
+ xgwa.width, xgwa.height);
+ }
+ else
+ {
+ gcv.function = GXcopy;
+ gc = XCreateGC (dpy, window, GCFunction, &gcv);
+
+ /* Ok, now we'll be needing that window on the screen... */
+ raise_window(dpy, window, dont_wait);
+
+ /* Plop down the bits... */
+ XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
+ }
+ XFreeGC (dpy, gc);
+
+ if (image->data)
+ {
+ free(image->data);
+ image->data = 0;
+ }
+ XDestroyImage(image);
+
+ if (install_p)
+ make_cubic_colormap (screen, window, xgwa.visual);
+
+ return True;
+}
+#endif /* HAVE_READ_DISPLAY_EXTENSION */
+
+
+#if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO)
+
+/* Makes and installs a colormap that makes a PseudoColor or DirectColor
+ visual behave like a TrueColor visual of the same depth.
+ */
+static void
+make_cubic_colormap (Screen *screen, Window window, Visual *visual)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll);
+ int nr, ng, nb, cells;
+ int r, g, b;
+ int depth;
+ XColor colors[4097];
+ int i;
+
+ depth = visual_depth(screen, visual);
+ switch (depth)
+ {
+ case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
+ case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
+ default: abort(); break;
+ }
+
+ memset(colors, 0, sizeof(colors));
+ for (i = 0; i < cells; i++)
+ {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ colors[i].red = colors[i].green = colors[i].blue = 0;
+ }
+
+ for (r = 0; r < (1 << nr); r++)
+ for (g = 0; g < (1 << ng); g++)
+ for (b = 0; b < (1 << nb); b++)
+ {
+ i = (r | (g << nr) | (b << (nr + ng)));
+ colors[i].pixel = i;
+ if (depth == 8)
+ {
+ colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
+ (r << 4) | (r << 1));
+ colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
+ (g << 4) | (g << 1));
+ colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
+ (b << 8) | (b << 6) | (b << 4) |
+ (b << 2) | b);
+ }
+ else
+ {
+ colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
+ colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
+ colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
+ }
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "%s: installing cubic colormap\n", progname);
+#endif /* DEBUG */
+
+ XStoreColors (dpy, cmap, colors, cells);
+ XSetWindowColormap (dpy, window, cmap);
+
+ /* Gag, install the colormap.
+ This is definitely right in the `if xscreensaver_window_p' case, since
+ it will never get installed otherwise. But, if we don't do it
+ unconditionally, then the new colormap won't get installed until the
+ window (re-)gains focus. It's generally very antisocial to install
+ the colormap of a non-OverrideRedirect window (that task belongs to
+ the WM) and if we were being kosher, we would only install this cmap
+ if the old cmap was already installed (or perhaps, if the window had
+ focus.) But, since this extension only exists on SGIs, and since SGIs
+ can handle four colormaps at once, let's go ahead and install it all
+ the time, so that even if the window pops up and has never had focus,
+ it will still display in the proper colors.
+ */
+ XInstallColormap (dpy, cmap);
+}
+
+#endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */