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 MacOS under X11, the usual X11 mechanism of getting a screen shot
70 doesn't work, and we need to use an external program. This is only
71 used when running under X11 on MacOS. If it's a Cocoa build, this
72 path is not taken, and OSX/osxgrabscreen.m is used instead.
74 # define USE_EXTERNAL_SCREEN_GRABBER
79 __extension__ /* shut up about "string length is greater than the length
80 ISO C89 compilers are required to support" when including
84 static char *defaults[] = {
85 #include "../driver/XScreenSaver_ad.h"
92 char *progclass = "XScreenSaver";
96 extern void grabscreen_verbose (void);
99 GRAB_DESK, GRAB_VIDEO, GRAB_FILE, GRAB_BARS
103 #define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
104 #define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file"
105 #define GETIMAGE_SCREEN_PROGRAM "xscreensaver-getimage-desktop"
107 extern const char *blurb (void);
117 x_ehandler (Display *dpy, XErrorEvent *error)
119 if (error->error_code == BadWindow || error->error_code == BadDrawable)
121 fprintf (stderr, "%s: target %s 0x%lx unexpectedly deleted\n", progname,
122 (error->error_code == BadWindow ? "window" : "pixmap"),
123 (unsigned long) error->resourceid);
127 fprintf (stderr, "\nX error in %s:\n", progname);
128 XmuPrintDefaultErrorMessage (dpy, error, stderr);
135 static Bool error_handler_hit_p = False;
138 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
140 error_handler_hit_p = True;
144 #ifndef USE_EXTERNAL_SCREEN_GRABBER
146 ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error)
148 if (error->error_code == BadMatch)
149 return ignore_all_errors_ehandler (dpy, error);
151 return x_ehandler (dpy, error);
153 #endif /* ! USE_EXTERNAL_SCREEN_GRABBER */
156 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
159 drawable_window_p (Display *dpy, Drawable d)
161 XErrorHandler old_handler;
162 XWindowAttributes xgwa;
165 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
166 error_handler_hit_p = False;
167 XGetWindowAttributes (dpy, d, &xgwa);
169 XSetErrorHandler (old_handler);
172 if (!error_handler_hit_p)
173 return True; /* It's a Window. */
175 return False; /* It's a Pixmap, or an invalid ID. */
179 /* Returns true if the window is the root window, or a virtual root window,
180 but *not* the xscreensaver window. That is, if it's a "real" desktop
181 root window of some kind.
184 root_window_p (Screen *screen, Window window)
186 Display *dpy = DisplayOfScreen (screen);
189 unsigned long nitems, bytesafter;
190 unsigned char *version;
192 if (window != RootWindowOfScreen (screen))
195 if (XGetWindowProperty (dpy, window,
196 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
197 0, 1, False, XA_STRING,
198 &type, &format, &nitems, &bytesafter,
208 /* Clear the window or pixmap to black, or its background color.
211 clear_drawable (Screen *screen, Drawable drawable)
213 Display *dpy = DisplayOfScreen (screen);
218 unsigned int w, h, bw, d;
219 XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
221 /* The window might have no-op background of None, so to clear it,
222 draw a black rectangle first, then do XClearWindow (in case the
223 actual background color is non-black...) */
225 /* #### really we should allocate "black" instead, but I'm lazy... */
226 gcv.foreground = BlackPixelOfScreen (screen);
227 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
228 XFillRectangle (dpy, drawable, gc, 0, 0, w, h);
230 if (drawable_window_p (dpy, drawable))
231 XClearWindow (dpy, (Window) drawable);
236 /* Figure out what kind of scaling/positioning we ought to do to display
237 a src-sized image in a dest-sized window/pixmap. Returns the width
238 and height to which the image should be scaled, and the position where
239 it should be displayed to center it.
242 compute_image_scaling (int src_w, int src_h,
243 int dest_w, int dest_h,
245 int *scaled_from_x_ret, int *scaled_from_y_ret,
246 int *scaled_to_x_ret, int *scaled_to_y_ret,
247 int *scaled_w_ret, int *scaled_h_ret)
249 int srcx, srcy, destx, desty;
251 Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
252 (src_h == dest_h && src_w <= dest_w));
254 if (!exact_fit_p) /* scale the image up or down */
256 float rw = (float) dest_w / src_w;
257 float rh = (float) dest_h / src_h;
258 float r = (rw < rh ? rw : rh);
264 /* this optimization breaks things */
265 if (pct < 95 || pct > 105) /* don't scale if it's close */
269 fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
270 progname, pct, src_w, src_h, tw, th);
276 /* Center the image on the window/pixmap. */
279 destx = (dest_w - src_w) / 2;
280 desty = (dest_h - src_h) / 2;
281 if (destx < 0) srcx = -destx, destx = 0;
282 if (desty < 0) srcy = -desty, desty = 0;
284 if (dest_w < src_w) src_w = dest_w;
285 if (dest_h < src_h) src_h = dest_h;
287 *scaled_w_ret = src_w;
288 *scaled_h_ret = src_h;
289 *scaled_from_x_ret = srcx;
290 *scaled_from_y_ret = srcy;
291 *scaled_to_x_ret = destx;
292 *scaled_to_y_ret = desty;
295 fprintf (stderr, "%s: displaying %dx%d image at %d,%d in %dx%d.\n",
296 progname, src_w, src_h, destx, desty, dest_w, dest_h);
300 /* Scales an XImage, modifying it in place.
301 This doesn't do dithering or smoothing, so it might have artifacts.
302 If out of memory, returns False, and the XImage will have been
305 #if !defined(USE_EXTERNAL_SCREEN_GRABBER) || defined(HAVE_JPEGLIB)
307 scale_ximage (Screen *screen, Visual *visual,
308 XImage *ximage, int new_width, int new_height)
310 Display *dpy = DisplayOfScreen (screen);
311 int depth = visual_depth (screen, visual);
313 double xscale, yscale;
315 XImage *ximage2 = XCreateImage (dpy, visual, depth,
317 new_width, new_height, 8, 0);
318 ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
322 fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
324 ximage->width, ximage->height,
325 ximage2->width, ximage2->height);
326 if (ximage->data) free (ximage->data);
327 if (ximage2->data) free (ximage2->data);
330 XDestroyImage (ximage);
331 XDestroyImage (ximage2);
335 /* Brute force scaling... */
336 xscale = (double) ximage->width / ximage2->width;
337 yscale = (double) ximage->height / ximage2->height;
338 for (y = 0; y < ximage2->height; y++)
339 for (x = 0; x < ximage2->width; x++)
340 XPutPixel (ximage2, x, y,
341 XGetPixel (ximage, x * xscale, y * yscale));
346 (*ximage) = (*ximage2);
349 XDestroyImage (ximage2);
353 #endif /* !USE_EXTERNAL_SCREEN_GRABBER || HAVE_JPEGLIB */
356 #ifdef HAVE_GDK_PIXBUF
358 /* Reads the given image file and renders it on the Drawable, using GDK.
359 Returns False if it fails.
362 read_file_gdk (Screen *screen, Window window, Drawable drawable,
363 const char *filename, Bool verbose_p,
364 XRectangle *geom_ret)
367 Display *dpy = DisplayOfScreen (screen);
368 unsigned int win_width, win_height, win_depth;
371 # endif /* HAVE_GTK2 */
373 /* Find the size of the Drawable. */
378 XGetGeometry (dpy, drawable,
379 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
382 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
385 # else /* !HAVE_GTK2 */
386 xlib_rgb_init (dpy, screen);
387 # endif /* !HAVE_GTK2 */
389 pb = gdk_pixbuf_new_from_file (filename
392 # endif /* HAVE_GTK2 */
397 fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
399 if (gerr && gerr->message && *gerr->message)
400 fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
401 # endif /* HAVE_GTK2 */
406 int w = gdk_pixbuf_get_width (pb);
407 int h = gdk_pixbuf_get_height (pb);
408 int srcx, srcy, destx, desty, w2, h2;
411 # ifdef HAVE_GDK_PIXBUF_APPLY_EMBEDDED_ORIENTATION
415 pb = gdk_pixbuf_apply_embedded_orientation (opb);
416 gdk_pixbuf_unref (opb);
417 w = gdk_pixbuf_get_width (pb);
418 h = gdk_pixbuf_get_height (pb);
419 if (verbose_p && (w != ow || h != oh))
420 fprintf (stderr, "%s: rotated %dx%d to %dx%d\n",
421 progname, ow, oh, w, h);
425 compute_image_scaling (w, h, win_width, win_height, verbose_p,
426 &srcx, &srcy, &destx, &desty, &w2, &h2);
427 if (w != w2 || h != h2)
429 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
430 GDK_INTERP_BILINEAR);
433 gdk_pixbuf_unref (pb);
439 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
442 /* If we're rendering onto the root window (and it's not the
443 xscreensaver pseudo-root) then put the image in the window's
444 background. Otherwise, just paint the image onto the window.
446 bg_p = (window == drawable && root_window_p (screen, window));
452 drawable = XCreatePixmap (dpy, window,
453 win_width, win_height, win_depth);
454 gcv.foreground = BlackPixelOfScreen (screen);
455 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
456 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
460 clear_drawable (screen, drawable);
462 /* #### Note that this always uses the default colormap! Morons!
463 Owen says that in Gnome 2.0, I should try using
464 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
467 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
468 srcx, srcy, destx, desty,
470 GDK_PIXBUF_ALPHA_FULL, 127,
471 XLIB_RGB_DITHER_NORMAL,
475 XSetWindowBackgroundPixmap (dpy, window, drawable);
476 XClearWindow (dpy, window);
484 geom_ret->height = h;
492 #endif /* HAVE_GDK_PIXBUF */
498 /* Allocates a colormap that makes a PseudoColor or DirectColor
499 visual behave like a TrueColor visual of the same depth.
501 #### Duplicated in utils/grabscreen.c
504 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
507 Display *dpy = DisplayOfScreen (screen);
508 int nr, ng, nb, cells;
514 depth = visual_depth (screen, visual);
518 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
519 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
520 default: abort(); break;
523 memset(colors, 0, sizeof(colors));
524 for (r = 0; r < (1 << nr); r++)
525 for (g = 0; g < (1 << ng); g++)
526 for (b = 0; b < (1 << nb); b++)
528 i = (r | (g << nr) | (b << (nr + ng)));
530 colors[i].flags = DoRed|DoGreen|DoBlue;
533 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
534 (r << 4) | (r << 1));
535 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
536 (g << 4) | (g << 1));
537 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
538 (b << 8) | (b << 6) | (b << 4) |
543 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
544 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
545 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
552 int interleave = cells / 8; /* skip around, rather than allocating in
553 order, so that we get better coverage if
554 we can't allocated all of them. */
555 for (j = 0; j < interleave; j++)
556 for (i = 0; i < cells; i += interleave)
557 if (XAllocColor (dpy, cmap, &colors[i + j]))
561 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
562 progname, allocated, cells);
566 /* Find the pixel index that is closest to the given color
567 (using linear distance in RGB space -- which is far from the best way.)
569 #### Duplicated in utils/grabscreen.c
572 find_closest_pixel (XColor *colors, int ncolors,
573 unsigned long r, unsigned long g, unsigned long b)
575 unsigned long distance = ~0;
580 for (i = 0; i < ncolors; i++)
585 rd = r - colors[i].red;
586 gd = g - colors[i].green;
587 bd = b - colors[i].blue;
588 if (rd < 0) rd = -rd;
589 if (gd < 0) gd = -gd;
590 if (bd < 0) bd = -bd;
591 d = (rd << 1) + (gd << 2) + bd;
606 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
607 displayable with the given X colormap. The farther from a perfect
608 color cube the contents of the colormap are, the lossier the
609 transformation will be. No dithering is done.
611 #### Duplicated in utils/grabscreen.c
614 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
616 Display *dpy = DisplayOfScreen (screen);
617 unsigned long map[4097];
622 if (image->depth == 8)
624 else if (image->depth == 12)
629 memset(map, -1, sizeof(*map));
630 memset(colors, -1, sizeof(*colors));
632 for (i = 0; i < cells; i++)
634 XQueryColors (dpy, cmap, colors, cells);
637 fprintf(stderr, "%s: building color cube for %d bit image\n",
638 progname, image->depth);
640 for (i = 0; i < cells; i++)
642 unsigned short r, g, b;
646 /* "RRR GGG BB" In an 8 bit map. Convert that to
647 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
653 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
654 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
655 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
656 (b << 6) | (b << 4) | (b << 2) | b);
660 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
661 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
664 g = (i & 0x0F0) >> 4;
665 b = (i & 0xF00) >> 8;
667 r = (r << 12) | (r << 8) | (r << 4) | r;
668 g = (g << 12) | (g << 8) | (g << 4) | g;
669 b = (b << 12) | (b << 8) | (b << 4) | b;
672 map[i] = find_closest_pixel (colors, cells, r, g, b);
676 fprintf(stderr, "%s: remapping colors in %d bit image\n",
677 progname, image->depth);
679 for (y = 0; y < image->height; y++)
680 for (x = 0; x < image->width; x++)
682 unsigned long pixel = XGetPixel(image, x, y);
683 if (pixel >= cells) abort();
684 XPutPixel(image, x, y, map[pixel]);
689 /* If the file has a PPM (P6) on it, read it and return an XImage.
690 Otherwise, rewind the fd back to the beginning, and return 0.
693 maybe_read_ppm (Screen *screen, Visual *visual,
694 const char *filename, FILE *in, Bool verbose_p)
696 Display *dpy = DisplayOfScreen (screen);
697 int depth = visual_depth (screen, visual);
703 int x, y, w, h, maxval;
706 if (fstat (fileno (in), &st))
710 buf = (char *) malloc (bufsiz + 1);
713 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
714 progname, bufsiz, filename);
718 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
721 if (!strncmp (buf, "\107\111", 2))
723 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
724 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
728 else if (!strncmp (buf, "\211\120", 2))
730 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
731 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
736 if (strncmp (s, "P6", 2))
739 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
741 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
743 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
747 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
749 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
751 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
756 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
757 progname, filename, maxval);
761 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
764 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
765 if (!ximage || !ximage->data)
767 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
768 progname, ximage->width, ximage->height, filename);
774 while ((i = fread (s, 1, j, in)) > 0)
778 for (y = 0; y < ximage->height; y++)
779 for (x = 0; x < ximage->width; x++)
781 unsigned char r = buf[i++];
782 unsigned char g = buf[i++];
783 unsigned char b = buf[i++];
787 pixel = (r << 16) | (g << 8) | b;
789 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
790 else if (depth == 12)
791 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
792 else if (depth == 16 || depth == 15)
793 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
797 XPutPixel (ximage, x, y, pixel);
805 if (ximage && ximage->data)
810 if (ximage) XDestroyImage (ximage);
811 fseek (in, 0, SEEK_SET);
817 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
818 const char *filename;
823 } getimg_jpg_error_mgr;
827 jpg_output_message (j_common_ptr cinfo)
829 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
830 char buf[JMSG_LENGTH_MAX];
831 cinfo->err->format_message (cinfo, buf);
832 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
837 jpg_error_exit (j_common_ptr cinfo)
839 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
840 cinfo->err->output_message (cinfo);
841 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
843 XSync (DisplayOfScreen (err->screen), False);
848 /* Reads a JPEG file, returns an RGB XImage of it.
851 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
852 Colormap cmap, const char *filename, Bool verbose_p)
854 Display *dpy = DisplayOfScreen (screen);
855 int depth = visual_depth (screen, visual);
859 struct jpeg_decompress_struct cinfo;
860 getimg_jpg_error_mgr jerr;
861 JSAMPARRAY scanbuf = 0;
864 jerr.filename = filename;
865 jerr.screen = screen;
866 jerr.visual = visual;
867 jerr.drawable = drawable;
870 if (! (depth >= 15 || depth == 12 || depth == 8))
872 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
876 in = fopen (filename, "rb");
879 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
883 /* Check to see if it's a PPM, and if so, read that instead of using
884 the JPEG library. Yeah, this is all modular and stuff.
886 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
892 cinfo.err = jpeg_std_error (&jerr.pub);
893 jerr.pub.output_message = jpg_output_message;
894 jerr.pub.error_exit = jpg_error_exit;
896 jpeg_create_decompress (&cinfo);
897 jpeg_stdio_src (&cinfo, in);
898 jpeg_read_header (&cinfo, TRUE);
900 /* set some decode parameters */
901 cinfo.out_color_space = JCS_RGB;
902 cinfo.quantize_colors = FALSE;
904 jpeg_start_decompress (&cinfo);
906 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
907 cinfo.output_width, cinfo.output_height,
910 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
912 if (ximage && ximage->data)
913 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
914 cinfo.rec_outbuf_height *
916 cinfo.output_components,
918 if (!ximage || !ximage->data || !scanbuf)
920 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
921 progname, ximage->width, ximage->height, filename);
926 while (cinfo.output_scanline < cinfo.output_height)
928 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
930 for (i = 0; i < n; i++)
933 for (x = 0; x < ximage->width; x++)
935 int j = x * cinfo.output_components;
936 unsigned char r = scanbuf[i][j];
937 unsigned char g = scanbuf[i][j+1];
938 unsigned char b = scanbuf[i][j+2];
942 pixel = (r << 16) | (g << 8) | b;
944 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
945 else if (depth == 12)
946 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
947 else if (depth == 15)
948 /* Gah! I don't understand why these are in the other
950 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
951 else if (depth == 16)
952 pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
956 XPutPixel (ximage, x, y, pixel);
962 if (cinfo.output_scanline < cinfo.output_height)
963 /* don't goto FAIL -- we might have viewable partial data. */
964 jpeg_abort_decompress (&cinfo);
966 jpeg_finish_decompress (&cinfo);
968 jpeg_destroy_decompress (&cinfo);
976 if (ximage && ximage->data)
981 if (ximage) XDestroyImage (ximage);
982 if (scanbuf) free (scanbuf);
987 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
988 Returns False if it fails.
991 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
992 const char *filename, Bool verbose_p,
993 XRectangle *geom_ret)
995 Display *dpy = DisplayOfScreen (screen);
1000 unsigned int win_width, win_height, win_depth;
1001 int srcx, srcy, destx, desty, w2, h2;
1003 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
1008 XWindowAttributes xgwa;
1009 XGetWindowAttributes (dpy, window, &xgwa);
1010 visual = xgwa.visual;
1011 cmap = xgwa.colormap;
1012 XGetGeometry (dpy, drawable,
1013 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
1016 /* Make sure we're not on some weirdo visual...
1018 class = visual_class (screen, visual);
1019 depth = visual_depth (screen, visual);
1020 if ((class == PseudoColor || class == DirectColor) &&
1021 (depth != 8 && depth != 12))
1023 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
1030 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
1031 filename, verbose_p);
1032 if (!ximage) return False;
1034 /* Scale it, if necessary...
1036 compute_image_scaling (ximage->width, ximage->height,
1037 win_width, win_height, verbose_p,
1038 &srcx, &srcy, &destx, &desty, &w2, &h2);
1039 if (ximage->width != w2 || ximage->height != h2)
1040 if (! scale_ximage (screen, visual, ximage, w2, h2))
1043 /* Allocate a colormap, if we need to...
1045 if (class == PseudoColor || class == DirectColor)
1047 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
1048 remap_image (screen, cmap, ximage, verbose_p);
1051 /* Finally, put the resized image on the window.
1057 /* If we're rendering onto the root window (and it's not the xscreensaver
1058 pseudo-root) then put the image in the window's background. Otherwise,
1059 just paint the image onto the window.
1061 if (window == drawable && root_window_p (screen, window))
1063 Pixmap bg = XCreatePixmap (dpy, window,
1064 win_width, win_height, win_depth);
1065 gcv.foreground = BlackPixelOfScreen (screen);
1066 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1067 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1068 XPutImage (dpy, bg, gc, ximage,
1069 srcx, srcy, destx, desty, ximage->width, ximage->height);
1070 XSetWindowBackgroundPixmap (dpy, window, bg);
1071 XClearWindow (dpy, window);
1075 gc = XCreateGC (dpy, drawable, 0, &gcv);
1076 clear_drawable (screen, drawable);
1077 XPutImage (dpy, drawable, gc, ximage,
1078 srcx, srcy, destx, desty, ximage->width, ximage->height);
1086 geom_ret->x = destx;
1087 geom_ret->y = desty;
1088 geom_ret->width = ximage->width;
1089 geom_ret->height = ximage->height;
1092 free (ximage->data);
1094 XDestroyImage (ximage);
1099 #endif /* HAVE_JPEGLIB */
1102 /* Reads the given image file and renders it on the Drawable.
1103 Returns False if it fails.
1106 display_file (Screen *screen, Window window, Drawable drawable,
1107 const char *filename, Bool verbose_p,
1108 XRectangle *geom_ret)
1111 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1113 # if defined(HAVE_GDK_PIXBUF)
1114 if (read_file_gdk (screen, window, drawable, filename, verbose_p, geom_ret))
1116 # elif defined(HAVE_JPEGLIB)
1117 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p,
1120 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1121 /* shouldn't get here if we have no image-loading methods available. */
1123 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1129 /* Invokes a sub-process and returns its output (presumably, a file to
1130 load.) Free the string when done. 'grab_type' controls which program
1131 to run. Returned pathname may be relative to 'directory', or absolute.
1134 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1137 Display *dpy = DisplayOfScreen (screen);
1148 av[ac++] = GETIMAGE_FILE_PROGRAM;
1150 av[ac++] = "--verbose";
1151 av[ac++] = "--name";
1152 av[ac++] = (char *) directory;
1156 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1158 av[ac++] = "--verbose";
1159 av[ac++] = "--name";
1162 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1164 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1166 av[ac++] = "--verbose";
1167 av[ac++] = "--name";
1179 fprintf (stderr, "%s: executing:", progname);
1180 for (i = 0; i < ac; i++)
1181 fprintf (stderr, " %s", av[i]);
1182 fprintf (stderr, "\n");
1187 sprintf (buf, "%s: error creating pipe", progname);
1195 switch ((int) (forked = fork ()))
1199 sprintf (buf, "%s: couldn't fork", progname);
1207 close (in); /* don't need this one */
1208 close (ConnectionNumber (dpy)); /* close display fd */
1210 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1212 sprintf (buf, "%s: could not dup() a new stdout", progname);
1213 exit (-1); /* exits fork */
1216 execvp (av[0], av); /* shouldn't return. */
1217 exit (-1); /* exits fork */
1223 int wait_status = 0;
1224 FILE *f = fdopen (in, "r");
1228 close (out); /* don't need this one */
1230 if (! fgets (buf, sizeof(buf)-1, f))
1234 /* Wait for the child to die. */
1235 waitpid (-1, &wait_status, 0);
1238 while (L && buf[L-1] == '\n')
1248 /* Program returned path relative to directory. Prepend dir
1249 to buf so that we can properly stat it. */
1250 strcpy (buf, directory);
1251 if (directory[strlen(directory)-1] != '/')
1258 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1272 /* Returns a pathname to an image file. Free the string when you're done.
1275 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1277 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1281 /* Grabs a video frame to a file, and returns a pathname to that file.
1282 Delete that file when you are done with it (and free the string.)
1285 get_video_filename (Screen *screen, Bool verbose_p)
1287 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1290 /* Grabs a desktop image to a file, and returns a pathname to that file.
1291 Delete that file when you are done with it (and free the string.)
1293 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1295 get_desktop_filename (Screen *screen, Bool verbose_p)
1297 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1299 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1302 /* Grabs a video frame, and renders it on the Drawable.
1303 Returns False if it fails;
1306 display_video (Screen *screen, Window window, Drawable drawable,
1307 Bool verbose_p, XRectangle *geom_ret)
1309 char *filename = get_video_filename (screen, verbose_p);
1315 fprintf (stderr, "%s: video grab failed.\n", progname);
1319 status = display_file (screen, window, drawable, filename, verbose_p,
1322 if (unlink (filename))
1325 sprintf (buf, "%s: rm %.100s", progname, filename);
1329 fprintf (stderr, "%s: rm %s\n", progname, filename);
1331 if (filename) free (filename);
1336 /* Grabs a desktop screen shot onto the window and the drawable.
1337 If the window and drawable are not the same size, the image in
1338 the drawable is scaled to fit.
1339 Returns False if it fails.
1342 display_desktop (Screen *screen, Window window, Drawable drawable,
1343 Bool verbose_p, XRectangle *geom_ret)
1345 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1347 Display *dpy = DisplayOfScreen (screen);
1348 Bool top_p = top_level_window_p (screen, window);
1355 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1356 (unsigned long) window);
1357 XUnmapWindow (dpy, window);
1361 filename = get_desktop_filename (screen, verbose_p);
1366 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1367 (unsigned long) window);
1368 XMapRaised (dpy, window);
1375 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1379 status = display_file (screen, window, drawable, filename, verbose_p,
1382 if (unlink (filename))
1385 sprintf (buf, "%s: rm %.100s", progname, filename);
1389 fprintf (stderr, "%s: rm %s\n", progname, filename);
1391 if (filename) free (filename);
1394 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1396 Display *dpy = DisplayOfScreen (screen);
1398 XWindowAttributes xgwa;
1401 unsigned int pw, ph, pbw, pd;
1402 int srcx, srcy, destx, desty, w2, h2;
1406 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1407 grabscreen_verbose();
1410 XGetWindowAttributes (dpy, window, &xgwa);
1411 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1413 grab_screen_image_internal (screen, window);
1415 compute_image_scaling (xgwa.width, xgwa.height,
1417 &srcx, &srcy, &destx, &desty, &w2, &h2);
1419 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1421 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1422 XCopyArea (dpy, window, drawable, gc,
1423 0, 0, xgwa.width, xgwa.height, 0, 0);
1426 else /* size mismatch -- must scale client-side images to fit drawable */
1430 XErrorHandler old_handler;
1433 old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
1434 error_handler_hit_p = False;
1436 /* This can return BadMatch if the window is not fully on screen.
1437 Trap that error and return color bars in that case.
1438 (Note that this only happens with XGetImage, not with XCopyArea:
1439 yet another totally gratuitous inconsistency in X, thanks.)
1441 ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1445 XSetErrorHandler (old_handler);
1448 if (error_handler_hit_p)
1452 fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
1453 progname, (unsigned int) window);
1457 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1460 gc = XCreateGC (dpy, drawable, 0, &gcv);
1461 clear_drawable (screen, drawable);
1462 XPutImage (dpy, drawable, gc, ximage,
1463 srcx, srcy, destx, desty, ximage->width, ximage->height);
1464 XDestroyImage (ximage);
1470 geom_ret->x = destx;
1471 geom_ret->y = desty;
1472 geom_ret->width = w2;
1473 geom_ret->height = h2;
1479 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1483 /* Whether the given Drawable is unreasonably small.
1486 drawable_miniscule_p (Display *dpy, Drawable drawable)
1490 unsigned int bw, d, w = 0, h = 0;
1491 XGetGeometry (dpy, drawable, &root, &xx, &yy, &w, &h, &bw, &d);
1492 return (w < 32 || h < 32);
1496 /* Grabs an image (from a file, video, or the desktop) and renders it on
1497 the Drawable. If `file' is specified, always use that file. Otherwise,
1498 select randomly, based on the other arguments.
1501 get_image (Screen *screen,
1502 Window window, Drawable drawable,
1510 Display *dpy = DisplayOfScreen (screen);
1511 grab_type which = GRAB_BARS;
1513 const char *file_prop = 0;
1515 XRectangle geom = { 0, 0, 0, 0 };
1517 if (! drawable_window_p (dpy, window))
1519 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1520 progname, (unsigned long) window);
1524 /* Make sure the Screen and the Window correspond. */
1526 XWindowAttributes xgwa;
1527 XGetWindowAttributes (dpy, window, &xgwa);
1528 screen = xgwa.screen;
1531 if (file && stat (file, &st))
1533 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1539 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1540 progname, desk_p ? "True" : "False");
1541 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1542 progname, video_p ? "True" : "False");
1543 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1544 progname, image_p ? "True" : "False");
1545 fprintf (stderr, "%s: imageDirectory: %s\n",
1546 progname, (file ? file : dir ? dir : ""));
1549 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1550 image_p = False; /* can't load images from files... */
1551 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1552 desk_p = False; /* ...or from desktops grabbed to files. */
1558 "%s: image file loading not available at compile-time\n",
1560 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1563 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1571 else if (!dir || !*dir)
1573 if (verbose_p && image_p)
1575 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1580 /* If the target drawable is really small, no good can come of that.
1581 Always do colorbars in that case.
1583 if (drawable_miniscule_p (dpy, drawable))
1591 # error Error! This file definitely needs vroot.h!
1594 /* We can grab desktop images (using the normal X11 method) if:
1595 - the window is the real root window;
1596 - the window is a toplevel window.
1597 We cannot grab desktop images that way if:
1598 - the window is a non-top-level window.
1600 Under X11 on MacOS, desktops are just like loaded image files.
1601 Under Cocoa on MacOS, this code is not used at all.
1603 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1606 if (!top_level_window_p (screen, window))
1611 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1612 progname, (unsigned int) window);
1615 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1617 if (! (desk_p || video_p || image_p))
1623 /* Loop until we get one that's permitted.
1624 If files or video are permitted, do them more often
1627 D+V+I: 10% + 45% + 45%.
1633 n = (random() % 100);
1634 if (++i > 300) abort();
1635 else if (desk_p && n < 10) which = GRAB_DESK; /* 10% */
1636 else if (video_p && n < 55) which = GRAB_VIDEO; /* 45% */
1637 else if (image_p) which = GRAB_FILE; /* 45% */
1642 /* If we're to search a directory to find an image file, do so now.
1644 if (which == GRAB_FILE && !file)
1646 file = get_filename (screen, dir, verbose_p);
1651 fprintf (stderr, "%s: no image files found.\n", progname);
1655 /* Now actually render something.
1661 XWindowAttributes xgwa;
1664 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1665 XGetWindowAttributes (dpy, window, &xgwa);
1666 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1673 if (! display_desktop (screen, window, drawable, verbose_p, &geom))
1675 file_prop = "desktop";
1679 if (*file && *file != '/') /* pathname is relative to dir. */
1681 if (absfile) free (absfile);
1682 absfile = malloc (strlen(dir) + strlen(file) + 10);
1683 strcpy (absfile, dir);
1684 if (dir[strlen(dir)-1] != '/')
1685 strcat (absfile, "/");
1686 strcat (absfile, file);
1688 if (! display_file (screen, window, drawable,
1689 (absfile ? absfile : file),
1696 if (! display_video (screen, window, drawable, verbose_p, &geom))
1698 file_prop = "video";
1707 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1708 if (file_prop && *file_prop)
1709 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1710 (unsigned char *) file_prop, strlen(file_prop));
1712 XDeleteProperty (dpy, window, a);
1714 a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
1718 sprintf (gstr, "%dx%d+%d+%d", geom.width, geom.height, geom.x, geom.y);
1719 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1720 (unsigned char *) gstr, strlen (gstr));
1723 XDeleteProperty (dpy, window, a);
1726 if (absfile) free (absfile);
1733 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1734 XrmRepresentation *type, XrmValue *value, XPointer closure)
1737 for (i = 0; quarks[i]; i++)
1739 if (bindings[i] == XrmBindTightly)
1740 fprintf (stderr, (i == 0 ? "" : "."));
1741 else if (bindings[i] == XrmBindLoosely)
1742 fprintf (stderr, "*");
1744 fprintf (stderr, " ??? ");
1745 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1748 fprintf (stderr, ": %s\n", (char *) value->addr);
1755 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1759 " %s puts an image on the given window or pixmap.\n" \
1761 " It is used by those xscreensaver demos that operate on images.\n" \
1762 " The image may be a file loaded from disk, a frame grabbed from\n" \
1763 " the system's video camera, or a screenshot of the desktop,\n" \
1764 " depending on command-line options or the ~/.xscreensaver file.\n" \
1766 " Options include:\n" \
1768 " -display host:dpy.screen which display to use\n" \
1769 " -root draw to the root window\n" \
1770 " -verbose print diagnostics\n" \
1771 " -images / -no-images whether to allow image file loading\n" \
1772 " -video / -no-video whether to allow video grabs\n" \
1773 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1774 " -directory <path> where to find image files to load\n" \
1775 " -file <filename> load this image file\n" \
1777 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1778 " defaults for these options in your ~/.xscreensaver file.\n" \
1782 main (int argc, char **argv)
1784 saver_preferences P;
1788 char *oprogname = progname;
1792 Window window = (Window) 0;
1793 Drawable drawable = (Drawable) 0;
1794 const char *window_str = 0;
1795 const char *drawable_str = 0;
1800 s = strrchr (progname, '/');
1801 if (s) progname = s+1;
1802 oprogname = progname;
1804 /* half-assed way of avoiding buffer-overrun attacks. */
1805 if (strlen (progname) >= 100) progname[100] = 0;
1808 # error Error! This file definitely needs vroot.h!
1811 /* Get the version number, for error messages. */
1813 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1814 char *s1, *s2, *s3, *s4;
1815 s1 = (char *) strchr(v, ' '); s1++;
1816 s2 = (char *) strchr(s1, ' ');
1817 s3 = (char *) strchr(v, '('); s3++;
1818 s4 = (char *) strchr(s3, ')');
1821 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1825 /* We must read exactly the same resources as xscreensaver.
1826 That means we must have both the same progclass *and* progname,
1827 at least as far as the resource database is concerned. So,
1828 put "xscreensaver" in argv[0] while initializing Xt.
1830 progname = argv[0] = "xscreensaver";
1832 /* allow one dash or two. */
1833 for (i = 1; i < argc; i++)
1834 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1836 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1838 dpy = XtDisplay (toplevel);
1839 screen = XtScreen (toplevel);
1840 db = XtDatabase (dpy);
1841 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1842 XSetErrorHandler (x_ehandler);
1845 /* Randomize -- only need to do this here because this program
1846 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1847 # undef ya_rand_init
1850 memset (&P, 0, sizeof(P));
1852 load_init_file (dpy, &P);
1854 progname = argv[0] = oprogname;
1856 for (i = 1; i < argc; i++)
1861 /* Have to re-process these, or else the .xscreensaver file
1862 has priority over the command line...
1864 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1866 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1867 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1868 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1869 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1870 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1871 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1872 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1873 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1874 P.image_directory = argv[++i];
1875 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1879 fprintf (stderr, "%s: both %s and %s specified?\n",
1880 progname, argv[i], window_str);
1883 window_str = argv[i];
1884 window = VirtualRootWindowOfScreen (screen);
1886 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1887 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1892 fprintf (stderr, "%s: both %s and %s specified?\n",
1893 progname, drawable_str, argv[i]);
1898 drawable_str = argv[i];
1899 drawable = (Drawable) w;
1903 window_str = argv[i];
1904 window = (Window) w;
1909 if (argv[i][0] == '-')
1910 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1913 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1917 __extension__ /* don't warn about "string length is greater than
1918 the length ISO C89 compilers are required to
1919 support" in the usage string... */
1921 fprintf (stderr, USAGE, progname, version, progname);
1928 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1934 if (P.verbose_p) /* Print out all the resources we can see. */
1936 XrmName name = { 0 };
1937 XrmClass class = { 0 };
1939 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1940 (XtPointer) &count);
1944 if (!window) abort();
1945 if (!drawable) drawable = window;
1947 get_image (screen, window, drawable, P.verbose_p,
1948 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1949 P.image_directory, file);