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"
31 int width, height; /* in cells */
32 unsigned short *cells;
36 int trail; /* length of trail */
41 /* Number of screens that should be drawn before reinitializing the
42 model, and count of the number of screens done so far. */
48 int n_restart, i_restart;
49 XWindowAttributes wattr;
52 XPoint *history; /* in cell coords */
56 CriticalSettings settings;
71 static CriticalModel * model_allocate (int w, int h);
72 static void model_initialize (CriticalModel *);
76 clip (int low, int val, int high)
87 /* Allocate an return a new simulation model datastructure.
90 static CriticalModel *
91 model_allocate (int model_w, int model_h)
95 mm = malloc (sizeof (CriticalModel));
100 mm->height = model_h;
102 mm->cells = malloc (sizeof (unsigned short) * model_w * model_h);
113 /* Initialize the data model underlying the hack.
115 For the self-organizing criticality hack, this consists of a 2d
116 array full of random integers.
118 I've considered storing the data as (say) a binary tree within a 2d
119 array, to make finding the highest value faster at the expense of
120 storage space: searching the whole array on each iteration seems a
121 little inefficient. However, the screensaver doesn't seem to take
122 up many cycles as it is: presumably the search is pretty quick
123 compared to the sleeps. The current version uses less than 1% of
124 the CPU time of an AMD K6-233. Many machines running X11 at this
125 point in time seem to be memory-limited, not CPU-limited.
127 The root of all evil, and all that.
132 model_initialize (CriticalModel *mm)
136 for (i = mm->width * mm->height - 1; i >= 0; i--)
138 mm->cells[i] = (unsigned short) random ();
143 /* Move one step forward in the criticality simulation.
145 This function locates and returns in (TOP_X, TOP_Y) the location of
146 the highest-valued cell in the model. It also replaces that cell
147 and it's eight nearest neighbours with new random values.
148 Neighbours that fall off the edge of the model are simply
151 model_step (CriticalModel *mm, XPoint *ptop)
155 unsigned short top_value = 0;
156 int top_x = 0, top_y = 0;
158 /* Find the top cell */
161 for (y = 0; y < mm->height; y++)
162 for (x = 0; x < mm->width; x++)
164 if (mm->cells[i] >= top_value)
166 top_value = mm->cells[i];
173 /* Replace it and its neighbours with new random values */
174 for (dy = -1; dy <= 1; dy++)
177 if (yy < 0 || yy >= mm->height)
180 for (dx = -1; dx <= 1; dx++)
183 if (xx < 0 || xx >= mm->width)
186 mm->cells[yy * mm->width + xx] = (unsigned short) random();
195 /* Construct and return in COLORS and N_COLORS a new set of colors,
196 depending on the resource settings. */
198 setup_colormap (struct state *st, XColor **colors, int *n_colors)
201 char const * color_scheme;
203 /* Make a colormap */
204 *n_colors = get_integer_resource (st->dpy, "ncolors", "Integer");
208 *colors = (XColor *) calloc (sizeof(XColor), *n_colors);
211 fprintf (stderr, "%s:%d: can't allocate memory for colors\n",
217 color_scheme = get_string_resource (st->dpy, "colorscheme", "ColorScheme");
219 if (!strcmp (color_scheme, "random"))
221 make_random_colormap (st->wattr.screen, st->wattr.visual,
224 True, True, &writable, True);
226 else if (!strcmp (color_scheme, "smooth"))
228 make_smooth_colormap (st->wattr.screen, st->wattr.visual,
231 True, &writable, True);
235 make_uniform_colormap (st->wattr.screen, st->wattr.visual,
237 *colors, n_colors, True,
243 /* Free allocated colormap created by setup_colormap. */
245 free_colormap (struct state *st, XColor **colors, int n_colors)
247 free_colors (st->wattr.screen, st->wattr.colormap, *colors, n_colors);
253 /* Draw one step of the hack. Positions are cell coordinates. */
255 draw_step (struct state *st, GC gc, int pos)
257 int cell_size = st->settings.cell_size;
258 int half = cell_size/2;
259 int old_pos = (pos + st->settings.trail - 1) % st->settings.trail;
261 pos = pos % st->settings.trail;
263 XDrawLine (st->dpy, st->window, gc,
264 st->history[pos].x * cell_size + half,
265 st->history[pos].y * cell_size + half,
266 st->history[old_pos].x * cell_size + half,
267 st->history[old_pos].y * cell_size + half);
273 critical_init (Display *dpy, Window window)
275 struct state *st = (struct state *) calloc (1, sizeof(*st));
276 int model_w, model_h;
280 /* Find window attributes */
281 XGetWindowAttributes (st->dpy, st->window, &st->wattr);
283 st->batchcount = get_integer_resource (st->dpy, "batchcount", "Integer");
284 if (st->batchcount < 5)
287 st->lines_per_color = 10;
289 /* For the moment the model size is just fixed -- making it vary
290 with the screen size just makes the hack boring on large
293 st->settings.cell_size = st->wattr.width / model_w;
294 model_h = st->settings.cell_size ?
295 st->wattr.height / st->settings.cell_size : 1;
297 /* Construct the initial model state. */
299 st->settings.trail = clip(2, get_integer_resource (st->dpy, "trail", "Integer"), 1000);
301 st->history = calloc (st->settings.trail, sizeof (st->history[0]));
304 fprintf (stderr, "critical: "
305 "couldn't allocate trail history of %d cells\n",
310 st->model = model_allocate (model_w, model_h);
313 fprintf (stderr, "critical: error preparing the model\n");
317 /* make a black gc for the background */
318 st->gcv.foreground = get_pixel_resource (st->dpy, st->wattr.colormap,
319 "background", "Background");
320 st->bgc = XCreateGC (st->dpy, st->window, GCForeground, &st->gcv);
322 st->fgc = XCreateGC (st->dpy, st->window, 0, &st->gcv);
325 jwxyz_XSetAntiAliasing (dpy, st->fgc, False);
326 jwxyz_XSetAntiAliasing (dpy, st->bgc, False);
329 st->delay_usecs = get_integer_resource (st->dpy, "delay", "Integer");
330 st->n_restart = get_integer_resource (st->dpy, "restart", "Integer");
332 setup_colormap (st, &st->d_colors, &st->d_n_colors);
333 model_initialize (st->model);
334 model_step (st->model, &st->history[0]);
338 st->d_i_batch = st->batchcount;
344 critical_draw (Display *dpy, Window window, void *closure)
346 struct state *st = (struct state *) closure;
349 st->eraser = erase_window (st->dpy, st->window, st->eraser);
350 return st->delay_usecs;
353 /* for (d_i_batch = batchcount; d_i_batch; d_i_batch--) */
356 if ((st->d_i_batch % st->lines_per_color) == 0)
358 st->d_i_color = (st->d_i_color + 1) % st->d_n_colors;
359 st->gcv.foreground = st->d_colors[st->d_i_color].pixel;
360 XChangeGC (st->dpy, st->fgc, GCForeground, &st->gcv);
363 assert(st->d_pos >= 0 && st->d_pos < st->settings.trail);
364 model_step (st->model, &st->history[st->d_pos]);
366 draw_step (st, st->fgc, st->d_pos);
368 /* we use the history as a ring buffer, but don't start erasing until
369 we've d_wrapped around once. */
370 if (++st->d_pos >= st->settings.trail)
372 st->d_pos -= st->settings.trail;
378 draw_step (st, st->bgc, st->d_pos+1);
384 if (st->d_i_batch < 0)
385 st->d_i_batch = st->batchcount;
387 return st->delay_usecs;
389 st->i_restart = (st->i_restart + 1) % st->n_restart;
391 if (st->i_restart == 0)
393 /* Time to start a new simulation, this one has probably got
394 to be a bit boring. */
395 free_colormap (st, &st->d_colors, st->d_n_colors);
396 setup_colormap (st, &st->d_colors, &st->d_n_colors);
397 st->eraser = erase_window (st->dpy, st->window, st->eraser);
398 model_initialize (st->model);
399 model_step (st->model, &st->history[0]);
402 st->d_i_batch = st->batchcount;
405 return st->delay_usecs;
409 critical_reshape (Display *dpy, Window window, void *closure,
410 unsigned int w, unsigned int h)
415 critical_event (Display *dpy, Window window, void *closure, XEvent *event)
421 critical_free (Display *dpy, Window window, void *closure)
423 struct state *st = (struct state *) closure;
428 /* Options this module understands. */
429 static XrmOptionDescRec critical_options[] = {
430 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
431 { "-delay", ".delay", XrmoptionSepArg, 0 },
432 { "-colorscheme", ".colorscheme", XrmoptionSepArg, 0 },
433 { "-restart", ".restart", XrmoptionSepArg, 0 },
434 { "-batchcount", ".batchcount", XrmoptionSepArg, 0 },
435 { "-trail", ".trail", XrmoptionSepArg, 0 },
436 { 0, 0, 0, 0 } /* end */
440 /* Default xrm resources. */
441 static const char *critical_defaults[] = {
442 ".background: black",
444 "*colorscheme: smooth",
454 XSCREENSAVER_MODULE ("Critical", critical)