1 /* rotzoomer - creates a collage of rotated and scaled portions of the screen
2 * Copyright (C) 2001-2016 Claudio Matsuoka <claudio@helllabs.org>
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
13 /* Circle-mode by jwz, 2014, 2016. */
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
30 #include "screenhack.h"
32 #ifdef HAVE_XSHM_EXTENSION
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 */
55 XImage *orig_map, *buffer_map;
59 struct zoom_area **zoom_box;
69 async_load_state *img_loader;
72 #ifdef HAVE_XSHM_EXTENSION
74 XShmSegmentInfo shm_info;
80 rotzoom (struct state *st, struct zoom_area *za)
82 int x, y, c, s, zoom, z;
83 int x2 = za->x + za->w - 1, y2 = za->y + za->h - 1;
85 int w2 = (za->w/2) * (za->w/2);
87 z = 8100 * sin (M_PI * za->a2 / 8192);
90 for (y = za->y; y <= y2; y++) {
91 for (x = za->x; x <= x2; x++) {
93 double a = M_PI * za->a1 / 8192;
97 int cx = za->x + za->w / 2;
98 int cy = za->y + za->h / 2;
101 int d2 = (dx*dx) + (dy*dy);
106 double r = sqrt ((double) d2);
107 double th = atan ((double)dy / (double) (dx == 0 ? 1 : dx));
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));
115 ox = (x * c + y * s) >> 13;
116 oy = (-x * s + y * c) >> 13;
124 while (ox >= st->width)
126 while (oy >= st->height)
129 XPutPixel (st->buffer_map, x, y, XGetPixel (st->orig_map, ox, oy));
134 za->a1 += za->inc1; /* Rotation angle */
137 za->a2 += za->inc2; /* Zoom */
140 za->ox = ox; /* Save state for next iteration */
143 if (st->circle && za->n <= 1)
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.
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++)
156 int d2 = (dx*dx) + (dy*dy);
158 XPutPixel (st->orig_map, x, y, XGetPixel (st->buffer_map, x, y));
167 reset_zoom (struct state *st, struct zoom_area *za)
170 int speed = random () % 100 + 100;
171 switch (random () % 4) {
179 za->n = (st->height - 10) * 256 / speed;
184 za->x = st->width - 10;
188 za->n = (st->width - 10) * 256 / speed;
194 za->y = st->height - 10;
197 za->n = (st->height - 10) * 256 / speed;
206 za->n = (st->width - 10) * 256 / speed;
209 za->ww = st->width - za->w;
210 za->hh = st->height - za->h;
212 /* We want smaller angle increments in sweep mode (looks better) */
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) {
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;
227 za->ww = st->width - za->w;
228 za->hh = st->height - za->h;
230 za->x = (za->ww ? random() % za->ww : 0);
231 za->y = (za->hh ? random() % za->hh : 0);
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);
242 za->n = 50 + random() % 100;
245 za->count = random() % (za->n / 2);
250 za->w = 50 + random() % 300;
251 za->h = 50 + random() % 300;
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;
258 za->ww = st->width - za->w;
259 za->hh = st->height - za->h;
261 za->x = (za->ww ? random() % za->ww : 0);
262 za->y = (za->hh ? random() % za->hh : 0);
264 za->dx = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
265 za->dy = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
268 za->n = 50 + random() % 1000;
272 za->n = 5 + random() % 10;
277 za->inc1 = ((2 * (random() & 1)) - 1) * (random () % 30);
278 za->inc2 = ((2 * (random() & 1)) - 1) * (random () % 30);
281 za->xx = za->x * 256;
282 za->yy = za->y * 256;
288 static struct zoom_area *
289 create_zoom (struct state *st)
291 struct zoom_area *za;
293 za = calloc (1, sizeof (struct zoom_area));
301 update_position (struct zoom_area *za)
311 za->dx = 100 + random() % 100;
316 za->dy = 100 + random() % 100;
319 if (za->x > za->ww) {
321 za->dx = -(100 + random() % 100);
324 if (za->y > za->hh) {
326 za->dy = -(100 + random() % 100);
332 DisplayImage (struct state *st, int x, int y, int w, int h)
334 #ifdef HAVE_XSHM_EXTENSION
336 XShmPutImage (st->dpy, st->window, st->gc, st->buffer_map, x, y, x, y,
339 #endif /* HAVE_XSHM_EXTENSION */
340 XPutImage(st->dpy, st->window, st->gc, st->buffer_map, x, y, x, y, w, h);
345 set_mode(struct state *st)
347 char *s = get_string_resource (st->dpy, "mode", "Mode");
348 if (!s || !*s || !strcasecmp (s, "random"))
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;
363 if (!strcasecmp (s, "stationary"))
365 else if (!strcasecmp (s, "move"))
367 else if (!strcasecmp (s, "sweep"))
369 else if (!strcasecmp (s, "circle"))
372 fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
377 init_hack (struct state *st)
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);
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);
393 DisplayImage(st, 0, 0, st->width, st->height);
398 rotzoomer_draw (Display *disp, Window win, void *closure)
400 struct state *st = (struct state *) closure;
401 int delay = st->delay;
404 if (st->img_loader) /* still loading */
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,
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,
429 st->start_time = time ((time_t *) 0);
433 for (i = 0; i < st->num_zoom; i++) {
434 if (st->move || st->sweep)
435 update_position (st->zoom_box[i]);
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]);
443 st->zoom_box[i]->n--;
445 reset_zoom (st, st->zoom_box[i]);
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);
459 setup_X (struct state *st)
461 XWindowAttributes xgwa;
466 XGetWindowAttributes (st->dpy, st->window, &xgwa);
468 st->colormap = xgwa.colormap;
469 st->width = xgwa.width;
470 st->height = xgwa.height;
471 st->visual = xgwa.visual;
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,
492 #ifdef HAVE_XSHM_EXTENSION
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) {
498 fprintf(stderr, "create_xshm_image failed\n");
501 #endif /* HAVE_XSHM_EXTENSION */
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);
513 rotzoomer_init (Display *dpy, Window window)
515 struct state *st = (struct state *) calloc (1, sizeof(*st));
518 #ifdef HAVE_XSHM_EXTENSION
519 st->use_shm = get_boolean_resource (st->dpy, "useSHM", "Boolean");
521 st->num_zoom = get_integer_resource (st->dpy, "numboxes", "Integer");
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;
531 /* In sweep or static mode, we want only one box */
532 if (st->sweep || !st->anim)
535 /* Can't have static sweep mode */
544 st->start_time = time ((time_t *) 0);
552 rotzoomer_reshape (Display *dpy, Window window, void *closure,
553 unsigned int w, unsigned int h)
558 rotzoomer_event (Display *dpy, Window window, void *closure, XEvent *event)
560 struct state *st = (struct state *) closure;
561 if (screenhack_event_helper (dpy, window, event))
570 rotzoomer_free (Display *dpy, Window window, void *closure)
572 struct state *st = (struct state *) closure;
573 if (st->pm) XFreePixmap (dpy, st->pm);
578 static const char *rotzoomer_defaults[] = {
579 ".background: black",
580 ".foreground: white",
582 #ifdef HAVE_XSHM_EXTENSION
593 "*ignoreRotation: True",
594 "*rotateImages: True",
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 },
616 XSCREENSAVER_MODULE ("RotZoomer", rotzoomer)