From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / shadebobs.c
index 34965dfc465303ffdec3764ef6c43d7d3d6b332e..64fa82c9772d7e28b3ba7a87d3f74e4fd00fd6e1 100644 (file)
  *                on cycle, and the extents of the sinus pattern change in
  *                real-time.
  * [06/22/99] - Shane Smit: Fixed delay to be fast and use little CPU :).
+ * [09/17/99] - Shane Smit: Made all calculations based on the size of the
+ *                window. Thus, it'll look the same at 100x100 as it does at
+ *                1600x1200 ( Only smaller :).
+ * [04/24/00] - Shane Smit: Revamped entire source code:
+ *                Shade Bob movement is calculated very differently.
+ *                Base color can be any color now.
  */
 
+
 #include <math.h>
 #include "screenhack.h"
-#include <X11/Xutil.h>
 
 /* #define VERBOSE */
 
-char *progclass = "ShadeBobs";
-
-char *defaults [] = {
-  "*degrees:  512",
+static const char *shadebobs_defaults [] = {
+  ".background: black",
+  ".foreground: white",
+  "*fpsSolid:  true",
+  "*degrees:  0",      /* default: Automatic degree calculation */
   "*color:    random",
-  "*count:    2",
-  "*cycles:   50",
+  "*count:    4",
+  "*cycles:   10",
   "*ncolors:  64",    /* changing this doesn't work particularly well */
-  "*delay:    5000",
+  "*delay:    10000",
+#ifdef USE_IPHONE
+  "*ignoreRotation: True",
+#endif
   0
 };
 
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec shadebobs_options [] = {
   { "-degrees", ".degrees", XrmoptionSepArg, 0 },
   { "-color",   ".color",   XrmoptionSepArg, 0 },
+  { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
   { "-count",   ".count",   XrmoptionSepArg, 0 },
   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
   { 0, 0, 0, 0 }
 };
 
-static unsigned short nDegreeCount;
-static double *anSinTable;
-static unsigned short nMaxExtentX, nMaxExtentY;
-static unsigned short nMinExtentX, nMinExtentY;
-static unsigned short nHalfWidth, nHalfHeight;
-static char *sColor;
-
-#define RANDOM() ((int) (random() & 0X7FFFFFFFL))
-
-
 /* Ahem. Chocolate is a flavor; not a food. Thank you */
 
 
-#define MAPSIZE 32
-
 typedef struct
 {
-  char anDeltaMap[ MAPSIZE * MAPSIZE ];  /* 32 x 32 Delta Map */
-  double nVelocityX, nVelocityY;
-  double nAngleX, nAngleY;
-  short nExtentX, nExtentY;
+       signed char *anDeltaMap;
+       double nAngle, nAngleDelta, nAngleInc;
+       double nPosX, nPosY;
 } SShadeBob;
 
+struct state {
+  Display *dpy;
+  Window window;
+  unsigned short iDegreeCount;
+  double *anSinTable, *anCosTable;
+  unsigned short iWinWidth, iWinHeight;
+  unsigned short iWinCenterX, iWinCenterY;
+  char *sColor;
+  unsigned char iBobRadius, iBobDiameter; 
+  unsigned char iVelocity;
+
+  unsigned long *aiColorVals;
+  signed short iColorCount;
+  int cycles;
+  XImage *pImage;
+  unsigned char nShadeBobCount, iShadeBob;
+  SShadeBob *aShadeBobs;
+  GC gc;
+  int delay;
+  int draw_i;
+};
 
-static void ResetShadeBob( SShadeBob *pShadeBob )
-{
-  pShadeBob->nAngleX = RANDOM() % nDegreeCount;
-  pShadeBob->nAngleY = RANDOM() % nDegreeCount;
+#define RANDOM() ((int) (random() & 0X7FFFFFFFL))
 
-  pShadeBob->nExtentX = (RANDOM() % (nMaxExtentX - nMinExtentX)) + nMinExtentX;
-  pShadeBob->nExtentY = (RANDOM() % (nMaxExtentY - nMinExtentY)) + nMinExtentY;
-}
 
 
-static void InitShadeBob( SShadeBob *pShadeBob, Bool bDark )
+static void ResetShadeBob( struct state *st, SShadeBob *pShadeBob )
 {
-  double nDelta;
-  char iWidth, iHeight;
+       pShadeBob->nPosX = RANDOM() % st->iWinWidth;
+       pShadeBob->nPosY = RANDOM() % st->iWinHeight;
+       pShadeBob->nAngle = RANDOM() % st->iDegreeCount;
+       pShadeBob->nAngleDelta = ( RANDOM() % st->iDegreeCount ) - ( st->iDegreeCount / 2.0F );
+       pShadeBob->nAngleInc = pShadeBob->nAngleDelta / 50.0F; 
+       if( pShadeBob->nAngleInc == 0.0F )      
+               pShadeBob->nAngleInc = ( pShadeBob->nAngleDelta > 0.0F ) ? 0.0001F : -0.0001F;
+}
 
-  for( iHeight=-16; iHeight<16; iHeight++ )
-    for( iWidth=-16; iWidth<16; iWidth++ )
-    {
-      nDelta = 9 - (sqrt( pow( iWidth+0.5, 2 ) + pow( iHeight+0.5, 2 ) ) / 2 );
-      if( nDelta < 0 )  nDelta = 0;
-      if( bDark ) nDelta = -nDelta;
-      pShadeBob->anDeltaMap[ (iWidth+(MAPSIZE/2))*MAPSIZE
-                           + iHeight+(MAPSIZE/2) ] = (char)nDelta;
-    }
 
-       ResetShadeBob( pShadeBob );
+static void InitShadeBob( struct state *st, SShadeBob *pShadeBob, Bool bDark )
+{
+       double nDelta;
+       int iWidth, iHeight;
+
+       if( ( pShadeBob->anDeltaMap = calloc( st->iBobDiameter * st->iBobDiameter, sizeof(char) ) ) == NULL )
+       {
+               fprintf( stderr, "%s: Could not allocate Delta Map!\n", progname );
+               return;
+       }
+
+       for( iHeight=-st->iBobRadius; iHeight<st->iBobRadius; iHeight++ )
+               for( iWidth=-st->iBobRadius; iWidth<st->iBobRadius; iWidth++ )
+               {
+                       nDelta = 9 - ( ( sqrt( pow( iWidth+0.5, 2 ) + pow( iHeight+0.5, 2 ) ) / st->iBobRadius ) * 8 );
+                       if( nDelta < 0 )  nDelta = 0;
+                       if( bDark ) nDelta = -nDelta;
+                       pShadeBob->anDeltaMap[ ( iWidth + st->iBobRadius ) * st->iBobDiameter + iHeight + st->iBobRadius ] = (char)nDelta;
+               }
+  
+       ResetShadeBob( st, pShadeBob );
 }
 
 
-static void Execute( SShadeBob *pShadeBob, Display *pDisplay,
-                     Window MainWindow,
-                     GC *pGC, XImage *pXImage,
-                     int ncolors, XColor *aXColors )
+/* A delta is calculated, and the shadebob turns at an increment.  When the delta
+ * falls to 0, a new delta and increment are calculated. */
+static void MoveShadeBob( struct state *st, SShadeBob *pShadeBob )
 {
-  long nColor;
-  short nIndex;
-  unsigned int nXPos, nYPos;
-  unsigned int iWidth, iHeight;
-
-  pShadeBob->nVelocityX += ( ( RANDOM() % 200 ) - 100 ) / 1000.0F;
-  pShadeBob->nVelocityY += ( ( RANDOM() % 200 ) - 100 ) / 1000.0F;
-
-  if(      pShadeBob->nVelocityX > 4 )  pShadeBob->nVelocityX = 4;
-  else if( pShadeBob->nVelocityX < 3 )  pShadeBob->nVelocityX = 3;
-  if(      pShadeBob->nVelocityY > 4 )  pShadeBob->nVelocityY = 4;
-  else if( pShadeBob->nVelocityY < 3 )  pShadeBob->nVelocityY = 3;
-
-  pShadeBob->nAngleX += pShadeBob->nVelocityX;
-  pShadeBob->nAngleY += pShadeBob->nVelocityY;
-
-  if(      pShadeBob->nAngleX >= nDegreeCount ) pShadeBob->nAngleX -= nDegreeCount;
-  else if( pShadeBob->nAngleX < 0 )             pShadeBob->nAngleX += nDegreeCount;
-  if(      pShadeBob->nAngleY >= nDegreeCount ) pShadeBob->nAngleY -= nDegreeCount;
-  else if( pShadeBob->nAngleY < 0 )             pShadeBob->nAngleY += nDegreeCount;
-
-  pShadeBob->nExtentX += ( RANDOM() % 5 ) - 2;
-  if( pShadeBob->nExtentX > nMaxExtentX ) pShadeBob->nExtentX = nMaxExtentX;
-  if( pShadeBob->nExtentX < nMinExtentX ) pShadeBob->nExtentX = nMinExtentX;
-  pShadeBob->nExtentY += ( RANDOM() % 5 ) - 2;
-  if( pShadeBob->nExtentY > nMaxExtentY ) pShadeBob->nExtentY = nMaxExtentY;
-  if( pShadeBob->nExtentY < nMinExtentY ) pShadeBob->nExtentY = nMinExtentY;
-
-  /* Trig is your friend :) */
-  nXPos = (unsigned int)(( anSinTable[ (int)pShadeBob->nAngleX ] * pShadeBob->nExtentX )
-                         + nHalfWidth);
-  nYPos = (unsigned int)(( anSinTable[ (int)pShadeBob->nAngleY ] * pShadeBob->nExtentY )
-                         + nHalfHeight);
-
-  for( iHeight=0; iHeight < MAPSIZE; iHeight++ )
-  {
-    for( iWidth=0; iWidth < MAPSIZE; iWidth++ )
-    {
-      nColor = XGetPixel( pXImage, nXPos + iWidth, nYPos + iHeight );
-
-      /*  FIXME: Here is a loop I'd love to take out. */
-      for( nIndex=0; nIndex < ncolors; nIndex++ )
-        if( aXColors[ nIndex ].pixel == nColor )
-          break;
+       pShadeBob->nAngle          += pShadeBob->nAngleInc;
+       pShadeBob->nAngleDelta -= pShadeBob->nAngleInc;
+
+       if( pShadeBob->nAngle >= st->iDegreeCount )     pShadeBob->nAngle -= st->iDegreeCount;
+       else if( pShadeBob->nAngle < 0 )                pShadeBob->nAngle += st->iDegreeCount;
+       
+       if( ( pShadeBob->nAngleInc>0.0F  && pShadeBob->nAngleDelta<pShadeBob->nAngleInc ) ||
+           ( pShadeBob->nAngleInc<=0.0F && pShadeBob->nAngleDelta>pShadeBob->nAngleInc ) )
+       {
+               pShadeBob->nAngleDelta = ( RANDOM() % st->iDegreeCount ) - ( st->iDegreeCount / 2.0F );
+               pShadeBob->nAngleInc = pShadeBob->nAngleDelta / 50.0F;
+               if( pShadeBob->nAngleInc == 0.0F )
+                       pShadeBob->nAngleInc = ( pShadeBob->nAngleDelta > 0.0F ) ? 0.0001F : -0.0001F;
+       }
+       
+       pShadeBob->nPosX = ( st->anSinTable[ (int)pShadeBob->nAngle ] * st->iVelocity ) + pShadeBob->nPosX;
+       pShadeBob->nPosY = ( st->anCosTable[ (int)pShadeBob->nAngle ] * st->iVelocity ) + pShadeBob->nPosY;
+
+       /* This wraps it around the screen. */
+       if( pShadeBob->nPosX >= st->iWinWidth ) pShadeBob->nPosX -= st->iWinWidth;
+       else if( pShadeBob->nPosX < 0 )         pShadeBob->nPosX += st->iWinWidth;
+       
+       if( pShadeBob->nPosY >= st->iWinHeight )        pShadeBob->nPosY -= st->iWinHeight;
+       else if( pShadeBob->nPosY < 0 )                 pShadeBob->nPosY += st->iWinHeight;
+}
 
-      nIndex += pShadeBob->anDeltaMap[ iWidth * MAPSIZE + iHeight ];
-      if( nIndex >= ncolors ) nIndex = ncolors-1;
-      if( nIndex < 0 )  nIndex = 0;
 
-      XPutPixel( pXImage, nXPos + iWidth, nYPos + iHeight,
-                 aXColors[ nIndex ].pixel );
-    }
-  }
-  /* Place graphics in window */
-  XPutImage( pDisplay, MainWindow, *pGC, pXImage,
-             nXPos, nYPos, nXPos, nYPos, MAPSIZE, MAPSIZE );
-  XSync (pDisplay, False);
+static void Execute( struct state *st, SShadeBob *pShadeBob )
+{
+       unsigned long iColor;
+       short iColorVal;
+       int iPixelX, iPixelY;
+       unsigned int iWidth, iHeight;
+
+       MoveShadeBob( st, pShadeBob );
+       
+       for( iHeight=0; iHeight<st->iBobDiameter; iHeight++ )
+       {
+               iPixelY = pShadeBob->nPosY + iHeight;
+               if( iPixelY >= st->iWinHeight ) iPixelY -= st->iWinHeight;
+
+               for( iWidth=0; iWidth<st->iBobDiameter; iWidth++ )
+               {
+                       iPixelX = pShadeBob->nPosX + iWidth;
+                       if( iPixelX >= st->iWinWidth )  iPixelX -= st->iWinWidth;
+
+                       iColor = XGetPixel( st->pImage, iPixelX, iPixelY );
+
+                       /*  FIXME: Here is a loop I'd love to take out. */
+                       for( iColorVal=0; iColorVal<st->iColorCount; iColorVal++ )
+                               if( st->aiColorVals[ iColorVal ] == iColor )
+                                       break;
+
+                       iColorVal += pShadeBob->anDeltaMap[ iWidth * st->iBobDiameter + iHeight ];
+                       if( iColorVal >= st->iColorCount ) iColorVal = st->iColorCount - 1;
+                       if( iColorVal < 0 )                        iColorVal = 0;
+
+                       XPutPixel( st->pImage, iPixelX, iPixelY, st->aiColorVals[ iColorVal ] );
+               }
+       }
+
+       /* FIXME: if it's next to the top or left sides of screen this will break. However, it's not noticable. */
+       XPutImage( st->dpy, st->window, st->gc, st->pImage,
+             pShadeBob->nPosX, pShadeBob->nPosY, pShadeBob->nPosX, pShadeBob->nPosY, st->iBobDiameter, st->iBobDiameter );
 }
 
 
-static void CreateTables( unsigned int nDegrees )
+static void CreateTables( struct state *st, unsigned int nDegrees )
 {
-  double nRadian;
-  unsigned int iDegree;
-  anSinTable = calloc( nDegrees, sizeof(double) );
-
-  for( iDegree=0; iDegree<nDegrees; iDegree++ )
-  {
-    nRadian = ( (double)(2*iDegree) / (double)nDegrees ) * M_PI;
-    anSinTable[ iDegree ] = sin( nRadian );
-  }
+       double nRadian;
+       unsigned int iDegree;
+       st->anSinTable = calloc( nDegrees, sizeof(double) );
+       st->anCosTable = calloc( nDegrees, sizeof(double) );
+
+       for( iDegree=0; iDegree<nDegrees; iDegree++ )
+       {
+               nRadian = ( (double)(2*iDegree) / (double)nDegrees ) * M_PI;
+               st->anSinTable[ iDegree ] = sin( nRadian );
+               st->anCosTable[ iDegree ] = cos( nRadian );
+       }
 }
 
 
-static void SetPalette(Display *pDisplay, Window Win, char *sColor,
-                       int *ncolorsP, XColor *aXColors )
+static unsigned long * SetPalette(struct state *st )
 {
-  XWindowAttributes XWinAttrib;
-  Colormap CMap;
-  XColor Color;
-  int nHue;
-  double nSat, nVal;
-
-  XGetWindowAttributes( pDisplay, Win, &XWinAttrib );
-  CMap = XWinAttrib.colormap;
-
-  Color.red = ( RANDOM() % 3 ) * 0x7FFF;    /*  Either full, half or none. */
-  Color.green = ( RANDOM() % 3 ) * 0x7FFF;
-  Color.blue = ( RANDOM() % 3 ) * 0x7FFF;
-
-  /*  If Color is black, grey, or white, then make SURE its a color */
-  if( Color.red == Color.green && Color.green == Color.blue )
-  { Color.red = 0xFFFF;
-    Color.green = ( RANDOM() % 3 ) * 0x7FFF;
-    Color.blue = 0; }
-
-  if( strcasecmp( sColor, "random" ) &&
-      !XParseColor( pDisplay, CMap, sColor, &Color ) )
-    fprintf( stderr,
-             "%s: color %s not found in database. Choosing to random...\n",
-             progname, sColor );
-
-  rgb_to_hsv( Color.red, Color.green, Color.blue, &nHue, &nSat, &nVal );
+       XWindowAttributes XWinAttribs;
+       XColor Color, *aColors;
+       signed short iColor;
+       float nHalfColors;
+       
+       XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
+       
+       Color.red =   RANDOM() % 0xFFFF;
+       Color.green = RANDOM() % 0xFFFF;
+       Color.blue =  RANDOM() % 0xFFFF;
+
+       if( strcasecmp( st->sColor, "random" ) && !XParseColor( st->dpy, XWinAttribs.colormap, st->sColor, &Color ) )
+               fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, st->sColor );
+
 #ifdef VERBOSE
-  printf( "RGB (%d, %d, %d) = HSV (%d, %g, %g)\n",
-          Color.red, Color.green, Color.blue, nHue, nSat, nVal );
+       printf( "%s: Base color (RGB): <%d, %d, %d>\n", progname, Color.red, Color.green, Color.blue );
 #endif  /*  VERBOSE */
 
-  if (*ncolorsP != 0)
-    free_colors (pDisplay, CMap, aXColors, *ncolorsP);
-
-  *ncolorsP = get_integer_resource ("ncolors", "Integer");
-  if (*ncolorsP <= 0) *ncolorsP = 64;
-
-  /* allocate two color ramps, black -> color -> white. */
-  {
-    int n1, n2;
-    n1 = *ncolorsP / 2;
-    make_color_ramp( pDisplay, CMap, 0, 0, 0, nHue, nSat, nVal,
-                     aXColors, &n1,
-                     False, True, False );
-    n2 = *ncolorsP - n1;
-    make_color_ramp( pDisplay, CMap, nHue, nSat, nVal, 0, 0, 1,
-                     aXColors + n1, &n2,
-                     False, True, False );
-    *ncolorsP = n1 + n2;
-  }
+       st->iColorCount = get_integer_resource(st->dpy,  "ncolors", "Integer" );
+       if( st->iColorCount <   2 )     st->iColorCount = 2;
+       if( st->iColorCount > 255 )     st->iColorCount = 255;
+
+       aColors    = calloc( st->iColorCount, sizeof(XColor) );
+       st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
+       
+       for( iColor=0; iColor<st->iColorCount; iColor++ )
+       {
+               nHalfColors = st->iColorCount / 2.0F;
+               /* Black -> Base Color */
+               if( iColor < (st->iColorCount/2) )
+               {
+                       aColors[ iColor ].red   = ( Color.red   / nHalfColors ) * iColor;
+                       aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor;
+                       aColors[ iColor ].blue  = ( Color.blue  / nHalfColors ) * iColor;
+               }
+               /* Base Color -> White */
+               else
+               {
+                       aColors[ iColor ].red   = ( ( ( 0xFFFF - Color.red )   / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red;
+                       aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green;
+                       aColors[ iColor ].blue  = ( ( ( 0xFFFF - Color.blue )  / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue;
+               }
+
+               if( !XAllocColor( st->dpy, XWinAttribs.colormap, &aColors[ iColor ] ) )
+               {
+                       /* start all over with less colors */   
+                       XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, iColor, 0 );
+                       free( aColors );
+                       free( st->aiColorVals );
+                       st->iColorCount--;
+                       aColors     = calloc( st->iColorCount, sizeof(XColor) );
+                       st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
+                       iColor = -1;
+               }
+               else
+                       st->aiColorVals[ iColor ] = aColors[ iColor ].pixel;
+       }
+
+       free( aColors );
+
+       XSetWindowBackground( st->dpy, st->window, st->aiColorVals[ 0 ] );
+
+       return st->aiColorVals;
 }
 
 
-static void Initialize( Display *pDisplay, Window Win,
-                        GC *pGC, XImage **pXImage,
-                        int *ncolorsP, XColor *aXColors )
+static void Initialize( struct state *st )
 {
-  XGCValues gcValues;
-  XWindowAttributes XWinAttribs;
-  int bpp;
-
-  /* Create the Image for drawing */
-  XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
-
-  /* Find the preferred bits-per-pixel. */
-  {
-    int i, pfvc = 0;
-    XPixmapFormatValues *pfv = XListPixmapFormats (pDisplay, &pfvc);
-    for (i = 0; i < pfvc; i++)
-      if (pfv[i].depth == XWinAttribs.depth)
-        {
-          bpp = pfv[i].bits_per_pixel;
-          break;
-        }
-    if (pfv)
-      XFree (pfv);
-  }
-
-  /*  Create the GC. */
-  *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
-
-  *pXImage = XCreateImage(pDisplay, XWinAttribs.visual,
-                         XWinAttribs.depth,
-                         ZPixmap, 0, NULL,
-                         XWinAttribs.width, XWinAttribs.height,
-                         BitmapPad(pDisplay), 0);
-  (*pXImage)->data = calloc((*pXImage)->bytes_per_line,
-                           (*pXImage)->height);
-
-  /*  These are precalculations used in Execute(). */
-  nMaxExtentX = ( XWinAttribs.width / 2 ) - 20;
-  nMaxExtentY = ( XWinAttribs.height / 2 ) - 20;
-  nMinExtentX = nMaxExtentX / 3;
-  nMinExtentY = nMaxExtentY / 3;
-  nHalfWidth = ( XWinAttribs.width / 2 ) - 16;
-  nHalfHeight = ( XWinAttribs.height / 2 ) - 16;
-
-  /*  Create the Sin and Cosine lookup tables. */
-  nDegreeCount = get_integer_resource( "degrees", "Integer" );
-  if( nDegreeCount < 90 ) nDegreeCount = 90;
-  if( nDegreeCount > 5400 ) nDegreeCount = 5400;
-  CreateTables( nDegreeCount );
-
-  /*  Get the colors. */
-  sColor = get_string_resource( "color", "Color" );
-  if( sColor == NULL)
-    SetPalette( pDisplay, Win, "random", ncolorsP, aXColors );
-  else
-    SetPalette( pDisplay, Win, sColor, ncolorsP, aXColors );
+       XGCValues gcValues;
+       XWindowAttributes XWinAttribs;
+       /*int iBitsPerPixel;*/
+
+       /* Create the Image for drawing */
+       XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
+
+#if 0
+  /* Find the preferred bits-per-pixel. (jwz) */
+       {
+               int i, pfvc = 0;
+               XPixmapFormatValues *pfv = XListPixmapFormats( st->dpy, &pfvc );
+               for( i=0; i<pfvc; i++ )
+                       if( pfv[ i ].depth == XWinAttribs.depth )
+                       {
+                               iBitsPerPixel = pfv[ i ].bits_per_pixel;
+                               break;
+                       }
+               if( pfv )
+                       XFree (pfv);
+       }
+#endif
+
+       /*  Create the GC. */
+       st->gc = XCreateGC( st->dpy, st->window, 0, &gcValues );
+
+       st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
+                                                         XWinAttribs.width, XWinAttribs.height, 8 /*BitmapPad( st->dpy )*/, 0 );
+       st->pImage->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height);
+
+       st->iWinWidth = XWinAttribs.width;
+       st->iWinHeight = XWinAttribs.height;
+
+       /*  These are precalculations used in Execute(). */
+       st->iBobDiameter = ( ( st->iWinWidth < st->iWinHeight ) ? st->iWinWidth : st->iWinHeight ) / 25;
+       st->iBobRadius = st->iBobDiameter / 2;
+#ifdef VERBOSE
+       printf( "%s: Bob Diameter = %d\n", progname, st->iBobDiameter );
+#endif
+
+       st->iWinCenterX = ( XWinAttribs.width / 2 ) - st->iBobRadius;
+       st->iWinCenterY = ( XWinAttribs.height / 2 ) - st->iBobRadius;
+
+       st->iVelocity = ( ( st->iWinWidth < st->iWinHeight ) ? st->iWinWidth : st->iWinHeight ) / 150;
+       
+       /*  Create the Sin and Cosine lookup tables. */
+       st->iDegreeCount = get_integer_resource(st->dpy,  "degrees", "Integer" );
+       if(      st->iDegreeCount == 0   ) st->iDegreeCount = ( XWinAttribs.width / 6 ) + 400;
+       else if( st->iDegreeCount < 90   ) st->iDegreeCount = 90;
+       else if( st->iDegreeCount > 5400 ) st->iDegreeCount = 5400;
+       CreateTables( st, st->iDegreeCount );
+#ifdef VERBOSE
+       printf( "%s: Using a %d degree circle.\n", progname, st->iDegreeCount );
+#endif /* VERBOSE */
+  
+       /*  Get the base color. */
+       st->sColor = get_string_resource(st->dpy,  "color", "Color" );
 }
 
 
-void screenhack(Display *pDisplay, Window Win )
+static void *
+shadebobs_init (Display *dpy, Window window)
 {
-  GC gc;
-  int ncolors = 0;
-  XColor aXColors[ 256 ];
-  XImage *pImage;
-  unsigned char nShadeBobCount, iShadeBob;
-  SShadeBob *aShadeBobs;
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
+
 #ifdef VERBOSE
-  time_t nTime = time( NULL );
-  unsigned short iFrame = 0;
+       time_t nTime = time( NULL );
+       unsigned short iFrame = 0;
 #endif  /*  VERBOSE */
-  int delay, cycles, i;
 
-  nShadeBobCount = get_integer_resource( "count", "Integer" );
-  if( nShadeBobCount > 64 ) nShadeBobCount = 64;
-  if( nShadeBobCount < 1 )  nShadeBobCount = 1;
+  st->dpy = dpy;
+  st->window = window;
 
-  if( ( aShadeBobs = calloc( nShadeBobCount, sizeof(SShadeBob) ) ) == NULL )
-  {
-    fprintf( stderr, "Could not allocate %d ShadeBobs\n", nShadeBobCount );
-    return;
-  }
+       st->nShadeBobCount = get_integer_resource(st->dpy,  "count", "Integer" );
+       if( st->nShadeBobCount > 64 ) st->nShadeBobCount = 64;
+       if( st->nShadeBobCount <  1 ) st->nShadeBobCount = 1;
+
+       if( ( st->aShadeBobs = calloc( st->nShadeBobCount, sizeof(SShadeBob) ) ) == NULL )
+       {
+               fprintf( stderr, "%s: Could not allocate %d ShadeBobs\n", progname, st->nShadeBobCount );
+                abort();
+       }
 #ifdef VERBOSE 
-  printf( "Allocated %d ShadeBobs\n", nShadeBobCount );
+       printf( "%s: Allocated %d ShadeBobs\n", progname, st->nShadeBobCount );
 #endif  /*  VERBOSE */
 
-  Initialize( pDisplay, Win, &gc, &pImage, &ncolors, aXColors );
+       Initialize( st );
+
+       for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
+               InitShadeBob( st, &st->aShadeBobs[ st->iShadeBob ], st->iShadeBob % 2 );
 
-  for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
-    InitShadeBob( &aShadeBobs[ iShadeBob ], iShadeBob % 2 );
+       st->delay = get_integer_resource(st->dpy,  "delay", "Integer" );
+       st->cycles = get_integer_resource(st->dpy,  "cycles", "Integer" ) * st->iDegreeCount;
 
-  delay = get_integer_resource( "delay", "Integer" );
-  cycles = get_integer_resource( "cycles", "Integer" ) * (nDegreeCount / 3);
-  i = cycles;
+        st->draw_i = 99999999;
+        return st;
+}
 
-  while (1)
-  {
-    screenhack_handle_events( pDisplay );
+static unsigned long
+shadebobs_draw (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
 
-    if (i++ >= cycles)
+  if( st->draw_i++ >= st->cycles )
     {
-      i = 0;
-      XClearWindow (pDisplay, Win);
-      memset (pImage->data, 0, pImage->bytes_per_line * pImage->height);
-      for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
-        ResetShadeBob( &aShadeBobs[ iShadeBob ] );
-      SetPalette( pDisplay, Win, sColor, &ncolors, aXColors );
+      XWindowAttributes XWinAttribs;
+      XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
+
+      st->draw_i = 0;
+#if 0
+      memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height );
+#else
+      {
+        /* fill the image with the actual value of the black pixel, not 0. */
+        unsigned long black = BlackPixelOfScreen (XWinAttribs.screen);
+        int x, y;
+        for (y = 0; y < XWinAttribs.height; y++)
+          for (x = 0; x < XWinAttribs.width; x++)
+            XPutPixel (st->pImage, x, y, black);
+      }
+#endif
+
+      for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
+        ResetShadeBob( st, &st->aShadeBobs[ st->iShadeBob ] );
+      XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, st->iColorCount, 0 );
+      free( st->aiColorVals );
+      st->aiColorVals = SetPalette( st );
+      XClearWindow( st->dpy, st->window );
     }
 
-    for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
-      Execute( &aShadeBobs[ iShadeBob ], pDisplay, Win, &gc,
-               pImage, ncolors, aXColors );
+  for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
+    Execute( st, &st->aShadeBobs[ st->iShadeBob ] );
 
-    if( delay && !(i % 4) )
-               usleep(delay);
+  return st->delay;
+}
 
-#ifdef VERBOSE
-    iFrame++;
-    if( nTime - time( NULL ) )
-    {
-      printf( "FPS: %d\n", iFrame );
-      nTime = time( NULL );
-      iFrame = 0;
-    }
-#endif  /*  VERBOSE */
-  }
+static void
+shadebobs_reshape (Display *dpy, Window window, void *closure, 
+                 unsigned int w, unsigned int h)
+{
+}
+
+static Bool
+shadebobs_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+  return False;
+}
 
-  free( anSinTable );
-  free( pImage->data );
-  XDestroyImage( pImage );
-  free( aShadeBobs );
+static void
+shadebobs_free (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+       free( st->anSinTable );
+       free( st->anCosTable );
+       /* free( st->pImage->data ); */
+       XDestroyImage( st->pImage );
+       for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
+               free( st->aShadeBobs[ st->iShadeBob ].anDeltaMap );
+       free( st->aShadeBobs );
+       free( st->aiColorVals );
 }
 
 
+XSCREENSAVER_MODULE ("ShadeBobs", shadebobs)
+
 /* End of Module - "shadebobs.c" */
+
+/* vim: ts=4
+ */