c63ceb957aadeddd73999e6cf9bbe74a220d6063
[xscreensaver] / hacks / substrate.c
1 /*
2  *  Substrate (dragorn@kismetwireless.net)
3  *  Directly ported code from complexification.net Substrate art
4  *  http://complexification.net/gallery/machines/substrate/applet_s/substrate_s.pde
5  *
6  *  Substrate code:
7  *  j.tarbell   June, 2004
8  *  Albuquerque, New Mexico
9  *  complexification.net
10  *
11  *  CHANGES
12  *
13  *  1.1  dragorn  Jan 04 2005    Fixed some indenting, typo in errors for parsing
14  *                                cmdline args
15  *  1.1  dagraz   Jan 04 2005    Added option for circular cracks (David Agraz)
16  *                               Cleaned up issues with timeouts in start_crack (DA)
17  *  1.0  dragorn  Oct 10 2004    First port done
18  *
19  * Directly based the hacks of: 
20  * 
21  * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
22  *
23  * Permission to use, copy, modify, distribute, and sell this software and its
24  * documentation for any purpose is hereby granted without fee, provided that
25  * the above copyright notice appear in all copies and that both that
26  * copyright notice and this permission notice appear in supporting
27  * documentation.  No representations are made about the suitability of this
28  * software for any purpose.  It is provided "as is" without express or 
29  * implied warranty.
30  */
31
32 #include <math.h>
33 #include "screenhack.h"
34
35 /* this program goes faster if some functions are inline.  The following is
36  * borrowed from ifs.c */
37 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
38 #undef inline
39 #define inline                  /* */
40 #endif
41
42 #define STEP 0.42
43
44 /* Raw colormap extracted from pollockEFF.gif */
45 static const char *rgb_colormap[] = {
46     "#201F21", "#262C2E", "#352626", "#372B27",
47     "#302C2E", "#392B2D", "#323229", "#3F3229",
48     "#38322E", "#2E333D", "#333A3D", "#473329",
49     "#40392C", "#40392E", "#47402C", "#47402E",
50     "#4E402C", "#4F402E", "#4E4738", "#584037",
51     "#65472D", "#6D5D3D", "#745530", "#755532",
52     "#745D32", "#746433", "#7C6C36", "#523152",
53     "#444842", "#4C5647", "#655D45", "#6D5D44",
54     "#6C5D4E", "#746C43", "#7C6C42", "#7C6C4B",
55     "#6B734B", "#73734B", "#7B7B4A", "#6B6C55",
56     "#696D5E", "#7B6C5D", "#6B7353", "#6A745D",
57     "#727B52", "#7B7B52", "#57746E", "#687466",
58     "#9C542B", "#9D5432", "#9D5B35", "#936B36",
59     "#AA7330", "#C45A27", "#D95223", "#D85A20",
60     "#DB5A23", "#E57037", "#836C4B", "#8C6B4B",
61     "#82735C", "#937352", "#817B63", "#817B6D",
62     "#927B63", "#D9893B", "#E49832", "#DFA133",
63     "#E5A037", "#F0AB3B", "#8A8A59", "#B29A58",
64     "#89826B", "#9A8262", "#888B7C", "#909A7A",
65     "#A28262", "#A18A69", "#A99968", "#99A160",
66     "#99A168", "#CA8148", "#EB8D43", "#C29160",
67     "#C29168", "#D1A977", "#C9B97F", "#F0E27B",
68     "#9F928B", "#C0B999", "#E6B88F", "#C8C187",
69     "#E0C886", "#F2CC85", "#F5DA83", "#ECDE9D",
70     "#F5D294", "#F5DA94", "#F4E784", "#F4E18A",
71     "#F4E193", "#E7D8A7", "#F1D4A5", "#F1DCA5",
72     "#F4DBAD", "#F1DCAE", "#F4DBB5", "#F5DBBD",
73     "#F4E2AD", "#F5E9AD", "#F4E3BE", "#F5EABE",
74     "#F7F0B6", "#D9D1C1", "#E0D0C0", "#E7D8C0",
75     "#F1DDC6", "#E8E1C0", "#F3EDC7", "#F6ECCE",
76     "#F8F2C7", "#EFEFD0", 0
77 };
78
79 typedef struct {
80     /* Synthesis of data from Crack:: and SandPainter:: */
81     float x, y;
82     float t;
83     float ys, xs, t_inc; /* for curvature calculations */
84
85     int curved;
86
87     unsigned long sandcolor;
88     float sandp, sandg;
89
90     float degrees_drawn;
91
92     int crack_num;
93
94 } crack;
95
96 struct field {
97     unsigned int height;
98     unsigned int width;
99
100     unsigned int initial_cracks;
101     
102     unsigned int num;
103     unsigned int max_num;
104
105     int grains; /* number of grains in the sand painting */
106
107     int circle_percent;
108
109     crack *cracks; /* grid of cracks */
110     int *cgrid; /* grid of actual crack placement */
111
112     /* Raw map of pixels we need to keep for alpha blending */
113     unsigned long int *off_img;
114    
115     /* color parms */
116     int numcolors;
117     unsigned long *parsedcolors;
118     unsigned long fgcolor;
119     unsigned long bgcolor;
120     int visdepth;
121
122     unsigned int cycles;
123
124     unsigned int wireframe;
125     unsigned int seamless;
126 };
127
128 struct state {
129   Display *dpy;
130   Window window;
131
132   struct field *f;
133   unsigned int max_cycles;
134   int growth_delay;
135   GC fgc;
136   XWindowAttributes xgwa;
137   XGCValues gcv;
138 };
139
140
141 static void 
142 *xrealloc(void *p, size_t size)
143 {
144     void *ret;
145     if ((ret = realloc(p, size)) == NULL) {
146         fprintf(stderr, "%s: out of memory\n", progname);
147         exit(1);
148     }
149     return ret;
150 }
151
152 static struct field 
153 *init_field(void)
154 {
155     struct field *f = xrealloc(NULL, sizeof(struct field));
156     f->height = 0;
157     f->width = 0;
158     f->initial_cracks = 0;
159     f->num = 0;
160     f->max_num = 0;
161     f->cracks = NULL;
162     f->cgrid = NULL;
163     f->off_img = NULL;
164     f->numcolors = 0;
165     f->parsedcolors = NULL;
166     f->cycles = 0;
167     f->wireframe = 0;
168     f->seamless = 0;
169     f->fgcolor = 0;
170     f->bgcolor = 0;
171     f->visdepth = 0;
172     f->grains = 0;
173     f->circle_percent = 0;
174     return f;
175 }
176
177 /* Quick references to pixels in the offscreen map and in the crack grid */
178 #define ref_pixel(f, x, y)   ((f)->off_img[(y) * (f)->width + (x)])
179 #define ref_cgrid(f, x, y)   ((f)->cgrid[(y) * (f)->width + (x)])
180
181 static inline void start_crack(struct field *f, crack *cr) 
182 {
183     /* synthesis of Crack::findStart() and crack::startCrack() */
184     int px = 0;
185     int py = 0;
186     int found = 0;
187     int timeout = 0;
188     float a;
189
190     /* shift until crack is found */
191     while ((!found) && (timeout++ < 10000)) {
192         px = (int) (random() % f->width);
193         py = (int) (random() % f->height);
194
195         if (ref_cgrid(f, px, py) < 10000)
196             found = 1;
197     }
198
199     if ( !found ) {
200         /* We timed out.  Use our default values */
201         px = cr->x;
202         py = cr->y;
203
204         /* Sanity check needed */
205         if (px < 0) px = 0;
206         if (px >= f->width) px = f->width - 1;
207         if (py < 0) py = 0;
208         if (py >= f->height) py = f->height - 1;
209
210         ref_cgrid(f, px, py) = cr->t;
211     }
212
213     /* start a crack */
214     a = ref_cgrid(f, px, py);
215
216     if ((random() % 100) < 50) {
217         /* conversion of the java int(random(-2, 2.1)) */
218         a -= 90 + (frand(4.1) - 2.0);
219     } else {
220         a += 90 + (frand(4.1) - 2.0);
221     }
222
223     if ((random() % 100) < f->circle_percent) {
224         float r; /* radius */
225         float radian_inc;
226
227         cr->curved = 1;
228         cr->degrees_drawn = 0;
229
230         r = 10 + (random() % ((f->width + f->height) / 2));
231
232         if ((random() % 100) < 50) {
233             r *= -1;
234         }
235
236         /* arc length = r * theta => theta = arc length / r */
237         radian_inc = STEP / r;
238         cr->t_inc = radian_inc * 360 / 2 / M_PI;
239
240         cr->ys = r * sin(radian_inc);
241         cr->xs = r * ( 1 - cos(radian_inc));
242
243     }
244     else {
245         cr->curved = 0;
246     }
247
248     /* Condensed from Crack::startCrack */
249     cr->x = px + ((float) 0.61 * cos(a * M_PI / 180));
250     cr->y = py + ((float) 0.61 * sin(a * M_PI / 180));
251     cr->t = a;
252
253 }
254
255 static inline void make_crack(struct field *f) 
256 {
257     crack *cr;
258
259     if (f->num < f->max_num) {
260         /* make a new crack */
261         f->cracks = (crack *) xrealloc(f->cracks, sizeof(crack) * (f->num + 1));
262
263         cr = &(f->cracks[f->num]);
264         /* assign colors */
265         cr->sandp = 0;
266         cr->sandg = (frand(0.2) - 0.01);
267         cr->sandcolor = f->parsedcolors[random() % f->numcolors];
268         cr->crack_num = f->num;
269         cr->curved = 0;
270         cr->degrees_drawn = 0;
271
272         /* We could use these values in the timeout case of start_crack */
273
274         cr->x = random() % f->width;
275         cr->y = random() % f->height;
276         cr->t = random() % 360;
277
278         /* start it */
279         start_crack(f, cr);
280
281         f->num++;
282     }
283 }
284
285 static inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b) 
286 {
287     switch(depth) {
288         case 32:
289         case 24:
290 #ifdef HAVE_COCOA
291             /* This program idiotically does not go through a color map, so
292                we have to hardcode in knowledge of how jwxyz.a packs pixels!
293                Fix it to go through st->colors[st->ncolors] instead!
294              */
295             *r = (c & 0x00ff0000) >> 16; 
296             *g = (c & 0x0000ffff) >>  8;
297             *b = (c & 0x000000ff); 
298 #else
299             *g = (c & 0xff00) >> 8; 
300             *r = (c & 0xff0000) >> 16; 
301             *b = c & 0xff; 
302 #endif
303             break;
304         case 16:
305             *g = ((c >> 5) & 0x3f) << 2;
306             *r = ((c >> 11) & 0x1f) << 3; 
307             *b = (c & 0x1f) << 3; 
308             break;
309         case 15:
310             *g = ((c >> 5) & 0x1f) << 3;
311             *r = ((c >> 10) & 0x1f) << 3;
312             *b = (c & 0x1f) << 3;
313             break;
314     }
315 }
316
317 static inline unsigned long rgb2point(int depth, int r, int g, int b) 
318 {
319     unsigned long ret = 0;
320
321     switch(depth) {
322         case 32:
323         case 24:
324 #ifdef HAVE_COCOA
325             /* This program idiotically does not go through a color map, so
326                we have to hardcode in knowledge of how jwxyz.a packs pixels!
327                Fix it to go through st->colors[st->ncolors] instead!
328              */
329             ret = 0xFF000000 | (r << 16) | (g << 8) | b;
330 #else
331             ret |= (r << 16) | (g << 8) | b;
332 #endif
333             break;
334         case 16:
335             ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
336             break;
337         case 15:
338             ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
339             break;
340     }
341
342     return ret;
343 }
344
345 /* alpha blended point drawing -- this is Not Right and will likely fail on 
346  * non-intel platforms as it is now, needs fixing */
347 static inline unsigned long
348 trans_point(struct state *st,
349             int x1, int y1, unsigned long myc, float a, 
350             struct field *f) 
351 {
352     if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
353         if (a >= 1.0) {
354             ref_pixel(f, x1, y1) = myc;
355         } else {
356             int or = 0, og = 0, ob = 0;
357             int r = 0, g = 0, b = 0;
358             int nr, ng, nb;
359             unsigned long c;
360
361             c = ref_pixel(f, x1, y1);
362
363             point2rgb(f->visdepth, c, &or, &og, &ob);
364             point2rgb(f->visdepth, myc, &r, &g, &b);
365
366             nr = or + (r - or) * a;
367             ng = og + (g - og) * a;
368             nb = ob + (b - ob) * a;
369
370             c = rgb2point(f->visdepth, nr, ng, nb);
371
372             ref_pixel(f, x1, y1) = c;
373
374             return c;
375         }
376     }
377
378     return f->bgcolor;
379 }
380
381 static inline void 
382 region_color(struct state *st, GC fgc, struct field *f, crack *cr) 
383 {
384     /* synthesis of Crack::regionColor() and SandPainter::render() */
385
386     float rx = cr->x;
387     float ry = cr->y;
388     int openspace = 1;
389     int cx, cy;
390     float maxg;
391     int grains, i;
392     float w;
393     float drawx, drawy;
394     unsigned long c;
395
396     while (openspace) {
397         /* move perpendicular to crack */
398         rx += (0.81 * sin(cr->t * M_PI/180));
399         ry -= (0.81 * cos(cr->t * M_PI/180));
400
401         cx = (int) rx;
402         cy = (int) ry;
403         if (f->seamless) {
404             cx %= f->width;
405             cy %= f->height;
406         }
407
408         if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
409             /* safe to check */
410             if (f->cgrid[cy * f->width + cx] > 10000) {
411                 /* space is open */
412             } else {
413                 openspace = 0;
414             }
415         } else {
416             openspace = 0;
417         }
418     }
419
420     /* SandPainter stuff here */
421
422     /* Modulate gain */
423     cr->sandg += (frand(0.1) - 0.050);
424     maxg = 1.0;
425
426     if (cr->sandg < 0)
427         cr->sandg = 0;
428
429     if (cr->sandg > maxg)
430         cr->sandg = maxg;
431
432     grains = f->grains;
433
434     /* Lay down grains of sand */
435     w = cr->sandg / (grains - 1);
436
437     for (i = 0; i < grains; i++) {
438         drawx = (cr->x + (rx - cr->x) * sin(cr->sandp + sin((float) i * w)));
439         drawy = (cr->y + (ry - cr->y) * sin(cr->sandp + sin((float) i * w)));
440         if (f->seamless) {
441             drawx = fmod(drawx + f->width, f->width);
442             drawy = fmod(drawy + f->height, f->height);
443         }
444
445         /* Draw sand bit */
446         c = trans_point(st, drawx, drawy, cr->sandcolor, (0.1 - i / (grains * 10.0)), f);
447
448         XSetForeground(st->dpy, fgc, c);
449         XDrawPoint(st->dpy, st->window, fgc, (int) drawx, (int) drawy);
450         XSetForeground(st->dpy, fgc, f->fgcolor);
451     }
452 }
453
454 static void build_substrate(struct field *f) 
455 {
456     int tx;
457     /* int ty; */
458
459     f->cycles = 0;
460
461     if (f->cgrid) {
462         free(f->cgrid);
463         f->cgrid = NULL;
464     }
465
466     if (f->cracks) {
467         free(f->cracks);
468         f->cracks = NULL;
469     }
470
471     f->num = 0;
472
473     /* erase the crack grid */
474     f->cgrid = (int *) xrealloc(f->cgrid, sizeof(int) * f->height * f->width);
475     {
476         int j;
477         int *p = f->cgrid;
478         for (j = 0; j < f->height * f->width; j++)
479             *p++ = 10001;
480     }
481
482     /* Not necessary now that make_crack ensures we have usable default
483      *  values in start_crack's timeout case 
484     * make random crack seeds *
485     for (tx = 0; tx < 16; tx++) {
486         ty = (int) (random() % (f->width * f->height - 1));
487         f->cgrid[ty] = (int) random() % 360;
488     }
489     */
490
491     /* make the initial cracks */
492     for (tx = 0; tx < f->initial_cracks; tx++)
493         make_crack(f);
494 }
495
496
497 static inline void
498 movedrawcrack(struct state *st, GC fgc, struct field *f, int cracknum) 
499 {
500     /* Basically Crack::move() */
501
502     int cx, cy;
503     crack *cr = &(f->cracks[cracknum]);
504
505     /* continue cracking */
506     if ( !cr->curved ) {
507         cr->x += ((float) STEP * cos(cr->t * M_PI/180));
508         cr->y += ((float) STEP * sin(cr->t * M_PI/180));
509     }
510     else {
511         cr->x += ((float) cr->ys * cos(cr->t * M_PI/180));
512         cr->y += ((float) cr->ys * sin(cr->t * M_PI/180));
513
514         cr->x += ((float) cr->xs * cos(cr->t * M_PI/180 - M_PI / 2));
515         cr->y += ((float) cr->xs * sin(cr->t * M_PI/180 - M_PI / 2));
516
517         cr->t += cr->t_inc;
518         cr->degrees_drawn += abs(cr->t_inc);
519     }
520     if (f->seamless) {
521         cr->x = fmod(cr->x + f->width, f->width);
522         cr->y = fmod(cr->y + f->height, f->height);
523     }
524
525     /* bounds check */
526     /* modification of random(-0.33,0.33) */
527     cx = (int) (cr->x + (frand(0.66) - 0.33));
528     cy = (int) (cr->y + (frand(0.66) - 0.33));
529     if (f->seamless) {
530         cx %= f->width;
531         cy %= f->height;
532     }
533
534     if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
535         /* draw sand painter if we're not wireframe */
536         if (!f->wireframe)
537             region_color(st, fgc, f, cr);
538
539         /* draw fgcolor crack */
540         ref_pixel(f, cx, cy) = f->fgcolor;
541         XDrawPoint(st->dpy, st->window, fgc, cx, cy);
542
543         if ( cr->curved && (cr->degrees_drawn > 360) ) {
544             /* completed the circle, stop cracking */
545             start_crack(f, cr); /* restart ourselves */
546             make_crack(f); /* generate a new crack */
547         }
548         /* safe to check */
549         else if ((f->cgrid[cy * f->width + cx] > 10000) ||
550                  (abs(f->cgrid[cy * f->width + cx] - cr->t) < 5)) {
551             /* continue cracking */
552             f->cgrid[cy * f->width + cx] = (int) cr->t;
553         } else if (abs(f->cgrid[cy * f->width + cx] - cr->t) > 2) {
554             /* crack encountered (not self), stop cracking */
555             start_crack(f, cr); /* restart ourselves */
556             make_crack(f); /* generate a new crack */
557         }
558     } else {
559         /* out of bounds, stop cracking */
560
561         /* need these in case of timeout in start_crack */
562         cr->x = random() % f->width;
563         cr->y = random() % f->height;
564         cr->t = random() % 360;
565
566         start_crack(f, cr); /* restart ourselves */
567         make_crack(f); /* generate a new crack */
568     }
569
570 }
571
572
573 static void build_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, 
574                struct field *f) 
575 {
576     if (f->off_img) {
577         free(f->off_img);
578         f->off_img = NULL;
579     }
580
581     f->off_img = (unsigned long *) xrealloc(f->off_img, sizeof(unsigned long) * 
582                                             f->width * f->height);
583
584     memset(f->off_img, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
585 }
586
587
588 static void *
589 substrate_init (Display *dpy, Window window)
590 {
591     struct state *st = (struct state *) calloc (1, sizeof(*st));
592     XColor tmpcolor;
593
594     st->dpy = dpy;
595     st->window = window;
596     st->f = init_field();
597
598     st->growth_delay = (get_integer_resource(st->dpy, "growthDelay", "Integer"));
599     st->max_cycles = (get_integer_resource(st->dpy, "maxCycles", "Integer"));
600     st->f->initial_cracks = (get_integer_resource(st->dpy, "initialCracks", "Integer"));
601     st->f->max_num = (get_integer_resource(st->dpy, "maxCracks", "Integer"));
602     st->f->wireframe = (get_boolean_resource(st->dpy, "wireFrame", "Boolean"));
603     st->f->grains = (get_integer_resource(st->dpy, "sandGrains", "Integer"));
604     st->f->circle_percent = (get_integer_resource(st->dpy, "circlePercent", "Integer"));
605     st->f->seamless = (get_boolean_resource(st->dpy, "seamless", "Boolean"));
606
607     if (st->f->initial_cracks <= 2) {
608         fprintf(stderr, "%s: Initial cracks must be greater than 2\n", progname);
609         exit (1);
610     }
611
612     if (st->f->max_num <= 10) {
613         fprintf(stderr, "%s: Maximum number of cracks must be less than 10\n", 
614                 progname);
615         exit (1);
616     }
617
618     if (st->f->circle_percent < 0) {
619         fprintf(stderr, "%s: circle percent must be at least 0\n", progname);
620         exit (1);
621     }
622
623     if (st->f->circle_percent > 100) {
624         fprintf(stderr, "%s: circle percent must be less than 100\n", progname);
625         exit (1);
626     }
627     
628     XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
629
630     st->f->height = st->xgwa.height;
631     st->f->width = st->xgwa.width;
632     st->f->visdepth = st->xgwa.depth;
633  
634     /* Count the colors in our map and assign them in a horrifically inefficient 
635      * manner but it only happens once */
636     while (rgb_colormap[st->f->numcolors] != NULL) {
637         st->f->parsedcolors = (unsigned long *) xrealloc(st->f->parsedcolors, 
638                                                      sizeof(unsigned long) * 
639                                                      (st->f->numcolors + 1));
640         if (!XParseColor(st->dpy, st->xgwa.colormap, rgb_colormap[st->f->numcolors], &tmpcolor)) {
641             fprintf(stderr, "%s: couldn't parse color %s\n", progname,
642                     rgb_colormap[st->f->numcolors]);
643             exit(1);
644         }
645
646         if (!XAllocColor(st->dpy, st->xgwa.colormap, &tmpcolor)) {
647             fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
648                     rgb_colormap[st->f->numcolors]);
649             exit(1);
650         }
651
652         st->f->parsedcolors[st->f->numcolors] = tmpcolor.pixel;
653
654         st->f->numcolors++;
655     }
656
657     st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
658                                         "foreground", "Foreground");
659     st->gcv.background = get_pixel_resource(st->dpy, st->xgwa.colormap,
660                                         "background", "Background");
661     st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
662
663     st->f->fgcolor = st->gcv.foreground;
664     st->f->bgcolor = st->gcv.background;
665
666     /* Initialize stuff */
667     build_img(st->dpy, st->window, st->xgwa, st->fgc, st->f);
668     build_substrate(st->f);
669     
670     return st;
671 }
672
673 static unsigned long
674 substrate_draw (Display *dpy, Window window, void *closure)
675 {
676   struct state *st = (struct state *) closure;
677   int tempx;
678
679   if ((st->f->cycles % 10) == 0) {
680
681     /* Restart if the window size changes */
682     XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
683
684     if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width) {
685       st->f->height = st->xgwa.height;
686       st->f->width = st->xgwa.width;
687       st->f->visdepth = st->xgwa.depth;
688
689       build_substrate(st->f);
690       build_img(st->dpy, st->window, st->xgwa, st->fgc, st->f);
691       XSetForeground(st->dpy, st->fgc, st->gcv.background);
692       XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
693       XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
694     }
695   }
696
697   for (tempx = 0; tempx < st->f->num; tempx++) {
698     movedrawcrack(st, st->fgc, st->f, tempx);
699   }
700
701   st->f->cycles++;
702
703   if (st->f->cycles >= st->max_cycles && st->max_cycles != 0) {
704     build_substrate(st->f);
705     build_img(st->dpy, st->window, st->xgwa, st->fgc, st->f);
706     XSetForeground(st->dpy, st->fgc, st->gcv.background);
707     XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
708     XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
709   }
710
711   /* #### mi->recursion_depth = st->f->cycles; */
712   return st->growth_delay;
713 }
714
715
716 static void
717 substrate_reshape (Display *dpy, Window window, void *closure, 
718                  unsigned int w, unsigned int h)
719 {
720 }
721
722 static Bool
723 substrate_event (Display *dpy, Window window, void *closure, XEvent *event)
724 {
725   struct state *st = (struct state *) closure;
726   if (screenhack_event_helper (dpy, window, event))
727     {
728       st->f->cycles = st->max_cycles;
729       return True;
730     }
731   return False;
732 }
733
734 static void
735 substrate_free (Display *dpy, Window window, void *closure)
736 {
737   struct state *st = (struct state *) closure;
738   free (st);
739 }
740
741 static const char *substrate_defaults[] = {
742     ".background: white",
743     ".foreground: black",
744     "*fpsSolid: true",
745     "*wireFrame: false",
746     "*seamless: false",
747     "*maxCycles: 10000",
748     "*growthDelay: 18000",
749     "*initialCracks: 3",
750     "*maxCracks: 100",
751     "*sandGrains: 64",
752     "*circlePercent: 33",
753 #ifdef USE_IPHONE
754   "*ignoreRotation: True",
755 #endif
756     0
757 };
758
759 static XrmOptionDescRec substrate_options[] = {
760     {"-background", ".background", XrmoptionSepArg, 0},
761     {"-foreground", ".foreground", XrmoptionSepArg, 0},
762     {"-wireframe", ".wireFrame", XrmoptionNoArg, "true"},
763     {"-seamless", ".seamless", XrmoptionNoArg, "true"},
764     {"-max-cycles", ".maxCycles", XrmoptionSepArg, 0},
765     {"-growth-delay", ".growthDelay", XrmoptionSepArg, 0},
766     {"-initial-cracks", ".initialCracks", XrmoptionSepArg, 0},
767     {"-max-cracks", ".maxCracks", XrmoptionSepArg, 0},
768     {"-sand-grains", ".sandGrains", XrmoptionSepArg, 0},
769     {"-circle-percent", ".circlePercent", XrmoptionSepArg, 0},
770     {0, 0, 0, 0}
771 };
772
773 XSCREENSAVER_MODULE ("Substrate", substrate)