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