http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[xscreensaver] / hacks / shadebobs.c
1 /* shadebobs, Copyright (c) 1999 Shane Smit <blackend@inconnect.com>
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 - "shadebobs.c"
12  *
13  * Description:
14  *  There are two little shading circles (bobs) that zip around the screen.
15  *  one of them shades up towards white, and the other shades down toward
16  *  black.
17  *  This keeps the screen in color balance at a chosen color.
18  *
19  *  Its kinda like 'The Force'
20  *   There is a light side, a dark side, and it keeps the world in balance.
21  *
22  * [05/23/99] - Shane Smit: Creation
23  * [05/26/99] - Shane Smit: Port to C/screenhack for use with XScreenSaver
24  * [06/11/99] - Shane Smit: Stopped trying to rape the palette.
25  * [06/20/99] - jwz: cleaned up ximage handling, gave resoources short names,
26  *                introduced delay, made it restart after N iterations.
27  * [06/21/99] - Shane Smit: Modified default values slightly, color changes
28  *                on cycle, and the extents of the sinus pattern change in
29  *                real-time.
30  * [06/22/99] - Shane Smit: Fixed delay to be fast and use little CPU :).
31  * [09/17/99] - Shane Smit: Made all calculations based on the size of the
32  *                window. Thus, it'll look the same at 100x100 as it does at
33  *                1600x1200 ( Only smaller :).
34  * [04/24/00] - Shane Smit: Revamped entire source code:
35  *                Shade Bob movement is calculated very differently.
36  *                Base color can be any color now.
37  */
38
39 #include <math.h>
40 #include "screenhack.h"
41 #include <X11/Xutil.h>
42
43 /* #define VERBOSE */
44
45 char *progclass = "ShadeBobs";
46
47 char *defaults [] = {
48   ".background: black",
49   ".foreground: white",
50   "*degrees:  0",       /* default: Automatic degree calculation */
51   "*color:    random",
52   "*count:    4",
53   "*cycles:   10",
54   "*ncolors:  64",    /* changing this doesn't work particularly well */
55   "*delay:    5000",
56   0
57 };
58
59 XrmOptionDescRec options [] = {
60   { "-degrees", ".degrees", XrmoptionSepArg, 0 },
61   { "-color",   ".color",   XrmoptionSepArg, 0 },
62   { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
63   { "-count",   ".count",   XrmoptionSepArg, 0 },
64   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
65   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
66   { 0, 0, 0, 0 }
67 };
68
69 static unsigned short iDegreeCount;
70 static double *anSinTable, *anCosTable;
71 static unsigned short iWinWidth, iWinHeight;
72 static unsigned short iWinCenterX, iWinCenterY;
73 static char *sColor;
74 static unsigned char iBobRadius, iBobDiameter; 
75 static unsigned char iVelocity;
76
77 #define RANDOM() ((int) (random() & 0X7FFFFFFFL))
78
79
80 /* Ahem. Chocolate is a flavor; not a food. Thank you */
81
82
83 typedef struct
84 {
85         signed char *anDeltaMap;
86         double nAngle, nAngleDelta, nAngleInc;
87         double nPosX, nPosY;
88 } SShadeBob;
89
90
91 static void ResetShadeBob( SShadeBob *pShadeBob )
92 {
93         pShadeBob->nPosX = RANDOM() % iWinWidth;
94         pShadeBob->nPosY = RANDOM() % iWinHeight;
95         pShadeBob->nAngle = RANDOM() % iDegreeCount;
96         pShadeBob->nAngleDelta = ( RANDOM() % iDegreeCount ) - ( iDegreeCount / 2.0F );
97         pShadeBob->nAngleInc = pShadeBob->nAngleDelta / 50.0F; 
98         if( pShadeBob->nAngleInc == 0.0F )      
99                 pShadeBob->nAngleInc = ( pShadeBob->nAngleDelta > 0.0F ) ? 0.0001F : -0.0001F;
100 }
101
102
103 static void InitShadeBob( SShadeBob *pShadeBob, Bool bDark )
104 {
105         double nDelta;
106         int iWidth, iHeight;
107
108         if( ( pShadeBob->anDeltaMap = calloc( iBobDiameter * iBobDiameter, sizeof(char) ) ) == NULL )
109         {
110                 fprintf( stderr, "%s: Could not allocate Delta Map!\n", progclass );
111                 return;
112         }
113
114         for( iHeight=-iBobRadius; iHeight<iBobRadius; iHeight++ )
115                 for( iWidth=-iBobRadius; iWidth<iBobRadius; iWidth++ )
116                 {
117                         nDelta = 9 - ( ( sqrt( pow( iWidth+0.5, 2 ) + pow( iHeight+0.5, 2 ) ) / iBobRadius ) * 8 );
118                         if( nDelta < 0 )  nDelta = 0;
119                         if( bDark ) nDelta = -nDelta;
120                         pShadeBob->anDeltaMap[ ( iWidth + iBobRadius ) * iBobDiameter + iHeight + iBobRadius ] = (char)nDelta;
121                 }
122   
123         ResetShadeBob( pShadeBob );
124 }
125
126
127 /* A delta is calculated, and the shadebob turns at an increment.  When the delta
128  * falls to 0, a new delta and increment are calculated. */
129 static void MoveShadeBob( SShadeBob *pShadeBob )
130 {
131         pShadeBob->nAngle          += pShadeBob->nAngleInc;
132         pShadeBob->nAngleDelta -= pShadeBob->nAngleInc;
133
134         if( pShadeBob->nAngle >= iDegreeCount ) pShadeBob->nAngle -= iDegreeCount;
135         else if( pShadeBob->nAngle < 0 )                pShadeBob->nAngle += iDegreeCount;
136         
137         if( ( pShadeBob->nAngleInc>0.0F  && pShadeBob->nAngleDelta<pShadeBob->nAngleInc ) ||
138             ( pShadeBob->nAngleInc<=0.0F && pShadeBob->nAngleDelta>pShadeBob->nAngleInc ) )
139         {
140                 pShadeBob->nAngleDelta = ( RANDOM() % iDegreeCount ) - ( iDegreeCount / 2.0F );
141                 pShadeBob->nAngleInc = pShadeBob->nAngleDelta / 50.0F;
142                 if( pShadeBob->nAngleInc == 0.0F )
143                         pShadeBob->nAngleInc = ( pShadeBob->nAngleDelta > 0.0F ) ? 0.0001F : -0.0001F;
144         }
145         
146         pShadeBob->nPosX = ( anSinTable[ (int)pShadeBob->nAngle ] * iVelocity ) + pShadeBob->nPosX;
147         pShadeBob->nPosY = ( anCosTable[ (int)pShadeBob->nAngle ] * iVelocity ) + pShadeBob->nPosY;
148
149         /* This wraps it around the screen. */
150         if( pShadeBob->nPosX >= iWinWidth )     pShadeBob->nPosX -= iWinWidth;
151         else if( pShadeBob->nPosX < 0 )         pShadeBob->nPosX += iWinWidth;
152         
153         if( pShadeBob->nPosY >= iWinHeight )    pShadeBob->nPosY -= iWinHeight;
154         else if( pShadeBob->nPosY < 0 )                 pShadeBob->nPosY += iWinHeight;
155 }
156
157
158 static void Execute( SShadeBob *pShadeBob, Display *pDisplay,
159                      Window MainWindow,
160                      GC *pGC, XImage *pImage,
161                      signed short iColorCount, unsigned long *aiColorVals )
162 {
163         unsigned long iColor;
164         short iColorVal;
165         int iPixelX, iPixelY;
166         unsigned int iWidth, iHeight;
167
168         MoveShadeBob( pShadeBob );
169         
170         for( iHeight=0; iHeight<iBobDiameter; iHeight++ )
171         {
172                 iPixelY = pShadeBob->nPosY + iHeight;
173                 if( iPixelY >= iWinHeight )     iPixelY -= iWinHeight;
174
175                 for( iWidth=0; iWidth<iBobDiameter; iWidth++ )
176                 {
177                         iPixelX = pShadeBob->nPosX + iWidth;
178                         if( iPixelX >= iWinWidth )      iPixelX -= iWinWidth;
179
180                         iColor = XGetPixel( pImage, iPixelX, iPixelY );
181
182                         /*  FIXME: Here is a loop I'd love to take out. */
183                         for( iColorVal=0; iColorVal<iColorCount; iColorVal++ )
184                                 if( aiColorVals[ iColorVal ] == iColor )
185                                         break;
186
187                         iColorVal += pShadeBob->anDeltaMap[ iWidth * iBobDiameter + iHeight ];
188                         if( iColorVal >= iColorCount ) iColorVal = iColorCount - 1;
189                         if( iColorVal < 0 )                        iColorVal = 0;
190
191                         XPutPixel( pImage, iPixelX, iPixelY, aiColorVals[ iColorVal ] );
192                 }
193         }
194
195         /* FIXME: if it's next to the top or left sides of screen this will break. However, it's not noticable. */
196         XPutImage( pDisplay, MainWindow, *pGC, pImage,
197              pShadeBob->nPosX, pShadeBob->nPosY, pShadeBob->nPosX, pShadeBob->nPosY, iBobDiameter, iBobDiameter );
198         XSync( pDisplay, False );
199 }
200
201
202 static void CreateTables( unsigned int nDegrees )
203 {
204         double nRadian;
205         unsigned int iDegree;
206         anSinTable = calloc( nDegrees, sizeof(double) );
207         anCosTable = calloc( nDegrees, sizeof(double) );
208
209         for( iDegree=0; iDegree<nDegrees; iDegree++ )
210         {
211                 nRadian = ( (double)(2*iDegree) / (double)nDegrees ) * M_PI;
212                 anSinTable[ iDegree ] = sin( nRadian );
213                 anCosTable[ iDegree ] = cos( nRadian );
214         }
215 }
216
217
218 static unsigned long * SetPalette(Display *pDisplay, Window Win, char *sColor, signed short *piColorCount )
219 {
220         XWindowAttributes XWinAttribs;
221         XColor Color, *aColors;
222         unsigned long *aiColorVals;
223         signed short iColor;
224         float nHalfColors;
225         
226         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
227         
228         Color.red =   RANDOM() % 0xFFFF;
229         Color.green = RANDOM() % 0xFFFF;
230         Color.blue =  RANDOM() % 0xFFFF;
231
232         if( strcasecmp( sColor, "random" ) && !XParseColor( pDisplay, XWinAttribs.colormap, sColor, &Color ) )
233                 fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, sColor );
234
235 #ifdef VERBOSE
236         printf( "%s: Base color (RGB): <%d, %d, %d>\n", progclass, Color.red, Color.green, Color.blue );
237 #endif  /*  VERBOSE */
238
239         *piColorCount = get_integer_resource( "ncolors", "Integer" );
240         if( *piColorCount <   2 )       *piColorCount = 2;
241         if( *piColorCount > 255 )       *piColorCount = 255;
242
243         aColors    = calloc( *piColorCount, sizeof(XColor) );
244         aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
245         
246         for( iColor=0; iColor<*piColorCount; iColor++ )
247         {
248                 nHalfColors = *piColorCount / 2.0F;
249                 /* Black -> Base Color */
250                 if( iColor < (*piColorCount/2) )
251                 {
252                         aColors[ iColor ].red   = ( Color.red   / nHalfColors ) * iColor;
253                         aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor;
254                         aColors[ iColor ].blue  = ( Color.blue  / nHalfColors ) * iColor;
255                 }
256                 /* Base Color -> White */
257                 else
258                 {
259                         aColors[ iColor ].red   = ( ( ( 0xFFFF - Color.red )   / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red;
260                         aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green;
261                         aColors[ iColor ].blue  = ( ( ( 0xFFFF - Color.blue )  / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue;
262                 }
263
264                 if( !XAllocColor( pDisplay, XWinAttribs.colormap, &aColors[ iColor ] ) )
265                 {
266                         /* start all over with less colors */   
267                         XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColor, 0 );
268                         free( aColors );
269                         free( aiColorVals );
270                         piColorCount--;
271                         aColors     = calloc( *piColorCount, sizeof(XColor) );
272                         aiColorVals = calloc( *piColorCount, sizeof(unsigned long) );
273                         iColor = -1;
274                 }
275                 else
276                         aiColorVals[ iColor ] = aColors[ iColor ].pixel;
277         }
278
279         free( aColors );
280
281         XSetWindowBackground( pDisplay, Win, aiColorVals[ 0 ] );
282
283         return aiColorVals;
284 }
285
286
287 static void Initialize( Display *pDisplay, Window Win, GC *pGC, XImage **ppImage )
288 {
289         XGCValues gcValues;
290         XWindowAttributes XWinAttribs;
291         int iBitsPerPixel;
292
293         /* Create the Image for drawing */
294         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
295
296         /* Find the preferred bits-per-pixel. (jwz) */
297         {
298                 int i, pfvc = 0;
299                 XPixmapFormatValues *pfv = XListPixmapFormats( pDisplay, &pfvc );
300                 for( i=0; i<pfvc; i++ )
301                         if( pfv[ i ].depth == XWinAttribs.depth )
302                         {
303                                 iBitsPerPixel = pfv[ i ].bits_per_pixel;
304                                 break;
305                         }
306                 if( pfv )
307                         XFree (pfv);
308         }
309
310         /*  Create the GC. */
311         *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
312
313         *ppImage = XCreateImage( pDisplay, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
314                                                           XWinAttribs.width, XWinAttribs.height, BitmapPad( pDisplay ), 0 );
315         (*ppImage)->data = calloc((*ppImage)->bytes_per_line, (*ppImage)->height);
316
317         iWinWidth = XWinAttribs.width;
318         iWinHeight = XWinAttribs.height;
319
320         /*  These are precalculations used in Execute(). */
321         iBobDiameter = ( ( iWinWidth < iWinHeight ) ? iWinWidth : iWinHeight ) / 25;
322         iBobRadius = iBobDiameter / 2;
323 #ifdef VERBOSE
324         printf( "%s: Bob Diameter = %d\n", progclass, iBobDiameter );
325 #endif
326
327         iWinCenterX = ( XWinAttribs.width / 2 ) - iBobRadius;
328         iWinCenterY = ( XWinAttribs.height / 2 ) - iBobRadius;
329
330         iVelocity = ( ( iWinWidth < iWinHeight ) ? iWinWidth : iWinHeight ) / 150;
331         
332         /*  Create the Sin and Cosine lookup tables. */
333         iDegreeCount = get_integer_resource( "degrees", "Integer" );
334         if(      iDegreeCount == 0   ) iDegreeCount = ( XWinAttribs.width / 6 ) + 400;
335         else if( iDegreeCount < 90   ) iDegreeCount = 90;
336         else if( iDegreeCount > 5400 ) iDegreeCount = 5400;
337         CreateTables( iDegreeCount );
338 #ifdef VERBOSE
339         printf( "%s: Using a %d degree circle.\n", progclass );
340 #endif /* VERBOSE */
341   
342         /*  Get the base color. */
343         sColor = get_string_resource( "color", "Color" );
344 }
345
346
347 void screenhack(Display *pDisplay, Window Win )
348 {
349         GC gc;
350         signed short iColorCount = 0;
351         unsigned long *aiColorVals = NULL;
352         XImage *pImage = NULL;
353         unsigned char nShadeBobCount, iShadeBob;
354         SShadeBob *aShadeBobs;
355 #ifdef VERBOSE
356         time_t nTime = time( NULL );
357         unsigned short iFrame = 0;
358 #endif  /*  VERBOSE */
359         int delay, cycles, i;
360
361         nShadeBobCount = get_integer_resource( "count", "Integer" );
362         if( nShadeBobCount > 64 ) nShadeBobCount = 64;
363         if( nShadeBobCount <  1 ) nShadeBobCount = 1;
364
365         if( ( aShadeBobs = calloc( nShadeBobCount, sizeof(SShadeBob) ) ) == NULL )
366         {
367                 fprintf( stderr, "%s: Could not allocate %d ShadeBobs\n", progclass, nShadeBobCount );
368                 return;
369         }
370 #ifdef VERBOSE 
371         printf( "%s: Allocated %d ShadeBobs\n", progclass, nShadeBobCount );
372 #endif  /*  VERBOSE */
373
374         Initialize( pDisplay, Win, &gc, &pImage );
375
376         for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
377                 InitShadeBob( &aShadeBobs[ iShadeBob ], iShadeBob % 2 );
378
379         delay = get_integer_resource( "delay", "Integer" );
380         cycles = get_integer_resource( "cycles", "Integer" ) * iDegreeCount;
381         i = cycles;
382
383         while( 1 )
384         {
385                 screenhack_handle_events( pDisplay );
386
387                 if( i++ >= cycles )
388                 {
389                         XWindowAttributes XWinAttribs;
390                         XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
391
392                         i = 0;
393                         memset( pImage->data, 0, pImage->bytes_per_line * pImage->height );
394                         for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
395                                 ResetShadeBob( &aShadeBobs[ iShadeBob ] );
396                         XFreeColors( pDisplay, XWinAttribs.colormap, aiColorVals, iColorCount, 0 );
397                         free( aiColorVals );
398                         aiColorVals = SetPalette( pDisplay, Win, sColor, &iColorCount );
399                         XClearWindow( pDisplay, Win );
400                 }
401
402                 for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
403                         Execute( &aShadeBobs[ iShadeBob ], pDisplay, Win, &gc, pImage, iColorCount, aiColorVals );
404
405                 if( delay && !(i % 4) )
406                         usleep(delay);
407
408 #ifdef VERBOSE
409                 iFrame++;
410                 if( nTime - time( NULL ) )
411                 {
412                         printf( "%s: %d FPS\n", progclass, iFrame );
413                         nTime = time( NULL );
414                         iFrame = 0;
415                 }
416 #endif  /*  VERBOSE */
417         }
418
419         free( anSinTable );
420         free( anCosTable );
421         free( pImage->data );
422         XDestroyImage( pImage );
423         for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
424                 free( aShadeBobs[ iShadeBob ].anDeltaMap );
425         free( aShadeBobs );
426         free( aiColorVals );
427 }
428
429
430 /* End of Module - "shadebobs.c" */
431
432 /* vim: ts=4
433  */