4cc3ce2a7b553a8efee03ccc011902fd73593af5
[oweals/cde.git] / cde / programs / dtscreen / pyro.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: pyro.c /main/3 1995/11/02 16:07:59 rswiston $ */
24 /*
25  */
26 /*                                                                      *
27  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
28  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
29  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
30  * (c) Copyright 1993, 1994 Novell, Inc.                                *
31  */
32 /*-
33  * pyro.c - Fireworks for dtscreen, the X Window System lockscreen.
34  *
35  * Copyright (c) 1991 by Patrick J. Naughton.
36  *
37  * See dtscreen.c for copying information.
38  *
39  * Revision History:
40  * 16-Mar-91: Written. (received from David Brooks, brooks@osf.org).
41  */
42
43 /* The physics of the rockets is a little bogus, but it looks OK.  Each is
44  * given an initial velocity impetus.  They decelerate slightly (gravity
45  * overcomes the rocket's impulse) and explode as the rocket's main fuse
46  * gives out (we could add a ballistic stage, maybe).  The individual
47  * stars fan out from the rocket, and they decelerate less quickly.
48  * That's called bouyancy, but really it's again a visual preference.
49  */
50
51 #include "dtscreen.h"
52 #include <math.h>
53 #include <stdlib.h>
54
55 #define TWOPI 6.2831853
56
57 /* Define this >1 to get small rectangles instead of points */
58 #ifndef STARSIZE
59 #define STARSIZE 2
60 #endif
61
62 #define SILENT 0
63 #define REDGLARE 1
64 #define BURSTINGINAIR 2
65
66 #define CLOUD 0
67 #define DOUBLECLOUD 1
68 /* Clearly other types and other fascinating visual effects could be added...*/
69
70 /* P_xxx parameters represent the reciprocal of the probability... */
71 #define P_IGNITE 5000           /* ...of ignition per cycle */
72 #define P_DOUBLECLOUD 10        /* ...of an ignition being double */
73 #define P_MULTI 75              /* ...of an ignition being several @ once */
74 #define P_FUSILLADE 250         /* ...of an ignition starting a fusillade */
75
76 #define ROCKETW 2               /* Dimensions of rocket */
77 #define ROCKETH 4
78 #define XVELFACTOR 0.0025       /* Max horizontal velocity / screen width */
79 #define MINYVELFACTOR 0.016     /* Min vertical velocity / screen height */
80 #define MAXYVELFACTOR 0.018
81 #define GRAVFACTOR 0.0002       /* delta v / screen height */
82 #define MINFUSE 50              /* range of fuse lengths for rocket */
83 #define MAXFUSE 100
84
85 #define FUSILFACTOR 10          /* Generate fusillade by reducing P_IGNITE */
86 #define FUSILLEN 100            /* Length of fusillade, in ignitions */
87
88 #define SVELFACTOR 0.1          /* Max star velocity / yvel */
89 #define BOUYANCY 0.2            /* Reduction in grav deceleration for stars */
90 #define MAXSTARS 75             /* Number of stars issued from a shell */
91 #define MINSTARS 50
92 #define MINSFUSE 50             /* Range of fuse lengths for stars */
93 #define MAXSFUSE 100
94
95 #define INTRAND(min,max) (random()%((max+1)-(min))+(min))
96 #define FLOATRAND(min,max) ((min)+(random()/MAXRAND)*((max)-(min)))
97
98 static void ignite();
99 static void animate();
100 static void shootup();
101 static void burst();
102
103 typedef struct {
104     int         state;
105     int         shelltype;
106     int         color1, color2;
107     int         fuse;
108     float       xvel, yvel;
109     float       x, y;
110     int         nstars;
111 #if STARSIZE > 1
112     XRectangle  Xpoints[MAXSTARS];
113     XRectangle  Xpoints2[MAXSTARS];
114 #else
115     XPoint      Xpoints[MAXSTARS];
116     XPoint      Xpoints2[MAXSTARS];
117 #endif
118     float       sx[MAXSTARS], sy[MAXSTARS];     /* Distance from notional
119                                                  * center  */
120     float       sxvel[MAXSTARS], syvel[MAXSTARS];       /* Relative to notional
121                                                          * center */
122 }           rocket;
123
124 typedef struct {
125     Screen     *scr;
126     Colormap    cmap;
127     int         p_ignite;
128     unsigned long bgpixel;
129     unsigned long fgpixel;
130     unsigned long rockpixel;
131     GC          bgGC;
132     int         nflying;
133     int         fusilcount;
134     int         width, lmargin, rmargin, height;
135     float       minvelx, maxvelx;
136     float       minvely, maxvely;
137     float       maxsvel;
138     float       rockdecel, stardecel;
139     rocket     *rockq;
140 }           pyrostruct;
141
142 static int  orig_p_ignite;
143 static int  just_started = True;/* Greet the user right away */
144
145 void
146 initpyro(perwindow *pwin)
147 {
148     pyrostruct *pp;
149     rocket     *rp;
150     XWindowAttributes xwa;
151     XGCValues   xgcv;
152     int         rockn, starn, bsize;
153
154     if (pwin->data) free(pwin->data);
155     pwin->data = (pyrostruct *)malloc(sizeof(pyrostruct));
156     memset(pwin->data, '\0', sizeof(pyrostruct));
157     pp = (pyrostruct *)pwin->data;
158     XGetWindowAttributes(dsp, pwin->w, &xwa);
159
160     orig_p_ignite = P_IGNITE / batchcount;
161     if (orig_p_ignite <= 0)
162         orig_p_ignite = 1;
163     pp->p_ignite = orig_p_ignite;
164
165     pp->rockq = (rocket *) malloc(batchcount * sizeof(rocket));
166
167     pp->nflying = pp->fusilcount = 0;
168
169     bsize = (xwa.height <= 64) ? 1 : STARSIZE;
170     for (rockn = 0, rp = pp->rockq; rockn < batchcount; rockn++, rp++) {
171         rp->state = SILENT;
172 #if STARSIZE > 1
173         for (starn = 0; starn < MAXSTARS; starn++) {
174             rp->Xpoints[starn].width = rp->Xpoints[starn].height =
175                 rp->Xpoints2[starn].width = rp->Xpoints2[starn].height = bsize;
176         }
177 #endif
178     }
179
180     pp->width = xwa.width;
181     pp->lmargin = xwa.width / 16;
182     pp->rmargin = xwa.width - pp->lmargin;
183     pp->height = xwa.height;
184     pp->scr = pwin->perscreen->screen;
185     pp->cmap = DefaultColormapOfScreen(pp->scr);
186
187     pp->fgpixel = WhitePixelOfScreen(pp->scr);
188     pp->bgpixel = BlackPixelOfScreen(pp->scr);
189     if (!mono && pwin->perscreen->npixels > 3)
190         pp->rockpixel = pwin->perscreen->pixels[3];/* Just the right shade of
191                                                  * orange */
192     else
193         pp->rockpixel = pp->fgpixel;
194
195     xgcv.foreground = pp->bgpixel;
196     pp->bgGC = XCreateGC(dsp, pwin->w, GCForeground, &xgcv);
197     
198 /* Geometry-dependent physical data: */
199     pp->maxvelx = (float) (xwa.width) * XVELFACTOR;
200     pp->minvelx = -pp->maxvelx;
201     pp->minvely = -(float) (xwa.height) * MINYVELFACTOR;
202     pp->maxvely = -(float) (xwa.height) * MAXYVELFACTOR;
203     pp->maxsvel = pp->minvely * SVELFACTOR;
204     pp->rockdecel = (float) (pp->height) * GRAVFACTOR;
205     pp->stardecel = pp->rockdecel * BOUYANCY;
206
207     XFillRectangle(dsp, pwin->w, pp->bgGC, 0, 0, xwa.width, xwa.height);
208 }
209
210 /*ARGSUSED*/
211 void
212 drawpyro(perwindow *pwin)
213 {
214     pyrostruct *pp;
215     rocket     *rp;
216     int         rockn;
217
218     pp = (pyrostruct *)pwin->data;
219     if (just_started || (random() % pp->p_ignite == 0)) {
220         just_started = False;
221         if (random() % P_FUSILLADE == 0) {
222             pp->p_ignite = orig_p_ignite / FUSILFACTOR;
223             pp->fusilcount = INTRAND(FUSILLEN * 9 / 10, FUSILLEN * 11 / 10);
224         }
225         ignite(pwin, pp);
226         if (pp->fusilcount > 0) {
227             if (--pp->fusilcount == 0)
228                 pp->p_ignite = orig_p_ignite;
229         }
230     }
231     for (rockn = pp->nflying, rp = pp->rockq; rockn > 0; rp++) {
232         if (rp->state != SILENT) {
233             animate(pwin, pp, rp);
234             rockn--;
235         }
236     }
237 }
238
239 static void
240 ignite(perwindow *pwin, pyrostruct *pp)
241 {
242     rocket     *rp;
243     int         multi, shelltype, nstars, fuse, npix, pix, color1, color2;
244     float       xvel, yvel, x;
245
246     x = random() % pp->width;
247     xvel = FLOATRAND(-pp->maxvelx, pp->maxvelx);
248 /* All this to stop too many rockets going offscreen: */
249     if ((x < pp->lmargin && xvel < 0.0) || (x > pp->rmargin && xvel > 0.0))
250         xvel = -xvel;
251     yvel = FLOATRAND(pp->minvely, pp->maxvely);
252     fuse = INTRAND(MINFUSE, MAXFUSE);
253     nstars = INTRAND(MINSTARS, MAXSTARS);
254     if (!mono && (npix = pwin->perscreen->npixels) > 2) {
255         color1 = pwin->perscreen->pixels[pix = random() % npix];
256         color2 = pwin->perscreen->pixels[(pix + (npix / 2)) % npix];
257     } else {
258         color1 = color2 = WhitePixelOfScreen(pwin->perscreen->screen);
259     }
260
261     multi = 1;
262     if (random() % P_DOUBLECLOUD == 0)
263         shelltype = DOUBLECLOUD;
264     else {
265         shelltype = CLOUD;
266         if (random() % P_MULTI == 0)
267             multi = INTRAND(5, 15);
268     }
269
270     rp = pp->rockq;
271     while (multi--) {
272         if (pp->nflying >= batchcount)
273             return;
274         while (rp->state != SILENT)
275             rp++;
276         pp->nflying++;
277         rp->shelltype = shelltype;
278         rp->state = REDGLARE;
279         rp->color1 = color1;
280         rp->color2 = color2;
281         rp->xvel = xvel;
282         rp->yvel = FLOATRAND(yvel * 0.97, yvel * 1.03);
283         rp->fuse = INTRAND((fuse * 90) / 100, (fuse * 110) / 100);
284         rp->x = x + FLOATRAND(multi * 7.6, multi * 8.4);
285         rp->y = pp->height - 1;
286         rp->nstars = nstars;
287     }
288 }
289
290 static void
291 animate(perwindow *pwin, pyrostruct *pp, rocket *rp)
292 {
293     int         starn;
294     float       r, theta;
295
296     if (rp->state == REDGLARE) {
297         shootup(pwin, pp, rp);
298
299 /* Handle setup for explosion */
300         if (rp->state == BURSTINGINAIR) {
301             for (starn = 0; starn < rp->nstars; starn++) {
302                 rp->sx[starn] = rp->sy[starn] = 0.0;
303                 rp->Xpoints[starn].x = (int) rp->x;
304                 rp->Xpoints[starn].y = (int) rp->y;
305                 if (rp->shelltype == DOUBLECLOUD) {
306                     rp->Xpoints2[starn].x = (int) rp->x;
307                     rp->Xpoints2[starn].y = (int) rp->y;
308                 }
309 /* This isn't accurate solid geometry, but it looks OK. */
310
311                 r = FLOATRAND(0.0, pp->maxsvel);
312                 theta = FLOATRAND(0.0, TWOPI);
313                 rp->sxvel[starn] = r * cos(theta);
314                 rp->syvel[starn] = r * sin(theta);
315             }
316             rp->fuse = INTRAND(MINSFUSE, MAXSFUSE);
317         }
318     }
319     if (rp->state == BURSTINGINAIR) {
320         burst(pwin, pp, rp);
321     }
322 }
323
324 static void
325 shootup(perwindow *pwin, pyrostruct *pp, rocket *rp)
326 {
327     XFillRectangle(dsp, pwin->w, pp->bgGC, (int) (rp->x), (int) (rp->y),
328                    ROCKETW, ROCKETH + 3);
329
330     if (rp->fuse-- <= 0) {
331         rp->state = BURSTINGINAIR;
332         return;
333     }
334     rp->x += rp->xvel;
335     rp->y += rp->yvel;
336     rp->yvel += pp->rockdecel;
337     XSetForeground(dsp, pwin->gc, pp->rockpixel);
338     XFillRectangle(dsp, pwin->w, pwin->gc, (int) (rp->x), (int) (rp->y),
339                    ROCKETW, ROCKETH + random() % 4);
340 }
341
342 static void
343 burst(perwindow *pwin, pyrostruct *pp, rocket *rp)
344 {
345     register int starn;
346     register int nstars, stype;
347     register float rx, ry, sd;  /* Help compiler optimize :-) */
348     register float sx, sy;
349
350     nstars = rp->nstars;
351     stype = rp->shelltype;
352
353 #if STARSIZE > 1
354     XFillRectangles(dsp, pwin->w, pp->bgGC, rp->Xpoints, nstars);
355     if (stype == DOUBLECLOUD)
356         XFillRectangles(dsp, pwin->w, pp->bgGC, rp->Xpoints2, nstars);
357 #else
358     XDrawPoints(dsp, pwin->w, pp->bgGC, rp->Xpoints, nstars, CoordModeOrigin);
359     if (stype == DOUBLECLOUD)
360         XDrawPoints(dsp, pwin->w, pp->bgGC, rp->Xpoints2, nstars, CoordModeOrigin);
361 #endif
362
363     if (rp->fuse-- <= 0) {
364         rp->state = SILENT;
365         pp->nflying--;
366         return;
367     }
368 /* Stagger the stars' decay */
369     if (rp->fuse <= 7) {
370         if ((rp->nstars = nstars = nstars * 90 / 100) == 0)
371             return;
372     }
373     rx = rp->x;
374     ry = rp->y;
375     sd = pp->stardecel;
376     for (starn = 0; starn < nstars; starn++) {
377         sx = rp->sx[starn] += rp->sxvel[starn];
378         sy = rp->sy[starn] += rp->syvel[starn];
379         rp->syvel[starn] += sd;
380         rp->Xpoints[starn].x = (int) (rx + sx);
381         rp->Xpoints[starn].y = (int) (ry + sy);
382         if (stype == DOUBLECLOUD) {
383             rp->Xpoints2[starn].x = (int) (rx + 1.7 * sx);
384             rp->Xpoints2[starn].y = (int) (ry + 1.7 * sy);
385         }
386     }
387     rp->x = rx + rp->xvel;
388     rp->y = ry + rp->yvel;
389     rp->yvel += sd;
390
391     XSetForeground(dsp, pwin->gc, rp->color1);
392 #if STARSIZE > 1
393     XFillRectangles(dsp, pwin->w, pwin->gc, rp->Xpoints, nstars);
394     if (stype == DOUBLECLOUD) {
395         XSetForeground(dsp, pwin->gc, rp->color2);
396         XFillRectangles(dsp, pwin->w, pwin->gc, rp->Xpoints2, nstars);
397     }
398 #else
399     XDrawPoints(dsp, pwin->w, pwin->gc, rp->Xpoints, nstars, CoordModeOrigin);
400     if (stype == DOUBLECLOUD) {
401         XSetForeground(dsp, pwin->gc, rp->color2);
402         XDrawPoints(dsp, pwin->w, pwin->gc, rp->Xpoints2, nstars,
403                     CoordModeOrigin);
404     }
405 #endif
406 }