From http://www.jwz.org/xscreensaver/xscreensaver-5.30.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 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   bg = get_pixel_resource(st->dpy, cmap, "background", "Background");
87
88   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
89   if (st->delay < 1)
90     st->delay = 1;
91   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
92   if (st->duration < 1)
93     st->duration = 1;
94   st->pixwidth = get_integer_resource(st->dpy, "pixwidth", "Integer");
95   if (st->pixwidth < 1)
96     st->pixwidth = 1;
97   st->pixheight = get_integer_resource(st->dpy, "pixheight", "Integer");
98   if (st->pixheight < 1)
99     st->pixheight = 1;
100   st->pixspacex = get_integer_resource(st->dpy, "pixspacex", "Integer");
101   if (st->pixspacex < 0)
102     st->pixspacex = 0;
103   st->pixspacey = get_integer_resource(st->dpy, "pixspacey", "Integer");
104   if (st->pixspacey < 0)
105     st->pixspacey = 0;
106   st->lenses = get_boolean_resource(st->dpy, "lenses", "Boolean");
107   st->lensoffsetx = get_integer_resource(st->dpy, "lensoffsetx", "Integer");
108   st->lensoffsetx = MAX(0, MIN(st->pixwidth, st->lensoffsetx));
109   st->lensoffsety = get_integer_resource(st->dpy, "lensoffsety", "Integer");
110   st->lensoffsety = MAX(0, MIN(st->pixwidth, st->lensoffsety));
111
112   gcv.function = GXcopy;
113   gcv.subwindow_mode = IncludeInferiors;
114   gcflags = GCForeground|GCFunction;
115   gcv.foreground = bg;
116   if (!st->lenses && use_subwindow_mode_p(xgwa.screen, st->window))       /* see grabscreen.c */
117     gcflags |= GCSubwindowMode;
118   st->window_gc = XCreateGC(st->dpy, st->window, gcflags, &gcv);
119
120
121   st->orig_map = NULL;
122   st->pm = XCreatePixmap(st->dpy, st->window, st->sizex, st->sizey, xgwa.depth);
123
124   XFillRectangle(st->dpy, st->window, st->window_gc, 0, 0, st->sizex, st->sizey);
125   XSetWindowBackground(st->dpy, st->window, bg);
126
127   st->start_time = time ((time_t) 0);
128   st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
129                                             st->pm, 0, 0);
130
131   /* We might have needed this to grab the image, but if we leave this set
132      to GCSubwindowMode, then we'll *draw* right over subwindows too. */
133   XSetSubwindowMode (st->dpy, st->window_gc, ClipByChildren);
134
135
136   nblocksx = (int)ceil((double)st->sizex / (double)(st->pixwidth + st->pixspacex));
137   nblocksy = (int)ceil((double)st->sizey / (double)(st->pixheight + st->pixspacey));
138   if (st->lenses)
139     st->s = MAX((nblocksx - 1) * st->lensoffsetx + st->pixwidth, 
140             (nblocksy - 1) * st->lensoffsety + st->pixheight) * 2;
141   else
142     st->s = MAX(nblocksx, nblocksy) * 2;
143
144   st->sinusoid_offset = random();
145
146   return st;
147 }
148
149 static unsigned long
150 zoom_draw (Display *dpy, Window window, void *closure)
151 {
152   struct state *st = (struct state *) closure;
153   unsigned x, y, i, j;
154
155   long now;
156
157   if (st->img_loader)   /* still loading */
158     {
159       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
160       if (! st->img_loader) {  /* just finished */
161         XClearWindow (st->dpy, st->window);
162         st->start_time = time ((time_t) 0);
163         if (!st->lenses) {
164           st->orig_map = XGetImage(st->dpy, st->pm, 0, 0, st->sizex, st->sizey, ~0L, ZPixmap);
165 /*          XFreePixmap(st->dpy, st->pm);
166           st->pm = 0;*/
167         }
168       }
169       return st->delay;
170     }
171
172   if (!st->img_loader &&
173       st->start_time + st->duration < time ((time_t) 0)) {
174     st->img_loader = load_image_async_simple (0, st->screen, st->window,
175                                               st->pm, 0, 0);
176     return st->delay;
177   }
178
179 #define nrnd(x) (random() % (x))
180
181   now = currentTimeInMs(st);
182   now += st->sinusoid_offset;  /* don't run multiple screens in lock-step */
183
184   /* find new x,y */
185   st->tlx = ((1. + sin(((double)now) / X_PERIOD * 2. * M_PI))/2.0)
186     * (st->sizex - st->s/2) /* -s/4 */ + MINX;
187   st->tly = ((1. + sin(((double)now) / Y_PERIOD * 2. * M_PI))/2.0)
188     * (st->sizey - st->s/2) /* -s/4 */ + MINY;
189
190   if (st->lenses) {
191     for (x = i = 0; x < st->sizex; x += (st->pixwidth + st->pixspacex), ++i)
192       for (y = j = 0; y < st->sizey; y += (st->pixheight + st->pixspacey), ++j) {
193         XCopyArea(st->dpy, st->pm /* src */, st->window /* dest */, st->window_gc,
194                   st->tlx + i * st->lensoffsetx /* src_x */, 
195                   st->tly + j * st->lensoffsety /* src_y */,
196                   st->pixwidth, st->pixheight,
197                   x /* dest_x */, y /* dest_y */);
198       }
199   } else {
200     for (x = i = 0; x < st->sizex; x += (st->pixwidth + st->pixspacex), ++i)
201       for (y = j = 0; y < st->sizey; y += (st->pixheight + st->pixspacey), ++j) {
202         XSetForeground(st->dpy, st->window_gc, XGetPixel(st->orig_map, st->tlx+i, st->tly+j));
203         XFillRectangle(st->dpy, st->window, st->window_gc, 
204                        i * (st->pixwidth + st->pixspacex),
205                        j * (st->pixheight + st->pixspacey), st->pixwidth, st->pixheight);
206       }
207   }
208
209   return st->delay;
210 }
211
212 static void
213 zoom_reshape (Display *dpy, Window window, void *closure, 
214                  unsigned int w, unsigned int h)
215 {
216 }
217
218 static Bool
219 zoom_event (Display *dpy, Window window, void *closure, XEvent *event)
220 {
221   struct state *st = (struct state *) closure;
222   if (screenhack_event_helper (dpy, window, event))
223     {
224       st->start_time = 0;
225       return True;
226     }
227   return False;
228 }
229
230 static void
231 zoom_free (Display *dpy, Window window, void *closure)
232 {
233   struct state *st = (struct state *) closure;
234   XFreeGC (st->dpy, st->window_gc);
235   if (st->orig_map) XDestroyImage (st->orig_map);
236   if (st->pm) XFreePixmap (st->dpy, st->pm);
237   free (st);
238 }
239
240
241 static const char *zoom_defaults[] = {
242   "*dontClearRoot: True",
243   ".foreground: white",
244   ".background: #111111",
245   "*fpsSolid:   true",
246 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
247   "*visualID: Best",
248 #endif
249   "*lenses:      true",
250   "*delay:       10000",
251   "*duration:    120",
252   "*pixwidth:    10",
253   "*pixheight:   10",
254   "*pixspacex:   2",
255   "*pixspacey:   2",
256   "*lensoffsetx: 5",
257   "*lensoffsety: 5",
258 #ifdef USE_IPHONE
259   "*ignoreRotation: True",
260   "*rotateImages:   True",
261 #endif
262   0
263 };
264
265 static XrmOptionDescRec zoom_options[] = {
266   { "-lenses", ".lenses", XrmoptionNoArg, "true" },
267   { "-no-lenses", ".lenses", XrmoptionNoArg, "false" },
268   { "-delay", ".delay", XrmoptionSepArg, 0 },
269   { "-duration",  ".duration", XrmoptionSepArg, 0 },
270   { "-pixwidth", ".pixwidth", XrmoptionSepArg, 0 },
271   { "-pixheight", ".pixheight", XrmoptionSepArg, 0 },
272   { "-pixspacex", ".pixspacex", XrmoptionSepArg, 0 },
273   { "-pixspacey", ".pixspacey", XrmoptionSepArg, 0 },
274   { "-lensoffsetx", ".lensoffsetx", XrmoptionSepArg, 0 },
275   { "-lensoffsety", ".lensoffsety", XrmoptionSepArg, 0 },
276   { 0, 0, 0, 0 }
277 };
278
279 XSCREENSAVER_MODULE ("Zoom", zoom)