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