From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / utils / grabclient.c
1 /* xscreensaver, Copyright (c) 1992-2016 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* This file contains code for running an external program to grab an image
13    onto the given window.  The external program in question must take a
14    window ID as its argument, e.g., the "xscreensaver-getimage" program
15    in the hacks/ directory.
16
17    That program links against utils/grabimage.c, which happens to export the
18    same API as this program (utils/grabclient.c).
19  */
20
21 /* This code is a mess.  There's two decades of history in this file.
22    There are several distinct paths through this file depending on what
23    platform it's being compiled for:
24
25
26    X11 execution path:
27
28        load_image_async CB
29            load_random_image_x11
30                fork_exec_cb
31                    "xscreensaver-getimage 0xWINDOW 0xPIXMAP"
32                        "xscreensaver-getimage-file --name /DIR"
33                        draw_colorbars
34                        XPutImage
35                    XtAppAddInput xscreensaver_getimage_cb
36        ...
37        xscreensaver_getimage_cb
38            get_name_from_xprops
39            get_original_geometry_from_xprops
40            CB name, geom, closure
41
42
43    MacOS execution path:
44
45        load_image_async CB
46            load_random_image_cocoa
47                osx_grab_desktop_image (osxgrabscreen.m, MacOS version)
48                    copy_framebuffer_to_ximage
49                    XPutImage
50                draw_colorbars
51                osx_load_image_file_async
52                    open_image_name_pipe
53                        "xscreensaver-getimage-file --name /DIR"
54                  XtAppAddInput xscreensaver_getimage_file_cb
55        ...
56        xscreensaver_getimage_file_cb
57            osx_load_image_file
58                CB name, geom, closure
59
60
61    iOS execution path:
62
63        load_image_async CB
64            load_random_image_cocoa
65                osx_grab_desktop_image (osxgrabscreen.m, iOS version)
66                    CGWindowListCreateImage
67                    jwxyz_draw_NSImage_or_CGImage
68                draw_colorbars
69                ios_load_random_image
70                    ios_load_random_image_cb
71                        jwxyz_draw_NSImage_or_CGImage
72                        CB name, geom, closure
73
74
75    Andrid execution path:
76
77        load_image_async CB
78            load_random_image_android
79                jwxyz_load_random_image (jwxyz-android.c)
80                    XPutImage
81                draw_colorbars
82                CB name, geom, closure
83  */
84
85 #include "utils.h"
86 #include "grabscreen.h"
87 #include "resources.h"
88 #include "yarandom.h"
89
90 #ifdef HAVE_JWXYZ
91 # include "jwxyz.h"
92 # include "colorbars.h"
93 #else /* !HAVE_COCOA -- real Xlib */
94 # include "vroot.h"
95 # include <X11/Xatom.h>
96 # include <X11/Intrinsic.h>   /* for XtInputId, etc */
97 #endif /* !HAVE_COCOA */
98
99 #include <sys/stat.h>
100
101 #ifdef HAVE_UNISTD_H
102 # include <unistd.h>
103 #endif
104 #ifdef HAVE_SYS_WAIT_H
105 # include <sys/wait.h>          /* for waitpid() and associated macros */
106 #endif
107
108
109 extern char *progname;
110
111 static void print_loading_msg (Screen *, Window);
112
113
114 /* Used for pipe callbacks in X11 or OSX mode.
115    X11: this is the xscreensaver_getimage_cb closure,
116      when waiting on the fork of "xscreensaver-getimage"
117    OSX: this is the xscreensaver_getimage_file_cb closure,
118      when waiting on the fork of "xscreensaver-getimage-file"
119  */
120 typedef struct {
121   void (*callback) (Screen *, Window, Drawable,
122                     const char *name, XRectangle *geom, void *closure);
123   Screen *screen;
124   Window window;
125   Drawable drawable;
126   void *closure;
127   XtInputId pipe_id;
128   FILE *pipe;
129
130 # if !defined(USE_IPHONE) && !defined(HAVE_COCOA)  /* Real X11 */
131   pid_t pid;
132 # endif
133
134 # if !defined(USE_IPHONE) && defined(HAVE_COCOA)   /* Desktop OSX */
135   char *directory;
136 # endif
137
138 } xscreensaver_getimage_data;
139
140
141 #if !defined(HAVE_COCOA) && !defined(HAVE_ANDROID)   /* Real X11 */
142
143 static Bool error_handler_hit_p = False;
144
145 static int
146 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
147 {
148   error_handler_hit_p = True;
149   return 0;
150 }
151
152
153 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
154  */
155 static Bool
156 drawable_window_p (Display *dpy, Drawable d)
157 {
158   XErrorHandler old_handler;
159   XWindowAttributes xgwa;
160
161   XSync (dpy, False);
162   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
163   error_handler_hit_p = False;
164   XGetWindowAttributes (dpy, d, &xgwa);
165   XSync (dpy, False);
166   XSetErrorHandler (old_handler);
167   XSync (dpy, False);
168
169   if (!error_handler_hit_p)
170     return True;   /* It's a Window. */
171   else
172     return False;  /* It's a Pixmap, or an invalid ID. */
173 }
174
175
176 static Bool
177 xscreensaver_window_p (Display *dpy, Window window)
178 {
179   Atom type;
180   int format;
181   unsigned long nitems, bytesafter;
182   unsigned char *version;
183   if (XGetWindowProperty (dpy, window,
184                           XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
185                           0, 1, False, XA_STRING,
186                           &type, &format, &nitems, &bytesafter,
187                           &version)
188       == Success
189       && type != None)
190     return True;
191   return False;
192 }
193
194
195 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
196    on a window whose depth is not the maximal depth of the screen?  Or
197    something.  Anyway, things don't work unless we: use SubwindowMode for
198    the real root window (or a legitimate virtual root window); but do not
199    use SubwindowMode for the xscreensaver window.  I make no attempt to
200    explain.
201  */
202 Bool
203 use_subwindow_mode_p (Screen *screen, Window window)
204 {
205   if (window != VirtualRootWindowOfScreen(screen))
206     return False;
207   else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
208     return False;
209   else
210     return True;
211 }
212
213
214 static void
215 checkerboard (Screen *screen, Drawable drawable)
216 {
217   Display *dpy = DisplayOfScreen (screen);
218   unsigned int x, y;
219   int size = 24;
220   XColor fg, bg;
221   XGCValues gcv;
222   GC gc = XCreateGC (dpy, drawable, 0, &gcv);
223   Colormap cmap;
224   unsigned int win_width, win_height;
225
226   fg.flags = bg.flags = DoRed|DoGreen|DoBlue;
227   fg.red = fg.green = fg.blue = 0x0000;
228   bg.red = bg.green = bg.blue = 0x4444;
229   fg.pixel = 0;
230   bg.pixel = 1;
231
232   if (drawable_window_p (dpy, drawable))
233     {
234       XWindowAttributes xgwa;
235       XGetWindowAttributes (dpy, drawable, &xgwa);
236       win_width = xgwa.width;
237       win_height = xgwa.height;
238       cmap = xgwa.colormap;
239       screen = xgwa.screen;
240     }
241   else  /* it's a pixmap */
242     {
243       XWindowAttributes xgwa;
244       Window root;
245       int x, y;
246       unsigned int bw, d;
247       XGetWindowAttributes (dpy, RootWindowOfScreen (screen), &xgwa);
248       cmap = xgwa.colormap;
249       XGetGeometry (dpy, drawable,
250                     &root, &x, &y, &win_width, &win_height, &bw, &d);
251     }
252
253   /* Allocate black and gray, but don't hold them locked. */
254   if (XAllocColor (dpy, cmap, &fg))
255     XFreeColors (dpy, cmap, &fg.pixel, 1, 0);
256   if (XAllocColor (dpy, cmap, &bg))
257     XFreeColors (dpy, cmap, &bg.pixel, 1, 0);
258
259   XSetForeground (dpy, gc, bg.pixel);
260   XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
261   XSetForeground (dpy, gc, fg.pixel);
262   for (y = 0; y < win_height; y += size+size)
263     for (x = 0; x < win_width; x += size+size)
264       {
265         XFillRectangle (dpy, drawable, gc, x,      y,      size, size);
266         XFillRectangle (dpy, drawable, gc, x+size, y+size, size, size);
267       }
268   XFreeGC (dpy, gc);
269 }
270
271
272 /* Read the image's original name off of the window's X properties.
273    Used only when running "real" X11, not jwxyz.
274  */
275 static char *
276 get_name_from_xprops (Display *dpy, Window window)
277 {
278   Atom type;
279   int format;
280   unsigned long nitems, bytesafter;
281   unsigned char *name = 0;
282   Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
283   if (XGetWindowProperty (dpy, window, atom,
284                           0, 1024, False, XA_STRING,
285                           &type, &format, &nitems, &bytesafter,
286                           &name)
287       == Success
288       && type != None)
289     return (char *) name;
290   else
291     return 0;
292 }
293
294
295 /* Read the image's original geometry off of the window's X properties.
296    Used only when running "real" X11, not jwxyz.
297  */
298 static Bool
299 get_original_geometry_from_xprops (Display *dpy, Window window, XRectangle *ret)
300 {
301   Atom type;
302   int format;
303   unsigned long nitems, bytesafter;
304   unsigned char *name = 0;
305   Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
306   int x, y;
307   unsigned int w, h;
308   if (XGetWindowProperty (dpy, window, atom,
309                           0, 1024, False, XA_STRING,
310                           &type, &format, &nitems, &bytesafter,
311                           &name)
312       == Success
313       && type != None)
314     {
315       int flags = XParseGeometry ((char *) name, &x, &y, &w, &h);
316       free (name);
317       /* Require all four, and don't allow negative positions. */
318       if (flags == (XValue|YValue|WidthValue|HeightValue))
319         {
320           ret->x = x;
321           ret->y = y;
322           ret->width  = w;
323           ret->height = h;
324           return True;
325         }
326       else
327         return False;
328     }
329   else
330     return False;
331 }
332
333
334 static void
335 hack_subproc_environment (Display *dpy)
336 {
337   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
338      the spawned processes inherit is what we are actually using.
339    */
340   const char *odpy = DisplayString (dpy);
341   char *ndpy = (char *) malloc(strlen(odpy) + 20);
342   strcpy (ndpy, "DISPLAY=");
343   strcat (ndpy, odpy);
344
345   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
346      any more, right?  It's not Posix, but everyone seems to have it. */
347 # ifdef HAVE_PUTENV
348   if (putenv (ndpy))
349     abort ();
350 # endif /* HAVE_PUTENV */
351
352   /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
353      glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2, MacOS)
354      do not.  So we must leak it (and/or the previous setting). Yay.
355    */
356 }
357
358
359 /* Spawn a program, and wait for it to finish.
360    If we just use system() for this, then sometimes the subprocess
361    doesn't die when *this* process is sent a TERM signal.  Perhaps
362    this is due to the intermediate /bin/sh that system() uses to
363    parse arguments?  I'm not sure.  But using fork() and execvp()
364    here seems to close the race.
365
366    Used to execute "xscreensaver-getimage".
367    Used only when running "real" X11, not jwxyz.
368  */
369 static void
370 exec_simple_command (const char *command)
371 {
372   char *av[1024];
373   int ac = 0;
374   char *token = strtok (strdup(command), " \t");
375   while (token)
376     {
377       av[ac++] = token;
378       token = strtok(0, " \t");
379     }
380   av[ac] = 0;
381
382   execvp (av[0], av);   /* shouldn't return. */
383 }
384
385
386 static void xscreensaver_getimage_cb (XtPointer closure,
387                                       int *fd, XtIntervalId *id);
388
389 /* Spawn a program, and run the callback when it finishes.
390    Used to execute "xscreensaver-getimage".
391    Used only when running "real" X11, not jwxyz.
392  */
393 static void
394 fork_exec_cb (const char *command,
395               Screen *screen, Window window, Drawable drawable,
396               void (*callback) (Screen *, Window, Drawable,
397                                 const char *name, XRectangle *geom,
398                                 void *closure),
399               void *closure)
400 {
401   XtAppContext app = XtDisplayToApplicationContext (DisplayOfScreen (screen));
402   xscreensaver_getimage_data *data;
403   char buf [255];
404   pid_t forked;
405   FILE *wpipe;
406
407   int fds [2];
408
409   if (pipe (fds))
410     {
411       sprintf (buf, "%s: creating pipe", progname);
412       perror (buf);
413       exit (1);
414     }
415
416   data = (xscreensaver_getimage_data *) calloc (1, sizeof(*data));
417   data->callback   = callback;
418   data->closure    = closure;
419   data->screen     = screen;
420   data->window     = window;
421   data->drawable   = drawable;
422   data->pipe       = fdopen (fds[0], "r");
423   wpipe            = fdopen (fds[1], "w");   /* Is this necessary? */
424
425   if (!data->pipe || !wpipe)
426     {
427       sprintf (buf, "%s: fdopen", progname);
428       perror (buf);
429       exit (1);
430     }
431
432   data->pipe_id =
433     XtAppAddInput (app, fileno (data->pipe),
434                    (XtPointer) (XtInputReadMask | XtInputExceptMask),
435                    xscreensaver_getimage_cb, (XtPointer) data);
436
437   forked = fork ();
438   switch ((int) forked)
439     {
440     case -1:
441       sprintf (buf, "%s: couldn't fork", progname);
442       perror (buf);
443       return;
444
445     case 0:                                     /* child */
446
447       fclose (data->pipe);
448       data->pipe = 0;
449
450       /* clone the write pipe onto stdout so that it gets closed
451          when the fork exits.  This will shut down the pipe and
452          signal the parent.
453        */
454       close (fileno (stdout));
455       dup2 (fds[1], fileno (stdout));
456       close (fds[1]);
457
458       close (fileno (stdin)); /* for kicks */
459
460       exec_simple_command (command);
461       exit (1);  /* exits child fork */
462       break;
463
464     default:                                    /* parent */
465       fclose (wpipe);
466       data->pid = forked;
467       break;
468     }
469 }
470
471
472 /* Called in the parent when the forked process dies.
473    Runs the caller's callback, and cleans up.
474    This runs when "xscreensaver-getimage" exits.
475    Used only when running "real" X11, not jwxyz.
476  */
477 static void
478 xscreensaver_getimage_cb (XtPointer closure, int *fd, XtIntervalId *id)
479 {
480   xscreensaver_getimage_data *data = (xscreensaver_getimage_data *) closure;
481   Display *dpy = DisplayOfScreen (data->screen);
482   char *name;
483   XRectangle geom = { 0, 0, 0, 0 };
484
485   XtRemoveInput (*id);
486
487   name = get_name_from_xprops (dpy, data->window);
488   get_original_geometry_from_xprops (dpy, data->window, &geom);
489
490   data->callback (data->screen, data->window, data->drawable,
491                   name, &geom, data->closure);
492   if (name) free (name);
493
494   fclose (data->pipe);
495
496   if (data->pid)        /* reap zombies */
497     {
498       int status;
499       waitpid (data->pid, &status, 0);
500       data->pid = 0;
501     }
502
503   memset (data, 0, sizeof (*data));
504   free (data);
505 }
506
507
508 /* Loads an image into the Drawable.
509    When grabbing desktop images, the Window will be unmapped first.
510    Used only when running "real" X11, not jwxyz.
511  */
512 static void
513 load_random_image_x11 (Screen *screen, Window window, Drawable drawable,
514                        void (*callback) (Screen *, Window, Drawable,
515                                          const char *name, XRectangle *geom,
516                                          void *closure),
517                        void *closure)
518 {
519   Display *dpy = DisplayOfScreen (screen);
520   char *grabber = get_string_resource(dpy, "desktopGrabber", "DesktopGrabber");
521   char *cmd;
522   char id[200];
523
524   if (!grabber || !*grabber)
525     {
526       fprintf (stderr,
527          "%s: resources installed incorrectly: \"desktopGrabber\" is unset!\n",
528                progname);
529       exit (1);
530     }
531
532   sprintf (id, "0x%lx 0x%lx",
533            (unsigned long) window,
534            (unsigned long) drawable);
535   cmd = (char *) malloc (strlen(grabber) + strlen(id) + 1);
536
537   /* Needn't worry about buffer overflows here, because the buffer is
538      longer than the length of the format string, and the length of what
539      we're putting into it.  So the only way to crash would be if the
540      format string itself was corrupted, but that comes from the
541      resource database, and if hostile forces have access to that,
542      then the game is already over.
543    */
544   sprintf (cmd, grabber, id);
545   free (grabber);
546   grabber = 0;
547
548   /* In case "cmd" fails, leave some random image on the screen, not just
549      black or white, so that it's more obvious what went wrong. */
550   checkerboard (screen, drawable);
551   if (window == drawable)
552     print_loading_msg (screen, window);
553
554   XSync (dpy, True);
555   hack_subproc_environment (dpy);
556
557   /* Start the image loading in another fork and return immediately.
558      Invoke the callback function when done. */
559   fork_exec_cb (cmd, screen, window, drawable, callback, closure);
560
561   free (cmd);
562   XSync (dpy, True);
563 }
564
565 #elif defined (HAVE_COCOA) /* OSX or iOS */
566
567 # ifndef USE_IPHONE   /* HAVE_COCOA && !USE_IPHONE -- desktop OSX */
568
569 # define BACKSLASH(c) \
570   (! ((c >= 'a' && c <= 'z') || \
571       (c >= 'A' && c <= 'Z') || \
572       (c >= '0' && c <= '9') || \
573       c == '.' || c == '_' || c == '-' || c == '+' || c == '/'))
574
575 /* Gets the name of an image file to load by running xscreensaver-getimage-file
576    at the end of a pipe.  This can be very slow!
577  */
578 static FILE *
579 open_image_name_pipe (const char *dir)
580 {
581   char *s;
582
583   /* /bin/sh on OS X 10.10 wipes out the PATH. */
584   const char *path = getenv("PATH");
585   char *cmd = s = malloc ((strlen(dir) + strlen(path)) * 2 + 100);
586   strcpy (s, "/bin/sh -c 'export PATH=");
587   s += strlen (s);
588   while (*path) {
589     char c = *path++;
590     if (BACKSLASH(c)) *s++ = '\\';
591     *s++ = c;
592   }
593   strcpy (s, "; ");
594   s += strlen (s);
595
596   strcpy (s, "xscreensaver-getimage-file --name ");
597   s += strlen (s);
598   while (*dir) {
599     char c = *dir++;
600     if (BACKSLASH(c)) *s++ = '\\';
601     *s++ = c;
602   }
603
604   strcpy (s, "'");
605   s += strlen (s);
606
607   *s = 0;
608
609   FILE *pipe = popen (cmd, "r");
610   free (cmd);
611   return pipe;
612 }
613
614
615 static void
616 xscreensaver_getimage_file_cb (XtPointer closure, int *source, XtInputId *id)
617 {
618   /* This is not called from a signal handler, so doing stuff here is fine.
619    */
620   xscreensaver_getimage_data *clo2 = (xscreensaver_getimage_data *) closure;
621   char buf[10240];
622   const char *dir = clo2->directory;
623   char *absfile = 0;
624   *buf = 0;
625   fgets (buf, sizeof(buf)-1, clo2->pipe);
626   pclose (clo2->pipe);
627   clo2->pipe = 0;
628   XtRemoveInput (clo2->pipe_id);
629   clo2->pipe_id = 0;
630
631   /* strip trailing newline */
632   int L = strlen(buf);
633   while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
634     buf[--L] = 0;
635
636   Display *dpy = DisplayOfScreen (clo2->screen);
637   XRectangle geom;
638
639   if (*buf && *buf != '/')              /* pathname is relative to dir. */
640     {
641       absfile = malloc (strlen(dir) + strlen(buf) + 10);
642       strcpy (absfile, dir);
643       if (dir[strlen(dir)-1] != '/')
644         strcat (absfile, "/");
645       strcat (absfile, buf);
646     }
647
648   if (! osx_load_image_file (clo2->screen, clo2->window, clo2->drawable,
649                              (absfile ? absfile : buf), &geom)) {
650     /* unable to load image - draw colorbars 
651      */
652     XWindowAttributes xgwa;
653     XGetWindowAttributes (dpy, clo2->window, &xgwa);
654     Window r;
655     int x, y;
656     unsigned int w, h, bbw, d;
657     struct stat st;
658
659     /* Log something to syslog so we can tell the difference between
660        corrupted images and broken symlinks. */
661     if (!*buf)
662       fprintf (stderr, "%s: no image filename found\n", progname);
663     else if (! stat (buf, &st))
664       fprintf (stderr, "%s: %s: unparsable\n", progname, buf);
665     else
666       {
667         char buf2[2048];
668         sprintf (buf2, "%.255s: %.1024s", progname, buf);
669         perror (buf2);
670       }
671
672     XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
673     draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
674                     0, 0, w, h);
675     geom.x = geom.y = 0;
676     geom.width = w;
677     geom.height = h;
678   }
679
680   /* Take the extension off of the file name. */
681   /* Duplicated in driver/xscreensaver-getimage.c. */
682   if (*buf)
683     {
684       char *slash = strrchr (buf, '/');
685       char *dot = strrchr ((slash ? slash : buf), '.');
686       if (dot) *dot = 0;
687       /* Replace slashes with newlines */
688       /* while (dot = strchr(buf, '/')) *dot = '\n'; */
689       /* Replace slashes with spaces */
690       /* while ((dot = strchr(buf, '/'))) *dot = ' '; */
691     }
692
693   if (absfile) free (absfile);
694   clo2->callback (clo2->screen, clo2->window, clo2->drawable, buf, &geom,
695                   clo2->closure);
696   clo2->callback = 0;
697   free (clo2->directory);
698   free (clo2);
699 }
700
701
702 # else   /* HAVE_COCOA && USE_IPHONE -- iOS */
703
704 /* Callback for ios_load_random_image(), called after we have loaded an
705    image from the iOS device's Photo Library.  See iosgrabimage.m.
706  */
707 static void
708 ios_load_random_image_cb (void *uiimage, const char *filename, 
709                           int width, int height, void *closure)
710 {
711   xscreensaver_getimage_data *clo2 = (xscreensaver_getimage_data *) closure;
712   Display *dpy = DisplayOfScreen (clo2->screen);
713   XRectangle geom;
714   XWindowAttributes xgwa;
715   Window r;
716   int x, y;
717   unsigned int w, h, bbw, d;
718   int rot = 0;
719
720   XGetWindowAttributes (dpy, clo2->window, &xgwa);
721   XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
722
723   /* If the image is portrait and the window is landscape, or vice versa,
724      rotate the image. The idea is to fill up as many pixels as possible,
725      and assume the user will just rotate their phone until it looks right.
726      This makes "decayscreen", etc. much more easily viewable.
727    */
728   if (get_boolean_resource (dpy, "rotateImages", "RotateImages")) {
729     if ((width > height) != (w > h))
730       rot = 5;
731   }
732
733   if (uiimage)
734     {
735       jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (clo2->screen), 
736                                      clo2->drawable,
737                                      True, uiimage, &geom,
738                                      rot);
739     }
740   else  /* Probably means no images in the gallery. */
741     {
742       draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
743                       0, 0, w, h);
744       geom.x = geom.y = 0;
745       geom.width = w;
746       geom.height = h;
747       filename = 0;
748     }
749
750   clo2->callback (clo2->screen, clo2->window, clo2->drawable,
751                   filename, &geom, clo2->closure);
752   clo2->callback = 0;
753   free (clo2);
754 }
755
756 # endif /* HAVE_COCOA && USE_IPHONE */
757
758
759 static void
760 osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
761                            const char *dir,
762                            void (*callback) (Screen *, Window, Drawable,
763                                              const char *name,
764                                              XRectangle *geom,
765                                              void *closure),
766                        void *closure)
767 {
768   xscreensaver_getimage_data *clo2 =
769     (xscreensaver_getimage_data *) calloc (1, sizeof(*clo2));
770
771   clo2->screen = screen;
772   clo2->window = xwindow;
773   clo2->drawable = drawable;
774   clo2->callback = callback;
775   clo2->closure = closure;
776
777 # ifndef USE_IPHONE   /* Desktop OSX */
778   clo2->directory = strdup (dir);
779   clo2->pipe = open_image_name_pipe (dir);
780   clo2->pipe_id = XtAppAddInput (XtDisplayToApplicationContext (
781                             DisplayOfScreen (screen)), 
782                             fileno (clo2->pipe),
783                             (XtPointer) (XtInputReadMask | XtInputExceptMask),
784                             xscreensaver_getimage_file_cb, (XtPointer) clo2);
785 # else /* USE_IPHONE */
786   ios_load_random_image (ios_load_random_image_cb, clo2);
787 # endif /* USE_IPHONE */
788 }
789
790
791 /* Loads an image into the Drawable, returning once the image is loaded.
792  */
793 static void
794 load_random_image_cocoa (Screen *screen, Window window, Drawable drawable,
795                          void (*callback) (Screen *, Window, Drawable,
796                                            const char *name, XRectangle *geom,
797                                            void *closure),
798                          void *closure)
799 {
800   Display *dpy = DisplayOfScreen (screen);
801   XWindowAttributes xgwa;
802   Bool deskp = get_boolean_resource (dpy, "grabDesktopImages",  "Boolean");
803   Bool filep = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
804   const char *dir = 0;
805   Bool done = False;
806   XRectangle geom;
807   char *name = 0;
808   
809   if (!drawable) abort();
810
811   XGetWindowAttributes (dpy, window, &xgwa);
812   {
813     Window r;
814     int x, y;
815     unsigned int w, h, bbw, d;
816     XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
817     xgwa.width = w;
818     xgwa.height = h;
819   }
820
821   geom.x = 0;
822   geom.y = 0;
823   geom.width  = xgwa.width;
824   geom.height = xgwa.height;
825
826 # ifndef USE_IPHONE
827   if (filep)
828     dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
829
830   if (!dir || !*dir)
831     filep = False;
832 # endif /* ! USE_IPHONE */
833
834   if (deskp && filep) {
835     deskp = !(random() & 5);    /* if both, desktop 1/5th of the time */
836     filep = !deskp;
837   }
838
839   if (filep && !done) {
840     osx_load_image_file_async (screen, window, drawable, dir, 
841                                callback, closure);
842     return;
843   }
844
845   if (deskp && !done) {
846     if (osx_grab_desktop_image (screen, window, drawable, &geom)) {
847       name = strdup ("desktop");
848       done = True;
849     }
850   }
851
852   if (! done)
853     draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
854                     0, 0, xgwa.width, xgwa.height);
855
856   /* If we got here, we loaded synchronously, so we're done. */
857   callback (screen, window, drawable, name, &geom, closure);
858   if (name) free (name);
859 }
860
861
862 #elif defined(HAVE_ANDROID)
863
864 /* Loads an image into the Drawable, returning once the image is loaded.
865  */
866 static void
867 load_random_image_android (Screen *screen, Window window, Drawable drawable,
868                            void (*callback) (Screen *, Window, Drawable,
869                                              const char *name,
870                                              XRectangle *geom, void *closure),
871                            void *closure)
872 {
873   Display *dpy = DisplayOfScreen (screen);
874   XWindowAttributes xgwa;
875   XRectangle geom;
876   char *name = 0;
877   char *data = 0;
878   int width  = 0;
879   int height = 0;
880   
881   if (!drawable) abort();
882
883   XGetWindowAttributes (dpy, window, &xgwa);
884   {
885     Window r;
886     int x, y;
887     unsigned int w, h, bbw, d;
888     XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
889     xgwa.width = w;
890     xgwa.height = h;
891   }
892
893   geom.x = 0;
894   geom.y = 0;
895   geom.width  = xgwa.width;
896   geom.height = xgwa.height;
897
898   data = jwxyz_load_random_image (dpy, &width, &height, &name);
899   if (! data)
900     draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
901                     0, 0, xgwa.width, xgwa.height);
902   else
903     {
904       XImage *img = XCreateImage (dpy, xgwa.visual, 32,
905                                   ZPixmap, 0, data, width, height, 0, 0);
906       XGCValues gcv;
907       GC gc;
908       gcv.foreground = BlackPixelOfScreen (screen);
909       gc = XCreateGC (dpy, drawable, GCForeground, &gcv);
910       XFillRectangle (dpy, drawable, gc, 0, 0, xgwa.width, xgwa.height);
911       XPutImage (dpy, drawable, gc, img, 0, 0, 
912                  (xgwa.width  - width) / 2,
913                  (xgwa.height - height) / 2,
914                  width, height);
915       XDestroyImage (img);
916       XFreeGC (dpy, gc);
917     }
918
919   callback (screen, window, drawable, name, &geom, closure);
920   if (name) free (name);
921 }
922
923 #endif /* HAVE_ANDROID */
924
925
926
927 /* Writes the string "Loading..." in the middle of the screen.
928    This will presumably get blown away when the image finally loads,
929    minutes or hours later...
930
931    This is called by load_image_async_simple() but not by load_image_async(),
932    since it is assumed that hacks that are loading more than one image
933    *at one time* will be doing something more clever than just blocking
934    with a blank screen.
935  */
936 static void
937 print_loading_msg (Screen *screen, Window window)
938 {
939   Display *dpy = DisplayOfScreen (screen);
940   XWindowAttributes xgwa;
941   XGCValues gcv;
942   XFontStruct *f = 0;
943   GC gc;
944   char *fn = get_string_resource (dpy, "labelFont", "Font");
945   const char *text = "Loading...";
946   int w;
947
948   if (!fn) fn = get_string_resource (dpy, "titleFont", "Font");
949   if (!fn) fn = get_string_resource (dpy, "font", "Font");
950   if (!fn) fn = strdup ("-*-times-bold-r-normal-*-180-*");
951   f = XLoadQueryFont (dpy, fn);
952   if (!f) f = XLoadQueryFont (dpy, "fixed");
953   if (!f) abort();
954   free (fn);
955   fn = 0;
956
957   XGetWindowAttributes (dpy, window, &xgwa);
958   w = XTextWidth (f, text, (int) strlen(text));
959
960   gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
961                                        "foreground", "Foreground");
962   gcv.background = get_pixel_resource (dpy, xgwa.colormap,
963                                        "background", "Background");
964   gcv.font = f->fid;
965   gc = XCreateGC (dpy, window, GCFont | GCForeground | GCBackground, &gcv);
966   XDrawImageString (dpy, window, gc,
967                     (xgwa.width - w) / 2,
968                     (xgwa.height - (f->ascent + f->descent)) / 2 + f->ascent,
969                     text, (int) strlen(text));
970   XFreeFont (dpy, f);
971   XFreeGC (dpy, gc);
972   XSync (dpy, False);
973 }
974
975
976 /* Loads an image into the Drawable in the background;
977    when the image is fully loaded, runs the callback.
978    When grabbing desktop images, the Window will be unmapped first.
979  */
980 void
981 load_image_async (Screen *screen, Window window, Drawable drawable,
982                   void (*callback) (Screen *, Window, Drawable,
983                                     const char *name, XRectangle *geom,
984                                     void *closure),
985                   void *closure)
986 {
987   if (!callback) abort();
988 # if defined(HAVE_COCOA)
989   load_random_image_cocoa   (screen, window, drawable, callback, closure);
990 # elif defined(HAVE_ANDROID)
991   load_random_image_android (screen, window, drawable, callback, closure);
992 # else /* real X11 */
993   load_random_image_x11     (screen, window, drawable, callback, closure);
994 # endif
995 }
996
997 struct async_load_state {
998   Bool done_p;
999   char *filename;
1000   XRectangle geom;
1001 };
1002
1003 static void
1004 load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
1005                             const char *name, XRectangle *geom, void *closure)
1006 {
1007   async_load_state *state = (async_load_state *) closure;
1008   state->done_p = True;
1009   state->filename = (name ? strdup (name) : 0);
1010   state->geom = *geom;
1011 }
1012
1013 async_load_state *
1014 load_image_async_simple (async_load_state *state,
1015                          Screen *screen,
1016                          Window window,
1017                          Drawable drawable, 
1018                          char **filename_ret,
1019                          XRectangle *geometry_ret)
1020 {
1021   if (state && state->done_p)           /* done! */
1022     {
1023       if (filename_ret)
1024         *filename_ret = state->filename;
1025       else if (state->filename)
1026         free (state->filename);
1027
1028       if (geometry_ret)
1029         *geometry_ret = state->geom;
1030
1031       free (state);
1032       return 0;
1033     }
1034   else if (! state)                     /* first time */
1035     {
1036       state = (async_load_state *) calloc (1, sizeof(*state));
1037       state->done_p = False;
1038       print_loading_msg (screen, window);
1039       load_image_async (screen, window, drawable, 
1040                         load_image_async_simple_cb,
1041                         state);
1042       return state;
1043     }
1044   else                                  /* still waiting */
1045     return state;
1046 }