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
rotzoom(struct state * st,struct zoom_area * za)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
reset_zoom(struct state * st,struct zoom_area * za)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 *
create_zoom(struct state * st)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
update_position(struct zoom_area * za)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
DisplayImage(struct state * st,int x,int y,int w,int h)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
set_mode(struct state * st)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       free (s);
340       switch (random() % 4) {
341       case 0: s = "stationary"; break;
342       case 1: s = "move"; break;
343       case 2: s = "sweep"; break;
344       case 3: s = "circle"; break;
345       default: abort();
346       }
347     }
348 
349   st->move = False;
350   st->sweep = False;
351   st->circle = False;
352 
353   if (!strcasecmp (s, "stationary"))
354     ;
355   else if (!strcasecmp (s, "move"))
356     st->move = True;
357   else if (!strcasecmp (s, "sweep"))
358     st->sweep = True;
359   else if (!strcasecmp (s, "circle"))
360     st->circle = True;
361   else
362     fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
363 }
364 
365 
366 static void
init_hack(struct state * st)367 init_hack (struct state *st)
368 {
369   int i;
370 
371   set_mode (st);
372 
373   st->start_time = time ((time_t *) 0);
374 
375   if (st->zoom_box) {
376     for (i = 0; i < st->num_zoom; i++)
377       if (st->zoom_box[i]) free (st->zoom_box[i]);
378     free (st->zoom_box);
379   }
380   st->zoom_box = calloc (st->num_zoom, sizeof (struct zoom_area *));
381   for (i = 0; i < st->num_zoom; i++) {
382     if (st->zoom_box[i]) free (st->zoom_box[i]);
383     st->zoom_box[i] = create_zoom (st);
384   }
385 
386   if (st->height && st->orig_map->data)
387     memcpy (st->buffer_map->data, st->orig_map->data,
388 	    st->height * st->buffer_map->bytes_per_line);
389 
390   DisplayImage(st, 0, 0, st->width, st->height);
391 }
392 
393 
394 static unsigned long
rotzoomer_draw(Display * disp,Window win,void * closure)395 rotzoomer_draw (Display *disp, Window win, void *closure)
396 {
397   struct state *st = (struct state *) closure;
398   int delay = st->delay;
399   int i;
400 
401   if (st->img_loader)   /* still loading */
402     {
403       st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0);
404       if (! st->img_loader) {  /* just finished */
405         if (! st->pm) abort();
406         if (st->orig_map) XDestroyImage (st->orig_map);
407 	st->orig_map = XGetImage (st->dpy, st->pm,
408                                   0, 0, st->width, st->height,
409                                   ~0L, ZPixmap);
410         init_hack (st);
411       }
412       return st->delay;
413     }
414 
415   if (!st->img_loader &&
416       st->start_time + st->duration < time ((time_t *) 0)) {
417     XWindowAttributes xgwa;
418     XGetWindowAttributes(st->dpy, st->window, &xgwa);
419     /* On MacOS X11, XGetImage on a Window often gets an inexplicable BadMatch,
420        possibly due to the window manager having occluded something?  It seems
421        nondeterministic. Loading the image into a pixmap instead fixes it. */
422     if (st->pm) XFreePixmap (st->dpy, st->pm);
423     st->pm = XCreatePixmap (st->dpy, st->window,
424                             xgwa.width, xgwa.height, xgwa.depth);
425     st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
426                                               st->pm, 0, 0);
427     st->start_time = time ((time_t *) 0);
428     return st->delay;
429   }
430 
431   for (i = 0; i < st->num_zoom; i++) {
432     if (st->move || st->sweep)
433       update_position (st->zoom_box[i]);
434 
435     if (st->zoom_box[i]->n > 0) {
436       if (st->anim || st->zoom_box[i]->count == 0) {
437         rotzoom (st, st->zoom_box[i]);
438       } else {
439         delay = 1000000;
440       }
441       st->zoom_box[i]->n--;
442     } else {
443       reset_zoom (st, st->zoom_box[i]);
444     }
445   }
446 
447   for (i = 0; i < st->num_zoom; i++) {
448     DisplayImage(st, st->zoom_box[i]->x, st->zoom_box[i]->y,
449                  st->zoom_box[i]->w, st->zoom_box[i]->h);
450   }
451 
452   return delay;
453 }
454 
455 
456 static void
setup_X(struct state * st)457 setup_X (struct state *st)
458 {
459   XWindowAttributes xgwa;
460   int depth;
461   XGCValues gcv;
462   long gcflags;
463 
464   XGetWindowAttributes (st->dpy, st->window, &xgwa);
465   depth = xgwa.depth;
466   st->colormap = xgwa.colormap;
467   st->width = xgwa.width;
468   st->height = xgwa.height;
469   st->visual = xgwa.visual;
470 
471   if (st->width % 2)
472     st->width--;
473   if (st->height % 2)
474     st->height--;
475 
476   gcv.function = GXcopy;
477   gcv.subwindow_mode = IncludeInferiors;
478   gcflags = GCFunction;
479   if (use_subwindow_mode_p (xgwa.screen, st->window))	/* see grabscreen.c */
480     gcflags |= GCSubwindowMode;
481   st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv);
482   if (st->pm) XFreePixmap (st->dpy, st->pm);
483   st->pm = XCreatePixmap (st->dpy, st->window,
484                           xgwa.width, xgwa.height, xgwa.depth);
485   st->img_loader = load_image_async_simple (0, xgwa.screen, st->window,
486                                             st->pm, 0, 0);
487 
488   st->buffer_map = create_xshm_image(st->dpy, xgwa.visual, depth,
489                                      ZPixmap, &st->shm_info, st->width, st->height);
490 }
491 
492 
493 static void *
rotzoomer_init(Display * dpy,Window window)494 rotzoomer_init (Display *dpy, Window window)
495 {
496   struct state *st = (struct state *) calloc (1, sizeof(*st));
497   st->dpy = dpy;
498   st->window = window;
499   st->num_zoom = get_integer_resource (st->dpy, "numboxes", "Integer");
500 
501   set_mode(st);
502 
503   st->anim = get_boolean_resource (st->dpy, "anim", "Boolean");
504   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
505   st->duration = get_integer_resource (st->dpy, "duration", "Seconds");
506   if (st->delay < 0) st->delay = 0;
507   if (st->duration < 1) st->duration = 1;
508 
509   /* In sweep or static mode, we want only one box */
510   if (st->sweep || !st->anim)
511     st->num_zoom = 1;
512 
513   /* Can't have static sweep mode */
514   if (!st->anim)
515     st->sweep = 0;
516 
517   if (st->circle) {
518     st->move = 0;
519     st->sweep = 0;
520   }
521 
522   st->start_time = time ((time_t *) 0);
523 
524   setup_X (st);
525 
526   return st;
527 }
528 
529 static void
rotzoomer_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)530 rotzoomer_reshape (Display *dpy, Window window, void *closure,
531                  unsigned int w, unsigned int h)
532 {
533 }
534 
535 static Bool
rotzoomer_event(Display * dpy,Window window,void * closure,XEvent * event)536 rotzoomer_event (Display *dpy, Window window, void *closure, XEvent *event)
537 {
538   struct state *st = (struct state *) closure;
539   if (screenhack_event_helper (dpy, window, event))
540     {
541       st->start_time = 0;
542       return True;
543     }
544   return False;
545 }
546 
547 static void
rotzoomer_free(Display * dpy,Window window,void * closure)548 rotzoomer_free (Display *dpy, Window window, void *closure)
549 {
550   struct state *st = (struct state *) closure;
551   if (st->pm) XFreePixmap (dpy, st->pm);
552   if (st->gc) XFreeGC (dpy, st->gc);
553   if (st->orig_map) XDestroyImage (st->orig_map);
554   if (st->buffer_map) destroy_xshm_image (dpy, st->buffer_map, &st->shm_info);
555   if (st->zoom_box) {
556     int i;
557     for (i = 0; i < st->num_zoom; i++)
558       if (st->zoom_box[i]) free (st->zoom_box[i]);
559     free (st->zoom_box);
560   }
561   free (st);
562 }
563 
564 
565 static const char *rotzoomer_defaults[] = {
566   ".background: black",
567   ".foreground: white",
568   "*fpsSolid:	true",
569 #ifdef HAVE_XSHM_EXTENSION
570   "*useSHM: True",
571 #else
572   "*useSHM: False",
573 #endif
574   "*anim: True",
575   "*mode: random",
576   "*numboxes: 2",
577   "*delay: 10000",
578   "*duration: 120",
579 #ifdef HAVE_MOBILE
580   "*ignoreRotation: True",
581   "*rotateImages:   True",
582 #endif
583   0
584 };
585 
586 
587 static XrmOptionDescRec rotzoomer_options[] = {
588   { "-shm",	".useSHM",	XrmoptionNoArg, "True"  },
589   { "-no-shm",	".useSHM",	XrmoptionNoArg, "False" },
590   { "-mode",	".mode",	XrmoptionSepArg, 0      },
591   { "-move",	".mode",	XrmoptionNoArg, "move"  },
592   { "-sweep",	".mode",	XrmoptionNoArg, "sweep" },
593   { "-circle",	".mode",	XrmoptionNoArg, "circle"},
594   { "-anim",	".anim",	XrmoptionNoArg, "True"  },
595   { "-no-anim",	".anim",	XrmoptionNoArg, "False" },
596   { "-delay",	".delay",	XrmoptionSepArg, 0      },
597   {"-duration",	".duration",	XrmoptionSepArg, 0      },
598   { "-n",	".numboxes",	XrmoptionSepArg, 0      },
599   { 0, 0, 0, 0 }
600 };
601 
602 
603 XSCREENSAVER_MODULE ("RotZoomer", rotzoomer)
604