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