+typedef struct {
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, XRectangle *geom, 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, XRectangle *geom,
+ 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;
+ Display *dpy = DisplayOfScreen (data->screen);
+ char *name;
+ XRectangle geom = { 0, 0, 0, 0 };
+
+ XtRemoveInput (*id);
+
+ name = get_name (dpy, data->window);
+ get_geometry (dpy, data->window, &geom);
+
+ data->callback (data->screen, data->window, data->drawable,
+ name, &geom, data->closure);
+ if (name) free (name);
+
+ fclose (data->read_pipe);
+ memset (data, 0, sizeof (*data));
+ free (data);
+}
+
+