911b99be27bd8d2c6581a5e5e59d8faed492bed8
[xscreensaver] / hacks / cloudlife.c
1 /* cloudlife by Don Marti <dmarti@zgp.org>
2  *
3  * Based on Conway's Life, but with one rule change to make it a better
4  * screensaver: cells have a max age.
5  *
6  * When a cell exceeds the max age, it counts as 3 for populating the next
7  * generation.  This makes long-lived formations explode instead of just 
8  * sitting there burning a hole in your screen.
9  *
10  * Cloudlife only draws one pixel of each cell per tick, whether the cell is
11  * alive or dead.  So gliders look like little comets.
12
13  * 20 May 2003 -- now includes color cycling and a man page.
14
15  * Based on several examples from the hacks directory of: 
16  
17  * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
18  *
19  * Permission to use, copy, modify, distribute, and sell this software and its
20  * documentation for any purpose is hereby granted without fee, provided that
21  * the above copyright notice appear in all copies and that both that
22  * copyright notice and this permission notice appear in supporting
23  * documentation.  No representations are made about the suitability of this
24  * software for any purpose.  It is provided "as is" without express or 
25  * implied warranty.
26  */
27
28 #include "screenhack.h"
29
30 #ifndef MAX_WIDTH
31 #include <limits.h>
32 #define MAX_WIDTH SHRT_MAX
33 #endif
34
35 #ifdef TIME_ME
36 #include <time.h>
37 #endif
38
39 /* this program goes faster if some functions are inline.  The following is
40  * borrowed from ifs.c */
41 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
42 #undef inline
43 #define inline                  /* */
44 #endif
45
46 struct field {
47     unsigned int height;
48     unsigned int width;
49     unsigned int max_age;
50     unsigned int cell_size;
51     unsigned char *cells;
52     unsigned char *new_cells;
53 };
54
55 struct state {
56   Display *dpy;
57   Window window;
58
59 #ifdef TIME_ME
60   time_t start_time;
61 #endif
62
63   unsigned int cycles;
64   unsigned int colorindex;  /* which color in the colormap are we on */
65   unsigned int colortimer;  /* when this reaches 0, cycle to next color */
66
67   int cycle_delay;
68   int cycle_colors;
69   int ncolors;
70   int density;
71
72   GC fgc, bgc;
73   XGCValues gcv;
74   XWindowAttributes xgwa;
75   XColor *colors;
76
77   struct field *field;
78
79   XPoint fg_points[MAX_WIDTH];
80   XPoint bg_points[MAX_WIDTH];
81 };
82
83
84 static void 
85 *xrealloc(void *p, size_t size)
86 {
87     void *ret;
88     if ((ret = realloc(p, size)) == NULL) {
89         fprintf(stderr, "%s: out of memory\n", progname);
90         exit(1);
91     }
92     return ret;
93 }
94
95 static struct field *
96 init_field(struct state *st)
97 {
98     struct field *f = xrealloc(NULL, sizeof(struct field));
99     f->height = 0;
100     f->width = 0;
101     f->cell_size = get_integer_resource(st->dpy, "cellSize", "Integer");
102     f->max_age = get_integer_resource(st->dpy, "maxAge", "Integer");
103
104     if (f->max_age > 255) {
105       fprintf (stderr, "%s: max-age must be < 256 (not %d)\n", progname,
106                f->max_age);
107       exit (1);
108     }
109
110     f->cells = NULL;
111     f->new_cells = NULL;
112     return f;
113 }
114
115 static void 
116 resize_field(struct field * f, unsigned int w, unsigned int h)
117 {
118     int s = w * h * sizeof(unsigned char);
119     f->width = w;
120     f->height = h;
121
122     f->cells = xrealloc(f->cells, s);
123     f->new_cells = xrealloc(f->new_cells, s);
124     memset(f->cells, 0, s);
125     memset(f->new_cells, 0, s);
126 }
127
128 static inline unsigned char 
129 *cell_at(struct field * f, unsigned int x, unsigned int y)
130 {
131     return (f->cells + x * sizeof(unsigned char) + 
132                        y * f->width * sizeof(unsigned char));
133 }
134
135 static inline unsigned char 
136 *new_cell_at(struct field * f, unsigned int x, unsigned int y)
137 {
138     return (f->new_cells + x * sizeof(unsigned char) + 
139                            y * f->width * sizeof(unsigned char));
140 }
141
142 static void
143 draw_field(struct state *st, struct field * f)
144 {
145     unsigned int x, y;
146     unsigned int rx, ry = 0;    /* random amount to offset the dot */
147     unsigned int size = 1 << f->cell_size;
148     unsigned int mask = size - 1;
149     unsigned int fg_count, bg_count;
150
151     /* columns 0 and width-1 are off screen and not drawn. */
152     for (y = 1; y < f->height - 1; y++) {
153         fg_count = 0;
154         bg_count = 0;
155
156         /* rows 0 and height-1 are off screen and not drawn. */
157         for (x = 1; x < f->width - 1; x++) {
158             rx = random();
159             ry = rx >> f->cell_size;
160             rx &= mask;
161             ry &= mask;
162
163             if (*cell_at(f, x, y)) {
164                 st->fg_points[fg_count].x = (short) x *size - rx - 1;
165                 st->fg_points[fg_count].y = (short) y *size - ry - 1;
166                 fg_count++;
167             } else {
168                 st->bg_points[bg_count].x = (short) x *size - rx - 1;
169                 st->bg_points[bg_count].y = (short) y *size - ry - 1;
170                 bg_count++;
171             }
172         }
173         XDrawPoints(st->dpy, st->window, st->fgc, st->fg_points, fg_count,
174                     CoordModeOrigin);
175         XDrawPoints(st->dpy, st->window, st->bgc, st->bg_points, bg_count,
176                     CoordModeOrigin);
177     }
178 }
179
180 static inline unsigned int 
181 cell_value(unsigned char c, unsigned int age)
182 {
183     if (!c) {
184         return 0;
185     } else if (c > age) {
186         return (3);
187     } else {
188         return (1);
189     }
190 }
191
192 static inline unsigned int 
193 is_alive(struct field * f, unsigned int x, unsigned int y)
194 {
195     unsigned int count;
196     unsigned int i, j;
197     unsigned char *p;
198
199     count = 0;
200
201     for (i = x - 1; i <= x + 1; i++) {
202         for (j = y - 1; j <= y + 1; j++) {
203             if (y != j || x != i) {
204                 count += cell_value(*cell_at(f, i, j), f->max_age);
205             }
206         }
207     }
208
209     p = cell_at(f, x, y);
210     if (*p) {
211         if (count == 2 || count == 3) {
212             return ((*p) + 1);
213         } else {
214             return (0);
215         }
216     } else {
217         if (count == 3) {
218             return (1);
219         } else {
220             return (0);
221         }
222     }
223 }
224
225 static unsigned int 
226 do_tick(struct field * f)
227 {
228     unsigned int x, y;
229     unsigned int count = 0;
230     for (x = 1; x < f->width - 1; x++) {
231         for (y = 1; y < f->height - 1; y++) {
232             count += *new_cell_at(f, x, y) = is_alive(f, x, y);
233         }
234     }
235     memcpy(f->cells, f->new_cells, f->width * f->height *
236            sizeof(unsigned char));
237     return count;
238 }
239
240
241 static unsigned int 
242 random_cell(unsigned int p)
243 {
244     int r = random() & 0xff;
245
246     if (r < p) {
247         return (1);
248     } else {
249         return (0);
250     }
251 }
252
253 static void 
254 populate_field(struct field * f, unsigned int p)
255 {
256     unsigned int x, y;
257
258     for (x = 0; x < f->width; x++) {
259         for (y = 0; y < f->height; y++) {
260             *cell_at(f, x, y) = random_cell(p);
261         }
262     }
263 }
264
265 static void 
266 populate_edges(struct field * f, unsigned int p)
267 {
268     unsigned int i;
269
270     for (i = f->width; i--;) {
271         *cell_at(f, i, 0) = random_cell(p);
272         *cell_at(f, i, f->height - 1) = random_cell(p);
273     }
274
275     for (i = f->height; i--;) {
276         *cell_at(f, f->width - 1, i) = random_cell(p);
277         *cell_at(f, 0, i) = random_cell(p);
278     }
279 }
280
281 static void *
282 cloudlife_init (Display *dpy, Window window)
283 {
284   struct state *st = (struct state *) calloc (1, sizeof(*st));
285     Bool tmp = True;
286
287     st->dpy = dpy;
288     st->window = window;
289     st->field = init_field(st);
290
291 #ifdef TIME_ME
292     st->start_time = time(NULL);
293 #endif
294
295     st->cycle_delay = get_integer_resource(st->dpy, "cycleDelay", "Integer");
296     st->cycle_colors = get_integer_resource(st->dpy, "cycleColors", "Integer");
297     st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer");
298     st->density = (get_integer_resource(st->dpy, "initialDensity", "Integer") 
299                   % 100 * 256)/100;
300
301     XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
302
303     if (st->cycle_colors) {
304         st->colors = (XColor *) xrealloc(st->colors, sizeof(XColor) * (st->ncolors+1));
305         make_smooth_colormap (st->dpy, st->xgwa.visual, st->xgwa.colormap, st->colors, &st->ncolors,
306                               True, &tmp, True);
307     }
308
309     st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
310                                         "foreground", "Foreground");
311     st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
312
313     st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
314                                         "background", "Background");
315     st->bgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
316
317     return st;
318 }
319
320 static unsigned long
321 cloudlife_draw (Display *dpy, Window window, void *closure)
322 {
323   struct state *st = (struct state *) closure;
324
325   if (st->cycle_colors) {
326     if (st->colortimer == 0) {
327       st->colortimer = st->cycle_colors;
328       if( st->colorindex == 0 ) 
329         st->colorindex = st->ncolors;
330       st->colorindex--;
331       XSetForeground(st->dpy, st->fgc, st->colors[st->colorindex].pixel);
332     }
333     st->colortimer--;
334   } 
335
336   XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
337   if (st->field->height != st->xgwa.height / (1 << st->field->cell_size) + 2 ||
338       st->field->width != st->xgwa.width / (1 << st->field->cell_size) + 2) {
339
340     resize_field(st->field, st->xgwa.width / (1 << st->field->cell_size) + 2,
341                  st->xgwa.height / (1 << st->field->cell_size) + 2);
342     populate_field(st->field, st->density);
343   }
344
345   draw_field(st, st->field);
346
347   if (do_tick(st->field) < (st->field->height + st->field->width) / 4) {
348     populate_field(st->field, st->density);
349   }
350
351   if (st->cycles % (st->field->max_age /2) == 0) {
352     populate_edges(st->field, st->density);
353     do_tick(st->field);
354     populate_edges(st->field, 0);
355   }
356
357   st->cycles++;
358
359 #ifdef TIME_ME
360   if (st->cycles % st->field->max_age == 0) {
361     printf("%g s.\n",
362            ((time(NULL) - st->start_time) * 1000.0) / st->cycles);
363   }
364 #endif
365
366   return (st->cycle_delay);
367 }
368
369
370 static void
371 cloudlife_reshape (Display *dpy, Window window, void *closure, 
372                  unsigned int w, unsigned int h)
373 {
374 }
375
376 static Bool
377 cloudlife_event (Display *dpy, Window window, void *closure, XEvent *event)
378 {
379   return False;
380 }
381
382 static void
383 cloudlife_free (Display *dpy, Window window, void *closure)
384 {
385   struct state *st = (struct state *) closure;
386   free (st);
387 }
388
389
390 static const char *cloudlife_defaults[] = {
391     ".background:       black",
392     ".foreground:       blue",
393     "*fpsSolid:         true",
394     "*cycleDelay:       25000",
395     "*cycleColors:      2",
396     "*ncolors:          64",
397     "*maxAge:           64",
398     "*initialDensity:   30",
399     "*cellSize:         3",
400     0
401 };
402
403 static XrmOptionDescRec cloudlife_options[] = {
404     {"-background", ".background", XrmoptionSepArg, 0},
405     {"-foreground", ".foreground", XrmoptionSepArg, 0},
406     {"-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0},
407     {"-cycle-colors", ".cycleColors", XrmoptionSepArg, 0},
408     {"-ncolors", ".ncolors", XrmoptionSepArg, 0},
409     {"-cell-size", ".cellSize", XrmoptionSepArg, 0},
410     {"-initial-density", ".initialDensity", XrmoptionSepArg, 0},
411     {"-max-age", ".maxAge", XrmoptionSepArg, 0},
412     {0, 0, 0, 0}
413 };
414
415
416 XSCREENSAVER_MODULE ("CloudLife", cloudlife)