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