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