851b5b490dfce2006d43377b00173e249bb0d3c9
[xscreensaver] / utils / erase.c
1 /* erase.c: Erase the screen in various more or less interesting ways.
2  * (c) 1997 by Johannes Keukelaar <johannes@nada.kth.se>
3  * Permission to use in any way granted. Provided "as is" without expressed
4  * or implied warranty. NO WARRANTY, NO EXPRESSION OF SUITABILITY FOR ANY
5  * PURPOSE. (I.e.: Use in any way, but at your own risk!)
6  */
7
8 #include "utils.h"
9 #include "yarandom.h"
10 #include "usleep.h"
11 #include "resources.h"
12
13 #undef countof
14 #define countof(x) (sizeof(x)/sizeof(*(x)))
15
16 typedef void (*Eraser) (Display *dpy, Window window, GC gc,
17                         int width, int height, int delay, int granularity);
18
19
20 static void
21 random_lines (Display *dpy, Window window, GC gc,
22               int width, int height, int delay, int granularity)
23 {
24   Bool horiz_p = (random() & 1);
25   int max = (horiz_p ? height : width);
26   int *lines = (int *) calloc(max, sizeof(*lines));
27   int i;
28
29   for (i = 0; i < max; i++)
30     lines[i] = i;
31
32   for (i = 0; i < max; i++)
33     {
34       int t, r;
35       t = lines[i];
36       r = random() % max;
37       lines[i] = lines[r];
38       lines[r] = t;
39     }
40
41   for (i = 0; i < max; i++)
42     { 
43       if (horiz_p)
44         XDrawLine (dpy, window, gc, 0, lines[i], width, lines[i]);
45       else
46         XDrawLine (dpy, window, gc, lines[i], 0, lines[i], height);
47
48       XSync (dpy, False);
49       if (delay > 0 && ((i % granularity) == 0))
50         usleep (delay * granularity);
51     }
52   free(lines);
53 }
54
55
56 static void
57 venetian (Display *dpy, Window window, GC gc,
58           int width, int height, int delay, int granularity)
59 {
60   Bool horiz_p = (random() & 1);
61   Bool flip_p = (random() & 1);
62   int max = (horiz_p ? height : width);
63   int *lines = (int *) calloc(max, sizeof(*lines));
64   int i, j;
65
66   granularity /= 6;
67
68   j = 0;
69   for (i = 0; i < max*2; i++)
70     {
71       int line = ((i / 16) * 16) - ((i % 16) * 15);
72       if (line >= 0 && line < max)
73         lines[j++] = (flip_p ? max - line : line);
74     }
75
76   for (i = 0; i < max; i++)
77     { 
78       if (horiz_p)
79         XDrawLine (dpy, window, gc, 0, lines[i], width, lines[i]);
80       else
81         XDrawLine (dpy, window, gc, lines[i], 0, lines[i], height);
82
83       XSync (dpy, False);
84       if (delay > 0 && ((i % granularity) == 0))
85         usleep (delay * granularity);
86     }
87   free(lines);
88 }
89
90
91 static void
92 triple_wipe (Display *dpy, Window window, GC gc,
93              int width, int height, int delay, int granularity)
94 {
95   Bool flip_x = random() & 1;
96   Bool flip_y = random() & 1;
97   int max = width + (height / 2);
98   int *lines = (int *)calloc(max, sizeof(int));
99   int i;
100
101   for(i = 0; i < width/2; i++)
102     lines[i] = i*2+height;
103   for(i = 0; i < height/2; i++)
104     lines[i+width/2] = i*2;
105   for(i = 0; i < width/2; i++)
106     lines[i+width/2+height/2] = width-i*2-(width%2?0:1)+height;
107
108   granularity /= 6;
109
110   for (i = 0; i < max; i++)
111     { 
112       int x, y, x2, y2;
113       if (lines[i] < height)
114         x = 0, y = lines[i], x2 = width, y2 = y;
115       else
116         x = lines[i]-height, y = 0, x2 = x, y2 = height;
117
118       if (flip_x)
119         x = width-x, x2 = width-x2;
120       if (flip_y)
121         y = height-y, y2 = height-y2;
122
123       XDrawLine (dpy, window, gc, x, y, x2, y2);
124       XSync (dpy, False);
125       if (delay > 0 && ((i % granularity) == 0))
126         usleep (delay*granularity);
127     }
128   free(lines);
129 }
130
131
132 static void
133 quad_wipe (Display *dpy, Window window, GC gc,
134            int width, int height, int delay, int granularity)
135 {
136   Bool flip_x = random() & 1;
137   Bool flip_y = random() & 1;
138   int max = width + height;
139   int *lines = (int *)calloc(max, sizeof(int));
140   int i;
141
142   granularity /= 3;
143
144   for (i = 0; i < max/4; i++)
145     {
146       lines[i*4]   = i*2;
147       lines[i*4+1] = height-i*2-(height%2?0:1);
148       lines[i*4+2] = height+i*2;
149       lines[i*4+3] = height+width-i*2-(width%2?0:1);
150     }
151
152   for (i = 0; i < max; i++)
153     { 
154       int x, y, x2, y2;
155       if (lines[i] < height)
156         x = 0, y = lines[i], x2 = width, y2 = y;
157       else
158         x = lines[i]-height, y = 0, x2 = x, y2 = height;
159
160       if (flip_x)
161         x = width-x, x2 = width-x2;
162       if (flip_y)
163         y = height-y, y2 = height-y2;
164
165       XDrawLine (dpy, window, gc, x, y, x2, y2);
166       XSync (dpy, False);
167       if (delay > 0 && ((i % granularity) == 0))
168         usleep (delay*granularity);
169     }
170   free(lines);
171 }
172
173
174
175 static void
176 circle_wipe (Display *dpy, Window window, GC gc,
177              int width, int height, int delay, int granularity)
178 {
179   int full = 360 * 64;
180   int inc = full / 64;
181   int start = random() % full;
182   int rad = (width > height ? width : height);
183   int i;
184   if (random() & 1)
185     inc = -inc;
186   for (i = (inc > 0 ? 0 : full);
187        (inc > 0 ? i < full : i > 0);
188        i += inc)
189     {
190       XFillArc(dpy, window, gc,
191                (width/2)-rad, (height/2)-rad, rad*2, rad*2,
192                (i+start) % full, inc);
193       XFlush (dpy);
194       usleep (delay*granularity);
195     }
196 }
197
198
199 static void
200 three_circle_wipe (Display *dpy, Window window, GC gc,
201                    int width, int height, int delay, int granularity)
202 {
203   int i;
204   int full = 360 * 64;
205   int q = full / 6;
206   int q2 = q * 2;
207   int inc = full / 240;
208   int start = random() % q;
209   int rad = (width > height ? width : height);
210
211   for (i = 0; i < q; i += inc)
212     {
213       XFillArc(dpy, window, gc, (width/2)-rad, (height/2)-rad, rad*2, rad*2,
214                (start+i) % full, inc);
215       XFillArc(dpy, window, gc, (width/2)-rad, (height/2)-rad, rad*2, rad*2,
216                (start-i) % full, -inc);
217
218       XFillArc(dpy, window, gc, (width/2)-rad, (height/2)-rad, rad*2, rad*2,
219                (start+q2+i) % full, inc);
220       XFillArc(dpy, window, gc, (width/2)-rad, (height/2)-rad, rad*2, rad*2,
221                (start+q2-i) % full, -inc);
222
223       XFillArc(dpy, window, gc, (width/2)-rad, (height/2)-rad, rad*2, rad*2,
224                (start+q2+q2+i) % full, inc);
225       XFillArc(dpy, window, gc, (width/2)-rad, (height/2)-rad, rad*2, rad*2,
226                (start+q2+q2-i) % full, -inc);
227
228       XSync (dpy, False);
229       usleep (delay*granularity);
230     }
231 }
232
233
234 static void
235 squaretate (Display *dpy, Window window, GC gc,
236             int width, int height, int delay, int granularity)
237 {
238   int steps = (((width > height ? width : width) * 2) / granularity);
239   int i;
240   Bool flip = random() & 1;
241
242 #define DRAW() \
243       if (flip) { \
244         points[0].x = width-points[0].x; \
245         points[1].x = width-points[1].x; \
246         points[2].x = width-points[2].x; } \
247       XFillPolygon (dpy, window, gc, points, 3, Convex, CoordModeOrigin)
248
249   for (i = 0; i < steps; i++)
250     {
251       XPoint points [3];
252       points[0].x = 0;
253       points[0].y = 0;
254       points[1].x = width;
255       points[1].y = 0;
256       points[2].x = 0;
257       points[2].y = points[0].y + ((i * height) / steps);
258       DRAW();
259
260       points[0].x = 0;
261       points[0].y = 0;
262       points[1].x = 0;
263       points[1].y = height;
264       points[2].x = ((i * width) / steps);
265       points[2].y = height;
266       DRAW();
267
268       points[0].x = width;
269       points[0].y = height;
270       points[1].x = 0;
271       points[1].y = height;
272       points[2].x = width;
273       points[2].y = height - ((i * height) / steps);
274       DRAW();
275
276       points[0].x = width;
277       points[0].y = height;
278       points[1].x = width;
279       points[1].y = 0;
280       points[2].x = width - ((i * width) / steps);
281       points[2].y = 0;
282       DRAW();
283
284       XSync (dpy, True);
285       if (delay > 0)
286         usleep (delay * granularity);
287    }
288 #undef DRAW
289 }
290
291
292 /* from Frederick Roeber <roeber@netscape.com> */
293 static void
294 fizzle (Display *dpy, Window window, GC gc,
295             int width, int height, int delay, int granularity)
296 {
297   /* These dimensions must be prime numbers.  They should be roughly the
298      square root of the width and height. */
299 # define BX 31
300 # define BY 31
301 # define SIZE (BX*BY)
302
303   int array[SIZE];
304   int i, j;
305   XPoint *skews;
306   int nx, ny;
307
308   /* Distribute the numbers [0,SIZE) randomly in the array */
309   {
310     int indices[SIZE];
311
312     for( i = 0; i < SIZE; i++ ) {
313       array[i] = -1;
314       indices[i] = i;
315     } 
316
317     for( i = 0; i < SIZE; i++ ) {
318       j = random()%(SIZE-i);
319       array[indices[j]] = i;
320       indices[j] = indices[SIZE-i-1];
321     }
322   }
323
324   /* nx, ny are the number of cells across and down, rounded up */
325   nx = width  / BX + (0 == (width %BX) ? 0 : 1);
326   ny = height / BY + (0 == (height%BY) ? 0 : 1);
327   skews = (XPoint *)malloc(sizeof(XPoint) * (nx*ny));
328   if( (XPoint *)0 != skews ) {
329     for( i = 0; i < nx; i++ ) {
330       for( j = 0; j < ny; j++ ) {
331         skews[j * nx + i].x = random()%BX;
332         skews[j * nx + i].y = random()%BY;
333       }
334     }
335   }
336
337 # define SKEWX(cx, cy) (((XPoint *)0 == skews)?0:skews[cy*nx + cx].x)
338 # define SKEWY(cx, cy) (((XPoint *)0 == skews)?0:skews[cy*nx + cx].y)
339
340   for( i = 0; i < SIZE; i++ ) {
341     int x = array[i] % BX;
342     int y = array[i] / BX;
343
344     {
345       int iy, cy;
346       for( iy = 0, cy = 0; iy < height; iy += BY, cy++ ) {
347         int ix, cx;
348         for( ix = 0, cx = 0; ix < width; ix += BX, cx++ ) {
349           int xx = ix + (SKEWX(cx, cy) + x*((cx%(BX-1))+1))%BX;
350           int yy = iy + (SKEWY(cx, cy) + y*((cy%(BY-1))+1))%BY;
351           XDrawPoint(dpy, window, gc, xx, yy);
352         }
353       }
354     }
355
356     if( (BX-1) == (i%BX) ) {
357       XSync (dpy, False);
358       usleep (delay*granularity);
359     }
360   }
361
362 # undef SKEWX
363 # undef SKEWY
364
365   if( (XPoint *)0 != skews ) {
366     free(skews);
367   }
368
369 # undef BX
370 # undef BY
371 # undef SIZE
372 }
373
374
375 /* from Rick Campbell <rick@campbellcentral.org> */
376 static void
377 spiral (Display *display, Window window, GC context,
378         int width, int height, int delay, int granularity)
379 {
380 # define SPIRAL_ERASE_PI_2 (M_PI + M_PI)
381 # define SPIRAL_ERASE_LOOP_COUNT (10)
382 # define SPIRAL_ERASE_ARC_COUNT (360.0)
383 # define SPIRAL_ERASE_ANGLE_INCREMENT (SPIRAL_ERASE_PI_2 /     \
384 SPIRAL_ERASE_ARC_COUNT)
385 # define SPIRAL_ERASE_DELAY (0)
386
387   double angle;
388   int arc_limit;
389   int arc_max_limit;
390   int length_step;
391   XPoint points [3];
392
393   angle = 0.0;
394   arc_limit = 1;
395   arc_max_limit = (int) (ceil (sqrt ((width * width) + (height * height)))
396                          / 2.0);
397   length_step = ((arc_max_limit + SPIRAL_ERASE_LOOP_COUNT - 1) /
398                  SPIRAL_ERASE_LOOP_COUNT);
399   arc_max_limit += length_step;
400   points [0].x = width / 2;
401   points [0].y = height / 2;
402   points [1].x = points [0].x + length_step;
403   points [1].y = points [0].y;
404   points [2].x = points [1].x;
405   points [2].y = points [1].y;
406
407   for (arc_limit = length_step;
408        arc_limit < arc_max_limit;
409        arc_limit += length_step)
410     {
411       int arc_length = length_step;
412       int length_base = arc_limit;
413       for (angle = 0.0; angle < SPIRAL_ERASE_PI_2;
414            angle += SPIRAL_ERASE_ANGLE_INCREMENT)
415         {
416           arc_length = length_base + ((length_step * angle) /
417                                       SPIRAL_ERASE_PI_2);
418           points [1].x = points [2].x;
419           points [1].y = points [2].y;
420           points [2].x = points [0].x + (int)(cos (angle) * arc_length);
421           points [2].y = points [0].y + (int)(sin (angle) * arc_length);
422           XFillPolygon (display, window, context, points, 3, Convex,
423                         CoordModeOrigin);
424 # if (SPIRAL_ERASE_DELAY != 0)
425           usleep (SPIRAL_ERASE_DELAY);
426 # endif /* (SPIRAL_ERASE_DELAY != 0) */
427         }
428     }
429 # undef SPIRAL_ERASE_DELAY
430 # undef SPIRAL_ERASE_ANGLE_INCREMENT
431 # undef SPIRAL_ERASE_ARC_COUNT
432 # undef SPIRAL_ERASE_LOOP_COUNT
433 # undef SPIRAL_ERASE_PI_2
434 }
435
436
437 #undef MAX
438 #undef MIN
439 #define MAX(a,b) ((a)>(b)?(a):(b))
440 #define MIN(a,b) ((a)<(b)?(a):(b))
441
442 /* from David Bagley <bagleyd@tux.org> */
443 static void
444 random_squares(Display * dpy, Window window, GC gc,
445                int width, int height, int delay, int granularity)
446 {
447   int randsize = MAX(1, MIN(width, height) / (16 + (random() % 32)));
448   int max = (height / randsize + 1) * (width / randsize + 1);
449   int *squares = (int *) calloc(max, sizeof (*squares));
450   int i;
451   int columns = width / randsize + 1;  /* Add an extra for roundoff */
452
453   for (i = 0; i < max; i++)
454     squares[i] = i;
455
456   for (i = 0; i < max; i++)
457     {
458       int t, r;
459       t = squares[i];
460       r = random() % max;
461       squares[i] = squares[r];
462       squares[r] = t;
463     }
464
465   for (i = 0; i < max; i++)
466     {
467       XFillRectangle(dpy, window, gc,
468                      (squares[i] % columns) * randsize,
469                      (squares[i] / columns) * randsize,
470                      randsize, randsize);
471
472       XSync(dpy, False);
473       if (delay > 0 && ((i % granularity) == 0))
474       usleep(delay * granularity);
475     }
476   free(squares);
477 }
478
479 /* I first saw something like this, albeit in reverse, in an early Tetris
480    implementation for the Mac.
481     -- Torbjörn Andersson <torbjorn@dev.eurotime.se>
482  */
483
484 static void
485 slide_lines (Display * dpy, Window window, GC gc, int width, int height,
486              int delay, int granularity)
487 {
488   int slide_old_x, slide_new_x, clear_x;
489   int x, y, dx, dy;
490
491   /* This might need some tuning. The idea is to get sensible values no
492      matter what the size of the window.
493
494      Everything moves at constant speed. Should it accelerate instead? */
495
496   granularity *= 2;
497
498   dy = MAX (1, height / granularity);
499   dx = MAX (1, width / 100);
500
501   for (x = 0; x < width; x += dx)
502     {
503       for (y = 0; y < height; y += dy)
504         {
505           if ((y / dy) & 1)
506             {
507               slide_old_x = x;
508               slide_new_x = x + dx;
509               clear_x = x;
510             }
511           else
512             {
513               slide_old_x = dx;
514               slide_new_x = 0;
515               clear_x = width - x - dx;
516             }
517
518           XCopyArea (dpy, window, window, gc, slide_old_x, y, width - x - dx,
519                      dy, slide_new_x, y);
520           XClearArea (dpy, window, clear_x, y, dx, dy, False);
521         }
522
523       XSync(dpy, False);
524       usleep(delay * 3);
525     }
526 }
527
528 static Eraser erasers[] = {
529   random_lines,
530   venetian,
531   triple_wipe,
532   quad_wipe,
533   circle_wipe,
534   three_circle_wipe,
535   squaretate,
536   fizzle,
537   random_squares,
538   spiral,
539   slide_lines,
540 };
541
542
543 void
544 erase_window(Display *dpy, Window window, GC gc,
545              int width, int height, int mode, int delay)
546 {
547   int granularity = 25;
548
549   if (mode < 0 || mode >= countof(erasers))
550     mode = random() % countof(erasers);
551   (*(erasers[mode])) (dpy, window, gc, width, height, delay, granularity);
552   XClearWindow (dpy, window);
553   XSync(dpy, False);
554 }
555
556
557 void
558 erase_full_window(Display *dpy, Window window)
559 {
560   XWindowAttributes xgwa;
561   XGCValues gcv;
562   GC erase_gc;
563   XColor black;
564   int erase_speed, erase_mode;
565   char *s;
566
567   s = get_string_resource("eraseSpeed", "Integer");
568   if (s && *s)
569     erase_speed = get_integer_resource("eraseSpeed", "Integer");
570   else
571     erase_speed = 400;
572   if (s) free(s);
573
574   s = get_string_resource("eraseMode", "Integer");
575   if (s && *s)
576     erase_mode = get_integer_resource("eraseMode", "Integer");
577   else
578     erase_mode = -1;
579   if (s) free(s);
580
581   XGetWindowAttributes (dpy, window, &xgwa);
582   black.flags = DoRed|DoGreen|DoBlue;
583   black.red = black.green = black.blue = 0;
584   XAllocColor(dpy, xgwa.colormap, &black);
585   gcv.foreground = black.pixel;
586   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
587   erase_window (dpy, window, erase_gc, xgwa.width, xgwa.height,
588                 erase_mode, erase_speed);
589   XFreeColors(dpy, xgwa.colormap, &black.pixel, 1, 0);
590   XFreeGC(dpy, erase_gc);
591 }
592
593
594 \f
595 #if 0
596 #include "screenhack.h"
597
598 char *progclass = "Erase";
599 char *defaults [] = {
600   0
601 };
602
603 XrmOptionDescRec options [] = {{0}};
604 int options_size = 0;
605
606 void
607 screenhack (dpy, window)
608      Display *dpy;
609      Window window;
610 {
611   int delay = 500000;
612   XGCValues gcv;
613   GC gc;
614   XColor white;
615   XWindowAttributes xgwa;
616   XGetWindowAttributes (dpy, window, &xgwa);
617   white.flags = DoRed|DoGreen|DoBlue;
618   white.red = white.green = white.blue = 0xFFFF;
619   XAllocColor(dpy, xgwa.colormap, &white);
620   gcv.foreground = white.pixel;
621   gc = XCreateGC (dpy, window, GCForeground, &gcv);
622
623   while (1)
624     {
625       XFillRectangle(dpy, window, gc, 0, 0, 1280, 1024);
626       XSync (dpy, False);
627       usleep (delay);
628       erase_full_window(dpy, window);
629       XSync (dpy, False);
630       usleep (delay);
631
632     }
633 }
634
635 #endif