610b952030c49c8d2eb6e9a84ab2798d23cccd2a
[xscreensaver] / hacks / rotzoomer.c
1 /* rotzoomer - creates a collage of rotated and scaled portions of the screen
2  * Copyright (C) 2001 Claudio Matsuoka <claudio@helllabs.org>
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 /*
14  * Options:
15  *
16  * -shm         enable MIT shared memory extension
17  * -no-shm      disable MIT shared memory extension
18  * -n <num>     number of zoomboxes
19  * -move        enable mobile zoomboxes
20  * -sweep       enable sweep mode
21  * -anim        enable snapshot mode
22  * -no-anim     enable snapshot mode
23  * -delay       delay in milliseconds
24  */
25
26 #include <math.h>
27 #include "screenhack.h"
28
29 #ifdef HAVE_XSHM_EXTENSION
30 #include "xshm.h"
31 #endif
32
33 struct zoom_area {
34   int w, h;             /* rectangle width and height */
35   int inc1, inc2;       /* rotation and zoom angle increments */
36   int dx, dy;           /* translation increments */
37   int a1, a2;           /* rotation and zoom angular variables */
38   int ox, oy;           /* origin in the background copy */
39   int xx, yy;           /* left-upper corner position (* 256) */
40   int x, y;             /* left-upper corner position */
41   int ww, hh;           /* valid area to place left-upper corner */
42   int n;                /* number of iteractions */
43   int count;            /* current iteraction */
44 };
45
46 struct state {
47   Display *dpy;
48   Window window;
49
50   GC gc;
51   Visual *visual;
52   XImage *orig_map, *buffer_map;
53   Colormap colormap;
54
55   int width, height;
56   struct zoom_area **zoom_box;
57   int num_zoom;
58   int move;
59   int sweep;
60   int delay;
61   int anim;
62   int duration;
63   time_t start_time;
64
65   async_load_state *img_loader;
66
67 #ifdef HAVE_XSHM_EXTENSION
68   Bool use_shm;
69   XShmSegmentInfo shm_info;
70 #endif
71 };
72
73
74 static void
75 rotzoom (struct state *st, struct zoom_area *za)
76 {
77   int x, y, c, s, zoom, z;
78   int x2 = za->x + za->w - 1, y2 = za->y + za->h - 1;
79   int ox = 0, oy = 0;
80
81   z = 8100 * sin (M_PI * za->a2 / 8192);
82   zoom = 8192 + z;
83
84   c = zoom * cos (M_PI * za->a1 / 8192);
85   s = zoom * sin (M_PI * za->a1 / 8192);
86   for (y = za->y; y <= y2; y++) {
87     for (x = za->x; x <= x2; x++) {
88       ox = (x * c + y * s) >> 13;
89       oy = (-x * s + y * c) >> 13;
90
91       while (ox < 0)
92         ox += st->width;
93       while (oy < 0)
94         oy += st->height;
95       while (ox >= st->width)
96         ox -= st->width;
97       while (oy >= st->height)
98         oy -= st->height;
99
100       XPutPixel (st->buffer_map, x, y, XGetPixel (st->orig_map, ox, oy));
101     }
102   }
103
104   za->a1 += za->inc1;           /* Rotation angle */
105   za->a1 &= 0x3fff;;
106
107   za->a2 += za->inc2;           /* Zoom */
108   za->a2 &= 0x3fff;
109
110   za->ox = ox;                  /* Save state for next iteration */
111   za->oy = oy;
112
113   za->count++;
114 }
115
116
117 static void
118 reset_zoom (struct state *st, struct zoom_area *za)
119 {
120   if (st->sweep) {
121     int speed = random () % 100 + 100;
122     switch (random () % 4) {
123     case 0:
124       za->w = st->width;
125       za->h = 10;
126       za->x = 0;
127       za->y = 0;
128       za->dx = 0;
129       za->dy = speed;
130       za->n = (st->height - 10) * 256 / speed;
131       break;
132     case 1:
133       za->w = 10;
134       za->h = st->height;
135       za->x = st->width - 10;
136       za->y = 0;
137       za->dx = -speed;
138       za->dy = 0;
139       za->n = (st->width - 10) * 256 / speed;
140       break;
141     case 2:
142       za->w = st->width;
143       za->h = 10;
144       za->x = 0;
145       za->y = st->height - 10;
146       za->dx = 0;
147       za->dy = -speed;
148       za->n = (st->height - 10) * 256 / speed;
149       break;
150     case 3:
151       za->w = 10;
152       za->h = st->height;
153       za->x = 0;
154       za->y = 0;
155       za->dx = speed;
156       za->dy = 0;
157       za->n = (st->width - 10) * 256 / speed;
158       break;
159     }
160     za->ww = st->width - za->w;
161     za->hh = st->height - za->h;
162
163     /* We want smaller angle increments in sweep mode (looks better) */
164
165     za->a1 = 0;
166     za->a2 = 0;
167     za->inc1 = ((2 * (random() & 1)) - 1) * (1 + random () % 7);
168     za->inc2 = ((2 * (random() & 1)) - 1) * (1 + random () % 7);
169   } else {
170     za->w = 50 + random() % 300;
171     za->h = 50 + random() % 300;
172
173     if (za->w > st->width / 3)
174       za->w = st->width / 3;
175     if (za->h > st->height / 3)
176       za->h = st->height / 3;
177
178     za->ww = st->width - za->w;
179     za->hh = st->height - za->h;
180
181     za->x = (za->ww ? random() % za->ww : 0);
182     za->y = (za->hh ? random() % za->hh : 0);
183
184     za->dx = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
185     za->dy = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
186
187     if (st->anim) {
188       za->n = 50 + random() % 1000;
189       za->a1 = 0;
190       za->a2 = 0;
191     } else {
192       za->n = 5 + random() % 10;
193       za->a1 = random ();
194       za->a2 = random ();
195     }
196
197     za->inc1 = ((2 * (random() & 1)) - 1) * (random () % 30);
198     za->inc2 = ((2 * (random() & 1)) - 1) * (random () % 30);
199   }
200
201   za->xx = za->x * 256;
202   za->yy = za->y * 256;
203
204   za->count = 0;
205 }
206
207
208 static struct zoom_area *
209 create_zoom (struct state *st)
210 {
211   struct zoom_area *za;
212
213   za = malloc (sizeof (struct zoom_area));
214   reset_zoom (st, za);
215
216   return za;
217 }
218
219
220 static void
221 update_position (struct zoom_area *za)
222 {
223   za->xx += za->dx;
224   za->yy += za->dy;
225
226   za->x = za->xx >> 8;
227   za->y = za->yy >> 8;
228
229   if (za->x < 0) {
230     za->x = 0;
231     za->dx = 100 + random() % 100;
232   }
233                 
234   if (za->y < 0) {
235     za->y = 0;
236     za->dy = 100 + random() % 100;
237   }
238                 
239   if (za->x > za->ww) {
240     za->x = za->ww;
241     za->dx = -(100 + random() % 100);
242   }
243
244   if (za->y > za->hh) {
245     za->y = za->hh;
246     za->dy = -(100 + random() % 100);
247   }
248 }
249
250
251 static void
252 DisplayImage (struct state *st, int x, int y, int w, int h)
253 {
254 #ifdef HAVE_XSHM_EXTENSION
255   if (st->use_shm)
256     XShmPutImage (st->dpy, st->window, st->gc, st->buffer_map, x, y, x, y,
257                   w, h, False);
258   else
259 #endif /* HAVE_XSHM_EXTENSION */
260     XPutImage(st->dpy, st->window, st->gc, st->buffer_map, x, y, x, y, w, h);
261 }
262
263
264 static void
265 init_hack (struct state *st)
266 {
267   int i;
268
269   st->start_time = time ((time_t) 0);
270   st->zoom_box = calloc (st->num_zoom, sizeof (struct zoom_area *));
271   for (i = 0; i < st->num_zoom; i++) {
272     st->zoom_box[i] = create_zoom (st);
273   }
274
275   if (st->height && st->orig_map->data)
276     memcpy (st->buffer_map->data, st->orig_map->data,
277             st->height * st->buffer_map->bytes_per_line);
278
279   DisplayImage(st, 0, 0, st->width, st->height);
280 }
281
282
283 static unsigned long
284 rotzoomer_draw (Display *disp, Window win, void *closure)
285 {
286   struct state *st = (struct state *) closure;
287   int delay = st->delay;
288   int i;
289
290   if (st->img_loader)   /* still loading */
291     {
292       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
293       if (! st->img_loader) {  /* just finished */
294         st->orig_map = XGetImage (st->dpy, st->window, 0, 0, 
295                                   st->width, st->height, ~0L, ZPixmap);
296         init_hack (st);
297       }
298       return st->delay;
299     }
300
301   if (!st->img_loader &&
302       st->start_time + st->duration < time ((time_t) 0)) {
303     XWindowAttributes xgwa;
304     XGetWindowAttributes(st->dpy, st->window, &xgwa);
305     st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
306                                               st->window, 0, 0);
307     st->start_time = time ((time_t) 0);
308     return st->delay;
309   }
310
311   for (i = 0; i < st->num_zoom; i++) {
312     if (st->move || st->sweep)
313       update_position (st->zoom_box[i]);
314
315     if (st->zoom_box[i]->n > 0) {
316       if (st->anim || st->zoom_box[i]->count == 0) {
317         rotzoom (st, st->zoom_box[i]);
318       } else {
319         delay = 1000000;
320       }
321       st->zoom_box[i]->n--;
322     } else {
323       reset_zoom (st, st->zoom_box[i]);
324     }
325   }
326
327   for (i = 0; i < st->num_zoom; i++) {
328     DisplayImage(st, st->zoom_box[i]->x, st->zoom_box[i]->y,
329                  st->zoom_box[i]->w, st->zoom_box[i]->h);
330   }
331
332   return delay;
333 }
334
335
336 static void
337 setup_X (struct state *st)
338 {
339   XWindowAttributes xgwa;
340   int depth;
341   XGCValues gcv;
342   long gcflags;
343
344   XGetWindowAttributes (st->dpy, st->window, &xgwa);
345   depth = xgwa.depth;
346   st->colormap = xgwa.colormap;
347   st->width = xgwa.width;
348   st->height = xgwa.height;
349   st->visual = xgwa.visual;
350
351   if (st->width % 2)
352     st->width--;
353   if (st->height % 2)
354     st->height--;
355
356   gcv.function = GXcopy;
357   gcv.subwindow_mode = IncludeInferiors;
358   gcflags = GCFunction;
359   if (use_subwindow_mode_p (xgwa.screen, st->window))   /* see grabscreen.c */
360     gcflags |= GCSubwindowMode;
361   st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
362   st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
363                                             st->window, 0, 0);
364
365   st->buffer_map = 0;
366
367 #ifdef HAVE_XSHM_EXTENSION
368   if (st->use_shm) {
369     st->buffer_map = create_xshm_image(st->dpy, xgwa.visual, depth,
370                                        ZPixmap, 0, &st->shm_info, st->width, st->height);
371     if (!st->buffer_map) {
372       st->use_shm = False;
373       fprintf(stderr, "create_xshm_image failed\n");
374     }
375   }
376 #endif /* HAVE_XSHM_EXTENSION */
377
378   if (!st->buffer_map) {
379     st->buffer_map = XCreateImage(st->dpy, xgwa.visual,
380                                   depth, ZPixmap, 0, 0, st->width, st->height, 8, 0);
381     st->buffer_map->data = (char *)calloc (st->buffer_map->height,
382                                            st->buffer_map->bytes_per_line);
383   }
384 }
385
386
387 static void *
388 rotzoomer_init (Display *dpy, Window window)
389 {
390   struct state *st = (struct state *) calloc (1, sizeof(*st));
391   char *s;
392   st->dpy = dpy;
393   st->window = window;
394 #ifdef HAVE_XSHM_EXTENSION
395   st->use_shm = get_boolean_resource (st->dpy, "useSHM", "Boolean");
396 #endif
397   st->num_zoom = get_integer_resource (st->dpy, "numboxes", "Integer");
398
399   s = get_string_resource (dpy, "mode", "Mode");
400   if (!s || !*s || !strcasecmp (s, "stationary"))
401     ;
402   else if (!strcasecmp (s, "move"))
403     st->move = True;
404   else if (!strcasecmp (s, "sweep"))
405     st->sweep = True;
406   else
407     fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
408
409   st->anim = get_boolean_resource (st->dpy, "anim", "Boolean");
410   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
411   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
412   if (st->delay < 0) st->delay = 0;
413   if (st->duration < 1) st->duration = 1;
414
415   /* In sweep or static mode, we want only one box */
416   if (st->sweep || !st->anim)
417     st->num_zoom = 1;
418
419   /* Can't have static sweep mode */
420   if (!st->anim)
421     st->sweep = 0;
422
423   st->start_time = time ((time_t) 0);
424
425   setup_X (st);
426
427   return st;
428 }
429
430 static void
431 rotzoomer_reshape (Display *dpy, Window window, void *closure, 
432                  unsigned int w, unsigned int h)
433 {
434 }
435
436 static Bool
437 rotzoomer_event (Display *dpy, Window window, void *closure, XEvent *event)
438 {
439   return False;
440 }
441
442 static void
443 rotzoomer_free (Display *dpy, Window window, void *closure)
444 {
445   struct state *st = (struct state *) closure;
446   free (st);
447 }
448
449
450 static const char *rotzoomer_defaults[] = {
451   ".background: black",
452   ".foreground: white",
453   "*fpsSolid:   true",
454 #ifdef HAVE_XSHM_EXTENSION
455   "*useSHM: True",
456 #else
457   "*useSHM: False",
458 #endif
459   "*anim: True",
460   "*mode: stationary",
461   "*numboxes: 2",
462   "*delay: 10000",
463   "*duration: 120",
464   0
465 };
466
467
468 static XrmOptionDescRec rotzoomer_options[] = {
469   { "-shm",     ".useSHM",      XrmoptionNoArg, "True"  },
470   { "-no-shm",  ".useSHM",      XrmoptionNoArg, "False" },
471   { "-mode",    ".mode",        XrmoptionSepArg, 0      },
472   { "-move",    ".mode",        XrmoptionNoArg, "move"  },
473   { "-sweep",   ".mode",        XrmoptionNoArg, "sweep" },
474   { "-anim",    ".anim",        XrmoptionNoArg, "True"  },
475   { "-no-anim", ".anim",        XrmoptionNoArg, "False" },
476   { "-delay",   ".delay",       XrmoptionSepArg, 0      },
477   {"-duration", ".duration",    XrmoptionSepArg, 0      },
478   { "-n",       ".numboxes",    XrmoptionSepArg, 0      },
479   { 0, 0, 0, 0 }
480 };
481
482
483 XSCREENSAVER_MODULE ("RotZoomer", rotzoomer)