ftp://updates.redhat.com/enterprise/2.1AS/en/os/SRPMS/xscreensaver-3.33-4.rhel21...
[xscreensaver] / hacks / pyro.c
1 /* xscreensaver, Copyright (c) 1992, 1994, 1996, 1998, 2001
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Draw some fireworks.  Inspired from TI Explorer Lisp code by 
14    John S. Pezaris <pz@hx.lcs.mit.edu>
15  */
16
17 #include <math.h>
18 #include "screenhack.h"
19
20 struct projectile {
21   int x, y;     /* position */
22   int dx, dy;   /* velocity */
23   int decay;
24   int size;
25   int fuse;
26   Bool primary;
27   Bool dead;
28   XColor color;
29   struct projectile *next_free;
30 };
31
32 static struct projectile *projectiles, *free_projectiles;
33 static struct projectile **sorted_projectiles;
34
35
36 /* Slightly whacked, for better explosions
37  */
38 #define PI_2000 6284
39
40 static int sin_cache[PI_2000];
41 static int cos_cache[PI_2000];
42
43 static void
44 cache(void)
45 {               /*needs to be run once. Could easily be */
46   int i;        /*reimplemented to run and cache at compile-time,*/
47   double dA;    /*saving on init_pyro time */
48   for (i=0; i<PI_2000; i++)
49     {
50       dA=sin(((double) (random() % (PI_2000/2)))/1000.0);
51       /*Emulation of spherical distribution*/
52       dA+=asin(frand(1.0))/M_PI_2*0.1;
53       /*Approximating the integration of the binominal, for
54         well-distributed randomness*/
55       cos_cache[i]=(int) (cos(((double)i)/1000.0)*dA*2500.0);
56       sin_cache[i]=(int) (sin(((double)i)/1000.0)*dA*2500.0);
57     }
58 }
59
60
61 static struct projectile *
62 get_projectile (void)
63 {
64   struct projectile *p;
65   if (free_projectiles)
66     {
67       p = free_projectiles;
68       free_projectiles = p->next_free;
69       p->next_free = 0;
70       p->dead = False;
71       return p;
72     }
73   else
74     return 0;
75 }
76
77 static void
78 free_projectile (struct projectile *p)
79 {
80   p->next_free = free_projectiles;
81   free_projectiles = p;
82   p->dead = True;
83 }
84
85 static void
86 launch (int xlim, int ylim, int g,
87         Display *dpy, Colormap cmap)
88 {
89   struct projectile *p = get_projectile ();
90   int x, dx, xxx;
91   if (! p) return;
92
93   do {
94     x = (random () % xlim);
95     dx = 30000 - (random () % 60000);
96     xxx = x + (dx * 200);
97   } while (xxx <= 0 || xxx >= xlim);
98
99   p->x = x;
100   p->y = ylim;
101   p->dx = dx;
102   p->size = 8000;
103   p->decay = 0;
104   p->dy = (random () % 4000) - 13000;
105   p->fuse = ((((random () % 500) + 500) * abs (p->dy / g)) / 1000);
106   p->primary = True;
107
108   if (! mono_p)
109     {
110       hsv_to_rgb (random () % 360, 1.0, 1.0,
111                   &p->color.red, &p->color.green, &p->color.blue);
112       p->color.flags = DoRed | DoGreen | DoBlue;
113       if (!XAllocColor (dpy, cmap, &p->color))
114         {
115           p->color.pixel = WhitePixel (dpy, DefaultScreen (dpy));
116           p->color.red = p->color.green = p->color.blue = 0xFFFF;
117         }
118     }
119 }
120
121 static struct projectile *
122 shrapnel (struct projectile *parent, Display *dpy, Colormap cmap)
123 {
124   struct projectile *p = get_projectile ();
125   int v;
126   if (! p) return 0;
127   p->x = parent->x;
128   p->y = parent->y;
129   v=random () % PI_2000;
130   p->dx =(sin_cache[v]) + parent->dx;
131   p->dy =(cos_cache[v]) + parent->dx;
132   p->decay = (random () % 50) - 60;
133   p->size = (parent->size * 2) / 3;
134   p->fuse = 0;
135   p->primary = False;
136
137   p->color = parent->color;
138   if (! mono_p)
139     XAllocColor (dpy, cmap, &p->color); /* dup the lock */
140   
141   return p;
142 }
143
144 static GC draw_gc, erase_gc;
145 static unsigned int default_fg_pixel;
146
147 static int how_many, frequency, scatter;
148
149 static Colormap
150 init_pyro (Display *dpy, Window window)
151 {
152   int i;
153   Colormap cmap;
154   XGCValues gcv;
155   XWindowAttributes xgwa;
156   XGetWindowAttributes (dpy, window, &xgwa);
157   cmap = xgwa.colormap;
158   how_many = get_integer_resource ("count", "Integer");
159   frequency = get_integer_resource ("frequency", "Integer");
160   scatter = get_integer_resource ("scatter", "Integer");
161   if (how_many <= 0) how_many = 100;
162   if (frequency <= 0) frequency = 30;
163   if (scatter <= 0) scatter = 20;
164   projectiles = 0;
165   free_projectiles = 0;
166   projectiles = (struct projectile *)
167     calloc (how_many, sizeof (*projectiles));
168   sorted_projectiles = (struct projectile **)
169     calloc (how_many, sizeof (*sorted_projectiles));
170   for (i = 0; i < how_many; i++)
171     free_projectile (&projectiles [i]);
172   for (i = 0; i < how_many; i++)
173     sorted_projectiles[i] = &projectiles[i];
174   gcv.foreground = default_fg_pixel =
175     get_pixel_resource ("foreground", "Foreground", dpy, cmap);
176   draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
177   gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
178   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
179   XClearWindow (dpy, window);
180   cache();  
181   return cmap;
182 }
183
184
185 static int
186 projectile_pixel_sorter (const void *a, const void *b)
187 {
188   struct projectile *pa = *(struct projectile **) a;
189   struct projectile *pb = *(struct projectile **) b;
190   if (pa->color.pixel == pb->color.pixel) return 0;
191   else if (pa->color.pixel < pb->color.pixel) return -1;
192   else return 1;
193 }
194
195 static void
196 sort_by_pixel (int length)
197 {
198   qsort ((void *) sorted_projectiles,
199          length,
200          sizeof(*sorted_projectiles),
201          projectile_pixel_sorter);
202 }
203
204
205 static void
206 pyro (Display *dpy, Window window, Colormap cmap)
207 {
208   XWindowAttributes xgwa;
209   static int xlim, ylim, real_xlim, real_ylim;
210   int g = 100;
211   int resort = 0;
212   int i;
213   
214   for (i = 0; i < how_many; i++)
215     {
216       struct projectile *p = sorted_projectiles [i];
217       int old_x, old_y, old_size;
218       int size, x, y;
219       if (p->dead) continue;
220       old_x = p->x >> 10;
221       old_y = p->y >> 10;
222       old_size = p->size >> 10;
223       size = (p->size += p->decay) >> 10;
224       x = (p->x += p->dx) >> 10;
225       y = (p->y += p->dy) >> 10;
226       p->dy += (p->size >> 6);
227       if (p->primary) p->fuse--;
228
229       /* erase old one */
230       if (old_size > 0)
231         {
232           if (old_size == 1)
233             XDrawPoint (dpy, window, erase_gc, old_x, old_y);
234           else
235             XFillRectangle (dpy, window, erase_gc, old_x, old_y,
236                             old_size, old_size);
237         }
238
239       if ((p->primary ? (p->fuse > 0) : (p->size > 0)) &&
240           x < real_xlim &&
241           y < real_ylim &&
242           x > 0 &&
243           y > 0)
244         {
245           if (size > 0)
246             {
247               static unsigned long last_pixel = ~0;
248               unsigned long pixel;
249
250               if (mono_p || p->primary)
251                 pixel = default_fg_pixel;
252               else
253                 pixel = p->color.pixel;
254
255               if (pixel != last_pixel)
256                 {
257                   last_pixel = pixel;
258                   XSetForeground (dpy, draw_gc, pixel);
259                 }
260
261               if (size == 1)
262                 XDrawPoint (dpy, window, draw_gc, x, y);
263               else if (size < 4)
264                 XFillRectangle (dpy, window, draw_gc, x, y, size, size);
265               else
266                 XFillArc (dpy, window, draw_gc, x, y, size, size, 0, 360*64);
267             }
268         }
269       else
270         {
271           free_projectile (p);
272           if (! mono_p)
273             if (p->color.pixel != WhitePixel (dpy, DefaultScreen (dpy)))
274               XFreeColors (dpy, cmap, &p->color.pixel, 1, 0);
275         }
276
277       if (p->primary && p->fuse <= 0)
278         {
279           int j = (random () % scatter) + (scatter/2);
280           while (j--)
281             shrapnel (p, dpy, cmap);
282           resort = 1;
283         }
284     }
285
286   if ((random () % frequency) == 0)
287     {
288       XGetWindowAttributes (dpy, window, &xgwa);
289       real_xlim = xgwa.width;
290       real_ylim = xgwa.height;
291       xlim = real_xlim * 1000;
292       ylim = real_ylim * 1000;
293       launch (xlim, ylim, g, dpy, cmap);
294       resort = 1;
295     }
296
297   /* being sorted lets us avoid changing the GC's foreground color as often. */
298   if (resort)
299     sort_by_pixel (how_many);
300 }
301
302 \f
303 char *progclass = "Pyro";
304
305 char *defaults [] = {
306   ".background: black",
307   ".foreground: white",
308   "*count:      600",
309   "*delay:      5000",
310   "*frequency:  30",
311   "*scatter:    100",
312   "*geometry:   800x500",
313   0
314 };
315
316 XrmOptionDescRec options [] = {
317   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
318   { "-count",           ".count",       XrmoptionSepArg, 0 },
319   { "-frequency",       ".frequency",   XrmoptionSepArg, 0 },
320   { "-scatter",         ".scatter",     XrmoptionSepArg, 0 },
321   { 0, 0, 0, 0 }
322 };
323
324 void
325 screenhack (Display *dpy, Window window)
326 {
327   Colormap cmap = init_pyro (dpy, window);
328   int delay = get_integer_resource ("delay", "Integer");
329
330   while (1)
331     {
332       pyro (dpy, window, cmap);
333
334       XSync (dpy, False);
335       screenhack_handle_events (dpy);
336       usleep (delay);
337     }
338 }