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 (unsigned short) * 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 - 1; 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 /* Free allocated colormap created by setup_colormap. */
242 free_colormap (Display *dpy, XWindowAttributes *wattr,
243 XColor **colors, int n_colors)
245 free_colors (dpy, wattr->colormap, *colors, n_colors);
251 /* Draw one step of the hack. Positions are cell coordinates. */
253 draw_step (CriticalSettings *settings,
254 Display *dpy, Window window, GC gc,
255 int pos, XPoint *history)
257 int cell_size = settings->cell_size;
258 int half = cell_size/2;
259 int old_pos = (pos + settings->trail - 1) % settings->trail;
261 pos = pos % settings->trail;
263 XDrawLine (dpy, window, gc,
264 history[pos].x * cell_size + half,
265 history[pos].y * cell_size + half,
266 history[old_pos].x * cell_size + half,
267 history[old_pos].y * cell_size + half);
272 /* Display a self-organizing criticality screen hack. The program
273 runs indefinately on the root window. */
275 screenhack (Display *dpy, Window window)
279 int model_w, model_h;
280 CriticalModel *model;
281 int lines_per_color = 10;
285 XPoint *history; /* in cell coords */
290 XWindowAttributes wattr;
291 CriticalSettings settings;
293 /* Number of screens that should be drawn before reinitializing the
294 model, and count of the number of screens done so far. */
295 int n_restart, i_restart;
297 /* Find window attributes */
298 XGetWindowAttributes (dpy, window, &wattr);
300 batchcount = get_integer_resource ("batchcount", "Integer");
304 /* For the moment the model size is just fixed -- making it vary
305 with the screen size just makes the hack boring on large
308 settings.cell_size = wattr.width / model_w;
309 model_h = wattr.height / settings.cell_size;
311 /* Construct the initial model state. */
313 settings.trail = clip(2, get_integer_resource ("trail", "Integer"), 1000);
315 history = malloc (sizeof history[0] * settings.trail);
318 fprintf (stderr, "critical: "
319 "couldn't allocate trail history of %d cells\n",
324 model = model_allocate (model_w, model_h);
327 fprintf (stderr, "critical: error preparing the model\n");
331 /* make a black gc for the background */
332 gcv.foreground = get_pixel_resource ("background", "Background",
333 dpy, wattr.colormap);
334 bgc = XCreateGC (dpy, window, GCForeground, &gcv);
336 fgc = XCreateGC (dpy, window, 0, &gcv);
338 delay_usecs = get_integer_resource ("delay", "Integer");
339 n_restart = get_integer_resource ("restart", "Integer");
341 /* xscreensaver will kill or stop us when the user does something
342 * that deserves attention. */
350 /* Time to start a new simulation, this one has probably got
351 to be a bit boring. */
352 setup_colormap (dpy, &wattr, &colors, &n_colors);
353 erase_full_window (dpy, window);
354 model_initialize (model);
355 model_step (model, &history[0]);
360 for (i_batch = batchcount; i_batch; i_batch--)
363 if ((i_batch % lines_per_color) == 0)
365 i_color = (i_color + 1) % n_colors;
366 gcv.foreground = colors[i_color].pixel;
367 XChangeGC (dpy, fgc, GCForeground, &gcv);
370 assert(pos >= 0 && pos < settings.trail);
371 model_step (model, &history[pos]);
373 draw_step (&settings, dpy, window, fgc, pos, history);
375 /* we use the history as a ring buffer, but don't start erasing until
376 we've wrapped around once. */
377 if (++pos >= settings.trail)
379 pos -= settings.trail;
385 draw_step (&settings, dpy, window, bgc, pos+1, history);
389 screenhack_handle_events (dpy);
392 usleep (delay_usecs);
396 i_restart = (i_restart + 1) % n_restart;
400 /* Clean up after completing a simulation. */
401 free_colormap (dpy, &wattr, &colors, n_colors);