http://www.archive.org/download/tucows_10294_XScreenSaver/xscreensaver-4.10.tar.gz
[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  * Based on several examples from the hacks directory of: 
14  
15  * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
16  *
17  * Permission to use, copy, modify, distribute, and sell this software and its
18  * documentation for any purpose is hereby granted without fee, provided that
19  * the above copyright notice appear in all copies and that both that
20  * copyright notice and this permission notice appear in supporting
21  * documentation.  No representations are made about the suitability of this
22  * software for any purpose.  It is provided "as is" without express or 
23  * implied warranty.
24  */
25
26 #include "screenhack.h"
27 #include <stdio.h>
28 #include <sys/time.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 typedef struct {
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 } field;
54
55 static void 
56 *xrealloc(void *p, size_t size)
57 {
58     void *ret;
59     if ((ret = realloc(p, size)) == NULL) {
60         fprintf(stderr, "%s: out of memory\n", progname);
61         exit(1);
62     }
63     return ret;
64 }
65
66 field 
67 *init_field(void)
68 {
69     field *f = xrealloc(NULL, sizeof(field));
70     f->height = 0;
71     f->width = 0;
72     f->cell_size = get_integer_resource("cellSize", "Integer");
73     f->max_age = get_integer_resource("maxAge", "Integer");
74     f->cells = NULL;
75     f->new_cells = NULL;
76     return f;
77 }
78
79 void 
80 resize_field(field * f, unsigned int w, unsigned int h)
81 {
82     f->width = w;
83     f->height = h;
84
85     f->cells = xrealloc(f->cells,
86                         w * sizeof(unsigned char) * 
87                         h * sizeof(unsigned char));
88     f->new_cells =
89         xrealloc(f->new_cells,
90                  w * sizeof(unsigned char) * h * sizeof(unsigned char));
91 }
92
93 inline unsigned char 
94 *cell_at(field * f, unsigned int x, unsigned int y)
95 {
96     return (f->cells + x * sizeof(unsigned char) + 
97                        y * f->width * sizeof(unsigned char));
98 }
99
100 inline unsigned char 
101 *new_cell_at(field * f, unsigned int x, unsigned int y)
102 {
103     return (f->new_cells + x * sizeof(unsigned char) + 
104                            y * f->width * sizeof(unsigned char));
105 }
106
107 static void
108 draw_field(Display * dpy,
109            Window window, Colormap cmap, GC fgc, GC bgc, field * f)
110 {
111     unsigned int x, y;
112     unsigned int rx, ry = 0;    /* random amount to offset the dot */
113     unsigned int size = 1 << f->cell_size;
114     unsigned int mask = size - 1;
115     static XPoint fg_points[MAX_WIDTH];
116     static XPoint bg_points[MAX_WIDTH];
117     unsigned int fg_count, bg_count;
118
119     /* columns 0 and width-1 are off screen and not drawn. */
120     for (y = 1; y < f->height - 1; y++) {
121         fg_count = 0;
122         bg_count = 0;
123
124         /* rows 0 and height-1 are off screen and not drawn. */
125         for (x = 1; x < f->width - 1; x++) {
126             rx = random();
127             ry = rx >> f->cell_size;
128             rx &= mask;
129             ry &= mask;
130
131             if (*cell_at(f, x, y)) {
132                 fg_points[fg_count].x = (short) x *size - rx - 1;
133                 fg_points[fg_count].y = (short) y *size - ry - 1;
134                 fg_count++;
135             } else {
136                 bg_points[bg_count].x = (short) x *size - rx - 1;
137                 bg_points[bg_count].y = (short) y *size - ry - 1;
138                 bg_count++;
139             }
140         }
141         XDrawPoints(dpy, window, fgc, fg_points, fg_count,
142                     CoordModeOrigin);
143         XDrawPoints(dpy, window, bgc, bg_points, bg_count,
144                     CoordModeOrigin);
145     }
146 }
147
148 inline unsigned int 
149 cell_value(unsigned char c, unsigned int age)
150 {
151     if (!c) {
152         return 0;
153     } else if (c > age) {
154         return (3);
155     } else {
156         return (1);
157     }
158 }
159
160 inline unsigned int 
161 is_alive(field * f, unsigned int x, unsigned int y)
162 {
163     unsigned int count;
164     unsigned int i, j;
165     unsigned char *p;
166
167     count = 0;
168
169     for (i = x - 1; i <= x + 1; i++) {
170         for (j = y - 1; j <= y + 1; j++) {
171             if (y != j || x != i) {
172                 count += cell_value(*cell_at(f, i, j), f->max_age);
173             }
174         }
175     }
176
177     p = cell_at(f, x, y);
178     if (*p) {
179         if (count == 2 || count == 3) {
180             return ((*p) + 1);
181         } else {
182             return (0);
183         }
184     } else {
185         if (count == 3) {
186             return (1);
187         } else {
188             return (0);
189         }
190     }
191 }
192
193 unsigned int 
194 do_tick(field * f)
195 {
196     unsigned int x, y;
197     unsigned int count = 0;
198     for (x = 1; x < f->width - 1; x++) {
199         for (y = 1; y < f->height - 1; y++) {
200             count += *new_cell_at(f, x, y) = is_alive(f, x, y);
201         }
202     }
203     memcpy(f->cells, f->new_cells, f->width * sizeof(unsigned char) *
204            f->height * sizeof(unsigned char));
205     return count;
206 }
207
208
209 unsigned int 
210 random_cell(unsigned int p)
211 {
212     int r = random() & 0xff;
213
214     if (r < p) {
215         return (1);
216     } else {
217         return (0);
218     }
219 }
220
221 void 
222 populate_field(field * f, unsigned int p)
223 {
224     unsigned int x, y;
225
226     for (x = 0; x < f->width; x++) {
227         for (y = 0; y < f->height; y++) {
228             *cell_at(f, x, y) = random_cell(p);
229         }
230     }
231 }
232
233 void 
234 populate_edges(field * f, unsigned int p)
235 {
236     unsigned int i;
237
238     for (i = f->width; i--;) {
239         *cell_at(f, i, 0) = random_cell(p);
240         *cell_at(f, i, f->height - 1) = random_cell(p);
241     }
242
243     for (i = f->height; i--;) {
244         *cell_at(f, f->width - 1, i) = random_cell(p);
245         *cell_at(f, 0, i) = random_cell(p);
246     }
247 }
248
249 \f
250 char *progclass = "Cloudlife";
251
252 char *defaults[] = {
253     ".background:       black",
254     ".foreground:       blue",
255     "*cycleDelay:       25000",
256     "*maxAge:           64",
257     "*initialDensity:   160",
258     "*cellSize:         3",
259     0
260 };
261
262 XrmOptionDescRec options[] = {
263     {"-background", ".background", XrmoptionSepArg, 0},
264     {"-foreground", ".foreground", XrmoptionSepArg, 0},
265     {"-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0},
266     {"-cell-size", ".cellSize", XrmoptionSepArg, 0},
267     {"-initial-density", ".initialDensity", XrmoptionSepArg, 0},
268     {"-max-age", ".maxAge", XrmoptionSepArg, 0},
269     {0, 0, 0, 0}
270 };
271
272 void screenhack(Display * dpy, Window window)
273 {
274     field *f = init_field();
275
276 #ifdef TIME_ME
277     time_t start_time = time(NULL);
278 #endif
279
280     unsigned int cycles = 0;
281
282     GC fgc, bgc;
283     XGCValues gcv;
284     XWindowAttributes xgwa;
285
286     unsigned int cycle_delay = (unsigned int)
287         get_integer_resource("cycleDelay", "Integer");
288     unsigned int density = (unsigned int)
289         get_integer_resource("initialDensity", "Integer") & 0xff;
290     XGetWindowAttributes(dpy, window, &xgwa);
291
292     gcv.foreground = get_pixel_resource("foreground", "Foreground",
293                                         dpy, xgwa.colormap);
294     fgc = XCreateGC(dpy, window, GCForeground, &gcv);
295
296     gcv.foreground = get_pixel_resource("background", "Background",
297                                         dpy, xgwa.colormap);
298     bgc = XCreateGC(dpy, window, GCForeground, &gcv);
299
300     while (1) {
301         XGetWindowAttributes(dpy, window, &xgwa);
302         if (f->height != xgwa.height / (1 << f->cell_size) + 2 ||
303             f->width != xgwa.width / (1 << f->cell_size) + 2) {
304
305             resize_field(f, xgwa.width / (1 << f->cell_size) + 2,
306                          xgwa.height / (1 << f->cell_size) + 2);
307             populate_field(f, density);
308         }
309
310         screenhack_handle_events(dpy);
311
312         draw_field(dpy, window, xgwa.colormap, fgc, bgc, f);
313
314         if (do_tick(f) < (f->height + f->width) / 4) {
315             populate_field(f, density);
316         }
317
318         if (cycles % (f->max_age /2) == 0) {
319             populate_edges(f, density);
320             do_tick(f);
321             populate_edges(f, 0);
322         }
323
324         XSync(dpy, False);
325
326         cycles++;
327
328         if (cycle_delay)
329             usleep(cycle_delay);
330
331 #ifdef TIME_ME
332         if (cycles % f->max_age == 0) {
333             printf("%g s.\n",
334                    ((time(NULL) - start_time) * 1000.0) / cycles);
335         }
336 #endif
337     }
338 }