1 /* xscreensaver, Copyright (c) 2001-2008 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 # ifdef HAVE_GDK_PIXBUF_APPLY_EMBEDDED_ORIENTATION
412 pb = gdk_pixbuf_apply_embedded_orientation (opb);
413 gdk_pixbuf_unref (opb);
414 w = gdk_pixbuf_get_width (pb);
415 h = gdk_pixbuf_get_height (pb);
416 if (verbose_p && (w != ow || h != oh))
417 fprintf (stderr, "%s: rotated %dx%d to %dx%d\n",
418 progname, ow, oh, w, h);
422 compute_image_scaling (w, h, win_width, win_height, verbose_p,
423 &srcx, &srcy, &destx, &desty, &w2, &h2);
424 if (w != w2 || h != h2)
426 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
427 GDK_INTERP_BILINEAR);
430 gdk_pixbuf_unref (pb);
436 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
439 /* If we're rendering onto the root window (and it's not the
440 xscreensaver pseudo-root) then put the image in the window's
441 background. Otherwise, just paint the image onto the window.
443 bg_p = (window == drawable && root_window_p (screen, window));
449 drawable = XCreatePixmap (dpy, window,
450 win_width, win_height, win_depth);
451 gcv.foreground = BlackPixelOfScreen (screen);
452 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
453 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
457 clear_drawable (screen, drawable);
459 /* #### Note that this always uses the default colormap! Morons!
460 Owen says that in Gnome 2.0, I should try using
461 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
464 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
465 srcx, srcy, destx, desty,
467 GDK_PIXBUF_ALPHA_FULL, 127,
468 XLIB_RGB_DITHER_NORMAL,
472 XSetWindowBackgroundPixmap (dpy, window, drawable);
473 XClearWindow (dpy, window);
481 geom_ret->height = h;
489 #endif /* HAVE_GDK_PIXBUF */
495 /* Allocates a colormap that makes a PseudoColor or DirectColor
496 visual behave like a TrueColor visual of the same depth.
499 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
502 Display *dpy = DisplayOfScreen (screen);
503 int nr, ng, nb, cells;
509 depth = visual_depth (screen, visual);
513 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
514 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
515 default: abort(); break;
518 memset(colors, 0, sizeof(colors));
519 for (r = 0; r < (1 << nr); r++)
520 for (g = 0; g < (1 << ng); g++)
521 for (b = 0; b < (1 << nb); b++)
523 i = (r | (g << nr) | (b << (nr + ng)));
525 colors[i].flags = DoRed|DoGreen|DoBlue;
528 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
529 (r << 4) | (r << 1));
530 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
531 (g << 4) | (g << 1));
532 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
533 (b << 8) | (b << 6) | (b << 4) |
538 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
539 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
540 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
547 int interleave = cells / 8; /* skip around, rather than allocating in
548 order, so that we get better coverage if
549 we can't allocated all of them. */
550 for (j = 0; j < interleave; j++)
551 for (i = 0; i < cells; i += interleave)
552 if (XAllocColor (dpy, cmap, &colors[i + j]))
556 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
557 progname, allocated, cells);
561 /* Find the pixel index that is closest to the given color
562 (using linear distance in RGB space -- which is far from the best way.)
565 find_closest_pixel (XColor *colors, int ncolors,
566 unsigned long r, unsigned long g, unsigned long b)
568 unsigned long distance = ~0;
573 for (i = 0; i < ncolors; i++)
578 rd = r - colors[i].red;
579 gd = g - colors[i].green;
580 bd = b - colors[i].blue;
581 if (rd < 0) rd = -rd;
582 if (gd < 0) gd = -gd;
583 if (bd < 0) bd = -bd;
584 d = (rd << 1) + (gd << 2) + bd;
599 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
600 displayable with the given X colormap. The farther from a perfect
601 color cube the contents of the colormap are, the lossier the
602 transformation will be. No dithering is done.
605 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
607 Display *dpy = DisplayOfScreen (screen);
608 unsigned long map[4097];
613 if (image->depth == 8)
615 else if (image->depth == 12)
620 memset(map, -1, sizeof(*map));
621 memset(colors, -1, sizeof(*colors));
623 for (i = 0; i < cells; i++)
625 XQueryColors (dpy, cmap, colors, cells);
628 fprintf(stderr, "%s: building color cube for %d bit image\n",
629 progname, image->depth);
631 for (i = 0; i < cells; i++)
633 unsigned short r, g, b;
637 /* "RRR GGG BB" In an 8 bit map. Convert that to
638 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
644 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
645 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
646 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
647 (b << 6) | (b << 4) | (b << 2) | b);
651 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
652 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
655 g = (i & 0x0F0) >> 4;
656 b = (i & 0xF00) >> 8;
658 r = (r << 12) | (r << 8) | (r << 4) | r;
659 g = (g << 12) | (g << 8) | (g << 4) | g;
660 b = (b << 12) | (b << 8) | (b << 4) | b;
663 map[i] = find_closest_pixel (colors, cells, r, g, b);
667 fprintf(stderr, "%s: remapping colors in %d bit image\n",
668 progname, image->depth);
670 for (y = 0; y < image->height; y++)
671 for (x = 0; x < image->width; x++)
673 unsigned long pixel = XGetPixel(image, x, y);
674 if (pixel >= cells) abort();
675 XPutPixel(image, x, y, map[pixel]);
680 /* If the file has a PPM (P6) on it, read it and return an XImage.
681 Otherwise, rewind the fd back to the beginning, and return 0.
684 maybe_read_ppm (Screen *screen, Visual *visual,
685 const char *filename, FILE *in, Bool verbose_p)
687 Display *dpy = DisplayOfScreen (screen);
688 int depth = visual_depth (screen, visual);
694 int x, y, w, h, maxval;
697 if (fstat (fileno (in), &st))
701 buf = (char *) malloc (bufsiz + 1);
704 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
705 progname, bufsiz, filename);
709 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
712 if (!strncmp (buf, "\107\111", 2))
714 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
715 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
719 else if (!strncmp (buf, "\211\120", 2))
721 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
722 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
727 if (strncmp (s, "P6", 2))
730 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
732 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
734 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
738 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
740 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
742 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
747 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
748 progname, filename, maxval);
752 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
755 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
756 if (!ximage || !ximage->data)
758 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
759 progname, ximage->width, ximage->height, filename);
765 while ((i = fread (s, 1, j, in)) > 0)
769 for (y = 0; y < ximage->height; y++)
770 for (x = 0; x < ximage->width; x++)
772 unsigned char r = buf[i++];
773 unsigned char g = buf[i++];
774 unsigned char b = buf[i++];
778 pixel = (r << 16) | (g << 8) | b;
780 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
781 else if (depth == 12)
782 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
783 else if (depth == 16 || depth == 15)
784 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
788 XPutPixel (ximage, x, y, pixel);
796 if (ximage && ximage->data)
801 if (ximage) XDestroyImage (ximage);
802 fseek (in, 0, SEEK_SET);
808 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
809 const char *filename;
814 } getimg_jpg_error_mgr;
818 jpg_output_message (j_common_ptr cinfo)
820 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
821 char buf[JMSG_LENGTH_MAX];
822 cinfo->err->format_message (cinfo, buf);
823 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
828 jpg_error_exit (j_common_ptr cinfo)
830 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
831 cinfo->err->output_message (cinfo);
832 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
834 XSync (DisplayOfScreen (err->screen), False);
839 /* Reads a JPEG file, returns an RGB XImage of it.
842 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
843 Colormap cmap, const char *filename, Bool verbose_p)
845 Display *dpy = DisplayOfScreen (screen);
846 int depth = visual_depth (screen, visual);
850 struct jpeg_decompress_struct cinfo;
851 getimg_jpg_error_mgr jerr;
852 JSAMPARRAY scanbuf = 0;
855 jerr.filename = filename;
856 jerr.screen = screen;
857 jerr.visual = visual;
858 jerr.drawable = drawable;
861 if (! (depth >= 15 || depth == 12 || depth == 8))
863 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
867 in = fopen (filename, "rb");
870 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
874 /* Check to see if it's a PPM, and if so, read that instead of using
875 the JPEG library. Yeah, this is all modular and stuff.
877 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
883 cinfo.err = jpeg_std_error (&jerr.pub);
884 jerr.pub.output_message = jpg_output_message;
885 jerr.pub.error_exit = jpg_error_exit;
887 jpeg_create_decompress (&cinfo);
888 jpeg_stdio_src (&cinfo, in);
889 jpeg_read_header (&cinfo, TRUE);
891 /* set some decode parameters */
892 cinfo.out_color_space = JCS_RGB;
893 cinfo.quantize_colors = FALSE;
895 jpeg_start_decompress (&cinfo);
897 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
898 cinfo.output_width, cinfo.output_height,
901 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
903 if (ximage && ximage->data)
904 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
905 cinfo.rec_outbuf_height *
907 cinfo.output_components,
909 if (!ximage || !ximage->data || !scanbuf)
911 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
912 progname, ximage->width, ximage->height, filename);
917 while (cinfo.output_scanline < cinfo.output_height)
919 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
921 for (i = 0; i < n; i++)
924 for (x = 0; x < ximage->width; x++)
926 int j = x * cinfo.output_components;
927 unsigned char r = scanbuf[i][j];
928 unsigned char g = scanbuf[i][j+1];
929 unsigned char b = scanbuf[i][j+2];
933 pixel = (r << 16) | (g << 8) | b;
935 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
936 else if (depth == 12)
937 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
938 else if (depth == 15)
939 /* Gah! I don't understand why these are in the other
941 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
942 else if (depth == 16)
943 pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
947 XPutPixel (ximage, x, y, pixel);
953 if (cinfo.output_scanline < cinfo.output_height)
954 /* don't goto FAIL -- we might have viewable partial data. */
955 jpeg_abort_decompress (&cinfo);
957 jpeg_finish_decompress (&cinfo);
959 jpeg_destroy_decompress (&cinfo);
967 if (ximage && ximage->data)
972 if (ximage) XDestroyImage (ximage);
973 if (scanbuf) free (scanbuf);
978 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
979 Returns False if it fails.
982 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
983 const char *filename, Bool verbose_p,
984 XRectangle *geom_ret)
986 Display *dpy = DisplayOfScreen (screen);
991 unsigned int win_width, win_height, win_depth;
992 int srcx, srcy, destx, desty, w2, h2;
994 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
999 XWindowAttributes xgwa;
1000 XGetWindowAttributes (dpy, window, &xgwa);
1001 visual = xgwa.visual;
1002 cmap = xgwa.colormap;
1003 XGetGeometry (dpy, drawable,
1004 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
1007 /* Make sure we're not on some weirdo visual...
1009 class = visual_class (screen, visual);
1010 depth = visual_depth (screen, visual);
1011 if ((class == PseudoColor || class == DirectColor) &&
1012 (depth != 8 && depth != 12))
1014 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
1021 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
1022 filename, verbose_p);
1023 if (!ximage) return False;
1025 /* Scale it, if necessary...
1027 compute_image_scaling (ximage->width, ximage->height,
1028 win_width, win_height, verbose_p,
1029 &srcx, &srcy, &destx, &desty, &w2, &h2);
1030 if (ximage->width != w2 || ximage->height != h2)
1031 if (! scale_ximage (screen, visual, ximage, w2, h2))
1034 /* Allocate a colormap, if we need to...
1036 if (class == PseudoColor || class == DirectColor)
1038 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
1039 remap_image (screen, cmap, ximage, verbose_p);
1042 /* Finally, put the resized image on the window.
1048 /* If we're rendering onto the root window (and it's not the xscreensaver
1049 pseudo-root) then put the image in the window's background. Otherwise,
1050 just paint the image onto the window.
1052 if (window == drawable && root_window_p (screen, window))
1054 Pixmap bg = XCreatePixmap (dpy, window,
1055 win_width, win_height, win_depth);
1056 gcv.foreground = BlackPixelOfScreen (screen);
1057 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1058 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1059 XPutImage (dpy, bg, gc, ximage,
1060 srcx, srcy, destx, desty, ximage->width, ximage->height);
1061 XSetWindowBackgroundPixmap (dpy, window, bg);
1062 XClearWindow (dpy, window);
1066 gc = XCreateGC (dpy, drawable, 0, &gcv);
1067 clear_drawable (screen, drawable);
1068 XPutImage (dpy, drawable, gc, ximage,
1069 srcx, srcy, destx, desty, ximage->width, ximage->height);
1077 geom_ret->x = destx;
1078 geom_ret->y = desty;
1079 geom_ret->width = ximage->width;
1080 geom_ret->height = ximage->height;
1083 free (ximage->data);
1085 XDestroyImage (ximage);
1090 #endif /* HAVE_JPEGLIB */
1093 /* Reads the given image file and renders it on the Drawable.
1094 Returns False if it fails.
1097 display_file (Screen *screen, Window window, Drawable drawable,
1098 const char *filename, Bool verbose_p,
1099 XRectangle *geom_ret)
1102 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1104 # if defined(HAVE_GDK_PIXBUF)
1105 if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret))
1107 # elif defined(HAVE_JPEGLIB)
1108 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p,
1111 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1112 /* shouldn't get here if we have no image-loading methods available. */
1114 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1120 /* Invokes a sub-process and returns its output (presumably, a file to
1121 load.) Free the string when done. 'grab_type' controls which program
1125 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1128 Display *dpy = DisplayOfScreen (screen);
1139 av[ac++] = GETIMAGE_FILE_PROGRAM;
1141 av[ac++] = "--verbose";
1142 av[ac++] = "--name";
1143 av[ac++] = (char *) directory;
1147 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1149 av[ac++] = "--verbose";
1150 av[ac++] = "--name";
1153 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1155 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1157 av[ac++] = "--verbose";
1158 av[ac++] = "--name";
1170 fprintf (stderr, "%s: executing:", progname);
1171 for (i = 0; i < ac; i++)
1172 fprintf (stderr, " %s", av[i]);
1173 fprintf (stderr, "\n");
1178 sprintf (buf, "%s: error creating pipe", progname);
1186 switch ((int) (forked = fork ()))
1190 sprintf (buf, "%s: couldn't fork", progname);
1198 close (in); /* don't need this one */
1199 close (ConnectionNumber (dpy)); /* close display fd */
1201 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1203 sprintf (buf, "%s: could not dup() a new stdout", progname);
1204 exit (-1); /* exits fork */
1207 execvp (av[0], av); /* shouldn't return. */
1208 exit (-1); /* exits fork */
1214 int wait_status = 0;
1215 FILE *f = fdopen (in, "r");
1218 close (out); /* don't need this one */
1220 if (! fgets (buf, sizeof(buf)-1, f))
1224 /* Wait for the child to die. */
1225 waitpid (-1, &wait_status, 0);
1228 while (L && buf[L-1] == '\n')
1235 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1240 return strdup (buf);
1248 /* Returns a pathname to an image file. Free the string when you're done.
1251 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1253 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1257 /* Grabs a video frame to a file, and returns a pathname to that file.
1258 Delete that file when you are done with it (and free the string.)
1261 get_video_filename (Screen *screen, Bool verbose_p)
1263 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1266 /* Grabs a desktop image to a file, and returns a pathname to that file.
1267 Delete that file when you are done with it (and free the string.)
1269 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1271 get_desktop_filename (Screen *screen, Bool verbose_p)
1273 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1275 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1278 /* Grabs a video frame, and renders it on the Drawable.
1279 Returns False if it fails;
1282 display_video (Screen *screen, Window window, Drawable drawable,
1283 Bool verbose_p, XRectangle *geom_ret)
1285 char *filename = get_video_filename (screen, verbose_p);
1291 fprintf (stderr, "%s: video grab failed.\n", progname);
1295 status = display_file (screen, window, drawable, filename, verbose_p,
1298 if (unlink (filename))
1301 sprintf (buf, "%s: rm %.100s", progname, filename);
1305 fprintf (stderr, "%s: rm %s\n", progname, filename);
1307 if (filename) free (filename);
1312 /* Grabs a desktop screen shot onto the window and the drawable.
1313 If the window and drawable are not the same size, the image in
1314 the drawable is scaled to fit.
1315 Returns False if it fails.
1318 display_desktop (Screen *screen, Window window, Drawable drawable,
1319 Bool verbose_p, XRectangle *geom_ret)
1321 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1323 Display *dpy = DisplayOfScreen (screen);
1324 Bool top_p = top_level_window_p (screen, window);
1331 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1332 (unsigned long) window);
1333 XUnmapWindow (dpy, window);
1337 filename = get_desktop_filename (screen, verbose_p);
1342 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1343 (unsigned long) window);
1344 XMapRaised (dpy, window);
1351 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1355 status = display_file (screen, window, drawable, filename, verbose_p,
1358 if (unlink (filename))
1361 sprintf (buf, "%s: rm %.100s", progname, filename);
1365 fprintf (stderr, "%s: rm %s\n", progname, filename);
1367 if (filename) free (filename);
1370 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1372 Display *dpy = DisplayOfScreen (screen);
1374 XWindowAttributes xgwa;
1377 unsigned int pw, ph, pbw, pd;
1378 int srcx, srcy, destx, desty, w2, h2;
1382 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1383 grabscreen_verbose();
1386 XGetWindowAttributes (dpy, window, &xgwa);
1387 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1389 grab_screen_image_internal (screen, window);
1391 compute_image_scaling (xgwa.width, xgwa.height,
1393 &srcx, &srcy, &destx, &desty, &w2, &h2);
1395 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1397 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1398 XCopyArea (dpy, window, drawable, gc,
1399 0, 0, xgwa.width, xgwa.height, 0, 0);
1402 else /* size mismatch -- must scale client-side images to fit drawable */
1406 XErrorHandler old_handler;
1409 old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
1410 error_handler_hit_p = False;
1412 /* This can return BadMatch if the window is not fully on screen.
1413 Trap that error and return color bars in that case.
1414 (Note that this only happens with XGetImage, not with XCopyArea:
1415 yet another totally gratuitous inconsistency in X, thanks.)
1417 ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1421 XSetErrorHandler (old_handler);
1424 if (error_handler_hit_p)
1428 fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
1429 progname, (unsigned int) window);
1433 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1436 gc = XCreateGC (dpy, drawable, 0, &gcv);
1437 clear_drawable (screen, drawable);
1438 XPutImage (dpy, drawable, gc, ximage,
1439 srcx, srcy, destx, desty, ximage->width, ximage->height);
1440 XDestroyImage (ximage);
1446 geom_ret->x = destx;
1447 geom_ret->y = desty;
1448 geom_ret->width = w2;
1449 geom_ret->height = h2;
1455 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1459 /* Whether the given Drawable is unreasonably small.
1462 drawable_miniscule_p (Display *dpy, Drawable drawable)
1466 unsigned int bw, d, w = 0, h = 0;
1467 XGetGeometry (dpy, drawable, &root, &xx, &yy, &w, &h, &bw, &d);
1468 return (w < 32 || h < 32);
1472 /* Grabs an image (from a file, video, or the desktop) and renders it on
1473 the Drawable. If `file' is specified, always use that file. Otherwise,
1474 select randomly, based on the other arguments.
1477 get_image (Screen *screen,
1478 Window window, Drawable drawable,
1486 Display *dpy = DisplayOfScreen (screen);
1487 grab_type which = GRAB_BARS;
1489 const char *file_prop = 0;
1490 XRectangle geom = { 0, 0, 0, 0 };
1492 if (! drawable_window_p (dpy, window))
1494 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1495 progname, (unsigned long) window);
1499 /* Make sure the Screen and the Window correspond. */
1501 XWindowAttributes xgwa;
1502 XGetWindowAttributes (dpy, window, &xgwa);
1503 screen = xgwa.screen;
1506 if (file && stat (file, &st))
1508 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1514 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1515 progname, desk_p ? "True" : "False");
1516 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1517 progname, video_p ? "True" : "False");
1518 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1519 progname, image_p ? "True" : "False");
1520 fprintf (stderr, "%s: imageDirectory: %s\n",
1521 progname, (file ? file : dir ? dir : ""));
1524 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1525 image_p = False; /* can't load images from files... */
1526 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1527 desk_p = False; /* ...or from desktops grabbed to files. */
1533 "%s: image file loading not available at compile-time\n",
1535 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1538 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1546 else if (!dir || !*dir)
1548 if (verbose_p && image_p)
1550 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1555 /* If the target drawable is really small, no good can come of that.
1556 Always do colorbars in that case.
1558 if (drawable_miniscule_p (dpy, drawable))
1566 # error Error! This file definitely needs vroot.h!
1569 /* We can grab desktop images (using the normal X11 method) if:
1570 - the window is the real root window;
1571 - the window is a toplevel window.
1572 We cannot grab desktop images that way if:
1573 - the window is a non-top-level window.
1575 Using the MacOS X way, desktops are just like loaded image files.
1577 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1580 if (!top_level_window_p (screen, window))
1585 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1586 progname, (unsigned int) window);
1589 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1591 if (! (desk_p || video_p || image_p))
1597 /* Loop until we get one that's permitted.
1598 If files or video are permitted, do them more often
1601 D+V+I: 10% + 45% + 45%.
1607 n = (random() % 100);
1608 if (++i > 300) abort();
1609 else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */
1610 else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */
1611 else if (image_p) which = GRAB_FILE; /* 45% */
1616 /* If we're to search a directory to find an image file, do so now.
1618 if (which == GRAB_FILE && !file)
1620 file = get_filename (screen, dir, verbose_p);
1625 fprintf (stderr, "%s: no image files found.\n", progname);
1629 /* Now actually render something.
1635 XWindowAttributes xgwa;
1638 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1639 XGetWindowAttributes (dpy, window, &xgwa);
1640 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1647 if (! display_desktop (screen, window, drawable, verbose_p, &geom))
1649 file_prop = "desktop";
1653 if (! display_file (screen, window, drawable, file, verbose_p, &geom))
1659 if (! display_video (screen, window, drawable, verbose_p, &geom))
1661 file_prop = "video";
1670 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1671 if (file_prop && *file_prop)
1672 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1673 (unsigned char *) file_prop, strlen(file_prop));
1675 XDeleteProperty (dpy, window, a);
1677 a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
1681 sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
1682 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1683 (unsigned char *) gstr, strlen (gstr));
1686 XDeleteProperty (dpy, window, a);
1695 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1696 XrmRepresentation *type, XrmValue *value, XPointer closure)
1699 for (i = 0; quarks[i]; i++)
1701 if (bindings[i] == XrmBindTightly)
1702 fprintf (stderr, (i == 0 ? "" : "."));
1703 else if (bindings[i] == XrmBindLoosely)
1704 fprintf (stderr, "*");
1706 fprintf (stderr, " ??? ");
1707 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1710 fprintf (stderr, ": %s\n", (char *) value->addr);
1717 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1721 " %s puts an image on the given window or pixmap.\n" \
1723 " It is used by those xscreensaver demos that operate on images.\n" \
1724 " The image may be a file loaded from disk, a frame grabbed from\n" \
1725 " the system's video camera, or a screenshot of the desktop,\n" \
1726 " depending on command-line options or the ~/.xscreensaver file.\n" \
1728 " Options include:\n" \
1730 " -display host:dpy.screen which display to use\n" \
1731 " -root draw to the root window\n" \
1732 " -verbose print diagnostics\n" \
1733 " -images / -no-images whether to allow image file loading\n" \
1734 " -video / -no-video whether to allow video grabs\n" \
1735 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1736 " -directory <path> where to find image files to load\n" \
1737 " -file <filename> load this image file\n" \
1739 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1740 " defaults for these options in your ~/.xscreensaver file.\n" \
1744 main (int argc, char **argv)
1746 saver_preferences P;
1750 char *oprogname = progname;
1754 Window window = (Window) 0;
1755 Drawable drawable = (Drawable) 0;
1756 const char *window_str = 0;
1757 const char *drawable_str = 0;
1762 s = strrchr (progname, '/');
1763 if (s) progname = s+1;
1764 oprogname = progname;
1766 /* half-assed way of avoiding buffer-overrun attacks. */
1767 if (strlen (progname) >= 100) progname[100] = 0;
1770 # error Error! This file definitely needs vroot.h!
1773 /* Get the version number, for error messages. */
1775 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1776 char *s1, *s2, *s3, *s4;
1777 s1 = (char *) strchr(v, ' '); s1++;
1778 s2 = (char *) strchr(s1, ' ');
1779 s3 = (char *) strchr(v, '('); s3++;
1780 s4 = (char *) strchr(s3, ')');
1783 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1787 /* We must read exactly the same resources as xscreensaver.
1788 That means we must have both the same progclass *and* progname,
1789 at least as far as the resource database is concerned. So,
1790 put "xscreensaver" in argv[0] while initializing Xt.
1792 progname = argv[0] = "xscreensaver";
1794 /* allow one dash or two. */
1795 for (i = 1; i < argc; i++)
1796 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1798 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1800 dpy = XtDisplay (toplevel);
1801 screen = XtScreen (toplevel);
1802 db = XtDatabase (dpy);
1803 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1804 XSetErrorHandler (x_ehandler);
1807 /* Randomize -- only need to do this here because this program
1808 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1809 # undef ya_rand_init
1812 memset (&P, 0, sizeof(P));
1814 load_init_file (dpy, &P);
1816 progname = argv[0] = oprogname;
1818 for (i = 1; i < argc; i++)
1823 /* Have to re-process these, or else the .xscreensaver file
1824 has priority over the command line...
1826 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1828 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1829 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1830 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1831 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1832 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1833 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1834 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1835 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1836 P.image_directory = argv[++i];
1837 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1841 fprintf (stderr, "%s: both %s and %s specified?\n",
1842 progname, argv[i], window_str);
1845 window_str = argv[i];
1846 window = VirtualRootWindowOfScreen (screen);
1848 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1849 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1854 fprintf (stderr, "%s: both %s and %s specified?\n",
1855 progname, drawable_str, argv[i]);
1860 drawable_str = argv[i];
1861 drawable = (Drawable) w;
1865 window_str = argv[i];
1866 window = (Window) w;
1871 if (argv[i][0] == '-')
1872 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1875 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1879 __extension__ /* don't warn about "string length is greater than
1880 the length ISO C89 compilers are required to
1881 support" in the usage string... */
1883 fprintf (stderr, USAGE, progname, version, progname);
1890 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1896 if (P.verbose_p) /* Print out all the resources we can see. */
1898 XrmName name = { 0 };
1899 XrmClass class = { 0 };
1901 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1902 (XtPointer) &count);
1906 if (!window) abort();
1907 if (!drawable) drawable = window;
1909 get_image (screen, window, drawable, P.verbose_p,
1910 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1911 P.image_directory, file);