1 /* xscreensaver, Copyright (c) 1992-2012 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 if (absfile) free (absfile);
623 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable, buf, &geom,
626 free (clo2->directory);
631 # else /* USE_IPHONE */
633 /* Callback for ios_load_random_image(), called after we have loaded an
634 image from the iOS device's Photo Library. See iosgrabimage.m.
637 ios_load_random_image_cb (void *uiimage, const char *filename, void *closure)
639 struct pipe_closure *clo2 = (struct pipe_closure *) closure;
640 Display *dpy = DisplayOfScreen (clo2->screen);
645 jwxyz_draw_NSImage_or_CGImage (DisplayOfScreen (clo2->screen),
647 True, uiimage, &geom,
650 else /* Probably means no images in the gallery. */
652 XWindowAttributes xgwa;
655 unsigned int w, h, bbw, d;
656 XGetWindowAttributes (dpy, clo2->xwindow, &xgwa);
657 XGetGeometry (dpy, clo2->drawable, &r, &x, &y, &w, &h, &bbw, &d);
658 draw_colorbars (clo2->screen, xgwa.visual, clo2->drawable, xgwa.colormap,
666 clo2->callback (clo2->screen, clo2->xwindow, clo2->drawable,
667 filename, &geom, clo2->closure);
669 if (clo2->directory) free (clo2->directory);
673 # endif /* USE_IPHONE */
677 osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable,
679 void (*callback) (Screen *, Window, Drawable,
685 # if 0 /* do it synchronously */
687 FILE *pipe = open_image_name_pipe (dir);
690 fgets (buf, sizeof(buf)-1, pipe);
693 /* strip trailing newline */
695 while (L > 0 && (buf[L-1] == '\r' || buf[L-1] == '\n'))
699 if (! osx_load_image_file (screen, xwindow, drawable, buf, &geom)) {
703 callback (screen, xwindow, drawable, buf, &geom, closure);
705 # else /* do it asynchronously */
707 struct pipe_closure *clo2 = (struct pipe_closure *) calloc (1, sizeof(*clo2));
709 clo2->screen = screen;
710 clo2->xwindow = xwindow;
711 clo2->drawable = drawable;
712 clo2->callback = callback;
713 clo2->closure = closure;
716 clo2->directory = strdup (dir);
717 clo2->pipe = open_image_name_pipe (dir);
718 clo2->id = XtAppAddInput (XtDisplayToApplicationContext (
719 DisplayOfScreen (screen)),
721 (XtPointer) (XtInputReadMask | XtInputExceptMask),
722 pipe_cb, (XtPointer) clo2);
723 # else /* USE_IPHONE */
724 ios_load_random_image (ios_load_random_image_cb, clo2);
725 # endif /* USE_IPHONE */
731 /* Loads an image into the Drawable, returning once the image is loaded.
734 load_random_image_1 (Screen *screen, Window window, Drawable drawable,
735 void (*callback) (Screen *, Window, Drawable,
736 const char *name, XRectangle *geom,
740 XRectangle *geom_ret)
742 Display *dpy = DisplayOfScreen (screen);
743 XWindowAttributes xgwa;
744 Bool deskp = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
745 Bool filep = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
748 XRectangle geom_ret_2;
749 char *name_ret_2 = 0;
751 if (!drawable) abort();
754 geom_ret = &geom_ret_2;
755 name_ret = &name_ret_2;
758 XGetWindowAttributes (dpy, window, &xgwa);
762 unsigned int w, h, bbw, d;
763 XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
774 geom_ret->width = xgwa.width;
775 geom_ret->height = xgwa.height;
780 dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory");
784 # endif /* ! USE_IPHONE */
786 if (deskp && filep) {
787 deskp = !(random() & 5); /* if both, desktop 1/5th of the time */
791 if (filep && !done) {
792 osx_load_image_file_async (screen, window, drawable, dir,
797 if (deskp && !done) {
798 if (osx_grab_desktop_image (screen, window, drawable, &geom_ret_2)) {
800 *name_ret = strdup ("desktop");
806 draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
807 0, 0, xgwa.width, xgwa.height);
810 /* If we got here, we loaded synchronously even though they wanted async.
812 callback (screen, window, drawable, name_ret_2, &geom_ret_2, closure);
816 #endif /* HAVE_COCOA */
819 /* Writes the string "Loading..." in the middle of the screen.
820 This will presumably get blown away when the image finally loads,
821 minutes or hours later...
823 This is called by load_image_async_simple() but not by load_image_async(),
824 since it is assumed that hacks that are loading more than one image
825 *at one time* will be doing something more clever than just blocking
829 print_loading_msg (Screen *screen, Window window)
831 Display *dpy = DisplayOfScreen (screen);
832 XWindowAttributes xgwa;
836 char *fn = get_string_resource (dpy, "labelFont", "Font");
837 const char *text = "Loading...";
840 if (!fn) fn = get_string_resource (dpy, "titleFont", "Font");
841 if (!fn) fn = get_string_resource (dpy, "font", "Font");
842 if (!fn) fn = strdup ("-*-times-bold-r-normal-*-180-*");
843 f = XLoadQueryFont (dpy, fn);
844 if (!f) f = XLoadQueryFont (dpy, "fixed");
849 XGetWindowAttributes (dpy, window, &xgwa);
850 w = XTextWidth (f, text, strlen(text));
852 gcv.foreground = get_pixel_resource (dpy, xgwa.colormap,
853 "foreground", "Foreground");
854 gcv.background = get_pixel_resource (dpy, xgwa.colormap,
855 "background", "Background");
857 gc = XCreateGC (dpy, window, GCFont | GCForeground | GCBackground, &gcv);
858 XDrawImageString (dpy, window, gc,
859 (xgwa.width - w) / 2,
860 (xgwa.height - (f->ascent + f->descent)) / 2 + f->ascent,
868 /* Loads an image into the Drawable in the background;
869 when the image is fully loaded, runs the callback.
870 When grabbing desktop images, the Window will be unmapped first.
873 load_image_async (Screen *screen, Window window, Drawable drawable,
874 void (*callback) (Screen *, Window, Drawable,
875 const char *name, XRectangle *geom,
879 load_random_image_1 (screen, window, drawable, callback, closure, 0, 0);
882 struct async_load_state {
889 load_image_async_simple_cb (Screen *screen, Window window, Drawable drawable,
890 const char *name, XRectangle *geom, void *closure)
892 async_load_state *state = (async_load_state *) closure;
893 state->done_p = True;
894 state->filename = (name ? strdup (name) : 0);
899 load_image_async_simple (async_load_state *state,
904 XRectangle *geometry_ret)
906 if (state && state->done_p) /* done! */
909 *filename_ret = state->filename;
910 else if (state->filename)
911 free (state->filename);
914 *geometry_ret = state->geom;
919 else if (! state) /* first time */
921 state = (async_load_state *) calloc (1, sizeof(*state));
922 state->done_p = False;
923 print_loading_msg (screen, window);
924 load_image_async (screen, window, drawable,
925 load_image_async_simple_cb,
929 else /* still waiting */