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 | |
---|
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 | } |
---|