1 /* erase.c: Erase the screen in various more or less interesting ways.
2 * Copyright (c) 1997-2001 Jamie Zawinski <jwz@jwz.org>
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
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!)
21 #include "resources.h"
23 extern char *progname;
26 #define countof(x) (sizeof(x)/sizeof(*(x)))
28 #define LITTLE_NAP 5000 /* 1/200th sec */
30 typedef void (*Eraser) (Display *dpy, Window window, GC gc,
31 int width, int height, int total_msecs);
38 # ifdef GETTIMEOFDAY_TWO_ARGS
40 gettimeofday (&tt, &tz);
44 return (tt.tv_sec * 1000) + (tt.tv_usec / 1000);
49 random_lines (Display *dpy, Window window, GC gc,
50 int width, int height, int total_msecs)
54 Bool horiz_p = (random() & 1);
55 int max = (horiz_p ? height : width);
56 int *lines = (int *) calloc(max, sizeof(*lines));
59 unsigned long start_tick = millitime();
60 unsigned long end_tick = start_tick + total_msecs;
61 unsigned long tick = start_tick;
65 for (i = 0; i < max; i++)
68 for (i = 0; i < max; i++)
77 while (tick < end_tick)
79 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
91 for (j = 0; j < granularity; j++)
93 int ii = i * granularity + j;
95 XDrawLine (dpy, window, gc, 0, lines[ii], width, lines[ii]);
97 XDrawLine (dpy, window, gc, lines[ii], 0, lines[ii], height);
112 venetian (Display *dpy, Window window, GC gc,
113 int width, int height, int total_msecs)
115 Bool horiz_p = (random() & 1);
116 Bool flip_p = (random() & 1);
117 int max = (horiz_p ? height : width);
118 int *lines = (int *) calloc(max, sizeof(*lines));
122 unsigned long start_tick = millitime();
123 unsigned long end_tick = (start_tick +
124 (1.5 * total_msecs)); /* this one needs more */
125 unsigned long tick = start_tick;
130 for (i = 0; i < max*2; i++)
132 int line = ((i / 16) * 16) - ((i % 16) * 15);
133 if (line >= 0 && line < max)
134 lines[j++] = (flip_p ? max - line : line);
137 while (tick < end_tick)
139 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
149 for (k = oi; k <= i; k++)
152 XDrawLine(dpy,window, gc, 0, lines[k], width, lines[k]);
154 XDrawLine(dpy,window, gc, lines[k], 0, lines[k], height);
168 triple_wipe (Display *dpy, Window window, GC gc,
169 int width, int height, int total_msecs)
171 Bool flip_x = random() & 1;
172 Bool flip_y = random() & 1;
173 int max = width + (height / 2);
174 int *lines = (int *)calloc(max, sizeof(int));
178 unsigned long start_tick = millitime();
179 unsigned long end_tick = start_tick + total_msecs;
180 unsigned long tick = start_tick;
184 for(i = 0; i < width/2; i++)
185 lines[i] = i*2+height;
186 for(i = 0; i < height/2; i++)
187 lines[i+width/2] = i*2;
188 for(i = 0; i < width/2; i++)
189 lines[i+width/2+height/2] = width-i*2-(width%2?0:1)+height;
191 while (tick < end_tick)
193 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
204 for (k = oi; k <= i; k++)
206 if (lines[k] < height)
207 x = 0, y = lines[k], x2 = width, y2 = y;
209 x = lines[k]-height, y = 0, x2 = x, y2 = height;
212 x = width-x, x2 = width-x2;
214 y = height-y, y2 = height-y2;
216 XDrawLine (dpy, window, gc, x, y, x2, y2);
230 quad_wipe (Display *dpy, Window window, GC gc,
231 int width, int height, int total_msecs)
233 Bool flip_x = random() & 1;
234 Bool flip_y = random() & 1;
235 int max = width + height;
236 int *lines = (int *)calloc(max, sizeof(int));
240 unsigned long start_tick = millitime();
241 unsigned long end_tick = start_tick + total_msecs;
242 unsigned long tick = start_tick;
246 for (i = 0; i < max/4; i++)
249 lines[i*4+1] = height-i*2-(height%2?0:1);
250 lines[i*4+2] = height+i*2;
251 lines[i*4+3] = height+width-i*2-(width%2?0:1);
254 while (tick < end_tick)
256 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
266 for (k = oi; k <= i; k++)
269 if (lines[k] < height)
270 x = 0, y = lines[k], x2 = width, y2 = y;
272 x = lines[k]-height, y = 0, x2 = x, y2 = height;
275 x = width-x, x2 = width-x2;
277 y = height-y, y2 = height-y2;
279 XDrawLine (dpy, window, gc, x, y, x2, y2);
295 circle_wipe (Display *dpy, Window window, GC gc,
296 int width, int height, int total_msecs)
299 int start = random() % max;
300 int rad = (width > height ? width : height);
301 int flip_p = random() & 1;
304 unsigned long start_tick = millitime();
305 unsigned long end_tick = start_tick + total_msecs;
306 unsigned long tick = start_tick;
311 oth = (flip_p ? max : 0);
312 while (tick < end_tick)
314 int th = (max * (tick - start_tick)) / (end_tick - start_tick);
316 th = (360 * 64) - th;
325 XFillArc(dpy, window, gc,
326 (width/2)-rad, (height/2)-rad, rad*2, rad*2,
327 (start+oth)%(360*64),
340 three_circle_wipe (Display *dpy, Window window, GC gc,
341 int width, int height, int total_msecs)
343 int max = (360 * 64) / 6;
344 int start = random() % max;
345 int rad = (width > height ? width : height);
348 unsigned long start_tick = millitime();
349 unsigned long end_tick = start_tick + total_msecs;
350 unsigned long tick = start_tick;
356 while (tick < end_tick)
358 int th = (max * (tick - start_tick)) / (end_tick - start_tick);
368 XFillArc(dpy, window, gc,
369 (width/2)-rad, (height/2)-rad, rad*2, rad*2,
370 (start+off+oth)%(360*64),
372 XFillArc(dpy, window, gc,
373 (width/2)-rad, (height/2)-rad, rad*2, rad*2,
374 ((start+off-oth))%(360*64),
378 XFillArc(dpy, window, gc,
379 (width/2)-rad, (height/2)-rad, rad*2, rad*2,
380 (start+off+oth)%(360*64),
382 XFillArc(dpy, window, gc,
383 (width/2)-rad, (height/2)-rad, rad*2, rad*2,
384 ((start+off-oth))%(360*64),
388 XFillArc(dpy, window, gc,
389 (width/2)-rad, (height/2)-rad, rad*2, rad*2,
390 (start+off+oth)%(360*64),
392 XFillArc(dpy, window, gc,
393 (width/2)-rad, (height/2)-rad, rad*2, rad*2,
394 ((start+off-oth))%(360*64),
408 squaretate (Display *dpy, Window window, GC gc,
409 int width, int height, int total_msecs)
411 int max = ((width > height ? width : width) * 2);
413 Bool flip = random() & 1;
415 unsigned long start_tick = millitime();
416 unsigned long end_tick = start_tick + total_msecs;
417 unsigned long tick = start_tick;
423 points[0].x = width-points[0].x; \
424 points[1].x = width-points[1].x; \
425 points[2].x = width-points[2].x; } \
426 XFillPolygon (dpy, window, gc, points, 3, Convex, CoordModeOrigin)
428 while (tick < end_tick)
430 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
445 points[2].y = points[0].y + ((i * height) / max);
451 points[1].y = height;
452 points[2].x = ((i * width) / max);
453 points[2].y = height;
457 points[0].y = height;
459 points[1].y = height;
461 points[2].y = height - ((i * height) / max);
465 points[0].y = height;
468 points[2].x = width - ((i * width) / max);
482 /* from Frederick Roeber <roeber@netscape.com> */
484 fizzle (Display *dpy, Window window, GC gc,
485 int width, int height, int total_msecs)
487 /* These dimensions must be prime numbers. They should be roughly the
488 square root of the width and height. */
491 # define SIZE (BX*BY)
501 unsigned long start_tick = millitime();
502 unsigned long end_tick = (start_tick +
503 (2.5 * total_msecs)); /* this one needs more */
504 unsigned long tick = start_tick;
508 /* Distribute the numbers [0,SIZE) randomly in the array */
512 for( i = 0; i < SIZE; i++ ) {
517 for( i = 0; i < SIZE; i++ ) {
518 j = random()%(SIZE-i);
519 array[indices[j]] = i;
520 indices[j] = indices[SIZE-i-1];
524 /* nx, ny are the number of cells across and down, rounded up */
525 nx = width / BX + (0 == (width %BX) ? 0 : 1);
526 ny = height / BY + (0 == (height%BY) ? 0 : 1);
527 skews = (XPoint *)malloc(sizeof(XPoint) * (nx*ny));
528 if( (XPoint *)0 != skews ) {
529 for( i = 0; i < nx; i++ ) {
530 for( j = 0; j < ny; j++ ) {
531 skews[j * nx + i].x = random()%BX;
532 skews[j * nx + i].y = random()%BY;
537 # define SKEWX(cx, cy) (((XPoint *)0 == skews)?0:skews[cy*nx + cx].x)
538 # define SKEWY(cx, cy) (((XPoint *)0 == skews)?0:skews[cy*nx + cx].y)
540 while (tick < end_tick)
542 int i = (SIZE * (tick - start_tick)) / (end_tick - start_tick);
552 for (j = oi; j < i; j++)
554 int x = array[j] % BX;
555 int y = array[j] / BX;
557 for (iy = 0, cy = 0; iy < height; iy += BY, cy++)
560 for( ix = 0, cx = 0; ix < width; ix += BX, cx++ ) {
561 int xx = ix + (SKEWX(cx, cy) + x*((cx%(BX-1))+1))%BX;
562 int yy = iy + (SKEWY(cx, cy) + y*((cy%(BY-1))+1))%BY;
563 if (xx < width && yy < height)
565 points[npoints].x = xx;
566 points[npoints].y = yy;
567 if (++npoints == countof(points))
569 XDrawPoints(dpy, window, gc, points, npoints,
587 XDrawPoints(dpy, window, gc, points, npoints, CoordModeOrigin);
598 if (skews) free(skews);
602 /* from Rick Campbell <rick@campbellcentral.org> */
604 spiral (Display *dpy, Window window, GC context,
605 int width, int height, int total_msecs)
607 int granularity = 1; /* #### */
609 double pi2 = (M_PI + M_PI);
611 int angle_step = 1000 / 8; /* disc granularity is 8 degrees */
612 int max = pi2 * angle_step;
619 total_msecs *= 2.5; /* this one needs more */
623 arc_max_limit = (ceil (sqrt ((width * width) + (height * height))) / 2.0);
624 length_step = ((arc_max_limit + loop_count - 1) / loop_count);
625 arc_max_limit += length_step;
626 points [0].x = width / 2;
627 points [0].y = height / 2;
628 points [1].x = points [0].x + length_step;
629 points [1].y = points [0].y;
630 points [2].x = points [1].x;
631 points [2].y = points [1].y;
633 for (arc_limit = length_step;
634 arc_limit < arc_max_limit;
635 arc_limit += length_step)
637 int arc_length = length_step;
638 int length_base = arc_limit;
640 unsigned long start_tick = millitime();
641 unsigned long end_tick = start_tick + (total_msecs /
642 (arc_max_limit / length_step));
643 unsigned long tick = start_tick;
650 int max2 = max / granularity;
653 while (tick < end_tick)
656 i = (max * (tick - start_tick)) / (end_tick - start_tick);
657 if (i > max) i = max;
670 for (k = oi; k <= i; k++)
675 for (j = 0; j < granularity; j++)
677 int ii = k * granularity + j;
678 angle = ii / (double) angle_step;
679 arc_length = length_base + ((length_step * angle) / pi2);
680 points [1].x = points [2].x;
681 points [1].y = points [2].y;
682 points [2].x = points [0].x +
683 (int)(cos(angle) * arc_length);
684 points [2].y = points [0].y +
685 (int)(sin(angle) * arc_length);
686 XFillPolygon (dpy, window, context, points, 3, Convex,
703 #define MAX(a,b) ((a)>(b)?(a):(b))
704 #define MIN(a,b) ((a)<(b)?(a):(b))
706 /* from David Bagley <bagleyd@tux.org> */
708 random_squares(Display * dpy, Window window, GC gc,
709 int width, int height, int total_msecs)
711 int granularity = 20;
713 int randsize = MAX(1, MIN(width, height) / (16 + (random() % 32)));
714 int max = (height / randsize + 1) * (width / randsize + 1);
715 int *squares = (int *) calloc(max, sizeof (*squares));
717 int columns = width / randsize + 1; /* Add an extra for roundoff */
720 unsigned long start_tick = millitime();
721 unsigned long end_tick = start_tick + total_msecs;
722 unsigned long tick = start_tick;
726 for (i = 0; i < max; i++)
729 for (i = 0; i < max; i++)
734 squares[i] = squares[r];
738 while (tick < end_tick)
740 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
752 for (j = 0; j < granularity; j++)
754 int ii = i * granularity + j;
756 XFillRectangle(dpy, window, gc,
757 (squares[ii] % columns) * randsize,
758 (squares[ii] / columns) * randsize,
771 /* I first saw something like this, albeit in reverse, in an early Tetris
772 implementation for the Mac.
773 -- Torbjörn Andersson <torbjorn@dev.eurotime.se>
776 slide_lines (Display *dpy, Window window, GC gc,
777 int width, int height, int total_msecs)
780 int dy = MAX (10, height/40);
783 unsigned long start_tick = millitime();
784 unsigned long end_tick = start_tick + total_msecs;
785 unsigned long tick = start_tick;
789 while (tick < end_tick)
791 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
805 int from2 = width - oi - w;
806 int to2 = width - i - w;
808 for (y = 0; y < height; y += dy)
812 XCopyArea (dpy, window, window, gc, from1, y, w, dy, to1, y);
813 XFillRectangle (dpy, window, gc, from1, y, to1-from1, dy);
817 XCopyArea (dpy, window, window, gc, from2, y, w, dy, to2, y);
818 XFillRectangle (dpy, window, gc, from2+w, y, to2-from2, dy);
832 /* from Frederick Roeber <roeber@xigo.com> */
834 losira (Display * dpy, Window window, GC gc,
835 int width, int height, int total_msecs)
838 XWindowAttributes wa;
842 double xx[8], yy[8], dx[8], dy[8];
848 int max_off = MAX(1, max / 12);
850 int msecs1 = (0.55 * total_msecs);
851 int msecs2 = (0.30 * total_msecs);
852 int msecs3 = (0.15 * total_msecs);
854 unsigned long start_tick = millitime();
855 unsigned long end_tick = start_tick + msecs1;
856 unsigned long tick = start_tick;
860 XGetWindowAttributes(dpy, window, &wa);
861 white.flags = DoRed|DoGreen|DoBlue;
862 white.red = white.green = white.blue = 65535;
863 XAllocColor(dpy, wa.colormap, &white);
864 gcv.foreground = white.pixel;
865 white_gc = XCreateGC(dpy, window, GCForeground, &gcv);
867 /* Squeeze in from the sides */
868 while (tick < end_tick)
870 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
879 int off = (max_off * (tick - start_tick)) / (end_tick - start_tick);
883 int w = max - to1 - off/2 + 1;
884 int from2 = max+(to1-from1)+off/2;
890 XCopyArea (dpy, window, window, gc, from1, 0, w, height, to1, 0);
891 XCopyArea (dpy, window, window, gc, from2, 0, w, height, to2, 0);
892 XFillRectangle (dpy, window, gc, from1, 0, (to1-from1), height);
893 XFillRectangle (dpy, window, gc, to2+w, 0, from2+w, height);
894 XFillRectangle (dpy, window, white_gc, max-off/2, 0, off, height);
904 XFillRectangle(dpy, window, white_gc, max-max_off/2, 0, max_off, height);
906 /* Cap the top and bottom of the line */
907 XFillRectangle(dpy, window, gc, max-max_off/2, 0, max_off, max_off/2);
908 XFillRectangle(dpy, window, gc, max-max_off/2, height-max_off/2,
910 XFillArc(dpy, window, white_gc, max-max_off/2-1, 0,
911 max_off-1, max_off-1, 0, 180*64);
912 XFillArc(dpy, window, white_gc, max-max_off/2-1, height-max_off,
913 max_off-1, max_off-1,
916 XFillRectangle(dpy, window, gc, 0, 0, max-max_off/2, height);
917 XFillRectangle(dpy, window, gc, max+max_off/2-1, 0, max-max_off/2, height);
920 /* Collapse vertically */
921 start_tick = millitime();
922 end_tick = start_tick + msecs2;
927 while (tick < end_tick)
929 int i = (max * (tick - start_tick)) / (end_tick - start_tick);
930 int x = (width-max_off)/2;
940 int off = (max_off * (tick - start_tick)) / (end_tick - start_tick);
944 int h = max - to1 - off/2;
945 int from2 = max+(to1-from1)+off/2;
951 XCopyArea (dpy, window, window, gc, x, from1, w, h, x, to1);
952 XCopyArea (dpy, window, window, gc, x, from2, w, h, x, to2);
953 XFillRectangle(dpy, window, gc, x, from1, w, (to1 - from1));
954 XFillRectangle(dpy, window, gc, x, to2+h, w, (to2 - from2));
963 /* "This is Sci-Fi" */
964 for( i = 0; i < 8; i++ ) {
965 arc[0][i].width = arc[0][i].height = max_off;
966 arc[1][i].width = arc[1][i].height = max_off;
967 arc[0][i].x = arc[1][i].x = width/2;
968 arc[0][i].y = arc[1][i].y = height/2;
969 xx[i] = (double)(width/2) - max_off/2;
970 yy[i] = (double)(height/2) - max_off/2;
973 arc[0][0].angle1 = arc[1][0].angle1 = 0*64; arc[0][0].angle2 = arc[1][0].angle2 = 45*64;
974 arc[0][1].angle1 = arc[1][1].angle1 = 45*64; arc[0][1].angle2 = arc[1][1].angle2 = 45*64;
975 arc[0][2].angle1 = arc[1][2].angle1 = 90*64; arc[0][2].angle2 = arc[1][2].angle2 = 45*64;
976 arc[0][3].angle1 = arc[1][3].angle1 = 135*64; arc[0][3].angle2 = arc[1][3].angle2 = 45*64;
977 arc[0][4].angle1 = arc[1][4].angle1 = 180*64; arc[0][4].angle2 = arc[1][4].angle2 = 45*64;
978 arc[0][5].angle1 = arc[1][5].angle1 = 225*64; arc[0][5].angle2 = arc[1][5].angle2 = 45*64;
979 arc[0][6].angle1 = arc[1][6].angle1 = 270*64; arc[0][6].angle2 = arc[1][6].angle2 = 45*64;
980 arc[0][7].angle1 = arc[1][7].angle1 = 315*64; arc[0][7].angle2 = arc[1][7].angle2 = 45*64;
982 for( i = 0; i < 8; i++ ) {
983 dx[i] = cos((i*45 + 22.5)/360 * 2*M_PI);
984 dy[i] = -sin((i*45 + 22.5)/360 * 2*M_PI);
988 XChangeGC(dpy, gc, GCLineWidth, &gcv);
990 XClearWindow (dpy, window);
991 XFillArc(dpy, window, white_gc,
992 width/2-max_off/2-1, height/2-max_off/2-1,
993 max_off-1, max_off-1,
995 XDrawLine(dpy, window, gc, 0, height/2-1, width, height/2-1);
996 XDrawLine(dpy, window, gc, width/2-1, 0, width/2-1, height);
997 XDrawLine(dpy, window, gc, width/2-1-max_off, height/2-1-max_off,
998 width/2+max_off, height/2+max_off);
999 XDrawLine(dpy, window, gc, width/2+max_off, height/2-1-max_off,
1000 width/2-1-max_off, height/2+max_off);
1006 start_tick = millitime();
1007 end_tick = start_tick + msecs3;
1010 while (tick < end_tick)
1012 int i = (max_off * (tick - start_tick)) / (end_tick - start_tick);
1016 usleep (LITTLE_NAP);
1022 for (j = 0; j < 8; j++)
1026 arc[(i+1)%2][j].x = xx[j];
1027 arc[(i+1)%2][j].y = yy[j];
1030 XFillRectangle (dpy, window, gc,
1031 (width-max_off*5)/2, (height-max_off*5)/2,
1032 max_off*5, max_off*5);
1033 XFillArcs(dpy, window, white_gc, arc[(i+1)%2], 8);
1044 /*XFreeColors(dpy, wa.colormap, &white.pixel, 1, 0);*/
1045 XFreeGC(dpy, white_gc);
1050 static Eraser erasers[] = {
1067 erase_window (Display *dpy, Window window, GC gc,
1068 int width, int height, int mode, int total_msecs)
1070 Bool verbose_p = False;
1071 unsigned long start = millitime();
1073 if (mode < 0 || mode >= countof(erasers))
1074 mode = random() % countof(erasers);
1076 (*(erasers[mode])) (dpy, window, gc, width, height, total_msecs);
1079 fprintf(stderr, "%s: eraser %d time: %4.2f sec\n",
1080 progname, mode, (millitime() - start) / 1000.0);
1082 XClearWindow (dpy, window);
1084 usleep (333333); /* 1/3 sec */
1089 erase_full_window(Display *dpy, Window window)
1091 XWindowAttributes xgwa;
1095 int erase_msecs, erase_mode;
1098 s = get_string_resource("eraseSeconds", "Integer");
1100 erase_msecs = 1000 * get_float_resource("eraseSeconds", "Float");
1104 if (erase_msecs < 10 || erase_msecs > 10000)
1109 s = get_string_resource("eraseMode", "Integer");
1111 erase_mode = get_integer_resource("eraseMode", "Integer");
1116 XGetWindowAttributes (dpy, window, &xgwa);
1117 black.flags = DoRed|DoGreen|DoBlue;
1118 black.red = black.green = black.blue = 0;
1119 XAllocColor(dpy, xgwa.colormap, &black);
1120 gcv.foreground = black.pixel;
1121 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1122 erase_window (dpy, window, erase_gc, xgwa.width, xgwa.height,
1123 erase_mode, erase_msecs);
1124 XFreeColors(dpy, xgwa.colormap, &black.pixel, 1, 0);
1125 XFreeGC(dpy, erase_gc);