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