From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / rotzoomer.c
1 /* rotzoomer - creates a collage of rotated and scaled portions of the screen
2  * Copyright (C) 2001-2016 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, 2014, 2016. */
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 #include "xshm.h"
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 circle;
61   int delay;
62   int anim;
63   int duration;
64   time_t start_time;
65
66   async_load_state *img_loader;
67   Pixmap pm;
68
69   XShmSegmentInfo shm_info;
70 };
71
72
73 static void
74 rotzoom (struct state *st, struct zoom_area *za)
75 {
76   int x, y, c, s, zoom, z;
77   int x2 = za->x + za->w - 1, y2 = za->y + za->h - 1;
78   int ox = 0, oy = 0;
79   int w2 = (za->w/2) * (za->w/2);
80
81   z = 8100 * sin (M_PI * za->a2 / 8192);
82   zoom = 8192 + z;
83
84   for (y = za->y; y <= y2; y++) {
85     for (x = za->x; x <= x2; x++) {
86       Bool copyp = True;
87       double a = M_PI * za->a1 / 8192;
88       c = zoom * cos (a);
89       s = zoom * sin (a);
90       if (st->circle) {
91         int cx = za->x + za->w / 2;
92         int cy = za->y + za->h / 2;
93         int dx = x - cx;
94         int dy = y - cy;
95         int d2 = (dx*dx) + (dy*dy);
96
97         if (d2 > w2) {
98           copyp = False;
99         } else {
100           double r = sqrt ((double) d2);
101           double th = atan ((double)dy / (double) (dx == 0 ? 1 : dx));
102           copyp = 1;
103           if (dx < 0) th += M_PI;
104           th += M_PI * (za->a1 / 600.0);
105           ox = cx + (int) (r * cos(th));
106           oy = cy + (int) (r * sin(th));
107         }
108       } else {
109         ox = (x * c + y * s) >> 13;
110         oy = (-x * s + y * c) >> 13;
111       }
112
113       if (copyp) {
114         while (ox < 0)
115           ox += st->width;
116         while (oy < 0)
117           oy += st->height;
118         while (ox >= st->width)
119           ox -= st->width;
120         while (oy >= st->height)
121           oy -= st->height;
122
123         XPutPixel (st->buffer_map, x, y, XGetPixel (st->orig_map, ox, oy));
124       }
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   if (st->circle && za->n <= 1)
138     {
139       /* Done rotating the circle: copy the bits from the working set back
140          into the origin, so that subsequent rotations pick up these changes.
141        */
142       int cx = za->x + za->w / 2;
143       int cy = za->y + za->h / 2;
144       int w2 = (za->w/2) * (za->w/2);
145       for (y = za->y; y < za->y + za->h; y++)
146         for (x = za->x; x < za->x + za->w; x++)
147           {
148             int dx = x - cx;
149             int dy = y - cy;
150             int d2 = (dx*dx) + (dy*dy);
151             if (d2 <= w2)
152               XPutPixel (st->orig_map, x, y, XGetPixel (st->buffer_map, x, y));
153           }
154     }
155
156   za->count++;
157 }
158
159
160 static void
161 reset_zoom (struct state *st, struct zoom_area *za)
162 {
163   if (st->sweep) {
164     int speed = random () % 100 + 100;
165     switch (random () % 4) {
166     case 0:
167       za->w = st->width;
168       za->h = 10;
169       za->x = 0;
170       za->y = 0;
171       za->dx = 0;
172       za->dy = speed;
173       za->n = (st->height - 10) * 256 / speed;
174       break;
175     case 1:
176       za->w = 10;
177       za->h = st->height;
178       za->x = st->width - 10;
179       za->y = 0;
180       za->dx = -speed;
181       za->dy = 0;
182       za->n = (st->width - 10) * 256 / speed;
183       break;
184     case 2:
185       za->w = st->width;
186       za->h = 10;
187       za->x = 0;
188       za->y = st->height - 10;
189       za->dx = 0;
190       za->dy = -speed;
191       za->n = (st->height - 10) * 256 / speed;
192       break;
193     case 3:
194       za->w = 10;
195       za->h = st->height;
196       za->x = 0;
197       za->y = 0;
198       za->dx = speed;
199       za->dy = 0;
200       za->n = (st->width - 10) * 256 / speed;
201       break;
202     }
203     za->ww = st->width - za->w;
204     za->hh = st->height - za->h;
205
206     /* We want smaller angle increments in sweep mode (looks better) */
207
208     za->a1 = 0;
209     za->a2 = 0;
210     za->inc1 = ((2 * (random() & 1)) - 1) * (1 + random () % 7);
211     za->inc2 = ((2 * (random() & 1)) - 1) * (1 + random () % 7);
212   } else if (st->circle) {
213
214     za->w = 50 + random() % 300;
215     if (za->w > st->width / 3)
216       za->w = st->width / 3;
217     if (za->w > st->height / 3)
218       za->w = st->height / 3;
219     za->h = za->w;
220
221     za->ww = st->width  - za->w;
222     za->hh = st->height - za->h;
223
224     za->x = (za->ww ? random() % za->ww : 0);
225     za->y = (za->hh ? random() % za->hh : 0);
226     za->dx = 0;
227     za->dy = 0;
228     za->a1 = 0;
229     za->a2 = 0;
230     za->count = 0;
231
232     /* #### If we go clockwise, it doesn't start rotating from 0.
233        So only go counter-clockwise for now. Sigh. */
234     za->inc1 = (random () % 30);
235     za->inc2 = 0;
236     za->n = 50 + random() % 100;
237
238     if (!st->anim) {
239       za->count = random() % (za->n / 2);
240       za->a1 = random();
241     }
242
243   } else {
244     za->w = 50 + random() % 300;
245     za->h = 50 + random() % 300;
246
247     if (za->w > st->width / 3)
248       za->w = st->width / 3;
249     if (za->h > st->height / 3)
250       za->h = st->height / 3;
251
252     za->ww = st->width - za->w;
253     za->hh = st->height - za->h;
254
255     za->x = (za->ww ? random() % za->ww : 0);
256     za->y = (za->hh ? random() % za->hh : 0);
257
258     za->dx = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
259     za->dy = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
260
261     if (st->anim) {
262       za->n = 50 + random() % 1000;
263       za->a1 = 0;
264       za->a2 = 0;
265     } else {
266       za->n = 5 + random() % 10;
267       za->a1 = random ();
268       za->a2 = random ();
269     }
270
271     za->inc1 = ((2 * (random() & 1)) - 1) * (random () % 30);
272     za->inc2 = ((2 * (random() & 1)) - 1) * (random () % 30);
273   }
274
275   za->xx = za->x * 256;
276   za->yy = za->y * 256;
277
278   za->count = 0;
279 }
280
281
282 static struct zoom_area *
283 create_zoom (struct state *st)
284 {
285   struct zoom_area *za;
286
287   za = calloc (1, sizeof (struct zoom_area));
288   reset_zoom (st, za);
289
290   return za;
291 }
292
293
294 static void
295 update_position (struct zoom_area *za)
296 {
297   za->xx += za->dx;
298   za->yy += za->dy;
299
300   za->x = za->xx >> 8;
301   za->y = za->yy >> 8;
302
303   if (za->x < 0) {
304     za->x = 0;
305     za->dx = 100 + random() % 100;
306   }
307                 
308   if (za->y < 0) {
309     za->y = 0;
310     za->dy = 100 + random() % 100;
311   }
312                 
313   if (za->x > za->ww) {
314     za->x = za->ww;
315     za->dx = -(100 + random() % 100);
316   }
317
318   if (za->y > za->hh) {
319     za->y = za->hh;
320     za->dy = -(100 + random() % 100);
321   }
322 }
323
324
325 static void
326 DisplayImage (struct state *st, int x, int y, int w, int h)
327 {
328   put_xshm_image (st->dpy, st->window, st->gc, st->buffer_map, x, y, x, y,
329                   w, h, &st->shm_info);
330 }
331
332
333 static void
334 set_mode(struct state *st)
335 {
336   char *s = get_string_resource (st->dpy, "mode", "Mode");
337   if (!s || !*s || !strcasecmp (s, "random"))
338     {
339       switch (random() % 4) {
340       case 0: s = "stationary"; break;
341       case 1: s = "move"; break;
342       case 2: s = "sweep"; break;
343       case 3: s = "circle"; break;
344       default: abort();
345       }
346     }
347
348   st->move = False;
349   st->sweep = False;
350   st->circle = False;
351
352   if (!strcasecmp (s, "stationary"))
353     ;
354   else if (!strcasecmp (s, "move"))
355     st->move = True;
356   else if (!strcasecmp (s, "sweep"))
357     st->sweep = True;
358   else if (!strcasecmp (s, "circle"))
359     st->circle = True;
360   else
361     fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
362 }
363
364
365 static void
366 init_hack (struct state *st)
367 {
368   int i;
369
370   set_mode (st);
371
372   st->start_time = time ((time_t *) 0);
373   st->zoom_box = calloc (st->num_zoom, sizeof (struct zoom_area *));
374   for (i = 0; i < st->num_zoom; i++) {
375     st->zoom_box[i] = create_zoom (st);
376   }
377
378   if (st->height && st->orig_map->data)
379     memcpy (st->buffer_map->data, st->orig_map->data,
380             st->height * st->buffer_map->bytes_per_line);
381
382   DisplayImage(st, 0, 0, st->width, st->height);
383 }
384
385
386 static unsigned long
387 rotzoomer_draw (Display *disp, Window win, void *closure)
388 {
389   struct state *st = (struct state *) closure;
390   int delay = st->delay;
391   int i;
392
393   if (st->img_loader)   /* still loading */
394     {
395       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
396       if (! st->img_loader) {  /* just finished */
397         if (! st->pm) abort();
398         st->orig_map = XGetImage (st->dpy, st->pm,
399                                   0, 0, st->width, st->height,
400                                   ~0L, ZPixmap);
401         init_hack (st);
402       }
403       return st->delay;
404     }
405
406   if (!st->img_loader &&
407       st->start_time + st->duration < time ((time_t *) 0)) {
408     XWindowAttributes xgwa;
409     XGetWindowAttributes(st->dpy, st->window, &xgwa);
410     /* On MacOS X11, XGetImage on a Window often gets an inexplicable BadMatch,
411        possibly due to the window manager having occluded something?  It seems
412        nondeterministic. Loading the image into a pixmap instead fixes it. */
413     if (st->pm) XFreePixmap (st->dpy, st->pm);
414     st->pm = XCreatePixmap (st->dpy, st->window,
415                             xgwa.width, xgwa.height, xgwa.depth);
416     st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
417                                               st->pm, 0, 0);
418     st->start_time = time ((time_t *) 0);
419     return st->delay;
420   }
421
422   for (i = 0; i < st->num_zoom; i++) {
423     if (st->move || st->sweep)
424       update_position (st->zoom_box[i]);
425
426     if (st->zoom_box[i]->n > 0) {
427       if (st->anim || st->zoom_box[i]->count == 0) {
428         rotzoom (st, st->zoom_box[i]);
429       } else {
430         delay = 1000000;
431       }
432       st->zoom_box[i]->n--;
433     } else {
434       reset_zoom (st, st->zoom_box[i]);
435     }
436   }
437
438   for (i = 0; i < st->num_zoom; i++) {
439     DisplayImage(st, st->zoom_box[i]->x, st->zoom_box[i]->y,
440                  st->zoom_box[i]->w, st->zoom_box[i]->h);
441   }
442
443   return delay;
444 }
445
446
447 static void
448 setup_X (struct state *st)
449 {
450   XWindowAttributes xgwa;
451   int depth;
452   XGCValues gcv;
453   long gcflags;
454
455   XGetWindowAttributes (st->dpy, st->window, &xgwa);
456   depth = xgwa.depth;
457   st->colormap = xgwa.colormap;
458   st->width = xgwa.width;
459   st->height = xgwa.height;
460   st->visual = xgwa.visual;
461
462   if (st->width % 2)
463     st->width--;
464   if (st->height % 2)
465     st->height--;
466
467   gcv.function = GXcopy;
468   gcv.subwindow_mode = IncludeInferiors;
469   gcflags = GCFunction;
470   if (use_subwindow_mode_p (xgwa.screen, st->window))   /* see grabscreen.c */
471     gcflags |= GCSubwindowMode;
472   st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
473   if (st->pm) XFreePixmap (st->dpy, st->pm);
474   st->pm = XCreatePixmap (st->dpy, st->window,
475                           xgwa.width, xgwa.height, xgwa.depth);
476   st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
477                                             st->pm, 0, 0);
478
479   st->buffer_map = create_xshm_image(st->dpy, xgwa.visual, depth,
480                                      ZPixmap, &st->shm_info, st->width, st->height);
481 }
482
483
484 static void *
485 rotzoomer_init (Display *dpy, Window window)
486 {
487   struct state *st = (struct state *) calloc (1, sizeof(*st));
488   st->dpy = dpy;
489   st->window = window;
490   st->num_zoom = get_integer_resource (st->dpy, "numboxes", "Integer");
491
492   set_mode(st);
493
494   st->anim = get_boolean_resource (st->dpy, "anim", "Boolean");
495   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
496   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
497   if (st->delay < 0) st->delay = 0;
498   if (st->duration < 1) st->duration = 1;
499
500   /* In sweep or static mode, we want only one box */
501   if (st->sweep || !st->anim)
502     st->num_zoom = 1;
503
504   /* Can't have static sweep mode */
505   if (!st->anim)
506     st->sweep = 0;
507
508   if (st->circle) {
509     st->move = 0;
510     st->sweep = 0;
511   }
512
513   st->start_time = time ((time_t *) 0);
514
515   setup_X (st);
516
517   return st;
518 }
519
520 static void
521 rotzoomer_reshape (Display *dpy, Window window, void *closure, 
522                  unsigned int w, unsigned int h)
523 {
524 }
525
526 static Bool
527 rotzoomer_event (Display *dpy, Window window, void *closure, XEvent *event)
528 {
529   struct state *st = (struct state *) closure;
530   if (screenhack_event_helper (dpy, window, event))
531     {
532       st->start_time = 0;
533       return True;
534     }
535   return False;
536 }
537
538 static void
539 rotzoomer_free (Display *dpy, Window window, void *closure)
540 {
541   struct state *st = (struct state *) closure;
542   if (st->pm) XFreePixmap (dpy, st->pm);
543   free (st);
544 }
545
546
547 static const char *rotzoomer_defaults[] = {
548   ".background: black",
549   ".foreground: white",
550   "*fpsSolid:   true",
551 #ifdef HAVE_XSHM_EXTENSION
552   "*useSHM: True",
553 #else
554   "*useSHM: False",
555 #endif
556   "*anim: True",
557   "*mode: random",
558   "*numboxes: 2",
559   "*delay: 10000",
560   "*duration: 120",
561 #ifdef HAVE_MOBILE
562   "*ignoreRotation: True",
563   "*rotateImages:   True",
564 #endif
565   0
566 };
567
568
569 static XrmOptionDescRec rotzoomer_options[] = {
570   { "-shm",     ".useSHM",      XrmoptionNoArg, "True"  },
571   { "-no-shm",  ".useSHM",      XrmoptionNoArg, "False" },
572   { "-mode",    ".mode",        XrmoptionSepArg, 0      },
573   { "-move",    ".mode",        XrmoptionNoArg, "move"  },
574   { "-sweep",   ".mode",        XrmoptionNoArg, "sweep" },
575   { "-circle",  ".mode",        XrmoptionNoArg, "circle"},
576   { "-anim",    ".anim",        XrmoptionNoArg, "True"  },
577   { "-no-anim", ".anim",        XrmoptionNoArg, "False" },
578   { "-delay",   ".delay",       XrmoptionSepArg, 0      },
579   {"-duration", ".duration",    XrmoptionSepArg, 0      },
580   { "-n",       ".numboxes",    XrmoptionSepArg, 0      },
581   { 0, 0, 0, 0 }
582 };
583
584
585 XSCREENSAVER_MODULE ("RotZoomer", rotzoomer)