4aa71da73800995b4fcd2a5398d2c2f65fa98546
[xscreensaver] / hacks / pyro.c
1 /* xscreensaver, Copyright (c) 1992, 1994, 1996
2  *  Jamie Zawinski <jwz@netscape.com>
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, True);
168   usleep (10000);
169
170   for (i = 0; i < how_many; i++)
171     {
172       struct projectile *p = &projectiles [i];
173       int old_x, old_y, old_size;
174       int size, x, y;
175       if (p->dead) continue;
176       old_x = p->x >> 10;
177       old_y = p->y >> 10;
178       old_size = p->size >> 10;
179       size = (p->size += p->decay) >> 10;
180       x = (p->x += p->dx) >> 10;
181       y = (p->y += p->dy) >> 10;
182       p->dy += (p->size >> 6);
183       if (p->primary) p->fuse--;
184
185       /* erase old one */
186       XFillRectangle (dpy, window, erase_gc, old_x, old_y,
187                       old_size, old_size);
188
189       if ((p->primary ? (p->fuse > 0) : (p->size > 0)) &&
190           x < real_xlim &&
191           y < real_ylim &&
192           x > 0 &&
193           y > 0)
194         {
195           if (mono_p || p->primary)
196             XSetForeground (dpy, draw_gc, default_fg_pixel);
197           else
198             XSetForeground (dpy, draw_gc, p->color.pixel);
199
200           if /*(p->primary)*/ (size > 2)
201             XFillArc (dpy, window, draw_gc, x, y, size, size, 0, 360*64);
202           else
203             XFillRectangle (dpy, window, draw_gc, x, y, size, size);
204         }
205       else
206         {
207           free_projectile (p);
208           if (! mono_p)
209             if (p->color.pixel != WhitePixel (dpy, DefaultScreen (dpy)))
210               XFreeColors (dpy, cmap, &p->color.pixel, 1, 0);
211         }
212
213       if (p->primary && p->fuse <= 0)
214         {
215           int j = (random () % scatter) + (scatter/2);
216           while (j--)
217             shrapnel (p, dpy, cmap);
218         }
219     }
220 }
221
222 \f
223 char *progclass = "Pyro";
224
225 char *defaults [] = {
226   "*background: black",
227   "*foreground: white",
228   "*count:      100",
229   "*frequency:  30",
230   "*scatter:    20",
231   "*geometry:   800x500",
232   0
233 };
234
235 XrmOptionDescRec options [] = {
236   { "-count",           ".count",       XrmoptionSepArg, 0 },
237   { "-frequency",       ".frequency",   XrmoptionSepArg, 0 },
238   { "-scatter",         ".scatter",     XrmoptionSepArg, 0 },
239   { 0, 0, 0, 0 }
240 };
241
242 void
243 screenhack (Display *dpy, Window window)
244 {
245   Colormap cmap = init_pyro (dpy, window);
246   while (1)
247     pyro (dpy, window, cmap);
248 }