/* erase.c: Erase the screen in various more or less interesting ways.
* Copyright (c) 1997-2008 Jamie Zawinski <jwz@jwz.org>
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* Portions (c) 1997 by Johannes Keukelaar <johannes@nada.kth.se>:
* Permission to use in any way granted. Provided "as is" without expressed
* or implied warranty. NO WARRANTY, NO EXPRESSION OF SUITABILITY FOR ANY
* PURPOSE. (I.e.: Use in any way, but at your own risk!)
#include <sys/time.h> /* for gettimeofday() */
#define countof(x) (sizeof(x)/sizeof(*(x)))
typedef void (*Eraser
) (eraser_state
*);
double start_time
, stop_time
;
double ratio
, prev_ratio
;
/* data for random_lines, venetian, random_squares */
/* data for triple_wipe, quad_wipe */
/* data for circle_wipe, three_circle_wipe */
/* data for random_squares */
unsigned short *fizzle_rnd
;
# ifdef GETTIMEOFDAY_TWO_ARGS
gettimeofday(&now
, &tzp
);
return (now
.tv_sec
+ ((double) now
.tv_usec
* 0.000001));
random_lines (eraser_state
*st
)
if (! st
->lines
) /* first time */
st
->horiz_p
= (random() & 1);
st
->nlines
= (st
->horiz_p
? st
->height
: st
->width
);
st
->lines
= (int *) calloc (st
->nlines
, sizeof(*st
->lines
));
for (i
= 0; i
< st
->nlines
; i
++) /* every line */
for (i
= 0; i
< st
->nlines
; i
++) /* shuffle */
r
= random() % st
->nlines
;
st
->lines
[i
] = st
->lines
[r
];
for (i
= st
->nlines
* st
->prev_ratio
;
i
< st
->nlines
* st
->ratio
;
XDrawLine (st
->dpy
, st
->window
, st
->bg_gc
,
0, st
->lines
[i
], st
->width
, st
->lines
[i
]);
XDrawLine (st
->dpy
, st
->window
, st
->bg_gc
,
st
->lines
[i
], 0, st
->lines
[i
], st
->height
);
venetian (eraser_state
*st
)
st
->horiz_p
= (random() & 1);
st
->flip_p
= (random() & 1);
st
->nlines
= (st
->horiz_p
? st
->height
: st
->width
);
st
->lines
= (int *) calloc (st
->nlines
, sizeof(*st
->lines
));
for (i
= 0; i
< st
->nlines
* 2; i
++)
int line
= ((i
/ 16) * 16) - ((i
% 16) * 15);
if (line
>= 0 && line
< st
->nlines
)
st
->lines
[j
++] = (st
->flip_p
? st
->nlines
- line
: line
);
for (i
= st
->nlines
* st
->prev_ratio
;
i
< st
->nlines
* st
->ratio
;
XDrawLine (st
->dpy
, st
->window
, st
->bg_gc
,
0, st
->lines
[i
], st
->width
, st
->lines
[i
]);
XDrawLine (st
->dpy
, st
->window
, st
->bg_gc
,
st
->lines
[i
], 0, st
->lines
[i
], st
->height
);
triple_wipe (eraser_state
*st
)
st
->flip_x
= random() & 1;
st
->flip_y
= random() & 1;
st
->nlines
= st
->width
+ (st
->height
/ 2);
st
->lines
= (int *) calloc (st
->nlines
, sizeof(*st
->lines
));
for (i
= 0; i
< st
->width
/ 2; i
++)
st
->lines
[i
] = i
* 2 + st
->height
;
for (i
= 0; i
< st
->height
/ 2; i
++)
st
->lines
[i
+ st
->width
/ 2] = i
*2;
for (i
= 0; i
< st
->width
/ 2; i
++)
st
->lines
[i
+ st
->width
/ 2 + st
->height
/ 2] =
st
->width
- i
* 2 - (st
->width
% 2 ? 0 : 1) + st
->height
;
for (i
= st
->nlines
* st
->prev_ratio
;
i
< st
->nlines
* st
->ratio
;
if (st
->lines
[i
] < st
->height
)
x
= 0, y
= st
->lines
[i
], x2
= st
->width
, y2
= y
;
x
= st
->lines
[i
]-st
->height
, y
= 0, x2
= x
, y2
= st
->height
;
x
= st
->width
- x
, x2
= st
->width
- x2
;
y
= st
->height
- y
, y2
= st
->height
- y2
;
XDrawLine (st
->dpy
, st
->window
, st
->bg_gc
, x
, y
, x2
, y2
);
quad_wipe (eraser_state
*st
)
st
->flip_x
= random() & 1;
st
->flip_y
= random() & 1;
st
->nlines
= st
->width
+ st
->height
;
st
->lines
= (int *) calloc (st
->nlines
, sizeof(*st
->lines
));
for (i
= 0; i
< st
->nlines
/4; i
++)
st
->lines
[i
*4+1] = st
->height
- i
*2 - (st
->height
% 2 ? 0 : 1);
st
->lines
[i
*4+2] = st
->height
+ i
*2;
st
->lines
[i
*4+3] = st
->height
+ st
->width
- i
*2
- (st
->width
% 2 ? 0 : 1);
for (i
= st
->nlines
* st
->prev_ratio
;
i
< st
->nlines
* st
->ratio
;
if (st
->lines
[i
] < st
->height
)
x
= 0, y
= st
->lines
[i
], x2
= st
->width
, y2
= y
;
x
= st
->lines
[i
] - st
->height
, y
= 0, x2
= x
, y2
= st
->height
;
x
= st
->width
-x
, x2
= st
->width
-x2
;
y
= st
->height
-y
, y2
= st
->height
-y2
;
XDrawLine (st
->dpy
, st
->window
, st
->bg_gc
, x
, y
, x2
, y2
);
circle_wipe (eraser_state
*st
)
int rad
= (st
->width
> st
->height
? st
->width
: st
->height
);
st
->flip_p
= random() & 1;
st
->start
= random() % max
;
oth
= max
* st
->prev_ratio
;
XFillArc (st
->dpy
, st
->window
, st
->bg_gc
,
three_circle_wipe (eraser_state
*st
)
int rad
= (st
->width
> st
->height
? st
->width
: st
->height
);
st
->start
= random() % max
;
oth
= max
/6 * st
->prev_ratio
;
XFillArc (st
->dpy
, st
->window
, st
->bg_gc
,
(st
->start
+ off
+ oth
) % max
,
XFillArc (st
->dpy
, st
->window
, st
->bg_gc
,
(st
->start
+ off
- oth
) % max
,
squaretate (eraser_state
*st
)
int max
= ((st
->width
> st
->height
? st
->width
: st
->height
) * 2);
st
->flip_p
= random() & 1;
points[0].x = st->width - points[0].x; \
points[1].x = st->width - points[1].x; \
points[2].x = st->width - points[2].x; \
XFillPolygon (st->dpy, st->window, st->bg_gc, \
points, 3, Convex, CoordModeOrigin)
points
[2].y
= points
[0].y
+ ((i
* st
->height
) / max
);
points
[1].y
= st
->height
;
points
[2].x
= ((i
* st
->width
) / max
);
points
[2].y
= st
->height
;
points
[0].y
= st
->height
;
points
[1].y
= st
->height
;
points
[2].y
= st
->height
- ((i
* st
->height
) / max
);
points
[0].y
= st
->height
;
points
[2].x
= st
->width
- ((i
* st
->width
) / max
);
fizzle (eraser_state
*st
)
const double overshoot
= 1.0625;
const unsigned int size
= 256;
(unsigned int)(size
* size
* st
->ratio
* overshoot
) -
(unsigned int)(size
* size
* st
->prev_ratio
* overshoot
);
((st
->width
+ size
- 1) / size
) * ((st
->height
+ size
- 1) / size
);
(unsigned short *) calloc (sizeof(unsigned short), chunks
);
for (i
= 0; i
!= chunks
; i
++)
st
->fizzle_rnd
[i
] = NRAND(0x10000) | 1; /* Seed can't be 0. */
points
= (XPoint
*) malloc ((npoints
+ 1) * sizeof(*points
));
for (y
= 0; y
< st
->height
; y
+= 256)
for (x
= 0; x
< st
->width
; x
+= 256)
for (i
= 0; i
!= npoints
; i
++)
points
[i
].x
= r
% size
+ x
;
points
[i
].y
= (r
>> 8) % size
+ y
;
/* Xorshift. This has a period of 2^16, which exactly matches
the number of pixels in each 256x256 chunk.
Other shift constants are possible, but it's hard to say
which constants are best: a 2^16 period PRNG will never score
r
= (r
^ (r
<< 3)) & 0xffff;
r
= (r
^ (r
<< 11)) & 0xffff;
need0
|= (r
== 0x8080); /* Can be anything, really. */
XDrawPoints (st
->dpy
, st
->window
, st
->bg_gc
,
points
, npoints
+ need0
, CoordModeOrigin
);
spiral (eraser_state
*st
)
int max_radius
= (st
->width
> st
->height
? st
->width
: st
->height
) * 0.7;
float max_th
= M_PI
* 2 * loops
;
int steps
= 360 * loops
/ 4;
st
->flip_p
= random() & 1;
st
->start
= random() % 360;
off
= st
->start
* M_PI
/ 180;
for (i
= steps
* st
->prev_ratio
;
float th1
= i
* max_th
/ steps
;
float th2
= (i
+1) * max_th
/ steps
;
int r1
= i
* max_radius
/ steps
;
int r2
= (i
+1) * max_radius
/ steps
;
points
[0].x
= st
->width
/ 2;
points
[0].y
= st
->height
/ 2;
points
[1].x
= points
[0].x
+ r1
* cos (off
+ th1
);
points
[1].y
= points
[0].y
+ r1
* sin (off
+ th1
);
points
[2].x
= points
[0].x
+ r2
* cos (off
+ th2
);
points
[2].y
= points
[0].y
+ r2
* sin (off
+ th2
);
/* XFillRectangle(st->dpy, st->window, st->fg_gc,0,0,st->width, st->height);*/
XFillPolygon (st
->dpy
, st
->window
, st
->bg_gc
,
points
, 3, Convex
, CoordModeOrigin
);
random_squares (eraser_state
*st
)
st
->cols
= 10 + random() % 30;
size
= st
->width
/ st
->cols
;
rows
= (size
? (st
->height
/ size
) : 0) + 1;
st
->nlines
= st
->cols
* rows
;
st
->lines
= (int *) calloc (st
->nlines
, sizeof(*st
->lines
));
for (i
= 0; i
< st
->nlines
; i
++) /* every square */
for (i
= 0; i
< st
->nlines
; i
++) /* shuffle */
r
= random() % st
->nlines
;
st
->lines
[i
] = st
->lines
[r
];
size
= st
->width
/ st
->cols
;
rows
= (size
? (st
->height
/ size
) : 0) + 1;
for (i
= st
->nlines
* st
->prev_ratio
;
i
< st
->nlines
* st
->ratio
;
int x
= st
->lines
[i
] % st
->cols
;
int y
= st
->lines
[i
] / st
->cols
;
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
st
->width
* x
/ st
->cols
,
/* I first saw something like this, albeit in reverse, in an early Tetris
implementation for the Mac.
-- Torbjörn Andersson <torbjorn@dev.eurotime.se>
slide_lines (eraser_state
*st
)
int max
= st
->width
* 1.1;
int h
= st
->height
/ nlines
;
step
= (max
* st
->ratio
) - (max
* st
->prev_ratio
);
for (y
= 0; y
< st
->height
; y
+= h
)
XCopyArea (st
->dpy
, st
->window
, st
->window
, st
->fg_gc
,
0, y
, st
->width
-step
, h
, step
, y
);
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
XCopyArea (st
->dpy
, st
->window
, st
->window
, st
->fg_gc
,
step
, y
, st
->width
-step
, h
, 0, y
);
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
st
->width
-step
, y
, step
, h
);
/* from Frederick Roeber <roeber@xigo.com> */
losira (eraser_state
*st
)
double mode2
= mode1
+ 0.30;
if (st
->ratio
< mode1
) /* squeeze from the sides */
double ratio
= st
->ratio
/ mode1
;
double prev_ratio
= st
->prev_ratio
/ mode1
;
int step
= (max
* ratio
) - (max
* prev_ratio
);
XCopyArea (st
->dpy
, st
->window
, st
->window
, st
->fg_gc
,
0, 0, max
- step
, st
->height
, step
, 0);
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
0, 0, max
* ratio
, st
->height
);
XCopyArea (st
->dpy
, st
->window
, st
->window
, st
->fg_gc
,
max
+step
, 0, max
- step
, st
->height
, max
, 0);
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
max
+ max
*(1-ratio
), 0, max
, st
->height
);
/* expand white from center */
XFillRectangle (st
->dpy
, st
->window
, st
->fg_gc
,
max
- (radius
* ratio
), 0,
radius
* ratio
* 2, st
->height
);
else if (st
->ratio
< mode2
) /* squeeze from the top/bottom */
double ratio
= (st
->ratio
- mode1
) / (mode2
- mode1
);
int max
= st
->height
/ 2;
XFillRectangle (st
->dpy
, st
->window
, st
->fg_gc
,
radius
*2, st
->height
* (1 - ratio
));
/* fill left and right */
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
0, 0, st
->width
/2 - radius
, st
->height
);
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
st
->width
/2 + radius
, 0, st
->width
/2, st
->height
);
/* fill top and bottom */
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
0, 0, st
->width
, max
* ratio
);
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
0, st
->height
- (max
* ratio
),
XFillArc (st
->dpy
, st
->window
, st
->fg_gc
,
XFillArc (st
->dpy
, st
->window
, st
->fg_gc
,
st
->height
- (max
* ratio
+ radius
),
else if (st
->ratio
< mode3
) /* starburst */
double ratio
= (st
->ratio
- mode2
) / (mode3
- mode2
);
double r2
= ratio
* radius
* 4;
int angle
= 360*64/countof(arc
);
for (i
= 0; i
< countof(arc
); i
++)
arc
[i
].angle1
= angle
* i
;
arc
[i
].width
= radius
*2 * (1 + ratio
);
arc
[i
].height
= radius
*2 * (1 + ratio
);
arc
[i
].x
= st
->width
/ 2 - radius
;
arc
[i
].y
= st
->height
/ 2 - radius
;
th
= ((arc
[i
].angle1
+ (arc
[i
].angle2
/ 2)) / 64.0 / 180 * M_PI
);
arc
[i
].x
+= r2
* cos (th
);
arc
[i
].y
-= r2
* sin (th
);
XFillRectangle (st
->dpy
, st
->window
, st
->bg_gc
,
0, 0, st
->width
, st
->height
);
XFillArcs (st
->dpy
, st
->window
, st
->fg_gc
, arc
, countof(arc
));
static Eraser erasers
[] = {
eraser_init (Display
*dpy
, Window window
)
eraser_state
*st
= (eraser_state
*) calloc (1, sizeof(*st
));
XGetWindowAttributes (dpy
, window
, &xgwa
);
st
->height
= xgwa
.height
;
bg
= get_pixel_resource (dpy
, xgwa
.colormap
, "background", "Background");
fg
= get_pixel_resource (dpy
, xgwa
.colormap
, "foreground", "Foreground");
st
->fg_gc
= XCreateGC (dpy
, window
, GCForeground
|GCBackground
, &gcv
);
st
->bg_gc
= XCreateGC (dpy
, window
, GCForeground
|GCBackground
, &gcv
);
/* Pretty much all of these leave turds if AA is on. */
jwxyz_XSetAntiAliasing (st
->dpy
, st
->fg_gc
, False
);
jwxyz_XSetAntiAliasing (st
->dpy
, st
->bg_gc
, False
);
s
= get_string_resource (dpy
, "eraseMode", "Integer");
which
= get_integer_resource(dpy
, "eraseMode", "Integer");
if (which
< 0 || which
>= countof(erasers
))
which
= random() % countof(erasers
);
duration
= get_float_resource (dpy
, "eraseSeconds", "Float");
if (duration
< 0.1 || duration
> 10)
st
->start_time
= double_time();
st
->stop_time
= st
->start_time
+ duration
;
eraser_free (eraser_state
*st
)
XClearWindow (st
->dpy
, st
->window
); /* Final draw is black-on-black. */
st
->fn (st
); /* Free any memory. May also draw, but that doesn't matter. */
XFreeGC (st
->dpy
, st
->fg_gc
);
XFreeGC (st
->dpy
, st
->bg_gc
);
erase_window (Display
*dpy
, Window window
, eraser_state
*st
)
st
= eraser_init (dpy
, window
);
now
= (first_p
? st
->start_time
: double_time());
duration
= st
->stop_time
- st
->start_time
;
st
->prev_ratio
= st
->ratio
;
st
->ratio
= (now
- st
->start_time
) / duration
;