http://ftp.x.org/contrib/applications/xscreensaver-3.18.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  */
35
36 #include <math.h>
37 #include "screenhack.h"
38 #include <X11/Xutil.h>
39
40 /* #define VERBOSE */
41
42 char *progclass = "ShadeBobs";
43
44 char *defaults [] = {
45   "*degrees:  0",       /* default: Automatic degree calculation */
46   "*color:    random",
47   "*count:    4",
48   "*cycles:   10",
49   "*ncolors:  64",    /* changing this doesn't work particularly well */
50   "*delay:    5000",
51   0
52 };
53
54 XrmOptionDescRec options [] = {
55   { "-degrees", ".degrees", XrmoptionSepArg, 0 },
56   { "-color",   ".color",   XrmoptionSepArg, 0 },
57   { "-count",   ".count",   XrmoptionSepArg, 0 },
58   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
59   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
60   { 0, 0, 0, 0 }
61 };
62
63 static unsigned short nDegreeCount;
64 static double *anSinTable;
65 static unsigned short nMaxExtentX, nMaxExtentY;
66 static unsigned short nMinExtentX, nMinExtentY;
67 static unsigned short nHalfWidth, nHalfHeight;
68 static char *sColor;
69 static unsigned char nBobRadius, nBobDiameter; 
70 static float nExtentDelta; 
71
72 #define RANDOM() ((int) (random() & 0X7FFFFFFFL))
73
74
75 /* Ahem. Chocolate is a flavor; not a food. Thank you */
76
77
78 typedef struct
79 {
80   char *anDeltaMap;
81   double nVelocityX, nVelocityY;
82   double nAngleX, nAngleY;
83   float nExtentX, nExtentY;
84 } SShadeBob;
85
86
87 static void ResetShadeBob( SShadeBob *pShadeBob )
88 {
89   pShadeBob->nAngleX = RANDOM() % nDegreeCount;
90   pShadeBob->nAngleY = RANDOM() % nDegreeCount;
91
92   pShadeBob->nExtentX = (RANDOM() % (nMaxExtentX - nMinExtentX)) + nMinExtentX;
93   pShadeBob->nExtentY = (RANDOM() % (nMaxExtentY - nMinExtentY)) + nMinExtentY;
94 }
95
96
97 static void InitShadeBob( SShadeBob *pShadeBob, Bool bDark )
98 {
99   double nDelta;
100   char iWidth, iHeight;
101
102   if( ( pShadeBob->anDeltaMap = calloc( nBobDiameter * nBobDiameter, sizeof(char) ) ) == NULL )
103   {
104     fprintf( stderr, "Could not allocate Delta Map!\n" );
105     return;
106   }
107
108   for( iHeight=-nBobRadius; iHeight<nBobRadius; iHeight++ )
109     for( iWidth=-nBobRadius; iWidth<nBobRadius; iWidth++ )
110     {
111       nDelta = 9 - ( ( sqrt( pow( iWidth+0.5, 2 ) + pow( iHeight+0.5, 2 ) ) / nBobRadius ) * 8 );
112       if( nDelta < 0 )  nDelta = 0;
113       if( bDark ) nDelta = -nDelta;
114       pShadeBob->anDeltaMap[ ( iWidth + nBobRadius ) * nBobDiameter
115                            + iHeight + nBobRadius ] = (char)nDelta;
116     }
117   
118   ResetShadeBob( pShadeBob );
119 }
120
121
122 static void Execute( SShadeBob *pShadeBob, Display *pDisplay,
123                      Window MainWindow,
124                      GC *pGC, XImage *pXImage,
125                      int ncolors, XColor *aXColors )
126 {
127   long nColor;
128   short nIndex;
129   unsigned int nXPos, nYPos;
130   unsigned int iWidth, iHeight;
131
132   pShadeBob->nVelocityX += ( ( RANDOM() % 200 ) - 100 ) / 1000.0F;
133   pShadeBob->nVelocityY += ( ( RANDOM() % 200 ) - 100 ) / 1000.0F;
134
135   if(      pShadeBob->nVelocityX > 4 )  pShadeBob->nVelocityX = 4;
136   else if( pShadeBob->nVelocityX < 3 )  pShadeBob->nVelocityX = 3;
137   if(      pShadeBob->nVelocityY > 4 )  pShadeBob->nVelocityY = 4;
138   else if( pShadeBob->nVelocityY < 3 )  pShadeBob->nVelocityY = 3;
139
140   pShadeBob->nAngleX += pShadeBob->nVelocityX;
141   pShadeBob->nAngleY += pShadeBob->nVelocityY;
142
143   if( pShadeBob->nAngleX >= nDegreeCount ) pShadeBob->nAngleX -= nDegreeCount;
144   if( pShadeBob->nAngleY >= nDegreeCount ) pShadeBob->nAngleY -= nDegreeCount;
145
146   pShadeBob->nExtentX += ( ( ( RANDOM() % 5 ) - 2 ) / 2.0F ) * nExtentDelta;
147   if( pShadeBob->nExtentX > nMaxExtentX ) pShadeBob->nExtentX = nMaxExtentX;
148   if( pShadeBob->nExtentX < nMinExtentX ) pShadeBob->nExtentX = nMinExtentX;
149   pShadeBob->nExtentY += ( ( ( RANDOM() % 5 ) - 2 ) / 2.0F ) * nExtentDelta;
150   if( pShadeBob->nExtentY > nMaxExtentY ) pShadeBob->nExtentY = nMaxExtentY;
151   if( pShadeBob->nExtentY < nMinExtentY ) pShadeBob->nExtentY = nMinExtentY;
152
153   /* Trig is your friend :) */
154   nXPos = (unsigned int)(( anSinTable[ (int)pShadeBob->nAngleX ] * pShadeBob->nExtentX )
155                          + nHalfWidth);
156   nYPos = (unsigned int)(( anSinTable[ (int)pShadeBob->nAngleY ] * pShadeBob->nExtentY )
157                          + nHalfHeight);
158
159   for( iHeight=0; iHeight<nBobDiameter; iHeight++ )
160   {
161     for( iWidth=0; iWidth<nBobDiameter; iWidth++ )
162     {
163       nColor = XGetPixel( pXImage, nXPos + iWidth, nYPos + iHeight );
164
165       /*  FIXME: Here is a loop I'd love to take out. */
166       for( nIndex=0; nIndex < ncolors; nIndex++ )
167         if( aXColors[ nIndex ].pixel == nColor )
168           break;
169
170       nIndex += pShadeBob->anDeltaMap[ iWidth * nBobDiameter + iHeight ];
171       if( nIndex >= ncolors ) nIndex = ncolors-1;
172       if( nIndex < 0 )  nIndex = 0;
173
174       XPutPixel( pXImage, nXPos + iWidth, nYPos + iHeight,
175                  aXColors[ nIndex ].pixel );
176     }
177   }
178
179   XPutImage( pDisplay, MainWindow, *pGC, pXImage,
180              nXPos, nYPos, nXPos, nYPos, nBobDiameter, nBobDiameter );
181   XSync (pDisplay, False);
182 }
183
184
185 static void CreateTables( unsigned int nDegrees )
186 {
187   double nRadian;
188   unsigned int iDegree;
189   anSinTable = calloc( nDegrees, sizeof(double) );
190
191   for( iDegree=0; iDegree<nDegrees; iDegree++ )
192   {
193     nRadian = ( (double)(2*iDegree) / (double)nDegrees ) * M_PI;
194     anSinTable[ iDegree ] = sin( nRadian );
195   }
196 }
197
198
199 static void SetPalette(Display *pDisplay, Window Win, char *sColor,
200                        int *ncolorsP, XColor *aXColors )
201 {
202   XWindowAttributes XWinAttrib;
203   Colormap CMap;
204   XColor Color;
205   int nHue;
206   double nSat, nVal;
207
208   XGetWindowAttributes( pDisplay, Win, &XWinAttrib );
209   CMap = XWinAttrib.colormap;
210
211   Color.red = ( RANDOM() % 3 ) * 0x7FFF;    /*  Either full, half or none. */
212   Color.green = ( RANDOM() % 3 ) * 0x7FFF;
213   Color.blue = ( RANDOM() % 3 ) * 0x7FFF;
214
215   /*  If Color is black, grey, or white, then make SURE its a color */
216   if( Color.red == Color.green && Color.green == Color.blue )
217   { Color.red = 0xFFFF;
218     Color.green = ( RANDOM() % 3 ) * 0x7FFF;
219     Color.blue = 0; }
220
221   if( strcasecmp( sColor, "random" ) &&
222       !XParseColor( pDisplay, CMap, sColor, &Color ) )
223     fprintf( stderr,
224              "%s: color %s not found in database. Choosing to random...\n",
225              progname, sColor );
226
227   rgb_to_hsv( Color.red, Color.green, Color.blue, &nHue, &nSat, &nVal );
228 #ifdef VERBOSE
229   printf( "RGB (%d, %d, %d) = HSV (%d, %g, %g)\n",
230           Color.red, Color.green, Color.blue, nHue, nSat, nVal );
231 #endif  /*  VERBOSE */
232
233   if (*ncolorsP != 0)
234     free_colors (pDisplay, CMap, aXColors, *ncolorsP);
235
236   *ncolorsP = get_integer_resource ("ncolors", "Integer");
237   if (*ncolorsP <= 0) *ncolorsP = 64;
238
239   /* allocate two color ramps, black -> color -> white. */
240   {
241     int n1, n2;
242     n1 = *ncolorsP / 2;
243     make_color_ramp( pDisplay, CMap, 0, 0, 0, nHue, nSat, nVal,
244                      aXColors, &n1,
245                      False, True, False );
246     n2 = *ncolorsP - n1;
247     make_color_ramp( pDisplay, CMap, nHue, nSat, nVal, 0, 0, 1,
248                      aXColors + n1, &n2,
249                      False, True, False );
250     *ncolorsP = n1 + n2;
251   }
252
253   XSetWindowBackground( pDisplay, Win, aXColors[ 0 ].pixel );
254 }
255
256
257 static void Initialize( Display *pDisplay, Window Win,
258                         GC *pGC, XImage **pXImage,
259                         int *ncolorsP, XColor *aXColors )
260 {
261   XGCValues gcValues;
262   XWindowAttributes XWinAttribs;
263   int bpp;
264
265   /* Create the Image for drawing */
266   XGetWindowAttributes( pDisplay, Win, &XWinAttribs );
267
268   /* Find the preferred bits-per-pixel. */
269   {
270     int i, pfvc = 0;
271     XPixmapFormatValues *pfv = XListPixmapFormats (pDisplay, &pfvc);
272     for (i = 0; i < pfvc; i++)
273       if (pfv[i].depth == XWinAttribs.depth)
274         {
275           bpp = pfv[i].bits_per_pixel;
276           break;
277         }
278     if (pfv)
279       XFree (pfv);
280   }
281
282   /*  Create the GC. */
283   *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
284
285   *pXImage = XCreateImage(pDisplay, XWinAttribs.visual,
286                           XWinAttribs.depth,
287                           ZPixmap, 0, NULL,
288                           XWinAttribs.width, XWinAttribs.height,
289                           BitmapPad(pDisplay), 0);
290   (*pXImage)->data = calloc((*pXImage)->bytes_per_line,
291                             (*pXImage)->height);
292
293   /*  These are precalculations used in Execute(). */
294   nBobDiameter = XWinAttribs.width / 25;
295   nBobRadius = nBobDiameter / 2;
296   nExtentDelta = nBobRadius / 5.0F;
297 #ifdef VERBOSE
298   printf( "Bob Diameter = %d\n", nBobDiameter );
299 #endif
300
301   nHalfWidth = ( XWinAttribs.width / 2 ) - nBobRadius;
302   nHalfHeight = ( XWinAttribs.height / 2 ) - nBobRadius;
303   nMaxExtentX = nHalfWidth - nBobRadius;
304   nMaxExtentY = nHalfHeight - nBobRadius;
305   nMinExtentX = nMaxExtentX / 3;
306   nMinExtentY = nMaxExtentY / 3;
307   
308   /*  Create the Sin and Cosine lookup tables. */
309   nDegreeCount = get_integer_resource( "degrees", "Integer" );
310   if(      nDegreeCount == 0   ) nDegreeCount = ( XWinAttribs.width / 6 ) + 400;
311   else if( nDegreeCount < 90   ) nDegreeCount = 90;
312   else if( nDegreeCount > 5400 ) nDegreeCount = 5400;
313   CreateTables( nDegreeCount );
314 #ifdef VERBOSE
315   printf( "Using a %d degree circle.\n", nDegreeCount );
316 #endif /* VERBOSE */
317   
318   /*  Get the colors. */
319   sColor = get_string_resource( "color", "Color" );
320   if( sColor == NULL)
321     SetPalette( pDisplay, Win, "random", ncolorsP, aXColors );
322   else
323     SetPalette( pDisplay, Win, sColor, ncolorsP, aXColors );
324 }
325
326
327 void screenhack(Display *pDisplay, Window Win )
328 {
329   GC gc;
330   int ncolors = 0;
331   XColor aXColors[ 256 ];
332   XImage *pImage;
333   unsigned char nShadeBobCount, iShadeBob;
334   SShadeBob *aShadeBobs;
335 #ifdef VERBOSE
336   time_t nTime = time( NULL );
337   unsigned short iFrame = 0;
338 #endif  /*  VERBOSE */
339   int delay, cycles, i;
340
341   nShadeBobCount = get_integer_resource( "count", "Integer" );
342   if( nShadeBobCount > 64 ) nShadeBobCount = 64;
343   if( nShadeBobCount < 1 )  nShadeBobCount = 1;
344
345   if( ( aShadeBobs = calloc( nShadeBobCount, sizeof(SShadeBob) ) ) == NULL )
346   {
347     fprintf( stderr, "Could not allocate %d ShadeBobs\n", nShadeBobCount );
348     return;
349   }
350 #ifdef VERBOSE 
351   printf( "Allocated %d ShadeBobs\n", nShadeBobCount );
352 #endif  /*  VERBOSE */
353
354   Initialize( pDisplay, Win, &gc, &pImage, &ncolors, aXColors );
355
356   for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
357     InitShadeBob( &aShadeBobs[ iShadeBob ], iShadeBob % 2 );
358
359   delay = get_integer_resource( "delay", "Integer" );
360   cycles = get_integer_resource( "cycles", "Integer" ) * nDegreeCount;
361   i = cycles;
362
363   while( 1 )
364   {
365     screenhack_handle_events( pDisplay );
366
367     if (i++ >= cycles)
368     {
369       i = 0;
370       XClearWindow( pDisplay, Win );
371       memset( pImage->data, 0, pImage->bytes_per_line * pImage->height );
372       for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
373         ResetShadeBob( &aShadeBobs[ iShadeBob ] );
374       SetPalette( pDisplay, Win, sColor, &ncolors, aXColors );
375     }
376
377     for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
378       Execute( &aShadeBobs[ iShadeBob ], pDisplay, Win, &gc,
379                pImage, ncolors, aXColors );
380
381     if( delay && !(i % 4) )
382                 usleep(delay);
383
384 #ifdef VERBOSE
385     iFrame++;
386     if( nTime - time( NULL ) )
387     {
388       printf( "FPS: %d\n", iFrame );
389       nTime = time( NULL );
390       iFrame = 0;
391     }
392 #endif  /*  VERBOSE */
393   }
394
395   free( anSinTable );
396   free( pImage->data );
397   XDestroyImage( pImage );
398   for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
399     free( aShadeBobs[ iShadeBob ].anDeltaMap );
400   free( aShadeBobs );
401 }
402
403
404 /* End of Module - "shadebobs.c" */