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