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"
32 #define MAX_WIDTH SHRT_MAX
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)
50 unsigned int cell_size;
52 unsigned char *new_cells;
64 unsigned int colorindex; /* which color in the colormap are we on */
65 unsigned int colortimer; /* when this reaches 0, cycle to next color */
74 XWindowAttributes xgwa;
79 XPoint fg_points[MAX_WIDTH];
80 XPoint bg_points[MAX_WIDTH];
85 *xrealloc(void *p, size_t size)
88 if ((ret = realloc(p, size)) == NULL) {
89 fprintf(stderr, "%s: out of memory\n", progname);
96 init_field(struct state *st)
98 struct field *f = xrealloc(NULL, sizeof(struct field));
101 f->cell_size = get_integer_resource(st->dpy, "cellSize", "Integer");
102 f->max_age = get_integer_resource(st->dpy, "maxAge", "Integer");
104 if (f->max_age > 255) {
105 fprintf (stderr, "%s: max-age must be < 256 (not %d)\n", progname,
116 resize_field(struct field * f, unsigned int w, unsigned int h)
118 int s = w * h * sizeof(unsigned char);
122 f->cells = xrealloc(f->cells, s);
123 f->new_cells = xrealloc(f->new_cells, s);
124 memset(f->cells, 0, s);
125 memset(f->new_cells, 0, s);
128 static inline unsigned char
129 *cell_at(struct field * f, unsigned int x, unsigned int y)
131 return (f->cells + x * sizeof(unsigned char) +
132 y * f->width * sizeof(unsigned char));
135 static inline unsigned char
136 *new_cell_at(struct field * f, unsigned int x, unsigned int y)
138 return (f->new_cells + x * sizeof(unsigned char) +
139 y * f->width * sizeof(unsigned char));
143 draw_field(struct state *st, struct field * f)
146 unsigned int rx, ry = 0; /* random amount to offset the dot */
147 unsigned int size = 1 << f->cell_size;
148 unsigned int mask = size - 1;
149 unsigned int fg_count, bg_count;
151 /* columns 0 and width-1 are off screen and not drawn. */
152 for (y = 1; y < f->height - 1; y++) {
156 /* rows 0 and height-1 are off screen and not drawn. */
157 for (x = 1; x < f->width - 1; x++) {
159 ry = rx >> f->cell_size;
163 if (*cell_at(f, x, y)) {
164 st->fg_points[fg_count].x = (short) x *size - rx - 1;
165 st->fg_points[fg_count].y = (short) y *size - ry - 1;
168 st->bg_points[bg_count].x = (short) x *size - rx - 1;
169 st->bg_points[bg_count].y = (short) y *size - ry - 1;
173 XDrawPoints(st->dpy, st->window, st->fgc, st->fg_points, fg_count,
175 XDrawPoints(st->dpy, st->window, st->bgc, st->bg_points, bg_count,
180 static inline unsigned int
181 cell_value(unsigned char c, unsigned int age)
185 } else if (c > age) {
192 static inline unsigned int
193 is_alive(struct field * f, unsigned int x, unsigned int y)
201 for (i = x - 1; i <= x + 1; i++) {
202 for (j = y - 1; j <= y + 1; j++) {
203 if (y != j || x != i) {
204 count += cell_value(*cell_at(f, i, j), f->max_age);
209 p = cell_at(f, x, y);
211 if (count == 2 || count == 3) {
226 do_tick(struct field * f)
229 unsigned int count = 0;
230 for (x = 1; x < f->width - 1; x++) {
231 for (y = 1; y < f->height - 1; y++) {
232 count += *new_cell_at(f, x, y) = is_alive(f, x, y);
235 memcpy(f->cells, f->new_cells, f->width * f->height *
236 sizeof(unsigned char));
242 random_cell(unsigned int p)
244 int r = random() & 0xff;
254 populate_field(struct field * f, unsigned int p)
258 for (x = 0; x < f->width; x++) {
259 for (y = 0; y < f->height; y++) {
260 *cell_at(f, x, y) = random_cell(p);
266 populate_edges(struct field * f, unsigned int p)
270 for (i = f->width; i--;) {
271 *cell_at(f, i, 0) = random_cell(p);
272 *cell_at(f, i, f->height - 1) = random_cell(p);
275 for (i = f->height; i--;) {
276 *cell_at(f, f->width - 1, i) = random_cell(p);
277 *cell_at(f, 0, i) = random_cell(p);
282 cloudlife_init (Display *dpy, Window window)
284 struct state *st = (struct state *) calloc (1, sizeof(*st));
289 st->field = init_field(st);
292 st->start_time = time(NULL);
295 st->cycle_delay = get_integer_resource(st->dpy, "cycleDelay", "Integer");
296 st->cycle_colors = get_integer_resource(st->dpy, "cycleColors", "Integer");
297 st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer");
298 st->density = (get_integer_resource(st->dpy, "initialDensity", "Integer")
301 XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
303 if (st->cycle_colors) {
304 st->colors = (XColor *) xrealloc(st->colors, sizeof(XColor) * (st->ncolors+1));
305 make_smooth_colormap (st->xgwa.screen, st->xgwa.visual,
306 st->xgwa.colormap, st->colors, &st->ncolors,
310 st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
311 "foreground", "Foreground");
312 st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
314 st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
315 "background", "Background");
316 st->bgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
322 cloudlife_draw (Display *dpy, Window window, void *closure)
324 struct state *st = (struct state *) closure;
326 if (st->cycle_colors) {
327 if (st->colortimer == 0) {
328 st->colortimer = st->cycle_colors;
329 if( st->colorindex == 0 )
330 st->colorindex = st->ncolors;
332 XSetForeground(st->dpy, st->fgc, st->colors[st->colorindex].pixel);
337 XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
338 if (st->field->height != st->xgwa.height / (1 << st->field->cell_size) + 2 ||
339 st->field->width != st->xgwa.width / (1 << st->field->cell_size) + 2) {
341 resize_field(st->field, st->xgwa.width / (1 << st->field->cell_size) + 2,
342 st->xgwa.height / (1 << st->field->cell_size) + 2);
343 populate_field(st->field, st->density);
346 draw_field(st, st->field);
348 if (do_tick(st->field) < (st->field->height + st->field->width) / 4) {
349 populate_field(st->field, st->density);
352 if (st->cycles % (st->field->max_age /2) == 0) {
353 populate_edges(st->field, st->density);
355 populate_edges(st->field, 0);
361 if (st->cycles % st->field->max_age == 0) {
363 ((time(NULL) - st->start_time) * 1000.0) / st->cycles);
367 return (st->cycle_delay);
372 cloudlife_reshape (Display *dpy, Window window, void *closure,
373 unsigned int w, unsigned int h)
378 cloudlife_event (Display *dpy, Window window, void *closure, XEvent *event)
380 struct state *st = (struct state *) closure;
381 if (screenhack_event_helper (dpy, window, event))
383 XClearWindow (dpy, window);
385 st->field = init_field(st);
392 cloudlife_free (Display *dpy, Window window, void *closure)
394 struct state *st = (struct state *) closure;
399 static const char *cloudlife_defaults[] = {
400 ".background: black",
403 "*cycleDelay: 25000",
407 "*initialDensity: 30",
410 "*ignoreRotation: True",
415 static XrmOptionDescRec cloudlife_options[] = {
416 {"-background", ".background", XrmoptionSepArg, 0},
417 {"-foreground", ".foreground", XrmoptionSepArg, 0},
418 {"-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0},
419 {"-cycle-colors", ".cycleColors", XrmoptionSepArg, 0},
420 {"-ncolors", ".ncolors", XrmoptionSepArg, 0},
421 {"-cell-size", ".cellSize", XrmoptionSepArg, 0},
422 {"-initial-density", ".initialDensity", XrmoptionSepArg, 0},
423 {"-max-age", ".maxAge", XrmoptionSepArg, 0},
428 XSCREENSAVER_MODULE ("CloudLife", cloudlife)