Added missing newline in NEDsim error message.
[screensavers] / screenhack / grabscreen.c
CommitLineData
3144ee8a
AT
1/* xscreensaver, Copyright (c) 1992-2016 Jamie Zawinski <jwz@jwz.org>
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 The hacks themselves use utils/grabclient.c to invoke the
19 "xscreensaver-getimage" program as a sub-process.
20
21 On "real" X11 systems:
22
23 "driver/xscreensaver-getimage" runs the code in this file to grab
24 the X11 root window image as a Pixmap.
25
26 On MacOS systems running X11, which nobody does any more:
27
28 "driver/xscreensaver-getimage" runs the Perl script
29 "driver/xscreensaver-getimage-desktop", which in turn runs the MacOS
30 program "/usr/sbin/screencapture" to get the Mac desktop image as a
31 PNG file.
32
33 On MacOS systems running the native Cocoa build, or on iOS or Android
34 systems:
35
36 "driver/xscreensaver-getimage" is not used. Instead, each saver's
37 "utils/grabclient.c" links against "OSX/grabclient-osx.m",
38 "OSX/grabclient-ios.m" or "jwxyz/jwxyz-android.c" to grab
39 screenshots directly without invoking a sub-process to do it.
40
41 See the comment at the top of utils/grabclient.c for a more detailed
42 explanation.
43 */
44
45#include "utils.h"
46#include "yarandom.h"
47
48#include <X11/Xatom.h>
49#include <X11/Xutil.h>
50
51#ifdef HAVE_XMU
52# ifndef VMS
53# include <X11/Xmu/WinUtil.h>
54# else /* VMS */
55# include <Xmu/WinUtil.h>
56# endif /* VMS */
57#endif
58
59#include "usleep.h"
60#include "colors.h"
61#include "grabscreen.h"
62#include "visual.h"
63#include "resources.h"
64
65#include "vroot.h"
66#undef RootWindowOfScreen
67#undef RootWindow
68#undef DefaultRootWindow
69
70
71#ifdef HAVE_READ_DISPLAY_EXTENSION
72# include <X11/extensions/readdisplay.h>
73 static Bool read_display (Screen *, Window, Pixmap, Bool);
74#endif /* HAVE_READ_DISPLAY_EXTENSION */
75
76
77static void copy_default_colormap_contents (Screen *, Colormap, Visual *);
78
79#ifdef HAVE_READ_DISPLAY_EXTENSION
80static void allocate_cubic_colormap (Screen *, Window, Visual *);
81void remap_image (Screen *, Window, Colormap, XImage *);
82#endif
83
84
85static Bool
86MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
87{
88 return (event->xany.type == MapNotify &&
89 event->xvisibility.window == (Window) window);
90}
91
92extern char *progname;
93Bool grab_verbose_p = False;
94
95void
96grabscreen_verbose(void)
97{
98 grab_verbose_p = True;
99}
100
101
102static void
103raise_window(Display *dpy, Window window, Bool dont_wait)
104{
105 if (grab_verbose_p)
106 fprintf(stderr, "%s: raising window 0x%08lX (%s)\n",
107 progname, (unsigned long) window,
108 (dont_wait ? "not waiting" : "waiting"));
109
110 if (! dont_wait)
111 {
112 XWindowAttributes xgwa;
113 XSizeHints hints;
114 long supplied = 0;
115 memset(&hints, 0, sizeof(hints));
116 XGetWMNormalHints(dpy, window, &hints, &supplied);
117 XGetWindowAttributes (dpy, window, &xgwa);
118 hints.x = xgwa.x;
119 hints.y = xgwa.y;
120 hints.width = xgwa.width;
121 hints.height = xgwa.height;
122 hints.flags |= (PPosition|USPosition|PSize|USSize);
123 XSetWMNormalHints(dpy, window, &hints);
124
125 XSelectInput (dpy, window, (xgwa.your_event_mask | StructureNotifyMask));
126 }
127
128 XMapRaised(dpy, window);
129
130 if (! dont_wait)
131 {
132 XEvent event;
133 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
134 XSync (dpy, True);
135 }
136}
137
138
139static Bool
140xscreensaver_window_p (Display *dpy, Window window)
141{
142 Atom type;
143 int format;
144 unsigned long nitems, bytesafter;
145 unsigned char *version;
146 if (XGetWindowProperty (dpy, window,
147 XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
148 0, 1, False, XA_STRING,
149 &type, &format, &nitems, &bytesafter,
150 &version)
151 == Success
152 && type != None)
153 return True;
154 return False;
155}
156
157
158
159/* Whether the given window is:
160 - the real root window;
161 - a direct child of the root window;
162 - a direct child of the window manager's decorations.
163 */
164Bool
165top_level_window_p (Screen *screen, Window window)
166{
167 Display *dpy = DisplayOfScreen (screen);
168 Window root, parent, *kids;
169 unsigned int nkids;
170
171 if (!XQueryTree (dpy, window, &root, &parent, &kids, &nkids))
172 return False;
173
174 if (window == root)
175 return True;
176
177 /* If our direct parent is the real root window, then yes. */
178 if (parent == root)
179 return True;
180 else
181 {
182 Atom type = None;
183 int format;
184 unsigned long nitems, bytesafter;
185 unsigned char *data;
186
187 /* If our direct parent has the WM_STATE property, then it is a
188 window manager decoration -- yes.
189 */
190 if (XGetWindowProperty (dpy, window,
191 XInternAtom (dpy, "WM_STATE", True),
192 0, 0, False, AnyPropertyType,
193 &type, &format, &nitems, &bytesafter,
194 (unsigned char **) &data)
195 == Success
196 && type != None)
197 return True;
198 }
199
200 /* Else, no. We're deep in a tree somewhere.
201 */
202 return False;
203}
204
205
206static Bool error_handler_hit_p = False;
207static XErrorHandler old_ehandler = 0;
208static int
209BadWindow_ehandler (Display *dpy, XErrorEvent *error)
210{
211 error_handler_hit_p = True;
212 if (error->error_code == BadWindow || error->error_code == BadDrawable)
213 return 0;
214 else if (!old_ehandler)
215 {
216 abort();
217 return 0;
218 }
219 else
220 return (*old_ehandler) (dpy, error);
221}
222
223
224/* XCopyArea seems not to work right on SGI O2s if you draw in SubwindowMode
225 on a window whose depth is not the maximal depth of the screen? Or
226 something. Anyway, things don't work unless we: use SubwindowMode for
227 the real root window (or a legitimate virtual root window); but do not
228 use SubwindowMode for the xscreensaver window. I make no attempt to
229 explain.
230 */
231Bool
232use_subwindow_mode_p(Screen *screen, Window window)
233{
234 if (window != VirtualRootWindowOfScreen(screen))
235 return False;
236 else if (xscreensaver_window_p(DisplayOfScreen(screen), window))
237 return False;
238 else
239 return True;
240}
241
242
243/* Install the colormaps of all visible windows, deepest first.
244 This should leave the colormaps of the topmost windows installed
245 (if only N colormaps can be installed at a time, then only the
246 topmost N windows will be shown in the right colors.)
247 */
248static void
249install_screen_colormaps (Screen *screen)
250{
251 unsigned int i;
252 Display *dpy = DisplayOfScreen (screen);
253 Window real_root;
254 Window parent, *kids = 0;
255 unsigned int nkids = 0;
256
257 XSync (dpy, False);
258 old_ehandler = XSetErrorHandler (BadWindow_ehandler);
259 error_handler_hit_p = False;
260
261 real_root = XRootWindowOfScreen (screen); /* not vroot */
262 if (XQueryTree (dpy, real_root, &real_root, &parent, &kids, &nkids))
263 for (i = 0; i < nkids; i++)
264 {
265 XWindowAttributes xgwa;
266 Window client;
267#ifdef HAVE_XMU
268 /* #### need to put XmuClientWindow() in xmu.c, sigh... */
269 if (! (client = XmuClientWindow (dpy, kids[i])))
270#endif
271 client = kids[i];
272 xgwa.colormap = 0;
273 XGetWindowAttributes (dpy, client, &xgwa);
274 if (xgwa.colormap && xgwa.map_state == IsViewable)
275 XInstallColormap (dpy, xgwa.colormap);
276 }
277 XInstallColormap (dpy, DefaultColormapOfScreen (screen));
278 XSync (dpy, False);
279 XSetErrorHandler (old_ehandler);
280 XSync (dpy, False);
281
282 if (kids)
283 XFree ((char *) kids);
284}
285
286
287void
288grab_screen_image_internal (Screen *screen, Window window)
289{
290 Display *dpy = DisplayOfScreen (screen);
291 XWindowAttributes xgwa;
292 Window real_root;
293 Bool root_p;
294 Bool saver_p;
295 Bool grab_mouse_p = False;
296 int unmap_time = 0;
297
298 real_root = XRootWindowOfScreen (screen); /* not vroot */
299 root_p = (window == real_root);
300 saver_p = xscreensaver_window_p (dpy, window);
301
302 XGetWindowAttributes (dpy, window, &xgwa);
303 screen = xgwa.screen;
304
305 if (saver_p)
306 /* I think this is redundant, but just to be safe... */
307 root_p = False;
308
309 if (saver_p)
310 /* The only time grabbing the mouse is important is if this program
311 is being run while the saver is locking the screen. */
312 grab_mouse_p = True;
313
314 if (!root_p)
315 {
316 double unmap = 0;
317 if (saver_p)
318 {
319 unmap = get_float_resource(dpy, "grabRootDelay", "Seconds");
320 if (unmap <= 0.00001 || unmap > 20) unmap = 2.5;
321 }
322 else
323 {
324 unmap = get_float_resource(dpy, "grabWindowDelay", "Seconds");
325 if (unmap <= 0.00001 || unmap > 20) unmap = 0.66;
326 }
327 unmap_time = unmap * 100000;
328 }
329
330 if (grab_verbose_p)
331 {
332 fprintf(stderr,
333 "\n%s: window 0x%08lX root: %d saver: %d grab: %d wait: %.1f\n",
334 progname, (unsigned long) window,
335 root_p, saver_p, grab_mouse_p, ((double)unmap_time)/1000000.0);
336
337 fprintf(stderr, "%s: ", progname);
338 describe_visual(stderr, screen, xgwa.visual, False);
339 fprintf (stderr, "\n");
340 }
341
342
343 if (!root_p && !top_level_window_p (screen, window))
344 {
345 if (grab_verbose_p)
346 fprintf (stderr, "%s: not a top-level window: 0x%08lX: not grabbing\n",
347 progname, (unsigned long) window);
348 return;
349 }
350
351
352 if (!root_p)
353 XSetWindowBackgroundPixmap (dpy, window, None);
354
355 if (grab_mouse_p)
356 {
357 /* prevent random viewer of the screen saver (locker) from messing
358 with windows. We don't check whether it succeeded, because what
359 are our options, really... */
360 XGrabPointer (dpy, real_root, True, ButtonPressMask|ButtonReleaseMask,
361 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
362 XGrabKeyboard (dpy, real_root, True, GrabModeSync, GrabModeAsync,
363 CurrentTime);
364 }
365
366 if (unmap_time > 0)
367 {
368 XUnmapWindow (dpy, window);
369 install_screen_colormaps (screen);
370 XSync (dpy, True);
371 usleep(unmap_time); /* wait for everyone to swap in and handle exposes */
372 }
373
374 if (!root_p)
375 {
376#ifdef HAVE_READ_DISPLAY_EXTENSION
377 if (! read_display(screen, window, 0, saver_p))
378#endif /* HAVE_READ_DISPLAY_EXTENSION */
379 {
380#ifdef HAVE_READ_DISPLAY_EXTENSION
381 if (grab_verbose_p)
382 fprintf(stderr, "%s: read_display() failed\n", progname);
383#endif /* HAVE_READ_DISPLAY_EXTENSION */
384
385 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
386 raise_window(dpy, window, saver_p);
387
388 /* Generally it's bad news to call XInstallColormap() explicitly,
389 but this file does a lot of sleazy stuff already... This is to
390 make sure that the window's colormap is installed, even in the
391 case where the window is OverrideRedirect. */
392 if (xgwa.colormap) XInstallColormap (dpy, xgwa.colormap);
393 XSync (dpy, False);
394 }
395 }
396 else /* root_p */
397 {
398 Pixmap pixmap;
399 pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
400
401#ifdef HAVE_READ_DISPLAY_EXTENSION
402 if (! read_display(screen, window, pixmap, True))
403#endif
404 {
405 Window real_root = XRootWindowOfScreen (screen); /* not vroot */
406 XGCValues gcv;
407 GC gc;
408
409#ifdef HAVE_READ_DISPLAY_EXTENSION
410 if (grab_verbose_p)
411 fprintf(stderr, "%s: read_display() failed\n", progname);
412#endif /* HAVE_READ_DISPLAY_EXTENSION */
413
414 copy_default_colormap_contents (screen, xgwa.colormap, xgwa.visual);
415
416 gcv.function = GXcopy;
417 gcv.subwindow_mode = IncludeInferiors;
418 gc = XCreateGC (dpy, window, GCFunction | GCSubwindowMode, &gcv);
419 XCopyArea (dpy, real_root, pixmap, gc,
420 xgwa.x, xgwa.y, xgwa.width, xgwa.height, 0, 0);
421 XFreeGC (dpy, gc);
422 }
423 XSetWindowBackgroundPixmap (dpy, window, pixmap);
424 XFreePixmap (dpy, pixmap);
425 }
426
427 if (grab_verbose_p)
428 fprintf (stderr, "%s: grabbed %d bit screen image to %swindow.\n",
429 progname, xgwa.depth,
430 (root_p ? "real root " : ""));
431
432 if (grab_mouse_p)
433 {
434 XUngrabPointer (dpy, CurrentTime);
435 XUngrabKeyboard (dpy, CurrentTime);
436 }
437
438 XSync (dpy, True);
439}
440
441
442/* When we are grabbing and manipulating a screen image, it's important that
443 we use the same colormap it originally had. So, if the screensaver was
444 started with -install, we need to copy the contents of the default colormap
445 into the screensaver's colormap.
446 */
447static void
448copy_default_colormap_contents (Screen *screen,
449 Colormap to_cmap,
450 Visual *to_visual)
451{
452 Display *dpy = DisplayOfScreen (screen);
453 Visual *from_visual = DefaultVisualOfScreen (screen);
454 Colormap from_cmap = XDefaultColormapOfScreen (screen);
455
456 XColor *old_colors, *new_colors;
457 unsigned long *pixels;
458 XVisualInfo vi_in, *vi_out;
459 int out_count;
460 int from_cells, to_cells, max_cells, got_cells;
461 int i;
462
463 if (from_cmap == to_cmap)
464 return;
465
466 vi_in.screen = XScreenNumberOfScreen (screen);
467 vi_in.visualid = XVisualIDFromVisual (from_visual);
468 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
469 &vi_in, &out_count);
470 if (! vi_out) abort ();
471 from_cells = vi_out [0].colormap_size;
472 XFree ((char *) vi_out);
473
474 vi_in.screen = XScreenNumberOfScreen (screen);
475 vi_in.visualid = XVisualIDFromVisual (to_visual);
476 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
477 &vi_in, &out_count);
478 if (! vi_out) abort ();
479 to_cells = vi_out [0].colormap_size;
480 XFree ((char *) vi_out);
481
482 max_cells = (from_cells > to_cells ? to_cells : from_cells);
483
484 old_colors = (XColor *) calloc (sizeof (XColor), max_cells);
485 new_colors = (XColor *) calloc (sizeof (XColor), max_cells);
486 pixels = (unsigned long *) calloc (sizeof (unsigned long), max_cells);
487 for (i = 0; i < max_cells; i++)
488 old_colors[i].pixel = i;
489 XQueryColors (dpy, from_cmap, old_colors, max_cells);
490
491 got_cells = max_cells;
492 allocate_writable_colors (screen, to_cmap, pixels, &got_cells);
493
494 if (grab_verbose_p && got_cells != max_cells)
495 fprintf(stderr, "%s: got only %d of %d cells\n", progname,
496 got_cells, max_cells);
497
498 if (got_cells <= 0) /* we're screwed */
499 ;
500 else if (got_cells == max_cells && /* we're golden */
501 from_cells == to_cells)
502 XStoreColors (dpy, to_cmap, old_colors, got_cells);
503 else /* try to cope... */
504 {
505 for (i = 0; i < got_cells; i++)
506 {
507 XColor *c = old_colors + i;
508 int j;
509 for (j = 0; j < got_cells; j++)
510 if (pixels[j] == c->pixel)
511 {
512 /* only store this color value if this is one of the pixels
513 we were able to allocate. */
514 XStoreColors (dpy, to_cmap, c, 1);
515 break;
516 }
517 }
518 }
519
520
521 if (grab_verbose_p)
522 fprintf(stderr, "%s: installing copy of default colormap\n", progname);
523
524 free (old_colors);
525 free (new_colors);
526 free (pixels);
527}
528
529
530\f
531/* The SGI ReadDisplay extension.
532 This extension lets you get back a 24-bit image of the screen, taking into
533 account the colors with which all windows are *currently* displayed, even
534 if those windows have different visuals. Without this extension, presence
535 of windows with different visuals or colormaps will result in technicolor
536 when one tries to grab the screen image.
537 */
538
539#ifdef HAVE_READ_DISPLAY_EXTENSION
540
541static Bool
542read_display (Screen *screen, Window window, Pixmap into_pixmap,
543 Bool dont_wait)
544{
545 Display *dpy = DisplayOfScreen (screen);
546 XWindowAttributes xgwa;
547 int rd_event_base = 0;
548 int rd_error_base = 0;
549 unsigned long hints = 0;
550 XImage *image = 0;
551 XGCValues gcv;
552 int class;
553 GC gc;
554 Bool remap_p = False;
555
556 /* Check to see if the server supports the extension, and bug out if not.
557 */
558 if (! XReadDisplayQueryExtension (dpy, &rd_event_base, &rd_error_base))
559 {
560 if (grab_verbose_p)
561 fprintf(stderr, "%s: no XReadDisplay extension\n", progname);
562 return False;
563 }
564
565 /* If this isn't a visual we know how to handle, bug out. We handle:
566 = TrueColor in depths 8, 12, 15, 16, and 32;
567 = PseudoColor and DirectColor in depths 8 and 12.
568 */
569 XGetWindowAttributes(dpy, window, &xgwa);
570 class = visual_class (screen, xgwa.visual);
571 if (class == TrueColor)
572 {
573 if (xgwa.depth != 8 && xgwa.depth != 12 && xgwa.depth != 15 &&
574 xgwa.depth != 16 && xgwa.depth != 24 && xgwa.depth != 32)
575 {
576 if (grab_verbose_p)
577 fprintf(stderr, "%s: TrueColor depth %d unsupported\n",
578 progname, xgwa.depth);
579 return False;
580 }
581 }
582 else if (class == PseudoColor || class == DirectColor)
583 {
584 if (xgwa.depth != 8 && xgwa.depth != 12)
585 {
586 if (grab_verbose_p)
587 fprintf(stderr, "%s: Pseudo/DirectColor depth %d unsupported\n",
588 progname, xgwa.depth);
589 return False;
590 }
591 else
592 /* Allocate a TrueColor-like spread of colors for the image. */
593 remap_p = True;
594 }
595
596
597 /* Try and read the screen.
598 */
599 hints = (XRD_TRANSPARENT | XRD_READ_POINTER);
600 image = XReadDisplay (dpy, window, xgwa.x, xgwa.y, xgwa.width, xgwa.height,
601 hints, &hints);
602 if (!image)
603 {
604 if (grab_verbose_p)
605 fprintf(stderr, "%s: XReadDisplay() failed\n", progname);
606 return False;
607 }
608 if (!image->data)
609 {
610 if (grab_verbose_p)
611 fprintf(stderr, "%s: XReadDisplay() returned no data\n", progname);
612 XDestroyImage(image);
613 return False;
614 }
615
616 /* XReadDisplay tends to LIE about the depth of the image it read.
617 It is returning an XImage which has `depth' and `bits_per_pixel'
618 confused!
619
620 That is, on a 24-bit display, where all visuals claim depth 24, and
621 where XGetImage would return an XImage with depth 24, and where
622 XPutImage will get a BadMatch with images that are not depth 24,
623 XReadDisplay is returning images with depth 32! Fuckwits!
624
625 So if the visual is of depth 24, but the image came back as depth 32,
626 hack it to be 24 lest we get a BadMatch from XPutImage.
627
628 I wonder what happens on an 8-bit SGI... Probably it still returns
629 an image claiming depth 32? Certainly it can't be 8. So, let's just
630 smash it to 32...
631 */
632 if (image->depth == 32 /* && xgwa.depth == 24 */ )
633 image->depth = 24;
634
635 /* If the visual of the window/pixmap into which we're going to draw is
636 less deep than the screen itself, then we need to convert the grabbed bits
637 to match the depth by clipping off the less significant bit-planes of each
638 color component.
639 */
640 if (image->depth > xgwa.depth)
641 {
642 int x, y;
643 /* We use the same image->data in both images -- that's ok, because
644 since we're reading from B and writing to A, and B uses more bytes
645 per pixel than A, the write pointer won't overrun the read pointer.
646 */
647 XImage *image2 = XCreateImage (dpy, xgwa.visual, xgwa.depth,
648 ZPixmap, 0, image->data,
649 xgwa.width, xgwa.height,
650 8, 0);
651 if (!image2)
652 {
653 if (grab_verbose_p)
654 fprintf(stderr, "%s: out of memory?\n", progname);
655 return False;
656 }
657
658 if (grab_verbose_p)
659 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
660 progname, image->depth, xgwa.depth);
661
662 for (y = 0; y < image->height; y++)
663 for (x = 0; x < image->width; x++)
664 {
665 /* #### really these shift values should be determined from the
666 mask values -- but that's a pain in the ass, and anyway,
667 this is an SGI-specific extension so hardcoding assumptions
668 about the SGI server's behavior isn't *too* heinous... */
669 unsigned long pixel = XGetPixel(image, x, y);
670 unsigned int r = (pixel & image->red_mask);
671 unsigned int g = (pixel & image->green_mask) >> 8;
672 unsigned int b = (pixel & image->blue_mask) >> 16;
673
674 if (xgwa.depth == 8)
675 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
676 else if (xgwa.depth == 12)
677 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
678 else if (xgwa.depth == 16 || xgwa.depth == 15)
679 /* Gah! I don't understand why these are in the other order. */
680 pixel = (((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3)));
681 else
682 abort();
683
684 XPutPixel(image2, x, y, pixel);
685 }
686 image->data = 0;
687 XDestroyImage(image);
688 image = image2;
689 }
690
691 if (remap_p)
692 {
693 allocate_cubic_colormap (screen, window, xgwa.visual);
694 remap_image (screen, window, xgwa.colormap, image);
695 }
696
697 /* Now actually put the bits into the window or pixmap -- note the design
698 bogosity of this extension, where we've been forced to take 24 bit data
699 from the server to the client, and then push it back from the client to
700 the server, *without alteration*. We should have just been able to tell
701 the server, "put a screen image in this drawable", instead of having to
702 go through the intermediate step of converting it to an Image. Geez.
703 (Assuming that the window is of screen depth; we happen to handle less
704 deep windows, but that's beside the point.)
705 */
706 gcv.function = GXcopy;
707 gc = XCreateGC (dpy, window, GCFunction, &gcv);
708
709 if (into_pixmap)
710 {
711 gcv.function = GXcopy;
712 gc = XCreateGC (dpy, into_pixmap, GCFunction, &gcv);
713 XPutImage (dpy, into_pixmap, gc, image, 0, 0, 0, 0,
714 xgwa.width, xgwa.height);
715 }
716 else
717 {
718 gcv.function = GXcopy;
719 gc = XCreateGC (dpy, window, GCFunction, &gcv);
720
721 /* Ok, now we'll be needing that window on the screen... */
722 raise_window(dpy, window, dont_wait);
723
724 /* Plop down the bits... */
725 XPutImage (dpy, window, gc, image, 0, 0, 0, 0, xgwa.width, xgwa.height);
726 }
727 XFreeGC (dpy, gc);
728
729 if (image->data)
730 {
731 free(image->data);
732 image->data = 0;
733 }
734 XDestroyImage(image);
735
736 return True;
737}
738#endif /* HAVE_READ_DISPLAY_EXTENSION */
739
740
741#ifdef HAVE_READ_DISPLAY_EXTENSION
742
743/* Makes and installs a colormap that makes a PseudoColor or DirectColor
744 visual behave like a TrueColor visual of the same depth.
745
746 #### Duplicated in driver/xscreensaver-getimage.c
747 */
748static void
749allocate_cubic_colormap (Screen *screen, Window window, Visual *visual)
750{
751 Display *dpy = DisplayOfScreen (screen);
752 XWindowAttributes xgwa;
753 Colormap cmap;
754 int nr, ng, nb, cells;
755 int r, g, b;
756 int depth;
757 XColor colors[4097];
758 int i;
759
760 XGetWindowAttributes (dpy, window, &xgwa);
761 cmap = xgwa.colormap;
762 depth = visual_depth (screen, visual);
763
764 switch (depth)
765 {
766 case 8: nr = 3; ng = 3; nb = 2; cells = 256; break;
767 case 12: nr = 4; ng = 4; nb = 4; cells = 4096; break;
768 default: abort(); break;
769 }
770
771 memset(colors, 0, sizeof(colors));
772 for (r = 0; r < (1 << nr); r++)
773 for (g = 0; g < (1 << ng); g++)
774 for (b = 0; b < (1 << nb); b++)
775 {
776 i = (r | (g << nr) | (b << (nr + ng)));
777 colors[i].pixel = i;
778 colors[i].flags = DoRed|DoGreen|DoBlue;
779 if (depth == 8)
780 {
781 colors[i].red = ((r << 13) | (r << 10) | (r << 7) |
782 (r << 4) | (r << 1));
783 colors[i].green = ((g << 13) | (g << 10) | (g << 7) |
784 (g << 4) | (g << 1));
785 colors[i].blue = ((b << 14) | (b << 12) | (b << 10) |
786 (b << 8) | (b << 6) | (b << 4) |
787 (b << 2) | b);
788 }
789 else
790 {
791 colors[i].red = (r << 12) | (r << 8) | (r << 4) | r;
792 colors[i].green = (g << 12) | (g << 8) | (g << 4) | g;
793 colors[i].blue = (b << 12) | (b << 8) | (b << 4) | b;
794 }
795 }
796
797 {
798 int j;
799 int allocated = 0;
800 int interleave = cells / 8; /* skip around, rather than allocating in
801 order, so that we get better coverage if
802 we can't allocated all of them. */
803 for (j = 0; j < interleave; j++)
804 for (i = 0; i < cells; i += interleave)
805 if (XAllocColor (dpy, cmap, &colors[i + j]))
806 allocated++;
807
808 if (grab_verbose_p)
809 fprintf (stderr, "%s: allocated %d of %d colors for cubic map\n",
810 progname, allocated, cells);
811 }
812}
813
814/* Find the pixel index that is closest to the given color
815 (using linear distance in RGB space -- which is far from the best way.)
816
817 #### Duplicated in driver/xscreensaver-getimage.c
818 */
819static unsigned long
820find_closest_pixel (XColor *colors, int ncolors,
821 unsigned long r, unsigned long g, unsigned long b)
822{
823 unsigned long distance = ~0;
824 int i, found = 0;
825
826 if (ncolors == 0)
827 abort();
828 for (i = 0; i < ncolors; i++)
829 {
830 unsigned long d;
831 int rd, gd, bd;
832
833 rd = r - colors[i].red;
834 gd = g - colors[i].green;
835 bd = b - colors[i].blue;
836 if (rd < 0) rd = -rd;
837 if (gd < 0) gd = -gd;
838 if (bd < 0) bd = -bd;
839 d = (rd << 1) + (gd << 2) + bd;
840
841 if (d < distance)
842 {
843 distance = d;
844 found = i;
845 if (distance == 0)
846 break;
847 }
848 }
849
850 return found;
851}
852
853
854/* Given an XImage with 8-bit or 12-bit RGB data, convert it to be
855 displayable with the given X colormap. The farther from a perfect
856 color cube the contents of the colormap are, the lossier the
857 transformation will be. No dithering is done.
858
859 #### Duplicated in driver/xscreensaver-getimage.c
860 */
861void
862remap_image (Screen *screen, Window window, Colormap cmap, XImage *image)
863{
864 Display *dpy = DisplayOfScreen (screen);
865 unsigned long map[4097];
866 int x, y, i;
867 int cells;
868 XColor colors[4097];
869
870 if (image->depth == 8)
871 cells = 256;
872 else if (image->depth == 12)
873 cells = 4096;
874 else
875 abort();
876
877 memset(map, -1, sizeof(*map));
878 memset(colors, -1, sizeof(*colors));
879
880 for (i = 0; i < cells; i++)
881 colors[i].pixel = i;
882 XQueryColors (dpy, cmap, colors, cells);
883
884 if (grab_verbose_p)
885 fprintf(stderr, "%s: building table for %d bit image\n",
886 progname, image->depth);
887
888 for (i = 0; i < cells; i++)
889 {
890 unsigned short r, g, b;
891
892 if (cells == 256)
893 {
894 /* "RRR GGG BB" In an 8 bit map. Convert that to
895 "RRR RRR RR" "GGG GGG GG" "BB BB BB BB" to give
896 an even spread. */
897 r = (i & 0x07);
898 g = (i & 0x38) >> 3;
899 b = (i & 0xC0) >> 6;
900
901 r = ((r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1));
902 g = ((g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1));
903 b = ((b << 14) | (b << 12) | (b << 10) | (b << 8) |
904 (b << 6) | (b << 4) | (b << 2) | b);
905 }
906 else
907 {
908 /* "RRRR GGGG BBBB" In a 12 bit map. Convert that to
909 "RRRR RRRR" "GGGG GGGG" "BBBB BBBB" to give an even
910 spread. */
911 r = (i & 0x00F);
912 g = (i & 0x0F0) >> 4;
913 b = (i & 0xF00) >> 8;
914
915 r = (r << 12) | (r << 8) | (r << 4) | r;
916 g = (g << 12) | (g << 8) | (g << 4) | g;
917 b = (b << 12) | (b << 8) | (b << 4) | b;
918 }
919
920 map[i] = find_closest_pixel (colors, cells, r, g, b);
921 }
922
923 if (grab_verbose_p)
924 fprintf(stderr, "%s: remapping colors in %d bit image\n",
925 progname, image->depth);
926
927 for (y = 0; y < image->height; y++)
928 for (x = 0; x < image->width; x++)
929 {
930 unsigned long pixel = XGetPixel(image, x, y);
931 if (pixel >= cells) abort();
932 XPutPixel(image, x, y, map[pixel]);
933 }
934}
935
936
937#endif /* HAVE_READ_DISPLAY_EXTENSION */