X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Fxscreensaver-getimage.c;h=599c50cd4d10e5cab70dd98ca47c21ddeb4e1011;hp=2fe88afc0fde6bf2a3604f520ae7567677e5457c;hb=40eacb5812ef7c0e3374fb139afbb4f5bc8bbfb5;hpb=2c902d6065f9856adf31e8540a94f1e42e68e905 diff --git a/driver/xscreensaver-getimage.c b/driver/xscreensaver-getimage.c index 2fe88afc..599c50cd 100644 --- a/driver/xscreensaver-getimage.c +++ b/driver/xscreensaver-getimage.c @@ -9,8 +9,10 @@ * implied warranty. */ -/* xscreensaver-getimage -- helper program that puts an image - (e.g., a snapshot of the desktop) onto the given window. +/* xscreensaver-getimage -- helper program that puts a random image + onto the given window or pixmap. That image is either a screen-grab, + a file loaded from disk, or a frame grabbed from the system's video + input. */ #include "utils.h" @@ -18,6 +20,8 @@ #include #include #include +#include +#include #ifdef HAVE_SYS_WAIT_H # include /* for waitpid() and associated macros */ @@ -42,16 +46,19 @@ #include "vroot.h" #ifdef HAVE_GDK_PIXBUF - +# undef HAVE_JPEGLIB # ifdef HAVE_GTK2 # include # else /* !HAVE_GTK2 */ # include # endif /* !HAVE_GTK2 */ - -# define HAVE_BUILTIN_IMAGE_LOADER #endif /* HAVE_GDK_PIXBUF */ +#ifdef HAVE_JPEGLIB +# undef HAVE_GDK_PIXBUF +# include +#endif + static char *defaults[] = { #include "../driver/XScreenSaver_ad.h" @@ -71,7 +78,6 @@ extern void grabscreen_verbose (void); #define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video" #define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file" - const char * blurb (void) { @@ -79,301 +85,925 @@ blurb (void) } +static int +x_ehandler (Display *dpy, XErrorEvent *error) +{ + fprintf (stderr, "\nX error in %s:\n", progname); + XmuPrintDefaultErrorMessage (dpy, error, stderr); + exit (-1); + return 0; +} + + +static Bool error_handler_hit_p = False; + +static int +ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) +{ + error_handler_hit_p = True; + return 0; +} + + +/* Returns True if the given Drawable is a Window; False if it's a Pixmap. + */ +static Bool +drawable_window_p (Display *dpy, Drawable d) +{ + XErrorHandler old_handler; + XWindowAttributes xgwa; + + XSync (dpy, False); + old_handler = XSetErrorHandler (ignore_all_errors_ehandler); + error_handler_hit_p = False; + XGetWindowAttributes (dpy, d, &xgwa); + XSync (dpy, False); + XSetErrorHandler (old_handler); + XSync (dpy, False); + + if (!error_handler_hit_p) + return True; /* It's a Window. */ + else + return False; /* It's a Pixmap, or an invalid ID. */ +} + + +/* Clear the window or pixmap to black, or its background color. + */ +static void +clear_drawable (Screen *screen, Drawable drawable) +{ + Display *dpy = DisplayOfScreen (screen); + XGCValues gcv; + GC gc; + Window root; + int x, y; + unsigned int w, h, bw, d; + XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d); + + /* The window might have no-op background of None, so to clear it, + draw a black rectangle first, then do XClearWindow (in case the + actual background color is non-black...) */ + + /* #### really we should allocate "black" instead, but I'm lazy... */ + gcv.foreground = BlackPixelOfScreen (screen); + gc = XCreateGC (dpy, drawable, GCForeground, &gcv); + XFillRectangle (dpy, drawable, gc, 0, 0, w, h); + XFreeGC (dpy, gc); + if (drawable_window_p (dpy, drawable)) + XClearWindow (dpy, (Window) drawable); + XFlush (dpy); +} + + +/* Figure out what kind of scaling/positioning we ought to do to display + a src-sized image in a dest-sized window/pixmap. Returns the width + and height to which the image should be scaled, and the position where + it should be displayed to center it. + */ static void -exec_error (char **av) +compute_image_scaling (int src_w, int src_h, + int dest_w, int dest_h, + Bool verbose_p, + int *scaled_from_x_ret, int *scaled_from_y_ret, + int *scaled_to_x_ret, int *scaled_to_y_ret, + int *scaled_w_ret, int *scaled_h_ret) { - char buf [512]; - char *token; + int srcx, srcy, destx, desty; - sprintf (buf, "%s: could not execute \"%s\"", progname, av[0]); - perror (buf); + Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) || + (src_h == dest_h && src_w <= dest_w)); - if (errno == ENOENT && - (token = getenv("PATH"))) + if (!exact_fit_p) /* scale the image up or down */ { -# ifndef PATH_MAX -# ifdef MAXPATHLEN -# define PATH_MAX MAXPATHLEN -# else -# define PATH_MAX 2048 -# endif -# endif - char path[PATH_MAX]; - fprintf (stderr, "\n"); - *path = 0; -# if defined(HAVE_GETCWD) - getcwd (path, sizeof(path)); -# elif defined(HAVE_GETWD) - getwd (path); -# endif - if (*path) - fprintf (stderr, " Current directory is: %s\n", path); - fprintf (stderr, " PATH is:\n"); - token = strtok (strdup(token), ":"); - while (token) + float rw = (float) dest_w / src_w; + float rh = (float) dest_h / src_h; + float r = (rw < rh ? rw : rh); + int tw = src_w * r; + int th = src_h * r; + int pct = (r * 100); + + if (pct < 95 || pct > 105) /* don't scale if it's close */ { - fprintf (stderr, " %s\n", token); - token = strtok(0, ":"); + if (verbose_p) + fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n", + progname, pct, src_w, src_h, tw, th); + src_w = tw; + src_h = th; } - fprintf (stderr, "\n"); } - exit (-1); + /* Center the image on the window/pixmap. */ + srcx = 0; + srcy = 0; + destx = (dest_w - src_w) / 2; + desty = (dest_h - src_h) / 2; + if (destx < 0) srcx = -destx, destx = 0; + if (desty < 0) srcy = -desty, desty = 0; + + if (dest_w < src_w) src_w = dest_w; + if (dest_h < src_h) src_h = dest_h; + + *scaled_w_ret = src_w; + *scaled_h_ret = src_h; + *scaled_from_x_ret = srcx; + *scaled_from_y_ret = srcy; + *scaled_to_x_ret = destx; + *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); } -static int -x_ehandler (Display *dpy, XErrorEvent *error) + +#ifdef HAVE_GDK_PIXBUF + +/* Reads the given image file and renders it on the Drawable, using GDK. + Returns False if it fails. + */ +static Bool +read_file_gdk (Screen *screen, Window window, Drawable drawable, + const char *filename, Bool verbose_p) { - fprintf (stderr, "\nX error in %s:\n", progname); - XmuPrintDefaultErrorMessage (dpy, error, stderr); - exit (-1); - return 0; + GdkPixbuf *pb; + Display *dpy = DisplayOfScreen (screen); + unsigned int win_width, win_height; +# ifdef HAVE_GTK2 + GError *gerr = 0; +# endif /* HAVE_GTK2 */ + + { + Window root; + int x, y; + unsigned int bw, d; + XWindowAttributes xgwa; + XGetWindowAttributes (dpy, window, &xgwa); + screen = xgwa.screen; + XGetGeometry (dpy, drawable, + &root, &x, &y, &win_width, &win_height, &bw, &d); + } + + gdk_pixbuf_xlib_init (dpy, screen_number (screen)); +# ifdef HAVE_GTK2 + g_type_init(); +# else /* !HAVE_GTK2 */ + xlib_rgb_init (dpy, screen); +# endif /* !HAVE_GTK2 */ + + pb = gdk_pixbuf_new_from_file (filename +# ifdef HAVE_GTK2 + , &gerr +# endif /* HAVE_GTK2 */ + ); + + if (!pb) + { + fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename); +# ifdef HAVE_GTK2 + if (gerr && gerr->message && *gerr->message) + fprintf (stderr, "%s: reason: %s\n", progname, gerr->message); +# endif /* HAVE_GTK2 */ + return False; + } + else + { + int w = gdk_pixbuf_get_width (pb); + int h = gdk_pixbuf_get_height (pb); + int srcx, srcy, destx, desty, w2, h2; + + compute_image_scaling (w, h, win_width, win_height, verbose_p, + &srcx, &srcy, &destx, &desty, &w2, &h2); + if (w != w2 || h != h2) + { + GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2, + GDK_INTERP_BILINEAR); + if (pb2) + { + gdk_pixbuf_unref (pb); + pb = pb2; + w = w2; + h = h2; + } + else + fprintf (stderr, "%s: out of memory when scaling?\n", progname); + } + + clear_drawable (screen, drawable); + + /* #### Note that this always uses the default colormap! Morons! + Owen says that in Gnome 2.0, I should try using + gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead. + But I haven't tried. + */ + gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable, + srcx, srcy, destx, desty, + w, h, + GDK_PIXBUF_ALPHA_FULL, 127, + XLIB_RGB_DITHER_NORMAL, + 0, 0); + XSync (dpy, False); + } + + return True; } +#endif /* HAVE_GDK_PIXBUF */ -#ifdef HAVE_BUILTIN_IMAGE_LOADER -static void load_image_internal (Screen *screen, Window window, - int win_width, int win_height, - Bool verbose_p, - int ac, char **av); -#endif /* HAVE_BUILTIN_IMAGE_LOADER */ +#ifdef HAVE_JPEGLIB +/* Allocates a colormap that makes a PseudoColor or DirectColor + visual behave like a TrueColor visual of the same depth. + */ static void -get_image (Screen *screen, Window window, - Bool verbose_p, - Bool desk_p, - Bool video_p, - Bool image_p, - char *dir) +allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap, + Bool verbose_p) { Display *dpy = DisplayOfScreen (screen); - enum { do_desk, do_video, do_image, do_bars } which = do_bars; - int count = 0; + int nr, ng, nb, cells; + int r, g, b; + int depth; + XColor colors[4097]; + int i; - XWindowAttributes xgwa; - XGetWindowAttributes (dpy, window, &xgwa); - screen = xgwa.screen; + depth = visual_depth (screen, visual); - if (verbose_p) + switch (depth) { - fprintf (stderr, "%s: grabDesktopImages: %s\n", - progname, desk_p ? "True" : "False"); - fprintf (stderr, "%s: grabVideoFrames: %s\n", - progname, video_p ? "True" : "False"); - fprintf (stderr, "%s: chooseRandomImages: %s\n", - progname, image_p ? "True" : "False"); - fprintf (stderr, "%s: imageDirectory: %s\n", - progname, (dir ? dir : "")); + 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; } - if (!dir || !*dir) + memset(colors, 0, sizeof(colors)); + 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) | + (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; + } + } + + { + 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 (verbose_p) + fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n", + progname, allocated, cells); + } +} + +/* Find the pixel index that is closest to the given color + (using linear distance in RGB space -- which is far from the best way.) + */ +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++) { - if (verbose_p && image_p) - fprintf (stderr, - "%s: no imageDirectory: turning off chooseRandomImages.\n", - progname); - image_p = False; + 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; + } } -# ifndef _VROOT_H_ -# error Error! This file definitely needs vroot.h! -# endif + return found; +} - /* If the window is not the root window (real or virtual!) then the hack - that called this program is running in "-window" mode instead of in - "-root" mode. - If the window is not the root window, then it's not possible to grab - video or images onto it (the contract with those programs is to draw on - the root.) So turn off those options in that case, and turn on desktop - grabbing. (Since we're running in a window on the desktop already, we - know it's not a security problem to expose desktop bits.) - */ +/* 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. + */ +static void +remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p) +{ + 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 (verbose_p) + fprintf(stderr, "%s: building color cube for %d bit image\n", + progname, image->depth); - if ((desk_p || video_p || image_p) && - !top_level_window_p (screen, window)) + for (i = 0; i < cells; i++) { - Bool changed_p = False; - if (desk_p) desk_p = False, changed_p = True; - if (video_p) video_p = False, changed_p = True; -# ifndef HAVE_BUILTIN_IMAGE_LOADER - /* We can display images on non-top-level windows with the builtin - loader, but not if we're using the external (chbg-based) loader. */ - if (image_p) image_p = False, changed_p = True; -# endif /* !HAVE_BUILTIN_IMAGE_LOADER */ + unsigned short r, g, b; - if (changed_p && verbose_p) - fprintf (stderr, "%s: not a top-level window: using colorbars.\n", - progname); + 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); } - else if (window != VirtualRootWindowOfScreen (screen)) - { - /* We can display images on non-root windows with the builtin loader, - but not if we're using the external (chbg-based) loader. - We can never display video on non-root windows (since that always - uses the external image loader.) - */ - Bool changed_p = False; - if (video_p) video_p = False, changed_p = True; -# ifndef HAVE_BUILTIN_IMAGE_LOADER - if (!desk_p) desk_p = True, changed_p = True; - if (image_p) image_p = False, changed_p = True; -# endif /* !HAVE_BUILTIN_IMAGE_LOADER */ - - if (changed_p && verbose_p) - fprintf (stderr, - "%s: not running on root window: grabbing desktop.\n", - progname); + + if (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]); + } +} + + +/* If the file has a PPM (P6) on it, read it and return an XImage. + Otherwise, rewind the fd back to the beginning, and return 0. + */ +static XImage * +maybe_read_ppm (Screen *screen, Visual *visual, + const char *filename, FILE *in, Bool verbose_p) +{ + Display *dpy = DisplayOfScreen (screen); + int depth = visual_depth (screen, visual); + struct stat st; + char *buf = 0; + int bufsiz = 0; + char *s, dummy; + int i, j; + int x, y, w, h, maxval; + XImage *ximage = 0; + + if (fstat (fileno (in), &st)) + goto FAIL; + + bufsiz = st.st_size; + buf = (char *) malloc (bufsiz + 1); + if (!buf) + { + fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n", + progname, bufsiz, filename); + goto FAIL; } - count = 0; - if (desk_p) count++; - if (video_p) count++; - if (image_p) count++; + if (! (s = fgets (buf, bufsiz, in))) /* line 1 */ + goto FAIL; - if (count == 0) - which = do_bars; - else + if (!strncmp (buf, "\107\111", 2)) { - 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(); - } + fprintf (stderr, "%s: %s: sorry, GIF files not supported" + " when compiled with JPEGlib instead of GDK_Pixbuf.\n", + progname, filename); + goto FAIL; } + else if (!strncmp (buf, "\211\120", 2)) + { + fprintf (stderr, "%s: %s: sorry, PNG files not supported" + " when compiled with JPEGlib instead of GDK_Pixbuf.\n", + progname, filename); + goto FAIL; + } + + if (strncmp (s, "P6", 2)) + goto FAIL; - if (which == do_desk) + if (! (s = fgets (buf, bufsiz, in))) /* line 2 */ + goto FAIL; + if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy)) { - if (verbose_p) - { - fprintf (stderr, "%s: grabbing desktop image\n", progname); - grabscreen_verbose(); - } - grab_screen_image (screen, window); - XSync (dpy, False); + fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename); + goto FAIL; } - else if (which == do_bars) + + if (! (s = fgets (buf, bufsiz, in))) /* line 3 */ + goto FAIL; + if (1 != sscanf (s, " %d %c", &maxval, &dummy)) { - if (verbose_p) - fprintf (stderr, "%s: drawing colorbars\n", progname); - draw_colorbars (dpy, window, 0, 0, xgwa.width, xgwa.height); - XSync (dpy, False); + fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename); + goto FAIL; } - else + if (maxval != 255) { - char *av[10]; - int ac = 0; - memset (av, 0, sizeof(av)); - switch (which) - { - case do_video: - if (verbose_p) - fprintf (stderr, "%s: grabbing video\n", progname); - av[ac++] = GETIMAGE_VIDEO_PROGRAM; - break; - case do_image: - if (verbose_p) - fprintf (stderr, "%s: loading random image file\n", progname); - av[ac++] = GETIMAGE_FILE_PROGRAM; - -# ifdef HAVE_BUILTIN_IMAGE_LOADER - av[ac++] = "--name"; -# endif /* !HAVE_BUILTIN_IMAGE_LOADER */ - av[ac++] = dir; - break; - default: + fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n", + progname, filename, maxval); + goto FAIL; + } + + 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); + if (!ximage || !ximage->data) + { + fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n", + progname, ximage->width, ximage->height, filename); + goto FAIL; + } + + s = buf; + j = bufsiz; + while ((i = fread (s, 1, j, in)) > 0) + s += i, j -= i; + + i = 0; + for (y = 0; y < ximage->height; y++) + for (x = 0; x < ximage->width; x++) + { + unsigned char r = buf[i++]; + unsigned char g = buf[i++]; + unsigned char b = buf[i++]; + unsigned long pixel; + + if (depth > 16) + pixel = (r << 16) | (g << 8) | b; + else if (depth == 8) + 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) + pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3))); + else abort(); - break; - } - if (verbose_p) - { - int i; - for (i = ac; i > 1; i--) - av[i] = av[i-1]; - av[1] = strdup ("--verbose"); - ac++; - } + XPutPixel (ximage, x, y, pixel); + } - if (verbose_p) + free (buf); + return ximage; + + FAIL: + if (buf) free (buf); + if (ximage && ximage->data) + { + free (ximage->data); + ximage->data = 0; + } + if (ximage) XDestroyImage (ximage); + fseek (in, 0, SEEK_SET); + return 0; +} + + +typedef struct { + struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */ + const char *filename; + Screen *screen; + Visual *visual; + Drawable drawable; + Colormap cmap; +} getimg_jpg_error_mgr; + + +static void +jpg_output_message (j_common_ptr cinfo) +{ + getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err; + char buf[JMSG_LENGTH_MAX]; + cinfo->err->format_message (cinfo, buf); + fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf); +} + + +static void +jpg_error_exit (j_common_ptr cinfo) +{ + getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err; + cinfo->err->output_message (cinfo); + draw_colorbars (err->screen, err->visual, err->drawable, err->cmap, + 0, 0, 0, 0); + XSync (DisplayOfScreen (err->screen), False); + exit (1); +} + + +/* Reads a JPEG file, returns an RGB XImage of it. + */ +static XImage * +read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable, + Colormap cmap, const char *filename, Bool verbose_p) +{ + Display *dpy = DisplayOfScreen (screen); + int depth = visual_depth (screen, visual); + + FILE *in = 0; + XImage *ximage = 0; + struct jpeg_decompress_struct cinfo; + getimg_jpg_error_mgr jerr; + JSAMPARRAY scanbuf = 0; + int y; + + jerr.filename = filename; + jerr.screen = screen; + jerr.visual = visual; + jerr.drawable = drawable; + jerr.cmap = cmap; + + if (! (depth >= 15 || depth == 12 || depth == 8)) + { + fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth); + goto FAIL; + } + + in = fopen (filename, "rb"); + if (!in) + { + fprintf (stderr, "%s: %s: unreadable\n", progname, filename); + goto FAIL; + } + + /* Check to see if it's a PPM, and if so, read that instead of using + the JPEG library. Yeah, this is all modular and stuff. + */ + if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p))) + { + fclose (in); + return ximage; + } + + cinfo.err = jpeg_std_error (&jerr.pub); + jerr.pub.output_message = jpg_output_message; + jerr.pub.error_exit = jpg_error_exit; + + jpeg_create_decompress (&cinfo); + jpeg_stdio_src (&cinfo, in); + jpeg_read_header (&cinfo, TRUE); + + /* set some decode parameters */ + cinfo.out_color_space = JCS_RGB; + cinfo.quantize_colors = FALSE; + + jpeg_start_decompress (&cinfo); + + ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0, + cinfo.output_width, cinfo.output_height, + 8, 0); + if (ximage) + ximage->data = (unsigned char *) + calloc (ximage->height, ximage->bytes_per_line); + + if (ximage && ximage->data) + scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, + cinfo.rec_outbuf_height * + cinfo.output_width * + cinfo.output_components, + 1); + if (!ximage || !ximage->data || !scanbuf) + { + fprintf (stderr, "%s: out of memory loading %dx%d file %s\n", + progname, ximage->width, ximage->height, filename); + goto FAIL; + } + + y = 0; + while (cinfo.output_scanline < cinfo.output_height) + { + int n = jpeg_read_scanlines (&cinfo, scanbuf, 1); + int i; + for (i = 0; i < n; i++) { - int i = 0; - fprintf (stderr, "%s: executing \"", progname); - while (av[i]) + int x; + for (x = 0; x < ximage->width; x++) { - fprintf (stderr, "%s", av[i]); - if (av[++i]) fprintf (stderr, " "); + int j = x * cinfo.num_components; + unsigned char r = scanbuf[i][j]; + unsigned char g = scanbuf[i][j+1]; + unsigned char b = scanbuf[i][j+2]; + unsigned long pixel; + + if (depth > 16) + pixel = (r << 16) | (g << 8) | b; + else if (depth == 8) + 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) + /* Gah! I don't understand why these are in the other + order. */ + pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3))); + else + abort(); + + XPutPixel (ximage, x, y, pixel); } - fprintf (stderr, "\"\n"); + y++; } + } -# ifdef HAVE_PUTENV - /* Store our "-display" argument into the $DISPLAY variable, - so that the subprocess gets the right display if the - prevailing $DISPLAY is different. */ - { - const char *odpy = DisplayString (dpy); - char *ndpy = (char *) malloc(strlen(odpy) + 20); - char *s; - int screen_no = screen_number (screen); /* might not be default now */ - - strcpy (ndpy, "DISPLAY="); - s = ndpy + strlen(ndpy); - strcpy (s, odpy); - - while (*s && *s != ':') s++; /* skip to colon */ - while (*s == ':') s++; /* skip over colons */ - while (isdigit(*s)) s++; /* skip over dpy number */ - while (*s == '.') s++; /* skip over dot */ - if (s[-1] != '.') *s++ = '.'; /* put on a dot */ - sprintf(s, "%d", screen_no); /* put on screen number */ - - if (putenv (ndpy)) - abort (); - - /* don't free (ndpy) -- some implementations of putenv (BSD - 4.4, glibc 2.0) copy the argument, but some (libc4,5, glibc - 2.1.2) do not. So we must leak it (and/or the previous - setting). Yay. - */ - } -# endif /* HAVE_PUTENV */ + if (cinfo.output_scanline < cinfo.output_height) + /* don't goto FAIL -- we might have viewable partial data. */ + jpeg_abort_decompress (&cinfo); + else + jpeg_finish_decompress (&cinfo); -# ifdef HAVE_BUILTIN_IMAGE_LOADER - if (which == do_image) - { - load_image_internal (screen, window, xgwa.width, xgwa.height, - verbose_p, ac, av); - return; - } -# endif /* HAVE_BUILTIN_IMAGE_LOADER */ + jpeg_destroy_decompress (&cinfo); + fclose (in); + in = 0; + + return ximage; + + FAIL: + if (in) fclose (in); + if (ximage && ximage->data) + { + free (ximage->data); + ximage->data = 0; + } + if (ximage) XDestroyImage (ximage); + if (scanbuf) free (scanbuf); + return 0; +} + + +/* 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; - close (ConnectionNumber (dpy)); /* close display fd */ + (*ximage) = (*ximage2); - execvp (av[0], av); /* shouldn't return */ - exec_error (av); + 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) +{ + Display *dpy = DisplayOfScreen (screen); + XImage *ximage; + Visual *visual; + int class, depth; + Colormap cmap; + unsigned int win_width, win_height, win_depth; + int srcx, srcy, destx, desty, w2, h2; + + { + 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); + } + + /* Make sure we're not on some weirdo visual... + */ + class = visual_class (screen, visual); + depth = visual_depth (screen, visual); + if ((class == PseudoColor || class == DirectColor) && + (depth != 8 && depth != 12)) + { + fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n", + progname, depth); + return False; } + + /* Read the file... + */ + ximage = read_jpeg_ximage (screen, visual, drawable, cmap, + filename, verbose_p); + if (!ximage) return False; + + /* Scale it, if necessary... + */ + compute_image_scaling (ximage->width, ximage->height, + win_width, win_height, verbose_p, + &srcx, &srcy, &destx, &desty, &w2, &h2); + if (ximage->width != w2 || ximage->height != h2) + if (! scale_ximage (screen, visual, ximage, w2, h2)) + return False; + + /* Allocate a colormap, if we need to... + */ + if (class == PseudoColor || class == DirectColor) + { + allocate_cubic_colormap (screen, visual, cmap, verbose_p); + remap_image (screen, cmap, ximage, verbose_p); + } + + /* 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); + XFreeGC (dpy, gc); + } + + free (ximage->data); + ximage->data = 0; + XDestroyImage (ximage); + XSync (dpy, False); + return True; } +#endif /* HAVE_JPEGLIB */ -#ifdef HAVE_BUILTIN_IMAGE_LOADER -/* Reads a filename from "GETIMAGE_FILE_PROGRAM --name /DIR" +/* Reads the given image file and renders it on the Drawable. + Returns False if it fails. + */ +static Bool +display_file (Screen *screen, Window window, Drawable drawable, + const char *filename, Bool verbose_p) +{ + 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)) + return True; +# elif defined(HAVE_JPEGLIB) + if (read_file_jpeglib (screen, window, drawable, filename, verbose_p)) + return True; +# else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */ + /* shouldn't get here if we have no image-loading methods available. */ + abort(); +# endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */ + + return False; +} + + +/* 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. */ static char * -get_filename (Display *dpy, int ac, char **av) +get_filename_1 (Screen *screen, const char *directory, Bool video_p, + Bool verbose_p) { + Display *dpy = DisplayOfScreen (screen); pid_t forked; int fds [2]; int in, out; char buf[1024]; + char *av[20]; + int ac = 0; + + if (!video_p) + { + av[ac++] = GETIMAGE_FILE_PROGRAM; + if (verbose_p) + av[ac++] = "--verbose"; + av[ac++] = "--name"; + av[ac++] = (char *) directory; + } + else + { + av[ac++] = GETIMAGE_VIDEO_PROGRAM; + if (verbose_p) + av[ac++] = "--verbose"; + av[ac++] = "--name"; + } + av[ac] = 0; + + if (verbose_p) + { + int i; + fprintf (stderr, "%s: executing:", progname); + for (i = 0; i < ac; i++) + fprintf (stderr, " %s", av[i]); + fprintf (stderr, "\n"); + } if (pipe (fds)) { @@ -412,6 +1042,7 @@ get_filename (Display *dpy, int ac, char **av) } default: { + struct stat st; int wait_status = 0; FILE *f = fdopen (in, "r"); int L; @@ -428,7 +1059,16 @@ get_filename (Display *dpy, int ac, char **av) while (L && buf[L-1] == '\n') buf[--L] = 0; - return strdup (buf); + if (!*buf) + return 0; + if (stat(buf, &st)) + { + fprintf (stderr, "%s: file does not exist: \"%s\"\n", + progname, buf); + return 0; + } + else + return strdup (buf); } } @@ -436,151 +1076,234 @@ get_filename (Display *dpy, int ac, char **av) } +/* Returns a pathname to an image file. Free the string when you're done. + */ +static char * +get_filename (Screen *screen, const char *directory, Bool verbose_p) +{ + return get_filename_1 (screen, directory, False, verbose_p); +} -static void -load_image_internal (Screen *screen, Window window, - int win_width, int win_height, - Bool verbose_p, - int ac, char **av) + +/* Grabs a video frame to a file, and returns a pathname to that file. + Delete that file when you are done with it (and free the string.) + */ +static char * +get_video_filename (Screen *screen, Bool verbose_p) { - GdkPixbuf *pb; - Display *dpy = DisplayOfScreen (screen); - char *filename = get_filename (dpy, ac, av); -#ifdef HAVE_GTK2 - GError *gerr = 0; -#endif /* HAVE_GTK2 */ + return get_filename_1 (screen, 0, True, verbose_p); +} + + +/* 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) +{ + char *filename = get_video_filename (screen, verbose_p); + Bool status; if (!filename) { - fprintf (stderr, "%s: no file name returned by %s\n", - progname, av[0]); - goto FAIL; + if (verbose_p) + fprintf (stderr, "%s: video grab failed.\n", progname); + return False; + } + + status = display_file (screen, window, drawable, filename, verbose_p); + + if (unlink (filename)) + { + char buf[512]; + sprintf (buf, "%s: rm %.100s", progname, filename); + perror (buf); } else if (verbose_p) - fprintf (stderr, "%s: loading \"%s\"\n", progname, filename); + fprintf (stderr, "%s: rm %s\n", progname, filename); - gdk_pixbuf_xlib_init (dpy, screen_number (screen)); -#ifdef HAVE_GTK2 - g_type_init(); -#else /* !HAVE_GTK2 */ - xlib_rgb_init (dpy, screen); -#endif /* !HAVE_GTK2 */ + if (filename) free (filename); + return status; +} - pb = gdk_pixbuf_new_from_file (filename -#ifdef HAVE_GTK2 - , &gerr -#endif /* HAVE_GTK2 */ - ); - if (pb) +/* 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. + */ +static void +get_image (Screen *screen, + Window window, Drawable drawable, + Bool verbose_p, + Bool desk_p, + Bool video_p, + Bool image_p, + const char *dir, + const char *file) +{ + Display *dpy = DisplayOfScreen (screen); + enum { do_desk, do_video, do_image, do_bars } which = do_bars; + int count = 0; + struct stat st; + + if (! drawable_window_p (dpy, window)) { - int w = gdk_pixbuf_get_width (pb); - int h = gdk_pixbuf_get_height (pb); - int srcx, srcy, destx, desty; + fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n", + progname, (unsigned long) window); + exit (1); + } + + if (file && stat (file, &st)) + { + fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file); + file = 0; + } + + if (verbose_p) + { + fprintf (stderr, "%s: grabDesktopImages: %s\n", + progname, desk_p ? "True" : "False"); + fprintf (stderr, "%s: grabVideoFrames: %s\n", + progname, video_p ? "True" : "False"); + fprintf (stderr, "%s: chooseRandomImages: %s\n", + progname, image_p ? "True" : "False"); + fprintf (stderr, "%s: imageDirectory: %s\n", + progname, (file ? file : dir ? dir : "")); + } + +# if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB)) + image_p = False; /* can't load images from files... */ + if (file) + { + fprintf (stderr, + "%s: image file loading not available at compile-time\n", + progname); + fprintf (stderr, "%s: can't load \"%s\"\n", progname, file); + file = 0; + } +# endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */ + + if (file) + { + desk_p = False; + video_p = False; + image_p = True; + } + + if (!dir || !*dir) + { + if (verbose_p && image_p) + fprintf (stderr, + "%s: no imageDirectory: turning off chooseRandomImages.\n", + progname); + image_p = False; + } + - Bool exact_fit_p = ((w == win_width && h <= win_height) || - (h == win_height && w <= win_width)); +# ifndef _VROOT_H_ +# error Error! This file definitely needs vroot.h! +# endif - if (!exact_fit_p) /* scale the image up or down */ + /* We can grab desktop images 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: + - the window is a non-top-level window. + */ + if (desk_p) + { + if (!top_level_window_p (screen, window)) { - float rw = (float) win_width / w; - float rh = (float) win_height / h; - float r = (rw < rh ? rw : rh); - int tw = w * r; - int th = h * r; - int pct = (r * 100); - - if (pct < 95 || pct > 105) /* don't scale if it's close */ - { - GdkPixbuf *pb2; - if (verbose_p) - fprintf (stderr, - "%s: scaling image by %d%% (%dx%d -> %dx%d)\n", - progname, pct, w, h, tw, th); - - pb2 = gdk_pixbuf_scale_simple (pb, tw, th, GDK_INTERP_BILINEAR); - if (pb2) - { - gdk_pixbuf_unref (pb); - pb = pb2; - w = tw; - h = th; - } - else - fprintf (stderr, "%s: out of memory when scaling?\n", - progname); - } + desk_p = False; + if (verbose_p) + fprintf (stderr, + "%s: 0x%x not top-level: turning off grabDesktopImages.\n", + progname, (unsigned int) window); } + } - /* Center the image on the window. */ - srcx = 0; - srcy = 0; - destx = (win_width - w) / 2; - desty = (win_height - h) / 2; - if (destx < 0) srcx = -destx, destx = 0; - if (desty < 0) srcy = -desty, desty = 0; + count = 0; + if (desk_p) count++; + if (video_p) count++; + if (image_p) count++; - if (win_width < w) w = win_width; - if (win_height < h) h = win_height; + if (count == 0) + which = do_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(); + } + } - /* The window might have no-op background of None, so to clear it, - draw a black rectangle first, then do XClearWindow (in case the - actual background color is non-black...) */ - { - XGCValues gcv; - GC gc; - /* #### really we should allocate "black" instead, but I'm lazy... */ - gcv.foreground = BlackPixelOfScreen (screen); - gc = XCreateGC (dpy, window, GCForeground, &gcv); - XFillRectangle (dpy, window, gc, 0, 0, win_width, win_height); - XFreeGC (dpy, gc); - XClearWindow (dpy, window); - XFlush (dpy); - } - /* #### Note that this always uses the default colormap! Morons! - Owen says that in Gnome 2.0, I should try using - gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead. - But I don't have Gnome 2.0 yet. - */ - gdk_pixbuf_xlib_render_to_drawable_alpha (pb, window, - srcx, srcy, destx, desty, w, h, - GDK_PIXBUF_ALPHA_FULL, 127, - XLIB_RGB_DITHER_NORMAL, 0, 0); - XSync (dpy, False); + /* If we're to search a directory to find an image file, do so now. + */ + if (which == do_image && !file) + { + file = get_filename (screen, dir, verbose_p); + if (!file) + { + which = do_bars; + if (verbose_p) + fprintf (stderr, "%s: no image files found.\n", progname); + } + } + /* Now actually render something. + */ + if (which == do_bars) + { + XWindowAttributes xgwa; + COLORBARS: if (verbose_p) - fprintf (stderr, "%s: displayed %dx%d image at %d,%d.\n", - progname, w, h, destx, desty); + 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 (filename) + else if (which == do_desk) { - fprintf (stderr, "%s: unable to load %s\n", progname, filename); -#ifdef HAVE_GTK2 - if (gerr && gerr->message && *gerr->message) - fprintf (stderr, "%s: reason %s\n", progname, gerr->message); -#endif /* HAVE_GTK2 */ + GC gc; + XGCValues gcv; + XWindowAttributes xgwa; - goto FAIL; + 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 + else if (which == do_image) { - fprintf (stderr, "%s: unable to initialize built-in images\n", progname); - goto FAIL; + if (! display_file (screen, window, drawable, file, verbose_p)) + goto COLORBARS; } - - return; - - FAIL: - if (verbose_p) - fprintf (stderr, "%s: drawing colorbars\n", progname); - draw_colorbars (dpy, window, 0, 0, win_width, win_height); - XSync (dpy, False); + else if (which == do_video) + { + if (! display_video (screen, window, drawable, verbose_p)) + goto COLORBARS; + } + else + abort(); } -#endif /* HAVE_BUILTIN_IMAGE_LOADER */ - - #ifdef DEBUG static Bool @@ -606,9 +1329,9 @@ mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks, #endif /* DEBUG */ -#define USAGE "usage: %s [ -options... ] window-id\n" \ +#define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \ "\n" \ - " This program puts an image on the given window.\n" \ + " This program puts an image on the given window or pixmap.\n" \ "\n" \ " It is used by those xscreensaver demos that operate on images.\n" \ " The image may be a file loaded from disk, a frame grabbed from\n" \ @@ -624,6 +1347,7 @@ mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks, " -video / -no-video whether to allow video grabs\n" \ " -desktop / -no-desktop whether to allow desktop screen grabs\n"\ " -directory where to find image files to load\n" \ + " -file load this image file\n" \ "\n" \ " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\ " defaults for these options in your ~/.xscreensaver file.\n" \ @@ -637,8 +1361,12 @@ main (int argc, char **argv) Display *dpy; Screen *screen; char *oprogname = progname; + char *file = 0; Window window = (Window) 0; + Drawable drawable = (Drawable) 0; + const char *window_str = 0; + const char *drawable_str = 0; char *s; int i; @@ -650,6 +1378,9 @@ main (int argc, char **argv) /* half-assed way of avoiding buffer-overrun attacks. */ if (strlen (progname) >= 100) progname[100] = 0; +# ifndef _VROOT_H_ +# error Error! This file definitely needs vroot.h! +# endif /* We must read exactly the same resources as xscreensaver. That means we must have both the same progclass *and* progname, @@ -684,6 +1415,9 @@ main (int argc, char **argv) for (i = 1; i < argc; i++) { + unsigned long w; + char dummy; + /* Have to re-process these, or else the .xscreensaver file has priority over the command line... */ @@ -695,37 +1429,50 @@ main (int argc, char **argv) else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False; else if (!strcmp (argv[i], "-images")) P.random_image_p = True; else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False; + else if (!strcmp (argv[i], "-file")) file = argv[++i]; else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir")) P.image_directory = argv[++i]; - else if (window == 0) + else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root")) { - unsigned long w; - char dummy; - - if (!strcmp (argv[i], "-root") || - !strcmp (argv[i], "root")) - window = RootWindowOfScreen (screen); - - else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) || - 1 == sscanf (argv[i], " %ld %c", &w, &dummy)) && - w != 0) - window = (Window) w; - else + if (window) { - if (argv[i][0] == '-') - fprintf (stderr, "\n%s: unknown option \"%s\"\n", - progname, argv[i]); - else - fprintf (stderr, "\n%s: unparsable window ID: \"%s\"\n", - progname, argv[i]); + fprintf (stderr, "%s: both %s and %s specified?\n", + progname, argv[i], window_str); goto LOSE; } + window_str = argv[i]; + window = (Window) RootWindowOfScreen (screen); + } + else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) || + 1 == sscanf (argv[i], " %ld %c", &w, &dummy)) && + w != 0) + { + if (drawable) + { + fprintf (stderr, "%s: both %s and %s specified?\n", + progname, drawable_str, argv[i]); + goto LOSE; + } + else if (window) + { + drawable_str = argv[i]; + drawable = (Drawable) w; + } + else + { + window_str = argv[i]; + window = (Window) w; + } } else { - fprintf (stderr, "\n%s: unknown option \"%s\"\n", - progname, argv[i]); - LOSE: + if (argv[i][0] == '-') + fprintf (stderr, "\n%s: unknown option \"%s\"\n", + progname, argv[i]); + else + fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n", + progname, argv[i]); + LOSE: fprintf (stderr, USAGE, progname); exit (1); } @@ -733,7 +1480,7 @@ main (int argc, char **argv) if (window == 0) { - fprintf (stderr, "\n%s: no window specified!\n", progname); + fprintf (stderr, "\n%s: no window ID specified!\n", progname); goto LOSE; } @@ -749,8 +1496,11 @@ main (int argc, char **argv) } #endif /* DEBUG */ - get_image (screen, window, P.verbose_p, + if (!window) abort(); + if (!drawable) drawable = window; + + get_image (screen, window, drawable, P.verbose_p, P.grab_desktop_p, P.grab_video_p, P.random_image_p, - P.image_directory); + P.image_directory, file); exit (0); }