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