1 /* xscreensaver, Copyright (c) 2001-2004 by Jamie Zawinski <jwz@jwz.org>
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
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
20 #include <X11/Intrinsic.h>
26 #ifdef HAVE_SYS_WAIT_H
27 # include <sys/wait.h> /* for waitpid() and associated macros */
32 # include <X11/Xmu/Error.h>
34 # include <Xmu/Error.h>
41 #include "grabscreen.h"
42 #include "resources.h"
43 #include "colorbars.h"
49 #ifndef _XSCREENSAVER_VROOT_H_
50 # error Error! You have an old version of vroot.h! Check -I args.
51 #endif /* _XSCREENSAVER_VROOT_H_ */
53 #ifdef HAVE_GDK_PIXBUF
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 */
63 # undef HAVE_GDK_PIXBUF
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
76 __extension__ /* shut up about "string length is greater than the length
77 ISO C89 compilers are required to support" when including
81 static char *defaults[] = {
82 #include "../driver/XScreenSaver_ad.h"
89 char *progclass = "XScreenSaver";
93 extern void grabscreen_verbose (void);
96 GRAB_DESK, GRAB_VIDEO, GRAB_FILE, GRAB_BARS
100 #define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
101 #define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file"
102 #define GETIMAGE_SCREEN_PROGRAM "xscreensaver-getimage-desktop"
112 x_ehandler (Display *dpy, XErrorEvent *error)
114 if (error->error_code == BadWindow || error->error_code == BadDrawable)
116 fprintf (stderr, "%s: target %s 0x%lx unexpectedly deleted\n", progname,
117 (error->error_code == BadWindow ? "window" : "pixmap"),
118 (unsigned long) error->resourceid);
122 fprintf (stderr, "\nX error in %s:\n", progname);
123 XmuPrintDefaultErrorMessage (dpy, error, stderr);
130 static Bool error_handler_hit_p = False;
133 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
135 error_handler_hit_p = True;
140 ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error)
142 if (error->error_code == BadMatch)
143 return ignore_all_errors_ehandler (dpy, error);
145 return x_ehandler (dpy, error);
149 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
152 drawable_window_p (Display *dpy, Drawable d)
154 XErrorHandler old_handler;
155 XWindowAttributes xgwa;
158 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
159 error_handler_hit_p = False;
160 XGetWindowAttributes (dpy, d, &xgwa);
162 XSetErrorHandler (old_handler);
165 if (!error_handler_hit_p)
166 return True; /* It's a Window. */
168 return False; /* It's a Pixmap, or an invalid ID. */
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.
177 root_window_p (Screen *screen, Window window)
179 Display *dpy = DisplayOfScreen (screen);
182 unsigned long nitems, bytesafter;
183 unsigned char *version;
185 if (window != RootWindowOfScreen (screen))
188 if (XGetWindowProperty (dpy, window,
189 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
190 0, 1, False, XA_STRING,
191 &type, &format, &nitems, &bytesafter,
201 /* Clear the window or pixmap to black, or its background color.
204 clear_drawable (Screen *screen, Drawable drawable)
206 Display *dpy = DisplayOfScreen (screen);
211 unsigned int w, h, bw, d;
212 XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
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...) */
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);
223 if (drawable_window_p (dpy, drawable))
224 XClearWindow (dpy, (Window) drawable);
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.
235 compute_image_scaling (int src_w, int src_h,
236 int dest_w, int dest_h,
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)
242 int srcx, srcy, destx, desty;
244 Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
245 (src_h == dest_h && src_w <= dest_w));
247 if (!exact_fit_p) /* scale the image up or down */
249 float rw = (float) dest_w / src_w;
250 float rh = (float) dest_h / src_h;
251 float r = (rw < rh ? rw : rh);
257 /* this optimization breaks things */
258 if (pct < 95 || pct > 105) /* don't scale if it's close */
262 fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
263 progname, pct, src_w, src_h, tw, th);
269 /* Center the image on the window/pixmap. */
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;
277 if (dest_w < src_w) src_w = dest_w;
278 if (dest_h < src_h) src_h = dest_h;
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;
288 fprintf (stderr, "%s: displaying %dx%d image at %d,%d in %dx%d.\n",
289 progname, src_w, src_h, destx, desty, dest_w, dest_h);
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
299 scale_ximage (Screen *screen, Visual *visual,
300 XImage *ximage, int new_width, int new_height)
302 Display *dpy = DisplayOfScreen (screen);
303 int depth = visual_depth (screen, visual);
305 double xscale, yscale;
307 XImage *ximage2 = XCreateImage (dpy, visual, depth,
309 new_width, new_height, 8, 0);
310 ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
314 fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
316 ximage->width, ximage->height,
317 ximage2->width, ximage2->height);
318 if (ximage->data) free (ximage->data);
319 if (ximage2->data) free (ximage2->data);
322 XDestroyImage (ximage);
323 XDestroyImage (ximage2);
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));
338 (*ximage) = (*ximage2);
341 XDestroyImage (ximage2);
347 #ifdef HAVE_GDK_PIXBUF
349 /* Reads the given image file and renders it on the Drawable, using GDK.
350 Returns False if it fails.
353 read_file_gdk (Screen *screen, Window window, Drawable drawable,
354 const char *filename, Bool verbose_p,
355 XRectangle *geom_ret)
358 Display *dpy = DisplayOfScreen (screen);
359 unsigned int win_width, win_height, win_depth;
362 # endif /* HAVE_GTK2 */
364 /* Find the size of the Drawable. */
369 XGetGeometry (dpy, drawable,
370 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
373 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
376 # else /* !HAVE_GTK2 */
377 xlib_rgb_init (dpy, screen);
378 # endif /* !HAVE_GTK2 */
380 pb = gdk_pixbuf_new_from_file (filename
383 # endif /* HAVE_GTK2 */
388 fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
390 if (gerr && gerr->message && *gerr->message)
391 fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
392 # endif /* HAVE_GTK2 */
397 int w = gdk_pixbuf_get_width (pb);
398 int h = gdk_pixbuf_get_height (pb);
399 int srcx, srcy, destx, desty, w2, h2;
402 compute_image_scaling (w, h, win_width, win_height, verbose_p,
403 &srcx, &srcy, &destx, &desty, &w2, &h2);
404 if (w != w2 || h != h2)
406 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
407 GDK_INTERP_BILINEAR);
410 gdk_pixbuf_unref (pb);
416 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
419 /* If we're rendering onto the root window (and it's not the
420 xscreensaver pseudo-root) then put the image in the window's
421 background. Otherwise, just paint the image onto the window.
423 bg_p = (window == drawable && root_window_p (screen, window));
429 drawable = XCreatePixmap (dpy, window,
430 win_width, win_height, win_depth);
431 gcv.foreground = BlackPixelOfScreen (screen);
432 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
433 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
437 clear_drawable (screen, drawable);
439 /* #### Note that this always uses the default colormap! Morons!
440 Owen says that in Gnome 2.0, I should try using
441 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
444 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
445 srcx, srcy, destx, desty,
447 GDK_PIXBUF_ALPHA_FULL, 127,
448 XLIB_RGB_DITHER_NORMAL,
452 XSetWindowBackgroundPixmap (dpy, window, drawable);
453 XClearWindow (dpy, window);
461 geom_ret->height = h;
469 #endif /* HAVE_GDK_PIXBUF */
475 /* Allocates a colormap that makes a PseudoColor or DirectColor
476 visual behave like a TrueColor visual of the same depth.
479 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
482 Display *dpy = DisplayOfScreen (screen);
483 int nr, ng, nb, cells;
489 depth = visual_depth (screen, visual);
493 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
494 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
495 default: abort(); break;
498 memset(colors, 0, sizeof(colors));
499 for (r = 0; r < (1 << nr); r++)
500 for (g = 0; g < (1 << ng); g++)
501 for (b = 0; b < (1 << nb); b++)
503 i = (r | (g << nr) | (b << (nr + ng)));
505 colors[i].flags = DoRed|DoGreen|DoBlue;
508 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
509 (r << 4) | (r << 1));
510 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
511 (g << 4) | (g << 1));
512 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
513 (b << 8) | (b << 6) | (b << 4) |
518 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
519 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
520 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
527 int interleave = cells / 8; /* skip around, rather than allocating in
528 order, so that we get better coverage if
529 we can't allocated all of them. */
530 for (j = 0; j < interleave; j++)
531 for (i = 0; i < cells; i += interleave)
532 if (XAllocColor (dpy, cmap, &colors[i + j]))
536 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
537 progname, allocated, cells);
541 /* Find the pixel index that is closest to the given color
542 (using linear distance in RGB space -- which is far from the best way.)
545 find_closest_pixel (XColor *colors, int ncolors,
546 unsigned long r, unsigned long g, unsigned long b)
548 unsigned long distance = ~0;
553 for (i = 0; i < ncolors; i++)
558 rd = r - colors[i].red;
559 gd = g - colors[i].green;
560 bd = b - colors[i].blue;
561 if (rd < 0) rd = -rd;
562 if (gd < 0) gd = -gd;
563 if (bd < 0) bd = -bd;
564 d = (rd << 1) + (gd << 2) + bd;
579 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
580 displayable with the given X colormap. The farther from a perfect
581 color cube the contents of the colormap are, the lossier the
582 transformation will be. No dithering is done.
585 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
587 Display *dpy = DisplayOfScreen (screen);
588 unsigned long map[4097];
593 if (image->depth == 8)
595 else if (image->depth == 12)
600 memset(map, -1, sizeof(*map));
601 memset(colors, -1, sizeof(*colors));
603 for (i = 0; i < cells; i++)
605 XQueryColors (dpy, cmap, colors, cells);
608 fprintf(stderr, "%s: building color cube for %d bit image\n",
609 progname, image->depth);
611 for (i = 0; i < cells; i++)
613 unsigned short r, g, b;
617 /* "RRR GGG BB" In an 8 bit map. Convert that to
618 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
624 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
625 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
626 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
627 (b << 6) | (b << 4) | (b << 2) | b);
631 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
632 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
635 g = (i & 0x0F0) >> 4;
636 b = (i & 0xF00) >> 8;
638 r = (r << 12) | (r << 8) | (r << 4) | r;
639 g = (g << 12) | (g << 8) | (g << 4) | g;
640 b = (b << 12) | (b << 8) | (b << 4) | b;
643 map[i] = find_closest_pixel (colors, cells, r, g, b);
647 fprintf(stderr, "%s: remapping colors in %d bit image\n",
648 progname, image->depth);
650 for (y = 0; y < image->height; y++)
651 for (x = 0; x < image->width; x++)
653 unsigned long pixel = XGetPixel(image, x, y);
654 if (pixel >= cells) abort();
655 XPutPixel(image, x, y, map[pixel]);
660 /* If the file has a PPM (P6) on it, read it and return an XImage.
661 Otherwise, rewind the fd back to the beginning, and return 0.
664 maybe_read_ppm (Screen *screen, Visual *visual,
665 const char *filename, FILE *in, Bool verbose_p)
667 Display *dpy = DisplayOfScreen (screen);
668 int depth = visual_depth (screen, visual);
674 int x, y, w, h, maxval;
677 if (fstat (fileno (in), &st))
681 buf = (char *) malloc (bufsiz + 1);
684 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
685 progname, bufsiz, filename);
689 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
692 if (!strncmp (buf, "\107\111", 2))
694 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
695 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
699 else if (!strncmp (buf, "\211\120", 2))
701 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
702 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
707 if (strncmp (s, "P6", 2))
710 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
712 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
714 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
718 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
720 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
722 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
727 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
728 progname, filename, maxval);
732 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
735 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
736 if (!ximage || !ximage->data)
738 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
739 progname, ximage->width, ximage->height, filename);
745 while ((i = fread (s, 1, j, in)) > 0)
749 for (y = 0; y < ximage->height; y++)
750 for (x = 0; x < ximage->width; x++)
752 unsigned char r = buf[i++];
753 unsigned char g = buf[i++];
754 unsigned char b = buf[i++];
758 pixel = (r << 16) | (g << 8) | b;
760 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
761 else if (depth == 12)
762 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
763 else if (depth == 16 || depth == 15)
764 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
768 XPutPixel (ximage, x, y, pixel);
776 if (ximage && ximage->data)
781 if (ximage) XDestroyImage (ximage);
782 fseek (in, 0, SEEK_SET);
788 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
789 const char *filename;
794 } getimg_jpg_error_mgr;
798 jpg_output_message (j_common_ptr cinfo)
800 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
801 char buf[JMSG_LENGTH_MAX];
802 cinfo->err->format_message (cinfo, buf);
803 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
808 jpg_error_exit (j_common_ptr cinfo)
810 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
811 cinfo->err->output_message (cinfo);
812 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
814 XSync (DisplayOfScreen (err->screen), False);
819 /* Reads a JPEG file, returns an RGB XImage of it.
822 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
823 Colormap cmap, const char *filename, Bool verbose_p)
825 Display *dpy = DisplayOfScreen (screen);
826 int depth = visual_depth (screen, visual);
830 struct jpeg_decompress_struct cinfo;
831 getimg_jpg_error_mgr jerr;
832 JSAMPARRAY scanbuf = 0;
835 jerr.filename = filename;
836 jerr.screen = screen;
837 jerr.visual = visual;
838 jerr.drawable = drawable;
841 if (! (depth >= 15 || depth == 12 || depth == 8))
843 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
847 in = fopen (filename, "rb");
850 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
854 /* Check to see if it's a PPM, and if so, read that instead of using
855 the JPEG library. Yeah, this is all modular and stuff.
857 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
863 cinfo.err = jpeg_std_error (&jerr.pub);
864 jerr.pub.output_message = jpg_output_message;
865 jerr.pub.error_exit = jpg_error_exit;
867 jpeg_create_decompress (&cinfo);
868 jpeg_stdio_src (&cinfo, in);
869 jpeg_read_header (&cinfo, TRUE);
871 /* set some decode parameters */
872 cinfo.out_color_space = JCS_RGB;
873 cinfo.quantize_colors = FALSE;
875 jpeg_start_decompress (&cinfo);
877 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
878 cinfo.output_width, cinfo.output_height,
881 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
883 if (ximage && ximage->data)
884 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
885 cinfo.rec_outbuf_height *
887 cinfo.output_components,
889 if (!ximage || !ximage->data || !scanbuf)
891 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
892 progname, ximage->width, ximage->height, filename);
897 while (cinfo.output_scanline < cinfo.output_height)
899 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
901 for (i = 0; i < n; i++)
904 for (x = 0; x < ximage->width; x++)
906 int j = x * cinfo.output_components;
907 unsigned char r = scanbuf[i][j];
908 unsigned char g = scanbuf[i][j+1];
909 unsigned char b = scanbuf[i][j+2];
913 pixel = (r << 16) | (g << 8) | b;
915 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
916 else if (depth == 12)
917 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
918 else if (depth == 15)
919 /* Gah! I don't understand why these are in the other
921 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
922 else if (depth == 16)
923 pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
927 XPutPixel (ximage, x, y, pixel);
933 if (cinfo.output_scanline < cinfo.output_height)
934 /* don't goto FAIL -- we might have viewable partial data. */
935 jpeg_abort_decompress (&cinfo);
937 jpeg_finish_decompress (&cinfo);
939 jpeg_destroy_decompress (&cinfo);
947 if (ximage && ximage->data)
952 if (ximage) XDestroyImage (ximage);
953 if (scanbuf) free (scanbuf);
958 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
959 Returns False if it fails.
962 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
963 const char *filename, Bool verbose_p,
964 XRectangle *geom_ret)
966 Display *dpy = DisplayOfScreen (screen);
971 unsigned int win_width, win_height, win_depth;
972 int srcx, srcy, destx, desty, w2, h2;
974 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
979 XWindowAttributes xgwa;
980 XGetWindowAttributes (dpy, window, &xgwa);
981 visual = xgwa.visual;
982 cmap = xgwa.colormap;
983 XGetGeometry (dpy, drawable,
984 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
987 /* Make sure we're not on some weirdo visual...
989 class = visual_class (screen, visual);
990 depth = visual_depth (screen, visual);
991 if ((class == PseudoColor || class == DirectColor) &&
992 (depth != 8 && depth != 12))
994 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
1001 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
1002 filename, verbose_p);
1003 if (!ximage) return False;
1005 /* Scale it, if necessary...
1007 compute_image_scaling (ximage->width, ximage->height,
1008 win_width, win_height, verbose_p,
1009 &srcx, &srcy, &destx, &desty, &w2, &h2);
1010 if (ximage->width != w2 || ximage->height != h2)
1011 if (! scale_ximage (screen, visual, ximage, w2, h2))
1014 /* Allocate a colormap, if we need to...
1016 if (class == PseudoColor || class == DirectColor)
1018 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
1019 remap_image (screen, cmap, ximage, verbose_p);
1022 /* Finally, put the resized image on the window.
1028 /* If we're rendering onto the root window (and it's not the xscreensaver
1029 pseudo-root) then put the image in the window's background. Otherwise,
1030 just paint the image onto the window.
1032 if (window == drawable && root_window_p (screen, window))
1034 Pixmap bg = XCreatePixmap (dpy, window,
1035 win_width, win_height, win_depth);
1036 gcv.foreground = BlackPixelOfScreen (screen);
1037 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1038 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1039 XPutImage (dpy, bg, gc, ximage,
1040 srcx, srcy, destx, desty, ximage->width, ximage->height);
1041 XSetWindowBackgroundPixmap (dpy, window, bg);
1042 XClearWindow (dpy, window);
1046 gc = XCreateGC (dpy, drawable, 0, &gcv);
1047 clear_drawable (screen, drawable);
1048 XPutImage (dpy, drawable, gc, ximage,
1049 srcx, srcy, destx, desty, ximage->width, ximage->height);
1057 geom_ret->x = destx;
1058 geom_ret->y = desty;
1059 geom_ret->width = ximage->width;
1060 geom_ret->height = ximage->height;
1063 free (ximage->data);
1065 XDestroyImage (ximage);
1070 #endif /* HAVE_JPEGLIB */
1073 /* Reads the given image file and renders it on the Drawable.
1074 Returns False if it fails.
1077 display_file (Screen *screen, Window window, Drawable drawable,
1078 const char *filename, Bool verbose_p,
1079 XRectangle *geom_ret)
1082 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1084 # if defined(HAVE_GDK_PIXBUF)
1085 if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret))
1087 # elif defined(HAVE_JPEGLIB)
1088 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p,
1091 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1092 /* shouldn't get here if we have no image-loading methods available. */
1094 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1100 /* Invokes a sub-process and returns its output (presumably, a file to
1101 load.) Free the string when done. 'grab_type' controls which program
1105 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1108 Display *dpy = DisplayOfScreen (screen);
1119 av[ac++] = GETIMAGE_FILE_PROGRAM;
1121 av[ac++] = "--verbose";
1122 av[ac++] = "--name";
1123 av[ac++] = (char *) directory;
1127 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1129 av[ac++] = "--verbose";
1130 av[ac++] = "--name";
1133 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1135 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1137 av[ac++] = "--verbose";
1138 av[ac++] = "--name";
1150 fprintf (stderr, "%s: executing:", progname);
1151 for (i = 0; i < ac; i++)
1152 fprintf (stderr, " %s", av[i]);
1153 fprintf (stderr, "\n");
1158 sprintf (buf, "%s: error creating pipe", progname);
1166 switch ((int) (forked = fork ()))
1170 sprintf (buf, "%s: couldn't fork", progname);
1178 close (in); /* don't need this one */
1179 close (ConnectionNumber (dpy)); /* close display fd */
1181 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1183 sprintf (buf, "%s: could not dup() a new stdout", progname);
1184 exit (-1); /* exits fork */
1187 execvp (av[0], av); /* shouldn't return. */
1188 exit (-1); /* exits fork */
1194 int wait_status = 0;
1195 FILE *f = fdopen (in, "r");
1198 close (out); /* don't need this one */
1200 fgets (buf, sizeof(buf)-1, f);
1203 /* Wait for the child to die. */
1204 waitpid (-1, &wait_status, 0);
1207 while (L && buf[L-1] == '\n')
1214 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1219 return strdup (buf);
1227 /* Returns a pathname to an image file. Free the string when you're done.
1230 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1232 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1236 /* Grabs a video frame to a file, and returns a pathname to that file.
1237 Delete that file when you are done with it (and free the string.)
1240 get_video_filename (Screen *screen, Bool verbose_p)
1242 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1245 /* Grabs a desktop image to a file, and returns a pathname to that file.
1246 Delete that file when you are done with it (and free the string.)
1248 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1250 get_desktop_filename (Screen *screen, Bool verbose_p)
1252 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1254 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1257 /* Grabs a video frame, and renders it on the Drawable.
1258 Returns False if it fails;
1261 display_video (Screen *screen, Window window, Drawable drawable,
1262 Bool verbose_p, XRectangle *geom_ret)
1264 char *filename = get_video_filename (screen, verbose_p);
1270 fprintf (stderr, "%s: video grab failed.\n", progname);
1274 status = display_file (screen, window, drawable, filename, verbose_p,
1277 if (unlink (filename))
1280 sprintf (buf, "%s: rm %.100s", progname, filename);
1284 fprintf (stderr, "%s: rm %s\n", progname, filename);
1286 if (filename) free (filename);
1291 /* Grabs a desktop screen shot onto the window and the drawable.
1292 If the window and drawable are not the same size, the image in
1293 the drawable is scaled to fit.
1294 Returns False if it fails.
1297 display_desktop (Screen *screen, Window window, Drawable drawable,
1298 Bool verbose_p, XRectangle *geom_ret)
1300 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1302 Display *dpy = DisplayOfScreen (screen);
1303 Bool top_p = top_level_window_p (screen, window);
1310 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1311 (unsigned long) window);
1312 XUnmapWindow (dpy, window);
1316 filename = get_desktop_filename (screen, verbose_p);
1321 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1322 (unsigned long) window);
1323 XMapRaised (dpy, window);
1330 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1334 status = display_file (screen, window, drawable, filename, verbose_p,
1337 if (unlink (filename))
1340 sprintf (buf, "%s: rm %.100s", progname, filename);
1344 fprintf (stderr, "%s: rm %s\n", progname, filename);
1346 if (filename) free (filename);
1349 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1351 Display *dpy = DisplayOfScreen (screen);
1353 XWindowAttributes xgwa;
1356 unsigned int pw, ph, pbw, pd;
1357 int srcx, srcy, destx, desty, w2, h2;
1361 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1362 grabscreen_verbose();
1365 XGetWindowAttributes (dpy, window, &xgwa);
1366 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1368 grab_screen_image_internal (screen, window);
1370 compute_image_scaling (xgwa.width, xgwa.height,
1372 &srcx, &srcy, &destx, &desty, &w2, &h2);
1374 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1376 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1377 XCopyArea (dpy, window, drawable, gc,
1378 0, 0, xgwa.width, xgwa.height, 0, 0);
1381 else /* size mismatch -- must scale client-side images to fit drawable */
1385 XErrorHandler old_handler;
1388 old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
1389 error_handler_hit_p = False;
1391 /* This can return BadMatch if the window is not fully on screen.
1392 Trap that error and return color bars in that case.
1393 (Note that this only happens with XGetImage, not with XCopyArea:
1394 yet another totally gratuitous inconsistency in X, thanks.)
1396 ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1400 XSetErrorHandler (old_handler);
1403 if (error_handler_hit_p)
1407 fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
1408 progname, (unsigned int) window);
1412 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1415 gc = XCreateGC (dpy, drawable, 0, &gcv);
1416 clear_drawable (screen, drawable);
1417 XPutImage (dpy, drawable, gc, ximage,
1418 srcx, srcy, destx, desty, ximage->width, ximage->height);
1419 XDestroyImage (ximage);
1425 geom_ret->x = destx;
1426 geom_ret->y = desty;
1427 geom_ret->width = w2;
1428 geom_ret->height = h2;
1434 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1438 /* Grabs an image (from a file, video, or the desktop) and renders it on
1439 the Drawable. If `file' is specified, always use that file. Otherwise,
1440 select randomly, based on the other arguments.
1443 get_image (Screen *screen,
1444 Window window, Drawable drawable,
1452 Display *dpy = DisplayOfScreen (screen);
1453 grab_type which = GRAB_BARS;
1455 const char *file_prop = 0;
1456 XRectangle geom = { 0, 0, 0, 0 };
1458 if (! drawable_window_p (dpy, window))
1460 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1461 progname, (unsigned long) window);
1465 /* Make sure the Screen and the Window correspond. */
1467 XWindowAttributes xgwa;
1468 XGetWindowAttributes (dpy, window, &xgwa);
1469 screen = xgwa.screen;
1472 if (file && stat (file, &st))
1474 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1480 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1481 progname, desk_p ? "True" : "False");
1482 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1483 progname, video_p ? "True" : "False");
1484 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1485 progname, image_p ? "True" : "False");
1486 fprintf (stderr, "%s: imageDirectory: %s\n",
1487 progname, (file ? file : dir ? dir : ""));
1490 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1491 image_p = False; /* can't load images from files... */
1492 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1493 desk_p = False; /* ...or from desktops grabbed to files. */
1499 "%s: image file loading not available at compile-time\n",
1501 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1504 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1512 else if (!dir || !*dir)
1514 if (verbose_p && image_p)
1516 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1523 # error Error! This file definitely needs vroot.h!
1526 /* We can grab desktop images (using the normal X11 method) if:
1527 - the window is the real root window;
1528 - the window is a toplevel window.
1529 We cannot grab desktop images that way if:
1530 - the window is a non-top-level window.
1532 Using the MacOS X way, desktops are just like loaded image files.
1534 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1537 if (!top_level_window_p (screen, window))
1542 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1543 progname, (unsigned int) window);
1546 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1548 if (! (desk_p || video_p || image_p))
1554 /* Loop until we get one that's permitted.
1555 If files or video are permitted, do them more often
1558 D+V+I: 10% + 45% + 45%.
1564 n = (random() % 100);
1565 if (++i > 300) abort();
1566 else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */
1567 else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */
1568 else if (image_p) which = GRAB_FILE; /* 45% */
1573 /* If we're to search a directory to find an image file, do so now.
1575 if (which == GRAB_FILE && !file)
1577 file = get_filename (screen, dir, verbose_p);
1582 fprintf (stderr, "%s: no image files found.\n", progname);
1586 /* Now actually render something.
1592 XWindowAttributes xgwa;
1595 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1596 XGetWindowAttributes (dpy, window, &xgwa);
1597 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1604 if (! display_desktop (screen, window, drawable, verbose_p, &geom))
1606 file_prop = "desktop";
1610 if (! display_file (screen, window, drawable, file, verbose_p, &geom))
1616 if (! display_video (screen, window, drawable, verbose_p, &geom))
1618 file_prop = "video";
1627 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1628 if (file_prop && *file_prop)
1629 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1630 (unsigned char *) file_prop, strlen(file_prop));
1632 XDeleteProperty (dpy, window, a);
1634 a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
1638 sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
1639 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1640 (unsigned char *) gstr, strlen (gstr));
1643 XDeleteProperty (dpy, window, a);
1652 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1653 XrmRepresentation *type, XrmValue *value, XPointer closure)
1656 for (i = 0; quarks[i]; i++)
1658 if (bindings[i] == XrmBindTightly)
1659 fprintf (stderr, (i == 0 ? "" : "."));
1660 else if (bindings[i] == XrmBindLoosely)
1661 fprintf (stderr, "*");
1663 fprintf (stderr, " ??? ");
1664 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1667 fprintf (stderr, ": %s\n", (char *) value->addr);
1674 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1678 " %s puts an image on the given window or pixmap.\n" \
1680 " It is used by those xscreensaver demos that operate on images.\n" \
1681 " The image may be a file loaded from disk, a frame grabbed from\n" \
1682 " the system's video camera, or a screenshot of the desktop,\n" \
1683 " depending on command-line options or the ~/.xscreensaver file.\n" \
1685 " Options include:\n" \
1687 " -display host:dpy.screen which display to use\n" \
1688 " -root draw to the root window\n" \
1689 " -verbose print diagnostics\n" \
1690 " -images / -no-images whether to allow image file loading\n" \
1691 " -video / -no-video whether to allow video grabs\n" \
1692 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1693 " -directory <path> where to find image files to load\n" \
1694 " -file <filename> load this image file\n" \
1696 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1697 " defaults for these options in your ~/.xscreensaver file.\n" \
1701 main (int argc, char **argv)
1703 saver_preferences P;
1707 char *oprogname = progname;
1711 Window window = (Window) 0;
1712 Drawable drawable = (Drawable) 0;
1713 const char *window_str = 0;
1714 const char *drawable_str = 0;
1719 s = strrchr (progname, '/');
1720 if (s) progname = s+1;
1721 oprogname = progname;
1723 /* half-assed way of avoiding buffer-overrun attacks. */
1724 if (strlen (progname) >= 100) progname[100] = 0;
1727 # error Error! This file definitely needs vroot.h!
1730 /* Get the version number, for error messages. */
1732 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1733 char *s1, *s2, *s3, *s4;
1734 s1 = (char *) strchr(v, ' '); s1++;
1735 s2 = (char *) strchr(s1, ' ');
1736 s3 = (char *) strchr(v, '('); s3++;
1737 s4 = (char *) strchr(s3, ')');
1740 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1744 /* We must read exactly the same resources as xscreensaver.
1745 That means we must have both the same progclass *and* progname,
1746 at least as far as the resource database is concerned. So,
1747 put "xscreensaver" in argv[0] while initializing Xt.
1749 progname = argv[0] = "xscreensaver";
1751 /* allow one dash or two. */
1752 for (i = 1; i < argc; i++)
1753 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1755 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1757 dpy = XtDisplay (toplevel);
1758 screen = XtScreen (toplevel);
1759 db = XtDatabase (dpy);
1760 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1761 XSetErrorHandler (x_ehandler);
1764 /* Randomize -- only need to do this here because this program
1765 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1766 # undef ya_rand_init
1769 memset (&P, 0, sizeof(P));
1771 load_init_file (&P);
1773 progname = argv[0] = oprogname;
1775 for (i = 1; i < argc; i++)
1780 /* Have to re-process these, or else the .xscreensaver file
1781 has priority over the command line...
1783 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1785 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1786 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1787 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1788 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1789 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1790 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1791 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1792 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1793 P.image_directory = argv[++i];
1794 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1798 fprintf (stderr, "%s: both %s and %s specified?\n",
1799 progname, argv[i], window_str);
1802 window_str = argv[i];
1803 window = VirtualRootWindowOfScreen (screen);
1805 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1806 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1811 fprintf (stderr, "%s: both %s and %s specified?\n",
1812 progname, drawable_str, argv[i]);
1817 drawable_str = argv[i];
1818 drawable = (Drawable) w;
1822 window_str = argv[i];
1823 window = (Window) w;
1828 if (argv[i][0] == '-')
1829 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1832 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1836 __extension__ /* don't warn about "string length is greater than
1837 the length ISO C89 compilers are required to
1838 support" in the usage string... */
1840 fprintf (stderr, USAGE, progname, version, progname);
1847 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1853 if (P.verbose_p) /* Print out all the resources we can see. */
1855 XrmName name = { 0 };
1856 XrmClass class = { 0 };
1858 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1859 (XtPointer) &count);
1863 if (!window) abort();
1864 if (!drawable) drawable = window;
1866 get_image (screen, window, drawable, P.verbose_p,
1867 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1868 P.image_directory, file);