+/* Spawn a program, and wait for it to finish.
+ If we just use system() for this, then sometimes the subprocess
+ doesn't die when *this* process is sent a TERM signal. Perhaps
+ this is due to the intermediate /bin/sh that system() uses to
+ parse arguments? I'm not sure. But using fork() and execvp()
+ here seems to close the race.
+ */
+
+static void
+exec_simple_command (const char *command)
+{
+ char *av[1024];
+ int ac = 0;
+ char *token = strtok (strdup(command), " \t");
+ while (token)
+ {
+ av[ac++] = token;
+ token = strtok(0, " \t");
+ }
+ av[ac] = 0;
+
+ execvp (av[0], av); /* shouldn't return. */
+}
+
+static void
+fork_exec_wait (const char *command)
+{
+ char buf [255];
+ pid_t forked;
+ int status;
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ sprintf (buf, "%s: couldn't fork", progname);
+ perror (buf);
+ return;
+
+ case 0:
+ exec_simple_command (command);
+ exit (1); /* exits child fork */
+ break;
+
+ default:
+ waitpid (forked, &status, 0);
+ break;
+ }
+}
+
+
+typedef struct {
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, void *closure);
+ Screen *screen;
+ Window window;
+ Drawable drawable;
+ void *closure;
+ FILE *read_pipe;
+ FILE *write_pipe;
+ XtInputId pipe_id;
+} grabclient_data;
+
+
+static void finalize_cb (XtPointer closure, int *fd, XtIntervalId *id);
+
+static void
+fork_exec_cb (const char *command,
+ Screen *screen, Window window, Drawable drawable,
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, void *closure),
+ void *closure)
+{
+ grabclient_data *data;
+ char buf [255];
+ pid_t forked;
+
+ int fds [2];
+
+ if (pipe (fds))
+ {
+ sprintf (buf, "%s: creating pipe", progname);
+ perror (buf);
+ exit (1);
+ }
+
+ data = (grabclient_data *) calloc (1, sizeof(*data));
+ data->callback = callback;
+ data->closure = closure;
+ data->screen = screen;
+ data->window = window;
+ data->drawable = drawable;
+ data->read_pipe = fdopen (fds[0], "r");
+ data->write_pipe = fdopen (fds[1], "w");
+
+ if (!data->read_pipe || !data->write_pipe)
+ {
+ sprintf (buf, "%s: fdopen", progname);
+ perror (buf);
+ exit (1);
+ }
+
+ data->pipe_id =
+ XtAppAddInput (app, fileno (data->read_pipe),
+ (XtPointer) (XtInputReadMask | XtInputExceptMask),
+ finalize_cb, (XtPointer) data);
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ sprintf (buf, "%s: couldn't fork", progname);
+ perror (buf);
+ return;
+
+ case 0: /* child */
+
+ fclose (data->read_pipe);
+ data->read_pipe = 0;
+
+ /* clone the write pipe onto stdout so that it gets closed
+ when the fork exits. This will shut down the pipe and
+ signal the parent.
+ */
+ close (fileno (stdout));
+ dup2 (fds[1], fileno (stdout));
+ close (fds[1]);
+
+ close (fileno (stdin)); /* for kicks */
+
+ exec_simple_command (command);
+ exit (1); /* exits child fork */
+ break;
+
+ default: /* parent */
+ fclose (data->write_pipe);
+ data->write_pipe = 0;
+ break;
+ }
+}
+
+
+/* Called in the parent when the forked process dies.
+ Runs the caller's callback, and cleans up.
+ */
+static void
+finalize_cb (XtPointer closure, int *fd, XtIntervalId *id)
+{
+ grabclient_data *data = (grabclient_data *) closure;
+ char *name;
+
+ XtRemoveInput (*id);
+
+ name = get_name (DisplayOfScreen (data->screen), data->window);
+ data->callback (data->screen, data->window, data->drawable,
+ name, data->closure);
+ free (name);
+
+ fclose (data->read_pipe);
+ memset (data, 0, sizeof (*data));
+ free (data);
+}
+
+
+/* Loads an image into the Drawable.
+ When grabbing desktop images, the Window will be unmapped first.
+ */
+static void
+load_random_image_1 (Screen *screen, Window window, Drawable drawable,
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, void *closure),
+ void *closure,
+ char **name_ret)