/* critical -- Self-organizing-criticality display hack for XScreenSaver
- * Copyright (C) 1998, 1999 Martin Pool <mbp@humbug.org.au>
+ * Copyright (C) 1998, 1999, 2000 Martin Pool <mbp@humbug.org.au>
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
*
* Revision history:
* 13 Nov 1998: Initial version, Martin Pool <mbp@humbug.org.au>
- */
+ * 08 Feb 2000: Change to keeping and erasing a trail, <mbp>
+ *
+ * It would be nice to draw curvy shapes rather than just straight
+ * lines, but X11 doesn't have spline primitives (?) so we'd have to
+ * do all the work ourselves */
#include "screenhack.h"
#include "erase.h"
#include <stdio.h>
#include <stdlib.h>
-
-char *progclass = "Critical";
+#include <assert.h>
typedef struct {
unsigned short *cells;
} CriticalModel;
+typedef struct {
+ int trail; /* length of trail */
+ int cell_size;
+} CriticalSettings;
-CriticalModel * model_allocate (int w, int h);
-void model_initialize (CriticalModel *model);
-static void model_step (CriticalModel *model, int *top_x, int *top_y);
+/* Number of screens that should be drawn before reinitializing the
+ model, and count of the number of screens done so far. */
-/* Options this module understands. */
-XrmOptionDescRec options[] = {
- { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
- { "-delay", ".delay", XrmoptionSepArg, 0 },
- { "-colorscheme", ".colorscheme", XrmoptionSepArg, 0 },
- { "-restart", ".restart", XrmoptionSepArg, 0 },
- { "-cellsize", ".cellsize", XrmoptionSepArg, 0 },
- { "-batchcount", ".batchcount", XrmoptionSepArg, 0 },
- { 0, 0, 0, 0 } /* end */
-};
+struct state {
+ Display *dpy;
+ Window window;
+
+ int n_restart, i_restart;
+ XWindowAttributes wattr;
+ CriticalModel *model;
+ int batchcount;
+ XPoint *history; /* in cell coords */
+ long delay_usecs;
+ GC fgc, bgc;
+ XGCValues gcv;
+ CriticalSettings settings;
+ int d_n_colors;
+ XColor *d_colors;
+ int lines_per_color;
+ int d_i_color;
+ int d_pos;
+ int d_wrapped;
+
+ int d_i_batch;
+ eraser_state *eraser;
-/* Default xrm resources. */
-char *defaults[] = {
- ".background: black",
- "*colorscheme: smooth",
- "*delay: 10000",
- "*ncolors: 64",
- "*restart: 8",
- "*cellsize: 9",
- "*batchcount: 1500",
- 0 /* end */
};
+static CriticalModel * model_allocate (int w, int h);
+static void model_initialize (CriticalModel *);
+
+
+static int
+clip (int low, int val, int high)
+{
+ if (val < low)
+ return low;
+ else if (val > high)
+ return high;
+ else
+ return val;
+}
+
+
/* Allocate an return a new simulation model datastructure.
*/
-CriticalModel *
+static CriticalModel *
model_allocate (int model_w, int model_h)
{
- CriticalModel *model;
+ CriticalModel *mm;
- model = malloc (sizeof (CriticalModel));
- if (!model)
+ mm = malloc (sizeof (CriticalModel));
+ if (!mm)
return 0;
- model->width = model_w;
- model->height = model_h;
+ mm->width = model_w;
+ mm->height = model_h;
- model->cells = malloc (sizeof (int) * model_w * model_h);
- if (!model->cells)
+ mm->cells = malloc (sizeof (unsigned short) * model_w * model_h);
+ if (!mm->cells)
return 0;
- return model;
+ return mm;
}
*/
-void
-model_initialize (CriticalModel *model)
+static void
+model_initialize (CriticalModel *mm)
{
int i;
- for (i = model->width * model->height; i >= 0; i--)
+ for (i = mm->width * mm->height - 1; i >= 0; i--)
{
- model->cells[i] = (unsigned short) random ();
+ mm->cells[i] = (unsigned short) random ();
}
}
Neighbours that fall off the edge of the model are simply
ignored. */
static void
-model_step (CriticalModel *model, int *top_x, int *top_y)
+model_step (CriticalModel *mm, XPoint *ptop)
{
int x, y, i;
int dx, dy;
- unsigned short top_value;
+ unsigned short top_value = 0;
+ int top_x = 0, top_y = 0;
/* Find the top cell */
top_value = 0;
i = 0;
- for (y = 0; y < model->height; y++)
- for (x = 0; x < model->width; x++)
+ for (y = 0; y < mm->height; y++)
+ for (x = 0; x < mm->width; x++)
{
- if (model->cells[i] >= top_value)
+ if (mm->cells[i] >= top_value)
{
- top_value = model->cells[i];
- *top_x = x;
- *top_y = y;
+ top_value = mm->cells[i];
+ top_x = x;
+ top_y = y;
}
i++;
}
/* Replace it and its neighbours with new random values */
for (dy = -1; dy <= 1; dy++)
{
- int y = *top_y + dy;
- if (y < 0 || y >= model->height)
+ int yy = top_y + dy;
+ if (yy < 0 || yy >= mm->height)
continue;
for (dx = -1; dx <= 1; dx++)
{
- int x = *top_x + dx;
- if (x < 0 || x >= model->width)
+ int xx = top_x + dx;
+ if (xx < 0 || xx >= mm->width)
continue;
- model->cells[y * model->width + x] = (unsigned short) random();
+ mm->cells[yy * mm->width + xx] = (unsigned short) random();
}
}
+
+ ptop->x = top_x;
+ ptop->y = top_y;
}
/* Construct and return in COLORS and N_COLORS a new set of colors,
depending on the resource settings. */
-void
-setup_colormap (Display *dpy, XWindowAttributes *wattr,
- XColor **colors,
- int *n_colors)
+static void
+setup_colormap (struct state *st, XColor **colors, int *n_colors)
{
Bool writable;
char const * color_scheme;
/* Make a colormap */
- *n_colors = get_integer_resource ("ncolors", "Integer");
- if (*n_colors < 2)
- *n_colors = 2;
+ *n_colors = get_integer_resource (st->dpy, "ncolors", "Integer");
+ if (*n_colors < 3)
+ *n_colors = 3;
*colors = (XColor *) calloc (sizeof(XColor), *n_colors);
if (!*colors)
}
writable = False;
- color_scheme = get_string_resource ("colorscheme", "ColorScheme");
+ color_scheme = get_string_resource (st->dpy, "colorscheme", "ColorScheme");
if (!strcmp (color_scheme, "random"))
{
- make_random_colormap (dpy, wattr->visual,
- wattr->colormap,
+ make_random_colormap (st->dpy, st->wattr.visual,
+ st->wattr.colormap,
*colors, n_colors,
True, True, &writable, True);
}
else if (!strcmp (color_scheme, "smooth"))
{
- make_smooth_colormap (dpy, wattr->visual,
- wattr->colormap,
+ make_smooth_colormap (st->dpy, st->wattr.visual,
+ st->wattr.colormap,
*colors, n_colors,
True, &writable, True);
}
else
{
- make_uniform_colormap (dpy, wattr->visual,
- wattr->colormap,
+ make_uniform_colormap (st->dpy, st->wattr.visual,
+ st->wattr.colormap,
*colors, n_colors, True,
&writable, True);
}
}
+/* Free allocated colormap created by setup_colormap. */
+static void
+free_colormap (struct state *st, XColor **colors, int n_colors)
+{
+ free_colors (st->dpy, st->wattr.colormap, *colors, n_colors);
+ free (*colors);
+}
+
-/* Display a self-organizing criticality screen hack. The program
- runs indefinately on the root window. */
-void
-screenhack (Display *dpy, Window window)
+
+/* Draw one step of the hack. Positions are cell coordinates. */
+static void
+draw_step (struct state *st, GC gc, int pos)
{
- GC fgc, bgc;
- XGCValues gcv;
- XWindowAttributes wattr;
- int n_colors;
- XColor *colors;
- int model_w, model_h;
- CriticalModel *model;
- int lines_per_color = 10;
- int i_color = 0;
- int x1, y1, x2, y2;
- long delay_usecs;
- int cell_size;
- int batchcount;
+ int cell_size = st->settings.cell_size;
+ int half = cell_size/2;
+ int old_pos = (pos + st->settings.trail - 1) % st->settings.trail;
+
+ pos = pos % st->settings.trail;
- /* Number of screens that should be drawn before reinitializing the
- model, and count of the number of screens done so far. */
- int n_restart, i_restart;
+ XDrawLine (st->dpy, st->window, gc,
+ st->history[pos].x * cell_size + half,
+ st->history[pos].y * cell_size + half,
+ st->history[old_pos].x * cell_size + half,
+ st->history[old_pos].y * cell_size + half);
+}
+
+
+
+static void *
+critical_init (Display *dpy, Window window)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+ int model_w, model_h;
+ st->dpy = dpy;
+ st->window = window;
/* Find window attributes */
- XGetWindowAttributes (dpy, window, &wattr);
+ XGetWindowAttributes (st->dpy, st->window, &st->wattr);
+
+ st->batchcount = get_integer_resource (st->dpy, "batchcount", "Integer");
+ if (st->batchcount < 5)
+ st->batchcount = 5;
+
+ st->lines_per_color = 10;
+
+ /* For the moment the model size is just fixed -- making it vary
+ with the screen size just makes the hack boring on large
+ screens. */
+ model_w = 80;
+ st->settings.cell_size = st->wattr.width / model_w;
+ model_h = st->wattr.height / st->settings.cell_size;
/* Construct the initial model state. */
- cell_size = get_integer_resource ("cellsize", "Integer");
- if (cell_size < 1)
- cell_size = 1;
- if (cell_size >= 100)
- cell_size = 99;
-
- batchcount = get_integer_resource ("batchcount", "Integer");
- if (batchcount < 5)
- batchcount = 5;
-
- model_w = wattr.width / cell_size;
- model_h = wattr.height / cell_size;
+
+ st->settings.trail = clip(2, get_integer_resource (st->dpy, "trail", "Integer"), 1000);
- model = model_allocate (model_w, model_h);
- if (!model)
+ st->history = calloc (st->settings.trail, sizeof (st->history[0]));
+ if (!st->history)
+ {
+ fprintf (stderr, "critical: "
+ "couldn't allocate trail history of %d cells\n",
+ st->settings.trail);
+ abort();
+ }
+
+ st->model = model_allocate (model_w, model_h);
+ if (!st->model)
{
fprintf (stderr, "critical: error preparing the model\n");
- return;
+ abort();
}
/* make a black gc for the background */
- gcv.foreground = get_pixel_resource ("background", "Background",
- dpy, wattr.colormap);
- bgc = XCreateGC (dpy, window, GCForeground, &gcv);
+ st->gcv.foreground = get_pixel_resource (st->dpy, st->wattr.colormap,
+ "background", "Background");
+ st->bgc = XCreateGC (st->dpy, st->window, GCForeground, &st->gcv);
- fgc = XCreateGC (dpy, window, 0, &gcv);
+ st->fgc = XCreateGC (st->dpy, st->window, 0, &st->gcv);
- x2 = random() % model_w;
- y2 = random() % model_h;
+#ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (dpy, st->fgc, False);
+ jwxyz_XSetAntiAliasing (dpy, st->bgc, False);
+#endif
- delay_usecs = get_integer_resource ("delay", "Integer");
- n_restart = get_integer_resource ("restart", "Integer");
+ st->delay_usecs = get_integer_resource (st->dpy, "delay", "Integer");
+ st->n_restart = get_integer_resource (st->dpy, "restart", "Integer");
- /* xscreensaver will kill or stop us when the user does something
- * that deserves attention. */
- i_restart = 0;
-
- while (1) {
- int i_batch;
+ setup_colormap (st, &st->d_colors, &st->d_n_colors);
+ model_initialize (st->model);
+ model_step (st->model, &st->history[0]);
+ st->d_pos = 1;
+ st->d_wrapped = 0;
+ st->i_restart = 0;
+ st->d_i_batch = st->batchcount;
+
+ return st;
+}
- if (!i_restart)
- {
- setup_colormap (dpy, &wattr, &colors, &n_colors);
- model_initialize (model);
- }
-
- for (i_batch = batchcount; i_batch; i_batch--)
- {
- /* Set color */
- if ((i_batch % lines_per_color) == 0)
- {
- i_color = (i_color + 1) % n_colors;
- gcv.foreground = colors[i_color].pixel;
- XChangeGC (dpy, fgc, GCForeground, &gcv);
- }
+static unsigned long
+critical_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+
+ if (st->eraser) {
+ st->eraser = erase_window (st->dpy, st->window, st->eraser);
+ return st->delay_usecs;
+ }
+
+ /* for (d_i_batch = batchcount; d_i_batch; d_i_batch--) */
+ {
+ /* Set color */
+ if ((st->d_i_batch % st->lines_per_color) == 0)
+ {
+ st->d_i_color = (st->d_i_color + 1) % st->d_n_colors;
+ st->gcv.foreground = st->d_colors[st->d_i_color].pixel;
+ XChangeGC (st->dpy, st->fgc, GCForeground, &st->gcv);
+ }
- /* draw a line */
- x1 = x2;
- y1 = y2;
+ assert(st->d_pos >= 0 && st->d_pos < st->settings.trail);
+ model_step (st->model, &st->history[st->d_pos]);
- model_step (model, &x2, &y2);
+ draw_step (st, st->fgc, st->d_pos);
- XDrawLine (dpy, window, fgc,
- x1 * cell_size + cell_size/2,
- y1 * cell_size + cell_size/2,
- x2 * cell_size + cell_size/2,
- y2 * cell_size + cell_size/2);
+ /* we use the history as a ring buffer, but don't start erasing until
+ we've d_wrapped around once. */
+ if (++st->d_pos >= st->settings.trail)
+ {
+ st->d_pos -= st->settings.trail;
+ st->d_wrapped = 1;
+ }
- XSync (dpy, False);
- screenhack_handle_events (dpy);
+ if (st->d_wrapped)
+ {
+ draw_step (st, st->bgc, st->d_pos+1);
+ }
- if (delay_usecs)
- usleep (delay_usecs);
}
+
+ st->d_i_batch--;
+ if (st->d_i_batch < 0)
+ st->d_i_batch = st->batchcount;
+ else
+ return st->delay_usecs;
- i_restart = (i_restart + 1) % n_restart;
- erase_full_window (dpy, window);
- }
+ st->i_restart = (st->i_restart + 1) % st->n_restart;
+
+ if (st->i_restart == 0)
+ {
+ /* Time to start a new simulation, this one has probably got
+ to be a bit boring. */
+ free_colormap (st, &st->d_colors, st->d_n_colors);
+ setup_colormap (st, &st->d_colors, &st->d_n_colors);
+ st->eraser = erase_window (st->dpy, st->window, st->eraser);
+ model_initialize (st->model);
+ model_step (st->model, &st->history[0]);
+ st->d_pos = 1;
+ st->d_wrapped = 0;
+ st->d_i_batch = st->batchcount;
+ }
+
+ return st->delay_usecs;
}
+static void
+critical_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+}
-/*
- * Local variables:
- * c-indent-mode: gnu
- * compile-command "make critical && ./critical"
- * End:
- */
+static Bool
+critical_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+critical_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ free (st);
+}
+
+
+/* Options this module understands. */
+static XrmOptionDescRec critical_options[] = {
+ { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
+ { "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-colorscheme", ".colorscheme", XrmoptionSepArg, 0 },
+ { "-restart", ".restart", XrmoptionSepArg, 0 },
+ { "-batchcount", ".batchcount", XrmoptionSepArg, 0 },
+ { "-trail", ".trail", XrmoptionSepArg, 0 },
+ { 0, 0, 0, 0 } /* end */
+};
+
+
+/* Default xrm resources. */
+static const char *critical_defaults[] = {
+ ".background: black",
+ "*colorscheme: smooth",
+ "*delay: 10000",
+ "*ncolors: 64",
+ "*restart: 8",
+ "*batchcount: 1500",
+ "*trail: 50",
+ 0 /* end */
+};
+
+
+XSCREENSAVER_MODULE ("Critical", critical)