1 /* xscreensaver, Copyright (c) 1992-1997 Jamie Zawinski <jwz@netscape.com>
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
14 #include <sys/time.h> /* for gettimeofday() */
17 # include "vms-gtod.h"
26 copy_colormap (Screen *screen, Visual *visual,
27 Colormap cmap, Colormap into_cmap)
30 Display *dpy = DisplayOfScreen (screen);
31 Window window = RootWindowOfScreen (screen);
32 int ncolors = CellsOfScreen (screen);
35 /* If this is a colormap on a mono visual, or one with insanely many
36 color cells, bug out. */
37 if (ncolors <= 2 || ncolors > 4096)
39 /* If this is a non-writable visual, bug out. */
40 if (!has_writable_cells (screen, visual))
44 into_cmap = XCreateColormap (dpy, window, visual, AllocAll);
46 cmap = DefaultColormapOfScreen (screen);
48 colors = (XColor *) calloc(sizeof(XColor), ncolors);
49 for (i = 0; i < ncolors; i++)
51 XQueryColors (dpy, cmap, colors, ncolors);
52 XStoreColors (dpy, into_cmap, colors, ncolors);
59 blacken_colormap (Screen *screen, Colormap cmap)
61 Display *dpy = DisplayOfScreen (screen);
62 int ncolors = CellsOfScreen (screen);
67 colors = (XColor *) calloc(sizeof(XColor), ncolors);
68 for (i = 0; i < ncolors; i++)
70 XStoreColors (dpy, cmap, colors, ncolors);
76 static void fade_screens_1 (Display *dpy, Colormap *cmaps,
77 Window *black_windows, int seconds, int ticks,
78 Bool out_p, Bool clear_windows);
79 #ifdef HAVE_SGI_VC_EXTENSION
80 static int sgi_gamma_fade (Display *dpy,
81 Window *black_windows, int seconds, int ticks,
82 Bool out_p, Bool clear_windows);
83 #endif /* HAVE_SGI_VC_EXTENSION */
88 fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
89 int seconds, int ticks,
90 Bool out_p, Bool clear_windows)
92 #ifdef HAVE_SGI_VC_EXTENSION
93 /* First try to do it by fading the gamma in an SGI-specific way... */
94 if (0 != sgi_gamma_fade(dpy, black_windows, seconds, ticks, out_p,
96 #endif /* HAVE_SGI_VC_EXTENSION */
97 /* Else, do it the old-fashioned way, which (somewhat) loses if
98 there are TrueColor windows visible. */
99 fade_screens_1 (dpy, cmaps, black_windows, seconds, ticks,
100 out_p, clear_windows);
104 /* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video
105 hardware, which is capable of installing multiple (4) colormaps
106 simultaniously. We have to install multiple copies of the same set of
107 colors in order to fill up all the available slots in the hardware color
108 lookup table, so we install an extra N colormaps per screen to make sure
109 that all screens really go black.
111 I'm told that this trick also works with XInside's AcceleratedX when using
112 the Matrox Millenium card (which also allows multiple PseudoColor and
113 TrueColor visuals to co-exist and display properly at the same time.)
115 This trick works ok on the 24-bit Indy video hardware, but doesn't work at
116 all on the O2 24-bit hardware. I guess the higher-end hardware is too
117 "good" for this to work (dammit.) So... I figured out the "right" way to
118 do this on SGIs, which is to ramp the monitor's gamma down to 0. That's
119 what is implemented in sgi_gamma_fade(), so we use that if we can.
122 fade_screens_1 (Display *dpy, Colormap *cmaps, Window *black_windows,
123 int seconds, int ticks,
124 Bool out_p, Bool clear_windows)
127 int steps = seconds * ticks;
128 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
130 int cmaps_per_screen = 5;
131 int nscreens = ScreenCount(dpy);
132 int ncmaps = nscreens * cmaps_per_screen;
133 Colormap *fade_cmaps = 0;
134 Bool installed = False;
136 XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors;
137 struct timeval then, now;
138 #ifdef GETTIMEOFDAY_TWO_ARGS
143 for (i = 0; i < nscreens; i++)
144 total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i));
146 orig_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
147 current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
149 /* Get the contents of the colormap we are fading from or to. */
150 screen_colors = orig_colors;
151 for (i = 0; i < nscreens; i++)
153 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, i));
154 Colormap cmap = (cmaps ? cmaps[i] : 0);
155 if (!cmap) cmap = DefaultColormap(dpy, i);
157 for (j = 0; j < ncolors; j++)
158 screen_colors[j].pixel = j;
159 XQueryColors (dpy, cmap, screen_colors, ncolors);
161 screen_colors += ncolors;
164 memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor));
167 /* Make the writable colormaps (we keep these around and reuse them.) */
170 fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps);
171 for (i = 0; i < nscreens; i++)
173 Visual *v = DefaultVisual(dpy, i);
174 Screen *s = ScreenOfDisplay(dpy, i);
175 if (has_writable_cells (s, v))
176 for (j = 0; j < cmaps_per_screen; j++)
177 fade_cmaps[(i * cmaps_per_screen) + j] =
178 XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll);
182 #ifdef GETTIMEOFDAY_TWO_ARGS
183 gettimeofday(&then, &tzp);
188 /* Iterate by steps of the animation... */
189 for (i = (out_p ? steps : 0);
190 (out_p ? i > 0 : i < steps);
194 /* For each screen, compute the current value of each color...
196 orig_screen_colors = orig_colors;
197 screen_colors = current_colors;
198 for (j = 0; j < nscreens; j++)
200 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
201 for (k = 0; k < ncolors; k++)
203 /* This doesn't take into account the relative luminance of the
204 RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but
205 the difference is imperceptible for this application... */
206 screen_colors[k].red = orig_screen_colors[k].red * i / steps;
207 screen_colors[k].green = orig_screen_colors[k].green * i / steps;
208 screen_colors[k].blue = orig_screen_colors[k].blue * i / steps;
210 screen_colors += ncolors;
211 orig_screen_colors += ncolors;
214 /* Put the colors into the maps...
216 screen_colors = current_colors;
217 for (j = 0; j < nscreens; j++)
219 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
220 for (k = 0; k < cmaps_per_screen; k++)
222 Colormap c = fade_cmaps[j * cmaps_per_screen + k];
224 XStoreColors (dpy, c, screen_colors, ncolors);
226 screen_colors += ncolors;
229 /* Put the maps on the screens, and then take the windows off the screen.
230 (only need to do this the first time through the loop.)
234 for (j = 0; j < ncmaps; j++)
236 XInstallColormap (dpy, fade_cmaps[j]);
239 if (black_windows && !out_p)
240 for (j = 0; j < nscreens; j++)
241 if (black_windows[j])
243 XUnmapWindow (dpy, black_windows[j]);
244 XClearWindow (dpy, black_windows[j]);
250 /* If there is user activity, bug out. (Bug out on keypresses or
251 mouse presses, but not motion, and not release events. Bugging
252 out on motion made the unfade hack be totally useless, I think.)
254 We put the event back so that the calling code can notice it too.
255 It would be better to not remove it at all, but that's harder
256 because Xlib has such a non-design for this kind of crap, and
257 in this application it doesn't matter if the events end up out
258 of order, so in the grand unix tradition we say "fuck it" and
259 do something that mostly works for the time being.
261 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), &dummy_event))
263 XPutBackEvent (dpy, &dummy_event);
267 #ifdef GETTIMEOFDAY_TWO_ARGS
268 gettimeofday(&now, &tzp);
273 /* If we haven't already used up our alotted time, sleep to avoid
274 changing the colormap too fast. */
276 long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
277 now.tv_usec - then.tv_usec);
278 then.tv_sec = now.tv_sec;
279 then.tv_usec = now.tv_usec;
280 if (usecs_per_step > diff)
281 usleep (usecs_per_step - diff);
287 if (orig_colors) free (orig_colors);
288 if (current_colors) free (current_colors);
290 /* If we've been given windows to raise after blackout, raise them before
291 releasing the colormaps.
293 if (out_p && black_windows)
294 for (i = 0; i < nscreens; i++)
297 XClearWindow (dpy, black_windows[i]);
298 XMapRaised (dpy, black_windows[i]);
302 /* Now put the target maps back.
303 If we're fading out, use the given cmap (or the default cmap, if none.)
304 If we're fading in, always use the default cmap.
306 for (i = 0; i < nscreens; i++)
308 Colormap cmap = (cmaps ? cmaps[i] : 0);
310 cmap = DefaultColormap(dpy, i);
311 XInstallColormap (dpy, cmap);
314 /* The fade (in or out) is complete, so we don't need the black maps on
317 for (i = 0; i < ncmaps; i++)
320 XUninstallColormap(dpy, fade_cmaps[i]);
321 XFreeColormap(dpy, fade_cmaps[i]);
329 #ifdef HAVE_SGI_VC_EXTENSION
331 # include <X11/extensions/XSGIvc.h>
333 struct screen_gamma_info {
334 int gamma_map; /* ??? always using 0 */
335 int nred, ngreen, nblue;
336 unsigned short *red1, *green1, *blue1;
337 unsigned short *red2, *green2, *blue2;
344 static void whack_gamma(Display *dpy, int screen,
345 struct screen_gamma_info *info, float ratio);
348 sgi_gamma_fade (Display *dpy,
349 Window *black_windows, int seconds, int ticks,
350 Bool out_p, Bool clear_windows)
352 int steps = seconds * ticks;
353 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
355 int nscreens = ScreenCount(dpy);
356 struct timeval then, now;
357 #ifdef GETTIMEOFDAY_TWO_ARGS
362 struct screen_gamma_info *info = (struct screen_gamma_info *)
363 calloc(nscreens, sizeof(*info));
365 /* Get the current gamma maps for all screens.
366 Bug out and return -1 if we can't get them for some screen.
368 for (screen = 0; screen < nscreens; screen++)
370 if (!XSGIvcQueryGammaMap(dpy, screen, info[screen].gamma_map,
371 &info[screen].gamma_size,
372 &info[screen].gamma_precision,
373 &info[screen].alpha_p))
376 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
377 XSGIVC_COMPONENT_RED,
378 &info[screen].nred, &info[screen].red1))
380 if (! XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
381 XSGIVC_COMPONENT_GREEN,
382 &info[screen].ngreen, &info[screen].green1))
384 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
385 XSGIVC_COMPONENT_BLUE,
386 &info[screen].nblue, &info[screen].blue1))
389 if (info[screen].gamma_precision == 8) /* Scale it up to 16 bits. */
392 for(j = 0; j < info[screen].nred; j++)
393 info[screen].red1[j] =
394 ((info[screen].red1[j] << 8) | info[screen].red1[j]);
395 for(j = 0; j < info[screen].ngreen; j++)
396 info[screen].green1[j] =
397 ((info[screen].green1[j] << 8) | info[screen].green1[j]);
398 for(j = 0; j < info[screen].nblue; j++)
399 info[screen].blue1[j] =
400 ((info[screen].blue1[j] << 8) | info[screen].blue1[j]);
403 info[screen].red2 = (unsigned short *)
404 malloc(sizeof(*info[screen].red2) * (info[screen].nred+1));
405 info[screen].green2 = (unsigned short *)
406 malloc(sizeof(*info[screen].green2) * (info[screen].ngreen+1));
407 info[screen].blue2 = (unsigned short *)
408 malloc(sizeof(*info[screen].blue2) * (info[screen].nblue+1));
411 #ifdef GETTIMEOFDAY_TWO_ARGS
412 gettimeofday(&then, &tzp);
417 /* If we're fading in (from black), then first crank the gamma all the
418 way down to 0, then take the windows off the screen.
421 for (screen = 0; screen < nscreens; screen++)
423 whack_gamma(dpy, screen, &info[screen], 0.0);
424 if (black_windows && black_windows[screen])
426 XUnmapWindow (dpy, black_windows[screen]);
427 XClearWindow (dpy, black_windows[screen]);
432 /* Iterate by steps of the animation... */
433 for (i = (out_p ? steps : 0);
434 (out_p ? i > 0 : i < steps);
437 for (screen = 0; screen < nscreens; screen++)
439 whack_gamma(dpy, screen, &info[screen],
440 (((float)i) / ((float)steps)));
442 /* If there is user activity, bug out. (Bug out on keypresses or
443 mouse presses, but not motion, and not release events. Bugging
444 out on motion made the unfade hack be totally useless, I think.)
446 We put the event back so that the calling code can notice it too.
447 It would be better to not remove it at all, but that's harder
448 because Xlib has such a non-design for this kind of crap, and
449 in this application it doesn't matter if the events end up out
450 of order, so in the grand unix tradition we say "fuck it" and
451 do something that mostly works for the time being.
453 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
456 XPutBackEvent (dpy, &dummy_event);
460 #ifdef GETTIMEOFDAY_TWO_ARGS
461 gettimeofday(&now, &tzp);
466 /* If we haven't already used up our alotted time, sleep to avoid
467 changing the colormap too fast. */
469 long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
470 now.tv_usec - then.tv_usec);
471 then.tv_sec = now.tv_sec;
472 then.tv_usec = now.tv_usec;
473 if (usecs_per_step > diff)
474 usleep (usecs_per_step - diff);
482 if (out_p && black_windows)
484 for (screen = 0; screen < nscreens; screen++)
487 XClearWindow (dpy, black_windows[i]);
488 XMapRaised (dpy, black_windows[i]);
493 for (screen = 0; screen < nscreens; screen++)
494 whack_gamma(dpy, screen, &info[screen], 1.0);
500 for (screen = 0; screen < nscreens; screen++)
502 if (info[screen].red1) free (info[screen].red1);
503 if (info[screen].green1) free (info[screen].green1);
504 if (info[screen].blue1) free (info[screen].blue1);
505 if (info[screen].red2) free (info[screen].red2);
506 if (info[screen].green2) free (info[screen].green2);
507 if (info[screen].blue2) free (info[screen].blue2);
514 whack_gamma(Display *dpy, int screen, struct screen_gamma_info *info,
518 if (ratio < 0) ratio = 0;
519 if (ratio > 1) ratio = 1;
520 for (k = 0; k < info->gamma_size; k++)
522 info->red2[k] = info->red1[k] * ratio;
523 info->green2[k] = info->green1[k] * ratio;
524 info->blue2[k] = info->blue1[k] * ratio;
526 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred,
527 XSGIVC_MComponentRed, info->red2);
528 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen,
529 XSGIVC_MComponentGreen, info->green2);
530 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nblue,
531 XSGIVC_MComponentBlue, info->blue2);
535 #endif /* HAVE_SGI_VC_EXTENSION */
541 #include "screenhack.h"
543 char *progclass = "foo";
544 char *defaults [] = {
548 XrmOptionDescRec options [] = {0};
549 int options_size = 0;
564 fprintf(stderr,"out..."); fflush(stderr);
565 fade_screens (dpy, 0, seconds, ticks, True);
566 fprintf(stderr, "done.\n"); fflush(stderr);
568 if (delay) sleep (delay);
570 fprintf(stderr,"in..."); fflush(stderr);
571 fade_screens (dpy, 0, seconds, ticks, False);
572 fprintf(stderr, "done.\n"); fflush(stderr);
574 if (delay) sleep (delay);