Commit | Line | Data |
---|---|---|
3144ee8a AT |
1 | /* erase.c: Erase the screen in various more or less interesting ways. |
2 | * Copyright (c) 1997-2008 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 | #include "erase.h" | |
23 | #include <sys/time.h> /* for gettimeofday() */ | |
24 | ||
25 | extern char *progname; | |
26 | ||
27 | #undef countof | |
28 | #define countof(x) (sizeof(x)/sizeof(*(x))) | |
29 | ||
30 | typedef void (*Eraser) (eraser_state *); | |
31 | ||
32 | struct eraser_state { | |
33 | Display *dpy; | |
34 | Window window; | |
35 | GC fg_gc, bg_gc; | |
36 | int width, height; | |
37 | Eraser fn; | |
38 | ||
39 | double start_time, stop_time; | |
40 | double ratio, prev_ratio; | |
41 | ||
42 | /* data for random_lines, venetian, random_squares */ | |
43 | Bool horiz_p; | |
44 | Bool flip_p; | |
45 | int nlines, *lines; | |
46 | ||
47 | /* data for triple_wipe, quad_wipe */ | |
48 | Bool flip_x, flip_y; | |
49 | ||
50 | /* data for circle_wipe, three_circle_wipe */ | |
51 | int start; | |
52 | ||
53 | /* data for random_squares */ | |
54 | int cols; | |
55 | ||
56 | /* data for fizzle */ | |
57 | unsigned short *fizzle_rnd; | |
58 | ||
59 | }; | |
60 | ||
61 | ||
62 | static double | |
63 | double_time (void) | |
64 | { | |
65 | struct timeval now; | |
66 | # ifdef GETTIMEOFDAY_TWO_ARGS | |
67 | struct timezone tzp; | |
68 | gettimeofday(&now, &tzp); | |
69 | # else | |
70 | gettimeofday(&now); | |
71 | # endif | |
72 | ||
73 | return (now.tv_sec + ((double) now.tv_usec * 0.000001)); | |
74 | } | |
75 | ||
76 | ||
77 | static void | |
78 | random_lines (eraser_state *st) | |
79 | { | |
80 | int i; | |
81 | ||
82 | if (! st->lines) /* first time */ | |
83 | { | |
84 | st->horiz_p = (random() & 1); | |
85 | st->nlines = (st->horiz_p ? st->height : st->width); | |
86 | st->lines = (int *) calloc (st->nlines, sizeof(*st->lines)); | |
87 | ||
88 | for (i = 0; i < st->nlines; i++) /* every line */ | |
89 | st->lines[i] = i; | |
90 | ||
91 | for (i = 0; i < st->nlines; i++) /* shuffle */ | |
92 | { | |
93 | int t, r; | |
94 | t = st->lines[i]; | |
95 | r = random() % st->nlines; | |
96 | st->lines[i] = st->lines[r]; | |
97 | st->lines[r] = t; | |
98 | } | |
99 | } | |
100 | ||
101 | for (i = st->nlines * st->prev_ratio; | |
102 | i < st->nlines * st->ratio; | |
103 | i++) | |
104 | { | |
105 | if (st->horiz_p) | |
106 | XDrawLine (st->dpy, st->window, st->bg_gc, | |
107 | 0, st->lines[i], st->width, st->lines[i]); | |
108 | else | |
109 | XDrawLine (st->dpy, st->window, st->bg_gc, | |
110 | st->lines[i], 0, st->lines[i], st->height); | |
111 | } | |
112 | ||
113 | if (st->ratio >= 1.0) | |
114 | { | |
115 | free (st->lines); | |
116 | st->lines = 0; | |
117 | } | |
118 | } | |
119 | ||
120 | ||
121 | static void | |
122 | venetian (eraser_state *st) | |
123 | { | |
124 | int i; | |
125 | if (st->ratio == 0.0) | |
126 | { | |
127 | int j = 0; | |
128 | st->horiz_p = (random() & 1); | |
129 | st->flip_p = (random() & 1); | |
130 | st->nlines = (st->horiz_p ? st->height : st->width); | |
131 | st->lines = (int *) calloc (st->nlines, sizeof(*st->lines)); | |
132 | ||
133 | for (i = 0; i < st->nlines * 2; i++) | |
134 | { | |
135 | int line = ((i / 16) * 16) - ((i % 16) * 15); | |
136 | if (line >= 0 && line < st->nlines) | |
137 | st->lines[j++] = (st->flip_p ? st->nlines - line : line); | |
138 | } | |
139 | } | |
140 | ||
141 | ||
142 | for (i = st->nlines * st->prev_ratio; | |
143 | i < st->nlines * st->ratio; | |
144 | i++) | |
145 | { | |
146 | if (st->horiz_p) | |
147 | XDrawLine (st->dpy, st->window, st->bg_gc, | |
148 | 0, st->lines[i], st->width, st->lines[i]); | |
149 | else | |
150 | XDrawLine (st->dpy, st->window, st->bg_gc, | |
151 | st->lines[i], 0, st->lines[i], st->height); | |
152 | } | |
153 | ||
154 | if (st->ratio >= 1.0) | |
155 | { | |
156 | free (st->lines); | |
157 | st->lines = 0; | |
158 | } | |
159 | } | |
160 | ||
161 | ||
162 | static void | |
163 | triple_wipe (eraser_state *st) | |
164 | { | |
165 | int i; | |
166 | if (st->ratio == 0.0) | |
167 | { | |
168 | st->flip_x = random() & 1; | |
169 | st->flip_y = random() & 1; | |
170 | st->nlines = st->width + (st->height / 2); | |
171 | st->lines = (int *) calloc (st->nlines, sizeof(*st->lines)); | |
172 | ||
173 | for (i = 0; i < st->width / 2; i++) | |
174 | st->lines[i] = i * 2 + st->height; | |
175 | for (i = 0; i < st->height / 2; i++) | |
176 | st->lines[i + st->width / 2] = i*2; | |
177 | for (i = 0; i < st->width / 2; i++) | |
178 | st->lines[i + st->width / 2 + st->height / 2] = | |
179 | st->width - i * 2 - (st->width % 2 ? 0 : 1) + st->height; | |
180 | } | |
181 | ||
182 | for (i = st->nlines * st->prev_ratio; | |
183 | i < st->nlines * st->ratio; | |
184 | i++) | |
185 | { | |
186 | int x, y, x2, y2; | |
187 | ||
188 | if (st->lines[i] < st->height) | |
189 | x = 0, y = st->lines[i], x2 = st->width, y2 = y; | |
190 | else | |
191 | x = st->lines[i]-st->height, y = 0, x2 = x, y2 = st->height; | |
192 | ||
193 | if (st->flip_x) | |
194 | x = st->width - x, x2 = st->width - x2; | |
195 | if (st->flip_y) | |
196 | y = st->height - y, y2 = st->height - y2; | |
197 | ||
198 | XDrawLine (st->dpy, st->window, st->bg_gc, x, y, x2, y2); | |
199 | } | |
200 | ||
201 | if (st->ratio >= 1.0) | |
202 | { | |
203 | free (st->lines); | |
204 | st->lines = 0; | |
205 | } | |
206 | } | |
207 | ||
208 | ||
209 | static void | |
210 | quad_wipe (eraser_state *st) | |
211 | { | |
212 | int i; | |
213 | if (st->ratio == 0.0) | |
214 | { | |
215 | st->flip_x = random() & 1; | |
216 | st->flip_y = random() & 1; | |
217 | st->nlines = st->width + st->height; | |
218 | st->lines = (int *) calloc (st->nlines, sizeof(*st->lines)); | |
219 | ||
220 | for (i = 0; i < st->nlines/4; i++) | |
221 | { | |
222 | st->lines[i*4] = i*2; | |
223 | st->lines[i*4+1] = st->height - i*2 - (st->height % 2 ? 0 : 1); | |
224 | st->lines[i*4+2] = st->height + i*2; | |
225 | st->lines[i*4+3] = st->height + st->width - i*2 | |
226 | - (st->width % 2 ? 0 : 1); | |
227 | } | |
228 | } | |
229 | ||
230 | for (i = st->nlines * st->prev_ratio; | |
231 | i < st->nlines * st->ratio; | |
232 | i++) | |
233 | { | |
234 | int x, y, x2, y2; | |
235 | if (st->lines[i] < st->height) | |
236 | x = 0, y = st->lines[i], x2 = st->width, y2 = y; | |
237 | else | |
238 | x = st->lines[i] - st->height, y = 0, x2 = x, y2 = st->height; | |
239 | ||
240 | if (st->flip_x) | |
241 | x = st->width-x, x2 = st->width-x2; | |
242 | if (st->flip_y) | |
243 | y = st->height-y, y2 = st->height-y2; | |
244 | ||
245 | XDrawLine (st->dpy, st->window, st->bg_gc, x, y, x2, y2); | |
246 | } | |
247 | ||
248 | if (st->ratio >= 1.0) | |
249 | { | |
250 | free (st->lines); | |
251 | st->lines = 0; | |
252 | } | |
253 | } | |
254 | ||
255 | ||
256 | static void | |
257 | circle_wipe (eraser_state *st) | |
258 | { | |
259 | int rad = (st->width > st->height ? st->width : st->height); | |
260 | int max = 360 * 64; | |
261 | int th, oth; | |
262 | ||
263 | if (st->ratio == 0.0) | |
264 | { | |
265 | st->flip_p = random() & 1; | |
266 | st->start = random() % max; | |
267 | } | |
268 | ||
269 | th = max * st->ratio; | |
270 | oth = max * st->prev_ratio; | |
271 | if (st->flip_p) | |
272 | { | |
273 | th = max - th; | |
274 | oth = max - oth; | |
275 | } | |
276 | XFillArc (st->dpy, st->window, st->bg_gc, | |
277 | (st->width / 2) - rad, | |
278 | (st->height / 2) - rad, | |
279 | rad*2, rad*2, | |
280 | (st->start + oth) % max, | |
281 | th-oth); | |
282 | } | |
283 | ||
284 | ||
285 | static void | |
286 | three_circle_wipe (eraser_state *st) | |
287 | { | |
288 | int rad = (st->width > st->height ? st->width : st->height); | |
289 | int max = 360 * 64; | |
290 | int th, oth; | |
291 | int i; | |
292 | ||
293 | if (st->ratio == 0.0) | |
294 | st->start = random() % max; | |
295 | ||
296 | th = max/6 * st->ratio; | |
297 | oth = max/6 * st->prev_ratio; | |
298 | ||
299 | for (i = 0; i < 3; i++) | |
300 | { | |
301 | int off = i * max / 3; | |
302 | XFillArc (st->dpy, st->window, st->bg_gc, | |
303 | (st->width / 2) - rad, | |
304 | (st->height / 2) - rad, | |
305 | rad*2, rad*2, | |
306 | (st->start + off + oth) % max, | |
307 | th-oth); | |
308 | XFillArc (st->dpy, st->window, st->bg_gc, | |
309 | (st->width / 2) - rad, | |
310 | (st->height / 2) - rad, | |
311 | rad*2, rad*2, | |
312 | (st->start + off - oth) % max, | |
313 | oth-th); | |
314 | } | |
315 | } | |
316 | ||
317 | ||
318 | static void | |
319 | squaretate (eraser_state *st) | |
320 | { | |
321 | int max = ((st->width > st->height ? st->width : st->height) * 2); | |
322 | XPoint points [3]; | |
323 | int i = max * st->ratio; | |
324 | ||
325 | if (st->ratio == 0.0) | |
326 | st->flip_p = random() & 1; | |
327 | ||
328 | # define DRAW() \ | |
329 | if (st->flip_p) { \ | |
330 | points[0].x = st->width - points[0].x; \ | |
331 | points[1].x = st->width - points[1].x; \ | |
332 | points[2].x = st->width - points[2].x; \ | |
333 | } \ | |
334 | XFillPolygon (st->dpy, st->window, st->bg_gc, \ | |
335 | points, 3, Convex, CoordModeOrigin) | |
336 | ||
337 | points[0].x = 0; | |
338 | points[0].y = 0; | |
339 | points[1].x = st->width; | |
340 | points[1].y = 0; | |
341 | points[2].x = 0; | |
342 | points[2].y = points[0].y + ((i * st->height) / max); | |
343 | DRAW(); | |
344 | ||
345 | points[0].x = 0; | |
346 | points[0].y = 0; | |
347 | points[1].x = 0; | |
348 | points[1].y = st->height; | |
349 | points[2].x = ((i * st->width) / max); | |
350 | points[2].y = st->height; | |
351 | DRAW(); | |
352 | ||
353 | points[0].x = st->width; | |
354 | points[0].y = st->height; | |
355 | points[1].x = 0; | |
356 | points[1].y = st->height; | |
357 | points[2].x = st->width; | |
358 | points[2].y = st->height - ((i * st->height) / max); | |
359 | DRAW(); | |
360 | ||
361 | points[0].x = st->width; | |
362 | points[0].y = st->height; | |
363 | points[1].x = st->width; | |
364 | points[1].y = 0; | |
365 | points[2].x = st->width - ((i * st->width) / max); | |
366 | points[2].y = 0; | |
367 | DRAW(); | |
368 | # undef DRAW | |
369 | } | |
370 | ||
371 | ||
372 | static void | |
373 | fizzle (eraser_state *st) | |
374 | { | |
375 | const double overshoot = 1.0625; | |
376 | ||
377 | unsigned int x, y, i; | |
378 | const unsigned int size = 256; | |
379 | unsigned short *rnd; | |
380 | XPoint *points; | |
381 | unsigned int npoints = | |
382 | (unsigned int)(size * size * st->ratio * overshoot) - | |
383 | (unsigned int)(size * size * st->prev_ratio * overshoot); | |
384 | ||
385 | if (st->ratio >= 1.0) | |
386 | { | |
387 | free (st->fizzle_rnd); | |
388 | st->fizzle_rnd = NULL; | |
389 | return; | |
390 | } | |
391 | ||
392 | if (! st->fizzle_rnd) | |
393 | { | |
394 | unsigned int chunks = | |
395 | ((st->width + size - 1) / size) * ((st->height + size - 1) / size); | |
396 | unsigned int i; | |
397 | ||
398 | st->fizzle_rnd = | |
399 | (unsigned short *) calloc (sizeof(unsigned short), chunks); | |
400 | ||
401 | if (! st->fizzle_rnd) | |
402 | return; | |
403 | ||
404 | for (i = 0; i != chunks; i++) | |
405 | st->fizzle_rnd[i] = NRAND(0x10000) | 1; /* Seed can't be 0. */ | |
406 | } | |
407 | ||
408 | points = (XPoint *) malloc ((npoints + 1) * sizeof(*points)); | |
409 | if (! points) return; | |
410 | ||
411 | rnd = st->fizzle_rnd; | |
412 | ||
413 | for (y = 0; y < st->height; y += 256) | |
414 | { | |
415 | for (x = 0; x < st->width; x += 256) | |
416 | { | |
417 | unsigned int need0 = 0; | |
418 | unsigned short r = *rnd; | |
419 | for (i = 0; i != npoints; i++) | |
420 | { | |
421 | points[i].x = r % size + x; | |
422 | points[i].y = (r >> 8) % size + y; | |
423 | ||
424 | /* Xorshift. This has a period of 2^16, which exactly matches | |
425 | the number of pixels in each 256x256 chunk. | |
426 | ||
427 | Other shift constants are possible, but it's hard to say | |
428 | which constants are best: a 2^16 period PRNG will never score | |
429 | well on BigCrush. | |
430 | */ | |
431 | r = (r ^ (r << 3)) & 0xffff; | |
432 | r = r ^ (r >> 5); | |
433 | r = (r ^ (r << 11)) & 0xffff; | |
434 | need0 |= (r == 0x8080); /* Can be anything, really. */ | |
435 | } | |
436 | ||
437 | if (need0) | |
438 | { | |
439 | points[npoints].x = x; | |
440 | points[npoints].y = y; | |
441 | } | |
442 | ||
443 | XDrawPoints (st->dpy, st->window, st->bg_gc, | |
444 | points, npoints + need0, CoordModeOrigin); | |
445 | *rnd = r; | |
446 | rnd++; | |
447 | } | |
448 | } | |
449 | free (points); | |
450 | } | |
451 | ||
452 | ||
453 | static void | |
454 | spiral (eraser_state *st) | |
455 | { | |
456 | int max_radius = (st->width > st->height ? st->width : st->height) * 0.7; | |
457 | int loops = 10; | |
458 | float max_th = M_PI * 2 * loops; | |
459 | int i; | |
460 | int steps = 360 * loops / 4; | |
461 | float off; | |
462 | ||
463 | if (st->ratio == 0.0) | |
464 | { | |
465 | st->flip_p = random() & 1; | |
466 | st->start = random() % 360; | |
467 | } | |
468 | ||
469 | off = st->start * M_PI / 180; | |
470 | ||
471 | for (i = steps * st->prev_ratio; | |
472 | i < steps * st->ratio; | |
473 | i++) | |
474 | { | |
475 | float th1 = i * max_th / steps; | |
476 | float th2 = (i+1) * max_th / steps; | |
477 | int r1 = i * max_radius / steps; | |
478 | int r2 = (i+1) * max_radius / steps; | |
479 | XPoint points[3]; | |
480 | ||
481 | if (st->flip_p) | |
482 | { | |
483 | th1 = max_th - th1; | |
484 | th2 = max_th - th2; | |
485 | } | |
486 | ||
487 | points[0].x = st->width / 2; | |
488 | points[0].y = st->height / 2; | |
489 | points[1].x = points[0].x + r1 * cos (off + th1); | |
490 | points[1].y = points[0].y + r1 * sin (off + th1); | |
491 | points[2].x = points[0].x + r2 * cos (off + th2); | |
492 | points[2].y = points[0].y + r2 * sin (off + th2); | |
493 | /* XFillRectangle(st->dpy, st->window, st->fg_gc,0,0,st->width, st->height);*/ | |
494 | XFillPolygon (st->dpy, st->window, st->bg_gc, | |
495 | points, 3, Convex, CoordModeOrigin); | |
496 | } | |
497 | } | |
498 | ||
499 | ||
500 | static void | |
501 | random_squares (eraser_state *st) | |
502 | { | |
503 | int i, size, rows; | |
504 | ||
505 | if (st->ratio == 0.0) | |
506 | { | |
507 | st->cols = 10 + random() % 30; | |
508 | size = st->width / st->cols; | |
509 | rows = (size ? (st->height / size) : 0) + 1; | |
510 | st->nlines = st->cols * rows; | |
511 | st->lines = (int *) calloc (st->nlines, sizeof(*st->lines)); | |
512 | ||
513 | for (i = 0; i < st->nlines; i++) /* every square */ | |
514 | st->lines[i] = i; | |
515 | ||
516 | for (i = 0; i < st->nlines; i++) /* shuffle */ | |
517 | { | |
518 | int t, r; | |
519 | t = st->lines[i]; | |
520 | r = random() % st->nlines; | |
521 | st->lines[i] = st->lines[r]; | |
522 | st->lines[r] = t; | |
523 | } | |
524 | } | |
525 | ||
526 | size = st->width / st->cols; | |
527 | rows = (size ? (st->height / size) : 0) + 1; | |
528 | ||
529 | for (i = st->nlines * st->prev_ratio; | |
530 | i < st->nlines * st->ratio; | |
531 | i++) | |
532 | { | |
533 | int x = st->lines[i] % st->cols; | |
534 | int y = st->lines[i] / st->cols; | |
535 | XFillRectangle (st->dpy, st->window, st->bg_gc, | |
536 | st->width * x / st->cols, | |
537 | st->height * y / rows, | |
538 | size+1, size+1); | |
539 | } | |
540 | ||
541 | if (st->ratio >= 1.0) | |
542 | { | |
543 | free (st->lines); | |
544 | st->lines = 0; | |
545 | } | |
546 | } | |
547 | ||
548 | ||
549 | /* I first saw something like this, albeit in reverse, in an early Tetris | |
550 | implementation for the Mac. | |
551 |