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