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