/* rotzoomer - creates a collage of rotated and scaled portions of the screen * Copyright (C) 2001-2016 Claudio Matsuoka * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* Circle-mode by jwz, 2014, 2016. */ /* * Options: * * -shm enable MIT shared memory extension * -no-shm disable MIT shared memory extension * -n number of zoomboxes * -move enable mobile zoomboxes * -sweep enable sweep mode * -circle enable circle mode * -anim enable snapshot mode * -no-anim enable snapshot mode * -delay delay in milliseconds */ #include #include "screenhack.h" #include "xshm.h" struct zoom_area { int w, h; /* rectangle width and height */ int inc1, inc2; /* rotation and zoom angle increments */ int dx, dy; /* translation increments */ int a1, a2; /* rotation and zoom angular variables */ int ox, oy; /* origin in the background copy */ int xx, yy; /* left-upper corner position (* 256) */ int x, y; /* left-upper corner position */ int ww, hh; /* valid area to place left-upper corner */ int n; /* number of iteractions */ int count; /* current iteraction */ }; struct state { Display *dpy; Window window; GC gc; Visual *visual; XImage *orig_map, *buffer_map; Colormap colormap; int width, height; struct zoom_area **zoom_box; int num_zoom; int move; int sweep; int circle; int delay; int anim; int duration; time_t start_time; async_load_state *img_loader; Pixmap pm; XShmSegmentInfo shm_info; }; static void rotzoom (struct state *st, struct zoom_area *za) { int x, y, c, s, zoom, z; int x2 = za->x + za->w - 1, y2 = za->y + za->h - 1; int ox = 0, oy = 0; int w2 = (za->w/2) * (za->w/2); z = 8100 * sin (M_PI * za->a2 / 8192); zoom = 8192 + z; for (y = za->y; y <= y2; y++) { for (x = za->x; x <= x2; x++) { Bool copyp = True; double a = M_PI * za->a1 / 8192; c = zoom * cos (a); s = zoom * sin (a); if (st->circle) { int cx = za->x + za->w / 2; int cy = za->y + za->h / 2; int dx = x - cx; int dy = y - cy; int d2 = (dx*dx) + (dy*dy); if (d2 > w2) { copyp = False; } else { double r = sqrt ((double) d2); double th = atan ((double)dy / (double) (dx == 0 ? 1 : dx)); copyp = 1; if (dx < 0) th += M_PI; th += M_PI * (za->a1 / 600.0); ox = cx + (int) (r * cos(th)); oy = cy + (int) (r * sin(th)); } } else { ox = (x * c + y * s) >> 13; oy = (-x * s + y * c) >> 13; } if (copyp) { while (ox < 0) ox += st->width; while (oy < 0) oy += st->height; while (ox >= st->width) ox -= st->width; while (oy >= st->height) oy -= st->height; XPutPixel (st->buffer_map, x, y, XGetPixel (st->orig_map, ox, oy)); } } } za->a1 += za->inc1; /* Rotation angle */ za->a1 &= 0x3fff; za->a2 += za->inc2; /* Zoom */ za->a2 &= 0x3fff; za->ox = ox; /* Save state for next iteration */ za->oy = oy; if (st->circle && za->n <= 1) { /* Done rotating the circle: copy the bits from the working set back into the origin, so that subsequent rotations pick up these changes. */ int cx = za->x + za->w / 2; int cy = za->y + za->h / 2; int w2 = (za->w/2) * (za->w/2); for (y = za->y; y < za->y + za->h; y++) for (x = za->x; x < za->x + za->w; x++) { int dx = x - cx; int dy = y - cy; int d2 = (dx*dx) + (dy*dy); if (d2 <= w2) XPutPixel (st->orig_map, x, y, XGetPixel (st->buffer_map, x, y)); } } za->count++; } static void reset_zoom (struct state *st, struct zoom_area *za) { if (st->sweep) { int speed = random () % 100 + 100; switch (random () % 4) { case 0: za->w = st->width; za->h = 10; za->x = 0; za->y = 0; za->dx = 0; za->dy = speed; za->n = (st->height - 10) * 256 / speed; break; case 1: za->w = 10; za->h = st->height; za->x = st->width - 10; za->y = 0; za->dx = -speed; za->dy = 0; za->n = (st->width - 10) * 256 / speed; break; case 2: za->w = st->width; za->h = 10; za->x = 0; za->y = st->height - 10; za->dx = 0; za->dy = -speed; za->n = (st->height - 10) * 256 / speed; break; case 3: za->w = 10; za->h = st->height; za->x = 0; za->y = 0; za->dx = speed; za->dy = 0; za->n = (st->width - 10) * 256 / speed; break; } za->ww = st->width - za->w; za->hh = st->height - za->h; /* We want smaller angle increments in sweep mode (looks better) */ za->a1 = 0; za->a2 = 0; za->inc1 = ((2 * (random() & 1)) - 1) * (1 + random () % 7); za->inc2 = ((2 * (random() & 1)) - 1) * (1 + random () % 7); } else if (st->circle) { za->w = 50 + random() % 300; if (za->w > st->width / 3) za->w = st->width / 3; if (za->w > st->height / 3) za->w = st->height / 3; za->h = za->w; za->ww = st->width - za->w; za->hh = st->height - za->h; za->x = (za->ww ? random() % za->ww : 0); za->y = (za->hh ? random() % za->hh : 0); za->dx = 0; za->dy = 0; za->a1 = 0; za->a2 = 0; za->count = 0; /* #### If we go clockwise, it doesn't start rotating from 0. So only go counter-clockwise for now. Sigh. */ za->inc1 = (random () % 30); za->inc2 = 0; za->n = 50 + random() % 100; if (!st->anim) { za->count = random() % (za->n / 2); za->a1 = random(); } } else { za->w = 50 + random() % 300; za->h = 50 + random() % 300; if (za->w > st->width / 3) za->w = st->width / 3; if (za->h > st->height / 3) za->h = st->height / 3; za->ww = st->width - za->w; za->hh = st->height - za->h; za->x = (za->ww ? random() % za->ww : 0); za->y = (za->hh ? random() % za->hh : 0); za->dx = ((2 * (random() & 1)) - 1) * (100 + random() % 300); za->dy = ((2 * (random() & 1)) - 1) * (100 + random() % 300); if (st->anim) { za->n = 50 + random() % 1000; za->a1 = 0; za->a2 = 0; } else { za->n = 5 + random() % 10; za->a1 = random (); za->a2 = random (); } za->inc1 = ((2 * (random() & 1)) - 1) * (random () % 30); za->inc2 = ((2 * (random() & 1)) - 1) * (random () % 30); } za->xx = za->x * 256; za->yy = za->y * 256; za->count = 0; } static struct zoom_area * create_zoom (struct state *st) { struct zoom_area *za; za = calloc (1, sizeof (struct zoom_area)); reset_zoom (st, za); return za; } static void update_position (struct zoom_area *za) { za->xx += za->dx; za->yy += za->dy; za->x = za->xx >> 8; za->y = za->yy >> 8; if (za->x < 0) { za->x = 0; za->dx = 100 + random() % 100; } if (za->y < 0) { za->y = 0; za->dy = 100 + random() % 100; } if (za->x > za->ww) { za->x = za->ww; za->dx = -(100 + random() % 100); } if (za->y > za->hh) { za->y = za->hh; za->dy = -(100 + random() % 100); } } static void DisplayImage (struct state *st, int x, int y, int w, int h) { put_xshm_image (st->dpy, st->window, st->gc, st->buffer_map, x, y, x, y, w, h, &st->shm_info); } static void set_mode(struct state *st) { char *s = get_string_resource (st->dpy, "mode", "Mode"); if (!s || !*s || !strcasecmp (s, "random")) { free (s); switch (random() % 4) { case 0: s = "stationary"; break; case 1: s = "move"; break; case 2: s = "sweep"; break; case 3: s = "circle"; break; default: abort(); } } st->move = False; st->sweep = False; st->circle = False; if (!strcasecmp (s, "stationary")) ; else if (!strcasecmp (s, "move")) st->move = True; else if (!strcasecmp (s, "sweep")) st->sweep = True; else if (!strcasecmp (s, "circle")) st->circle = True; else fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s); } static void init_hack (struct state *st) { int i; set_mode (st); st->start_time = time ((time_t *) 0); if (st->zoom_box) { for (i = 0; i < st->num_zoom; i++) if (st->zoom_box[i]) free (st->zoom_box[i]); free (st->zoom_box); } st->zoom_box = calloc (st->num_zoom, sizeof (struct zoom_area *)); for (i = 0; i < st->num_zoom; i++) { if (st->zoom_box[i]) free (st->zoom_box[i]); st->zoom_box[i] = create_zoom (st); } if (st->height && st->orig_map->data) memcpy (st->buffer_map->data, st->orig_map->data, st->height * st->buffer_map->bytes_per_line); DisplayImage(st, 0, 0, st->width, st->height); } static unsigned long rotzoomer_draw (Display *disp, Window win, void *closure) { struct state *st = (struct state *) closure; int delay = st->delay; int i; if (st->img_loader) /* still loading */ { st->img_loader = load_image_async_simple (st->img_loader, 0, 0, 0, 0, 0); if (! st->img_loader) { /* just finished */ if (! st->pm) abort(); if (st->orig_map) XDestroyImage (st->orig_map); st->orig_map = XGetImage (st->dpy, st->pm, 0, 0, st->width, st->height, ~0L, ZPixmap); init_hack (st); } return st->delay; } if (!st->img_loader && st->start_time + st->duration < time ((time_t *) 0)) { XWindowAttributes xgwa; XGetWindowAttributes(st->dpy, st->window, &xgwa); /* On MacOS X11, XGetImage on a Window often gets an inexplicable BadMatch, possibly due to the window manager having occluded something? It seems nondeterministic. Loading the image into a pixmap instead fixes it. */ if (st->pm) XFreePixmap (st->dpy, st->pm); st->pm = XCreatePixmap (st->dpy, st->window, xgwa.width, xgwa.height, xgwa.depth); st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, st->pm, 0, 0); st->start_time = time ((time_t *) 0); return st->delay; } for (i = 0; i < st->num_zoom; i++) { if (st->move || st->sweep) update_position (st->zoom_box[i]); if (st->zoom_box[i]->n > 0) { if (st->anim || st->zoom_box[i]->count == 0) { rotzoom (st, st->zoom_box[i]); } else { delay = 1000000; } st->zoom_box[i]->n--; } else { reset_zoom (st, st->zoom_box[i]); } } for (i = 0; i < st->num_zoom; i++) { DisplayImage(st, st->zoom_box[i]->x, st->zoom_box[i]->y, st->zoom_box[i]->w, st->zoom_box[i]->h); } return delay; } static void setup_X (struct state *st) { XWindowAttributes xgwa; int depth; XGCValues gcv; long gcflags; XGetWindowAttributes (st->dpy, st->window, &xgwa); depth = xgwa.depth; st->colormap = xgwa.colormap; st->width = xgwa.width; st->height = xgwa.height; st->visual = xgwa.visual; if (st->width % 2) st->width--; if (st->height % 2) st->height--; gcv.function = GXcopy; gcv.subwindow_mode = IncludeInferiors; gcflags = GCFunction; if (use_subwindow_mode_p (xgwa.screen, st->window)) /* see grabscreen.c */ gcflags |= GCSubwindowMode; st->gc = XCreateGC (st->dpy, st->window, gcflags, &gcv); if (st->pm) XFreePixmap (st->dpy, st->pm); st->pm = XCreatePixmap (st->dpy, st->window, xgwa.width, xgwa.height, xgwa.depth); st->img_loader = load_image_async_simple (0, xgwa.screen, st->window, st->pm, 0, 0); st->buffer_map = create_xshm_image(st->dpy, xgwa.visual, depth, ZPixmap, &st->shm_info, st->width, st->height); } static void * rotzoomer_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); st->dpy = dpy; st->window = window; st->num_zoom = get_integer_resource (st->dpy, "numboxes", "Integer"); set_mode(st); st->anim = get_boolean_resource (st->dpy, "anim", "Boolean"); st->delay = get_integer_resource (st->dpy, "delay", "Integer"); st->duration = get_integer_resource (st->dpy, "duration", "Seconds"); if (st->delay < 0) st->delay = 0; if (st->duration < 1) st->duration = 1; /* In sweep or static mode, we want only one box */ if (st->sweep || !st->anim) st->num_zoom = 1; /* Can't have static sweep mode */ if (!st->anim) st->sweep = 0; if (st->circle) { st->move = 0; st->sweep = 0; } st->start_time = time ((time_t *) 0); setup_X (st); return st; } static void rotzoomer_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { } static Bool rotzoomer_event (Display *dpy, Window window, void *closure, XEvent *event) { struct state *st = (struct state *) closure; if (screenhack_event_helper (dpy, window, event)) { st->start_time = 0; return True; } return False; } static void rotzoomer_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; if (st->pm) XFreePixmap (dpy, st->pm); if (st->gc) XFreeGC (dpy, st->gc); if (st->orig_map) XDestroyImage (st->orig_map); if (st->buffer_map) destroy_xshm_image (dpy, st->buffer_map, &st->shm_info); if (st->zoom_box) { int i; for (i = 0; i < st->num_zoom; i++) if (st->zoom_box[i]) free (st->zoom_box[i]); free (st->zoom_box); } free (st); } static const char *rotzoomer_defaults[] = { ".background: black", ".foreground: white", "*fpsSolid: true", #ifdef HAVE_XSHM_EXTENSION "*useSHM: True", #else "*useSHM: False", #endif "*anim: True", "*mode: random", "*numboxes: 2", "*delay: 10000", "*duration: 120", #ifdef HAVE_MOBILE "*ignoreRotation: True", "*rotateImages: True", #endif 0 }; static XrmOptionDescRec rotzoomer_options[] = { { "-shm", ".useSHM", XrmoptionNoArg, "True" }, { "-no-shm", ".useSHM", XrmoptionNoArg, "False" }, { "-mode", ".mode", XrmoptionSepArg, 0 }, { "-move", ".mode", XrmoptionNoArg, "move" }, { "-sweep", ".mode", XrmoptionNoArg, "sweep" }, { "-circle", ".mode", XrmoptionNoArg, "circle"}, { "-anim", ".anim", XrmoptionNoArg, "True" }, { "-no-anim", ".anim", XrmoptionNoArg, "False" }, { "-delay", ".delay", XrmoptionSepArg, 0 }, {"-duration", ".duration", XrmoptionSepArg, 0 }, { "-n", ".numboxes", XrmoptionSepArg, 0 }, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("RotZoomer", rotzoomer)