--- /dev/null
+/* critical -- Self-organizing-criticality display hack for XScreenSaver
+ * Copyright (C) 1998, 1999 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
+ * 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.
+ *
+ * See `critical.man' for more information.
+ *
+ * Revision history:
+ * 13 Nov 1998: Initial version, Martin Pool <mbp@humbug.org.au>
+ */
+
+#include "screenhack.h"
+#include "erase.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+char *progclass = "Critical";
+
+
+typedef struct {
+ int width, height; /* in cells */
+ unsigned short *cells;
+} CriticalModel;
+
+
+CriticalModel * model_allocate (int w, int h);
+void model_initialize (CriticalModel *model);
+static void model_step (CriticalModel *model, int *top_x, int *top_y);
+
+
+/* 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 */
+};
+
+
+/* Default xrm resources. */
+char *defaults[] = {
+ ".background: black",
+ "*colorscheme: smooth",
+ "*delay: 10000",
+ "*ncolors: 64",
+ "*restart: 8",
+ "*cellsize: 9",
+ "*batchcount: 1500",
+ 0 /* end */
+};
+
+
+/* Allocate an return a new simulation model datastructure.
+ */
+
+CriticalModel *
+model_allocate (int model_w, int model_h)
+{
+ CriticalModel *model;
+
+ model = malloc (sizeof (CriticalModel));
+ if (!model)
+ return 0;
+
+ model->width = model_w;
+ model->height = model_h;
+
+ model->cells = malloc (sizeof (int) * model_w * model_h);
+ if (!model->cells)
+ return 0;
+
+ return model;
+}
+
+
+
+/* Initialize the data model underlying the hack.
+
+ For the self-organizing criticality hack, this consists of a 2d
+ array full of random integers.
+
+ I've considered storing the data as (say) a binary tree within a 2d
+ array, to make finding the highest value faster at the expense of
+ storage space: searching the whole array on each iteration seems a
+ little inefficient. However, the screensaver doesn't seem to take
+ up many cycles as it is: presumably the search is pretty quick
+ compared to the sleeps. The current version uses less than 1% of
+ the CPU time of an AMD K6-233. Many machines running X11 at this
+ point in time seem to be memory-limited, not CPU-limited.
+
+ The root of all evil, and all that.
+*/
+
+
+void
+model_initialize (CriticalModel *model)
+{
+ int i;
+
+ for (i = model->width * model->height; i >= 0; i--)
+ {
+ model->cells[i] = (unsigned short) random ();
+ }
+}
+
+
+/* Move one step forward in the criticality simulation.
+
+ This function locates and returns in (TOP_X, TOP_Y) the location of
+ the highest-valued cell in the model. It also replaces that cell
+ and it's eight nearest neighbours with new random values.
+ Neighbours that fall off the edge of the model are simply
+ ignored. */
+static void
+model_step (CriticalModel *model, int *top_x, int *top_y)
+{
+ int x, y, i;
+ int dx, dy;
+ unsigned short top_value;
+
+ /* Find the top cell */
+ top_value = 0;
+ i = 0;
+ for (y = 0; y < model->height; y++)
+ for (x = 0; x < model->width; x++)
+ {
+ if (model->cells[i] >= top_value)
+ {
+ top_value = model->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)
+ continue;
+
+ for (dx = -1; dx <= 1; dx++)
+ {
+ int x = *top_x + dx;
+ if (x < 0 || x >= model->width)
+ continue;
+
+ model->cells[y * model->width + x] = (unsigned short) random();
+ }
+ }
+}
+
+
+/* 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)
+{
+ Bool writable;
+ char const * color_scheme;
+
+ /* Make a colormap */
+ *n_colors = get_integer_resource ("ncolors", "Integer");
+ if (*n_colors < 2)
+ *n_colors = 2;
+
+ *colors = (XColor *) calloc (sizeof(XColor), *n_colors);
+ if (!*colors)
+ {
+ fprintf (stderr, "%s:%d: can't allocate memory for colors\n",
+ __FILE__, __LINE__);
+ return;
+ }
+
+ writable = False;
+ color_scheme = get_string_resource ("colorscheme", "ColorScheme");
+
+ if (!strcmp (color_scheme, "random"))
+ {
+ make_random_colormap (dpy, wattr->visual,
+ wattr->colormap,
+ *colors, n_colors,
+ True, True, &writable, True);
+ }
+ else if (!strcmp (color_scheme, "smooth"))
+ {
+ make_smooth_colormap (dpy, wattr->visual,
+ wattr->colormap,
+ *colors, n_colors,
+ True, &writable, True);
+ }
+ else
+ {
+ make_uniform_colormap (dpy, wattr->visual,
+ wattr->colormap,
+ *colors, n_colors, True,
+ &writable, True);
+ }
+}
+
+
+
+/* Display a self-organizing criticality screen hack. The program
+ runs indefinately on the root window. */
+void
+screenhack (Display *dpy, Window window)
+{
+ 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;
+
+ /* 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;
+
+ /* Find window attributes */
+ XGetWindowAttributes (dpy, window, &wattr);
+
+ /* 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;
+
+ model = model_allocate (model_w, model_h);
+ if (!model)
+ {
+ fprintf (stderr, "critical: error preparing the model\n");
+ return;
+ }
+
+ /* make a black gc for the background */
+ gcv.foreground = get_pixel_resource ("background", "Background",
+ dpy, wattr.colormap);
+ bgc = XCreateGC (dpy, window, GCForeground, &gcv);
+
+ fgc = XCreateGC (dpy, window, 0, &gcv);
+
+ x2 = rand() % model_w;
+ y2 = rand() % model_h;
+
+ delay_usecs = get_integer_resource ("delay", "Integer");
+ n_restart = get_integer_resource ("restart", "Integer");
+
+ /* xscreensaver will kill or stop us when the user does something
+ * that deserves attention. */
+ i_restart = 0;
+
+ while (1) {
+ int i_batch;
+
+ 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);
+ }
+
+ /* draw a line */
+ x1 = x2;
+ y1 = y2;
+
+ model_step (model, &x2, &y2);
+
+ 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);
+
+ XSync (dpy, False);
+ screenhack_handle_events (dpy);
+
+ if (delay_usecs)
+ usleep (delay_usecs);
+ }
+
+ i_restart = (i_restart + 1) % n_restart;
+ erase_full_window (dpy, window);
+ }
+}
+
+
+/*
+ * Local variables:
+ * c-indent-mode: gnu
+ * compile-command "make critical && ./critical"
+ * End:
+ */