1 /* cloudlife by Don Marti <dmarti@zgp.org>
3 * Based on Conway's Life, but with one rule change to make it a better
4 * screensaver: cells have a max age.
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.
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.
13 * 20 May 2003 -- now includes color cycling and a man page.
15 * Based on several examples from the hacks directory of:
17 * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
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
28 #include "screenhack.h"
34 #define MAX_WIDTH SHRT_MAX
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)
52 unsigned int cell_size;
54 unsigned char *new_cells;
58 *xrealloc(void *p, size_t size)
61 if ((ret = realloc(p, size)) == NULL) {
62 fprintf(stderr, "%s: out of memory\n", progname);
71 struct field *f = xrealloc(NULL, sizeof(struct field));
74 f->cell_size = get_integer_resource("cellSize", "Integer");
75 f->max_age = get_integer_resource("maxAge", "Integer");
77 if (f->max_age > 255) {
78 fprintf (stderr, "%s: max-age must be < 256 (not %d)\n", progname,
89 resize_field(struct field * f, unsigned int w, unsigned int h)
91 int s = w * h * sizeof(unsigned char);
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);
102 *cell_at(struct field * f, unsigned int x, unsigned int y)
104 return (f->cells + x * sizeof(unsigned char) +
105 y * f->width * sizeof(unsigned char));
109 *new_cell_at(struct field * f, unsigned int x, unsigned int y)
111 return (f->new_cells + x * sizeof(unsigned char) +
112 y * f->width * sizeof(unsigned char));
116 draw_field(Display * dpy,
117 Window window, GC fgc, GC bgc, struct field * f)
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;
127 /* columns 0 and width-1 are off screen and not drawn. */
128 for (y = 1; y < f->height - 1; y++) {
132 /* rows 0 and height-1 are off screen and not drawn. */
133 for (x = 1; x < f->width - 1; x++) {
135 ry = rx >> f->cell_size;
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;
144 bg_points[bg_count].x = (short) x *size - rx - 1;
145 bg_points[bg_count].y = (short) y *size - ry - 1;
149 XDrawPoints(dpy, window, fgc, fg_points, fg_count,
151 XDrawPoints(dpy, window, bgc, bg_points, bg_count,
157 cell_value(unsigned char c, unsigned int age)
161 } else if (c > age) {
169 is_alive(struct field * f, unsigned int x, unsigned int y)
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);
185 p = cell_at(f, x, y);
187 if (count == 2 || count == 3) {
202 do_tick(struct field * f)
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);
211 memcpy(f->cells, f->new_cells, f->width * f->height *
212 sizeof(unsigned char));
218 random_cell(unsigned int p)
220 int r = random() & 0xff;
230 populate_field(struct field * f, unsigned int p)
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);
242 populate_edges(struct field * f, unsigned int p)
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);
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);
258 char *progclass = "Cloudlife";
261 ".background: black",
263 "*cycleDelay: 25000",
267 "*initialDensity: 30",
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},
284 void screenhack(Display * dpy, Window window)
286 struct field *f = init_field();
289 time_t start_time = time(NULL);
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 */
303 XWindowAttributes xgwa;
304 XColor *colors = NULL;
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")
313 XGetWindowAttributes(dpy, window, &xgwa);
316 colors = (XColor *) xrealloc(colors, sizeof(XColor) * (ncolors+1));
317 make_smooth_colormap (dpy, xgwa.visual, xgwa.colormap, colors, &ncolors,
321 gcv.foreground = get_pixel_resource("foreground", "Foreground",
323 fgc = XCreateGC(dpy, window, GCForeground, &gcv);
325 gcv.foreground = get_pixel_resource("background", "Background",
327 bgc = XCreateGC(dpy, window, GCForeground, &gcv);
332 if (colortimer == 0) {
333 colortimer = cycle_colors;
334 if( colorindex == 0 )
335 colorindex = ncolors;
337 XSetForeground(dpy, fgc, colors[colorindex].pixel);
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) {
346 resize_field(f, xgwa.width / (1 << f->cell_size) + 2,
347 xgwa.height / (1 << f->cell_size) + 2);
348 populate_field(f, density);
351 screenhack_handle_events(dpy);
353 draw_field(dpy, window, fgc, bgc, f);
355 if (do_tick(f) < (f->height + f->width) / 4) {
356 populate_field(f, density);
359 if (cycles % (f->max_age /2) == 0) {
360 populate_edges(f, density);
362 populate_edges(f, 0);
373 if (cycles % f->max_age == 0) {
375 ((time(NULL) - start_time) * 1000.0) / cycles);