1 /* xscreensaver, Copyright (c) 1992-1998 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"
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 int oseconds = seconds;
93 Bool was_in_p = !out_p;
95 /* When we're asked to fade in, first fade out, then fade in.
96 That way all the transitions are smooth -- from what's on the
97 screen, to black, to the desktop.
101 clear_windows = True;
110 #ifdef HAVE_SGI_VC_EXTENSION
111 /* First try to do it by fading the gamma in an SGI-specific way... */
112 if (0 != sgi_gamma_fade(dpy, black_windows, seconds, ticks, out_p,
114 #endif /* HAVE_SGI_VC_EXTENSION */
115 /* Else, do it the old-fashioned way, which (somewhat) loses if
116 there are TrueColor windows visible. */
117 fade_screens_1 (dpy, cmaps, black_windows, seconds, ticks,
118 out_p, clear_windows);
120 /* If we were supposed to be fading in, do so now (we just faded out,
121 so now fade back in.)
127 seconds = oseconds * 2 / 3;
135 /* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video
136 hardware, which is capable of installing multiple (4) colormaps
137 simultaniously. We have to install multiple copies of the same set of
138 colors in order to fill up all the available slots in the hardware color
139 lookup table, so we install an extra N colormaps per screen to make sure
140 that all screens really go black.
142 I'm told that this trick also works with XInside's AcceleratedX when using
143 the Matrox Millenium card (which also allows multiple PseudoColor and
144 TrueColor visuals to co-exist and display properly at the same time.)
146 This trick works ok on the 24-bit Indy video hardware, but doesn't work at
147 all on the O2 24-bit hardware. I guess the higher-end hardware is too
148 "good" for this to work (dammit.) So... I figured out the "right" way to
149 do this on SGIs, which is to ramp the monitor's gamma down to 0. That's
150 what is implemented in sgi_gamma_fade(), so we use that if we can.
153 fade_screens_1 (Display *dpy, Colormap *cmaps, Window *black_windows,
154 int seconds, int ticks,
155 Bool out_p, Bool clear_windows)
158 int steps = seconds * ticks;
159 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
161 int cmaps_per_screen = 5;
162 int nscreens = ScreenCount(dpy);
163 int ncmaps = nscreens * cmaps_per_screen;
164 Colormap *fade_cmaps = 0;
165 Bool installed = False;
167 XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors;
168 struct timeval then, now;
169 #ifdef GETTIMEOFDAY_TWO_ARGS
174 for (i = 0; i < nscreens; i++)
175 total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i));
177 orig_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
178 current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
180 /* Get the contents of the colormap we are fading from or to. */
181 screen_colors = orig_colors;
182 for (i = 0; i < nscreens; i++)
184 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, i));
185 Colormap cmap = (cmaps ? cmaps[i] : 0);
186 if (!cmap) cmap = DefaultColormap(dpy, i);
188 for (j = 0; j < ncolors; j++)
189 screen_colors[j].pixel = j;
190 XQueryColors (dpy, cmap, screen_colors, ncolors);
192 screen_colors += ncolors;
195 memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor));
198 /* Make the writable colormaps (we keep these around and reuse them.) */
201 fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps);
202 for (i = 0; i < nscreens; i++)
204 Visual *v = DefaultVisual(dpy, i);
205 Screen *s = ScreenOfDisplay(dpy, i);
206 if (has_writable_cells (s, v))
207 for (j = 0; j < cmaps_per_screen; j++)
208 fade_cmaps[(i * cmaps_per_screen) + j] =
209 XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll);
213 #ifdef GETTIMEOFDAY_TWO_ARGS
214 gettimeofday(&then, &tzp);
219 /* Iterate by steps of the animation... */
220 for (i = (out_p ? steps : 0);
221 (out_p ? i > 0 : i < steps);
225 /* For each screen, compute the current value of each color...
227 orig_screen_colors = orig_colors;
228 screen_colors = current_colors;
229 for (j = 0; j < nscreens; j++)
231 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
232 for (k = 0; k < ncolors; k++)
234 /* This doesn't take into account the relative luminance of the
235 RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but
236 the difference is imperceptible for this application... */
237 screen_colors[k].red = orig_screen_colors[k].red * i / steps;
238 screen_colors[k].green = orig_screen_colors[k].green * i / steps;
239 screen_colors[k].blue = orig_screen_colors[k].blue * i / steps;
241 screen_colors += ncolors;
242 orig_screen_colors += ncolors;
245 /* Put the colors into the maps...
247 screen_colors = current_colors;
248 for (j = 0; j < nscreens; j++)
250 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
251 for (k = 0; k < cmaps_per_screen; k++)
253 Colormap c = fade_cmaps[j * cmaps_per_screen + k];
255 XStoreColors (dpy, c, screen_colors, ncolors);
257 screen_colors += ncolors;
260 /* Put the maps on the screens, and then take the windows off the screen.
261 (only need to do this the first time through the loop.)
265 for (j = 0; j < ncmaps; j++)
267 XInstallColormap (dpy, fade_cmaps[j]);
270 if (black_windows && !out_p)
271 for (j = 0; j < nscreens; j++)
272 if (black_windows[j])
274 XUnmapWindow (dpy, black_windows[j]);
275 XClearWindow (dpy, black_windows[j]);
281 /* If there is user activity, bug out. (Bug out on keypresses or
282 mouse presses, but not motion, and not release events. Bugging
283 out on motion made the unfade hack be totally useless, I think.)
285 We put the event back so that the calling code can notice it too.
286 It would be better to not remove it at all, but that's harder
287 because Xlib has such a non-design for this kind of crap, and
288 in this application it doesn't matter if the events end up out
289 of order, so in the grand unix tradition we say "fuck it" and
290 do something that mostly works for the time being.
292 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), &dummy_event))
294 XPutBackEvent (dpy, &dummy_event);
298 #ifdef GETTIMEOFDAY_TWO_ARGS
299 gettimeofday(&now, &tzp);
304 /* If we haven't already used up our alotted time, sleep to avoid
305 changing the colormap too fast. */
307 long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
308 now.tv_usec - then.tv_usec);
309 then.tv_sec = now.tv_sec;
310 then.tv_usec = now.tv_usec;
311 if (usecs_per_step > diff)
312 usleep (usecs_per_step - diff);
318 if (orig_colors) free (orig_colors);
319 if (current_colors) free (current_colors);
321 /* If we've been given windows to raise after blackout, raise them before
322 releasing the colormaps.
324 if (out_p && black_windows)
326 for (i = 0; i < nscreens; i++)
329 XClearWindow (dpy, black_windows[i]);
330 XMapRaised (dpy, black_windows[i]);
335 /* Now put the target maps back.
336 If we're fading out, use the given cmap (or the default cmap, if none.)
337 If we're fading in, always use the default cmap.
339 for (i = 0; i < nscreens; i++)
341 Colormap cmap = (cmaps ? cmaps[i] : 0);
343 cmap = DefaultColormap(dpy, i);
344 XInstallColormap (dpy, cmap);
347 /* The fade (in or out) is complete, so we don't need the black maps on
350 for (i = 0; i < ncmaps; i++)
353 XUninstallColormap(dpy, fade_cmaps[i]);
354 XFreeColormap(dpy, fade_cmaps[i]);
362 #ifdef HAVE_SGI_VC_EXTENSION
364 # include <X11/extensions/XSGIvc.h>
366 struct screen_gamma_info {
367 int gamma_map; /* ??? always using 0 */
368 int nred, ngreen, nblue;
369 unsigned short *red1, *green1, *blue1;
370 unsigned short *red2, *green2, *blue2;
377 static void whack_gamma(Display *dpy, int screen,
378 struct screen_gamma_info *info, float ratio);
381 sgi_gamma_fade (Display *dpy,
382 Window *black_windows, int seconds, int ticks,
383 Bool out_p, Bool clear_windows)
385 int steps = seconds * ticks;
386 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
388 int nscreens = ScreenCount(dpy);
389 struct timeval then, now;
390 #ifdef GETTIMEOFDAY_TWO_ARGS
395 struct screen_gamma_info *info = (struct screen_gamma_info *)
396 calloc(nscreens, sizeof(*info));
398 /* Get the current gamma maps for all screens.
399 Bug out and return -1 if we can't get them for some screen.
401 for (screen = 0; screen < nscreens; screen++)
403 if (!XSGIvcQueryGammaMap(dpy, screen, info[screen].gamma_map,
404 &info[screen].gamma_size,
405 &info[screen].gamma_precision,
406 &info[screen].alpha_p))
409 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
410 XSGIVC_COMPONENT_RED,
411 &info[screen].nred, &info[screen].red1))
413 if (! XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
414 XSGIVC_COMPONENT_GREEN,
415 &info[screen].ngreen, &info[screen].green1))
417 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
418 XSGIVC_COMPONENT_BLUE,
419 &info[screen].nblue, &info[screen].blue1))
422 if (info[screen].gamma_precision == 8) /* Scale it up to 16 bits. */
425 for(j = 0; j < info[screen].nred; j++)
426 info[screen].red1[j] =
427 ((info[screen].red1[j] << 8) | info[screen].red1[j]);
428 for(j = 0; j < info[screen].ngreen; j++)
429 info[screen].green1[j] =
430 ((info[screen].green1[j] << 8) | info[screen].green1[j]);
431 for(j = 0; j < info[screen].nblue; j++)
432 info[screen].blue1[j] =
433 ((info[screen].blue1[j] << 8) | info[screen].blue1[j]);
436 info[screen].red2 = (unsigned short *)
437 malloc(sizeof(*info[screen].red2) * (info[screen].nred+1));
438 info[screen].green2 = (unsigned short *)
439 malloc(sizeof(*info[screen].green2) * (info[screen].ngreen+1));
440 info[screen].blue2 = (unsigned short *)
441 malloc(sizeof(*info[screen].blue2) * (info[screen].nblue+1));
444 #ifdef GETTIMEOFDAY_TWO_ARGS
445 gettimeofday(&then, &tzp);
450 /* If we're fading in (from black), then first crank the gamma all the
451 way down to 0, then take the windows off the screen.
454 for (screen = 0; screen < nscreens; screen++)
456 whack_gamma(dpy, screen, &info[screen], 0.0);
457 if (black_windows && black_windows[screen])
459 XUnmapWindow (dpy, black_windows[screen]);
460 XClearWindow (dpy, black_windows[screen]);
466 /* Iterate by steps of the animation... */
467 for (i = (out_p ? steps : 0);
468 (out_p ? i > 0 : i < steps);
471 for (screen = 0; screen < nscreens; screen++)
473 whack_gamma(dpy, screen, &info[screen],
474 (((float)i) / ((float)steps)));
476 /* If there is user activity, bug out. (Bug out on keypresses or
477 mouse presses, but not motion, and not release events. Bugging
478 out on motion made the unfade hack be totally useless, I think.)
480 We put the event back so that the calling code can notice it too.
481 It would be better to not remove it at all, but that's harder
482 because Xlib has such a non-design for this kind of crap, and
483 in this application it doesn't matter if the events end up out
484 of order, so in the grand unix tradition we say "fuck it" and
485 do something that mostly works for the time being.
487 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
490 XPutBackEvent (dpy, &dummy_event);
494 #ifdef GETTIMEOFDAY_TWO_ARGS
495 gettimeofday(&now, &tzp);
500 /* If we haven't already used up our alotted time, sleep to avoid
501 changing the colormap too fast. */
503 long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
504 now.tv_usec - then.tv_usec);
505 then.tv_sec = now.tv_sec;
506 then.tv_usec = now.tv_usec;
507 if (usecs_per_step > diff)
508 usleep (usecs_per_step - diff);
516 if (out_p && black_windows)
518 for (screen = 0; screen < nscreens; screen++)
521 XClearWindow (dpy, black_windows[screen]);
522 XMapRaised (dpy, black_windows[screen]);
527 /* I can't explain this; without this delay, we get a flicker.
528 I suppose there's some lossage with stale bits being in the
529 hardware frame buffer or something, and this delay gives it
530 time to flush out. This sucks! */
531 usleep(100000); /* 1/10th second */
533 for (screen = 0; screen < nscreens; screen++)
534 whack_gamma(dpy, screen, &info[screen], 1.0);
540 for (screen = 0; screen < nscreens; screen++)
542 if (info[screen].red1) free (info[screen].red1);
543 if (info[screen].green1) free (info[screen].green1);
544 if (info[screen].blue1) free (info[screen].blue1);
545 if (info[screen].red2) free (info[screen].red2);
546 if (info[screen].green2) free (info[screen].green2);
547 if (info[screen].blue2) free (info[screen].blue2);
555 whack_gamma(Display *dpy, int screen, struct screen_gamma_info *info,
560 if (ratio < 0) ratio = 0;
561 if (ratio > 1) ratio = 1;
562 for (k = 0; k < info->gamma_size; k++)
564 info->red2[k] = info->red1[k] * ratio;
565 info->green2[k] = info->green1[k] * ratio;
566 info->blue2[k] = info->blue1[k] * ratio;
569 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred,
570 XSGIVC_MComponentRed, info->red2);
571 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen,
572 XSGIVC_MComponentGreen, info->green2);
573 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nblue,
574 XSGIVC_MComponentBlue, info->blue2);
578 #endif /* HAVE_SGI_VC_EXTENSION */
584 #include "screenhack.h"
586 char *progclass = "foo";
587 char *defaults [] = {
591 XrmOptionDescRec options [] = {0};
592 int options_size = 0;
607 fprintf(stderr,"out..."); fflush(stderr);
608 fade_screens (dpy, 0, seconds, ticks, True);
609 fprintf(stderr, "done.\n"); fflush(stderr);
611 if (delay) sleep (delay);
613 fprintf(stderr,"in..."); fflush(stderr);
614 fade_screens (dpy, 0, seconds, ticks, False);
615 fprintf(stderr, "done.\n"); fflush(stderr);
617 if (delay) sleep (delay);