1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998, 2001, 2003, 2004
2 * Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* This file contains code for running an external program to grab an image
14 onto the given window. The external program in question must take a
15 window ID as its argument, e.g., the "xscreensaver-getimage" program
16 in the hacks/ directory.
18 That program links against utils/grabimage.c, which happens to export the
19 same API as this program (utils/grabclient.c).
23 #include "grabscreen.h"
24 #include "resources.h"
27 #include <X11/Xatom.h>
29 #include <X11/Intrinsic.h> /* for XtInputId, etc */
35 #ifdef HAVE_SYS_WAIT_H
36 # include <sys/wait.h> /* for waitpid() and associated macros */
40 extern char *progname;
41 extern XtAppContext app;
44 static Bool error_handler_hit_p = False;
47 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
49 error_handler_hit_p = True;
54 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
57 drawable_window_p (Display *dpy, Drawable d)
59 XErrorHandler old_handler;
60 XWindowAttributes xgwa;
63 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
64 error_handler_hit_p = False;
65 XGetWindowAttributes (dpy, d, &xgwa);
67 XSetErrorHandler (old_handler);
70 if (!error_handler_hit_p)
71 return True; /* It's a Window. */
73 return False; /* It's a Pixmap, or an invalid ID. */
78 xscreensaver_window_p (Display *dpy, Window window)
82 unsigned long nitems, bytesafter;
83 unsigned char *version;
84 if (XGetWindowProperty (dpy, window,
85 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
86 0, 1, False, XA_STRING,
87 &type, &format, &nitems, &bytesafter,
96 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
97 on a window whose depth is not the maximal depth of the screen? Or
98 something. Anyway, things don't work unless we: use SubwindowMode for
99 the real root window (or a legitimate virtual root window); but do not
100 use SubwindowMode for the xscreensaver window. I make no attempt to
104 use_subwindow_mode_p(Screen *screen, Window window)
106 if (window != VirtualRootWindowOfScreen(screen))
108 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
116 checkerboard (Screen *screen, Drawable drawable)
118 Display *dpy = DisplayOfScreen (screen);
123 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
125 unsigned int win_width, win_height;
127 fg.flags = bg.flags = DoRed|DoGreen|DoBlue;
128 fg.red = fg.green = fg.blue = 0x0000;
129 bg.red = bg.green = bg.blue = 0x4444;
133 if (drawable_window_p (dpy, drawable))
135 XWindowAttributes xgwa;
136 XGetWindowAttributes (dpy, drawable, &xgwa);
137 win_width = xgwa.width;
138 win_height = xgwa.height;
139 cmap = xgwa.colormap;
140 screen = xgwa.screen;
142 else /* it's a pixmap */
144 XWindowAttributes xgwa;
148 XGetWindowAttributes (dpy, RootWindowOfScreen (screen), &xgwa);
149 cmap = xgwa.colormap;
150 XGetGeometry (dpy, drawable,
151 &root, &x, &y, &win_width, &win_height, &bw, &d);
154 /* Allocate black and gray, but don't hold them locked. */
155 if (XAllocColor (dpy, cmap, &fg))
156 XFreeColors (dpy, cmap, &fg.pixel, 1, 0);
157 if (XAllocColor (dpy, cmap, &bg))
158 XFreeColors (dpy, cmap, &bg.pixel, 1, 0);
160 XSetForeground (dpy, gc, bg.pixel);
161 XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
162 XSetForeground (dpy, gc, fg.pixel);
163 for (y = 0; y < win_height; y += size+size)
164 for (x = 0; x < win_width; x += size+size)
166 XFillRectangle (dpy, drawable, gc, x, y, size, size);
167 XFillRectangle (dpy, drawable, gc, x+size, y+size, size, size);
173 get_name (Display *dpy, Window window)
177 unsigned long nitems, bytesafter;
178 unsigned char *name = 0;
179 Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
180 if (XGetWindowProperty (dpy, window, atom,
181 0, 1024, False, XA_STRING,
182 &type, &format, &nitems, &bytesafter,
186 return strdup((char *) name);
193 hack_subproc_environment (Display *dpy)
195 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
196 the spawned processes inherit is what we are actually using.
198 const char *odpy = DisplayString (dpy);
199 char *ndpy = (char *) malloc(strlen(odpy) + 20);
200 strcpy (ndpy, "DISPLAY=");
203 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
204 any more, right? It's not Posix, but everyone seems to have it. */
208 #endif /* HAVE_PUTENV */
212 /* Spawn a program, and wait for it to finish.
213 If we just use system() for this, then sometimes the subprocess
214 doesn't die when *this* process is sent a TERM signal. Perhaps
215 this is due to the intermediate /bin/sh that system() uses to
216 parse arguments? I'm not sure. But using fork() and execvp()
217 here seems to close the race.
221 exec_simple_command (const char *command)
225 char *token = strtok (strdup(command), " \t");
229 token = strtok(0, " \t");
233 execvp (av[0], av); /* shouldn't return. */
237 fork_exec_wait (const char *command)
243 switch ((int) (forked = fork ()))
246 sprintf (buf, "%s: couldn't fork", progname);
251 exec_simple_command (command);
252 exit (1); /* exits child fork */
256 waitpid (forked, &status, 0);
263 void (*callback) (Screen *, Window, Drawable,
264 const char *name, void *closure);
275 static void finalize_cb (XtPointer closure, int *fd, XtIntervalId *id);
278 fork_exec_cb (const char *command,
279 Screen *screen, Window window, Drawable drawable,
280 void (*callback) (Screen *, Window, Drawable,
281 const char *name, void *closure),
284 grabclient_data *data;
292 sprintf (buf, "%s: creating pipe", progname);
297 data = (grabclient_data *) calloc (1, sizeof(*data));
298 data->callback = callback;
299 data->closure = closure;
300 data->screen = screen;
301 data->window = window;
302 data->drawable = drawable;
303 data->read_pipe = fdopen (fds[0], "r");
304 data->write_pipe = fdopen (fds[1], "w");
306 if (!data->read_pipe || !data->write_pipe)
308 sprintf (buf, "%s: fdopen", progname);
314 XtAppAddInput (app, fileno (data->read_pipe),
315 (XtPointer) (XtInputReadMask | XtInputExceptMask),
316 finalize_cb, (XtPointer) data);
318 switch ((int) (forked = fork ()))
321 sprintf (buf, "%s: couldn't fork", progname);
327 fclose (data->read_pipe);
330 /* clone the write pipe onto stdout so that it gets closed
331 when the fork exits. This will shut down the pipe and
334 close (fileno (stdout));
335 dup2 (fds[1], fileno (stdout));
338 close (fileno (stdin)); /* for kicks */
340 exec_simple_command (command);
341 exit (1); /* exits child fork */
344 default: /* parent */
345 fclose (data->write_pipe);
346 data->write_pipe = 0;
352 /* Called in the parent when the forked process dies.
353 Runs the caller's callback, and cleans up.
356 finalize_cb (XtPointer closure, int *fd, XtIntervalId *id)
358 grabclient_data *data = (grabclient_data *) closure;
363 name = get_name (DisplayOfScreen (data->screen), data->window);
364 data->callback (data->screen, data->window, data->drawable,
365 name, data->closure);
368 fclose (data->read_pipe);
369 memset (data, 0, sizeof (*data));
374 /* Loads an image into the Drawable.
375 When grabbing desktop images, the Window will be unmapped first.
378 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
379 void (*callback) (Screen *, Window, Drawable,
380 const char *name, void *closure),
384 Display *dpy = DisplayOfScreen (screen);
385 char *grabber = get_string_resource ("desktopGrabber", "DesktopGrabber");
389 if (!grabber || !*grabber)
392 "%s: resources installed incorrectly: \"desktopGrabber\" is unset!\n",
397 sprintf (id, "0x%lx 0x%lx",
398 (unsigned long) window,
399 (unsigned long) drawable);
400 cmd = (char *) malloc (strlen(grabber) + strlen(id) + 1);
402 /* Needn't worry about buffer overflows here, because the buffer is
403 longer than the length of the format string, and the length of what
404 we're putting into it. So the only way to crash would be if the
405 format string itself was corrupted, but that comes from the
406 resource database, and if hostile forces have access to that,
407 then the game is already over.
409 sprintf (cmd, grabber, id);
411 /* In case "cmd" fails, leave some random image on the screen, not just
412 black or white, so that it's more obvious what went wrong. */
413 checkerboard (screen, drawable);
416 hack_subproc_environment (dpy);
420 /* Start the image loading in another fork and return immediately.
421 Invoke the callback function when done.
423 if (name_ret) abort();
424 fork_exec_cb (cmd, screen, window, drawable, callback, closure);
428 /* Wait for the image to load, and return it immediately.
430 fork_exec_wait (cmd);
432 *name_ret = get_name (dpy, window);
440 /* Loads an image into the Drawable in the background;
441 when the image is fully loaded, runs the callback.
442 When grabbing desktop images, the Window will be unmapped first.
445 fork_load_random_image (Screen *screen, Window window, Drawable drawable,
446 void (*callback) (Screen *, Window, Drawable,
447 const char *name, void *closure),
450 load_random_image_1 (screen, window, drawable, callback, closure, 0);
456 /* Loads an image into the Drawable, returning once the image is loaded.
457 When grabbing desktop images, the Window will be unmapped first.
460 load_random_image (Screen *screen, Window window, Drawable drawable,
463 load_random_image_1 (screen, window, drawable, 0, 0, name_ret);
474 debug_cb (Screen *screen, Window window, Drawable drawable,
475 const char *name, void *closure)
477 debug_closure *data = (debug_closure *) closure;
478 fprintf (stderr, "%s: GRAB DEBUG: callback\n", progname);
480 *data->name_ret = (name ? strdup (name) : 0);
485 load_random_image (Screen *screen, Window window, Drawable drawable,
489 data.name_ret = name_ret;
491 fprintf (stderr, "%s: GRAB DEBUG: forking\n", progname);
492 fork_load_random_image (screen, window, drawable, debug_cb, &data);
495 fprintf (stderr, "%s: GRAB DEBUG: waiting\n", progname);
496 if (XtAppPending (app) & XtIMAlternateInput)
497 XtAppProcessEvent (app, XtIMAlternateInput);
500 fprintf (stderr, "%s: GRAB DEBUG: done\n", progname);