From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / rotzoomer.c
index acdb181ecd25e88a43a1ea0da73a298865980f46..58f805cc72f6c24da3dc50e3e86ecc6099ac724c 100644 (file)
@@ -1,5 +1,5 @@
 /* rotzoomer - creates a collage of rotated and scaled portions of the screen
- * Copyright (C) 2001 Claudio Matsuoka <claudio@helllabs.org>
+ * Copyright (C) 2001-2016 Claudio Matsuoka <claudio@helllabs.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -10,6 +10,8 @@
  * implied warranty.
  */
 
+/* Circle-mode by jwz, 2014, 2016. */
+
 /*
  * Options:
  *
@@ -18,6 +20,7 @@
  * -n <num>    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 <math.h>
 #include "screenhack.h"
-#include <X11/Xutil.h>
-
-#ifdef HAVE_XSHM_EXTENSION
 #include "xshm.h"
-static Bool use_shm;
-static XShmSegmentInfo shm_info;
-#endif
 
 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 */
+  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 */
 };
 
-static Window window;
-static Display *display;
-static GC gc;
-static Visual *visual;
-static XImage *orig_map, *buffer_map;
-static Colormap colormap;
-
-static int width, height;
-static struct zoom_area **zoom_box;
-static int num_zoom = 2;
-static int move = 0;
-static int sweep = 0;
-static int delay = 10;
-static int anim = 1;
+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 zoom_area *za)
+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;
-
-       z = 8100 * sin (M_PI * za->a2 / 8192);
-       zoom = 8192 + z;
-
-       c = zoom * cos (M_PI * za->a1 / 8192);
-       s = zoom * sin (M_PI * za->a1 / 8192);
-       for (y = za->y; y <= y2; y++) {
-               for (x = za->x; x <= x2; x++) {
-                       ox = (x * c + y * s) >> 13;
-                       oy = (-x * s + y * c) >> 13;
-
-                       while (ox < 0)
-                               ox += width;
-                       while (oy < 0)
-                               oy += height;
-                       while (ox >= width)
-                               ox -= width;
-                       while (oy >= height)
-                               oy -= height;
-
-                       XPutPixel (buffer_map, x, y, XGetPixel (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;
-
-       za->count++;
+  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 zoom_area *za)
+static void
+reset_zoom (struct state *st, struct zoom_area *za)
 {
-       if (sweep) {
-               int speed = random () % 100 + 100;
-               switch (random () % 4) {
-               case 0:
-                       za->w = width;
-                       za->h = 10;
-                       za->x = 0;
-                       za->y = 0;
-                       za->dx = 0;
-                       za->dy = speed;
-                       za->n = (height - 10) * 256 / speed;
-                       break;
-               case 1:
-                       za->w = 10;
-                       za->h = height;
-                       za->x = width - 10;
-                       za->y = 0;
-                       za->dx = -speed;
-                       za->dy = 0;
-                       za->n = (width - 10) * 256 / speed;
-                       break;
-               case 2:
-                       za->w = width;
-                       za->h = 10;
-                       za->x = 0;
-                       za->y = height - 10;
-                       za->dx = 0;
-                       za->dy = -speed;
-                       za->n = (height - 10) * 256 / speed;
-                       break;
-               case 3:
-                       za->w = 10;
-                       za->h = height;
-                       za->x = 0;
-                       za->y = 0;
-                       za->dx = speed;
-                       za->dy = 0;
-                       za->n = (width - 10) * 256 / speed;
-                       break;
-               }
-               za->ww = width - za->w;
-               za->hh = 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 {
-               za->w = 50 + random() % 300;
-               za->h = 50 + random() % 300;
-
-               if (za->w > width / 3)
-                       za->w = width / 3;
-               if (za->h > height / 3)
-                       za->h = height / 3;
-
-               za->ww = width - za->w;
-               za->hh = height - za->h;
-
-               za->x = (random() % za->ww);
-               za->y = (random() % za->hh);
-
-               za->dx = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
-               za->dy = ((2 * (random() & 1)) - 1) * (100 + random() % 300);
-
-               if (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;
+  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 (void)
+static struct zoom_area *
+create_zoom (struct state *st)
 {
-       struct zoom_area *za;
+  struct zoom_area *za;
 
-       za = malloc (sizeof (struct zoom_area));
-       reset_zoom (za);
+  za = calloc (1, sizeof (struct zoom_area));
+  reset_zoom (st, za);
 
-       return za;
+  return za;
 }
 
 
-static void update_position (struct zoom_area *za)
+static void
+update_position (struct zoom_area *za)
 {
-       za->xx += za->dx;
-       za->yy += za->dy;
+  za->xx += za->dx;
+  za->yy += za->dy;
 
-       za->x = za->xx >> 8;
-       za->y = za->yy >> 8;
+  za->x = za->xx >> 8;
+  za->y = za->yy >> 8;
 
-       if (za->x < 0) {
-               za->x = 0;
-               za->dx = 100 + random() % 100;
-       }
+  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->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);
-       }
+  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 (int x, int y, int w, int h)
+static void
+DisplayImage (struct state *st, int x, int y, int w, int h)
 {
-#ifdef HAVE_XSHM_EXTENSION
-       if (use_shm)
-               XShmPutImage (display, window, gc, buffer_map, x, y, x, y,
-                       w, h, False);
-       else
-#endif /* HAVE_XSHM_EXTENSION */
-               XPutImage(display, window, gc, buffer_map, x, y, x, y, w, h);
+  put_xshm_image (st->dpy, st->window, st->gc, st->buffer_map, x, y, x, y,
+                  w, h, &st->shm_info);
 }
 
 
-static void hack_main (void)
+static void
+set_mode(struct state *st)
 {
-       int i;
-
-       for (i = 0; i < num_zoom; i++) {
-               if (move || sweep)
-                       update_position (zoom_box[i]);
-
-               if (zoom_box[i]->n > 0) {
-                       if (anim || zoom_box[i]->count == 0) {
-                               rotzoom (zoom_box[i]);
-                       } else {
-                               sleep (1);
-                       }
-                       zoom_box[i]->n--;
-               } else {
-                       reset_zoom (zoom_box[i]);
-               }
-       }
-
-       for (i = 0; i < num_zoom; i++) {
-               DisplayImage(zoom_box[i]->x, zoom_box[i]->y,
-                       zoom_box[i]->w, zoom_box[i]->h);
-       }
-
-       XSync(display,False);
-       screenhack_handle_events(display);
+  char *s = get_string_resource (st->dpy, "mode", "Mode");
+  if (!s || !*s || !strcasecmp (s, "random"))
+    {
+      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 (void)
+static void
+init_hack (struct state *st)
 {
-       int i;
+  int i;
+
+  set_mode (st);
 
-       zoom_box = calloc (num_zoom, sizeof (struct zoom_area *));
-       for (i = 0; i < num_zoom; i++) {
-               zoom_box[i] = create_zoom ();
-       }
+  st->start_time = time ((time_t *) 0);
+  st->zoom_box = calloc (st->num_zoom, sizeof (struct zoom_area *));
+  for (i = 0; i < st->num_zoom; i++) {
+    st->zoom_box[i] = create_zoom (st);
+  }
 
-       memcpy (buffer_map->data, orig_map->data,
-               height * buffer_map->bytes_per_line);
+  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(0, 0, width, height);
-       XSync(display,False);
+  DisplayImage(st, 0, 0, st->width, st->height);
 }
 
 
-static void setup_X (Display * disp, Window win)
+static unsigned long
+rotzoomer_draw (Display *disp, Window win, void *closure)
 {
-       XWindowAttributes xwa;
-       int depth;
-       XGCValues gcv;
-       long gcflags;
-
-       XGetWindowAttributes (disp, win, &xwa);
-       window = win;
-       display = disp;
-       depth = xwa.depth;
-       colormap = xwa.colormap;
-       width = xwa.width;
-       height = xwa.height;
-       visual = xwa.visual;
-
-       if (width % 2)
-               width--;
-       if (height % 2)
-               height--;
-
-       gcv.function = GXcopy;
-       gcv.subwindow_mode = IncludeInferiors;
-       gcflags = GCForeground | GCFunction;
-       if (use_subwindow_mode_p (xwa.screen, window))  /* see grabscreen.c */
-               gcflags |= GCSubwindowMode;
-       gc = XCreateGC (display, window, gcflags, &gcv);
-       grab_screen_image (xwa.screen, window);
-
-       orig_map = XGetImage (display, window, 0, 0, width, height, ~0L, ZPixmap);
-
-       if (!gc) {
-               fprintf(stderr, "XCreateGC failed\n");
-               exit(1);
-       }
-
-       buffer_map = 0;
+  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();
+       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;
+}
 
-#ifdef HAVE_XSHM_EXTENSION
-       if (use_shm) {
-               buffer_map = create_xshm_image(display, xwa.visual, depth,
-                       ZPixmap, 0, &shm_info, width, height);
-               if (!buffer_map) {
-                       use_shm = False;
-                       fprintf(stderr, "create_xshm_image failed\n");
-               }
-       }
-#endif /* HAVE_XSHM_EXTENSION */
-
-       if (!buffer_map) {
-               buffer_map = XCreateImage(display, xwa.visual,
-                       depth, ZPixmap, 0, 0, width, height, 8, 0);
-               buffer_map->data = (char *)calloc (buffer_map->height,
-                       buffer_map->bytes_per_line);
-       }
+
+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);
 }
 
 
-char *progclass = "Rotzoomer";
+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");
 
-char *defaults[] = {
-#ifdef HAVE_XSHM_EXTENSION
-       "*useSHM: True",
-#endif
-       "*move: False",
-       "*sweep: False",
-       "*anim: True",
-       "*numboxes: 2",
-       "*delay: 10",
-       0
-};
+  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;
 
-XrmOptionDescRec options[] = {
-#ifdef HAVE_XSHM_EXTENSION
-       { "-shm",       ".useSHM",      XrmoptionNoArg, "True"  },
-       { "-no-shm",    ".useSHM",      XrmoptionNoArg, "False" },
-#endif
-       { "-move",      ".move",        XrmoptionNoArg, "True"  },
-       { "-sweep",     ".sweep",       XrmoptionNoArg, "True"  },
-       { "-anim",      ".anim",        XrmoptionNoArg, "True"  },
-       { "-no-anim",   ".anim",        XrmoptionNoArg, "False" },
-       { "-delay",     ".delay",       XrmoptionSepArg, 0      },
-       { "-n",         ".numboxes",    XrmoptionSepArg, 0      },
-       { 0, 0, 0, 0 }
-};
+  /* 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;
 
-void screenhack(Display *disp, Window win)
+  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);
+  free (st);
+}
+
+
+static const char *rotzoomer_defaults[] = {
+  ".background: black",
+  ".foreground: white",
+  "*fpsSolid:  true",
 #ifdef HAVE_XSHM_EXTENSION
-       use_shm = get_boolean_resource ("useSHM", "Boolean");
+  "*useSHM: True",
+#else
+  "*useSHM: False",
 #endif
-       num_zoom = get_integer_resource ("numboxes", "Integer");
-       move = get_boolean_resource ("move", "Boolean");
-       delay = get_integer_resource ("delay", "Integer");
-       sweep = get_boolean_resource ("sweep", "Boolean");
-       anim = get_boolean_resource ("anim", "Boolean");
-
-       /* In sweep or static mode, we want only one box */
-       if (sweep || !anim)
-               num_zoom = 1;
+  "*anim: True",
+  "*mode: random",
+  "*numboxes: 2",
+  "*delay: 10000",
+  "*duration: 120",
+#ifdef HAVE_MOBILE
+  "*ignoreRotation: True",
+  "*rotateImages:   True",
+#endif
+  0
+};
 
-       /* Can't have static sweep mode */
-       if (!anim)
-               sweep = 0;
 
-       setup_X (disp, win);
+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 }
+};
 
-       init_hack ();
 
-       /* Main drawing loop */
-       while (42) {
-               hack_main ();
-               usleep (delay * 1000);
-       }
-}
+XSCREENSAVER_MODULE ("RotZoomer", rotzoomer)