92481b20292181a0da98844acc36ea286a2c233a
[xscreensaver] / utils / grabclient.c
1 /* xscreensaver, Copyright (c) 1992, 1993, 1994, 1997, 1998, 2001, 2003
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* This file contains code for running an external program to grab an image
14    onto the given window.  The external program in question must take a
15    window ID as its argument, e.g., the "xscreensaver-getimage" program
16    in the hacks/ directory.
17
18    That program links against utils/grabimage.c, which happens to export the
19    same API as this program (utils/grabclient.c).
20  */
21
22 #include "utils.h"
23 #include "grabscreen.h"
24 #include "resources.h"
25
26 #include "vroot.h"
27 #include <X11/Xatom.h>
28
29 extern char *progname;
30
31
32 static Bool error_handler_hit_p = False;
33
34 static int
35 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
36 {
37   error_handler_hit_p = True;
38   return 0;
39 }
40
41
42 /* Returns True if the given Drawable is a Window; False if it's a Pixmap.
43  */
44 static Bool
45 drawable_window_p (Display *dpy, Drawable d)
46 {
47   XErrorHandler old_handler;
48   XWindowAttributes xgwa;
49
50   XSync (dpy, False);
51   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
52   error_handler_hit_p = False;
53   XGetWindowAttributes (dpy, d, &xgwa);
54   XSync (dpy, False);
55   XSetErrorHandler (old_handler);
56   XSync (dpy, False);
57
58   if (!error_handler_hit_p)
59     return True;   /* It's a Window. */
60   else
61     return False;  /* It's a Pixmap, or an invalid ID. */
62 }
63
64
65 static Bool
66 xscreensaver_window_p (Display *dpy, Window window)
67 {
68   Atom type;
69   int format;
70   unsigned long nitems, bytesafter;
71   char *version;
72   if (XGetWindowProperty (dpy, window,
73                           XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
74                           0, 1, False, XA_STRING,
75                           &type, &format, &nitems, &bytesafter,
76                           (unsigned char **) &version)
77       == Success
78       && type != None)
79     return True;
80   return False;
81 }
82
83
84 /* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
85    on a window whose depth is not the maximal depth of the screen?  Or
86    something.  Anyway, things don't work unless we: use SubwindowMode for
87    the real root window (or a legitimate virtual root window); but do not
88    use SubwindowMode for the xscreensaver window.  I make no attempt to
89    explain.
90  */
91 Bool
92 use_subwindow_mode_p(Screen *screen, Window window)
93 {
94   if (window != VirtualRootWindowOfScreen(screen))
95     return False;
96   else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
97     return False;
98   else
99     return True;
100 }
101
102
103 static void
104 checkerboard (Screen *screen, Drawable drawable)
105 {
106   Display *dpy = DisplayOfScreen (screen);
107   int x, y;
108   int size = 24;
109   XColor fg, bg;
110   XGCValues gcv;
111   GC gc = XCreateGC (dpy, drawable, 0, &gcv);
112   Colormap cmap;
113   int win_width, win_height;
114
115   fg.flags = bg.flags = DoRed|DoGreen|DoBlue;
116   fg.red = fg.green = fg.blue = 0x0000;
117   bg.red = bg.green = bg.blue = 0x4444;
118   fg.pixel = 0;
119   bg.pixel = 1;
120
121   if (drawable_window_p (dpy, drawable))
122     {
123       XWindowAttributes xgwa;
124       XGetWindowAttributes (dpy, drawable, &xgwa);
125       win_width = xgwa.width;
126       win_height = xgwa.height;
127       cmap = xgwa.colormap;
128       screen = xgwa.screen;
129     }
130   else  /* it's a pixmap */
131     {
132       XWindowAttributes xgwa;
133       Window root;
134       int x, y;
135       unsigned int bw, d;
136       XGetWindowAttributes (dpy, RootWindowOfScreen (screen), &xgwa);
137       cmap = xgwa.colormap;
138       XGetGeometry (dpy, drawable,
139                     &root, &x, &y, &win_width, &win_height, &bw, &d);
140     }
141
142   /* Allocate black and gray, but don't hold them locked. */
143   if (XAllocColor (dpy, cmap, &fg))
144     XFreeColors (dpy, cmap, &fg.pixel, 1, 0);
145   if (XAllocColor (dpy, cmap, &bg))
146     XFreeColors (dpy, cmap, &bg.pixel, 1, 0);
147
148   XSetForeground (dpy, gc, bg.pixel);
149   XFillRectangle (dpy, drawable, gc, 0, 0, win_width, win_height);
150   XSetForeground (dpy, gc, fg.pixel);
151   for (y = 0; y < win_height; y += size+size)
152     for (x = 0; x < win_width; x += size+size)
153       {
154         XFillRectangle (dpy, drawable, gc, x,      y,      size, size);
155         XFillRectangle (dpy, drawable, gc, x+size, y+size, size, size);
156       }
157 }
158
159 static void
160 hack_subproc_environment (Display *dpy)
161 {
162   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
163      the spawned processes inherit is what we are actually using.
164    */
165   const char *odpy = DisplayString (dpy);
166   char *ndpy = (char *) malloc(strlen(odpy) + 20);
167   strcpy (ndpy, "DISPLAY=");
168   strcat (ndpy, odpy);
169
170   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
171      any more, right?  It's not Posix, but everyone seems to have it. */
172 #ifdef HAVE_PUTENV
173   if (putenv (ndpy))
174     abort ();
175 #endif /* HAVE_PUTENV */
176 }
177
178
179 /* Loads an image into the Drawable.
180    When grabbing desktop images, the Window will be unmapped first.
181  */
182 void
183 load_random_image (Screen *screen, Window window, Drawable drawable)
184 {
185   Display *dpy = DisplayOfScreen (screen);
186   char *grabber = get_string_resource ("desktopGrabber", "DesktopGrabber");
187   char *cmd;
188   char id[200];
189
190   if (!grabber || !*grabber)
191     {
192       fprintf (stderr,
193          "%s: resources installed incorrectly: \"desktopGrabber\" is unset!\n",
194                progname);
195       exit (1);
196     }
197
198   sprintf (id, "0x%lx 0x%lx",
199            (unsigned long) window,
200            (unsigned long) drawable);
201   cmd = (char *) malloc (strlen(grabber) + strlen(id) + 1);
202
203   /* Needn't worry about buffer overflows here, because the buffer is
204      longer than the length of the format string, and the length of what
205      we're putting into it.  So the only way to crash would be if the
206      format string itself was corrupted, but that comes from the
207      resource database, and if hostile forces have access to that,
208      then the game is already over.
209    */
210   sprintf (cmd, grabber, id);
211
212   /* In case "cmd" fails, leave some random image on the screen, not just
213      black or white, so that it's more obvious what went wrong. */
214   checkerboard (screen, drawable);
215
216   XSync (dpy, True);
217   hack_subproc_environment (dpy);
218   system (cmd);
219   free (cmd);
220   XSync (dpy, True);
221 }