http://ftp.nluug.nl/pub/os/Linux/distr/pardusrepo/sources/xscreensaver-5.02.tar.gz
[xscreensaver] / hacks / ifs.c
index 7e55af5167d1210589b011678f5efd52cea779c5..d79f0f32c9f9b23272a50817a920e629bd69fdd0 100644 (file)
-/* -*- Mode: C; tab-width: 4 -*-
- * ifs --- Modified iterated functions system.
- */
-#if !defined( lint ) && !defined( SABER )
-static const char sccsid[] = "@(#)ifs.c           4.02 97/04/01 xlockmore";
-#endif
-
-/* Copyright (c) 1997 by Massimino Pascal (Pascal.Massimon@ens.fr)
- *
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted,
- * provided that the above copyright notice appear in all copies and that
- * both that copyright notice and this permission notice appear in
- * supporting documentation.
- *
- * This file is provided AS IS with no warranties of any kind.  The author
- * shall have no liability with respect to the infringement of copyrights,
- * trade secrets or any patents by this file or any part thereof.  In no
- * event will the author be liable for any lost revenue or profits or
- * other special, indirect and consequential damages.
- *
- * Revision History:
- * 10-May-97: jwz@netscape.com: turned into a standalone program.
- *                       Made it render into an offscreen bitmap and then copy
- *                       that onto the screen, to reduce flicker.
- */
-
-#ifdef STANDALONE
-# define PROGCLASS                                     "IFS"
-# define HACK_INIT                                     init_ifs
-# define HACK_DRAW                                     draw_ifs
-# define ifs_opts                                      xlockmore_opts
-# define DEFAULTS      "*delay:                20000 \n"                       \
-                                       "*ncolors:              100   \n"
-# define SMOOTH_COLORS
-# include "xlockmore.h"                                /* from the xscreensaver distribution */
-#else  /* !STANDALONE */
-# include "xlock.h"                                    /* from the xlockmore distribution */
-#endif /* !STANDALONE */
-
-ModeSpecOpt ifs_opts = {
-  0, NULL, 0, NULL, NULL };
-
-/*****************************************************/
-/*****************************************************/
-
-typedef float DBL;
-typedef short int F_PT;
-
-/* typedef float               F_PT; */
-
-/*****************************************************/
+/* Copyright © Chris Le Sueur and Robby Griffin, 2005-2006
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Ultimate thanks go to Massimino Pascal, who created the original
+xscreensaver hack, and inspired me with it's swirly goodness. This
+version adds things like variable quality, number of functions and also
+a groovier colouring mode.
+
+This version by Chris Le Sueur <thefishface@gmail.com>, Feb 2005
+Many improvements by Robby Griffin <rmg@terc.edu>, Mar 2006
+Multi-coloured mode added by Jack Grahl <j.grahl@ucl.ac.uk>, Jan 2007
+*/
+
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "screenhack.h"
+
+#undef countof
+#define countof(x) (sizeof((x)) / sizeof(*(x)))
+
+typedef struct {
+  float r, s, tx, ty;   /* Rotation, Scale, Translation X & Y */
+  float ro, rt, rc;     /* Old Rotation, Rotation Target, Rotation Counter */
+  float so, st, sc;     /* Old Scale, Scale Target, Scale Counter */
+  float sa, txa, tya;   /* Scale change, Translation change */
+
+  int ua, ub, utx;      /* Precomputed combined r,s,t values */
+  int uc, ud, uty;      /* Precomputed combined r,s,t values */
+
+} Lens;        
+
+struct state {
+  Display *dpy;
+  Window window;
+  GC gc;
+  Drawable backbuffer;
+  XColor *colours;
+  int ncolours;
+  int ccolour;
+  int blackColor, whiteColor;
+
+  int width, widthb, height;
+  int width8, height8;
+  unsigned int *board;
+  XPoint pointbuf[1000];
+  int npoints;
+  int xmin, xmax, ymin, ymax;
+  int x, y;
+
+  int delay;
+
+  int lensnum;
+  Lens *lenses;
+  int length;
+  int mode;
+  Bool recurse;
+  Bool multi;
+  Bool translate, scale, rotate;
+};
 
-#define FIX 12
-#define UNIT   ( 1<<FIX )
-#define MAX_SIMI  6
+#define getdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] &  (1<<((x) & 31)))
+#define setdot(x,y) (st->board[((y)*st->widthb)+((x)>>5)] |= (1<<((x) & 31)))
 
-   /* settings for a PC 120Mhz... */
-#define MAX_DEPTH_2  10
-#define MAX_DEPTH_3  6
-#define MAX_DEPTH_4  4
-#define MAX_DEPTH_5  3
+static float
+myrandom(float up)
+{
+  return (((float)random() / RAND_MAX) * up);
+}
 
-#define DBL_To_F_PT(x)  (F_PT)( (DBL)(UNIT)*(x) )
+static const char *ifs_defaults [] = {
+  ".background:                Black",
+  "*lensnum:           3",
+  "*length:            9",
+  "*mode:              0",
+  "*colors:            200",
+  "*delay:             20000",
+  "*translate:         True",
+  "*scale:             True",
+  "*rotate:            True",
+  "*recurse:           False",
+  "*multi:              True",
+# ifdef HAVE_COCOA     /* Don't second-guess Quartz's double-buffering */
+  "*doubleBuffer:      False",
+#else
+  "*doubleBuffer:      True",
+#endif
+  0
+};
 
-/*****************************************************/
+static XrmOptionDescRec ifs_options [] = {
+  { "-detail",         ".length",      XrmoptionSepArg, 0 },
+  { "-delay",          ".delay",       XrmoptionSepArg, 0 },
+  { "-mode",           ".mode",        XrmoptionSepArg, 0 },
+  { "-colors",         ".colors",      XrmoptionSepArg, 0 },
+  { "-functions",      ".lensnum",     XrmoptionSepArg, 0 },
+  { "-no-translate",   ".translate",   XrmoptionNoArg, "False" },
+  { "-no-scale",       ".scale",       XrmoptionNoArg, "False" },
+  { "-no-rotate",      ".rotate",      XrmoptionNoArg, "False" },
+  { "-recurse",                ".recurse",     XrmoptionNoArg, "True" },
+  { "-iterate",                ".recurse",     XrmoptionNoArg, "False" },
+  { "-multi",           ".multi",       XrmoptionNoArg, "True" },
+  { "-no-multi",        ".multi",       XrmoptionNoArg, "False" },
+  { "-db",             ".doubleBuffer",XrmoptionNoArg, "True" },
+  { "-no-db",          ".doubleBuffer",XrmoptionNoArg, "False" },
+  { 0, 0, 0, 0 }
+};
 
-static int  Max_Colors;
-static ModeInfo *The_MI;
-static F_PT Lx, Ly;
-static int  D;
-static Display *display;
-static GC   gc;
-static Window window;
 
-/*****************************************************/
+/* Draw all the queued points on the backbuffer */
+static void
+drawpoints(struct state *st)
+{
+  XDrawPoints(st->dpy, st->backbuffer, st->gc, st->pointbuf, st->npoints,
+             CoordModeOrigin);
+  st->npoints = 0;
+}
 
-typedef struct Similitude_Struct SIMI;
-typedef struct Fractal_Struct FRACTAL;
+/* Set a point to be drawn, if it hasn't been already.
+ * Expects coordinates in 256ths of a pixel. */
+static void
+sp(struct state *st, int x, int y)
+{
+  if (x < 0 || x >= st->width8 || y < 0 || y >= st->height8)
+    return;
 
-struct Similitude_Struct {
+  x >>= 8;
+  y >>= 8;
 
-       DBL         c_x, c_y;
-       DBL         r, r2, A, A2;
-       F_PT        Ct, St, Ct2, St2;
-       F_PT        Cx, Cy;
-       F_PT        R, R2;
-};
+  if (getdot(x, y)) return;
+  setdot(x, y);
 
-struct Fractal_Struct {
-
-       int         Nb_Simi;
-       SIMI        Components[5 * MAX_SIMI];
-       int         Depth, Col;
-       int         Count, Speed;
-       int         Width, Height, Lx, Ly;
-       DBL         r_mean, dr_mean, dr2_mean;
-       int         Cur_Pt, Max_Pt;
-       XPoint     *Buffer1, *Buffer2;
-    Pixmap      dbuf;  /* jwz */
-    GC          dbuf_gc;
-};
+  if (x < st->xmin) st->xmin = x;
+  if (x > st->xmax) st->xmax = x;
+  if (y < st->ymin) st->ymin = y;
+  if (y > st->ymax) st->ymax = y;
 
-static FRACTAL *Root = NULL, *Cur_F;
-static XPoint *Buf;
-static int  Cur_Pt;
+  st->pointbuf[st->npoints].x = x;
+  st->pointbuf[st->npoints].y = y;
+  st->npoints++;
 
+  if (st->npoints >= countof(st->pointbuf)) {
+    drawpoints(st);
+  }
+}
 
-/*****************************************************/
-/*****************************************************/
 
-static      DBL
-Gauss_Rand(DBL c, DBL A, DBL S)
+/* Precompute integer values for matrix multiplication and vector
+ * addition. The matrix multiplication will go like this (see iterate()):
+ *   |x2|     |ua ub|   |x|     |utx|
+ *   |  |  =  |     | * | |  +  |   |
+ *   |y2|     |uc ud|   |y|     |uty|
+ * 
+ * There is an extra factor of 2^10 in these values, and an extra factor of
+ * 2^8 in the coordinates, in order to implement fixed-point arithmetic.
+ */
+static void
+lensmatrix(struct state *st, Lens *l)
 {
-       DBL         y;
-
-       y = (DBL) LRAND() / MAXRAND;
-       y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
-       if (NRAND(2))
-               return (c + y);
-       return (c - y);
+  l->ua = 1024.0 * l->s * cos(l->r);
+  l->ub = -1024.0 * l->s * sin(l->r);
+  l->uc = -l->ub;
+  l->ud = l->ua;
+  l->utx = 131072.0 * st->width * (l->s * (sin(l->r) - cos(l->r))
+                                  + l->tx / 16 + 1);
+  l->uty = -131072.0 * st->height * (l->s * (sin(l->r) + cos(l->r))
+                                    + l->ty / 16 - 1);
 }
 
-static      DBL
-Half_Gauss_Rand(DBL c, DBL A, DBL S)
+static void
+CreateLens(struct state *st,
+           float nr,
+           float ns,
+           float nx,
+           float ny,
+           Lens *newlens)
 {
-       DBL         y;
-
-       y = (DBL) LRAND() / MAXRAND;
-       y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
-       return (c + y);
+  newlens->sa = newlens->txa = newlens->tya = 0;
+  if (st->rotate) {
+    newlens->r = newlens->ro = newlens->rt = nr;
+    newlens->rc = 1;
+  }
+  else newlens->r = 0;
+
+  if (st->scale) {
+    newlens->s = newlens->so = newlens->st = ns;
+    newlens->sc = 1;
+  }
+  else newlens->s = 0.5;
+
+  newlens->tx = nx;
+  newlens->ty = ny;
+
+  lensmatrix(st, newlens);
 }
-
+       
 static void
-Random_Simis(FRACTAL * F, SIMI * Cur, int i)
+mutate(struct state *st, Lens *l)
 {
-       while (i--) {
-               Cur->c_x = Gauss_Rand(0.0, .8, 4.0);
-               Cur->c_y = Gauss_Rand(0.0, .8, 4.0);
-               Cur->r = Gauss_Rand(F->r_mean, F->dr_mean, 3.0);
-               Cur->r2 = Half_Gauss_Rand(0.0, F->dr2_mean, 2.0);
-               Cur->A = Gauss_Rand(0.0, 360.0, 4.0) * (M_PI / 180.0);
-               Cur->A2 = Gauss_Rand(0.0, 360.0, 4.0) * (M_PI / 180.0);
-               Cur++;
-       }
+  if (st->rotate) {
+    float factor;
+    if(l->rc >= 1) {
+      l->rc = 0;
+      l->ro = l->rt;
+      l->rt = myrandom(4) - 2;
+    }
+    factor = (sin((-M_PI / 2.0) + M_PI * l->rc) + 1.0) / 2.0;
+    l->r = l->ro + (l->rt - l->ro) * factor;
+    l->rc += 0.01;
+  }
+  if (st->scale) {
+    float factor;
+    if (l->sc >= 1) {
+      /* Reset counter, obtain new target value */
+      l->sc = 0;
+      l->so = l->st;
+      l->st = myrandom(2) - 1;
+    }
+    factor = (sin((-M_PI / 2.0) + M_PI * l->sc) + 1.0) / 2.0;
+    /* Take average of old target and new target, using factor to *
+     * weight. It's computed sinusoidally, resulting in smooth,   *
+     * rhythmic transitions.                                      */
+    l->s = l->so + (l->st - l->so) * factor;
+    l->sc += 0.01;
+  }
+  if (st->translate) {
+    l->txa += myrandom(0.004) - 0.002;
+    l->tya += myrandom(0.004) - 0.002;
+    l->tx += l->txa;
+    l->ty += l->tya;
+    if (l->tx > 6) l->txa -= 0.004;
+    if (l->ty > 6) l->tya -= 0.004;
+    if (l->tx < -6) l->txa += 0.004;
+    if (l->ty < -6) l->tya += 0.004;
+    if (l->txa > 0.05 || l->txa < -0.05) l->txa /= 1.7;
+    if (l->tya > 0.05 || l->tya < -0.05) l->tya /= 1.7;
+  }
+  if (st->rotate || st->scale || st->translate) {
+    lensmatrix(st, l);
+  }
 }
 
-/***************************************************************/
 
-void
-init_ifs(ModeInfo * mi)
+#define STEPX(l,x,y) (((l)->ua * (x) + (l)->ub * (y) + (l)->utx) >> 10)
+#define STEPY(l,x,y) (((l)->uc * (x) + (l)->ud * (y) + (l)->uty) >> 10)
+/*#define STEPY(l,x,y) (((l)->ua * (y) - (l)->ub * (x) + (l)->uty) >> 10)*/
+
+/* Calls itself <lensnum> times - with results from each lens/function.  *
+ * After <length> calls to itself, it stops iterating and draws a point. */
+static void
+recurse(struct state *st, int x, int y, int length, int p)
 {
-       int         i;
-       FRACTAL    *Fractal;
-
-       if (Root == NULL) {
-               Root = (FRACTAL *) calloc(
-                                      MI_NUM_SCREENS(mi), sizeof (FRACTAL));
-               if (Root == NULL)
-                       return;
-       }
-       Fractal = &Root[MI_SCREEN(mi)];
-
-       if (Fractal->Max_Pt) {
-               free(Fractal->Buffer1);
-               free(Fractal->Buffer2);
-       }
-       i = (NRAND(4)) + 2;     /* Number of centers */
-       switch (i) {
-               case 3:
-                       Fractal->Depth = MAX_DEPTH_3;
-                       Fractal->r_mean = .6;
-                       Fractal->dr_mean = .4;
-                       Fractal->dr2_mean = .3;
-                       break;
-
-               case 4:
-                       Fractal->Depth = MAX_DEPTH_4;
-                       Fractal->r_mean = .5;
-                       Fractal->dr_mean = .4;
-                       Fractal->dr2_mean = .3;
-                       break;
-
-               case 5:
-                       Fractal->Depth = MAX_DEPTH_5;
-                       Fractal->r_mean = .5;
-                       Fractal->dr_mean = .4;
-                       Fractal->dr2_mean = .3;
-                       break;
-
-               default:
-               case 2:
-                       Fractal->Depth = MAX_DEPTH_2;
-                       Fractal->r_mean = .7;
-                       Fractal->dr_mean = .3;
-                       Fractal->dr2_mean = .4;
-                       break;
-       }
-       /* (void) fprintf( stderr, "N=%d\n", i ); */
-       Fractal->Nb_Simi = i;
-       Fractal->Max_Pt = Fractal->Nb_Simi - 1;
-       for (i = 0; i <= Fractal->Depth + 2; ++i)
-               Fractal->Max_Pt *= Fractal->Nb_Simi;
-
-       Fractal->Buffer1 = (XPoint *) calloc(Fractal->Max_Pt, sizeof (XPoint));
-       if (Fractal->Buffer1 == NULL)
-               goto Abort;
-       Fractal->Buffer2 = (XPoint *) calloc(Fractal->Max_Pt, sizeof (XPoint));
-       if (Fractal->Buffer2 == NULL)
-               goto Abort;
-
-       Fractal->Speed = 6;
-       Fractal->Width = MI_WIN_WIDTH(mi);
-       Fractal->Height = MI_WIN_HEIGHT(mi);
-       Fractal->Cur_Pt = 0;
-       Fractal->Count = 0;
-       Fractal->Lx = (Fractal->Width - 1) / 2;
-       Fractal->Ly = (Fractal->Height - 1) / 2;
-       Fractal->Col = NRAND(MI_NPIXELS(mi) - 1) + 1;
-
-       Random_Simis(Fractal, Fractal->Components, 5 * MAX_SIMI);
-
-       Fractal->dbuf = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
-                                                                 Fractal->Width, Fractal->Height, 1);
-       if (Fractal->dbuf)
-         {
-               XGCValues gcv;
-               gcv.foreground = 0;
-               gcv.background = 0;
-               gcv.function = GXcopy;
-               Fractal->dbuf_gc = XCreateGC(MI_DISPLAY(mi), Fractal->dbuf,
-                                                                        GCForeground|GCBackground|GCFunction,
-                                                                        &gcv);
-               XFillRectangle(MI_DISPLAY(mi), Fractal->dbuf,
-                                          Fractal->dbuf_gc, 0,0, Fractal->Width, Fractal->Height);
-
-               XSetBackground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
-               XSetFunction(MI_DISPLAY(mi), MI_GC(mi), GXcopy);
-         }
-
-       XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
-       return;
-
-      Abort:
-       if (Fractal->Buffer1 != NULL)
-               free(Fractal->Buffer1);
-       if (Fractal->Buffer2 != NULL)
-               free(Fractal->Buffer2);
-       Fractal->Buffer1 = NULL;
-       Fractal->Buffer2 = NULL;
-       Fractal->Max_Pt = 0;
-       return;
+  int i;
+  Lens *l;
+
+  if (length == 0) {
+    if (p == 0) 
+      sp(st, x, y);
+    else {
+      l = &st->lenses[p];
+      sp(st, STEPX(l, x, y), STEPY(l, x, y));
+    }
+  }
+  else {
+    for (i = 0; i < st->lensnum; i++) {
+      l = &st->lenses[i];
+      recurse(st, STEPX(l, x, y), STEPY(l, x, y), length - 1, p);
+    }
+  }
 }
 
-
-/***************************************************************/
-
-#ifndef __GNUC__
-# define inline /* */
-#endif
-
-static inline void
-Transform(SIMI * Simi, F_PT xo, F_PT yo, F_PT * x, F_PT * y)
+/* Performs <count> random lens transformations, drawing a point at each
+ * iteration after the first 10.
+ */
+static void
+iterate(struct state *st, int count, int p)
 {
-       F_PT        xx, yy;
-
-       xo = xo - Simi->Cx;
-       xo = (xo * Simi->R) / UNIT;
-       yo = yo - Simi->Cy;
-       yo = (yo * Simi->R) / UNIT;
-
-       xx = xo - Simi->Cx;
-       xx = (xx * Simi->R2) / UNIT;
-       yy = -yo - Simi->Cy;
-       yy = (yy * Simi->R2) / UNIT;
-
-       *x = ((xo * Simi->Ct - yo * Simi->St + xx * Simi->Ct2 - yy * Simi->St2) / UNIT) + Simi->Cx;
-       *y = ((xo * Simi->St + yo * Simi->Ct + xx * Simi->St2 + yy * Simi->Ct2) / UNIT) + Simi->Cy;
+  int i;
+  Lens *l;
+  int x = st->x;
+  int y = st->y;
+  int tx;
+
+# define STEP()                              \
+    l = &st->lenses[random() % st->lensnum]; \
+    tx = STEPX(l, x, y);                     \
+    y = STEPY(l, x, y);                      \
+    x = tx
+
+  for (i = 0; i < 10; i++) {
+    STEP();
+  }
+
+  for ( ; i < count; i++) {
+    STEP();
+    if (p == 0)
+      sp(st, x, y);
+    else
+      {
+       l = &st->lenses[p];
+       sp(st, STEPX(l, x, y), STEPY(l, x, y));
+      }
+  }
+
+# undef STEP
+
+  st->x = x;
+  st->y = y;
 }
 
-/***************************************************************/
+/* Come on and iterate, iterate, iterate and sing... *
+ * Yeah, this function just calls iterate, mutate,   *
+ * and then draws everything.                        */
+static unsigned long
+ifs_draw (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  int i;
+  int xmin = st->xmin, xmax = st->xmax, ymin = st->ymin, ymax = st->ymax;
+  int partcolor, x, y;
+  
+
+  /* erase whatever was drawn in the previous frame */
+  if (xmin <= xmax && ymin <= ymax) {
+    XSetForeground(st->dpy, st->gc, st->blackColor);
+    XFillRectangle(st->dpy, st->backbuffer, st->gc,
+                  xmin, ymin,
+                  xmax - xmin + 1, ymax - ymin + 1);
+    st->xmin = st->width + 1;
+    st->xmax = st->ymax = -1;
+    st->ymin = st->height + 1;
+  }
+
+  st->ccolour++;
+  st->ccolour %= st->ncolours;
+
+  /* calculate and draw points for this frame */
+  x = st->width << 7;
+  y = st->height << 7;
+  
+  if (st->multi) {
+    for (i = 0; i < st->lensnum; i++) {  
+      partcolor = st->ccolour * (i+1);
+      partcolor %= st->ncolours;
+      XSetForeground(st->dpy, st->gc, st->colours[partcolor].pixel);
+      memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
+      if (st->recurse)   
+       recurse(st, x, y, st->length - 1, i);
+      else
+       iterate(st, pow(st->lensnum, st->length - 1), i);
+      if (st->npoints) 
+       drawpoints(st);
+    }
+  } 
+  else {
+    
+    XSetForeground(st->dpy, st->gc, st->colours[st->ccolour].pixel);
+    memset(st->board, 0, st->widthb * st->height * sizeof(*st->board));
+    if (st->recurse)
+      recurse(st, x, y, st->length, 0);
+    else
+      iterate(st, pow(st->lensnum, st->length), 0);
+    if (st->npoints)
+      drawpoints(st);
+  }
+  
+  /* if we just drew into a buffer, copy the changed area (including
+   * erased area) to screen */
+  if (st->backbuffer != st->window
+      && ((st->xmin <= st->xmax && st->ymin <= st->ymax)
+         || (xmin <= xmax && ymin <= ymax))) {
+    if (st->xmin < xmin) xmin = st->xmin;
+    if (st->xmax > xmax) xmax = st->xmax;
+    if (st->ymin < ymin) ymin = st->ymin;
+    if (st->ymax > ymax) ymax = st->ymax;
+    XCopyArea(st->dpy, st->backbuffer, st->window, st->gc,
+             xmin, ymin,
+             xmax - xmin + 1, ymax - ymin + 1,
+             xmin, ymin);
+  }
+
+  for(i = 0; i < st->lensnum; i++) {
+    mutate(st, &st->lenses[i]);
+  }
+
+  return st->delay;
+}
 
 static void
-Trace(F_PT xo, F_PT yo)
+ifs_reshape (Display *, Window, void *, unsigned int, unsigned int);
+
+static void *
+ifs_init (Display *d_arg, Window w_arg)
 {
-       F_PT        x, y, i;
-       SIMI       *Cur;
-
-       Cur = Cur_F->Components;
-       for (i = Cur_F->Nb_Simi; i; --i, Cur++) {
-               Transform(Cur, xo, yo, &x, &y);
-               Buf->x = Lx + (x * Lx / (UNIT * 2));
-               Buf->y = Ly - (y * Ly / (UNIT * 2));
-               Buf++;
-               Cur_Pt++;
-
-               if (D && ((x - xo) >> 4) && ((y - yo) >> 4)) {
-                       D--;
-                       Trace(x, y);
-                       D++;
-               }
-       }
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
+  int i;
+  XWindowAttributes xgwa;
+       
+  /* Initialise all this X shizzle */
+  st->dpy = d_arg;
+  st->window = w_arg;
+
+  st->blackColor = BlackPixel(st->dpy, DefaultScreen(st->dpy));
+  st->whiteColor = WhitePixel(st->dpy, DefaultScreen(st->dpy));
+  st->gc = XCreateGC(st->dpy, st->window, 0, NULL);
+
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+  ifs_reshape(st->dpy, st->window, st, xgwa.width, xgwa.height);
+       
+  st->ncolours = get_integer_resource(st->dpy, "colors", "Colors");
+  if (st->ncolours < st->lensnum)
+    st->ncolours = st->lensnum;
+  if (st->colours) free(st->colours);
+  st->colours = (XColor *)calloc(st->ncolours, sizeof(XColor));
+  if (!st->colours) exit(1);
+  make_smooth_colormap (st->dpy, xgwa.visual, xgwa.colormap, 
+                        st->colours, &st->ncolours,
+                        True, 0, False);
+
+  /* Initialize IFS data */
+  st->delay = get_integer_resource(st->dpy, "delay", "Delay");
+  st->length = get_integer_resource(st->dpy, "length", "Detail");
+  if (st->length < 0) st->length = 0;
+  st->mode = get_integer_resource(st->dpy, "mode", "Mode");
+
+  st->rotate    = get_boolean_resource(st->dpy, "rotate", "Boolean");
+  st->scale     = get_boolean_resource(st->dpy, "scale", "Boolean");
+  st->translate = get_boolean_resource(st->dpy, "translate", "Boolean");
+  st->recurse = get_boolean_resource(st->dpy, "recurse", "Boolean");
+  st->multi = get_boolean_resource(st->dpy, "multi", "Boolean");
+
+  st->lensnum = get_integer_resource(st->dpy, "lensnum", "Functions");
+  if (st->lenses) free (st->lenses);
+  st->lenses = (Lens *)calloc(st->lensnum, sizeof(Lens));
+  if (!st->lenses) exit(1);
+  for (i = 0; i < st->lensnum; i++) {
+    CreateLens(st,
+              myrandom(1)-0.5,
+              myrandom(1),
+              myrandom(4)-2,
+              myrandom(4)+2,
+              &st->lenses[i]);
+  }
+
+  return st;
 }
 
 static void
-Draw_Fractal(FRACTAL * F)
+ifs_reshape (Display *dpy, Window window, void *closure, 
+            unsigned int w, unsigned int h)
 {
-       int         i, j;
-       F_PT        x, y, xo, yo;
-       SIMI       *Cur, *Simi;
-
-       for (Cur = F->Components, i = F->Nb_Simi; i; --i, Cur++) {
-               Cur->Cx = DBL_To_F_PT(Cur->c_x);
-               Cur->Cy = DBL_To_F_PT(Cur->c_y);
-
-               Cur->Ct = DBL_To_F_PT(cos(Cur->A));
-               Cur->St = DBL_To_F_PT(sin(Cur->A));
-               Cur->Ct2 = DBL_To_F_PT(cos(Cur->A2));
-               Cur->St2 = DBL_To_F_PT(sin(Cur->A2));
-
-               Cur->R = DBL_To_F_PT(Cur->r);
-               Cur->R2 = DBL_To_F_PT(Cur->r2);
-       }
-
-
-       Cur_Pt = 0;
-       Cur_F = F;
-       Buf = F->Buffer2;
-       Lx = F->Lx;
-       Ly = F->Ly;
-       D = F->Depth;
-       for (Cur = F->Components, i = F->Nb_Simi; i; --i, Cur++) {
-               xo = Cur->Cx;
-               yo = Cur->Cy;
-               for (Simi = F->Components, j = F->Nb_Simi; j; --j, Simi++) {
-                       if (Simi == Cur)
-                               continue;
-                       Transform(Simi, xo, yo, &x, &y);
-                       Trace(x, y);
-               }
-       }
-
-       /* Erase previous */
-
-       if (F->Cur_Pt) {
-               XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(The_MI));
-               if (F->dbuf)    /* jwz */
-                 {
-                       XSetForeground(display, F->dbuf_gc, 0);
-/*                 XDrawPoints(display, F->dbuf, F->dbuf_gc, F->Buffer1, F->Cur_Pt,
-                                           CoordModeOrigin); */
-                   XFillRectangle(display, F->dbuf, F->dbuf_gc, 0, 0,
-                                                  F->Width, F->Height);
-                 }
-               else
-                 XDrawPoints(display, window, gc, F->Buffer1, F->Cur_Pt,
-                                         CoordModeOrigin);
-       }
-       if (Max_Colors < 2)
-               XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(The_MI));
-       else
-               XSetForeground(display, gc, MI_PIXEL(The_MI, F->Col % Max_Colors));
-       if (Cur_Pt) {
-         if (F->dbuf)
-               {
-                 XSetForeground(display, F->dbuf_gc, 1);
-                 XDrawPoints(display, F->dbuf, F->dbuf_gc, F->Buffer2, Cur_Pt,
-                                         CoordModeOrigin);
-               }
-         else
-               XDrawPoints(display, window, gc, F->Buffer2, Cur_Pt, CoordModeOrigin);
-       }
-
-       if (F->dbuf)
-         XCopyPlane(display, F->dbuf, window, gc, 0,0,F->Width,F->Height,0,0, 1);
-
-       F->Cur_Pt = Cur_Pt;
-       Buf = F->Buffer1;
-       F->Buffer1 = F->Buffer2;
-       F->Buffer2 = Buf;
+  struct state *st = (struct state *)closure;
+  XWindowAttributes xgwa;
+
+  /* oh well, we need the screen depth anyway */
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+
+  st->width = xgwa.width;
+  st->widthb = ((xgwa.width + 31) >> 5);
+  st->height = xgwa.height;
+  st->width8 = xgwa.width << 8;
+  st->height8 = xgwa.height << 8;
+
+  if (!st->xmax && !st->ymax && !st->xmin && !st->ymin) {
+    st->xmin = xgwa.width + 1;
+    st->xmax = st->ymax = -1;
+    st->ymin = xgwa.height + 1;
+  }
+
+  if (st->backbuffer != None && st->backbuffer != st->window) {
+    XFreePixmap(st->dpy, st->backbuffer);
+    st->backbuffer = None;
+  }
+
+  if (get_boolean_resource (st->dpy, "doubleBuffer", "Boolean")) {
+    st->backbuffer = XCreatePixmap(st->dpy, st->window, st->width, st->height, xgwa.depth);
+    XSetForeground(st->dpy, st->gc, st->blackColor);
+    XFillRectangle(st->dpy, st->backbuffer, st->gc,
+                  0, 0, st->width, st->height);
+  } else {
+    st->backbuffer = st->window;
+    XClearWindow(st->dpy, st->window);
+  }
+
+  if (st->board) free(st->board);
+  st->board = (unsigned int *)calloc(st->widthb * st->height, sizeof(unsigned int));
+  if (!st->board) exit(1);
 }
 
-
-void
-draw_ifs(ModeInfo * mi)
+static Bool
+ifs_event (Display *dpy, Window window, void *closure, XEvent *event)
 {
-       int         i;
-       FRACTAL    *F;
-       DBL         u, uu, v, vv, u0, u1, u2, u3;
-       SIMI       *S, *S1, *S2, *S3, *S4;
-
-       The_MI = mi;
-       display = MI_DISPLAY(mi);
-       window = MI_WINDOW(mi);
-       gc = MI_GC(mi);
-       Max_Colors = MI_NPIXELS(mi);
-
-       F = &Root[MI_SCREEN(mi)];
-
-       u = (DBL) (F->Count) * (DBL) (F->Speed) / 1000.0;
-       uu = u * u;
-       v = 1.0 - u;
-       vv = v * v;
-       u0 = vv * v;
-       u1 = 3.0 * vv * u;
-       u2 = 3.0 * v * uu;
-       u3 = u * uu;
-
-       S = F->Components;
-       S1 = S + F->Nb_Simi;
-       S2 = S1 + F->Nb_Simi;
-       S3 = S2 + F->Nb_Simi;
-       S4 = S3 + F->Nb_Simi;
-
-       for (i = F->Nb_Simi; i; --i, S++, S1++, S2++, S3++, S4++) {
-               S->c_x = u0 * S1->c_x + u1 * S2->c_x + u2 * S3->c_x + u3 * S4->c_x;
-               S->c_y = u0 * S1->c_y + u1 * S2->c_y + u2 * S3->c_y + u3 * S4->c_y;
-               S->r = u0 * S1->r + u1 * S2->r + u2 * S3->r + u3 * S4->r;
-               S->r2 = u0 * S1->r2 + u1 * S2->r2 + u2 * S3->r2 + u3 * S4->r2;
-               S->A = u0 * S1->A + u1 * S2->A + u2 * S3->A + u3 * S4->A;
-               S->A2 = u0 * S1->A2 + u1 * S2->A2 + u2 * S3->A2 + u3 * S4->A2;
-       }
-
-       Draw_Fractal(F);
-
-       if (F->Count >= 1000 / F->Speed) {
-               S = F->Components;
-               S1 = S + F->Nb_Simi;
-               S2 = S1 + F->Nb_Simi;
-               S3 = S2 + F->Nb_Simi;
-               S4 = S3 + F->Nb_Simi;
-
-               for (i = F->Nb_Simi; i; --i, S++, S1++, S2++, S3++, S4++) {
-                       S2->c_x = 2.0 * S4->c_x - S3->c_x;
-                       S2->c_y = 2.0 * S4->c_y - S3->c_y;
-                       S2->r = 2.0 * S4->r - S3->r;
-                       S2->r2 = 2.0 * S4->r2 - S3->r2;
-                       S2->A = 2.0 * S4->A - S3->A;
-                       S2->A2 = 2.0 * S4->A2 - S3->A2;
-
-                       *S1 = *S4;
-               }
-               Random_Simis(F, F->Components + 3 * F->Nb_Simi, F->Nb_Simi);
-
-               Random_Simis(F, F->Components + 4 * F->Nb_Simi, F->Nb_Simi);
-
-               F->Count = 0;
-       } else
-               F->Count++;
-
-       F->Col++;
+  return False;
 }
 
-
-/***************************************************************/
-
-void
-release_ifs(ModeInfo * mi)
+static void
+ifs_free (Display *dpy, Window window, void *closure)
 {
-       int         i;
-
-       if (Root == NULL)
-               return;
-
-       for (i = 0; i < MI_NUM_SCREENS(mi); ++i) {
-               if (Root[i].Buffer1 != NULL)
-                       free(Root[i].Buffer1);
-               if (Root[i].Buffer2 != NULL)
-                       free(Root[i].Buffer2);
-               if (Root[i].dbuf)
-                       XFreePixmap(MI_DISPLAY(mi), Root[i].dbuf);
-               if (Root[i].dbuf_gc)
-                       XFreeGC(MI_DISPLAY(mi), Root[i].dbuf_gc);
-       }
-       free(Root);
-       Root = NULL;
+  struct state *st = (struct state *) closure;
+
+  if (st->board) free(st->board);
+  if (st->lenses) free(st->lenses);
+  if (st->colours) free(st->colours);
+  if (st->backbuffer != None && st->backbuffer != st->window)
+    XFreePixmap(st->dpy, st->backbuffer);
+  free(st);
 }
+
+XSCREENSAVER_MODULE ("IFS", ifs)