http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[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   unsigned 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   unsigned 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                    char **name_ret)
185 {
186   Display *dpy = DisplayOfScreen (screen);
187   char *grabber = get_string_resource ("desktopGrabber", "DesktopGrabber");
188   char *cmd;
189   char id[200];
190
191   if (!grabber || !*grabber)
192     {
193       fprintf (stderr,
194          "%s: resources installed incorrectly: \"desktopGrabber\" is unset!\n",
195                progname);
196       exit (1);
197     }
198
199   sprintf (id, "0x%lx 0x%lx",
200            (unsigned long) window,
201            (unsigned long) drawable);
202   cmd = (char *) malloc (strlen(grabber) + strlen(id) + 1);
203
204   /* Needn't worry about buffer overflows here, because the buffer is
205      longer than the length of the format string, and the length of what
206      we're putting into it.  So the only way to crash would be if the
207      format string itself was corrupted, but that comes from the
208      resource database, and if hostile forces have access to that,
209      then the game is already over.
210    */
211   sprintf (cmd, grabber, id);
212
213   /* In case "cmd" fails, leave some random image on the screen, not just
214      black or white, so that it's more obvious what went wrong. */
215   checkerboard (screen, drawable);
216
217   XSync (dpy, True);
218   hack_subproc_environment (dpy);
219   system (cmd);
220   free (cmd);
221   XSync (dpy, True);
222
223   if (name_ret) 
224     {
225       Atom type;
226       int format;
227       unsigned long nitems, bytesafter;
228       char *name=NULL;
229
230       *name_ret = NULL;
231
232       if (XGetWindowProperty (dpy, window,
233                               XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME,
234                                            False),
235                               0, 1024, False, XA_STRING,
236                               &type, &format, &nitems, &bytesafter,
237                               (unsigned char **) &name)
238           == Success
239           && type != None)
240         *name_ret = strdup(name);
241     }
242 }