1 /* xscreensaver, Copyright (c) 2001-2016 by Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* xscreensaver-getimage -- helper program that puts a random image
13 onto the given window or pixmap. That image is either a screen-grab,
14 a file loaded from disk, or a frame grabbed from the system's video
20 #include <X11/Intrinsic.h>
26 #ifdef HAVE_SYS_WAIT_H
27 # include <sys/wait.h> /* for waitpid() and associated macros */
32 # include <X11/Xmu/Error.h>
34 # include <Xmu/Error.h>
41 #include "grabscreen.h"
42 #include "resources.h"
43 #include "colorbars.h"
49 #ifndef _XSCREENSAVER_VROOT_H_
50 # error Error! You have an old version of vroot.h! Check -I args.
51 #endif /* _XSCREENSAVER_VROOT_H_ */
53 #ifdef HAVE_GDK_PIXBUF
56 # include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
57 # else /* !HAVE_GTK2 */
58 # include <gdk-pixbuf/gdk-pixbuf-xlib.h>
59 # endif /* !HAVE_GTK2 */
60 #endif /* HAVE_GDK_PIXBUF */
63 # undef HAVE_GDK_PIXBUF
69 /* On MacOS under X11, the usual X11 mechanism of getting a screen shot
70 doesn't work, and we need to use an external program. This is only
71 used when running under X11 on MacOS. If it's a Cocoa build, this
72 path is not taken, and OSX/grabclient-osx.m is used instead.
74 # define USE_EXTERNAL_SCREEN_GRABBER
79 __extension__ /* shut up about "string length is greater than the length
80 ISO C89 compilers are required to support" when including
84 static char *defaults[] = {
85 #include "../driver/XScreenSaver_ad.h"
92 char *progclass = "XScreenSaver";
96 extern void grabscreen_verbose (void);
99 GRAB_DESK, GRAB_VIDEO, GRAB_FILE, GRAB_BARS
103 #define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
104 #define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file"
105 #define GETIMAGE_SCREEN_PROGRAM "xscreensaver-getimage-desktop"
107 extern const char *blurb (void);
117 x_ehandler (Display *dpy, XErrorEvent *error)
119 if (error->error_code == BadWindow || error->error_code == BadDrawable)
121 fprintf (stderr, "%s: target %s 0x%lx unexpectedly deleted\n", progname,
122 (error->error_code == BadWindow ? "window" : "pixmap"),
123 (unsigned long) error->resourceid);
127 fprintf (stderr, "\nX error in %s:\n", progname);
128 XmuPrintDefaultErrorMessage (dpy, error, stderr);
135 static Bool error_handler_hit_p = False;
138 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
140 error_handler_hit_p = True;
144 #ifndef USE_EXTERNAL_SCREEN_GRABBER
146 ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error)
148 if (error->error_code == BadMatch)
149 return ignore_all_errors_ehandler (dpy, error);
151 return x_ehandler (dpy, error);
153 #endif /* ! USE_EXTERNAL_SCREEN_GRABBER */
156 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
159 drawable_window_p (Display *dpy, Drawable d)
161 XErrorHandler old_handler;
162 XWindowAttributes xgwa;
165 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
166 error_handler_hit_p = False;
167 XGetWindowAttributes (dpy, d, &xgwa);
169 XSetErrorHandler (old_handler);
172 if (!error_handler_hit_p)
173 return True; /* It's a Window. */
175 return False; /* It's a Pixmap, or an invalid ID. */
179 /* Returns true if the window is the root window, or a virtual root window,
180 but *not* the xscreensaver window. That is, if it's a "real" desktop
181 root window of some kind.
184 root_window_p (Screen *screen, Window window)
186 Display *dpy = DisplayOfScreen (screen);
189 unsigned long nitems, bytesafter;
190 unsigned char *version;
192 if (window != RootWindowOfScreen (screen))
195 if (XGetWindowProperty (dpy, window,
196 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
197 0, 1, False, XA_STRING,
198 &type, &format, &nitems, &bytesafter,
208 /* Clear the window or pixmap to black, or its background color.
211 clear_drawable (Screen *screen, Drawable drawable)
213 Display *dpy = DisplayOfScreen (screen);
218 unsigned int w, h, bw, d;
219 XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
221 /* The window might have no-op background of None, so to clear it,
222 draw a black rectangle first, then do XClearWindow (in case the
223 actual background color is non-black...) */
225 /* #### really we should allocate "black" instead, but I'm lazy... */
226 gcv.foreground = BlackPixelOfScreen (screen);
227 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
228 XFillRectangle (dpy, drawable, gc, 0, 0, w, h);
230 if (drawable_window_p (dpy, drawable))
231 XClearWindow (dpy, (Window) drawable);
236 /* Figure out what kind of scaling/positioning we ought to do to display
237 a src-sized image in a dest-sized window/pixmap. Returns the width
238 and height to which the image should be scaled, and the position where
239 it should be displayed to center it.
242 compute_image_scaling (int src_w, int src_h,
243 int dest_w, int dest_h,
245 int *scaled_from_x_ret, int *scaled_from_y_ret,
246 int *scaled_to_x_ret, int *scaled_to_y_ret,
247 int *scaled_w_ret, int *scaled_h_ret)
249 int srcx, srcy, destx, desty;
251 Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
252 (src_h == dest_h && src_w <= dest_w));
254 if (!exact_fit_p) /* scale the image up or down */
256 float rw = (float) dest_w / src_w;
257 float rh = (float) dest_h / src_h;
258 float r = (rw < rh ? rw : rh);
264 /* this optimization breaks things */
265 if (pct < 95 || pct > 105) /* don't scale if it's close */
269 fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
270 progname, pct, src_w, src_h, tw, th);
276 /* Center the image on the window/pixmap. */
279 destx = (dest_w - src_w) / 2;
280 desty = (dest_h - src_h) / 2;
281 if (destx < 0) srcx = -destx, destx = 0;
282 if (desty < 0) srcy = -desty, desty = 0;
284 if (dest_w < src_w) src_w = dest_w;
285 if (dest_h < src_h) src_h = dest_h;
287 *scaled_w_ret = src_w;
288 *scaled_h_ret = src_h;
289 *scaled_from_x_ret = srcx;
290 *scaled_from_y_ret = srcy;
291 *scaled_to_x_ret = destx;
292 *scaled_to_y_ret = desty;
295 fprintf (stderr, "%s: displaying %dx%d image at %d,%d in %dx%d.\n",
296 progname, src_w, src_h, destx, desty, dest_w, dest_h);
300 /* Scales an XImage, modifying it in place.
301 This doesn't do dithering or smoothing, so it might have artifacts.
302 If out of memory, returns False, and the XImage will have been
305 #if !defined(USE_EXTERNAL_SCREEN_GRABBER) || defined(HAVE_JPEGLIB)
307 scale_ximage (Screen *screen, Visual *visual,
308 XImage *ximage, int new_width, int new_height)
310 Display *dpy = DisplayOfScreen (screen);
311 int depth = visual_depth (screen, visual);
313 double xscale, yscale;
315 XImage *ximage2 = XCreateImage (dpy, visual, depth,
317 new_width, new_height, 8, 0);
318 ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
322 fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
324 ximage->width, ximage->height,
325 ximage2->width, ximage2->height);
326 if (ximage->data) free (ximage->data);
327 if (ximage2->data) free (ximage2->data);
330 XDestroyImage (ximage);
331 XDestroyImage (ximage2);
335 /* Brute force scaling... */
336 xscale = (double) ximage->width / ximage2->width;
337 yscale = (double) ximage->height / ximage2->height;
338 for (y = 0; y < ximage2->height; y++)
339 for (x = 0; x < ximage2->width; x++)
340 XPutPixel (ximage2, x, y,
341 XGetPixel (ximage, x * xscale, y * yscale));
346 (*ximage) = (*ximage2);
349 XDestroyImage (ximage2);
353 #endif /* !USE_EXTERNAL_SCREEN_GRABBER || HAVE_JPEGLIB */
356 #ifdef HAVE_GDK_PIXBUF
358 /* Reads the given image file and renders it on the Drawable, using GDK.
359 Returns False if it fails.
362 read_file_gdk (Screen *screen, Window window, Drawable drawable,
363 const char *filename, Bool verbose_p,
364 XRectangle *geom_ret)
367 Display *dpy = DisplayOfScreen (screen);
368 unsigned int win_width, win_height, win_depth;
371 # endif /* HAVE_GTK2 */
373 /* Find the size of the Drawable. */
378 XGetGeometry (dpy, drawable,
379 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
382 gdk_pixbuf_xlib_init_with_depth (dpy, screen_number (screen), win_depth);
384 # if !GLIB_CHECK_VERSION(2, 36 ,0)
387 # else /* !HAVE_GTK2 */
388 xlib_rgb_init (dpy, screen);
389 # endif /* !HAVE_GTK2 */
391 pb = gdk_pixbuf_new_from_file (filename
394 # endif /* HAVE_GTK2 */
399 fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
401 if (gerr && gerr->message && *gerr->message)
402 fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
403 # endif /* HAVE_GTK2 */
408 int w = gdk_pixbuf_get_width (pb);
409 int h = gdk_pixbuf_get_height (pb);
410 int srcx, srcy, destx, desty, w2, h2;
413 # ifdef HAVE_GDK_PIXBUF_APPLY_EMBEDDED_ORIENTATION
417 pb = gdk_pixbuf_apply_embedded_orientation (opb);
418 g_object_unref (opb);
419 w = gdk_pixbuf_get_width (pb);
420 h = gdk_pixbuf_get_height (pb);
421 if (verbose_p && (w != ow || h != oh))
422 fprintf (stderr, "%s: rotated %dx%d to %dx%d\n",
423 progname, ow, oh, w, h);
427 compute_image_scaling (w, h, win_width, win_height, verbose_p,
428 &srcx, &srcy, &destx, &desty, &w2, &h2);
429 if (w != w2 || h != h2)
431 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
432 GDK_INTERP_BILINEAR);
441 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
444 /* If we're rendering onto the root window (and it's not the
445 xscreensaver pseudo-root) then put the image in the window's
446 background. Otherwise, just paint the image onto the window.
448 bg_p = (window == drawable && root_window_p (screen, window));
454 drawable = XCreatePixmap (dpy, window,
455 win_width, win_height, win_depth);
456 gcv.foreground = BlackPixelOfScreen (screen);
457 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
458 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
462 clear_drawable (screen, drawable);
464 /* #### Note that this always uses the default colormap! Morons!
465 Owen says that in Gnome 2.0, I should try using
466 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
469 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
470 srcx, srcy, destx, desty,
472 GDK_PIXBUF_ALPHA_FULL, 127,
473 XLIB_RGB_DITHER_NORMAL,
477 XSetWindowBackgroundPixmap (dpy, window, drawable);
478 XClearWindow (dpy, window);
486 geom_ret->height = h;
494 #endif /* HAVE_GDK_PIXBUF */
500 /* Allocates a colormap that makes a PseudoColor or DirectColor
501 visual behave like a TrueColor visual of the same depth.
503 #### Duplicated in utils/grabscreen.c
506 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
509 Display *dpy = DisplayOfScreen (screen);
510 int nr, ng, nb, cells;
516 depth = visual_depth (screen, visual);
520 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
521 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
522 default: abort(); break;
525 memset(colors, 0, sizeof(colors));
526 for (r = 0; r < (1 << nr); r++)
527 for (g = 0; g < (1 << ng); g++)
528 for (b = 0; b < (1 << nb); b++)
530 i = (r | (g << nr) | (b << (nr + ng)));
532 colors[i].flags = DoRed|DoGreen|DoBlue;
535 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
536 (r << 4) | (r << 1));
537 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
538 (g << 4) | (g << 1));
539 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
540 (b << 8) | (b << 6) | (b << 4) |
545 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
546 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
547 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
554 int interleave = cells / 8; /* skip around, rather than allocating in
555 order, so that we get better coverage if
556 we can't allocated all of them. */
557 for (j = 0; j < interleave; j++)
558 for (i = 0; i < cells; i += interleave)
559 if (XAllocColor (dpy, cmap, &colors[i + j]))
563 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
564 progname, allocated, cells);
568 /* Find the pixel index that is closest to the given color
569 (using linear distance in RGB space -- which is far from the best way.)
571 #### Duplicated in utils/grabscreen.c
574 find_closest_pixel (XColor *colors, int ncolors,
575 unsigned long r, unsigned long g, unsigned long b)
577 unsigned long distance = ~0;
582 for (i = 0; i < ncolors; i++)
587 rd = r - colors[i].red;
588 gd = g - colors[i].green;
589 bd = b - colors[i].blue;
590 if (rd < 0) rd = -rd;
591 if (gd < 0) gd = -gd;
592 if (bd < 0) bd = -bd;
593 d = (rd << 1) + (gd << 2) + bd;
608 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
609 displayable with the given X colormap. The farther from a perfect
610 color cube the contents of the colormap are, the lossier the
611 transformation will be. No dithering is done.
613 #### Duplicated in utils/grabscreen.c
616 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
618 Display *dpy = DisplayOfScreen (screen);
619 unsigned long map[4097];
624 if (image->depth == 8)
626 else if (image->depth == 12)
631 memset(map, -1, sizeof(*map));
632 memset(colors, -1, sizeof(*colors));
634 for (i = 0; i < cells; i++)
636 XQueryColors (dpy, cmap, colors, cells);
639 fprintf(stderr, "%s: building color cube for %d bit image\n",
640 progname, image->depth);
642 for (i = 0; i < cells; i++)
644 unsigned short r, g, b;
648 /* "RRR GGG BB" In an 8 bit map. Convert that to
649 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
655 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
656 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
657 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
658 (b << 6) | (b << 4) | (b << 2) | b);
662 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
663 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
666 g = (i & 0x0F0) >> 4;
667 b = (i & 0xF00) >> 8;
669 r = (r << 12) | (r << 8) | (r << 4) | r;
670 g = (g << 12) | (g << 8) | (g << 4) | g;
671 b = (b << 12) | (b << 8) | (b << 4) | b;
674 map[i] = find_closest_pixel (colors, cells, r, g, b);
678 fprintf(stderr, "%s: remapping colors in %d bit image\n",
679 progname, image->depth);
681 for (y = 0; y < image->height; y++)
682 for (x = 0; x < image->width; x++)
684 unsigned long pixel = XGetPixel(image, x, y);
685 if (pixel >= cells) abort();
686 XPutPixel(image, x, y, map[pixel]);
691 /* If the file has a PPM (P6) on it, read it and return an XImage.
692 Otherwise, rewind the fd back to the beginning, and return 0.
695 maybe_read_ppm (Screen *screen, Visual *visual,
696 const char *filename, FILE *in, Bool verbose_p)
698 Display *dpy = DisplayOfScreen (screen);
699 int depth = visual_depth (screen, visual);
705 int x, y, w, h, maxval;
708 if (fstat (fileno (in), &st))
712 buf = (char *) malloc (bufsiz + 1);
715 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
716 progname, bufsiz, filename);
720 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
723 if (!strncmp (buf, "\107\111", 2))
725 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
726 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
730 else if (!strncmp (buf, "\211\120", 2))
732 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
733 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
738 if (strncmp (s, "P6", 2))
741 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
743 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
745 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
749 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
751 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
753 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
758 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
759 progname, filename, maxval);
763 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
766 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
767 if (!ximage || !ximage->data)
769 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
770 progname, ximage->width, ximage->height, filename);
776 while ((i = fread (s, 1, j, in)) > 0)
780 for (y = 0; y < ximage->height; y++)
781 for (x = 0; x < ximage->width; x++)
783 unsigned char r = buf[i++];
784 unsigned char g = buf[i++];
785 unsigned char b = buf[i++];
789 pixel = (r << 16) | (g << 8) | b;
791 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
792 else if (depth == 12)
793 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
794 else if (depth == 16 || depth == 15)
795 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
799 XPutPixel (ximage, x, y, pixel);
807 if (ximage && ximage->data)
812 if (ximage) XDestroyImage (ximage);
813 fseek (in, 0, SEEK_SET);
819 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
820 const char *filename;
825 } getimg_jpg_error_mgr;
829 jpg_output_message (j_common_ptr cinfo)
831 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
832 char buf[JMSG_LENGTH_MAX];
833 cinfo->err->format_message (cinfo, buf);
834 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
839 jpg_error_exit (j_common_ptr cinfo)
841 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
842 cinfo->err->output_message (cinfo);
843 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
845 XSync (DisplayOfScreen (err->screen), False);
850 /* Reads a JPEG file, returns an RGB XImage of it.
853 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
854 Colormap cmap, const char *filename, Bool verbose_p)
856 Display *dpy = DisplayOfScreen (screen);
857 int depth = visual_depth (screen, visual);
861 struct jpeg_decompress_struct cinfo;
862 getimg_jpg_error_mgr jerr;
863 JSAMPARRAY scanbuf = 0;
866 jerr.filename = filename;
867 jerr.screen = screen;
868 jerr.visual = visual;
869 jerr.drawable = drawable;
872 if (! (depth >= 15 || depth == 12 || depth == 8))
874 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
878 in = fopen (filename, "rb");
881 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
885 /* Check to see if it's a PPM, and if so, read that instead of using
886 the JPEG library. Yeah, this is all modular and stuff.
888 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
894 cinfo.err = jpeg_std_error (&jerr.pub);
895 jerr.pub.output_message = jpg_output_message;
896 jerr.pub.error_exit = jpg_error_exit;
898 jpeg_create_decompress (&cinfo);
899 jpeg_stdio_src (&cinfo, in);
900 jpeg_read_header (&cinfo, TRUE);
902 /* set some decode parameters */
903 cinfo.out_color_space = JCS_RGB;
904 cinfo.quantize_colors = FALSE;
906 jpeg_start_decompress (&cinfo);
908 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
909 cinfo.output_width, cinfo.output_height,
912 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
914 if (ximage && ximage->data)
915 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
916 cinfo.rec_outbuf_height *
918 cinfo.output_components,
920 if (!ximage || !ximage->data || !scanbuf)
922 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
923 progname, ximage->width, ximage->height, filename);
928 while (cinfo.output_scanline < cinfo.output_height)
930 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
932 for (i = 0; i < n; i++)
935 for (x = 0; x < ximage->width; x++)
937 int j = x * cinfo.output_components;
938 unsigned char r = scanbuf[i][j];
939 unsigned char g = scanbuf[i][j+1];
940 unsigned char b = scanbuf[i][j+2];
944 pixel = (r << 16) | (g << 8) | b;
946 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
947 else if (depth == 12)
948 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
949 else if (depth == 15)
950 /* Gah! I don't understand why these are in the other
952 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
953 else if (depth == 16)
954 pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
958 XPutPixel (ximage, x, y, pixel);
964 if (cinfo.output_scanline < cinfo.output_height)
965 /* don't goto FAIL -- we might have viewable partial data. */
966 jpeg_abort_decompress (&cinfo);
968 jpeg_finish_decompress (&cinfo);
970 jpeg_destroy_decompress (&cinfo);
978 if (ximage && ximage->data)
983 if (ximage) XDestroyImage (ximage);
984 if (scanbuf) free (scanbuf);
989 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
990 Returns False if it fails.
993 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
994 const char *filename, Bool verbose_p,
995 XRectangle *geom_ret)
997 Display *dpy = DisplayOfScreen (screen);
1002 unsigned int win_width, win_height, win_depth;
1003 int srcx, srcy, destx, desty, w2, h2;
1005 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
1010 XWindowAttributes xgwa;
1011 XGetWindowAttributes (dpy, window, &xgwa);
1012 visual = xgwa.visual;
1013 cmap = xgwa.colormap;
1014 XGetGeometry (dpy, drawable,
1015 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
1018 /* Make sure we're not on some weirdo visual...
1020 class = visual_class (screen, visual);
1021 depth = visual_depth (screen, visual);
1022 if ((class == PseudoColor || class == DirectColor) &&
1023 (depth != 8 && depth != 12))
1025 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
1032 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
1033 filename, verbose_p);
1034 if (!ximage) return False;
1036 /* Scale it, if necessary...
1038 compute_image_scaling (ximage->width, ximage->height,
1039 win_width, win_height, verbose_p,
1040 &srcx, &srcy, &destx, &desty, &w2, &h2);
1041 if (ximage->width != w2 || ximage->height != h2)
1042 if (! scale_ximage (screen, visual, ximage, w2, h2))
1045 /* Allocate a colormap, if we need to...
1047 if (class == PseudoColor || class == DirectColor)
1049 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
1050 remap_image (screen, cmap, ximage, verbose_p);
1053 /* Finally, put the resized image on the window.
1059 /* If we're rendering onto the root window (and it's not the xscreensaver
1060 pseudo-root) then put the image in the window's background. Otherwise,
1061 just paint the image onto the window.
1063 if (window == drawable && root_window_p (screen, window))
1065 Pixmap bg = XCreatePixmap (dpy, window,
1066 win_width, win_height, win_depth);
1067 gcv.foreground = BlackPixelOfScreen (screen);
1068 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1069 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1070 XPutImage (dpy, bg, gc, ximage,
1071 srcx, srcy, destx, desty, ximage->width, ximage->height);
1072 XSetWindowBackgroundPixmap (dpy, window, bg);
1073 XClearWindow (dpy, window);
1077 gc = XCreateGC (dpy, drawable, 0, &gcv);
1078 clear_drawable (screen, drawable);
1079 XPutImage (dpy, drawable, gc, ximage,
1080 srcx, srcy, destx, desty, ximage->width, ximage->height);
1088 geom_ret->x = destx;
1089 geom_ret->y = desty;
1090 geom_ret->width = ximage->width;
1091 geom_ret->height = ximage->height;
1094 free (ximage->data);
1096 XDestroyImage (ximage);
1101 #endif /* HAVE_JPEGLIB */
1104 /* Reads the given image file and renders it on the Drawable.
1105 Returns False if it fails.
1108 display_file (Screen *screen, Window window, Drawable drawable,
1109 const char *filename, Bool verbose_p,
1110 XRectangle *geom_ret)
1113 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1115 # if defined(HAVE_GDK_PIXBUF)
1116 if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret))
1118 # elif defined(HAVE_JPEGLIB)
1119 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p,
1122 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1123 /* shouldn't get here if we have no image-loading methods available. */
1125 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1131 /* Invokes a sub-process and returns its output (presumably, a file to
1132 load.) Free the string when done. 'grab_type' controls which program
1133 to run. Returned pathname may be relative to 'directory', or absolute.
1136 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1139 Display *dpy = DisplayOfScreen (screen);
1150 av[ac++] = GETIMAGE_FILE_PROGRAM;
1152 av[ac++] = "--verbose";
1153 av[ac++] = "--name";
1154 av[ac++] = (char *) directory;
1158 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1160 av[ac++] = "--verbose";
1161 av[ac++] = "--name";
1164 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1166 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1168 av[ac++] = "--verbose";
1169 av[ac++] = "--name";
1181 fprintf (stderr, "%s: executing:", progname);
1182 for (i = 0; i < ac; i++)
1183 fprintf (stderr, " %s", av[i]);
1184 fprintf (stderr, "\n");
1189 sprintf (buf, "%s: error creating pipe", progname);
1197 switch ((int) (forked = fork ()))
1201 sprintf (buf, "%s: couldn't fork", progname);
1209 close (in); /* don't need this one */
1210 close (ConnectionNumber (dpy)); /* close display fd */
1212 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1214 sprintf (buf, "%s: could not dup() a new stdout", progname);
1215 exit (-1); /* exits fork */
1218 execvp (av[0], av); /* shouldn't return. */
1219 exit (-1); /* exits fork */
1225 int wait_status = 0;
1226 FILE *f = fdopen (in, "r");
1230 close (out); /* don't need this one */
1232 if (! fgets (buf, sizeof(buf)-1, f))
1236 /* Wait for the child to die. */
1237 waitpid (-1, &wait_status, 0);
1240 while (L && buf[L-1] == '\n')
1250 /* Program returned path relative to directory. Prepend dir
1251 to buf so that we can properly stat it. */
1252 strcpy (buf, directory);
1253 if (directory[strlen(directory)-1] != '/')
1260 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1274 /* Returns a pathname to an image file. Free the string when you're done.
1277 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1279 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1283 /* Grabs a video frame to a file, and returns a pathname to that file.
1284 Delete that file when you are done with it (and free the string.)
1287 get_video_filename (Screen *screen, Bool verbose_p)
1289 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1292 /* Grabs a desktop image to a file, and returns a pathname to that file.
1293 Delete that file when you are done with it (and free the string.)
1295 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1297 get_desktop_filename (Screen *screen, Bool verbose_p)
1299 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1301 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1304 /* Grabs a video frame, and renders it on the Drawable.
1305 Returns False if it fails;
1308 display_video (Screen *screen, Window window, Drawable drawable,
1309 Bool verbose_p, XRectangle *geom_ret)
1311 char *filename = get_video_filename (screen, verbose_p);
1317 fprintf (stderr, "%s: video grab failed.\n", progname);
1321 status = display_file (screen, window, drawable, filename, verbose_p,
1324 if (unlink (filename))
1327 sprintf (buf, "%s: rm %.100s", progname, filename);
1331 fprintf (stderr, "%s: rm %s\n", progname, filename);
1333 if (filename) free (filename);
1338 /* Grabs a desktop screen shot onto the window and the drawable.
1339 If the window and drawable are not the same size, the image in
1340 the drawable is scaled to fit.
1341 Returns False if it fails.
1344 display_desktop (Screen *screen, Window window, Drawable drawable,
1345 Bool verbose_p, XRectangle *geom_ret)
1347 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1349 Display *dpy = DisplayOfScreen (screen);
1350 Bool top_p = top_level_window_p (screen, window);
1357 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1358 (unsigned long) window);
1359 XUnmapWindow (dpy, window);
1363 filename = get_desktop_filename (screen, verbose_p);
1368 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1369 (unsigned long) window);
1370 XMapRaised (dpy, window);
1377 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1381 status = display_file (screen, window, drawable, filename, verbose_p,
1384 if (unlink (filename))
1387 sprintf (buf, "%s: rm %.100s", progname, filename);
1391 fprintf (stderr, "%s: rm %s\n", progname, filename);
1393 if (filename) free (filename);
1396 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1398 Display *dpy = DisplayOfScreen (screen);
1400 XWindowAttributes xgwa;
1403 unsigned int pw, ph, pbw, pd;
1404 int srcx, srcy, destx, desty, w2, h2;
1408 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1409 grabscreen_verbose();
1412 XGetWindowAttributes (dpy, window, &xgwa);
1413 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1415 grab_screen_image_internal (screen, window);
1417 compute_image_scaling (xgwa.width, xgwa.height,
1419 &srcx, &srcy, &destx, &desty, &w2, &h2);
1421 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1423 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1424 XCopyArea (dpy, window, drawable, gc,
1425 0, 0, xgwa.width, xgwa.height, 0, 0);
1428 else /* size mismatch -- must scale client-side images to fit drawable */
1432 XErrorHandler old_handler;
1435 old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
1436 error_handler_hit_p = False;
1438 /* This can return BadMatch if the window is not fully on screen.
1439 Trap that error and return color bars in that case.
1440 (Note that this only happens with XGetImage, not with XCopyArea:
1441 yet another totally gratuitous inconsistency in X, thanks.)
1443 ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1447 XSetErrorHandler (old_handler);
1450 if (error_handler_hit_p)
1454 fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
1455 progname, (unsigned int) window);
1459 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1462 gc = XCreateGC (dpy, drawable, 0, &gcv);
1463 clear_drawable (screen, drawable);
1464 XPutImage (dpy, drawable, gc, ximage,
1465 srcx, srcy, destx, desty, ximage->width, ximage->height);
1466 XDestroyImage (ximage);
1472 geom_ret->x = destx;
1473 geom_ret->y = desty;
1474 geom_ret->width = w2;
1475 geom_ret->height = h2;
1481 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1485 /* Whether the given Drawable is unreasonably small.
1488 drawable_miniscule_p (Display *dpy, Drawable drawable)
1492 unsigned int bw, d, w = 0, h = 0;
1493 XGetGeometry (dpy, drawable, &root, &xx, &yy, &w, &h, &bw, &d);
1494 return (w < 32 || h < 32);
1498 /* Grabs an image (from a file, video, or the desktop) and renders it on
1499 the Drawable. If `file' is specified, always use that file. Otherwise,
1500 select randomly, based on the other arguments.
1503 get_image (Screen *screen,
1504 Window window, Drawable drawable,
1512 Display *dpy = DisplayOfScreen (screen);
1513 grab_type which = GRAB_BARS;
1515 const char *file_prop = 0;
1517 XRectangle geom = { 0, 0, 0, 0 };
1519 if (! drawable_window_p (dpy, window))
1521 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1522 progname, (unsigned long) window);
1526 /* Make sure the Screen and the Window correspond. */
1528 XWindowAttributes xgwa;
1529 XGetWindowAttributes (dpy, window, &xgwa);
1530 screen = xgwa.screen;
1533 if (file && stat (file, &st))
1535 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1541 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1542 progname, desk_p ? "True" : "False");
1543 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1544 progname, video_p ? "True" : "False");
1545 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1546 progname, image_p ? "True" : "False");
1547 fprintf (stderr, "%s: imageDirectory: %s\n",
1548 progname, (file ? file : dir ? dir : ""));
1551 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1552 image_p = False; /* can't load images from files... */
1553 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1554 desk_p = False; /* ...or from desktops grabbed to files. */
1560 "%s: image file loading not available at compile-time\n",
1562 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1565 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1573 else if (!dir || !*dir)
1575 if (verbose_p && image_p)
1577 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1582 /* If the target drawable is really small, no good can come of that.
1583 Always do colorbars in that case.
1585 if (drawable_miniscule_p (dpy, drawable))
1593 # error Error! This file definitely needs vroot.h!
1596 /* We can grab desktop images (using the normal X11 method) if:
1597 - the window is the real root window;
1598 - the window is a toplevel window.
1599 We cannot grab desktop images that way if:
1600 - the window is a non-top-level window.
1602 Under X11 on MacOS, desktops are just like loaded image files.
1603 Under Cocoa on MacOS, this code is not used at all.
1605 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1608 if (!top_level_window_p (screen, window))
1613 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1614 progname, (unsigned int) window);
1617 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1619 if (! (desk_p || video_p || image_p))
1625 /* Loop until we get one that's permitted.
1626 If files or video are permitted, do them more often
1629 D+V+I: 10% + 45% + 45%.
1635 n = (random() % 100);
1636 if (++i > 300) abort();
1637 else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */
1638 else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */
1639 else if (image_p) which = GRAB_FILE; /* 45% */
1644 /* If we're to search a directory to find an image file, do so now.
1646 if (which == GRAB_FILE && !file)
1648 file = get_filename (screen, dir, verbose_p);
1653 fprintf (stderr, "%s: no image files found.\n", progname);
1657 /* Now actually render something.
1663 XWindowAttributes xgwa;
1666 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1667 XGetWindowAttributes (dpy, window, &xgwa);
1668 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1671 if (! file_prop) file_prop = "";
1677 if (! display_desktop (screen, window, drawable, verbose_p, &geom))
1679 file_prop = "desktop";
1683 if (*file && *file != '/') /* pathname is relative to dir. */
1685 if (absfile) free (absfile);
1686 absfile = malloc (strlen(dir) + strlen(file) + 10);
1687 strcpy (absfile, dir);
1688 if (dir[strlen(dir)-1] != '/')
1689 strcat (absfile, "/");
1690 strcat (absfile, file);
1692 if (! display_file (screen, window, drawable,
1693 (absfile ? absfile : file),
1700 if (! display_video (screen, window, drawable, verbose_p, &geom))
1702 file_prop = "video";
1711 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1712 if (file_prop && *file_prop)
1714 char *f2 = strdup (file_prop);
1716 /* Take the extension off of the file name. */
1717 /* Duplicated in utils/grabclient.c. */
1718 char *slash = strrchr (f2, '/');
1719 char *dot = strrchr ((slash ? slash : f2), '.');
1721 /* Replace slashes with newlines */
1722 /* while ((dot = strchr(f2, '/'))) *dot = '\n'; */
1723 /* Replace slashes with spaces */
1724 /* while ((dot = strchr(f2, '/'))) *dot = ' '; */
1726 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1727 (unsigned char *) f2, strlen(f2));
1731 XDeleteProperty (dpy, window, a);
1733 a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
1737 sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
1738 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1739 (unsigned char *) gstr, strlen (gstr));
1742 XDeleteProperty (dpy, window, a);
1745 if (absfile) free (absfile);
1752 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1753 XrmRepresentation *type, XrmValue *value, XPointer closure)
1756 for (i = 0; quarks[i]; i++)
1758 if (bindings[i] == XrmBindTightly)
1759 fprintf (stderr, (i == 0 ? "" : "."));
1760 else if (bindings[i] == XrmBindLoosely)
1761 fprintf (stderr, "*");
1763 fprintf (stderr, " ??? ");
1764 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1767 fprintf (stderr, ": %s\n", (char *) value->addr);
1774 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1778 " %s puts an image on the given window or pixmap.\n" \
1780 " It is used by those xscreensaver demos that operate on images.\n" \
1781 " The image may be a file loaded from disk, a frame grabbed from\n" \
1782 " the system's video camera, or a screenshot of the desktop,\n" \
1783 " depending on command-line options or the ~/.xscreensaver file.\n" \
1785 " Options include:\n" \
1787 " -display host:dpy.screen which display to use\n" \
1788 " -root draw to the root window\n" \
1789 " -verbose print diagnostics\n" \
1790 " -images / -no-images whether to allow image file loading\n" \
1791 " -video / -no-video whether to allow video grabs\n" \
1792 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1793 " -directory <path> where to find image files to load\n" \
1794 " -file <filename> load this image file\n" \
1796 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1797 " defaults for these options in your ~/.xscreensaver file.\n" \
1801 main (int argc, char **argv)
1803 saver_preferences P;
1807 char *oprogname = progname;
1811 Window window = (Window) 0;
1812 Drawable drawable = (Drawable) 0;
1813 const char *window_str = 0;
1814 const char *drawable_str = 0;
1819 s = strrchr (progname, '/');
1820 if (s) progname = s+1;
1821 oprogname = progname;
1823 /* half-assed way of avoiding buffer-overrun attacks. */
1824 if (strlen (progname) >= 100) progname[100] = 0;
1827 # error Error! This file definitely needs vroot.h!
1830 /* Get the version number, for error messages. */
1832 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1833 char *s1, *s2, *s3, *s4;
1834 s1 = (char *) strchr(v, ' '); s1++;
1835 s2 = (char *) strchr(s1, ' ');
1836 s3 = (char *) strchr(v, '('); s3++;
1837 s4 = (char *) strchr(s3, ')');
1840 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1844 /* We must read exactly the same resources as xscreensaver.
1845 That means we must have both the same progclass *and* progname,
1846 at least as far as the resource database is concerned. So,
1847 put "xscreensaver" in argv[0] while initializing Xt.
1849 progname = argv[0] = "xscreensaver";
1851 /* allow one dash or two. */
1852 for (i = 1; i < argc; i++)
1853 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1855 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1857 dpy = XtDisplay (toplevel);
1858 screen = XtScreen (toplevel);
1859 db = XtDatabase (dpy);
1860 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1861 XSetErrorHandler (x_ehandler);
1864 /* Randomize -- only need to do this here because this program
1865 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1866 # undef ya_rand_init
1869 memset (&P, 0, sizeof(P));
1871 load_init_file (dpy, &P);
1873 progname = argv[0] = oprogname;
1875 for (i = 1; i < argc; i++)
1880 /* Have to re-process these, or else the .xscreensaver file
1881 has priority over the command line...
1883 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1885 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1886 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1887 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1888 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1889 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1890 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1891 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1892 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1893 P.image_directory = argv[++i];
1894 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1898 fprintf (stderr, "%s: both %s and %s specified?\n",
1899 progname, argv[i], window_str);
1902 window_str = argv[i];
1903 window = VirtualRootWindowOfScreen (screen);
1905 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1906 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1911 fprintf (stderr, "%s: both %s and %s specified?\n",
1912 progname, drawable_str, argv[i]);
1917 drawable_str = argv[i];
1918 drawable = (Drawable) w;
1922 window_str = argv[i];
1923 window = (Window) w;
1928 if (argv[i][0] == '-')
1929 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1932 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1936 __extension__ /* don't warn about "string length is greater than
1937 the length ISO C89 compilers are required to
1938 support" in the usage string... */
1940 fprintf (stderr, USAGE, progname, version, progname);
1947 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1953 if (P.verbose_p) /* Print out all the resources we can see. */
1955 XrmName name = { 0 };
1956 XrmClass class = { 0 };
1958 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1959 (XtPointer) &count);
1963 if (!window) abort();
1964 if (!drawable) drawable = window;
1966 get_image (screen, window, drawable, P.verbose_p,
1967 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1968 P.image_directory, file);