1 /* xscreensaver, Copyright (c) 1992-2013 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 /* Gets the name of an image file to load by running xscreensaver-getimage-file
531 at the end of a pipe. This can be very slow!
534 open_image_name_pipe (const char *dir)
536 char *cmd = malloc (strlen(dir) * 2 + 100);
538 strcpy (cmd, "xscreensaver-getimage-file --name ");
539 s = cmd + strlen (cmd);
542 /* put a backslash in front of any character that might confuse sh. */
543 if (! ((c >= 'a' && c <= 'z') ||
544 (c >= 'A' && c <= 'Z') ||
545 (c >= '0' && c <= '9') ||
546 c == '.' || c == '_' || c == '-' || c == '+' || c == '/'))
552 FILE *pipe = popen (cmd, "r");
559 pipe_cb (XtPointer closure, int *source, XtInputId *id)
561 /* This is not called from a signal handler, so doing stuff here is fine.
563 struct pipe_closure *clo2 = (struct pipe_closure *) closure;
565 const char *dir = clo2->directory;
568 fgets (buf, sizeof(buf)-1, clo2->pipe);
571 XtRemoveInput (clo2->id);
574 /* strip trailing newline */
576 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
579 Display *dpy = DisplayOfScreen (clo2->screen);
582 if (*buf && *buf != '/') /* pathname is relative to dir. */
584 absfile = malloc (strlen(dir) + strlen(buf) + 10);
585 strcpy (absfile, dir);
586 if (dir[strlen(dir)-1] != '/')
587 strcat (absfile, "/");
588 strcat (absfile, buf);
591 if (! osx_load_image_file (clo2->screen, clo2->xwindow, clo2->drawable,
592 (absfile ? absfile : buf), &geom)) {
593 /* unable to load image - draw colorbars
595 XWindowAttributes xgwa;
596 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
599 unsigned int w, h, bbw, d;
602 /* Log something to syslog so we can tell the difference between
603 corrupted images and broken symlinks. */
605 fprintf (stderr, "%s: no image filename found\n", progname);
606 else if (! stat (buf, &st))
607 fprintf (stderr, "%s: %s: unparsable\n", progname, buf);
611 sprintf (buf2, "%.255s: %.1024s", progname, buf);
615 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
616 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
623 /* Take the extension off of the file name. */
624 /* Duplicated in driver/xscreensaver-getimage.c. */
627 char *slash = strrchr (buf, '/');
628 char *dot = strrchr ((slash ? slash : buf), '.');
630 /* Replace slashes with newlines */
631 /* while (dot = strchr(buf, '/')) *dot = '\n'; */
632 /* Replace slashes with spaces */
633 /* while ((dot = strchr(buf, '/'))) *dot = ' '; */
636 if (absfile) free (absfile);
637 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable, buf, &geom,
640 free (clo2->directory);
645 # else /* USE_IPHONE */
647 /* Callback for ios_load_random_image(), called after we have loaded an
648 image from the iOS device's Photo Library. See iosgrabimage.m.
651 ios_load_random_image_cb (void *uiimage, const char *filename, void *closure)
653 struct pipe_closure *clo2 = (struct pipe_closure *) closure;
654 Display *dpy = DisplayOfScreen (clo2->screen);
659 jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (clo2->screen),
661 True, uiimage, &geom,
664 else /* Probably means no images in the gallery. */
666 XWindowAttributes xgwa;
669 unsigned int w, h, bbw, d;
670 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
671 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
672 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
680 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable,
681 filename, &geom, clo2->closure);
683 if (clo2->directory) free (clo2->directory);
687 # endif /* USE_IPHONE */
691 osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
693 void (*callback) (Screen *, Window, Drawable,
699 # if 0 /* do it synchronously */
701 FILE *pipe = open_image_name_pipe (dir);
704 fgets (buf, sizeof(buf)-1, pipe);
707 /* strip trailing newline */
709 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
713 if (! osx_load_image_file (screen, xwindow, drawable, buf, &geom)) {
717 callback (screen, xwindow, drawable, buf, &geom, closure);
719 # else /* do it asynchronously */
721 struct pipe_closure *clo2 = (struct pipe_closure *) calloc (1, sizeof(*clo2));
723 clo2->screen = screen;
724 clo2->xwindow = xwindow;
725 clo2->drawable = drawable;
726 clo2->callback = callback;
727 clo2->closure = closure;
730 clo2->directory = strdup (dir);
731 clo2->pipe = open_image_name_pipe (dir);
732 clo2->id = XtAppAddInput (XtDisplayToApplicationContext (
733 DisplayOfScreen (screen)),
735 (XtPointer) (XtInputReadMask | XtInputExceptMask),
736 pipe_cb, (XtPointer) clo2);
737 # else /* USE_IPHONE */
738 ios_load_random_image (ios_load_random_image_cb, clo2);
739 # endif /* USE_IPHONE */
745 /* Loads an image into the Drawable, returning once the image is loaded.
748 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
749 void (*callback) (Screen *, Window, Drawable,
750 const char *name, XRectangle *geom,
754 XRectangle *geom_ret)
756 Display *dpy = DisplayOfScreen (screen);
757 XWindowAttributes xgwa;
758 Bool deskp = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
759 Bool filep = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
762 XRectangle geom_ret_2;
763 char *name_ret_2 = 0;
765 if (!drawable) abort();
768 geom_ret = &geom_ret_2;
769 name_ret = &name_ret_2;
772 XGetWindowAttributes (dpy, window, &xgwa);
776 unsigned int w, h, bbw, d;
777 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
788 geom_ret->width = xgwa.width;
789 geom_ret->height = xgwa.height;
794 dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
798 # endif /* ! USE_IPHONE */
800 if (deskp && filep) {
801 deskp = !(random() & 5); /* if both, desktop 1/5th of the time */
805 if (filep && !done) {
806 osx_load_image_file_async (screen, window, drawable, dir,
811 if (deskp && !done) {
812 if (osx_grab_desktop_image (screen, window, drawable, &geom_ret_2)) {
814 *name_ret = strdup ("desktop");
820 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
821 0, 0, xgwa.width, xgwa.height);
824 /* If we got here, we loaded synchronously even though they wanted async.
826 callback (screen, window, drawable, name_ret_2, &geom_ret_2, closure);
827 if (name_ret_2) free (name_ret_2);
831 #endif /* HAVE_COCOA */
834 /* Writes the string "Loading..." in the middle of the screen.
835 This will presumably get blown away when the image finally loads,
836 minutes or hours later...
838 This is called by load_image_async_simple() but not by load_image_async(),
839 since it is assumed that hacks that are loading more than one image
840 *at one time* will be doing something more clever than just blocking
844 print_loading_msg (Screen *screen, Window window)
846 Display *dpy = DisplayOfScreen (screen);
847 XWindowAttributes xgwa;
851 char *fn = get_string_resource (dpy, "labelFont", "Font");
852 const char *text = "Loading...";
855 if (!fn) fn = get_string_resource (dpy, "titleFont", "Font");
856 if (!fn) fn = get_string_resource (dpy, "font", "Font");
857 if (!fn) fn = strdup ("-*-times-bold-r-normal-*-180-*");
858 f = XLoadQueryFont (dpy, fn);
859 if (!f) f = XLoadQueryFont (dpy, "fixed");
864 XGetWindowAttributes (dpy, window, &xgwa);
865 w = XTextWidth (f, text, (int) strlen(text));
867 gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
868 "foreground", "Foreground");
869 gcv.background = get_pixel_resource (dpy, xgwa.colormap,
870 "background", "Background");
872 gc = XCreateGC (dpy, window, GCFont | GCForeground | GCBackground, &gcv);
873 XDrawImageString (dpy, window, gc,
874 (xgwa.width - w) / 2,
875 (xgwa.height - (f->ascent + f->descent)) / 2 + f->ascent,
876 text, (int) strlen(text));
883 /* Loads an image into the Drawable in the background;
884 when the image is fully loaded, runs the callback.
885 When grabbing desktop images, the Window will be unmapped first.
888 load_image_async (Screen *screen, Window window, Drawable drawable,
889 void (*callback) (Screen *, Window, Drawable,
890 const char *name, XRectangle *geom,
894 load_random_image_1 (screen, window, drawable, callback, closure, 0, 0);
897 struct async_load_state {
904 load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
905 const char *name, XRectangle *geom, void *closure)
907 async_load_state *state = (async_load_state *) closure;
908 state->done_p = True;
909 state->filename = (name ? strdup (name) : 0);
914 load_image_async_simple (async_load_state *state,
919 XRectangle *geometry_ret)
921 if (state && state->done_p) /* done! */
924 *filename_ret = state->filename;
925 else if (state->filename)
926 free (state->filename);
929 *geometry_ret = state->geom;
934 else if (! state) /* first time */
936 state = (async_load_state *) calloc (1, sizeof(*state));
937 state->done_p = False;
938 print_loading_msg (screen, window);
939 load_image_async (screen, window, drawable,
940 load_image_async_simple_cb,
944 else /* still waiting */