1 /* critical -- Self-organizing-criticality display hack for XScreenSaver
2 * Copyright (C) 1998, 1999, 2000 Martin Pool <mbp@humbug.org.au>
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation. No representations are made
9 * about the suitability of this software for any purpose. It is
10 * provided "as is" without express or implied warranty.
12 * See `critical.man' for more information.
15 * 13 Nov 1998: Initial version, Martin Pool <mbp@humbug.org.au>
16 * 08 Feb 2000: Change to keeping and erasing a trail, <mbp>
18 * It would be nice to draw curvy shapes rather than just straight
19 * lines, but X11 doesn't have spline primitives (?) so we'd have to
20 * do all the work ourselves */
22 #include "screenhack.h"
29 char *progclass = "Critical";
33 int width, height; /* in cells */
34 unsigned short *cells;
38 int trail; /* length of trail */
43 CriticalModel * model_allocate (int w, int h);
44 void model_initialize (CriticalModel *model);
46 /* Options this module understands. */
47 XrmOptionDescRec options[] = {
48 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
49 { "-delay", ".delay", XrmoptionSepArg, 0 },
50 { "-colorscheme", ".colorscheme", XrmoptionSepArg, 0 },
51 { "-restart", ".restart", XrmoptionSepArg, 0 },
52 { "-cellsize", ".cellsize", XrmoptionSepArg, 0 },
53 { "-batchcount", ".batchcount", XrmoptionSepArg, 0 },
54 { "-trail", ".trail", XrmoptionSepArg, 0 },
55 { 0, 0, 0, 0 } /* end */
59 /* Default xrm resources. */
62 "*colorscheme: smooth",
73 clip (int low, int val, int high)
84 /* Allocate an return a new simulation model datastructure.
88 model_allocate (int model_w, int model_h)
92 model = malloc (sizeof (CriticalModel));
96 model->width = model_w;
97 model->height = model_h;
99 model->cells = malloc (sizeof (int) * model_w * model_h);
108 /* Initialize the data model underlying the hack.
110 For the self-organizing criticality hack, this consists of a 2d
111 array full of random integers.
113 I've considered storing the data as (say) a binary tree within a 2d
114 array, to make finding the highest value faster at the expense of
115 storage space: searching the whole array on each iteration seems a
116 little inefficient. However, the screensaver doesn't seem to take
117 up many cycles as it is: presumably the search is pretty quick
118 compared to the sleeps. The current version uses less than 1% of
119 the CPU time of an AMD K6-233. Many machines running X11 at this
120 point in time seem to be memory-limited, not CPU-limited.
122 The root of all evil, and all that.
127 model_initialize (CriticalModel *model)
131 for (i = model->width * model->height; i >= 0; i--)
133 model->cells[i] = (unsigned short) random ();
138 /* Move one step forward in the criticality simulation.
140 This function locates and returns in (TOP_X, TOP_Y) the location of
141 the highest-valued cell in the model. It also replaces that cell
142 and it's eight nearest neighbours with new random values.
143 Neighbours that fall off the edge of the model are simply
146 model_step (CriticalModel *model, XPoint *ptop)
150 unsigned short top_value = 0;
151 int top_x = 0, top_y = 0;
153 /* Find the top cell */
156 for (y = 0; y < model->height; y++)
157 for (x = 0; x < model->width; x++)
159 if (model->cells[i] >= top_value)
161 top_value = model->cells[i];
168 /* Replace it and its neighbours with new random values */
169 for (dy = -1; dy <= 1; dy++)
172 if (y < 0 || y >= model->height)
175 for (dx = -1; dx <= 1; dx++)
178 if (x < 0 || x >= model->width)
181 model->cells[y * model->width + x] = (unsigned short) random();
190 /* Construct and return in COLORS and N_COLORS a new set of colors,
191 depending on the resource settings. */
193 setup_colormap (Display *dpy, XWindowAttributes *wattr,
198 char const * color_scheme;
200 /* Make a colormap */
201 *n_colors = get_integer_resource ("ncolors", "Integer");
205 *colors = (XColor *) calloc (sizeof(XColor), *n_colors);
208 fprintf (stderr, "%s:%d: can't allocate memory for colors\n",
214 color_scheme = get_string_resource ("colorscheme", "ColorScheme");
216 if (!strcmp (color_scheme, "random"))
218 make_random_colormap (dpy, wattr->visual,
221 True, True, &writable, True);
223 else if (!strcmp (color_scheme, "smooth"))
225 make_smooth_colormap (dpy, wattr->visual,
228 True, &writable, True);
232 make_uniform_colormap (dpy, wattr->visual,
234 *colors, n_colors, True,
240 /* Draw one step of the hack. Positions are cell coordinates. */
242 draw_step (CriticalSettings *settings,
243 Display *dpy, Window window, GC gc,
244 int pos, XPoint *history)
246 int cell_size = settings->cell_size;
247 int half = cell_size/2;
248 int old_pos = (pos + settings->trail - 1) % settings->trail;
250 pos = pos % settings->trail;
252 XDrawLine (dpy, window, gc,
253 history[pos].x * cell_size + half,
254 history[pos].y * cell_size + half,
255 history[old_pos].x * cell_size + half,
256 history[old_pos].y * cell_size + half);
261 /* Display a self-organizing criticality screen hack. The program
262 runs indefinately on the root window. */
264 screenhack (Display *dpy, Window window)
268 int model_w, model_h;
269 CriticalModel *model;
270 int lines_per_color = 10;
274 XPoint *history; /* in cell coords */
279 XWindowAttributes wattr;
280 CriticalSettings settings;
282 /* Number of screens that should be drawn before reinitializing the
283 model, and count of the number of screens done so far. */
284 int n_restart, i_restart;
286 /* Find window attributes */
287 XGetWindowAttributes (dpy, window, &wattr);
289 batchcount = get_integer_resource ("batchcount", "Integer");
293 /* For the moment the model size is just fixed -- making it vary
294 with the screen size just makes the hack boring on large
297 settings.cell_size = wattr.width / model_w;
298 model_h = wattr.height / settings.cell_size;
300 /* Construct the initial model state. */
302 settings.trail = clip(2, get_integer_resource ("trail", "Integer"), 1000);
304 history = malloc (sizeof history[0] * settings.trail);
307 fprintf (stderr, "critical: "
308 "couldn't allocate trail history of %d cells\n",
313 model = model_allocate (model_w, model_h);
316 fprintf (stderr, "critical: error preparing the model\n");
320 /* make a black gc for the background */
321 gcv.foreground = get_pixel_resource ("background", "Background",
322 dpy, wattr.colormap);
323 bgc = XCreateGC (dpy, window, GCForeground, &gcv);
325 fgc = XCreateGC (dpy, window, 0, &gcv);
327 delay_usecs = get_integer_resource ("delay", "Integer");
328 n_restart = get_integer_resource ("restart", "Integer");
330 /* xscreensaver will kill or stop us when the user does something
331 * that deserves attention. */
339 /* Time to start a new simulation, this one has probably got
340 to be a bit boring. */
341 setup_colormap (dpy, &wattr, &colors, &n_colors);
342 erase_full_window (dpy, window);
343 model_initialize (model);
348 for (i_batch = batchcount; i_batch; i_batch--)
351 if ((i_batch % lines_per_color) == 0)
353 i_color = (i_color + 1) % n_colors;
354 gcv.foreground = colors[i_color].pixel;
355 XChangeGC (dpy, fgc, GCForeground, &gcv);
358 assert(pos >= 0 && pos < settings.trail);
359 model_step (model, &history[pos]);
361 draw_step (&settings, dpy, window, fgc, pos, history);
363 /* we use the history as a ring buffer, but don't start erasing until
364 we've wrapped around once. */
365 if (++pos >= settings.trail)
367 pos -= settings.trail;
373 draw_step (&settings, dpy, window, bgc, pos+1, history);
377 screenhack_handle_events (dpy);
380 usleep (delay_usecs);
384 i_restart = (i_restart + 1) % n_restart;