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