1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* petri ---  */
3 
4 #if 0
5 static const char sccsid[] = "@(#)petri.c	5.04 2002/07/19 xlockmore";
6 
7 #endif
8 
9 /* petri, simulate mold in a petri dish. v2.7
10  * by Dan Bornstein, danfuzz@milk.com
11  * with help from Jamie Zawinski, jwz AT jwz.org
12  * Copyright (c) 1992-1999 Dan Bornstein.
13  *
14  * Permission to use, copy, modify, distribute, and sell this software and its
15  * documentation for any purpose is hereby granted without fee, provided that
16  * the above copyright notice appear in all copies and that both that
17  * copyright notice and this permission notice appear in supporting
18  * documentation.  No representations are made about the suitability of this
19  * software for any purpose.  It is provided "as is" without express or
20  * implied warranty.
21  *
22  *
23  * Brief description of options/resources:
24  *
25  * delay: the delay in microseconds between iterations
26  * size: the size of a cell in pixels
27  * count: the number of different kinds of mold (minimum: 2)
28  * diaglim: the age limit for diagonal growth as a multiplier of orthogonal
29  *   growth (minimum: 1, maximum 2). 1 means square growth, 1.414
30  *   (i.e., sqrt(2)) means approximately circular growth, 2 means diamond
31  *   growth.
32  * anychan: the chance (fraction, between 0 and 1) that at each iteration,
33  *   any new cell will be born
34  * minorchan: the chance (fraction, between 0 and 1) that, given that new
35  *   cells will be added, that only two will be added (a minor cell birth
36  *   event)
37  * instantdeathchan: the chance (fraction, between 0 and 1) that, given
38  *   that death and destruction will happen, that instead of using plague
39  *   cells, death will be instantaneous
40  * minlifespan: the minimum lifespan of a colony (before black death ensues)
41  * maxlifespan: the maximum lifespan of a colony (before black death ensues)
42  * minlifespeed: the minimum speed for living cells as a fraction of the
43  *   maximum possible speed (fraction, between 0 and 1)
44  * maxlifespeed: the maximum speed for living cells as a fraction of the
45  *   maximum possible speed (fraction, between 0 and 1)
46  * mindeathspeed: the minimum speed for black death cells as a fraction of the
47  *   maximum possible speed (fraction, between 0 and 1)
48  * maxdeathspeed: the maximum speed for black death cells as a fraction of the
49  *   maximum possible speed (fraction, between 0 and 1)
50  * originalcolors: if true, count must be 8 or less and the colors are a
51  *   fixed set of primary and secondary colors (the artist's original choices)
52  *
53  * Interesting settings:
54  *
55  *      petri -originalcolors -size 8
56  *      petri -size 2
57  *      petri -size 8 -diaglim 1.8
58  *      petri -diaglim 1.1
59  *
60  *      petri -count 4 -anychan 0.01 -minorchan 1 \
61  *              -minlifespan 2000 -maxlifespan 5000
62  *
63  *      petri -count 3 -anychan 1 -minlifespan 100000 \
64  *              -instantdeathchan 0
65  *
66  *      petri -minlifespeed 0.02 -maxlifespeed 0.03 -minlifespan 1 \
67  *              -maxlifespan 1 -instantdeathchan 0 -minorchan 0 \
68  *              -anychan 0.3 -delay 4000
69  *
70  * Revision History:
71  * 22 Jul-2002: xlockmore version by Jouk Jansen <joukj AT hrem.nano.tudelft.nl>
72  */
73 
74 #ifdef STANDALONE
75 #define MODE_petri
76 #define DEFAULTS "*delay: 10000 \n" \
77 	"*size: 4 \n" \
78 	"*ncolors: 8 \n" \
79 	"*fullrandom: True \n" \
80 	"*verbose: False \n" \
81 
82 # define free_petri 0
83 # define reshape_petri 0
84 # define petri_handle_event 0
85 #include "xlockmore.h"		/* in xscreensaver distribution */
86 #else /* STANDALONE */
87 #include "xlock.h"		/* in xlockmore distribution */
88 #include "color.h"
89 #define DO_STIPPLE
90 #endif /* STANDALONE */
91 #include "automata.h"
92 
93 #ifdef MODE_petri
94 
95 #include <math.h>
96 
97 #define FLOAT float
98 #define RAND_FLOAT (((FLOAT) (LRAND() & 0xffff)) / ((FLOAT) 0x10000))
99 
100 #define DEF_MEMTHROTTLE "22M"
101 #define DEF_DIAGLIM "1.414"
102 #define DEF_ANYCHAN "0.0015"
103 #define DEF_MINORCHAN "0.5"
104 #define DEF_INSTANTDEATHCHAN "0.2"
105 #define DEF_MINLIFESPAN "500"
106 #define DEF_MAXLIFESPAN "1500"
107 #define DEF_MINLIFESPEED "0.04"
108 #define DEF_MAXLIFESPEED "0.13"
109 #define DEF_MINDEATHSPEED "0.42"
110 #define DEF_MAXDEATHSPEED "0.46"
111 
112 #define REDRAWSTEP 2000         /* How many cells to draw per cycle */
113 
114 static char* memThrottle;
115 static FLOAT st_diaglim;
116 static FLOAT orthlim = 1.0;
117 static FLOAT st_anychan;
118 static FLOAT st_minorchan;
119 static FLOAT st_instantdeathchan;
120 static int st_minlifespan;
121 static int st_maxlifespan;
122 static FLOAT st_minlifespeed;
123 static FLOAT st_maxlifespeed;
124 static FLOAT st_mindeathspeed;
125 static FLOAT st_maxdeathspeed;
126 
127 static XrmOptionDescRec opts[] =
128 {
129    {(char *) "-anychan", (char *) ".petri.anychan", XrmoptionSepArg, (caddr_t) NULL},
130    {(char *) "-diaglim", (char *) ".petri.diaglim", XrmoptionSepArg, (caddr_t) NULL},
131    {(char *) "-instantdeathchan", (char *) ".petri.instantdeathchan", XrmoptionSepArg, (caddr_t) NULL},
132    {(char *) "-memthrottle", (char *) ".petri.memthrottle", XrmoptionSepArg, (caddr_t) NULL},
133    {(char *) "-maxlifespan", (char *) ".petri.maxlifespan", XrmoptionSepArg, (caddr_t) NULL},
134    {(char *) "-minlifespan", (char *) ".petri.minlifespan", XrmoptionSepArg, (caddr_t) NULL},
135    {(char *) "-maxlifespeed", (char *) ".petri.maxlifespeed", XrmoptionSepArg, (caddr_t) NULL},
136    {(char *) "-minlifespeed", (char *) ".petri.minlifespeed", XrmoptionSepArg, (caddr_t) NULL},
137    {(char *) "-maxdeathspeed", (char *) ".petri.maxdeathspeed", XrmoptionSepArg, (caddr_t) NULL},
138    {(char *) "-mindeathspeed", (char *) ".petri.mindeathspeed", XrmoptionSepArg, (caddr_t) NULL},
139    {(char *) "-minorchan", (char *) ".petri.minorchan", XrmoptionSepArg, (caddr_t) NULL}
140 };
141 
142 static argtype vars[] =
143 {
144    {(void *) &st_anychan, (char *) "anychan",
145    (char *) "Anychan", (char *) DEF_ANYCHAN , t_Float},
146    {(void *) &st_diaglim, (char *) "diaglim",
147    (char *) "Diaglim", (char *) DEF_DIAGLIM , t_Float},
148    {(void *) &st_instantdeathchan, (char *) "instantdeathchan",
149    (char *) "Instantdeathchan", (char *) DEF_DIAGLIM , t_Float},
150    {(void *) &memThrottle, (char *) "memthrottle",
151    (char *) "Memthrottle", (char *) DEF_MEMTHROTTLE , t_String},
152    {(void *) &st_maxlifespan, (char *) "maxlifespan",
153    (char *) "Maxlifespan", (char *) DEF_MAXLIFESPAN , t_Int},
154    {(void *) &st_minlifespan, (char *) "minlifespan",
155    (char *) "Minlifespan", (char *) DEF_MINLIFESPAN , t_Int},
156    {(void *) &st_maxlifespeed, (char *) "maxlifespeed",
157    (char *) "Maxlifespeed", (char *) DEF_MAXLIFESPEED , t_Float},
158    {(void *) &st_minlifespeed, (char *) "minlifespeed",
159    (char *) "Minlifespeed", (char *) DEF_MINLIFESPEED , t_Float},
160    {(void *) &st_maxdeathspeed, (char *) "maxdeathspeed",
161    (char *) "Maxdeathspeed", (char *) DEF_MAXDEATHSPEED , t_Float},
162    {(void *) &st_mindeathspeed, (char *) "mindeathspeed",
163    (char *) "Mindeathspeed", (char *) DEF_MINDEATHSPEED , t_Float},
164    {(void *) &st_minorchan, (char *) "minorchan",
165    (char *) "Minorchan", (char *) DEF_MINORCHAN , t_Float}
166 };
167 
168 static OptionStruct desc[] =
169 {
170      {(char *) "-anychan float", (char *) "The chance that new cell will be born"},
171    {(char *) "-diaglim float", (char *) "The age limit for diagonal growth"},
172    {(char *) "-instantdeathchan float", (char *) "Instant death chance"},
173    {(char *) "-mem-throttle string", (char *) "memThrottle"},
174    {(char *) "-maxlifespan num", (char *) "the maximum lifespan of a colony"},
175    {(char *) "-minlifespan num", (char *) "the minimum lifespan of a colony"},
176    {(char *) "-maxlifespeed float", (char *) "the maximum speed for living cells"},
177    {(char *) "-minlifespeed float", (char *) "the minimum speed for living cells"},
178    {(char *) "-maxdeathspeed float", (char *) "the maximum speed for black death cells"},
179    {(char *) "-mindeathspeed float", (char *) "the minimum speed for black death cells"},
180    {(char *) "-minorchan float", (char *) "The chance on a minor cell birth"}
181 };
182 
183 ENTRYPOINT ModeSpecOpt petri_opts =
184 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
185 
186 #ifdef USE_MODULES
187 ModStruct   petri_description =
188 {"petri", "init_petri", "draw_petri", "release_petri",
189  "refresh_petri", "init_petri", (char *) NULL, &petri_opts,
190  10000, 1, 1, 4, 8, 1.0, "",
191  "Shows a mold simulation in a petri dish", 0, NULL};
192 
193 #endif
194 
195 typedef struct cell_s
196 {
197     unsigned char col;              /*  0      */
198     unsigned char isnext;           /*  1      */
199     unsigned char nextcol;          /*  2      */
200                                     /*  3      */
201     struct cell_s *next;            /*  4      */
202     struct cell_s *prev;            /*  8    - */
203     FLOAT speed;                    /* 12      */
204     FLOAT growth;                   /* 16 20 - */
205     FLOAT nextspeed;                /* 20 28   */
206                                     /* 24 36 - */
207 } cell;
208 
209 #define PETRIBITS(n,w,h)\
210   if ((sp->pixmaps[sp->init_bits]=\
211   XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\
212   free_petri_screen(display,sp); return False;} else {sp->init_bits++;}
213 
214 #define cell_x(c) (((c) - sp->arr) % sp->arr_width)
215 #define cell_y(c) (((c) - sp->arr) / sp->arr_width)
216 
217 typedef struct {
218    cell *arr;
219    cell *head;
220    cell *tail;
221    unsigned int count;  /* Must be unsigned due to shifting */
222    Bool originalcolors;
223    GC *coloredGCs;
224    int blastcount;
225    int windowWidth;
226    int windowHeight;
227    int arr_width;
228    int arr_height;
229    int xSize;
230    int ySize;
231    int xOffset;
232    int yOffset;
233    FLOAT diaglim;
234    FLOAT anychan;
235    FLOAT minorchan;
236    FLOAT instantdeathchan;
237    int minlifespan;
238    int maxlifespan;
239    FLOAT minlifespeed;
240    FLOAT maxlifespeed;
241    FLOAT mindeathspeed;
242    FLOAT maxdeathspeed;
243    int redrawing, redrawpos;
244    Colormap cmap;
245    Bool mono;
246    int         init_bits;
247    unsigned char colors[NUMSTIPPLES - 1];
248    GC          stippledGC;
249    Pixmap      pixmaps[NUMSTIPPLES - 1];
250 } petristruct;
251 
252 static petristruct *petries = (petristruct *) NULL;
253 
random_life_value(petristruct * sp)254 static int random_life_value (petristruct *sp)
255 {
256     return (int) ((RAND_FLOAT * (sp->maxlifespan - sp->minlifespan)) +
257 		  sp->minlifespan);
258 }
259 
260 static void
free_petri_screen(Display * display,petristruct * sp)261 free_petri_screen(Display *display, petristruct *sp)
262 {
263     int n, shade;
264 
265     if (sp == NULL) {
266         return;
267     }
268     if (sp->coloredGCs != NULL) {
269 	for (n = 0; n < sp->count*2; n++)
270 		if (sp->coloredGCs[n] != None)
271 			XFreeGC(display, sp->coloredGCs[n]);
272 	free(sp->coloredGCs);
273 	sp->coloredGCs = (GC *) NULL;
274     }
275     if (sp->stippledGC != None) {
276         XFreeGC(display, sp->stippledGC);
277         sp->stippledGC = None;
278     }
279     for (shade = 0; shade < sp->init_bits; shade++) {
280         XFreePixmap(display, sp->pixmaps[shade]);
281     }
282     sp->init_bits = 0;
283     if (sp->arr != NULL) {
284 		free(sp->arr);
285 		sp->arr = (cell *) NULL;
286     }
287     if (sp->head != NULL) {
288 		free(sp->head);
289 		sp->head = (cell *) NULL;
290     }
291     if (sp->tail != NULL) {
292 		free(sp->tail);
293 		sp->tail = (cell *) NULL;
294     }
295     sp = NULL;
296 }
297 
setup_random_colormap(ModeInfo * mi)298 static int setup_random_colormap (ModeInfo * mi)
299 {
300    petristruct *sp = &petries[MI_SCREEN(mi)];
301    int lose = 0;
302    int ncolors = sp->count - 1;
303    int n;
304    XColor *colors = (XColor *) NULL;
305 #ifdef STANDALONE
306   Screen *screen = MI_SCREENPTR(mi);
307 #endif
308    Display    *display = MI_DISPLAY(mi);
309    Window      window = MI_WINDOW(mi);
310 
311     colors = (XColor *) calloc (sizeof(*colors), sp->count*2);
312     if (colors == NULL) {
313 	free_petri_screen(display, sp);
314         return False;
315     }
316     colors[0].pixel = MI_BLACK_PIXEL(mi);
317 
318     if (MI_NPIXELS(mi) <= 2) {
319  	sp->mono = True;
320     } else
321         make_random_colormap (
322 #ifdef STANDALONE
323             screen, MI_VISUAL(mi),
324             sp->cmap, colors + 1, &ncolors,
325             True, True, 0, True
326 #else
327             mi,
328             sp->cmap, colors + 1, &ncolors,
329             True, True, 0
330 #endif
331             );
332     if (ncolors < 1) {
333 	sp->mono = True;
334     }
335 
336     if (sp->mono) {
337 	if (colors != NULL) {
338 		XFree((caddr_t) colors);
339 		colors = (XColor *) NULL;
340 	}
341 
342         if (2 * sp->count > NUMSTIPPLES - 1)
343 		sp->count = (NUMSTIPPLES - 1) / 2;
344 	if (sp->stippledGC == None) {
345 	    XGCValues   gcv;
346 
347 	    gcv.fill_style = FillOpaqueStippled;
348 	    if ((sp->stippledGC = XCreateGC(display, window, GCFillStyle,
349 		   &gcv)) == None) {
350 		free_petri_screen(display, sp);
351 		return False;
352 	    }
353 	}
354 	if (sp->init_bits == 0) {
355             int i;
356 	    for (i = 1; i < 2 * sp->count; i++) {
357 		PETRIBITS(stipples[i], STIPPLESIZE, STIPPLESIZE);
358 	    }
359 	}
360 	return True;
361     }
362     ncolors++;
363     sp->count = ncolors;
364     (void) memcpy (colors + sp->count, colors, sp->count * sizeof(*colors));
365     colors[sp->count].pixel = MI_WHITE_PIXEL(mi);
366     for (n = 1; n < sp->count; n++)
367     {
368 	int m = n + sp->count;
369 	colors[n].red = colors[m].red / 2;
370 	colors[n].green = colors[m].green / 2;
371 	colors[n].blue = colors[m].blue / 2;
372 
373 	if (!XAllocColor (display, sp->cmap, &colors[n]))
374 	{
375 	    lose++;
376 	    colors[n] = colors[m];
377 	}
378     }
379     if (lose)
380     {
381 	(void) fprintf (stderr,
382 		 "petri: unable to allocate %d half-intensity colors.\n",
383 		 lose);
384     }
385     for (n = 0; n < sp->count*2; n++)
386     {
387         XGCValues gcv;
388 
389 #ifdef WIN32
390         if (n == 0)
391 		gcv.foreground = MI_BLACK_PIXEL(mi);
392 	else
393 		gcv.foreground = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
394 #else
395 	gcv.foreground = colors[n].pixel;
396 #endif
397 	sp->coloredGCs[n] = XCreateGC (display, window, GCForeground, &gcv);
398     }
399     if (colors != NULL) {
400 	XFree((caddr_t) colors);
401 	colors = (XColor *) NULL;
402     }
403     return True;
404 }
405 
406 #ifndef WIN32
setup_original_colormap(ModeInfo * mi)407 static int setup_original_colormap (ModeInfo * mi)
408 {
409    petristruct *sp = &petries[MI_SCREEN(mi)];
410    int lose = 0;
411    int n;
412    XColor *colors = (XColor *) NULL;
413    Display    *display = MI_DISPLAY(mi);
414    Window      window = MI_WINDOW(mi);
415 
416     if (MI_NPIXELS(mi) <= 2)
417  	sp->mono = True;
418     if (sp->mono) {
419         if (2 * sp->count > NUMSTIPPLES - 1)
420 		sp->count = (NUMSTIPPLES - 1) / 2;
421 	if (sp->stippledGC == None) {
422 	    XGCValues   gcv;
423 
424 	    gcv.fill_style = FillOpaqueStippled;
425 	    if ((sp->stippledGC = XCreateGC(display, window, GCFillStyle,
426 		   &gcv)) == None) {
427 		free_petri_screen(display, sp);
428 		return False;
429 	    }
430 	}
431 	if (sp->init_bits == 0) {
432             int i;
433 	    for (i = 1; i < 2 * sp->count; i++) {
434 		PETRIBITS(stipples[i], STIPPLESIZE, STIPPLESIZE);
435 	    }
436 	}
437 	return True;
438     }
439 
440     colors = (XColor *) calloc (sizeof(*colors), sp->count*2);
441     if (colors == NULL) {
442 	free_petri_screen(display, sp);
443         return False;
444     }
445     colors[0].pixel = MI_BLACK_PIXEL(mi);
446     colors[sp->count].pixel = MI_WHITE_PIXEL(mi);
447 
448     for (n = 1; n < sp->count; n++)
449     {
450 	int m = n + sp->count;
451 	colors[n].red =   ((n & 0x01) != 0) * 0x8000;
452 	colors[n].green = ((n & 0x02) != 0) * 0x8000;
453 	colors[n].blue =  ((n & 0x04) != 0) * 0x8000;
454 
455 	if (!sp->mono && !XAllocColor (display, sp->cmap, &colors[n]))
456 	{
457 	    lose++;
458 	    colors[n] = colors[0];
459 	}
460 
461 	colors[m].red   = colors[n].red + 0x4000;
462 	colors[m].green = colors[n].green + 0x4000;
463 	colors[m].blue  = colors[n].blue + 0x4000;
464 
465 	if (!XAllocColor (display, sp->cmap, &colors[m]))
466 	{
467 	    lose++;
468 	    colors[m] = colors[sp->count];
469 	}
470     }
471 
472     if (lose)
473     {
474 	(void) fprintf (stderr,
475 		 "petri: unable to allocate %d colors.\n",
476 		 lose);
477     }
478 
479     for (n = 0; n < sp->count*2; n++)
480     {
481         XGCValues gcv;
482 
483 	gcv.foreground = colors[n].pixel;
484 	sp->coloredGCs[n] = XCreateGC (display, window, GCForeground, &gcv);
485     }
486     if (colors != NULL) {
487 	XFree((caddr_t) colors);
488 	colors = (XColor *) NULL;
489     }
490     return True;
491 }
492 #endif
493 
setup_display(ModeInfo * mi)494 static int setup_display (ModeInfo * mi)
495 {
496    petristruct *sp = &petries[MI_SCREEN(mi)];
497    Display    *display = MI_DISPLAY(mi);
498    Window      window = MI_WINDOW(mi);
499     XWindowAttributes xgwa;
500 
501     int cell_size = MI_SIZE( mi );
502     int osize, alloc_size, oalloc;
503     int mem_throttle = 0;
504     char *s;
505 
506     if (cell_size < 1) cell_size = 1;
507 
508     osize = cell_size;
509 
510    s = (char*) malloc( strlen( memThrottle ) + 1);
511     if (s)
512       {
513         int n;
514         char c;
515 
516 	(void) strcpy( s , memThrottle );
517         if (1 == sscanf (s, " %d M %c", &n, &c) ||
518             1 == sscanf (s, " %d m %c", &n, &c))
519           mem_throttle = n * (1 << 20);
520         else if (1 == sscanf (s, " %d K %c", &n, &c) ||
521                  1 == sscanf (s, " %d k %c", &n, &c))
522           mem_throttle = n * (1 << 10);
523         else if (1 == sscanf (s, " %d %c", &n, &c))
524           mem_throttle = n;
525         else
526           {
527             (void) fprintf (stderr,
528 		     "petri: invalid memThrottle \"%s\" (try \"10M\")\n", s);
529             free_petri_screen(display, sp);
530 	    return False;
531           }
532 
533         free(s);
534       }
535 
536     (void) XGetWindowAttributes (display, window, &xgwa);
537 
538     sp->originalcolors = ( NRAND( 3 ) == 1 );
539 
540     sp->count = MI_NCOLORS(mi);
541     if ( sp->count < 2) sp->count = 2;
542 
543     /* number of colors can't be greater than the half depth of the screen. */
544     if ( sp->count > (1L << (xgwa.depth-1)))
545       sp->count = (1L << (xgwa.depth-1));
546 
547     /* Actually, since cell->col is of type char, this has to be small. */
548     if ( sp->count >= (1L << ((sizeof( sp->arr[0].col) * 8) - 1)))
549       sp->count = (1L << ((sizeof( sp->arr[0].col) * 8) - 1));
550 
551     if ( sp->originalcolors && ( sp->count > 8))
552     {
553 	sp->count = 8;
554     }
555 
556     sp->coloredGCs = (GC *) calloc (sizeof(GC), sp->count * 2);
557     if (sp->coloredGCs == NULL) {
558 	free_petri_screen(display, sp);
559         return False;
560     }
561 
562    if (MI_IS_FULLRANDOM(mi))
563      sp->diaglim = 1.0 + (float) LRAND() / (float) MAXRAND;
564    else
565      sp->diaglim = st_diaglim;
566     if (sp->diaglim < 1.0)
567     {
568 	sp->diaglim = 1.0;
569     }
570     else if (sp->diaglim > 2.0)
571     {
572 	sp->diaglim = 2.0;
573     }
574     sp->diaglim *= orthlim;
575 
576    if (MI_IS_FULLRANDOM(mi))
577      sp->anychan = (float) pow( (double) LRAND() / (double) MAXRAND , 15.0 );
578    else
579      sp->anychan = st_anychan;
580     if (sp->anychan < 0.0)
581     {
582 	sp->anychan = 0.0;
583     }
584     else if (sp->anychan > 1.0)
585     {
586 	sp->anychan = 1.0;
587     }
588 
589    if (MI_IS_FULLRANDOM(mi))
590      sp->minorchan = (float) LRAND() / (float) MAXRAND;
591    else
592      sp->minorchan = st_minorchan;
593     if (sp->minorchan < 0.0)
594     {
595 	sp->minorchan = 0.0;
596     }
597     else if (sp->minorchan > 1.0)
598     {
599 	sp->minorchan = 1.0;
600     }
601 
602    if (MI_IS_FULLRANDOM(mi))
603      sp->instantdeathchan = (float) pow( (double) LRAND() / (double) MAXRAND ,
604 					 8.0 );
605    else
606      sp->instantdeathchan = st_instantdeathchan;
607     if (sp->instantdeathchan < 0.0)
608     {
609 	sp->instantdeathchan = 0.0;
610     }
611     else if (sp->instantdeathchan > 1.0)
612     {
613 	sp->instantdeathchan = 1.0;
614     }
615 
616    if (MI_IS_FULLRANDOM(mi))
617      sp->minlifespan = NRAND( st_minlifespan ) + 1;
618    else
619      sp->minlifespan = st_minlifespan;
620     if (sp->minlifespan < 1)
621     {
622 	sp->minlifespan = 1;
623     }
624 
625    if (MI_IS_FULLRANDOM(mi))
626      sp->maxlifespan = NRAND( st_maxlifespan ) + sp->minlifespan;
627    else
628      sp->maxlifespan = st_maxlifespan;
629     if (sp->maxlifespan < sp->minlifespan)
630     {
631 	sp->maxlifespan = sp->minlifespan;
632     }
633 
634    if (MI_IS_FULLRANDOM(mi))
635      sp->minlifespeed = st_maxlifespeed * (float) LRAND() / (float) MAXRAND;
636    else
637      sp->minlifespeed = st_minlifespeed;
638     if (sp->minlifespeed < 0.0)
639     {
640 	sp->minlifespeed = 0.0;
641     }
642     else if (sp->minlifespeed > 1.0 )
643     {
644 	sp->minlifespeed = 1.0;
645     }
646 
647    if (MI_IS_FULLRANDOM(mi))
648      sp->maxlifespeed = ( (st_maxlifespeed - sp->minlifespeed ) *
649 			 (float) LRAND() / (float) MAXRAND ) +
650      sp->minlifespeed;
651    else
652      sp->maxlifespeed = st_maxlifespeed;
653     if (sp->maxlifespeed < sp->minlifespeed)
654     {
655 	sp->maxlifespeed = sp->minlifespeed;
656     }
657     else if (sp->maxlifespeed > 1.0)
658     {
659 	sp->maxlifespeed = 1.0;
660     }
661 
662    if (MI_IS_FULLRANDOM(mi))
663      sp->mindeathspeed = st_maxdeathspeed * (float) LRAND() / (float) MAXRAND;
664    else
665      sp->mindeathspeed = st_mindeathspeed;
666     if (sp->mindeathspeed < 0.0)
667     {
668 	sp->mindeathspeed = 0.0;
669     }
670     else if (sp->mindeathspeed > 1.0)
671     {
672 	sp->mindeathspeed = 1.0;
673     }
674 
675    if (MI_IS_FULLRANDOM(mi))
676      sp->maxdeathspeed = ( (st_maxdeathspeed - sp->mindeathspeed ) *
677 			 (float) LRAND() / (float) MAXRAND ) +
678      sp->mindeathspeed;
679    else
680      sp->maxdeathspeed = st_maxdeathspeed;
681     if (sp->maxdeathspeed < sp->mindeathspeed)
682     {
683 	sp->maxdeathspeed = sp->mindeathspeed;
684     }
685     else if (sp->maxdeathspeed > 1.0)
686     {
687 	sp->maxdeathspeed = 1.0;
688     }
689 
690     sp->minlifespeed *= sp->diaglim;
691     sp->maxlifespeed *= sp->diaglim;
692     sp->mindeathspeed *= sp->diaglim;
693     sp->maxdeathspeed *= sp->diaglim;
694 
695     sp->cmap = xgwa.colormap;
696 
697     sp->windowWidth = xgwa.width;
698     sp->windowHeight = xgwa.height;
699 
700     sp->arr_width = sp->windowWidth / cell_size;
701     sp->arr_height = sp->windowHeight / cell_size;
702 
703     alloc_size = sizeof(cell) * sp->arr_width * sp->arr_height;
704     oalloc = alloc_size;
705 
706     if (mem_throttle > 0)
707       while (cell_size < sp->windowWidth/10 &&
708              cell_size < sp->windowHeight/10 &&
709              alloc_size > mem_throttle)
710         {
711           cell_size++;
712           sp->arr_width = sp->windowWidth / cell_size;
713           sp->arr_height = sp->windowHeight / cell_size;
714           alloc_size = sizeof(cell) * sp->arr_width * sp->arr_height;
715         }
716 
717     if (osize != cell_size)
718       {
719         static int warned = 0;
720         if (!warned)
721           {
722             (void) fprintf (stderr,
723              "petri: throttling cell size from %d to %d because of %dM limit.\n",
724                      osize, cell_size, mem_throttle / (1 << 20));
725             (void) fprintf (stderr, "petri: %dx%dx%d = %.1fM, %dx%dx%d = %.1fM.\n",
726                      sp->windowWidth, sp->windowHeight, osize,
727                      ((float) oalloc) / (1 << 20),
728                      sp->windowWidth, sp->windowHeight, cell_size,
729                      ((float) alloc_size) / (1 << 20));
730             warned = 1;
731           }
732       }
733 
734     sp->xSize = sp->windowWidth / sp->arr_width;
735     sp->ySize = sp->windowHeight / sp->arr_height;
736     if (sp->xSize > sp->ySize)
737     {
738 	sp->xSize = sp->ySize;
739     }
740     else
741     {
742 	sp->ySize = sp->xSize;
743     }
744 
745     sp->xOffset = (sp->windowWidth - (sp->arr_width * sp->xSize)) / 2;
746     sp->yOffset = (sp->windowHeight - (sp->arr_height * sp->ySize)) / 2;
747 
748 #ifndef WIN32
749     if (sp->originalcolors)
750     {
751 	if (!setup_original_colormap (mi))
752 		return False;
753     }
754     else
755 #endif
756     {
757 	if (!setup_random_colormap (mi)) /* PseudoColor can error out */
758 	/*if (!setup_original_colormap (mi))*/
759 		return False;
760     }
761     return True;
762 }
763 
drawblock(ModeInfo * mi,int x,int y,unsigned char c)764 static void drawblock (ModeInfo * mi , int x, int y, unsigned char c)
765 {
766    petristruct *sp = &petries[MI_SCREEN(mi)];
767    Display    *display = MI_DISPLAY(mi);
768    Window      window = MI_WINDOW(mi);
769 
770    if (sp->mono) {
771 	if (c == 0) {
772 	   XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
773        XFillRectangle (display, window, MI_GC(mi),
774 		    x * sp->xSize + sp->xOffset, y * sp->ySize + sp->yOffset,
775 		    sp->xSize, sp->ySize);
776 	} else {
777                 XGCValues   gcv;
778 
779                 gcv.stipple = sp->pixmaps[c - 1];
780                 gcv.foreground = MI_WHITE_PIXEL(mi);
781                 gcv.background = MI_BLACK_PIXEL(mi);
782                 XChangeGC(display, sp->stippledGC,
783                           GCStipple | GCForeground | GCBackground, &gcv);
784        XFillRectangle (display, window, sp->stippledGC,
785 		    x * sp->xSize + sp->xOffset, y * sp->ySize + sp->yOffset,
786 		    sp->xSize, sp->ySize);
787 	}
788    } else
789    if (sp->xSize == 1 && sp->ySize == 1)
790     XDrawPoint (display, window, sp->coloredGCs[c], x + sp->xOffset, y +
791 		sp->yOffset);
792    else
793     XFillRectangle (display, window, sp->coloredGCs[c],
794 		    x * sp->xSize + sp->xOffset, y * sp->ySize + sp->yOffset,
795 		    sp->xSize, sp->ySize);
796 }
797 
setup_arr(ModeInfo * mi)798 static int setup_arr (ModeInfo * mi)
799 {
800    petristruct *sp = &petries[MI_SCREEN(mi)];
801     int x, y;
802    Display    *display = MI_DISPLAY(mi);
803    Window      window = MI_WINDOW(mi);
804 
805     if (sp->arr != NULL)
806     {
807 	free(sp->arr);
808     }
809 
810     if (sp->mono) {
811       XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
812       XFillRectangle (display, window, MI_GC(mi), 0, 0,
813 		    sp->windowWidth, sp->windowHeight);
814     } else
815       XFillRectangle (display, window, sp->coloredGCs[0], 0, 0,
816 		    sp->windowWidth, sp->windowHeight);
817 
818     sp->arr = (cell *) calloc (sizeof(cell), sp->arr_width * sp->arr_height);
819     if (!sp->arr)
820       {
821         (void) fprintf (stderr, "petri: out of memory allocating %dx%d grid\n",
822                  sp->arr_width, sp->arr_height);
823 	free_petri_screen(display, sp);
824         return False;
825       }
826 
827     for (y = 0; y < sp->arr_height; y++)
828     {
829       int row = y * sp->arr_width;
830 	for (x = 0; x < sp->arr_width; x++)
831 	{
832 	    sp->arr[row+x].speed = 0.0;
833 	    sp->arr[row+x].growth = 0.0;
834 	    sp->arr[row+x].col = 0;
835 	    sp->arr[row+x].isnext = 0;
836 	    sp->arr[row+x].next = 0;
837 	    sp->arr[row+x].prev = 0;
838 	}
839     }
840 
841     if (sp->head == NULL)
842     {
843 	sp->head = (cell *) malloc (sizeof (cell));
844     if (!sp->head)
845       {
846         (void) fprintf (stderr, "petri: out of memory allocating %dx%d grid\n",
847                  sp->arr_width, sp->arr_height);
848 	free_petri_screen(display, sp);
849         return False;
850     }
851     }
852 
853     if (sp->tail == NULL)
854     {
855 	sp->tail = (cell *) malloc (sizeof (cell));
856     if (!sp->tail)
857       {
858         (void) fprintf (stderr, "petri: out of memory allocating %dx%d grid\n",
859                  sp->arr_width, sp->arr_height);
860 	free_petri_screen(display, sp);
861         return False;
862     }
863     }
864 
865     sp->head->next = sp->tail;
866     sp->head->prev = sp->head;
867     sp->tail->next = sp->tail;
868     sp->tail->prev = sp->head;
869 
870     sp->blastcount = random_life_value (sp);
871     return True;
872 }
873 
newcell(ModeInfo * mi,cell * c,unsigned char col,FLOAT spf)874 static void newcell (ModeInfo * mi , cell *c, unsigned char col, FLOAT spf)
875 {
876    petristruct *sp = &petries[MI_SCREEN(mi)];
877     if (! c) return;
878 
879     if (c->col == col) return;
880 
881     c->nextcol = col;
882     c->nextspeed = spf;
883     c->isnext = 1;
884 
885     if (c->prev == 0) {
886 	c->next = sp->head->next;
887 	c->prev = sp->head;
888 	sp->head->next = c;
889 	c->next->prev = c;
890     }
891 }
892 
killcell(ModeInfo * mi,cell * c)893 static void killcell (ModeInfo * mi , cell *c)
894 {
895    petristruct *sp = &petries[MI_SCREEN(mi)];
896 
897    c->prev->next = c->next;
898     c->next->prev = c->prev;
899     c->prev = 0;
900     c->speed = 0.0;
901     drawblock (mi , cell_x(c), cell_y(c), c->col);
902 }
903 
randblip(ModeInfo * mi,int doit)904 static int randblip (ModeInfo * mi , int doit)
905 {
906    petristruct *sp = &petries[MI_SCREEN(mi)];
907     int n;
908     int b = 0;
909     if (!doit
910 	&& (sp->blastcount-- >= 0)
911 	&& (RAND_FLOAT > sp->anychan))
912     {
913 	return True;
914     }
915 
916     if (sp->blastcount < 0)
917     {
918 	b = 1;
919 	n = 2;
920 	sp->blastcount = random_life_value(sp);
921 	if (RAND_FLOAT < sp->instantdeathchan)
922 	{
923 	    /* clear everything every so often to keep from getting into a
924 	     * rut */
925 	    if (!setup_arr(mi)) {
926                return False;
927 	    }
928 	    b = 0;
929 	}
930     }
931     else if (RAND_FLOAT <= sp->minorchan)
932     {
933 	n = 2;
934     }
935     else
936     {
937 	n = NRAND(3) + 3;
938     }
939 
940     while (n--)
941     {
942 	int x = NRAND(sp->arr_width);
943 	int y = NRAND(sp->arr_height);
944 	int c;
945 	FLOAT s;
946 	if (b)
947 	{
948 	    c = 0;
949 	    s = RAND_FLOAT * (sp->maxdeathspeed - sp->mindeathspeed) + sp->mindeathspeed;
950 	}
951 	else
952 	{
953 	    if ( sp->count <= 1 )
954 	      c = 1;
955 	    else
956 	      c = (NRAND(sp->count-1)) + 1;
957 	    s = RAND_FLOAT * (sp->maxlifespeed - sp->minlifespeed) + sp->minlifespeed;
958 	}
959 	newcell (mi , &sp->arr[y * sp->arr_width + x], c, s);
960     }
961     return True;
962 }
963 
964 ENTRYPOINT void
draw_petri(ModeInfo * mi)965 draw_petri (ModeInfo * mi)
966 {
967    petristruct *sp = &petries[MI_SCREEN(mi)];
968     cell *a;
969 
970    if (petries == NULL)
971 	return;
972    if (sp->arr == NULL)
973       return;
974    MI_IS_DRAWN(mi) = True;
975 
976     for (a = sp->head->next; a != sp->tail; a = a->next)
977     {
978 	static XPoint all_coords[] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1},
979 				      {-1,  0}, { 1, 0}, {0, -1}, {0, 1},
980 				      {99, 99}};
981 
982         XPoint *coords = 0;
983 
984         if (a->speed == 0) continue;
985         a->growth += a->speed;
986 
987 	if (a->growth >= sp->diaglim)
988 	{
989 	    coords = all_coords;
990 	}
991         else if (a->growth >= orthlim)
992 	{
993 	    coords = &all_coords[4];
994 	}
995 	else
996 	{
997 	    continue;
998 	}
999 
1000 	while (coords->x != 99)
1001 	{
1002 	    int x = cell_x(a) + coords->x;
1003 	    int y = cell_y(a) + coords->y;
1004 	    coords++;
1005 
1006 	    if (x < 0) x = sp->arr_width - 1;
1007 	    else if (x >= sp->arr_width) x = 0;
1008 
1009 	    if (y < 0) y = sp->arr_height - 1;
1010 	    else if (y >= sp->arr_height) y = 0;
1011 
1012 	    newcell (mi , &sp->arr[y * sp->arr_width + x], a->col, a->speed);
1013 	}
1014 
1015 	if (a->growth >= sp->diaglim)
1016 	    killcell (mi ,a);
1017     }
1018 
1019     if (!randblip (mi , (sp->head->next) == sp->tail))
1020 	 return;
1021     for (a = sp->head->next; a != sp->tail; a = a->next)
1022     {
1023 	if (a->isnext)
1024 	{
1025 	    a->isnext = 0;
1026 	    a->speed = a->nextspeed;
1027 	    a->growth = 0.0;
1028 	    a->col = a->nextcol;
1029 	    drawblock (mi , cell_x(a), cell_y(a), a->col + sp->count);
1030 	}
1031     }
1032     if (sp->redrawing) {
1033         int i;
1034 
1035         for (i = 0; i < REDRAWSTEP; i++) {
1036             cell *a = &sp->arr[sp->redrawpos];
1037             drawblock(mi, cell_x(a), cell_y(a), a->col);
1038             if (++(sp->redrawpos) >= sp->arr_width * sp->arr_height) {
1039                 sp->redrawing = 0;
1040                 break;
1041             }
1042         }
1043     }
1044 }
1045 
1046 ENTRYPOINT void
init_petri(ModeInfo * mi)1047 init_petri(ModeInfo * mi)
1048 {
1049 	Display    *display = MI_DISPLAY(mi);
1050 	petristruct *sp;
1051 
1052    if (MI_IS_VERBOSE(mi))
1053      {
1054 	(void) printf( "memThrottle : %s\n" , memThrottle );
1055         (void) printf( "diaglim : %f\n" , st_diaglim );
1056         (void) printf( "anychan : %f\n" , st_anychan );
1057         (void) printf( "minorchan : %f\n" , st_minorchan );
1058         (void) printf( "instantdeathchan : %f\n" , st_instantdeathchan );
1059         (void) printf( "minlifespan : %d\n" , st_minlifespan );
1060         (void) printf( "maxlifespan : %d\n" , st_maxlifespan );
1061         (void) printf( "minlifespeed : %f\n" , st_minlifespeed );
1062         (void) printf( "maxlifespeed : %f\n" , st_maxlifespeed );
1063         (void) printf( "mindeathspeed : %f\n" , st_mindeathspeed );
1064         (void) printf( "maxdeathspeed : %f\n" , st_maxdeathspeed );
1065      }
1066 
1067 /* initialize */
1068 	MI_INIT(mi, petries);
1069 	sp = &petries[MI_SCREEN(mi)];
1070 	free_petri_screen(display, sp);
1071         sp->redrawing = 0;
1072         if (!setup_display(mi))
1073 	   return;
1074 	if (!setup_arr(mi))
1075 	    return;
1076 
1077     (void) randblip (mi , 1);
1078 }
1079 
1080 ENTRYPOINT void
release_petri(ModeInfo * mi)1081 release_petri(ModeInfo * mi)
1082 {
1083    if (petries != NULL) {
1084       int screen;
1085 
1086       for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1087 	free_petri_screen(MI_DISPLAY(mi), &petries[screen]);
1088       free(petries);
1089       petries = (petristruct *) NULL;
1090    }
1091 }
1092 
1093 #ifndef STANDALONE
1094 ENTRYPOINT void
refresh_petri(ModeInfo * mi)1095 refresh_petri(ModeInfo * mi)
1096 {
1097   petristruct *sp;
1098 
1099   if (petries == NULL)
1100     return;
1101   sp = &petries[MI_SCREEN(mi)];
1102 
1103   sp->redrawing = 1;
1104   sp->redrawpos = 0;
1105 }
1106 #endif
1107 
1108 XSCREENSAVER_MODULE ("Petri", petri)
1109 
1110 #endif /* MODE_petri */
1111