X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=utils%2Fgrabscreen.c;h=23f40aad9b98122db39454075c5eafffd79c4106;hb=4ade52359b6eba3621566dac79793a33aa4c915f;hp=328029e33eb2d785753cdf97e33ec5d30fb6ca2a;hpb=585e1a6717d1dd9b90fbb53acaaae82106354d33;p=xscreensaver diff --git a/utils/grabscreen.c b/utils/grabscreen.c index 328029e3..23f40aad 100644 --- a/utils/grabscreen.c +++ b/utils/grabscreen.c @@ -1,5 +1,4 @@ -/* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998 - * Jamie Zawinski +/* xscreensaver, Copyright (c) 1992-2013 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 @@ -15,6 +14,20 @@ the difference between drawing on the actual root window, and on the fake root window used by the screensaver, since at this level the illusion breaks down... + + The hacks themselves use utils/grabclient.c to invoke the + "xscreensaver-getimage" program as a sub-process. + + This code is linked only into "driver/xscreensaver-getimage". On normal + X11 systems, "xscreensaver-getimage.c" invokes the code in this file. + + However, under X11 on MacOS, "xscreensaver-getimage" instead runs the + script "driver/xscreensaver-getimage-desktop", which invokes the MacOS- + specific program "/usr/sbin/screencapture" to get the desktop image. + + However again, for the MacOS-native (Cocoa) build of the screen savers, + "utils/grabclient.c" instead links against "OSX/osxgrabscreen.m", which + grabs screen images directly without invoking a sub-process to do it. */ #include "utils.h" @@ -117,12 +130,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; @@ -131,10 +144,59 @@ 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) @@ -174,17 +236,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; @@ -210,16 +273,23 @@ install_screen_colormaps (Screen *screen) void -grab_screen_image (Screen *screen, Window window) +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; @@ -234,12 +304,12 @@ grab_screen_image (Screen *screen, Window window) double unmap = 0; if (saver_p) { - unmap = get_float_resource("grabRootDelay", "Seconds"); + unmap = get_float_resource(dpy, "grabRootDelay", "Seconds"); if (unmap <= 0.00001 || unmap > 20) unmap = 2.5; } else { - unmap = get_float_resource("grabWindowDelay", "Seconds"); + unmap = get_float_resource(dpy, "grabWindowDelay", "Seconds"); if (unmap <= 0.00001 || unmap > 20) unmap = 0.66; } unmap_time = unmap * 100000; @@ -247,18 +317,26 @@ grab_screen_image (Screen *screen, Window window) if (grab_verbose_p) { - XWindowAttributes xgwa2; 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); - XGetWindowAttributes (dpy, window, &xgwa2); fprintf(stderr, "%s: ", progname); - describe_visual(stderr, screen, xgwa2.visual, False); + 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); @@ -281,8 +359,6 @@ grab_screen_image (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 @@ -308,15 +384,13 @@ grab_screen_image (Screen *screen, Window window) 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; @@ -403,7 +477,7 @@ copy_default_colormap_contents (Screen *screen, XQueryColors (dpy, from_cmap, old_colors, max_cells); got_cells = max_cells; - allocate_writable_colors (dpy, to_cmap, pixels, &got_cells); + allocate_writable_colors (screen, to_cmap, pixels, &got_cells); if (grab_verbose_p && got_cells != max_cells) fprintf(stderr, "%s: got only %d of %d cells\n", progname, @@ -470,24 +544,38 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, /* 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 /* Allocate a TrueColor-like spread of colors for the image. */ remap_p = True; @@ -500,9 +588,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; } @@ -543,7 +637,11 @@ 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; + } if (grab_verbose_p) fprintf(stderr, "%s: converting from depth %d to depth %d\n", @@ -565,8 +663,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(); @@ -631,6 +730,8 @@ read_display (Screen *screen, Window window, Pixmap into_pixmap, /* Makes and installs a colormap that makes a PseudoColor or DirectColor visual behave like a TrueColor visual of the same depth. + + #### Duplicated in driver/xscreensaver-getimage.c */ static void allocate_cubic_colormap (Screen *screen, Window window, Visual *visual) @@ -646,7 +747,7 @@ allocate_cubic_colormap (Screen *screen, Window window, Visual *visual) XGetWindowAttributes (dpy, window, &xgwa); cmap = xgwa.colormap; - depth = visual_depth(screen, visual); + depth = visual_depth (screen, visual); switch (depth) { @@ -698,6 +799,11 @@ allocate_cubic_colormap (Screen *screen, Window window, Visual *visual) } } +/* Find the pixel index that is closest to the given color + (using linear distance in RGB space -- which is far from the best way.) + + #### Duplicated in driver/xscreensaver-getimage.c + */ static unsigned long find_closest_pixel (XColor *colors, int ncolors, unsigned long r, unsigned long g, unsigned long b) @@ -733,6 +839,13 @@ find_closest_pixel (XColor *colors, int ncolors, } +/* Given an XImage with 8-bit or 12-bit RGB data, convert it to be + displayable with the given X colormap. The farther from a perfect + color cube the contents of the colormap are, the lossier the + transformation will be. No dithering is done. + + #### Duplicated in driver/xscreensaver-getimage.c + */ void remap_image (Screen *screen, Window window, Colormap cmap, XImage *image) {