1 /* shadebobs, Copyright (c) 1999 Shane Smit <blackend@inconnect.com>
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
11 * Module - "shadebobs.c"
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
17 * This keeps the screen in color balance at a chosen color.
19 * Its kinda like 'The Force'
20 * There is a light side, a dark side, and it keeps the world in balance.
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
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.
41 #include "screenhack.h"
45 static const char *shadebobs_defaults [] = {
49 "*degrees: 0", /* default: Automatic degree calculation */
53 "*ncolors: 64", /* changing this doesn't work particularly well */
56 "*ignoreRotation: True",
61 static XrmOptionDescRec shadebobs_options [] = {
62 { "-degrees", ".degrees", XrmoptionSepArg, 0 },
63 { "-color", ".color", XrmoptionSepArg, 0 },
64 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
65 { "-count", ".count", XrmoptionSepArg, 0 },
66 { "-delay", ".delay", XrmoptionSepArg, 0 },
67 { "-cycles", ".cycles", XrmoptionSepArg, 0 },
71 /* Ahem. Chocolate is a flavor; not a food. Thank you */
76 signed char *anDeltaMap;
77 double nAngle, nAngleDelta, nAngleInc;
84 unsigned short iDegreeCount;
85 double *anSinTable, *anCosTable;
86 unsigned short iWinWidth, iWinHeight;
87 unsigned short iWinCenterX, iWinCenterY;
89 unsigned char iBobRadius, iBobDiameter;
90 unsigned char iVelocity;
92 unsigned long *aiColorVals;
93 signed short iColorCount;
96 unsigned char nShadeBobCount, iShadeBob;
97 SShadeBob *aShadeBobs;
103 #define RANDOM() ((int) (random() & 0X7FFFFFFFL))
107 static void ResetShadeBob( struct state *st, SShadeBob *pShadeBob )
109 pShadeBob->nPosX = RANDOM() % st->iWinWidth;
110 pShadeBob->nPosY = RANDOM() % st->iWinHeight;
111 pShadeBob->nAngle = RANDOM() % st->iDegreeCount;
112 pShadeBob->nAngleDelta = ( RANDOM() % st->iDegreeCount ) - ( st->iDegreeCount / 2.0F );
113 pShadeBob->nAngleInc = pShadeBob->nAngleDelta / 50.0F;
114 if( pShadeBob->nAngleInc == 0.0F )
115 pShadeBob->nAngleInc = ( pShadeBob->nAngleDelta > 0.0F ) ? 0.0001F : -0.0001F;
119 static void InitShadeBob( struct state *st, SShadeBob *pShadeBob, Bool bDark )
124 if( ( pShadeBob->anDeltaMap = calloc( st->iBobDiameter * st->iBobDiameter, sizeof(char) ) ) == NULL )
126 fprintf( stderr, "%s: Could not allocate Delta Map!\n", progname );
130 for( iHeight=-st->iBobRadius; iHeight<st->iBobRadius; iHeight++ )
131 for( iWidth=-st->iBobRadius; iWidth<st->iBobRadius; iWidth++ )
133 nDelta = 9 - ( ( sqrt( pow( iWidth+0.5, 2 ) + pow( iHeight+0.5, 2 ) ) / st->iBobRadius ) * 8 );
134 if( nDelta < 0 ) nDelta = 0;
135 if( bDark ) nDelta = -nDelta;
136 pShadeBob->anDeltaMap[ ( iWidth + st->iBobRadius ) * st->iBobDiameter + iHeight + st->iBobRadius ] = (char)nDelta;
139 ResetShadeBob( st, pShadeBob );
143 /* A delta is calculated, and the shadebob turns at an increment. When the delta
144 * falls to 0, a new delta and increment are calculated. */
145 static void MoveShadeBob( struct state *st, SShadeBob *pShadeBob )
147 pShadeBob->nAngle += pShadeBob->nAngleInc;
148 pShadeBob->nAngleDelta -= pShadeBob->nAngleInc;
150 /* Since it can happen that nAngle < 0 and nAngle + iDegreeCount >= iDegreeCount
151 on floating point, we set some marginal value.
153 if( pShadeBob->nAngle + 0.5 >= st->iDegreeCount ) pShadeBob->nAngle -= st->iDegreeCount;
154 else if( pShadeBob->nAngle < -0.5 ) pShadeBob->nAngle += st->iDegreeCount;
156 if( ( pShadeBob->nAngleInc>0.0F && pShadeBob->nAngleDelta<pShadeBob->nAngleInc ) ||
157 ( pShadeBob->nAngleInc<=0.0F && pShadeBob->nAngleDelta>pShadeBob->nAngleInc ) )
159 pShadeBob->nAngleDelta = ( RANDOM() % st->iDegreeCount ) - ( st->iDegreeCount / 2.0F );
160 pShadeBob->nAngleInc = pShadeBob->nAngleDelta / 50.0F;
161 if( pShadeBob->nAngleInc == 0.0F )
162 pShadeBob->nAngleInc = ( pShadeBob->nAngleDelta > 0.0F ) ? 0.0001F : -0.0001F;
165 pShadeBob->nPosX = ( st->anSinTable[ (int)pShadeBob->nAngle ] * st->iVelocity ) + pShadeBob->nPosX;
166 pShadeBob->nPosY = ( st->anCosTable[ (int)pShadeBob->nAngle ] * st->iVelocity ) + pShadeBob->nPosY;
168 /* This wraps it around the screen. */
169 if( pShadeBob->nPosX >= st->iWinWidth ) pShadeBob->nPosX -= st->iWinWidth;
170 else if( pShadeBob->nPosX < 0 ) pShadeBob->nPosX += st->iWinWidth;
172 if( pShadeBob->nPosY >= st->iWinHeight ) pShadeBob->nPosY -= st->iWinHeight;
173 else if( pShadeBob->nPosY < 0 ) pShadeBob->nPosY += st->iWinHeight;
177 static void Execute( struct state *st, SShadeBob *pShadeBob )
179 unsigned long iColor;
181 int iPixelX, iPixelY;
182 unsigned int iWidth, iHeight;
184 MoveShadeBob( st, pShadeBob );
186 for( iHeight=0; iHeight<st->iBobDiameter; iHeight++ )
188 iPixelY = pShadeBob->nPosY + iHeight;
189 if( iPixelY >= st->iWinHeight ) iPixelY -= st->iWinHeight;
191 for( iWidth=0; iWidth<st->iBobDiameter; iWidth++ )
193 iPixelX = pShadeBob->nPosX + iWidth;
194 if( iPixelX >= st->iWinWidth ) iPixelX -= st->iWinWidth;
196 iColor = XGetPixel( st->pImage, iPixelX, iPixelY );
198 /* FIXME: Here is a loop I'd love to take out. */
199 for( iColorVal=0; iColorVal<st->iColorCount; iColorVal++ )
200 if( st->aiColorVals[ iColorVal ] == iColor )
203 iColorVal += pShadeBob->anDeltaMap[ iWidth * st->iBobDiameter + iHeight ];
204 if( iColorVal >= st->iColorCount ) iColorVal = st->iColorCount - 1;
205 if( iColorVal < 0 ) iColorVal = 0;
207 XPutPixel( st->pImage, iPixelX, iPixelY, st->aiColorVals[ iColorVal ] );
211 /* FIXME: if it's next to the top or left sides of screen this will break. However, it's not noticable. */
212 XPutImage( st->dpy, st->window, st->gc, st->pImage,
213 pShadeBob->nPosX, pShadeBob->nPosY, pShadeBob->nPosX, pShadeBob->nPosY, st->iBobDiameter, st->iBobDiameter );
217 static void CreateTables( struct state *st, unsigned int nDegrees )
220 unsigned int iDegree;
221 st->anSinTable = calloc( nDegrees, sizeof(double) );
222 st->anCosTable = calloc( nDegrees, sizeof(double) );
224 for( iDegree=0; iDegree<nDegrees; iDegree++ )
226 nRadian = ( (double)(2*iDegree) / (double)nDegrees ) * M_PI;
227 st->anSinTable[ iDegree ] = sin( nRadian );
228 st->anCosTable[ iDegree ] = cos( nRadian );
233 static unsigned long * SetPalette(struct state *st )
235 XWindowAttributes XWinAttribs;
236 XColor Color, *aColors;
240 XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
242 Color.red = RANDOM() % 0xFFFF;
243 Color.green = RANDOM() % 0xFFFF;
244 Color.blue = RANDOM() % 0xFFFF;
246 if( strcasecmp( st->sColor, "random" ) && !XParseColor( st->dpy, XWinAttribs.colormap, st->sColor, &Color ) )
247 fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, st->sColor );
250 printf( "%s: Base color (RGB): <%d, %d, %d>\n", progname, Color.red, Color.green, Color.blue );
253 st->iColorCount = get_integer_resource(st->dpy, "ncolors", "Integer" );
254 if( st->iColorCount < 2 ) st->iColorCount = 2;
255 if( st->iColorCount > 255 ) st->iColorCount = 255;
257 aColors = calloc( st->iColorCount, sizeof(XColor) );
258 st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
260 for( iColor=0; iColor<st->iColorCount; iColor++ )
262 nHalfColors = st->iColorCount / 2.0F;
263 /* Black -> Base Color */
264 if( iColor < (st->iColorCount/2) )
266 aColors[ iColor ].red = ( Color.red / nHalfColors ) * iColor;
267 aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor;
268 aColors[ iColor ].blue = ( Color.blue / nHalfColors ) * iColor;
270 /* Base Color -> White */
273 aColors[ iColor ].red = ( ( ( 0xFFFF - Color.red ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red;
274 aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green;
275 aColors[ iColor ].blue = ( ( ( 0xFFFF - Color.blue ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue;
278 if( !XAllocColor( st->dpy, XWinAttribs.colormap, &aColors[ iColor ] ) )
280 /* start all over with less colors */
281 XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, iColor, 0 );
283 free( st->aiColorVals );
285 aColors = calloc( st->iColorCount, sizeof(XColor) );
286 st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
290 st->aiColorVals[ iColor ] = aColors[ iColor ].pixel;
295 XSetWindowBackground( st->dpy, st->window, st->aiColorVals[ 0 ] );
297 return st->aiColorVals;
301 static void Initialize( struct state *st )
304 XWindowAttributes XWinAttribs;
305 /*int iBitsPerPixel;*/
307 /* Create the Image for drawing */
308 XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
311 /* Find the preferred bits-per-pixel. (jwz) */
314 XPixmapFormatValues *pfv = XListPixmapFormats( st->dpy, &pfvc );
315 for( i=0; i<pfvc; i++ )
316 if( pfv[ i ].depth == XWinAttribs.depth )
318 iBitsPerPixel = pfv[ i ].bits_per_pixel;
327 st->gc = XCreateGC( st->dpy, st->window, 0, &gcValues );
329 st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
330 XWinAttribs.width, XWinAttribs.height, 8 /*BitmapPad( st->dpy )*/, 0 );
331 st->pImage->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height);
333 st->iWinWidth = XWinAttribs.width;
334 st->iWinHeight = XWinAttribs.height;
336 /* These are precalculations used in Execute(). */
337 st->iBobDiameter = ( ( st->iWinWidth < st->iWinHeight ) ? st->iWinWidth : st->iWinHeight ) / 25;
338 st->iBobRadius = st->iBobDiameter / 2;
340 printf( "%s: Bob Diameter = %d\n", progname, st->iBobDiameter );
343 st->iWinCenterX = ( XWinAttribs.width / 2 ) - st->iBobRadius;
344 st->iWinCenterY = ( XWinAttribs.height / 2 ) - st->iBobRadius;
346 st->iVelocity = ( ( st->iWinWidth < st->iWinHeight ) ? st->iWinWidth : st->iWinHeight ) / 150;
348 /* Create the Sin and Cosine lookup tables. */
349 st->iDegreeCount = get_integer_resource(st->dpy, "degrees", "Integer" );
350 if( st->iDegreeCount == 0 ) st->iDegreeCount = ( XWinAttribs.width / 6 ) + 400;
351 else if( st->iDegreeCount < 90 ) st->iDegreeCount = 90;
352 else if( st->iDegreeCount > 5400 ) st->iDegreeCount = 5400;
353 CreateTables( st, st->iDegreeCount );
355 printf( "%s: Using a %d degree circle.\n", progname, st->iDegreeCount );
358 /* Get the base color. */
359 st->sColor = get_string_resource(st->dpy, "color", "Color" );
364 shadebobs_init (Display *dpy, Window window)
366 struct state *st = (struct state *) calloc (1, sizeof(*st));
369 time_t nTime = time( NULL );
370 unsigned short iFrame = 0;
376 st->nShadeBobCount = get_integer_resource(st->dpy, "count", "Integer" );
377 if( st->nShadeBobCount > 64 ) st->nShadeBobCount = 64;
378 if( st->nShadeBobCount < 1 ) st->nShadeBobCount = 1;
380 if( ( st->aShadeBobs = calloc( st->nShadeBobCount, sizeof(SShadeBob) ) ) == NULL )
382 fprintf( stderr, "%s: Could not allocate %d ShadeBobs\n", progname, st->nShadeBobCount );
386 printf( "%s: Allocated %d ShadeBobs\n", progname, st->nShadeBobCount );
391 for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
392 InitShadeBob( st, &st->aShadeBobs[ st->iShadeBob ], st->iShadeBob % 2 );
394 st->delay = get_integer_resource(st->dpy, "delay", "Integer" );
395 st->cycles = get_integer_resource(st->dpy, "cycles", "Integer" ) * st->iDegreeCount;
397 st->draw_i = 99999999;
402 shadebobs_draw (Display *dpy, Window window, void *closure)
404 struct state *st = (struct state *) closure;
406 if( st->draw_i++ >= st->cycles )
408 XWindowAttributes XWinAttribs;
409 XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
413 memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height );
416 /* fill the image with the actual value of the black pixel, not 0. */
417 unsigned long black = BlackPixelOfScreen (XWinAttribs.screen);
419 for (y = 0; y < XWinAttribs.height; y++)
420 for (x = 0; x < XWinAttribs.width; x++)
421 XPutPixel (st->pImage, x, y, black);
425 for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
426 ResetShadeBob( st, &st->aShadeBobs[ st->iShadeBob ] );
427 XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, st->iColorCount, 0 );
428 free( st->aiColorVals );
429 st->aiColorVals = SetPalette( st );
430 XClearWindow( st->dpy, st->window );
433 for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
434 Execute( st, &st->aShadeBobs[ st->iShadeBob ] );
440 shadebobs_reshape (Display *dpy, Window window, void *closure,
441 unsigned int w, unsigned int h)
446 shadebobs_event (Display *dpy, Window window, void *closure, XEvent *event)
452 shadebobs_free (Display *dpy, Window window, void *closure)
454 struct state *st = (struct state *) closure;
455 free( st->anSinTable );
456 free( st->anCosTable );
457 /* free( st->pImage->data ); */
458 XDestroyImage( st->pImage );
459 for( st->iShadeBob=0; st->iShadeBob<st->nShadeBobCount; st->iShadeBob++ )
460 free( st->aShadeBobs[ st->iShadeBob ].anDeltaMap );
461 free( st->aShadeBobs );
462 free( st->aiColorVals );
466 XSCREENSAVER_MODULE ("ShadeBobs", shadebobs)
468 /* End of Module - "shadebobs.c" */