http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[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 "screenhack.h"
33 #include <X11/Xutil.h>
34 #include <stdio.h>
35 #include <sys/time.h>
36
37 #ifndef MAX_WIDTH
38 #include <limits.h>
39 #define MAX_WIDTH SHRT_MAX
40 #endif
41
42 #ifdef TIME_ME
43 #include <time.h>
44 #endif
45
46 #include <math.h>
47
48 /* this program goes faster if some functions are inline.  The following is
49  * borrowed from ifs.c */
50 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
51 #undef inline
52 #define inline                  /* */
53 #endif
54
55 #define STEP 0.42
56
57 /* Raw colormap extracted from pollockEFF.gif */
58 char *rgb_colormap[] = {
59     "rgb:20/1F/21", "rgb:26/2C/2E", "rgb:35/26/26", "rgb:37/2B/27",
60     "rgb:30/2C/2E", "rgb:39/2B/2D", "rgb:32/32/29", "rgb:3F/32/29",
61     "rgb:38/32/2E", "rgb:2E/33/3D", "rgb:33/3A/3D", "rgb:47/33/29",
62     "rgb:40/39/2C", "rgb:40/39/2E", "rgb:47/40/2C", "rgb:47/40/2E",
63     "rgb:4E/40/2C", "rgb:4F/40/2E", "rgb:4E/47/38", "rgb:58/40/37",
64     "rgb:65/47/2D", "rgb:6D/5D/3D", "rgb:74/55/30", "rgb:75/55/32",
65     "rgb:74/5D/32", "rgb:74/64/33", "rgb:7C/6C/36", "rgb:52/31/52",
66     "rgb:44/48/42", "rgb:4C/56/47", "rgb:65/5D/45", "rgb:6D/5D/44",
67     "rgb:6C/5D/4E", "rgb:74/6C/43", "rgb:7C/6C/42", "rgb:7C/6C/4B",
68     "rgb:6B/73/4B", "rgb:73/73/4B", "rgb:7B/7B/4A", "rgb:6B/6C/55",
69     "rgb:69/6D/5E", "rgb:7B/6C/5D", "rgb:6B/73/53", "rgb:6A/74/5D",
70     "rgb:72/7B/52", "rgb:7B/7B/52", "rgb:57/74/6E", "rgb:68/74/66",
71     "rgb:9C/54/2B", "rgb:9D/54/32", "rgb:9D/5B/35", "rgb:93/6B/36",
72     "rgb:AA/73/30", "rgb:C4/5A/27", "rgb:D9/52/23", "rgb:D8/5A/20",
73     "rgb:DB/5A/23", "rgb:E5/70/37", "rgb:83/6C/4B", "rgb:8C/6B/4B",
74     "rgb:82/73/5C", "rgb:93/73/52", "rgb:81/7B/63", "rgb:81/7B/6D",
75     "rgb:92/7B/63", "rgb:D9/89/3B", "rgb:E4/98/32", "rgb:DF/A1/33",
76     "rgb:E5/A0/37", "rgb:F0/AB/3B", "rgb:8A/8A/59", "rgb:B2/9A/58",
77     "rgb:89/82/6B", "rgb:9A/82/62", "rgb:88/8B/7C", "rgb:90/9A/7A",
78     "rgb:A2/82/62", "rgb:A1/8A/69", "rgb:A9/99/68", "rgb:99/A1/60",
79     "rgb:99/A1/68", "rgb:CA/81/48", "rgb:EB/8D/43", "rgb:C2/91/60",
80     "rgb:C2/91/68", "rgb:D1/A9/77", "rgb:C9/B9/7F", "rgb:F0/E2/7B",
81     "rgb:9F/92/8B", "rgb:C0/B9/99", "rgb:E6/B8/8F", "rgb:C8/C1/87",
82     "rgb:E0/C8/86", "rgb:F2/CC/85", "rgb:F5/DA/83", "rgb:EC/DE/9D",
83     "rgb:F5/D2/94", "rgb:F5/DA/94", "rgb:F4/E7/84", "rgb:F4/E1/8A",
84     "rgb:F4/E1/93", "rgb:E7/D8/A7", "rgb:F1/D4/A5", "rgb:F1/DC/A5",
85     "rgb:F4/DB/AD", "rgb:F1/DC/AE", "rgb:F4/DB/B5", "rgb:F5/DB/BD",
86     "rgb:F4/E2/AD", "rgb:F5/E9/AD", "rgb:F4/E3/BE", "rgb:F5/EA/BE",
87     "rgb:F7/F0/B6", "rgb:D9/D1/C1", "rgb:E0/D0/C0", "rgb:E7/D8/C0",
88     "rgb:F1/DD/C6", "rgb:E8/E1/C0", "rgb:F3/ED/C7", "rgb:F6/EC/CE",
89     "rgb:F8/F2/C7", "rgb:EF/EF/D0", 0
90 };
91
92 typedef struct {
93     /* Synthesis of data from Crack:: and SandPainter:: */
94     float x, y;
95     float t;
96     float ys, xs, t_inc; /* for curvature calculations */
97
98     int curved;
99
100     unsigned long sandcolor;
101     float sandp, sandg;
102
103     float degrees_drawn;
104
105     int crack_num;
106
107 } crack;
108
109 struct field {
110     unsigned int height;
111     unsigned int width;
112
113     unsigned int initial_cracks;
114     
115     unsigned int num;
116     unsigned int max_num;
117
118     int grains; /* number of grains in the sand painting */
119
120     int circle_percent;
121
122     crack *cracks; /* grid of cracks */
123     int *cgrid; /* grid of actual crack placement */
124
125     /* Raw map of pixels we need to keep for alpha blending */
126     unsigned long int *off_img;
127    
128     /* color parms */
129     int numcolors;
130     unsigned long *parsedcolors;
131     unsigned long fgcolor;
132     unsigned long bgcolor;
133     int visdepth;
134
135     unsigned int cycles;
136
137     unsigned int wireframe;
138 };
139
140 static void 
141 *xrealloc(void *p, size_t size)
142 {
143     void *ret;
144     if ((ret = realloc(p, size)) == NULL) {
145         fprintf(stderr, "%s: out of memory\n", progname);
146         exit(1);
147     }
148     return ret;
149 }
150
151 struct field 
152 *init_field(void)
153 {
154     struct field *f = xrealloc(NULL, sizeof(struct field));
155     f->height = 0;
156     f->width = 0;
157     f->initial_cracks = 0;
158     f->num = 0;
159     f->max_num = 0;
160     f->cracks = NULL;
161     f->cgrid = NULL;
162     f->off_img = NULL;
163     f->numcolors = 0;
164     f->parsedcolors = NULL;
165     f->cycles = 0;
166     f->wireframe = 0;
167     f->fgcolor = 0;
168     f->bgcolor = 0;
169     f->visdepth = 0;
170     f->grains = 0;
171     f->circle_percent = 0;
172     return f;
173 }
174
175 /* Quick references to pixels in the offscreen map and in the crack grid */
176 #define ref_pixel(f, x, y)   ((f)->off_img[(y) * (f)->width + (x)])
177 #define ref_cgrid(f, x, y)   ((f)->cgrid[(y) * (f)->width + (x)])
178
179 inline void start_crack(struct field *f, crack *cr) {
180     /* synthesis of Crack::findStart() and crack::startCrack() */
181     int px = 0;
182     int py = 0;
183     int found = 0;
184     int timeout = 0;
185     float a;
186
187     /* shift until crack is found */
188     while ((!found) && (timeout++ < 10000)) {
189         px = (int) (random() % f->width);
190         py = (int) (random() % f->height);
191
192         if (ref_cgrid(f, px, py) < 10000)
193             found = 1;
194     }
195
196     if ( !found ) {
197         /* We timed out.  Use our default values */
198         px = cr->x;
199         py = cr->y;
200         ref_cgrid(f, px, py) = cr->t;
201     }
202
203     /* start a crack */
204     a = ref_cgrid(f, px, py);
205
206     if ((random() % 100) < 50) {
207         /* conversion of the java int(random(-2, 2.1)) */
208         a -= 90 + (frand(4.1) - 2.0);
209     } else {
210         a += 90 + (frand(4.1) - 2.0);
211     }
212
213     if ((random() % 100) < f->circle_percent) {
214         float r; /* radius */
215         float radian_inc;
216
217         cr->curved = 1;
218         cr->degrees_drawn = 0;
219
220         r = 10 + (random() % ((f->width + f->height) / 2));
221
222         if ((random() % 100) < 50) {
223             r *= -1;
224         }
225
226         /* arc length = r * theta => theta = arc length / r */
227         radian_inc = STEP / r;
228         cr->t_inc = radian_inc * 360 / 2 / M_PI;
229
230         cr->ys = r * sin(radian_inc);
231         cr->xs = r * ( 1 - cos(radian_inc));
232
233     }
234     else {
235         cr->curved = 0;
236     }
237
238     /* Condensed from Crack::startCrack */
239     cr->x = px + ((float) 0.61 * cos(a * M_PI / 180));
240     cr->y = py + ((float) 0.61 * sin(a * M_PI / 180));
241     cr->t = a;
242
243 }
244
245 inline void make_crack(struct field *f) {
246     crack *cr;
247
248     if (f->num < f->max_num) {
249         /* make a new crack */
250         f->cracks = (crack *) xrealloc(f->cracks, sizeof(crack) * (f->num + 1));
251
252         cr = &(f->cracks[f->num]);
253         /* assign colors */
254         cr->sandp = 0;
255         cr->sandg = (frand(0.2) - 0.01);
256         cr->sandcolor = f->parsedcolors[random() % f->numcolors];
257         cr->crack_num = f->num;
258         cr->curved = 0;
259         cr->degrees_drawn = 0;
260
261         /* We could use these values in the timeout case of start_crack */
262
263         cr->x = random() % f->width;
264         cr->y = random() % f->height;
265         cr->t = random() % 360;
266
267         /* start it */
268         start_crack(f, cr);
269
270         f->num++;
271     }
272 }
273
274 inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b) {
275     switch(depth) {
276         case 32:
277         case 24:
278             *g = (c & 0xff00) >> 8; 
279             *r = (c & 0xff0000) >> 16; 
280             *b = c & 0xff; 
281             break;
282         case 16:
283             *g = ((c >> 5) & 0x3f) << 2;
284             *r = ((c >> 11) & 0x1f) << 3; 
285             *b = (c & 0x1f) << 3; 
286             break;
287         case 15:
288             *g = ((c >> 5) & 0x1f) << 3;
289             *r = ((c >> 10) & 0x1f) << 3;
290             *b = (c & 0x1f) << 3;
291             break;
292     }
293 }
294
295 inline unsigned long rgb2point(int depth, int r, int g, int b) {
296     unsigned long ret = 0;
297
298     switch(depth) {
299         case 32:
300             ret = 0xff000000;
301         case 24:
302             ret |= (r << 16) | (g << 8) | b;
303             break;
304         case 16:
305             ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
306             break;
307         case 15:
308             ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
309             break;
310     }
311
312     return ret;
313 }
314
315 /* alpha blended point drawing -- this is Not Right and will likely fail on 
316  * non-intel platforms as it is now, needs fixing */
317 inline unsigned long trans_point(int x1, int y1, unsigned long myc, float a, 
318                                  struct field *f) {
319     if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
320         if (a >= 1.0) {
321             ref_pixel(f, x1, y1) = myc;
322         } else {
323             int or, og, ob;
324             int r, g, b;
325             int nr, ng, nb;
326             unsigned long c;
327
328             c = ref_pixel(f, x1, y1);
329
330             point2rgb(f->visdepth, c, &or, &og, &ob);
331             point2rgb(f->visdepth, myc, &r, &g, &b);
332
333             nr = or + (r - or) * a;
334             ng = og + (g - og) * a;
335             nb = ob + (b - ob) * a;
336
337             c = rgb2point(f->visdepth, nr, ng, nb);
338
339             ref_pixel(f, x1, y1) = c;
340
341             return c;
342         }
343     }
344
345     return 0;
346 }
347
348 inline void region_color(Display *dpy, Window window, GC fgc, struct field *f, 
349                          crack *cr) {
350     /* synthesis of Crack::regionColor() and SandPainter::render() */
351
352     float rx = cr->x;
353     float ry = cr->y;
354     int openspace = 1;
355     int cx, cy;
356     float maxg;
357     int grains, i;
358     float w;
359     float drawx, drawy;
360     unsigned long c;
361
362     while (openspace) {
363         /* move perpendicular to crack */
364         rx += (0.81 * sin(cr->t * M_PI/180));
365         ry -= (0.81 * cos(cr->t * M_PI/180));
366
367         cx = (int) rx;
368         cy = (int) ry;
369
370         if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
371             /* safe to check */
372             if (f->cgrid[cy * f->width + cx] > 10000) {
373                 /* space is open */
374             } else {
375                 openspace = 0;
376             }
377         } else {
378             openspace = 0;
379         }
380     }
381
382     /* SandPainter stuff here */
383
384     /* Modulate gain */
385     cr->sandg += (frand(0.1) - 0.050);
386     maxg = 1.0;
387
388     if (cr->sandg < 0)
389         cr->sandg = 0;
390
391     if (cr->sandg > maxg)
392         cr->sandg = maxg;
393
394     grains = f->grains;
395
396     /* Lay down grains of sand */
397     w = cr->sandg / (grains - 1);
398
399     for (i = 0; i < grains; i++) {
400         drawx = (cr->x + (rx - cr->x) * sin(cr->sandp + sin((float) i * w)));
401         drawy = (cr->y + (ry - cr->y) * sin(cr->sandp + sin((float) i * w)));
402
403         /* Draw sand bit */
404         c = trans_point(drawx, drawy, cr->sandcolor, (0.1 - i / (grains * 10.0)), f);
405
406         XSetForeground(dpy, fgc, c);
407         XDrawPoint(dpy, window, fgc, (int) drawx, (int) drawy);
408         XSetForeground(dpy, fgc, f->fgcolor);
409     }
410 }
411
412 void build_substrate(struct field *f) {
413     int tx;
414     /* int ty; */
415
416     f->cycles = 0;
417
418     if (f->cgrid) {
419         free(f->cgrid);
420         f->cgrid = NULL;
421     }
422
423     if (f->cracks) {
424         free(f->cracks);
425         f->cracks = NULL;
426     }
427
428     f->num = 0;
429
430     /* erase the crack grid */
431     f->cgrid = (int *) xrealloc(f->cgrid, sizeof(int) * f->height * f->width);
432     memset(f->cgrid, 10001, f->height * f->width * sizeof(int));
433
434     /* Not necessary now that make_crack ensures we have usable default
435      *  values in start_crack's timeout case 
436     * make random crack seeds *
437     for (tx = 0; tx < 16; tx++) {
438         ty = (int) (random() % (f->width * f->height - 1));
439         f->cgrid[ty] = (int) random() % 360;
440     }
441     */
442
443     /* make the initial cracks */
444     for (tx = 0; tx < f->initial_cracks; tx++)
445         make_crack(f);
446 }
447
448
449 inline void movedrawcrack(Display *dpy, Window window, GC fgc, struct field *f, 
450                           int cracknum) {
451     /* Basically Crack::move() */
452
453     int cx, cy;
454     crack *cr = &(f->cracks[cracknum]);
455
456     /* continue cracking */
457     if ( !cr->curved ) {
458         cr->x += ((float) STEP * cos(cr->t * M_PI/180));
459         cr->y += ((float) STEP * sin(cr->t * M_PI/180));
460     }
461     else {
462         float oldx, oldy;
463
464         oldx = cr->x;
465         oldy = cr->y;
466
467         cr->x += ((float) cr->ys * cos(cr->t * M_PI/180));
468         cr->y += ((float) cr->ys * sin(cr->t * M_PI/180));
469
470         cr->x += ((float) cr->xs * cos(cr->t * M_PI/180 - M_PI / 2));
471         cr->x += ((float) cr->xs * sin(cr->t * M_PI/180 - M_PI / 2));
472
473         cr->t += cr->t_inc;
474         cr->degrees_drawn += abs(cr->t_inc);
475     }
476
477     /* bounds check */
478     /* modification of random(-0.33,0.33) */
479     cx = (int) (cr->x + (frand(0.66) - 0.33));
480     cy = (int) (cr->y + (frand(0.66) - 0.33));
481
482
483     if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
484         /* draw sand painter if we're not wireframe */
485         if (!f->wireframe)
486             region_color(dpy, window, fgc, f, cr);
487
488         /* draw fgcolor crack */
489         ref_pixel(f, cx, cy) = f->fgcolor;
490         XDrawPoint(dpy, window, fgc, cx, cy);
491
492         if ( cr->curved && (cr->degrees_drawn > 360) ) {
493             /* completed the circle, stop cracking */
494             start_crack(f, cr); /* restart ourselves */
495             make_crack(f); /* generate a new crack */
496         }
497         /* safe to check */
498         else if ((f->cgrid[cy * f->width + cx] > 10000) ||
499                  (abs(f->cgrid[cy * f->width + cx] - cr->t) < 5)) {
500             /* continue cracking */
501             f->cgrid[cy * f->width + cx] = (int) cr->t;
502         } else if (abs(f->cgrid[cy * f->width + cx] - cr->t) > 2) {
503             /* crack encountered (not self), stop cracking */
504             start_crack(f, cr); /* restart ourselves */
505             make_crack(f); /* generate a new crack */
506         }
507     } else {
508         /* out of bounds, stop cracking */
509
510         /* need these in case of timeout in start_crack */
511         cr->x = random() % f->width;
512         cr->y = random() % f->height;
513         cr->t = random() % 360;
514
515         start_crack(f, cr); /* restart ourselves */
516         make_crack(f); /* generate a new crack */
517     }
518
519 }
520
521 char *progclass = "Substrate";
522
523 char *defaults[] = {
524     ".background: white",
525     ".foreground: black",
526     "*wireFrame: false",
527     "*maxCycles: 10000",
528     "*growthDelay: 18000",
529     "*initialCracks: 3",
530     "*maxCracks: 100",
531     "*sandGrains: 64",
532     "*circlePercent: 0",
533     0
534 };
535
536 XrmOptionDescRec options[] = {
537     {"-background", ".background", XrmoptionSepArg, 0},
538     {"-foreground", ".foreground", XrmoptionSepArg, 0},
539     {"-wireframe", ".wireFrame", XrmoptionNoArg, "true"},
540     {"-max-cycles", ".maxCycles", XrmoptionSepArg, 0},
541     {"-growth-delay", ".growthDelay", XrmoptionSepArg, 0},
542     {"-initial-cracks", ".initialCracks", XrmoptionSepArg, 0},
543     {"-max-cracks", ".maxCracks", XrmoptionSepArg, 0},
544     {"-sand-grains", ".sandGrains", XrmoptionSepArg, 0},
545     {"-circle-percent", ".circlePercent", XrmoptionSepArg, 0},
546     {0, 0, 0, 0}
547 };
548
549 void build_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc, 
550                struct field *f) {
551     if (f->off_img) {
552         free(f->off_img);
553         f->off_img = NULL;
554     }
555
556     f->off_img = (unsigned long *) xrealloc(f->off_img, sizeof(unsigned long) * 
557                                             f->width * f->height);
558
559     memset(f->off_img, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
560 }
561
562 void screenhack(Display * dpy, Window window)
563 {
564     struct field *f = init_field();
565
566     unsigned int max_cycles = 0;
567     int growth_delay = 0;
568     int tempx;
569
570     GC fgc;
571     XGCValues gcv;
572     XWindowAttributes xgwa;
573     XColor tmpcolor;
574
575     growth_delay = (get_integer_resource("growthDelay", "Integer"));
576     max_cycles = (get_integer_resource("maxCycles", "Integer"));
577     f->initial_cracks = (get_integer_resource("initialCracks", "Integer"));
578     f->max_num = (get_integer_resource("maxCracks", "Integer"));
579     f->wireframe = (get_boolean_resource("wireFrame", "Boolean"));
580     f->grains = (get_integer_resource("sandGrains", "Integer"));
581     f->circle_percent = (get_integer_resource("circlePercent", "Integer"));
582
583     if (f->initial_cracks <= 2) {
584         fprintf(stderr, "%s: Initial cracks must be greater than 2\n", progname);
585         return;
586     }
587
588     if (f->max_num <= 10) {
589         fprintf(stderr, "%s: Maximum number of cracks must be less than 10\n", 
590                 progname);
591         return;
592     }
593
594     if (f->circle_percent < 0) {
595         fprintf(stderr, "%s: circle percent must be at least 0\n", progname);
596         return;
597     }
598
599     if (f->circle_percent > 100) {
600         fprintf(stderr, "%s: circle percent must be less than 100\n", progname);
601         return;
602     }
603     
604     XGetWindowAttributes(dpy, window, &xgwa);
605
606     f->height = xgwa.height;
607     f->width = xgwa.width;
608     f->visdepth = xgwa.depth;
609  
610     /* Count the colors in our map and assign them in a horrifically inefficient 
611      * manner but it only happens once */
612     while (rgb_colormap[f->numcolors] != NULL) {
613         f->parsedcolors = (unsigned long *) xrealloc(f->parsedcolors, 
614                                                      sizeof(unsigned long) * 
615                                                      (f->numcolors + 1));
616         if (!XParseColor(dpy, xgwa.colormap, rgb_colormap[f->numcolors], &tmpcolor)) {
617             fprintf(stderr, "%s: couldn't parse color %s\n", progname,
618                     rgb_colormap[f->numcolors]);
619             exit(1);
620         }
621
622         if (!XAllocColor(dpy, xgwa.colormap, &tmpcolor)) {
623             fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
624                     rgb_colormap[f->numcolors]);
625             exit(1);
626         }
627
628         f->parsedcolors[f->numcolors] = tmpcolor.pixel;
629
630         f->numcolors++;
631     }
632
633     gcv.foreground = get_pixel_resource("foreground", "Foreground",
634                                         dpy, xgwa.colormap);
635     gcv.background = get_pixel_resource("background", "Background",
636                                         dpy, xgwa.colormap);
637     fgc = XCreateGC(dpy, window, GCForeground, &gcv);
638
639     f->fgcolor = gcv.foreground;
640     f->bgcolor = gcv.background;
641
642     /* Initialize stuff */
643     build_img(dpy, window, xgwa, fgc, f);
644     build_substrate(f);
645     
646     while (1) {
647         if ((f->cycles % 10) == 0) {
648             /* Restart if the window size changes */
649             XGetWindowAttributes(dpy, window, &xgwa);
650
651             if (f->height != xgwa.height || f->width != xgwa.width) {
652                 f->height = xgwa.height;
653                 f->width = xgwa.width;
654                 f->visdepth = xgwa.depth;
655
656                 build_substrate(f);
657                 build_img(dpy, window, xgwa, fgc, f);
658                 XSetForeground(dpy, fgc, gcv.background);
659                 XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
660                 XSetForeground(dpy, fgc, gcv.foreground);
661             }
662         }
663
664         for (tempx = 0; tempx < f->num; tempx++) {
665             movedrawcrack(dpy, window, fgc, f, tempx);
666         }
667
668         f->cycles++;
669
670         XSync(dpy, False);
671
672         screenhack_handle_events(dpy);
673
674         if (f->cycles >= max_cycles && max_cycles != 0) {
675             build_substrate(f);
676             build_img(dpy, window, xgwa, fgc, f);
677             XSetForeground(dpy, fgc, gcv.background);
678             XFillRectangle(dpy, window, fgc, 0, 0, xgwa.width, xgwa.height);
679             XSetForeground(dpy, fgc, gcv.foreground);
680         }
681
682         if (growth_delay)
683             usleep(growth_delay);
684     }
685 }
686