1 /* xscreensaver, Copyright (c) 1992-2001 Jamie Zawinski <jwz@jwz.org>
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"
25 copy_colormap (Screen *screen, Visual *visual,
26 Colormap cmap, Colormap into_cmap)
29 Display *dpy = DisplayOfScreen (screen);
30 Window window = RootWindowOfScreen (screen);
31 int ncolors = CellsOfScreen (screen);
34 /* If this is a colormap on a mono visual, or one with insanely many
35 color cells, bug out. */
36 if (ncolors <= 2 || ncolors > 4096)
38 /* If this is a non-writable visual, bug out. */
39 if (!has_writable_cells (screen, visual))
43 into_cmap = XCreateColormap (dpy, window, visual, AllocAll);
45 cmap = DefaultColormapOfScreen (screen);
47 colors = (XColor *) calloc(sizeof(XColor), ncolors);
48 for (i = 0; i < ncolors; i++)
50 XQueryColors (dpy, cmap, colors, ncolors);
51 XStoreColors (dpy, into_cmap, colors, ncolors);
58 blacken_colormap (Screen *screen, Colormap cmap)
60 Display *dpy = DisplayOfScreen (screen);
61 int ncolors = CellsOfScreen (screen);
66 colors = (XColor *) calloc(sizeof(XColor), ncolors);
67 for (i = 0; i < ncolors; i++)
69 XStoreColors (dpy, cmap, colors, ncolors);
75 static void fade_screens_1 (Display *dpy, Colormap *cmaps,
76 Window *black_windows, int seconds, int ticks,
77 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 */
85 #ifdef HAVE_XF86VMODE_GAMMA
86 static int xf86_gamma_fade (Display *dpy,
87 Window *black_windows, int seconds, int ticks,
88 Bool out_p, Bool clear_windows);
89 #endif /* HAVE_XF86VMODE_GAMMA */
93 fade_screens (Display *dpy, Colormap *cmaps, Window *black_windows,
94 int seconds, int ticks,
95 Bool out_p, Bool clear_windows)
97 int oseconds = seconds;
98 Bool was_in_p = !out_p;
100 /* When we're asked to fade in, first fade out, then fade in.
101 That way all the transitions are smooth -- from what's on the
102 screen, to black, to the desktop.
106 clear_windows = True;
115 /* #### printf("\n\nfade_screens %d %d %d\n", seconds, ticks, out_p); */
117 #ifdef HAVE_SGI_VC_EXTENSION
118 /* First try to do it by fading the gamma in an SGI-specific way... */
119 if (0 == sgi_gamma_fade(dpy, black_windows, seconds, ticks, out_p,
123 #endif /* HAVE_SGI_VC_EXTENSION */
125 #ifdef HAVE_XF86VMODE_GAMMA
126 /* Then try to do it by fading the gamma in an XFree86-specific way... */
127 if (0 == xf86_gamma_fade(dpy, black_windows, seconds, ticks, out_p,
131 #endif /* HAVE_XF86VMODE_GAMMA */
133 /* Else, do it the old-fashioned way, which (somewhat) loses if
134 there are TrueColor windows visible. */
135 fade_screens_1 (dpy, cmaps, black_windows, seconds, ticks,
136 out_p, clear_windows);
138 /* If we were supposed to be fading in, do so now (we just faded out,
139 so now fade back in.)
145 seconds = oseconds * 2 / 3;
153 /* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video
154 hardware, which is capable of installing multiple (4) colormaps
155 simultaniously. We have to install multiple copies of the same set of
156 colors in order to fill up all the available slots in the hardware color
157 lookup table, so we install an extra N colormaps per screen to make sure
158 that all screens really go black.
160 I'm told that this trick also works with XInside's AcceleratedX when using
161 the Matrox Millennium card (which also allows multiple PseudoColor and
162 TrueColor visuals to co-exist and display properly at the same time.)
164 This trick works ok on the 24-bit Indy video hardware, but doesn't work at
165 all on the O2 24-bit hardware. I guess the higher-end hardware is too
166 "good" for this to work (dammit.) So... I figured out the "right" way to
167 do this on SGIs, which is to ramp the monitor's gamma down to 0. That's
168 what is implemented in sgi_gamma_fade(), so we use that if we can.
171 fade_screens_1 (Display *dpy, Colormap *cmaps, Window *black_windows,
172 int seconds, int ticks,
173 Bool out_p, Bool clear_windows)
176 int steps = seconds * ticks;
177 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
179 int cmaps_per_screen = 5;
180 int nscreens = ScreenCount(dpy);
181 int ncmaps = nscreens * cmaps_per_screen;
182 Colormap *fade_cmaps = 0;
183 Bool installed = False;
185 XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors;
186 struct timeval then, now;
187 #ifdef GETTIMEOFDAY_TWO_ARGS
192 for (i = 0; i < nscreens; i++)
193 total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i));
195 orig_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
196 current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
198 /* Get the contents of the colormap we are fading from or to. */
199 screen_colors = orig_colors;
200 for (i = 0; i < nscreens; i++)
202 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, i));
203 Colormap cmap = (cmaps ? cmaps[i] : 0);
204 if (!cmap) cmap = DefaultColormap(dpy, i);
206 for (j = 0; j < ncolors; j++)
207 screen_colors[j].pixel = j;
208 XQueryColors (dpy, cmap, screen_colors, ncolors);
210 screen_colors += ncolors;
213 memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor));
216 /* Make the writable colormaps (we keep these around and reuse them.) */
219 fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps);
220 for (i = 0; i < nscreens; i++)
222 Visual *v = DefaultVisual(dpy, i);
223 Screen *s = ScreenOfDisplay(dpy, i);
224 if (has_writable_cells (s, v))
225 for (j = 0; j < cmaps_per_screen; j++)
226 fade_cmaps[(i * cmaps_per_screen) + j] =
227 XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll);
231 #ifdef GETTIMEOFDAY_TWO_ARGS
232 gettimeofday(&then, &tzp);
237 /* Iterate by steps of the animation... */
238 for (i = (out_p ? steps : 0);
239 (out_p ? i > 0 : i < steps);
243 /* For each screen, compute the current value of each color...
245 orig_screen_colors = orig_colors;
246 screen_colors = current_colors;
247 for (j = 0; j < nscreens; j++)
249 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
250 for (k = 0; k < ncolors; k++)
252 /* This doesn't take into account the relative luminance of the
253 RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but
254 the difference is imperceptible for this application... */
255 screen_colors[k].red = orig_screen_colors[k].red * i / steps;
256 screen_colors[k].green = orig_screen_colors[k].green * i / steps;
257 screen_colors[k].blue = orig_screen_colors[k].blue * i / steps;
259 screen_colors += ncolors;
260 orig_screen_colors += ncolors;
263 /* Put the colors into the maps...
265 screen_colors = current_colors;
266 for (j = 0; j < nscreens; j++)
268 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
269 for (k = 0; k < cmaps_per_screen; k++)
271 Colormap c = fade_cmaps[j * cmaps_per_screen + k];
273 XStoreColors (dpy, c, screen_colors, ncolors);
275 screen_colors += ncolors;
278 /* Put the maps on the screens, and then take the windows off the screen.
279 (only need to do this the first time through the loop.)
283 for (j = 0; j < ncmaps; j++)
285 XInstallColormap (dpy, fade_cmaps[j]);
288 if (black_windows && !out_p)
289 for (j = 0; j < nscreens; j++)
290 if (black_windows[j])
292 XUnmapWindow (dpy, black_windows[j]);
293 XClearWindow (dpy, black_windows[j]);
299 /* If there is user activity, bug out. (Bug out on keypresses or
300 mouse presses, but not motion, and not release events. Bugging
301 out on motion made the unfade hack be totally useless, I think.)
303 We put the event back so that the calling code can notice it too.
304 It would be better to not remove it at all, but that's harder
305 because Xlib has such a non-design for this kind of crap, and
306 in this application it doesn't matter if the events end up out
307 of order, so in the grand unix tradition we say "fuck it" and
308 do something that mostly works for the time being.
310 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), &dummy_event))
312 XPutBackEvent (dpy, &dummy_event);
316 #ifdef GETTIMEOFDAY_TWO_ARGS
317 gettimeofday(&now, &tzp);
322 /* If we haven't already used up our alotted time, sleep to avoid
323 changing the colormap too fast. */
325 long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
326 now.tv_usec - then.tv_usec);
327 then.tv_sec = now.tv_sec;
328 then.tv_usec = now.tv_usec;
329 if (usecs_per_step > diff)
330 usleep (usecs_per_step - diff);
336 if (orig_colors) free (orig_colors);
337 if (current_colors) free (current_colors);
339 /* If we've been given windows to raise after blackout, raise them before
340 releasing the colormaps.
342 if (out_p && black_windows)
344 for (i = 0; i < nscreens; i++)
347 XClearWindow (dpy, black_windows[i]);
348 XMapRaised (dpy, black_windows[i]);
353 /* Now put the target maps back.
354 If we're fading out, use the given cmap (or the default cmap, if none.)
355 If we're fading in, always use the default cmap.
357 for (i = 0; i < nscreens; i++)
359 Colormap cmap = (cmaps ? cmaps[i] : 0);
361 cmap = DefaultColormap(dpy, i);
362 XInstallColormap (dpy, cmap);
365 /* The fade (in or out) is complete, so we don't need the black maps on
368 for (i = 0; i < ncmaps; i++)
371 XUninstallColormap(dpy, fade_cmaps[i]);
372 XFreeColormap(dpy, fade_cmaps[i]);
381 /* SGI Gamma fading */
383 #ifdef HAVE_SGI_VC_EXTENSION
385 # include <X11/extensions/XSGIvc.h>
387 struct screen_sgi_gamma_info {
388 int gamma_map; /* ??? always using 0 */
389 int nred, ngreen, nblue;
390 unsigned short *red1, *green1, *blue1;
391 unsigned short *red2, *green2, *blue2;
398 static void sgi_whack_gamma(Display *dpy, int screen,
399 struct screen_sgi_gamma_info *info, float ratio);
402 sgi_gamma_fade (Display *dpy,
403 Window *black_windows, int seconds, int ticks,
404 Bool out_p, Bool clear_windows)
406 int steps = seconds * ticks;
407 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
409 int nscreens = ScreenCount(dpy);
410 struct timeval then, now;
411 #ifdef GETTIMEOFDAY_TWO_ARGS
416 struct screen_sgi_gamma_info *info = (struct screen_sgi_gamma_info *)
417 calloc(nscreens, sizeof(*info));
419 /* Get the current gamma maps for all screens.
420 Bug out and return -1 if we can't get them for some screen.
422 for (screen = 0; screen < nscreens; screen++)
424 if (!XSGIvcQueryGammaMap(dpy, screen, info[screen].gamma_map,
425 &info[screen].gamma_size,
426 &info[screen].gamma_precision,
427 &info[screen].alpha_p))
430 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
431 XSGIVC_COMPONENT_RED,
432 &info[screen].nred, &info[screen].red1))
434 if (! XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
435 XSGIVC_COMPONENT_GREEN,
436 &info[screen].ngreen, &info[screen].green1))
438 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
439 XSGIVC_COMPONENT_BLUE,
440 &info[screen].nblue, &info[screen].blue1))
443 if (info[screen].gamma_precision == 8) /* Scale it up to 16 bits. */
446 for(j = 0; j < info[screen].nred; j++)
447 info[screen].red1[j] =
448 ((info[screen].red1[j] << 8) | info[screen].red1[j]);
449 for(j = 0; j < info[screen].ngreen; j++)
450 info[screen].green1[j] =
451 ((info[screen].green1[j] << 8) | info[screen].green1[j]);
452 for(j = 0; j < info[screen].nblue; j++)
453 info[screen].blue1[j] =
454 ((info[screen].blue1[j] << 8) | info[screen].blue1[j]);
457 info[screen].red2 = (unsigned short *)
458 malloc(sizeof(*info[screen].red2) * (info[screen].nred+1));
459 info[screen].green2 = (unsigned short *)
460 malloc(sizeof(*info[screen].green2) * (info[screen].ngreen+1));
461 info[screen].blue2 = (unsigned short *)
462 malloc(sizeof(*info[screen].blue2) * (info[screen].nblue+1));
465 #ifdef GETTIMEOFDAY_TWO_ARGS
466 gettimeofday(&then, &tzp);
471 /* If we're fading in (from black), then first crank the gamma all the
472 way down to 0, then take the windows off the screen.
475 for (screen = 0; screen < nscreens; screen++)
477 sgi_whack_gamma(dpy, screen, &info[screen], 0.0);
478 if (black_windows && black_windows[screen])
480 XUnmapWindow (dpy, black_windows[screen]);
481 XClearWindow (dpy, black_windows[screen]);
487 /* Iterate by steps of the animation... */
488 for (i = (out_p ? steps : 0);
489 (out_p ? i > 0 : i < steps);
492 for (screen = 0; screen < nscreens; screen++)
494 sgi_whack_gamma(dpy, screen, &info[screen],
495 (((float)i) / ((float)steps)));
497 /* If there is user activity, bug out. (Bug out on keypresses or
498 mouse presses, but not motion, and not release events. Bugging
499 out on motion made the unfade hack be totally useless, I think.)
501 We put the event back so that the calling code can notice it too.
502 It would be better to not remove it at all, but that's harder
503 because Xlib has such a non-design for this kind of crap, and
504 in this application it doesn't matter if the events end up out
505 of order, so in the grand unix tradition we say "fuck it" and
506 do something that mostly works for the time being.
508 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
511 XPutBackEvent (dpy, &dummy_event);
515 #ifdef GETTIMEOFDAY_TWO_ARGS
516 gettimeofday(&now, &tzp);
521 /* If we haven't already used up our alotted time, sleep to avoid
522 changing the colormap too fast. */
524 long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
525 now.tv_usec - then.tv_usec);
526 then.tv_sec = now.tv_sec;
527 then.tv_usec = now.tv_usec;
528 if (usecs_per_step > diff)
529 usleep (usecs_per_step - diff);
537 if (out_p && black_windows)
539 for (screen = 0; screen < nscreens; screen++)
542 XClearWindow (dpy, black_windows[screen]);
543 XMapRaised (dpy, black_windows[screen]);
548 /* I can't explain this; without this delay, we get a flicker.
549 I suppose there's some lossage with stale bits being in the
550 hardware frame buffer or something, and this delay gives it
551 time to flush out. This sucks! */
552 usleep(100000); /* 1/10th second */
554 for (screen = 0; screen < nscreens; screen++)
555 sgi_whack_gamma(dpy, screen, &info[screen], 1.0);
561 for (screen = 0; screen < nscreens; screen++)
563 if (info[screen].red1) free (info[screen].red1);
564 if (info[screen].green1) free (info[screen].green1);
565 if (info[screen].blue1) free (info[screen].blue1);
566 if (info[screen].red2) free (info[screen].red2);
567 if (info[screen].green2) free (info[screen].green2);
568 if (info[screen].blue2) free (info[screen].blue2);
576 sgi_whack_gamma(Display *dpy, int screen, struct screen_sgi_gamma_info *info,
581 if (ratio < 0) ratio = 0;
582 if (ratio > 1) ratio = 1;
583 for (k = 0; k < info->gamma_size; k++)
585 info->red2[k] = info->red1[k] * ratio;
586 info->green2[k] = info->green1[k] * ratio;
587 info->blue2[k] = info->blue1[k] * ratio;
590 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred,
591 XSGIVC_MComponentRed, info->red2);
592 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen,
593 XSGIVC_MComponentGreen, info->green2);
594 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nblue,
595 XSGIVC_MComponentBlue, info->blue2);
599 #endif /* HAVE_SGI_VC_EXTENSION */
603 /* XFree86 4.x+ Gamma fading */
605 #ifdef HAVE_XF86VMODE_GAMMA
607 #include <X11/extensions/xf86vmode.h>
609 static Bool xf86_whack_gamma(Display *dpy, int screen,
610 XF86VidModeGamma *info, float ratio);
611 static Bool xf86_check_gamma_extension (Display *dpy);
614 xf86_gamma_fade (Display *dpy,
615 Window *black_windows, int seconds, int ticks,
616 Bool out_p, Bool clear_windows)
618 int steps = seconds * ticks;
619 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
621 int nscreens = ScreenCount(dpy);
622 struct timeval then, now;
623 #ifdef GETTIMEOFDAY_TWO_ARGS
628 XF86VidModeGamma *info = 0;
630 static int ext_ok = -1;
632 /* Only probe the extension once: the answer isn't going to change. */
634 ext_ok = (xf86_check_gamma_extension (dpy) ? 1 : 0);
636 /* If this server doesn't have the gamma extension, bug out. */
640 info = (XF86VidModeGamma *) calloc(nscreens, sizeof(*info));
642 /* Get the current gamma maps for all screens.
643 Bug out and return -1 if we can't get them for some screen.
645 for (screen = 0; screen < nscreens; screen++)
647 if (!XF86VidModeGetGamma(dpy, screen, &info[screen]))
651 #ifdef GETTIMEOFDAY_TWO_ARGS
652 gettimeofday(&then, &tzp);
657 /* If we're fading in (from black), then first crank the gamma all the
658 way down to 0, then take the windows off the screen.
661 for (screen = 0; screen < nscreens; screen++)
663 xf86_whack_gamma(dpy, screen, &info[screen], 0.0);
664 if (black_windows && black_windows[screen])
666 XUnmapWindow (dpy, black_windows[screen]);
667 XClearWindow (dpy, black_windows[screen]);
673 /* Iterate by steps of the animation... */
674 for (i = (out_p ? steps : 0);
675 (out_p ? i > 0 : i < steps);
678 for (screen = 0; screen < nscreens; screen++)
680 xf86_whack_gamma(dpy, screen, &info[screen],
681 (((float)i) / ((float)steps)));
683 /* If there is user activity, bug out. (Bug out on keypresses or
684 mouse presses, but not motion, and not release events. Bugging
685 out on motion made the unfade hack be totally useless, I think.)
687 We put the event back so that the calling code can notice it too.
688 It would be better to not remove it at all, but that's harder
689 because Xlib has such a non-design for this kind of crap, and
690 in this application it doesn't matter if the events end up out
691 of order, so in the grand unix tradition we say "fuck it" and
692 do something that mostly works for the time being.
694 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
697 XPutBackEvent (dpy, &dummy_event);
701 #ifdef GETTIMEOFDAY_TWO_ARGS
702 gettimeofday(&now, &tzp);
707 /* If we haven't already used up our alotted time, sleep to avoid
708 changing the colormap too fast. */
710 long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
711 now.tv_usec - then.tv_usec);
712 then.tv_sec = now.tv_sec;
713 then.tv_usec = now.tv_usec;
714 if (usecs_per_step > diff)
715 usleep (usecs_per_step - diff);
723 if (out_p && black_windows)
725 for (screen = 0; screen < nscreens; screen++)
728 XClearWindow (dpy, black_windows[screen]);
729 XMapRaised (dpy, black_windows[screen]);
734 /* I can't explain this; without this delay, we get a flicker.
735 I suppose there's some lossage with stale bits being in the
736 hardware frame buffer or something, and this delay gives it
737 time to flush out. This sucks! */
738 usleep(100000); /* 1/10th second */
740 for (screen = 0; screen < nscreens; screen++)
741 xf86_whack_gamma(dpy, screen, &info[screen], 1.0);
747 if (info) free(info);
753 /* VidModeExtension version 2.0 or better is needed to do gamma. */
754 # define XF86_VIDMODE_NAME "XFree86-VidModeExtension"
755 # define XF86_VIDMODE_MIN_MAJOR 2
756 # define XF86_VIDMODE_MIN_MINOR 0
759 xf86_check_gamma_extension (Display *dpy)
761 int op, event, error, major, minor;
763 if (!XQueryExtension (dpy, XF86_VIDMODE_NAME, &op, &event, &error))
764 return False; /* display doesn't have the extension. */
766 if (!XF86VidModeQueryVersion (dpy, &major, &minor))
767 return False; /* unable to get version number? */
769 if (major < XF86_VIDMODE_MIN_MAJOR ||
770 (major == XF86_VIDMODE_MIN_MAJOR &&
771 minor < XF86_VIDMODE_MIN_MINOR))
772 return False; /* extension is too old. */
779 /* XFree doesn't let you set gamma to a value smaller than this.
780 Apparently they didn't anticipate the trick I'm doing here...
782 #define XF86_MIN_GAMMA 0.1
786 xf86_whack_gamma(Display *dpy, int screen, XF86VidModeGamma *info,
792 if (ratio < 0) ratio = 0;
793 if (ratio > 1) ratio = 1;
795 g2.red = info->red * ratio;
796 g2.green = info->green * ratio;
797 g2.blue = info->blue * ratio;
799 # ifdef XF86_MIN_GAMMA
800 if (g2.red < XF86_MIN_GAMMA) g2.red = XF86_MIN_GAMMA;
801 if (g2.green < XF86_MIN_GAMMA) g2.green = XF86_MIN_GAMMA;
802 if (g2.blue < XF86_MIN_GAMMA) g2.blue = XF86_MIN_GAMMA;
805 /* #### printf(" G %4.2f %4.2f\n", ratio, g2.red); */
807 status = XF86VidModeSetGamma (dpy, screen, &g2);
812 #endif /* HAVE_XF86VMODE_GAMMA */