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