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