5b73e7c569002079a56035bae2f705590c9021c2
[xscreensaver] / hacks / bumps.c
1 /* -*- mode: C; tab-width: 4 -*-
2  * Bumps, Copyright (c) 2002 Shane Smit <CodeWeaver@DigitalLoom.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or
10  * implied warranty.
11  *
12  * Module: "bumps.c"
13  * Tab Size: 4
14  *
15  * Description:
16  *  This is typical bump-mapping.  The actual bump map is generated by a screen
17  *  grab.  The light source is represented by a spotlight of random color. This
18  *  spotlight randomly traverses the bump map in a sinus pattern.
19  *
20  *  Essentially, it 3D-izes your desktop, based on color intensity.
21  *
22  * Modification History:
23  *  [10/01/99] - Shane Smit: Creation
24  *  [10/08/99] - Shane Smit: Port to C. (Ick)
25  *  [03/08/02] - Shane Smit: New movement code.
26  *  [09/12/02] - Shane Smit: MIT-SHM XImages.
27  *                                                       Thanks to Kennett Galbraith <http://www.Alpha-II.com/>
28  *                                                       for code optimization.
29  */
30
31 #include "bumps.h"
32
33 /* This function pointer will point to the appropriate PutPixel*() function below. */
34 void (*MyPutPixel)( int8_ *, uint32_ );
35
36 void PutPixel32( int8_ *pData, uint32_ pixel )
37 {
38         *(uint32_ *)pData = pixel;
39 }
40
41 void PutPixel24( int8_ *pData, uint32_ pixel )
42 {
43         pData[ 2 ] = ( pixel & 0x00FF0000 ) >> 16;
44         pData[ 1 ] = ( pixel & 0x0000FF00 ) >> 8;
45         pData[ 0 ] = ( pixel & 0x000000FF );
46 }
47
48 void PutPixel16( int8_ *pData, uint32_ pixel )
49 {
50         *(uint16_ *)pData = (uint16_)pixel;
51 }
52
53 void PutPixel8( int8_ *pData, uint32_ pixel )
54 {
55         *(uint8_ *)pData = (uint8_)pixel;
56 }
57
58 /* Creates the light map, which is a circular image... going from black around the edges
59  * to white in the center. */
60 void CreateSpotLight( SSpotLight *pSpotLight, uint16_ iDiameter, uint16_ nColorCount )
61 {
62         double nDist;
63         int16_ iDistX, iDistY;
64         uint8_ *pLOffset;
65         
66         pSpotLight->nFalloffDiameter = iDiameter;
67         pSpotLight->nFalloffRadius = pSpotLight->nFalloffDiameter / 2;
68         pSpotLight->nLightDiameter = iDiameter / 2;
69         pSpotLight->nLightRadius = pSpotLight->nLightDiameter / 2;
70 #ifdef VERBOSE
71         printf( "%s: Falloff Diameter: %d\n", progclass, pSpotLight->nFalloffDiameter );
72         printf( "%s: Spot Light Diameter: %d\n", progclass, pSpotLight->nLightDiameter );
73 #endif
74
75         pSpotLight->aLightMap = malloc( pSpotLight->nLightDiameter * pSpotLight->nLightDiameter * sizeof(uint8_) );
76
77         pLOffset = pSpotLight->aLightMap;
78         for( iDistY=-pSpotLight->nLightRadius; iDistY<pSpotLight->nLightRadius; ++iDistY )
79         {
80                 for( iDistX=-pSpotLight->nLightRadius; iDistX<pSpotLight->nLightRadius; ++iDistX )
81                 {
82                         nDist = sqrt( pow( iDistX+0.5F, 2 ) + pow( iDistY+0.5F, 2 ) );
83                         if( nDist / pSpotLight->nLightRadius <= 1.0f )
84                                 *pLOffset = (uint8_)(nColorCount - ( ( nDist / pSpotLight->nLightRadius ) * ( nColorCount - 1 ) ));
85                         else
86                                 *pLOffset = 0;
87
88                         ++pLOffset;
89                 }
90         }
91                 
92         /* Initialize movement variables.       */
93         pSpotLight->nAccelX = 0;
94         pSpotLight->nAccelY = 0;
95         pSpotLight->nVelocityX = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
96         pSpotLight->nVelocityY = ( RANDOM() % 2 ) ? pSpotLight->nVelocityMax : -pSpotLight->nVelocityMax;
97 }
98
99
100 /* Calculates the position of the spot light on the screen. */
101 void CalcLightPos( SBumps *pBumps )
102 {
103         SSpotLight *pSpotLight = &pBumps->SpotLight;
104         float nGravity;
105
106         /* X */
107         if( pSpotLight->nXPos < pSpotLight->nFalloffRadius )                                                    nGravity = 1.0f;
108         else if( pSpotLight->nXPos > pBumps->iWinWidth - pSpotLight->nFalloffRadius )   nGravity = -1.0f;
109         else                                                                                                                                                    nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
110                 
111         pSpotLight->nAccelX += nGravity * ( pSpotLight->nAccelMax / 5.0f );
112         if( pSpotLight->nAccelX < -pSpotLight->nAccelMax )              pSpotLight->nAccelX = -pSpotLight->nAccelMax;
113         else if( pSpotLight->nAccelX > pSpotLight->nAccelMax )  pSpotLight->nAccelX = pSpotLight->nAccelMax;
114
115         pSpotLight->nVelocityX += pSpotLight->nAccelX;
116         if( pSpotLight->nVelocityX < -pSpotLight->nVelocityMax )                pSpotLight->nVelocityX = -pSpotLight->nVelocityMax;
117         else if( pSpotLight->nVelocityX > pSpotLight->nVelocityMax )    pSpotLight->nVelocityX = pSpotLight->nVelocityMax;
118
119         pSpotLight->nXPos += pSpotLight->nVelocityX;
120
121         /* Y */
122         if( pSpotLight->nYPos < pSpotLight->nFalloffRadius )                                                            nGravity = 1.0f;
123         else if( pSpotLight->nYPos > pBumps->iWinHeight - pSpotLight->nFalloffRadius )  nGravity = -1.0f;
124         else                                                                                                                                                    nGravity = ( ( RANDOM() % 201 ) / 100.0f ) - 1.0f;
125                 
126         pSpotLight->nAccelY += nGravity * ( pSpotLight->nAccelMax / 5.0f );
127         if( pSpotLight->nAccelY < -pSpotLight->nAccelMax )              pSpotLight->nAccelY = -pSpotLight->nAccelMax;
128         else if( pSpotLight->nAccelY > pSpotLight->nAccelMax )  pSpotLight->nAccelY = pSpotLight->nAccelMax;
129
130         pSpotLight->nVelocityY += pSpotLight->nAccelY;
131         if( pSpotLight->nVelocityY < -pSpotLight->nVelocityMax )                pSpotLight->nVelocityY = -pSpotLight->nVelocityMax;
132         else if( pSpotLight->nVelocityY > pSpotLight->nVelocityMax )    pSpotLight->nVelocityY = pSpotLight->nVelocityMax;
133
134         pSpotLight->nYPos += pSpotLight->nVelocityY;
135 }
136
137
138 /* Main initialization function. */
139 void CreateBumps( SBumps *pBumps, Display *pNewDisplay, Window NewWin )
140 {
141         XWindowAttributes XWinAttribs;
142         XGCValues GCValues;
143         int32_ nGCFlags;
144         uint16_ iDiameter;
145
146         /* Make size and velocity a function of window size, so it appears the same at 100x60 as it does in 3200x1200. */
147         XGetWindowAttributes( pNewDisplay, NewWin, &XWinAttribs );
148         pBumps->iWinWidth = XWinAttribs.width;
149         pBumps->iWinHeight = XWinAttribs.height;
150         pBumps->SpotLight.nXPos = XWinAttribs.width / 2.0f;
151         pBumps->SpotLight.nYPos = XWinAttribs.height / 2.0f;
152         pBumps->SpotLight.nVelocityMax = ( ( XWinAttribs.width < XWinAttribs.height ) ? XWinAttribs.width : XWinAttribs.height ) / 140.0f;
153         pBumps->SpotLight.nAccelMax = pBumps->SpotLight.nVelocityMax / 10.0f;
154         pBumps->pDisplay = pNewDisplay;
155         pBumps->Win = NewWin;
156         pBumps->pXImage = NULL;
157         
158         iDiameter = ( ( pBumps->iWinWidth < pBumps->iWinHeight ) ? pBumps->iWinWidth : pBumps->iWinHeight ) / 2;
159
160 #ifdef HAVE_XSHM_EXTENSION
161         pBumps->bUseShm = get_boolean_resource( "useSHM", "Boolean" );
162
163         if( pBumps->bUseShm )
164         {
165                 pBumps->pXImage = create_xshm_image( pBumps->pDisplay, XWinAttribs.visual, XWinAttribs.depth,
166                                                                                          ZPixmap, NULL, &pBumps->XShmInfo, iDiameter, iDiameter );
167                 if( !pBumps->pXImage )
168                 {
169                         fprintf( stderr, "%s: Unable to create XShmImage.\n", progname );
170                         pBumps->bUseShm = False;
171                 }
172         }
173 #endif /* HAVE_XSHM_EXTENSION */
174         if( !pBumps->pXImage )
175         {
176                 pBumps->pXImage = XCreateImage( pBumps->pDisplay, XWinAttribs.visual, XWinAttribs.depth, 
177                                                                         ZPixmap, 0, NULL, iDiameter, iDiameter, BitmapPad( pBumps->pDisplay ), 0 );
178                 pBumps->pXImage->data = malloc( pBumps->pXImage->bytes_per_line * pBumps->pXImage->height * sizeof(int8_) );
179         }
180
181         /* For speed, access the XImage data directly using my own PutPixel routine. */
182         switch( pBumps->pXImage->bits_per_pixel )
183         {
184                 case 32:
185                         pBumps->bytesPerPixel = 4;
186                         MyPutPixel = PutPixel32;
187                         break;
188                 
189                 case 24:
190                         pBumps->bytesPerPixel = 3;
191                         MyPutPixel = PutPixel24;
192                         break;
193
194                 case 16:
195                         pBumps->bytesPerPixel = 2;
196                         MyPutPixel = PutPixel16;
197                         break;
198
199                 case 8:
200                         pBumps->bytesPerPixel = 1;
201                         MyPutPixel = PutPixel8;
202                         break;
203
204                 default:
205                         fprintf( stderr, "%s: Unknown XImage depth.", progname );
206 #ifdef HAVE_XSHM_EXTENSION
207                         if( pBumps->bUseShm )
208                                 destroy_xshm_image( pBumps->pDisplay, pBumps->pXImage, &pBumps->XShmInfo );
209                         else
210 #endif /* HAVE_XSHM_EXTENSION */
211                                 XDestroyImage( pBumps->pXImage );
212                         exit( 1 );
213         }
214         
215         GCValues.function = GXcopy;
216         GCValues.subwindow_mode = IncludeInferiors;
217         nGCFlags = GCForeground | GCFunction;
218         if( use_subwindow_mode_p( XWinAttribs.screen, pBumps->Win ) ) /* See grabscreen.c */
219                 nGCFlags |= GCSubwindowMode;
220         pBumps->GraphicsContext = XCreateGC( pBumps->pDisplay, pBumps->Win, nGCFlags, &GCValues );
221         
222         SetPalette( pBumps, &XWinAttribs );
223         CreateSpotLight( &pBumps->SpotLight, iDiameter, pBumps->nColorCount );
224         InitBumpMap( pBumps, &XWinAttribs );
225
226         XSetWindowBackground( pBumps->pDisplay, pBumps->Win, pBumps->aColors[ 0 ] );
227         XClearWindow (pBumps->pDisplay, pBumps->Win);
228 }
229
230
231 /* Creates a specialized phong shade palette. */
232 void SetPalette( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
233 {
234         XColor BaseColor;
235         XColor Color;
236         char *sColor;                   /* Spotlight Color */
237         int16_ iColor;
238         
239         sColor = get_string_resource( "color", "Color" );
240
241         BaseColor.red = RANDOM() % 0xFFFF; 
242         BaseColor.green = RANDOM() % 0xFFFF;
243         BaseColor.blue = RANDOM() % 0xFFFF;
244         
245         /* Make one color full intesity to avoid dark spotlights.       */
246         switch( RANDOM() % 3 )
247         {
248                 case 0: BaseColor.red   = 0xFFFF;       break;
249                 case 1: BaseColor.green = 0xFFFF;       break;
250                 case 2: BaseColor.blue  = 0xFFFF;       break;
251         }
252
253         if( strcasecmp( sColor, "random" ) && !XParseColor( pBumps->pDisplay, pXWinAttribs->colormap, sColor, &BaseColor ) )
254                 fprintf( stderr, "%s: color %s not found in database. Choosing random...\n", progname, sColor );
255
256 #ifdef VERBOSE
257         printf( "%s: Spotlight color is <%d,%d,%d> RGB.\n", progclass, BaseColor.red, BaseColor.green, BaseColor.blue );
258 #endif  /*  VERBOSE */
259
260         pBumps->nColorCount = get_integer_resource( "colorcount", "Integer" );
261         if( pBumps->nColorCount < 2 )   pBumps->nColorCount = 2;
262         if( pBumps->nColorCount > 128 ) pBumps->nColorCount = 128;
263
264         pBumps->aColors = malloc( pBumps->nColorCount * sizeof(uint32_ ) );
265
266         /* Creates a phong shade:                 / BaseColor  \                               Index/ColorCount 
267          *                                                      PhongShade = | ------------ | Index + ( 65535 - BaseColor )^ 
268          *                                                                                \ ColorCount /                                                                                                */
269         pBumps->nColorCount--;
270         for( iColor=0; iColor<=pBumps->nColorCount; iColor++ )
271         {
272                 Color.red   = (uint16_)( ( ( BaseColor.red   / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.red,   iColor/(double)pBumps->nColorCount ) );
273                 Color.green = (uint16_)( ( ( BaseColor.green / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.green, iColor/(double)pBumps->nColorCount ) );
274                 Color.blue  = (uint16_)( ( ( BaseColor.blue  / (double)pBumps->nColorCount ) * iColor ) + pow( 0xFFFF - BaseColor.blue,  iColor/(double)pBumps->nColorCount ) );
275
276                 if( !XAllocColor( pBumps->pDisplay, pXWinAttribs->colormap, &Color ) )
277                 {
278                         XFreeColors( pBumps->pDisplay, pXWinAttribs->colormap, pBumps->aColors, iColor, 0 );
279                         free( pBumps->aColors );
280                         pBumps->aColors = malloc( pBumps->nColorCount * sizeof(uint32_) );
281                         pBumps->nColorCount--;
282                         iColor = -1;
283                 }
284                 else
285                         pBumps->aColors[ iColor ] = Color.pixel;
286         }
287         pBumps->nColorCount++;
288
289 #ifdef VERBOSE
290         printf( "%s: Allocated %d colors.\n", progclass, pBumps->nColorCount );
291 #endif  /*  VERBOSE */
292
293         XSetWindowBackground( pBumps->pDisplay, pBumps->Win, pBumps->aColors[ 0 ] );
294 }
295
296
297 /* Grabs the current contents of the window to use an intensity-based bump map. */
298 void InitBumpMap( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
299 {
300         XImage *pScreenImage;
301         XColor *aColors, *pColor;
302         uint8_ nSoften;
303         uint16_ iWidth, iHeight;
304         uint32_ nAverager;
305         uint16_ *pBump;
306         uint16_ maxHeight;
307         double softenMultiplier = 1.0f;
308         BOOL bInvert = (BOOL)get_boolean_resource( "invert", "Boolean" );
309     Pixmap p;
310
311         aColors = (XColor*)malloc( pBumps->iWinWidth * sizeof(XColor) );
312
313     p = XCreatePixmap(pBumps->pDisplay, pBumps->Win,
314                       pXWinAttribs->width, pXWinAttribs->height,
315                       pXWinAttribs->depth);
316     load_random_image (pXWinAttribs->screen, pBumps->Win, p, NULL);
317
318         pScreenImage = XGetImage( pBumps->pDisplay, p, 0, 0, pBumps->iWinWidth, pBumps->iWinHeight, ~0L, ZPixmap );
319     XFreePixmap (pBumps->pDisplay, p);
320
321         /* jwz: get the grabbed bits off the screen fast */
322         XClearWindow (pBumps->pDisplay, pBumps->Win);
323         XSync (pBumps->pDisplay, 0);
324
325         pBumps->aBumpMap = malloc( pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
326         
327         nSoften = get_integer_resource( "soften", "Integer" );
328         while( nSoften-- )
329                 softenMultiplier *= 1.0f + ( 1.0f / 3.0f );     /* Softening takes the max height down, so scale up to compensate. */
330         maxHeight = pBumps->SpotLight.nLightRadius * softenMultiplier;
331         nAverager = ( 3 * 0xFFFF ) / maxHeight;
332
333         pBump = pBumps->aBumpMap;
334         if( bInvert )   /* Funny, it's actually the 'else' that inverts the bump map... */
335         {
336                 for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
337                 {
338                         pColor = aColors;
339                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
340                                 (pColor++)->pixel = XGetPixel( pScreenImage, iWidth, iHeight );
341
342                         XQueryColors( pBumps->pDisplay, pXWinAttribs->colormap, aColors, pBumps->iWinWidth );
343
344                         pColor = aColors;
345                         for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pColor, ++pBump )
346                                 *pBump = ( ( pColor->red + pColor->green + pColor->blue ) / nAverager );
347                 }
348         }
349         else
350         {
351                 for( iHeight=0; iHeight<pBumps->iWinHeight; iHeight++ )
352                 {
353                         pColor = aColors;
354                         for( iWidth=0; iWidth<pBumps->iWinWidth; iWidth++ )
355                                 (pColor++)->pixel = XGetPixel( pScreenImage, iWidth, iHeight );
356
357                         XQueryColors( pBumps->pDisplay, pXWinAttribs->colormap, aColors, pBumps->iWinWidth );
358         
359                         pColor = aColors;
360                         for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pColor, ++pBump )
361                                 *pBump = ( maxHeight - ( ( pColor->red + pColor->green + pColor->blue ) / nAverager ) );
362                 }
363         }
364
365         XDestroyImage( pScreenImage );
366
367         nSoften = get_integer_resource( "soften", "Integer" );
368 #ifdef VERBOSE
369         if( nSoften )   printf( "%s: Softening Bump Map %d time(s)...\n", progclass, nSoften );
370 #endif
371         while( nSoften-- )
372                 SoftenBumpMap( pBumps );
373
374         free( aColors );
375 }
376
377 /* Soften the bump map.  This is to avoid pixelated-looking ridges.
378  * |-----|-----|-----|
379  * |  0% |12.5%|  0% |  The adjacent pixels are averaged together
380  * |-----|-----|-----|  first.  Then than value is averaged with
381  * |12.5%| 50% |12.5%|  the pixel is question. This essentially weights
382  * |-----|-----|-----|  each pixel as shown on the left.
383  * |  0% |12.5%|  0% |
384  * |-----|-----|-----|
385  */
386 void SoftenBumpMap( SBumps *pBumps )
387 {
388         uint16_ *pOffset, *pTOffset;
389         uint32_ nHeight;
390         uint32_ iWidth, iHeight;
391         uint16_ *aTempBuffer = malloc( pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
392
393         pOffset = pBumps->aBumpMap;
394         pTOffset = aTempBuffer;
395         for( iHeight=pBumps->iWinHeight; iHeight; --iHeight )
396         {
397                 for( iWidth=pBumps->iWinWidth; iWidth; --iWidth, ++pOffset, ++pTOffset )
398                 {
399                         if( iHeight==pBumps->iWinHeight || iHeight==1 ||
400                                 iWidth==pBumps->iWinWidth || iWidth==1 )
401                         {
402                                 *pTOffset = 0;
403                                 continue;
404                         }
405
406                         nHeight = pOffset[ -pBumps->iWinWidth ];
407                         nHeight += pOffset[ 1 ];
408                         nHeight += pOffset[ pBumps->iWinWidth ];
409                         nHeight += pOffset[ -1 ];
410                         nHeight >>= 2;
411                         nHeight += pOffset[ 0 ];
412                         nHeight >>= 1;
413                         *pTOffset = nHeight;
414                 }
415         }                                               
416
417         memcpy( pBumps->aBumpMap, aTempBuffer, pBumps->iWinWidth * pBumps->iWinHeight * sizeof(uint16_) );
418         free( aTempBuffer );
419 }
420
421
422 /* This is where we slap down some pixels... */
423 void Execute( SBumps *pBumps )
424 {
425         int32_ nLightXPos, nLightYPos;
426         int32_ iScreenX, iScreenY;
427         int32_ iLightX, iLightY;
428         uint16_ *pBOffset;
429         int8_ *pDOffset;
430         int32_ nX, nY;
431         uint16_ nColor;
432         int32_ nLightOffsetFar = pBumps->SpotLight.nFalloffDiameter - pBumps->SpotLight.nLightRadius;
433
434         CalcLightPos( pBumps );
435         
436         /* Offset to upper left hand corner. */
437         nLightXPos = pBumps->SpotLight.nXPos - pBumps->SpotLight.nFalloffRadius;
438         nLightYPos = pBumps->SpotLight.nYPos - pBumps->SpotLight.nFalloffRadius;
439         
440         for( iScreenY=nLightYPos, iLightY=-pBumps->SpotLight.nLightRadius; iLightY<nLightOffsetFar; ++iScreenY, ++iLightY )
441         {
442                 if( iScreenY < 0 )                                                      continue;
443                 else if( iScreenY >= pBumps->iWinHeight )       break;
444
445     /* warning: pointer targets in assignment differ in signedness
446        Should pDOffset be a int8?  I can't tell.  -jwz, 22-Jul-2003 */
447                 pDOffset = (int8_ *) &pBumps->pXImage->data[ (iLightY+pBumps->SpotLight.nLightRadius) * pBumps->pXImage->bytes_per_line ];
448                 pBOffset = pBumps->aBumpMap + ( iScreenY * pBumps->iWinWidth ) + nLightXPos;
449                 for( iScreenX=nLightXPos, iLightX=-pBumps->SpotLight.nLightRadius; iLightX<nLightOffsetFar; ++iScreenX, ++iLightX, ++pBOffset, pDOffset+=pBumps->bytesPerPixel )
450                 {
451                         if( iScreenX < 0 )                                                      continue;
452                         else if( iScreenX >= pBumps->iWinWidth )        break;
453                         else if( iScreenY == 0 || iScreenY >= pBumps->iWinHeight-2 ||
454                                          iScreenX == 0 || iScreenX >= pBumps->iWinWidth-2 )
455                         {
456                                 MyPutPixel( pDOffset, pBumps->aColors[ 0 ] );
457                                 continue;
458                         }
459
460                         /* That's right folks, all the magic of bump mapping occurs in these two lines.  (kinda disappointing, isn't it?) */
461                         nX = ( pBOffset[ 1 ] - pBOffset[ 0 ] ) + iLightX;
462                         nY = ( pBOffset[ pBumps->iWinWidth ] - pBOffset[ 0 ] ) + iLightY;
463
464                         if( nX<0 || nX>=pBumps->SpotLight.nLightDiameter
465                          || nY<0 || nY>=pBumps->SpotLight.nLightDiameter )
466                         {
467                                 MyPutPixel( pDOffset, pBumps->aColors[ 0 ] );
468                                 continue;
469                         }
470                                 
471                         nColor = pBumps->SpotLight.aLightMap[ ( nY * pBumps->SpotLight.nLightDiameter ) + nX ];
472                         MyPutPixel( pDOffset, pBumps->aColors[ nColor ] );
473                 }
474         }       
475
476         /* Allow the spotlight to go *slightly* off the screen by clipping the XImage. */
477         iLightX = iLightY = 0;  /* Use these for XImages X and Y now.   */
478         nX = nY = pBumps->SpotLight.nFalloffDiameter;   /* Use these for XImage width and height now.   */
479         if( nLightXPos < 0 )
480         {
481                 iLightX = -nLightXPos;
482                 nX -= iLightX;
483                 nLightXPos = 0;
484         }
485         else if( nLightXPos + nX >= pBumps->iWinWidth )
486         {
487                 nX -= ( nLightXPos + nX ) - pBumps->iWinWidth;
488         }
489         
490         if( nLightYPos < 0 )
491         {
492                 iLightY = -nLightYPos;
493                 nY -= iLightY;
494                 nLightYPos = 0;
495         }
496         else if( nLightYPos + nY >= pBumps->iWinHeight )
497         {
498                 nY -= ( nLightYPos + nY ) - pBumps->iWinHeight;
499         }
500         
501 #ifdef HAVE_XSHM_EXTENSION
502         if( pBumps->bUseShm )
503                 XShmPutImage( pBumps->pDisplay, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, iLightX, iLightY, nLightXPos, nLightYPos,
504                                           nX, nY, False);
505         else
506 #endif /* HAVE_XSHM_EXTENSION */
507                 XPutImage( pBumps->pDisplay, pBumps->Win, pBumps->GraphicsContext, pBumps->pXImage, iLightX, iLightY, nLightXPos, nLightYPos,
508                                    nX, nY );
509         
510         XSync( pBumps->pDisplay, False );
511 }
512
513
514 /* Clean up */
515 void DestroyBumps( SBumps *pBumps )
516 {
517         DestroySpotLight( &pBumps->SpotLight );
518         free( pBumps->aColors );
519         free( pBumps->aBumpMap );
520 #ifdef HAVE_XSHM_EXTENSION
521         if( pBumps->bUseShm )
522                 destroy_xshm_image( pBumps->pDisplay, pBumps->pXImage, &pBumps->XShmInfo );
523         else
524 #endif /* HAVE_XSHM_EXTENSION */
525                 XDestroyImage( pBumps->pXImage );
526 }
527
528
529 /* All messages to the screensaver are processed here. */
530 void screenhack( Display *pDisplay, Window Win )
531 {
532         SBumps Bumps;
533         uint32_ iDelay;
534 #ifdef VERBOSE
535         time_t Time = time( NULL );
536         uint16_ iFrame = 0;
537 #endif  /*  VERBOSE */
538         
539         CreateBumps( &Bumps, pDisplay, Win );
540         iDelay = get_integer_resource( "delay", "Integer" );
541
542         while( 1 )
543         {
544                 screenhack_handle_events( pDisplay );
545                 Execute( &Bumps );
546                 usleep( iDelay );
547
548 #ifdef VERBOSE
549                 iFrame++;
550                 if( Time - time( NULL ) )
551                 {
552                         printf( "FPS: %d\n", iFrame );
553                         Time = time( NULL );
554                         iFrame = 0;
555                 }
556 #endif  /*  VERBOSE */
557         }
558
559         DestroyBumps( &Bumps );
560 }
561
562  
563 /* vim: ts=4
564  */