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 /* Grabs an image (from a file, video, or the desktop) and renders it on
1446 the Drawable. If `file' is specified, always use that file. Otherwise,
1447 select randomly, based on the other arguments.
1450 get_image (Screen *screen,
1451 Window window, Drawable drawable,
1459 Display *dpy = DisplayOfScreen (screen);
1460 grab_type which = GRAB_BARS;
1462 const char *file_prop = 0;
1463 XRectangle geom = { 0, 0, 0, 0 };
1465 if (! drawable_window_p (dpy, window))
1467 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1468 progname, (unsigned long) window);
1472 /* Make sure the Screen and the Window correspond. */
1474 XWindowAttributes xgwa;
1475 XGetWindowAttributes (dpy, window, &xgwa);
1476 screen = xgwa.screen;
1479 if (file && stat (file, &st))
1481 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1487 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1488 progname, desk_p ? "True" : "False");
1489 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1490 progname, video_p ? "True" : "False");
1491 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1492 progname, image_p ? "True" : "False");
1493 fprintf (stderr, "%s: imageDirectory: %s\n",
1494 progname, (file ? file : dir ? dir : ""));
1497 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1498 image_p = False; /* can't load images from files... */
1499 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1500 desk_p = False; /* ...or from desktops grabbed to files. */
1506 "%s: image file loading not available at compile-time\n",
1508 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1511 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1519 else if (!dir || !*dir)
1521 if (verbose_p && image_p)
1523 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1530 # error Error! This file definitely needs vroot.h!
1533 /* We can grab desktop images (using the normal X11 method) if:
1534 - the window is the real root window;
1535 - the window is a toplevel window.
1536 We cannot grab desktop images that way if:
1537 - the window is a non-top-level window.
1539 Using the MacOS X way, desktops are just like loaded image files.
1541 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1544 if (!top_level_window_p (screen, window))
1549 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1550 progname, (unsigned int) window);
1553 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1555 if (! (desk_p || video_p || image_p))
1561 /* Loop until we get one that's permitted.
1562 If files or video are permitted, do them more often
1565 D+V+I: 10% + 45% + 45%.
1571 n = (random() % 100);
1572 if (++i > 300) abort();
1573 else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */
1574 else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */
1575 else if (image_p) which = GRAB_FILE; /* 45% */
1580 /* If we're to search a directory to find an image file, do so now.
1582 if (which == GRAB_FILE && !file)
1584 file = get_filename (screen, dir, verbose_p);
1589 fprintf (stderr, "%s: no image files found.\n", progname);
1593 /* Now actually render something.
1599 XWindowAttributes xgwa;
1602 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1603 XGetWindowAttributes (dpy, window, &xgwa);
1604 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1611 if (! display_desktop (screen, window, drawable, verbose_p, &geom))
1613 file_prop = "desktop";
1617 if (! display_file (screen, window, drawable, file, verbose_p, &geom))
1623 if (! display_video (screen, window, drawable, verbose_p, &geom))
1625 file_prop = "video";
1634 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1635 if (file_prop && *file_prop)
1636 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1637 (unsigned char *) file_prop, strlen(file_prop));
1639 XDeleteProperty (dpy, window, a);
1641 a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
1645 sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
1646 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1647 (unsigned char *) gstr, strlen (gstr));
1650 XDeleteProperty (dpy, window, a);
1659 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1660 XrmRepresentation *type, XrmValue *value, XPointer closure)
1663 for (i = 0; quarks[i]; i++)
1665 if (bindings[i] == XrmBindTightly)
1666 fprintf (stderr, (i == 0 ? "" : "."));
1667 else if (bindings[i] == XrmBindLoosely)
1668 fprintf (stderr, "*");
1670 fprintf (stderr, " ??? ");
1671 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1674 fprintf (stderr, ": %s\n", (char *) value->addr);
1681 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1685 " %s puts an image on the given window or pixmap.\n" \
1687 " It is used by those xscreensaver demos that operate on images.\n" \
1688 " The image may be a file loaded from disk, a frame grabbed from\n" \
1689 " the system's video camera, or a screenshot of the desktop,\n" \
1690 " depending on command-line options or the ~/.xscreensaver file.\n" \
1692 " Options include:\n" \
1694 " -display host:dpy.screen which display to use\n" \
1695 " -root draw to the root window\n" \
1696 " -verbose print diagnostics\n" \
1697 " -images / -no-images whether to allow image file loading\n" \
1698 " -video / -no-video whether to allow video grabs\n" \
1699 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1700 " -directory <path> where to find image files to load\n" \
1701 " -file <filename> load this image file\n" \
1703 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1704 " defaults for these options in your ~/.xscreensaver file.\n" \
1708 main (int argc, char **argv)
1710 saver_preferences P;
1714 char *oprogname = progname;
1718 Window window = (Window) 0;
1719 Drawable drawable = (Drawable) 0;
1720 const char *window_str = 0;
1721 const char *drawable_str = 0;
1726 s = strrchr (progname, '/');
1727 if (s) progname = s+1;
1728 oprogname = progname;
1730 /* half-assed way of avoiding buffer-overrun attacks. */
1731 if (strlen (progname) >= 100) progname[100] = 0;
1734 # error Error! This file definitely needs vroot.h!
1737 /* Get the version number, for error messages. */
1739 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1740 char *s1, *s2, *s3, *s4;
1741 s1 = (char *) strchr(v, ' '); s1++;
1742 s2 = (char *) strchr(s1, ' ');
1743 s3 = (char *) strchr(v, '('); s3++;
1744 s4 = (char *) strchr(s3, ')');
1747 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1751 /* We must read exactly the same resources as xscreensaver.
1752 That means we must have both the same progclass *and* progname,
1753 at least as far as the resource database is concerned. So,
1754 put "xscreensaver" in argv[0] while initializing Xt.
1756 progname = argv[0] = "xscreensaver";
1758 /* allow one dash or two. */
1759 for (i = 1; i < argc; i++)
1760 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1762 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1764 dpy = XtDisplay (toplevel);
1765 screen = XtScreen (toplevel);
1766 db = XtDatabase (dpy);
1767 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1768 XSetErrorHandler (x_ehandler);
1771 /* Randomize -- only need to do this here because this program
1772 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1773 # undef ya_rand_init
1776 memset (&P, 0, sizeof(P));
1778 load_init_file (dpy, &P);
1780 progname = argv[0] = oprogname;
1782 for (i = 1; i < argc; i++)
1787 /* Have to re-process these, or else the .xscreensaver file
1788 has priority over the command line...
1790 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1792 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1793 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1794 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1795 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1796 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1797 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1798 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1799 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1800 P.image_directory = argv[++i];
1801 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1805 fprintf (stderr, "%s: both %s and %s specified?\n",
1806 progname, argv[i], window_str);
1809 window_str = argv[i];
1810 window = VirtualRootWindowOfScreen (screen);
1812 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1813 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1818 fprintf (stderr, "%s: both %s and %s specified?\n",
1819 progname, drawable_str, argv[i]);
1824 drawable_str = argv[i];
1825 drawable = (Drawable) w;
1829 window_str = argv[i];
1830 window = (Window) w;
1835 if (argv[i][0] == '-')
1836 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1839 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1843 __extension__ /* don't warn about "string length is greater than
1844 the length ISO C89 compilers are required to
1845 support" in the usage string... */
1847 fprintf (stderr, USAGE, progname, version, progname);
1854 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1860 if (P.verbose_p) /* Print out all the resources we can see. */
1862 XrmName name = { 0 };
1863 XrmClass class = { 0 };
1865 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1866 (XtPointer) &count);
1870 if (!window) abort();
1871 if (!drawable) drawable = window;
1873 get_image (screen, window, drawable, P.verbose_p,
1874 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1875 P.image_directory, file);