| 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 |