source: trunk/third/xscreensaver/hacks/critical.c @ 20148

Revision 20148, 9.9 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* critical -- Self-organizing-criticality display hack for XScreenSaver
2 * Copyright (C) 1998, 1999, 2000 Martin Pool <mbp@humbug.org.au>
3 *
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.
11 *
12 * See `critical.man' for more information.
13 *
14 * Revision history:
15 * 13 Nov 1998: Initial version, Martin Pool <mbp@humbug.org.au>
16 * 08 Feb 2000: Change to keeping and erasing a trail, <mbp>
17 *
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  */
21
22#include "screenhack.h"
23#include "erase.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <assert.h>
28
29char *progclass = "Critical";
30
31
32typedef struct {
33  int width, height;            /* in cells */
34  unsigned short *cells;
35} CriticalModel;
36
37typedef struct {
38  int trail;                    /* length of trail */
39  int cell_size;
40} CriticalSettings;
41
42
43CriticalModel * model_allocate (int w, int h);
44void model_initialize (CriticalModel *model);
45
46/* Options this module understands.  */
47XrmOptionDescRec 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 */
56};
57
58
59/* Default xrm resources. */
60char *defaults[] = {
61  ".background:                 black",
62  "*colorscheme:                smooth",
63  "*delay:                      10000",
64  "*ncolors:                    64",
65  "*restart:                    8",
66  "*batchcount:                 1500",
67  "*trail:                      50",
68  0                             /* end */
69};
70
71
72int
73clip (int low, int val, int high)
74{
75  if (val < low)
76    return low;
77  else if (val > high)
78    return high;
79  else
80    return val;
81}
82
83
84/* Allocate an return a new simulation model datastructure.
85 */
86
87CriticalModel *
88model_allocate (int model_w, int model_h)
89{
90  CriticalModel         *model;
91
92  model = malloc (sizeof (CriticalModel));
93  if (!model)
94    return 0;
95
96  model->width = model_w;
97  model->height = model_h;
98
99  model->cells = malloc (sizeof (unsigned short) * model_w * model_h);
100  if (!model->cells)
101    return 0;
102
103  return model;
104}
105
106
107
108/* Initialize the data model underlying the hack.
109
110   For the self-organizing criticality hack, this consists of a 2d
111   array full of random integers.
112
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.
121
122   The root of all evil, and all that.
123*/
124
125
126void
127model_initialize (CriticalModel *model)
128{
129  int i;
130 
131  for (i = model->width * model->height - 1; i >= 0; i--)
132    {
133      model->cells[i] = (unsigned short) random ();
134    }
135}
136
137
138/* Move one step forward in the criticality simulation.
139
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
144   ignored. */
145static void
146model_step (CriticalModel *model, XPoint *ptop)
147{
148  int                   x, y, i;
149  int                   dx, dy;
150  unsigned short        top_value = 0;
151  int                   top_x = 0, top_y = 0;
152
153  /* Find the top cell */
154  top_value = 0;
155  i = 0;
156  for (y = 0; y < model->height; y++)
157    for (x = 0; x < model->width; x++)
158      {
159        if (model->cells[i] >= top_value)
160          {
161            top_value = model->cells[i];
162            top_x = x;
163            top_y = y;
164          }
165        i++;
166      }
167
168  /* Replace it and its neighbours with new random values */
169  for (dy = -1; dy <= 1; dy++)
170    {
171      int y = top_y + dy;
172      if (y < 0  ||  y >= model->height)
173        continue;
174     
175      for (dx = -1; dx <= 1; dx++)
176        {
177          int x = top_x + dx;
178          if (x < 0  ||  x >= model->width)
179            continue;
180         
181          model->cells[y * model->width + x] = (unsigned short) random();
182        }
183    }
184
185  ptop->x = top_x;
186  ptop->y = top_y;
187}
188
189
190/* Construct and return in COLORS and N_COLORS a new set of colors,
191   depending on the resource settings.  */
192void
193setup_colormap (Display *dpy, XWindowAttributes *wattr,
194                XColor **colors,
195                int *n_colors)
196{
197  Bool                  writable;
198  char const *          color_scheme;
199
200  /* Make a colormap */
201  *n_colors = get_integer_resource ("ncolors", "Integer");
202  if (*n_colors < 3)
203    *n_colors = 3;
204 
205  *colors = (XColor *) calloc (sizeof(XColor), *n_colors);
206  if (!*colors)
207    {
208      fprintf (stderr, "%s:%d: can't allocate memory for colors\n",
209               __FILE__, __LINE__);
210      return;
211    }
212
213  writable = False;
214  color_scheme = get_string_resource ("colorscheme", "ColorScheme");
215 
216  if (!strcmp (color_scheme, "random"))
217    {
218      make_random_colormap (dpy, wattr->visual,
219                            wattr->colormap,
220                            *colors, n_colors,
221                            True, True, &writable, True);
222    }
223  else if (!strcmp (color_scheme, "smooth"))
224    {
225      make_smooth_colormap (dpy, wattr->visual,
226                            wattr->colormap,
227                            *colors, n_colors,
228                            True, &writable, True);
229    }
230  else
231    {
232      make_uniform_colormap (dpy, wattr->visual,
233                             wattr->colormap,
234                             *colors, n_colors, True,
235                             &writable, True);
236    }
237}
238
239
240/* Free allocated colormap created by setup_colormap. */
241static void
242free_colormap (Display *dpy, XWindowAttributes *wattr,
243               XColor **colors, int n_colors)
244{
245  free_colors (dpy, wattr->colormap, *colors, n_colors);
246  free (*colors);
247}
248
249
250
251/* Draw one step of the hack.  Positions are cell coordinates. */
252static void
253draw_step (CriticalSettings *settings,
254           Display *dpy, Window window, GC gc,
255           int pos, XPoint *history)
256{
257  int cell_size = settings->cell_size;
258  int half = cell_size/2;
259  int old_pos = (pos + settings->trail - 1) % settings->trail;
260 
261  pos = pos % settings->trail;
262
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);
268}
269
270
271
272/* Display a self-organizing criticality screen hack.  The program
273   runs indefinately on the root window. */
274void
275screenhack (Display *dpy, Window window)
276{
277  int                   n_colors;
278  XColor                *colors;
279  int                   model_w, model_h;
280  CriticalModel         *model;
281  int                   lines_per_color = 10;
282  int                   i_color = 0;
283  long                  delay_usecs;
284  int                   batchcount;
285  XPoint                *history; /* in cell coords */
286  int                   pos = 0;
287  int                   wrapped = 0;
288  GC                    fgc, bgc;
289  XGCValues             gcv;
290  XWindowAttributes     wattr;
291  CriticalSettings      settings;
292
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;
296
297  /* Find window attributes */
298  XGetWindowAttributes (dpy, window, &wattr);
299
300  batchcount = get_integer_resource ("batchcount", "Integer");
301  if (batchcount < 5)
302    batchcount = 5;
303
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
306     screens. */
307  model_w = 80;
308  settings.cell_size = wattr.width / model_w;
309  model_h = wattr.height / settings.cell_size;
310
311  /* Construct the initial model state. */
312
313  settings.trail = clip(2, get_integer_resource ("trail", "Integer"), 1000);
314 
315  history = malloc (sizeof history[0] * settings.trail);
316  if (!history)
317    {
318      fprintf (stderr, "critical: "
319               "couldn't allocate trail history of %d cells\n",
320               settings.trail);
321      return;
322    }
323
324  model = model_allocate (model_w, model_h);
325  if (!model)
326    {
327      fprintf (stderr, "critical: error preparing the model\n");
328      return;
329    }
330 
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);
335
336  fgc = XCreateGC (dpy, window, 0, &gcv);
337
338  delay_usecs = get_integer_resource ("delay", "Integer");
339  n_restart = get_integer_resource ("restart", "Integer");
340   
341  /* xscreensaver will kill or stop us when the user does something
342   * that deserves attention. */
343  i_restart = 0;
344 
345  while (1) {
346    int i_batch;
347
348    if (i_restart == 0)
349      {
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]);
356        pos = 1;
357        wrapped = 0;
358      }
359   
360    for (i_batch = batchcount; i_batch; i_batch--)
361      {
362        /* Set color */
363        if ((i_batch % lines_per_color) == 0)
364          {
365            i_color = (i_color + 1) % n_colors;
366            gcv.foreground = colors[i_color].pixel;
367            XChangeGC (dpy, fgc, GCForeground, &gcv);
368          }
369       
370        assert(pos >= 0 && pos < settings.trail);
371        model_step (model, &history[pos]);
372
373        draw_step (&settings, dpy, window, fgc, pos, history);
374
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)
378          {
379            pos -= settings.trail;
380            wrapped = 1;
381          }
382
383        if (wrapped)
384          {
385            draw_step (&settings, dpy, window, bgc, pos+1, history);
386          }
387
388        XSync (dpy, False);
389        screenhack_handle_events (dpy);
390       
391        if (delay_usecs)
392          usleep (delay_usecs);
393
394      }
395   
396    i_restart = (i_restart + 1) % n_restart;
397
398    if (i_restart == 0)
399      {
400        /* Clean up after completing a simulation. */
401        free_colormap (dpy, &wattr, &colors, n_colors);
402      }
403  }
404}
Note: See TracBrowser for help on using the repository browser.