ftp://netsw.org/x11/tools/desktop/xscreensaver-4.07.tar.gz
[xscreensaver] / hacks / eruption.c
diff --git a/hacks/eruption.c b/hacks/eruption.c
new file mode 100644 (file)
index 0000000..e312f6e
--- /dev/null
@@ -0,0 +1,440 @@
+/* Eruption, Copyright (c) 2002-2003 W.P. van Paassen <peter@paassen.tmfweb.nl>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Module - "eruption.c"
+ *
+ * [01-2003] - W.P. van Paassen: Port to X for use with XScreenSaver, the shadebob hack by Shane Smit was used as a template
+ * [04-2002] - W.P. van Paassen: Creation for the Demo Effects Collection (http://demo-effects.sourceforge.net)
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include <X11/Xutil.h>
+
+/*#define VERBOSE*/ 
+
+char *progclass = "Eruption";
+
+char *defaults [] = {
+  ".background: black",
+  ".foreground: white",
+  "*cycles:   80",
+  "*ncolors:  256",
+  "*delay:    5000",
+  "*particles: 300",
+  "*cooloff: 2",
+  "*gravity: 1",
+  "*heat: 256",
+  0
+};
+
+XrmOptionDescRec options [] = {
+  { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
+  { "-delay",   ".delay",   XrmoptionSepArg, 0 },
+  { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
+  { "-particles",  ".particles",  XrmoptionSepArg, 0 },
+  { "-cooloff",  ".cooloff",  XrmoptionSepArg, 0 },
+  { "-gravity",  ".gravity",  XrmoptionSepArg, 0 },
+  { "-heat",  ".heat",  XrmoptionSepArg, 0 },
+  { 0, 0, 0, 0 }
+};
+
+/*particle structure*/
+typedef struct 
+{
+  short xpos, ypos, xdir, ydir;
+  unsigned char colorindex;
+  unsigned char dead;
+} PARTICLE;
+
+static PARTICLE *particles;
+static unsigned short iWinWidth, iWinHeight;
+static unsigned char **fire;
+static unsigned short nParticleCount;
+static unsigned char xdelta, ydelta, decay;
+static signed char gravity;
+static signed short heat;
+
+void init_particle(PARTICLE* particle, signed short iColorCount)
+{
+  /* randomly init particles, generate them in the center of the screen */
+  particle->xpos = (iWinWidth >> 1) - 15 + (int)(14.0 * frand(2.0));
+  particle->ypos = (iWinHeight >> 1) - 15 + (int)(16.0 * frand(2.0));
+  particle->xdir =   -xdelta + (int)(xdelta * frand(2.0));
+  particle->ydir =   -ydelta + (int)((ydelta / 2) * frand(2.0));
+  particle->colorindex = iColorCount;
+  particle->dead = 0;
+}
+
+static void Execute( Display *pDisplay,
+                     Window MainWindow,
+                     GC *pGC, XImage *pImage,
+                     signed short iColorCount, unsigned long *aiColorVals )
+{
+  int i, j;
+  unsigned int temp;
+
+  /* move and draw particles into fire array */
+  
+  for (i = 0; i < nParticleCount; i++)
+    {
+      if (!particles[i].dead)
+       {
+         particles[i].xpos += particles[i].xdir;
+         particles[i].ypos += particles[i].ydir;
+         
+         /* is particle dead? */
+         
+         if (particles[i].colorindex == 0)
+           {
+             particles[i].dead = 1;
+             continue;
+           }
+         
+         if (particles[i].xpos < 2)
+           {
+             particles[i].xpos = 2;
+             particles[i].xdir = -particles[i].xdir - 4;
+             particles[i].colorindex = iColorCount;
+           }
+         else if (particles[i].xpos > iWinWidth - 3)
+           {
+             particles[i].xpos = iWinWidth - 3;
+             particles[i].xdir = -particles[i].xdir + 4;
+             particles[i].colorindex = iColorCount;
+           }
+         
+         if (particles[i].ypos < 2)
+           {
+             particles[i].ypos = 2;
+             particles[i].ydir = -particles[i].ydir;
+             particles[i].colorindex = iColorCount;
+           }
+         else if (particles[i].ypos > iWinHeight - 3)
+           {
+             particles[i].ypos = iWinHeight - 3;
+             particles[i].ydir = (-particles[i].ydir >> 2) - (random() % 4);
+             particles[i].colorindex = iColorCount;
+           }
+         
+         /* gravity kicks in */
+         particles[i].ydir += gravity;
+         
+         /* particle cools off */
+         particles[i].colorindex--;
+         
+         /* draw particle */
+         fire[particles[i].ypos][particles[i].xpos] = particles[i].colorindex;
+         fire[particles[i].ypos][particles[i].xpos - 1] = particles[i].colorindex;
+         fire[particles[i].ypos + 1][particles[i].xpos] = particles[i].colorindex;
+         fire[particles[i].ypos - 1][particles[i].xpos] = particles[i].colorindex;
+         fire[particles[i].ypos][particles[i].xpos + 1] = particles[i].colorindex;
+       }
+    }
+  
+  /* create fire effect */ 
+  for (i = 0; i < iWinHeight; i++)
+    {
+      for (j = 0; j < iWinWidth; j++)
+       {
+         if (j + 1 >= iWinWidth)
+           temp = 0;
+         else
+           temp = fire[i][j + 1];
+
+         if (j - 1 >= 0)  
+           temp += fire[i][j - 1];
+
+         if (i - 1 >= 0)
+           {
+             temp += fire[i - 1][j];
+             if (j - 1 >= 0)
+               temp += fire[i - 1][j - 1];
+             if (j + 1 < iWinWidth)
+               temp += fire[i - 1][j + 1];
+           }
+         
+         if (i + 1 < iWinHeight)
+           {
+             temp += fire[i + 1][j];
+             if (j + 1 < iWinWidth)
+               temp += fire[i + 1][j + 1];
+             if (j - 1 >= 0)
+               temp += fire[i + 1][j - 1];
+           }
+         
+         temp >>= 3;
+         
+         if (temp > decay)
+           {
+             temp -= decay;
+           }
+         else
+           temp = 0;
+         
+         fire[i][j] = temp;
+       }
+    }
+  
+  memset( pImage->data, 0, pImage->bytes_per_line * pImage->height );
+  
+  /* draw fire array to screen */
+  for (i = 0; i < iWinHeight; ++i)
+    {
+      for (j = 0; j < iWinWidth; ++j)
+       {
+         if (fire[i][j] > 0)
+           XPutPixel( pImage, j, i, aiColorVals[ fire[i][j] ] );
+       }
+    }
+  XPutImage( pDisplay, MainWindow, *pGC, pImage,
+            0,0,0,0, iWinWidth, iWinHeight );
+  XSync( pDisplay, False );
+}
+
+static unsigned long * SetPalette(Display *pDisplay, Window Win, signed short *piColorCount )
+{
+       XWindowAttributes XWinAttribs;
+       XColor Color, *aColors;
+       unsigned long *aiColorVals;
+       signed short iColor;
+
+       XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
+       
+       *piColorCount = get_integer_resource( "ncolors", "Integer" );
+       if( *piColorCount <   16 )      *piColorCount = 16;
+       if( *piColorCount > 255 )       *piColorCount = 256;
+
+       aColors    = calloc( *piColorCount, sizeof(XColor) );
+       aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
+       
+       Color.red = Color.green = Color.blue = 65535 / *piColorCount;
+
+       /* create fire palette */
+       for( iColor=0; iColor < *piColorCount; iColor++ )
+       {
+         if (iColor < *piColorCount >> 3)
+           {
+             /* black to blue */
+             aColors[iColor].red = 0;
+             aColors[iColor].green = 0;
+             aColors[iColor].blue = Color.blue * (iColor << 1);
+           }
+         else if (iColor < *piColorCount >> 2)
+           {
+             /* blue to red */
+             signed short temp = (iColor - (*piColorCount >> 3));
+             aColors[iColor].red = Color.red * (temp << 3);
+             aColors[iColor].green = 0;
+             aColors[iColor].blue = 16383 - Color.blue * (temp << 1);
+           }
+         else if (iColor < (*piColorCount >> 2) + (*piColorCount >> 3))
+           {
+             /* red to yellow */
+             signed short temp = (iColor - (*piColorCount >> 2)) << 3;
+             aColors[iColor].red = 65535;
+             aColors[iColor].green = Color.green * temp;
+             aColors[iColor].blue = 0;
+           }
+         else if (iColor < *piColorCount >> 1)
+           {
+             /* yellow to white */
+             signed int temp = (iColor - ((*piColorCount >> 2) + (*piColorCount >> 3))) << 3;
+             aColors[iColor].red = 65535;
+             aColors[iColor].green = 65535;
+             aColors[iColor].blue = Color.blue * temp;
+           }
+         else
+           {
+             /* white */
+             aColors[iColor].red = aColors[iColor].green = aColors[iColor].blue = 65535;
+           }
+
+         if( !XAllocColor( pDisplay, XWinAttribs.colormap, &aColors[ iColor ] ) )
+           {
+             /* start all over with less colors */     
+             XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColor, 0 );
+             free( aColors );
+             free( aiColorVals );
+             (*piColorCount)--;
+
+              if (*piColorCount < 6)
+                {
+                  fprintf (stderr, "%s: insufficient colors!\n", progname);
+                  exit (1);
+                }
+
+             aColors     = calloc( *piColorCount, sizeof(XColor) );
+             aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
+             iColor = -1;
+           }
+         else
+           aiColorVals[ iColor ] = aColors[ iColor ].pixel;
+       }
+
+       if (heat < *piColorCount)
+         *piColorCount = heat;
+       
+       free( aColors );
+
+       XSetWindowBackground( pDisplay, Win, aiColorVals[ 0 ] );
+
+       return aiColorVals;
+}
+
+
+static void Initialize( Display *pDisplay, Window Win, GC *pGC, XImage **ppImage )
+{
+       XGCValues gcValues;
+       XWindowAttributes XWinAttribs;
+       int iBitsPerPixel, i;
+
+       /* Create the Image for drawing */
+       XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
+
+       /* Find the preferred bits-per-pixel. (jwz) */
+       {
+               int i, pfvc = 0;
+               XPixmapFormatValues *pfv = XListPixmapFormats( pDisplay, &pfvc );
+               for( i=0; i<pfvc; i++ )
+                       if( pfv[ i ].depth == XWinAttribs.depth )
+                       {
+                               iBitsPerPixel = pfv[ i ].bits_per_pixel;
+                               break;
+                       }
+               if( pfv )
+                       XFree (pfv);
+       }
+
+       /*  Create the GC. */
+       *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
+
+       *ppImage = XCreateImage( pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
+                                                         XWinAttribs.width, XWinAttribs.height, BitmapPad( pDisplay ), 0 );
+       (*ppImage)->data = calloc((*ppImage)->bytes_per_line, (*ppImage)->height);
+
+       iWinWidth = XWinAttribs.width;
+       iWinHeight = XWinAttribs.height;
+
+       /* create fire array */
+       fire = malloc( iWinHeight * sizeof(unsigned char*));
+       for (i = 0; i < iWinHeight; ++i)
+         fire[i] = malloc( iWinWidth * sizeof(unsigned char));
+
+       /*create particles */
+       particles = malloc (nParticleCount * sizeof(PARTICLE));
+}
+
+void screenhack(Display *pDisplay, Window Win )
+{
+       XWindowAttributes XWinAttribs;
+       GC gc;
+       signed short iColorCount = 0;
+       unsigned long *aiColorVals = NULL;
+       unsigned short sum = 0;
+       XImage *pImage = NULL;
+#ifdef VERBOSE
+       time_t nTime = time( NULL );
+       unsigned short iFrame = 0;
+#endif  /*  VERBOSE */
+       int delay, cycles, i;
+
+       nParticleCount = get_integer_resource( "particles", "Integer" );
+       if (nParticleCount < 100)
+         nParticleCount = 100;
+       if (nParticleCount > 2000)
+         nParticleCount = 2000;
+
+       decay = get_integer_resource( "cooloff", "Integer" );
+       if (decay <= 0)
+         decay = 0;
+       if (decay > 10)
+         decay = 10;
+
+       gravity = get_integer_resource( "gravity", "Integer" );
+       if (gravity < -5)
+         gravity = -5;
+       if (gravity > 5)
+         gravity = 5;
+
+       heat = get_integer_resource( "heat", "Integer" );
+       if (heat < 64)
+         heat = 64;
+       if (heat > 256)
+         heat = 256;
+
+#ifdef VERBOSE 
+       printf( "%s: Allocated %d particles\n", progclass, nParticleCount );
+#endif  /*  VERBOSE */
+
+       Initialize( pDisplay, Win, &gc, &pImage );
+
+       ydelta = 0;
+       while (sum < (iWinHeight >> 1) - 15)
+         {
+           ydelta++;
+           sum += ydelta;
+         }
+
+       sum = 0;
+       while (sum < (iWinWidth >> 3))
+         {
+           xdelta++;
+           sum += xdelta;
+         }
+
+       delay = get_integer_resource( "delay", "Integer" );
+       cycles = get_integer_resource( "cycles", "Integer" );
+       i = cycles;
+
+       XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
+       XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColorCount, 0 );
+       free( aiColorVals );
+       aiColorVals = SetPalette( pDisplay, Win, &iColorCount );
+       XClearWindow( pDisplay, Win );
+
+       while( 1 )
+       {
+               screenhack_handle_events( pDisplay );
+
+               if( i++ >= cycles )
+               {
+                       for (i = 0; i < nParticleCount; i++)
+                         init_particle(particles + i, iColorCount - 1);
+                       i = 0;
+               }
+
+               Execute( pDisplay, Win, &gc, pImage, iColorCount - 1, aiColorVals );
+
+               if( delay && !(i % 4) )
+                       usleep(delay);
+
+#ifdef VERBOSE
+               iFrame++;
+               if( nTime - time( NULL ) )
+               {
+                       printf( "%s: %d FPS\n", progclass, iFrame );
+                       nTime = time( NULL );
+                       iFrame = 0;
+               }
+#endif  /*  VERBOSE */
+       }
+
+       free( pImage->data );
+       XDestroyImage( pImage );
+       free( aiColorVals );
+       for (i = 0; i < iWinHeight; ++i)
+         free( fire[i] );
+       free( fire );
+       free( particles );
+}
+
+/* End of Module - "eruption.c" */
+