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