X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Fxscreensaver-getimage.c;h=68637a7be4e2fb8662d035a293247ea48ef11993;hp=47247686b428a9508bcc1e3573153b1d22a492c5;hb=07faf451b99879183ed7e909e43a0e065be1ee7f;hpb=6cee540bdbb571485cd5e519f89f389faebd0495 diff --git a/driver/xscreensaver-getimage.c b/driver/xscreensaver-getimage.c index 47247686..68637a7b 100644 --- a/driver/xscreensaver-getimage.c +++ b/driver/xscreensaver-getimage.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2001, 2002, 2003 by Jamie Zawinski +/* xscreensaver, Copyright (c) 2001-2006 by 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 @@ -46,6 +46,10 @@ #include "version.h" #include "vroot.h" +#ifndef _XSCREENSAVER_VROOT_H_ +# error Error! You have an old version of vroot.h! Check -I args. +#endif /* _XSCREENSAVER_VROOT_H_ */ + #ifdef HAVE_GDK_PIXBUF # undef HAVE_JPEGLIB # ifdef HAVE_GTK2 @@ -61,6 +65,19 @@ #endif +#ifdef __APPLE__ + /* On MacOSX / XDarwin, the usual X11 mechanism of getting a screen shot + doesn't work, and we need to use an external program. */ +# define USE_EXTERNAL_SCREEN_GRABBER +#endif + + +#ifdef __GNUC__ + __extension__ /* shut up about "string length is greater than the length + ISO C89 compilers are required to support" when including + the .ad file... */ +#endif + static char *defaults[] = { #include "../driver/XScreenSaver_ad.h" 0 @@ -75,9 +92,16 @@ XtAppContext app; extern void grabscreen_verbose (void); +typedef enum { + GRAB_DESK, GRAB_VIDEO, GRAB_FILE, GRAB_BARS +} grab_type; -#define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video" -#define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file" + +#define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video" +#define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file" +#define GETIMAGE_SCREEN_PROGRAM "xscreensaver-getimage-desktop" + +extern const char *blurb (void); const char * blurb (void) @@ -89,8 +113,17 @@ blurb (void) static int x_ehandler (Display *dpy, XErrorEvent *error) { - fprintf (stderr, "\nX error in %s:\n", progname); - XmuPrintDefaultErrorMessage (dpy, error, stderr); + if (error->error_code == BadWindow || error->error_code == BadDrawable) + { + fprintf (stderr, "%s: target %s 0x%lx unexpectedly deleted\n", progname, + (error->error_code == BadWindow ? "window" : "pixmap"), + (unsigned long) error->resourceid); + } + else + { + fprintf (stderr, "\nX error in %s:\n", progname); + XmuPrintDefaultErrorMessage (dpy, error, stderr); + } exit (-1); return 0; } @@ -105,6 +138,17 @@ ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) return 0; } +#ifndef USE_EXTERNAL_SCREEN_GRABBER +static int +ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error) +{ + if (error->error_code == BadMatch) + return ignore_all_errors_ehandler (dpy, error); + else + return x_ehandler (dpy, error); +} +#endif /* ! USE_EXTERNAL_SCREEN_GRABBER */ + /* Returns True if the given Drawable is a Window; False if it's a Pixmap. */ @@ -129,6 +173,35 @@ drawable_window_p (Display *dpy, Drawable d) } +/* Returns true if the window is the root window, or a virtual root window, + but *not* the xscreensaver window. That is, if it's a "real" desktop + root window of some kind. + */ +static Bool +root_window_p (Screen *screen, Window window) +{ + Display *dpy = DisplayOfScreen (screen); + Atom type; + int format; + unsigned long nitems, bytesafter; + unsigned char *version; + + if (window != RootWindowOfScreen (screen)) + return False; + + if (XGetWindowProperty (dpy, window, + XInternAtom (dpy, "_SCREENSAVER_VERSION", False), + 0, 1, False, XA_STRING, + &type, &format, &nitems, &bytesafter, + &version) + == Success + && type != None) + return False; + + return True; +} + + /* Clear the window or pixmap to black, or its background color. */ static void @@ -184,7 +257,10 @@ compute_image_scaling (int src_w, int src_h, int th = src_h * r; int pct = (r * 100); +#if 0 + /* this optimization breaks things */ if (pct < 95 || pct > 105) /* don't scale if it's close */ +#endif { if (verbose_p) fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n", @@ -213,9 +289,65 @@ compute_image_scaling (int src_w, int src_h, *scaled_to_y_ret = desty; if (verbose_p) - fprintf (stderr, "%s: displaying %dx%d image at %d,%d.\n", - progname, src_w, src_h, destx, desty); + fprintf (stderr, "%s: displaying %dx%d image at %d,%d in %dx%d.\n", + progname, src_w, src_h, destx, desty, dest_w, dest_h); +} + + +/* Scales an XImage, modifying it in place. + This doesn't do dithering or smoothing, so it might have artifacts. + If out of memory, returns False, and the XImage will have been + destroyed and freed. + */ +#if !defined(USE_EXTERNAL_SCREEN_GRABBER) || defined(HAVE_JPEGLIB) +static Bool +scale_ximage (Screen *screen, Visual *visual, + XImage *ximage, int new_width, int new_height) +{ + Display *dpy = DisplayOfScreen (screen); + int depth = visual_depth (screen, visual); + int x, y; + double xscale, yscale; + + XImage *ximage2 = XCreateImage (dpy, visual, depth, + ZPixmap, 0, 0, + new_width, new_height, 8, 0); + ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line); + + if (!ximage2->data) + { + fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n", + progname, + ximage->width, ximage->height, + ximage2->width, ximage2->height); + if (ximage->data) free (ximage->data); + if (ximage2->data) free (ximage2->data); + ximage->data = 0; + ximage2->data = 0; + XDestroyImage (ximage); + XDestroyImage (ximage2); + return False; + } + + /* Brute force scaling... */ + xscale = (double) ximage->width / ximage2->width; + yscale = (double) ximage->height / ximage2->height; + for (y = 0; y < ximage2->height; y++) + for (x = 0; x < ximage2->width; x++) + XPutPixel (ximage2, x, y, + XGetPixel (ximage, x * xscale, y * yscale)); + + free (ximage->data); + ximage->data = 0; + + (*ximage) = (*ximage2); + + ximage2->data = 0; + XDestroyImage (ximage2); + + return True; } +#endif /* !USE_EXTERNAL_SCREEN_GRABBER || HAVE_JPEGLIB */ #ifdef HAVE_GDK_PIXBUF @@ -225,24 +357,23 @@ compute_image_scaling (int src_w, int src_h, */ static Bool read_file_gdk (Screen *screen, Window window, Drawable drawable, - const char *filename, Bool verbose_p) + const char *filename, Bool verbose_p, + XRectangle *geom_ret) { GdkPixbuf *pb; Display *dpy = DisplayOfScreen (screen); - unsigned int win_width, win_height; + unsigned int win_width, win_height, win_depth; # ifdef HAVE_GTK2 GError *gerr = 0; # endif /* HAVE_GTK2 */ + /* Find the size of the Drawable. */ { Window root; int x, y; - unsigned int bw, d; - XWindowAttributes xgwa; - XGetWindowAttributes (dpy, window, &xgwa); - screen = xgwa.screen; + unsigned int bw; XGetGeometry (dpy, drawable, - &root, &x, &y, &win_width, &win_height, &bw, &d); + &root, &x, &y, &win_width, &win_height, &bw, &win_depth); } gdk_pixbuf_xlib_init (dpy, screen_number (screen)); @@ -272,6 +403,7 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, int w = gdk_pixbuf_get_width (pb); int h = gdk_pixbuf_get_height (pb); int srcx, srcy, destx, desty, w2, h2; + Bool bg_p = False; compute_image_scaling (w, h, win_width, win_height, verbose_p, &srcx, &srcy, &destx, &desty, &w2, &h2); @@ -290,7 +422,25 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, fprintf (stderr, "%s: out of memory when scaling?\n", progname); } - clear_drawable (screen, drawable); + /* If we're rendering onto the root window (and it's not the + xscreensaver pseudo-root) then put the image in the window's + background. Otherwise, just paint the image onto the window. + */ + bg_p = (window == drawable && root_window_p (screen, window)); + + if (bg_p) + { + XGCValues gcv; + GC gc; + drawable = XCreatePixmap (dpy, window, + win_width, win_height, win_depth); + gcv.foreground = BlackPixelOfScreen (screen); + gc = XCreateGC (dpy, drawable, GCForeground, &gcv); + XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height); + XFreeGC (dpy, gc); + } + else + clear_drawable (screen, drawable); /* #### Note that this always uses the default colormap! Morons! Owen says that in Gnome 2.0, I should try using @@ -303,9 +453,22 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, GDK_PIXBUF_ALPHA_FULL, 127, XLIB_RGB_DITHER_NORMAL, 0, 0); - XSync (dpy, False); + if (bg_p) + { + XSetWindowBackgroundPixmap (dpy, window, drawable); + XClearWindow (dpy, window); + } + + if (geom_ret) + { + geom_ret->x = destx; + geom_ret->y = desty; + geom_ret->width = w; + geom_ret->height = h; + } } + XSync (dpy, False); return True; } @@ -575,8 +738,7 @@ maybe_read_ppm (Screen *screen, Visual *visual, ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0, w, h, 8, 0); if (ximage) - ximage->data = (unsigned char *) - calloc (ximage->height, ximage->bytes_per_line); + ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line); if (!ximage || !ximage->data) { fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n", @@ -722,8 +884,7 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable, cinfo.output_width, cinfo.output_height, 8, 0); if (ximage) - ximage->data = (unsigned char *) - calloc (ximage->height, ximage->bytes_per_line); + ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line); if (ximage && ximage->data) scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, @@ -748,7 +909,7 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable, int x; for (x = 0; x < ximage->width; x++) { - int j = x * cinfo.num_components; + int j = x * cinfo.output_components; unsigned char r = scanbuf[i][j]; unsigned char g = scanbuf[i][j+1]; unsigned char b = scanbuf[i][j+2]; @@ -760,10 +921,12 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable, pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6)); else if (depth == 12) pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8)); - else if (depth == 16 || depth == 15) + else if (depth == 15) /* Gah! I don't understand why these are in the other order. */ pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3))); + else if (depth == 16) + pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3))); else abort(); @@ -798,66 +961,13 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable, } -/* Scales an XImage, modifying it in place. - If out of memory, returns False, and the XImage will have been - destroyed and freed. - */ -static Bool -scale_ximage (Screen *screen, Visual *visual, - XImage *ximage, int new_width, int new_height) -{ - Display *dpy = DisplayOfScreen (screen); - int depth = visual_depth (screen, visual); - int x, y; - double xscale, yscale; - - XImage *ximage2 = XCreateImage (dpy, visual, depth, - ZPixmap, 0, 0, - new_width, new_height, 8, 0); - ximage2->data = (unsigned char *) - calloc (ximage2->height, ximage2->bytes_per_line); - - if (!ximage2->data) - { - fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n", - progname, - ximage->width, ximage->height, - ximage2->width, ximage2->height); - if (ximage->data) free (ximage->data); - if (ximage2->data) free (ximage2->data); - ximage->data = 0; - ximage2->data = 0; - XDestroyImage (ximage); - XDestroyImage (ximage2); - return False; - } - - /* Brute force scaling... */ - xscale = (double) ximage->width / ximage2->width; - yscale = (double) ximage->height / ximage2->height; - for (y = 0; y < ximage2->height; y++) - for (x = 0; x < ximage2->width; x++) - XPutPixel (ximage2, x, y, - XGetPixel (ximage, x * xscale, y * yscale)); - - free (ximage->data); - ximage->data = 0; - - (*ximage) = (*ximage2); - - ximage2->data = 0; - XDestroyImage (ximage2); - - return True; -} - - /* Reads the given image file and renders it on the Drawable, using JPEG lib. Returns False if it fails. */ static Bool read_file_jpeglib (Screen *screen, Window window, Drawable drawable, - const char *filename, Bool verbose_p) + const char *filename, Bool verbose_p, + XRectangle *geom_ret) { Display *dpy = DisplayOfScreen (screen); XImage *ximage; @@ -867,17 +977,15 @@ read_file_jpeglib (Screen *screen, Window window, Drawable drawable, unsigned int win_width, win_height, win_depth; int srcx, srcy, destx, desty, w2, h2; + /* Find the size of the Drawable, and the Visual/Colormap of the Window. */ { Window root; int x, y; unsigned int bw; XWindowAttributes xgwa; - XGetWindowAttributes (dpy, window, &xgwa); - screen = xgwa.screen; visual = xgwa.visual; cmap = xgwa.colormap; - XGetGeometry (dpy, drawable, &root, &x, &y, &win_width, &win_height, &bw, &win_depth); } @@ -919,16 +1027,45 @@ read_file_jpeglib (Screen *screen, Window window, Drawable drawable, /* Finally, put the resized image on the window. */ - clear_drawable (screen, drawable); { GC gc; XGCValues gcv; - gc = XCreateGC (dpy, drawable, 0, &gcv); - XPutImage (dpy, drawable, gc, ximage, - srcx, srcy, destx, desty, ximage->width, ximage->height); + + /* If we're rendering onto the root window (and it's not the xscreensaver + pseudo-root) then put the image in the window's background. Otherwise, + just paint the image onto the window. + */ + if (window == drawable && root_window_p (screen, window)) + { + Pixmap bg = XCreatePixmap (dpy, window, + win_width, win_height, win_depth); + gcv.foreground = BlackPixelOfScreen (screen); + gc = XCreateGC (dpy, drawable, GCForeground, &gcv); + XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height); + XPutImage (dpy, bg, gc, ximage, + srcx, srcy, destx, desty, ximage->width, ximage->height); + XSetWindowBackgroundPixmap (dpy, window, bg); + XClearWindow (dpy, window); + } + else + { + gc = XCreateGC (dpy, drawable, 0, &gcv); + clear_drawable (screen, drawable); + XPutImage (dpy, drawable, gc, ximage, + srcx, srcy, destx, desty, ximage->width, ximage->height); + } + XFreeGC (dpy, gc); } + if (geom_ret) + { + geom_ret->x = destx; + geom_ret->y = desty; + geom_ret->width = ximage->width; + geom_ret->height = ximage->height; + } + free (ximage->data); ximage->data = 0; XDestroyImage (ximage); @@ -944,16 +1081,18 @@ read_file_jpeglib (Screen *screen, Window window, Drawable drawable, */ static Bool display_file (Screen *screen, Window window, Drawable drawable, - const char *filename, Bool verbose_p) + const char *filename, Bool verbose_p, + XRectangle *geom_ret) { if (verbose_p) fprintf (stderr, "%s: loading \"%s\"\n", progname, filename); # if defined(HAVE_GDK_PIXBUF) - if (read_file_gdk (screen, window, drawable, filename, verbose_p)) + if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret)) return True; # elif defined(HAVE_JPEGLIB) - if (read_file_jpeglib (screen, window, drawable, filename, verbose_p)) + if (read_file_jpeglib (screen, window, drawable, filename, verbose_p, + geom_ret)) return True; # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */ /* shouldn't get here if we have no image-loading methods available. */ @@ -965,11 +1104,11 @@ display_file (Screen *screen, Window window, Drawable drawable, /* Invokes a sub-process and returns its output (presumably, a file to - load.) Free the string when done. video_p controls which program + load.) Free the string when done. 'grab_type' controls which program to run. */ static char * -get_filename_1 (Screen *screen, const char *directory, Bool video_p, +get_filename_1 (Screen *screen, const char *directory, grab_type type, Bool verbose_p) { Display *dpy = DisplayOfScreen (screen); @@ -980,20 +1119,34 @@ get_filename_1 (Screen *screen, const char *directory, Bool video_p, char *av[20]; int ac = 0; - if (!video_p) + switch (type) { + case GRAB_FILE: av[ac++] = GETIMAGE_FILE_PROGRAM; if (verbose_p) av[ac++] = "--verbose"; av[ac++] = "--name"; av[ac++] = (char *) directory; - } - else - { + break; + + case GRAB_VIDEO: av[ac++] = GETIMAGE_VIDEO_PROGRAM; if (verbose_p) av[ac++] = "--verbose"; av[ac++] = "--name"; + break; + +# ifdef USE_EXTERNAL_SCREEN_GRABBER + case GRAB_DESK: + av[ac++] = GETIMAGE_SCREEN_PROGRAM; + if (verbose_p) + av[ac++] = "--verbose"; + av[ac++] = "--name"; + break; +# endif + + default: + abort(); } av[ac] = 0; @@ -1050,7 +1203,8 @@ get_filename_1 (Screen *screen, const char *directory, Bool video_p, close (out); /* don't need this one */ *buf = 0; - fgets (buf, sizeof(buf)-1, f); + if (! fgets (buf, sizeof(buf)-1, f)) + *buf = 0; fclose (f); /* Wait for the child to die. */ @@ -1082,7 +1236,7 @@ get_filename_1 (Screen *screen, const char *directory, Bool video_p, static char * get_filename (Screen *screen, const char *directory, Bool verbose_p) { - return get_filename_1 (screen, directory, False, verbose_p); + return get_filename_1 (screen, directory, GRAB_FILE, verbose_p); } @@ -1092,16 +1246,27 @@ get_filename (Screen *screen, const char *directory, Bool verbose_p) static char * get_video_filename (Screen *screen, Bool verbose_p) { - return get_filename_1 (screen, 0, True, verbose_p); + return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p); } +/* Grabs a desktop image to a file, and returns a pathname to that file. + Delete that file when you are done with it (and free the string.) + */ +# ifdef USE_EXTERNAL_SCREEN_GRABBER +static char * +get_desktop_filename (Screen *screen, Bool verbose_p) +{ + return get_filename_1 (screen, 0, GRAB_DESK, verbose_p); +} +#endif /* USE_EXTERNAL_SCREEN_GRABBER */ + /* Grabs a video frame, and renders it on the Drawable. Returns False if it fails; */ static Bool display_video (Screen *screen, Window window, Drawable drawable, - Bool verbose_p) + Bool verbose_p, XRectangle *geom_ret) { char *filename = get_video_filename (screen, verbose_p); Bool status; @@ -1113,7 +1278,68 @@ display_video (Screen *screen, Window window, Drawable drawable, return False; } - status = display_file (screen, window, drawable, filename, verbose_p); + status = display_file (screen, window, drawable, filename, verbose_p, + geom_ret); + + if (unlink (filename)) + { + char buf[512]; + sprintf (buf, "%s: rm %.100s", progname, filename); + perror (buf); + } + else if (verbose_p) + fprintf (stderr, "%s: rm %s\n", progname, filename); + + if (filename) free (filename); + return status; +} + + +/* Grabs a desktop screen shot onto the window and the drawable. + If the window and drawable are not the same size, the image in + the drawable is scaled to fit. + Returns False if it fails. + */ +static Bool +display_desktop (Screen *screen, Window window, Drawable drawable, + Bool verbose_p, XRectangle *geom_ret) +{ +# ifdef USE_EXTERNAL_SCREEN_GRABBER + + Display *dpy = DisplayOfScreen (screen); + Bool top_p = top_level_window_p (screen, window); + char *filename; + Bool status; + + if (top_p) + { + if (verbose_p) + fprintf (stderr, "%s: unmapping 0x%lx.\n", progname, + (unsigned long) window); + XUnmapWindow (dpy, window); + XSync (dpy, False); + } + + filename = get_desktop_filename (screen, verbose_p); + + if (top_p) + { + if (verbose_p) + fprintf (stderr, "%s: mapping 0x%lx.\n", progname, + (unsigned long) window); + XMapRaised (dpy, window); + XSync (dpy, False); + } + + if (!filename) + { + if (verbose_p) + fprintf (stderr, "%s: desktop grab failed.\n", progname); + return False; + } + + status = display_file (screen, window, drawable, filename, verbose_p, + geom_ret); if (unlink (filename)) { @@ -1126,6 +1352,106 @@ display_video (Screen *screen, Window window, Drawable drawable, if (filename) free (filename); return status; + +# else /* !USE_EXTERNAL_SCREEN_GRABBER */ + + Display *dpy = DisplayOfScreen (screen); + XGCValues gcv; + XWindowAttributes xgwa; + Window root; + int px, py; + unsigned int pw, ph, pbw, pd; + int srcx, srcy, destx, desty, w2, h2; + + if (verbose_p) + { + fprintf (stderr, "%s: grabbing desktop image\n", progname); + grabscreen_verbose(); + } + + XGetWindowAttributes (dpy, window, &xgwa); + XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd); + + grab_screen_image_internal (screen, window); + + compute_image_scaling (xgwa.width, xgwa.height, + pw, ph, verbose_p, + &srcx, &srcy, &destx, &desty, &w2, &h2); + + if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */ + { + GC gc = XCreateGC (dpy, drawable, 0, &gcv); + XCopyArea (dpy, window, drawable, gc, + 0, 0, xgwa.width, xgwa.height, 0, 0); + XFreeGC (dpy, gc); + } + else /* size mismatch -- must scale client-side images to fit drawable */ + { + GC gc; + XImage *ximage = 0; + XErrorHandler old_handler; + + XSync (dpy, False); + old_handler = XSetErrorHandler (ignore_badmatch_ehandler); + error_handler_hit_p = False; + + /* This can return BadMatch if the window is not fully on screen. + Trap that error and return color bars in that case. + (Note that this only happens with XGetImage, not with XCopyArea: + yet another totally gratuitous inconsistency in X, thanks.) + */ + ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height, + ~0L, ZPixmap); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + if (error_handler_hit_p) + { + ximage = 0; + if (verbose_p) + fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n", + progname, (unsigned int) window); + } + + if (!ximage || + !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2)) + return False; + + gc = XCreateGC (dpy, drawable, 0, &gcv); + clear_drawable (screen, drawable); + XPutImage (dpy, drawable, gc, ximage, + srcx, srcy, destx, desty, ximage->width, ximage->height); + XDestroyImage (ximage); + XFreeGC (dpy, gc); + } + + if (geom_ret) + { + geom_ret->x = destx; + geom_ret->y = desty; + geom_ret->width = w2; + geom_ret->height = h2; + } + + XSync (dpy, False); + return True; + +# endif /* !USE_EXTERNAL_SCREEN_GRABBER */ +} + + +/* Whether the given Drawable is unreasonably small. + */ +static Bool +drawable_miniscule_p (Display *dpy, Drawable drawable) +{ + Window root; + int xx, yy; + unsigned int bw, d, w = 0, h = 0; + XGetGeometry (dpy, drawable, &root, &xx, &yy, &w, &h, &bw, &d); + return (w < 32 || h < 32); } @@ -1144,9 +1470,10 @@ get_image (Screen *screen, const char *file) { Display *dpy = DisplayOfScreen (screen); - enum { do_desk, do_video, do_image, do_bars } which = do_bars; - int count = 0; + grab_type which = GRAB_BARS; struct stat st; + const char *file_prop = 0; + XRectangle geom = { 0, 0, 0, 0 }; if (! drawable_window_p (dpy, window)) { @@ -1155,6 +1482,13 @@ get_image (Screen *screen, exit (1); } + /* Make sure the Screen and the Window correspond. */ + { + XWindowAttributes xgwa; + XGetWindowAttributes (dpy, window, &xgwa); + screen = xgwa.screen; + } + if (file && stat (file, &st)) { fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file); @@ -1175,6 +1509,10 @@ get_image (Screen *screen, # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB)) image_p = False; /* can't load images from files... */ +# ifdef USE_EXTERNAL_SCREEN_GRABBER + desk_p = False; /* ...or from desktops grabbed to files. */ +# endif + if (file) { fprintf (stderr, @@ -1191,8 +1529,7 @@ get_image (Screen *screen, video_p = False; image_p = True; } - - if (!dir || !*dir) + else if (!dir || !*dir) { if (verbose_p && image_p) fprintf (stderr, @@ -1201,18 +1538,29 @@ get_image (Screen *screen, image_p = False; } + /* If the target drawable is really small, no good can come of that. + Always do colorbars in that case. + */ + if (drawable_miniscule_p (dpy, drawable)) + { + desk_p = False; + video_p = False; + image_p = False; + } # ifndef _VROOT_H_ # error Error! This file definitely needs vroot.h! # endif - /* We can grab desktop images if: + /* We can grab desktop images (using the normal X11 method) if: - the window is the real root window; - - the window is the virtal root window; - the window is a toplevel window. - We cannot grab desktop images if: + We cannot grab desktop images that way if: - the window is a non-top-level window. + + Using the MacOS X way, desktops are just like loaded image files. */ +# ifndef USE_EXTERNAL_SCREEN_GRABBER if (desk_p) { if (!top_level_window_p (screen, window)) @@ -1224,36 +1572,41 @@ get_image (Screen *screen, progname, (unsigned int) window); } } +# endif /* !USE_EXTERNAL_SCREEN_GRABBER */ - count = 0; - if (desk_p) count++; - if (video_p) count++; - if (image_p) count++; - - if (count == 0) - which = do_bars; + if (! (desk_p || video_p || image_p)) + which = GRAB_BARS; else { int i = 0; - while (1) /* loop until we get one that's permitted */ - { - which = (random() % 3); - if (which == do_desk && desk_p) break; - if (which == do_video && video_p) break; - if (which == do_image && image_p) break; - if (++i > 200) abort(); - } + int n; + /* Loop until we get one that's permitted. + If files or video are permitted, do them more often + than desktop. + + D+V+I: 10% + 45% + 45%. + V+I: 50% + 50% + D+V: 18% + 82% + D+I: 18% + 82% + */ + AGAIN: + n = (random() % 100); + if (++i > 300) abort(); + else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */ + else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */ + else if (image_p) which = GRAB_FILE; /* 45% */ + else goto AGAIN; } /* If we're to search a directory to find an image file, do so now. */ - if (which == do_image && !file) + if (which == GRAB_FILE && !file) { file = get_filename (screen, dir, verbose_p); if (!file) { - which = do_bars; + which = GRAB_BARS; if (verbose_p) fprintf (stderr, "%s: no image files found.\n", progname); } @@ -1261,48 +1614,65 @@ get_image (Screen *screen, /* Now actually render something. */ - if (which == do_bars) - { - XWindowAttributes xgwa; - COLORBARS: - if (verbose_p) - fprintf (stderr, "%s: drawing colorbars.\n", progname); - XGetWindowAttributes (dpy, window, &xgwa); - draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap, - 0, 0, 0, 0); - XSync (dpy, False); - } - else if (which == do_desk) + switch (which) { - GC gc; - XGCValues gcv; - XWindowAttributes xgwa; + case GRAB_BARS: + { + XWindowAttributes xgwa; + COLORBARS: + if (verbose_p) + fprintf (stderr, "%s: drawing colorbars.\n", progname); + XGetWindowAttributes (dpy, window, &xgwa); + draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap, + 0, 0, 0, 0); + XSync (dpy, False); + } + break; - if (verbose_p) - { - fprintf (stderr, "%s: grabbing desktop image\n", progname); - grabscreen_verbose(); - } - gc = XCreateGC (dpy, drawable, 0, &gcv); - XGetWindowAttributes (dpy, window, &xgwa); - grab_screen_image (screen, window); - XCopyArea (dpy, window, drawable, gc, - 0, 0, xgwa.width, xgwa.height, 0, 0); - XFreeGC (dpy, gc); - XSync (dpy, False); - } - else if (which == do_image) - { - if (! display_file (screen, window, drawable, file, verbose_p)) + case GRAB_DESK: + if (! display_desktop (screen, window, drawable, verbose_p, &geom)) goto COLORBARS; - } - else if (which == do_video) - { - if (! display_video (screen, window, drawable, verbose_p)) + file_prop = "desktop"; + break; + + case GRAB_FILE: + if (! display_file (screen, window, drawable, file, verbose_p, &geom)) + goto COLORBARS; + file_prop = file; + break; + + case GRAB_VIDEO: + if (! display_video (screen, window, drawable, verbose_p, &geom)) goto COLORBARS; + file_prop = "video"; + break; + + default: + abort(); + break; } - else - abort(); + + { + Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False); + if (file_prop && *file_prop) + XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, + (unsigned char *) file_prop, strlen(file_prop)); + else + XDeleteProperty (dpy, window, a); + + a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False); + if (geom.width > 0) + { + char gstr[30]; + sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y); + XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, + (unsigned char *) gstr, strlen (gstr)); + } + else + XDeleteProperty (dpy, window, a); + } + + XSync (dpy, False); } @@ -1427,7 +1797,7 @@ main (int argc, char **argv) memset (&P, 0, sizeof(P)); P.db = db; - load_init_file (&P); + load_init_file (dpy, &P); progname = argv[0] = oprogname; @@ -1459,10 +1829,10 @@ main (int argc, char **argv) goto LOSE; } window_str = argv[i]; - window = (Window) RootWindowOfScreen (screen); + window = VirtualRootWindowOfScreen (screen); } else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) || - 1 == sscanf (argv[i], " %ld %c", &w, &dummy)) && + 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) && w != 0) { if (drawable) @@ -1491,6 +1861,11 @@ main (int argc, char **argv) fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n", progname, argv[i]); LOSE: +# ifdef __GNUC__ + __extension__ /* don't warn about "string length is greater than + the length ISO C89 compilers are required to + support" in the usage string... */ +# endif fprintf (stderr, USAGE, progname, version, progname); exit (1); }