1 /* xscreensaver, Copyright (c) 1992-2016 Jamie Zawinski <jwz@jwz.org>
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
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.
17 That program links against utils/grabimage.c, which happens to export the
18 same API as this program (utils/grabclient.c).
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:
31 "xscreensaver-getimage 0xWINDOW 0xPIXMAP"
32 "xscreensaver-getimage-file --name /DIR"
35 XtAppAddInput xscreensaver_getimage_cb
37 xscreensaver_getimage_cb
39 get_original_geometry_from_xprops
40 CB name, geom, closure
46 load_random_image_cocoa
47 osx_grab_desktop_image (osxgrabscreen.m, MacOS version)
48 copy_framebuffer_to_ximage
51 osx_load_image_file_async
53 "xscreensaver-getimage-file --name /DIR"
54 XtAppAddInput xscreensaver_getimage_file_cb
56 xscreensaver_getimage_file_cb
58 CB name, geom, closure
64 load_random_image_cocoa
65 osx_grab_desktop_image (osxgrabscreen.m, iOS version)
66 CGWindowListCreateImage
67 jwxyz_draw_NSImage_or_CGImage
70 ios_load_random_image_cb
71 jwxyz_draw_NSImage_or_CGImage
72 CB name, geom, closure
75 Andrid execution path:
78 load_random_image_android
79 jwxyz_load_random_image (jwxyz-android.c)
82 CB name, geom, closure
86 #include "grabscreen.h"
87 #include "resources.h"
92 # include "colorbars.h"
93 #else /* !HAVE_COCOA -- real Xlib */
95 # include <X11/Xatom.h>
96 # include <X11/Intrinsic.h> /* for XtInputId, etc */
97 #endif /* !HAVE_COCOA */
104 #ifdef HAVE_SYS_WAIT_H
105 # include <sys/wait.h> /* for waitpid() and associated macros */
109 extern char *progname;
111 static void print_loading_msg (Screen *, Window);
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"
121 void (*callback) (Screen *, Window, Drawable,
122 const char *name, XRectangle *geom, void *closure);
130 # if !defined(USE_IPHONE) && !defined(HAVE_COCOA) /* Real X11 */
134 # if !defined(USE_IPHONE) && defined(HAVE_COCOA) /* Desktop OSX */
138 } xscreensaver_getimage_data;
141 #if !defined(HAVE_COCOA) && !defined(HAVE_ANDROID) /* Real X11 */
143 static Bool error_handler_hit_p = False;
146 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
148 error_handler_hit_p = True;
153 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
156 drawable_window_p (Display *dpy, Drawable d)
158 XErrorHandler old_handler;
159 XWindowAttributes xgwa;
162 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
163 error_handler_hit_p = False;
164 XGetWindowAttributes (dpy, d, &xgwa);
166 XSetErrorHandler (old_handler);
169 if (!error_handler_hit_p)
170 return True; /* It's a Window. */
172 return False; /* It's a Pixmap, or an invalid ID. */
177 xscreensaver_window_p (Display *dpy, Window window)
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,
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
203 use_subwindow_mode_p (Screen *screen, Window window)
205 if (window != VirtualRootWindowOfScreen(screen))
207 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
215 checkerboard (Screen *screen, Drawable drawable)
217 Display *dpy = DisplayOfScreen (screen);
222 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
224 unsigned int win_width, win_height;
226 fg.flags = bg.flags = DoRed|DoGreen|DoBlue;
227 fg.red = fg.green = fg.blue = 0x0000;
228 bg.red = bg.green = bg.blue = 0x4444;
232 if (drawable_window_p (dpy, drawable))
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;
241 else /* it's a pixmap */
243 XWindowAttributes xgwa;
247 XGetWindowAttributes (dpy, RootWindowOfScreen (screen), &xgwa);
248 cmap = xgwa.colormap;
249 XGetGeometry (dpy, drawable,
250 &root, &x, &y, &win_width, &win_height, &bw, &d);
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);
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)
265 XFillRectangle (dpy, drawable, gc, x, y, size, size);
266 XFillRectangle (dpy, drawable, gc, x+size, y+size, size, size);
272 /* Read the image's original name off of the window's X properties.
273 Used only when running "real" X11, not jwxyz.
276 get_name_from_xprops (Display *dpy, Window window)
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,
289 return (char *) name;
295 /* Read the image's original geometry off of the window's X properties.
296 Used only when running "real" X11, not jwxyz.
299 get_original_geometry_from_xprops (Display *dpy, Window window, XRectangle *ret)
303 unsigned long nitems, bytesafter;
304 unsigned char *name = 0;
305 Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
308 if (XGetWindowProperty (dpy, window, atom,
309 0, 1024, False, XA_STRING,
310 &type, &format, &nitems, &bytesafter,
315 int flags = XParseGeometry ((char *) name, &x, &y, &w, &h);
317 /* Require all four, and don't allow negative positions. */
318 if (flags == (XValue|YValue|WidthValue|HeightValue))
335 hack_subproc_environment (Display *dpy)
337 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
338 the spawned processes inherit is what we are actually using.
340 const char *odpy = DisplayString (dpy);
341 char *ndpy = (char *) malloc(strlen(odpy) + 20);
342 strcpy (ndpy, "DISPLAY=");
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. */
350 # endif /* HAVE_PUTENV */
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.
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.
366 Used to execute "xscreensaver-getimage".
367 Used only when running "real" X11, not jwxyz.
370 exec_simple_command (const char *command)
374 char *token = strtok (strdup(command), " \t");
378 token = strtok(0, " \t");
382 execvp (av[0], av); /* shouldn't return. */
386 static void xscreensaver_getimage_cb (XtPointer closure,
387 int *fd, XtIntervalId *id);
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.
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,
401 XtAppContext app = XtDisplayToApplicationContext (DisplayOfScreen (screen));
402 xscreensaver_getimage_data *data;
411 sprintf (buf, "%s: creating pipe", progname);
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? */
425 if (!data->pipe || !wpipe)
427 sprintf (buf, "%s: fdopen", progname);
433 XtAppAddInput (app, fileno (data->pipe),
434 (XtPointer) (XtInputReadMask | XtInputExceptMask),
435 xscreensaver_getimage_cb, (XtPointer) data);
438 switch ((int) forked)
441 sprintf (buf, "%s: couldn't fork", progname);
450 /* clone the write pipe onto stdout so that it gets closed
451 when the fork exits. This will shut down the pipe and
454 close (fileno (stdout));
455 dup2 (fds[1], fileno (stdout));
458 close (fileno (stdin)); /* for kicks */
460 exec_simple_command (command);
461 exit (1); /* exits child fork */
464 default: /* parent */
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.
478 xscreensaver_getimage_cb (XtPointer closure, int *fd, XtIntervalId *id)
480 xscreensaver_getimage_data *data = (xscreensaver_getimage_data *) closure;
481 Display *dpy = DisplayOfScreen (data->screen);
483 XRectangle geom = { 0, 0, 0, 0 };
487 name = get_name_from_xprops (dpy, data->window);
488 get_original_geometry_from_xprops (dpy, data->window, &geom);
490 data->callback (data->screen, data->window, data->drawable,
491 name, &geom, data->closure);
492 if (name) free (name);
496 if (data->pid) /* reap zombies */
499 waitpid (data->pid, &status, 0);
503 memset (data, 0, sizeof (*data));
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.
513 load_random_image_x11 (Screen *screen, Window window, Drawable drawable,
514 void (*callback) (Screen *, Window, Drawable,
515 const char *name, XRectangle *geom,
519 Display *dpy = DisplayOfScreen (screen);
520 char *grabber = get_string_resource(dpy, "desktopGrabber", "DesktopGrabber");
524 if (!grabber || !*grabber)
527 "%s: resources installed incorrectly: \"desktopGrabber\" is unset!\n",
532 sprintf (id, "0x%lx 0x%lx",
533 (unsigned long) window,
534 (unsigned long) drawable);
535 cmd = (char *) malloc (strlen(grabber) + strlen(id) + 1);
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.
544 sprintf (cmd, grabber, id);
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);
555 hack_subproc_environment (dpy);
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);
565 #elif defined (HAVE_COCOA) /* OSX or iOS */
567 # ifndef USE_IPHONE /* HAVE_COCOA && !USE_IPHONE -- desktop OSX */
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 == '/'))
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!
579 open_image_name_pipe (const char *dir)
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=");
590 if (BACKSLASH(c)) *s++ = '\\';
596 strcpy (s, "xscreensaver-getimage-file --name ");
600 if (BACKSLASH(c)) *s++ = '\\';
609 FILE *pipe = popen (cmd, "r");
616 xscreensaver_getimage_file_cb (XtPointer closure, int *source, XtInputId *id)
618 /* This is not called from a signal handler, so doing stuff here is fine.
620 xscreensaver_getimage_data *clo2 = (xscreensaver_getimage_data *) closure;
622 const char *dir = clo2->directory;
625 fgets (buf, sizeof(buf)-1, clo2->pipe);
628 XtRemoveInput (clo2->pipe_id);
631 /* strip trailing newline */
633 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
636 Display *dpy = DisplayOfScreen (clo2->screen);
639 if (*buf && *buf != '/') /* pathname is relative to dir. */
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);
648 if (! osx_load_image_file (clo2->screen, clo2->window, clo2->drawable,
649 (absfile ? absfile : buf), &geom)) {
650 /* unable to load image - draw colorbars
652 XWindowAttributes xgwa;
653 XGetWindowAttributes (dpy, clo2->window, &xgwa);
656 unsigned int w, h, bbw, d;
659 /* Log something to syslog so we can tell the difference between
660 corrupted images and broken symlinks. */
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);
668 sprintf (buf2, "%.255s: %.1024s", progname, buf);
672 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
673 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
680 /* Take the extension off of the file name. */
681 /* Duplicated in driver/xscreensaver-getimage.c. */
684 char *slash = strrchr (buf, '/');
685 char *dot = strrchr ((slash ? slash : buf), '.');
687 /* Replace slashes with newlines */
688 /* while (dot = strchr(buf, '/')) *dot = '\n'; */
689 /* Replace slashes with spaces */
690 /* while ((dot = strchr(buf, '/'))) *dot = ' '; */
693 if (absfile) free (absfile);
694 clo2->callback (clo2->screen, clo2->window, clo2->drawable, buf, &geom,
697 free (clo2->directory);
702 # else /* HAVE_COCOA && USE_IPHONE -- iOS */
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.
708 ios_load_random_image_cb (void *uiimage, const char *filename,
709 int width, int height, void *closure)
711 xscreensaver_getimage_data *clo2 = (xscreensaver_getimage_data *) closure;
712 Display *dpy = DisplayOfScreen (clo2->screen);
714 XWindowAttributes xgwa;
717 unsigned int w, h, bbw, d;
720 XGetWindowAttributes (dpy, clo2->window, &xgwa);
721 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
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.
728 if (get_boolean_resource (dpy, "rotateImages", "RotateImages")) {
729 if ((width > height) != (w > h))
735 jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (clo2->screen),
737 True, uiimage, &geom,
740 else /* Probably means no images in the gallery. */
742 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
750 clo2->callback (clo2->screen, clo2->window, clo2->drawable,
751 filename, &geom, clo2->closure);
756 # endif /* HAVE_COCOA && USE_IPHONE */
760 osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
762 void (*callback) (Screen *, Window, Drawable,
768 xscreensaver_getimage_data *clo2 =
769 (xscreensaver_getimage_data *) calloc (1, sizeof(*clo2));
771 clo2->screen = screen;
772 clo2->window = xwindow;
773 clo2->drawable = drawable;
774 clo2->callback = callback;
775 clo2->closure = closure;
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)),
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 */
791 /* Loads an image into the Drawable, returning once the image is loaded.
794 load_random_image_cocoa (Screen *screen, Window window, Drawable drawable,
795 void (*callback) (Screen *, Window, Drawable,
796 const char *name, XRectangle *geom,
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");
809 if (!drawable) abort();
811 XGetWindowAttributes (dpy, window, &xgwa);
815 unsigned int w, h, bbw, d;
816 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
823 geom.width = xgwa.width;
824 geom.height = xgwa.height;
828 dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
832 # endif /* ! USE_IPHONE */
834 if (deskp && filep) {
835 deskp = !(random() & 5); /* if both, desktop 1/5th of the time */
839 if (filep && !done) {
840 osx_load_image_file_async (screen, window, drawable, dir,
845 if (deskp && !done) {
846 if (osx_grab_desktop_image (screen, window, drawable, &geom)) {
847 name = strdup ("desktop");
853 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
854 0, 0, xgwa.width, xgwa.height);
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);
862 #elif defined(HAVE_ANDROID)
864 /* Loads an image into the Drawable, returning once the image is loaded.
867 load_random_image_android (Screen *screen, Window window, Drawable drawable,
868 void (*callback) (Screen *, Window, Drawable,
870 XRectangle *geom, void *closure),
873 Display *dpy = DisplayOfScreen (screen);
874 XWindowAttributes xgwa;
881 if (!drawable) abort();
883 XGetWindowAttributes (dpy, window, &xgwa);
887 unsigned int w, h, bbw, d;
888 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
895 geom.width = xgwa.width;
896 geom.height = xgwa.height;
898 data = jwxyz_load_random_image (dpy, &width, &height, &name);
900 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
901 0, 0, xgwa.width, xgwa.height);
904 XImage *img = XCreateImage (dpy, xgwa.visual, 32,
905 ZPixmap, 0, data, width, height, 0, 0);
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,
919 callback (screen, window, drawable, name, &geom, closure);
920 if (name) free (name);
923 #endif /* HAVE_ANDROID */
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...
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
937 print_loading_msg (Screen *screen, Window window)
939 Display *dpy = DisplayOfScreen (screen);
940 XWindowAttributes xgwa;
944 char *fn = get_string_resource (dpy, "labelFont", "Font");
945 const char *text = "Loading...";
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");
957 XGetWindowAttributes (dpy, window, &xgwa);
958 w = XTextWidth (f, text, (int) strlen(text));
960 gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
961 "foreground", "Foreground");
962 gcv.background = get_pixel_resource (dpy, xgwa.colormap,
963 "background", "Background");
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));
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.
981 load_image_async (Screen *screen, Window window, Drawable drawable,
982 void (*callback) (Screen *, Window, Drawable,
983 const char *name, XRectangle *geom,
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);
997 struct async_load_state {
1004 load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
1005 const char *name, XRectangle *geom, void *closure)
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;
1014 load_image_async_simple (async_load_state *state,
1018 char **filename_ret,
1019 XRectangle *geometry_ret)
1021 if (state && state->done_p) /* done! */
1024 *filename_ret = state->filename;
1025 else if (state->filename)
1026 free (state->filename);
1029 *geometry_ret = state->geom;
1034 else if (! state) /* first time */
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,
1044 else /* still waiting */