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