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