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->dpy, st->xgwa.visual, st->xgwa.colormap, st->colors, &st->ncolors,
309 st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
310 "foreground", "Foreground");
311 st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
313 st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
314 "background", "Background");
315 st->bgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
321 cloudlife_draw (Display *dpy, Window window, void *closure)
323 struct state *st = (struct state *) closure;
325 if (st->cycle_colors) {
326 if (st->colortimer == 0) {
327 st->colortimer = st->cycle_colors;
328 if( st->colorindex == 0 )
329 st->colorindex = st->ncolors;
331 XSetForeground(st->dpy, st->fgc, st->colors[st->colorindex].pixel);
336 XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
337 if (st->field->height != st->xgwa.height / (1 << st->field->cell_size) + 2 ||
338 st->field->width != st->xgwa.width / (1 << st->field->cell_size) + 2) {
340 resize_field(st->field, st->xgwa.width / (1 << st->field->cell_size) + 2,
341 st->xgwa.height / (1 << st->field->cell_size) + 2);
342 populate_field(st->field, st->density);
345 draw_field(st, st->field);
347 if (do_tick(st->field) < (st->field->height + st->field->width) / 4) {
348 populate_field(st->field, st->density);
351 if (st->cycles % (st->field->max_age /2) == 0) {
352 populate_edges(st->field, st->density);
354 populate_edges(st->field, 0);
360 if (st->cycles % st->field->max_age == 0) {
362 ((time(NULL) - st->start_time) * 1000.0) / st->cycles);
366 return (st->cycle_delay);
371 cloudlife_reshape (Display *dpy, Window window, void *closure,
372 unsigned int w, unsigned int h)
377 cloudlife_event (Display *dpy, Window window, void *closure, XEvent *event)
383 cloudlife_free (Display *dpy, Window window, void *closure)
385 struct state *st = (struct state *) closure;
390 static const char *cloudlife_defaults[] = {
391 ".background: black",
394 "*cycleDelay: 25000",
398 "*initialDensity: 30",
403 static XrmOptionDescRec cloudlife_options[] = {
404 {"-background", ".background", XrmoptionSepArg, 0},
405 {"-foreground", ".foreground", XrmoptionSepArg, 0},
406 {"-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0},
407 {"-cycle-colors", ".cycleColors", XrmoptionSepArg, 0},
408 {"-ncolors", ".ncolors", XrmoptionSepArg, 0},
409 {"-cell-size", ".cellSize", XrmoptionSepArg, 0},
410 {"-initial-density", ".initialDensity", XrmoptionSepArg, 0},
411 {"-max-age", ".maxAge", XrmoptionSepArg, 0},
416 XSCREENSAVER_MODULE ("CloudLife", cloudlife)