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