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