http://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996...
[xscreensaver] / utils / grabscreen.c
1 /* xscreensaver, Copyright (c) 1992, 1993, 1994 Jamie Zawinski <jwz@netscape.com>
2  *
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 
9  * implied warranty.
10  */
11
12 /* This file contains code for grabbing an image of the screen to hack its
13    bits.  This is a little tricky, since doing this involves the need to tell
14    the difference between drawing on the actual root window, and on the fake
15    root window used by the screensaver, since at this level the illusion 
16    breaks down...
17  */
18
19 #if __STDC__
20 #include <stdlib.h>
21 #include <unistd.h>
22 #endif
23
24 #include <X11/Xlib.h>
25 #include <X11/Xatom.h>
26 #include <X11/Xutil.h>
27
28 static Bool
29 MapNotify_event_p (dpy, event, window)
30      Display *dpy;
31      XEvent *event;
32      XPointer window;
33 {
34   return (event->xany.type == MapNotify &&
35           event->xvisibility.window == (Window) window);
36 }
37
38
39 #if __STDC__
40 static Bool screensaver_window_p (Display *, Window);
41 #endif
42
43 static Bool
44 screensaver_window_p (dpy, window)
45      Display *dpy;
46      Window window;
47 {
48   Atom type;
49   int format;
50   unsigned long nitems, bytesafter;
51   char *version;
52   if (XGetWindowProperty (dpy, window,
53                           XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
54                           0, 1, False, XA_STRING,
55                           &type, &format, &nitems, &bytesafter,
56                           (unsigned char **) &version)
57       == Success
58       && type != None)
59     return True;
60   return False;
61 }
62
63 Pixmap
64 grab_screen_image (dpy, window, root_p)
65      Display *dpy;
66      Window window;
67      int root_p;
68 {
69   Pixmap pixmap = 0;
70   XWindowAttributes xgwa;
71
72   XGetWindowAttributes (dpy, window, &xgwa);
73
74   if (screensaver_window_p (dpy, window))
75     {
76       /* note: this assumes vroot.h didn't encapsulate the XRootWindowOfScreen
77          function, only the RootWindowOfScreen macro... */
78       Window real_root = XRootWindowOfScreen (DefaultScreenOfDisplay (dpy));
79
80       XSetWindowBackgroundPixmap (dpy, window, None);
81
82       /* prevent random viewer of the screen saver (locker) from messing
83          with windows.   We don't check whether it succeeded, because what
84          are our options, really... */
85       XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
86                     GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
87       XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
88                      CurrentTime);
89
90       XUnmapWindow (dpy, window);
91       XSync (dpy, True);
92       sleep (5);     /* wait for everyone to swap in and handle exposes... */
93       XMapRaised (dpy, window);
94
95       XUngrabPointer (dpy, CurrentTime);
96       XUngrabKeyboard (dpy, CurrentTime);
97
98       XSync (dpy, True);
99     }
100   else if (root_p)
101     {
102       XGCValues gcv;
103       GC gc;
104       gcv.function = GXcopy;
105       gcv.subwindow_mode = IncludeInferiors;
106       gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
107       pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
108       XCopyArea (dpy, RootWindowOfScreen (xgwa.screen), pixmap, gc,
109                  xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
110       XFreeGC (dpy, gc);
111       XSetWindowBackgroundPixmap (dpy, window, pixmap);
112     }
113   else
114     {
115       XEvent event;
116       XSetWindowBackgroundPixmap (dpy, window, None);
117       XMapWindow (dpy, window);
118       XFlush (dpy);
119       XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
120       XSync (dpy, True);
121     }
122   return pixmap;
123 }
124
125
126 /* When we are grabbing and manipulating a screen image, it's important that
127    we use the same colormap it originally had.  So, if the screensaver was
128    started with -install, we need to copy the contents of the default colormap
129    into the screensaver's colormap.
130  */
131 void
132 copy_default_colormap_contents (dpy, to_cmap, to_visual)
133      Display *dpy;
134      Colormap to_cmap;
135      Visual *to_visual;
136 {
137   Screen *screen = DefaultScreenOfDisplay (dpy);
138   Visual *from_visual = DefaultVisualOfScreen (screen);
139   Colormap from_cmap = XDefaultColormapOfScreen (screen);
140
141   XColor *old_colors, *new_colors;
142   unsigned long *pixels;
143   XVisualInfo vi_in, *vi_out;
144   int out_count;
145   int from_cells, to_cells, max_cells;
146   int requested;
147   int i;
148
149   if (from_cmap == to_cmap)
150     return;
151
152   vi_in.screen = XScreenNumberOfScreen (screen);
153   vi_in.visualid = XVisualIDFromVisual (from_visual);
154   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
155                            &vi_in, &out_count);
156   if (! vi_out) abort ();
157   from_cells = vi_out [0].colormap_size;
158   XFree ((char *) vi_out);
159
160   vi_in.screen = XScreenNumberOfScreen (screen);
161   vi_in.visualid = XVisualIDFromVisual (to_visual);
162   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
163                            &vi_in, &out_count);
164   if (! vi_out) abort ();
165   to_cells = vi_out [0].colormap_size;
166   XFree ((char *) vi_out);
167
168   max_cells = (from_cells > to_cells ? to_cells : from_cells);
169
170   old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
171   new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
172   pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
173   for (i = 0; i < max_cells; i++)
174     old_colors[i].pixel = i;
175   XQueryColors (dpy, from_cmap, old_colors, max_cells);
176
177   requested = max_cells;
178   while (requested > 0)
179     {
180       if (XAllocColorCells (dpy, to_cmap, False, 0, 0, pixels, requested))
181         {
182           /* Got all the pixels we asked for. */
183           for (i = 0; i < requested; i++)
184             new_colors[i] = old_colors [pixels[i]];
185           XStoreColors (dpy, to_cmap, new_colors, requested);
186         }
187       else
188         {
189           /* We didn't get all/any of the pixels we asked for.  This time, ask
190              for half as many.  (If we do get all that we ask for, we ask for
191              the same number again next time, so we only do O(log(n)) server
192              roundtrips.) */
193           requested = requested / 2;
194         }
195     }
196
197   free (old_colors);
198   free (new_colors);
199   free (pixels);
200 }