From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / epicycle.c
index b812e6ae38968adb6e036c9b8aed8d5e8e43c294..288999fa95bbf7220b50769c69d3d950a3600db3 100644 (file)
 #define FULLCIRCLE (2.0 * M_PI)        /* radians in a circle. */
 
 
-/* Name of the Screensaver hack */
-char *progclass="Epicycle";
-
 /* Some of these resource values here are hand-tuned to give a
  * pleasing variety of interesting shapes.  These are not the only
  * good settings, but you may find you need to change some as a group
  * to get pleasing figures.
  */
-char *defaults [] = {
+static const char *epicycle_defaults [] = {
   ".background:        black",
   ".foreground:        white",
+  "*fpsSolid:  true",
   "*colors:    100",
   "*color0:    red",
-  "*delay:     1000",
+  "*delay:     20000",
   "*holdtime:  2",
   "*lineWidth: 4",
   "*minCircles:  2",
@@ -67,11 +65,14 @@ char *defaults [] = {
   "*divisorPoisson: 0.4",
   "*sizeFactorMin: 1.05",
   "*sizeFactorMax: 2.05",
+#ifdef USE_IPHONE
+  "*ignoreRotation: True",
+#endif
   0
 };
 
 /* options passed to this program */
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec epicycle_options [] = {
   { "-color0",         ".color0",        XrmoptionSepArg, 0 },
   { "-colors",         ".colors",        XrmoptionSepArg, 0 },
   { "-colours",                ".colors",        XrmoptionSepArg, 0 },
@@ -92,28 +93,6 @@ XrmOptionDescRec options [] = {
 };
 
 
-static Display *dpy;
-static Window window;
-static GC color0;
-static int width, height;
-static int x_offset, y_offset;
-static int unit_pixels;
-static unsigned long bg;
-static Colormap cmap;
-static int restart = 0;
-static int stop = 0;
-static double wdot_max;
-static XColor *colors = NULL;
-static int ncolors = 2;
-static int color_shift_pos=0;  /* how far we are towards that. */
-static double colour_cycle_rate = 1.0;
-static int harmonics = 8;
-static double divisorPoisson = 0.4;
-static double sizeFactorMin = 1.05;
-static double sizeFactorMax = 2.05;
-static int minCircles;
-static int maxCircles;
-
 /* Each circle is centred on a point on the rim of another circle.
  */
 struct tagCircle
@@ -141,6 +120,43 @@ struct tagBody                     /* a body that moves on a system of circles. */
 typedef struct tagBody Body;
 
 
+struct state {
+   Display *dpy;
+   Window window;
+   GC color0;
+   int width, height;
+   int x_offset, y_offset;
+   int unit_pixels;
+   unsigned long bg;
+   Colormap cmap;
+   int restart;
+   double wdot_max;
+   XColor *colors;
+   int ncolors;
+   int color_shift_pos;        /* how far we are towards that. */
+   double colour_cycle_rate;
+   int harmonics;
+   double divisorPoisson;
+   double sizeFactorMin;
+   double sizeFactorMax;
+   int minCircles;
+   int maxCircles;
+
+   Bool done;
+
+   long L;
+   double T, timestep, circle, timestep_coarse;
+   int delay;
+   int uncleared;
+   int holdtime;
+   int xmax, xmin, ymax, ymin;
+   Body *pb0;
+   double xtime;
+   eraser_state *eraser;
+};
+
+
+
 /* Determine the GCD of two numbers using Euclid's method.  The other
  * possible algorighm is Stein's method, but it's probably only going
  * to be much faster on machines with no divide instruction, like the
@@ -186,11 +202,11 @@ lcm(int u, int v)
 }
 
 static long 
-random_radius(double scale)    
+random_radius(struct state *st, double scale)  
 {
   long r;
 
-  r = frand(scale) * unit_pixels/2; /* for frand() see utils/yarandom.h */
+  r = frand(scale) * st->unit_pixels/2; /* for frand() see utils/yarandom.h */
   if (r < MIN_RADIUS)
     r = MIN_RADIUS;
   return r;
@@ -198,12 +214,12 @@ random_radius(double scale)
 
 
 static long
-random_divisor(void)
+random_divisor(struct state *st)
 {
   int divisor = 1;
   int sign;
 
-  while (frand(1.0) < divisorPoisson && divisor <= harmonics)
+  while (frand(1.0) < st->divisorPoisson && divisor <= st->harmonics)
     {
       ++divisor;
     }
@@ -212,25 +228,17 @@ random_divisor(void)
 }
 
 
-static void
-oom(void)
-{
-  fprintf(stderr, "Failed to allocate memory!\n");
-  exit(-1);
-}
-
-
 /* Construct a circle or die.
  */
-Circle *
-new_circle(double scale)
+static Circle *
+new_circle(struct state *st, double scale)
 {
   Circle *p = malloc(sizeof(Circle));
   
-  p->radius = random_radius(scale);
+  p->radius = random_radius(st, scale);
   p->w = p->initial_w = 0.0;
-  p->divisor = random_divisor();
-  p->wdot = wdot_max / p->divisor;
+  p->divisor = random_divisor(st);
+  p->wdot = st->wdot_max / p->divisor;
   p->pchild = NULL;
   
   return p;
@@ -252,8 +260,8 @@ delete_circle_chain(Circle *p)
     }
 }
 
-Circle *
-new_circle_chain(void)
+static Circle *
+new_circle_chain(struct state *st)
 {
   Circle *head;
   double scale = 1.0, factor;
@@ -262,16 +270,19 @@ new_circle_chain(void)
   /* Parent circles are larger than their children by a factor of at
    * least FACTOR_MIN and at most FACTOR_MAX.
    */
-  factor = sizeFactorMin + frand(sizeFactorMax - sizeFactorMin);
+  factor = st->sizeFactorMin + frand(st->sizeFactorMax - st->sizeFactorMin);
   
   /* There are between minCircles and maxCircles in each figure.
    */
-  n = minCircles + rand() % (maxCircles - minCircles);
+  if (st->maxCircles == st->minCircles)
+    n = st->minCircles;            /* Avoid division by zero. */
+  else
+    n = st->minCircles + random() % (st->maxCircles - st->minCircles);
   
   head = NULL;
   while (n--)
     {
-      Circle *p = new_circle(scale);
+      Circle *p = new_circle(st, scale);
       p->pchild = head;
       head = p;
 
@@ -292,12 +303,11 @@ assign_random_common_w(Circle *p)
 }
 
 static Body *
-new_body(void)
+new_body(struct state *st)
 {
   Body *p = malloc(sizeof(Body));
-  if (NULL == p)
-    oom();
-  p->epicycles = new_circle_chain();
+  if (!p) abort();
+  p->epicycles = new_circle_chain(st);
   p->current_color = 0;                /* ?? start them all on different colors? */
   p->next = NULL;
   p->x = p->y = 0;
@@ -322,9 +332,9 @@ delete_body(Body *p)
 
 
 static void 
-draw_body(Body *pb, GC gc)
+draw_body(struct state *st, Body *pb, GC gc)
 {
-  XDrawLine(dpy, window, gc, pb->old_x, pb->old_y, pb->x, pb->y);
+  XDrawLine(st->dpy, st->window, gc, pb->old_x, pb->old_y, pb->x, pb->y);
 }
 
 static long
@@ -374,7 +384,7 @@ move_body(Body *pb, double t)
 }
 
 static int
-colour_init(XWindowAttributes *pxgwa)
+colour_init(struct state *st, XWindowAttributes *pxgwa)
 {
   XGCValues gcv;
 
@@ -392,48 +402,48 @@ colour_init(XWindowAttributes *pxgwa)
   
   /* Free any already allocated colors...
    */
-  if (colors)
+  if (st->colors)
     {
-      free_colors(dpy, cmap, colors, ncolors);
-      colors = 0;
-      ncolors = 0;
+      free_colors(pxgwa->screen, st->cmap, st->colors, st->ncolors);
+      st->colors = 0;
+      st->ncolors = 0;
     }
        
-  ncolors = get_integer_resource ("colors", "Colors");
-  if (0 == ncolors)            /* English spelling? */
-    ncolors = get_integer_resource ("colours", "Colors");
+  st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
+  if (0 == st->ncolors)                /* English spelling? */
+    st->ncolors = get_integer_resource (st->dpy, "colours", "Colors");
   
-  if (ncolors < 2)
-    ncolors = 2;
-  if (ncolors <= 2)
+  if (st->ncolors < 2)
+    st->ncolors = 2;
+  if (st->ncolors <= 2)
     mono_p = True;
-  colors = 0;
+  st->colors = 0;
 
   if (!mono_p)
     {
-      colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
-      if (!colors)
-       oom();
+      st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
+      if (!st->colors) abort();
          
-      make_smooth_colormap (dpy, pxgwa->visual, cmap, colors, &ncolors,
+      make_smooth_colormap (pxgwa->screen, pxgwa->visual, st->cmap,
+                            st->colors, &st->ncolors,
                            True, /* allocate */
                            False, /* not writable */
                            True); /* verbose (complain about failure) */
-      if (ncolors <= 2)
+      if (st->ncolors <= 2)
        {
-         if (colors)
-           free (colors);
-         colors = 0;
+         if (st->colors)
+           free (st->colors);
+         st->colors = 0;
          mono_p = True;
        }
     }
 
   
-  bg = get_pixel_resource ("background", "Background", dpy, cmap);
+  st->bg = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
 
   /* Set the line width
    */
-  gcv.line_width = get_integer_resource ("lineWidth", "Integer");
+  gcv.line_width = get_integer_resource (st->dpy, "lineWidth", "Integer");
   if (gcv.line_width)
     {
       valuemask |= GCLineWidth;
@@ -452,163 +462,67 @@ colour_init(XWindowAttributes *pxgwa)
   
   /* Set the foreground.
    */
-  if (mono_p)
-    fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
-  else
-    fg = bg ^ get_pixel_resource (("color0"), "Foreground", dpy, cmap); 
+/*  if (mono_p)*/
+    fg = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
+/* WTF?
+else
+    fg = st->bg ^ get_pixel_resource (st->dpy, st->cmap, ("color0"), "Foreground"); 
+*/
   gcv.foreground = fg;
   valuemask |= GCForeground;
 
   /* Actually create the GC.
    */
-  color0 = XCreateGC (dpy, window, valuemask, &gcv);
+  st->color0 = XCreateGC (st->dpy, st->window, valuemask, &gcv);
   
   return retval;
 }
 
 
-/* check_events(); originally from XScreensaver: hacks/maze.c,
- * but now quite heavily modified.
- *
- * Reaction to events:-
- *
- * Mouse 1 -- new figure }
- *       2 -- new figure }-- ignored when running on root window.
- *       3 -- exit       }
- *
- * Window resized or exposed -- new figure.
- * Window iconised -- wait until it's re-mapped, then start a new figure.
- */
-static int
-check_events (void)                        /* X event handler [ rhess ] */
-{
-  XEvent e;
-  int unmapped = 0;
-       
-  while (unmapped || XPending(dpy))
-    {
-      XNextEvent(dpy, &e);
-               
-      switch (e.type)
-       {
-       case ButtonPress:
-         switch (e.xbutton.button)
-           {
-           case 3:
-             exit (0);
-             break;
-                               
-           case 2:
-           case 1:
-           default:
-             restart = 1 ;
-             stop = 0 ;
-             break;
-           }
-         break;
-                       
-       case ConfigureNotify:
-         restart = 1;
-         break;
-                       
-       case UnmapNotify:
-         printf("unmapped!\n");
-         unmapped = 1;
-         restart = 1;                  /* restart with new fig. when re-mapped. */
-         break;
-                       
-       case Expose:            
-         if (0 == e.xexpose.count)
-           {
-                               /* We can get several expose events in the queue.
-                                * Only the last one has a zero count.  We eat
-                                * events in this function so as to avoid restarting
-                                * the screensaver many times in quick succession.
-                                */
-             restart = 1;
-           }
-         /* If we had been unmapped and are now waiting to be re-mapped,
-          * indicate that we condition we are waiting for is now met.
-          */
-         if (unmapped)
-           printf("re-mapped!\n");
-         unmapped = 0;
-         break;
-
-        default:
-          screenhack_handle_event(dpy, &e);
-          break;
-       }
-               
-      /* If we're unmapped, don't return to the caller.  This
-       * prevents us wasting CPU, calculating new positions for
-       * things that will never be plotted.   This is a real CPU
-       * saver.
-       */
-      if (!unmapped)
-       return 1;
-    }
-  return 0;
-}
 
 
 static void
-setup(void)
+setup(struct state *st)
 {
   XWindowAttributes xgwa;
-  int root;
   
-  XGetWindowAttributes (dpy, window, &xgwa);
-  cmap = xgwa.colormap;
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+  st->cmap = xgwa.colormap;
 
-  width = xgwa.width;
-  height = xgwa.height;
-  x_offset = width / 2;
-  y_offset = height / 2;
-  unit_pixels = width < height ? width : height;
+  st->width = xgwa.width;
+  st->height = xgwa.height;
+  st->x_offset = st->width / 2;
+  st->y_offset = st->height / 2;
+  st->unit_pixels = st->width < st->height ? st->width : st->height;
 
   {
-    static Bool done = False;
-    if (!done)
+    if (!st->done)
       {
-       colour_init(&xgwa);
-       done = True;
+       colour_init(st, &xgwa);
+       st->done = True;
       }
   }
-  
-  root = get_boolean_resource("root", "Boolean");
-  
-  if (root)
-    {
-      XSelectInput(dpy, window, ExposureMask);
-    }
-  else
-    {
-      XWindowAttributes xgwa;
-      XGetWindowAttributes (dpy, window, &xgwa);
-      XSelectInput (dpy, window,
-                    xgwa.your_event_mask | ExposureMask |
-                    ButtonPressMask |StructureNotifyMask);
-    }
-  
 }
+
+
 static void
-color_step(Body *pb, double frac)
+color_step(struct state *st, Body *pb, double frac)
 {
   if (!mono_p)
     {
-      int newshift = ncolors * fmod(frac * colour_cycle_rate, 1.0);
-      if (newshift != color_shift_pos)
+      int newshift = st->ncolors * fmod(frac * st->colour_cycle_rate, 1.0);
+      if (newshift != st->color_shift_pos)
        {
          pb->current_color = newshift;
-         XSetForeground (dpy, color0, colors[pb->current_color].pixel);
-         color_shift_pos = newshift;
+         XSetForeground (st->dpy, st->color0, st->colors[pb->current_color].pixel);
+         st->color_shift_pos = newshift;
        }
     }
 }
 
 
-long
+#if 0
+static long
 distance(long x1, long y1, long x2, long y2)
 {
   long dx, dy;
@@ -618,7 +532,6 @@ distance(long x1, long y1, long x2, long y2)
   return dx*dx + dy*dy;
 }
 
-#if 0
 static int poisson_irand(double p)
 {
   int r = 1;
@@ -630,7 +543,7 @@ static int poisson_irand(double p)
 
 static void
 precalculate_figure(Body *pb,
-                   double xtime, double step,
+                   double this_xtime, double step,
                    int *x_max, int *y_max,
                    int *x_min, int *y_min)
 {
@@ -640,7 +553,7 @@ precalculate_figure(Body *pb,
   *x_min = *x_max = pb->x;
   *y_min = *y_max = pb->y;
   
-  for (t=0.0; t<xtime; t += step)
+  for (t=0.0; t<this_xtime; t += step)
     {
       move_body(pb, t); /* move once to avoid initial line from origin */
       if (pb->x > *x_max)
@@ -659,24 +572,24 @@ static int i_max(int a, int b)
   return (a>b) ? a : b;
 }
 
-static void rescale_circles(Body *pb,
+static void rescale_circles(struct state *st, Body *pb,
                            int x_max, int y_max,
                            int x_min, int y_min)
 {
   double xscale, yscale, scale;
   double xm, ym;
   
-  x_max -= x_offset;
-  x_min -= x_offset;
-  y_max -= y_offset;
-  y_min -= y_offset;
+  x_max -= st->x_offset;
+  x_min -= st->x_offset;
+  y_max -= st->y_offset;
+  y_min -= st->y_offset;
 
   x_max = i_max(x_max, -x_min);
   y_max = i_max(y_max, -y_min);
 
 
-  xm = width / 2.0;
-  ym = height / 2.0;
+  xm = st->width / 2.0;
+  ym = st->height / 2.0;
   if (x_max > xm)
     xscale = xm / x_max;
   else
@@ -712,131 +625,159 @@ static void rescale_circles(Body *pb,
  * value.  That should please the Pythagoreans among you... :-)
  */
 static double 
-random_wdot_max(void)
+random_wdot_max(struct state *st)
 {
   /* Maximum and minimum values for the choice of wdot_max.  Possible
    * epicycle speeds vary from wdot_max to (wdot_max * harmonics).
    */
   double minspeed, maxspeed;
-  minspeed = get_float_resource("minSpeed", "Double");
-  maxspeed = get_float_resource("maxSpeed", "Double");
-  return harmonics * (minspeed + FULLCIRCLE * frand(maxspeed-minspeed));
+  minspeed = get_float_resource(st->dpy, "minSpeed", "Double");
+  maxspeed = get_float_resource(st->dpy, "maxSpeed", "Double");
+  return st->harmonics * (minspeed + FULLCIRCLE * frand(maxspeed-minspeed));
 }
 
-/* this is the function called for your screensaver */
-/*GLOBAL*/ void
-screenhack(Display *disp, Window win)
+
+static void *
+epicycle_init (Display *disp, Window win)
 {
-  Body *pb = NULL;
-  long l;
-  double t, timestep, circle, xtime, timestep_coarse;
-  int delay;
-  int uncleared = 1;
-  int xmax, xmin, ymax, ymin;
-  int holdtime = get_integer_resource ("holdtime", "Integer");
-
-  dpy = disp;
-  window = win;
-
-  circle = FULLCIRCLE;
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
+  st->dpy = disp;
+  st->window = win;
+
+  st->holdtime = get_integer_resource (st->dpy, "holdtime", "Integer");
+
+  st->circle = FULLCIRCLE;
   
-  XClearWindow(dpy, window);
-  uncleared = 0;
+  XClearWindow(st->dpy, st->window);
+  st->uncleared = 0;
+  st->restart = 1;
   
-  delay = get_integer_resource ("delay", "Integer");
-  harmonics = get_integer_resource("harmonics", "Integer");
-  divisorPoisson = get_float_resource("divisorPoisson", "Double");
+  st->delay = get_integer_resource (st->dpy, "delay", "Integer");
+  st->harmonics = get_integer_resource(st->dpy, "harmonics", "Integer");
+  st->divisorPoisson = get_float_resource(st->dpy, "divisorPoisson", "Double");
   
-  timestep = get_float_resource("timestep", "Double");
-  timestep_coarse = timestep *
-    get_float_resource("timestepCoarseFactor", "Double");
+  st->timestep = get_float_resource(st->dpy, "timestep", "Double");
+  st->timestep_coarse = st->timestep *
+    get_float_resource(st->dpy, "timestepCoarseFactor", "Double");
   
-  sizeFactorMin = get_float_resource("sizeFactorMin", "Double");
-  sizeFactorMax = get_float_resource("sizeFactorMax", "Double");
+  st->sizeFactorMin = get_float_resource(st->dpy, "sizeFactorMin", "Double");
+  st->sizeFactorMax = get_float_resource(st->dpy, "sizeFactorMax", "Double");
+
+  st->minCircles = get_integer_resource (st->dpy, "minCircles", "Integer");
+  st->maxCircles = get_integer_resource (st->dpy, "maxCircles", "Integer");
 
-  minCircles = get_integer_resource ("minCircles", "Integer");
-  maxCircles = get_integer_resource ("maxCircles", "Integer");
+  st->xtime = 0; /* is this right? */
 
-  xtime = 0; /* is this right? */
-  while (0 == stop)
+  return st;
+}
+
+static unsigned long
+epicycle_draw (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  int this_delay = st->delay;
+
+  if (st->eraser) {
+    st->eraser = erase_window (st->dpy, st->window, st->eraser);
+    return 10000;
+  }
+
+  if (st->restart)
     {
-      setup(); /* do this inside the loop to cope with any window resizing */
-      restart = 0;
+      setup(st);
+      st->restart = 0;
 
       /* Flush any outstanding events; this has the side effect of
        * reducing the number of "false restarts"; resdtarts caused by
        * one event (e.g. ConfigureNotify) followed by another
        * (e.g. Expose).
        */
-      XSync(dpy, True);
          
-      wdot_max = random_wdot_max();
+      st->wdot_max = random_wdot_max(st);
          
-      if (pb)
-       {
-         delete_body(pb);
-         pb = NULL;
-       }
-      pb = new_body();
-      pb->x_origin = pb->x = x_offset;
-      pb->y_origin = pb->y = y_offset;
-         
-      
-      if (uncleared)
-       {
-         erase_full_window(dpy, window);
-         uncleared = 0;
-       }
-
-      fflush(stdout);
-      precalculate_figure(pb, xtime, timestep_coarse,
-                         &xmax, &ymax, &xmin, &ymin);
-
-      rescale_circles(pb, xmax, ymax, xmin, ymin);
+      if (st->pb0)
+        {
+          delete_body(st->pb0);
+          st->pb0 = NULL;
+        }
+      st->pb0 = new_body(st);
+      st->pb0->x_origin = st->pb0->x = st->x_offset;
+      st->pb0->y_origin = st->pb0->y = st->y_offset;
+
+      if (st->uncleared)
+        {
+          st->eraser = erase_window (st->dpy, st->window, st->eraser);
+          st->uncleared = 0;
+        }
+
+      precalculate_figure(st->pb0, st->xtime, st->timestep_coarse,
+                          &st->xmax, &st->ymax, &st->xmin, &st->ymin);
+
+      rescale_circles(st, st->pb0, st->xmax, st->ymax, st->xmin, st->ymin);
       
-      move_body(pb, 0.0); /* move once to avoid initial line from origin */
-      move_body(pb, 0.0); /* move once to avoid initial line from origin */
+      move_body(st->pb0, 0.0); /* move once to avoid initial line from origin */
+      move_body(st->pb0, 0.0); /* move once to avoid initial line from origin */
 
       
-      t = 0.0;                 /* start at time zero. */
+      st->T = 0.0;                     /* start at time zero. */
 
-      l = compute_divisor_lcm(pb->epicycles);
+      st->L = compute_divisor_lcm(st->pb0->epicycles);
       
-      colour_cycle_rate = fabs(l);
+      st->colour_cycle_rate = fabs(st->L);
       
-      xtime = fabs(l * circle / wdot_max);
-
-      if (colors)                              /* (colors==NULL) if mono_p */
-       XSetForeground (dpy, color0, colors[pb->current_color].pixel);
+      st->xtime = fabs(st->L * st->circle / st->wdot_max);
 
-      while (0 == restart)
-       {
-         color_step(pb, t/xtime );
-         draw_body(pb, color0);
-         uncleared = 1;
+      if (st->colors)                          /* (colors==NULL) if mono_p */
+        XSetForeground (st->dpy, st->color0, st->colors[st->pb0->current_color].pixel);
+    }
 
-         
-         /* Check if the figure is complete...*/
-         if (t > xtime)
-           {
-             XSync (dpy, False);
 
-              check_events();
-             if (holdtime)
-               sleep(holdtime); /* show complete figure for a bit. */
+  color_step(st, st->pb0, st->T/st->xtime );
+  draw_body(st, st->pb0, st->color0);
+  st->uncleared = 1;
 
-             restart = 1;      /* begin new figure. */
-           }
          
+  /* Check if the figure is complete...*/
+  if (st->T > st->xtime)
+    {
+      this_delay = st->holdtime * 1000000;
+      st->restart = 1; /* begin new figure. */
+    }
          
-         check_events();
-         if (delay)
-           usleep (delay);
-         
-         t += timestep;
-         move_body(pb, t);
-         check_events();
-       }
+
+
+  st->T += st->timestep;
+  move_body(st->pb0, st->T);
+
+  return this_delay;
+}
+
+static void
+epicycle_reshape (Display *dpy, Window window, void *closure, 
+                 unsigned int w, unsigned int h)
+{
+  struct state *st = (struct state *) closure;
+  st->restart = 1;
+}
+
+static Bool
+epicycle_event (Display *dpy, Window window, void *closure, XEvent *e)
+{
+  struct state *st = (struct state *) closure;
+  if (e->type == ButtonPress)
+    {
+      st->restart = 1;
+      return True;
     }
+
+  return False;
+}
+
+static void
+epicycle_free (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  free (st);
 }
 
+XSCREENSAVER_MODULE ("Epicycle", epicycle)