http://www.jwz.org/xscreensaver/xscreensaver-5.10.tar.gz
[xscreensaver] / hacks / twang.c
1 /* twang, twist around screen bits, v1.3
2  * by Dan Bornstein, danfuzz@milk.com
3  * Copyright (c) 2003 Dan Bornstein. All rights reserved.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  *
13  * See the included man page for more details.
14  */
15
16 #include <math.h>
17 #include "screenhack.h"
18
19 #ifdef HAVE_XSHM_EXTENSION
20 #include "xshm.h"
21 #endif
22
23 #define FLOAT double
24
25 /* random float in the range (-1..1) */
26 #define RAND_FLOAT_PM1 \
27         (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
28
29 /* random float in the range (0..1) */
30 #define RAND_FLOAT_01 \
31         (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
32
33
34 typedef struct
35 {
36     int x;        /* x coordinate of the center of the tile */
37     int y;        /* y coordinate of the center of the tile */
38     FLOAT angle;  /* angle of the tile (-pi..pi) */
39     FLOAT zoom;   /* log of the zoom of the tile (-1..1) */
40     FLOAT vAngle; /* angular velocity (-pi/4..pi/4) */
41     FLOAT vZoom;  /* zoomular velocity (-0.25..0.25) */
42 }
43 Tile;
44
45
46 struct state {
47   Display *dpy;
48   Window window;
49
50   int delay;                    /* delay (usec) between iterations */
51   int duration;                 /* time (sec) before loading new image */
52   int maxColumns;               /* the maximum number of columns of tiles */
53   int maxRows;                  /* the maximum number of rows of tiles */
54   int tileSize;                 /* the size (width and height) of a tile */
55   int borderWidth;              /* the width of the border around each tile */
56   FLOAT eventChance;            /* the chance, per iteration, of an interesting event happening */
57   FLOAT friction;               /* friction: the fraction (0..1) by which velocity decreased per iteration */
58   FLOAT springiness;            /* springiness: the fraction (0..1) of the orientation that turns into velocity towards the center */
59   FLOAT transference;           /* transference: the fraction (0..1) of the orientations of orthogonal neighbors that turns into velocity (in the same direction as the orientation) */
60   int windowWidth;              /* width and height of the window */
61   int windowHeight;
62   Screen *screen;                  /* the screen to draw on */
63   XImage *sourceImage;             /* image source of stuff to draw */
64   XImage *workImage;               /* work area image, used when rendering */
65   XImage *backgroundImage;       /* image filled with background pixels */
66
67   GC backgroundGC;               /* GC for the background color */
68   GC foregroundGC;               /* GC for the foreground color */
69   GC borderGC;                   /* GC for the border color */
70   unsigned long backgroundPixel;  /* background color as a pixel value */
71   unsigned long borderPixel;      /* border color as a pixel value */
72
73   Tile *tiles;  /* array of tiles (left->right, top->bottom, row major) */
74   int rows;     /* number of rows of tiles */
75   int columns;  /* number of columns of tiles */
76
77   Tile **sortedTiles; /* array of tile pointers, sorted by zoom */
78   int tileCount;     /* total number of tiles */
79
80   time_t start_time;
81   async_load_state *img_loader;
82
83   Bool useShm;          /* whether or not to use xshm */
84 #ifdef HAVE_XSHM_EXTENSION
85   XShmSegmentInfo shmInfo;
86 #endif
87 };
88
89
90 #define TILE_AT(col,row) (&st->tiles[(row) * st->columns + (col)])
91
92 #define MAX_VANGLE (M_PI / 4.0)
93 #define MAX_VZOOM 0.25
94
95 #define RAND_ANGLE (RAND_FLOAT_PM1 * M_PI)
96 #define RAND_ZOOM (RAND_FLOAT_PM1)
97 #define RAND_VANGLE (RAND_FLOAT_PM1 * MAX_VANGLE)
98 #define RAND_VZOOM (RAND_FLOAT_PM1 * MAX_VZOOM)
99
100
101
102 /*
103  * overall setup stuff
104  */
105
106 /* grab the source image */
107 static void
108 grabImage_start (struct state *st, XWindowAttributes *xwa)
109 {
110     XFillRectangle (st->dpy, st->window, st->backgroundGC, 0, 0, 
111                     st->windowWidth, st->windowHeight);
112     st->backgroundImage = 
113         XGetImage (st->dpy, st->window, 0, 0, st->windowWidth, st->windowHeight,
114                    ~0L, ZPixmap);
115
116     st->start_time = time ((time_t) 0);
117     st->img_loader = load_image_async_simple (0, xwa->screen, st->window,
118                                               st->window, 0, 0);
119 }
120
121 static void
122 grabImage_done (struct state *st)
123 {
124     XWindowAttributes xwa;
125     XGetWindowAttributes (st->dpy, st->window, &xwa);
126
127     st->start_time = time ((time_t) 0);
128     if (st->sourceImage) XDestroyImage (st->sourceImage);
129     st->sourceImage = XGetImage (st->dpy, st->window, 0, 0, st->windowWidth, st->windowHeight,
130                              ~0L, ZPixmap);
131
132     if (st->workImage) XDestroyImage (st->workImage);
133     st->workImage = NULL;
134
135 #ifdef HAVE_XSHM_EXTENSION
136     if (st->useShm) 
137     {
138         st->workImage = create_xshm_image (st->dpy, xwa.visual, xwa.depth,
139                                        ZPixmap, 0, &st->shmInfo, 
140                                        st->windowWidth, st->windowHeight);
141         if (!st->workImage) 
142         {
143             st->useShm = False;
144             fprintf (stderr, "create_xshm_image failed\n");
145         }
146     }
147
148     if (st->workImage == NULL)
149 #endif /* HAVE_XSHM_EXTENSION */
150
151         /* just use XSubImage to acquire the right visual, depth, etc;
152          * easier than the other alternatives */
153         st->workImage = XSubImage (st->sourceImage, 0, 0, st->windowWidth, st->windowHeight);
154 }
155
156 /* set up the system */
157 static void setup (struct state *st)
158 {
159     XWindowAttributes xgwa;
160     XGCValues gcv;
161
162     XGetWindowAttributes (st->dpy, st->window, &xgwa);
163
164     st->screen = xgwa.screen;
165     st->windowWidth = xgwa.width;
166     st->windowHeight = xgwa.height;
167
168     gcv.line_width = st->borderWidth;
169     gcv.foreground = get_pixel_resource (st->dpy, xgwa.colormap,
170                                          "borderColor", "BorderColor");
171     st->borderPixel = gcv.foreground;
172     st->borderGC = XCreateGC (st->dpy, st->window, GCForeground | GCLineWidth, 
173                           &gcv);
174
175     gcv.foreground = get_pixel_resource (st->dpy, xgwa.colormap,
176                                          "background", "Background");
177     st->backgroundPixel = gcv.foreground;
178     st->backgroundGC = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
179
180     gcv.foreground = get_pixel_resource (st->dpy, xgwa.colormap,
181                                          "foreground", "Foreground");
182     st->foregroundGC = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
183
184     grabImage_start (st, &xgwa);
185 }
186
187
188
189 /*
190  * the simulation
191  */
192
193 /* event: randomize all the angular velocities */
194 static void randomizeAllAngularVelocities (struct state *st)
195 {
196     int c;
197     int r;
198
199     for (r = 0; r < st->rows; r++)
200     {
201         for (c = 0; c < st->columns; c++)
202         {
203             TILE_AT (c, r)->vAngle = RAND_VANGLE;
204         }
205     }
206 }
207
208 /* event: randomize all the zoomular velocities */
209 static void randomizeAllZoomularVelocities (struct state *st)
210 {
211     int c;
212     int r;
213
214     for (r = 0; r < st->rows; r++)
215     {
216         for (c = 0; c < st->columns; c++)
217         {
218             TILE_AT (c, r)->vZoom = RAND_VZOOM;
219         }
220     }
221 }
222
223 /* event: randomize all the velocities */
224 static void randomizeAllVelocities (struct state *st)
225 {
226     randomizeAllAngularVelocities (st);
227     randomizeAllZoomularVelocities (st);
228 }
229
230 /* event: randomize all the angular orientations */
231 static void randomizeAllAngularOrientations (struct state *st)
232 {
233     int c;
234     int r;
235
236     for (r = 0; r < st->rows; r++)
237     {
238         for (c = 0; c < st->columns; c++)
239         {
240             TILE_AT (c, r)->angle = RAND_ANGLE;
241         }
242     }
243 }
244
245 /* event: randomize all the zoomular orientations */
246 static void randomizeAllZoomularOrientations (struct state *st)
247 {
248     int c;
249     int r;
250
251     for (r = 0; r < st->rows; r++)
252     {
253         for (c = 0; c < st->columns; c++)
254         {
255             TILE_AT (c, r)->zoom = RAND_ZOOM;
256         }
257     }
258 }
259
260 /* event: randomize all the orientations */
261 static void randomizeAllOrientations (struct state *st)
262 {
263     randomizeAllAngularOrientations (st);
264     randomizeAllZoomularOrientations (st);
265 }
266
267 /* event: randomize everything */
268 static void randomizeEverything (struct state *st)
269 {
270     randomizeAllVelocities (st);
271     randomizeAllOrientations (st);
272 }
273
274 /* event: pick one tile and randomize all its stats */
275 static void randomizeOneTile (struct state *st)
276 {
277     int c = RAND_FLOAT_01 * st->columns;
278     int r = RAND_FLOAT_01 * st->rows;
279
280     Tile *t = TILE_AT (c, r);
281     t->angle = RAND_ANGLE;
282     t->zoom = RAND_ZOOM;
283     t->vAngle = RAND_VANGLE;
284     t->vZoom = RAND_VZOOM;
285 }
286
287 /* event: pick one row and randomize everything about each of its tiles */
288 static void randomizeOneRow (struct state *st)
289 {
290     int c;
291     int r = RAND_FLOAT_01 * st->rows;
292
293     for (c = 0; c < st->columns; c++)
294     {
295         Tile *t = TILE_AT (c, r);
296         t->angle = RAND_ANGLE;
297         t->zoom = RAND_ZOOM;
298         t->vAngle = RAND_VANGLE;
299         t->vZoom = RAND_VZOOM;
300     }
301 }
302
303 /* event: pick one column and randomize everything about each of its tiles */
304 static void randomizeOneColumn (struct state *st)
305 {
306     int c = RAND_FLOAT_01 * st->columns;
307     int r;
308
309     for (r = 0; r < st->rows; r++)
310     {
311         Tile *t = TILE_AT (c, r);
312         t->angle = RAND_ANGLE;
313         t->zoom = RAND_ZOOM;
314         t->vAngle = RAND_VANGLE;
315         t->vZoom = RAND_VZOOM;
316     }
317 }
318
319 /* do model event processing */
320 static void modelEvents (struct state *st)
321 {
322     int which;
323
324     if (RAND_FLOAT_01 > st->eventChance)
325     {
326         return;
327     }
328
329     which = RAND_FLOAT_01 * 10;
330
331     switch (which)
332     {
333         case 0: randomizeAllAngularVelocities (st);    break;
334         case 1: randomizeAllZoomularVelocities (st);   break;
335         case 2: randomizeAllVelocities (st);           break;
336         case 3: randomizeAllAngularOrientations (st);  break;
337         case 4: randomizeAllZoomularOrientations (st); break;
338         case 5: randomizeAllOrientations (st);         break;
339         case 6: randomizeEverything (st);              break;
340         case 7: randomizeOneTile (st);                 break;
341         case 8: randomizeOneColumn (st);               break;
342         case 9: randomizeOneRow (st);                  break;
343     }
344 }
345
346 /* update the model for one iteration */
347 static void updateModel (struct state *st)
348 {
349     int r;
350     int c;
351
352     /* for each tile, decrease its velocities according to the friction,
353      * and increase them based on its current orientation and the orientations
354      * of its orthogonal neighbors */
355     for (r = 0; r < st->rows; r++)
356     {
357         for (c = 0; c < st->columns; c++)
358         {
359             Tile *t = TILE_AT (c, r);
360             FLOAT a = t->angle;
361             FLOAT z = t->zoom;
362             FLOAT va = t->vAngle;
363             FLOAT vz = t->vZoom;
364
365             va -= t->angle * st->springiness;
366             vz -= t->zoom * st->springiness;
367
368             if (c > 0)
369             {
370                 Tile *t2 = TILE_AT (c - 1, r);
371                 va += (t2->angle - a) * st->transference;
372                 vz += (t2->zoom - z) * st->transference;
373             }
374
375             if (c < (st->columns - 1))
376             {
377                 Tile *t2 = TILE_AT (c + 1, r);
378                 va += (t2->angle - a) * st->transference;
379                 vz += (t2->zoom - z) * st->transference;
380             }
381
382             if (r > 0)
383             {
384                 Tile *t2 = TILE_AT (c, r - 1);
385                 va += (t2->angle - a) * st->transference;
386                 vz += (t2->zoom - z) * st->transference;
387             }
388
389             if (r < (st->rows - 1))
390             {
391                 Tile *t2 = TILE_AT (c, r + 1);
392                 va += (t2->angle - a) * st->transference;
393                 vz += (t2->zoom - z) * st->transference;
394             }
395
396             va *= (1.0 - st->friction);
397             vz *= (1.0 - st->friction);
398
399             if (va > MAX_VANGLE) va = MAX_VANGLE;
400             else if (va < -MAX_VANGLE) va = -MAX_VANGLE;
401             t->vAngle = va;
402
403             if (vz > MAX_VZOOM) vz = MAX_VZOOM;
404             else if (vz < -MAX_VZOOM) vz = -MAX_VZOOM;
405             t->vZoom = vz;
406         }
407     }
408
409     /* for each tile, update its orientation based on its velocities */
410     for (r = 0; r < st->rows; r++)
411     {
412         for (c = 0; c < st->columns; c++)
413         {
414             Tile *t = TILE_AT (c, r);
415             FLOAT a = t->angle + t->vAngle;
416             FLOAT z = t->zoom + t->vZoom;
417
418             if (a > M_PI) a = M_PI;
419             else if (a < -M_PI) a = -M_PI;
420             t->angle = a;
421
422             if (z > 1.0) z = 1.0;
423             else if (z < -1.0) z = -1.0;
424             t->zoom = z;
425         }
426     }
427 }
428
429 /* the comparator to us to sort the tiles (used immediately below); it'd
430  * sure be nice if C allowed inner functions (or jeebus-forbid *real
431  * closures*!) */
432 static int sortTilesComparator (const void *v1, const void *v2)
433 {
434     Tile *t1 = *(Tile **) v1;
435     Tile *t2 = *(Tile **) v2;
436     
437     if (t1->zoom < t2->zoom)
438     {
439         return -1;
440     }
441
442     if (t1->zoom > t2->zoom)
443     {
444         return 1;
445     }
446
447     return 0;
448 }
449
450 /* sort the tiles in sortedTiles by zoom */
451 static void sortTiles (struct state *st)
452 {
453     qsort (st->sortedTiles, st->tileCount, sizeof (Tile *), sortTilesComparator);
454 }
455
456 /* render the given tile */
457 static void renderTile (struct state *st, Tile *t)
458 {
459     /* note: the zoom as stored per tile is log-based (centered on 0, with
460      * 0 being no zoom, but the range for zoom-as-drawn is 0.4..2.5,
461      * hence the alteration of t->zoom, below */
462
463     int x, y;
464
465     int tx = t->x;
466     int ty = t->y;
467
468     FLOAT zoom = pow (2.5, t->zoom);
469     FLOAT ang = -t->angle;
470     FLOAT sinAng = sin (ang);
471     FLOAT cosAng = cos (ang);
472
473     FLOAT innerBorder = (st->tileSize - st->borderWidth) / 2.0;
474     FLOAT outerBorder = innerBorder + st->borderWidth;
475
476     int maxCoord = outerBorder * zoom * (fabs (sinAng) + fabs (cosAng));
477     int minX = tx - maxCoord;
478     int maxX = tx + maxCoord;
479     int minY = ty - maxCoord;
480     int maxY = ty + maxCoord;
481
482     FLOAT prey;
483
484     if (minX < 0) minX = 0;
485     if (maxX > st->windowWidth) maxX = st->windowWidth;
486     if (minY < 0) minY = 0;
487     if (maxY > st->windowHeight) maxY = st->windowHeight;
488
489     sinAng /= zoom;
490     cosAng /= zoom;
491
492     for (y = minY, prey = y - ty; y < maxY; y++, prey++)
493     {
494         FLOAT prex = minX - tx;
495         FLOAT srcx = prex * cosAng - prey * sinAng;
496         FLOAT srcy = prex * sinAng + prey * cosAng;
497
498         for (x = minX; 
499              x < maxX; 
500              x++, srcx += cosAng, srcy += sinAng)
501         {
502             if ((srcx < -innerBorder) || (srcx >= innerBorder) ||
503                 (srcy < -innerBorder) || (srcy >= innerBorder))
504             {
505                 if ((srcx < -outerBorder) || (srcx >= outerBorder) ||
506                     (srcy < -outerBorder) || (srcy >= outerBorder))
507                 {
508                     continue;
509                 }
510                 XPutPixel (st->workImage, x, y, st->borderPixel);
511             }
512             else
513             {
514                 unsigned long p = 
515                     XGetPixel (st->sourceImage, srcx + tx, srcy + ty);
516                 XPutPixel (st->workImage, x, y, p);
517             }
518         }
519     }
520 }
521
522 /* render and display the current model */
523 static void renderFrame (struct state *st)
524 {
525     int n;
526
527     memcpy (st->workImage->data, st->backgroundImage->data, 
528             st->workImage->bytes_per_line * st->workImage->height);
529
530     sortTiles (st);
531
532     for (n = 0; n < st->tileCount; n++)
533     {
534         renderTile (st, st->sortedTiles[n]);
535     }
536
537 #ifdef HAVE_XSHM_EXTENSION
538     if (st->useShm)
539         XShmPutImage (st->dpy, st->window, st->backgroundGC, st->workImage, 0, 0, 0, 0,
540                       st->windowWidth, st->windowHeight, False);
541     else
542 #endif /* HAVE_XSHM_EXTENSION */
543         XPutImage (st->dpy, st->window, st->backgroundGC, st->workImage, 
544                    0, 0, 0, 0, st->windowWidth, st->windowHeight);
545 }
546
547 /* set up the model */
548 static void setupModel (struct state *st)
549 {
550     int c;
551     int r;
552
553     int leftX; /* x of the center of the top-left tile */
554     int topY;  /* y of the center of the top-left tile */
555
556     if (st->tileSize > (st->windowWidth / 2))
557     {
558         st->tileSize = st->windowWidth / 2;
559     }
560
561     if (st->tileSize > (st->windowHeight / 2))
562     {
563         st->tileSize = st->windowHeight / 2;
564     }
565
566     st->columns = st->tileSize ? st->windowWidth / st->tileSize : 0;
567     st->rows = st->tileSize ? st->windowHeight / st->tileSize : 0;
568
569     if ((st->maxColumns != 0) && (st->columns > st->maxColumns))
570     {
571         st->columns = st->maxColumns;
572     }
573
574     if ((st->maxRows != 0) && (st->rows > st->maxRows))
575     {
576         st->rows = st->maxRows;
577     }
578
579     st->tileCount = st->rows * st->columns;
580
581     leftX = (st->windowWidth - (st->columns * st->tileSize) + st->tileSize) / 2;
582     topY = (st->windowHeight - (st->rows * st->tileSize) + st->tileSize) / 2;
583
584     st->tiles = calloc (st->tileCount, sizeof (Tile));
585     st->sortedTiles = calloc (st->tileCount, sizeof (Tile *));
586
587     for (r = 0; r < st->rows; r++)
588     {
589         for (c = 0; c < st->columns; c++)
590         {
591             Tile *t = TILE_AT (c, r);
592             t->x = leftX + c * st->tileSize;
593             t->y = topY + r * st->tileSize;
594             st->sortedTiles[c + r * st->columns] = t;
595         }
596     }
597
598     randomizeEverything (st);
599 }
600
601 /* do one iteration */
602
603 static unsigned long
604 twang_draw (Display *dpy, Window window, void *closure)
605 {
606   struct state *st = (struct state *) closure;
607
608   if (st->img_loader)   /* still loading */
609     {
610       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
611       if (! st->img_loader) {  /* just finished */
612         grabImage_done (st);
613       }
614       return st->delay;
615     }
616
617   if (!st->img_loader &&
618       st->start_time + st->duration < time ((time_t) 0)) {
619     XWindowAttributes xgwa;
620     XGetWindowAttributes (st->dpy, st->window, &xgwa);
621     grabImage_start (st, &xgwa);
622     return st->delay;
623   }
624
625   modelEvents (st);
626   updateModel (st);
627   renderFrame (st);
628   return st->delay;
629 }
630
631
632 static void
633 twang_reshape (Display *dpy, Window window, void *closure, 
634                  unsigned int w, unsigned int h)
635 {
636 }
637
638 static Bool
639 twang_event (Display *dpy, Window window, void *closure, XEvent *event)
640 {
641   return False;
642 }
643
644 static void
645 twang_free (Display *dpy, Window window, void *closure)
646 {
647   struct state *st = (struct state *) closure;
648   free (st);
649 }
650
651
652
653 /* main and options and stuff */
654
655 /* initialize the user-specifiable params */
656 static void initParams (struct state *st)
657 {
658     int problems = 0;
659
660     st->borderWidth = get_integer_resource (st->dpy, "borderWidth", "Integer");
661     if (st->borderWidth < 0)
662     {
663         fprintf (stderr, "error: border width must be at least 0\n");
664         problems = 1;
665     }
666
667     st->delay = get_integer_resource (st->dpy, "delay", "Delay");
668     if (st->delay < 0)
669     {
670         fprintf (stderr, "error: delay must be at least 0\n");
671         problems = 1;
672     }
673
674     st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
675     if (st->duration < 1) st->duration = 1;
676
677     st->eventChance = get_float_resource (st->dpy, "eventChance", "Double");
678     if ((st->eventChance < 0.0) || (st->eventChance > 1.0))
679     {
680         fprintf (stderr, "error: eventChance must be in the range 0..1\n");
681         problems = 1;
682     }
683
684     st->friction = get_float_resource (st->dpy, "friction", "Double");
685     if ((st->friction < 0.0) || (st->friction > 1.0))
686     {
687         fprintf (stderr, "error: friction must be in the range 0..1\n");
688         problems = 1;
689     }
690
691     st->maxColumns = get_integer_resource (st->dpy, "maxColumns", "Integer");
692     if (st->maxColumns < 0)
693     {
694         fprintf (stderr, "error: max columns must be at least 0\n");
695         problems = 1;
696     }
697
698     st->maxRows = get_integer_resource (st->dpy, "maxRows", "Integer");
699     if (st->maxRows < 0)
700     {
701         fprintf (stderr, "error: max rows must be at least 0\n");
702         problems = 1;
703     }
704
705     st->springiness = get_float_resource (st->dpy, "springiness", "Double");
706     if ((st->springiness < 0.0) || (st->springiness > 1.0))
707     {
708         fprintf (stderr, "error: springiness must be in the range 0..1\n");
709         problems = 1;
710     }
711
712     st->tileSize = get_integer_resource (st->dpy, "tileSize", "Integer");
713     if (st->tileSize < 1)
714     {
715         fprintf (stderr, "error: tile size must be at least 1\n");
716         problems = 1;
717     }
718     
719     st->transference = get_float_resource (st->dpy, "transference", "Double");
720     if ((st->transference < 0.0) || (st->transference > 1.0))
721     {
722         fprintf (stderr, "error: transference must be in the range 0..1\n");
723         problems = 1;
724     }
725
726 #ifdef HAVE_XSHM_EXTENSION
727     st->useShm = get_boolean_resource (st->dpy, "useSHM", "Boolean");
728 #endif
729
730     if (problems)
731     {
732         exit (1);
733     }
734 }
735
736 static void *
737 twang_init (Display *dpy, Window win)
738 {
739     struct state *st = (struct state *) calloc (1, sizeof(*st));
740     st->dpy = dpy;
741     st->window = win;
742
743     initParams (st);
744     setup (st);
745     setupModel (st);
746
747     return st;
748 }
749
750
751 static const char *twang_defaults [] = {
752     ".background:       black",
753     ".foreground:       white",
754     "*borderColor:      blue",
755     "*borderWidth:      3",
756     "*delay:            10000",
757     "*duration:         120",
758     "*eventChance:      0.01",
759     "*friction:         0.05",
760     "*maxColumns:       0",
761     "*maxRows:          0",
762     "*springiness:      0.1",
763     "*tileSize:         120",
764     "*transference:     0.025",
765 #ifdef HAVE_XSHM_EXTENSION
766     "*useSHM: True",
767 #else
768     "*useSHM: False",
769 #endif
770     0
771 };
772
773 static XrmOptionDescRec twang_options [] = {
774   { "-border-color",     ".borderColor",    XrmoptionSepArg, 0 },
775   { "-border-width",     ".borderWidth",    XrmoptionSepArg, 0 },
776   { "-delay",            ".delay",          XrmoptionSepArg, 0 },
777   { "-duration",         ".duration",       XrmoptionSepArg, 0 },
778   { "-event-chance",     ".eventChance",    XrmoptionSepArg, 0 },
779   { "-friction",         ".friction",       XrmoptionSepArg, 0 },
780   { "-max-columns",      ".maxColumns",     XrmoptionSepArg, 0 },
781   { "-max-rows",         ".maxRows",        XrmoptionSepArg, 0 },
782   { "-springiness",      ".springiness",    XrmoptionSepArg, 0 },
783   { "-tile-size",        ".tileSize",       XrmoptionSepArg, 0 },
784   { "-transference",     ".transference",   XrmoptionSepArg, 0 },
785   { "-shm",              ".useSHM",         XrmoptionNoArg, "True" },
786   { "-no-shm",           ".useSHM",         XrmoptionNoArg, "False" },
787   { 0, 0, 0, 0 }
788 };
789
790
791 XSCREENSAVER_MODULE ("Twang", twang)