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;
567 fgets (buf, sizeof(buf)-1, clo2->pipe);
570 XtRemoveInput (clo2->id);
573 /* strip trailing newline */
575 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
578 Display *dpy = DisplayOfScreen (clo2->screen);
581 if (*buf && *buf != '/') /* pathname is relative to dir. */
583 absfile = malloc (strlen(dir) + strlen(buf) + 10);
584 strcpy (absfile, dir);
585 if (dir[strlen(dir)-1] != '/')
586 strcat (absfile, "/");
587 strcat (absfile, buf);
590 if (! osx_load_image_file (clo2->screen, clo2->xwindow, clo2->drawable,
591 (absfile ? absfile : buf), &geom)) {
592 /* unable to load image - draw colorbars
594 XWindowAttributes xgwa;
595 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
598 unsigned int w, h, bbw, d;
601 /* Log something to syslog so we can tell the difference between
602 corrupted images and broken symlinks. */
604 fprintf (stderr, "%s: no image filename found\n", progname);
605 else if (! stat (buf, &st))
606 fprintf (stderr, "%s: %s: unparsable\n", progname, buf);
610 sprintf (buf2, "%.255s: %.1024s", progname, buf);
614 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
615 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
622 /* Take the extension off of the file name. */
623 /* Duplicated in driver/xscreensaver-getimage.c. */
626 char *slash = strrchr (buf, '/');
627 char *dot = strrchr ((slash ? slash : buf), '.');
629 /* Replace slashes with newlines */
630 /* while (dot = strchr(buf, '/')) *dot = '\n'; */
631 /* Replace slashes with spaces */
632 /* while ((dot = strchr(buf, '/'))) *dot = ' '; */
635 if (absfile) free (absfile);
636 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable, buf, &geom,
639 free (clo2->directory);
644 # else /* USE_IPHONE */
646 /* Callback for ios_load_random_image(), called after we have loaded an
647 image from the iOS device's Photo Library. See iosgrabimage.m.
650 ios_load_random_image_cb (void *uiimage, const char *filename, void *closure)
652 struct pipe_closure *clo2 = (struct pipe_closure *) closure;
653 Display *dpy = DisplayOfScreen (clo2->screen);
658 jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (clo2->screen),
660 True, uiimage, &geom,
663 else /* Probably means no images in the gallery. */
665 XWindowAttributes xgwa;
668 unsigned int w, h, bbw, d;
669 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
670 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
671 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
679 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable,
680 filename, &geom, clo2->closure);
682 if (clo2->directory) free (clo2->directory);
686 # endif /* USE_IPHONE */
690 osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
692 void (*callback) (Screen *, Window, Drawable,
698 # if 0 /* do it synchronously */
700 FILE *pipe = open_image_name_pipe (dir);
703 fgets (buf, sizeof(buf)-1, pipe);
706 /* strip trailing newline */
708 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
712 if (! osx_load_image_file (screen, xwindow, drawable, buf, &geom)) {
716 callback (screen, xwindow, drawable, buf, &geom, closure);
718 # else /* do it asynchronously */
720 struct pipe_closure *clo2 = (struct pipe_closure *) calloc (1, sizeof(*clo2));
722 clo2->screen = screen;
723 clo2->xwindow = xwindow;
724 clo2->drawable = drawable;
725 clo2->callback = callback;
726 clo2->closure = closure;
729 clo2->directory = strdup (dir);
730 clo2->pipe = open_image_name_pipe (dir);
731 clo2->id = XtAppAddInput (XtDisplayToApplicationContext (
732 DisplayOfScreen (screen)),
734 (XtPointer) (XtInputReadMask | XtInputExceptMask),
735 pipe_cb, (XtPointer) clo2);
736 # else /* USE_IPHONE */
737 ios_load_random_image (ios_load_random_image_cb, clo2);
738 # endif /* USE_IPHONE */
744 /* Loads an image into the Drawable, returning once the image is loaded.
747 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
748 void (*callback) (Screen *, Window, Drawable,
749 const char *name, XRectangle *geom,
753 XRectangle *geom_ret)
755 Display *dpy = DisplayOfScreen (screen);
756 XWindowAttributes xgwa;
757 Bool deskp = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
758 Bool filep = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
761 XRectangle geom_ret_2;
762 char *name_ret_2 = 0;
764 if (!drawable) abort();
767 geom_ret = &geom_ret_2;
768 name_ret = &name_ret_2;
771 XGetWindowAttributes (dpy, window, &xgwa);
775 unsigned int w, h, bbw, d;
776 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
787 geom_ret->width = xgwa.width;
788 geom_ret->height = xgwa.height;
793 dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
797 # endif /* ! USE_IPHONE */
799 if (deskp && filep) {
800 deskp = !(random() & 5); /* if both, desktop 1/5th of the time */
804 if (filep && !done) {
805 osx_load_image_file_async (screen, window, drawable, dir,
810 if (deskp && !done) {
811 if (osx_grab_desktop_image (screen, window, drawable, &geom_ret_2)) {
813 *name_ret = strdup ("desktop");
819 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
820 0, 0, xgwa.width, xgwa.height);
823 /* If we got here, we loaded synchronously even though they wanted async.
825 callback (screen, window, drawable, name_ret_2, &geom_ret_2, closure);
826 if (name_ret_2) free (name_ret_2);
830 #endif /* HAVE_COCOA */
833 /* Writes the string "Loading..." in the middle of the screen.
834 This will presumably get blown away when the image finally loads,
835 minutes or hours later...
837 This is called by load_image_async_simple() but not by load_image_async(),
838 since it is assumed that hacks that are loading more than one image
839 *at one time* will be doing something more clever than just blocking
843 print_loading_msg (Screen *screen, Window window)
845 Display *dpy = DisplayOfScreen (screen);
846 XWindowAttributes xgwa;
850 char *fn = get_string_resource (dpy, "labelFont", "Font");
851 const char *text = "Loading...";
854 if (!fn) fn = get_string_resource (dpy, "titleFont", "Font");
855 if (!fn) fn = get_string_resource (dpy, "font", "Font");
856 if (!fn) fn = strdup ("-*-times-bold-r-normal-*-180-*");
857 f = XLoadQueryFont (dpy, fn);
858 if (!f) f = XLoadQueryFont (dpy, "fixed");
863 XGetWindowAttributes (dpy, window, &xgwa);
864 w = XTextWidth (f, text, strlen(text));
866 gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
867 "foreground", "Foreground");
868 gcv.background = get_pixel_resource (dpy, xgwa.colormap,
869 "background", "Background");
871 gc = XCreateGC (dpy, window, GCFont | GCForeground | GCBackground, &gcv);
872 XDrawImageString (dpy, window, gc,
873 (xgwa.width - w) / 2,
874 (xgwa.height - (f->ascent + f->descent)) / 2 + f->ascent,
882 /* Loads an image into the Drawable in the background;
883 when the image is fully loaded, runs the callback.
884 When grabbing desktop images, the Window will be unmapped first.
887 load_image_async (Screen *screen, Window window, Drawable drawable,
888 void (*callback) (Screen *, Window, Drawable,
889 const char *name, XRectangle *geom,
893 load_random_image_1 (screen, window, drawable, callback, closure, 0, 0);
896 struct async_load_state {
903 load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
904 const char *name, XRectangle *geom, void *closure)
906 async_load_state *state = (async_load_state *) closure;
907 state->done_p = True;
908 state->filename = (name ? strdup (name) : 0);
913 load_image_async_simple (async_load_state *state,
918 XRectangle *geometry_ret)
920 if (state && state->done_p) /* done! */
923 *filename_ret = state->filename;
924 else if (state->filename)
925 free (state->filename);
928 *geometry_ret = state->geom;
933 else if (! state) /* first time */
935 state = (async_load_state *) calloc (1, sizeof(*state));
936 state->done_p = False;
937 print_loading_msg (screen, window);
938 load_image_async (screen, window, drawable,
939 load_image_async_simple_cb,
943 else /* still waiting */