ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-2.07.tar.gz
[xscreensaver] / utils / fade.c
1 /* xscreensaver, Copyright (c) 1992-1997 Jamie Zawinski <jwz@netscape.com>
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
25 Colormap
26 copy_colormap (Screen *screen, Visual *visual,
27                Colormap cmap, Colormap into_cmap)
28 {
29   int i;
30   Display *dpy = DisplayOfScreen (screen);
31   Window window = RootWindowOfScreen (screen);
32   int ncolors = CellsOfScreen (screen);
33   XColor *colors = 0;
34
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)
38     return 0;
39   /* If this is a non-writable visual, bug out. */
40   if (!has_writable_cells (screen, visual))
41     return 0;
42
43   if (! into_cmap)
44     into_cmap = XCreateColormap (dpy, window, visual, AllocAll);
45   if (! cmap)
46     cmap = DefaultColormapOfScreen (screen);
47
48   colors = (XColor *) calloc(sizeof(XColor), ncolors);
49   for (i = 0; i < ncolors; i++)
50     colors [i].pixel = i;
51   XQueryColors (dpy, cmap, colors, ncolors);
52   XStoreColors (dpy, into_cmap, colors, ncolors);
53   free (colors);
54   return into_cmap;
55 }
56
57
58 void
59 blacken_colormap (Screen *screen, Colormap cmap)
60 {
61   Display *dpy = DisplayOfScreen (screen);
62   int ncolors = CellsOfScreen (screen);
63   XColor *colors;
64   int i;
65   if (ncolors > 4096)
66     return;
67   colors = (XColor *) calloc(sizeof(XColor), ncolors);
68   for (i = 0; i < ncolors; i++)
69     colors[i].pixel = i;
70   XStoreColors (dpy, cmap, colors, ncolors);
71   free (colors);
72 }
73
74
75 /* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video
76    hardware, which is capable of installing multiple (4) colormaps
77    simultaniously.  We have to install multiple copies of the same set of
78    colors in order to fill up all the available slots in the hardware color
79    lookup table, so we install an extra N colormaps per screen to make sure
80    that all screens really go black.  */
81
82 void
83 fade_screens (Display *dpy, Colormap *cmaps,
84               int seconds, int ticks,
85               Bool out_p)
86 {
87   int i, j, k;
88   int steps = seconds * ticks;
89   long usecs_per_step = (long)(seconds * 1000000) / (long)steps;
90   XEvent dummy_event;
91   int cmaps_per_screen = 5;
92   int nscreens = ScreenCount(dpy);
93   int ncmaps = nscreens * cmaps_per_screen;
94   static Colormap *fade_cmaps = 0;
95   Bool installed = False;
96   int total_ncolors;
97   XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors;
98   struct timeval then, now;
99 #ifdef GETTIMEOFDAY_TWO_ARGS
100   struct timezone tzp;
101 #endif
102
103   total_ncolors = 0;
104   for (i = 0; i < nscreens; i++)
105     total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i));
106
107   orig_colors    = (XColor *) calloc(sizeof(XColor), total_ncolors);
108   current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors);
109
110   /* Get the contents of the colormap we are fading from or to. */
111   screen_colors = orig_colors;
112   for (i = 0; i < nscreens; i++)
113     {
114       int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, i));
115       Colormap cmap = (cmaps ? cmaps[i] : 0);
116       if (!cmap) cmap = DefaultColormap(dpy, i);
117
118       for (j = 0; j < ncolors; j++)
119         screen_colors[j].pixel = j;
120       XQueryColors (dpy, cmap, screen_colors, ncolors);
121
122       screen_colors += ncolors;
123     }
124
125   memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor));
126
127
128   /* Make the writable colormaps (we keep these around and reuse them.) */
129   if (!fade_cmaps)
130     {
131       fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps);
132       for (i = 0; i < nscreens; i++)
133         {
134           Visual *v = DefaultVisual(dpy, i);
135           Screen *s = ScreenOfDisplay(dpy, i);
136           if (has_writable_cells (s, v))
137             for (j = 0; j < cmaps_per_screen; j++)
138               fade_cmaps[(i * cmaps_per_screen) + j] =
139                 XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll);
140         }
141     }
142
143 #ifdef GETTIMEOFDAY_TWO_ARGS
144   gettimeofday(&then, &tzp);
145 #else
146   gettimeofday(&then);
147 #endif
148
149   /* Iterate by steps of the animation... */
150   for (i = (out_p ? steps : 0);
151        (out_p ? i > 0 : i < steps);
152        (out_p ? i-- : i++))
153     {
154
155       /* For each screen, compute the current value of each color...
156        */
157       orig_screen_colors = orig_colors;
158       screen_colors = current_colors;
159       for (j = 0; j < nscreens; j++)
160         {
161           int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
162           for (k = 0; k < ncolors; k++)
163             {
164               /* This doesn't take into account the relative luminance of the
165                  RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but
166                  the difference is imperceptible for this application... */
167               screen_colors[k].red   = orig_screen_colors[k].red   * i / steps;
168               screen_colors[k].green = orig_screen_colors[k].green * i / steps;
169               screen_colors[k].blue  = orig_screen_colors[k].blue  * i / steps;
170             }
171           screen_colors      += ncolors;
172           orig_screen_colors += ncolors;
173         }
174
175       /* Put the colors into the maps...
176        */
177       screen_colors = current_colors;
178       for (j = 0; j < nscreens; j++)
179         {
180           int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j));
181           for (k = 0; k < cmaps_per_screen; k++)
182             {
183               Colormap c = fade_cmaps[j * cmaps_per_screen + k];
184               if (c)
185                 XStoreColors (dpy, c, screen_colors, ncolors);
186             }
187           screen_colors += ncolors;
188         }
189
190       /* Put the maps on the screens...
191          (only need to do this the first time through the loop.)
192        */
193       if (!installed)
194         {
195           for (j = 0; j < ncmaps; j++)
196             if (fade_cmaps[j])
197               XInstallColormap (dpy, fade_cmaps[j]);
198           installed = True;
199         }
200
201       XSync (dpy, False);
202
203       /* If there is user activity, bug out.  (Bug out on keypresses or
204          mouse presses, but not motion, and not release events.  Bugging
205          out on motion made the unfade hack be totally useless, I think.)
206
207          We put the event back so that the calling code can notice it too.
208          It would be better to not remove it at all, but that's harder
209          because Xlib has such a non-design for this kind of crap, and
210          in this application it doesn't matter if the events end up out
211          of order, so in the grand unix tradition we say "fuck it" and
212          do something that mostly works for the time being.
213        */
214       if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), &dummy_event))
215         {
216           XPutBackEvent (dpy, &dummy_event);
217           goto DONE;
218         }
219
220 #ifdef GETTIMEOFDAY_TWO_ARGS
221       gettimeofday(&now, &tzp);
222 #else
223       gettimeofday(&now);
224 #endif
225
226       /* If we haven't already used up our alotted time, sleep to avoid
227          changing the colormap too fast. */
228       {
229         long diff = (((now.tv_sec - then.tv_sec) * 1000000) +
230                      now.tv_usec - then.tv_usec);
231         then.tv_sec = now.tv_sec;
232         then.tv_usec = now.tv_usec;
233         if (usecs_per_step > diff)
234           usleep (usecs_per_step - diff);
235       }
236     }
237
238  DONE:
239
240   if (orig_colors)    free (orig_colors);
241   if (current_colors) free (current_colors);
242
243   /* Now put the original maps back, if we want to end up with them.
244    */
245   if (!out_p)
246     {
247       for (i = 0; i < nscreens; i++)
248         {
249           Colormap cmap = (cmaps ? cmaps[i] : 0);
250           if (!cmap) cmap = DefaultColormap(dpy, i);
251           XInstallColormap (dpy, cmap);
252         }
253
254       /* We've faded to the default cmaps, so we don't need the black maps
255          on stage any more.  (We can't uninstall these maps yet if we've
256          faded to black, because that would lead to flicker between when
257          we uninstalled them and when the caller raised its black window.)
258        */
259       for (i = 0; i < ncmaps; i++)
260         if (fade_cmaps[i])
261           {
262             XFreeColormap(dpy, fade_cmaps[i]);
263             fade_cmaps[i] = 0;
264           }
265       free(fade_cmaps);
266       fade_cmaps = 0;
267     }
268 }
269
270 \f
271 #if 0
272 #include "screenhack.h"
273
274 char *progclass = "foo";
275 char *defaults [] = {
276   0
277 };
278
279 XrmOptionDescRec options [] = {0};
280 int options_size = 0;
281
282 void
283 screenhack (dpy, w)
284      Display *dpy;
285      Window w;
286 {
287   int seconds = 3;
288   int ticks = 20;
289   int delay = 1;
290
291   while (1)
292     {
293       XSync (dpy, False);
294
295       fprintf(stderr,"out..."); fflush(stderr);
296       fade_screens (dpy, 0, seconds, ticks, True);
297       fprintf(stderr, "done.\n"); fflush(stderr);
298
299       if (delay) sleep (delay);
300
301       fprintf(stderr,"in..."); fflush(stderr);
302       fade_screens (dpy, 0, seconds, ticks, False);
303       fprintf(stderr, "done.\n"); fflush(stderr);
304
305       if (delay) sleep (delay);
306     }
307 }
308
309 #endif