From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / decayscreen.c
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
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 *
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
126   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
127   if (st->delay < 0) st->delay = 0;
128
129   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
130   if (st->duration < 1) st->duration = 1;
131
132   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
133
134   gcv.function = GXcopy;
135   gcv.subwindow_mode = IncludeInferiors;
136   bg = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background");
137   gcv.foreground = bg;
138
139   gcflags = GCForeground | GCFunction;
140   if (use_subwindow_mode_p(st->xgwa.screen, st->window)) /* see grabscreen.c */
141     gcflags |= GCSubwindowMode;
142   st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
143
144   st->start_time = time ((time_t *) 0);
145   decayscreen_load_image (st);
146
147   return st;
148 }
149
150
151 /*
152  * perform one iteration of decay
153  */
154 static unsigned long
155 decayscreen_draw (Display *dpy, Window window, void *closure)
156 {
157     struct state *st = (struct state *) closure;
158     int left, top, width, height, toleft, totop;
159
160 #define L 101
161 #define R 102
162 #define U 103
163 #define D 104
164     static const int no_bias[]        = { L,L,L,L, R,R,R,R, U,U,U,U, D,D,D,D };
165     static const int up_bias[]        = { L,L,L,L, R,R,R,R, U,U,U,U, U,U,D,D };
166     static const int down_bias[]      = { L,L,L,L, R,R,R,R, U,U,D,D, D,D,D,D };
167     static const int left_bias[]      = { L,L,L,L, L,L,R,R, U,U,U,U, D,D,D,D };
168     static const int right_bias[]     = { L,L,R,R, R,R,R,R, U,U,U,U, D,D,D,D };
169
170     static const int upleft_bias[]    = { L,L,L,L, L,R,R,R, U,U,U,U, U,D,D,D };
171     static const int downleft_bias[]  = { L,L,L,L, L,R,R,R, U,U,U,D, D,D,D,D };
172     static const int upright_bias[]   = { L,L,L,R, R,R,R,R, U,U,U,U, U,D,D,D };
173     static const int downright_bias[] = { L,L,L,R, R,R,R,R, U,U,U,D, D,D,D,D };
174
175     int off = 1;
176     if (st->sizex > 2560) off *= 2;  /* Retina displays */
177
178     if (st->img_loader)   /* still loading */
179       {
180         st->img_loader = load_image_async_simple (st->img_loader, 
181                                                   0, 0, 0, 0, 0);
182         if (! st->img_loader) {  /* just finished */
183
184           st->start_time = time ((time_t *) 0);
185           if (st->random_p)
186             st->mode = random() % (FUZZ+1);
187
188           if (st->mode == MELT || st->mode == STRETCH)
189             /* make sure screen eventually turns background color */
190             XDrawLine (st->dpy, st->window, st->gc, 0, 0, st->sizex, 0); 
191
192           if (!st->saved) {
193             st->saved = XCreatePixmap (st->dpy, st->window,
194                                        st->sizex, st->sizey,
195                                        st->xgwa.depth);
196             st->saved_w = st->sizex;
197             st->saved_h = st->sizey;
198           }
199           XCopyArea (st->dpy, st->window, st->saved, st->gc, 0, 0,
200                      st->sizex, st->sizey, 0, 0);
201         }
202       return st->delay;
203     }
204
205     if (!st->img_loader &&
206         st->start_time + st->duration < time ((time_t *) 0)) {
207       decayscreen_load_image (st);
208     }
209
210     switch (st->mode) {
211       case SHUFFLE:     st->current_bias = no_bias; break;
212       case UP:          st->current_bias = up_bias; break;
213       case LEFT:        st->current_bias = left_bias; break;
214       case RIGHT:       st->current_bias = right_bias; break;
215       case DOWN:        st->current_bias = down_bias; break;
216       case UPLEFT:      st->current_bias = upleft_bias; break;
217       case DOWNLEFT:    st->current_bias = downleft_bias; break;
218       case UPRIGHT:     st->current_bias = upright_bias; break;
219       case DOWNRIGHT:   st->current_bias = downright_bias; break;
220       case IN:          st->current_bias = no_bias; break;
221       case OUT:         st->current_bias = no_bias; break;
222       case MELT:        st->current_bias = no_bias; break;
223       case STRETCH:     st->current_bias = no_bias; break;
224       case FUZZ:        st->current_bias = no_bias; break;
225      default: abort();
226     }
227
228 #define nrnd(x) ((x) ? random() % (x) : x)
229
230     if (st->mode == MELT || st->mode == STRETCH) {
231       left = nrnd(st->sizex/2);
232       top = nrnd(st->sizey);
233       width = nrnd( st->sizex/2 ) + st->sizex/2 - left;
234       height = nrnd(st->sizey - top);
235       toleft = left;
236       totop = top+off;
237
238     } else if (st->mode == FUZZ) {  /* By Vince Levey <vincel@vincel.org>;
239                                    inspired by the "melt" mode of the
240                                    "scrhack" IrisGL program by Paul Haeberli
241                                    circa 1991. */
242       left = nrnd(st->sizex - 1);
243       top  = nrnd(st->sizey - 1);
244       st->fuzz_toggle = !st->fuzz_toggle;
245       if (st->fuzz_toggle)
246         {
247           totop = top;
248           height = off;
249           toleft = nrnd(st->sizex - 1);
250           if (toleft > left)
251             {
252               width = toleft-left;
253               toleft = left;
254               left++;
255             }
256           else
257             {
258               width = left-toleft;
259               left = toleft;
260               toleft++;
261             }
262         }
263       else
264         {
265           toleft = left;
266           width = off;
267           totop  = nrnd(st->sizey - 1);
268           if (totop > top)
269             {
270               height = totop-top;
271               totop = top;
272               top++;
273             }
274           else
275             {
276               height = top-totop;
277               top = totop;
278               totop++;
279             }
280         }
281
282     } else {
283
284       left = nrnd(st->sizex - 1);
285       top = nrnd(st->sizey);
286       width = nrnd(st->sizex - left);
287       height = nrnd(st->sizey - top);
288       
289       toleft = left;
290       totop = top;
291       if (st->mode == IN || st->mode == OUT) {
292         int x = left+(width/2);
293         int y = top+(height/2);
294         int cx = st->sizex/2;
295         int cy = st->sizey/2;
296         if (st->mode == IN) {
297           if      (x > cx && y > cy)   st->current_bias = upleft_bias;
298           else if (x < cx && y > cy)   st->current_bias = upright_bias;
299           else if (x < cx && y < cy)   st->current_bias = downright_bias;
300           else /* (x > cx && y < cy)*/ st->current_bias = downleft_bias;
301         } else {
302           if      (x > cx && y > cy)   st->current_bias = downright_bias;
303           else if (x < cx && y > cy)   st->current_bias = downleft_bias;
304           else if (x < cx && y < cy)   st->current_bias = upleft_bias;
305           else /* (x > cx && y < cy)*/ st->current_bias = upright_bias;
306         }
307       }
308       
309       switch (st->current_bias[random() % (sizeof(no_bias)/sizeof(*no_bias))]) {
310       case L: toleft = left-off; break;
311       case R: toleft = left+off; break;
312       case U: totop = top-off; break;
313       case D: totop = top+off; break;
314       default: abort(); break;
315       }
316     }
317     
318     if (st->mode == STRETCH) {
319       XCopyArea (st->dpy, st->window, st->window, st->gc,
320                  0, st->sizey-top-off*2, st->sizex, top+off, 
321                  0, st->sizey-top-off);
322     } else {
323       XCopyArea (st->dpy, st->window, st->window, st->gc,
324                  left, top, width, height,
325                  toleft, totop);
326     }
327
328 #undef nrnd
329
330     return st->delay;
331 }
332
333 static void
334 decayscreen_reshape (Display *dpy, Window window, void *closure, 
335                  unsigned int w, unsigned int h)
336 {
337   struct state *st = (struct state *) closure;
338   if (! st->saved) return; /* Image might not be loaded yet */
339   XClearWindow (st->dpy, st->window);
340   XCopyArea (st->dpy, st->saved, st->window, st->gc,
341              0, 0, st->saved_w, st->saved_h,
342              ((int)w - st->saved_w) / 2,
343              ((int)h - st->saved_h) / 2);
344   st->sizex = w;
345   st->sizey = h;
346 }
347
348 static Bool
349 decayscreen_event (Display *dpy, Window window, void *closure, XEvent *event)
350 {
351   struct state *st = (struct state *) closure;
352   if (screenhack_event_helper (dpy, window, event))
353     {
354       st->start_time = 0;
355       return True;
356     }
357   return False;
358 }
359
360 static void
361 decayscreen_free (Display *dpy, Window window, void *closure)
362 {
363   struct state *st = (struct state *) closure;
364   free (st);
365 }
366
367 \f
368
369 static const char *decayscreen_defaults [] = {
370   ".background:                 Black",
371   ".foreground:                 Yellow",
372   "*dontClearRoot:              True",
373   "*fpsSolid:                   True",
374
375 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
376   "*visualID:                   Best",
377 #endif
378
379   "*delay:                      10000",
380   "*mode:                       random",
381   "*duration:                   120",
382 #ifdef HAVE_MOBILE
383   "*ignoreRotation:             True",
384   "*rotateImages:               True",
385 #endif
386   0
387 };
388
389 static XrmOptionDescRec decayscreen_options [] = {
390   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
391   { "-mode",            ".mode",                XrmoptionSepArg, 0 },
392   { "-duration",        ".duration",            XrmoptionSepArg, 0 },
393   { 0, 0, 0, 0 }
394 };
395
396
397 XSCREENSAVER_MODULE ("DecayScreen", decayscreen)