ftp://netsw.org/x11/tools/desktop/xscreensaver-4.07.tar.gz
[xscreensaver] / hacks / eruption.c
1 /* Eruption, Copyright (c) 2002-2003 W.P. van Paassen <peter@paassen.tmfweb.nl>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Module - "eruption.c"
12  *
13  * [01-2003] - W.P. van Paassen: Port to X for use with XScreenSaver, the shadebob hack by Shane Smit was used as a template
14  * [04-2002] - W.P. van Paassen: Creation for the Demo Effects Collection (http://demo-effects.sourceforge.net)
15  */
16
17 #include <math.h>
18 #include "screenhack.h"
19 #include <X11/Xutil.h>
20
21 /*#define VERBOSE*/ 
22
23 char *progclass = "Eruption";
24
25 char *defaults [] = {
26   ".background: black",
27   ".foreground: white",
28   "*cycles:   80",
29   "*ncolors:  256",
30   "*delay:    5000",
31   "*particles: 300",
32   "*cooloff: 2",
33   "*gravity: 1",
34   "*heat: 256",
35   0
36 };
37
38 XrmOptionDescRec options [] = {
39   { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
40   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
41   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
42   { "-particles",  ".particles",  XrmoptionSepArg, 0 },
43   { "-cooloff",  ".cooloff",  XrmoptionSepArg, 0 },
44   { "-gravity",  ".gravity",  XrmoptionSepArg, 0 },
45   { "-heat",  ".heat",  XrmoptionSepArg, 0 },
46   { 0, 0, 0, 0 }
47 };
48
49 /*particle structure*/
50 typedef struct 
51 {
52   short xpos, ypos, xdir, ydir;
53   unsigned char colorindex;
54   unsigned char dead;
55 } PARTICLE;
56
57 static PARTICLE *particles;
58 static unsigned short iWinWidth, iWinHeight;
59 static unsigned char **fire;
60 static unsigned short nParticleCount;
61 static unsigned char xdelta, ydelta, decay;
62 static signed char gravity;
63 static signed short heat;
64
65 void init_particle(PARTICLE* particle, signed short iColorCount)
66 {
67   /* randomly init particles, generate them in the center of the screen */
68   particle->xpos = (iWinWidth >> 1) - 15 + (int)(14.0 * frand(2.0));
69   particle->ypos = (iWinHeight >> 1) - 15 + (int)(16.0 * frand(2.0));
70   particle->xdir =   -xdelta + (int)(xdelta * frand(2.0));
71   particle->ydir =   -ydelta + (int)((ydelta / 2) * frand(2.0));
72   particle->colorindex = iColorCount;
73   particle->dead = 0;
74 }
75
76 static void Execute( Display *pDisplay,
77                      Window MainWindow,
78                      GC *pGC, XImage *pImage,
79                      signed short iColorCount, unsigned long *aiColorVals )
80 {
81   int i, j;
82   unsigned int temp;
83
84   /* move and draw particles into fire array */
85   
86   for (i = 0; i < nParticleCount; i++)
87     {
88       if (!particles[i].dead)
89         {
90           particles[i].xpos += particles[i].xdir;
91           particles[i].ypos += particles[i].ydir;
92           
93           /* is particle dead? */
94           
95           if (particles[i].colorindex == 0)
96             {
97               particles[i].dead = 1;
98               continue;
99             }
100           
101           if (particles[i].xpos < 2)
102             {
103               particles[i].xpos = 2;
104               particles[i].xdir = -particles[i].xdir - 4;
105               particles[i].colorindex = iColorCount;
106             }
107           else if (particles[i].xpos > iWinWidth - 3)
108             {
109               particles[i].xpos = iWinWidth - 3;
110               particles[i].xdir = -particles[i].xdir + 4;
111               particles[i].colorindex = iColorCount;
112             }
113           
114           if (particles[i].ypos < 2)
115             {
116               particles[i].ypos = 2;
117               particles[i].ydir = -particles[i].ydir;
118               particles[i].colorindex = iColorCount;
119             }
120           else if (particles[i].ypos > iWinHeight - 3)
121             {
122               particles[i].ypos = iWinHeight - 3;
123               particles[i].ydir = (-particles[i].ydir >> 2) - (random() % 4);
124               particles[i].colorindex = iColorCount;
125             }
126           
127           /* gravity kicks in */
128           particles[i].ydir += gravity;
129           
130           /* particle cools off */
131           particles[i].colorindex--;
132           
133           /* draw particle */
134           fire[particles[i].ypos][particles[i].xpos] = particles[i].colorindex;
135           fire[particles[i].ypos][particles[i].xpos - 1] = particles[i].colorindex;
136           fire[particles[i].ypos + 1][particles[i].xpos] = particles[i].colorindex;
137           fire[particles[i].ypos - 1][particles[i].xpos] = particles[i].colorindex;
138           fire[particles[i].ypos][particles[i].xpos + 1] = particles[i].colorindex;
139         }
140     }
141   
142   /* create fire effect */ 
143   for (i = 0; i < iWinHeight; i++)
144     {
145       for (j = 0; j < iWinWidth; j++)
146         {
147           if (j + 1 >= iWinWidth)
148             temp = 0;
149           else
150             temp = fire[i][j + 1];
151
152           if (j - 1 >= 0)  
153             temp += fire[i][j - 1];
154
155           if (i - 1 >= 0)
156             {
157               temp += fire[i - 1][j];
158               if (j - 1 >= 0)
159                 temp += fire[i - 1][j - 1];
160               if (j + 1 < iWinWidth)
161                 temp += fire[i - 1][j + 1];
162             }
163           
164           if (i + 1 < iWinHeight)
165             {
166               temp += fire[i + 1][j];
167               if (j + 1 < iWinWidth)
168                 temp += fire[i + 1][j + 1];
169               if (j - 1 >= 0)
170                 temp += fire[i + 1][j - 1];
171             }
172           
173           temp >>= 3;
174           
175           if (temp > decay)
176             {
177               temp -= decay;
178             }
179           else
180             temp = 0;
181           
182           fire[i][j] = temp;
183         }
184     }
185   
186   memset( pImage->data, 0, pImage->bytes_per_line * pImage->height );
187   
188   /* draw fire array to screen */
189   for (i = 0; i < iWinHeight; ++i)
190     {
191       for (j = 0; j < iWinWidth; ++j)
192         {
193           if (fire[i][j] > 0)
194             XPutPixel( pImage, j, i, aiColorVals[ fire[i][j] ] );
195         }
196     }
197   XPutImage( pDisplay, MainWindow, *pGC, pImage,
198              0,0,0,0, iWinWidth, iWinHeight );
199   XSync( pDisplay, False );
200 }
201
202 static unsigned long * SetPalette(Display *pDisplay, Window Win, signed short *piColorCount )
203 {
204         XWindowAttributes XWinAttribs;
205         XColor Color, *aColors;
206         unsigned long *aiColorVals;
207         signed short iColor;
208
209         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
210         
211         *piColorCount = get_integer_resource( "ncolors", "Integer" );
212         if( *piColorCount <   16 )      *piColorCount = 16;
213         if( *piColorCount > 255 )       *piColorCount = 256;
214
215         aColors    = calloc( *piColorCount, sizeof(XColor) );
216         aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
217         
218         Color.red = Color.green = Color.blue = 65535 / *piColorCount;
219
220         /* create fire palette */
221         for( iColor=0; iColor < *piColorCount; iColor++ )
222         {
223           if (iColor < *piColorCount >> 3)
224             {
225               /* black to blue */
226               aColors[iColor].red = 0;
227               aColors[iColor].green = 0;
228               aColors[iColor].blue = Color.blue * (iColor << 1);
229             }
230           else if (iColor < *piColorCount >> 2)
231             {
232               /* blue to red */
233               signed short temp = (iColor - (*piColorCount >> 3));
234               aColors[iColor].red = Color.red * (temp << 3);
235               aColors[iColor].green = 0;
236               aColors[iColor].blue = 16383 - Color.blue * (temp << 1);
237             }
238           else if (iColor < (*piColorCount >> 2) + (*piColorCount >> 3))
239             {
240               /* red to yellow */
241               signed short temp = (iColor - (*piColorCount >> 2)) << 3;
242               aColors[iColor].red = 65535;
243               aColors[iColor].green = Color.green * temp;
244               aColors[iColor].blue = 0;
245             }
246           else if (iColor < *piColorCount >> 1)
247             {
248               /* yellow to white */
249               signed int temp = (iColor - ((*piColorCount >> 2) + (*piColorCount >> 3))) << 3;
250               aColors[iColor].red = 65535;
251               aColors[iColor].green = 65535;
252               aColors[iColor].blue = Color.blue * temp;
253             }
254           else
255             {
256               /* white */
257               aColors[iColor].red = aColors[iColor].green = aColors[iColor].blue = 65535;
258             }
259
260           if( !XAllocColor( pDisplay, XWinAttribs.colormap, &aColors[ iColor ] ) )
261             {
262               /* start all over with less colors */     
263               XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColor, 0 );
264               free( aColors );
265               free( aiColorVals );
266               (*piColorCount)--;
267
268               if (*piColorCount < 6)
269                 {
270                   fprintf (stderr, "%s: insufficient colors!\n", progname);
271                   exit (1);
272                 }
273
274               aColors     = calloc( *piColorCount, sizeof(XColor) );
275               aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
276               iColor = -1;
277             }
278           else
279             aiColorVals[ iColor ] = aColors[ iColor ].pixel;
280         }
281
282         if (heat < *piColorCount)
283           *piColorCount = heat;
284         
285         free( aColors );
286
287         XSetWindowBackground( pDisplay, Win, aiColorVals[ 0 ] );
288
289         return aiColorVals;
290 }
291
292
293 static void Initialize( Display *pDisplay, Window Win, GC *pGC, XImage **ppImage )
294 {
295         XGCValues gcValues;
296         XWindowAttributes XWinAttribs;
297         int iBitsPerPixel, i;
298
299         /* Create the Image for drawing */
300         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
301
302         /* Find the preferred bits-per-pixel. (jwz) */
303         {
304                 int i, pfvc = 0;
305                 XPixmapFormatValues *pfv = XListPixmapFormats( pDisplay, &pfvc );
306                 for( i=0; i<pfvc; i++ )
307                         if( pfv[ i ].depth == XWinAttribs.depth )
308                         {
309                                 iBitsPerPixel = pfv[ i ].bits_per_pixel;
310                                 break;
311                         }
312                 if( pfv )
313                         XFree (pfv);
314         }
315
316         /*  Create the GC. */
317         *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
318
319         *ppImage = XCreateImage( pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
320                                                           XWinAttribs.width, XWinAttribs.height, BitmapPad( pDisplay ), 0 );
321         (*ppImage)->data = calloc((*ppImage)->bytes_per_line, (*ppImage)->height);
322
323         iWinWidth = XWinAttribs.width;
324         iWinHeight = XWinAttribs.height;
325
326         /* create fire array */
327         fire = malloc( iWinHeight * sizeof(unsigned char*));
328         for (i = 0; i < iWinHeight; ++i)
329           fire[i] = malloc( iWinWidth * sizeof(unsigned char));
330
331         /*create particles */
332         particles = malloc (nParticleCount * sizeof(PARTICLE));
333 }
334
335 void screenhack(Display *pDisplay, Window Win )
336 {
337         XWindowAttributes XWinAttribs;
338         GC gc;
339         signed short iColorCount = 0;
340         unsigned long *aiColorVals = NULL;
341         unsigned short sum = 0;
342         XImage *pImage = NULL;
343 #ifdef VERBOSE
344         time_t nTime = time( NULL );
345         unsigned short iFrame = 0;
346 #endif  /*  VERBOSE */
347         int delay, cycles, i;
348
349         nParticleCount = get_integer_resource( "particles", "Integer" );
350         if (nParticleCount < 100)
351           nParticleCount = 100;
352         if (nParticleCount > 2000)
353           nParticleCount = 2000;
354
355         decay = get_integer_resource( "cooloff", "Integer" );
356         if (decay <= 0)
357           decay = 0;
358         if (decay > 10)
359           decay = 10;
360
361         gravity = get_integer_resource( "gravity", "Integer" );
362         if (gravity < -5)
363           gravity = -5;
364         if (gravity > 5)
365           gravity = 5;
366
367         heat = get_integer_resource( "heat", "Integer" );
368         if (heat < 64)
369           heat = 64;
370         if (heat > 256)
371           heat = 256;
372
373 #ifdef VERBOSE 
374         printf( "%s: Allocated %d particles\n", progclass, nParticleCount );
375 #endif  /*  VERBOSE */
376
377         Initialize( pDisplay, Win, &gc, &pImage );
378
379         ydelta = 0;
380         while (sum < (iWinHeight >> 1) - 15)
381           {
382             ydelta++;
383             sum += ydelta;
384           }
385
386         sum = 0;
387         while (sum < (iWinWidth >> 3))
388           {
389             xdelta++;
390             sum += xdelta;
391           }
392
393         delay = get_integer_resource( "delay", "Integer" );
394         cycles = get_integer_resource( "cycles", "Integer" );
395         i = cycles;
396
397         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
398         XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColorCount, 0 );
399         free( aiColorVals );
400         aiColorVals = SetPalette( pDisplay, Win, &iColorCount );
401         XClearWindow( pDisplay, Win );
402
403         while( 1 )
404         {
405                 screenhack_handle_events( pDisplay );
406
407                 if( i++ >= cycles )
408                 {
409                         for (i = 0; i < nParticleCount; i++)
410                           init_particle(particles + i, iColorCount - 1);
411                         i = 0;
412                 }
413
414                 Execute( pDisplay, Win, &gc, pImage, iColorCount - 1, aiColorVals );
415
416                 if( delay && !(i % 4) )
417                         usleep(delay);
418
419 #ifdef VERBOSE
420                 iFrame++;
421                 if( nTime - time( NULL ) )
422                 {
423                         printf( "%s: %d FPS\n", progclass, iFrame );
424                         nTime = time( NULL );
425                         iFrame = 0;
426                 }
427 #endif  /*  VERBOSE */
428         }
429
430         free( pImage->data );
431         XDestroyImage( pImage );
432         free( aiColorVals );
433         for (i = 0; i < iWinHeight; ++i)
434           free( fire[i] );
435         free( fire );
436         free( particles );
437 }
438
439 /* End of Module - "eruption.c" */
440