http://ftp.ussg.iu.edu/linux/slackware/slackware-9.0/source/xap/xscreensaver/xscreens...
[xscreensaver] / hacks / piecewise.c
1 /* piecewise, 21jan2003
2  * Geoffrey Irving <irving@caltech.edu>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #include <stdarg.h>
14 #include <math.h>
15 #include "screenhack.h"
16
17 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
18 # include "xdbe.h"
19 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
20
21 #define X_PI (180 * 64)
22
23 /******** splaying code */
24
25 typedef struct _tree {
26   struct _tree *l, *r;   /* left and right children */
27                          /* extra stuff would go here */
28   } tree;
29
30 typedef int (*cut)(tree*);    /* cut x is <, =, or > 0 given a <, =, or > x for some a */
31
32 /* Top-down splay routine.  Reference:
33  *   "Self-adjusting Binary Search Trees", Sleator and Tarjan,
34  *   JACM Volume 32, No 3, July 1985, pp 652-686.
35  * See page 668 for specific splay transformations */
36
37 tree *splay(cut c, tree *t) {
38   int v, vv;
39   tree *l, *r;
40   tree **lr, **rl;
41   tree *x, *y, *z;
42
43   if (!t)
44     return 0;
45
46   /* initialization */
47   x = t;
48   l = r = 0;
49   lr = &l;
50   rl = &r;
51
52   /* top-down splaying loop */
53   for (;;) {
54     v = c(x);
55     if (v == 0)
56       break;               /*** success ***/
57     else if (v < 0) {
58       y = x->l;
59       if (!y)
60         break;             /*** trivial ***/
61       else {
62         vv = c(y);
63         if (vv == 0) {
64           *rl = x;         /*** zig ***/
65           rl = &x->l;
66           x = y;
67           break;
68           }
69         else if (vv < 0) {
70           z = y->l;
71           if (!z) {
72             *rl = x;       /*** zig ***/
73             rl = &x->l;
74             x = y;
75             break;
76             }
77           else {
78             x->l = y->r;   /*** zig-zig ***/
79             y->r = x;
80             *rl = y;
81             rl = &y->l;
82             x = z;
83             }
84           }
85         else { /* vv > 0 */
86           z = y->r;
87           if (!z) {
88             *rl = x;       /*** zig ***/
89             rl = &x->l;
90             x = y;
91             break;
92             }
93           else {           /*** zig-zag ***/
94             *lr = y;
95             lr = &y->r;
96             *rl = x;
97             rl = &x->l;
98             x = z;
99             }
100           }
101         }
102       }
103     else { /* v > 0 */
104       y = x->r;
105       if (!y)
106         break;             /*** trivial ***/
107       else {
108         vv = c(y);
109         if (vv == 0) {
110           *lr = x;         /*** zig ***/
111           lr = &x->r;
112           x = y;
113           break;
114           }
115         else if (vv > 0) {
116           z = y->r;
117           if (!z) {
118             *lr = x;       /*** zig ***/
119             lr = &x->r;
120             x = y;
121             break;
122             }
123           else {
124             x->r = y->l;   /*** zig-zig ***/
125             y->l = x;
126             *lr = y;
127             lr = &y->r;
128             x = z;
129             }
130           }
131         else { /* vv < 0 */
132           z = y->l;
133           if (!z) {
134             *lr = x;       /*** zig ***/
135             lr = &x->r;
136             x = y;
137             break;
138             }
139           else {           /*** zig-zag ***/
140             *rl = y;
141             rl = &y->l;
142             *lr = x;
143             lr = &x->r;
144             x = z;
145             }
146           }
147         }
148       }
149     }
150
151   /* completion */
152   *lr = x->l;
153   x->l = l;
154   *rl = x->r;
155   x->r = r;
156   return x;
157   }
158
159 tree *splay_min(tree *t) {
160   tree *r, **rl;
161   tree *x, *y, *z;
162
163   if (!t)
164     return 0;
165
166   x = t;
167   r = 0;
168   rl = &r;
169
170   for (;;) {
171     y = x->l;
172     if (!y)
173       break;           /*** trivial ***/
174     else {
175       z = y->l;
176       if (!z) {
177         *rl = x;       /*** zig ***/
178         rl = &x->l;
179         x = y;
180         break;
181         }
182       else {
183         x->l = y->r;   /*** zig-zig ***/
184         y->r = x;
185         *rl = y;
186         rl = &y->l;
187         x = z;
188         }
189       }
190     }
191
192   x->l = 0;
193   *rl = x->r;
194   x->r = r;
195   return x;
196   }
197
198 tree *splay_max(tree *t) {
199   tree *l, **lr;
200   tree *x, *y, *z;
201
202   if (!t)
203     return 0;
204
205   x = t;
206   l = 0;
207   lr = &l;
208
209   for (;;) {
210     y = x->r;
211     if (!y)
212       break;           /*** trivial ***/
213     else {
214       z = y->r;
215       if (!z) {
216         *lr = x;       /*** zig ***/
217         lr = &x->r;
218         x = y;
219         break;
220         }
221       else {
222         x->r = y->l;   /*** zig-zig ***/
223         y->l = x;
224         *lr = y;
225         lr = &y->r;
226         x = z;
227         }
228       }
229     }
230
231   *lr = x->l;
232   x->l = l;
233   x->r = 0;
234   return x;
235   }
236
237 /******** circles and fringe */
238
239 struct _fringe;
240
241 typedef struct _circle {
242   int r;                    /* radius */
243   double x, y;              /* position */
244   double dx, dy;            /* velocity */
245
246   int visible;              /* default visibility */
247   struct _fringe *lo, *hi;  /* lo and hi fringes */
248
249   int ni;                   /* number of intersections */
250   int *i;                   /* sorted intersection list */
251   } circle;             
252
253 typedef struct _fringe {
254   struct _fringe *l, *r;  /* left and right children for splay trees */
255
256   circle *c;              /* associated circle */
257   int side;               /* 0 for lo, 1 for hi */
258
259   int mni;                /* size of intersection array */
260   int ni;                 /* number of intersections */
261   int *i;                 /* sorted intersection list */
262   } fringe;
263
264 inline double fringe_x(fringe *f, double y) {
265   double dy, d;
266   dy = f->c->y - y;
267   d = sqrt(f->c->r * f->c->r - dy * dy);
268   return f->side ? f->c->x + d : f->c->x - d;
269   }
270
271 inline void fringe_add_intersection(fringe *f, double x, double y) {
272   f->ni++;
273   if (f->mni < f->ni) {
274     f->mni += 2;
275     f->i = realloc(f->i, sizeof(int) * f->mni);
276     }
277   f->i[f->ni-1] = rint(atan2(y - f->c->y, x - f->c->x) * X_PI / M_PI);
278   }
279
280 circle *init_circles(int n, int w, int h) {
281   int i, r0, dr, speed;
282   double v, a;
283   double minradius, maxradius;
284   fringe *s = malloc(sizeof(fringe) * n * 2);    /* never freed */
285   circle *c = malloc(sizeof(circle) * n);
286
287   speed = get_integer_resource("speed", "Speed");
288   minradius = get_float_resource("minradius", "Float");
289   maxradius = get_float_resource("maxradius", "Float");
290   if (maxradius < minradius)
291     maxradius = minradius;
292
293   r0 = ceil(minradius * h); 
294   dr = floor(maxradius * h) - r0 + 1;
295
296   for (i=0;i<n;i++) {
297     c[i].r = r0 + random() % dr;
298     c[i].x = c[i].r + frand(w - 1 - 2 * c[i].r);
299     c[i].y = c[i].r + frand(h - 1 - 2 * c[i].r);
300     c[i].visible = random() & 1;
301
302     c[i].ni = 0;
303     c[i].i = 0;
304
305     a = frand(2 * M_PI);
306     v = (1 + frand(0.5)) * speed / 10.0;
307     c[i].dx = v * cos(a);
308     c[i].dy = v * sin(a);
309
310     c[i].lo = s+i+i;
311     c[i].hi = s+i+i+1;
312     c[i].lo->c = c[i].hi->c = c+i;
313     c[i].lo->side = 0;
314     c[i].hi->side = 1;
315     c[i].lo->mni = c[i].lo->ni = c[i].hi->mni = c[i].hi->ni = 0;
316     c[i].lo->i = c[i].hi->i = 0;
317     }
318
319   return c;
320   }
321
322 /* this is a hack, but I guess that's what I writing anyways */
323 void tweak_circle(circle *c) {
324   c->x += frand(2) - 1;
325   c->y += frand(1) + 0.1;
326   }
327
328 void move_circle(circle *c, int w, int h) {
329   c->x += c->dx;
330   if (c->x < c->r) {
331     c->x = c->r;
332     c->dx = -c->dx;
333     }
334   else if (c->x >= w - c->r) {
335     c->x = w - 1 - c->r;
336     c->dx = -c->dx;
337     }
338   c->y += c->dy;
339   if (c->y < c->r) {
340     c->y = c->r;
341     c->dy = -c->dy;
342     }
343   else if (c->y >= h - c->r) {
344     c->y = h - 1 - c->r;
345     c->dy = -c->dy;
346     }
347   }
348
349 /******** event queue */
350
351 #define START 0
352 #define CROSS 1
353 #define FINISH 2
354
355 typedef struct _event {
356   struct _event *l, *r;    /* left and right children for splay tree */
357
358   int kind;                /* type of event */
359   double x, y;             /* position */
360   fringe *lo, *hi;         /* fringes */
361   } event;
362
363 static double event_cut_y;
364
365 int event_cut(event *e) {
366   return event_cut_y == e->y ? 0 : event_cut_y < e->y ? -1 : 1;
367   }
368
369 void event_insert(event **eq, event *e) {
370   if (!*eq) {
371     e->l = e->r = 0;
372     *eq = e;
373     }
374
375   event_cut_y = e->y;
376   *eq = (event*)splay((cut)event_cut, (tree*)*eq);
377
378   if (e->y == (*eq)->y) {
379     if (!((e->lo == (*eq)->lo && e->hi == (*eq)->hi) || (e->lo == (*eq)->hi && e->hi == (*eq)->lo))) {
380       e->l = (*eq)->l;
381       e->r = 0;             /* doing this instead of dying might be dangerous */
382       (*eq)->l = e;           
383       }
384     }
385   else if (e->y < (*eq)->y) {
386     e->l = (*eq)->l;
387     e->r = *eq;
388     (*eq)->l = 0;
389     *eq = e;
390     }
391   else {
392     e->l = *eq;
393     e->r = (*eq)->r; 
394     (*eq)->r = 0;
395     *eq = e;
396     }
397   }
398
399 void circle_start_event(event **eq, circle *c) {
400   event *s;
401   s = malloc(sizeof(event));
402   s->kind = START;
403   s->x = c->x;
404   s->y = c->y - c->r;
405   s->lo = c->lo;
406   s->hi = c->hi;
407   event_insert(eq, s);
408   }
409
410 void circle_finish_event(event **eq, circle *c) {
411   event *f;
412   f = malloc(sizeof(event));
413   f->kind = FINISH;
414   f->x = c->x;
415   f->y = c->y + c->r;
416   f->lo = c->lo;
417   f->hi = c->hi;
418   event_insert(eq, f);
419   }
420
421 event *event_next(event **eq) {
422   event *e;
423   if (!*eq)
424     return 0;
425   else {
426     e = (event*)splay_min((tree*)*eq);
427     *eq = e->r; 
428     return e;
429     }
430   }
431
432 void event_shred(event *e) {
433   if (e) {
434     event_shred(e->l);
435     event_shred(e->r);
436     free(e);
437     }
438   }
439
440 /******** fringe intersection */
441
442 inline int check_fringe_intersection(double ye, fringe *lo, fringe *hi, double x, double y) {
443   return ye <= y && ((x < lo->c->x) ^ lo->side) && ((x < hi->c->x) ^ hi->side);
444   }
445
446 void fringe_intersect(event **eq, double y, fringe *lo, fringe *hi) {
447   event *e;
448   double dx, dy, sd, rs, rd, d, sx, sy, rp, sqd;
449   double x1, y1, x2, y2;
450
451   if (lo->c == hi->c)
452     return;
453
454   dx = hi->c->x - lo->c->x; 
455   dy = hi->c->y - lo->c->y; 
456   sd = dx * dx + dy * dy; 
457
458   if (sd == 0)
459     return;
460
461   rs = hi->c->r + lo->c->r; 
462   rd = hi->c->r - lo->c->r; 
463   d = (rd * rd - sd) * (sd - rs * rs);
464
465   if (d <= 0)
466     return;
467  
468   sd = 0.5 / sd;
469   rp = rs * rd; 
470   sqd = sqrt(d); 
471   sx = (lo->c->x + hi->c->x) / 2;
472   sy = (lo->c->y + hi->c->y) / 2;
473   x1 = sx + sd * (dy * sqd - dx * rp); 
474   y1 = sy - sd * (dx * sqd + dy * rp);
475   x2 = sx - sd * (dy * sqd + dx * rp);
476   y2 = sy + sd * (dx * sqd - dy * rp);
477
478   #define CHECK(xi, yi) (y <= yi && ((xi < lo->c->x) ^ lo->side) && ((xi < hi->c->x) ^ hi->side))
479
480   #define ADD_CROSS(xi, yi, ilo, ihi) {  \
481     e = malloc(sizeof(event));           \
482     e->kind = CROSS;                     \
483     e->x = xi; e->y = yi;                \
484     e->lo = ilo; e->hi = ihi;            \
485     event_insert(eq, e);                 \
486     }
487
488   if (CHECK(x1, y1)) {
489     if (CHECK(x2, y2)) {
490       if (y1 < y2) {
491         ADD_CROSS(x1, y1, lo, hi);
492         ADD_CROSS(x2, y2, hi, lo);
493         }
494       else {
495         ADD_CROSS(x1, y1, hi, lo);
496         ADD_CROSS(x2, y2, lo, hi);
497         }
498       }
499     else
500       ADD_CROSS(x1, y1, lo, hi);
501     }
502   else if (CHECK(x2, y2))
503     ADD_CROSS(x2, y2, lo, hi); 
504
505   return;
506   }
507
508 /******** fringe trees and event handling */
509
510 #define PANIC ((fringe*)1)     /* by alignment, no fringe should every be 1 */
511
512 fringe *check_lo(event **eq, double y, fringe *f, fringe *hi) {
513   if (f) {
514     f = (fringe*)splay_max((tree*)f);
515     fringe_intersect(eq, y, f, hi);
516     }
517   return f;
518   }
519
520 fringe *check_hi(event **eq, double y, fringe *lo, fringe *f) {
521   if (f) {
522     f = (fringe*)splay_min((tree*)f);
523     fringe_intersect(eq, y, lo, f);
524     }
525   return f;
526   }
527
528 double fringe_start_cut_x;
529 double fringe_start_cut_y;
530
531 int fringe_start_cut(fringe *f) {
532   double x = fringe_x(f, fringe_start_cut_y);
533   return fringe_start_cut_x == x ? 0 : fringe_start_cut_x < x ? -1 : 1;
534   }
535
536 fringe *fringe_start(event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi) {
537   double sx;
538
539   if (!f) {
540     circle_finish_event(eq, lo->c);
541     lo->l = 0;
542     lo->r = hi;
543     hi->l = hi->r = 0;
544     return lo;
545     }
546
547   fringe_start_cut_x = x;
548   fringe_start_cut_y = y;
549   f = (fringe*)splay((cut)fringe_start_cut, (tree*)f);
550
551   sx = fringe_x(f, y);
552   if (x == sx) {       /* time to cheat my way out of handling degeneracies */
553     tweak_circle(lo->c);
554     circle_start_event(eq, lo->c);
555     return f;
556     }
557   else if (x < sx) {
558     circle_finish_event(eq, lo->c);
559     f->l = check_lo(eq, y, f->l, lo);    
560     fringe_intersect(eq, y, hi, f);
561     lo->l = f->l;
562     lo->r = f;
563     f->l = hi;
564     hi->l = hi->r = 0;
565     return lo;
566     }
567   else {
568     circle_finish_event(eq, lo->c);
569     fringe_intersect(eq, y, f, lo);
570     f->r = check_hi(eq, y, hi, f->r);
571     hi->r = f->r;
572     hi->l = f;
573     f->r = lo;
574     lo->l = lo->r = 0;
575     return hi;
576     }
577   }
578
579 double fringe_double_cut_x;
580 double fringe_double_cut_y;
581 fringe *fringe_double_cut_lo;
582 fringe *fringe_double_cut_hi;
583
584 int fringe_double_cut(fringe *f) {
585   double x;
586   if (f == fringe_double_cut_lo || f == fringe_double_cut_hi)
587     return 0;
588   x = fringe_x(f, fringe_double_cut_y);
589   return fringe_double_cut_x == x ? 0 : fringe_double_cut_x < x ? -1 : 1;
590   }
591
592 int fringe_double_splay(fringe *f, double x, double y, fringe *lo, fringe *hi) {
593   fringe_double_cut_x = x;
594   fringe_double_cut_y = y;
595   fringe_double_cut_lo = lo;
596   fringe_double_cut_hi = hi;
597   f = (fringe*)splay((cut)fringe_double_cut, (tree*)f);
598
599   if (f == lo)
600     return (f->r = (fringe*)splay_min((tree*)f->r)) == hi;
601   else if (f == hi)
602     return (f->l = (fringe*)splay_max((tree*)f->l)) == lo;
603   else
604     return 0;
605   }
606
607 fringe *fringe_cross(event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi) {
608   fringe *l, *r;
609   if (!fringe_double_splay(f, x, y, lo, hi))
610     return PANIC;
611   l = check_lo(eq, y, lo->l, hi);
612   r = check_hi(eq, y, lo, hi->r);
613   lo->l = hi;
614   lo->r = r;
615   hi->l = l;
616   hi->r = 0;
617   return lo;
618   }
619
620 fringe *fringe_finish(event **eq, fringe *f, double x, double y, fringe *lo, fringe *hi) {
621   if (!fringe_double_splay(f, x, y, lo, hi))
622     return PANIC;
623   else if (!lo->l)
624     return hi->r;
625   else if (!hi->r)
626     return lo->l;
627   else {
628     lo->l = (fringe*)splay_max((tree*)lo->l);
629     hi->r = (fringe*)splay_min((tree*)hi->r);
630     fringe_intersect(eq, y, lo->l, hi->r);
631     lo->l->r = hi->r;
632     hi->r->l = 0;
633     return lo->l;
634     }
635   }
636
637 /******** plane sweep */
638
639 void sweep(int n, circle *c) {
640   int i;
641   event *eq, *e;
642   fringe *f;
643
644   RESTART:
645   #define CHECK_PANIC()                 \
646     if (f == PANIC) {                   \
647       free(e);                          \
648       event_shred(eq);                  \
649       for (i=0;i<n;i++) {               \
650         tweak_circle(c+i);              \
651         c[i].lo->ni = c[i].hi->ni = 0;  \
652         }                               \
653       goto RESTART;                     \
654       }
655
656   eq = 0;
657   for (i=0;i<n;i++)
658     circle_start_event(&eq, c+i); 
659   f = 0;
660
661   while ((e = event_next(&eq))) {
662     switch (e->kind) {
663       case START:
664         f = fringe_start(&eq, f, e->x, e->y, e->lo, e->hi);
665         break;
666       case CROSS:
667         f = fringe_cross(&eq, f, e->x, e->y, e->lo, e->hi);
668         CHECK_PANIC();
669         fringe_add_intersection(e->lo, e->x, e->y);
670         fringe_add_intersection(e->hi, e->x, e->y);
671         break;
672       case FINISH:
673         f = fringe_finish(&eq, f, e->x, e->y, e->lo, e->hi);
674         CHECK_PANIC();
675         break;
676       }
677     free(e);
678     }
679   }
680
681 /******** circle drawing */
682
683 void adjust_circle_visibility(circle *c) {
684   int i, j, n, a;
685   int *in;
686   n = c->lo->ni + c->hi->ni;
687   in = malloc(sizeof(int) * n);
688   for (i=0;i<c->hi->ni;i++)
689     in[i] = c->hi->i[i]; 
690   for (i=c->lo->ni-1;i>=0;i--)
691     in[n-i-1] = c->lo->i[i] > 0 ? c->lo->i[i] : c->lo->i[i] + 2 * X_PI;
692   c->lo->ni = c->hi->ni = 0;
693
694   i = j = 0;
695   a = 0;
696   while (i < n && j < c->ni)           /* whee */
697     a = (in[i] < c->i[j] ? in[i++] : c->i[j++]) - a;
698   while (i < n)
699     a = in[i++] - a;
700   while (j < c->ni)
701     a = c->i[j++] - a;
702
703   if (a > X_PI) 
704     c->visible = !c->visible;
705   free(c->i);  
706   c->ni = n;
707   c->i = in;
708   }
709
710 #define ARC_BUFFER_SIZE 256
711 int arc_buffer_count = 0;
712 XArc arc_buffer[ARC_BUFFER_SIZE];
713
714 void flush_arc_buffer(Display *dpy, Drawable w, GC gc) {
715   if (arc_buffer_count) {
716     XDrawArcs(dpy, w, gc, arc_buffer, arc_buffer_count);
717     arc_buffer_count = 0;
718     }
719   }
720
721 void draw_circle(Display *dpy, Drawable w, GC gc, circle *c) {
722   int i, xi, yi, di;
723   adjust_circle_visibility(c); 
724
725   xi = rint(c->x - c->r);
726   yi = rint(c->y - c->r);
727   di = c->r << 1;
728
729   #define ARC(p, a1, a2) {                                \
730     if (((p) & 1) ^ c->visible) {                         \
731       arc_buffer[arc_buffer_count].x      = xi;           \
732       arc_buffer[arc_buffer_count].y      = yi;           \
733       arc_buffer[arc_buffer_count].width  = di;           \
734       arc_buffer[arc_buffer_count].height = di;           \
735       arc_buffer[arc_buffer_count].angle1 = -(a1);        \
736       arc_buffer[arc_buffer_count].angle2 = (a1) - (a2);  \
737       arc_buffer_count++;                                 \
738       if (arc_buffer_count == ARC_BUFFER_SIZE)            \
739         flush_arc_buffer(dpy, w, gc);                     \
740       }                                                   \
741     }
742
743   if (!c->ni)
744     ARC(0, 0, 2 * X_PI)
745   else
746     ARC(0, c->i[c->ni-1], c->i[0] + 2 * X_PI)
747   for (i=1;i<c->ni;i++)
748     ARC(i, c->i[i-1], c->i[i])
749   }
750
751 /******** toplevel */
752
753 char *progclass = "Piecewise";
754
755 char *defaults [] = {
756   ".background:         black",
757   ".foreground:         white",
758   "*delay:              5000",
759   "*speed:              15",
760   "*ncolors:            256",
761   ".colorspeed:         10",
762
763   ".count:              32",
764   ".minradius:          0.05",
765   ".maxradius:          0.2",   
766
767   "*doubleBuffer:       True",
768 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
769   "*useDBE:             True",
770 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
771   0
772   };
773
774 XrmOptionDescRec options [] = {
775   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
776   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
777   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
778   { "-colorspeed",      ".colorspeed",  XrmoptionSepArg, 0 },
779
780   { "-count",           ".count",       XrmoptionSepArg, 0 },
781   { "-minradius",       ".minradius",   XrmoptionSepArg, 0 },
782   { "-maxradius",       ".maxradius",   XrmoptionSepArg, 0 },
783
784   { "-db",              ".doubleBuffer", XrmoptionNoArg,  "True" },
785   { "-no-db",           ".doubleBuffer", XrmoptionNoArg,  "False" },
786   { 0, 0, 0, 0 }
787   };
788
789 void screenhack(Display *dpy, Window window) {
790   int i;
791   Bool dbuf;
792   XColor *colors;
793   XGCValues gcv;
794   GC erase_gc, draw_gc;
795   XWindowAttributes xgwa;
796   Pixmap b = 0, ba = 0, bb = 0;    /* double-buffering pixmap */
797
798 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
799   XdbeBackBuffer backb = 0;
800 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
801
802   int count, delay, ncolors, colorspeed, color_index, flags, iterations;
803   int color_iterations;
804   circle *circles;
805
806   count = get_integer_resource("count", "Integer");
807   delay = get_integer_resource("delay", "Integer");
808   ncolors = get_integer_resource("ncolors", "Integer");
809   colorspeed = get_integer_resource("colorspeed", "Integer");
810   dbuf = get_boolean_resource("doubleBuffer", "Boolean");
811
812   color_iterations = colorspeed ? 100 / colorspeed : 100000;
813   if (!color_iterations)
814     color_iterations = 1;
815
816   XGetWindowAttributes(dpy, window, &xgwa);
817   colors = calloc(sizeof(XColor), ncolors);
818
819   if (get_boolean_resource("mono", "Boolean")) {  
820     MONO:
821       ncolors = 1;
822       colors[0].pixel = get_pixel_resource("foreground", "Foreground", dpy, xgwa.colormap);
823     }
824   else {
825     make_color_loop(dpy, xgwa.colormap, 0, 1, 1, 120, 1, 1, 240, 1, 1, colors, &ncolors, True, False);
826     if (ncolors < 2)
827       goto MONO; 
828     }
829
830   if (dbuf) {
831 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
832     b = backb = xdbe_get_backbuffer(dpy, window, XdbeUndefined);
833 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
834     
835     if (!b) {
836       ba = XCreatePixmap(dpy, window, xgwa.width, xgwa.height,xgwa.depth);
837       bb = XCreatePixmap(dpy, window, xgwa.width, xgwa.height,xgwa.depth);
838       b = ba;
839       }
840     }
841   else
842     b = window;
843
844   /* erasure gc */
845   gcv.foreground = get_pixel_resource("background", "Background", dpy, xgwa.colormap);
846   erase_gc = XCreateGC (dpy, b, GCForeground, &gcv);
847
848   /* drawing gc */
849   flags = GCForeground;
850   color_index = random() % ncolors;
851   gcv.foreground = colors[color_index].pixel;
852   draw_gc = XCreateGC(dpy, b, flags, &gcv);
853
854   /* initialize circles */
855   circles = init_circles(count, xgwa.width, xgwa.height);
856   
857   iterations = 0;
858   for (;;) {
859     XFillRectangle (dpy, b, erase_gc, 0, 0, xgwa.width, xgwa.height);
860
861     sweep(count, circles);
862     for (i=0;i<count;i++) {
863       draw_circle(dpy, b, draw_gc, circles+i);
864       move_circle(circles+i, xgwa.width, xgwa.height);
865       }
866     flush_arc_buffer(dpy, b, draw_gc);
867
868     if (++iterations % color_iterations == 0) {
869       color_index = (color_index + 1) % ncolors;
870       XSetForeground(dpy, draw_gc, colors[color_index].pixel);
871       }
872
873 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
874     if (backb) {
875       XdbeSwapInfo info[1];
876       info[0].swap_window = window;
877       info[0].swap_action = XdbeUndefined;
878       XdbeSwapBuffers (dpy, info, 1);
879       }
880     else
881 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
882     if (dbuf) {
883       XCopyArea (dpy, b, window, erase_gc, 0, 0, xgwa.width, xgwa.height, 0, 0);
884       b = (b == ba ? bb : ba);
885       }
886
887     XSync(dpy, False);
888     screenhack_handle_events(dpy);
889     if (delay)
890       usleep(delay);
891     }
892   }