1 /* xscreensaver, Copyright (c) 1992-2014 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).
22 #include "grabscreen.h"
23 #include "resources.h"
28 # include "colorbars.h"
29 #else /* !HAVE_COCOA -- real Xlib */
31 # include <X11/Xatom.h>
32 # include <X11/Intrinsic.h> /* for XtInputId, etc */
33 #endif /* !HAVE_COCOA */
40 #ifdef HAVE_SYS_WAIT_H
41 # include <sys/wait.h> /* for waitpid() and associated macros */
45 extern char *progname;
47 static void print_loading_msg (Screen *, Window);
51 static Bool error_handler_hit_p = False;
54 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
56 error_handler_hit_p = True;
61 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
64 drawable_window_p (Display *dpy, Drawable d)
66 XErrorHandler old_handler;
67 XWindowAttributes xgwa;
70 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
71 error_handler_hit_p = False;
72 XGetWindowAttributes (dpy, d, &xgwa);
74 XSetErrorHandler (old_handler);
77 if (!error_handler_hit_p)
78 return True; /* It's a Window. */
80 return False; /* It's a Pixmap, or an invalid ID. */
85 xscreensaver_window_p (Display *dpy, Window window)
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,
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
111 use_subwindow_mode_p (Screen *screen, Window window)
113 if (window != VirtualRootWindowOfScreen(screen))
115 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
123 checkerboard (Screen *screen, Drawable drawable)
125 Display *dpy = DisplayOfScreen (screen);
130 GC gc = XCreateGC (dpy, drawable, 0, &gcv);
132 unsigned int win_width, win_height;
134 fg.flags = bg.flags = DoRed|DoGreen|DoBlue;
135 fg.red = fg.green = fg.blue = 0x0000;
136 bg.red = bg.green = bg.blue = 0x4444;
140 if (drawable_window_p (dpy, drawable))
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;
149 else /* it's a pixmap */
151 XWindowAttributes xgwa;
155 XGetWindowAttributes (dpy, RootWindowOfScreen (screen), &xgwa);
156 cmap = xgwa.colormap;
157 XGetGeometry (dpy, drawable,
158 &root, &x, &y, &win_width, &win_height, &bw, &d);
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);
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)
173 XFillRectangle (dpy, drawable, gc, x, y, size, size);
174 XFillRectangle (dpy, drawable, gc, x+size, y+size, size, size);
181 get_name (Display *dpy, Window window)
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,
194 return (char *) name;
201 get_geometry (Display *dpy, Window window, XRectangle *ret)
205 unsigned long nitems, bytesafter;
206 unsigned char *name = 0;
207 Atom atom = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_GEOMETRY, False);
210 if (XGetWindowProperty (dpy, window, atom,
211 0, 1024, False, XA_STRING,
212 &type, &format, &nitems, &bytesafter,
217 int flags = XParseGeometry ((char *) name, &x, &y, &w, &h);
219 /* Require all four, and don't allow negative positions. */
220 if (flags == (XValue|YValue|WidthValue|HeightValue))
237 hack_subproc_environment (Display *dpy)
239 /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
240 the spawned processes inherit is what we are actually using.
242 const char *odpy = DisplayString (dpy);
243 char *ndpy = (char *) malloc(strlen(odpy) + 20);
244 strcpy (ndpy, "DISPLAY=");
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. */
252 #endif /* HAVE_PUTENV */
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.
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.
269 exec_simple_command (const char *command)
273 char *token = strtok (strdup(command), " \t");
277 token = strtok(0, " \t");
281 execvp (av[0], av); /* shouldn't return. */
286 fork_exec_wait (const char *command)
292 switch ((int) (forked = fork ()))
295 sprintf (buf, "%s: couldn't fork", progname);
300 exec_simple_command (command);
301 exit (1); /* exits child fork */
305 waitpid (forked, &status, 0);
312 void (*callback) (Screen *, Window, Drawable,
313 const char *name, XRectangle *geom, void *closure);
325 static void finalize_cb (XtPointer closure, int *fd, XtIntervalId *id);
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,
335 XtAppContext app = XtDisplayToApplicationContext (DisplayOfScreen (screen));
336 grabclient_data *data;
344 sprintf (buf, "%s: creating pipe", progname);
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");
358 if (!data->read_pipe || !data->write_pipe)
360 sprintf (buf, "%s: fdopen", progname);
366 XtAppAddInput (app, fileno (data->read_pipe),
367 (XtPointer) (XtInputReadMask | XtInputExceptMask),
368 finalize_cb, (XtPointer) data);
371 switch ((int) forked)
374 sprintf (buf, "%s: couldn't fork", progname);
380 fclose (data->read_pipe);
383 /* clone the write pipe onto stdout so that it gets closed
384 when the fork exits. This will shut down the pipe and
387 close (fileno (stdout));
388 dup2 (fds[1], fileno (stdout));
391 close (fileno (stdin)); /* for kicks */
393 exec_simple_command (command);
394 exit (1); /* exits child fork */
397 default: /* parent */
398 fclose (data->write_pipe);
399 data->write_pipe = 0;
406 /* Called in the parent when the forked process dies.
407 Runs the caller's callback, and cleans up.
410 finalize_cb (XtPointer closure, int *fd, XtIntervalId *id)
412 grabclient_data *data = (grabclient_data *) closure;
413 Display *dpy = DisplayOfScreen (data->screen);
415 XRectangle geom = { 0, 0, 0, 0 };
419 name = get_name (dpy, data->window);
420 get_geometry (dpy, data->window, &geom);
422 data->callback (data->screen, data->window, data->drawable,
423 name, &geom, data->closure);
424 if (name) free (name);
426 fclose (data->read_pipe);
428 if (data->pid) /* reap zombies */
431 waitpid (data->pid, &status, 0);
435 memset (data, 0, sizeof (*data));
440 /* Loads an image into the Drawable.
441 When grabbing desktop images, the Window will be unmapped first.
444 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
445 void (*callback) (Screen *, Window, Drawable,
446 const char *name, XRectangle *geom,
450 XRectangle *geom_ret)
452 Display *dpy = DisplayOfScreen (screen);
453 char *grabber = get_string_resource(dpy, "desktopGrabber", "DesktopGrabber");
457 if (!grabber || !*grabber)
460 "%s: resources installed incorrectly: \"desktopGrabber\" is unset!\n",
465 sprintf (id, "0x%lx 0x%lx",
466 (unsigned long) window,
467 (unsigned long) drawable);
468 cmd = (char *) malloc (strlen(grabber) + strlen(id) + 1);
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.
477 sprintf (cmd, grabber, id);
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);
488 hack_subproc_environment (dpy);
492 /* Start the image loading in another fork and return immediately.
493 Invoke the callback function when done.
495 if (name_ret) abort();
496 fork_exec_cb (cmd, screen, window, drawable, callback, closure);
500 /* Wait for the image to load, and return it immediately.
502 fork_exec_wait (cmd);
504 *name_ret = get_name (dpy, window);
506 get_geometry (dpy, window, geom_ret);
513 #else /* HAVE_COCOA */
515 struct pipe_closure {
522 void (*callback) (Screen *, Window, Drawable,
523 const char *name, XRectangle *geom,
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 == '/'))
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!
540 open_image_name_pipe (const char *dir)
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=");
552 if (BACKSLASH(c)) *s++ = '\\';
558 char *cmd = s = malloc (strlen(dir) * 2 + 100);
561 strcpy (s, "xscreensaver-getimage-file --name ");
565 if (BACKSLASH(c)) *s++ = '\\';
574 FILE *pipe = popen (cmd, "r");
581 pipe_cb (XtPointer closure, int *source, XtInputId *id)
583 /* This is not called from a signal handler, so doing stuff here is fine.
585 struct pipe_closure *clo2 = (struct pipe_closure *) closure;
587 const char *dir = clo2->directory;
590 fgets (buf, sizeof(buf)-1, clo2->pipe);
593 XtRemoveInput (clo2->id);
596 /* strip trailing newline */
598 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
601 Display *dpy = DisplayOfScreen (clo2->screen);
604 if (*buf && *buf != '/') /* pathname is relative to dir. */
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);
613 if (! osx_load_image_file (clo2->screen, clo2->xwindow, clo2->drawable,
614 (absfile ? absfile : buf), &geom)) {
615 /* unable to load image - draw colorbars
617 XWindowAttributes xgwa;
618 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
621 unsigned int w, h, bbw, d;
624 /* Log something to syslog so we can tell the difference between
625 corrupted images and broken symlinks. */
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);
633 sprintf (buf2, "%.255s: %.1024s", progname, buf);
637 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
638 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
645 /* Take the extension off of the file name. */
646 /* Duplicated in driver/xscreensaver-getimage.c. */
649 char *slash = strrchr (buf, '/');
650 char *dot = strrchr ((slash ? slash : buf), '.');
652 /* Replace slashes with newlines */
653 /* while (dot = strchr(buf, '/')) *dot = '\n'; */
654 /* Replace slashes with spaces */
655 /* while ((dot = strchr(buf, '/'))) *dot = ' '; */
658 if (absfile) free (absfile);
659 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable, buf, &geom,
662 free (clo2->directory);
667 # else /* USE_IPHONE */
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.
673 ios_load_random_image_cb (void *uiimage, const char *filename,
674 int width, int height, void *closure)
676 struct pipe_closure *clo2 = (struct pipe_closure *) closure;
677 Display *dpy = DisplayOfScreen (clo2->screen);
679 XWindowAttributes xgwa;
682 unsigned int w, h, bbw, d;
685 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
686 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
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.
693 if (get_boolean_resource (dpy, "rotateImages", "RotateImages")) {
694 if ((width > height) != (w > h))
700 jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (clo2->screen),
702 True, uiimage, &geom,
705 else /* Probably means no images in the gallery. */
707 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
715 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable,
716 filename, &geom, clo2->closure);
718 if (clo2->directory) free (clo2->directory);
722 # endif /* USE_IPHONE */
726 osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
728 void (*callback) (Screen *, Window, Drawable,
734 # if 0 /* do it synchronously */
736 FILE *pipe = open_image_name_pipe (dir);
739 fgets (buf, sizeof(buf)-1, pipe);
742 /* strip trailing newline */
744 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
748 if (! osx_load_image_file (screen, xwindow, drawable, buf, &geom)) {
752 callback (screen, xwindow, drawable, buf, &geom, closure);
754 # else /* do it asynchronously */
756 struct pipe_closure *clo2 = (struct pipe_closure *) calloc (1, sizeof(*clo2));
758 clo2->screen = screen;
759 clo2->xwindow = xwindow;
760 clo2->drawable = drawable;
761 clo2->callback = callback;
762 clo2->closure = closure;
765 clo2->directory = strdup (dir);
766 clo2->pipe = open_image_name_pipe (dir);
767 clo2->id = XtAppAddInput (XtDisplayToApplicationContext (
768 DisplayOfScreen (screen)),
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 */
780 /* Loads an image into the Drawable, returning once the image is loaded.
783 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
784 void (*callback) (Screen *, Window, Drawable,
785 const char *name, XRectangle *geom,
789 XRectangle *geom_ret)
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");
797 XRectangle geom_ret_2;
798 char *name_ret_2 = 0;
800 if (!drawable) abort();
803 geom_ret = &geom_ret_2;
804 name_ret = &name_ret_2;
807 XGetWindowAttributes (dpy, window, &xgwa);
811 unsigned int w, h, bbw, d;
812 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
823 geom_ret->width = xgwa.width;
824 geom_ret->height = xgwa.height;
829 dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
833 # endif /* ! USE_IPHONE */
835 if (deskp && filep) {
836 deskp = !(random() & 5); /* if both, desktop 1/5th of the time */
840 if (filep && !done) {
841 osx_load_image_file_async (screen, window, drawable, dir,
846 if (deskp && !done) {
847 if (osx_grab_desktop_image (screen, window, drawable, &geom_ret_2)) {
849 *name_ret = strdup ("desktop");
855 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
856 0, 0, xgwa.width, xgwa.height);
859 /* If we got here, we loaded synchronously even though they wanted async.
861 callback (screen, window, drawable, name_ret_2, &geom_ret_2, closure);
862 if (name_ret_2) free (name_ret_2);
866 #endif /* HAVE_COCOA */
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...
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
879 print_loading_msg (Screen *screen, Window window)
881 Display *dpy = DisplayOfScreen (screen);
882 XWindowAttributes xgwa;
886 char *fn = get_string_resource (dpy, "labelFont", "Font");
887 const char *text = "Loading...";
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");
899 XGetWindowAttributes (dpy, window, &xgwa);
900 w = XTextWidth (f, text, (int) strlen(text));
902 gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
903 "foreground", "Foreground");
904 gcv.background = get_pixel_resource (dpy, xgwa.colormap,
905 "background", "Background");
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));
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.
923 load_image_async (Screen *screen, Window window, Drawable drawable,
924 void (*callback) (Screen *, Window, Drawable,
925 const char *name, XRectangle *geom,
929 load_random_image_1 (screen, window, drawable, callback, closure, 0, 0);
932 struct async_load_state {
939 load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
940 const char *name, XRectangle *geom, void *closure)
942 async_load_state *state = (async_load_state *) closure;
943 state->done_p = True;
944 state->filename = (name ? strdup (name) : 0);
949 load_image_async_simple (async_load_state *state,
954 XRectangle *geometry_ret)
956 if (state && state->done_p) /* done! */
959 *filename_ret = state->filename;
960 else if (state->filename)
961 free (state->filename);
964 *geometry_ret = state->geom;
969 else if (! state) /* first time */
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,
979 else /* still waiting */