http://www.mirrorservice.org/sites/master.us.finkmirrors.net/distfiles/md5/fa43fdd68d...
[xscreensaver] / driver / xscreensaver-getimage.c
1 /* xscreensaver, Copyright (c) 2001, 2002, 2003 by Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  */
11
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
15    input.
16  */
17
18 #include "utils.h"
19
20 #include <X11/Intrinsic.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25
26 #ifdef HAVE_SYS_WAIT_H
27 # include <sys/wait.h>          /* for waitpid() and associated macros */
28 #endif
29
30 #ifdef HAVE_XMU
31 # ifndef VMS
32 #  include <X11/Xmu/Error.h>
33 # else /* VMS */
34 #  include <Xmu/Error.h>
35 # endif
36 #else
37 # include "xmu.h"
38 #endif
39
40 #include "yarandom.h"
41 #include "grabscreen.h"
42 #include "resources.h"
43 #include "colorbars.h"
44 #include "visual.h"
45 #include "prefs.h"
46 #include "vroot.h"
47
48 #ifdef HAVE_GDK_PIXBUF
49 # undef HAVE_JPEGLIB
50 # ifdef HAVE_GTK2
51 #  include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
52 # else  /* !HAVE_GTK2 */
53 #  include <gdk-pixbuf/gdk-pixbuf-xlib.h>
54 # endif /* !HAVE_GTK2 */
55 #endif /* HAVE_GDK_PIXBUF */
56
57 #ifdef HAVE_JPEGLIB
58 # undef HAVE_GDK_PIXBUF
59 # include <jpeglib.h>
60 #endif
61
62
63 static char *defaults[] = {
64 #include "../driver/XScreenSaver_ad.h"
65  0
66 };
67
68
69
70 char *progname = 0;
71 char *progclass = "XScreenSaver";
72 XrmDatabase db;
73 XtAppContext app;
74
75 extern void grabscreen_verbose (void);
76
77
78 #define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
79 #define GETIMAGE_FILE_PROGRAM  "xscreensaver-getimage-file"
80
81 const char *
82 blurb (void)
83 {
84   return progname;
85 }
86
87
88 static int
89 x_ehandler (Display *dpy, XErrorEvent *error)
90 {
91   fprintf (stderr, "\nX error in %s:\n", progname);
92   XmuPrintDefaultErrorMessage (dpy, error, stderr);
93   exit (-1);
94   return 0;
95 }
96
97
98 static Bool error_handler_hit_p = False;
99
100 static int
101 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
102 {
103   error_handler_hit_p = True;
104   return 0;
105 }
106
107
108 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
109  */
110 static Bool
111 drawable_window_p (Display *dpy, Drawable d)
112 {
113   XErrorHandler old_handler;
114   XWindowAttributes xgwa;
115
116   XSync (dpy, False);
117   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
118   error_handler_hit_p = False;
119   XGetWindowAttributes (dpy, d, &xgwa);
120   XSync (dpy, False);
121   XSetErrorHandler (old_handler);
122   XSync (dpy, False);
123
124   if (!error_handler_hit_p)
125     return True;   /* It's a Window. */
126   else
127     return False;  /* It's a Pixmap, or an invalid ID. */
128 }
129
130
131 /* Clear the window or pixmap to black, or its background color.
132  */
133 static void
134 clear_drawable (Screen *screen, Drawable drawable)
135 {
136   Display *dpy = DisplayOfScreen (screen);
137   XGCValues gcv;
138   GC gc;
139   Window root;
140   int x, y;
141   unsigned int w, h, bw, d;
142   XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
143
144   /* The window might have no-op background of None, so to clear it,
145      draw a black rectangle first, then do XClearWindow (in case the
146      actual background color is non-black...) */
147
148   /* #### really we should allocate "black" instead, but I'm lazy... */
149   gcv.foreground = BlackPixelOfScreen (screen);
150   gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
151   XFillRectangle (dpy, drawable, gc, 0, 0, w, h);
152   XFreeGC (dpy, gc);
153   if (drawable_window_p (dpy, drawable))
154     XClearWindow (dpy, (Window) drawable);
155   XFlush (dpy);
156 }
157
158
159 /* Figure out what kind of scaling/positioning we ought to do to display
160    a src-sized image in a dest-sized window/pixmap.  Returns the width
161    and height to which the image should be scaled, and the position where
162    it should be displayed to center it.
163  */
164 static void
165 compute_image_scaling (int src_w, int src_h,
166                        int dest_w, int dest_h,
167                        Bool verbose_p,
168                        int *scaled_from_x_ret, int *scaled_from_y_ret,
169                        int *scaled_to_x_ret, int *scaled_to_y_ret,
170                        int *scaled_w_ret, int *scaled_h_ret)
171 {
172   int srcx, srcy, destx, desty;
173
174   Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
175                       (src_h == dest_h && src_w <= dest_w));
176
177   if (!exact_fit_p)  /* scale the image up or down */
178     {
179       float rw = (float) dest_w  / src_w;
180       float rh = (float) dest_h / src_h;
181       float r = (rw < rh ? rw : rh);
182       int tw = src_w * r;
183       int th = src_h * r;
184       int pct = (r * 100);
185
186       if (pct < 95 || pct > 105)  /* don't scale if it's close */
187         {
188           if (verbose_p)
189             fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
190                      progname, pct, src_w, src_h, tw, th);
191           src_w = tw;
192           src_h = th;
193         }
194     }
195
196   /* Center the image on the window/pixmap. */
197   srcx = 0;
198   srcy = 0;
199   destx = (dest_w - src_w) / 2;
200   desty = (dest_h - src_h) / 2;
201   if (destx < 0) srcx = -destx, destx = 0;
202   if (desty < 0) srcy = -desty, desty = 0;
203
204   if (dest_w < src_w) src_w = dest_w;
205   if (dest_h < src_h) src_h = dest_h;
206
207   *scaled_w_ret = src_w;
208   *scaled_h_ret = src_h;
209   *scaled_from_x_ret = srcx;
210   *scaled_from_y_ret = srcy;
211   *scaled_to_x_ret = destx;
212   *scaled_to_y_ret = desty;
213
214   if (verbose_p)
215     fprintf (stderr, "%s: displaying %dx%d image at %d,%d.\n",
216              progname, src_w, src_h, destx, desty);
217 }
218
219
220 #ifdef HAVE_GDK_PIXBUF
221
222 /* Reads the given image file and renders it on the Drawable, using GDK.
223    Returns False if it fails.
224  */
225 static Bool
226 read_file_gdk (Screen *screen, Window window, Drawable drawable,
227                const char *filename, Bool verbose_p)
228 {
229   GdkPixbuf *pb;
230   Display *dpy = DisplayOfScreen (screen);
231   unsigned int win_width, win_height;
232 # ifdef HAVE_GTK2
233   GError *gerr = 0;
234 # endif /* HAVE_GTK2 */
235
236   {
237     Window root;
238     int x, y;
239     unsigned int bw, d;
240     XWindowAttributes xgwa;
241     XGetWindowAttributes (dpy, window, &xgwa);
242     screen = xgwa.screen;
243     XGetGeometry (dpy, drawable,
244                   &root, &x, &y, &win_width, &win_height, &bw, &d);
245   }
246
247   gdk_pixbuf_xlib_init (dpy, screen_number (screen));
248 # ifdef HAVE_GTK2
249   g_type_init();
250 # else  /* !HAVE_GTK2 */
251   xlib_rgb_init (dpy, screen);
252 # endif /* !HAVE_GTK2 */
253
254   pb = gdk_pixbuf_new_from_file (filename
255 # ifdef HAVE_GTK2
256                                  , &gerr
257 # endif /* HAVE_GTK2 */
258                                  );
259
260   if (!pb)
261     {
262       fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
263 #  ifdef HAVE_GTK2
264       if (gerr && gerr->message && *gerr->message)
265         fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
266 #  endif /* HAVE_GTK2 */
267       return False;
268     }
269   else
270     {
271       int w = gdk_pixbuf_get_width (pb);
272       int h = gdk_pixbuf_get_height (pb);
273       int srcx, srcy, destx, desty, w2, h2;
274
275       compute_image_scaling (w, h, win_width, win_height, verbose_p,
276                              &srcx, &srcy, &destx, &desty, &w2, &h2);
277       if (w != w2 || h != h2)
278         {
279           GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
280                                                     GDK_INTERP_BILINEAR);
281           if (pb2)
282             {
283               gdk_pixbuf_unref (pb);
284               pb = pb2;
285               w = w2;
286               h = h2;
287             }
288           else
289             fprintf (stderr, "%s: out of memory when scaling?\n", progname);
290         }
291
292       clear_drawable (screen, drawable);
293
294       /* #### Note that this always uses the default colormap!  Morons!
295          Owen says that in Gnome 2.0, I should try using
296          gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
297          But I haven't tried.
298        */
299       gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
300                                                 srcx, srcy, destx, desty,
301                                                 w, h,
302                                                 GDK_PIXBUF_ALPHA_FULL, 127,
303                                                 XLIB_RGB_DITHER_NORMAL,
304                                                 0, 0);
305       XSync (dpy, False);
306     }
307
308   return True;
309 }
310
311 #endif /* HAVE_GDK_PIXBUF */
312
313
314
315 #ifdef HAVE_JPEGLIB
316
317 /* Allocates a colormap that makes a PseudoColor or DirectColor
318    visual behave like a TrueColor visual of the same depth.
319  */
320 static void
321 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
322                          Bool verbose_p)
323 {
324   Display *dpy = DisplayOfScreen (screen);
325   int nr, ng, nb, cells;
326   int r, g, b;
327   int depth;
328   XColor colors[4097];
329   int i;
330
331   depth = visual_depth (screen, visual);
332
333   switch (depth)
334     {
335     case 8:  nr = 3; ng = 3; nb = 2; cells = 256;  break;
336     case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
337     default: abort(); break;
338     }
339
340   memset(colors, 0, sizeof(colors));
341   for (r = 0; r < (1 << nr); r++)
342     for (g = 0; g < (1 << ng); g++)
343       for (b = 0; b < (1 << nb); b++)
344         {
345           i = (r | (g << nr) | (b << (nr + ng)));
346           colors[i].pixel = i;
347           colors[i].flags = DoRed|DoGreen|DoBlue;
348           if (depth == 8)
349             {
350               colors[i].red   = ((r << 13) | (r << 10) | (r << 7) |
351                                  (r <<  4) | (r <<  1));
352               colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
353                                  (g <<  4) | (g <<  1));
354               colors[i].blue  = ((b << 14) | (b << 12) | (b << 10) |
355                                  (b <<  8) | (b <<  6) | (b <<  4) |
356                                  (b <<  2) | b);
357             }
358           else
359             {
360               colors[i].red   = (r << 12) | (r << 8) | (r << 4) | r;
361               colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
362               colors[i].blue  = (b << 12) | (b << 8) | (b << 4) | b;
363             }
364         }
365
366   {
367     int j;
368     int allocated = 0;
369     int interleave = cells / 8;  /* skip around, rather than allocating in
370                                     order, so that we get better coverage if
371                                     we can't allocated all of them. */
372     for (j = 0; j < interleave; j++)
373       for (i = 0; i < cells; i += interleave)
374         if (XAllocColor (dpy, cmap, &colors[i + j]))
375           allocated++;
376
377     if (verbose_p)
378       fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
379                progname, allocated, cells);
380   }
381 }
382
383 /* Find the pixel index that is closest to the given color
384    (using linear distance in RGB space -- which is far from the best way.)
385  */
386 static unsigned long
387 find_closest_pixel (XColor *colors, int ncolors,
388                     unsigned long r, unsigned long g, unsigned long b)
389 {
390   unsigned long distance = ~0;
391   int i, found = 0;
392
393   if (ncolors == 0)
394     abort();
395   for (i = 0; i < ncolors; i++)
396     {
397       unsigned long d;
398       int rd, gd, bd;
399
400       rd = r - colors[i].red;
401       gd = g - colors[i].green;
402       bd = b - colors[i].blue;
403       if (rd < 0) rd = -rd;
404       if (gd < 0) gd = -gd;
405       if (bd < 0) bd = -bd;
406       d = (rd << 1) + (gd << 2) + bd;
407       
408       if (d < distance)
409         {
410           distance = d;
411           found = i;
412           if (distance == 0)
413               break;
414         }
415     }
416
417   return found;
418 }
419
420
421 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be 
422    displayable with the given X colormap.  The farther from a perfect
423    color cube the contents of the colormap are, the lossier the 
424    transformation will be.  No dithering is done.
425  */
426 static void
427 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
428 {
429   Display *dpy = DisplayOfScreen (screen);
430   unsigned long map[4097];
431   int x, y, i;
432   int cells;
433   XColor colors[4097];
434
435   if (image->depth == 8)
436     cells = 256;
437   else if (image->depth == 12)
438     cells = 4096;
439   else
440     abort();
441
442   memset(map,    -1, sizeof(*map));
443   memset(colors, -1, sizeof(*colors));
444
445   for (i = 0; i < cells; i++)
446     colors[i].pixel = i;
447   XQueryColors (dpy, cmap, colors, cells);
448
449   if (verbose_p)
450     fprintf(stderr, "%s: building color cube for %d bit image\n",
451             progname, image->depth);
452
453   for (i = 0; i < cells; i++)
454     {
455       unsigned short r, g, b;
456
457       if (cells == 256)
458         {
459           /* "RRR GGG BB" In an 8 bit map.  Convert that to
460              "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
461              an even spread. */
462           r = (i & 0x07);
463           g = (i & 0x38) >> 3;
464           b = (i & 0xC0) >> 6;
465
466           r = ((r << 13) | (r << 10) | (r << 7) | (r <<  4) | (r <<  1));
467           g = ((g << 13) | (g << 10) | (g << 7) | (g <<  4) | (g <<  1));
468           b = ((b << 14) | (b << 12) | (b << 10) | (b <<  8) |
469                (b <<  6) | (b <<  4) | (b <<  2) | b);
470         }
471       else
472         {
473           /* "RRRR GGGG BBBB" In a 12 bit map.  Convert that to
474              "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
475              spread. */
476           r = (i & 0x00F);
477           g = (i & 0x0F0) >> 4;
478           b = (i & 0xF00) >> 8;
479
480           r = (r << 12) | (r << 8) | (r << 4) | r;
481           g = (g << 12) | (g << 8) | (g << 4) | g;
482           b = (b << 12) | (b << 8) | (b << 4) | b;
483         }
484
485       map[i] = find_closest_pixel (colors, cells, r, g, b);
486     }
487
488   if (verbose_p)
489     fprintf(stderr, "%s: remapping colors in %d bit image\n",
490             progname, image->depth);
491
492   for (y = 0; y < image->height; y++)
493     for (x = 0; x < image->width; x++)
494       {
495         unsigned long pixel = XGetPixel(image, x, y);
496         if (pixel >= cells) abort();
497         XPutPixel(image, x, y, map[pixel]);
498       }
499 }
500
501
502 /* If the file has a PPM (P6) on it, read it and return an XImage.
503    Otherwise, rewind the fd back to the beginning, and return 0.
504  */
505 static XImage *
506 maybe_read_ppm (Screen *screen, Visual *visual,
507                 const char *filename, FILE *in, Bool verbose_p)
508 {
509   Display *dpy = DisplayOfScreen (screen);
510   int depth = visual_depth (screen, visual);
511   struct stat st;
512   char *buf = 0;
513   int bufsiz = 0;
514   char *s, dummy;
515   int i, j;
516   int x, y, w, h, maxval;
517   XImage *ximage = 0;
518
519   if (fstat (fileno (in), &st))
520     goto FAIL;
521
522   bufsiz = st.st_size;
523   buf = (char *) malloc (bufsiz + 1);
524   if (!buf)
525     {
526       fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
527                progname, bufsiz, filename);
528       goto FAIL;
529     }
530
531   if (! (s = fgets (buf, bufsiz, in)))   /* line 1 */
532     goto FAIL;
533
534   if (!strncmp (buf, "\107\111", 2))
535     {
536       fprintf (stderr, "%s: %s: sorry, GIF files not supported"
537                " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
538                progname, filename);
539       goto FAIL;
540     }
541   else if (!strncmp (buf, "\211\120", 2))
542     {
543       fprintf (stderr, "%s: %s: sorry, PNG files not supported"
544                " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
545                progname, filename);
546       goto FAIL;
547     }
548
549   if (strncmp (s, "P6", 2))
550     goto FAIL;
551
552   if (! (s = fgets (buf, bufsiz, in)))   /* line 2 */
553     goto FAIL;
554   if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
555     {
556       fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
557       goto FAIL;
558     }
559
560   if (! (s = fgets (buf, bufsiz, in)))   /* line 3 */
561     goto FAIL;
562   if (1 != sscanf (s, " %d %c", &maxval, &dummy))
563     {
564       fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
565       goto FAIL;
566     }
567   if (maxval != 255)
568     {
569       fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
570                progname, filename, maxval);
571       goto FAIL;
572     }
573
574   ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
575                          w, h, 8, 0);
576   if (ximage)
577     ximage->data = (unsigned char *)
578       calloc (ximage->height, ximage->bytes_per_line);
579   if (!ximage || !ximage->data)
580     {
581       fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
582                progname, ximage->width, ximage->height, filename);
583       goto FAIL;
584     }
585
586   s = buf;
587   j = bufsiz;
588   while ((i = fread (s, 1, j, in)) > 0)
589     s += i, j -= i;
590
591   i = 0;
592   for (y = 0; y < ximage->height; y++)
593     for (x = 0; x < ximage->width; x++)
594       {
595         unsigned char r = buf[i++];
596         unsigned char g = buf[i++];
597         unsigned char b = buf[i++];
598         unsigned long pixel;
599
600         if (depth > 16)
601           pixel = (r << 16) | (g << 8) | b;
602         else if (depth == 8)
603           pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
604         else if (depth == 12)
605           pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
606         else if (depth == 16 || depth == 15)
607           pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
608         else
609           abort();
610
611         XPutPixel (ximage, x, y, pixel);
612       }
613
614   free (buf);
615   return ximage;
616
617  FAIL:
618   if (buf) free (buf);
619   if (ximage && ximage->data)
620     {
621       free (ximage->data);
622       ximage->data = 0;
623     }
624   if (ximage) XDestroyImage (ximage);
625   fseek (in, 0, SEEK_SET);
626   return 0;
627 }
628
629
630 typedef struct {
631   struct jpeg_error_mgr pub;   /* this is what passes for subclassing in C */
632   const char *filename;
633   Screen *screen;
634   Visual *visual;
635   Drawable drawable;
636   Colormap cmap;
637 } getimg_jpg_error_mgr;
638
639
640 static void
641 jpg_output_message (j_common_ptr cinfo)
642 {
643   getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
644   char buf[JMSG_LENGTH_MAX];
645   cinfo->err->format_message (cinfo, buf);
646   fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
647 }
648
649
650 static void
651 jpg_error_exit (j_common_ptr cinfo)
652 {
653   getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
654   cinfo->err->output_message (cinfo);
655   draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
656                   0, 0, 0, 0);
657   XSync (DisplayOfScreen (err->screen), False);
658   exit (1);
659 }
660
661
662 /* Reads a JPEG file, returns an RGB XImage of it.
663  */
664 static XImage *
665 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
666                   Colormap cmap, const char *filename, Bool verbose_p)
667 {
668   Display *dpy = DisplayOfScreen (screen);
669   int depth = visual_depth (screen, visual);
670
671   FILE *in = 0;
672   XImage *ximage = 0;
673   struct jpeg_decompress_struct cinfo;
674   getimg_jpg_error_mgr jerr;
675   JSAMPARRAY scanbuf = 0;
676   int y;
677
678   jerr.filename = filename;
679   jerr.screen = screen;
680   jerr.visual = visual;
681   jerr.drawable = drawable;
682   jerr.cmap = cmap;
683
684   if (! (depth >= 15 || depth == 12 || depth == 8))
685     {
686       fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
687       goto FAIL;
688     }
689
690   in = fopen (filename, "rb");
691   if (!in)
692     {
693       fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
694       goto FAIL;
695     }
696
697   /* Check to see if it's a PPM, and if so, read that instead of using
698      the JPEG library.  Yeah, this is all modular and stuff.
699    */
700   if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
701     {
702       fclose (in);
703       return ximage;
704     }
705
706   cinfo.err = jpeg_std_error (&jerr.pub);
707   jerr.pub.output_message = jpg_output_message;
708   jerr.pub.error_exit = jpg_error_exit;
709
710   jpeg_create_decompress (&cinfo);
711   jpeg_stdio_src (&cinfo, in);
712   jpeg_read_header (&cinfo, TRUE);
713
714   /* set some decode parameters */
715   cinfo.out_color_space = JCS_RGB;
716   cinfo.quantize_colors = FALSE;
717
718   jpeg_start_decompress (&cinfo);
719
720   ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
721                          cinfo.output_width, cinfo.output_height,
722                          8, 0);
723   if (ximage)
724     ximage->data = (unsigned char *)
725       calloc (ximage->height, ximage->bytes_per_line);
726
727   if (ximage && ximage->data)
728     scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
729                                           cinfo.rec_outbuf_height *
730                                           cinfo.output_width *
731                                           cinfo.output_components,
732                                           1);
733   if (!ximage || !ximage->data || !scanbuf)
734     {
735       fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
736                progname, ximage->width, ximage->height, filename);
737       goto FAIL;
738     }
739
740   y = 0;
741   while (cinfo.output_scanline < cinfo.output_height)
742     {
743       int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
744       int i;
745       for (i = 0; i < n; i++)
746         {
747           int x;
748           for (x = 0; x < ximage->width; x++)
749             {
750               int j = x * cinfo.num_components;
751               unsigned char r = scanbuf[i][j];
752               unsigned char g = scanbuf[i][j+1];
753               unsigned char b = scanbuf[i][j+2];
754               unsigned long pixel;
755
756               if (depth > 16)
757                 pixel = (r << 16) | (g << 8) | b;
758               else if (depth == 8)
759                 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
760               else if (depth == 12)
761                 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
762               else if (depth == 16 || depth == 15)
763                 /* Gah! I don't understand why these are in the other
764                    order. */
765                 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
766               else
767                 abort();
768
769               XPutPixel (ximage, x, y, pixel);
770             }
771           y++;
772         }
773     }
774
775   if (cinfo.output_scanline < cinfo.output_height)
776     /* don't goto FAIL -- we might have viewable partial data. */
777     jpeg_abort_decompress (&cinfo);
778   else
779     jpeg_finish_decompress (&cinfo);
780
781   jpeg_destroy_decompress (&cinfo);
782   fclose (in);
783   in = 0;
784
785   return ximage;
786
787  FAIL:
788   if (in) fclose (in);
789   if (ximage && ximage->data)
790     {
791       free (ximage->data);
792       ximage->data = 0;
793     }
794   if (ximage) XDestroyImage (ximage);
795   if (scanbuf) free (scanbuf);
796   return 0;
797 }
798
799
800 /* Scales an XImage, modifying it in place.
801    If out of memory, returns False, and the XImage will have been
802    destroyed and freed.
803  */
804 static Bool
805 scale_ximage (Screen *screen, Visual *visual,
806               XImage *ximage, int new_width, int new_height)
807 {
808   Display *dpy = DisplayOfScreen (screen);
809   int depth = visual_depth (screen, visual);
810   int x, y;
811   double xscale, yscale;
812
813   XImage *ximage2 = XCreateImage (dpy, visual, depth,
814                                   ZPixmap, 0, 0,
815                                   new_width, new_height, 8, 0);
816   ximage2->data = (unsigned char *)
817     calloc (ximage2->height, ximage2->bytes_per_line);
818
819   if (!ximage2->data)
820     {
821       fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
822                progname,
823                ximage->width, ximage->height,
824                ximage2->width, ximage2->height);
825       if (ximage->data) free (ximage->data);
826       if (ximage2->data) free (ximage2->data);
827       ximage->data = 0;
828       ximage2->data = 0;
829       XDestroyImage (ximage);
830       XDestroyImage (ximage2);
831       return False;
832     }
833
834   /* Brute force scaling... */
835   xscale = (double) ximage->width  / ximage2->width;
836   yscale = (double) ximage->height / ximage2->height;
837   for (y = 0; y < ximage2->height; y++)
838     for (x = 0; x < ximage2->width; x++)
839       XPutPixel (ximage2, x, y,
840                  XGetPixel (ximage, x * xscale, y * yscale));
841
842   free (ximage->data);
843   ximage->data = 0;
844
845   (*ximage) = (*ximage2);
846
847   ximage2->data = 0;
848   XDestroyImage (ximage2);
849
850   return True;
851 }
852
853
854 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
855    Returns False if it fails.
856  */
857 static Bool
858 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
859                    const char *filename, Bool verbose_p)
860 {
861   Display *dpy = DisplayOfScreen (screen);
862   XImage *ximage;
863   Visual *visual;
864   int class, depth;
865   Colormap cmap;
866   unsigned int win_width, win_height, win_depth;
867   int srcx, srcy, destx, desty, w2, h2;
868
869   {
870     Window root;
871     int x, y;
872     unsigned int bw;
873     XWindowAttributes xgwa;
874
875     XGetWindowAttributes (dpy, window, &xgwa);
876     screen = xgwa.screen;
877     visual = xgwa.visual;
878     cmap = xgwa.colormap;
879
880     XGetGeometry (dpy, drawable,
881                   &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
882   }
883
884   /* Make sure we're not on some weirdo visual...
885    */
886   class = visual_class (screen, visual);
887   depth = visual_depth (screen, visual);
888   if ((class == PseudoColor || class == DirectColor) &&
889       (depth != 8 && depth != 12))
890     {
891       fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
892                progname, depth);
893       return False;
894     }
895
896   /* Read the file...
897    */
898   ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
899                              filename, verbose_p);
900   if (!ximage) return False;
901
902   /* Scale it, if necessary...
903    */
904   compute_image_scaling (ximage->width, ximage->height,
905                          win_width, win_height, verbose_p,
906                          &srcx, &srcy, &destx, &desty, &w2, &h2);
907   if (ximage->width != w2 || ximage->height != h2)
908     if (! scale_ximage (screen, visual, ximage, w2, h2))
909       return False;
910
911   /* Allocate a colormap, if we need to...
912    */
913   if (class == PseudoColor || class == DirectColor)
914     {
915       allocate_cubic_colormap (screen, visual, cmap, verbose_p);
916       remap_image (screen, cmap, ximage, verbose_p);
917     }
918
919   /* Finally, put the resized image on the window.
920    */
921   clear_drawable (screen, drawable);
922   {
923     GC gc;
924     XGCValues gcv;
925     gc = XCreateGC (dpy, drawable, 0, &gcv);
926     XPutImage (dpy, drawable, gc, ximage,
927                srcx, srcy, destx, desty, ximage->width, ximage->height);
928     XFreeGC (dpy, gc);
929   }
930
931   free (ximage->data);
932   ximage->data = 0;
933   XDestroyImage (ximage);
934   XSync (dpy, False);
935   return True;
936 }
937
938 #endif /* HAVE_JPEGLIB */
939
940
941 /* Reads the given image file and renders it on the Drawable.
942    Returns False if it fails.
943  */
944 static Bool
945 display_file (Screen *screen, Window window, Drawable drawable,
946               const char *filename, Bool verbose_p)
947 {
948   if (verbose_p)
949     fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
950
951 # if defined(HAVE_GDK_PIXBUF)
952   if (read_file_gdk (screen, window, drawable, filename, verbose_p))
953     return True;
954 # elif defined(HAVE_JPEGLIB)
955   if (read_file_jpeglib (screen, window, drawable, filename, verbose_p))
956     return True;
957 # else  /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
958   /* shouldn't get here if we have no image-loading methods available. */
959   abort();
960 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
961
962   return False;
963 }
964
965
966 /* Invokes a sub-process and returns its output (presumably, a file to
967    load.)  Free the string when done.  video_p controls which program
968    to run.
969  */
970 static char *
971 get_filename_1 (Screen *screen, const char *directory, Bool video_p,
972                 Bool verbose_p)
973 {
974   Display *dpy = DisplayOfScreen (screen);
975   pid_t forked;
976   int fds [2];
977   int in, out;
978   char buf[1024];
979   char *av[20];
980   int ac = 0;
981
982   if (!video_p)
983     {
984       av[ac++] = GETIMAGE_FILE_PROGRAM;
985       if (verbose_p)
986         av[ac++] = "--verbose";
987       av[ac++] = "--name";
988       av[ac++] = (char *) directory;
989     }
990   else
991     {
992       av[ac++] = GETIMAGE_VIDEO_PROGRAM;
993       if (verbose_p)
994         av[ac++] = "--verbose";
995       av[ac++] = "--name";
996     }
997   av[ac] = 0;
998
999   if (verbose_p)
1000     {
1001       int i;
1002       fprintf (stderr, "%s: executing:", progname);
1003       for (i = 0; i < ac; i++)
1004         fprintf (stderr, " %s", av[i]);
1005       fprintf (stderr, "\n");
1006     }
1007
1008   if (pipe (fds))
1009     {
1010       sprintf (buf, "%s: error creating pipe", progname);
1011       perror (buf);
1012       return 0;
1013     }
1014
1015   in = fds [0];
1016   out = fds [1];
1017
1018   switch ((int) (forked = fork ()))
1019     {
1020     case -1:
1021       {
1022         sprintf (buf, "%s: couldn't fork", progname);
1023         perror (buf);
1024         return 0;
1025       }
1026     case 0:
1027       {
1028         int stdout_fd = 1;
1029
1030         close (in);  /* don't need this one */
1031         close (ConnectionNumber (dpy));         /* close display fd */
1032
1033         if (dup2 (out, stdout_fd) < 0)          /* pipe stdout */
1034           {
1035             sprintf (buf, "%s: could not dup() a new stdout", progname);
1036             exit (-1);                          /* exits fork */
1037           }
1038
1039         execvp (av[0], av);                     /* shouldn't return. */
1040         exit (-1);                              /* exits fork */
1041         break;
1042       }
1043     default:
1044       {
1045         struct stat st;
1046         int wait_status = 0;
1047         FILE *f = fdopen (in, "r");
1048         int L;
1049
1050         close (out);  /* don't need this one */
1051         *buf = 0;
1052         fgets (buf, sizeof(buf)-1, f);
1053         fclose (f);
1054
1055         /* Wait for the child to die. */
1056         waitpid (-1, &wait_status, 0);
1057
1058         L = strlen (buf);
1059         while (L && buf[L-1] == '\n')
1060           buf[--L] = 0;
1061           
1062         if (!*buf)
1063           return 0;
1064         if (stat(buf, &st))
1065           {
1066             fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1067                      progname, buf);
1068             return 0;
1069           }
1070         else
1071           return strdup (buf);
1072       }
1073     }
1074
1075   abort();
1076 }
1077
1078
1079 /* Returns a pathname to an image file.  Free the string when you're done.
1080  */
1081 static char *
1082 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1083 {
1084   return get_filename_1 (screen, directory, False, verbose_p);
1085 }
1086
1087
1088 /* Grabs a video frame to a file, and returns a pathname to that file.
1089    Delete that file when you are done with it (and free the string.)
1090  */
1091 static char *
1092 get_video_filename (Screen *screen, Bool verbose_p)
1093 {
1094   return get_filename_1 (screen, 0, True, verbose_p);
1095 }
1096
1097
1098 /* Grabs a video frame, and renders it on the Drawable.
1099    Returns False if it fails;
1100  */
1101 static Bool
1102 display_video (Screen *screen, Window window, Drawable drawable,
1103                Bool verbose_p)
1104 {
1105   char *filename = get_video_filename (screen, verbose_p);
1106   Bool status;
1107
1108   if (!filename)
1109     {
1110       if (verbose_p)
1111         fprintf (stderr, "%s: video grab failed.\n", progname);
1112       return False;
1113     }
1114
1115   status = display_file (screen, window, drawable, filename, verbose_p);
1116
1117   if (unlink (filename))
1118     {
1119       char buf[512];
1120       sprintf (buf, "%s: rm %.100s", progname, filename);
1121       perror (buf);
1122     }
1123   else if (verbose_p)
1124     fprintf (stderr, "%s: rm %s\n", progname, filename);
1125
1126   if (filename) free (filename);
1127   return status;
1128 }
1129
1130
1131 /* Grabs an image (from a file, video, or the desktop) and renders it on
1132    the Drawable.  If `file' is specified, always use that file.  Otherwise,
1133    select randomly, based on the other arguments.
1134  */
1135 static void
1136 get_image (Screen *screen,
1137            Window window, Drawable drawable,
1138            Bool verbose_p,
1139            Bool desk_p,
1140            Bool video_p,
1141            Bool image_p,
1142            const char *dir,
1143            const char *file)
1144 {
1145   Display *dpy = DisplayOfScreen (screen);
1146   enum { do_desk, do_video, do_image, do_bars } which = do_bars;
1147   int count = 0;
1148   struct stat st;
1149
1150   if (! drawable_window_p (dpy, window))
1151     {
1152       fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1153                progname, (unsigned long) window);
1154       exit (1);
1155     }
1156
1157   if (file && stat (file, &st))
1158     {
1159       fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1160       file = 0;
1161     }
1162
1163   if (verbose_p)
1164     {
1165       fprintf (stderr, "%s: grabDesktopImages:  %s\n",
1166                progname, desk_p ? "True" : "False");
1167       fprintf (stderr, "%s: grabVideoFrames:    %s\n",
1168                progname, video_p ? "True" : "False");
1169       fprintf (stderr, "%s: chooseRandomImages: %s\n",
1170                progname, image_p ? "True" : "False");
1171       fprintf (stderr, "%s: imageDirectory:     %s\n",
1172                progname, (file ? file : dir ? dir : ""));
1173     }
1174
1175 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1176   image_p = False;    /* can't load images from files... */
1177   if (file)
1178     {
1179       fprintf (stderr,
1180                "%s: image file loading not available at compile-time\n",
1181                progname);
1182       fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1183       file = 0;
1184     }
1185 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1186
1187   if (file)
1188     {
1189       desk_p = False;
1190       video_p = False;
1191       image_p = True;
1192     }
1193
1194   if (!dir || !*dir)
1195     {
1196       if (verbose_p && image_p)
1197         fprintf (stderr,
1198                  "%s: no imageDirectory: turning off chooseRandomImages.\n",
1199                  progname);
1200       image_p = False;
1201     }
1202
1203
1204 # ifndef _VROOT_H_
1205 #  error Error!  This file definitely needs vroot.h!
1206 # endif
1207
1208   /* We can grab desktop images if:
1209        - the window is the real root window;
1210        - the window is the virtal root window;
1211        - the window is a toplevel window.
1212      We cannot grab desktop images if:
1213        - the window is a non-top-level window.
1214    */
1215   if (desk_p)
1216     {
1217       if (!top_level_window_p (screen, window))
1218         {
1219           desk_p = False;
1220           if (verbose_p)
1221             fprintf (stderr,
1222                     "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1223                      progname, (unsigned int) window);
1224         }
1225     }
1226
1227   count = 0;
1228   if (desk_p)  count++;
1229   if (video_p) count++;
1230   if (image_p) count++;
1231
1232   if (count == 0)
1233     which = do_bars;
1234   else
1235     {
1236       int i = 0;
1237       while (1)  /* loop until we get one that's permitted */
1238         {
1239           which = (random() % 3);
1240           if (which == do_desk  && desk_p)  break;
1241           if (which == do_video && video_p) break;
1242           if (which == do_image && image_p) break;
1243           if (++i > 200) abort();
1244         }
1245     }
1246
1247
1248   /* If we're to search a directory to find an image file, do so now.
1249    */
1250   if (which == do_image && !file)
1251     {
1252       file = get_filename (screen, dir, verbose_p);
1253       if (!file)
1254         {
1255           which = do_bars;
1256           if (verbose_p)
1257             fprintf (stderr, "%s: no image files found.\n", progname);
1258         }
1259     }
1260
1261   /* Now actually render something.
1262    */
1263   if (which == do_bars)
1264     {
1265       XWindowAttributes xgwa;
1266     COLORBARS:
1267       if (verbose_p)
1268         fprintf (stderr, "%s: drawing colorbars.\n", progname);
1269       XGetWindowAttributes (dpy, window, &xgwa);
1270       draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1271                       0, 0, 0, 0);
1272       XSync (dpy, False);
1273     }
1274   else if (which == do_desk)
1275     {
1276       GC gc;
1277       XGCValues gcv;
1278       XWindowAttributes xgwa;
1279
1280       if (verbose_p)
1281         {
1282           fprintf (stderr, "%s: grabbing desktop image\n", progname);
1283           grabscreen_verbose();
1284         }
1285       gc = XCreateGC (dpy, drawable, 0, &gcv);
1286       XGetWindowAttributes (dpy, window, &xgwa);
1287       grab_screen_image (screen, window);
1288       XCopyArea (dpy, window, drawable, gc,
1289                  0, 0, xgwa.width, xgwa.height, 0, 0);
1290       XFreeGC (dpy, gc);
1291       XSync (dpy, False);
1292     }
1293   else if (which == do_image)
1294     {
1295       if (! display_file (screen, window, drawable, file, verbose_p))
1296         goto COLORBARS;
1297     }
1298   else if (which == do_video)
1299     {
1300       if (! display_video (screen, window, drawable, verbose_p))
1301         goto COLORBARS;
1302     }
1303   else
1304     abort();
1305 }
1306
1307
1308 #ifdef DEBUG
1309 static Bool
1310 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1311         XrmRepresentation *type, XrmValue *value, XPointer closure)
1312 {
1313   int i;
1314   for (i = 0; quarks[i]; i++)
1315     {
1316       if (bindings[i] == XrmBindTightly)
1317         fprintf (stderr, (i == 0 ? "" : "."));
1318       else if (bindings[i] == XrmBindLoosely)
1319         fprintf (stderr, "*");
1320       else
1321         fprintf (stderr, " ??? ");
1322       fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1323     }
1324
1325   fprintf (stderr, ": %s\n", (char *) value->addr);
1326
1327   return False;
1328 }
1329 #endif /* DEBUG */
1330
1331
1332 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n"             \
1333    "\n"                                                                       \
1334    "    This program puts an image on the given window or pixmap.\n"          \
1335    "\n"                                                                       \
1336    "    It is used by those xscreensaver demos that operate on images.\n"     \
1337    "    The image may be a file loaded from disk, a frame grabbed from\n"     \
1338    "    the system's video camera, or a screenshot of the desktop,\n"         \
1339    "    depending on command-line options or the ~/.xscreensaver file.\n"     \
1340    "\n"                                                                       \
1341    "    Options include:\n"                                                   \
1342    "\n"                                                                       \
1343    "      -display host:dpy.screen    which display to use\n"                 \
1344    "      -root                       draw to the root window\n"              \
1345    "      -verbose                    print diagnostics\n"                    \
1346    "      -images  / -no-images       whether to allow image file loading\n"  \
1347    "      -video   / -no-video        whether to allow video grabs\n"         \
1348    "      -desktop / -no-desktop      whether to allow desktop screen grabs\n"\
1349    "      -directory <path>           where to find image files to load\n"    \
1350    "      -file <filename>            load this image file\n"                 \
1351    "\n"                                                                       \
1352    "    The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1353    "    defaults for these options in your ~/.xscreensaver file.\n"           \
1354    "\n"
1355
1356 int
1357 main (int argc, char **argv)
1358 {
1359   saver_preferences P;
1360   Widget toplevel;
1361   Display *dpy;
1362   Screen *screen;
1363   char *oprogname = progname;
1364   char *file = 0;
1365
1366   Window window = (Window) 0;
1367   Drawable drawable = (Drawable) 0;
1368   const char *window_str = 0;
1369   const char *drawable_str = 0;
1370   char *s;
1371   int i;
1372
1373   progname = argv[0];
1374   s = strrchr (progname, '/');
1375   if (s) progname = s+1;
1376   oprogname = progname;
1377
1378   /* half-assed way of avoiding buffer-overrun attacks. */
1379   if (strlen (progname) >= 100) progname[100] = 0;
1380
1381 # ifndef _VROOT_H_
1382 #  error Error!  This file definitely needs vroot.h!
1383 # endif
1384
1385   /* We must read exactly the same resources as xscreensaver.
1386      That means we must have both the same progclass *and* progname,
1387      at least as far as the resource database is concerned.  So,
1388      put "xscreensaver" in argv[0] while initializing Xt.
1389    */
1390   progname = argv[0] = "xscreensaver";
1391
1392   /* allow one dash or two. */
1393   for (i = 1; i < argc; i++)
1394     if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1395
1396   toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1397                               defaults, 0, 0);
1398   dpy = XtDisplay (toplevel);
1399   screen = XtScreen (toplevel);
1400   db = XtDatabase (dpy);
1401   XtGetApplicationNameAndClass (dpy, &s, &progclass);
1402   XSetErrorHandler (x_ehandler);
1403   XSync (dpy, False);
1404
1405   /* Randomize -- only need to do this here because this program
1406      doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1407 # undef ya_rand_init
1408   ya_rand_init (0);
1409
1410   memset (&P, 0, sizeof(P));
1411   P.db = db;
1412   load_init_file (&P);
1413
1414   progname = argv[0] = oprogname;
1415
1416   for (i = 1; i < argc; i++)
1417     {
1418       unsigned long w;
1419       char dummy;
1420
1421       /* Have to re-process these, or else the .xscreensaver file
1422          has priority over the command line...
1423        */
1424       if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1425         P.verbose_p = True;
1426       else if (!strcmp (argv[i], "-desktop"))    P.grab_desktop_p = True;
1427       else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1428       else if (!strcmp (argv[i], "-video"))      P.grab_video_p = True;
1429       else if (!strcmp (argv[i], "-no-video"))   P.grab_video_p = False;
1430       else if (!strcmp (argv[i], "-images"))     P.random_image_p = True;
1431       else if (!strcmp (argv[i], "-no-images"))  P.random_image_p = False;
1432       else if (!strcmp (argv[i], "-file"))       file = argv[++i];
1433       else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1434         P.image_directory = argv[++i];
1435       else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1436         {
1437           if (window)
1438             {
1439               fprintf (stderr, "%s: both %s and %s specified?\n",
1440                        progname, argv[i], window_str);
1441               goto LOSE;
1442             }
1443           window_str = argv[i];
1444           window = (Window) RootWindowOfScreen (screen);
1445         }
1446       else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1447                 1 == sscanf (argv[i], " %ld %c",   &w, &dummy)) &&
1448                w != 0)
1449         {
1450           if (drawable)
1451             {
1452               fprintf (stderr, "%s: both %s and %s specified?\n",
1453                        progname, drawable_str, argv[i]);
1454               goto LOSE;
1455             }
1456           else if (window)
1457             {
1458               drawable_str = argv[i];
1459               drawable = (Drawable) w;
1460             }
1461           else
1462             {
1463               window_str = argv[i];
1464               window = (Window) w;
1465             }
1466         }
1467       else
1468         {
1469           if (argv[i][0] == '-')
1470             fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1471                      progname, argv[i]);
1472           else
1473             fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1474                      progname, argv[i]);
1475         LOSE:
1476           fprintf (stderr, USAGE, progname);
1477           exit (1);
1478         }
1479     }
1480
1481   if (window == 0)
1482     {
1483       fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1484       goto LOSE;
1485     }
1486
1487
1488 #ifdef DEBUG
1489   if (P.verbose_p)       /* Print out all the resources we can see. */
1490     {
1491       XrmName name = { 0 };
1492       XrmClass class = { 0 };
1493       int count = 0;
1494       XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1495                             (XtPointer) &count);
1496     }
1497 #endif /* DEBUG */
1498
1499   if (!window) abort();
1500   if (!drawable) drawable = window;
1501
1502   get_image (screen, window, drawable, P.verbose_p,
1503              P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1504              P.image_directory, file);
1505   exit (0);
1506 }