X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=utils%2Fgrabscreen.c;h=cc5b57522de05d06d31e8cee2811fc4eae033797;hp=6bf8cc8b848bdd8e6edd5ce57025a8642a6b44bb;hb=ffd8c0873576a9e3065696a624dce6b766b77062;hpb=481b95e2617b69e6fd4444432747d7e1e0c3dc85 diff --git a/utils/grabscreen.c b/utils/grabscreen.c index 6bf8cc8b..cc5b5752 100644 --- a/utils/grabscreen.c +++ b/utils/grabscreen.c @@ -1,5 +1,5 @@ -/* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998 - * Jamie Zawinski +/* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998, 2003, 2004 + * Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -34,7 +34,6 @@ #include "usleep.h" #include "colors.h" #include "grabscreen.h" -#include "sgivideo.h" #include "visual.h" #include "resources.h" @@ -52,8 +51,9 @@ static void copy_default_colormap_contents (Screen *, Colormap, Visual *); -#if defined(HAVE_READ_DISPLAY_EXTENSION) || defined(HAVE_SGI_VIDEO) -static void make_cubic_colormap (Screen *, Window, Visual *); +#ifdef HAVE_READ_DISPLAY_EXTENSION +static void allocate_cubic_colormap (Screen *, Window, Visual *); +void remap_image (Screen *, Window, Colormap, XImage *); #endif @@ -64,19 +64,23 @@ MapNotify_event_p (Display *dpy, XEvent *event, XPointer window) event->xvisibility.window == (Window) window); } -#ifdef DEBUG extern char *progname; -#endif /* DEBUG */ +Bool grab_verbose_p = False; + +void +grabscreen_verbose(void) +{ + grab_verbose_p = True; +} static void raise_window(Display *dpy, Window window, Bool dont_wait) { -#ifdef DEBUG - fprintf(stderr, "%s: raising window 0x%08lX (%s)\n", - progname, (unsigned long) window, - (dont_wait ? "not waiting" : "waiting")); -#endif /* DEBUG */ + if (grab_verbose_p) + fprintf(stderr, "%s: raising window 0x%08lX (%s)\n", + progname, (unsigned long) window, + (dont_wait ? "not waiting" : "waiting")); if (! dont_wait) { @@ -92,6 +96,8 @@ raise_window(Display *dpy, Window window, Bool dont_wait) hints.height = xgwa.height; hints.flags |= (PPosition|USPosition|PSize|USSize); XSetWMNormalHints(dpy, window, &hints); + + XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask)); } XMapRaised(dpy, window); @@ -111,12 +117,12 @@ xscreensaver_window_p (Display *dpy, Window window) Atom type; int format; unsigned long nitems, bytesafter; - char *version; + unsigned char *version; if (XGetWindowProperty (dpy, window, XInternAtom (dpy, "_SCREENSAVER_VERSION", False), 0, 1, False, XA_STRING, &type, &format, &nitems, &bytesafter, - (unsigned char **) &version) + &version) == Success && type != None) return True; @@ -125,14 +131,66 @@ xscreensaver_window_p (Display *dpy, Window window) +/* Whether the given window is: + - the real root window; + - a direct child of the root window; + - a direct child of the window manager's decorations. + */ +Bool +top_level_window_p (Screen *screen, Window window) +{ + Display *dpy = DisplayOfScreen (screen); + Window root, parent, *kids; + unsigned int nkids; + + if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids)) + return False; + + if (window == root) + return True; + + /* If our direct parent is the real root window, then yes. */ + if (parent == root) + return True; + else + { + Atom type = None; + int format; + unsigned long nitems, bytesafter; + unsigned char *data; + + /* If our direct parent has the WM_STATE property, then it is a + window manager decoration -- yes. + */ + if (XGetWindowProperty (dpy, window, + XInternAtom (dpy, "WM_STATE", True), + 0, 0, False, AnyPropertyType, + &type, &format, &nitems, &bytesafter, + (unsigned char **) &data) + == Success + && type != None) + return True; + } + + /* Else, no. We're deep in a tree somewhere. + */ + return False; +} + + +static Bool error_handler_hit_p = False; static XErrorHandler old_ehandler = 0; static int BadWindow_ehandler (Display *dpy, XErrorEvent *error) { + error_handler_hit_p = True; if (error->error_code == BadWindow || error->error_code == BadDrawable) return 0; else if (!old_ehandler) - abort(); + { + abort(); + return 0; + } else return (*old_ehandler) (dpy, error); } @@ -165,17 +223,18 @@ use_subwindow_mode_p(Screen *screen, Window window) static void install_screen_colormaps (Screen *screen) { - int i; + unsigned int i; Display *dpy = DisplayOfScreen (screen); - Window vroot, real_root; + Window real_root; Window parent, *kids = 0; unsigned int nkids = 0; XSync (dpy, False); old_ehandler = XSetErrorHandler (BadWindow_ehandler); + error_handler_hit_p = False; - vroot = VirtualRootWindowOfScreen (screen); - if (XQueryTree (dpy, vroot, &real_root, &parent, &kids, &nkids)) + real_root = XRootWindowOfScreen (screen); /* not vroot */ + if (XQueryTree (dpy, real_root, &real_root, &parent, &kids, &nkids)) for (i = 0; i < nkids; i++) { XWindowAttributes xgwa; @@ -200,17 +259,24 @@ install_screen_colormaps (Screen *screen) } -static void -grab_screen_image_1 (Screen *screen, Window window) +void +grab_screen_image_internal (Screen *screen, Window window) { Display *dpy = DisplayOfScreen (screen); XWindowAttributes xgwa; - Window real_root = XRootWindowOfScreen (screen); /* not vroot */ - Bool root_p = (window == real_root); - Bool saver_p = xscreensaver_window_p (dpy, window); + Window real_root; + Bool root_p; + Bool saver_p; Bool grab_mouse_p = False; int unmap_time = 0; + real_root = XRootWindowOfScreen (screen); /* not vroot */ + root_p = (window == real_root); + saver_p = xscreensaver_window_p (dpy, window); + + XGetWindowAttributes (dpy, window, &xgwa); + screen = xgwa.screen; + if (saver_p) /* I think this is redundant, but just to be safe... */ root_p = False; @@ -236,19 +302,27 @@ grab_screen_image_1 (Screen *screen, Window window) unmap_time = unmap * 100000; } -#ifdef DEBUG - fprintf(stderr, - "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n", - progname, (unsigned long) window, - root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0); - { - XWindowAttributes xgwa2; - XGetWindowAttributes (dpy, window, &xgwa2); - fprintf(stderr, "%s: ", progname); - describe_visual(stderr, screen, xgwa2.visual, ####); - fprintf (stderr, "\n"); - } -#endif /* DEBUG */ + if (grab_verbose_p) + { + fprintf(stderr, + "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n", + progname, (unsigned long) window, + root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0); + + fprintf(stderr, "%s: ", progname); + describe_visual(stderr, screen, xgwa.visual, False); + fprintf (stderr, "\n"); + } + + + if (!root_p && !top_level_window_p (screen, window)) + { + if (grab_verbose_p) + fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n", + progname, (unsigned long) window); + return; + } + if (!root_p) XSetWindowBackgroundPixmap (dpy, window, None); @@ -272,39 +346,45 @@ grab_screen_image_1 (Screen *screen, Window window) usleep(unmap_time); /* wait for everyone to swap in and handle exposes */ } - XGetWindowAttributes (dpy, window, &xgwa); - if (!root_p) { #ifdef HAVE_READ_DISPLAY_EXTENSION if (! read_display(screen, window, 0, saver_p)) #endif /* HAVE_READ_DISPLAY_EXTENSION */ { -#if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG) - fprintf(stderr, "%s: read_display() failed\n", progname); -#endif /* DEBUG */ +#ifdef HAVE_READ_DISPLAY_EXTENSION + if (grab_verbose_p) + fprintf(stderr, "%s: read_display() failed\n", progname); +#endif /* HAVE_READ_DISPLAY_EXTENSION */ + copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual); raise_window(dpy, window, saver_p); + + /* Generally it's bad news to call XInstallColormap() explicitly, + but this file does a lot of sleazy stuff already... This is to + make sure that the window's colormap is installed, even in the + case where the window is OverrideRedirect. */ + if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap); + XSync (dpy, False); } } else /* root_p */ { Pixmap pixmap; - XWindowAttributes xgwa; - XGetWindowAttributes(dpy, window, &xgwa); pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth); #ifdef HAVE_READ_DISPLAY_EXTENSION if (! read_display(screen, window, pixmap, True)) #endif { - Window real_root = XRootWindowOfScreen (xgwa.screen); /* not vroot */ + Window real_root = XRootWindowOfScreen (screen); /* not vroot */ XGCValues gcv; GC gc; -#if defined(HAVE_READ_DISPLAY_EXTENSION) && defined(DEBUG) - fprintf(stderr, "%s: read_display() failed\n", progname); -#endif /* DEBUG */ +#ifdef HAVE_READ_DISPLAY_EXTENSION + if (grab_verbose_p) + fprintf(stderr, "%s: read_display() failed\n", progname); +#endif /* HAVE_READ_DISPLAY_EXTENSION */ copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual); @@ -319,6 +399,11 @@ grab_screen_image_1 (Screen *screen, Window window) XFreePixmap (dpy, pixmap); } + if (grab_verbose_p) + fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n", + progname, xgwa.depth, + (root_p ? "real root " : "")); + if (grab_mouse_p) { XUngrabPointer (dpy, CurrentTime); @@ -328,42 +413,6 @@ grab_screen_image_1 (Screen *screen, Window window) XSync (dpy, True); } -void -grab_screen_image (Screen *screen, Window window) -{ -#ifdef HAVE_SGI_VIDEO - char c, *s = get_string_resource("grabVideoProbability", "Float"); - double prob = -1; - if (!s || - (1 != sscanf (s, " %lf %c", &prob, &c)) || - prob < 0 || - prob > 1) - prob = 0.5; - - if ((random() % 100) < ((int) (100 * prob))) - { - XWindowAttributes xgwa; - Display *dpy = DisplayOfScreen (screen); - XGetWindowAttributes (dpy, window, &xgwa); -# ifdef DEBUG - fprintf(stderr, "%s: trying to grab from video...\n", progname); -# endif /* DEBUG */ - if (grab_video_frame (screen, xgwa.visual, window)) - { - if (xgwa.depth < 24) - { - int class = visual_class (screen, xgwa.visual); - if (class == PseudoColor || class == DirectColor) - make_cubic_colormap (screen, window, xgwa.visual); - } - return; - } - } -#endif /* HAVE_SGI_VIDEO */ - - grab_screen_image_1 (screen, window); -} - /* When we are grabbing and manipulating a screen image, it's important that we use the same colormap it originally had. So, if the screensaver was @@ -417,11 +466,9 @@ copy_default_colormap_contents (Screen *screen, got_cells = max_cells; allocate_writable_colors (dpy, to_cmap, pixels, &got_cells); -#ifdef DEBUG - if (got_cells != max_cells) + if (grab_verbose_p && got_cells != max_cells) fprintf(stderr, "%s: got only %d of %d cells\n", progname, got_cells, max_cells); -#endif /* DEBUG */ if (got_cells <= 0) /* we're screwed */ ; @@ -446,9 +493,8 @@ copy_default_colormap_contents (Screen *screen, } -#ifdef DEBUG - fprintf(stderr, "%s: installing copy of default colormap\n", progname); -#endif /* DEBUG */ + if (grab_verbose_p) + fprintf(stderr, "%s: installing copy of default colormap\n", progname); free (old_colors); free (new_colors); @@ -480,33 +526,46 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, XGCValues gcv; int class; GC gc; - Bool install_p = False; + Bool remap_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 (grab_verbose_p) + fprintf(stderr, "%s: no XReadDisplay extension\n", progname); + 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; + = TrueColor in depths 8, 12, 15, 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; + if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 && + xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32) + { + if (grab_verbose_p) + fprintf(stderr, "%s: TrueColor depth %d unsupported\n", + progname, xgwa.depth); + return False; + } } else if (class == PseudoColor || class == DirectColor) { if (xgwa.depth != 8 && xgwa.depth != 12) - return False; + { + if (grab_verbose_p) + fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n", + progname, xgwa.depth); + return False; + } else - /* Install a colormap that makes this visual behave like - a TrueColor visual of the same depth. */ - install_p = True; + /* Allocate a TrueColor-like spread of colors for the image. */ + remap_p = True; } @@ -516,9 +575,15 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height, hints, &hints); if (!image) - return False; + { + if (grab_verbose_p) + fprintf(stderr, "%s: XReadDisplay() failed\n", progname); + return False; + } if (!image->data) { + if (grab_verbose_p) + fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname); XDestroyImage(image); return False; } @@ -559,12 +624,15 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, xgwa.width, xgwa.height, 8, 0); if (!image2) - return False; + { + if (grab_verbose_p) + fprintf(stderr, "%s: out of memory?\n", progname); + return False; + } -#ifdef DEBUG - fprintf(stderr, "%s: converting from depth %d to depth %d\n", - progname, image->depth, xgwa.depth); -#endif /* DEBUG */ + if (grab_verbose_p) + fprintf(stderr, "%s: converting from depth %d to depth %d\n", + progname, image->depth, xgwa.depth); for (y = 0; y < image->height; y++) for (x = 0; x < image->width; x++) @@ -582,8 +650,9 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, 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 if (xgwa.depth == 16 || xgwa.depth == 15) + /* Gah! I don't understand why these are in the other order. */ + pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3))); else abort(); @@ -594,6 +663,11 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, image = image2; } + if (remap_p) + { + allocate_cubic_colormap (screen, window, xgwa.visual); + remap_image (screen, window, xgwa.colormap, image); + } /* 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 @@ -634,31 +708,32 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, } 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) +#ifdef HAVE_READ_DISPLAY_EXTENSION /* 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) +allocate_cubic_colormap (Screen *screen, Window window, Visual *visual) { Display *dpy = DisplayOfScreen (screen); - Colormap cmap = XCreateColormap(dpy, window, visual, AllocAll); + XWindowAttributes xgwa; + Colormap cmap; int nr, ng, nb, cells; int r, g, b; int depth; XColor colors[4097]; int i; + XGetWindowAttributes (dpy, window, &xgwa); + cmap = xgwa.colormap; depth = visual_depth(screen, visual); + switch (depth) { case 8: nr = 3; ng = 3; nb = 2; cells = 256; break; @@ -667,18 +742,13 @@ make_cubic_colormap (Screen *screen, Window window, Visual *visual) } 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; + colors[i].flags = DoRed|DoGreen|DoBlue; if (depth == 8) { colors[i].red = ((r << 13) | (r << 10) | (r << 7) | @@ -697,27 +767,132 @@ make_cubic_colormap (Screen *screen, Window window, Visual *visual) } } -#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); + { + int j; + int allocated = 0; + int interleave = cells / 8; /* skip around, rather than allocating in + order, so that we get better coverage if + we can't allocated all of them. */ + for (j = 0; j < interleave; j++) + for (i = 0; i < cells; i += interleave) + if (XAllocColor (dpy, cmap, &colors[i + j])) + allocated++; + + if (grab_verbose_p) + fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n", + progname, allocated, cells); + } } -#endif /* HAVE_READ_DISPLAY_EXTENSION || HAVE_SGI_VIDEO */ +static unsigned long +find_closest_pixel (XColor *colors, int ncolors, + unsigned long r, unsigned long g, unsigned long b) +{ + unsigned long distance = ~0; + int i, found = 0; + + if (ncolors == 0) + abort(); + for (i = 0; i < ncolors; i++) + { + unsigned long d; + int rd, gd, bd; + + rd = r - colors[i].red; + gd = g - colors[i].green; + bd = b - colors[i].blue; + if (rd < 0) rd = -rd; + if (gd < 0) gd = -gd; + if (bd < 0) bd = -bd; + d = (rd << 1) + (gd << 2) + bd; + + if (d < distance) + { + distance = d; + found = i; + if (distance == 0) + break; + } + } + + return found; +} + + +void +remap_image (Screen *screen, Window window, Colormap cmap, XImage *image) +{ + Display *dpy = DisplayOfScreen (screen); + unsigned long map[4097]; + int x, y, i; + int cells; + XColor colors[4097]; + + if (image->depth == 8) + cells = 256; + else if (image->depth == 12) + cells = 4096; + else + abort(); + + memset(map, -1, sizeof(*map)); + memset(colors, -1, sizeof(*colors)); + + for (i = 0; i < cells; i++) + colors[i].pixel = i; + XQueryColors (dpy, cmap, colors, cells); + + if (grab_verbose_p) + fprintf(stderr, "%s: building table for %d bit image\n", + progname, image->depth); + + for (i = 0; i < cells; i++) + { + unsigned short r, g, b; + + if (cells == 256) + { + /* "RRR GGG BB" In an 8 bit map. Convert that to + "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give + an even spread. */ + r = (i & 0x07); + g = (i & 0x38) >> 3; + b = (i & 0xC0) >> 6; + + r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1)); + g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1)); + b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) | + (b << 6) | (b << 4) | (b << 2) | b); + } + else + { + /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to + "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even + spread. */ + r = (i & 0x00F); + g = (i & 0x0F0) >> 4; + b = (i & 0xF00) >> 8; + + r = (r << 12) | (r << 8) | (r << 4) | r; + g = (g << 12) | (g << 8) | (g << 4) | g; + b = (b << 12) | (b << 8) | (b << 4) | b; + } + + map[i] = find_closest_pixel (colors, cells, r, g, b); + } + + if (grab_verbose_p) + fprintf(stderr, "%s: remapping colors in %d bit image\n", + progname, image->depth); + + for (y = 0; y < image->height; y++) + for (x = 0; x < image->width; x++) + { + unsigned long pixel = XGetPixel(image, x, y); + if (pixel >= cells) abort(); + XPutPixel(image, x, y, map[pixel]); + } +} + + +#endif /* HAVE_READ_DISPLAY_EXTENSION */