http://ftp.x.org/contrib/applications/xscreensaver-3.07.tar.gz
[xscreensaver] / hacks / pyro.c
1 /* xscreensaver, Copyright (c) 1992, 1994, 1996, 1998
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 "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 static struct projectile *projectiles, *free_projectiles;
32
33 static struct projectile *
34 get_projectile (void)
35 {
36   struct projectile *p;
37   if (free_projectiles)
38     {
39       p = free_projectiles;
40       free_projectiles = p->next_free;
41       p->next_free = 0;
42       p->dead = False;
43       return p;
44     }
45   else
46     return 0;
47 }
48
49 static void
50 free_projectile (struct projectile *p)
51 {
52   p->next_free = free_projectiles;
53   free_projectiles = p;
54   p->dead = True;
55 }
56
57 static void
58 launch (int xlim, int ylim, int g,
59         Display *dpy, Colormap cmap)
60 {
61   struct projectile *p = get_projectile ();
62   int x, dx, xxx;
63   if (! p) return;
64
65   do {
66     x = (random () % xlim);
67     dx = 30000 - (random () % 60000);
68     xxx = x + (dx * 200);
69   } while (xxx <= 0 || xxx >= xlim);
70
71   p->x = x;
72   p->y = ylim;
73   p->dx = dx;
74   p->size = 8000;
75   p->decay = 0;
76   p->dy = (random () % 4000) - 13000;
77   p->fuse = ((((random () % 500) + 500) * abs (p->dy / g)) / 1000);
78   p->primary = True;
79
80   if (! mono_p)
81     {
82       hsv_to_rgb (random () % 360, 1.0, 1.0,
83                   &p->color.red, &p->color.green, &p->color.blue);
84       p->color.flags = DoRed | DoGreen | DoBlue;
85       if (!XAllocColor (dpy, cmap, &p->color))
86         {
87           p->color.pixel = WhitePixel (dpy, DefaultScreen (dpy));
88           p->color.red = p->color.green = p->color.blue = 0xFFFF;
89         }
90     }
91 }
92
93 static struct projectile *
94 shrapnel (struct projectile *parent, Display *dpy, Colormap cmap)
95 {
96   struct projectile *p = get_projectile ();
97   if (! p) return 0;
98   p->x = parent->x;
99   p->y = parent->y;
100   p->dx = (random () % 5000) - 2500 + parent->dx;
101   p->dy = (random () % 5000) - 2500 + parent->dy;
102   p->decay = (random () % 50) - 60;
103   p->size = (parent->size * 2) / 3;
104   p->fuse = 0;
105   p->primary = False;
106
107   p->color = parent->color;
108   if (! mono_p)
109     XAllocColor (dpy, cmap, &p->color); /* dup the lock */
110   
111   return p;
112 }
113
114 static GC draw_gc, erase_gc;
115 static unsigned int default_fg_pixel;
116
117 static int how_many, frequency, scatter;
118
119 static Colormap
120 init_pyro (Display *dpy, Window window)
121 {
122   int i;
123   Colormap cmap;
124   XGCValues gcv;
125   XWindowAttributes xgwa;
126   XGetWindowAttributes (dpy, window, &xgwa);
127   cmap = xgwa.colormap;
128   how_many = get_integer_resource ("count", "Integer");
129   frequency = get_integer_resource ("frequency", "Integer");
130   scatter = get_integer_resource ("scatter", "Integer");
131   if (how_many <= 0) how_many = 100;
132   if (frequency <= 0) frequency = 30;
133   if (scatter <= 0) scatter = 20;
134   projectiles = 0;
135   free_projectiles = 0;
136   projectiles = (struct projectile *)
137     calloc (how_many, sizeof (struct projectile));
138   for (i = 0; i < how_many; i++)
139     free_projectile (&projectiles [i]);
140   gcv.foreground = default_fg_pixel =
141     get_pixel_resource ("foreground", "Foreground", dpy, cmap);
142   draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
143   gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
144   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
145   XClearWindow (dpy, window);
146   return cmap;
147 }
148
149 static void
150 pyro (Display *dpy, Window window, Colormap cmap)
151 {
152   XWindowAttributes xgwa;
153   static int xlim, ylim, real_xlim, real_ylim;
154   int g = 100;
155   int i;
156
157   if ((random () % frequency) == 0)
158     {
159       XGetWindowAttributes (dpy, window, &xgwa);
160       real_xlim = xgwa.width;
161       real_ylim = xgwa.height;
162       xlim = real_xlim * 1000;
163       ylim = real_ylim * 1000;
164       launch (xlim, ylim, g, dpy, cmap);
165     }
166
167   XSync (dpy, False);
168   screenhack_handle_events (dpy);
169   usleep (10000);
170
171   for (i = 0; i < how_many; i++)
172     {
173       struct projectile *p = &projectiles [i];
174       int old_x, old_y, old_size;
175       int size, x, y;
176       if (p->dead) continue;
177       old_x = p->x >> 10;
178       old_y = p->y >> 10;
179       old_size = p->size >> 10;
180       size = (p->size += p->decay) >> 10;
181       x = (p->x += p->dx) >> 10;
182       y = (p->y += p->dy) >> 10;
183       p->dy += (p->size >> 6);
184       if (p->primary) p->fuse--;
185
186       /* erase old one */
187       XFillRectangle (dpy, window, erase_gc, old_x, old_y,
188                       old_size, old_size);
189
190       if ((p->primary ? (p->fuse > 0) : (p->size > 0)) &&
191           x < real_xlim &&
192           y < real_ylim &&
193           x > 0 &&
194           y > 0)
195         {
196           if (mono_p || p->primary)
197             XSetForeground (dpy, draw_gc, default_fg_pixel);
198           else
199             XSetForeground (dpy, draw_gc, p->color.pixel);
200
201           if /*(p->primary)*/ (size > 2)
202             XFillArc (dpy, window, draw_gc, x, y, size, size, 0, 360*64);
203           else
204             XFillRectangle (dpy, window, draw_gc, x, y, size, size);
205         }
206       else
207         {
208           free_projectile (p);
209           if (! mono_p)
210             if (p->color.pixel != WhitePixel (dpy, DefaultScreen (dpy)))
211               XFreeColors (dpy, cmap, &p->color.pixel, 1, 0);
212         }
213
214       if (p->primary && p->fuse <= 0)
215         {
216           int j = (random () % scatter) + (scatter/2);
217           while (j--)
218             shrapnel (p, dpy, cmap);
219         }
220     }
221 }
222
223 \f
224 char *progclass = "Pyro";
225
226 char *defaults [] = {
227   ".background: black",
228   ".foreground: white",
229   "*count:      100",
230   "*frequency:  30",
231   "*scatter:    20",
232   "*geometry:   800x500",
233   0
234 };
235
236 XrmOptionDescRec options [] = {
237   { "-count",           ".count",       XrmoptionSepArg, 0 },
238   { "-frequency",       ".frequency",   XrmoptionSepArg, 0 },
239   { "-scatter",         ".scatter",     XrmoptionSepArg, 0 },
240   { 0, 0, 0, 0 }
241 };
242
243 void
244 screenhack (Display *dpy, Window window)
245 {
246   Colormap cmap = init_pyro (dpy, window);
247   while (1)
248     pyro (dpy, window, cmap);
249 }