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