http://www.uw-madison.lkams.kernel.org/pub/mirrors/fink/distfiles/xscreensaver-4...
[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 #include <stdio.h>
30 #include <sys/time.h>
31
32 #ifndef MAX_WIDTH
33 #include <limits.h>
34 #define MAX_WIDTH SHRT_MAX
35 #endif
36
37 #ifdef TIME_ME
38 #include <time.h>
39 #endif
40
41 /* this program goes faster if some functions are inline.  The following is
42  * borrowed from ifs.c */
43 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
44 #undef inline
45 #define inline                  /* */
46 #endif
47
48 struct field {
49     unsigned int height;
50     unsigned int width;
51     unsigned int max_age;
52     unsigned int cell_size;
53     unsigned char *cells;
54     unsigned char *new_cells;
55 };
56
57 static void 
58 *xrealloc(void *p, size_t size)
59 {
60     void *ret;
61     if ((ret = realloc(p, size)) == NULL) {
62         fprintf(stderr, "%s: out of memory\n", progname);
63         exit(1);
64     }
65     return ret;
66 }
67
68 struct field 
69 *init_field(void)
70 {
71     struct field *f = xrealloc(NULL, sizeof(struct field));
72     f->height = 0;
73     f->width = 0;
74     f->cell_size = get_integer_resource("cellSize", "Integer");
75     f->max_age = get_integer_resource("maxAge", "Integer");
76     f->cells = NULL;
77     f->new_cells = NULL;
78     return f;
79 }
80
81 void 
82 resize_field(struct field * f, unsigned int w, unsigned int h)
83 {
84     f->width = w;
85     f->height = h;
86
87     f->cells = xrealloc(f->cells,
88                         w * sizeof(unsigned char) * 
89                         h * sizeof(unsigned char));
90     f->new_cells =
91         xrealloc(f->new_cells,
92                  w * sizeof(unsigned char) * h * sizeof(unsigned char));
93 }
94
95 inline unsigned char 
96 *cell_at(struct field * f, unsigned int x, unsigned int y)
97 {
98     return (f->cells + x * sizeof(unsigned char) + 
99                        y * f->width * sizeof(unsigned char));
100 }
101
102 inline unsigned char 
103 *new_cell_at(struct field * f, unsigned int x, unsigned int y)
104 {
105     return (f->new_cells + x * sizeof(unsigned char) + 
106                            y * f->width * sizeof(unsigned char));
107 }
108
109 static void
110 draw_field(Display * dpy,
111            Window window, GC fgc, GC bgc, struct field * f)
112 {
113     unsigned int x, y;
114     unsigned int rx, ry = 0;    /* random amount to offset the dot */
115     unsigned int size = 1 << f->cell_size;
116     unsigned int mask = size - 1;
117     static XPoint fg_points[MAX_WIDTH];
118     static XPoint bg_points[MAX_WIDTH];
119     unsigned int fg_count, bg_count;
120
121     /* columns 0 and width-1 are off screen and not drawn. */
122     for (y = 1; y < f->height - 1; y++) {
123         fg_count = 0;
124         bg_count = 0;
125
126         /* rows 0 and height-1 are off screen and not drawn. */
127         for (x = 1; x < f->width - 1; x++) {
128             rx = random();
129             ry = rx >> f->cell_size;
130             rx &= mask;
131             ry &= mask;
132
133             if (*cell_at(f, x, y)) {
134                 fg_points[fg_count].x = (short) x *size - rx - 1;
135                 fg_points[fg_count].y = (short) y *size - ry - 1;
136                 fg_count++;
137             } else {
138                 bg_points[bg_count].x = (short) x *size - rx - 1;
139                 bg_points[bg_count].y = (short) y *size - ry - 1;
140                 bg_count++;
141             }
142         }
143         XDrawPoints(dpy, window, fgc, fg_points, fg_count,
144                     CoordModeOrigin);
145         XDrawPoints(dpy, window, bgc, bg_points, bg_count,
146                     CoordModeOrigin);
147     }
148 }
149
150 inline unsigned int 
151 cell_value(unsigned char c, unsigned int age)
152 {
153     if (!c) {
154         return 0;
155     } else if (c > age) {
156         return (3);
157     } else {
158         return (1);
159     }
160 }
161
162 inline unsigned int 
163 is_alive(struct field * f, unsigned int x, unsigned int y)
164 {
165     unsigned int count;
166     unsigned int i, j;
167     unsigned char *p;
168
169     count = 0;
170
171     for (i = x - 1; i <= x + 1; i++) {
172         for (j = y - 1; j <= y + 1; j++) {
173             if (y != j || x != i) {
174                 count += cell_value(*cell_at(f, i, j), f->max_age);
175             }
176         }
177     }
178
179     p = cell_at(f, x, y);
180     if (*p) {
181         if (count == 2 || count == 3) {
182             return ((*p) + 1);
183         } else {
184             return (0);
185         }
186     } else {
187         if (count == 3) {
188             return (1);
189         } else {
190             return (0);
191         }
192     }
193 }
194
195 unsigned int 
196 do_tick(struct field * f)
197 {
198     unsigned int x, y;
199     unsigned int count = 0;
200     for (x = 1; x < f->width - 1; x++) {
201         for (y = 1; y < f->height - 1; y++) {
202             count += *new_cell_at(f, x, y) = is_alive(f, x, y);
203         }
204     }
205     memcpy(f->cells, f->new_cells, f->width * sizeof(unsigned char) *
206            f->height * sizeof(unsigned char));
207     return count;
208 }
209
210
211 unsigned int 
212 random_cell(unsigned int p)
213 {
214     int r = random() & 0xff;
215
216     if (r < p) {
217         return (1);
218     } else {
219         return (0);
220     }
221 }
222
223 void 
224 populate_field(struct field * f, unsigned int p)
225 {
226     unsigned int x, y;
227
228     for (x = 0; x < f->width; x++) {
229         for (y = 0; y < f->height; y++) {
230             *cell_at(f, x, y) = random_cell(p);
231         }
232     }
233 }
234
235 void 
236 populate_edges(struct field * f, unsigned int p)
237 {
238     unsigned int i;
239
240     for (i = f->width; i--;) {
241         *cell_at(f, i, 0) = random_cell(p);
242         *cell_at(f, i, f->height - 1) = random_cell(p);
243     }
244
245     for (i = f->height; i--;) {
246         *cell_at(f, f->width - 1, i) = random_cell(p);
247         *cell_at(f, 0, i) = random_cell(p);
248     }
249 }
250
251 \f
252 char *progclass = "Cloudlife";
253
254 char *defaults[] = {
255     ".background:       black",
256     ".foreground:       blue",
257     "*cycleDelay:       25000",
258     "*cycleColors:      2",
259     "*ncolors:          64",
260     "*maxAge:           64",
261     "*initialDensity:   30",
262     "*cellSize:         3",
263     0
264 };
265
266 XrmOptionDescRec options[] = {
267     {"-background", ".background", XrmoptionSepArg, 0},
268     {"-foreground", ".foreground", XrmoptionSepArg, 0},
269     {"-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0},
270     {"-cycle-colors", ".cycleColors", XrmoptionSepArg, 0},
271     {"-ncolors", ".ncolors", XrmoptionSepArg, 0},
272     {"-cell-size", ".cellSize", XrmoptionSepArg, 0},
273     {"-initial-density", ".initialDensity", XrmoptionSepArg, 0},
274     {"-max-age", ".maxAge", XrmoptionSepArg, 0},
275     {0, 0, 0, 0}
276 };
277
278 void screenhack(Display * dpy, Window window)
279 {
280     struct field *f = init_field();
281
282 #ifdef TIME_ME
283     time_t start_time = time(NULL);
284 #endif
285
286     unsigned int cycles = 0;
287     unsigned int colorindex = 0;  /* which color in the colormap are we on */
288     unsigned int colortimer = 0;  /* when this reaches 0, cycle to next color */
289
290     int cycle_delay;
291     int cycle_colors;
292     int ncolors;
293     int density;
294
295     GC fgc, bgc;
296     XGCValues gcv;
297     XWindowAttributes xgwa;
298     XColor *colors = NULL;
299     Bool tmp = True;
300
301     cycle_delay = get_integer_resource("cycleDelay", "Integer");
302     cycle_colors = get_integer_resource("cycleColors", "Integer");
303     ncolors = get_integer_resource("ncolors", "Integer");
304     density = (get_integer_resource("initialDensity", "Integer") 
305                   % 100 * 256)/100;
306
307     XGetWindowAttributes(dpy, window, &xgwa);
308
309     if (cycle_colors) {
310         colors = (XColor *) xrealloc(colors, sizeof(XColor) * (ncolors+1));
311         make_smooth_colormap (dpy, xgwa.visual, xgwa.colormap, colors, &ncolors,
312                               True, &tmp, True);
313     }
314
315     gcv.foreground = get_pixel_resource("foreground", "Foreground",
316                                         dpy, xgwa.colormap);
317     fgc = XCreateGC(dpy, window, GCForeground, &gcv);
318
319     gcv.foreground = get_pixel_resource("background", "Background",
320                                         dpy, xgwa.colormap);
321     bgc = XCreateGC(dpy, window, GCForeground, &gcv);
322
323     while (1) {
324
325         if (cycle_colors) {
326             if (colortimer == 0) {
327                colortimer = cycle_colors;
328                if( colorindex == 0 ) 
329                    colorindex = ncolors;
330                colorindex--;
331                XSetForeground(dpy, fgc, colors[colorindex].pixel);
332             }
333             colortimer--;
334         } 
335
336         XGetWindowAttributes(dpy, window, &xgwa);
337         if (f->height != xgwa.height / (1 << f->cell_size) + 2 ||
338             f->width != xgwa.width / (1 << f->cell_size) + 2) {
339
340             resize_field(f, xgwa.width / (1 << f->cell_size) + 2,
341                          xgwa.height / (1 << f->cell_size) + 2);
342             populate_field(f, density);
343         }
344
345         screenhack_handle_events(dpy);
346
347         draw_field(dpy, window, fgc, bgc, f);
348
349         if (do_tick(f) < (f->height + f->width) / 4) {
350             populate_field(f, density);
351         }
352
353         if (cycles % (f->max_age /2) == 0) {
354             populate_edges(f, density);
355             do_tick(f);
356             populate_edges(f, 0);
357         }
358
359         XSync(dpy, False);
360  
361         cycles++;
362
363         if (cycle_delay)
364             usleep(cycle_delay);
365
366 #ifdef TIME_ME
367         if (cycles % f->max_age == 0) {
368             printf("%g s.\n",
369                    ((time(NULL) - start_time) * 1000.0) / cycles);
370         }
371 #endif
372     }
373 }