X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fxscreensaver-getimage.c;h=f702f679294f7c05f353f1ed18ad5460e4987bb3;hb=d6b0217f2417bd19187f0ebc389d6c5c2233b11c;hp=e46bc5c97262a6e9d55ce566c0142ec8c4b25228;hpb=96a411663168b0ba5432b407a83be55f3df0c802;p=xscreensaver diff --git a/driver/xscreensaver-getimage.c b/driver/xscreensaver-getimage.c index e46bc5c9..f702f679 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-2016 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 @@ -66,8 +66,11 @@ #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. */ + /* On MacOS under X11, the usual X11 mechanism of getting a screen shot + doesn't work, and we need to use an external program. This is only + used when running under X11 on MacOS. If it's a Cocoa build, this + path is not taken, and OSX/grabclient-osx.m is used instead. + */ # define USE_EXTERNAL_SCREEN_GRABBER #endif @@ -101,6 +104,8 @@ typedef enum { #define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file" #define GETIMAGE_SCREEN_PROGRAM "xscreensaver-getimage-desktop" +extern const char *blurb (void); + const char * blurb (void) { @@ -136,6 +141,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. */ @@ -171,7 +187,7 @@ root_window_p (Screen *screen, Window window) Atom type; int format; unsigned long nitems, bytesafter; - char *version; + unsigned char *version; if (window != RootWindowOfScreen (screen)) return False; @@ -180,7 +196,7 @@ root_window_p (Screen *screen, Window window) XInternAtom (dpy, "_SCREENSAVER_VERSION", False), 0, 1, False, XA_STRING, &type, &format, &nitems, &bytesafter, - (unsigned char **) &version) + &version) == Success && type != None) return False; @@ -276,8 +292,8 @@ 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); } @@ -286,6 +302,7 @@ compute_image_scaling (int src_w, int src_h, 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) @@ -333,6 +350,7 @@ scale_ximage (Screen *screen, Visual *visual, return True; } +#endif /* !USE_EXTERNAL_SCREEN_GRABBER || HAVE_JPEGLIB */ #ifdef HAVE_GDK_PIXBUF @@ -342,7 +360,8 @@ scale_ximage (Screen *screen, Visual *visual, */ 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); @@ -360,9 +379,11 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, &root, &x, &y, &win_width, &win_height, &bw, &win_depth); } - gdk_pixbuf_xlib_init (dpy, screen_number (screen)); + gdk_pixbuf_xlib_init_with_depth (dpy, screen_number (screen), win_depth); # ifdef HAVE_GTK2 +# if !GLIB_CHECK_VERSION(2, 36 ,0) g_type_init(); +# endif # else /* !HAVE_GTK2 */ xlib_rgb_init (dpy, screen); # endif /* !HAVE_GTK2 */ @@ -389,6 +410,20 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, int srcx, srcy, destx, desty, w2, h2; Bool bg_p = False; +# ifdef HAVE_GDK_PIXBUF_APPLY_EMBEDDED_ORIENTATION + { + int ow = w, oh = h; + GdkPixbuf *opb = pb; + pb = gdk_pixbuf_apply_embedded_orientation (opb); + g_object_unref (opb); + w = gdk_pixbuf_get_width (pb); + h = gdk_pixbuf_get_height (pb); + if (verbose_p && (w != ow || h != oh)) + fprintf (stderr, "%s: rotated %dx%d to %dx%d\n", + progname, ow, oh, w, h); + } +# endif + compute_image_scaling (w, h, win_width, win_height, verbose_p, &srcx, &srcy, &destx, &desty, &w2, &h2); if (w != w2 || h != h2) @@ -397,7 +432,7 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, GDK_INTERP_BILINEAR); if (pb2) { - gdk_pixbuf_unref (pb); + g_object_unref (pb); pb = pb2; w = w2; h = h2; @@ -442,6 +477,14 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, 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); @@ -456,6 +499,8 @@ read_file_gdk (Screen *screen, Window window, Drawable drawable, /* Allocates a colormap that makes a PseudoColor or DirectColor visual behave like a TrueColor visual of the same depth. + + #### Duplicated in utils/grabscreen.c */ static void allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap, @@ -522,6 +567,8 @@ allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap, /* 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 utils/grabscreen.c */ static unsigned long find_closest_pixel (XColor *colors, int ncolors, @@ -562,6 +609,8 @@ find_closest_pixel (XColor *colors, int ncolors, 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 utils/grabscreen.c */ static void remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p) @@ -885,7 +934,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]; @@ -897,10 +946,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(); @@ -940,7 +991,8 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable, */ 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; @@ -1031,6 +1083,14 @@ read_file_jpeglib (Screen *screen, Window window, Drawable drawable, 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); @@ -1046,16 +1106,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. */ @@ -1067,8 +1129,8 @@ 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 - to run. + load.) Free the string when done. 'grab_type' controls which program + to run. Returned pathname may be relative to 'directory', or absolute. */ static char * get_filename_1 (Screen *screen, const char *directory, grab_type type, @@ -1078,7 +1140,7 @@ get_filename_1 (Screen *screen, const char *directory, grab_type type, pid_t forked; int fds [2]; int in, out; - char buf[1024]; + char buf[10240]; char *av[20]; int ac = 0; @@ -1163,10 +1225,12 @@ get_filename_1 (Screen *screen, const char *directory, grab_type type, int wait_status = 0; FILE *f = fdopen (in, "r"); int L; + char *ret = 0; 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. */ @@ -1178,14 +1242,28 @@ get_filename_1 (Screen *screen, const char *directory, grab_type type, if (!*buf) return 0; + + ret = strdup (buf); + + if (*ret != '/') + { + /* Program returned path relative to directory. Prepend dir + to buf so that we can properly stat it. */ + strcpy (buf, directory); + if (directory[strlen(directory)-1] != '/') + strcat (buf, "/"); + strcat (buf, ret); + } + if (stat(buf, &st)) { fprintf (stderr, "%s: file does not exist: \"%s\"\n", progname, buf); + free (ret); return 0; } else - return strdup (buf); + return ret; } } @@ -1228,7 +1306,7 @@ get_desktop_filename (Screen *screen, Bool verbose_p) */ 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; @@ -1240,7 +1318,8 @@ 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)) { @@ -1263,7 +1342,7 @@ display_video (Screen *screen, Window window, Drawable drawable, */ static Bool display_desktop (Screen *screen, Window window, Drawable drawable, - Bool verbose_p) + Bool verbose_p, XRectangle *geom_ret) { # ifdef USE_EXTERNAL_SCREEN_GRABBER @@ -1299,7 +1378,8 @@ display_desktop (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)) { @@ -1347,12 +1427,38 @@ display_desktop (Screen *screen, Window window, Drawable drawable, } else /* size mismatch -- must scale client-side images to fit drawable */ { - XImage *ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height, - ~0L, ZPixmap); 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, @@ -1361,6 +1467,14 @@ display_desktop (Screen *screen, Window window, Drawable drawable, 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; @@ -1368,6 +1482,19 @@ display_desktop (Screen *screen, Window window, Drawable drawable, } +/* 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); +} + + /* Grabs an image (from a file, video, or the desktop) and renders it on the Drawable. If `file' is specified, always use that file. Otherwise, select randomly, based on the other arguments. @@ -1384,9 +1511,10 @@ get_image (Screen *screen, { Display *dpy = DisplayOfScreen (screen); grab_type which = GRAB_BARS; - int count = 0; struct stat st; const char *file_prop = 0; + char *absfile = 0; + XRectangle geom = { 0, 0, 0, 0 }; if (! drawable_window_p (dpy, window)) { @@ -1442,8 +1570,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, @@ -1452,6 +1579,15 @@ 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! @@ -1463,7 +1599,8 @@ get_image (Screen *screen, 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. + Under X11 on MacOS, desktops are just like loaded image files. + Under Cocoa on MacOS, this code is not used at all. */ # ifndef USE_EXTERNAL_SCREEN_GRABBER if (desk_p) @@ -1479,24 +1616,28 @@ get_image (Screen *screen, } # endif /* !USE_EXTERNAL_SCREEN_GRABBER */ - count = 0; - if (desk_p) count++; - if (video_p) count++; - if (image_p) count++; - - if (count == 0) + 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 == GRAB_DESK && desk_p) break; - if (which == GRAB_VIDEO && video_p) break; - if (which == GRAB_FILE && 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; } @@ -1527,23 +1668,36 @@ get_image (Screen *screen, draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap, 0, 0, 0, 0); XSync (dpy, False); + if (! file_prop) file_prop = ""; + } break; case GRAB_DESK: - if (! display_desktop (screen, window, drawable, verbose_p)) + if (! display_desktop (screen, window, drawable, verbose_p, &geom)) goto COLORBARS; file_prop = "desktop"; break; case GRAB_FILE: - if (! display_file (screen, window, drawable, file, verbose_p)) + if (*file && *file != '/') /* pathname is relative to dir. */ + { + if (absfile) free (absfile); + absfile = malloc (strlen(dir) + strlen(file) + 10); + strcpy (absfile, dir); + if (dir[strlen(dir)-1] != '/') + strcat (absfile, "/"); + strcat (absfile, file); + } + if (! display_file (screen, window, drawable, + (absfile ? absfile : file), + verbose_p, &geom)) goto COLORBARS; file_prop = file; break; case GRAB_VIDEO: - if (! display_video (screen, window, drawable, verbose_p)) + if (! display_video (screen, window, drawable, verbose_p, &geom)) goto COLORBARS; file_prop = "video"; break; @@ -1556,12 +1710,39 @@ get_image (Screen *screen, { 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)); + { + char *f2 = strdup (file_prop); + + /* Take the extension off of the file name. */ + /* Duplicated in utils/grabclient.c. */ + char *slash = strrchr (f2, '/'); + char *dot = strrchr ((slash ? slash : f2), '.'); + if (dot) *dot = 0; + /* Replace slashes with newlines */ + /* while ((dot = strchr(f2, '/'))) *dot = '\n'; */ + /* Replace slashes with spaces */ + /* while ((dot = strchr(f2, '/'))) *dot = ' '; */ + + XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, + (unsigned char *) f2, strlen(f2)); + free (f2); + } + 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); } + if (absfile) free (absfile); XSync (dpy, False); } @@ -1687,7 +1868,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;