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