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);
111 /* Initialize the data model underlying the hack.
113 For the self-organizing criticality hack, this consists of a 2d
114 array full of random integers.
116 I've considered storing the data as (say) a binary tree within a 2d
117 array, to make finding the highest value faster at the expense of
118 storage space: searching the whole array on each iteration seems a
119 little inefficient. However, the screensaver doesn't seem to take
120 up many cycles as it is: presumably the search is pretty quick
121 compared to the sleeps. The current version uses less than 1% of
122 the CPU time of an AMD K6-233. Many machines running X11 at this
123 point in time seem to be memory-limited, not CPU-limited.
125 The root of all evil, and all that.
130 model_initialize (CriticalModel *mm)
134 for (i = mm->width * mm->height - 1; i >= 0; i--)
136 mm->cells[i] = (unsigned short) random ();
141 /* Move one step forward in the criticality simulation.
143 This function locates and returns in (TOP_X, TOP_Y) the location of
144 the highest-valued cell in the model. It also replaces that cell
145 and it's eight nearest neighbours with new random values.
146 Neighbours that fall off the edge of the model are simply
149 model_step (CriticalModel *mm, XPoint *ptop)
153 unsigned short top_value = 0;
154 int top_x = 0, top_y = 0;
156 /* Find the top cell */
159 for (y = 0; y < mm->height; y++)
160 for (x = 0; x < mm->width; x++)
162 if (mm->cells[i] >= top_value)
164 top_value = mm->cells[i];
171 /* Replace it and its neighbours with new random values */
172 for (dy = -1; dy <= 1; dy++)
175 if (yy < 0 || yy >= mm->height)
178 for (dx = -1; dx <= 1; dx++)
181 if (xx < 0 || xx >= mm->width)
184 mm->cells[yy * mm->width + xx] = (unsigned short) random();
193 /* Construct and return in COLORS and N_COLORS a new set of colors,
194 depending on the resource settings. */
196 setup_colormap (struct state *st, XColor **colors, int *n_colors)
199 char const * color_scheme;
201 /* Make a colormap */
202 *n_colors = get_integer_resource (st->dpy, "ncolors", "Integer");
206 *colors = (XColor *) calloc (sizeof(XColor), *n_colors);
209 fprintf (stderr, "%s:%d: can't allocate memory for colors\n",
215 color_scheme = get_string_resource (st->dpy, "colorscheme", "ColorScheme");
217 if (!strcmp (color_scheme, "random"))
219 make_random_colormap (st->wattr.screen, st->wattr.visual,
222 True, True, &writable, True);
224 else if (!strcmp (color_scheme, "smooth"))
226 make_smooth_colormap (st->wattr.screen, st->wattr.visual,
229 True, &writable, True);
233 make_uniform_colormap (st->wattr.screen, st->wattr.visual,
235 *colors, n_colors, True,
241 /* Free allocated colormap created by setup_colormap. */
243 free_colormap (struct state *st, XColor **colors, int n_colors)
245 free_colors (st->wattr.screen, st->wattr.colormap, *colors, n_colors);
251 /* Draw one step of the hack. Positions are cell coordinates. */
253 draw_step (struct state *st, GC gc, int pos)
255 int cell_size = st->settings.cell_size;
256 int half = cell_size/2;
257 int old_pos = (pos + st->settings.trail - 1) % st->settings.trail;
259 pos = pos % st->settings.trail;
261 XDrawLine (st->dpy, st->window, gc,
262 st->history[pos].x * cell_size + half,
263 st->history[pos].y * cell_size + half,
264 st->history[old_pos].x * cell_size + half,
265 st->history[old_pos].y * cell_size + half);
271 critical_init (Display *dpy, Window window)
273 struct state *st = (struct state *) calloc (1, sizeof(*st));
274 int model_w, model_h;
278 /* Find window attributes */
279 XGetWindowAttributes (st->dpy, st->window, &st->wattr);
281 st->batchcount = get_integer_resource (st->dpy, "batchcount", "Integer");
282 if (st->batchcount < 5)
285 st->lines_per_color = 10;
287 /* For the moment the model size is just fixed -- making it vary
288 with the screen size just makes the hack boring on large
291 st->settings.cell_size = st->wattr.width / model_w;
292 model_h = st->settings.cell_size ?
293 st->wattr.height / st->settings.cell_size : 0;
295 /* Construct the initial model state. */
297 st->settings.trail = clip(2, get_integer_resource (st->dpy, "trail", "Integer"), 1000);
299 st->history = calloc (st->settings.trail, sizeof (st->history[0]));
302 fprintf (stderr, "critical: "
303 "couldn't allocate trail history of %d cells\n",
308 st->model = model_allocate (model_w, model_h);
311 fprintf (stderr, "critical: error preparing the model\n");
315 /* make a black gc for the background */
316 st->gcv.foreground = get_pixel_resource (st->dpy, st->wattr.colormap,
317 "background", "Background");
318 st->bgc = XCreateGC (st->dpy, st->window, GCForeground, &st->gcv);
320 st->fgc = XCreateGC (st->dpy, st->window, 0, &st->gcv);
323 jwxyz_XSetAntiAliasing (dpy, st->fgc, False);
324 jwxyz_XSetAntiAliasing (dpy, st->bgc, False);
327 st->delay_usecs = get_integer_resource (st->dpy, "delay", "Integer");
328 st->n_restart = get_integer_resource (st->dpy, "restart", "Integer");
330 setup_colormap (st, &st->d_colors, &st->d_n_colors);
331 model_initialize (st->model);
332 model_step (st->model, &st->history[0]);
336 st->d_i_batch = st->batchcount;
342 critical_draw (Display *dpy, Window window, void *closure)
344 struct state *st = (struct state *) closure;
347 st->eraser = erase_window (st->dpy, st->window, st->eraser);
348 return st->delay_usecs;
351 /* for (d_i_batch = batchcount; d_i_batch; d_i_batch--) */
354 if ((st->d_i_batch % st->lines_per_color) == 0)
356 st->d_i_color = (st->d_i_color + 1) % st->d_n_colors;
357 st->gcv.foreground = st->d_colors[st->d_i_color].pixel;
358 XChangeGC (st->dpy, st->fgc, GCForeground, &st->gcv);
361 assert(st->d_pos >= 0 && st->d_pos < st->settings.trail);
362 model_step (st->model, &st->history[st->d_pos]);
364 draw_step (st, st->fgc, st->d_pos);
366 /* we use the history as a ring buffer, but don't start erasing until
367 we've d_wrapped around once. */
368 if (++st->d_pos >= st->settings.trail)
370 st->d_pos -= st->settings.trail;
376 draw_step (st, st->bgc, st->d_pos+1);
382 if (st->d_i_batch < 0)
383 st->d_i_batch = st->batchcount;
385 return st->delay_usecs;
387 st->i_restart = (st->i_restart + 1) % st->n_restart;
389 if (st->i_restart == 0)
391 /* Time to start a new simulation, this one has probably got
392 to be a bit boring. */
393 free_colormap (st, &st->d_colors, st->d_n_colors);
394 setup_colormap (st, &st->d_colors, &st->d_n_colors);
395 st->eraser = erase_window (st->dpy, st->window, st->eraser);
396 model_initialize (st->model);
397 model_step (st->model, &st->history[0]);
400 st->d_i_batch = st->batchcount;
403 return st->delay_usecs;
407 critical_reshape (Display *dpy, Window window, void *closure,
408 unsigned int w, unsigned int h)
413 critical_event (Display *dpy, Window window, void *closure, XEvent *event)
419 critical_free (Display *dpy, Window window, void *closure)
421 struct state *st = (struct state *) closure;
426 /* Options this module understands. */
427 static XrmOptionDescRec critical_options[] = {
428 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
429 { "-delay", ".delay", XrmoptionSepArg, 0 },
430 { "-colorscheme", ".colorscheme", XrmoptionSepArg, 0 },
431 { "-restart", ".restart", XrmoptionSepArg, 0 },
432 { "-batchcount", ".batchcount", XrmoptionSepArg, 0 },
433 { "-trail", ".trail", XrmoptionSepArg, 0 },
434 { 0, 0, 0, 0 } /* end */
438 /* Default xrm resources. */
439 static const char *critical_defaults[] = {
440 ".background: black",
442 "*colorscheme: smooth",
452 XSCREENSAVER_MODULE ("Critical", critical)