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