7f1002e60812a07a8a154d976256875f1518f52a
[xscreensaver] / hacks / fireworkx.c
1 /*
2  * Fireworkx 1.3 - pyrotechnics simulation program
3  * Copyright (c) 1999-2004 Rony B Chandran <ronybc@asia.com>
4  *
5  * url: http://www.ronybc.8k.com
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation.  No representations are made about the suitability of this
12  * software for any purpose.  It is provided "as is" without express or 
13  * implied warranty.
14  *
15  * Additional programming: 
16  * ------------------------
17  * Support for different display color modes: 
18  * Jean-Pierre Demailly <Jean-Pierre.Demailly@ujf-grenoble.fr>
19  *
20  */
21
22 #include <math.h>
23 #include "screenhack.h"
24 #include <X11/Xutil.h>
25
26 #define FWXVERSION "1.3"
27
28 #define WIDTH 640
29 #define HEIGHT 480
30 #define SHELLCOUNT 3
31 #define PIXCOUNT 500                   /* 500     */
32 #define RNDLIFE0 250                   /* violent */
33 #define RNDLIFE1 1200                  /* 1200    */
34 #define MINLIFE0 50                    /* violent */
35 #define MINLIFE1 500                   /* 500     */
36 #define POWER 5                        /* 5       */
37 #define FTWEAK 12                      /* 12      */
38
39 static int depth;
40 static int bigendian;
41 static Bool light_on = True;
42 static Bool fade_on  = False;
43 static Bool shoot    = False;
44 static Bool verbose  = False;
45 static int delay     = 0;
46 static int fsc_width = 0;
47 static int fsc_height= 0;
48 static int rndlife = RNDLIFE1;
49 static int minlife = MINLIFE1;
50 static float light_fade = 0.99;
51 static unsigned char *palaka1=NULL;
52 static unsigned char *palaka2=NULL;
53 static XImage *xim=NULL;
54 static XColor *colors;
55 static int ncolors = 255;
56
57 typedef struct {
58   unsigned int burn;
59   float x;
60   float y;
61   float xv;
62   float yv;}firepix;
63
64 typedef struct {
65   unsigned int life;
66   unsigned int color;
67   unsigned int cx,cy;
68   unsigned int special;
69   unsigned int cshift;
70   unsigned int vgn,shy;
71   float air,lum;
72   firepix *fpix; }fireshell;
73
74 int seed = 2387776;
75 int rnd(int x)
76 {   /* xscreensaver */
77   if ((seed = seed % 44488 * 48271 - seed / 44488 * 3399) < 0)
78   seed += 2147483647;
79   return (seed-1) % x;
80 }
81
82 int explode(fireshell *fs)
83 {
84   float air,adg = 0.001;     /* gravity */
85   unsigned int n,c;
86   unsigned int h = fsc_height;
87   unsigned int w = fsc_width;
88   unsigned int *prgb;
89   unsigned char *palaka = palaka1;
90   firepix *fp = fs->fpix;
91   if(fs->vgn){
92     if(--fs->cy == fs->shy){  
93       fs->vgn = 0;
94       fs->lum = 20000;}    /* millions of jouls..! */
95     else{  
96       fs->lum = 50+(fs->cy - fs->shy)*2;
97       return(1);}}    
98   if(fs->cshift) --fs->cshift;
99   if((fs->cshift+1)%50==0) fs->color = ~fs->color;
100   c = fs->color;
101   air = fs->air;
102   fs->lum *= light_fade;
103   for(n=PIXCOUNT;n;n--){
104   if(fp->burn){ --fp->burn; 
105   if(fs->special){
106   fp->x += fp->xv = fp->xv * air + (float)(rnd(200)-100)/2000;
107   fp->y += fp->yv = fp->yv * air + (float)(rnd(200)-100)/2000 +adg; }
108   else{
109   fp->x += fp->xv = fp->xv * air + (float)(rnd(200)-100)/20000;
110   fp->y += fp->yv = fp->yv * air + adg; }
111   if(fp->y > h){
112   if(rnd(5)==3) {fp->yv *= -0.24; fp->y = h;}
113   else fp->burn=0;} /* touch muddy ground :) */
114   if(fp->x < w && fp->x > 0 && fp->y < h && fp->y > 0){
115      prgb = (unsigned int *)(palaka + ((int)fp->y * w + (int)fp->x)*4);
116     *prgb = c; }
117   } fp++;
118   } return(--(fs->life));
119 }
120
121 void recycle(fireshell *fs,int x,int y)
122 {
123   unsigned int n,pixlife;
124   firepix *fp = fs->fpix;
125   fs->vgn = shoot;
126   fs->shy = y;
127   fs->cx = x;
128   fs->cy = shoot ? fsc_height : y ;
129   fs->color = (rnd(155)+100) <<16 |
130               (rnd(155)+100) <<8  |
131                rnd(255);
132   fs->life = rnd(rndlife)+minlife;
133   fs->air  = 1-(float)(rnd(200))/10000;
134   fs->lum  = 20000;
135   fs->cshift  = !rnd(5) ? 120:0; 
136   fs->special = !rnd(10) ? 1:0; 
137   pixlife = rnd(fs->life)+fs->life/10+1;    /* ! */
138   for(n=0;n<PIXCOUNT;n++){
139   fp->burn = rnd(pixlife)+32;
140   fp->xv = POWER*(float)(rnd(20000)-10000)/10000;
141   fp->yv = sqrt(POWER*POWER - fp->xv * fp->xv) *
142                (float)(rnd(20000)-10000)/10000;
143   fp->x = x;
144   fp->y = y; 
145   fp++;             }
146 }
147
148 void blur_best(void)
149 {
150   unsigned int n;
151   unsigned int w = fsc_width;
152   unsigned int h = fsc_height;
153   unsigned char *pa, *pb, *pm;
154   pm = palaka1;
155   for(n=0;n<w*4;n++) pm[n]=0;    /* clean first line */
156   pm+=n;
157   h-=2; 
158   pa = pm-(w*4);
159   pb = pm+(w*4);
160   if(fade_on){
161   for(n=0;n<w*h*4;n++){
162   pm[n]=(pm[n-4] + pm[n] + pm[n+4] + 
163          pa[n-4] + pa[n] + pa[n+4] + 
164          pb[n-4] + pb[n] + pb[n+4] +
165          pm[n] + pm[n] + (pm[n]<<2))/ 16;}}
166   else{
167   for(n=0;n<w*h*4;n++){
168   pm[n]=(pm[n-4] + pm[n] + pm[n+4] + 
169          pa[n-4] + pa[n] + pa[n+4] + 
170          pb[n-4] + pb[n] + pb[n+4])/ 9;}}
171   pm+=n;
172   for(n=0;n<w*4;n++) pm[n]=0;    /* clean last line */
173 }
174
175 void light_2x2(fireshell *fss)
176 {
177   unsigned int l,t,n,x,y;
178   float s;
179   int w = fsc_width;
180   int h = fsc_height;
181   unsigned char *dim = palaka2;
182   unsigned char *sim = palaka1;
183   int nl = w*4;
184   fireshell *f;
185   for(y=0;y<h;y+=2){
186   for(x=0;x<w;x+=2){
187   f = fss; s = 0;
188   for(n=SHELLCOUNT;n;n--,f++){
189   s += f->lum / ( 1 + sqrt((f->cx - x)*(f->cx - x)+(f->cy - y)*(f->cy - y))); }
190   l = s;
191
192   t = l + sim[0];
193   dim[0] = (t > 255 ? 255 : t);  /* cmov's */
194   t = l + sim[1];
195   dim[1] = (t > 255 ? 255 : t);
196   t = l + sim[2];
197   dim[2] = (t > 255 ? 255 : t);
198
199   t = l + sim[4];
200   dim[4] = (t > 255 ? 255 : t);
201   t = l + sim[5];
202   dim[5] = (t > 255 ? 255 : t);
203   t = l + sim[6];
204   dim[6] = (t > 255 ? 255 : t);
205
206   t = l + sim[nl+0];
207   dim[nl+0] = (t > 255 ? 255 : t);
208   t = l + sim[nl+1];
209   dim[nl+1] = (t > 255 ? 255 : t);
210   t = l + sim[nl+2];
211   dim[nl+2] = (t > 255 ? 255 : t);
212
213   t = l + sim[nl+4];
214   dim[nl+4] = (t > 255 ? 255 : t);
215   t = l + sim[nl+5];
216   dim[nl+5] = (t > 255 ? 255 : t);
217   t = l + sim[nl+6];
218   dim[nl+6] = (t > 255 ? 255 : t);
219
220   sim += 8; dim += 8; } sim += nl; dim += nl;}
221 }
222
223 void resize(Display *display, Window win)
224 {
225   XWindowAttributes xwa;
226   XGetWindowAttributes (display, win, &xwa);
227   xwa.width  -= xwa.width % 4;
228   xwa.height -= xwa.height % 4;
229   if(xwa.height != fsc_height || xwa.width != fsc_width) {
230   fsc_width  = xwa.width;
231   fsc_height = xwa.height;
232   if (xim) {
233   if (xim->data==(char *)palaka2) xim->data=NULL;  
234   XDestroyImage(xim);
235   if (palaka2!=palaka1) free(palaka2);
236   free(palaka1); 
237   }
238   palaka1 = NULL;     
239   palaka2 = NULL; 
240   xim = XCreateImage(display, xwa.visual, xwa.depth, ZPixmap, 0, 0,
241                      fsc_width, fsc_height, 32, 0);
242   palaka1 = calloc(xim->height,xim->width*4);
243   if(light_on)
244   palaka2 = calloc(xim->height,xim->width*4);
245   else
246   palaka2 = palaka1;
247   if (depth>=24)
248   xim->data = (char *)palaka2;
249   else
250   xim->data = calloc(xim->height,xim->bytes_per_line);       
251  }
252 }
253
254 void put_image(Display *display, Window win, GC gc, XImage *xim)
255 {
256   int x,y,i,j;
257   unsigned char r, g, b;
258   i = 0;
259   j = 0;
260   if (depth==16) {
261      if(bigendian)
262      for (y=0;y<xim->height; y++)
263      for (x=0;x<xim->width; x++) {
264      r = palaka2[j++];
265      g = palaka2[j++];
266      b = palaka2[j++];
267      j++;
268      xim->data[i++] = (g&224)>>5 | (r&248);
269      xim->data[i++] = (b&248)>>3 | (g&28)<<3;
270      }
271      else
272      for (y=0;y<xim->height; y++)
273      for (x=0;x<xim->width; x++) {
274      r = palaka2[j++];
275      g = palaka2[j++];
276      b = palaka2[j++];
277      j++;
278      xim->data[i++] = (b&248)>>3 | (g&28)<<3;
279      xim->data[i++] = (g&224)>>5 | (r&248);
280      }
281   }
282   if (depth==15) {
283      if(bigendian)
284      for (y=0;y<xim->height; y++)
285      for (x=0;x<xim->width; x++) {
286      r = palaka2[j++];
287      g = palaka2[j++];
288      b = palaka2[j++];
289      j++;
290      xim->data[i++] = (g&192)>>6 | (r&248)>>1;
291      xim->data[i++] = (b&248)>>3 | (g&56)<<2;
292      }
293      else
294      for (y=0;y<xim->height; y++)
295      for (x=0;x<xim->width; x++) {
296      r = palaka2[j++];
297      g = palaka2[j++];
298      b = palaka2[j++];
299      j++;
300      xim->data[i++] = (b&248)>>3 | (g&56)<<2;
301      xim->data[i++] = (g&192)>>6 | (r&248)>>1;
302      }
303   }
304   if (depth==8) {
305      for (y=0;y<xim->height; y++)
306      for (x=0;x<xim->width; x++) {
307      r = palaka2[j++];
308      g = palaka2[j++];
309      b = palaka2[j++];
310      j++;     
311      xim->data[i++] = (((7*g)/256)*36)+(((6*r)/256)*6)+((6*b)/256);
312      }
313   }
314   XPutImage(display,win,gc,xim,0,0,0,0,xim->width,xim->height); 
315 }
316
317 void sniff_events(Display *dis, Window win, fireshell *fss)
318 {
319   XEvent e;
320   while (XPending(dis)){
321   XNextEvent (dis, &e);
322   if (e.type == ConfigureNotify) resize(dis,win);
323   if (e.type == ButtonPress)     recycle(fss,e.xbutton.x, e.xbutton.y);
324   screenhack_handle_event(dis,&e);}
325 }
326
327
328 char *progclass = "Fireworkx";
329
330 char *defaults [] = {
331   ".background: black",
332   ".foreground: white",
333   "*delay:      5000",
334   "*maxlife:    1200",
335   "*light:      True",
336   "*fade:       False",
337   "*shoot:      False",
338   "*verbose:    False",
339   0
340 };
341
342 XrmOptionDescRec options [] = {
343   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
344   { "-maxlife",         ".maxlife",     XrmoptionSepArg, 0 },
345   { "-nolight",         ".light",       XrmoptionNoArg, "False" },
346   { "-fade",            ".fade",        XrmoptionNoArg, "True" },
347   { "-shoot",           ".shoot",       XrmoptionNoArg, "True" },
348   { "-verbose",         ".verbose",     XrmoptionNoArg, "True" },
349   { 0, 0, 0, 0 }
350 };
351
352 void
353 screenhack (Display *display, Window win)
354 {
355   unsigned int n,q;
356   Visual *vi;
357   Colormap cmap;
358   Bool writable;
359   XWindowAttributes xwa;
360   GC gc;
361   XGCValues gcv;
362   firepix *fpixs, *fpix;
363   fireshell *fshells, *fshell;
364   fade_on  = get_boolean_resource("fade"    , "Boolean");
365   light_on = get_boolean_resource("light"   , "Boolean");
366   shoot    = get_boolean_resource("shoot"   , "Boolean");
367   verbose  = get_boolean_resource("verbose" , "Boolean");
368   rndlife  = get_integer_resource("maxlife" , "Integer");
369   delay    = get_integer_resource("delay"   , "Integer");
370   minlife  = rndlife/4;
371   if(rndlife<1000) light_fade=0.98;
372   if(rndlife<500) light_fade=0.97;
373   if(fade_on) light_fade=0.97;
374   if(verbose){
375   printf("Fireworkx %s - pyrotechnics simulation program \n", FWXVERSION);
376   printf("Copyright (c) 1999-2004 Rony B Chandran <ronybc@asia.com> \n\n");
377   printf("url: http://www.ronybc.8k.com \n\n");}
378
379   XGetWindowAttributes(display,win,&xwa);
380   depth     = xwa.depth;
381   vi        = xwa.visual;
382   cmap      = xwa.colormap;
383   bigendian = (ImageByteOrder(display) == MSBFirst);
384  
385   if(depth==8){
386   if(verbose){
387   printf("Pseudocolor color: use '-fade' & '-nolight' for better results.\n");}
388   colors = (XColor *) calloc(sizeof(XColor),ncolors+1);
389   writable = False;
390   make_smooth_colormap(display, vi, cmap, colors, &ncolors,
391                                 False, &writable, True);
392   }
393   gc = XCreateGC(display, win, 0, &gcv);
394
395   resize(display,win);   /* initialize palakas */ 
396   seed += time(0);
397   
398   fpixs = malloc(sizeof(firepix) * PIXCOUNT * SHELLCOUNT);
399   fshells = malloc(sizeof(fireshell) * SHELLCOUNT);
400   fshell = fshells;
401   fpix = fpixs;
402   for (n=0;n<SHELLCOUNT;n++){
403   fshell->fpix = fpix;
404   recycle (fshell,rnd(fsc_width),rnd(fsc_height));
405   fshell++; 
406   fpix += PIXCOUNT; }
407   
408   while(1) {
409   for(q=FTWEAK;q;q--){
410   fshell=fshells;
411   for(n=SHELLCOUNT;n;n--){
412   if (!explode(fshell)){
413        recycle(fshell,rnd(fsc_width),rnd(fsc_height)); }
414        fshell++; }}
415   if(light_on) light_2x2(fshells);
416   put_image(display,win,gc,xim);
417   usleep(delay);
418   XSync(display,0);
419   sniff_events(display, win, fshells);
420   blur_best();}
421
422 }