97299d65073154fed5362aa9f4db5f08d231182f
[xscreensaver] / hacks / petri.c
1 /* petri, simulate mold in a petri dish. v2.7
2  * by Dan Bornstein, danfuzz@milk.com
3  * with help from Jamie Zawinski, jwz@jwz.org
4  * Copyright (c) 1992-1999 Dan Bornstein.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation.  No representations are made about the suitability of this
11  * software for any purpose.  It is provided "as is" without express or 
12  * implied warranty.
13  *
14  *
15  * Brief description of options/resources:
16  *
17  * delay: the delay in microseconds between iterations
18  * size: the size of a cell in pixels
19  * count: the number of different kinds of mold (minimum: 2)
20  * diaglim: the age limit for diagonal growth as a multiplier of orthogonal
21  *   growth (minimum: 1, maximum 2). 1 means square growth, 1.414 
22  *   (i.e., sqrt(2)) means approximately circular growth, 2 means diamond
23  *   growth.
24  * anychan: the chance (fraction, between 0 and 1) that at each iteration,
25  *   any new cell will be born
26  * minorchan: the chance (fraction, between 0 and 1) that, given that new
27  *   cells will be added, that only two will be added (a minor cell birth
28  *   event)
29  * instantdeathchan: the chance (fraction, between 0 and 1) that, given
30  *   that death and destruction will happen, that instead of using plague
31  *   cells, death will be instantaneous
32  * minlifespan: the minimum lifespan of a colony (before black death ensues)
33  * maxlifespan: the maximum lifespan of a colony (before black death ensues)
34  * minlifespeed: the minimum speed for living cells as a fraction of the
35  *   maximum possible speed (fraction, between 0 and 1)
36  * maxlifespeed: the maximum speed for living cells as a fraction of the
37  *   maximum possible speed (fraction, between 0 and 1)
38  * mindeathspeed: the minimum speed for black death cells as a fraction of the
39  *   maximum possible speed (fraction, between 0 and 1)
40  * maxdeathspeed: the maximum speed for black death cells as a fraction of the
41  *   maximum possible speed (fraction, between 0 and 1)
42  * originalcolors: if true, count must be 8 or less and the colors are a 
43  *   fixed set of primary and secondary colors (the artist's original choices)
44  *
45  * Interesting settings:
46  *
47  *      petri -originalcolors -size 8
48  *      petri -size 2
49  *      petri -size 8 -diaglim 1.8
50  *      petri -diaglim 1.1
51  *
52  *      petri -count 4 -anychan 0.01 -minorchan 1 \
53  *              -minlifespan 2000 -maxlifespan 5000
54  *
55  *      petri -count 3 -anychan 1 -minlifespan 100000 \ 
56  *              -instantdeathchan 0
57  *
58  *      petri -minlifespeed 0.02 -maxlifespeed 0.03 -minlifespan 1 \
59  *              -maxlifespan 1 -instantdeathchan 0 -minorchan 0 \
60  *              -anychan 0.3 -delay 4000
61  */
62
63 #include <math.h>
64 #include "screenhack.h"
65 #include "spline.h"
66
67 #define FLOAT float
68 #define RAND_FLOAT (((FLOAT) (random() & 0xffff)) / ((FLOAT) 0x10000))
69
70 typedef struct cell_s 
71 {
72     unsigned char col;              /*  0      */
73     unsigned char isnext;           /*  1      */
74     unsigned char nextcol;          /*  2      */
75                                     /*  3      */
76     struct cell_s *next;            /*  4      */
77     struct cell_s *prev;            /*  8    - */
78     FLOAT speed;                    /* 12      */
79     FLOAT growth;                   /* 16 20 - */
80     FLOAT nextspeed;                /* 20 28   */
81                                     /* 24 36 - */
82 } cell;
83
84 struct state {
85   Display *dpy;
86   Window window;
87
88   int arr_width;
89   int arr_height;
90   int count;
91
92   cell *arr;
93   cell *head;
94   cell *tail;
95   int blastcount;
96
97   GC *coloredGCs;
98
99   int windowWidth;
100   int windowHeight;
101   int xOffset;
102   int yOffset;
103   int xSize;
104   int ySize;
105
106   FLOAT orthlim;
107   FLOAT diaglim;
108   FLOAT anychan;
109   FLOAT minorchan;
110   FLOAT instantdeathchan;
111   int minlifespan;
112   int maxlifespan;
113   FLOAT minlifespeed;
114   FLOAT maxlifespeed;
115   FLOAT mindeathspeed;
116   FLOAT maxdeathspeed;
117   Bool originalcolors;
118
119   int warned;
120   int delay;
121 };
122
123
124 #define cell_x(c) (st->arr_width ? ((c) - st->arr) % st->arr_width : 0)
125 #define cell_y(c) (st->arr_width ? ((c) - st->arr) / st->arr_width : 0)
126
127
128 static int random_life_value (struct state *st)
129 {
130     return (int) ((RAND_FLOAT * (st->maxlifespan - st->minlifespan)) + st->minlifespan);
131 }
132
133 static void setup_random_colormap (struct state *st, XWindowAttributes *xgwa)
134 {
135     XGCValues gcv;
136     int lose = 0;
137     int ncolors = st->count - 1;
138     int n;
139     XColor *colors = (XColor *) calloc (sizeof(*colors), st->count*2);
140     
141     colors[0].pixel = get_pixel_resource (st->dpy, xgwa->colormap,
142                                           "background", "Background");
143     
144     make_random_colormap (xgwa->screen, xgwa->visual, xgwa->colormap,
145                           colors+1, &ncolors, True, True, 0, True);
146     if (ncolors < 1)
147       {
148         fprintf (stderr, "%s: couldn't allocate any colors\n", progname);
149         exit (-1);
150       }
151     
152     ncolors++;
153     st->count = ncolors;
154     
155     memcpy (colors + st->count, colors, st->count * sizeof(*colors));
156     colors[st->count].pixel = get_pixel_resource (st->dpy, xgwa->colormap,
157                                               "foreground", "Foreground");
158     
159     for (n = 1; n < st->count; n++)
160     {
161         int m = n + st->count;
162         colors[n].red = colors[m].red / 2;
163         colors[n].green = colors[m].green / 2;
164         colors[n].blue = colors[m].blue / 2;
165         
166         if (!XAllocColor (st->dpy, xgwa->colormap, &colors[n]))
167         {
168             lose++;
169             colors[n] = colors[m];
170         }
171     }
172
173     if (lose)
174     {
175         fprintf (stderr, 
176                  "%s: unable to allocate %d half-intensity colors.\n",
177                  progname, lose);
178     }
179     
180     for (n = 0; n < st->count*2; n++) 
181     {
182         gcv.foreground = colors[n].pixel;
183         st->coloredGCs[n] = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
184     }
185
186     free (colors);
187 }
188
189 static void setup_original_colormap (struct state *st, XWindowAttributes *xgwa)
190 {
191     XGCValues gcv;
192     int lose = 0;
193     int n;
194     XColor *colors = (XColor *) calloc (sizeof(*colors), st->count*2);
195     
196     colors[0].pixel = get_pixel_resource (st->dpy, xgwa->colormap,
197                                           "background", "Background");
198
199     colors[st->count].pixel = get_pixel_resource (st->dpy, xgwa->colormap,
200                                               "foreground", "Foreground");
201
202     for (n = 1; n < st->count; n++)
203     {
204         int m = n + st->count;
205         colors[n].red =   ((n & 0x01) != 0) * 0x8000;
206         colors[n].green = ((n & 0x02) != 0) * 0x8000;
207         colors[n].blue =  ((n & 0x04) != 0) * 0x8000;
208
209         if (!XAllocColor (st->dpy, xgwa->colormap, &colors[n]))
210         {
211             lose++;
212             colors[n] = colors[0];
213         }
214
215         colors[m].red   = colors[n].red + 0x4000;
216         colors[m].green = colors[n].green + 0x4000;
217         colors[m].blue  = colors[n].blue + 0x4000;
218
219         if (!XAllocColor (st->dpy, xgwa->colormap, &colors[m]))
220         {
221             lose++;
222             colors[m] = colors[st->count];
223         }
224     }
225
226     if (lose)
227     {
228         fprintf (stderr, 
229                  "%s: unable to allocate %d colors.\n",
230                  progname, lose);
231     }
232     
233     for (n = 0; n < st->count*2; n++) 
234     {
235         gcv.foreground = colors[n].pixel;
236         st->coloredGCs[n] = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
237     }
238
239     free (colors);
240 }
241
242 static void
243 setup_display (struct state *st)
244 {
245     XWindowAttributes xgwa;
246
247     int cell_size = get_integer_resource (st->dpy, "size", "Integer");
248     int osize, alloc_size, oalloc;
249     int mem_throttle = 0;
250     char *s;
251
252     if (cell_size < 1) cell_size = 1;
253
254     osize = cell_size;
255
256     s = get_string_resource (st->dpy, "memThrottle", "MemThrottle");
257     if (s)
258       {
259         int n;
260         char c;
261         if (1 == sscanf (s, " %d M %c", &n, &c) ||
262             1 == sscanf (s, " %d m %c", &n, &c))
263           mem_throttle = n * (1 << 20);
264         else if (1 == sscanf (s, " %d K %c", &n, &c) ||
265                  1 == sscanf (s, " %d k %c", &n, &c))
266           mem_throttle = n * (1 << 10);
267         else if (1 == sscanf (s, " %d %c", &n, &c))
268           mem_throttle = n;
269         else
270           {
271             fprintf (stderr, "%s: invalid memThrottle \"%s\" (try \"10M\")\n",
272                      progname, s);
273             exit (1);
274           }
275         
276         free (s);
277       }
278
279     XGetWindowAttributes (st->dpy, st->window, &xgwa);
280
281     st->originalcolors = get_boolean_resource (st->dpy, "originalcolors", "Boolean");
282
283     st->count = get_integer_resource (st->dpy, "count", "Integer");
284     if (st->count < 2) st->count = 2;
285
286     /* number of colors can't be greater than the half depth of the screen. */
287     if (st->count > (unsigned int) (1L << (xgwa.depth-1)))
288       st->count = (unsigned int) (1L << (xgwa.depth-1));
289
290     /* Actually, since cell->col is of type char, this has to be small. */
291     if (st->count >= (unsigned int) (1L << ((sizeof(st->arr[0].col) * 8) - 1)))
292       st->count = (unsigned int) (1L << ((sizeof(st->arr[0].col) * 8) - 1));
293
294
295     if (st->originalcolors && (st->count > 8))
296     {
297         st->count = 8;
298     }
299
300     st->coloredGCs = (GC *) calloc (sizeof(GC), st->count * 2);
301
302     st->diaglim  = get_float_resource (st->dpy, "diaglim", "Float");
303     if (st->diaglim < 1.0)
304     {
305         st->diaglim = 1.0;
306     }
307     else if (st->diaglim > 2.0)
308     {
309         st->diaglim = 2.0;
310     }
311     st->diaglim *= st->orthlim;
312
313     st->anychan  = get_float_resource (st->dpy, "anychan", "Float");
314     if (st->anychan < 0.0)
315     {
316         st->anychan = 0.0;
317     }
318     else if (st->anychan > 1.0)
319     {
320         st->anychan = 1.0;
321     }
322     
323     st->minorchan = get_float_resource (st->dpy, "minorchan","Float");
324     if (st->minorchan < 0.0)
325     {
326         st->minorchan = 0.0;
327     }
328     else if (st->minorchan > 1.0)
329     {
330         st->minorchan = 1.0;
331     }
332     
333     st->instantdeathchan = get_float_resource (st->dpy, "instantdeathchan","Float");
334     if (st->instantdeathchan < 0.0)
335     {
336         st->instantdeathchan = 0.0;
337     }
338     else if (st->instantdeathchan > 1.0)
339     {
340         st->instantdeathchan = 1.0;
341     }
342
343     st->minlifespan = get_integer_resource (st->dpy, "minlifespan", "Integer");
344     if (st->minlifespan < 1)
345     {
346         st->minlifespan = 1;
347     }
348
349     st->maxlifespan = get_integer_resource (st->dpy, "maxlifespan", "Integer");
350     if (st->maxlifespan < st->minlifespan)
351     {
352         st->maxlifespan = st->minlifespan;
353     }
354
355     st->minlifespeed = get_float_resource (st->dpy, "minlifespeed", "Float");
356     if (st->minlifespeed < 0.0)
357     {
358         st->minlifespeed = 0.0;
359     }
360     else if (st->minlifespeed > 1.0)
361     {
362         st->minlifespeed = 1.0;
363     }
364
365     st->maxlifespeed = get_float_resource (st->dpy, "maxlifespeed", "Float");
366     if (st->maxlifespeed < st->minlifespeed)
367     {
368         st->maxlifespeed = st->minlifespeed;
369     }
370     else if (st->maxlifespeed > 1.0)
371     {
372         st->maxlifespeed = 1.0;
373     }
374
375     st->mindeathspeed = get_float_resource (st->dpy, "mindeathspeed", "Float");
376     if (st->mindeathspeed < 0.0)
377     {
378         st->mindeathspeed = 0.0;
379     }
380     else if (st->mindeathspeed > 1.0)
381     {
382         st->mindeathspeed = 1.0;
383     }
384
385     st->maxdeathspeed = get_float_resource (st->dpy, "maxdeathspeed", "Float");
386     if (st->maxdeathspeed < st->mindeathspeed)
387     {
388         st->maxdeathspeed = st->mindeathspeed;
389     }
390     else if (st->maxdeathspeed > 1.0)
391     {
392         st->maxdeathspeed = 1.0;
393     }
394
395     st->minlifespeed *= st->diaglim;
396     st->maxlifespeed *= st->diaglim;
397     st->mindeathspeed *= st->diaglim;
398     st->maxdeathspeed *= st->diaglim;
399
400     st->windowWidth = xgwa.width;
401     st->windowHeight = xgwa.height;
402     
403     st->arr_width = st->windowWidth / cell_size;
404     st->arr_height = st->windowHeight / cell_size;
405
406     alloc_size = sizeof(cell) * st->arr_width * st->arr_height;
407     oalloc = alloc_size;
408
409     if (mem_throttle > 0)
410       while (cell_size < st->windowWidth/10 &&
411              cell_size < st->windowHeight/10 &&
412              alloc_size > mem_throttle)
413         {
414           cell_size++;
415           st->arr_width = st->windowWidth / cell_size;
416           st->arr_height = st->windowHeight / cell_size;
417           alloc_size = sizeof(cell) * st->arr_width * st->arr_height;
418         }
419
420     if (osize != cell_size)
421       {
422         if (!st->warned)
423           {
424             fprintf (stderr,
425              "%s: throttling cell size from %d to %d because of %dM limit.\n",
426                      progname, osize, cell_size, mem_throttle / (1 << 20));
427             fprintf (stderr, "%s: %dx%dx%d = %.1fM, %dx%dx%d = %.1fM.\n",
428                      progname,
429                      st->windowWidth, st->windowHeight, osize,
430                      ((float) oalloc) / (1 << 20),
431                      st->windowWidth, st->windowHeight, cell_size,
432                      ((float) alloc_size) / (1 << 20));
433             st->warned = 1;
434           }
435       }
436
437     st->xSize = st->arr_width ? st->windowWidth / st->arr_width : 0;
438     st->ySize = st->arr_height ? st->windowHeight / st->arr_height : 0;
439     if (st->xSize > st->ySize)
440     {
441         st->xSize = st->ySize;
442     }
443     else
444     {
445         st->ySize = st->xSize;
446     }
447     
448     st->xOffset = (st->windowWidth - (st->arr_width * st->xSize)) / 2;
449     st->yOffset = (st->windowHeight - (st->arr_height * st->ySize)) / 2;
450
451     if (st->originalcolors)
452     {
453         setup_original_colormap (st, &xgwa);
454     }
455     else
456     {
457         setup_random_colormap (st, &xgwa);
458     }
459 }
460
461 static void drawblock (struct state *st, int x, int y, unsigned char c)
462 {
463   if (st->xSize == 1 && st->ySize == 1)
464     XDrawPoint (st->dpy, st->window, st->coloredGCs[c], x + st->xOffset, y + st->yOffset);
465   else
466     XFillRectangle (st->dpy, st->window, st->coloredGCs[c],
467                     x * st->xSize + st->xOffset, y * st->ySize + st->yOffset,
468                     st->xSize, st->ySize);
469 }
470
471 static void setup_arr (struct state *st)
472 {
473     int x, y;
474
475     if (st->arr != NULL)
476     {
477         free (st->arr);
478     }
479
480     XFillRectangle (st->dpy, st->window, st->coloredGCs[0], 0, 0, 
481                     st->windowWidth, st->windowHeight);
482     
483     if (!st->arr_width) st->arr_width = 1;
484     if (!st->arr_height) st->arr_height = 1;
485
486     st->arr = (cell *) calloc (sizeof(cell), st->arr_width * st->arr_height);  
487     if (!st->arr)
488       {
489         fprintf (stderr, "%s: out of memory allocating %dx%d grid\n",
490                  progname, st->arr_width, st->arr_height);
491         exit (1);
492       }
493
494     for (y = 0; y < st->arr_height; y++)
495     {
496       int row = y * st->arr_width;
497        for (x = 0; x < st->arr_width; x++) 
498         {
499             st->arr[row+x].speed = 0.0;
500             st->arr[row+x].growth = 0.0;
501             st->arr[row+x].col = 0;
502             st->arr[row+x].isnext = 0;
503             st->arr[row+x].next = 0;
504             st->arr[row+x].prev = 0;
505         }
506     }
507
508     if (st->head == NULL)
509     {
510         st->head = (cell *) malloc (sizeof (cell));
511     }
512     
513     if (st->tail == NULL)
514     {
515         st->tail = (cell *) malloc (sizeof (cell));
516     }
517
518     st->head->next = st->tail;
519     st->head->prev = st->head;
520     st->tail->next = st->tail;
521     st->tail->prev = st->head;
522
523     st->blastcount = random_life_value (st);
524 }
525
526 static void newcell (struct state *st, cell *c, unsigned char col, FLOAT sp)
527 {
528     if (! c) return;
529     
530     if (c->col == col) return;
531     
532     c->nextcol = col;
533     c->nextspeed = sp;
534     c->isnext = 1;
535     
536     if (c->prev == 0) {
537         c->next = st->head->next;
538         c->prev = st->head;
539         st->head->next = c;
540         c->next->prev = c;
541     }
542 }
543
544 static void killcell (struct state *st, cell *c)
545 {
546     c->prev->next = c->next;
547     c->next->prev = c->prev;
548     c->prev = 0;
549     c->speed = 0.0;
550     drawblock (st, cell_x(c), cell_y(c), c->col);
551 }
552
553
554 static void randblip (struct state *st, int doit)
555 {
556     int n;
557     int b = 0;
558     if (!doit 
559         && (st->blastcount-- >= 0) 
560         && (RAND_FLOAT > st->anychan))
561     {
562         return;
563     }
564     
565     if (st->blastcount < 0) 
566     {
567         b = 1;
568         n = 2;
569         st->blastcount = random_life_value (st);
570         if (RAND_FLOAT < st->instantdeathchan)
571         {
572             /* clear everything every so often to keep from getting into a
573              * rut */
574             setup_arr (st);
575             b = 0;
576         }
577     }
578     else if (RAND_FLOAT <= st->minorchan) 
579     {
580         n = 2;
581     }
582     else 
583     {
584         n = random () % 3 + 3;
585     }
586     
587     while (n--) 
588     {
589       int x = st->arr_width ? random () % st->arr_width : 0;
590       int y = st->arr_height ? random () % st->arr_height : 0;
591         int c;
592         FLOAT s;
593         if (b)
594         {
595             c = 0;
596             s = RAND_FLOAT * (st->maxdeathspeed - st->mindeathspeed) + st->mindeathspeed;
597         }
598         else
599         {
600             c = ((st->count - 1) ? random () % (st->count-1) : 0) + 1;
601             s = RAND_FLOAT * (st->maxlifespeed - st->minlifespeed) + st->minlifespeed;
602         }
603         newcell (st, &st->arr[y * st->arr_width + x], c, s);
604     }
605 }
606
607 static void update (struct state *st)
608 {
609     cell *a;
610     
611     for (a = st->head->next; a != st->tail; a = a->next) 
612     {
613         static const XPoint all_coords[] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1},
614                                             {-1,  0}, { 1, 0}, {0, -1}, {0, 1},
615                                             {99, 99}};
616
617         const XPoint *coords = 0;
618
619         if (a->speed == 0) continue;
620         a->growth += a->speed;
621
622         if (a->growth >= st->diaglim) 
623         {
624             coords = all_coords;
625         }
626         else if (a->growth >= st->orthlim)
627         {
628             coords = &all_coords[4];
629         }
630         else
631         {
632             continue;
633         }
634
635         while (coords->x != 99)
636         {
637             int x = cell_x(a) + coords->x;
638             int y = cell_y(a) + coords->y;
639             coords++;
640
641             if (x < 0) x = st->arr_width - 1;
642             else if (x >= st->arr_width) x = 0;
643             
644             if (y < 0) y = st->arr_height - 1;
645             else if (y >= st->arr_height) y = 0;
646             
647             newcell (st, &st->arr[y * st->arr_width + x], a->col, a->speed);
648         }
649
650         if (a->growth >= st->diaglim) 
651             killcell (st, a);
652     }
653
654     randblip (st, (st->head->next) == st->tail);
655
656     for (a = st->head->next; a != st->tail; a = a->next)
657     {
658         if (a->isnext) 
659         {
660             a->isnext = 0;
661             a->speed = a->nextspeed;
662             a->growth = 0.0;
663             a->col = a->nextcol;
664             drawblock (st, cell_x(a), cell_y(a), a->col + st->count);
665         }
666     }
667 }
668
669 static void *
670 petri_init (Display *dpy, Window win)
671 {
672     struct state *st = (struct state *) calloc (1, sizeof(*st));
673     st->dpy = dpy;
674     st->window = win;
675
676     st->delay = get_integer_resource (st->dpy, "delay", "Delay");
677     st->orthlim = 1;
678
679     setup_display (st);
680     setup_arr (st);
681     randblip (st, 1);
682     
683     return st;
684 }
685
686 static unsigned long
687 petri_draw (Display *dpy, Window window, void *closure)
688 {
689   struct state *st = (struct state *) closure;
690   update (st);
691   return st->delay;
692 }
693
694 static void
695 petri_reshape (Display *dpy, Window window, void *closure, 
696                  unsigned int w, unsigned int h)
697 {
698 }
699
700 static Bool
701 petri_event (Display *dpy, Window window, void *closure, XEvent *event)
702 {
703   return False;
704 }
705
706 static void
707 petri_free (Display *dpy, Window window, void *closure)
708 {
709   struct state *st = (struct state *) closure;
710   free (st);
711 }
712
713
714
715 static const char *petri_defaults [] = {
716   ".background:         black",
717   ".foreground:         white",
718   "*fpsSolid:           true",
719   "*delay:              10000",
720   "*count:              20",
721   "*size:               2",
722   "*diaglim:            1.414",
723   "*anychan:            0.0015",
724   "*minorchan:          0.5",
725   "*instantdeathchan:   0.2",
726   "*minlifespan:        500",
727   "*maxlifespan:        1500",
728   "*minlifespeed:       0.04",
729   "*maxlifespeed:       0.13",
730   "*mindeathspeed:      0.42",
731   "*maxdeathspeed:      0.46",
732   "*originalcolors:     false",
733   "*memThrottle:        22M",   /* don't malloc more than this much.
734                                    Scale the pixels up if necessary. */
735 #ifdef HAVE_MOBILE
736   "*ignoreRotation:     True",
737 #endif
738     0
739 };
740
741 static XrmOptionDescRec petri_options [] = {
742   { "-delay",            ".delay",              XrmoptionSepArg, 0 },
743   { "-size",             ".size",               XrmoptionSepArg, 0 },
744   { "-count",            ".count",              XrmoptionSepArg, 0 },
745   { "-diaglim",          ".diaglim",            XrmoptionSepArg, 0 },
746   { "-anychan",          ".anychan",            XrmoptionSepArg, 0 },
747   { "-minorchan",        ".minorchan",          XrmoptionSepArg, 0 },
748   { "-instantdeathchan", ".instantdeathchan",   XrmoptionSepArg, 0 },
749   { "-minlifespan",      ".minlifespan",        XrmoptionSepArg, 0 },
750   { "-maxlifespan",      ".maxlifespan",        XrmoptionSepArg, 0 },
751   { "-minlifespeed",     ".minlifespeed",       XrmoptionSepArg, 0 },
752   { "-maxlifespeed",     ".maxlifespeed",       XrmoptionSepArg, 0 },
753   { "-mindeathspeed",    ".mindeathspeed",      XrmoptionSepArg, 0 },
754   { "-maxdeathspeed",    ".maxdeathspeed",      XrmoptionSepArg, 0 },
755   { "-originalcolors",   ".originalcolors",     XrmoptionNoArg,  "true" },
756   { "-mem-throttle",     ".memThrottle",        XrmoptionSepArg,  0 },
757   { 0, 0, 0, 0 }
758 };
759
760
761 XSCREENSAVER_MODULE ("Petri", petri)