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