-/* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998, 2001, 2003
+/* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998, 2001, 2003, 2004
* Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
#include "vroot.h"
#include <X11/Xatom.h>
+#include <X11/Intrinsic.h> /* for XtInputId, etc */
+
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h> /* for waitpid() and associated macros */
+#endif
+
+
extern char *progname;
+extern XtAppContext app;
static Bool error_handler_hit_p = False;
Atom type;
int format;
unsigned long nitems, bytesafter;
- char *version;
+ unsigned char *version;
if (XGetWindowProperty (dpy, window,
XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
0, 1, False, XA_STRING,
&type, &format, &nitems, &bytesafter,
- (unsigned char **) &version)
+ &version)
== Success
&& type != None)
return True;
checkerboard (Screen *screen, Drawable drawable)
{
Display *dpy = DisplayOfScreen (screen);
- int x, y;
+ unsigned int x, y;
int size = 24;
XColor fg, bg;
XGCValues gcv;
GC gc = XCreateGC (dpy, drawable, 0, &gcv);
Colormap cmap;
- int win_width, win_height;
+ unsigned int win_width, win_height;
fg.flags = bg.flags = DoRed|DoGreen|DoBlue;
fg.red = fg.green = fg.blue = 0x0000;
}
}
+
+static char *
+get_name (Display *dpy, Window window)
+{
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *name = 0;
+ Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
+ if (XGetWindowProperty (dpy, window, atom,
+ 0, 1024, False, XA_STRING,
+ &type, &format, &nitems, &bytesafter,
+ &name)
+ == Success
+ && type != None)
+ return strdup((char *) name);
+ else
+ return 0;
+}
+
+
static void
hack_subproc_environment (Display *dpy)
{
}
+/* 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.
*/
-void
-load_random_image (Screen *screen, Window window, Drawable drawable)
+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)
{
Display *dpy = DisplayOfScreen (screen);
char *grabber = get_string_resource ("desktopGrabber", "DesktopGrabber");
XSync (dpy, True);
hack_subproc_environment (dpy);
- system (cmd);
+
+ if (callback)
+ {
+ /* Start the image loading in another fork and return immediately.
+ Invoke the callback function when done.
+ */
+ if (name_ret) abort();
+ fork_exec_cb (cmd, screen, window, drawable, callback, closure);
+ }
+ else
+ {
+ /* Wait for the image to load, and return it immediately.
+ */
+ fork_exec_wait (cmd);
+ if (name_ret)
+ *name_ret = get_name (dpy, window);
+ }
+
free (cmd);
XSync (dpy, True);
}
+
+
+/* Loads an image into the Drawable in the background;
+ when the image is fully loaded, runs the callback.
+ When grabbing desktop images, the Window will be unmapped first.
+ */
+void
+fork_load_random_image (Screen *screen, Window window, Drawable drawable,
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, void *closure),
+ void *closure)
+{
+ load_random_image_1 (screen, window, drawable, callback, closure, 0);
+}
+
+
+#ifndef DEBUG
+
+/* Loads an image into the Drawable, returning once the image is loaded.
+ When grabbing desktop images, the Window will be unmapped first.
+ */
+void
+load_random_image (Screen *screen, Window window, Drawable drawable,
+ char **name_ret)
+{
+ load_random_image_1 (screen, window, drawable, 0, 0, name_ret);
+}
+
+#else /* DEBUG */
+
+typedef struct {
+ char **name_ret;
+ Bool done;
+} debug_closure;
+
+static void
+debug_cb (Screen *screen, Window window, Drawable drawable,
+ const char *name, void *closure)
+{
+ debug_closure *data = (debug_closure *) closure;
+ fprintf (stderr, "%s: GRAB DEBUG: callback\n", progname);
+ if (data->name_ret)
+ *data->name_ret = (name ? strdup (name) : 0);
+ data->done = True;
+}
+
+void
+load_random_image (Screen *screen, Window window, Drawable drawable,
+ char **name_ret)
+{
+ debug_closure data;
+ data.name_ret = name_ret;
+ data.done = False;
+ fprintf (stderr, "%s: GRAB DEBUG: forking\n", progname);
+ fork_load_random_image (screen, window, drawable, debug_cb, &data);
+ while (! data.done)
+ {
+ fprintf (stderr, "%s: GRAB DEBUG: waiting\n", progname);
+ if (XtAppPending (app) & XtIMAlternateInput)
+ XtAppProcessEvent (app, XtIMAlternateInput);
+ usleep (50000);
+ }
+ fprintf (stderr, "%s: GRAB DEBUG: done\n", progname);
+}
+
+#endif /* DEBUG */