From http://www.jwz.org/xscreensaver/xscreensaver-5.30.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
40 struct state {
41   Display *dpy;
42   Window window;
43   XWindowAttributes xgwa;
44   Pixmap saved;
45   int saved_w, saved_h;
46
47   int sizex, sizey;
48   int delay;
49   int duration;
50   GC gc;
51   int mode;
52   int random_p;
53   time_t start_time;
54
55   int fuzz_toggle;
56   const int *current_bias;
57
58   async_load_state *img_loader;
59 };
60
61
62 #define SHUFFLE         0
63 #define UP              1
64 #define LEFT            2
65 #define RIGHT           3
66 #define DOWN            4
67 #define UPLEFT          5
68 #define DOWNLEFT        6
69 #define UPRIGHT         7
70 #define DOWNRIGHT       8
71 #define IN              9
72 #define OUT             10
73 #define MELT            11
74 #define STRETCH         12
75 #define FUZZ            13
76
77 static void
78 decayscreen_load_image (struct state *st)
79 {
80   XWindowAttributes xgwa;
81   XGetWindowAttributes (st->dpy, st->window, &xgwa);
82   st->sizex = xgwa.width;
83   st->sizey = xgwa.height;
84   if (st->img_loader) abort();
85
86   st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
87                                             st->window, 0, 0);
88 }
89
90 static void *
91 decayscreen_init (Display *dpy, Window window)
92 {
93   struct state *st = (struct state *) calloc (1, sizeof(*st));
94   XGCValues gcv;
95   long gcflags;
96   unsigned long bg;
97   char *s;
98
99   st->dpy = dpy;
100   st->window = window;
101   st->random_p = 0;
102
103   s = get_string_resource(st->dpy, "mode", "Mode");
104   if      (s && !strcmp(s, "shuffle")) st->mode = SHUFFLE;
105   else if (s && !strcmp(s, "up")) st->mode = UP;
106   else if (s && !strcmp(s, "left")) st->mode = LEFT;
107   else if (s && !strcmp(s, "right")) st->mode = RIGHT;
108   else if (s && !strcmp(s, "down")) st->mode = DOWN;
109   else if (s && !strcmp(s, "upleft")) st->mode = UPLEFT;
110   else if (s && !strcmp(s, "downleft")) st->mode = DOWNLEFT;
111   else if (s && !strcmp(s, "upright")) st->mode = UPRIGHT;
112   else if (s && !strcmp(s, "downright")) st->mode = DOWNRIGHT;
113   else if (s && !strcmp(s, "in")) st->mode = IN;
114   else if (s && !strcmp(s, "out")) st->mode = OUT;
115   else if (s && !strcmp(s, "melt")) st->mode = MELT;
116   else if (s && !strcmp(s, "stretch")) st->mode = STRETCH;
117   else if (s && !strcmp(s, "fuzz")) st->mode = FUZZ;
118   else {
119     if (s && *s && !!strcmp(s, "random"))
120       fprintf(stderr, "%s: unknown mode %s\n", progname, s);
121     st->random_p = 1;
122     st->mode = random() % (FUZZ+1);
123   }
124
125   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
126   if (st->delay < 0) st->delay = 0;
127
128   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
129   if (st->duration < 1) st->duration = 1;
130
131   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
132
133   gcv.function = GXcopy;
134   gcv.subwindow_mode = IncludeInferiors;
135   bg = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background");
136   gcv.foreground = bg;
137
138   gcflags = GCForeground | GCFunction;
139   if (use_subwindow_mode_p(st->xgwa.screen, st->window)) /* see grabscreen.c */
140     gcflags |= GCSubwindowMode;
141   st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
142
143   st->start_time = time ((time_t) 0);
144   decayscreen_load_image (st);
145
146   return st;
147 }
148
149
150 /*
151  * perform one iteration of decay
152  */
153 static unsigned long
154 decayscreen_draw (Display *dpy, Window window, void *closure)
155 {
156     struct state *st = (struct state *) closure;
157     int left, top, width, height, toleft, totop;
158
159 #define L 101
160 #define R 102
161 #define U 103
162 #define D 104
163     static const int no_bias[]        = { L,L,L,L, R,R,R,R, U,U,U,U, D,D,D,D };
164     static const int up_bias[]        = { L,L,L,L, R,R,R,R, U,U,U,U, U,U,D,D };
165     static const int down_bias[]      = { L,L,L,L, R,R,R,R, U,U,D,D, D,D,D,D };
166     static const int left_bias[]      = { L,L,L,L, L,L,R,R, U,U,U,U, D,D,D,D };
167     static const int right_bias[]     = { L,L,R,R, R,R,R,R, U,U,U,U, D,D,D,D };
168
169     static const int upleft_bias[]    = { L,L,L,L, L,R,R,R, U,U,U,U, U,D,D,D };
170     static const int downleft_bias[]  = { L,L,L,L, L,R,R,R, U,U,U,D, D,D,D,D };
171     static const int upright_bias[]   = { L,L,L,R, R,R,R,R, U,U,U,U, U,D,D,D };
172     static const int downright_bias[] = { L,L,L,R, R,R,R,R, U,U,U,D, D,D,D,D };
173
174     if (st->img_loader)   /* still loading */
175       {
176         st->img_loader = load_image_async_simple (st->img_loader, 
177                                                   0, 0, 0, 0, 0);
178         if (! st->img_loader) {  /* just finished */
179
180           st->start_time = time ((time_t) 0);
181           if (st->random_p)
182             st->mode = random() % (FUZZ+1);
183
184           if (st->mode == MELT || st->mode == STRETCH)
185             /* make sure screen eventually turns background color */
186             XDrawLine (st->dpy, st->window, st->gc, 0, 0, st->sizex, 0); 
187
188           if (!st->saved) {
189             st->saved = XCreatePixmap (st->dpy, st->window,
190                                        st->sizex, st->sizey,
191                                        st->xgwa.depth);
192             st->saved_w = st->sizex;
193             st->saved_h = st->sizey;
194           }
195           XCopyArea (st->dpy, st->window, st->saved, st->gc, 0, 0,
196                      st->sizex, st->sizey, 0, 0);
197         }
198       return st->delay;
199     }
200
201     if (!st->img_loader &&
202         st->start_time + st->duration < time ((time_t) 0)) {
203       decayscreen_load_image (st);
204     }
205
206     switch (st->mode) {
207       case SHUFFLE:     st->current_bias = no_bias; break;
208       case UP:          st->current_bias = up_bias; break;
209       case LEFT:        st->current_bias = left_bias; break;
210       case RIGHT:       st->current_bias = right_bias; break;
211       case DOWN:        st->current_bias = down_bias; break;
212       case UPLEFT:      st->current_bias = upleft_bias; break;
213       case DOWNLEFT:    st->current_bias = downleft_bias; break;
214       case UPRIGHT:     st->current_bias = upright_bias; break;
215       case DOWNRIGHT:   st->current_bias = downright_bias; break;
216       case IN:          st->current_bias = no_bias; break;
217       case OUT:         st->current_bias = no_bias; break;
218       case MELT:        st->current_bias = no_bias; break;
219       case STRETCH:     st->current_bias = no_bias; break;
220       case FUZZ:        st->current_bias = no_bias; break;
221      default: abort();
222     }
223
224 #define nrnd(x) ((x) ? random() % (x) : x)
225
226     if (st->mode == MELT || st->mode == STRETCH) {
227       left = nrnd(st->sizex/2);
228       top = nrnd(st->sizey);
229       width = nrnd( st->sizex/2 ) + st->sizex/2 - left;
230       height = nrnd(st->sizey - top);
231       toleft = left;
232       totop = top+1;
233
234     } else if (st->mode == FUZZ) {  /* By Vince Levey <vincel@vincel.org>;
235                                    inspired by the "melt" mode of the
236                                    "scrhack" IrisGL program by Paul Haeberli
237                                    circa 1991. */
238       left = nrnd(st->sizex - 1);
239       top  = nrnd(st->sizey - 1);
240       st->fuzz_toggle = !st->fuzz_toggle;
241       if (st->fuzz_toggle)
242         {
243           totop = top;
244           height = 1;
245           toleft = nrnd(st->sizex - 1);
246           if (toleft > left)
247             {
248               width = toleft-left;
249               toleft = left;
250               left++;
251             }
252           else
253             {
254               width = left-toleft;
255               left = toleft;
256               toleft++;
257             }
258         }
259       else
260         {
261           toleft = left;
262           width = 1;
263           totop  = nrnd(st->sizey - 1);
264           if (totop > top)
265             {
266               height = totop-top;
267               totop = top;
268               top++;
269             }
270           else
271             {
272               height = top-totop;
273               top = totop;
274               totop++;
275             }
276         }
277
278     } else {
279
280       left = nrnd(st->sizex - 1);
281       top = nrnd(st->sizey);
282       width = nrnd(st->sizex - left);
283       height = nrnd(st->sizey - top);
284       
285       toleft = left;
286       totop = top;
287       if (st->mode == IN || st->mode == OUT) {
288         int x = left+(width/2);
289         int y = top+(height/2);
290         int cx = st->sizex/2;
291         int cy = st->sizey/2;
292         if (st->mode == IN) {
293           if      (x > cx && y > cy)   st->current_bias = upleft_bias;
294           else if (x < cx && y > cy)   st->current_bias = upright_bias;
295           else if (x < cx && y < cy)   st->current_bias = downright_bias;
296           else /* (x > cx && y < cy)*/ st->current_bias = downleft_bias;
297         } else {
298           if      (x > cx && y > cy)   st->current_bias = downright_bias;
299           else if (x < cx && y > cy)   st->current_bias = downleft_bias;
300           else if (x < cx && y < cy)   st->current_bias = upleft_bias;
301           else /* (x > cx && y < cy)*/ st->current_bias = upright_bias;
302         }
303       }
304       
305       switch (st->current_bias[random() % (sizeof(no_bias)/sizeof(*no_bias))]) {
306       case L: toleft = left-1; break;
307       case R: toleft = left+1; break;
308       case U: totop = top-1; break;
309       case D: totop = top+1; break;
310       default: abort(); break;
311       }
312     }
313     
314     if (st->mode == STRETCH) {
315       XCopyArea (st->dpy, st->window, st->window, st->gc, 0, st->sizey-top-2, st->sizex, top+1, 
316                  0, st->sizey-top-1); 
317     } else {
318       XCopyArea (st->dpy, st->window, st->window, st->gc, left, top, width, height,
319                  toleft, totop);
320     }
321
322 #undef nrnd
323
324     return st->delay;
325 }
326
327 static void
328 decayscreen_reshape (Display *dpy, Window window, void *closure, 
329                  unsigned int w, unsigned int h)
330 {
331   struct state *st = (struct state *) closure;
332   XClearWindow (st->dpy, st->window);
333   XCopyArea (st->dpy, st->saved, st->window, st->gc,
334              0, 0, st->saved_w, st->saved_h,
335              ((int)w - st->saved_w) / 2,
336              ((int)h - st->saved_h) / 2);
337   st->sizex = w;
338   st->sizey = h;
339 }
340
341 static Bool
342 decayscreen_event (Display *dpy, Window window, void *closure, XEvent *event)
343 {
344   struct state *st = (struct state *) closure;
345   if (screenhack_event_helper (dpy, window, event))
346     {
347       st->start_time = 0;
348       return True;
349     }
350   return False;
351 }
352
353 static void
354 decayscreen_free (Display *dpy, Window window, void *closure)
355 {
356   struct state *st = (struct state *) closure;
357   free (st);
358 }
359
360 \f
361
362 static const char *decayscreen_defaults [] = {
363   ".background:                 Black",
364   ".foreground:                 Yellow",
365   "*dontClearRoot:              True",
366   "*fpsSolid:                   True",
367
368 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
369   "*visualID:                   Best",
370 #endif
371
372   "*delay:                      10000",
373   "*mode:                       random",
374   "*duration:                   120",
375 #ifdef USE_IPHONE
376   "*ignoreRotation:             True",
377   "*rotateImages:               True",
378 #endif
379   0
380 };
381
382 static XrmOptionDescRec decayscreen_options [] = {
383   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
384   { "-mode",            ".mode",                XrmoptionSepArg, 0 },
385   { "-duration",        ".duration",            XrmoptionSepArg, 0 },
386   { 0, 0, 0, 0 }
387 };
388
389
390 XSCREENSAVER_MODULE ("DecayScreen", decayscreen)