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