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