http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.00.tar.gz
[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   /* cope with small windows -- those constants assume big windows. */
109   {
110     int div = 1000000 / ylim;
111     if (div > 1)
112       p->fuse /= div;
113   }
114
115   if (! mono_p)
116     {
117       hsv_to_rgb (random () % 360, 1.0, 1.0,
118                   &p->color.red, &p->color.green, &p->color.blue);
119       p->color.flags = DoRed | DoGreen | DoBlue;
120       if (!XAllocColor (dpy, cmap, &p->color))
121         {
122           p->color.pixel = WhitePixel (dpy, DefaultScreen (dpy));
123           p->color.red = p->color.green = p->color.blue = 0xFFFF;
124         }
125     }
126 }
127
128 static struct projectile *
129 shrapnel (struct projectile *parent, Display *dpy, Colormap cmap)
130 {
131   struct projectile *p = get_projectile ();
132   int v;
133   if (! p) return 0;
134   p->x = parent->x;
135   p->y = parent->y;
136   v=random () % PI_2000;
137   p->dx =(sin_cache[v]) + parent->dx;
138   p->dy =(cos_cache[v]) + parent->dx;
139   p->decay = (random () % 50) - 60;
140   p->size = (parent->size * 2) / 3;
141   p->fuse = 0;
142   p->primary = False;
143
144   p->color = parent->color;
145   if (! mono_p)
146     XAllocColor (dpy, cmap, &p->color); /* dup the lock */
147   
148   return p;
149 }
150
151 static GC draw_gc, erase_gc;
152 static unsigned int default_fg_pixel;
153
154 static int how_many, frequency, scatter;
155
156 static Colormap
157 init_pyro (Display *dpy, Window window)
158 {
159   int i;
160   Colormap cmap;
161   XGCValues gcv;
162   XWindowAttributes xgwa;
163   XGetWindowAttributes (dpy, window, &xgwa);
164   cmap = xgwa.colormap;
165   how_many = get_integer_resource ("count", "Integer");
166   frequency = get_integer_resource ("frequency", "Integer");
167   scatter = get_integer_resource ("scatter", "Integer");
168   if (how_many <= 0) how_many = 100;
169   if (frequency <= 0) frequency = 30;
170   if (scatter <= 0) scatter = 20;
171   projectiles = 0;
172   free_projectiles = 0;
173   projectiles = (struct projectile *)
174     calloc (how_many, sizeof (*projectiles));
175   sorted_projectiles = (struct projectile **)
176     calloc (how_many, sizeof (*sorted_projectiles));
177   for (i = 0; i < how_many; i++)
178     free_projectile (&projectiles [i]);
179   for (i = 0; i < how_many; i++)
180     sorted_projectiles[i] = &projectiles[i];
181   gcv.foreground = default_fg_pixel =
182     get_pixel_resource ("foreground", "Foreground", dpy, cmap);
183   draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
184   gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
185   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
186   XClearWindow (dpy, window);
187   cache();  
188   return cmap;
189 }
190
191
192 static int
193 projectile_pixel_sorter (const void *a, const void *b)
194 {
195   struct projectile *pa = *(struct projectile **) a;
196   struct projectile *pb = *(struct projectile **) b;
197   if (pa->color.pixel == pb->color.pixel) return 0;
198   else if (pa->color.pixel < pb->color.pixel) return -1;
199   else return 1;
200 }
201
202 static void
203 sort_by_pixel (int length)
204 {
205   qsort ((void *) sorted_projectiles,
206          length,
207          sizeof(*sorted_projectiles),
208          projectile_pixel_sorter);
209 }
210
211
212 static void
213 pyro (Display *dpy, Window window, Colormap cmap)
214 {
215   XWindowAttributes xgwa;
216   static int xlim, ylim, real_xlim, real_ylim;
217   int g = 100;
218   int resort = 0;
219   int i;
220   
221   for (i = 0; i < how_many; i++)
222     {
223       struct projectile *p = sorted_projectiles [i];
224       int old_x, old_y, old_size;
225       int size, x, y;
226       if (p->dead) continue;
227       old_x = p->x >> 10;
228       old_y = p->y >> 10;
229       old_size = p->size >> 10;
230       size = (p->size += p->decay) >> 10;
231       x = (p->x += p->dx) >> 10;
232       y = (p->y += p->dy) >> 10;
233       p->dy += (p->size >> 6);
234       if (p->primary) p->fuse--;
235
236       /* erase old one */
237       if (old_size > 0)
238         {
239           if (old_size == 1)
240             XDrawPoint (dpy, window, erase_gc, old_x, old_y);
241           else
242             XFillRectangle (dpy, window, erase_gc, old_x, old_y,
243                             old_size, old_size);
244         }
245
246       if ((p->primary ? (p->fuse > 0) : (p->size > 0)) &&
247           x < real_xlim &&
248           y < real_ylim &&
249           x > 0 &&
250           y > 0)
251         {
252           if (size > 0)
253             {
254               static unsigned long last_pixel = ~0;
255               unsigned long pixel;
256
257               if (mono_p || p->primary)
258                 pixel = default_fg_pixel;
259               else
260                 pixel = p->color.pixel;
261
262               if (pixel != last_pixel)
263                 {
264                   last_pixel = pixel;
265                   XSetForeground (dpy, draw_gc, pixel);
266                 }
267
268               if (size == 1)
269                 XDrawPoint (dpy, window, draw_gc, x, y);
270               else if (size < 4)
271                 XFillRectangle (dpy, window, draw_gc, x, y, size, size);
272               else
273                 XFillArc (dpy, window, draw_gc, x, y, size, size, 0, 360*64);
274             }
275         }
276       else
277         {
278           free_projectile (p);
279           if (! mono_p)
280             if (p->color.pixel != WhitePixel (dpy, DefaultScreen (dpy)))
281               XFreeColors (dpy, cmap, &p->color.pixel, 1, 0);
282         }
283
284       if (p->primary && p->fuse <= 0)
285         {
286           int j = (random () % scatter) + (scatter/2);
287           while (j--)
288             shrapnel (p, dpy, cmap);
289           resort = 1;
290         }
291     }
292
293   if ((random () % frequency) == 0)
294     {
295       XGetWindowAttributes (dpy, window, &xgwa);
296       real_xlim = xgwa.width;
297       real_ylim = xgwa.height;
298       xlim = real_xlim * 1000;
299       ylim = real_ylim * 1000;
300       launch (xlim, ylim, g, dpy, cmap);
301       resort = 1;
302     }
303
304   /* being sorted lets us avoid changing the GC's foreground color as often. */
305   if (resort)
306     sort_by_pixel (how_many);
307 }
308
309 \f
310 char *progclass = "Pyro";
311
312 char *defaults [] = {
313   ".background: black",
314   ".foreground: white",
315   "*count:      600",
316   "*delay:      5000",
317   "*frequency:  30",
318   "*scatter:    100",
319   "*geometry:   800x500",
320   0
321 };
322
323 XrmOptionDescRec options [] = {
324   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
325   { "-count",           ".count",       XrmoptionSepArg, 0 },
326   { "-frequency",       ".frequency",   XrmoptionSepArg, 0 },
327   { "-scatter",         ".scatter",     XrmoptionSepArg, 0 },
328   { 0, 0, 0, 0 }
329 };
330
331 void
332 screenhack (Display *dpy, Window window)
333 {
334   Colormap cmap = init_pyro (dpy, window);
335   int delay = get_integer_resource ("delay", "Integer");
336
337   while (1)
338     {
339       pyro (dpy, window, cmap);
340
341       XSync (dpy, False);
342       screenhack_handle_events (dpy);
343       usleep (delay);
344     }
345 }