+
+#else /* HAVE_COCOA */
+
+struct pipe_closure {
+ FILE *pipe;
+ XtInputId id;
+ Screen *screen;
+ Window xwindow;
+ Drawable drawable;
+ char *directory;
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, XRectangle *geom,
+ void *closure);
+ void *closure;
+};
+
+# ifndef USE_IPHONE
+
+/* Gets the name of an image file to load by running xscreensaver-getimage-file
+ at the end of a pipe. This can be very slow!
+ */
+static FILE *
+open_image_name_pipe (const char *dir)
+{
+ char *cmd = malloc (strlen(dir) * 2 + 100);
+ char *s;
+ strcpy (cmd, "xscreensaver-getimage-file --name ");
+ s = cmd + strlen (cmd);
+ while (*dir) {
+ char c = *dir++;
+ /* put a backslash in front of any character that might confuse sh. */
+ if (! ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '.' || c == '_' || c == '-' || c == '+' || c == '/'))
+ *s++ = '\\';
+ *s++ = c;
+ }
+ *s = 0;
+
+ FILE *pipe = popen (cmd, "r");
+ free (cmd);
+ return pipe;
+}
+
+
+static void
+pipe_cb (XtPointer closure, int *source, XtInputId *id)
+{
+ /* This is not called from a signal handler, so doing stuff here is fine.
+ */
+ struct pipe_closure *clo2 = (struct pipe_closure *) closure;
+ char buf[10240];
+ const char *dir = clo2->directory;
+ char *absfile = 0;
+ fgets (buf, sizeof(buf)-1, clo2->pipe);
+ pclose (clo2->pipe);
+ clo2->pipe = 0;
+ XtRemoveInput (clo2->id);
+ clo2->id = 0;
+
+ /* strip trailing newline */
+ int L = strlen(buf);
+ while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
+ buf[--L] = 0;
+
+ Display *dpy = DisplayOfScreen (clo2->screen);
+ XRectangle geom;
+
+ if (*buf && *buf != '/') /* pathname is relative to dir. */
+ {
+ absfile = malloc (strlen(dir) + strlen(buf) + 10);
+ strcpy (absfile, dir);
+ if (dir[strlen(dir)-1] != '/')
+ strcat (absfile, "/");
+ strcat (absfile, buf);
+ }
+
+ if (! osx_load_image_file (clo2->screen, clo2->xwindow, clo2->drawable,
+ (absfile ? absfile : buf), &geom)) {
+ /* unable to load image - draw colorbars
+ */
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
+ Window r;
+ int x, y;
+ unsigned int w, h, bbw, d;
+ struct stat st;
+
+ /* Log something to syslog so we can tell the difference between
+ corrupted images and broken symlinks. */
+ if (!*buf)
+ fprintf (stderr, "%s: no image filename found\n", progname);
+ else if (! stat (buf, &st))
+ fprintf (stderr, "%s: %s: unparsable\n", progname, buf);
+ else
+ {
+ char buf2[2048];
+ sprintf (buf2, "%.255s: %.1024s", progname, buf);
+ perror (buf2);
+ }
+
+ XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
+ draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
+ 0, 0, w, h);
+ geom.x = geom.y = 0;
+ geom.width = w;
+ geom.height = h;
+ }
+
+ if (absfile) free (absfile);
+ clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable, buf, &geom,
+ clo2->closure);
+ clo2->callback = 0;
+ free (clo2->directory);
+ free (clo2);
+}
+
+
+# else /* USE_IPHONE */
+
+/* Callback for ios_load_random_image(), called after we have loaded an
+ image from the iOS device's Photo Library. See iosgrabimage.m.
+ */
+static void
+ios_load_random_image_cb (void *uiimage, const char *filename, void *closure)
+{
+ struct pipe_closure *clo2 = (struct pipe_closure *) closure;
+ Display *dpy = DisplayOfScreen (clo2->screen);
+ XRectangle geom;
+
+ if (uiimage)
+ {
+ jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (clo2->screen),
+ clo2->drawable,
+ True, uiimage, &geom,
+ 0);
+ }
+ else /* Probably means no images in the gallery. */
+ {
+ XWindowAttributes xgwa;
+ Window r;
+ int x, y;
+ unsigned int w, h, bbw, d;
+ XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
+ XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
+ draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
+ 0, 0, w, h);
+ geom.x = geom.y = 0;
+ geom.width = w;
+ geom.height = h;
+ filename = 0;
+ }
+
+ clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable,
+ filename, &geom, clo2->closure);
+ clo2->callback = 0;
+ if (clo2->directory) free (clo2->directory);
+ free (clo2);
+}
+
+# endif /* USE_IPHONE */
+
+
+static void
+osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
+ const char *dir,
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name,
+ XRectangle *geom,
+ void *closure),
+ void *closure)
+{
+# if 0 /* do it synchronously */
+
+ FILE *pipe = open_image_name_pipe (dir);
+ char buf[10240];
+ *buf = 0;
+ fgets (buf, sizeof(buf)-1, pipe);
+ pclose (pipe);
+
+ /* strip trailing newline */
+ int L = strlen(buf);
+ while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
+ buf[--L] = 0;
+
+ XRectangle geom;
+ if (! osx_load_image_file (screen, xwindow, drawable, buf, &geom)) {
+ /* draw colorbars */
+ abort();
+ }
+ callback (screen, xwindow, drawable, buf, &geom, closure);
+
+# else /* do it asynchronously */
+
+ struct pipe_closure *clo2 = (struct pipe_closure *) calloc (1, sizeof(*clo2));
+
+ clo2->screen = screen;
+ clo2->xwindow = xwindow;
+ clo2->drawable = drawable;
+ clo2->callback = callback;
+ clo2->closure = closure;
+
+# ifndef USE_IPHONE
+ clo2->directory = strdup (dir);
+ clo2->pipe = open_image_name_pipe (dir);
+ clo2->id = XtAppAddInput (XtDisplayToApplicationContext (
+ DisplayOfScreen (screen)),
+ fileno (clo2->pipe),
+ (XtPointer) (XtInputReadMask | XtInputExceptMask),
+ pipe_cb, (XtPointer) clo2);
+# else /* USE_IPHONE */
+ ios_load_random_image (ios_load_random_image_cb, clo2);
+# endif /* USE_IPHONE */
+
+# endif
+}
+
+
+/* Loads an image into the Drawable, returning once the image is loaded.
+ */
+static void
+load_random_image_1 (Screen *screen, Window window, Drawable drawable,
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, XRectangle *geom,
+ void *closure),
+ void *closure,
+ char **name_ret,
+ XRectangle *geom_ret)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ Bool deskp = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
+ Bool filep = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
+ const char *dir = 0;
+ Bool done = False;
+ XRectangle geom_ret_2;
+ char *name_ret_2 = 0;
+
+ if (!drawable) abort();
+
+ if (callback) {
+ geom_ret = &geom_ret_2;
+ name_ret = &name_ret_2;
+ }
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ {
+ Window r;
+ int x, y;
+ unsigned int w, h, bbw, d;
+ XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
+ xgwa.width = w;
+ xgwa.height = h;
+ }
+
+ if (name_ret)
+ *name_ret = 0;
+
+ if (geom_ret) {
+ geom_ret->x = 0;
+ geom_ret->y = 0;
+ geom_ret->width = xgwa.width;
+ geom_ret->height = xgwa.height;
+ }
+
+# ifndef USE_IPHONE
+ if (filep)
+ dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
+
+ if (!dir || !*dir)
+ filep = False;
+# endif /* ! USE_IPHONE */
+
+ if (deskp && filep) {
+ deskp = !(random() & 5); /* if both, desktop 1/5th of the time */
+ filep = !deskp;
+ }
+
+ if (filep && !done) {
+ osx_load_image_file_async (screen, window, drawable, dir,
+ callback, closure);
+ return;
+ }
+
+ if (deskp && !done) {
+ if (osx_grab_desktop_image (screen, window, drawable, &geom_ret_2)) {
+ if (name_ret)
+ *name_ret = strdup ("desktop");
+ done = True;
+ }
+ }
+
+ if (! done)
+ draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
+ 0, 0, xgwa.width, xgwa.height);
+
+ if (callback) {
+ /* If we got here, we loaded synchronously even though they wanted async.
+ */
+ callback (screen, window, drawable, name_ret_2, &geom_ret_2, closure);
+ }
+}
+
+#endif /* HAVE_COCOA */
+
+
+/* Writes the string "Loading..." in the middle of the screen.
+ This will presumably get blown away when the image finally loads,
+ minutes or hours later...
+
+ This is called by load_image_async_simple() but not by load_image_async(),
+ since it is assumed that hacks that are loading more than one image
+ *at one time* will be doing something more clever than just blocking
+ with a blank screen.
+ */
+static void
+print_loading_msg (Screen *screen, Window window)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ XGCValues gcv;
+ XFontStruct *f = 0;
+ GC gc;
+ char *fn = get_string_resource (dpy, "labelFont", "Font");
+ const char *text = "Loading...";
+ int w;
+
+ if (!fn) fn = get_string_resource (dpy, "titleFont", "Font");
+ if (!fn) fn = get_string_resource (dpy, "font", "Font");
+ if (!fn) fn = strdup ("-*-times-bold-r-normal-*-180-*");
+ f = XLoadQueryFont (dpy, fn);
+ if (!f) f = XLoadQueryFont (dpy, "fixed");
+ if (!f) abort();
+ free (fn);
+ fn = 0;
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ w = XTextWidth (f, text, strlen(text));
+
+ gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
+ "foreground", "Foreground");
+ gcv.background = get_pixel_resource (dpy, xgwa.colormap,
+ "background", "Background");
+ gcv.font = f->fid;
+ gc = XCreateGC (dpy, window, GCFont | GCForeground | GCBackground, &gcv);
+ XDrawImageString (dpy, window, gc,
+ (xgwa.width - w) / 2,
+ (xgwa.height - (f->ascent + f->descent)) / 2 + f->ascent,
+ text, strlen(text));
+ XFreeFont (dpy, f);
+ XFreeGC (dpy, gc);
+ XSync (dpy, False);
+}
+
+
+/* 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
+load_image_async (Screen *screen, Window window, Drawable drawable,
+ void (*callback) (Screen *, Window, Drawable,
+ const char *name, XRectangle *geom,
+ void *closure),
+ void *closure)
+{
+ load_random_image_1 (screen, window, drawable, callback, closure, 0, 0);
+}
+
+struct async_load_state {
+ Bool done_p;
+ char *filename;
+ XRectangle geom;
+};
+
+static void
+load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
+ const char *name, XRectangle *geom, void *closure)
+{
+ async_load_state *state = (async_load_state *) closure;
+ state->done_p = True;
+ state->filename = (name ? strdup (name) : 0);
+ state->geom = *geom;
+}
+
+async_load_state *
+load_image_async_simple (async_load_state *state,
+ Screen *screen,
+ Window window,
+ Drawable drawable,
+ char **filename_ret,
+ XRectangle *geometry_ret)
+{
+ if (state && state->done_p) /* done! */
+ {
+ if (filename_ret)
+ *filename_ret = state->filename;
+ else if (state->filename)
+ free (state->filename);
+
+ if (geometry_ret)
+ *geometry_ret = state->geom;
+
+ free (state);
+ return 0;
+ }
+ else if (! state) /* first time */
+ {
+ state = (async_load_state *) calloc (1, sizeof(*state));
+ state->done_p = False;
+ print_loading_msg (screen, window);
+ load_image_async (screen, window, drawable,
+ load_image_async_simple_cb,
+ state);
+ return state;
+ }
+ else /* still waiting */
+ return state;
+}