summaryrefslogtreecommitdiffstats
path: root/hacks/cloudlife.c
diff options
context:
space:
mode:
authorSimon Rettberg2018-10-16 10:08:48 +0200
committerSimon Rettberg2018-10-16 10:08:48 +0200
commitd3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch)
treecbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /hacks/cloudlife.c
downloadxscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip
Original 5.40
Diffstat (limited to 'hacks/cloudlife.c')
-rw-r--r--hacks/cloudlife.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/hacks/cloudlife.c b/hacks/cloudlife.c
new file mode 100644
index 0000000..4b65f9e
--- /dev/null
+++ b/hacks/cloudlife.c
@@ -0,0 +1,428 @@
+/* cloudlife by Don Marti <dmarti@zgp.org>
+ *
+ * Based on Conway's Life, but with one rule change to make it a better
+ * screensaver: cells have a max age.
+ *
+ * When a cell exceeds the max age, it counts as 3 for populating the next
+ * generation. This makes long-lived formations explode instead of just
+ * sitting there burning a hole in your screen.
+ *
+ * Cloudlife only draws one pixel of each cell per tick, whether the cell is
+ * alive or dead. So gliders look like little comets.
+
+ * 20 May 2003 -- now includes color cycling and a man page.
+
+ * Based on several examples from the hacks directory of:
+
+ * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "screenhack.h"
+
+#ifndef MAX_WIDTH
+#include <limits.h>
+#define MAX_WIDTH SHRT_MAX
+#endif
+
+#ifdef TIME_ME
+#include <time.h>
+#endif
+
+/* this program goes faster if some functions are inline. The following is
+ * borrowed from ifs.c */
+#if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
+#undef inline
+#define inline /* */
+#endif
+
+struct field {
+ unsigned int height;
+ unsigned int width;
+ unsigned int max_age;
+ unsigned int cell_size;
+ unsigned char *cells;
+ unsigned char *new_cells;
+};
+
+struct state {
+ Display *dpy;
+ Window window;
+
+#ifdef TIME_ME
+ time_t start_time;
+#endif
+
+ unsigned int cycles;
+ unsigned int colorindex; /* which color in the colormap are we on */
+ unsigned int colortimer; /* when this reaches 0, cycle to next color */
+
+ int cycle_delay;
+ int cycle_colors;
+ int ncolors;
+ int density;
+
+ GC fgc, bgc;
+ XGCValues gcv;
+ XWindowAttributes xgwa;
+ XColor *colors;
+
+ struct field *field;
+
+ XPoint fg_points[MAX_WIDTH];
+ XPoint bg_points[MAX_WIDTH];
+};
+
+
+static void
+*xrealloc(void *p, size_t size)
+{
+ void *ret;
+ if ((ret = realloc(p, size)) == NULL) {
+ fprintf(stderr, "%s: out of memory\n", progname);
+ exit(1);
+ }
+ return ret;
+}
+
+static struct field *
+init_field(struct state *st)
+{
+ struct field *f = xrealloc(NULL, sizeof(struct field));
+ f->height = 0;
+ f->width = 0;
+ f->cell_size = get_integer_resource(st->dpy, "cellSize", "Integer");
+ f->max_age = get_integer_resource(st->dpy, "maxAge", "Integer");
+
+ if (f->max_age > 255) {
+ fprintf (stderr, "%s: max-age must be < 256 (not %d)\n", progname,
+ f->max_age);
+ exit (1);
+ }
+
+ f->cells = NULL;
+ f->new_cells = NULL;
+ return f;
+}
+
+static void
+resize_field(struct field * f, unsigned int w, unsigned int h)
+{
+ int s = w * h * sizeof(unsigned char);
+ f->width = w;
+ f->height = h;
+
+ f->cells = xrealloc(f->cells, s);
+ f->new_cells = xrealloc(f->new_cells, s);
+ memset(f->cells, 0, s);
+ memset(f->new_cells, 0, s);
+}
+
+static inline unsigned char
+*cell_at(struct field * f, unsigned int x, unsigned int y)
+{
+ return (f->cells + x * sizeof(unsigned char) +
+ y * f->width * sizeof(unsigned char));
+}
+
+static inline unsigned char
+*new_cell_at(struct field * f, unsigned int x, unsigned int y)
+{
+ return (f->new_cells + x * sizeof(unsigned char) +
+ y * f->width * sizeof(unsigned char));
+}
+
+static void
+draw_field(struct state *st, struct field * f)
+{
+ unsigned int x, y;
+ unsigned int rx, ry = 0; /* random amount to offset the dot */
+ unsigned int size = 1 << f->cell_size;
+ unsigned int mask = size - 1;
+ unsigned int fg_count, bg_count;
+
+ /* columns 0 and width-1 are off screen and not drawn. */
+ for (y = 1; y < f->height - 1; y++) {
+ fg_count = 0;
+ bg_count = 0;
+
+ /* rows 0 and height-1 are off screen and not drawn. */
+ for (x = 1; x < f->width - 1; x++) {
+ rx = random();
+ ry = rx >> f->cell_size;
+ rx &= mask;
+ ry &= mask;
+
+ if (*cell_at(f, x, y)) {
+ st->fg_points[fg_count].x = (short) x *size - rx - 1;
+ st->fg_points[fg_count].y = (short) y *size - ry - 1;
+ fg_count++;
+ } else {
+ st->bg_points[bg_count].x = (short) x *size - rx - 1;
+ st->bg_points[bg_count].y = (short) y *size - ry - 1;
+ bg_count++;
+ }
+ }
+ XDrawPoints(st->dpy, st->window, st->fgc, st->fg_points, fg_count,
+ CoordModeOrigin);
+ XDrawPoints(st->dpy, st->window, st->bgc, st->bg_points, bg_count,
+ CoordModeOrigin);
+ }
+}
+
+static inline unsigned int
+cell_value(unsigned char c, unsigned int age)
+{
+ if (!c) {
+ return 0;
+ } else if (c > age) {
+ return (3);
+ } else {
+ return (1);
+ }
+}
+
+static inline unsigned int
+is_alive(struct field * f, unsigned int x, unsigned int y)
+{
+ unsigned int count;
+ unsigned int i, j;
+ unsigned char *p;
+
+ count = 0;
+
+ for (i = x - 1; i <= x + 1; i++) {
+ for (j = y - 1; j <= y + 1; j++) {
+ if (y != j || x != i) {
+ count += cell_value(*cell_at(f, i, j), f->max_age);
+ }
+ }
+ }
+
+ p = cell_at(f, x, y);
+ if (*p) {
+ if (count == 2 || count == 3) {
+ return ((*p) + 1);
+ } else {
+ return (0);
+ }
+ } else {
+ if (count == 3) {
+ return (1);
+ } else {
+ return (0);
+ }
+ }
+}
+
+static unsigned int
+do_tick(struct field * f)
+{
+ unsigned int x, y;
+ unsigned int count = 0;
+ for (x = 1; x < f->width - 1; x++) {
+ for (y = 1; y < f->height - 1; y++) {
+ count += *new_cell_at(f, x, y) = is_alive(f, x, y);
+ }
+ }
+ memcpy(f->cells, f->new_cells, f->width * f->height *
+ sizeof(unsigned char));
+ return count;
+}
+
+
+static unsigned int
+random_cell(unsigned int p)
+{
+ int r = random() & 0xff;
+
+ if (r < p) {
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+static void
+populate_field(struct field * f, unsigned int p)
+{
+ unsigned int x, y;
+
+ for (x = 0; x < f->width; x++) {
+ for (y = 0; y < f->height; y++) {
+ *cell_at(f, x, y) = random_cell(p);
+ }
+ }
+}
+
+static void
+populate_edges(struct field * f, unsigned int p)
+{
+ unsigned int i;
+
+ for (i = f->width; i--;) {
+ *cell_at(f, i, 0) = random_cell(p);
+ *cell_at(f, i, f->height - 1) = random_cell(p);
+ }
+
+ for (i = f->height; i--;) {
+ *cell_at(f, f->width - 1, i) = random_cell(p);
+ *cell_at(f, 0, i) = random_cell(p);
+ }
+}
+
+static void *
+cloudlife_init (Display *dpy, Window window)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+ Bool tmp = True;
+
+ st->dpy = dpy;
+ st->window = window;
+ st->field = init_field(st);
+
+#ifdef TIME_ME
+ st->start_time = time(NULL);
+#endif
+
+ st->cycle_delay = get_integer_resource(st->dpy, "cycleDelay", "Integer");
+ st->cycle_colors = get_integer_resource(st->dpy, "cycleColors", "Integer");
+ st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer");
+ st->density = (get_integer_resource(st->dpy, "initialDensity", "Integer")
+ % 100 * 256)/100;
+
+ XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
+
+ if (st->cycle_colors) {
+ st->colors = (XColor *) xrealloc(st->colors, sizeof(XColor) * (st->ncolors+1));
+ make_smooth_colormap (st->xgwa.screen, st->xgwa.visual,
+ st->xgwa.colormap, st->colors, &st->ncolors,
+ True, &tmp, True);
+ }
+
+ st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
+ "foreground", "Foreground");
+ st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
+
+ st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
+ "background", "Background");
+ st->bgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
+
+ return st;
+}
+
+static unsigned long
+cloudlife_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+
+ if (st->cycle_colors) {
+ if (st->colortimer == 0) {
+ st->colortimer = st->cycle_colors;
+ if( st->colorindex == 0 )
+ st->colorindex = st->ncolors;
+ st->colorindex--;
+ XSetForeground(st->dpy, st->fgc, st->colors[st->colorindex].pixel);
+ }
+ st->colortimer--;
+ }
+
+ XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
+ if (st->field->height != st->xgwa.height / (1 << st->field->cell_size) + 2 ||
+ st->field->width != st->xgwa.width / (1 << st->field->cell_size) + 2) {
+
+ resize_field(st->field, st->xgwa.width / (1 << st->field->cell_size) + 2,
+ st->xgwa.height / (1 << st->field->cell_size) + 2);
+ populate_field(st->field, st->density);
+ }
+
+ draw_field(st, st->field);
+
+ if (do_tick(st->field) < (st->field->height + st->field->width) / 4) {
+ populate_field(st->field, st->density);
+ }
+
+ if (st->cycles % (st->field->max_age /2) == 0) {
+ populate_edges(st->field, st->density);
+ do_tick(st->field);
+ populate_edges(st->field, 0);
+ }
+
+ st->cycles++;
+
+#ifdef TIME_ME
+ if (st->cycles % st->field->max_age == 0) {
+ printf("%g s.\n",
+ ((time(NULL) - st->start_time) * 1000.0) / st->cycles);
+ }
+#endif
+
+ return (st->cycle_delay);
+}
+
+
+static void
+cloudlife_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+}
+
+static Bool
+cloudlife_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ struct state *st = (struct state *) closure;
+ if (screenhack_event_helper (dpy, window, event))
+ {
+ XClearWindow (dpy, window);
+ st->cycles = 0;
+ st->field = init_field(st);
+ return True;
+ }
+ return False;
+}
+
+static void
+cloudlife_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ free (st);
+}
+
+
+static const char *cloudlife_defaults[] = {
+ ".background: black",
+ ".foreground: blue",
+ "*fpsSolid: true",
+ "*cycleDelay: 25000",
+ "*cycleColors: 2",
+ "*ncolors: 64",
+ "*maxAge: 64",
+ "*initialDensity: 30",
+ "*cellSize: 3",
+#ifdef HAVE_MOBILE
+ "*ignoreRotation: True",
+#endif
+ 0
+};
+
+static XrmOptionDescRec cloudlife_options[] = {
+ {"-background", ".background", XrmoptionSepArg, 0},
+ {"-foreground", ".foreground", XrmoptionSepArg, 0},
+ {"-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0},
+ {"-cycle-colors", ".cycleColors", XrmoptionSepArg, 0},
+ {"-ncolors", ".ncolors", XrmoptionSepArg, 0},
+ {"-cell-size", ".cellSize", XrmoptionSepArg, 0},
+ {"-initial-density", ".initialDensity", XrmoptionSepArg, 0},
+ {"-max-age", ".maxAge", XrmoptionSepArg, 0},
+ {0, 0, 0, 0}
+};
+
+
+XSCREENSAVER_MODULE ("CloudLife", cloudlife)