http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / hacks / zoom.c
1 /*
2  *  Copyright (C) 2000 James Macnicol
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #include <math.h>
14 #include "screenhack.h"
15
16 #ifndef MIN
17 #define MIN(a, b) (((a) < (b))?(a):(b))
18 #endif
19
20 #ifndef MAX
21 #define MAX(a, b) (((a) > (b))?(a):(b))
22 #endif
23
24 #define MINX 0.0
25 #define MINY 0.0
26 /* This should be *way* slower than the spotlight hack was */
27 #define X_PERIOD 45000.0
28 #define Y_PERIOD 36000.0
29
30 struct state {
31   Display *dpy;
32   Window window;
33   Screen *screen;
34
35   int sizex, sizey;
36
37   int delay;
38   int duration;
39   int pixwidth, pixheight, pixspacex, pixspacey, lensoffsetx, lensoffsety;
40   Bool lenses;
41
42   GC window_gc;
43
44   XImage *orig_map;
45   Pixmap pm;
46
47   int tlx, tly, s;
48
49   int sinusoid_offset;
50
51   time_t start_time;
52   async_load_state *img_loader;
53 };
54
55
56 static long currentTimeInMs(struct state *st)
57
58   struct timeval curTime;
59 #ifdef GETTIMEOFDAY_TWO_ARGS
60   struct timezone tz = {0,0};
61   gettimeofday(&curTime, &tz);
62 #else
63   gettimeofday(&curTime);
64 #endif
65   return curTime.tv_sec*1000 + curTime.tv_usec/1000.0;
66 }
67
68 static void *
69 zoom_init (Display *dpy, Window window)
70 {
71   struct state *st = (struct state *) calloc (1, sizeof(*st));
72   XGCValues gcv;
73   XWindowAttributes xgwa;
74   Colormap cmap;
75   unsigned long fg, bg;
76   long gcflags;
77   int nblocksx, nblocksy;
78
79   st->dpy = dpy;
80   st->window = window;
81   XGetWindowAttributes(st->dpy, st->window, &xgwa);
82   st->screen = xgwa.screen;
83   st->sizex = xgwa.width;
84   st->sizey = xgwa.height;
85   cmap = xgwa.colormap;
86   fg = get_pixel_resource(st->dpy, cmap, "foreground", "Foreground");
87   bg = get_pixel_resource(st->dpy, cmap, "background", "Background");
88
89   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
90   if (st->delay < 1)
91     st->delay = 1;
92   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
93   if (st->duration < 1)
94     st->duration = 1;
95   st->pixwidth = get_integer_resource(st->dpy, "pixwidth", "Integer");
96   if (st->pixwidth < 1)
97     st->pixwidth = 1;
98   st->pixheight = get_integer_resource(st->dpy, "pixheight", "Integer");
99   if (st->pixheight < 1)
100     st->pixheight = 1;
101   st->pixspacex = get_integer_resource(st->dpy, "pixspacex", "Integer");
102   if (st->pixspacex < 0)
103     st->pixspacex = 0;
104   st->pixspacey = get_integer_resource(st->dpy, "pixspacey", "Integer");
105   if (st->pixspacey < 0)
106     st->pixspacey = 0;
107   st->lenses = get_boolean_resource(st->dpy, "lenses", "Boolean");
108   st->lensoffsetx = get_integer_resource(st->dpy, "lensoffsetx", "Integer");
109   st->lensoffsetx = MAX(0, MIN(st->pixwidth, st->lensoffsetx));
110   st->lensoffsety = get_integer_resource(st->dpy, "lensoffsety", "Integer");
111   st->lensoffsety = MAX(0, MIN(st->pixwidth, st->lensoffsety));
112
113   gcv.function = GXcopy;
114   gcv.subwindow_mode = IncludeInferiors;
115   gcflags = GCForeground|GCFunction;
116   gcv.foreground = bg;
117   if (!st->lenses && use_subwindow_mode_p(xgwa.screen, st->window))       /* see grabscreen.c */
118     gcflags |= GCSubwindowMode;
119   st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
120
121
122   st->orig_map = NULL;
123   st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
124
125   XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
126   XSetWindowBackground(st->dpy, st->window, bg);
127
128   st->start_time = time ((time_t) 0);
129   st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
130                                             st->pm, 0, 0);
131
132   /* We might have needed this to grab the image, but if we leave this set
133      to GCSubwindowMode, then we'll *draw* right over subwindows too. */
134   XSetSubwindowMode (st->dpy, st->window_gc, ClipByChildren);
135
136
137   nblocksx = (int)ceil((double)st->sizex / (double)(st->pixwidth + st->pixspacex));
138   nblocksy = (int)ceil((double)st->sizey / (double)(st->pixheight + st->pixspacey));
139   if (st->lenses)
140     st->s = MAX((nblocksx - 1) * st->lensoffsetx + st->pixwidth, 
141             (nblocksy - 1) * st->lensoffsety + st->pixheight) * 2;
142   else
143     st->s = MAX(nblocksx, nblocksy) * 2;
144
145   st->sinusoid_offset = random();
146
147   return st;
148 }
149
150 static unsigned long
151 zoom_draw (Display *dpy, Window window, void *closure)
152 {
153   struct state *st = (struct state *) closure;
154   unsigned x, y, i, j;
155
156   long now;
157
158   if (st->img_loader)   /* still loading */
159     {
160       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
161       if (! st->img_loader) {  /* just finished */
162         XClearWindow (st->dpy, st->window);
163         st->start_time = time ((time_t) 0);
164         if (!st->lenses) {
165           st->orig_map = XGetImage(st->dpy, st->pm, 0, 0, st->sizex, st->sizey, ~0L, ZPixmap);
166 /*          XFreePixmap(st->dpy, st->pm);
167           st->pm = 0;*/
168         }
169       }
170       return st->delay;
171     }
172
173   if (!st->img_loader &&
174       st->start_time + st->duration < time ((time_t) 0)) {
175     st->img_loader = load_image_async_simple (0, st->screen, st->window,
176                                               st->pm, 0, 0);
177     return st->delay;
178   }
179
180 #define nrnd(x) (random() % (x))
181
182   now = currentTimeInMs(st);
183   now += st->sinusoid_offset;  /* don't run multiple screens in lock-step */
184
185   /* find new x,y */
186   st->tlx = ((1. + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
187     * (st->sizex - st->s/2) /* -s/4 */ + MINX;
188   st->tly = ((1. + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
189     * (st->sizey - st->s/2) /* -s/4 */ + MINY;
190
191   if (st->lenses) {
192     for (x = i = 0; x < st->sizex; x += (st->pixwidth + st->pixspacex), ++i)
193       for (y = j = 0; y < st->sizey; y += (st->pixheight + st->pixspacey), ++j) {
194         XCopyArea(st->dpy, st->pm /* src */, st->window /* dest */, st->window_gc,
195                   st->tlx + i * st->lensoffsetx /* src_x */, 
196                   st->tly + j * st->lensoffsety /* src_y */,
197                   st->pixwidth, st->pixheight,
198                   x /* dest_x */, y /* dest_y */);
199       }
200   } else {
201     for (x = i = 0; x < st->sizex; x += (st->pixwidth + st->pixspacex), ++i)
202       for (y = j = 0; y < st->sizey; y += (st->pixheight + st->pixspacey), ++j) {
203         XSetForeground(st->dpy, st->window_gc, XGetPixel(st->orig_map, st->tlx+i, st->tly+j));
204         XFillRectangle(st->dpy, st->window, st->window_gc, 
205                        i * (st->pixwidth + st->pixspacex),
206                        j * (st->pixheight + st->pixspacey), st->pixwidth, st->pixheight);
207       }
208   }
209
210   return st->delay;
211 }
212
213 static void
214 zoom_reshape (Display *dpy, Window window, void *closure, 
215                  unsigned int w, unsigned int h)
216 {
217 }
218
219 static Bool
220 zoom_event (Display *dpy, Window window, void *closure, XEvent *event)
221 {
222   return False;
223 }
224
225 static void
226 zoom_free (Display *dpy, Window window, void *closure)
227 {
228   struct state *st = (struct state *) closure;
229   XFreeGC (st->dpy, st->window_gc);
230   if (st->orig_map) XDestroyImage (st->orig_map);
231   if (st->pm) XFreePixmap (st->dpy, st->pm);
232   free (st);
233 }
234
235
236 static const char *zoom_defaults[] = {
237   "*dontClearRoot: True",
238   ".foreground: white",
239   ".background: #111111",
240 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
241   "*visualID: Best",
242 #endif
243   "*lenses:      false",
244   "*delay:       10000",
245   "*duration:    120",
246   "*pixwidth:    10",
247   "*pixheight:   10",
248   "*pixspacex:   2",
249   "*pixspacey:   2",
250   "*lensoffsetx: 5",
251   "*lensoffsety: 5",
252   0
253 };
254
255 static XrmOptionDescRec zoom_options[] = {
256   { "-lenses", ".lenses", XrmoptionNoArg, "true" },
257   { "-delay", ".delay", XrmoptionSepArg, 0 },
258   { "-duration",  ".duration", XrmoptionSepArg, 0 },
259   { "-pixwidth", ".pixwidth", XrmoptionSepArg, 0 },
260   { "-pixheight", ".pixheight", XrmoptionSepArg, 0 },
261   { "-pixspacex", ".pixspacex", XrmoptionSepArg, 0 },
262   { "-pixspacey", ".pixspacey", XrmoptionSepArg, 0 },
263   { "-lensoffsetx", ".lensoffsetx", XrmoptionSepArg, 0 },
264   { "-lensoffsety", ".lensoffsety", XrmoptionSepArg, 0 },
265   { 0, 0, 0, 0 }
266 };
267
268 XSCREENSAVER_MODULE ("Zoom", zoom)