1 /* xscreensaver, Copyright (c) 1992-2010 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 /* This file contains code for running an external program to grab an image
13 onto the given window. The external program in question must take a
14 window ID as its argument, e.g., the "xscreensaver-getimage" program
15 in the hacks/ directory.
17 That program links against utils/grabimage.c, which happens to export the
18 same API as this program (utils/grabclient.c).
22 #include "grabscreen.h"
23 #include "resources.h"
27 # include "colorbars.h"
28 #else /* !HAVE_COCOA -- real Xlib */
30 # include <X11/Xatom.h>
31 # include <X11/Intrinsic.h> /* for XtInputId, etc */
32 #endif /* !HAVE_COCOA */
39 #ifdef HAVE_SYS_WAIT_H
40 # include <sys/wait.h> /* for waitpid() and associated macros */
44 extern char *progname;
46 static void print_loading_msg (Screen *, Window);
50 static Bool error_handler_hit_p = False;
53 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
55 error_handler_hit_p = True;
60 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
63 drawable_window_p (Display *dpy, Drawable d)
65 XErrorHandler old_handler;
66 XWindowAttributes xgwa;
69 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
70 error_handler_hit_p = False;
71 XGetWindowAttributes (dpy, d, &xgwa);
73 XSetErrorHandler (old_handler);
76 if (!error_handler_hit_p)
77 return True; /* It's a Window. */
79 return False; /* It's a Pixmap, or an invalid ID. */
84 xscreensaver_window_p (Display *dpy, Window window)
88 unsigned long nitems, bytesafter;
89 unsigned char *version;
90 if (XGetWindowProperty (dpy, window,
91 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
92 0, 1, False, XA_STRING,
93 &type, &format, &nitems, &bytesafter,
102 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
103 on a window whose depth is not the maximal depth of the screen? Or
104 something. Anyway, things don't work unless we: use SubwindowMode for
105 the real root window (or a legitimate virtual root window); but do not
106 use SubwindowMode for the xscreensaver window. I make no attempt to
110 use_subwindow_mode_p (Screen *screen, Window window)
112 if (window != VirtualRootWindowOfScreen(screen))
114 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
122 checkerboard (Screen *screen, Drawable drawable)
124 Display *dpy = DisplayOfScreen (screen);
129 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
131 unsigned int win_width, win_height;
133 fg.flags = bg.flags = DoRed|DoGreen|DoBlue;
134 fg.red = fg.green = fg.blue = 0x0000;
135 bg.red = bg.green = bg.blue = 0x4444;
139 if (drawable_window_p (dpy, drawable))
141 XWindowAttributes xgwa;
142 XGetWindowAttributes (dpy, drawable, &xgwa);
143 win_width = xgwa.width;
144 win_height = xgwa.height;
145 cmap = xgwa.colormap;
146 screen = xgwa.screen;
148 else /* it's a pixmap */
150 XWindowAttributes xgwa;
154 XGetWindowAttributes (dpy, RootWindowOfScreen (screen), &xgwa);
155 cmap = xgwa.colormap;
156 XGetGeometry (dpy, drawable,
157 &root, &x, &y, &win_width, &win_height, &bw, &d);
160 /* Allocate black and gray, but don't hold them locked. */
161 if (XAllocColor (dpy, cmap, &fg))
162 XFreeColors (dpy, cmap, &fg.pixel, 1, 0);
163 if (XAllocColor (dpy, cmap, &bg))
164 XFreeColors (dpy, cmap, &bg.pixel, 1, 0);
166 XSetForeground (dpy, gc, bg.pixel);
167 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
168 XSetForeground (dpy, gc, fg.pixel);
169 for (y = 0; y < win_height; y += size+size)
170 for (x = 0; x < win_width; x += size+size)
172 XFillRectangle (dpy, drawable, gc, x, y, size, size);
173 XFillRectangle (dpy, drawable, gc, x+size, y+size, size, size);
180 get_name (Display *dpy, Window window)
184 unsigned long nitems, bytesafter;
185 unsigned char *name = 0;
186 Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
187 if (XGetWindowProperty (dpy, window, atom,
188 0, 1024, False, XA_STRING,
189 &type, &format, &nitems, &bytesafter,
193 return (char *) name;
200 get_geometry (Display *dpy, Window window, XRectangle *ret)
204 unsigned long nitems, bytesafter;
205 unsigned char *name = 0;
206 Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
209 if (XGetWindowProperty (dpy, window, atom,
210 0, 1024, False, XA_STRING,
211 &type, &format, &nitems, &bytesafter,
216 int flags = XParseGeometry ((char *) name, &x, &y, &w, &h);
218 /* Require all four, and don't allow negative positions. */
219 if (flags == (XValue|YValue|WidthValue|HeightValue))
236 hack_subproc_environment (Display *dpy)
238 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
239 the spawned processes inherit is what we are actually using.
241 const char *odpy = DisplayString (dpy);
242 char *ndpy = (char *) malloc(strlen(odpy) + 20);
243 strcpy (ndpy, "DISPLAY=");
246 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
247 any more, right? It's not Posix, but everyone seems to have it. */
251 #endif /* HAVE_PUTENV */
253 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
254 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2, MacOS)
255 do not. So we must leak it (and/or the previous setting). Yay.
260 /* Spawn a program, and wait for it to finish.
261 If we just use system() for this, then sometimes the subprocess
262 doesn't die when *this* process is sent a TERM signal. Perhaps
263 this is due to the intermediate /bin/sh that system() uses to
264 parse arguments? I'm not sure. But using fork() and execvp()
265 here seems to close the race.
268 exec_simple_command (const char *command)
272 char *token = strtok (strdup(command), " \t");
276 token = strtok(0, " \t");
280 execvp (av[0], av); /* shouldn't return. */
285 fork_exec_wait (const char *command)
291 switch ((int) (forked = fork ()))
294 sprintf (buf, "%s: couldn't fork", progname);
299 exec_simple_command (command);
300 exit (1); /* exits child fork */
304 waitpid (forked, &status, 0);
311 void (*callback) (Screen *, Window, Drawable,
312 const char *name, XRectangle *geom, void *closure);
324 static void finalize_cb (XtPointer closure, int *fd, XtIntervalId *id);
327 fork_exec_cb (const char *command,
328 Screen *screen, Window window, Drawable drawable,
329 void (*callback) (Screen *, Window, Drawable,
330 const char *name, XRectangle *geom,
334 XtAppContext app = XtDisplayToApplicationContext (DisplayOfScreen (screen));
335 grabclient_data *data;
343 sprintf (buf, "%s: creating pipe", progname);
348 data = (grabclient_data *) calloc (1, sizeof(*data));
349 data->callback = callback;
350 data->closure = closure;
351 data->screen = screen;
352 data->window = window;
353 data->drawable = drawable;
354 data->read_pipe = fdopen (fds[0], "r");
355 data->write_pipe = fdopen (fds[1], "w");
357 if (!data->read_pipe || !data->write_pipe)
359 sprintf (buf, "%s: fdopen", progname);
365 XtAppAddInput (app, fileno (data->read_pipe),
366 (XtPointer) (XtInputReadMask | XtInputExceptMask),
367 finalize_cb, (XtPointer) data);
370 switch ((int) forked)
373 sprintf (buf, "%s: couldn't fork", progname);
379 fclose (data->read_pipe);
382 /* clone the write pipe onto stdout so that it gets closed
383 when the fork exits. This will shut down the pipe and
386 close (fileno (stdout));
387 dup2 (fds[1], fileno (stdout));
390 close (fileno (stdin)); /* for kicks */
392 exec_simple_command (command);
393 exit (1); /* exits child fork */
396 default: /* parent */
397 fclose (data->write_pipe);
398 data->write_pipe = 0;
405 /* Called in the parent when the forked process dies.
406 Runs the caller's callback, and cleans up.
409 finalize_cb (XtPointer closure, int *fd, XtIntervalId *id)
411 grabclient_data *data = (grabclient_data *) closure;
412 Display *dpy = DisplayOfScreen (data->screen);
414 XRectangle geom = { 0, 0, 0, 0 };
418 name = get_name (dpy, data->window);
419 get_geometry (dpy, data->window, &geom);
421 data->callback (data->screen, data->window, data->drawable,
422 name, &geom, data->closure);
423 if (name) free (name);
425 fclose (data->read_pipe);
427 if (data->pid) /* reap zombies */
430 waitpid (data->pid, &status, 0);
434 memset (data, 0, sizeof (*data));
439 /* Loads an image into the Drawable.
440 When grabbing desktop images, the Window will be unmapped first.
443 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
444 void (*callback) (Screen *, Window, Drawable,
445 const char *name, XRectangle *geom,
449 XRectangle *geom_ret)
451 Display *dpy = DisplayOfScreen (screen);
452 char *grabber = get_string_resource(dpy, "desktopGrabber", "DesktopGrabber");
457 view = get_integer_resource(dpy, "view", "view");
458 if (!grabber || !*grabber)
461 "%s: resources installed incorrectly: \"desktopGrabber\" is unset!\n",
466 sprintf (id, "-file /home/user/.backgrounds/background-%i.png 0x%lx 0x%lx", view,
467 (unsigned long) window,
468 (unsigned long) drawable);
469 cmd = (char *) malloc (strlen(grabber) + strlen(id) + 1);
471 /* Needn't worry about buffer overflows here, because the buffer is
472 longer than the length of the format string, and the length of what
473 we're putting into it. So the only way to crash would be if the
474 format string itself was corrupted, but that comes from the
475 resource database, and if hostile forces have access to that,
476 then the game is already over.
478 sprintf (cmd, grabber, id);
482 /* In case "cmd" fails, leave some random image on the screen, not just
483 black or white, so that it's more obvious what went wrong. */
484 checkerboard (screen, drawable);
485 if (window == drawable)
486 print_loading_msg (screen, window);
489 hack_subproc_environment (dpy);
493 /* Start the image loading in another fork and return immediately.
494 Invoke the callback function when done.
496 if (name_ret) abort();
497 fork_exec_cb (cmd, screen, window, drawable, callback, closure);
501 /* Wait for the image to load, and return it immediately.
503 fork_exec_wait (cmd);
505 *name_ret = get_name (dpy, window);
507 get_geometry (dpy, window, geom_ret);
514 #else /* HAVE_COCOA */
516 /* Gets the name of an image file to load by running xscreensaver-getimage-file
517 at the end of a pipe. This can be very slow!
520 open_image_name_pipe (const char *dir)
522 char *cmd = malloc (strlen(dir) * 2 + 100);
524 strcpy (cmd, "xscreensaver-getimage-file --name ");
525 s = cmd + strlen (cmd);
528 /* put a backslash in front of any character that might confuse sh. */
529 if (! ((c >= 'a' && c <= 'z') ||
530 (c >= 'A' && c <= 'Z') ||
531 (c >= '0' && c <= '9') ||
532 c == '.' || c == '_' || c == '-' || c == '+' || c == '/'))
538 FILE *pipe = popen (cmd, "r");
544 struct pipe_closure {
550 void (*callback) (Screen *, Window, Drawable,
551 const char *name, XRectangle *geom,
558 pipe_cb (XtPointer closure, int *source, XtInputId *id)
560 /* This is not called from a signal handler, so doing stuff here is fine.
562 struct pipe_closure *clo2 = (struct pipe_closure *) closure;
564 fgets (buf, sizeof(buf)-1, clo2->pipe);
567 XtRemoveInput (clo2->id);
570 /* strip trailing newline */
572 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
575 Display *dpy = DisplayOfScreen (clo2->screen);
578 if (! osx_load_image_file (clo2->screen, clo2->xwindow, clo2->drawable,
580 /* unable to load image - draw colorbars
582 XWindowAttributes xgwa;
583 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
586 unsigned int w, h, bbw, d;
589 /* Log something to syslog so we can tell the difference between
590 corrupted images and broken symlinks. */
592 fprintf (stderr, "%s: no image filename found\n", progname);
593 else if (! stat (buf, st))
594 fprintf (stderr, "%s: %s: unparsable\n", progname, buf);
598 sprintf (buf2, "%.255s: %.1024s", progname, buf);
602 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
603 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
610 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable, buf, &geom,
618 osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
620 void (*callback) (Screen *, Window, Drawable,
626 #if 0 /* do it synchronously */
628 FILE *pipe = open_image_name_pipe (dir);
631 fgets (buf, sizeof(buf)-1, pipe);
634 /* strip trailing newline */
636 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
640 if (! osx_load_image_file (screen, xwindow, drawable, buf, &geom)) {
644 callback (screen, xwindow, drawable, buf, &geom, closure);
646 #else /* do it asynchronously */
648 Display *dpy = DisplayOfScreen (screen);
649 struct pipe_closure *clo2 = (struct pipe_closure *) calloc (1, sizeof(*clo2));
650 clo2->pipe = open_image_name_pipe (dir);
651 clo2->id = XtAppAddInput (XtDisplayToApplicationContext (dpy),
653 (XtPointer) (XtInputReadMask | XtInputExceptMask),
654 pipe_cb, (XtPointer) clo2);
655 clo2->screen = screen;
656 clo2->xwindow = xwindow;
657 clo2->drawable = drawable;
658 clo2->callback = callback;
659 clo2->closure = closure;
664 /* Loads an image into the Drawable, returning once the image is loaded.
667 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
668 void (*callback) (Screen *, Window, Drawable,
669 const char *name, XRectangle *geom,
673 XRectangle *geom_ret)
675 Display *dpy = DisplayOfScreen (screen);
676 XWindowAttributes xgwa;
677 Bool deskp = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
678 Bool filep = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
681 XRectangle geom_ret_2;
682 char *name_ret_2 = 0;
684 if (!drawable) abort();
687 geom_ret = &geom_ret_2;
688 name_ret = &name_ret_2;
691 XGetWindowAttributes (dpy, window, &xgwa);
695 unsigned int w, h, bbw, d;
696 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
707 geom_ret->width = xgwa.width;
708 geom_ret->height = xgwa.height;
712 dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
717 if (deskp && filep) {
718 deskp = !(random() & 5); /* if both, desktop 1/5th of the time */
722 if (filep && !done) {
723 osx_load_image_file_async (screen, window, drawable, dir,
728 if (deskp && !done) {
729 osx_grab_desktop_image (screen, window, drawable);
731 *name_ret = strdup ("desktop");
736 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
737 0, 0, xgwa.width, xgwa.height);
742 /* If we got here, we loaded synchronously even though they wanted async.
744 callback (screen, window, drawable, name_ret_2, &geom_ret_2, closure);
748 #endif /* HAVE_COCOA */
751 /* Writes the string "Loading..." in the middle of the screen.
752 This will presumably get blown away when the image finally loads,
753 minutes or hours later...
755 This is called by load_image_async_simple() but not by load_image_async(),
756 since it is assumed that hacks that are loading more than one image
757 *at one time* will be doing something more clever than just blocking
761 print_loading_msg (Screen *screen, Window window)
763 Display *dpy = DisplayOfScreen (screen);
764 XWindowAttributes xgwa;
768 char *fn = get_string_resource (dpy, "labelFont", "Font");
769 const char *text = "Loading...";
772 if (!fn) fn = get_string_resource (dpy, "titleFont", "Font");
773 if (!fn) fn = get_string_resource (dpy, "font", "Font");
774 if (!fn) fn = strdup ("-*-times-bold-r-normal-*-180-*");
775 f = XLoadQueryFont (dpy, fn);
776 if (!f) f = XLoadQueryFont (dpy, "fixed");
781 XGetWindowAttributes (dpy, window, &xgwa);
782 w = XTextWidth (f, text, strlen(text));
784 gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
785 "foreground", "Foreground");
786 gcv.background = get_pixel_resource (dpy, xgwa.colormap,
787 "background", "Background");
789 gc = XCreateGC (dpy, window, GCFont | GCForeground | GCBackground, &gcv);
790 XDrawImageString (dpy, window, gc,
791 (xgwa.width - w) / 2,
792 (xgwa.height - (f->ascent + f->descent)) / 2 + f->ascent,
800 /* Loads an image into the Drawable in the background;
801 when the image is fully loaded, runs the callback.
802 When grabbing desktop images, the Window will be unmapped first.
805 load_image_async (Screen *screen, Window window, Drawable drawable,
806 void (*callback) (Screen *, Window, Drawable,
807 const char *name, XRectangle *geom,
811 load_random_image_1 (screen, window, drawable, callback, closure, 0, 0);
814 struct async_load_state {
821 load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
822 const char *name, XRectangle *geom, void *closure)
824 async_load_state *state = (async_load_state *) closure;
825 state->done_p = True;
826 state->filename = (name ? strdup (name) : 0);
831 load_image_async_simple (async_load_state *state,
836 XRectangle *geometry_ret)
838 if (state && state->done_p) /* done! */
841 *filename_ret = state->filename;
842 else if (state->filename)
843 free (state->filename);
846 *geometry_ret = state->geom;
851 else if (! state) /* first time */
853 state = (async_load_state *) calloc (1, sizeof(*state));
854 state->done_p = False;
855 print_loading_msg (screen, window);
856 load_image_async (screen, window, drawable,
857 load_image_async_simple_cb,
861 else /* still waiting */