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 an image
13 (e.g., a snapshot of the desktop) onto the given window.
18 #include <X11/Intrinsic.h>
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h> /* for waitpid() and associated macros */
28 # include <X11/Xmu/Error.h>
30 # include <Xmu/Error.h>
37 #include "grabscreen.h"
38 #include "resources.h"
39 #include "colorbars.h"
44 #ifdef HAVE_GDK_PIXBUF
47 # include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
48 # else /* !HAVE_GTK2 */
49 # include <gdk-pixbuf/gdk-pixbuf-xlib.h>
50 # endif /* !HAVE_GTK2 */
52 # define HAVE_BUILTIN_IMAGE_LOADER
53 #endif /* HAVE_GDK_PIXBUF */
56 static char *defaults[] = {
57 #include "../driver/XScreenSaver_ad.h"
64 char *progclass = "XScreenSaver";
68 extern void grabscreen_verbose (void);
71 #define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
72 #define GETIMAGE_FILE_PROGRAM "xscreensaver-getimage-file"
83 exec_error (char **av)
88 sprintf (buf, "%s: could not execute \"%s\"", progname, av[0]);
91 if (errno == ENOENT &&
92 (token = getenv("PATH")))
96 # define PATH_MAX MAXPATHLEN
98 # define PATH_MAX 2048
102 fprintf (stderr, "\n");
104 # if defined(HAVE_GETCWD)
105 getcwd (path, sizeof(path));
106 # elif defined(HAVE_GETWD)
110 fprintf (stderr, " Current directory is: %s\n", path);
111 fprintf (stderr, " PATH is:\n");
112 token = strtok (strdup(token), ":");
115 fprintf (stderr, " %s\n", token);
116 token = strtok(0, ":");
118 fprintf (stderr, "\n");
125 x_ehandler (Display *dpy, XErrorEvent *error)
127 fprintf (stderr, "\nX error in %s:\n", progname);
128 XmuPrintDefaultErrorMessage (dpy, error, stderr);
135 #ifdef HAVE_BUILTIN_IMAGE_LOADER
136 static void load_image_internal (Screen *screen, Window window,
137 int win_width, int win_height,
140 #endif /* HAVE_BUILTIN_IMAGE_LOADER */
144 get_image (Screen *screen, Window window,
151 Display *dpy = DisplayOfScreen (screen);
152 enum { do_desk, do_video, do_image, do_bars } which = do_bars;
155 XWindowAttributes xgwa;
156 XGetWindowAttributes (dpy, window, &xgwa);
157 screen = xgwa.screen;
161 fprintf (stderr, "%s: grabDesktopImages: %s\n",
162 progname, desk_p ? "True" : "False");
163 fprintf (stderr, "%s: grabVideoFrames: %s\n",
164 progname, video_p ? "True" : "False");
165 fprintf (stderr, "%s: chooseRandomImages: %s\n",
166 progname, image_p ? "True" : "False");
167 fprintf (stderr, "%s: imageDirectory: %s\n",
168 progname, (dir ? dir : ""));
173 if (verbose_p && image_p)
175 "%s: no imageDirectory: turning off chooseRandomImages.\n",
181 # error Error! This file definitely needs vroot.h!
184 /* If the window is not the root window (real or virtual!) then the hack
185 that called this program is running in "-window" mode instead of in
188 If the window is not the root window, then it's not possible to grab
189 video or images onto it (the contract with those programs is to draw on
190 the root.) So turn off those options in that case, and turn on desktop
191 grabbing. (Since we're running in a window on the desktop already, we
192 know it's not a security problem to expose desktop bits.)
195 if ((desk_p || video_p || image_p) &&
196 !top_level_window_p (screen, window))
198 Bool changed_p = False;
199 if (desk_p) desk_p = False, changed_p = True;
200 if (video_p) video_p = False, changed_p = True;
201 # ifndef HAVE_BUILTIN_IMAGE_LOADER
202 /* We can display images on non-top-level windows with the builtin
203 loader, but not if we're using the external (chbg-based) loader. */
204 if (image_p) image_p = False, changed_p = True;
205 # endif /* !HAVE_BUILTIN_IMAGE_LOADER */
207 if (changed_p && verbose_p)
208 fprintf (stderr, "%s: not a top-level window: using colorbars.\n",
211 else if (window != VirtualRootWindowOfScreen (screen))
213 /* We can display images on non-root windows with the builtin loader,
214 but not if we're using the external (chbg-based) loader.
215 We can never display video on non-root windows (since that always
216 uses the external image loader.)
218 Bool changed_p = False;
219 if (video_p) video_p = False, changed_p = True;
220 # ifndef HAVE_BUILTIN_IMAGE_LOADER
221 if (!desk_p) desk_p = True, changed_p = True;
222 if (image_p) image_p = False, changed_p = True;
223 # endif /* !HAVE_BUILTIN_IMAGE_LOADER */
225 if (changed_p && verbose_p)
227 "%s: not running on root window: grabbing desktop.\n",
233 if (video_p) count++;
234 if (image_p) count++;
241 while (1) /* loop until we get one that's permitted */
243 which = (random() % 3);
244 if (which == do_desk && desk_p) break;
245 if (which == do_video && video_p) break;
246 if (which == do_image && image_p) break;
247 if (++i > 200) abort();
251 if (which == do_desk)
255 fprintf (stderr, "%s: grabbing desktop image\n", progname);
256 grabscreen_verbose();
258 grab_screen_image (screen, window);
261 else if (which == do_bars)
264 fprintf (stderr, "%s: drawing colorbars\n", progname);
265 draw_colorbars (dpy, window, 0, 0, xgwa.width, xgwa.height);
272 memset (av, 0, sizeof(av));
277 fprintf (stderr, "%s: grabbing video\n", progname);
278 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
282 fprintf (stderr, "%s: loading random image file\n", progname);
283 av[ac++] = GETIMAGE_FILE_PROGRAM;
285 # ifdef HAVE_BUILTIN_IMAGE_LOADER
287 # endif /* !HAVE_BUILTIN_IMAGE_LOADER */
298 for (i = ac; i > 1; i--)
300 av[1] = strdup ("--verbose");
307 fprintf (stderr, "%s: executing \"", progname);
310 fprintf (stderr, "%s", av[i]);
311 if (av[++i]) fprintf (stderr, " ");
313 fprintf (stderr, "\"\n");
317 /* Store our "-display" argument into the $DISPLAY variable,
318 so that the subprocess gets the right display if the
319 prevailing $DISPLAY is different. */
321 const char *odpy = DisplayString (dpy);
322 char *ndpy = (char *) malloc(strlen(odpy) + 20);
324 int screen_no = screen_number (screen); /* might not be default now */
326 strcpy (ndpy, "DISPLAY=");
327 s = ndpy + strlen(ndpy);
330 while (*s && *s != ':') s++; /* skip to colon */
331 while (*s == ':') s++; /* skip over colons */
332 while (isdigit(*s)) s++; /* skip over dpy number */
333 while (*s == '.') s++; /* skip over dot */
334 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
335 sprintf(s, "%d", screen_no); /* put on screen number */
340 /* don't free (ndpy) -- some implementations of putenv (BSD
341 4.4, glibc 2.0) copy the argument, but some (libc4,5, glibc
342 2.1.2) do not. So we must leak it (and/or the previous
346 # endif /* HAVE_PUTENV */
348 # ifdef HAVE_BUILTIN_IMAGE_LOADER
349 if (which == do_image)
351 load_image_internal (screen, window, xgwa.width, xgwa.height,
355 # endif /* HAVE_BUILTIN_IMAGE_LOADER */
358 close (ConnectionNumber (dpy)); /* close display fd */
360 execvp (av[0], av); /* shouldn't return */
366 #ifdef HAVE_BUILTIN_IMAGE_LOADER
368 /* Reads a filename from "GETIMAGE_FILE_PROGRAM --name /DIR"
371 get_filename (Display *dpy, int ac, char **av)
380 sprintf (buf, "%s: error creating pipe", progname);
388 switch ((int) (forked = fork ()))
392 sprintf (buf, "%s: couldn't fork", progname);
400 close (in); /* don't need this one */
401 close (ConnectionNumber (dpy)); /* close display fd */
403 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
405 sprintf (buf, "%s: could not dup() a new stdout", progname);
406 exit (-1); /* exits fork */
409 execvp (av[0], av); /* shouldn't return. */
410 exit (-1); /* exits fork */
416 FILE *f = fdopen (in, "r");
419 close (out); /* don't need this one */
421 fgets (buf, sizeof(buf)-1, f);
424 /* Wait for the child to die. */
425 waitpid (-1, &wait_status, 0);
428 while (L && buf[L-1] == '\n')
441 load_image_internal (Screen *screen, Window window,
442 int win_width, int win_height,
447 Display *dpy = DisplayOfScreen (screen);
448 char *filename = get_filename (dpy, ac, av);
451 #endif /* HAVE_GTK2 */
455 fprintf (stderr, "%s: no file name returned by %s\n",
460 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
462 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
465 #else /* !HAVE_GTK2 */
466 xlib_rgb_init (dpy, screen);
467 #endif /* !HAVE_GTK2 */
469 pb = gdk_pixbuf_new_from_file (filename
472 #endif /* HAVE_GTK2 */
477 int w = gdk_pixbuf_get_width (pb);
478 int h = gdk_pixbuf_get_height (pb);
479 int srcx, srcy, destx, desty;
481 Bool exact_fit_p = ((w == win_width && h <= win_height) ||
482 (h == win_height && w <= win_width));
484 if (!exact_fit_p) /* scale the image up or down */
486 float rw = (float) win_width / w;
487 float rh = (float) win_height / h;
488 float r = (rw < rh ? rw : rh);
493 if (pct < 95 || pct > 105) /* don't scale if it's close */
498 "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
499 progname, pct, w, h, tw, th);
501 pb2 = gdk_pixbuf_scale_simple (pb, tw, th, GDK_INTERP_BILINEAR);
504 gdk_pixbuf_unref (pb);
510 fprintf (stderr, "%s: out of memory when scaling?\n",
515 /* Center the image on the window. */
518 destx = (win_width - w) / 2;
519 desty = (win_height - h) / 2;
520 if (destx < 0) srcx = -destx, destx = 0;
521 if (desty < 0) srcy = -desty, desty = 0;
523 if (win_width < w) w = win_width;
524 if (win_height < h) h = win_height;
526 /* The window might have no-op background of None, so to clear it,
527 draw a black rectangle first, then do XClearWindow (in case the
528 actual background color is non-black...) */
532 /* #### really we should allocate "black" instead, but I'm lazy... */
533 gcv.foreground = BlackPixelOfScreen (screen);
534 gc = XCreateGC (dpy, window, GCForeground, &gcv);
535 XFillRectangle (dpy, window, gc, 0, 0, win_width, win_height);
537 XClearWindow (dpy, window);
541 /* #### Note that this always uses the default colormap! Morons!
542 Owen says that in Gnome 2.0, I should try using
543 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
544 But I don't have Gnome 2.0 yet.
546 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, window,
547 srcx, srcy, destx, desty, w, h,
548 GDK_PIXBUF_ALPHA_FULL, 127,
549 XLIB_RGB_DITHER_NORMAL, 0, 0);
553 fprintf (stderr, "%s: displayed %dx%d image at %d,%d.\n",
554 progname, w, h, destx, desty);
558 fprintf (stderr, "%s: unable to load %s\n", progname, filename);
560 if (gerr && gerr->message && *gerr->message)
561 fprintf (stderr, "%s: reason %s\n", progname, gerr->message);
562 #endif /* HAVE_GTK2 */
568 fprintf (stderr, "%s: unable to initialize built-in images\n", progname);
576 fprintf (stderr, "%s: drawing colorbars\n", progname);
577 draw_colorbars (dpy, window, 0, 0, win_width, win_height);
581 #endif /* HAVE_BUILTIN_IMAGE_LOADER */
587 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
588 XrmRepresentation *type, XrmValue *value, XPointer closure)
591 for (i = 0; quarks[i]; i++)
593 if (bindings[i] == XrmBindTightly)
594 fprintf (stderr, (i == 0 ? "" : "."));
595 else if (bindings[i] == XrmBindLoosely)
596 fprintf (stderr, "*");
598 fprintf (stderr, " ??? ");
599 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
602 fprintf (stderr, ": %s\n", (char *) value->addr);
609 #define USAGE "usage: %s [ -options... ] window-id\n" \
611 " This program puts an image on the given window.\n" \
613 " It is used by those xscreensaver demos that operate on images.\n" \
614 " The image may be a file loaded from disk, a frame grabbed from\n" \
615 " the system's video camera, or a screenshot of the desktop,\n" \
616 " depending on command-line options or the ~/.xscreensaver file.\n" \
618 " Options include:\n" \
620 " -display host:dpy.screen which display to use\n" \
621 " -root draw to the root window\n" \
622 " -verbose print diagnostics\n" \
623 " -images / -no-images whether to allow image file loading\n" \
624 " -video / -no-video whether to allow video grabs\n" \
625 " -desktop / -no-desktop whether to allow desktop screen grabs\n"\
626 " -directory <path> where to find image files to load\n" \
628 " The XScreenSaver Control Panel (xscreensaver-demo) lets you set the\n"\
629 " defaults for these options in your ~/.xscreensaver file.\n" \
633 main (int argc, char **argv)
639 char *oprogname = progname;
641 Window window = (Window) 0;
646 s = strrchr (progname, '/');
647 if (s) progname = s+1;
648 oprogname = progname;
650 /* half-assed way of avoiding buffer-overrun attacks. */
651 if (strlen (progname) >= 100) progname[100] = 0;
654 /* We must read exactly the same resources as xscreensaver.
655 That means we must have both the same progclass *and* progname,
656 at least as far as the resource database is concerned. So,
657 put "xscreensaver" in argv[0] while initializing Xt.
659 progname = argv[0] = "xscreensaver";
661 /* allow one dash or two. */
662 for (i = 1; i < argc; i++)
663 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
665 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
667 dpy = XtDisplay (toplevel);
668 screen = XtScreen (toplevel);
669 db = XtDatabase (dpy);
670 XtGetApplicationNameAndClass (dpy, &s, &progclass);
671 XSetErrorHandler (x_ehandler);
674 /* Randomize -- only need to do this here because this program
675 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
679 memset (&P, 0, sizeof(P));
683 progname = argv[0] = oprogname;
685 for (i = 1; i < argc; i++)
687 /* Have to re-process these, or else the .xscreensaver file
688 has priority over the command line...
690 if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
692 else if (!strcmp (argv[i], "-desktop")) P.grab_desktop_p = True;
693 else if (!strcmp (argv[i], "-no-desktop")) P.grab_desktop_p = False;
694 else if (!strcmp (argv[i], "-video")) P.grab_video_p = True;
695 else if (!strcmp (argv[i], "-no-video")) P.grab_video_p = False;
696 else if (!strcmp (argv[i], "-images")) P.random_image_p = True;
697 else if (!strcmp (argv[i], "-no-images")) P.random_image_p = False;
698 else if (!strcmp (argv[i], "-directory") || !strcmp (argv[i], "-dir"))
699 P.image_directory = argv[++i];
700 else if (window == 0)
705 if (!strcmp (argv[i], "-root") ||
706 !strcmp (argv[i], "root"))
707 window = RootWindowOfScreen (screen);
709 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
710 1 == sscanf (argv[i], " %ld %c", &w, &dummy)) &&
715 if (argv[i][0] == '-')
716 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
719 fprintf (stderr, "\n%s: unparsable window ID: \"%s\"\n",
726 fprintf (stderr, "\n%s: unknown option \"%s\"\n",
729 fprintf (stderr, USAGE, progname);
736 fprintf (stderr, "\n%s: no window specified!\n", progname);
742 if (P.verbose_p) /* Print out all the resources we can see. */
744 XrmName name = { 0 };
745 XrmClass class = { 0 };
747 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
752 get_image (screen, window, P.verbose_p,
753 P.grab_desktop_p, P.grab_video_p, P.random_image_p,