1 /* xscreensaver, Copyright (c) 2001-2004 by Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* xscreensaver-getimage -- helper program that puts a random image
13 onto the given window or pixmap. That image is either a screen-grab,
14 a file loaded from disk, or a frame grabbed from the system's video
20 #include <X11/Intrinsic.h>
26 #ifdef HAVE_SYS_WAIT_H
27 # include <sys/wait.h> /* for waitpid() and associated macros */
32 # include <X11/Xmu/Error.h>
34 # include <Xmu/Error.h>
41 #include "grabscreen.h"
42 #include "resources.h"
43 #include "colorbars.h"
49 #ifndef _XSCREENSAVER_VROOT_H_
50 # error Error! You have an old version of vroot.h! Check -I args.
51 #endif /* _XSCREENSAVER_VROOT_H_ */
53 #ifdef HAVE_GDK_PIXBUF
56 # include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
57 # else /* !HAVE_GTK2 */
58 # include <gdk-pixbuf/gdk-pixbuf-xlib.h>
59 # endif /* !HAVE_GTK2 */
60 #endif /* HAVE_GDK_PIXBUF */
63 # undef HAVE_GDK_PIXBUF
69 /* On MacOSX / XDarwin, the usual X11 mechanism of getting a screen shot
70 doesn't work, and we need to use an external program. */
71 # define USE_EXTERNAL_SCREEN_GRABBER
76 __extension__ /* shut up about "string length is greater than the length
77 ISO C89 compilers are required to support" when including
81 static char *defaults[] = {
82 #include "../driver/XScreenSaver_ad.h"
89 char *progclass = "XScreenSaver";
93 extern void grabscreen_verbose (void);
96 GRAB_DESK, GRAB_VIDEO, GRAB_FILE, GRAB_BARS
100 #define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
101 #define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file"
102 #define GETIMAGE_SCREEN_PROGRAM "xscreensaver-getimage-desktop"
112 x_ehandler (Display *dpy, XErrorEvent *error)
114 if (error->error_code == BadWindow || error->error_code == BadDrawable)
116 fprintf (stderr, "%s: target %s 0x%lx unexpectedly deleted\n", progname,
117 (error->error_code == BadWindow ? "window" : "pixmap"),
118 (unsigned long) error->resourceid);
122 fprintf (stderr, "\nX error in %s:\n", progname);
123 XmuPrintDefaultErrorMessage (dpy, error, stderr);
130 static Bool error_handler_hit_p = False;
133 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
135 error_handler_hit_p = True;
140 ignore_badmatch_ehandler (Display *dpy, XErrorEvent *error)
142 if (error->error_code == BadMatch)
143 return ignore_all_errors_ehandler (dpy, error);
145 return x_ehandler (dpy, error);
149 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
152 drawable_window_p (Display *dpy, Drawable d)
154 XErrorHandler old_handler;
155 XWindowAttributes xgwa;
158 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
159 error_handler_hit_p = False;
160 XGetWindowAttributes (dpy, d, &xgwa);
162 XSetErrorHandler (old_handler);
165 if (!error_handler_hit_p)
166 return True; /* It's a Window. */
168 return False; /* It's a Pixmap, or an invalid ID. */
172 /* Returns true if the window is the root window, or a virtual root window,
173 but *not* the xscreensaver window. That is, if it's a "real" desktop
174 root window of some kind.
177 root_window_p (Screen *screen, Window window)
179 Display *dpy = DisplayOfScreen (screen);
182 unsigned long nitems, bytesafter;
185 if (window != RootWindowOfScreen (screen))
188 if (XGetWindowProperty (dpy, window,
189 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
190 0, 1, False, XA_STRING,
191 &type, &format, &nitems, &bytesafter,
192 (unsigned char **) &version)
201 /* Clear the window or pixmap to black, or its background color.
204 clear_drawable (Screen *screen, Drawable drawable)
206 Display *dpy = DisplayOfScreen (screen);
211 unsigned int w, h, bw, d;
212 XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
214 /* The window might have no-op background of None, so to clear it,
215 draw a black rectangle first, then do XClearWindow (in case the
216 actual background color is non-black...) */
218 /* #### really we should allocate "black" instead, but I'm lazy... */
219 gcv.foreground = BlackPixelOfScreen (screen);
220 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
221 XFillRectangle (dpy, drawable, gc, 0, 0, w, h);
223 if (drawable_window_p (dpy, drawable))
224 XClearWindow (dpy, (Window) drawable);
229 /* Figure out what kind of scaling/positioning we ought to do to display
230 a src-sized image in a dest-sized window/pixmap. Returns the width
231 and height to which the image should be scaled, and the position where
232 it should be displayed to center it.
235 compute_image_scaling (int src_w, int src_h,
236 int dest_w, int dest_h,
238 int *scaled_from_x_ret, int *scaled_from_y_ret,
239 int *scaled_to_x_ret, int *scaled_to_y_ret,
240 int *scaled_w_ret, int *scaled_h_ret)
242 int srcx, srcy, destx, desty;
244 Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
245 (src_h == dest_h && src_w <= dest_w));
247 if (!exact_fit_p) /* scale the image up or down */
249 float rw = (float) dest_w / src_w;
250 float rh = (float) dest_h / src_h;
251 float r = (rw < rh ? rw : rh);
257 /* this optimization breaks things */
258 if (pct < 95 || pct > 105) /* don't scale if it's close */
262 fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
263 progname, pct, src_w, src_h, tw, th);
269 /* Center the image on the window/pixmap. */
272 destx = (dest_w - src_w) / 2;
273 desty = (dest_h - src_h) / 2;
274 if (destx < 0) srcx = -destx, destx = 0;
275 if (desty < 0) srcy = -desty, desty = 0;
277 if (dest_w < src_w) src_w = dest_w;
278 if (dest_h < src_h) src_h = dest_h;
280 *scaled_w_ret = src_w;
281 *scaled_h_ret = src_h;
282 *scaled_from_x_ret = srcx;
283 *scaled_from_y_ret = srcy;
284 *scaled_to_x_ret = destx;
285 *scaled_to_y_ret = desty;
288 fprintf (stderr, "%s: displaying %dx%d image at %d,%d.\n",
289 progname, src_w, src_h, destx, desty);
293 /* Scales an XImage, modifying it in place.
294 This doesn't do dithering or smoothing, so it might have artifacts.
295 If out of memory, returns False, and the XImage will have been
299 scale_ximage (Screen *screen, Visual *visual,
300 XImage *ximage, int new_width, int new_height)
302 Display *dpy = DisplayOfScreen (screen);
303 int depth = visual_depth (screen, visual);
305 double xscale, yscale;
307 XImage *ximage2 = XCreateImage (dpy, visual, depth,
309 new_width, new_height, 8, 0);
310 ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
314 fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
316 ximage->width, ximage->height,
317 ximage2->width, ximage2->height);
318 if (ximage->data) free (ximage->data);
319 if (ximage2->data) free (ximage2->data);
322 XDestroyImage (ximage);
323 XDestroyImage (ximage2);
327 /* Brute force scaling... */
328 xscale = (double) ximage->width / ximage2->width;
329 yscale = (double) ximage->height / ximage2->height;
330 for (y = 0; y < ximage2->height; y++)
331 for (x = 0; x < ximage2->width; x++)
332 XPutPixel (ximage2, x, y,
333 XGetPixel (ximage, x * xscale, y * yscale));
338 (*ximage) = (*ximage2);
341 XDestroyImage (ximage2);
347 #ifdef HAVE_GDK_PIXBUF
349 /* Reads the given image file and renders it on the Drawable, using GDK.
350 Returns False if it fails.
353 read_file_gdk (Screen *screen, Window window, Drawable drawable,
354 const char *filename, Bool verbose_p)
357 Display *dpy = DisplayOfScreen (screen);
358 unsigned int win_width, win_height, win_depth;
361 # endif /* HAVE_GTK2 */
363 /* Find the size of the Drawable. */
368 XGetGeometry (dpy, drawable,
369 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
372 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
375 # else /* !HAVE_GTK2 */
376 xlib_rgb_init (dpy, screen);
377 # endif /* !HAVE_GTK2 */
379 pb = gdk_pixbuf_new_from_file (filename
382 # endif /* HAVE_GTK2 */
387 fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
389 if (gerr && gerr->message && *gerr->message)
390 fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
391 # endif /* HAVE_GTK2 */
396 int w = gdk_pixbuf_get_width (pb);
397 int h = gdk_pixbuf_get_height (pb);
398 int srcx, srcy, destx, desty, w2, h2;
401 compute_image_scaling (w, h, win_width, win_height, verbose_p,
402 &srcx, &srcy, &destx, &desty, &w2, &h2);
403 if (w != w2 || h != h2)
405 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
406 GDK_INTERP_BILINEAR);
409 gdk_pixbuf_unref (pb);
415 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
418 /* If we're rendering onto the root window (and it's not the
419 xscreensaver pseudo-root) then put the image in the window's
420 background. Otherwise, just paint the image onto the window.
422 bg_p = (window == drawable && root_window_p (screen, window));
428 drawable = XCreatePixmap (dpy, window,
429 win_width, win_height, win_depth);
430 gcv.foreground = BlackPixelOfScreen (screen);
431 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
432 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
436 clear_drawable (screen, drawable);
438 /* #### Note that this always uses the default colormap! Morons!
439 Owen says that in Gnome 2.0, I should try using
440 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
443 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
444 srcx, srcy, destx, desty,
446 GDK_PIXBUF_ALPHA_FULL, 127,
447 XLIB_RGB_DITHER_NORMAL,
451 XSetWindowBackgroundPixmap (dpy, window, drawable);
452 XClearWindow (dpy, window);
460 #endif /* HAVE_GDK_PIXBUF */
466 /* Allocates a colormap that makes a PseudoColor or DirectColor
467 visual behave like a TrueColor visual of the same depth.
470 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
473 Display *dpy = DisplayOfScreen (screen);
474 int nr, ng, nb, cells;
480 depth = visual_depth (screen, visual);
484 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
485 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
486 default: abort(); break;
489 memset(colors, 0, sizeof(colors));
490 for (r = 0; r < (1 << nr); r++)
491 for (g = 0; g < (1 << ng); g++)
492 for (b = 0; b < (1 << nb); b++)
494 i = (r | (g << nr) | (b << (nr + ng)));
496 colors[i].flags = DoRed|DoGreen|DoBlue;
499 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
500 (r << 4) | (r << 1));
501 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
502 (g << 4) | (g << 1));
503 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
504 (b << 8) | (b << 6) | (b << 4) |
509 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
510 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
511 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
518 int interleave = cells / 8; /* skip around, rather than allocating in
519 order, so that we get better coverage if
520 we can't allocated all of them. */
521 for (j = 0; j < interleave; j++)
522 for (i = 0; i < cells; i += interleave)
523 if (XAllocColor (dpy, cmap, &colors[i + j]))
527 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
528 progname, allocated, cells);
532 /* Find the pixel index that is closest to the given color
533 (using linear distance in RGB space -- which is far from the best way.)
536 find_closest_pixel (XColor *colors, int ncolors,
537 unsigned long r, unsigned long g, unsigned long b)
539 unsigned long distance = ~0;
544 for (i = 0; i < ncolors; i++)
549 rd = r - colors[i].red;
550 gd = g - colors[i].green;
551 bd = b - colors[i].blue;
552 if (rd < 0) rd = -rd;
553 if (gd < 0) gd = -gd;
554 if (bd < 0) bd = -bd;
555 d = (rd << 1) + (gd << 2) + bd;
570 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
571 displayable with the given X colormap. The farther from a perfect
572 color cube the contents of the colormap are, the lossier the
573 transformation will be. No dithering is done.
576 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
578 Display *dpy = DisplayOfScreen (screen);
579 unsigned long map[4097];
584 if (image->depth == 8)
586 else if (image->depth == 12)
591 memset(map, -1, sizeof(*map));
592 memset(colors, -1, sizeof(*colors));
594 for (i = 0; i < cells; i++)
596 XQueryColors (dpy, cmap, colors, cells);
599 fprintf(stderr, "%s: building color cube for %d bit image\n",
600 progname, image->depth);
602 for (i = 0; i < cells; i++)
604 unsigned short r, g, b;
608 /* "RRR GGG BB" In an 8 bit map. Convert that to
609 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
615 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
616 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
617 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
618 (b << 6) | (b << 4) | (b << 2) | b);
622 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
623 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
626 g = (i & 0x0F0) >> 4;
627 b = (i & 0xF00) >> 8;
629 r = (r << 12) | (r << 8) | (r << 4) | r;
630 g = (g << 12) | (g << 8) | (g << 4) | g;
631 b = (b << 12) | (b << 8) | (b << 4) | b;
634 map[i] = find_closest_pixel (colors, cells, r, g, b);
638 fprintf(stderr, "%s: remapping colors in %d bit image\n",
639 progname, image->depth);
641 for (y = 0; y < image->height; y++)
642 for (x = 0; x < image->width; x++)
644 unsigned long pixel = XGetPixel(image, x, y);
645 if (pixel >= cells) abort();
646 XPutPixel(image, x, y, map[pixel]);
651 /* If the file has a PPM (P6) on it, read it and return an XImage.
652 Otherwise, rewind the fd back to the beginning, and return 0.
655 maybe_read_ppm (Screen *screen, Visual *visual,
656 const char *filename, FILE *in, Bool verbose_p)
658 Display *dpy = DisplayOfScreen (screen);
659 int depth = visual_depth (screen, visual);
665 int x, y, w, h, maxval;
668 if (fstat (fileno (in), &st))
672 buf = (char *) malloc (bufsiz + 1);
675 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
676 progname, bufsiz, filename);
680 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
683 if (!strncmp (buf, "\107\111", 2))
685 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
686 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
690 else if (!strncmp (buf, "\211\120", 2))
692 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
693 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
698 if (strncmp (s, "P6", 2))
701 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
703 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
705 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
709 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
711 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
713 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
718 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
719 progname, filename, maxval);
723 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
726 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
727 if (!ximage || !ximage->data)
729 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
730 progname, ximage->width, ximage->height, filename);
736 while ((i = fread (s, 1, j, in)) > 0)
740 for (y = 0; y < ximage->height; y++)
741 for (x = 0; x < ximage->width; x++)
743 unsigned char r = buf[i++];
744 unsigned char g = buf[i++];
745 unsigned char b = buf[i++];
749 pixel = (r << 16) | (g << 8) | b;
751 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
752 else if (depth == 12)
753 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
754 else if (depth == 16 || depth == 15)
755 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
759 XPutPixel (ximage, x, y, pixel);
767 if (ximage && ximage->data)
772 if (ximage) XDestroyImage (ximage);
773 fseek (in, 0, SEEK_SET);
779 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
780 const char *filename;
785 } getimg_jpg_error_mgr;
789 jpg_output_message (j_common_ptr cinfo)
791 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
792 char buf[JMSG_LENGTH_MAX];
793 cinfo->err->format_message (cinfo, buf);
794 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
799 jpg_error_exit (j_common_ptr cinfo)
801 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
802 cinfo->err->output_message (cinfo);
803 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
805 XSync (DisplayOfScreen (err->screen), False);
810 /* Reads a JPEG file, returns an RGB XImage of it.
813 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
814 Colormap cmap, const char *filename, Bool verbose_p)
816 Display *dpy = DisplayOfScreen (screen);
817 int depth = visual_depth (screen, visual);
821 struct jpeg_decompress_struct cinfo;
822 getimg_jpg_error_mgr jerr;
823 JSAMPARRAY scanbuf = 0;
826 jerr.filename = filename;
827 jerr.screen = screen;
828 jerr.visual = visual;
829 jerr.drawable = drawable;
832 if (! (depth >= 15 || depth == 12 || depth == 8))
834 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
838 in = fopen (filename, "rb");
841 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
845 /* Check to see if it's a PPM, and if so, read that instead of using
846 the JPEG library. Yeah, this is all modular and stuff.
848 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
854 cinfo.err = jpeg_std_error (&jerr.pub);
855 jerr.pub.output_message = jpg_output_message;
856 jerr.pub.error_exit = jpg_error_exit;
858 jpeg_create_decompress (&cinfo);
859 jpeg_stdio_src (&cinfo, in);
860 jpeg_read_header (&cinfo, TRUE);
862 /* set some decode parameters */
863 cinfo.out_color_space = JCS_RGB;
864 cinfo.quantize_colors = FALSE;
866 jpeg_start_decompress (&cinfo);
868 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
869 cinfo.output_width, cinfo.output_height,
872 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
874 if (ximage && ximage->data)
875 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
876 cinfo.rec_outbuf_height *
878 cinfo.output_components,
880 if (!ximage || !ximage->data || !scanbuf)
882 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
883 progname, ximage->width, ximage->height, filename);
888 while (cinfo.output_scanline < cinfo.output_height)
890 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
892 for (i = 0; i < n; i++)
895 for (x = 0; x < ximage->width; x++)
897 int j = x * cinfo.output_components;
898 unsigned char r = scanbuf[i][j];
899 unsigned char g = scanbuf[i][j+1];
900 unsigned char b = scanbuf[i][j+2];
904 pixel = (r << 16) | (g << 8) | b;
906 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
907 else if (depth == 12)
908 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
909 else if (depth == 15)
910 /* Gah! I don't understand why these are in the other
912 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
913 else if (depth == 16)
914 pixel = (((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3)));
918 XPutPixel (ximage, x, y, pixel);
924 if (cinfo.output_scanline < cinfo.output_height)
925 /* don't goto FAIL -- we might have viewable partial data. */
926 jpeg_abort_decompress (&cinfo);
928 jpeg_finish_decompress (&cinfo);
930 jpeg_destroy_decompress (&cinfo);
938 if (ximage && ximage->data)
943 if (ximage) XDestroyImage (ximage);
944 if (scanbuf) free (scanbuf);
949 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
950 Returns False if it fails.
953 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
954 const char *filename, Bool verbose_p)
956 Display *dpy = DisplayOfScreen (screen);
961 unsigned int win_width, win_height, win_depth;
962 int srcx, srcy, destx, desty, w2, h2;
964 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
969 XWindowAttributes xgwa;
970 XGetWindowAttributes (dpy, window, &xgwa);
971 visual = xgwa.visual;
972 cmap = xgwa.colormap;
973 XGetGeometry (dpy, drawable,
974 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
977 /* Make sure we're not on some weirdo visual...
979 class = visual_class (screen, visual);
980 depth = visual_depth (screen, visual);
981 if ((class == PseudoColor || class == DirectColor) &&
982 (depth != 8 && depth != 12))
984 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
991 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
992 filename, verbose_p);
993 if (!ximage) return False;
995 /* Scale it, if necessary...
997 compute_image_scaling (ximage->width, ximage->height,
998 win_width, win_height, verbose_p,
999 &srcx, &srcy, &destx, &desty, &w2, &h2);
1000 if (ximage->width != w2 || ximage->height != h2)
1001 if (! scale_ximage (screen, visual, ximage, w2, h2))
1004 /* Allocate a colormap, if we need to...
1006 if (class == PseudoColor || class == DirectColor)
1008 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
1009 remap_image (screen, cmap, ximage, verbose_p);
1012 /* Finally, put the resized image on the window.
1018 /* If we're rendering onto the root window (and it's not the xscreensaver
1019 pseudo-root) then put the image in the window's background. Otherwise,
1020 just paint the image onto the window.
1022 if (window == drawable && root_window_p (screen, window))
1024 Pixmap bg = XCreatePixmap (dpy, window,
1025 win_width, win_height, win_depth);
1026 gcv.foreground = BlackPixelOfScreen (screen);
1027 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1028 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1029 XPutImage (dpy, bg, gc, ximage,
1030 srcx, srcy, destx, desty, ximage->width, ximage->height);
1031 XSetWindowBackgroundPixmap (dpy, window, bg);
1032 XClearWindow (dpy, window);
1036 gc = XCreateGC (dpy, drawable, 0, &gcv);
1037 clear_drawable (screen, drawable);
1038 XPutImage (dpy, drawable, gc, ximage,
1039 srcx, srcy, destx, desty, ximage->width, ximage->height);
1045 free (ximage->data);
1047 XDestroyImage (ximage);
1052 #endif /* HAVE_JPEGLIB */
1055 /* Reads the given image file and renders it on the Drawable.
1056 Returns False if it fails.
1059 display_file (Screen *screen, Window window, Drawable drawable,
1060 const char *filename, Bool verbose_p)
1063 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1065 # if defined(HAVE_GDK_PIXBUF)
1066 if (read_file_gdk (screen, window, drawable, filename, verbose_p))
1068 # elif defined(HAVE_JPEGLIB)
1069 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p))
1071 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1072 /* shouldn't get here if we have no image-loading methods available. */
1074 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1080 /* Invokes a sub-process and returns its output (presumably, a file to
1081 load.) Free the string when done. video_p controls which program
1085 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1088 Display *dpy = DisplayOfScreen (screen);
1099 av[ac++] = GETIMAGE_FILE_PROGRAM;
1101 av[ac++] = "--verbose";
1102 av[ac++] = "--name";
1103 av[ac++] = (char *) directory;
1107 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1109 av[ac++] = "--verbose";
1110 av[ac++] = "--name";
1113 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1115 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1117 av[ac++] = "--verbose";
1118 av[ac++] = "--name";
1130 fprintf (stderr, "%s: executing:", progname);
1131 for (i = 0; i < ac; i++)
1132 fprintf (stderr, " %s", av[i]);
1133 fprintf (stderr, "\n");
1138 sprintf (buf, "%s: error creating pipe", progname);
1146 switch ((int) (forked = fork ()))
1150 sprintf (buf, "%s: couldn't fork", progname);
1158 close (in); /* don't need this one */
1159 close (ConnectionNumber (dpy)); /* close display fd */
1161 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1163 sprintf (buf, "%s: could not dup() a new stdout", progname);
1164 exit (-1); /* exits fork */
1167 execvp (av[0], av); /* shouldn't return. */
1168 exit (-1); /* exits fork */
1174 int wait_status = 0;
1175 FILE *f = fdopen (in, "r");
1178 close (out); /* don't need this one */
1180 fgets (buf, sizeof(buf)-1, f);
1183 /* Wait for the child to die. */
1184 waitpid (-1, &wait_status, 0);
1187 while (L && buf[L-1] == '\n')
1194 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1199 return strdup (buf);
1207 /* Returns a pathname to an image file. Free the string when you're done.
1210 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1212 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1216 /* Grabs a video frame to a file, and returns a pathname to that file.
1217 Delete that file when you are done with it (and free the string.)
1220 get_video_filename (Screen *screen, Bool verbose_p)
1222 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1225 /* Grabs a desktop image to a file, and returns a pathname to that file.
1226 Delete that file when you are done with it (and free the string.)
1228 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1230 get_desktop_filename (Screen *screen, Bool verbose_p)
1232 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1234 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1237 /* Grabs a video frame, and renders it on the Drawable.
1238 Returns False if it fails;
1241 display_video (Screen *screen, Window window, Drawable drawable,
1244 char *filename = get_video_filename (screen, verbose_p);
1250 fprintf (stderr, "%s: video grab failed.\n", progname);
1254 status = display_file (screen, window, drawable, filename, verbose_p);
1256 if (unlink (filename))
1259 sprintf (buf, "%s: rm %.100s", progname, filename);
1263 fprintf (stderr, "%s: rm %s\n", progname, filename);
1265 if (filename) free (filename);
1270 /* Grabs a desktop screen shot onto the window and the drawable.
1271 If the window and drawable are not the same size, the image in
1272 the drawable is scaled to fit.
1273 Returns False if it fails.
1276 display_desktop (Screen *screen, Window window, Drawable drawable,
1279 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1281 Display *dpy = DisplayOfScreen (screen);
1282 Bool top_p = top_level_window_p (screen, window);
1289 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1290 (unsigned long) window);
1291 XUnmapWindow (dpy, window);
1295 filename = get_desktop_filename (screen, verbose_p);
1300 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1301 (unsigned long) window);
1302 XMapRaised (dpy, window);
1309 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1313 status = display_file (screen, window, drawable, filename, verbose_p);
1315 if (unlink (filename))
1318 sprintf (buf, "%s: rm %.100s", progname, filename);
1322 fprintf (stderr, "%s: rm %s\n", progname, filename);
1324 if (filename) free (filename);
1327 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1329 Display *dpy = DisplayOfScreen (screen);
1331 XWindowAttributes xgwa;
1334 unsigned int pw, ph, pbw, pd;
1335 int srcx, srcy, destx, desty, w2, h2;
1339 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1340 grabscreen_verbose();
1343 XGetWindowAttributes (dpy, window, &xgwa);
1344 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1346 grab_screen_image_internal (screen, window);
1348 compute_image_scaling (xgwa.width, xgwa.height,
1350 &srcx, &srcy, &destx, &desty, &w2, &h2);
1352 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1354 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1355 XCopyArea (dpy, window, drawable, gc,
1356 0, 0, xgwa.width, xgwa.height, 0, 0);
1359 else /* size mismatch -- must scale client-side images to fit drawable */
1363 XErrorHandler old_handler;
1366 old_handler = XSetErrorHandler (ignore_badmatch_ehandler);
1367 error_handler_hit_p = False;
1369 /* This can return BadMatch if the window is not fully on screen.
1370 Trap that error and return color bars in that case.
1371 (Note that this only happens with XGetImage, not with XCopyArea:
1372 yet another totally gratuitous inconsistency in X, thanks.)
1374 ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1378 XSetErrorHandler (old_handler);
1381 if (error_handler_hit_p)
1385 fprintf (stderr, "%s: BadMatch reading window 0x%x contents!\n",
1386 progname, (unsigned int) window);
1390 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1393 gc = XCreateGC (dpy, drawable, 0, &gcv);
1394 clear_drawable (screen, drawable);
1395 XPutImage (dpy, drawable, gc, ximage,
1396 srcx, srcy, destx, desty, ximage->width, ximage->height);
1397 XDestroyImage (ximage);
1404 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1408 /* Grabs an image (from a file, video, or the desktop) and renders it on
1409 the Drawable. If `file' is specified, always use that file. Otherwise,
1410 select randomly, based on the other arguments.
1413 get_image (Screen *screen,
1414 Window window, Drawable drawable,
1422 Display *dpy = DisplayOfScreen (screen);
1423 grab_type which = GRAB_BARS;
1426 const char *file_prop = 0;
1428 if (! drawable_window_p (dpy, window))
1430 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1431 progname, (unsigned long) window);
1435 /* Make sure the Screen and the Window correspond. */
1437 XWindowAttributes xgwa;
1438 XGetWindowAttributes (dpy, window, &xgwa);
1439 screen = xgwa.screen;
1442 if (file && stat (file, &st))
1444 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1450 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1451 progname, desk_p ? "True" : "False");
1452 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1453 progname, video_p ? "True" : "False");
1454 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1455 progname, image_p ? "True" : "False");
1456 fprintf (stderr, "%s: imageDirectory: %s\n",
1457 progname, (file ? file : dir ? dir : ""));
1460 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1461 image_p = False; /* can't load images from files... */
1462 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1463 desk_p = False; /* ...or from desktops grabbed to files. */
1469 "%s: image file loading not available at compile-time\n",
1471 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1474 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1482 else if (!dir || !*dir)
1484 if (verbose_p && image_p)
1486 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1493 # error Error! This file definitely needs vroot.h!
1496 /* We can grab desktop images (using the normal X11 method) if:
1497 - the window is the real root window;
1498 - the window is a toplevel window.
1499 We cannot grab desktop images that way if:
1500 - the window is a non-top-level window.
1502 Using the MacOS X way, desktops are just like loaded image files.
1504 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1507 if (!top_level_window_p (screen, window))
1512 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1513 progname, (unsigned int) window);
1516 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1519 if (desk_p) count++;
1520 if (video_p) count++;
1521 if (image_p) count++;
1528 while (1) /* loop until we get one that's permitted */
1530 which = (random() % 3);
1531 if (which == GRAB_DESK && desk_p) break;
1532 if (which == GRAB_VIDEO && video_p) break;
1533 if (which == GRAB_FILE && image_p) break;
1534 if (++i > 200) abort();
1539 /* If we're to search a directory to find an image file, do so now.
1541 if (which == GRAB_FILE && !file)
1543 file = get_filename (screen, dir, verbose_p);
1548 fprintf (stderr, "%s: no image files found.\n", progname);
1552 /* Now actually render something.
1558 XWindowAttributes xgwa;
1561 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1562 XGetWindowAttributes (dpy, window, &xgwa);
1563 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1570 if (! display_desktop (screen, window, drawable, verbose_p))
1572 file_prop = "desktop";
1576 if (! display_file (screen, window, drawable, file, verbose_p))
1582 if (! display_video (screen, window, drawable, verbose_p))
1584 file_prop = "video";
1593 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1594 if (file_prop && *file_prop)
1595 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1596 (unsigned char *) file_prop, strlen(file_prop));
1598 XDeleteProperty (dpy, window, a);
1607 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1608 XrmRepresentation *type, XrmValue *value, XPointer closure)
1611 for (i = 0; quarks[i]; i++)
1613 if (bindings[i] == XrmBindTightly)
1614 fprintf (stderr, (i == 0 ? "" : "."));
1615 else if (bindings[i] == XrmBindLoosely)
1616 fprintf (stderr, "*");
1618 fprintf (stderr, " ??? ");
1619 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1622 fprintf (stderr, ": %s\n", (char *) value->addr);
1629 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1633 " %s puts an image on the given window or pixmap.\n" \
1635 " It is used by those xscreensaver demos that operate on images.\n" \
1636 " The image may be a file loaded from disk, a frame grabbed from\n" \
1637 " the system's video camera, or a screenshot of the desktop,\n" \
1638 " depending on command-line options or the ~/.xscreensaver file.\n" \
1640 " Options include:\n" \
1642 " -display host:dpy.screen which display to use\n" \
1643 " -root draw to the root window\n" \
1644 " -verbose print diagnostics\n" \
1645 " -images / -no-images whether to allow image file loading\n" \
1646 " -video / -no-video whether to allow video grabs\n" \
1647 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1648 " -directory <path> where to find image files to load\n" \
1649 " -file <filename> load this image file\n" \
1651 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1652 " defaults for these options in your ~/.xscreensaver file.\n" \
1656 main (int argc, char **argv)
1658 saver_preferences P;
1662 char *oprogname = progname;
1666 Window window = (Window) 0;
1667 Drawable drawable = (Drawable) 0;
1668 const char *window_str = 0;
1669 const char *drawable_str = 0;
1674 s = strrchr (progname, '/');
1675 if (s) progname = s+1;
1676 oprogname = progname;
1678 /* half-assed way of avoiding buffer-overrun attacks. */
1679 if (strlen (progname) >= 100) progname[100] = 0;
1682 # error Error! This file definitely needs vroot.h!
1685 /* Get the version number, for error messages. */
1687 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1688 char *s1, *s2, *s3, *s4;
1689 s1 = (char *) strchr(v, ' '); s1++;
1690 s2 = (char *) strchr(s1, ' ');
1691 s3 = (char *) strchr(v, '('); s3++;
1692 s4 = (char *) strchr(s3, ')');
1695 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1699 /* We must read exactly the same resources as xscreensaver.
1700 That means we must have both the same progclass *and* progname,
1701 at least as far as the resource database is concerned. So,
1702 put "xscreensaver" in argv[0] while initializing Xt.
1704 progname = argv[0] = "xscreensaver";
1706 /* allow one dash or two. */
1707 for (i = 1; i < argc; i++)
1708 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1710 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1712 dpy = XtDisplay (toplevel);
1713 screen = XtScreen (toplevel);
1714 db = XtDatabase (dpy);
1715 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1716 XSetErrorHandler (x_ehandler);
1719 /* Randomize -- only need to do this here because this program
1720 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1721 # undef ya_rand_init
1724 memset (&P, 0, sizeof(P));
1726 load_init_file (&P);
1728 progname = argv[0] = oprogname;
1730 for (i = 1; i < argc; i++)
1735 /* Have to re-process these, or else the .xscreensaver file
1736 has priority over the command line...
1738 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1740 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1741 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1742 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1743 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1744 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1745 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1746 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1747 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1748 P.image_directory = argv[++i];
1749 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1753 fprintf (stderr, "%s: both %s and %s specified?\n",
1754 progname, argv[i], window_str);
1757 window_str = argv[i];
1758 window = VirtualRootWindowOfScreen (screen);
1760 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1761 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1766 fprintf (stderr, "%s: both %s and %s specified?\n",
1767 progname, drawable_str, argv[i]);
1772 drawable_str = argv[i];
1773 drawable = (Drawable) w;
1777 window_str = argv[i];
1778 window = (Window) w;
1783 if (argv[i][0] == '-')
1784 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1787 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1791 __extension__ /* don't warn about "string length is greater than
1792 the length ISO C89 compilers are required to
1793 support" in the usage string... */
1795 fprintf (stderr, USAGE, progname, version, progname);
1802 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1808 if (P.verbose_p) /* Print out all the resources we can see. */
1810 XrmName name = { 0 };
1811 XrmClass class = { 0 };
1813 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1814 (XtPointer) &count);
1818 if (!window) abort();
1819 if (!drawable) drawable = window;
1821 get_image (screen, window, drawable, P.verbose_p,
1822 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1823 P.image_directory, file);