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;
139 #ifndef USE_EXTERNAL_SCREEN_GRABBER
141 ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error)
143 if (error->error_code == BadMatch)
144 return ignore_all_errors_ehandler (dpy, error);
146 return x_ehandler (dpy, error);
148 #endif /* ! USE_EXTERNAL_SCREEN_GRABBER */
151 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
154 drawable_window_p (Display *dpy, Drawable d)
156 XErrorHandler old_handler;
157 XWindowAttributes xgwa;
160 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
161 error_handler_hit_p = False;
162 XGetWindowAttributes (dpy, d, &xgwa);
164 XSetErrorHandler (old_handler);
167 if (!error_handler_hit_p)
168 return True; /* It's a Window. */
170 return False; /* It's a Pixmap, or an invalid ID. */
174 /* Returns true if the window is the root window, or a virtual root window,
175 but *not* the xscreensaver window. That is, if it's a "real" desktop
176 root window of some kind.
179 root_window_p (Screen *screen, Window window)
181 Display *dpy = DisplayOfScreen (screen);
184 unsigned long nitems, bytesafter;
185 unsigned char *version;
187 if (window != RootWindowOfScreen (screen))
190 if (XGetWindowProperty (dpy, window,
191 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
192 0, 1, False, XA_STRING,
193 &type, &format, &nitems, &bytesafter,
203 /* Clear the window or pixmap to black, or its background color.
206 clear_drawable (Screen *screen, Drawable drawable)
208 Display *dpy = DisplayOfScreen (screen);
213 unsigned int w, h, bw, d;
214 XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
216 /* The window might have no-op background of None, so to clear it,
217 draw a black rectangle first, then do XClearWindow (in case the
218 actual background color is non-black...) */
220 /* #### really we should allocate "black" instead, but I'm lazy... */
221 gcv.foreground = BlackPixelOfScreen (screen);
222 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
223 XFillRectangle (dpy, drawable, gc, 0, 0, w, h);
225 if (drawable_window_p (dpy, drawable))
226 XClearWindow (dpy, (Window) drawable);
231 /* Figure out what kind of scaling/positioning we ought to do to display
232 a src-sized image in a dest-sized window/pixmap. Returns the width
233 and height to which the image should be scaled, and the position where
234 it should be displayed to center it.
237 compute_image_scaling (int src_w, int src_h,
238 int dest_w, int dest_h,
240 int *scaled_from_x_ret, int *scaled_from_y_ret,
241 int *scaled_to_x_ret, int *scaled_to_y_ret,
242 int *scaled_w_ret, int *scaled_h_ret)
244 int srcx, srcy, destx, desty;
246 Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
247 (src_h == dest_h && src_w <= dest_w));
249 if (!exact_fit_p) /* scale the image up or down */
251 float rw = (float) dest_w / src_w;
252 float rh = (float) dest_h / src_h;
253 float r = (rw < rh ? rw : rh);
259 /* this optimization breaks things */
260 if (pct < 95 || pct > 105) /* don't scale if it's close */
264 fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
265 progname, pct, src_w, src_h, tw, th);
271 /* Center the image on the window/pixmap. */
274 destx = (dest_w - src_w) / 2;
275 desty = (dest_h - src_h) / 2;
276 if (destx < 0) srcx = -destx, destx = 0;
277 if (desty < 0) srcy = -desty, desty = 0;
279 if (dest_w < src_w) src_w = dest_w;
280 if (dest_h < src_h) src_h = dest_h;
282 *scaled_w_ret = src_w;
283 *scaled_h_ret = src_h;
284 *scaled_from_x_ret = srcx;
285 *scaled_from_y_ret = srcy;
286 *scaled_to_x_ret = destx;
287 *scaled_to_y_ret = desty;
290 fprintf (stderr, "%s: displaying %dx%d image at %d,%d in %dx%d.\n",
291 progname, src_w, src_h, destx, desty, dest_w, dest_h);
295 /* Scales an XImage, modifying it in place.
296 This doesn't do dithering or smoothing, so it might have artifacts.
297 If out of memory, returns False, and the XImage will have been
300 #ifndef USE_EXTERNAL_SCREEN_GRABBER
302 scale_ximage (Screen *screen, Visual *visual,
303 XImage *ximage, int new_width, int new_height)
305 Display *dpy = DisplayOfScreen (screen);
306 int depth = visual_depth (screen, visual);
308 double xscale, yscale;
310 XImage *ximage2 = XCreateImage (dpy, visual, depth,
312 new_width, new_height, 8, 0);
313 ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
317 fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
319 ximage->width, ximage->height,
320 ximage2->width, ximage2->height);
321 if (ximage->data) free (ximage->data);
322 if (ximage2->data) free (ximage2->data);
325 XDestroyImage (ximage);
326 XDestroyImage (ximage2);
330 /* Brute force scaling... */
331 xscale = (double) ximage->width / ximage2->width;
332 yscale = (double) ximage->height / ximage2->height;
333 for (y = 0; y < ximage2->height; y++)
334 for (x = 0; x < ximage2->width; x++)
335 XPutPixel (ximage2, x, y,
336 XGetPixel (ximage, x * xscale, y * yscale));
341 (*ximage) = (*ximage2);
344 XDestroyImage (ximage2);
348 #endif /* ! USE_EXTERNAL_SCREEN_GRABBER */
351 #ifdef HAVE_GDK_PIXBUF
353 /* Reads the given image file and renders it on the Drawable, using GDK.
354 Returns False if it fails.
357 read_file_gdk (Screen *screen, Window window, Drawable drawable,
358 const char *filename, Bool verbose_p,
359 XRectangle *geom_ret)
362 Display *dpy = DisplayOfScreen (screen);
363 unsigned int win_width, win_height, win_depth;
366 # endif /* HAVE_GTK2 */
368 /* Find the size of the Drawable. */
373 XGetGeometry (dpy, drawable,
374 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
377 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
380 # else /* !HAVE_GTK2 */
381 xlib_rgb_init (dpy, screen);
382 # endif /* !HAVE_GTK2 */
384 pb = gdk_pixbuf_new_from_file (filename
387 # endif /* HAVE_GTK2 */
392 fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
394 if (gerr && gerr->message && *gerr->message)
395 fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
396 # endif /* HAVE_GTK2 */
401 int w = gdk_pixbuf_get_width (pb);
402 int h = gdk_pixbuf_get_height (pb);
403 int srcx, srcy, destx, desty, w2, h2;
406 compute_image_scaling (w, h, win_width, win_height, verbose_p,
407 &srcx, &srcy, &destx, &desty, &w2, &h2);
408 if (w != w2 || h != h2)
410 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
411 GDK_INTERP_BILINEAR);
414 gdk_pixbuf_unref (pb);
420 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
423 /* If we're rendering onto the root window (and it's not the
424 xscreensaver pseudo-root) then put the image in the window's
425 background. Otherwise, just paint the image onto the window.
427 bg_p = (window == drawable && root_window_p (screen, window));
433 drawable = XCreatePixmap (dpy, window,
434 win_width, win_height, win_depth);
435 gcv.foreground = BlackPixelOfScreen (screen);
436 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
437 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
441 clear_drawable (screen, drawable);
443 /* #### Note that this always uses the default colormap! Morons!
444 Owen says that in Gnome 2.0, I should try using
445 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
448 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
449 srcx, srcy, destx, desty,
451 GDK_PIXBUF_ALPHA_FULL, 127,
452 XLIB_RGB_DITHER_NORMAL,
456 XSetWindowBackgroundPixmap (dpy, window, drawable);
457 XClearWindow (dpy, window);
465 geom_ret->height = h;
473 #endif /* HAVE_GDK_PIXBUF */
479 /* Allocates a colormap that makes a PseudoColor or DirectColor
480 visual behave like a TrueColor visual of the same depth.
483 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
486 Display *dpy = DisplayOfScreen (screen);
487 int nr, ng, nb, cells;
493 depth = visual_depth (screen, visual);
497 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
498 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
499 default: abort(); break;
502 memset(colors, 0, sizeof(colors));
503 for (r = 0; r < (1 << nr); r++)
504 for (g = 0; g < (1 << ng); g++)
505 for (b = 0; b < (1 << nb); b++)
507 i = (r | (g << nr) | (b << (nr + ng)));
509 colors[i].flags = DoRed|DoGreen|DoBlue;
512 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
513 (r << 4) | (r << 1));
514 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
515 (g << 4) | (g << 1));
516 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
517 (b << 8) | (b << 6) | (b << 4) |
522 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
523 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
524 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
531 int interleave = cells / 8; /* skip around, rather than allocating in
532 order, so that we get better coverage if
533 we can't allocated all of them. */
534 for (j = 0; j < interleave; j++)
535 for (i = 0; i < cells; i += interleave)
536 if (XAllocColor (dpy, cmap, &colors[i + j]))
540 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
541 progname, allocated, cells);
545 /* Find the pixel index that is closest to the given color
546 (using linear distance in RGB space -- which is far from the best way.)
549 find_closest_pixel (XColor *colors, int ncolors,
550 unsigned long r, unsigned long g, unsigned long b)
552 unsigned long distance = ~0;
557 for (i = 0; i < ncolors; i++)
562 rd = r - colors[i].red;
563 gd = g - colors[i].green;
564 bd = b - colors[i].blue;
565 if (rd < 0) rd = -rd;
566 if (gd < 0) gd = -gd;
567 if (bd < 0) bd = -bd;
568 d = (rd << 1) + (gd << 2) + bd;
583 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
584 displayable with the given X colormap. The farther from a perfect
585 color cube the contents of the colormap are, the lossier the
586 transformation will be. No dithering is done.
589 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
591 Display *dpy = DisplayOfScreen (screen);
592 unsigned long map[4097];
597 if (image->depth == 8)
599 else if (image->depth == 12)
604 memset(map, -1, sizeof(*map));
605 memset(colors, -1, sizeof(*colors));
607 for (i = 0; i < cells; i++)
609 XQueryColors (dpy, cmap, colors, cells);
612 fprintf(stderr, "%s: building color cube for %d bit image\n",
613 progname, image->depth);
615 for (i = 0; i < cells; i++)
617 unsigned short r, g, b;
621 /* "RRR GGG BB" In an 8 bit map. Convert that to
622 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
628 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
629 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
630 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
631 (b << 6) | (b << 4) | (b << 2) | b);
635 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
636 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
639 g = (i & 0x0F0) >> 4;
640 b = (i & 0xF00) >> 8;
642 r = (r << 12) | (r << 8) | (r << 4) | r;
643 g = (g << 12) | (g << 8) | (g << 4) | g;
644 b = (b << 12) | (b << 8) | (b << 4) | b;
647 map[i] = find_closest_pixel (colors, cells, r, g, b);
651 fprintf(stderr, "%s: remapping colors in %d bit image\n",
652 progname, image->depth);
654 for (y = 0; y < image->height; y++)
655 for (x = 0; x < image->width; x++)
657 unsigned long pixel = XGetPixel(image, x, y);
658 if (pixel >= cells) abort();
659 XPutPixel(image, x, y, map[pixel]);
664 /* If the file has a PPM (P6) on it, read it and return an XImage.
665 Otherwise, rewind the fd back to the beginning, and return 0.
668 maybe_read_ppm (Screen *screen, Visual *visual,
669 const char *filename, FILE *in, Bool verbose_p)
671 Display *dpy = DisplayOfScreen (screen);
672 int depth = visual_depth (screen, visual);
678 int x, y, w, h, maxval;
681 if (fstat (fileno (in), &st))
685 buf = (char *) malloc (bufsiz + 1);
688 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
689 progname, bufsiz, filename);
693 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
696 if (!strncmp (buf, "\107\111", 2))
698 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
699 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
703 else if (!strncmp (buf, "\211\120", 2))
705 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
706 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
711 if (strncmp (s, "P6", 2))
714 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
716 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
718 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
722 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
724 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
726 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
731 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
732 progname, filename, maxval);
736 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
739 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
740 if (!ximage || !ximage->data)
742 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
743 progname, ximage->width, ximage->height, filename);
749 while ((i = fread (s, 1, j, in)) > 0)
753 for (y = 0; y < ximage->height; y++)
754 for (x = 0; x < ximage->width; x++)
756 unsigned char r = buf[i++];
757 unsigned char g = buf[i++];
758 unsigned char b = buf[i++];
762 pixel = (r << 16) | (g << 8) | b;
764 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
765 else if (depth == 12)
766 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
767 else if (depth == 16 || depth == 15)
768 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
772 XPutPixel (ximage, x, y, pixel);
780 if (ximage && ximage->data)
785 if (ximage) XDestroyImage (ximage);
786 fseek (in, 0, SEEK_SET);
792 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
793 const char *filename;
798 } getimg_jpg_error_mgr;
802 jpg_output_message (j_common_ptr cinfo)
804 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
805 char buf[JMSG_LENGTH_MAX];
806 cinfo->err->format_message (cinfo, buf);
807 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
812 jpg_error_exit (j_common_ptr cinfo)
814 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
815 cinfo->err->output_message (cinfo);
816 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
818 XSync (DisplayOfScreen (err->screen), False);
823 /* Reads a JPEG file, returns an RGB XImage of it.
826 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
827 Colormap cmap, const char *filename, Bool verbose_p)
829 Display *dpy = DisplayOfScreen (screen);
830 int depth = visual_depth (screen, visual);
834 struct jpeg_decompress_struct cinfo;
835 getimg_jpg_error_mgr jerr;
836 JSAMPARRAY scanbuf = 0;
839 jerr.filename = filename;
840 jerr.screen = screen;
841 jerr.visual = visual;
842 jerr.drawable = drawable;
845 if (! (depth >= 15 || depth == 12 || depth == 8))
847 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
851 in = fopen (filename, "rb");
854 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
858 /* Check to see if it's a PPM, and if so, read that instead of using
859 the JPEG library. Yeah, this is all modular and stuff.
861 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
867 cinfo.err = jpeg_std_error (&jerr.pub);
868 jerr.pub.output_message = jpg_output_message;
869 jerr.pub.error_exit = jpg_error_exit;
871 jpeg_create_decompress (&cinfo);
872 jpeg_stdio_src (&cinfo, in);
873 jpeg_read_header (&cinfo, TRUE);
875 /* set some decode parameters */
876 cinfo.out_color_space = JCS_RGB;
877 cinfo.quantize_colors = FALSE;
879 jpeg_start_decompress (&cinfo);
881 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
882 cinfo.output_width, cinfo.output_height,
885 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
887 if (ximage && ximage->data)
888 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
889 cinfo.rec_outbuf_height *
891 cinfo.output_components,
893 if (!ximage || !ximage->data || !scanbuf)
895 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
896 progname, ximage->width, ximage->height, filename);
901 while (cinfo.output_scanline < cinfo.output_height)
903 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
905 for (i = 0; i < n; i++)
908 for (x = 0; x < ximage->width; x++)
910 int j = x * cinfo.output_components;
911 unsigned char r = scanbuf[i][j];
912 unsigned char g = scanbuf[i][j+1];
913 unsigned char b = scanbuf[i][j+2];
917 pixel = (r << 16) | (g << 8) | b;
919 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
920 else if (depth == 12)
921 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
922 else if (depth == 15)
923 /* Gah! I don't understand why these are in the other
925 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
926 else if (depth == 16)
927 pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
931 XPutPixel (ximage, x, y, pixel);
937 if (cinfo.output_scanline < cinfo.output_height)
938 /* don't goto FAIL -- we might have viewable partial data. */
939 jpeg_abort_decompress (&cinfo);
941 jpeg_finish_decompress (&cinfo);
943 jpeg_destroy_decompress (&cinfo);
951 if (ximage && ximage->data)
956 if (ximage) XDestroyImage (ximage);
957 if (scanbuf) free (scanbuf);
962 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
963 Returns False if it fails.
966 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
967 const char *filename, Bool verbose_p,
968 XRectangle *geom_ret)
970 Display *dpy = DisplayOfScreen (screen);
975 unsigned int win_width, win_height, win_depth;
976 int srcx, srcy, destx, desty, w2, h2;
978 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
983 XWindowAttributes xgwa;
984 XGetWindowAttributes (dpy, window, &xgwa);
985 visual = xgwa.visual;
986 cmap = xgwa.colormap;
987 XGetGeometry (dpy, drawable,
988 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
991 /* Make sure we're not on some weirdo visual...
993 class = visual_class (screen, visual);
994 depth = visual_depth (screen, visual);
995 if ((class == PseudoColor || class == DirectColor) &&
996 (depth != 8 && depth != 12))
998 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
1005 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
1006 filename, verbose_p);
1007 if (!ximage) return False;
1009 /* Scale it, if necessary...
1011 compute_image_scaling (ximage->width, ximage->height,
1012 win_width, win_height, verbose_p,
1013 &srcx, &srcy, &destx, &desty, &w2, &h2);
1014 if (ximage->width != w2 || ximage->height != h2)
1015 if (! scale_ximage (screen, visual, ximage, w2, h2))
1018 /* Allocate a colormap, if we need to...
1020 if (class == PseudoColor || class == DirectColor)
1022 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
1023 remap_image (screen, cmap, ximage, verbose_p);
1026 /* Finally, put the resized image on the window.
1032 /* If we're rendering onto the root window (and it's not the xscreensaver
1033 pseudo-root) then put the image in the window's background. Otherwise,
1034 just paint the image onto the window.
1036 if (window == drawable && root_window_p (screen, window))
1038 Pixmap bg = XCreatePixmap (dpy, window,
1039 win_width, win_height, win_depth);
1040 gcv.foreground = BlackPixelOfScreen (screen);
1041 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1042 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1043 XPutImage (dpy, bg, gc, ximage,
1044 srcx, srcy, destx, desty, ximage->width, ximage->height);
1045 XSetWindowBackgroundPixmap (dpy, window, bg);
1046 XClearWindow (dpy, window);
1050 gc = XCreateGC (dpy, drawable, 0, &gcv);
1051 clear_drawable (screen, drawable);
1052 XPutImage (dpy, drawable, gc, ximage,
1053 srcx, srcy, destx, desty, ximage->width, ximage->height);
1061 geom_ret->x = destx;
1062 geom_ret->y = desty;
1063 geom_ret->width = ximage->width;
1064 geom_ret->height = ximage->height;
1067 free (ximage->data);
1069 XDestroyImage (ximage);
1074 #endif /* HAVE_JPEGLIB */
1077 /* Reads the given image file and renders it on the Drawable.
1078 Returns False if it fails.
1081 display_file (Screen *screen, Window window, Drawable drawable,
1082 const char *filename, Bool verbose_p,
1083 XRectangle *geom_ret)
1086 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1088 # if defined(HAVE_GDK_PIXBUF)
1089 if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret))
1091 # elif defined(HAVE_JPEGLIB)
1092 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p,
1095 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1096 /* shouldn't get here if we have no image-loading methods available. */
1098 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1104 /* Invokes a sub-process and returns its output (presumably, a file to
1105 load.) Free the string when done. 'grab_type' controls which program
1109 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1112 Display *dpy = DisplayOfScreen (screen);
1123 av[ac++] = GETIMAGE_FILE_PROGRAM;
1125 av[ac++] = "--verbose";
1126 av[ac++] = "--name";
1127 av[ac++] = (char *) directory;
1131 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1133 av[ac++] = "--verbose";
1134 av[ac++] = "--name";
1137 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1139 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1141 av[ac++] = "--verbose";
1142 av[ac++] = "--name";
1154 fprintf (stderr, "%s: executing:", progname);
1155 for (i = 0; i < ac; i++)
1156 fprintf (stderr, " %s", av[i]);
1157 fprintf (stderr, "\n");
1162 sprintf (buf, "%s: error creating pipe", progname);
1170 switch ((int) (forked = fork ()))
1174 sprintf (buf, "%s: couldn't fork", progname);
1182 close (in); /* don't need this one */
1183 close (ConnectionNumber (dpy)); /* close display fd */
1185 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1187 sprintf (buf, "%s: could not dup() a new stdout", progname);
1188 exit (-1); /* exits fork */
1191 execvp (av[0], av); /* shouldn't return. */
1192 exit (-1); /* exits fork */
1198 int wait_status = 0;
1199 FILE *f = fdopen (in, "r");
1202 close (out); /* don't need this one */
1204 if (! fgets (buf, sizeof(buf)-1, f))
1208 /* Wait for the child to die. */
1209 waitpid (-1, &wait_status, 0);
1212 while (L && buf[L-1] == '\n')
1219 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1224 return strdup (buf);
1232 /* Returns a pathname to an image file. Free the string when you're done.
1235 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1237 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1241 /* Grabs a video frame to a file, and returns a pathname to that file.
1242 Delete that file when you are done with it (and free the string.)
1245 get_video_filename (Screen *screen, Bool verbose_p)
1247 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1250 /* Grabs a desktop image to a file, and returns a pathname to that file.
1251 Delete that file when you are done with it (and free the string.)
1253 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1255 get_desktop_filename (Screen *screen, Bool verbose_p)
1257 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1259 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1262 /* Grabs a video frame, and renders it on the Drawable.
1263 Returns False if it fails;
1266 display_video (Screen *screen, Window window, Drawable drawable,
1267 Bool verbose_p, XRectangle *geom_ret)
1269 char *filename = get_video_filename (screen, verbose_p);
1275 fprintf (stderr, "%s: video grab failed.\n", progname);
1279 status = display_file (screen, window, drawable, filename, verbose_p,
1282 if (unlink (filename))
1285 sprintf (buf, "%s: rm %.100s", progname, filename);
1289 fprintf (stderr, "%s: rm %s\n", progname, filename);
1291 if (filename) free (filename);
1296 /* Grabs a desktop screen shot onto the window and the drawable.
1297 If the window and drawable are not the same size, the image in
1298 the drawable is scaled to fit.
1299 Returns False if it fails.
1302 display_desktop (Screen *screen, Window window, Drawable drawable,
1303 Bool verbose_p, XRectangle *geom_ret)
1305 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1307 Display *dpy = DisplayOfScreen (screen);
1308 Bool top_p = top_level_window_p (screen, window);
1315 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1316 (unsigned long) window);
1317 XUnmapWindow (dpy, window);
1321 filename = get_desktop_filename (screen, verbose_p);
1326 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1327 (unsigned long) window);
1328 XMapRaised (dpy, window);
1335 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1339 status = display_file (screen, window, drawable, filename, verbose_p,
1342 if (unlink (filename))
1345 sprintf (buf, "%s: rm %.100s", progname, filename);
1349 fprintf (stderr, "%s: rm %s\n", progname, filename);
1351 if (filename) free (filename);
1354 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1356 Display *dpy = DisplayOfScreen (screen);
1358 XWindowAttributes xgwa;
1361 unsigned int pw, ph, pbw, pd;
1362 int srcx, srcy, destx, desty, w2, h2;
1366 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1367 grabscreen_verbose();
1370 XGetWindowAttributes (dpy, window, &xgwa);
1371 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1373 grab_screen_image_internal (screen, window);
1375 compute_image_scaling (xgwa.width, xgwa.height,
1377 &srcx, &srcy, &destx, &desty, &w2, &h2);
1379 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1381 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1382 XCopyArea (dpy, window, drawable, gc,
1383 0, 0, xgwa.width, xgwa.height, 0, 0);
1386 else /* size mismatch -- must scale client-side images to fit drawable */
1390 XErrorHandler old_handler;
1393 old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
1394 error_handler_hit_p = False;
1396 /* This can return BadMatch if the window is not fully on screen.
1397 Trap that error and return color bars in that case.
1398 (Note that this only happens with XGetImage, not with XCopyArea:
1399 yet another totally gratuitous inconsistency in X, thanks.)
1401 ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1405 XSetErrorHandler (old_handler);
1408 if (error_handler_hit_p)
1412 fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
1413 progname, (unsigned int) window);
1417 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1420 gc = XCreateGC (dpy, drawable, 0, &gcv);
1421 clear_drawable (screen, drawable);
1422 XPutImage (dpy, drawable, gc, ximage,
1423 srcx, srcy, destx, desty, ximage->width, ximage->height);
1424 XDestroyImage (ximage);
1430 geom_ret->x = destx;
1431 geom_ret->y = desty;
1432 geom_ret->width = w2;
1433 geom_ret->height = h2;
1439 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1443 /* Grabs an image (from a file, video, or the desktop) and renders it on
1444 the Drawable. If `file' is specified, always use that file. Otherwise,
1445 select randomly, based on the other arguments.
1448 get_image (Screen *screen,
1449 Window window, Drawable drawable,
1457 Display *dpy = DisplayOfScreen (screen);
1458 grab_type which = GRAB_BARS;
1460 const char *file_prop = 0;
1461 XRectangle geom = { 0, 0, 0, 0 };
1463 if (! drawable_window_p (dpy, window))
1465 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1466 progname, (unsigned long) window);
1470 /* Make sure the Screen and the Window correspond. */
1472 XWindowAttributes xgwa;
1473 XGetWindowAttributes (dpy, window, &xgwa);
1474 screen = xgwa.screen;
1477 if (file && stat (file, &st))
1479 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1485 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1486 progname, desk_p ? "True" : "False");
1487 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1488 progname, video_p ? "True" : "False");
1489 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1490 progname, image_p ? "True" : "False");
1491 fprintf (stderr, "%s: imageDirectory: %s\n",
1492 progname, (file ? file : dir ? dir : ""));
1495 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1496 image_p = False; /* can't load images from files... */
1497 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1498 desk_p = False; /* ...or from desktops grabbed to files. */
1504 "%s: image file loading not available at compile-time\n",
1506 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1509 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1517 else if (!dir || !*dir)
1519 if (verbose_p && image_p)
1521 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1528 # error Error! This file definitely needs vroot.h!
1531 /* We can grab desktop images (using the normal X11 method) if:
1532 - the window is the real root window;
1533 - the window is a toplevel window.
1534 We cannot grab desktop images that way if:
1535 - the window is a non-top-level window.
1537 Using the MacOS X way, desktops are just like loaded image files.
1539 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1542 if (!top_level_window_p (screen, window))
1547 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1548 progname, (unsigned int) window);
1551 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1553 if (! (desk_p || video_p || image_p))
1559 /* Loop until we get one that's permitted.
1560 If files or video are permitted, do them more often
1563 D+V+I: 10% + 45% + 45%.
1569 n = (random() % 100);
1570 if (++i > 300) abort();
1571 else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */
1572 else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */
1573 else if (image_p) which = GRAB_FILE; /* 45% */
1578 /* If we're to search a directory to find an image file, do so now.
1580 if (which == GRAB_FILE && !file)
1582 file = get_filename (screen, dir, verbose_p);
1587 fprintf (stderr, "%s: no image files found.\n", progname);
1591 /* Now actually render something.
1597 XWindowAttributes xgwa;
1600 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1601 XGetWindowAttributes (dpy, window, &xgwa);
1602 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1609 if (! display_desktop (screen, window, drawable, verbose_p, &geom))
1611 file_prop = "desktop";
1615 if (! display_file (screen, window, drawable, file, verbose_p, &geom))
1621 if (! display_video (screen, window, drawable, verbose_p, &geom))
1623 file_prop = "video";
1632 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1633 if (file_prop && *file_prop)
1634 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1635 (unsigned char *) file_prop, strlen(file_prop));
1637 XDeleteProperty (dpy, window, a);
1639 a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
1643 sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
1644 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1645 (unsigned char *) gstr, strlen (gstr));
1648 XDeleteProperty (dpy, window, a);
1657 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1658 XrmRepresentation *type, XrmValue *value, XPointer closure)
1661 for (i = 0; quarks[i]; i++)
1663 if (bindings[i] == XrmBindTightly)
1664 fprintf (stderr, (i == 0 ? "" : "."));
1665 else if (bindings[i] == XrmBindLoosely)
1666 fprintf (stderr, "*");
1668 fprintf (stderr, " ??? ");
1669 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1672 fprintf (stderr, ": %s\n", (char *) value->addr);
1679 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1683 " %s puts an image on the given window or pixmap.\n" \
1685 " It is used by those xscreensaver demos that operate on images.\n" \
1686 " The image may be a file loaded from disk, a frame grabbed from\n" \
1687 " the system's video camera, or a screenshot of the desktop,\n" \
1688 " depending on command-line options or the ~/.xscreensaver file.\n" \
1690 " Options include:\n" \
1692 " -display host:dpy.screen which display to use\n" \
1693 " -root draw to the root window\n" \
1694 " -verbose print diagnostics\n" \
1695 " -images / -no-images whether to allow image file loading\n" \
1696 " -video / -no-video whether to allow video grabs\n" \
1697 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1698 " -directory <path> where to find image files to load\n" \
1699 " -file <filename> load this image file\n" \
1701 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1702 " defaults for these options in your ~/.xscreensaver file.\n" \
1706 main (int argc, char **argv)
1708 saver_preferences P;
1712 char *oprogname = progname;
1716 Window window = (Window) 0;
1717 Drawable drawable = (Drawable) 0;
1718 const char *window_str = 0;
1719 const char *drawable_str = 0;
1724 s = strrchr (progname, '/');
1725 if (s) progname = s+1;
1726 oprogname = progname;
1728 /* half-assed way of avoiding buffer-overrun attacks. */
1729 if (strlen (progname) >= 100) progname[100] = 0;
1732 # error Error! This file definitely needs vroot.h!
1735 /* Get the version number, for error messages. */
1737 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1738 char *s1, *s2, *s3, *s4;
1739 s1 = (char *) strchr(v, ' '); s1++;
1740 s2 = (char *) strchr(s1, ' ');
1741 s3 = (char *) strchr(v, '('); s3++;
1742 s4 = (char *) strchr(s3, ')');
1745 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1749 /* We must read exactly the same resources as xscreensaver.
1750 That means we must have both the same progclass *and* progname,
1751 at least as far as the resource database is concerned. So,
1752 put "xscreensaver" in argv[0] while initializing Xt.
1754 progname = argv[0] = "xscreensaver";
1756 /* allow one dash or two. */
1757 for (i = 1; i < argc; i++)
1758 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1760 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1762 dpy = XtDisplay (toplevel);
1763 screen = XtScreen (toplevel);
1764 db = XtDatabase (dpy);
1765 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1766 XSetErrorHandler (x_ehandler);
1769 /* Randomize -- only need to do this here because this program
1770 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1771 # undef ya_rand_init
1774 memset (&P, 0, sizeof(P));
1776 load_init_file (&P);
1778 progname = argv[0] = oprogname;
1780 for (i = 1; i < argc; i++)
1785 /* Have to re-process these, or else the .xscreensaver file
1786 has priority over the command line...
1788 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1790 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1791 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1792 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1793 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1794 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1795 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1796 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1797 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1798 P.image_directory = argv[++i];
1799 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1803 fprintf (stderr, "%s: both %s and %s specified?\n",
1804 progname, argv[i], window_str);
1807 window_str = argv[i];
1808 window = VirtualRootWindowOfScreen (screen);
1810 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1811 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1816 fprintf (stderr, "%s: both %s and %s specified?\n",
1817 progname, drawable_str, argv[i]);
1822 drawable_str = argv[i];
1823 drawable = (Drawable) w;
1827 window_str = argv[i];
1828 window = (Window) w;
1833 if (argv[i][0] == '-')
1834 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1837 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1841 __extension__ /* don't warn about "string length is greater than
1842 the length ISO C89 compilers are required to
1843 support" in the usage string... */
1845 fprintf (stderr, USAGE, progname, version, progname);
1852 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1858 if (P.verbose_p) /* Print out all the resources we can see. */
1860 XrmName name = { 0 };
1861 XrmClass class = { 0 };
1863 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1864 (XtPointer) &count);
1868 if (!window) abort();
1869 if (!drawable) drawable = window;
1871 get_image (screen, window, drawable, P.verbose_p,
1872 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1873 P.image_directory, file);