Added missing newline in NEDsim error message.
[screensavers] / screenhack / fade.c
CommitLineData
3144ee8a
AT
1/* xscreensaver, Copyright (c) 1992-2011 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#include "utils.h"
13
14#include <sys/time.h> /* for gettimeofday() */
15
16#ifdef VMS
17# include "vms-gtod.h"
18#endif /* VMS */
19
20#include "visual.h"
21#include "usleep.h"
22#include "fade.h"
23
24Colormap
25copy_colormap (Screen *screen, Visual *visual,
26 Colormap cmap, Colormap into_cmap)
27{
28 int i;
29 Display *dpy = DisplayOfScreen (screen);
30 Window window = RootWindowOfScreen (screen);
31 int ncolors = CellsOfScreen (screen);
32 XColor *colors = 0;
33
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)
37 return 0;
38 /* If this is a non-writable visual, bug out. */
39 if (!has_writable_cells (screen, visual))
40 return 0;
41
42 if (! into_cmap)
43 into_cmap = XCreateColormap (dpy, window, visual, AllocAll);
44 if (! cmap)
45 cmap = DefaultColormapOfScreen (screen);
46
47 colors = (XColor *) calloc(sizeof(XColor), ncolors);
48 for (i = 0; i < ncolors; i++)
49 colors [i].pixel = i;
50 XQueryColors (dpy, cmap, colors, ncolors);
51 XStoreColors (dpy, into_cmap, colors, ncolors);
52 free (colors);
53 return into_cmap;
54}
55
56
57void
58blacken_colormap (Screen *screen, Colormap cmap)
59{
60 Display *dpy = DisplayOfScreen (screen);
61 int ncolors = CellsOfScreen (screen);
62 XColor *colors;
63 int i;
64 if (ncolors > 4096)
65 return;
66 colors = (XColor *) calloc(sizeof(XColor), ncolors);
67 for (i = 0; i < ncolors; i++)
68 colors[i].pixel = i;
69 XStoreColors (dpy, cmap, colors, ncolors);
70 free (colors);
71}
72
73
74
75static void fade_screens_1 (Display *dpy, Colormap *cmaps,
76 Window *black_windows, int nwindows,
77 int seconds, int ticks,
78 Bool out_p, Bool clear_windows);
79
80#ifdef HAVE_SGI_VC_EXTENSION
81static int sgi_gamma_fade (Display *dpy,
82 Window *black_windows, int nwindows,
83 int seconds, int ticks,
84 Bool out_p, Bool clear_windows);
85#endif /* HAVE_SGI_VC_EXTENSION */
86
87#ifdef HAVE_XF86VMODE_GAMMA
88static int xf86_gamma_fade (Display *dpy,
89 Window *black_windows, int nwindows,
90 int seconds, int ticks,
91 Bool out_p, Bool clear_windows);
92#endif /* HAVE_XF86VMODE_GAMMA */
93
94
95void
96fade_screens (Display *dpy, Colormap *cmaps,
97 Window *black_windows, int nwindows,
98 int seconds, int ticks,
99 Bool out_p, Bool clear_windows)
100{
101 int oseconds = seconds;
102 Bool was_in_p = !out_p;
103
104 /* When we're asked to fade in, first fade out, then fade in.
105 That way all the transitions are smooth -- from what's on the
106 screen, to black, to the desktop.
107 */
108 if (was_in_p)
109 {
110 clear_windows = True;
111 out_p = True;
112 seconds /= 3;
113 if (seconds == 0)
114 seconds = 1;
115 }
116
117 AGAIN:
118
119/* #### printf("\n\nfade_screens %d %d %d\n", seconds, ticks, out_p); */
120
121#ifdef HAVE_SGI_VC_EXTENSION
122 /* First try to do it by fading the gamma in an SGI-specific way... */
123 if (0 == sgi_gamma_fade(dpy, black_windows, nwindows,
124 seconds, ticks, out_p,
125 clear_windows))
126 ;
127 else
128#endif /* HAVE_SGI_VC_EXTENSION */
129
130#ifdef HAVE_XF86VMODE_GAMMA
131 /* Then try to do it by fading the gamma in an XFree86-specific way... */
132 if (0 == xf86_gamma_fade(dpy, black_windows, nwindows,
133 seconds, ticks, out_p,
134 clear_windows))
135 ;
136 else
137#endif /* HAVE_XF86VMODE_GAMMA */
138
139 /* Else, do it the old-fashioned way, which (somewhat) loses if
140 there are TrueColor windows visible. */
141 fade_screens_1 (dpy, cmaps, black_windows, nwindows,
142 seconds, ticks,
143 out_p, clear_windows);
144
145 /* If we were supposed to be fading in, do so now (we just faded out,
146 so now fade back in.)
147 */
148 if (was_in_p)
149 {
150 was_in_p = False;
151 out_p = False;
152 seconds = oseconds * 2 / 3;
153 if (seconds == 0)
154 seconds = 1;
155 goto AGAIN;
156 }
157}
158
159
160static void
161sleep_from (struct timeval *now, struct timeval *then, long usecs_per_step)
162{
163 /* If several seconds have passed, the machine must have been asleep
164 or thrashing or something. Don't sleep in that case, to avoid
165 overflowing and sleeping for an unconscionably long time. This
166 function should only be sleeping for very short periods.
167 */
168 if (now->tv_sec - then->tv_sec < 5)
169 {
170 long diff = (((now->tv_sec - then->tv_sec) * 1000000) +
171 now->tv_usec - then->tv_usec);
172 if (usecs_per_step > diff)
173 usleep (usecs_per_step - diff);
174 }
175
176 then->tv_sec = now->tv_sec;
177 then->tv_usec = now->tv_usec;
178}
179
180
181
182/* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video
183 hardware, which is capable of installing multiple (4) colormaps
184 simultaneously. We have to install multiple copies of the same set of
185 colors in order to fill up all the available slots in the hardware color
186 lookup table, so we install an extra N colormaps per screen to make sure
187 that all screens really go black.
188
189 I'm told that this trick also works with XInside's AcceleratedX when using
190 the Matrox Millennium card (which also allows multiple PseudoColor and
191 TrueColor visuals to co-exist and display properly at the same time.)
192
193 This trick works ok on the 24-bit Indy video hardware, but doesn't work at
194 all on the O2 24-bit hardware. I guess the higher-end hardware is too
195 "good" for this to work (dammit.) So... I figured out the "right" way to
196 do this on SGIs, which is to ramp the monitor's gamma down to 0. That's
197 what is implemented in sgi_gamma_fade(), so we use that if we can.
198 */
199static void
200fade_screens_1 (Display *dpy, Colormap *cmaps,
201 Window *black_windows, int nwindows,
202 int seconds, int ticks,
203 Bool out_p, Bool clear_windows)
204{
205 int i, j, k;
206 int steps = seconds * ticks;
207 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
208 XEvent dummy_event;
209 int cmaps_per_screen = 5;
210 int nscreens = ScreenCount(dpy);
211 int ncmaps = nscreens * cmaps_per_screen;
212 Colormap *fade_cmaps = 0;
213 Bool installed = False;
214 int total_ncolors;
215 XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors;
216 struct timeval then, now;
217#ifdef GETTIMEOFDAY_TWO_ARGS
218 struct timezone tzp;
219#endif
220
221 total_ncolors = 0;
222 for (i = 0; i < nscreens; i++)
223 total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i));
224
225 orig_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
226 current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
227
228 /* Get the contents of the colormap we are fading from or to. */
229 screen_colors = orig_colors;
230 for (i = 0; i < nscreens; i++)
231 {
232 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, i));
233 Colormap cmap = (cmaps ? cmaps[i] : 0);
234 if (!cmap) cmap = DefaultColormap(dpy, i);
235
236 for (j = 0; j < ncolors; j++)
237 screen_colors[j].pixel = j;
238 XQueryColors (dpy, cmap, screen_colors, ncolors);
239
240 screen_colors += ncolors;
241 }
242
243 memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor));
244
245
246 /* Make the writable colormaps (we keep these around and reuse them.) */
247 if (!fade_cmaps)
248 {
249 fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps);
250 for (i = 0; i < nscreens; i++)
251 {
252 Visual *v = DefaultVisual(dpy, i);
253 Screen *s = ScreenOfDisplay(dpy, i);
254 if (has_writable_cells (s, v))
255 for (j = 0; j < cmaps_per_screen; j++)
256 fade_cmaps[(i * cmaps_per_screen) + j] =
257 XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll);
258 }
259 }
260
261#ifdef GETTIMEOFDAY_TWO_ARGS
262 gettimeofday(&then, &tzp);
263#else
264 gettimeofday(&then);
265#endif
266
267 /* Iterate by steps of the animation... */
268 for (i = (out_p ? steps : 0);
269 (out_p ? i > 0 : i < steps);
270 (out_p ? i-- : i++))
271 {
272
273 /* For each screen, compute the current value of each color...
274 */
275 orig_screen_colors = orig_colors;
276 screen_colors = current_colors;
277 for (j = 0; j < nscreens; j++)
278 {
279 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
280 for (k = 0; k < ncolors; k++)
281 {
282 /* This doesn't take into account the relative luminance of the
283 RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but
284 the difference is imperceptible for this application... */
285 screen_colors[k].red = orig_screen_colors[k].red * i / steps;
286 screen_colors[k].green = orig_screen_colors[k].green * i / steps;
287 screen_colors[k].blue = orig_screen_colors[k].blue * i / steps;
288 }
289 screen_colors += ncolors;
290 orig_screen_colors += ncolors;
291 }
292
293 /* Put the colors into the maps...
294 */
295 screen_colors = current_colors;
296 for (j = 0; j < nscreens; j++)
297 {
298 int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
299 for (k = 0; k < cmaps_per_screen; k++)
300 {
301 Colormap c = fade_cmaps[j * cmaps_per_screen + k];
302 if (c)
303 XStoreColors (dpy, c, screen_colors, ncolors);
304 }
305 screen_colors += ncolors;
306 }
307
308 /* Put the maps on the screens, and then take the windows off the screen.
309 (only need to do this the first time through the loop.)
310 */
311 if (!installed)
312 {
313 for (j = 0; j < ncmaps; j++)
314 if (fade_cmaps[j])
315 XInstallColormap (dpy, fade_cmaps[j]);
316 installed = True;
317
318 if (black_windows && !out_p)
319 for (j = 0; j < nwindows; j++)
320 if (black_windows[j])
321 {
322 XUnmapWindow (dpy, black_windows[j]);
323 XClearWindow (dpy, black_windows[j]);
324 }
325 }
326
327 XSync (dpy, False);
328
329 /* If there is user activity, bug out. (Bug out on keypresses or
330 mouse presses, but not motion, and not release events. Bugging
331 out on motion made the unfade hack be totally useless, I think.)
332
333 We put the event back so that the calling code can notice it too.
334 It would be better to not remove it at all, but that's harder
335 because Xlib has such a non-design for this kind of crap, and
336 in this application it doesn't matter if the events end up out
337 of order, so in the grand unix tradition we say "fuck it" and
338 do something that mostly works for the time being.
339 */
340 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), &dummy_event))
341 {
342 XPutBackEvent (dpy, &dummy_event);
343 goto DONE;
344 }
345
346#ifdef GETTIMEOFDAY_TWO_ARGS
347 gettimeofday(&now, &tzp);
348#else
349 gettimeofday(&now);
350#endif
351
352 /* If we haven't already used up our alotted time, sleep to avoid
353 changing the colormap too fast. */
354 sleep_from (&now, &then, usecs_per_step);
355 }
356
357 DONE:
358
359 if (orig_colors) free (orig_colors);
360 if (current_colors) free (current_colors);
361
362 /* If we've been given windows to raise after blackout, raise them before
363 releasing the colormaps.
364 */
365 if (out_p && black_windows)
366 {
367 for (i = 0; i < nwindows; i++)
368 {
369 if (clear_windows)
370 XClearWindow (dpy, black_windows[i]);
371 XMapRaised (dpy, black_windows[i]);
372 }
373 XSync(dpy, False);
374 }
375
376 /* Now put the target maps back.
377 If we're fading out, use the given cmap (or the default cmap, if none.)
378 If we're fading in, always use the default cmap.
379 */
380 for (i = 0; i < nscreens; i++)
381 {
382 Colormap cmap = (cmaps ? cmaps[i] : 0);
383 if (!cmap || !out_p)
384 cmap = DefaultColormap(dpy, i);
385 XInstallColormap (dpy, cmap);
386 }
387
388 /* The fade (in or out) is complete, so we don't need the black maps on
389 stage any more.
390 */
391 for (i = 0; i < ncmaps; i++)
392 if (fade_cmaps[i])
393 {
394 XUninstallColormap(dpy, fade_cmaps[i]);
395 XFreeColormap(dpy, fade_cmaps[i]);
396 fade_cmaps[i] = 0;
397 }
398 free(fade_cmaps);
399 fade_cmaps = 0;
400}
401
402
403\f
404/* SGI Gamma fading */
405
406#ifdef HAVE_SGI_VC_EXTENSION
407
408# include <X11/extensions/XSGIvc.h>
409
410struct screen_sgi_gamma_info {
411 int gamma_map; /* ??? always using 0 */
412 int nred, ngreen, nblue;
413 unsigned short *red1, *green1, *blue1;
414 unsigned short *red2, *green2, *blue2;
415 int gamma_size;
416 int gamma_precision;
417 Bool alpha_p;
418};
419
420
421static void sgi_whack_gamma(Display *dpy, int screen,
422 struct screen_sgi_gamma_info *info, float ratio);
423
424static int
425sgi_gamma_fade (Display *dpy,
426 Window *black_windows, int nwindows,
427 int seconds, int ticks,
428 Bool out_p, Bool clear_windows)
429{
430 int steps = seconds * ticks;
431 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
432 XEvent dummy_event;
433 int nscreens = ScreenCount(dpy);
434 struct timeval then, now;
435#ifdef GETTIMEOFDAY_TWO_ARGS
436 struct timezone tzp;
437#endif
438 int i, screen;
439 int status = -1;
440 struct screen_sgi_gamma_info *info = (struct screen_sgi_gamma_info *)
441 calloc(nscreens, sizeof(*info));
442
443 /* Get the current gamma maps for all screens.
444 Bug out and return -1 if we can't get them for some screen.
445 */
446 for (screen = 0; screen < nscreens; screen++)
447 {
448 if (!XSGIvcQueryGammaMap(dpy, screen, info[screen].gamma_map,
449 &info[screen].gamma_size,
450 &info[screen].gamma_precision,
451 &info[screen].alpha_p))
452 goto FAIL;
453
454 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
455 XSGIVC_COMPONENT_RED,
456 &info[screen].nred, &info[screen].red1))
457 goto FAIL;
458 if (! XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
459 XSGIVC_COMPONENT_GREEN,
460 &info[screen].ngreen, &info[screen].green1))
461 goto FAIL;
462 if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map,
463 XSGIVC_COMPONENT_BLUE,
464 &info[screen].nblue, &info[screen].blue1))
465 goto FAIL;
466
467 if (info[screen].gamma_precision == 8) /* Scale it up to 16 bits. */
468 {
469 int j;
470 for(j = 0; j < info[screen].nred; j++)
471 info[screen].red1[j] =
472 ((info[screen].red1[j] << 8) | info[screen].red1[j]);
473 for(j = 0; j < info[screen].ngreen; j++)
474 info[screen].green1[j] =
475 ((info[screen].green1[j] << 8) | info[screen].green1[j]);
476 for(j = 0; j < info[screen].nblue; j++)
477 info[screen].blue1[j] =
478 ((info[screen].blue1[j] << 8) | info[screen].blue1[j]);
479 }
480
481 info[screen].red2 = (unsigned short *)
482 malloc(sizeof(*info[screen].red2) * (info[screen].nred+1));
483 info[screen].green2 = (unsigned short *)
484 malloc(sizeof(*info[screen].green2) * (info[screen].ngreen+1));
485 info[screen].blue2 = (unsigned short *)
486 malloc(sizeof(*info[screen].blue2) * (info[screen].nblue+1));
487 }
488
489#ifdef GETTIMEOFDAY_TWO_ARGS
490 gettimeofday(&then, &tzp);
491#else
492 gettimeofday(&then);
493#endif
494
495 /* If we're fading in (from black), then first crank the gamma all the
496 way down to 0, then take the windows off the screen.
497 */
498 if (!out_p)
499 {
500 for (screen = 0; screen < nscreens; screen++)
501 sgi_whack_gamma(dpy, screen, &info[screen], 0.0);
502
503 for (screen = 0; screen < nwindows; screen++)
504 if (black_windows && black_windows[screen])
505 {
506 XUnmapWindow (dpy, black_windows[screen]);
507 XClearWindow (dpy, black_windows[screen]);
508 XSync(dpy, False);
509 }
510 }
511
512 /* Iterate by steps of the animation... */
513 for (i = (out_p ? steps : 0);
514 (out_p ? i > 0 : i < steps);
515 (out_p ? i-- : i++))
516 {
517 for (screen = 0; screen < nscreens; screen++)
518 {
519 sgi_whack_gamma(dpy, screen, &info[screen],
520 (((float)i) / ((float)steps)));
521
522 /* If there is user activity, bug out. (Bug out on keypresses or
523 mouse presses, but not motion, and not release events. Bugging
524 out on motion made the unfade hack be totally useless, I think.)
525
526 We put the event back so that the calling code can notice it too.
527 It would be better to not remove it at all, but that's harder
528 because Xlib has such a non-design for this kind of crap, and
529 in this application it doesn't matter if the events end up out
530 of order, so in the grand unix tradition we say "fuck it" and
531 do something that mostly works for the time being.
532 */
533 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
534 &dummy_event))
535 {
536 XPutBackEvent (dpy, &dummy_event);
537 goto DONE;
538 }
539
540#ifdef GETTIMEOFDAY_TWO_ARGS
541 gettimeofday(&now, &tzp);
542#else
543 gettimeofday(&now);
544#endif
545
546 /* If we haven't already used up our alotted time, sleep to avoid
547 changing the colormap too fast. */
548 sleep_from (&now, &then, usecs_per_step);
549 }
550 }
551
552
553 DONE:
554
555 if (out_p && black_windows)
556 {
557 for (screen = 0; screen < nwindows; screen++)
558 {
559 if (clear_windows)
560 XClearWindow (dpy, black_windows[screen]);
561 XMapRaised (dpy, black_windows[screen]);
562 }
563 XSync(dpy, False);
564 }
565
566 /* I can't explain this; without this delay, we get a flicker.
567 I suppose there's some lossage with stale bits being in the
568 hardware frame buffer or something, and this delay gives it
569 time to flush out. This sucks! */
570 usleep(100000); /* 1/10th second */
571
572 for (screen = 0; screen < nscreens; screen++)
573 sgi_whack_gamma(dpy, screen, &info[screen], 1.0);
574 XSync(dpy, False);
575
576 status = 0;
577
578 FAIL:
579 for (screen = 0; screen < nscreens; screen++)
580 {
581 if (info[screen].red1) free (info[screen].red1);
582 if (info[screen].green1) free (info[screen].green1);
583 if (info[screen].blue1) free (info[screen].blue1);
584 if (info[screen].red2) free (info[screen].red2);
585 if (info[screen].green2) free (info[screen].green2);
586 if (info[screen].blue2) free (info[screen].blue2);
587 }
588 free(info);
589
590 return status;
591}
592
593static void
594sgi_whack_gamma(Display *dpy, int screen, struct screen_sgi_gamma_info *info,
595 float ratio)
596{
597 int k;
598
599 if (ratio < 0) ratio = 0;
600 if (ratio > 1) ratio = 1;
601 for (k = 0; k < info->gamma_size; k++)
602 {
603 info->red2[k] = info->red1[k] * ratio;
604 info->green2[k] = info->green1[k] * ratio;
605 info->blue2[k] = info->blue1[k] * ratio;
606 }
607
608 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred,
609 XSGIVC_MComponentRed, info->red2);
610 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen,
611 XSGIVC_MComponentGreen, info->green2);
612 XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nblue,
613 XSGIVC_MComponentBlue, info->blue2);
614 XSync(dpy, False);
615}
616
617#endif /* HAVE_SGI_VC_EXTENSION */
618
619
620\f
621/* XFree86 4.x+ Gamma fading */
622
623#ifdef HAVE_XF86VMODE_GAMMA
624
625#include <X11/extensions/xf86vmode.h>
626
627typedef struct {
628 XF86VidModeGamma vmg;
629 int size;
630 unsigned short *r, *g, *b;
631} xf86_gamma_info;
632
633static int xf86_check_gamma_extension (Display *dpy);
634static Bool xf86_whack_gamma (Display *dpy, int screen,
635 xf86_gamma_info *ginfo, float ratio);
636
637static int
638xf86_gamma_fade (Display *dpy,
639 Window *black_windows, int nwindows,
640 int seconds, int ticks,
641 Bool out_p, Bool clear_windows)
642{
643 int steps = seconds * ticks;
644 long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
645 XEvent dummy_event;
646 int nscreens = ScreenCount(dpy);
647 struct timeval then, now;
648#ifdef GETTIMEOFDAY_TWO_ARGS
649 struct timezone tzp;
650#endif
651 int i, screen;
652 int status = -1;
653 xf86_gamma_info *info = 0;
654
655 static int ext_ok = -1;
656
657 /* Only probe the extension once: the answer isn't going to change. */
658 if (ext_ok == -1)
659 ext_ok = xf86_check_gamma_extension (dpy);
660
661 /* If this server doesn't have the gamma extension, bug out. */
662 if (ext_ok == 0)
663 goto FAIL;
664
665# ifndef HAVE_XF86VMODE_GAMMA_RAMP
666 if (ext_ok == 2) ext_ok = 1; /* server is newer than client! */
667# endif
668
669 info = (xf86_gamma_info *) calloc(nscreens, sizeof(*info));
670
671 /* Get the current gamma maps for all screens.
672 Bug out and return -1 if we can't get them for some screen.
673 */
674 for (screen = 0; screen < nscreens; screen++)
675 {
676 if (ext_ok == 1) /* only have gamma parameter, not ramps. */
677 {
678 if (!XF86VidModeGetGamma(dpy, screen, &info[screen].vmg))
679 goto FAIL;
680 }
681# ifdef HAVE_XF86VMODE_GAMMA_RAMP
682 else if (ext_ok == 2) /* have ramps */
683 {
684 if (!XF86VidModeGetGammaRampSize(dpy, screen, &info[screen].size))
685 goto FAIL;
686 if (info[screen].size <= 0)
687 goto FAIL;
688
689 info[screen].r = (unsigned short *)
690 calloc(info[screen].size, sizeof(unsigned short));
691 info[screen].g = (unsigned short *)
692 calloc(info[screen].size, sizeof(unsigned short));
693 info[screen].b = (unsigned short *)
694 calloc(info[screen].size, sizeof(unsigned short));
695
696 if (!(info[screen].r && info[screen].g && info[screen].b))
697 goto FAIL;
698
699 if (!XF86VidModeGetGammaRamp(dpy, screen, info[screen].size,
700 info[screen].r,
701 info[screen].g,
702 info[screen].b))
703 goto FAIL;
704 }
705# endif /* HAVE_XF86VMODE_GAMMA_RAMP */
706 else
707 abort();
708 }
709
710#ifdef GETTIMEOFDAY_TWO_ARGS
711 gettimeofday(&then, &tzp);
712#else
713 gettimeofday(&then);
714#endif
715
716 /* If we're fading in (from black), then first crank the gamma all the
717 way down to 0, then take the windows off the screen.
718 */
719 if (!out_p)
720 {
721 for (screen = 0; screen < nscreens; screen++)
722 xf86_whack_gamma(dpy, screen, &info[screen], 0.0);
723 for (screen = 0; screen < nwindows; screen++)
724 if (black_windows && black_windows[screen])
725 {
726 XUnmapWindow (dpy, black_windows[screen]);
727 XClearWindow (dpy, black_windows[screen]);
728 XSync(dpy, False);
729 }
730 }
731
732 /* Iterate by steps of the animation... */
733 for (i = (out_p ? steps : 0);
734 (out_p ? i > 0 : i < steps);
735 (out_p ? i-- : i++))
736 {
737 for (screen = 0; screen < nscreens; screen++)
738 {
739 xf86_whack_gamma(dpy, screen, &info[screen],
740 (((float)i) / ((float)steps)));
741
742 /* If there is user activity, bug out. (Bug out on keypresses or
743 mouse presses, but not motion, and not release events. Bugging
744 out on motion made the unfade hack be totally useless, I think.)
745
746 We put the event back so that the calling code can notice it too.
747 It would be better to not remove it at all, but that's harder
748 because Xlib has such a non-design for this kind of crap, and
749 in this application it doesn't matter if the events end up out
750 of order, so in the grand unix tradition we say "fuck it" and
751 do something that mostly works for the time being.
752 */
753 if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask),
754 &dummy_event))
755 {
756 XPutBackEvent (dpy, &dummy_event);
757 goto DONE;
758 }
759
760#ifdef GETTIMEOFDAY_TWO_ARGS
761 gettimeofday(&now, &tzp);
762#else
763 gettimeofday(&now);
764#endif
765
766 /* If we haven't already used up our alotted time, sleep to avoid
767 changing the colormap too fast. */
768 sleep_from (&now, &then, usecs_per_step);
769 }
770 }
771
772
773 DONE:
774
775 if (out_p && black_windows)
776 {
777 for (screen = 0; screen < nwindows; screen++)
778 {
779 if (clear_windows)
780 XClearWindow (dpy, black_windows[screen]);
781 XMapRaised (dpy, black_windows[screen]);
782 }
783 XSync(dpy, False);
784 }
785
786 /* I can't explain this; without this delay, we get a flicker.
787 I suppose there's some lossage with stale bits being in the
788 hardware frame buffer or something, and this delay gives it
789 time to flush out. This sucks! */
790 usleep(100000); /* 1/10th second */
791
792 for (screen = 0; screen < nscreens; screen++)
793 xf86_whack_gamma(dpy, screen, &info[screen], 1.0);
794 XSync(dpy, False);
795
796 status = 0;
797
798 FAIL:
799 if (info)
800 {
801 for (screen = 0; screen < nscreens; screen++)
802 {
803 if (info[screen].r) free(info[screen].r);
804 if (info[screen].g) free(info[screen].g);
805 if (info[screen].b) free(info[screen].b);
806 }
807 free(info);
808 }
809
810 return status;
811}
812
813
814/* This bullshit is needed because the VidMode extension doesn't work
815 on remote displays -- but if the remote display has the extension
816 at all, XF86VidModeQueryExtension returns true, and then
817 XF86VidModeQueryVersion dies with an X error. Thank you XFree,
818 may I have another.
819 */
820
821static Bool error_handler_hit_p = False;
822
823static int
824ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
825{
826 error_handler_hit_p = True;
827 return 0;
828}
829
830static Bool
831safe_XF86VidModeQueryVersion (Display *dpy, int *majP, int *minP)
832{
833 Bool result;
834 XErrorHandler old_handler;
835 XSync (dpy, False);
836 error_handler_hit_p = False;
837 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
838
839 result = XF86VidModeQueryVersion (dpy, majP, minP);
840
841 XSync (dpy, False);
842 XSetErrorHandler (old_handler);
843 XSync (dpy, False);
844
845 return (error_handler_hit_p
846 ? False
847 : result);
848}
849
850
851
852/* VidModeExtension version 2.0 or better is needed to do gamma.
853 2.0 added gamma values; 2.1 added gamma ramps.
854 */
855# define XF86_VIDMODE_GAMMA_MIN_MAJOR 2
856# define XF86_VIDMODE_GAMMA_MIN_MINOR 0
857# define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2
858# define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1
859
860
861
862/* Returns 0 if gamma fading not available; 1 if only gamma value setting
863 is available; 2 if gamma ramps are available.
864 */
865static int
866xf86_check_gamma_extension (Display *dpy)
867{
868 int event, error, major, minor;
869
870 if (!XF86VidModeQueryExtension (dpy, &event, &error))
871 return 0; /* display doesn't have the extension. */
872
873 if (!safe_XF86VidModeQueryVersion (dpy, &major, &minor))
874 return 0; /* unable to get version number? */
875
876 if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR ||
877 (major == XF86_VIDMODE_GAMMA_MIN_MAJOR &&
878 minor < XF86_VIDMODE_GAMMA_MIN_MINOR))
879 return 0; /* extension is too old for gamma. */
880
881 if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR ||
882 (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR &&
883 minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR))
884 return 1; /* extension is too old for gamma ramps. */
885
886 /* Copacetic */
887 return 2;
888}
889
890
891/* XFree doesn't let you set gamma to a value smaller than this.
892 Apparently they didn't anticipate the trick I'm doing here...
893 */
894#define XF86_MIN_GAMMA 0.1
895
896
897static Bool
898xf86_whack_gamma(Display *dpy, int screen, xf86_gamma_info *info,
899 float ratio)
900{
901 Bool status;
902
903 XErrorHandler old_handler;
904 XSync (dpy, False);
905 error_handler_hit_p = False;
906 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
907
908 if (ratio < 0) ratio = 0;
909 if (ratio > 1) ratio = 1;
910
911 if (info->size == 0) /* we only have a gamma number, not a ramp. */
912 {
913 XF86VidModeGamma g2;
914
915 g2.red = info->vmg.red * ratio;
916 g2.green = info->vmg.green * ratio;
917 g2.blue = info->vmg.blue * ratio;
918
919# ifdef XF86_MIN_GAMMA
920 if (g2.red < XF86_MIN_GAMMA) g2.red = XF86_MIN_GAMMA;
921 if (g2.green < XF86_MIN_GAMMA) g2.green = XF86_MIN_GAMMA;
922 if (g2.blue < XF86_MIN_GAMMA) g2.blue = XF86_MIN_GAMMA;
923# endif
924
925 status = XF86VidModeSetGamma (dpy, screen, &g2);
926 }
927 else
928 {
929# ifdef HAVE_XF86VMODE_GAMMA_RAMP
930
931 unsigned short *r, *g, *b;
932 int i;
933 r = (unsigned short *) malloc(info->size * sizeof(unsigned short));
934 g = (unsigned short *) malloc(info->size * sizeof(unsigned short));
935 b = (unsigned short *) malloc(info->size * sizeof(unsigned short));
936
937 for (i = 0; i < info->size; i++)
938 {
939 r[i] = info->r[i] * ratio;
940 g[i] = info->g[i] * ratio;
941 b[i] = info->b[i] * ratio;
942 }
943
944 status = XF86VidModeSetGammaRamp(dpy, screen, info->size, r, g, b);
945
946 free (r);
947 free (g);
948 free (b);
949
950# else /* !HAVE_XF86VMODE_GAMMA_RAMP */
951 abort();
952# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */
953 }
954
955 XSync (dpy, False);
956 XSetErrorHandler (old_handler);
957 XSync (dpy, False);
958
959 return status;
960}
961
962#endif /* HAVE_XF86VMODE_GAMMA */