Commit | Line | Data |
---|---|---|
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 | ||
24 | Colormap | |
25 | copy_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 | ||
57 | void | |
58 | blacken_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 | ||
75 | static 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 | |
81 | static 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 | |
88 | static 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 | ||
95 | void | |
96 | fade_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 | ||
160 | static void | |
161 | sleep_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 | */ | |
199 | static void | |
200 | fade_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 | ||
410 | struct 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 | ||
421 | static void sgi_whack_gamma(Display *dpy, int screen, | |
422 | struct screen_sgi_gamma_info *info, float ratio); | |
423 | ||
424 | static int | |
425 | sgi_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 | ||
593 | static void | |
594 | sgi_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 | ||
627 | typedef struct { | |
628 | XF86VidModeGamma vmg; | |
629 | int size; | |
630 | unsigned short *r, *g, *b; | |
631 | } xf86_gamma_info; | |
632 | ||
633 | static int xf86_check_gamma_extension (Display *dpy); | |
634 | static Bool xf86_whack_gamma (Display *dpy, int screen, | |
635 | xf86_gamma_info *ginfo, float ratio); | |
636 | ||
637 | static int | |
638 | xf86_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 | ||
821 | static Bool error_handler_hit_p = False; | |
822 | ||
823 | static int | |
824 | ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) | |
825 | { | |
826 | error_handler_hit_p = True; | |
827 | return 0; | |
828 | } | |
829 | ||
830 | static Bool | |
831 | safe_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 | */ | |
865 | static int | |
866 | xf86_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 | ||
897 | static Bool | |
898 | xf86_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 */ |