http://ftp.x.org/contrib/applications/xscreensaver-3.10.tar.gz
[xscreensaver] / hacks / critical.c
1 /* critical -- Self-organizing-criticality display hack for XScreenSaver
2  * Copyright (C) 1998, 1999 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  */
17
18 #include "screenhack.h"
19 #include "erase.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 char *progclass = "Critical";
25
26
27 typedef struct {
28   int width, height;            /* in cells */
29   unsigned short *cells;
30 } CriticalModel;
31
32
33 CriticalModel * model_allocate (int w, int h);
34 void model_initialize (CriticalModel *model);
35 static void model_step (CriticalModel *model, int *top_x, int *top_y);
36
37
38 /* Options this module understands.  */
39 XrmOptionDescRec options[] = {
40   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
41   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
42   { "-colorscheme",     ".colorscheme", XrmoptionSepArg, 0 },
43   { "-restart",         ".restart",     XrmoptionSepArg, 0 },
44   { "-cellsize",        ".cellsize",    XrmoptionSepArg, 0 },
45   { "-batchcount",      ".batchcount",  XrmoptionSepArg, 0 },
46   { 0, 0, 0, 0 }                /* end */
47 };
48
49
50 /* Default xrm resources. */
51 char *defaults[] = {
52   ".background:                 black",
53   "*colorscheme:                smooth",
54   "*delay:                      10000", 
55   "*ncolors:                    64",
56   "*restart:                    8",
57   "*cellsize:                   9",
58   "*batchcount:                 1500",
59   0                             /* end */
60 };
61
62
63 /* Allocate an return a new simulation model datastructure.
64  */
65
66 CriticalModel *
67 model_allocate (int model_w, int model_h)
68 {
69   CriticalModel         *model;
70
71   model = malloc (sizeof (CriticalModel));
72   if (!model)
73     return 0;
74
75   model->width = model_w;
76   model->height = model_h;
77
78   model->cells = malloc (sizeof (int) * model_w * model_h);
79   if (!model->cells)
80     return 0;
81
82   return model;
83 }
84
85
86
87 /* Initialize the data model underlying the hack.
88
89    For the self-organizing criticality hack, this consists of a 2d
90    array full of random integers.
91
92    I've considered storing the data as (say) a binary tree within a 2d
93    array, to make finding the highest value faster at the expense of
94    storage space: searching the whole array on each iteration seems a
95    little inefficient.  However, the screensaver doesn't seem to take
96    up many cycles as it is: presumably the search is pretty quick
97    compared to the sleeps.  The current version uses less than 1% of
98    the CPU time of an AMD K6-233.  Many machines running X11 at this
99    point in time seem to be memory-limited, not CPU-limited.
100
101    The root of all evil, and all that.
102 */
103
104
105 void
106 model_initialize (CriticalModel *model)
107 {
108   int i;
109   
110   for (i = model->width * model->height; i >= 0; i--)
111     {
112       model->cells[i] = (unsigned short) random ();
113     }
114 }
115
116
117 /* Move one step forward in the criticality simulation.
118
119    This function locates and returns in (TOP_X, TOP_Y) the location of
120    the highest-valued cell in the model.  It also replaces that cell
121    and it's eight nearest neighbours with new random values.
122    Neighbours that fall off the edge of the model are simply
123    ignored. */
124 static void
125 model_step (CriticalModel *model, int *top_x, int *top_y)
126 {
127   int                   x, y, i;
128   int                   dx, dy;
129   unsigned short        top_value;
130
131   /* Find the top cell */
132   top_value = 0;
133   i = 0;
134   for (y = 0; y < model->height; y++)
135     for (x = 0; x < model->width; x++)
136       {
137         if (model->cells[i] >= top_value)
138           {
139             top_value = model->cells[i];
140             *top_x = x;
141             *top_y = y;
142           }
143         i++;
144       }
145
146   /* Replace it and its neighbours with new random values */
147   for (dy = -1; dy <= 1; dy++)
148     {
149       int y = *top_y + dy;
150       if (y < 0  ||  y >= model->height)
151         continue;
152       
153       for (dx = -1; dx <= 1; dx++)
154         {
155           int x = *top_x + dx;
156           if (x < 0  ||  x >= model->width)
157             continue;
158           
159           model->cells[y * model->width + x] = (unsigned short) random();
160         }
161     }
162 }
163
164
165 /* Construct and return in COLORS and N_COLORS a new set of colors,
166    depending on the resource settings.  */
167 void
168 setup_colormap (Display *dpy, XWindowAttributes *wattr,
169                 XColor **colors,
170                 int *n_colors)
171 {
172   Bool                  writable;
173   char const *          color_scheme;
174
175   /* Make a colormap */
176   *n_colors = get_integer_resource ("ncolors", "Integer");
177   if (*n_colors < 2)
178     *n_colors = 2;
179   
180   *colors = (XColor *) calloc (sizeof(XColor), *n_colors);
181   if (!*colors)
182     {
183       fprintf (stderr, "%s:%d: can't allocate memory for colors\n",
184                __FILE__, __LINE__);
185       return;
186     }
187
188   writable = False;
189   color_scheme = get_string_resource ("colorscheme", "ColorScheme");
190   
191   if (!strcmp (color_scheme, "random"))
192     {
193       make_random_colormap (dpy, wattr->visual,
194                             wattr->colormap,
195                             *colors, n_colors,
196                             True, True, &writable, True);
197     }
198   else if (!strcmp (color_scheme, "smooth"))
199     {
200       make_smooth_colormap (dpy, wattr->visual,
201                             wattr->colormap,
202                             *colors, n_colors,
203                             True, &writable, True);
204     }
205   else 
206     {
207       make_uniform_colormap (dpy, wattr->visual,
208                              wattr->colormap,
209                              *colors, n_colors, True,
210                              &writable, True);
211     }
212 }
213
214
215
216 /* Display a self-organizing criticality screen hack.  The program
217    runs indefinately on the root window. */
218 void
219 screenhack (Display *dpy, Window window)
220 {
221   GC                    fgc, bgc;
222   XGCValues             gcv;
223   XWindowAttributes     wattr;
224   int                   n_colors;
225   XColor                *colors;
226   int                   model_w, model_h;
227   CriticalModel         *model;
228   int                   lines_per_color = 10;
229   int                   i_color = 0;
230   int                   x1, y1, x2, y2;
231   long                  delay_usecs;
232   int                   cell_size;
233   int                   batchcount;
234
235   /* Number of screens that should be drawn before reinitializing the
236      model, and count of the number of screens done so far. */
237   int                   n_restart, i_restart;
238
239   /* Find window attributes */
240   XGetWindowAttributes (dpy, window, &wattr);
241
242   /* Construct the initial model state. */
243   cell_size = get_integer_resource ("cellsize", "Integer");
244   if (cell_size < 1)
245     cell_size = 1;
246   if (cell_size >= 100)
247     cell_size = 99;
248
249   batchcount = get_integer_resource ("batchcount", "Integer");
250   if (batchcount < 5)
251     batchcount = 5;
252   
253   model_w = wattr.width / cell_size;
254   model_h = wattr.height  / cell_size;
255   
256   model = model_allocate (model_w, model_h);
257   if (!model)
258     {
259       fprintf (stderr, "critical: error preparing the model\n");
260       return;
261     }
262   
263   /* make a black gc for the background */
264   gcv.foreground = get_pixel_resource ("background", "Background",
265                                        dpy, wattr.colormap);
266   bgc = XCreateGC (dpy, window, GCForeground, &gcv);
267
268   fgc = XCreateGC (dpy, window, 0, &gcv);
269
270   x2 = rand() % model_w;
271   y2 = rand() % model_h;
272
273   delay_usecs = get_integer_resource ("delay", "Integer");
274   n_restart = get_integer_resource ("restart", "Integer");
275     
276   /* xscreensaver will kill or stop us when the user does something
277    * that deserves attention. */
278   i_restart = 0;
279   
280   while (1) {
281     int i_batch;
282
283     if (!i_restart)
284       {
285         setup_colormap (dpy, &wattr, &colors, &n_colors);
286         model_initialize (model);
287       }
288     
289     for (i_batch = batchcount; i_batch; i_batch--)
290       {
291         /* Set color */
292         if ((i_batch % lines_per_color) == 0)
293           {
294             i_color = (i_color + 1) % n_colors;
295             gcv.foreground = colors[i_color].pixel;
296             XChangeGC (dpy, fgc, GCForeground, &gcv);
297           }
298         
299       /* draw a line */
300       x1 = x2;
301       y1 = y2;
302
303       model_step (model, &x2, &y2);
304
305       XDrawLine (dpy, window, fgc,
306                  x1 * cell_size + cell_size/2,
307                  y1 * cell_size + cell_size/2,
308                  x2 * cell_size + cell_size/2,
309                  y2 * cell_size + cell_size/2);
310
311       XSync (dpy, False); 
312       screenhack_handle_events (dpy);
313
314       if (delay_usecs)
315         usleep (delay_usecs);
316     }
317
318     i_restart = (i_restart + 1) % n_restart;
319     erase_full_window (dpy, window);
320   }
321 }
322
323
324 /*
325  * Local variables:
326  * c-indent-mode: gnu
327  * compile-command "make critical && ./critical"
328  * End:
329  */