1 /* xscreensaver, Copyright (c) 2001, 2002, 2003 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 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
143 drawable_window_p (Display *dpy, Drawable d)
145 XErrorHandler old_handler;
146 XWindowAttributes xgwa;
149 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
150 error_handler_hit_p = False;
151 XGetWindowAttributes (dpy, d, &xgwa);
153 XSetErrorHandler (old_handler);
156 if (!error_handler_hit_p)
157 return True; /* It's a Window. */
159 return False; /* It's a Pixmap, or an invalid ID. */
163 /* Returns true if the window is the root window, or a virtual root window,
164 but *not* the xscreensaver window. That is, if it's a "real" desktop
165 root window of some kind.
168 root_window_p (Screen *screen, Window window)
170 Display *dpy = DisplayOfScreen (screen);
173 unsigned long nitems, bytesafter;
176 if (window != RootWindowOfScreen (screen))
179 if (XGetWindowProperty (dpy, window,
180 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
181 0, 1, False, XA_STRING,
182 &type, &format, &nitems, &bytesafter,
183 (unsigned char **) &version)
192 /* Clear the window or pixmap to black, or its background color.
195 clear_drawable (Screen *screen, Drawable drawable)
197 Display *dpy = DisplayOfScreen (screen);
202 unsigned int w, h, bw, d;
203 XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &d);
205 /* The window might have no-op background of None, so to clear it,
206 draw a black rectangle first, then do XClearWindow (in case the
207 actual background color is non-black...) */
209 /* #### really we should allocate "black" instead, but I'm lazy... */
210 gcv.foreground = BlackPixelOfScreen (screen);
211 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
212 XFillRectangle (dpy, drawable, gc, 0, 0, w, h);
214 if (drawable_window_p (dpy, drawable))
215 XClearWindow (dpy, (Window) drawable);
220 /* Figure out what kind of scaling/positioning we ought to do to display
221 a src-sized image in a dest-sized window/pixmap. Returns the width
222 and height to which the image should be scaled, and the position where
223 it should be displayed to center it.
226 compute_image_scaling (int src_w, int src_h,
227 int dest_w, int dest_h,
229 int *scaled_from_x_ret, int *scaled_from_y_ret,
230 int *scaled_to_x_ret, int *scaled_to_y_ret,
231 int *scaled_w_ret, int *scaled_h_ret)
233 int srcx, srcy, destx, desty;
235 Bool exact_fit_p = ((src_w == dest_w && src_h <= dest_h) ||
236 (src_h == dest_h && src_w <= dest_w));
238 if (!exact_fit_p) /* scale the image up or down */
240 float rw = (float) dest_w / src_w;
241 float rh = (float) dest_h / src_h;
242 float r = (rw < rh ? rw : rh);
248 /* this optimization breaks things */
249 if (pct < 95 || pct > 105) /* don't scale if it's close */
253 fprintf (stderr, "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
254 progname, pct, src_w, src_h, tw, th);
260 /* Center the image on the window/pixmap. */
263 destx = (dest_w - src_w) / 2;
264 desty = (dest_h - src_h) / 2;
265 if (destx < 0) srcx = -destx, destx = 0;
266 if (desty < 0) srcy = -desty, desty = 0;
268 if (dest_w < src_w) src_w = dest_w;
269 if (dest_h < src_h) src_h = dest_h;
271 *scaled_w_ret = src_w;
272 *scaled_h_ret = src_h;
273 *scaled_from_x_ret = srcx;
274 *scaled_from_y_ret = srcy;
275 *scaled_to_x_ret = destx;
276 *scaled_to_y_ret = desty;
279 fprintf (stderr, "%s: displaying %dx%d image at %d,%d.\n",
280 progname, src_w, src_h, destx, desty);
284 /* Scales an XImage, modifying it in place.
285 This doesn't do dithering or smoothing, so it might have artifacts.
286 If out of memory, returns False, and the XImage will have been
290 scale_ximage (Screen *screen, Visual *visual,
291 XImage *ximage, int new_width, int new_height)
293 Display *dpy = DisplayOfScreen (screen);
294 int depth = visual_depth (screen, visual);
296 double xscale, yscale;
298 XImage *ximage2 = XCreateImage (dpy, visual, depth,
300 new_width, new_height, 8, 0);
301 ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
305 fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
307 ximage->width, ximage->height,
308 ximage2->width, ximage2->height);
309 if (ximage->data) free (ximage->data);
310 if (ximage2->data) free (ximage2->data);
313 XDestroyImage (ximage);
314 XDestroyImage (ximage2);
318 /* Brute force scaling... */
319 xscale = (double) ximage->width / ximage2->width;
320 yscale = (double) ximage->height / ximage2->height;
321 for (y = 0; y < ximage2->height; y++)
322 for (x = 0; x < ximage2->width; x++)
323 XPutPixel (ximage2, x, y,
324 XGetPixel (ximage, x * xscale, y * yscale));
329 (*ximage) = (*ximage2);
332 XDestroyImage (ximage2);
338 #ifdef HAVE_GDK_PIXBUF
340 /* Reads the given image file and renders it on the Drawable, using GDK.
341 Returns False if it fails.
344 read_file_gdk (Screen *screen, Window window, Drawable drawable,
345 const char *filename, Bool verbose_p)
348 Display *dpy = DisplayOfScreen (screen);
349 unsigned int win_width, win_height, win_depth;
352 # endif /* HAVE_GTK2 */
354 /* Find the size of the Drawable. */
359 XGetGeometry (dpy, drawable,
360 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
363 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
366 # else /* !HAVE_GTK2 */
367 xlib_rgb_init (dpy, screen);
368 # endif /* !HAVE_GTK2 */
370 pb = gdk_pixbuf_new_from_file (filename
373 # endif /* HAVE_GTK2 */
378 fprintf (stderr, "%s: unable to load \"%s\"\n", progname, filename);
380 if (gerr && gerr->message && *gerr->message)
381 fprintf (stderr, "%s: reason: %s\n", progname, gerr->message);
382 # endif /* HAVE_GTK2 */
387 int w = gdk_pixbuf_get_width (pb);
388 int h = gdk_pixbuf_get_height (pb);
389 int srcx, srcy, destx, desty, w2, h2;
392 compute_image_scaling (w, h, win_width, win_height, verbose_p,
393 &srcx, &srcy, &destx, &desty, &w2, &h2);
394 if (w != w2 || h != h2)
396 GdkPixbuf *pb2 = gdk_pixbuf_scale_simple (pb, w2, h2,
397 GDK_INTERP_BILINEAR);
400 gdk_pixbuf_unref (pb);
406 fprintf (stderr, "%s: out of memory when scaling?\n", progname);
409 /* If we're rendering onto the root window (and it's not the
410 xscreensaver pseudo-root) then put the image in the window's
411 background. Otherwise, just paint the image onto the window.
413 bg_p = (window == drawable && root_window_p (screen, window));
419 drawable = XCreatePixmap (dpy, window,
420 win_width, win_height, win_depth);
421 gcv.foreground = BlackPixelOfScreen (screen);
422 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
423 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
427 clear_drawable (screen, drawable);
429 /* #### Note that this always uses the default colormap! Morons!
430 Owen says that in Gnome 2.0, I should try using
431 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
434 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, drawable,
435 srcx, srcy, destx, desty,
437 GDK_PIXBUF_ALPHA_FULL, 127,
438 XLIB_RGB_DITHER_NORMAL,
442 XSetWindowBackgroundPixmap (dpy, window, drawable);
443 XClearWindow (dpy, window);
451 #endif /* HAVE_GDK_PIXBUF */
457 /* Allocates a colormap that makes a PseudoColor or DirectColor
458 visual behave like a TrueColor visual of the same depth.
461 allocate_cubic_colormap (Screen *screen, Visual *visual, Colormap cmap,
464 Display *dpy = DisplayOfScreen (screen);
465 int nr, ng, nb, cells;
471 depth = visual_depth (screen, visual);
475 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
476 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
477 default: abort(); break;
480 memset(colors, 0, sizeof(colors));
481 for (r = 0; r < (1 << nr); r++)
482 for (g = 0; g < (1 << ng); g++)
483 for (b = 0; b < (1 << nb); b++)
485 i = (r | (g << nr) | (b << (nr + ng)));
487 colors[i].flags = DoRed|DoGreen|DoBlue;
490 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
491 (r << 4) | (r << 1));
492 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
493 (g << 4) | (g << 1));
494 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
495 (b << 8) | (b << 6) | (b << 4) |
500 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
501 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
502 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
509 int interleave = cells / 8; /* skip around, rather than allocating in
510 order, so that we get better coverage if
511 we can't allocated all of them. */
512 for (j = 0; j < interleave; j++)
513 for (i = 0; i < cells; i += interleave)
514 if (XAllocColor (dpy, cmap, &colors[i + j]))
518 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
519 progname, allocated, cells);
523 /* Find the pixel index that is closest to the given color
524 (using linear distance in RGB space -- which is far from the best way.)
527 find_closest_pixel (XColor *colors, int ncolors,
528 unsigned long r, unsigned long g, unsigned long b)
530 unsigned long distance = ~0;
535 for (i = 0; i < ncolors; i++)
540 rd = r - colors[i].red;
541 gd = g - colors[i].green;
542 bd = b - colors[i].blue;
543 if (rd < 0) rd = -rd;
544 if (gd < 0) gd = -gd;
545 if (bd < 0) bd = -bd;
546 d = (rd << 1) + (gd << 2) + bd;
561 /* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
562 displayable with the given X colormap. The farther from a perfect
563 color cube the contents of the colormap are, the lossier the
564 transformation will be. No dithering is done.
567 remap_image (Screen *screen, Colormap cmap, XImage *image, Bool verbose_p)
569 Display *dpy = DisplayOfScreen (screen);
570 unsigned long map[4097];
575 if (image->depth == 8)
577 else if (image->depth == 12)
582 memset(map, -1, sizeof(*map));
583 memset(colors, -1, sizeof(*colors));
585 for (i = 0; i < cells; i++)
587 XQueryColors (dpy, cmap, colors, cells);
590 fprintf(stderr, "%s: building color cube for %d bit image\n",
591 progname, image->depth);
593 for (i = 0; i < cells; i++)
595 unsigned short r, g, b;
599 /* "RRR GGG BB" In an 8 bit map. Convert that to
600 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
606 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
607 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
608 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
609 (b << 6) | (b << 4) | (b << 2) | b);
613 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
614 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
617 g = (i & 0x0F0) >> 4;
618 b = (i & 0xF00) >> 8;
620 r = (r << 12) | (r << 8) | (r << 4) | r;
621 g = (g << 12) | (g << 8) | (g << 4) | g;
622 b = (b << 12) | (b << 8) | (b << 4) | b;
625 map[i] = find_closest_pixel (colors, cells, r, g, b);
629 fprintf(stderr, "%s: remapping colors in %d bit image\n",
630 progname, image->depth);
632 for (y = 0; y < image->height; y++)
633 for (x = 0; x < image->width; x++)
635 unsigned long pixel = XGetPixel(image, x, y);
636 if (pixel >= cells) abort();
637 XPutPixel(image, x, y, map[pixel]);
642 /* If the file has a PPM (P6) on it, read it and return an XImage.
643 Otherwise, rewind the fd back to the beginning, and return 0.
646 maybe_read_ppm (Screen *screen, Visual *visual,
647 const char *filename, FILE *in, Bool verbose_p)
649 Display *dpy = DisplayOfScreen (screen);
650 int depth = visual_depth (screen, visual);
656 int x, y, w, h, maxval;
659 if (fstat (fileno (in), &st))
663 buf = (char *) malloc (bufsiz + 1);
666 fprintf (stderr, "%s: out of memory loading %d byte PPM file %s\n",
667 progname, bufsiz, filename);
671 if (! (s = fgets (buf, bufsiz, in))) /* line 1 */
674 if (!strncmp (buf, "\107\111", 2))
676 fprintf (stderr, "%s: %s: sorry, GIF files not supported"
677 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
681 else if (!strncmp (buf, "\211\120", 2))
683 fprintf (stderr, "%s: %s: sorry, PNG files not supported"
684 " when compiled with JPEGlib instead of GDK_Pixbuf.\n",
689 if (strncmp (s, "P6", 2))
692 if (! (s = fgets (buf, bufsiz, in))) /* line 2 */
694 if (2 != sscanf (s, " %d %d %c", &w, &h, &dummy))
696 fprintf (stderr, "%s: %s: invalid PPM (line 2)\n", progname, filename);
700 if (! (s = fgets (buf, bufsiz, in))) /* line 3 */
702 if (1 != sscanf (s, " %d %c", &maxval, &dummy))
704 fprintf (stderr, "%s: %s: invalid PPM (line 3)\n", progname, filename);
709 fprintf (stderr, "%s: %s: unparsable PPM: maxval is %d\n",
710 progname, filename, maxval);
714 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
717 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
718 if (!ximage || !ximage->data)
720 fprintf (stderr, "%s: out of memory loading %dx%d PPM file %s\n",
721 progname, ximage->width, ximage->height, filename);
727 while ((i = fread (s, 1, j, in)) > 0)
731 for (y = 0; y < ximage->height; y++)
732 for (x = 0; x < ximage->width; x++)
734 unsigned char r = buf[i++];
735 unsigned char g = buf[i++];
736 unsigned char b = buf[i++];
740 pixel = (r << 16) | (g << 8) | b;
742 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
743 else if (depth == 12)
744 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
745 else if (depth == 16 || depth == 15)
746 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
750 XPutPixel (ximage, x, y, pixel);
758 if (ximage && ximage->data)
763 if (ximage) XDestroyImage (ximage);
764 fseek (in, 0, SEEK_SET);
770 struct jpeg_error_mgr pub; /* this is what passes for subclassing in C */
771 const char *filename;
776 } getimg_jpg_error_mgr;
780 jpg_output_message (j_common_ptr cinfo)
782 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
783 char buf[JMSG_LENGTH_MAX];
784 cinfo->err->format_message (cinfo, buf);
785 fprintf (stderr, "%s: %s: %s\n", progname, err->filename, buf);
790 jpg_error_exit (j_common_ptr cinfo)
792 getimg_jpg_error_mgr *err = (getimg_jpg_error_mgr *) cinfo->err;
793 cinfo->err->output_message (cinfo);
794 draw_colorbars (err->screen, err->visual, err->drawable, err->cmap,
796 XSync (DisplayOfScreen (err->screen), False);
801 /* Reads a JPEG file, returns an RGB XImage of it.
804 read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
805 Colormap cmap, const char *filename, Bool verbose_p)
807 Display *dpy = DisplayOfScreen (screen);
808 int depth = visual_depth (screen, visual);
812 struct jpeg_decompress_struct cinfo;
813 getimg_jpg_error_mgr jerr;
814 JSAMPARRAY scanbuf = 0;
817 jerr.filename = filename;
818 jerr.screen = screen;
819 jerr.visual = visual;
820 jerr.drawable = drawable;
823 if (! (depth >= 15 || depth == 12 || depth == 8))
825 fprintf (stderr, "%s: unsupported depth: %d\n", progname, depth);
829 in = fopen (filename, "rb");
832 fprintf (stderr, "%s: %s: unreadable\n", progname, filename);
836 /* Check to see if it's a PPM, and if so, read that instead of using
837 the JPEG library. Yeah, this is all modular and stuff.
839 if ((ximage = maybe_read_ppm (screen, visual, filename, in, verbose_p)))
845 cinfo.err = jpeg_std_error (&jerr.pub);
846 jerr.pub.output_message = jpg_output_message;
847 jerr.pub.error_exit = jpg_error_exit;
849 jpeg_create_decompress (&cinfo);
850 jpeg_stdio_src (&cinfo, in);
851 jpeg_read_header (&cinfo, TRUE);
853 /* set some decode parameters */
854 cinfo.out_color_space = JCS_RGB;
855 cinfo.quantize_colors = FALSE;
857 jpeg_start_decompress (&cinfo);
859 ximage = XCreateImage (dpy, visual, depth, ZPixmap, 0, 0,
860 cinfo.output_width, cinfo.output_height,
863 ximage->data = (char *) calloc (ximage->height, ximage->bytes_per_line);
865 if (ximage && ximage->data)
866 scanbuf = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
867 cinfo.rec_outbuf_height *
869 cinfo.output_components,
871 if (!ximage || !ximage->data || !scanbuf)
873 fprintf (stderr, "%s: out of memory loading %dx%d file %s\n",
874 progname, ximage->width, ximage->height, filename);
879 while (cinfo.output_scanline < cinfo.output_height)
881 int n = jpeg_read_scanlines (&cinfo, scanbuf, 1);
883 for (i = 0; i < n; i++)
886 for (x = 0; x < ximage->width; x++)
888 int j = x * cinfo.num_components;
889 unsigned char r = scanbuf[i][j];
890 unsigned char g = scanbuf[i][j+1];
891 unsigned char b = scanbuf[i][j+2];
895 pixel = (r << 16) | (g << 8) | b;
897 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
898 else if (depth == 12)
899 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
900 else if (depth == 16 || depth == 15)
901 /* Gah! I don't understand why these are in the other
903 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
907 XPutPixel (ximage, x, y, pixel);
913 if (cinfo.output_scanline < cinfo.output_height)
914 /* don't goto FAIL -- we might have viewable partial data. */
915 jpeg_abort_decompress (&cinfo);
917 jpeg_finish_decompress (&cinfo);
919 jpeg_destroy_decompress (&cinfo);
927 if (ximage && ximage->data)
932 if (ximage) XDestroyImage (ximage);
933 if (scanbuf) free (scanbuf);
938 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
939 Returns False if it fails.
942 read_file_jpeglib (Screen *screen, Window window, Drawable drawable,
943 const char *filename, Bool verbose_p)
945 Display *dpy = DisplayOfScreen (screen);
950 unsigned int win_width, win_height, win_depth;
951 int srcx, srcy, destx, desty, w2, h2;
953 /* Find the size of the Drawable, and the Visual/Colormap of the Window. */
958 XWindowAttributes xgwa;
959 XGetWindowAttributes (dpy, window, &xgwa);
960 visual = xgwa.visual;
961 cmap = xgwa.colormap;
962 XGetGeometry (dpy, drawable,
963 &root, &x, &y, &win_width, &win_height, &bw, &win_depth);
966 /* Make sure we're not on some weirdo visual...
968 class = visual_class (screen, visual);
969 depth = visual_depth (screen, visual);
970 if ((class == PseudoColor || class == DirectColor) &&
971 (depth != 8 && depth != 12))
973 fprintf (stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
980 ximage = read_jpeg_ximage (screen, visual, drawable, cmap,
981 filename, verbose_p);
982 if (!ximage) return False;
984 /* Scale it, if necessary...
986 compute_image_scaling (ximage->width, ximage->height,
987 win_width, win_height, verbose_p,
988 &srcx, &srcy, &destx, &desty, &w2, &h2);
989 if (ximage->width != w2 || ximage->height != h2)
990 if (! scale_ximage (screen, visual, ximage, w2, h2))
993 /* Allocate a colormap, if we need to...
995 if (class == PseudoColor || class == DirectColor)
997 allocate_cubic_colormap (screen, visual, cmap, verbose_p);
998 remap_image (screen, cmap, ximage, verbose_p);
1001 /* Finally, put the resized image on the window.
1007 /* If we're rendering onto the root window (and it's not the xscreensaver
1008 pseudo-root) then put the image in the window's background. Otherwise,
1009 just paint the image onto the window.
1011 if (window == drawable && root_window_p (screen, window))
1013 Pixmap bg = XCreatePixmap (dpy, window,
1014 win_width, win_height, win_depth);
1015 gcv.foreground = BlackPixelOfScreen (screen);
1016 gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
1017 XFillRectangle (dpy, bg, gc, 0, 0, win_width, win_height);
1018 XPutImage (dpy, bg, gc, ximage,
1019 srcx, srcy, destx, desty, ximage->width, ximage->height);
1020 XSetWindowBackgroundPixmap (dpy, window, bg);
1021 XClearWindow (dpy, window);
1025 gc = XCreateGC (dpy, drawable, 0, &gcv);
1026 clear_drawable (screen, drawable);
1027 XPutImage (dpy, drawable, gc, ximage,
1028 srcx, srcy, destx, desty, ximage->width, ximage->height);
1034 free (ximage->data);
1036 XDestroyImage (ximage);
1041 #endif /* HAVE_JPEGLIB */
1044 /* Reads the given image file and renders it on the Drawable.
1045 Returns False if it fails.
1048 display_file (Screen *screen, Window window, Drawable drawable,
1049 const char *filename, Bool verbose_p)
1052 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
1054 # if defined(HAVE_GDK_PIXBUF)
1055 if (read_file_gdk (screen, window, drawable, filename, verbose_p))
1057 # elif defined(HAVE_JPEGLIB)
1058 if (read_file_jpeglib (screen, window, drawable, filename, verbose_p))
1060 # else /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1061 /* shouldn't get here if we have no image-loading methods available. */
1063 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1069 /* Invokes a sub-process and returns its output (presumably, a file to
1070 load.) Free the string when done. video_p controls which program
1074 get_filename_1 (Screen *screen, const char *directory, grab_type type,
1077 Display *dpy = DisplayOfScreen (screen);
1088 av[ac++] = GETIMAGE_FILE_PROGRAM;
1090 av[ac++] = "--verbose";
1091 av[ac++] = "--name";
1092 av[ac++] = (char *) directory;
1096 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
1098 av[ac++] = "--verbose";
1099 av[ac++] = "--name";
1102 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1104 av[ac++] = GETIMAGE_SCREEN_PROGRAM;
1106 av[ac++] = "--verbose";
1107 av[ac++] = "--name";
1119 fprintf (stderr, "%s: executing:", progname);
1120 for (i = 0; i < ac; i++)
1121 fprintf (stderr, " %s", av[i]);
1122 fprintf (stderr, "\n");
1127 sprintf (buf, "%s: error creating pipe", progname);
1135 switch ((int) (forked = fork ()))
1139 sprintf (buf, "%s: couldn't fork", progname);
1147 close (in); /* don't need this one */
1148 close (ConnectionNumber (dpy)); /* close display fd */
1150 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
1152 sprintf (buf, "%s: could not dup() a new stdout", progname);
1153 exit (-1); /* exits fork */
1156 execvp (av[0], av); /* shouldn't return. */
1157 exit (-1); /* exits fork */
1163 int wait_status = 0;
1164 FILE *f = fdopen (in, "r");
1167 close (out); /* don't need this one */
1169 fgets (buf, sizeof(buf)-1, f);
1172 /* Wait for the child to die. */
1173 waitpid (-1, &wait_status, 0);
1176 while (L && buf[L-1] == '\n')
1183 fprintf (stderr, "%s: file does not exist: \"%s\"\n",
1188 return strdup (buf);
1196 /* Returns a pathname to an image file. Free the string when you're done.
1199 get_filename (Screen *screen, const char *directory, Bool verbose_p)
1201 return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
1205 /* Grabs a video frame to a file, and returns a pathname to that file.
1206 Delete that file when you are done with it (and free the string.)
1209 get_video_filename (Screen *screen, Bool verbose_p)
1211 return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
1214 /* Grabs a desktop image to a file, and returns a pathname to that file.
1215 Delete that file when you are done with it (and free the string.)
1217 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1219 get_desktop_filename (Screen *screen, Bool verbose_p)
1221 return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
1223 #endif /* USE_EXTERNAL_SCREEN_GRABBER */
1226 /* Grabs a video frame, and renders it on the Drawable.
1227 Returns False if it fails;
1230 display_video (Screen *screen, Window window, Drawable drawable,
1233 char *filename = get_video_filename (screen, verbose_p);
1239 fprintf (stderr, "%s: video grab failed.\n", progname);
1243 status = display_file (screen, window, drawable, filename, verbose_p);
1245 if (unlink (filename))
1248 sprintf (buf, "%s: rm %.100s", progname, filename);
1252 fprintf (stderr, "%s: rm %s\n", progname, filename);
1254 if (filename) free (filename);
1259 /* Grabs a desktop screen shot onto the window and the drawable.
1260 If the window and drawable are not the same size, the image in
1261 the drawable is scaled to fit.
1262 Returns False if it fails.
1265 display_desktop (Screen *screen, Window window, Drawable drawable,
1268 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1270 Display *dpy = DisplayOfScreen (screen);
1271 Bool top_p = top_level_window_p (screen, window);
1278 fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
1279 (unsigned long) window);
1280 XUnmapWindow (dpy, window);
1284 filename = get_desktop_filename (screen, verbose_p);
1289 fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
1290 (unsigned long) window);
1291 XMapRaised (dpy, window);
1298 fprintf (stderr, "%s: desktop grab failed.\n", progname);
1302 status = display_file (screen, window, drawable, filename, verbose_p);
1304 if (unlink (filename))
1307 sprintf (buf, "%s: rm %.100s", progname, filename);
1311 fprintf (stderr, "%s: rm %s\n", progname, filename);
1313 if (filename) free (filename);
1316 # else /* !USE_EXTERNAL_SCREEN_GRABBER */
1318 Display *dpy = DisplayOfScreen (screen);
1320 XWindowAttributes xgwa;
1323 unsigned int pw, ph, pbw, pd;
1324 int srcx, srcy, destx, desty, w2, h2;
1328 fprintf (stderr, "%s: grabbing desktop image\n", progname);
1329 grabscreen_verbose();
1332 XGetWindowAttributes (dpy, window, &xgwa);
1333 XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
1335 grab_screen_image_internal (screen, window);
1337 compute_image_scaling (xgwa.width, xgwa.height,
1339 &srcx, &srcy, &destx, &desty, &w2, &h2);
1341 if (pw == w2 && ph == h2) /* it fits -- just copy server-side pixmaps */
1343 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
1344 XCopyArea (dpy, window, drawable, gc,
1345 0, 0, xgwa.width, xgwa.height, 0, 0);
1348 else /* size mismatch -- must scale client-side images to fit drawable */
1350 XImage *ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
1354 !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
1356 gc = XCreateGC (dpy, drawable, 0, &gcv);
1357 clear_drawable (screen, drawable);
1358 XPutImage (dpy, drawable, gc, ximage,
1359 srcx, srcy, destx, desty, ximage->width, ximage->height);
1360 XDestroyImage (ximage);
1367 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1371 /* Grabs an image (from a file, video, or the desktop) and renders it on
1372 the Drawable. If `file' is specified, always use that file. Otherwise,
1373 select randomly, based on the other arguments.
1376 get_image (Screen *screen,
1377 Window window, Drawable drawable,
1385 Display *dpy = DisplayOfScreen (screen);
1386 grab_type which = GRAB_BARS;
1389 const char *file_prop = 0;
1391 if (! drawable_window_p (dpy, window))
1393 fprintf (stderr, "%s: 0x%lx is a pixmap, not a window!\n",
1394 progname, (unsigned long) window);
1398 /* Make sure the Screen and the Window correspond. */
1400 XWindowAttributes xgwa;
1401 XGetWindowAttributes (dpy, window, &xgwa);
1402 screen = xgwa.screen;
1405 if (file && stat (file, &st))
1407 fprintf (stderr, "%s: file \"%s\" does not exist\n", progname, file);
1413 fprintf (stderr, "%s: grabDesktopImages: %s\n",
1414 progname, desk_p ? "True" : "False");
1415 fprintf (stderr, "%s: grabVideoFrames: %s\n",
1416 progname, video_p ? "True" : "False");
1417 fprintf (stderr, "%s: chooseRandomImages: %s\n",
1418 progname, image_p ? "True" : "False");
1419 fprintf (stderr, "%s: imageDirectory: %s\n",
1420 progname, (file ? file : dir ? dir : ""));
1423 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
1424 image_p = False; /* can't load images from files... */
1425 # ifdef USE_EXTERNAL_SCREEN_GRABBER
1426 desk_p = False; /* ...or from desktops grabbed to files. */
1432 "%s: image file loading not available at compile-time\n",
1434 fprintf (stderr, "%s: can't load \"%s\"\n", progname, file);
1437 # endif /* !(HAVE_GDK_PIXBUF || HAVE_JPEGLIB) */
1448 if (verbose_p && image_p)
1450 "%s: no imageDirectory: turning off chooseRandomImages.\n",
1457 # error Error! This file definitely needs vroot.h!
1460 /* We can grab desktop images (using the normal X11 method) if:
1461 - the window is the real root window;
1462 - the window is a toplevel window.
1463 We cannot grab desktop images that way if:
1464 - the window is a non-top-level window.
1466 Using the MacOS X way, desktops are just like loaded image files.
1468 # ifndef USE_EXTERNAL_SCREEN_GRABBER
1471 if (!top_level_window_p (screen, window))
1476 "%s: 0x%x not top-level: turning off grabDesktopImages.\n",
1477 progname, (unsigned int) window);
1480 # endif /* !USE_EXTERNAL_SCREEN_GRABBER */
1483 if (desk_p) count++;
1484 if (video_p) count++;
1485 if (image_p) count++;
1492 while (1) /* loop until we get one that's permitted */
1494 which = (random() % 3);
1495 if (which == GRAB_DESK && desk_p) break;
1496 if (which == GRAB_VIDEO && video_p) break;
1497 if (which == GRAB_FILE && image_p) break;
1498 if (++i > 200) abort();
1503 /* If we're to search a directory to find an image file, do so now.
1505 if (which == GRAB_FILE && !file)
1507 file = get_filename (screen, dir, verbose_p);
1512 fprintf (stderr, "%s: no image files found.\n", progname);
1516 /* Now actually render something.
1522 XWindowAttributes xgwa;
1525 fprintf (stderr, "%s: drawing colorbars.\n", progname);
1526 XGetWindowAttributes (dpy, window, &xgwa);
1527 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
1534 if (! display_desktop (screen, window, drawable, verbose_p))
1536 file_prop = "desktop";
1540 if (! display_file (screen, window, drawable, file, verbose_p))
1546 if (! display_video (screen, window, drawable, verbose_p))
1548 file_prop = "video";
1557 Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
1558 if (file_prop && *file_prop)
1559 XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace,
1560 (unsigned char *) file_prop, strlen(file_prop));
1562 XDeleteProperty (dpy, window, a);
1571 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1572 XrmRepresentation *type, XrmValue *value, XPointer closure)
1575 for (i = 0; quarks[i]; i++)
1577 if (bindings[i] == XrmBindTightly)
1578 fprintf (stderr, (i == 0 ? "" : "."));
1579 else if (bindings[i] == XrmBindLoosely)
1580 fprintf (stderr, "*");
1582 fprintf (stderr, " ??? ");
1583 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1586 fprintf (stderr, ": %s\n", (char *) value->addr);
1593 #define USAGE "usage: %s [ -options... ] window-id [pixmap-id]\n" \
1597 " %s puts an image on the given window or pixmap.\n" \
1599 " It is used by those xscreensaver demos that operate on images.\n" \
1600 " The image may be a file loaded from disk, a frame grabbed from\n" \
1601 " the system's video camera, or a screenshot of the desktop,\n" \
1602 " depending on command-line options or the ~/.xscreensaver file.\n" \
1604 " Options include:\n" \
1606 " -display host:dpy.screen which display to use\n" \
1607 " -root draw to the root window\n" \
1608 " -verbose print diagnostics\n" \
1609 " -images / -no-images whether to allow image file loading\n" \
1610 " -video / -no-video whether to allow video grabs\n" \
1611 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
1612 " -directory <path> where to find image files to load\n" \
1613 " -file <filename> load this image file\n" \
1615 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
1616 " defaults for these options in your ~/.xscreensaver file.\n" \
1620 main (int argc, char **argv)
1622 saver_preferences P;
1626 char *oprogname = progname;
1630 Window window = (Window) 0;
1631 Drawable drawable = (Drawable) 0;
1632 const char *window_str = 0;
1633 const char *drawable_str = 0;
1638 s = strrchr (progname, '/');
1639 if (s) progname = s+1;
1640 oprogname = progname;
1642 /* half-assed way of avoiding buffer-overrun attacks. */
1643 if (strlen (progname) >= 100) progname[100] = 0;
1646 # error Error! This file definitely needs vroot.h!
1649 /* Get the version number, for error messages. */
1651 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1652 char *s1, *s2, *s3, *s4;
1653 s1 = (char *) strchr(v, ' '); s1++;
1654 s2 = (char *) strchr(s1, ' ');
1655 s3 = (char *) strchr(v, '('); s3++;
1656 s4 = (char *) strchr(s3, ')');
1659 sprintf (version, "Part of XScreenSaver %s -- %s.", s1, s3);
1663 /* We must read exactly the same resources as xscreensaver.
1664 That means we must have both the same progclass *and* progname,
1665 at least as far as the resource database is concerned. So,
1666 put "xscreensaver" in argv[0] while initializing Xt.
1668 progname = argv[0] = "xscreensaver";
1670 /* allow one dash or two. */
1671 for (i = 1; i < argc; i++)
1672 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
1674 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
1676 dpy = XtDisplay (toplevel);
1677 screen = XtScreen (toplevel);
1678 db = XtDatabase (dpy);
1679 XtGetApplicationNameAndClass (dpy, &s, &progclass);
1680 XSetErrorHandler (x_ehandler);
1683 /* Randomize -- only need to do this here because this program
1684 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
1685 # undef ya_rand_init
1688 memset (&P, 0, sizeof(P));
1690 load_init_file (&P);
1692 progname = argv[0] = oprogname;
1694 for (i = 1; i < argc; i++)
1699 /* Have to re-process these, or else the .xscreensaver file
1700 has priority over the command line...
1702 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
1704 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
1705 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
1706 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
1707 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
1708 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
1709 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
1710 else if (!strcmp (argv[i], "-file")) file = argv[++i];
1711 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
1712 P.image_directory = argv[++i];
1713 else if (!strcmp (argv[i], "-root") || !strcmp (argv[i], "root"))
1717 fprintf (stderr, "%s: both %s and %s specified?\n",
1718 progname, argv[i], window_str);
1721 window_str = argv[i];
1722 window = VirtualRootWindowOfScreen (screen);
1724 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
1725 1 == sscanf (argv[i], " %lu %c", &w, &dummy)) &&
1730 fprintf (stderr, "%s: both %s and %s specified?\n",
1731 progname, drawable_str, argv[i]);
1736 drawable_str = argv[i];
1737 drawable = (Drawable) w;
1741 window_str = argv[i];
1742 window = (Window) w;
1747 if (argv[i][0] == '-')
1748 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
1751 fprintf (stderr, "\n%s: unparsable window/pixmap ID: \"%s\"\n",
1755 __extension__ /* don't warn about "string length is greater than
1756 the length ISO C89 compilers are required to
1757 support" in the usage string... */
1759 fprintf (stderr, USAGE, progname, version, progname);
1766 fprintf (stderr, "\n%s: no window ID specified!\n", progname);
1772 if (P.verbose_p) /* Print out all the resources we can see. */
1774 XrmName name = { 0 };
1775 XrmClass class = { 0 };
1777 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1778 (XtPointer) &count);
1782 if (!window) abort();
1783 if (!drawable) drawable = window;
1785 get_image (screen, window, drawable, P.verbose_p,
1786 P.grab_desktop_p, P.grab_video_p, P.random_image_p,
1787 P.image_directory, file);