http://ftp.ussg.iu.edu/linux/slackware/slackware-9.0/source/xap/xscreensaver/xscreens...
[xscreensaver] / utils / erase.c
1 /* erase.c: Erase the screen in various more or less interesting ways.
2  * Copyright (c) 1997-2001 Jamie Zawinski <jwz@jwz.org>
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  * Portions (c) 1997 by Johannes Keukelaar <johannes@nada.kth.se>:
13  *   Permission to use in any way granted. Provided "as is" without expressed
14  *   or implied warranty. NO WARRANTY, NO EXPRESSION OF SUITABILITY FOR ANY
15  *   PURPOSE. (I.e.: Use in any way, but at your own risk!)
16  */
17
18 #include "utils.h"
19 #include "yarandom.h"
20 #include "usleep.h"
21 #include "resources.h"
22
23 extern char *progname;
24
25 #undef countof
26 #define countof(x) (sizeof(x)/sizeof(*(x)))
27
28 #define LITTLE_NAP 5000   /* 1/200th sec */
29
30 typedef void (*Eraser) (Display *dpy, Window window, GC gc,
31                         int width, int height, int total_msecs);
32
33
34 static unsigned long
35 millitime (void)
36 {
37   struct timeval tt;
38 # ifdef GETTIMEOFDAY_TWO_ARGS
39   struct timezone tz;
40   gettimeofday (&tt, &tz);
41 # else
42   gettimeofday (&tt);
43 # endif
44   return (tt.tv_sec * 1000) + (tt.tv_usec / 1000);
45 }
46
47
48 static void
49 random_lines (Display *dpy, Window window, GC gc,
50               int width, int height, int total_msecs)
51 {
52   int granularity = 50;
53
54   Bool horiz_p = (random() & 1);
55   int max = (horiz_p ? height : width);
56   int *lines = (int *) calloc(max, sizeof(*lines));
57   int oi = -1;
58   int i;
59   unsigned long start_tick = millitime();
60   unsigned long end_tick = start_tick + total_msecs;
61   unsigned long tick = start_tick;
62   int hits = 0;
63   int nonhits = 0;
64
65   for (i = 0; i < max; i++)
66     lines[i] = i;
67
68   for (i = 0; i < max; i++)
69     {
70       int t, r;
71       t = lines[i];
72       r = random() % max;
73       lines[i] = lines[r];
74       lines[r] = t;
75     }
76
77   while (tick < end_tick)
78     {
79       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
80
81       i /= granularity;
82
83       if (i == oi)
84         {
85           usleep (LITTLE_NAP);
86           nonhits++;
87         }
88       else
89         {
90           int j;
91           for (j = 0; j < granularity; j++)
92             {
93               int ii = i * granularity + j;
94
95               if (ii >= horiz_p ? height : width) /* don't go off array */
96                 break;
97
98               if (horiz_p)
99                 XDrawLine (dpy, window, gc, 0, lines[ii], width, lines[ii]);
100               else
101                 XDrawLine (dpy, window, gc, lines[ii], 0, lines[ii], height);
102               hits++;
103             }
104           XSync (dpy, False);
105         }
106
107       oi = i;
108       tick = millitime();
109     }
110
111   free(lines);
112 }
113
114
115 static void
116 venetian (Display *dpy, Window window, GC gc,
117           int width, int height, int total_msecs)
118 {
119   Bool horiz_p = (random() & 1);
120   Bool flip_p = (random() & 1);
121   int max = (horiz_p ? height : width);
122   int *lines = (int *) calloc(max, sizeof(*lines));
123   int i, j;
124   int oi = -1;
125
126   unsigned long start_tick = millitime();
127   unsigned long end_tick = (start_tick +
128                             (1.5 * total_msecs));  /* this one needs more */
129   unsigned long tick = start_tick;
130   int hits = 0;
131   int nonhits = 0;
132
133   j = 0;
134   for (i = 0; i < max*2; i++)
135     {
136       int line = ((i / 16) * 16) - ((i % 16) * 15);
137       if (line >= 0 && line < max)
138         lines[j++] = (flip_p ? max - line : line);
139     }
140
141   while (tick < end_tick)
142     {
143       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
144
145       if (i == oi)
146         {
147           usleep (LITTLE_NAP);
148           nonhits++;
149         }
150       else
151         {
152           int k;
153           for (k = oi; k <= i; k++)
154             {
155               if (horiz_p)
156                 XDrawLine(dpy,window, gc, 0, lines[k], width, lines[k]);
157               else
158                 XDrawLine(dpy,window, gc, lines[k], 0, lines[k], height);
159               hits++;
160             }
161           XSync (dpy, False);
162         }
163
164       oi = i;
165       tick = millitime();
166     }
167   free(lines);
168 }
169
170
171 static void
172 triple_wipe (Display *dpy, Window window, GC gc,
173              int width, int height, int total_msecs)
174 {
175   Bool flip_x = random() & 1;
176   Bool flip_y = random() & 1;
177   int max = width + (height / 2);
178   int *lines = (int *)calloc(max, sizeof(int));
179   int i;
180   int oi = -1;
181
182   unsigned long start_tick = millitime();
183   unsigned long end_tick = start_tick + total_msecs;
184   unsigned long tick = start_tick;
185   int hits = 0;
186   int nonhits = 0;
187
188   for(i = 0; i < width/2; i++)
189     lines[i] = i*2+height;
190   for(i = 0; i < height/2; i++)
191     lines[i+width/2] = i*2;
192   for(i = 0; i < width/2; i++)
193     lines[i+width/2+height/2] = width-i*2-(width%2?0:1)+height;
194
195   while (tick < end_tick)
196     {
197       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
198       int x, y, x2, y2;
199
200       if (i == oi)
201         {
202           usleep (LITTLE_NAP);
203           nonhits++;
204         }
205       else
206         {
207           int k;
208           for (k = oi; k <= i; k++)
209             {
210               if (lines[k] < height)
211                 x = 0, y = lines[k], x2 = width, y2 = y;
212               else
213                 x = lines[k]-height, y = 0, x2 = x, y2 = height;
214
215               if (flip_x)
216                 x = width-x, x2 = width-x2;
217               if (flip_y)
218                 y = height-y, y2 = height-y2;
219
220               XDrawLine (dpy, window, gc, x, y, x2, y2);
221               hits++;
222             }
223           XSync (dpy, False);
224         }
225
226       oi = i;
227       tick = millitime();
228     }
229   free(lines);
230 }
231
232
233 static void
234 quad_wipe (Display *dpy, Window window, GC gc,
235            int width, int height, int total_msecs)
236 {
237   Bool flip_x = random() & 1;
238   Bool flip_y = random() & 1;
239   int max = width + height;
240   int *lines = (int *)calloc(max, sizeof(int));
241   int i;
242   int oi = -1;
243
244   unsigned long start_tick = millitime();
245   unsigned long end_tick = start_tick + total_msecs;
246   unsigned long tick = start_tick;
247   int hits = 0;
248   int nonhits = 0;
249
250   for (i = 0; i < max/4; i++)
251     {
252       lines[i*4]   = i*2;
253       lines[i*4+1] = height-i*2-(height%2?0:1);
254       lines[i*4+2] = height+i*2;
255       lines[i*4+3] = height+width-i*2-(width%2?0:1);
256     }
257
258   while (tick < end_tick)
259     {
260       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
261
262       if (i == oi)
263         {
264           usleep (LITTLE_NAP);
265           nonhits++;
266         }
267       else
268         {
269           int k;
270           for (k = oi; k <= i; k++)
271             {
272               int x, y, x2, y2;
273               if (lines[k] < height)
274                 x = 0, y = lines[k], x2 = width, y2 = y;
275               else
276                 x = lines[k]-height, y = 0, x2 = x, y2 = height;
277
278               if (flip_x)
279                 x = width-x, x2 = width-x2;
280               if (flip_y)
281                 y = height-y, y2 = height-y2;
282
283               XDrawLine (dpy, window, gc, x, y, x2, y2);
284               hits++;
285             }
286           XSync (dpy, False);
287         }
288
289       oi = i;
290       tick = millitime();
291     }
292
293   free(lines);
294 }
295
296
297
298 static void
299 circle_wipe (Display *dpy, Window window, GC gc,
300              int width, int height, int total_msecs)
301 {
302   int max = 360 * 64;
303   int start = random() % max;
304   int rad = (width > height ? width : height);
305   int flip_p = random() & 1;
306   int oth;
307
308   unsigned long start_tick = millitime();
309   unsigned long end_tick = start_tick + total_msecs;
310   unsigned long tick = start_tick;
311
312   int hits = 0;
313   int nonhits = 0;
314
315   oth = (flip_p ? max : 0);
316   while (tick < end_tick)
317     {
318       int th = (max * (tick - start_tick)) / (end_tick - start_tick);
319       if (flip_p)
320         th = (360 * 64) - th;
321
322       if (th == oth)
323         {
324           usleep (LITTLE_NAP);
325           nonhits++;
326         }
327       else
328         {
329           XFillArc(dpy, window, gc,
330                    (width/2)-rad, (height/2)-rad, rad*2, rad*2,
331                    (start+oth)%(360*64),
332                    (th-oth));
333           hits++;
334           XSync (dpy, False);
335         }
336
337       oth = th;
338       tick = millitime();
339     }
340 }
341
342
343 static void
344 three_circle_wipe (Display *dpy, Window window, GC gc,
345                    int width, int height, int total_msecs)
346 {
347   int max = (360 * 64) / 6;
348   int start = random() % max;
349   int rad = (width > height ? width : height);
350   int oth;
351
352   unsigned long start_tick = millitime();
353   unsigned long end_tick = start_tick + total_msecs;
354   unsigned long tick = start_tick;
355
356   int hits = 0;
357   int nonhits = 0;
358
359   oth = 0;
360   while (tick < end_tick)
361     {
362       int th = (max * (tick - start_tick)) / (end_tick - start_tick);
363
364       if (th == oth)
365         {
366           usleep (LITTLE_NAP);
367           nonhits++;
368         }
369       else
370         {
371           int off = 0;
372           XFillArc(dpy, window, gc,
373                    (width/2)-rad, (height/2)-rad, rad*2, rad*2,
374                    (start+off+oth)%(360*64),
375                    (th-oth));
376           XFillArc(dpy, window, gc,
377                    (width/2)-rad, (height/2)-rad, rad*2, rad*2,
378                    ((start+off-oth))%(360*64),
379                    -(th-oth));
380
381           off += max + max;
382           XFillArc(dpy, window, gc,
383                    (width/2)-rad, (height/2)-rad, rad*2, rad*2,
384                    (start+off+oth)%(360*64),
385                    (th-oth));
386           XFillArc(dpy, window, gc,
387                    (width/2)-rad, (height/2)-rad, rad*2, rad*2,
388                    ((start+off-oth))%(360*64),
389                    -(th-oth));
390
391           off += max + max;
392           XFillArc(dpy, window, gc,
393                    (width/2)-rad, (height/2)-rad, rad*2, rad*2,
394                    (start+off+oth)%(360*64),
395                    (th-oth));
396           XFillArc(dpy, window, gc,
397                    (width/2)-rad, (height/2)-rad, rad*2, rad*2,
398                    ((start+off-oth))%(360*64),
399                    -(th-oth));
400
401           hits++;
402           XSync (dpy, False);
403         }
404
405       oth = th;
406       tick = millitime();
407     }
408 }
409
410
411 static void
412 squaretate (Display *dpy, Window window, GC gc,
413             int width, int height, int total_msecs)
414 {
415   int max = ((width > height ? width : width) * 2);
416   int oi = -1;
417   Bool flip = random() & 1;
418
419   unsigned long start_tick = millitime();
420   unsigned long end_tick = start_tick + total_msecs;
421   unsigned long tick = start_tick;
422   int hits = 0;
423   int nonhits = 0;
424
425 #define DRAW() \
426       if (flip) { \
427         points[0].x = width-points[0].x; \
428         points[1].x = width-points[1].x; \
429         points[2].x = width-points[2].x; } \
430       XFillPolygon (dpy, window, gc, points, 3, Convex, CoordModeOrigin)
431
432   while (tick < end_tick)
433     {
434       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
435
436       if (i == oi)
437         {
438           usleep (LITTLE_NAP);
439           nonhits++;
440         }
441       else
442         {
443           XPoint points [3];
444           points[0].x = 0;
445           points[0].y = 0;
446           points[1].x = width;
447           points[1].y = 0;
448           points[2].x = 0;
449           points[2].y = points[0].y + ((i * height) / max);
450           DRAW();
451
452           points[0].x = 0;
453           points[0].y = 0;
454           points[1].x = 0;
455           points[1].y = height;
456           points[2].x = ((i * width) / max);
457           points[2].y = height;
458           DRAW();
459
460           points[0].x = width;
461           points[0].y = height;
462           points[1].x = 0;
463           points[1].y = height;
464           points[2].x = width;
465           points[2].y = height - ((i * height) / max);
466           DRAW();
467
468           points[0].x = width;
469           points[0].y = height;
470           points[1].x = width;
471           points[1].y = 0;
472           points[2].x = width - ((i * width) / max);
473           points[2].y = 0;
474           DRAW();
475           hits++;
476           XSync (dpy, True);
477         }
478
479       oi = i;
480       tick = millitime();
481     }
482 #undef DRAW
483 }
484
485
486 /* from Frederick Roeber <roeber@netscape.com> */
487 static void
488 fizzle (Display *dpy, Window window, GC gc,
489         int width, int height, int total_msecs)
490 {
491   /* These dimensions must be prime numbers.  They should be roughly the
492      square root of the width and height. */
493 # define BX 41
494 # define BY 31
495 # define SIZE (BX*BY)
496
497   int array[SIZE];
498   int i, j;
499   int oi = -1;
500   XPoint *skews;
501   XPoint points[250];
502   int npoints = 0;
503   int nx, ny;
504
505   unsigned long start_tick = millitime();
506   unsigned long end_tick = (start_tick +
507                             (2.5 * total_msecs));  /* this one needs more */
508   unsigned long tick = start_tick;
509   int hits = 0;
510   int nonhits = 0;
511
512   /* Distribute the numbers [0,SIZE) randomly in the array */
513   {
514     int indices[SIZE];
515
516     for( i = 0; i < SIZE; i++ ) {
517       array[i] = -1;
518       indices[i] = i;
519     } 
520
521     for( i = 0; i < SIZE; i++ ) {
522       j = random()%(SIZE-i);
523       array[indices[j]] = i;
524       indices[j] = indices[SIZE-i-1];
525     }
526   }
527
528   /* nx, ny are the number of cells across and down, rounded up */
529   nx = width  / BX + (0 == (width %BX) ? 0 : 1);
530   ny = height / BY + (0 == (height%BY) ? 0 : 1);
531   skews = (XPoint *)malloc(sizeof(XPoint) * (nx*ny));
532   if( (XPoint *)0 != skews ) {
533     for( i = 0; i < nx; i++ ) {
534       for( j = 0; j < ny; j++ ) {
535         skews[j * nx + i].x = random()%BX;
536         skews[j * nx + i].y = random()%BY;
537       }
538     }
539   }
540
541 # define SKEWX(cx, cy) (((XPoint *)0 == skews)?0:skews[cy*nx + cx].x)
542 # define SKEWY(cx, cy) (((XPoint *)0 == skews)?0:skews[cy*nx + cx].y)
543
544   while (tick < end_tick)
545     {
546       int i = (SIZE * (tick - start_tick)) / (end_tick - start_tick);
547
548       if (i == oi)
549         {
550           usleep (LITTLE_NAP);
551           nonhits++;
552         }
553       else
554         {
555           int j;
556           for (j = oi; j < i; j++)
557             {
558               int x = array[j] % BX;
559               int y = array[j] / BX;
560               int iy, cy;
561               for (iy = 0, cy = 0; iy < height; iy += BY, cy++)
562                 {
563                   int ix, cx;
564                   for( ix = 0, cx = 0; ix < width; ix += BX, cx++ ) {
565                     int xx = ix + (SKEWX(cx, cy) + x*((cx%(BX-1))+1))%BX;
566                     int yy = iy + (SKEWY(cx, cy) + y*((cy%(BY-1))+1))%BY;
567                     if (xx < width && yy < height)
568                       {
569                         points[npoints].x = xx;
570                         points[npoints].y = yy;
571                         if (++npoints == countof(points))
572                           {
573                             XDrawPoints(dpy, window, gc, points, npoints,
574                                         CoordModeOrigin);
575                             XSync (dpy, False);
576                             npoints = 0;
577                           }
578                       }
579                   }
580                 }
581             }
582           hits++;
583         }
584
585       oi = i;
586       tick = millitime();
587     }
588
589   if (npoints > 100)
590     {
591       XDrawPoints(dpy, window, gc, points, npoints, CoordModeOrigin);
592       XSync (dpy, False);
593       usleep (10000);
594     }
595
596 # undef SKEWX
597 # undef SKEWY
598 # undef BX
599 # undef BY
600 # undef SIZE
601
602   if (skews) free(skews);
603 }
604
605
606 /* from Rick Campbell <rick@campbellcentral.org> */
607 static void
608 spiral (Display *dpy, Window window, GC context,
609         int width, int height, int total_msecs)
610 {
611   int granularity = 1; /* #### */
612
613   double pi2 = (M_PI + M_PI);
614   int loop_count = 10;
615   int angle_step = 1000 / 8;  /* disc granularity is 8 degrees */
616   int max = pi2 * angle_step;
617   double angle;
618   int arc_limit;
619   int arc_max_limit;
620   int length_step;
621   XPoint points [3];
622
623   total_msecs *= 2.5;  /* this one needs more */
624
625   angle = 0.0;
626   arc_limit = 1;
627   arc_max_limit = (ceil (sqrt ((width * width) + (height * height))) / 2.0);
628   length_step = ((arc_max_limit + loop_count - 1) / loop_count);
629   arc_max_limit += length_step;
630   points [0].x = width / 2;
631   points [0].y = height / 2;
632   points [1].x = points [0].x + length_step;
633   points [1].y = points [0].y;
634   points [2].x = points [1].x;
635   points [2].y = points [1].y;
636
637   for (arc_limit = length_step;
638        arc_limit < arc_max_limit;
639        arc_limit += length_step)
640     {
641       int arc_length = length_step;
642       int length_base = arc_limit;
643
644       unsigned long start_tick = millitime();
645       unsigned long end_tick = start_tick + (total_msecs /
646                                              (arc_max_limit / length_step));
647       unsigned long tick = start_tick;
648       int hits = 0;
649       int nonhits = 0;
650       int i = 0;
651       int oi = -1;
652
653 #if 0
654       int max2 = max / granularity;
655       while (i < max2)
656 #else
657       while (tick < end_tick)
658 #endif
659         {
660           i = (max * (tick - start_tick)) / (end_tick - start_tick);
661           if (i > max) i = max;
662
663           i /= granularity;
664
665           if (i == oi)
666             {
667               usleep (LITTLE_NAP);
668               nonhits++;
669             }
670           else
671             {
672               int j, k;
673 #if 0
674               for (k = oi; k <= i; k++)
675 #else
676               k = i;
677 #endif
678                 {
679                   for (j = 0; j < granularity; j++)
680                     {
681                       int ii = k * granularity + j;
682                       angle = ii / (double) angle_step;
683                       arc_length = length_base + ((length_step * angle) / pi2);
684                       points [1].x = points [2].x;
685                       points [1].y = points [2].y;
686                       points [2].x = points [0].x +
687                         (int)(cos(angle) * arc_length);
688                       points [2].y = points [0].y +
689                         (int)(sin(angle) * arc_length);
690                       XFillPolygon (dpy, window, context, points, 3, Convex,
691                                     CoordModeOrigin);
692                       hits++;
693                     }
694                 }
695               XSync (dpy, False);
696             }
697
698           oi = i;
699           tick = millitime();
700         }
701     }
702 }
703
704
705 #undef MAX
706 #undef MIN
707 #define MAX(a,b) ((a)>(b)?(a):(b))
708 #define MIN(a,b) ((a)<(b)?(a):(b))
709
710 /* from David Bagley <bagleyd@tux.org> */
711 static void
712 random_squares(Display * dpy, Window window, GC gc,
713                int width, int height, int total_msecs)
714 {
715   int granularity = 20;
716
717   int randsize = MAX(1, MIN(width, height) / (16 + (random() % 32)));
718   int max = (height / randsize + 1) * (width / randsize + 1);
719   int *squares = (int *) calloc(max, sizeof (*squares));
720   int i;
721   int columns = width / randsize + 1;  /* Add an extra for roundoff */
722
723   int oi = -1;
724   unsigned long start_tick = millitime();
725   unsigned long end_tick = start_tick + total_msecs;
726   unsigned long tick = start_tick;
727   int hits = 0;
728   int nonhits = 0;
729
730   for (i = 0; i < max; i++)
731     squares[i] = i;
732
733   for (i = 0; i < max; i++)
734     {
735       int t, r;
736       t = squares[i];
737       r = random() % max;
738       squares[i] = squares[r];
739       squares[r] = t;
740     }
741
742   while (tick < end_tick)
743     {
744       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
745
746       i /= granularity;
747
748       if (i == oi)
749         {
750           usleep (LITTLE_NAP);
751           nonhits++;
752         }
753       else
754         {
755           int j;
756           for (j = 0; j < granularity; j++)
757             {
758               int ii = i * granularity + j;
759
760               XFillRectangle(dpy, window, gc,
761                              (squares[ii] % columns) * randsize,
762                              (squares[ii] / columns) * randsize,
763                              randsize, randsize);
764               hits++;
765             }
766         }
767       XSync (dpy, False);
768
769       oi = i;
770       tick = millitime();
771     }
772   free(squares);
773 }
774
775 /* I first saw something like this, albeit in reverse, in an early Tetris
776    implementation for the Mac.
777     -- Torbjörn Andersson <torbjorn@dev.eurotime.se>
778  */
779 static void
780 slide_lines (Display *dpy, Window window, GC gc,
781              int width, int height, int total_msecs)
782 {
783   int max = width;
784   int dy = MAX (10, height/40);
785
786   int oi = 0;
787   unsigned long start_tick = millitime();
788   unsigned long end_tick = start_tick + total_msecs;
789   unsigned long tick = start_tick;
790   int hits = 0;
791   int nonhits = 0;
792
793   while (tick < end_tick)
794     {
795       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
796
797       if (i == oi)
798         {
799           usleep (LITTLE_NAP);
800           nonhits++;
801         }
802       else
803         {
804           int y;
805           int tick = 0;
806           int from1 = oi;
807           int to1 = i;
808           int w = width-to1;
809           int from2 = width - oi - w;
810           int to2 = width - i - w;
811
812           for (y = 0; y < height; y += dy)
813             {
814               if (++tick & 1)
815                 {
816                   XCopyArea (dpy, window, window, gc, from1, y, w, dy, to1, y);
817                   XFillRectangle (dpy, window, gc, from1, y, to1-from1, dy);
818                 }
819               else
820                 {
821                   XCopyArea (dpy, window, window, gc, from2, y, w, dy, to2, y);
822                   XFillRectangle (dpy, window, gc, from2+w, y, to2-from2, dy);
823                 }
824             }
825
826           hits++;
827           XSync (dpy, False);
828         }
829
830       oi = i;
831       tick = millitime();
832     }
833 }
834
835
836 /* from Frederick Roeber <roeber@xigo.com> */
837 static void
838 losira (Display * dpy, Window window, GC gc,
839         int width, int height, int total_msecs)
840 {
841   XGCValues gcv;
842   XWindowAttributes wa;
843   XColor white;
844   GC white_gc; 
845   XArc arc[2][8];
846   double xx[8], yy[8], dx[8], dy[8];
847
848   int i;
849   int oi = 0;
850
851   int max = width/2;
852   int max_off = MAX(1, max / 12);
853
854   int msecs1 = (0.55 * total_msecs);
855   int msecs2 = (0.30 * total_msecs);
856   int msecs3 = (0.15 * total_msecs);
857
858   unsigned long start_tick = millitime();
859   unsigned long end_tick = start_tick + msecs1;
860   unsigned long tick = start_tick;
861   int hits = 0;
862   int nonhits = 0;
863
864   XGetWindowAttributes(dpy, window, &wa);
865   white.flags = DoRed|DoGreen|DoBlue;
866   white.red = white.green = white.blue = 65535;
867   XAllocColor(dpy, wa.colormap, &white);
868   gcv.foreground = white.pixel;
869   white_gc = XCreateGC(dpy, window, GCForeground, &gcv);
870
871   /* Squeeze in from the sides */
872   while (tick < end_tick)
873     {
874       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
875
876       if (i == oi)
877         {
878           usleep (LITTLE_NAP);
879           nonhits++;
880         }
881       else
882         {
883           int off = (max_off * (tick - start_tick)) / (end_tick - start_tick);
884
885           int from1 = oi;
886           int to1 = i;
887           int w = max - to1 - off/2 + 1;
888           int from2 = max+(to1-from1)+off/2;
889           int to2 = max+off/2;
890
891           if (w < 0)
892             break;
893
894           XCopyArea (dpy, window, window, gc, from1, 0, w, height, to1, 0);
895           XCopyArea (dpy, window, window, gc, from2, 0, w, height, to2, 0);
896           XFillRectangle (dpy, window, gc, from1, 0, (to1-from1), height);
897           XFillRectangle (dpy, window, gc, to2+w, 0, from2+w,     height);
898           XFillRectangle (dpy, window, white_gc, max-off/2, 0, off, height);
899           hits++;
900           XSync(dpy, False);
901         }
902
903       oi = i;
904       tick = millitime();
905     }
906
907
908   XFillRectangle(dpy, window, white_gc, max-max_off/2, 0, max_off, height);
909
910   /* Cap the top and bottom of the line */
911   XFillRectangle(dpy, window, gc, max-max_off/2, 0, max_off, max_off/2);
912   XFillRectangle(dpy, window, gc, max-max_off/2, height-max_off/2,
913                  max_off, max_off/2);
914   XFillArc(dpy, window, white_gc, max-max_off/2-1, 0,
915            max_off-1, max_off-1, 0, 180*64);
916   XFillArc(dpy, window, white_gc, max-max_off/2-1, height-max_off,
917            max_off-1, max_off-1,
918            180*64, 360*64);
919
920   XFillRectangle(dpy, window, gc, 0,               0, max-max_off/2, height);
921   XFillRectangle(dpy, window, gc, max+max_off/2-1, 0, max-max_off/2, height);
922   XSync(dpy, False);
923
924   /* Collapse vertically */
925   start_tick = millitime();
926   end_tick = start_tick + msecs2;
927   tick = start_tick;
928
929   max = height/2;
930   oi = 0;
931   while (tick < end_tick)
932     {
933       int i = (max * (tick - start_tick)) / (end_tick - start_tick);
934       int x = (width-max_off)/2;
935       int w = max_off;
936
937       if (i == oi)
938         {
939           usleep (LITTLE_NAP);
940           nonhits++;
941         }
942       else
943         {
944           int off = (max_off * (tick - start_tick)) / (end_tick - start_tick);
945
946           int from1 = oi;
947           int to1 = i;
948           int h = max - to1 - off/2;
949           int from2 = max+(to1-from1)+off/2;
950           int to2 = max+off/2;
951
952           if (h < max_off/2)
953             break;
954
955           XCopyArea (dpy, window, window, gc, x, from1, w, h, x, to1);
956           XCopyArea (dpy, window, window, gc, x, from2, w, h, x, to2);
957           XFillRectangle(dpy, window, gc, x, from1, w, (to1 - from1));
958           XFillRectangle(dpy, window, gc, x, to2+h, w, (to2 - from2));
959           hits++;
960           XSync(dpy, False);
961         }
962
963       oi = i;
964       tick = millitime();
965     }
966
967   /* "This is Sci-Fi" */
968   for( i = 0; i < 8; i++ ) {
969     arc[0][i].width = arc[0][i].height = max_off;
970     arc[1][i].width = arc[1][i].height = max_off;
971     arc[0][i].x = arc[1][i].x = width/2;
972     arc[0][i].y = arc[1][i].y = height/2;
973     xx[i] = (double)(width/2)  - max_off/2;
974     yy[i] = (double)(height/2) - max_off/2;
975   }
976
977   arc[0][0].angle1 = arc[1][0].angle1 =   0*64; arc[0][0].angle2 = arc[1][0].angle2 =  45*64;
978   arc[0][1].angle1 = arc[1][1].angle1 =  45*64; arc[0][1].angle2 = arc[1][1].angle2 =  45*64;
979   arc[0][2].angle1 = arc[1][2].angle1 =  90*64; arc[0][2].angle2 = arc[1][2].angle2 =  45*64;
980   arc[0][3].angle1 = arc[1][3].angle1 = 135*64; arc[0][3].angle2 = arc[1][3].angle2 =  45*64;
981   arc[0][4].angle1 = arc[1][4].angle1 = 180*64; arc[0][4].angle2 = arc[1][4].angle2 =  45*64;
982   arc[0][5].angle1 = arc[1][5].angle1 = 225*64; arc[0][5].angle2 = arc[1][5].angle2 =  45*64;
983   arc[0][6].angle1 = arc[1][6].angle1 = 270*64; arc[0][6].angle2 = arc[1][6].angle2 =  45*64;
984   arc[0][7].angle1 = arc[1][7].angle1 = 315*64; arc[0][7].angle2 = arc[1][7].angle2 =  45*64;
985   
986   for( i = 0; i < 8; i++ ) {
987     dx[i] =  cos((i*45 + 22.5)/360 * 2*M_PI);
988     dy[i] = -sin((i*45 + 22.5)/360 * 2*M_PI);
989   }
990
991   gcv.line_width = 3;  
992   XChangeGC(dpy, gc, GCLineWidth, &gcv);
993
994   XClearWindow (dpy, window);
995   XFillArc(dpy, window, white_gc,
996            width/2-max_off/2-1, height/2-max_off/2-1,
997            max_off-1, max_off-1,
998            0, 360*64);
999   XDrawLine(dpy, window, gc, 0, height/2-1, width, height/2-1);
1000   XDrawLine(dpy, window, gc, width/2-1, 0, width/2-1, height);
1001   XDrawLine(dpy, window, gc, width/2-1-max_off, height/2-1-max_off,
1002             width/2+max_off, height/2+max_off);
1003   XDrawLine(dpy, window, gc, width/2+max_off, height/2-1-max_off,
1004             width/2-1-max_off, height/2+max_off);
1005
1006   XSync(dpy, False);
1007
1008
1009   /* Fan out */
1010   start_tick = millitime();
1011   end_tick = start_tick + msecs3;
1012   tick = start_tick;
1013   oi = 0;
1014   while (tick < end_tick)
1015     {
1016       int i = (max_off * (tick - start_tick)) / (end_tick - start_tick);
1017
1018       if (i == oi)
1019         {
1020           usleep (LITTLE_NAP);
1021           nonhits++;
1022         }
1023       else
1024         {
1025           int j;
1026           for (j = 0; j < 8; j++)
1027             {
1028               xx[j] += 2*dx[j];
1029               yy[j] += 2*dy[j];
1030               arc[(i+1)%2][j].x = xx[j];
1031               arc[(i+1)%2][j].y = yy[j];
1032             }
1033
1034           XFillRectangle (dpy, window, gc,
1035                           (width-max_off*5)/2, (height-max_off*5)/2,
1036                           max_off*5, max_off*5);
1037           XFillArcs(dpy, window, white_gc, arc[(i+1)%2], 8);
1038           XSync(dpy, False);
1039           hits++;
1040         }
1041
1042       oi = i;
1043       tick = millitime();
1044     }
1045   
1046   XSync (dpy, False);
1047
1048   /*XFreeColors(dpy, wa.colormap, &white.pixel, 1, 0);*/
1049   XFreeGC(dpy, white_gc);
1050 }
1051
1052
1053
1054 static Eraser erasers[] = {
1055   random_lines,
1056   venetian,
1057   triple_wipe,
1058   quad_wipe,
1059   circle_wipe,
1060   three_circle_wipe,
1061   squaretate,
1062   fizzle,
1063   spiral,
1064   random_squares,
1065   slide_lines,
1066   losira,
1067 };
1068
1069
1070 static void
1071 erase_window (Display *dpy, Window window, GC gc,
1072               int width, int height, int mode, int total_msecs)
1073 {
1074   Bool verbose_p = False;
1075   unsigned long start = millitime();
1076
1077   if (mode < 0 || mode >= countof(erasers))
1078     mode = random() % countof(erasers);
1079
1080   (*(erasers[mode])) (dpy, window, gc, width, height, total_msecs);
1081
1082   if (verbose_p)
1083     fprintf(stderr, "%s: eraser %d time: %4.2f sec\n",
1084             progname, mode, (millitime() - start) / 1000.0);
1085
1086   XClearWindow (dpy, window);
1087   XSync(dpy, False);
1088   usleep (333333);  /* 1/3 sec */
1089 }
1090
1091
1092 void
1093 erase_full_window(Display *dpy, Window window)
1094 {
1095   XWindowAttributes xgwa;
1096   XGCValues gcv;
1097   GC erase_gc;
1098   XColor black;
1099   int erase_msecs, erase_mode;
1100   char *s;
1101
1102   s = get_string_resource("eraseSeconds", "Integer");
1103   if (s && *s)
1104     erase_msecs = 1000 * get_float_resource("eraseSeconds", "Float");
1105   else
1106     erase_msecs = 1000;
1107
1108   if (erase_msecs < 10 || erase_msecs > 10000)
1109     erase_msecs = 1000;
1110
1111   if (s) free(s);
1112
1113   s = get_string_resource("eraseMode", "Integer");
1114   if (s && *s)
1115     erase_mode = get_integer_resource("eraseMode", "Integer");
1116   else
1117     erase_mode = -1;
1118   if (s) free(s);
1119
1120   XGetWindowAttributes (dpy, window, &xgwa);
1121   black.flags = DoRed|DoGreen|DoBlue;
1122   black.red = black.green = black.blue = 0;
1123   XAllocColor(dpy, xgwa.colormap, &black);
1124   gcv.foreground = black.pixel;
1125   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1126   erase_window (dpy, window, erase_gc, xgwa.width, xgwa.height,
1127                 erase_mode, erase_msecs);
1128   XFreeColors(dpy, xgwa.colormap, &black.pixel, 1, 0);
1129   XFreeGC(dpy, erase_gc);
1130 }