1 /* xscreensaver, Copyright (c) 2001, 2002 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, Bool verbose_p)
146 Display *dpy = DisplayOfScreen (screen);
147 Bool desk_p = get_boolean_resource ("grabDesktopImages", "Boolean");
148 Bool video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
149 Bool image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
150 char *dir = get_string_resource ("imageDirectory", "ImageDirectory");
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 if (image_p) image_p = False, changed_p = True;
203 if (changed_p && verbose_p)
204 fprintf (stderr, "%s: not a top-level window: using colorbars.\n",
206 # endif /* !HAVE_BUILTIN_IMAGE_LOADER */
208 else if (window != VirtualRootWindowOfScreen (screen))
210 Bool changed_p = False;
211 if (video_p) video_p = False, changed_p = True;
212 # ifndef HAVE_BUILTIN_IMAGE_LOADER
213 if (!desk_p) desk_p = True, changed_p = True;
214 if (image_p) image_p = False, changed_p = True;
215 if (changed_p && verbose_p)
217 "%s: not running on root window: grabbing desktop.\n",
219 # endif /* !HAVE_BUILTIN_IMAGE_LOADER */
224 if (video_p) count++;
225 if (image_p) count++;
232 while (1) /* loop until we get one that's permitted */
234 which = (random() % 3);
235 if (which == do_desk && desk_p) break;
236 if (which == do_video && video_p) break;
237 if (which == do_image && image_p) break;
238 if (++i > 200) abort();
242 if (which == do_desk)
246 fprintf (stderr, "%s: grabbing desktop image\n", progname);
247 grabscreen_verbose();
249 grab_screen_image (screen, window);
252 else if (which == do_bars)
255 fprintf (stderr, "%s: drawing colorbars\n", progname);
256 draw_colorbars (dpy, window, 0, 0, xgwa.width, xgwa.height);
263 memset (av, 0, sizeof(av));
268 fprintf (stderr, "%s: grabbing video\n", progname);
269 av[ac++] = GETIMAGE_VIDEO_PROGRAM;
273 fprintf (stderr, "%s: loading random image file\n", progname);
274 av[ac++] = GETIMAGE_FILE_PROGRAM;
276 # ifdef HAVE_BUILTIN_IMAGE_LOADER
278 # endif /* !HAVE_BUILTIN_IMAGE_LOADER */
289 for (i = ac; i > 1; i--)
291 av[1] = strdup ("--verbose");
298 fprintf (stderr, "%s: executing \"", progname);
301 fprintf (stderr, "%s", av[i]);
302 if (av[++i]) fprintf (stderr, " ");
304 fprintf (stderr, "\"\n");
308 /* Store our "-display" argument into the $DISPLAY variable,
309 so that the subprocess gets the right display if the
310 prevailing $DISPLAY is different. */
312 const char *odpy = DisplayString (dpy);
313 char *ndpy = (char *) malloc(strlen(odpy) + 20);
315 int screen_no = screen_number (screen); /* might not be default now */
317 strcpy (ndpy, "DISPLAY=");
318 s = ndpy + strlen(ndpy);
321 while (*s && *s != ':') s++; /* skip to colon */
322 while (*s == ':') s++; /* skip over colons */
323 while (isdigit(*s)) s++; /* skip over dpy number */
324 while (*s == '.') s++; /* skip over dot */
325 if (s[-1] != '.') *s++ = '.'; /* put on a dot */
326 sprintf(s, "%d", screen_no); /* put on screen number */
331 /* don't free (ndpy) -- some implementations of putenv (BSD
332 4.4, glibc 2.0) copy the argument, but some (libc4,5, glibc
333 2.1.2) do not. So we must leak it (and/or the previous
337 # endif /* HAVE_PUTENV */
339 # ifdef HAVE_BUILTIN_IMAGE_LOADER
340 if (which == do_image)
342 load_image_internal (screen, window, xgwa.width, xgwa.height,
346 # endif /* HAVE_BUILTIN_IMAGE_LOADER */
349 close (ConnectionNumber (dpy)); /* close display fd */
351 execvp (av[0], av); /* shouldn't return */
357 #ifdef HAVE_BUILTIN_IMAGE_LOADER
359 /* Reads a filename from "GETIMAGE_FILE_PROGRAM --name /DIR"
362 get_filename (Display *dpy, int ac, char **av)
371 sprintf (buf, "%s: error creating pipe", progname);
379 switch ((int) (forked = fork ()))
383 sprintf (buf, "%s: couldn't fork", progname);
391 close (in); /* don't need this one */
392 close (ConnectionNumber (dpy)); /* close display fd */
394 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
396 sprintf (buf, "%s: could not dup() a new stdout", progname);
397 exit (-1); /* exits fork */
400 execvp (av[0], av); /* shouldn't return. */
401 exit (-1); /* exits fork */
407 FILE *f = fdopen (in, "r");
410 close (out); /* don't need this one */
412 fgets (buf, sizeof(buf)-1, f);
415 /* Wait for the child to die. */
416 waitpid (-1, &wait_status, 0);
419 while (L && buf[L-1] == '\n')
432 load_image_internal (Screen *screen, Window window,
433 int win_width, int win_height,
438 Display *dpy = DisplayOfScreen (screen);
439 char *filename = get_filename (dpy, ac, av);
442 #endif /* HAVE_GTK2 */
446 fprintf (stderr, "%s: no file name returned by %s\n",
451 fprintf (stderr, "%s: loading \"%s\"\n", progname, filename);
453 gdk_pixbuf_xlib_init (dpy, screen_number (screen));
456 #else /* !HAVE_GTK2 */
457 xlib_rgb_init (dpy, screen);
458 #endif /* !HAVE_GTK2 */
460 pb = gdk_pixbuf_new_from_file (filename
463 #endif /* HAVE_GTK2 */
468 int w = gdk_pixbuf_get_width (pb);
469 int h = gdk_pixbuf_get_height (pb);
470 int srcx, srcy, destx, desty;
472 Bool exact_fit_p = ((w == win_width && h <= win_height) ||
473 (h == win_height && w <= win_width));
475 if (!exact_fit_p) /* scale the image up or down */
477 float rw = (float) win_width / w;
478 float rh = (float) win_height / h;
479 float r = (rw < rh ? rw : rh);
484 if (pct < 95 || pct > 105) /* don't scale if it's close */
489 "%s: scaling image by %d%% (%dx%d -> %dx%d)\n",
490 progname, pct, w, h, tw, th);
492 pb2 = gdk_pixbuf_scale_simple (pb, tw, th, GDK_INTERP_BILINEAR);
495 gdk_pixbuf_unref (pb);
501 fprintf (stderr, "%s: out of memory when scaling?\n",
506 /* Center the image on the window. */
509 destx = (win_width - w) / 2;
510 desty = (win_height - h) / 2;
511 if (destx < 0) srcx = -destx, destx = 0;
512 if (desty < 0) srcy = -desty, desty = 0;
514 if (win_width < w) w = win_width;
515 if (win_height < h) h = win_height;
517 /* The window might have no-op background of None, so to clear it,
518 draw a black rectangle first, then do XClearWindow (in case the
519 actual background color is non-black...) */
523 /* #### really we should allocate "black" instead, but I'm lazy... */
524 gcv.foreground = BlackPixelOfScreen (screen);
525 gc = XCreateGC (dpy, window, GCForeground, &gcv);
526 XFillRectangle (dpy, window, gc, 0, 0, win_width, win_height);
528 XClearWindow (dpy, window);
532 /* #### Note that this always uses the default colormap! Morons!
533 Owen says that in Gnome 2.0, I should try using
534 gdk_pixbuf_render_pixmap_and_mask_for_colormap() instead.
535 But I don't have Gnome 2.0 yet.
537 gdk_pixbuf_xlib_render_to_drawable_alpha (pb, window,
538 srcx, srcy, destx, desty, w, h,
539 GDK_PIXBUF_ALPHA_FULL, 127,
540 XLIB_RGB_DITHER_NORMAL, 0, 0);
544 fprintf (stderr, "%s: displayed %dx%d image at %d,%d.\n",
545 progname, w, h, destx, desty);
549 fprintf (stderr, "%s: unable to load %s\n", progname, filename);
551 if (gerr && gerr->message && *gerr->message)
552 fprintf (stderr, "%s: reason %s\n", progname, gerr->message);
553 #endif /* HAVE_GTK2 */
559 fprintf (stderr, "%s: unable to initialize built-in images\n", progname);
567 fprintf (stderr, "%s: drawing colorbars\n", progname);
568 draw_colorbars (dpy, window, 0, 0, win_width, win_height);
572 #endif /* HAVE_BUILTIN_IMAGE_LOADER */
578 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
579 XrmRepresentation *type, XrmValue *value, XPointer closure)
582 for (i = 0; quarks[i]; i++)
584 if (bindings[i] == XrmBindTightly)
585 fprintf (stderr, (i == 0 ? "" : "."));
586 else if (bindings[i] == XrmBindLoosely)
587 fprintf (stderr, "*");
589 fprintf (stderr, " ??? ");
590 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
593 fprintf (stderr, ": %s\n", (char *) value->addr);
601 main (int argc, char **argv)
607 Window window = (Window) 0;
608 Bool verbose_p = False;
613 s = strrchr (progname, '/');
614 if (s) progname = s+1;
616 /* We must read exactly the same resources as xscreensaver.
617 That means we must have both the same progclass *and* progname,
618 at least as far as the resource database is concerned. So,
619 put "xscreensaver" in argv[0] while initializing Xt.
621 argv[0] = "xscreensaver";
622 toplevel = XtAppInitialize (&app, progclass, 0, 0, &argc, argv,
625 dpy = XtDisplay (toplevel);
626 screen = XtScreen (toplevel);
627 db = XtDatabase (dpy);
629 XtGetApplicationNameAndClass (dpy, &s, &progclass);
630 XSetErrorHandler (x_ehandler);
633 /* half-assed way of avoiding buffer-overrun attacks. */
634 if (strlen (progname) >= 100) progname[100] = 0;
636 for (i = 1; i < argc; i++)
638 if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++;
639 if (!strcmp (argv[i], "-v") ||
640 !strcmp (argv[i], "-verbose"))
642 else if (window == 0)
647 if (!strcmp (argv[i], "root") ||
648 !strcmp (argv[i], "-root") ||
649 !strcmp (argv[i], "--root"))
650 window = RootWindowOfScreen (screen);
652 else if ((1 == sscanf (argv[i], " 0x%lx %c", &w, &dummy) ||
653 1 == sscanf (argv[i], " %ld %c", &w, &dummy)) &&
663 "usage: %s [ -display host:dpy.screen ] [ -v ] window-id\n",
665 fprintf (stderr, "\n"
666 "\tThis program puts an image of the desktop on the given window.\n"
667 "\tIt is used by those xscreensaver demos that operate on images.\n"
673 if (window == 0) goto LOSE;
675 /* Randomize -- only need to do this here because this program
676 doesn't use the `screenhack.h' or `lockmore.h' APIs. */
680 memset (&P, 0, sizeof(P));
688 /* Print out all the resources we read. */
690 XrmName name = { 0 };
691 XrmClass class = { 0 };
693 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
698 get_image (screen, window, verbose_p);