1 /* xscreensaver, Copyright (c) 2001-2006 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"
104 extern const char *blurb (void);
114 x_ehandler (Display *dpy, XErrorEvent *error)
116 if (error->error_code == BadWindow || error->error_code == BadDrawable)
118 fprintf (stderr, "%s: target %s 0x%lx unexpectedly deleted\n", progname,
119 (error->error_code == BadWindow ? "window" : "pixmap"),
120 (unsigned long) error->resourceid);
124 fprintf (stderr, "\nX error in %s:\n", progname);
125 XmuPrintDefaultErrorMessage (dpy, error, stderr);
132 static Bool error_handler_hit_p = False;
135 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
137 error_handler_hit_p = True;
141 #ifndef USE_EXTERNAL_SCREEN_GRABBER
143 ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error)
145 if (error->error_code == BadMatch)
146 return ignore_all_errors_ehandler (dpy, error);
148 return x_ehandler (dpy, error);
150 #endif /* ! USE_EXTERNAL_SCREEN_GRABBER */
153 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
156 drawable_window_p (Display *dpy, Drawable d)
158 XErrorHandler old_handler;
159 XWindowAttributes xgwa;
162 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
163 error_handler_hit_p = False;
164 XGetWindowAttributes (dpy, d, &xgwa);
166 XSetErrorHandler (old_handler);
169 if (!error_handler_hit_p)
170 return True; /* It's a Window. */
172 return False; /* It's a Pixmap, or an invalid ID. */
176 /* Returns true if the window is the root window, or a virtual root window,
177 but *not* the xscreensaver window. That is, if it's a "real" desktop
178 root window of some kind.
181 root_window_p (Screen *screen, Window window)
183 Display *dpy = DisplayOfScreen (screen);
186 unsigned long nitems, bytesafter;
187 unsigned char *version;
189 if (window != RootWindowOfScreen (screen))
192 if (XGetWindowProperty (dpy, window,
193 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
194 0, 1, False, XA_STRING,
195 &type, &format, &nitems, &bytesafter,
205 /* Clear the window or pixmap to black, or its background color.
208 clear_drawable (Screen *screen, Drawable drawable)
210 Display *dpy = DisplayOfScreen (screen);
215 unsigned int w, h, bw, d;
216 XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
218 /* The window might have no-op background of None, so to clear it,
219 draw a black rectangle first, then do XClearWindow (in case the
220 actual background color is non-black...) */
222 /* #### really we should allocate "black" instead, but I'm lazy... */
223 gcv.foreground = BlackPixelOfScreen (screen);
224 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
225 XFillRectangle (dpy, drawable, gc, 0, 0, w, h);
227 if (drawable_window_p (dpy, drawable))
228 XClearWindow (dpy, (Window) drawable);
233 /* Figure out what kind of scaling/positioning we ought to do to display
234 a src-sized image in a dest-sized window/pixmap. Returns the width
235 and height to which the image should be scaled, and the position where
236 it should be displayed to center it.
239 compute_image_scaling (int src_w, int src_h,
240 int dest_w, int dest_h,
242 int *scaled_from_x_ret, int *scaled_from_y_ret,
243 int *scaled_to_x_ret, int *scaled_to_y_ret,
244 int *scaled_w_ret, int *scaled_h_ret)
246 int srcx, srcy, destx, desty;
248 Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
249 (src_h == dest_h && src_w <= dest_w));
251 if (!exact_fit_p) /* scale the image up or down */
253 float rw = (float) dest_w / src_w;
254 float rh = (float) dest_h / src_h;
255 float r = (rw < rh ? rw : rh);
261 /* this optimization breaks things */
262 if (pct < 95 || pct > 105) /* don't scale if it's close */
266 fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
267 progname, pct, src_w, src_h, tw, th);
273 /* Center the image on the window/pixmap. */
276 destx = (dest_w - src_w) / 2;
277 desty = (dest_h - src_h) / 2;
278 if (destx < 0) srcx = -destx, destx = 0;
279 if (desty < 0) srcy = -desty, desty = 0;
281 if (dest_w < src_w) src_w = dest_w;
282 if (dest_h < src_h) src_h = dest_h;
284 *scaled_w_ret = src_w;
285 *scaled_h_ret = src_h;
286 *scaled_from_x_ret = srcx;
287 *scaled_from_y_ret = srcy;
288 *scaled_to_x_ret = destx;
289 *scaled_to_y_ret = desty;
292 fprintf (stderr, "%s: displaying %dx%d image at %d,%d in %dx%d.\n",
293 progname, src_w, src_h, destx, desty, dest_w, dest_h);
297 /* Scales an XImage, modifying it in place.
298 This doesn't do dithering or smoothing, so it might have artifacts.
299 If out of memory, returns False, and the XImage will have been
302 #if !defined(USE_EXTERNAL_SCREEN_GRABBER) || defined(HAVE_JPEGLIB)
304 scale_ximage (Screen *screen, Visual *visual,
305 XImage *ximage, int new_width, int new_height)
307 Display *dpy = DisplayOfScreen (screen);
308 int depth = visual_depth (screen, visual);
310 double xscale, yscale;
312 XImage *ximage2 = XCreateImage (dpy, visual, depth,
314 new_width, new_height, 8, 0);
315 ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
319 fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
321 ximage->width, ximage->height,
322 ximage2->width, ximage2->height);
323 if (ximage->data) free (ximage->data);
324 if (ximage2->data) free (ximage2->data);
327 XDestroyImage (ximage);
328 XDestroyImage (ximage2);
332 /* Brute force scaling... */
333 xscale = (double) ximage->width / ximage2->width;
334 yscale = (double) ximage->height / ximage2->height;
335 for (y = 0; y < ximage2->height; y++)
336 for (x = 0; x < ximage2->width; x++)
337 XPutPixel (ximage2, x, y,
338 XGetPixel (ximage, x * xscale, y * yscale));
343 (*ximage) = (*ximage2);
346 XDestroyImage (ximage2);
350 #endif /* !USE_EXTERNAL_SCREEN_GRABBER || HAVE_JPEGLIB */
353 #ifdef HAVE_GDK_PIXBUF
355 /* Reads the given image file and renders it on the Drawable, using GDK.
356 Returns False if it fails.
359 read_file_gdk (Screen *screen, Window window, Drawable drawable,
360 const char *filename, Bool verbose_p,
361 XRectangle *geom_ret)
364 Display *dpy = DisplayOfScreen (screen);
365 unsigned int win_width, win_height, win_depth;
368 # endif /* HAVE_GTK2 */
370 /* Find the size of the Drawable. */
375 XGetGeometry (dpy, drawable,
376 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
379 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
382 # else /* !HAVE_GTK2 */
383 xlib_rgb_init (dpy, screen);
384 # endif /* !HAVE_GTK2 */
386 pb = gdk_pixbuf_new_from_file (filename
389 # endif /* HAVE_GTK2 */
394 fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
396 if (gerr && gerr->message && *gerr->message)
397 fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
398 # endif /* HAVE_GTK2 */
403 int w = gdk_pixbuf_get_width (pb);
404 int h = gdk_pixbuf_get_height (pb);
405 int srcx, srcy, destx, desty, w2, h2;
408 compute_image_scaling (w, h, win_width, win_height, verbose_p,
409 &srcx, &srcy, &destx, &desty, &w2, &h2);
410 if (w != w2 || h != h2)
412 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
413 GDK_INTERP_BILINEAR);
416 gdk_pixbuf_unref (pb);
422 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
425 /* If we're rendering onto the root window (and it's not the
426 xscreensaver pseudo-root) then put the image in the window's
427 background. Otherwise, just paint the image onto the window.
429 bg_p = (window == drawable && root_window_p (screen, window));
435 drawable = XCreatePixmap (dpy, window,
436 win_width, win_height, win_depth);
437 gcv.foreground = BlackPixelOfScreen (screen);
438 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
439 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
443 clear_drawable (screen, drawable);
445 /* #### Note that this always uses the default colormap! Morons!
446 Owen says that in Gnome 2.0, I should try using
447 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
450 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
451 srcx, srcy, destx, desty,
453 GDK_PIXBUF_ALPHA_FULL, 127,
454 XLIB_RGB_DITHER_NORMAL,
458 XSetWindowBackgroundPixmap (dpy, window, drawable);
459 XClearWindow (dpy, window);
467 geom_ret->height = h;
475 #endif /* HAVE_GDK_PIXBUF */
481 /* Allocates a colormap that makes a PseudoColor or DirectColor
482 visual behave like a TrueColor visual of the same depth.
485 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
488 Display *dpy = DisplayOfScreen (screen);
489 int nr, ng, nb, cells;
495 depth = visual_depth (screen, visual);
499 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
500 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
501 default: abort(); break;
504 memset(colors, 0, sizeof(colors));
505 for (r = 0; r < (1 << nr); r++)
506 for (g = 0; g < (1 << ng); g++)
507 for (b = 0; b < (1 << nb); b++)
509 i = (r | (g << nr) | (b << (nr + ng)));
511 colors[i].flags = DoRed|DoGreen|DoBlue;
514 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
515 (r << 4) | (r << 1));
516 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
517 (g << 4) | (g << 1));
518 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
519 (b << 8) | (b << 6) | (b << 4) |
524 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
525 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
526 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
533 int interleave = cells / 8; /* skip around, rather than allocating in
534 order, so that we get better coverage if
535 we can't allocated all of them. */
536 for (j = 0; j < interleave; j++)
537 for (i = 0; i < cells; i += interleave)
538 if (XAllocColor (dpy, cmap, &colors[i + j]))
542 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
543 progname, allocated, cells);
547 /* Find the pixel index that is closest to the given color
548 (using linear distance in RGB space -- which is far from the best way.)
551 find_closest_pixel (XColor *colors, int ncolors,
552 unsigned long r, unsigned long g, unsigned long b)
554 unsigned long distance = ~0;
559 for (i = 0; i < ncolors; i++)
564 rd = r - colors[i].red;
565 gd = g - colors[i].green;
566 bd = b - colors[i].blue;
567 if (rd < 0) rd = -rd;
568 if (gd < 0) gd = -gd;
569 if (bd < 0) bd = -bd;
570 d = (rd << 1) + (gd << 2) + bd;
585 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
586 displayable with the given X colormap. The farther from a perfect
587 color cube the contents of the colormap are, the lossier the
588 transformation will be. No dithering is done.
591 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
593 Display *dpy = DisplayOfScreen (screen);
594 unsigned long map[4097];
599 if (image->depth == 8)
601 else if (image->depth == 12)
606 memset(map, -1, sizeof(*map));
607 memset(colors, -1, sizeof(*colors));
609 for (i = 0; i < cells; i++)
611 XQueryColors (dpy, cmap, colors, cells);
614 fprintf(stderr, "%s: building color cube for %d bit image\n",
615 progname, image->depth);
617 for (i = 0; i < cells; i++)
619 unsigned short r, g, b;
623 /* "RRR GGG BB" In an 8 bit map. Convert that to
624 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
630 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
631 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
632 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
633 (b << 6) | (b << 4) | (b << 2) | b);
637 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
638 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
641 g = (i & 0x0F0) >> 4;
642 b = (i & 0xF00) >> 8;
644 r = (r << 12) | (r << 8) | (r << 4) | r;
645 g = (g << 12) | (g << 8) | (g << 4) | g;
646 b = (b << 12) | (b << 8) | (b << 4) | b;
649 map[i] = find_closest_pixel (colors, cells, r, g, b);
653 fprintf(stderr, "%s: remapping colors in %d bit image\n",
654 progname, image->depth);
656 for (y = 0; y < image->height; y++)
657 for (x = 0; x < image->width; x++)
659 unsigned long pixel = XGetPixel(image, x, y);
660 if (pixel >= cells) abort();
661 XPutPixel(image, x, y, map[pixel]);
666 /* If the file has a PPM (P6) on it, read it and return an XImage.
667 Otherwise, rewind the fd back to the beginning, and return 0.
670 maybe_read_ppm (Screen *screen, Visual *visual,
671 const char *filename, FILE *in, Bool verbose_p)
673 Display *dpy = DisplayOfScreen (screen);
674 int depth = visual_depth (screen, visual);
680 int x, y, w, h, maxval;
683 if (fstat (fileno (in), &st))
687 buf = (char *) malloc (bufsiz + 1);
690 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
691 progname, bufsiz, filename);
695 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
698 if (!strncmp (buf, "\107\111", 2))
700 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
701 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
705 else if (!strncmp (buf, "\211\120", 2))
707 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
708 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
713 if (strncmp (s, "P6", 2))
716 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
718 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
720 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
724 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
726 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
728 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
733 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
734 progname, filename, maxval);
738 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
741 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
742 if (!ximage || !ximage->data)
744 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
745 progname, ximage->width, ximage->height, filename);
751 while ((i = fread (s, 1, j, in)) > 0)
755 for (y = 0; y < ximage->height; y++)
756 for (x = 0; x < ximage->width; x++)
758 unsigned char r = buf[i++];
759 unsigned char g = buf[i++];
760 unsigned char b = buf[i++];
764 pixel = (r << 16) | (g << 8) | b;
766 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
767 else if (depth == 12)
768 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
769 else if (depth == 16 || depth == 15)
770 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
774 XPutPixel (ximage, x, y, pixel);
782 if (ximage && ximage->data)
787 if (ximage) XDestroyImage (ximage);
788 fseek (in, 0, SEEK_SET);
794 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
795 const char *filename;
800 } getimg_jpg_error_mgr;
804 jpg_output_message (j_common_ptr cinfo)
806 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
807 char buf[JMSG_LENGTH_MAX];
808 cinfo->err->format_message (cinfo, buf);
809 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
814 jpg_error_exit (j_common_ptr cinfo)
816 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
817 cinfo->err->output_message (cinfo);
818 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
820 XSync (DisplayOfScreen (err->screen), False);
825 /* Reads a JPEG file, returns an RGB XImage of it.
828 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
829 Colormap cmap, const char *filename, Bool verbose_p)
831 Display *dpy = DisplayOfScreen (screen);
832 int depth = visual_depth (screen, visual);
836 struct jpeg_decompress_struct cinfo;
837 getimg_jpg_error_mgr jerr;
838 JSAMPARRAY scanbuf = 0;
841 jerr.filename = filename;
842 jerr.screen = screen;
843 jerr.visual = visual;
844 jerr.drawable = drawable;
847 if (! (depth >= 15 || depth == 12 || depth == 8))
849 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
853 in = fopen (filename, "rb");
856 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
860 /* Check to see if it's a PPM, and if so, read that instead of using
861 the JPEG library. Yeah, this is all modular and stuff.
863 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
869 cinfo.err = jpeg_std_error (&jerr.pub);
870 jerr.pub.output_message = jpg_output_message;
871 jerr.pub.error_exit = jpg_error_exit;
873 jpeg_create_decompress (&cinfo);
874 jpeg_stdio_src (&cinfo, in);
875 jpeg_read_header (&cinfo, TRUE);
877 /* set some decode parameters */
878 cinfo.out_color_space = JCS_RGB;
879 cinfo.quantize_colors = FALSE;
881 jpeg_start_decompress (&cinfo);
883 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
884 cinfo.output_width, cinfo.output_height,
887 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
889 if (ximage && ximage->data)
890 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
891 cinfo.rec_outbuf_height *
893 cinfo.output_components,
895 if (!ximage || !ximage->data || !scanbuf)
897 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
898 progname, ximage->width, ximage->height, filename);
903 while (cinfo.output_scanline < cinfo.output_height)
905 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
907 for (i = 0; i < n; i++)
910 for (x = 0; x < ximage->width; x++)
912 int j = x * cinfo.output_components;
913 unsigned char r = scanbuf[i][j];
914 unsigned char g = scanbuf[i][j+1];
915 unsigned char b = scanbuf[i][j+2];
919 pixel = (r << 16) | (g << 8) | b;
921 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
922 else if (depth == 12)
923 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
924 else if (depth == 15)
925 /* Gah! I don't understand why these are in the other
927 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
928 else if (depth == 16)
929 pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
933 XPutPixel (ximage, x, y, pixel);
939 if (cinfo.output_scanline < cinfo.output_height)
940 /* don't goto FAIL -- we might have viewable partial data. */
941 jpeg_abort_decompress (&cinfo);
943 jpeg_finish_decompress (&cinfo);
945 jpeg_destroy_decompress (&cinfo);
953 if (ximage && ximage->data)
958 if (ximage) XDestroyImage (ximage);
959 if (scanbuf) free (scanbuf);
964 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
965 Returns False if it fails.
968 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
969 const char *filename, Bool verbose_p,
970 XRectangle *geom_ret)
972 Display *dpy = DisplayOfScreen (screen);
977 unsigned int win_width, win_height, win_depth;
978 int srcx, srcy, destx, desty, w2, h2;
980 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
985 XWindowAttributes xgwa;
986 XGetWindowAttributes (dpy, window, &xgwa);
987 visual = xgwa.visual;
988 cmap = xgwa.colormap;
989 XGetGeometry (dpy, drawable,
990 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
993 /* Make sure we're not on some weirdo visual...
995 class = visual_class (screen, visual);
996 depth = visual_depth (screen, visual);
997 if ((class == PseudoColor || class == DirectColor) &&
998 (depth != 8 && depth != 12))
1000 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
1007 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
1008 filename, verbose_p);
1009 if (!ximage) return False;
1011 /* Scale it, if necessary...
1013 compute_image_scaling (ximage->width, ximage->height,
1014 win_width, win_height, verbose_p,
1015 &srcx, &srcy, &destx, &desty, &w2, &h2);
1016 if (ximage->width != w2 || ximage->height != h2)
1017 if (! scale_ximage (screen, visual, ximage, w2, h2))
1020 /* Allocate a colormap, if we need to...
1022 if (class == PseudoColor || class == DirectColor)
1024 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
1025 remap_image (screen, cmap, ximage, verbose_p);
1028 /* Finally, put the resized image on the window.
1034 /* If we're rendering onto the root window (and it's not the xscreensaver
1035 pseudo-root) then put the image in the window's background. Otherwise,
1036 just paint the image onto the window.
1038 if (window == drawable && root_window_p (screen, window))
1040 Pixmap bg = XCreatePixmap (dpy, window,
1041 win_width, win_height, win_depth);
1042 gcv.foreground = BlackPixelOfScreen (screen);
1043 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1044 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1045 XPutImage (dpy, bg, gc, ximage,
1046 srcx, srcy, destx, desty, ximage->width, ximage->height);
1047 XSetWindowBackgroundPixmap (dpy, window, bg);
1048 XClearWindow (dpy, window);
1052 gc = XCreateGC (dpy, drawable, 0, &gcv);
1053 clear_drawable (screen, drawable);
1054 XPutImage (dpy, drawable, gc, ximage,
1055 srcx, srcy, destx, desty, ximage->width, ximage->height);
1063 geom_ret->x = destx;
1064 geom_ret->y = desty;
1065 geom_ret->width = ximage->width;
1066 geom_ret->height = ximage->height;
1069 free (ximage->data);
1071 XDestroyImage (ximage);
1076 #endif /* HAVE_JPEGLIB */
1079 /* Reads the given image file and renders it on the Drawable.
1080 Returns False if it fails.
1083 display_file (Screen *screen, Window window, Drawable drawable,
1084 const char *filename, Bool verbose_p,
1085 XRectangle *geom_ret)
1088 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1090 # if defined(HAVE_GDK_PIXBUF)
1091 if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret))
1093 # elif defined(HAVE_JPEGLIB)
1094 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p,
1097 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1098 /* shouldn't get here if we have no image-loading methods available. */
1100 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1106 /* Invokes a sub-process and returns its output (presumably, a file to
1107 load.) Free the string when done. 'grab_type' controls which program
1111 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1114 Display *dpy = DisplayOfScreen (screen);
1125 av[ac++] = GETIMAGE_FILE_PROGRAM;
1127 av[ac++] = "--verbose";
1128 av[ac++] = "--name";
1129 av[ac++] = (char *) directory;
1133 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1135 av[ac++] = "--verbose";
1136 av[ac++] = "--name";
1139 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1141 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1143 av[ac++] = "--verbose";
1144 av[ac++] = "--name";
1156 fprintf (stderr, "%s: executing:", progname);
1157 for (i = 0; i < ac; i++)
1158 fprintf (stderr, " %s", av[i]);
1159 fprintf (stderr, "\n");
1164 sprintf (buf, "%s: error creating pipe", progname);
1172 switch ((int) (forked = fork ()))
1176 sprintf (buf, "%s: couldn't fork", progname);
1184 close (in); /* don't need this one */
1185 close (ConnectionNumber (dpy)); /* close display fd */
1187 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1189 sprintf (buf, "%s: could not dup() a new stdout", progname);
1190 exit (-1); /* exits fork */
1193 execvp (av[0], av); /* shouldn't return. */
1194 exit (-1); /* exits fork */
1200 int wait_status = 0;
1201 FILE *f = fdopen (in, "r");
1204 close (out); /* don't need this one */
1206 if (! fgets (buf, sizeof(buf)-1, f))
1210 /* Wait for the child to die. */
1211 waitpid (-1, &wait_status, 0);
1214 while (L && buf[L-1] == '\n')
1221 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1226 return strdup (buf);
1234 /* Returns a pathname to an image file. Free the string when you're done.
1237 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1239 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1243 /* Grabs a video frame to a file, and returns a pathname to that file.
1244 Delete that file when you are done with it (and free the string.)
1247 get_video_filename (Screen *screen, Bool verbose_p)
1249 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1252 /* Grabs a desktop image to a file, and returns a pathname to that file.
1253 Delete that file when you are done with it (and free the string.)
1255 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1257 get_desktop_filename (Screen *screen, Bool verbose_p)
1259 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1261 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1264 /* Grabs a video frame, and renders it on the Drawable.
1265 Returns False if it fails;
1268 display_video (Screen *screen, Window window, Drawable drawable,
1269 Bool verbose_p, XRectangle *geom_ret)
1271 char *filename = get_video_filename (screen, verbose_p);
1277 fprintf (stderr, "%s: video grab failed.\n", progname);
1281 status = display_file (screen, window, drawable, filename, verbose_p,
1284 if (unlink (filename))
1287 sprintf (buf, "%s: rm %.100s", progname, filename);
1291 fprintf (stderr, "%s: rm %s\n", progname, filename);
1293 if (filename) free (filename);
1298 /* Grabs a desktop screen shot onto the window and the drawable.
1299 If the window and drawable are not the same size, the image in
1300 the drawable is scaled to fit.
1301 Returns False if it fails.
1304 display_desktop (Screen *screen, Window window, Drawable drawable,
1305 Bool verbose_p, XRectangle *geom_ret)
1307 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1309 Display *dpy = DisplayOfScreen (screen);
1310 Bool top_p = top_level_window_p (screen, window);
1317 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1318 (unsigned long) window);
1319 XUnmapWindow (dpy, window);
1323 filename = get_desktop_filename (screen, verbose_p);
1328 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1329 (unsigned long) window);
1330 XMapRaised (dpy, window);
1337 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1341 status = display_file (screen, window, drawable, filename, verbose_p,
1344 if (unlink (filename))
1347 sprintf (buf, "%s: rm %.100s", progname, filename);
1351 fprintf (stderr, "%s: rm %s\n", progname, filename);
1353 if (filename) free (filename);
1356 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1358 Display *dpy = DisplayOfScreen (screen);
1360 XWindowAttributes xgwa;
1363 unsigned int pw, ph, pbw, pd;
1364 int srcx, srcy, destx, desty, w2, h2;
1368 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1369 grabscreen_verbose();
1372 XGetWindowAttributes (dpy, window, &xgwa);
1373 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1375 grab_screen_image_internal (screen, window);
1377 compute_image_scaling (xgwa.width, xgwa.height,
1379 &srcx, &srcy, &destx, &desty, &w2, &h2);
1381 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1383 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1384 XCopyArea (dpy, window, drawable, gc,
1385 0, 0, xgwa.width, xgwa.height, 0, 0);
1388 else /* size mismatch -- must scale client-side images to fit drawable */
1392 XErrorHandler old_handler;
1395 old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
1396 error_handler_hit_p = False;
1398 /* This can return BadMatch if the window is not fully on screen.
1399 Trap that error and return color bars in that case.
1400 (Note that this only happens with XGetImage, not with XCopyArea:
1401 yet another totally gratuitous inconsistency in X, thanks.)
1403 ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1407 XSetErrorHandler (old_handler);
1410 if (error_handler_hit_p)
1414 fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
1415 progname, (unsigned int) window);
1419 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1422 gc = XCreateGC (dpy, drawable, 0, &gcv);
1423 clear_drawable (screen, drawable);
1424 XPutImage (dpy, drawable, gc, ximage,
1425 srcx, srcy, destx, desty, ximage->width, ximage->height);
1426 XDestroyImage (ximage);
1432 geom_ret->x = destx;
1433 geom_ret->y = desty;
1434 geom_ret->width = w2;
1435 geom_ret->height = h2;
1441 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1445 /* Whether the given Drawable is unreasonably small.
1448 drawable_miniscule_p (Display *dpy, Drawable drawable)
1452 unsigned int bw, d, w = 0, h = 0;
1453 XGetGeometry (dpy, drawable, &root, &xx, &yy, &w, &h, &bw, &d);
1454 return (w < 32 || h < 32);
1458 /* Grabs an image (from a file, video, or the desktop) and renders it on
1459 the Drawable. If `file' is specified, always use that file. Otherwise,
1460 select randomly, based on the other arguments.
1463 get_image (Screen *screen,
1464 Window window, Drawable drawable,
1472 Display *dpy = DisplayOfScreen (screen);
1473 grab_type which = GRAB_BARS;
1475 const char *file_prop = 0;
1476 XRectangle geom = { 0, 0, 0, 0 };
1478 if (! drawable_window_p (dpy, window))
1480 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1481 progname, (unsigned long) window);
1485 /* Make sure the Screen and the Window correspond. */
1487 XWindowAttributes xgwa;
1488 XGetWindowAttributes (dpy, window, &xgwa);
1489 screen = xgwa.screen;
1492 if (file && stat (file, &st))
1494 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1500 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1501 progname, desk_p ? "True" : "False");
1502 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1503 progname, video_p ? "True" : "False");
1504 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1505 progname, image_p ? "True" : "False");
1506 fprintf (stderr, "%s: imageDirectory: %s\n",
1507 progname, (file ? file : dir ? dir : ""));
1510 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1511 image_p = False; /* can't load images from files... */
1512 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1513 desk_p = False; /* ...or from desktops grabbed to files. */
1519 "%s: image file loading not available at compile-time\n",
1521 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1524 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1532 else if (!dir || !*dir)
1534 if (verbose_p && image_p)
1536 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1541 /* If the target drawable is really small, no good can come of that.
1542 Always do colorbars in that case.
1544 if (drawable_miniscule_p (dpy, drawable))
1552 # error Error! This file definitely needs vroot.h!
1555 /* We can grab desktop images (using the normal X11 method) if:
1556 - the window is the real root window;
1557 - the window is a toplevel window.
1558 We cannot grab desktop images that way if:
1559 - the window is a non-top-level window.
1561 Using the MacOS X way, desktops are just like loaded image files.
1563 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1566 if (!top_level_window_p (screen, window))
1571 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1572 progname, (unsigned int) window);
1575 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1577 if (! (desk_p || video_p || image_p))
1583 /* Loop until we get one that's permitted.
1584 If files or video are permitted, do them more often
1587 D+V+I: 10% + 45% + 45%.
1593 n = (random() % 100);
1594 if (++i > 300) abort();
1595 else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */
1596 else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */
1597 else if (image_p) which = GRAB_FILE; /* 45% */
1602 /* If we're to search a directory to find an image file, do so now.
1604 if (which == GRAB_FILE && !file)
1606 file = get_filename (screen, dir, verbose_p);
1611 fprintf (stderr, "%s: no image files found.\n", progname);
1615 /* Now actually render something.
1621 XWindowAttributes xgwa;
1624 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1625 XGetWindowAttributes (dpy, window, &xgwa);
1626 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1633 if (! display_desktop (screen, window, drawable, verbose_p, &geom))
1635 file_prop = "desktop";
1639 if (! display_file (screen, window, drawable, file, verbose_p, &geom))
1645 if (! display_video (screen, window, drawable, verbose_p, &geom))
1647 file_prop = "video";
1656 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1657 if (file_prop && *file_prop)
1658 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1659 (unsigned char *) file_prop, strlen(file_prop));
1661 XDeleteProperty (dpy, window, a);
1663 a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
1667 sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
1668 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1669 (unsigned char *) gstr, strlen (gstr));
1672 XDeleteProperty (dpy, window, a);
1681 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1682 XrmRepresentation *type, XrmValue *value, XPointer closure)
1685 for (i = 0; quarks[i]; i++)
1687 if (bindings[i] == XrmBindTightly)
1688 fprintf (stderr, (i == 0 ? "" : "."));
1689 else if (bindings[i] == XrmBindLoosely)
1690 fprintf (stderr, "*");
1692 fprintf (stderr, " ??? ");
1693 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1696 fprintf (stderr, ": %s\n", (char *) value->addr);
1703 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1707 " %s puts an image on the given window or pixmap.\n" \
1709 " It is used by those xscreensaver demos that operate on images.\n" \
1710 " The image may be a file loaded from disk, a frame grabbed from\n" \
1711 " the system's video camera, or a screenshot of the desktop,\n" \
1712 " depending on command-line options or the ~/.xscreensaver file.\n" \
1714 " Options include:\n" \
1716 " -display host:dpy.screen which display to use\n" \
1717 " -root draw to the root window\n" \
1718 " -verbose print diagnostics\n" \
1719 " -images / -no-images whether to allow image file loading\n" \
1720 " -video / -no-video whether to allow video grabs\n" \
1721 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1722 " -directory <path> where to find image files to load\n" \
1723 " -file <filename> load this image file\n" \
1725 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1726 " defaults for these options in your ~/.xscreensaver file.\n" \
1730 main (int argc, char **argv)
1732 saver_preferences P;
1736 char *oprogname = progname;
1740 Window window = (Window) 0;
1741 Drawable drawable = (Drawable) 0;
1742 const char *window_str = 0;
1743 const char *drawable_str = 0;
1748 s = strrchr (progname, '/');
1749 if (s) progname = s+1;
1750 oprogname = progname;
1752 /* half-assed way of avoiding buffer-overrun attacks. */
1753 if (strlen (progname) >= 100) progname[100] = 0;
1756 # error Error! This file definitely needs vroot.h!
1759 /* Get the version number, for error messages. */
1761 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1762 char *s1, *s2, *s3, *s4;
1763 s1 = (char *) strchr(v, ' '); s1++;
1764 s2 = (char *) strchr(s1, ' ');
1765 s3 = (char *) strchr(v, '('); s3++;
1766 s4 = (char *) strchr(s3, ')');
1769 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1773 /* We must read exactly the same resources as xscreensaver.
1774 That means we must have both the same progclass *and* progname,
1775 at least as far as the resource database is concerned. So,
1776 put "xscreensaver" in argv[0] while initializing Xt.
1778 progname = argv[0] = "xscreensaver";
1780 /* allow one dash or two. */
1781 for (i = 1; i < argc; i++)
1782 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1784 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1786 dpy = XtDisplay (toplevel);
1787 screen = XtScreen (toplevel);
1788 db = XtDatabase (dpy);
1789 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1790 XSetErrorHandler (x_ehandler);
1793 /* Randomize -- only need to do this here because this program
1794 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1795 # undef ya_rand_init
1798 memset (&P, 0, sizeof(P));
1800 load_init_file (dpy, &P);
1802 progname = argv[0] = oprogname;
1804 for (i = 1; i < argc; i++)
1809 /* Have to re-process these, or else the .xscreensaver file
1810 has priority over the command line...
1812 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1814 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1815 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1816 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1817 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1818 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1819 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1820 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1821 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1822 P.image_directory = argv[++i];
1823 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1827 fprintf (stderr, "%s: both %s and %s specified?\n",
1828 progname, argv[i], window_str);
1831 window_str = argv[i];
1832 window = VirtualRootWindowOfScreen (screen);
1834 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1835 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1840 fprintf (stderr, "%s: both %s and %s specified?\n",
1841 progname, drawable_str, argv[i]);
1846 drawable_str = argv[i];
1847 drawable = (Drawable) w;
1851 window_str = argv[i];
1852 window = (Window) w;
1857 if (argv[i][0] == '-')
1858 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1861 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1865 __extension__ /* don't warn about "string length is greater than
1866 the length ISO C89 compilers are required to
1867 support" in the usage string... */
1869 fprintf (stderr, USAGE, progname, version, progname);
1876 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1882 if (P.verbose_p) /* Print out all the resources we can see. */
1884 XrmName name = { 0 };
1885 XrmClass class = { 0 };
1887 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1888 (XtPointer) &count);
1892 if (!window) abort();
1893 if (!drawable) drawable = window;
1895 get_image (screen, window, drawable, P.verbose_p,
1896 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1897 P.image_directory, file);