1 /* xscreensaver, Copyright (c) 1992-2018 Jamie Zawinski <jwz@jwz.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
9 * implied warranty.
10 */
11
12 /* decayscreen
13 *
14 * Based on slidescreen program from the xscreensaver application and the
15 * decay program for Sun framebuffers. This is the comment from the decay.c
16 * file:
17
18 * decay.c
19 * find the screen bitmap for the console and make it "decay" by
20 * randomly shifting random rectangles by one pixelwidth at a time.
21 *
22 * by David Wald, 1988
23 * rewritten by Natuerlich!
24 * based on a similar "utility" on the Apollo ring at Yale.
25
26 * X version by
27 *
28 * Vivek Khera <khera@cs.duke.edu>
29 * 5-AUG-1993
30 *
31 * Hacked by jwz, 28-Nov-97 (sped up and added new motion directions)
32
33 * R. Schultz
34 * Added "melt" & "stretch" modes 28-Mar-1999
35 *
36 */
37
38 #include "screenhack.h"
39 #include <time.h>
40
41 struct state {
42 Display *dpy;
43 Window window;
44 XWindowAttributes xgwa;
45 Pixmap saved;
46 int saved_w, saved_h;
47
48 int sizex, sizey;
49 int delay;
50 int duration;
51 GC gc;
52 int mode;
53 int random_p;
54 time_t start_time;
55
56 int fuzz_toggle;
57 const int *current_bias;
58
59 async_load_state *img_loader;
60 };
61
62
63 #define SHUFFLE 0
64 #define UP 1
65 #define LEFT 2
66 #define RIGHT 3
67 #define DOWN 4
68 #define UPLEFT 5
69 #define DOWNLEFT 6
70 #define UPRIGHT 7
71 #define DOWNRIGHT 8
72 #define IN 9
73 #define OUT 10
74 #define MELT 11
75 #define STRETCH 12
76 #define FUZZ 13
77
78 static void
decayscreen_load_image(struct state * st)79 decayscreen_load_image (struct state *st)
80 {
81 XWindowAttributes xgwa;
82 XGetWindowAttributes (st->dpy, st->window, &xgwa);
83 st->sizex = xgwa.width;
84 st->sizey = xgwa.height;
85 if (st->img_loader) abort();
86
87 st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
88 st->window, 0, 0);
89 }
90
91 static void *
decayscreen_init(Display * dpy,Window window)92 decayscreen_init (Display *dpy, Window window)
93 {
94 struct state *st = (struct state *) calloc (1, sizeof(*st));
95 XGCValues gcv;
96 long gcflags;
97 unsigned long bg;
98 char *s;
99
100 st->dpy = dpy;
101 st->window = window;
102 st->random_p = 0;
103
104 s = get_string_resource(st->dpy, "mode", "Mode");
105 if (s && !strcmp(s, "shuffle")) st->mode = SHUFFLE;
106 else if (s && !strcmp(s, "up")) st->mode = UP;
107 else if (s && !strcmp(s, "left")) st->mode = LEFT;
108 else if (s && !strcmp(s, "right")) st->mode = RIGHT;
109 else if (s && !strcmp(s, "down")) st->mode = DOWN;
110 else if (s && !strcmp(s, "upleft")) st->mode = UPLEFT;
111 else if (s && !strcmp(s, "downleft")) st->mode = DOWNLEFT;
112 else if (s && !strcmp(s, "upright")) st->mode = UPRIGHT;
113 else if (s && !strcmp(s, "downright")) st->mode = DOWNRIGHT;
114 else if (s && !strcmp(s, "in")) st->mode = IN;
115 else if (s && !strcmp(s, "out")) st->mode = OUT;
116 else if (s && !strcmp(s, "melt")) st->mode = MELT;
117 else if (s && !strcmp(s, "stretch")) st->mode = STRETCH;
118 else if (s && !strcmp(s, "fuzz")) st->mode = FUZZ;
119 else {
120 if (s && *s && !!strcmp(s, "random"))
121 fprintf(stderr, "%s: unknown mode %s\n", progname, s);
122 st->random_p = 1;
123 st->mode = random() % (FUZZ+1);
124 }
125 if (s) free (s);
126
127 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
128 if (st->delay < 0) st->delay = 0;
129
130 st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
131 if (st->duration < 1) st->duration = 1;
132
133 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
134
135 gcv.function = GXcopy;
136 gcv.subwindow_mode = IncludeInferiors;
137 bg = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background");
138 gcv.foreground = bg;
139
140 gcflags = GCForeground | GCFunction;
141 if (use_subwindow_mode_p(st->xgwa.screen, st->window)) /* see grabscreen.c */
142 gcflags |= GCSubwindowMode;
143 st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
144
145 st->start_time = time ((time_t *) 0);
146 decayscreen_load_image (st);
147
148 return st;
149 }
150
151
152 /*
153 * perform one iteration of decay
154 */
155 static unsigned long
decayscreen_draw(Display * dpy,Window window,void * closure)156 decayscreen_draw (Display *dpy, Window window, void *closure)
157 {
158 struct state *st = (struct state *) closure;
159 int left, top, width, height, toleft, totop;
160
161 #define L 101
162 #define R 102
163 #define U 103
164 #define D 104
165 static const int no_bias[] = { L,L,L,L, R,R,R,R, U,U,U,U, D,D,D,D };
166 static const int up_bias[] = { L,L,L,L, R,R,R,R, U,U,U,U, U,U,D,D };
167 static const int down_bias[] = { L,L,L,L, R,R,R,R, U,U,D,D, D,D,D,D };
168 static const int left_bias[] = { L,L,L,L, L,L,R,R, U,U,U,U, D,D,D,D };
169 static const int right_bias[] = { L,L,R,R, R,R,R,R, U,U,U,U, D,D,D,D };
170
171 static const int upleft_bias[] = { L,L,L,L, L,R,R,R, U,U,U,U, U,D,D,D };
172 static const int downleft_bias[] = { L,L,L,L, L,R,R,R, U,U,U,D, D,D,D,D };
173 static const int upright_bias[] = { L,L,L,R, R,R,R,R, U,U,U,U, U,D,D,D };
174 static const int downright_bias[] = { L,L,L,R, R,R,R,R, U,U,U,D, D,D,D,D };
175
176 int off = 1;
177 if (st->sizex > 2560) off *= 2; /* Retina displays */
178
179 if (st->img_loader) /* still loading */
180 {
181 st->img_loader = load_image_async_simple (st->img_loader,
182 0, 0, 0, 0, 0);
183 if (! st->img_loader) { /* just finished */
184
185 st->start_time = time ((time_t *) 0);
186 if (st->random_p)
187 st->mode = random() % (FUZZ+1);
188
189 if (st->mode == MELT || st->mode == STRETCH)
190 /* make sure screen eventually turns background color */
191 XDrawLine (st->dpy, st->window, st->gc, 0, 0, st->sizex, 0);
192
193 if (!st->saved) {
194 st->saved = XCreatePixmap (st->dpy, st->window,
195 st->sizex, st->sizey,
196 st->xgwa.depth);
197 st->saved_w = st->sizex;
198 st->saved_h = st->sizey;
199 }
200 XCopyArea (st->dpy, st->window, st->saved, st->gc, 0, 0,
201 st->sizex, st->sizey, 0, 0);
202 }
203 return st->delay;
204 }
205
206 if (!st->img_loader &&
207 st->start_time + st->duration < time ((time_t *) 0)) {
208 decayscreen_load_image (st);
209 }
210
211 switch (st->mode) {
212 case SHUFFLE: st->current_bias = no_bias; break;
213 case UP: st->current_bias = up_bias; break;
214 case LEFT: st->current_bias = left_bias; break;
215 case RIGHT: st->current_bias = right_bias; break;
216 case DOWN: st->current_bias = down_bias; break;
217 case UPLEFT: st->current_bias = upleft_bias; break;
218 case DOWNLEFT: st->current_bias = downleft_bias; break;
219 case UPRIGHT: st->current_bias = upright_bias; break;
220 case DOWNRIGHT: st->current_bias = downright_bias; break;
221 case IN: st->current_bias = no_bias; break;
222 case OUT: st->current_bias = no_bias; break;
223 case MELT: st->current_bias = no_bias; break;
224 case STRETCH: st->current_bias = no_bias; break;
225 case FUZZ: st->current_bias = no_bias; break;
226 default: abort();
227 }
228
229 #define nrnd(x) ((x) ? random() % (x) : x)
230
231 if (st->mode == MELT || st->mode == STRETCH) {
232 left = nrnd(st->sizex/2);
233 top = nrnd(st->sizey);
234 width = nrnd( st->sizex/2 ) + st->sizex/2 - left;
235 height = nrnd(st->sizey - top);
236 toleft = left;
237 totop = top+off;
238
239 } else if (st->mode == FUZZ) { /* By Vince Levey <vincel@vincel.org>;
240 inspired by the "melt" mode of the
241 "scrhack" IrisGL program by Paul Haeberli
242 circa 1991. */
243 left = nrnd(st->sizex - 1);
244 top = nrnd(st->sizey - 1);
245 st->fuzz_toggle = !st->fuzz_toggle;
246 if (st->fuzz_toggle)
247 {
248 totop = top;
249 height = off;
250 toleft = nrnd(st->sizex - 1);
251 if (toleft > left)
252 {
253 width = toleft-left;
254 toleft = left;
255 left++;
256 }
257 else
258 {
259 width = left-toleft;
260 left = toleft;
261 toleft++;
262 }
263 }
264 else
265 {
266 toleft = left;
267 width = off;
268 totop = nrnd(st->sizey - 1);
269 if (totop > top)
270 {
271 height = totop-top;
272 totop = top;
273 top++;
274 }
275 else
276 {
277 height = top-totop;
278 top = totop;
279 totop++;
280 }
281 }
282
283 } else {
284
285 left = nrnd(st->sizex - 1);
286 top = nrnd(st->sizey);
287 width = nrnd(st->sizex - left);
288 height = nrnd(st->sizey - top);
289
290 toleft = left;
291 totop = top;
292 if (st->mode == IN || st->mode == OUT) {
293 int x = left+(width/2);
294 int y = top+(height/2);
295 int cx = st->sizex/2;
296 int cy = st->sizey/2;
297 if (st->mode == IN) {
298 if (x > cx && y > cy) st->current_bias = upleft_bias;
299 else if (x < cx && y > cy) st->current_bias = upright_bias;
300 else if (x < cx && y < cy) st->current_bias = downright_bias;
301 else /* (x > cx && y < cy)*/ st->current_bias = downleft_bias;
302 } else {
303 if (x > cx && y > cy) st->current_bias = downright_bias;
304 else if (x < cx && y > cy) st->current_bias = downleft_bias;
305 else if (x < cx && y < cy) st->current_bias = upleft_bias;
306 else /* (x > cx && y < cy)*/ st->current_bias = upright_bias;
307 }
308 }
309
310 switch (st->current_bias[random() % (sizeof(no_bias)/sizeof(*no_bias))]) {
311 case L: toleft = left-off; break;
312 case R: toleft = left+off; break;
313 case U: totop = top-off; break;
314 case D: totop = top+off; break;
315 default: abort(); break;
316 }
317 }
318
319 if (st->mode == STRETCH) {
320 XCopyArea (st->dpy, st->window, st->window, st->gc,
321 0, st->sizey-top-off*2, st->sizex, top+off,
322 0, st->sizey-top-off);
323 } else {
324 XCopyArea (st->dpy, st->window, st->window, st->gc,
325 left, top, width, height,
326 toleft, totop);
327 }
328
329 #undef nrnd
330
331 return st->delay;
332 }
333
334 static void
decayscreen_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)335 decayscreen_reshape (Display *dpy, Window window, void *closure,
336 unsigned int w, unsigned int h)
337 {
338 struct state *st = (struct state *) closure;
339 if (! st->saved) return; /* Image might not be loaded yet */
340 XClearWindow (st->dpy, st->window);
341 XCopyArea (st->dpy, st->saved, st->window, st->gc,
342 0, 0, st->saved_w, st->saved_h,
343 ((int)w - st->saved_w) / 2,
344 ((int)h - st->saved_h) / 2);
345 st->sizex = w;
346 st->sizey = h;
347 }
348
349 static Bool
decayscreen_event(Display * dpy,Window window,void * closure,XEvent * event)350 decayscreen_event (Display *dpy, Window window, void *closure, XEvent *event)
351 {
352 struct state *st = (struct state *) closure;
353 if (screenhack_event_helper (dpy, window, event))
354 {
355 st->start_time = 0;
356 return True;
357 }
358 return False;
359 }
360
361 static void
decayscreen_free(Display * dpy,Window window,void * closure)362 decayscreen_free (Display *dpy, Window window, void *closure)
363 {
364 struct state *st = (struct state *) closure;
365 XFreeGC (dpy, st->gc);
366 free (st);
367 }
368
369
370
371 static const char *decayscreen_defaults [] = {
372 ".background: Black",
373 ".foreground: Yellow",
374 "*dontClearRoot: True",
375 "*fpsSolid: True",
376
377 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
378 "*visualID: Best",
379 #endif
380
381 "*delay: 10000",
382 "*mode: random",
383 "*duration: 120",
384 #ifdef HAVE_MOBILE
385 "*ignoreRotation: True",
386 "*rotateImages: True",
387 #endif
388 0
389 };
390
391 static XrmOptionDescRec decayscreen_options [] = {
392 { "-delay", ".delay", XrmoptionSepArg, 0 },
393 { "-mode", ".mode", XrmoptionSepArg, 0 },
394 { "-duration", ".duration", XrmoptionSepArg, 0 },
395 { 0, 0, 0, 0 }
396 };
397
398
399 XSCREENSAVER_MODULE ("DecayScreen", decayscreen)
400