http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[xscreensaver] / hacks / glx / mirrorblob.c
index b5a9275602478c7d2a553d1f2d84856ac5dfb82d..c6a5c84d2f9d96870de85d644520ef9c71341e0b 100644 (file)
  * 10-Feb-2004:  jon.dowdall@bigpond.com  Added motion blur
  *
  * The mirrorblob screensaver draws a pulsing blob on the screen.  Options
- * include adding a background (via screen_to_texture), texturing the blob,
+ * include adding a background (via load_texture_async), texturing the blob,
  * making the blob semi-transparent and varying the resolution of the blob
  * tessellation.
  *
  * The blob was inspired by a lavalamp is in no way a simulation.  The code is
  * just an attempt to generate some eye-candy.
  *
- * Much of xscreensaver code framework is taken from the pulsar module by David
+ * Much of xmirrorblob code framework is taken from the pulsar module by David
  * Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski.
  *
  */
 
 #include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/time.h>
 
 #ifdef STANDALONE
-# define PROGCLASS         "Screensaver"
-# define HACK_INIT         init_screensaver
-# define HACK_DRAW         draw_screensaver
-# define HACK_RESHAPE      reshape_screensaver
-# define screensaver_opts  xlockmore_opts
+#define DEFAULTS \
+    "*delay:             " DEF_DELAY "\n" \
+    "*showFPS:           " DEF_FPS   "\n" \
+    "*useSHM:              True      \n"
+
+# define refresh_mirrorblob 0
+# define mirrorblob_handle_event 0
+# include "xlockmore.h"    /* from the xmirrorblob distribution */
+#else /* !STANDALONE */
+# include "xlock.h"        /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL /* whole file */
+
 
 #define DEF_DELAY            "10000"
 #define DEF_FPS              "False"
 #define DEF_HOLD_TIME        "30"
 #define DEF_FADE_TIME        "5"
 
-#define DEFAULTS \
-    "*delay:             " DEF_DELAY "\n" \
-    "*showFPS:           " DEF_FPS   "\n" \
-    "*useSHM:              True      \n"
-
-# include "xlockmore.h"    /* from the xscreensaver distribution */
-#else /* !STANDALONE */
-# include "xlock.h"        /* from the xlockmore distribution */
-#endif /* !STANDALONE */
-
-#ifdef USE_GL /* whole file */
-
 #ifdef HAVE_XMU
 # ifndef VMS
 #  include <X11/Xmu/Drawing.h>
 # endif /* VMS */
 #endif
 
-#include <GL/gl.h>
-#include <GL/glu.h>
-#include "GL/glx.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-/*#include <string.h>*/
 #include "grab-ximage.h"
 
 #undef countof
@@ -115,8 +102,8 @@ static int fade_time;
 static int hold_time;
 
 static XrmOptionDescRec opts[] = {
-    {"-wire",             ".blob.wire",             XrmoptionNoArg, "true" },
-    {"+wire",             ".blob.wire",             XrmoptionNoArg, "false" },
+    {"-wireframe",        ".blob.wire",             XrmoptionNoArg, "true" },
+    {"+wireframe",        ".blob.wire",             XrmoptionNoArg, "false" },
     {"-blend",            ".blob.blend",            XrmoptionNoArg, "true" },
     {"+blend",            ".blob.blend",            XrmoptionNoArg, "false" },
     {"-fog",              ".blob.fog",              XrmoptionNoArg, "true" },
@@ -182,25 +169,17 @@ static OptionStruct desc[] =
     {"-hold_time", "Number of frames before next image"},
 };
 
-ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc};
+ENTRYPOINT ModeSpecOpt mirrorblob_opts = {countof(opts), opts, countof(vars), vars, desc};
 
 #ifdef USE_MODULES
-ModStruct   screensaver_description =
-{"screensaver", "init_screensaver", "draw_screensaver", "release_screensaver",
- "draw_screensaver", "init_screensaver", NULL, &screensaver_opts,
+ModStruct   mirrorblob_description =
+{"mirrorblob", "init_mirrorblob", "draw_mirrorblob", "release_mirrorblob",
+ "draw_mirrorblob", "init_mirrorblob", NULL, &mirrorblob_opts,
  1000, 1, 2, 1, 4, 1.0, "",
- "OpenGL screensaver", 0, NULL};
+ "OpenGL mirrorblob", 0, NULL};
 #endif
 
-/* structure for holding the screensaver data */
-typedef struct {
-    int screen_width, screen_height;
-    GLXContext *glx_context;
-    Window window;
-    XColor fg, bg;
-} screensaverstruct;
-
-static screensaverstruct *Screensaver = NULL;
+#define NUM_TEXTURES 2
 
 /*****************************************************************************
  * Types used in blob code
@@ -240,49 +219,61 @@ typedef struct
     Vector3D pos;
 } Field_Data;
 
-/*****************************************************************************
- * Static blob data
- *****************************************************************************/
+typedef enum
+{
+  HOLDING=0,
+  TRANSITIONING
+} Frame_State;
 
-static Row_Data *row_data;
 
-/* Parameters controlling the position of the blob as a whole */
-static Vector3D blob_center = {0.0, 0.0, 0.0};
-static Vector3D blob_anchor = {0.0, 0.0, 0.0};
-static Vector3D blob_velocity = {0.0, 0.0, 0.0};
-static Vector3D blob_force = {0.0, 0.0, 0.0};
+/* structure for holding the mirrorblob data */
+typedef struct {
+  int screen_width, screen_height;
+  GLXContext *glx_context;
+  Window window;
+  XColor fg, bg;
+  double freak, v_freak;
 
-/* Count of the total number of points */
-static int num_points;
+  Row_Data *row_data;
 
-static Vector3D *dots = NULL;
-static Vector3D *normals = NULL;
-static Colour   *colours = NULL;
-static Vector2D *tex_coords = NULL;
+  /* Parameters controlling the position of the blob as a whole */
+  Vector3D blob_center;
+  Vector3D blob_anchor;
+  Vector3D blob_velocity;
+  Vector3D blob_force;
 
-/* Pointer to the field function results */
-static double *field = 0, *wall_field = 0;
+  /* Count of the total number of points */
+  int num_points;
 
-Field_Data *field_data;
+  Vector3D *dots;
+  Vector3D *normals;
+  Colour   *colours;
+  Vector2D *tex_coords;
 
-/* Use 2 textures to allow a gradual fade between images */
-#define NUM_TEXTURES 2
-static int current_texture;
+  /* Pointer to the field function results */
+  double *field, *wall_field;
 
-/* Ratio of used texture size to total texture size */
-GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
-GLuint textures[NUM_TEXTURES];
+  Field_Data *field_data;
 
-typedef enum
-{
-  HOLDING,
-  TRANSITIONING
-} Frame_State;
+  /* Use 2 textures to allow a gradual fade between images */
+  int current_texture;
+
+  /* Ratio of used texture size to total texture size */
+  GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
+  GLuint textures[NUM_TEXTURES];
 
-static Frame_State state = HOLDING;
-static double state_start_time = 0;
+  Frame_State state;
+  double state_start_time;
 
-static int colour_cycle = 0;
+  int colour_cycle;
+
+  Bool mipmap_p;
+  Bool waiting_for_image_p;
+  Bool first_image_p;
+
+} mirrorblobstruct;
+
+static mirrorblobstruct *Mirrorblob = NULL;
 
 /******************************************************************************
  *
@@ -321,30 +312,57 @@ reset_projection(int width, int height)
 
 /****************************************************************************
  *
- * Load a texture using the screen_to_texture function.
+ * Load a texture.
  */
-void
-grab_texture(ModeInfo *mi, int texture_index)
+
+static void
+image_loaded_cb (const char *filename, XRectangle *geometry,
+                 int image_width, int image_height, 
+                 int texture_width, int texture_height,
+                 void *closure)
 {
-    Bool mipmap_p = True;
-    int iw, ih, tw, th;
+  mirrorblobstruct *mp = (mirrorblobstruct *) closure;
+  GLint texid = -1;
+  int texture_index = -1;
+  int i;
+
+  glGetIntegerv (GL_TEXTURE_BINDING_2D, &texid);
+  if (texid < 0) abort();
+
+  for (i = 0; i < NUM_TEXTURES; i++) {
+    if (mp->textures[i] == texid) {
+      texture_index = i;
+      break;
+    }
+  }
+  if (texture_index < 0) abort();
 
-    glBindTexture (GL_TEXTURE_2D, textures[texture_index]);
+  mp->tex_width [texture_index] =  (GLfloat) image_width  / texture_width;
+  mp->tex_height[texture_index] = -(GLfloat) image_height / texture_height;
 
-    if (! screen_to_texture (mi->xgwa.screen, mi->window, 0, 0, mipmap_p,
-                             NULL, NULL, &iw, &ih, &tw, &th))
-      exit(1);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                   (mp->mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+  glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
-    tex_width [texture_index] =  (GLfloat) iw / tw;
-    tex_height[texture_index] = -(GLfloat) ih / th;
+  mp->waiting_for_image_p = False;
+  mp->first_image_p = True;
+}
 
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
-                     (mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
 
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+static void
+grab_texture(ModeInfo *mi, int texture_index)
+{
+  mirrorblobstruct *mp = &Mirrorblob[MI_SCREEN(mi)];
+
+  mp->waiting_for_image_p = True;
+  mp->mipmap_p = True;
+  load_texture_async (mi->xgwa.screen, mi->window,
+                      *mp->glx_context, 0, 0, mp->mipmap_p, 
+                      mp->textures[texture_index],
+                      image_loaded_cb, mp);
 }
 
 /******************************************************************************
@@ -354,6 +372,8 @@ grab_texture(ModeInfo *mi, int texture_index)
 static void
 initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
 {
+    mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
+
     GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
     /* Lighting values */
     GLfloat lightPos0[] = {500.0f, 100.0f, 200.0f, 1.0f };
@@ -447,9 +467,9 @@ initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
         glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
 
         glEnable (GL_TEXTURE_2D);
-        current_texture = 0;
-        glGenTextures (NUM_TEXTURES, textures);
-        grab_texture (mi, current_texture);
+        gp->current_texture = 0;
+        glGenTextures (NUM_TEXTURES, gp->textures);
+        grab_texture (mi, gp->current_texture);
 
         glEnableClientState (GL_TEXTURE_COORD_ARRAY);
     }
@@ -512,119 +532,119 @@ calculate_normal (Vector3D point1,
  * Return 0 on success.
  */
 static int
-initialise_blob(int width,
-                int height,
+initialise_blob(mirrorblobstruct *gp,
+                int width, int height,
                 int field_array_size)
 {
     /* Loop variables */
     int x, y, i;
     double xd;
 
-    colour_cycle = 0;
+    gp->colour_cycle = 0;
 
-    row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
-    if (!row_data)
+    gp->row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
+    if (!gp->row_data)
     {
         fprintf(stderr, "Couldn't allocate row data buffer\n");
         return -1;
     }
 
-    field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
-    if (!field_data)
+    gp->field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
+    if (!gp->field_data)
     {
         fprintf(stderr, "Couldn't allocate field data buffer\n");
         return -1;
     }
 
-    field = (double *)malloc(field_array_size * sizeof(double));
-    if (!field)
+    gp->field = (double *)malloc(field_array_size * sizeof(double));
+    if (!gp->field)
     {
         fprintf(stderr, "Couldn't allocate field buffer\n");
         return -1;
     }
 
-    wall_field = (double *)malloc(field_array_size * sizeof(double));
-    if (!wall_field)
+    gp->wall_field = (double *)malloc(field_array_size * sizeof(double));
+    if (!gp->wall_field)
     {
         fprintf(stderr, "Couldn't allocate wall field buffer\n");
         return -1;
     }
 
-    dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
-    if (!dots)
+    gp->dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
+    if (!gp->dots)
     {
         fprintf(stderr, "Couldn't allocate points buffer\n");
         return -1;
     }
-    glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) dots);
+    glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) gp->dots);
 
-    normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
-    if (!normals)
+    gp->normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
+    if (!gp->normals)
     {
         fprintf(stderr, "Couldn't allocate normals buffer\n");
         return -1;
     }
-    glNormalPointer (GL_DOUBLE, 0, (GLvoid *) normals);
+    glNormalPointer (GL_DOUBLE, 0, (GLvoid *) gp->normals);
 
     if (do_colour)
     {
-        colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
-        if (!colours)
+        gp->colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
+        if (!gp->colours)
         {
             fprintf(stderr, "Couldn't allocate colours buffer\n");
             return -1;
         }
-        glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colours);
+        glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) gp->colours);
     }
 
     if (do_texture)
     {
-        tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
+        gp->tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
                                         * sizeof(Vector2D));
-        if (!tex_coords)
+        if (!gp->tex_coords)
         {
             fprintf(stderr, "Couldn't allocate tex_coords buffer\n");
             return -1;
         }
-        glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) tex_coords);
+        glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) gp->tex_coords);
     }
 
-    num_points = 0;
+    gp->num_points = 0;
     /* Generate constant row data and count of total number of points */
     for (y = 0; y < y_resolution; y++)
     {
-        row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1))
+        gp->row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1))
                                 / (double)(y_resolution * y_resolution));
-        row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1))
+        gp->row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1))
                                 / (double)(y_resolution * y_resolution));
-        row_data[y].num_x_points = (int)(x_resolution * row_data[y].sinyd + 1.0);
-        num_points += row_data[y].num_x_points;
+        gp->row_data[y].num_x_points = (int)(x_resolution * gp->row_data[y].sinyd + 1.0);
+        gp->num_points += gp->row_data[y].num_x_points;
     }
 
     /* Initialise field data */
     for (i = 0; i < field_points; i++)
     {
-        field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
-        field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
-        field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5);
-
-        field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay)
-            * cos(PI *  field_data[i].ax);
-        field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay);
-        field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay)
-            * sin(PI *  field_data[i].ax);
-
-        field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
-        field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
-        field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5);
-
-        field_data[i].vx = 0.0;
-        field_data[i].vy = 0.0;
-        field_data[i].vpower = 0.0;
-
-        field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
-        field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
-        field_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
+        gp->field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+        gp->field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+        gp->field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5);
+
+        gp->field_data[i].pos.x = 1.5 * sin(PI * gp->field_data[i].ay)
+            * cos(PI *  gp->field_data[i].ax);
+        gp->field_data[i].pos.y = 1.5 * cos(PI * gp->field_data[i].ay);
+        gp->field_data[i].pos.z = 1.5 * sin(PI * gp->field_data[i].ay)
+            * sin(PI *  gp->field_data[i].ax);
+
+        gp->field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+        gp->field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+        gp->field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5);
+
+        gp->field_data[i].vx = 0.0;
+        gp->field_data[i].vy = 0.0;
+        gp->field_data[i].vpower = 0.0;
+
+        gp->field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
+        gp->field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
+        gp->field_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
     }
 
     /* Initialise lookup table of field strength */
@@ -633,29 +653,29 @@ initialise_blob(int width,
         xd = 2.0 * (((double)i / (double)field_array_size));
 
         xd = 3.0 * xd * xd * xd * xd;
-        field[i] = 0.4 / (field_points * (xd + 0.1));
+        gp->field[i] = 0.4 / (field_points * (xd + 0.1));
 
         xd = 10.0 * (((double)i / (double)field_array_size));
-        wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0);
+        gp->wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0);
     }
 
     for (y = 0; y < y_resolution; y++)
     {
-        for (x = 0; x < row_data[y].num_x_points; x++)
+        for (x = 0; x < gp->row_data[y].num_x_points; x++)
         {
             i = x + y * x_resolution;
-            xd = 2.0 * (((double)x / (double)row_data[y].num_x_points) - 0.5);
-
-            dots[i].x = row_data[y].sinyd * cos(PI * xd);
-            dots[i].y = row_data[y].cosyd;
-            dots[i].z = row_data[y].sinyd * sin(PI * xd);
-            normals[i].x = row_data[y].sinyd * cos(PI * xd);
-            normals[i].y = row_data[y].cosyd;
-            normals[i].z = row_data[y].sinyd * sin(PI * xd);
+            xd = 2.0 * (((double)x / (double)gp->row_data[y].num_x_points) - 0.5);
+
+            gp->dots[i].x = gp->row_data[y].sinyd * cos(PI * xd);
+            gp->dots[i].y = gp->row_data[y].cosyd;
+            gp->dots[i].z = gp->row_data[y].sinyd * sin(PI * xd);
+            gp->normals[i].x = gp->row_data[y].sinyd * cos(PI * xd);
+            gp->normals[i].y = gp->row_data[y].cosyd;
+            gp->normals[i].z = gp->row_data[y].sinyd * sin(PI * xd);
             if (do_texture)
             {
-                tex_coords[i].x = 2.0 - 2.0 * x / (float) row_data[y].num_x_points;
-                tex_coords[i].y = 1.0 - y / (float) y_resolution;
+                gp->tex_coords[i].x = 2.0 - 2.0 * x / (float) gp->row_data[y].num_x_points;
+                gp->tex_coords[i].y = 1.0 - y / (float) y_resolution;
             }
         }
     }
@@ -668,16 +688,12 @@ initialise_blob(int width,
  * Calculate the blob shape.
  */
 static void
-calc_blob(int width,
-          int height,
+calc_blob(mirrorblobstruct *gp,
+          int width, int height,
           int field_array_size,
           float limit,
           double fade)
 {
-    static double freak = 0.0;
-
-    static double v_freak = 0.0007;
-
     /* Loop variables */
     int x, y, i, index, index1, index2, index3;
     /* position of a point */
@@ -688,46 +704,46 @@ calc_blob(int width,
 
     /* Color components */
 
-    colour_cycle++;
+    gp->colour_cycle++;
 
     /* Update position and strength of points used to distort the blob */
     for (i = 0; i < field_points; i++)
     {
-        field_data[i].vx += field_data[i].mx*(field_data[i].cx - field_data[i].ax);
-        field_data[i].vy += field_data[i].my*(field_data[i].cy - field_data[i].ay);
-        field_data[i].vpower += field_data[i].mpower
-            * (field_data[i].cpower - field_data[i].apower);
-
-        field_data[i].ax += 0.1 * field_data[i].vx;
-        field_data[i].ay += 0.1 * field_data[i].vy;
-        field_data[i].apower += 0.1 * field_data[i].vpower;
-
-        field_data[i].pos.x = 1.0 * sin(PI * field_data[i].ay)
-            * cos(PI * field_data[i].ax);
-        field_data[i].pos.y = 1.0 * cos(PI * field_data[i].ay);
-        field_data[i].pos.z = 1.0 * sin(PI * field_data[i].ay)
-            * sin(PI * field_data[i].ax);
+        gp->field_data[i].vx += gp->field_data[i].mx*(gp->field_data[i].cx - gp->field_data[i].ax);
+        gp->field_data[i].vy += gp->field_data[i].my*(gp->field_data[i].cy - gp->field_data[i].ay);
+        gp->field_data[i].vpower += gp->field_data[i].mpower
+            * (gp->field_data[i].cpower - gp->field_data[i].apower);
+
+        gp->field_data[i].ax += 0.1 * gp->field_data[i].vx;
+        gp->field_data[i].ay += 0.1 * gp->field_data[i].vy;
+        gp->field_data[i].apower += 0.1 * gp->field_data[i].vpower;
+
+        gp->field_data[i].pos.x = 1.0 * sin(PI * gp->field_data[i].ay)
+            * cos(PI * gp->field_data[i].ax);
+        gp->field_data[i].pos.y = 1.0 * cos(PI * gp->field_data[i].ay);
+        gp->field_data[i].pos.z = 1.0 * sin(PI * gp->field_data[i].ay)
+            * sin(PI * gp->field_data[i].ax);
     }
 
-    blob_force.x = 0.0;
-    blob_force.y = 0.0;
-    blob_force.z = 0.0;
+    gp->blob_force.x = 0.0;
+    gp->blob_force.y = 0.0;
+    gp->blob_force.z = 0.0;
     for (y = 0; y < y_resolution; y++)
     {
-        for (x = 0; x < row_data[y].num_x_points; x++)
+        for (x = 0; x < gp->row_data[y].num_x_points; x++)
         {
             index = x + y * x_resolution;
-            xd = 2.0 * PI * (((double)x / (double)row_data[y].num_x_points) - 0.5);
+            xd = 2.0 * PI * (((double)x / (double)gp->row_data[y].num_x_points) - 0.5);
 
             radius = 1.0 + 0.0 * sin (xd * 10);
 
-            zd = radius * row_data[y].sinyd * sin(xd);
-            xd = radius * row_data[y].sinyd * cos(xd);
-            yd = radius * row_data[y].cosyd;
+            zd = radius * gp->row_data[y].sinyd * sin(xd);
+            xd = radius * gp->row_data[y].sinyd * cos(xd);
+            yd = radius * gp->row_data[y].cosyd;
 
-            normals[index].x = xd;
-            normals[index].y = yd;
-            normals[index].z = zd;
+            gp->normals[index].x = xd;
+            gp->normals[index].y = yd;
+            gp->normals[index].z = zd;
 
             offset_x = 0.0;
             offset_y = 0.0;
@@ -735,32 +751,32 @@ calc_blob(int width,
             strength = 0.0;
             for ( i = 0; i < field_points; i++)
             {
-                xdist = field_data[i].pos.x - xd;
-                ydist = field_data[i].pos.y - yd;
-                zdist = field_data[i].pos.z - zd;
+                xdist = gp->field_data[i].pos.x - xd;
+                ydist = gp->field_data[i].pos.y - yd;
+                zdist = gp->field_data[i].pos.z - zd;
                 dist = field_array_size * (xdist * xdist + ydist * ydist
                                            + zdist * zdist) * 0.1;
 
-                strength += PI * field_data[i].apower;
+                strength += PI * gp->field_data[i].apower;
 
                 if (dist < field_array_size)
                 {
-                    offset_x += xd * field_data[i].apower * field[dist];
-                    offset_y += yd * field_data[i].apower * field[dist];
-                    offset_z += zd * field_data[i].apower * field[dist];
+                    offset_x += xd * gp->field_data[i].apower * gp->field[dist];
+                    offset_y += yd * gp->field_data[i].apower * gp->field[dist];
+                    offset_z += zd * gp->field_data[i].apower * gp->field[dist];
 
-                    blob_force.x += 1.0 * xd * field_data[i].apower * field[dist];
-                    blob_force.y += 1.0 * yd * field_data[i].apower * field[dist];
-                    blob_force.z += 1.0 * zd * field_data[i].apower * field[dist];
+                    gp->blob_force.x += 1.0 * xd * gp->field_data[i].apower * gp->field[dist];
+                    gp->blob_force.y += 1.0 * yd * gp->field_data[i].apower * gp->field[dist];
+                    gp->blob_force.z += 1.0 * zd * gp->field_data[i].apower * gp->field[dist];
 
-                    strength *= 2.0 * field[dist];
+                    strength *= 2.0 * gp->field[dist];
                 }
 
                 if (incremental)
                 {
-                    xd += offset_x * freak * freak;
-                    yd += offset_y * freak * freak;
-                    zd += offset_z * freak * freak;
+                    xd += offset_x * gp->freak * gp->freak;
+                    yd += offset_y * gp->freak * gp->freak;
+                    zd += offset_z * gp->freak * gp->freak;
                 }
                 if (incremental == 1)
                 {
@@ -776,16 +792,16 @@ calc_blob(int width,
                 yd += offset_y;
                 zd += offset_z;
             }
-            xd += blob_center.x;
-            yd += blob_center.y;
-            zd += blob_center.z;
+            xd += gp->blob_center.x;
+            yd += gp->blob_center.y;
+            zd += gp->blob_center.z;
 
             if (do_colour)
             {
-                colours[index].red = 128 + (int)(sin(strength + colour_cycle * 0.01 + 2.0 * PI * x / row_data[y].num_x_points) * 127.0);
-                colours[index].green = 128 + (int)(cos(strength + colour_cycle * 0.025) * 127.0);
-                colours[index].blue = 128 + (int)(sin(strength + colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0);
-                colours[index].alpha = (int)(255.0 * fade);
+                gp->colours[index].red = 128 + (int)(sin(strength + gp->colour_cycle * 0.01 + 2.0 * PI * x / gp->row_data[y].num_x_points) * 127.0);
+                gp->colours[index].green = 128 + (int)(cos(strength + gp->colour_cycle * 0.025) * 127.0);
+                gp->colours[index].blue = 128 + (int)(sin(strength + gp->colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0);
+                gp->colours[index].alpha = (int)(255.0 * fade);
             }
 
             /* Add walls */
@@ -797,18 +813,18 @@ calc_blob(int width,
                 dist = field_array_size * (zd + limit) * (zd + limit) * 0.5;
                 if (dist < field_array_size)
                 {
-                    xd += (xd - blob_center.x) * wall_field[dist];
-                    yd += (yd - blob_center.y) * wall_field[dist];
-                    blob_force.z += (zd + limit);
+                    xd += (xd - gp->blob_center.x) * gp->wall_field[dist];
+                    yd += (yd - gp->blob_center.y) * gp->wall_field[dist];
+                    gp->blob_force.z += (zd + limit);
                 }
                 else
                 {
                     dist = field_array_size * (zd - limit) * (zd - limit) * 0.5;
                     if (dist < field_array_size)
                     {
-                        xd += (xd - blob_center.x) * wall_field[dist];
-                        yd += (yd - blob_center.y) * wall_field[dist];
-                        blob_force.z -= (zd - limit);
+                        xd += (xd - gp->blob_center.x) * gp->wall_field[dist];
+                        yd += (yd - gp->blob_center.y) * gp->wall_field[dist];
+                        gp->blob_force.z -= (zd - limit);
                     }
 
                     if (yd < -limit) yd = -limit;
@@ -817,18 +833,18 @@ calc_blob(int width,
                     dist = field_array_size * (yd + limit) * (yd + limit) * 0.5;
                     if (dist < field_array_size)
                     {
-                        xd += (xd - blob_center.x) * wall_field[dist];
-                        zd += (zd - blob_center.z) * wall_field[dist];
-                        blob_force.y += (yd + limit);
+                        xd += (xd - gp->blob_center.x) * gp->wall_field[dist];
+                        zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
+                        gp->blob_force.y += (yd + limit);
                     }
                     else
                     {
                         dist = field_array_size * (yd - limit) * (yd - limit) * 0.5;
                         if (dist < field_array_size)
                         {
-                            xd += (xd - blob_center.x) * wall_field[dist];
-                            zd += (zd - blob_center.z) * wall_field[dist];
-                            blob_force.y -= (yd - limit);
+                            xd += (xd - gp->blob_center.x) * gp->wall_field[dist];
+                            zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
+                            gp->blob_force.y -= (yd - limit);
                         }
                     }
 
@@ -838,18 +854,18 @@ calc_blob(int width,
                     dist = field_array_size * (xd + limit) * (xd + limit) * 0.5;
                     if (dist < field_array_size)
                     {
-                        yd += (yd - blob_center.y) * wall_field[dist];
-                        zd += (zd - blob_center.z) * wall_field[dist];
-                        blob_force.x += (xd + limit);
+                        yd += (yd - gp->blob_center.y) * gp->wall_field[dist];
+                        zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
+                        gp->blob_force.x += (xd + limit);
                     }
                     else
                     {
                         dist = field_array_size * (xd - limit) * (xd - limit) * 0.5;
                         if (dist < field_array_size)
                         {
-                            yd += (yd - blob_center.y) * wall_field[dist];
-                            zd += (zd - blob_center.z) * wall_field[dist];
-                            blob_force.x -= (xd - limit);
+                            yd += (yd - gp->blob_center.y) * gp->wall_field[dist];
+                            zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
+                            gp->blob_force.x -= (xd - limit);
                         }
                     }
 
@@ -858,9 +874,9 @@ calc_blob(int width,
                 }
             }
 
-            dots[index].x = xd;
-            dots[index].y = yd;
-            dots[index].z = zd;
+            gp->dots[index].x = xd;
+            gp->dots[index].y = yd;
+            gp->dots[index].z = zd;
         }
     }
 
@@ -876,34 +892,34 @@ calc_blob(int width,
     index1 = 0;
     index2 = y * x_resolution;
     index3 = 1 + y * x_resolution;
-    calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
+    calculate_normal (gp->dots[index1], gp->dots[index2], gp->dots[index3], &gp->normals[index1]);
     if (do_texture)
     {
         if (offset_texture)
         {
-            tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
-                * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
-            tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
-                * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
+            gp->tex_coords[index1].x = gp->dots[index1].x * 0.125 + 0.5
+                * (1.0 + 0.25 * asin(gp->normals[index1].x) / (0.5 * PI));
+            gp->tex_coords[index1].y = gp->dots[index1].y * 0.125 + 0.5
+                * (1.0 + 0.25 * asin(gp->normals[index1].y) / (0.5 * PI));
         }
         else
         {
-            tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
+            gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
                                                  / (0.5 * PI)));
-            tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
+            gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
                                                  / (0.5 * PI)));
         }
-        tex_coords[index1].x *= tex_width[current_texture];
-        tex_coords[index1].y *= tex_height[current_texture];
+        gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
+        gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
     }
 
     for (y = 1; y < y_resolution - 1; y++)
     {
-        if (row_data[y - 1].num_x_points)
+        if (gp->row_data[y - 1].num_x_points)
         {
-            for (x = 0; x < row_data[y].num_x_points; x++)
+            for (x = 0; x < gp->row_data[y].num_x_points; x++)
             {
-                if (x == row_data[y].num_x_points - 1)
+                if (x == gp->row_data[y].num_x_points - 1)
                 {
                     index1 = y * x_resolution;
                 }
@@ -912,28 +928,28 @@ calc_blob(int width,
                     index1 = x + 1 + y * x_resolution;
                 }
                 index2 = x + y * x_resolution;
-                index3 = ((x + 0.5) * row_data[y - 1].num_x_points
-                          / row_data[y].num_x_points) + (y - 1) * x_resolution;
-                calculate_normal (dots[index1], dots[index2], dots[index3],
-                                  &normals[index1]);
+                index3 = ((x + 0.5) * gp->row_data[y - 1].num_x_points
+                          / gp->row_data[y].num_x_points) + (y - 1) * x_resolution;
+                calculate_normal (gp->dots[index1], gp->dots[index2], gp->dots[index3],
+                                  &gp->normals[index1]);
                 if (do_texture)
                 {
                     if (offset_texture)
                     {
-                        tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
-                            * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
-                        tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
-                            * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
+                        gp->tex_coords[index1].x = gp->dots[index1].x * 0.125 + 0.5
+                            * (1.0 + 0.25 * asin(gp->normals[index1].x) / (0.5 * PI));
+                        gp->tex_coords[index1].y = gp->dots[index1].y * 0.125 + 0.5
+                            * (1.0 + 0.25 * asin(gp->normals[index1].y) / (0.5 * PI));
                     }
                     else
                     {
-                        tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
+                        gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
                                                              / (0.5 * PI)));
-                        tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
+                        gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
                                                              / (0.5 * PI)));
                     }
-                    tex_coords[index1].x *= tex_width[current_texture];
-                    tex_coords[index1].y *= tex_height[current_texture];
+                    gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
+                    gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
                 }
             }
         }
@@ -941,46 +957,46 @@ calc_blob(int width,
     index1 = (y_resolution - 1) * x_resolution;
     index2 = (y_resolution - 2) * x_resolution;
     index3 = 1 + (y_resolution - 2) * x_resolution;
-    calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
+    calculate_normal (gp->dots[index1], gp->dots[index2], gp->dots[index3], &gp->normals[index1]);
     if (do_texture)
     {
         if (offset_texture)
         {
-            tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
-                * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
-            tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
-                * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
+            gp->tex_coords[index1].x = gp->dots[index1].x * 0.125 + 0.5
+                * (1.0 + 0.25 * asin(gp->normals[index1].x) / (0.5 * PI));
+            gp->tex_coords[index1].y = gp->dots[index1].y * 0.125 + 0.5
+                * (1.0 + 0.25 * asin(gp->normals[index1].y) / (0.5 * PI));
         }
         else
         {
-            tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
+            gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
                                                  / (0.5 * PI)));
-            tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
+            gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
                                                  / (0.5 * PI)));
         }
-        tex_coords[index1].x *= tex_width[current_texture];
-        tex_coords[index1].y *= tex_height[current_texture];
+        gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
+        gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
     }
 
 
-    freak += v_freak;
-    v_freak += -freak / 2000000.0;
+    gp->freak += gp->v_freak;
+    gp->v_freak += -gp->freak / 2000000.0;
 
     /* Update the center of the whole blob */
-    blob_velocity.x += (blob_anchor.x - blob_center.x) / 80.0
-        + 0.01 * blob_force.x / num_points;
-    blob_velocity.y += (blob_anchor.y - blob_center.y) / 80.0
-        + 0.01 * blob_force.y / num_points;
-    blob_velocity.z += (blob_anchor.z - blob_center.z) / 80.0
-        + 0.01 * blob_force.z / num_points;
-
-    blob_center.x += blob_velocity.x * 0.5;
-    blob_center.y += blob_velocity.y * 0.5;
-    blob_center.z += blob_velocity.z * 0.5;
-
-    blob_velocity.x *= 0.99;
-    blob_velocity.y *= 0.99;
-    blob_velocity.z *= 0.99;
+    gp->blob_velocity.x += (gp->blob_anchor.x - gp->blob_center.x) / 80.0
+        + 0.01 * gp->blob_force.x / gp->num_points;
+    gp->blob_velocity.y += (gp->blob_anchor.y - gp->blob_center.y) / 80.0
+        + 0.01 * gp->blob_force.y / gp->num_points;
+    gp->blob_velocity.z += (gp->blob_anchor.z - gp->blob_center.z) / 80.0
+        + 0.01 * gp->blob_force.z / gp->num_points;
+
+    gp->blob_center.x += gp->blob_velocity.x * 0.5;
+    gp->blob_center.y += gp->blob_velocity.y * 0.5;
+    gp->blob_center.z += gp->blob_velocity.z * 0.5;
+
+    gp->blob_velocity.x *= 0.99;
+    gp->blob_velocity.y *= 0.99;
+    gp->blob_velocity.z *= 0.99;
 }
 
 /******************************************************************************
@@ -993,7 +1009,7 @@ calc_blob(int width,
  * with the more interesting bits of the code.
  */
 static void
-draw_blob (void)
+draw_blob (mirrorblobstruct *gp)
 {
     int x, y, x2, x3;
     int index1, index2, index3;
@@ -1007,12 +1023,12 @@ draw_blob (void)
 
     for (y = 1; y < y_resolution; y++)
     {
-        if (row_data[y - 1].num_x_points)
+        if (gp->row_data[y - 1].num_x_points)
         {
-            for (x = 0; x < row_data[y].num_x_points; x++)
+            for (x = 0; x < gp->row_data[y].num_x_points; x++)
             {
                 glBegin (GL_TRIANGLES);
-                if (x == row_data[y].num_x_points - 1)
+                if (x == gp->row_data[y].num_x_points - 1)
                 {
                     index1 = y * x_resolution;
                 }
@@ -1021,17 +1037,17 @@ draw_blob (void)
                     index1 = x + 1 + y * x_resolution;
                 }
                 index2 = x + y * x_resolution;
-                index3 = ((x + 0.5) * row_data[y - 1].num_x_points
-                          / row_data[y].num_x_points) + (y - 1) * x_resolution;
+                index3 = ((x + 0.5) * gp->row_data[y - 1].num_x_points
+                          / gp->row_data[y].num_x_points) + (y - 1) * x_resolution;
                 glArrayElement(index1);
                 glArrayElement(index2);
                 glArrayElement(index3);
                 glEnd();
 
-                lower = ((x - 0.5) * row_data[y - 1].num_x_points
-                         / (float)row_data[y].num_x_points);
-                upper = ((x + 0.5) * row_data[y - 1].num_x_points
-                         / (float)row_data[y].num_x_points);
+                lower = ((x - 0.5) * gp->row_data[y - 1].num_x_points
+                         / (float)gp->row_data[y].num_x_points);
+                upper = ((x + 0.5) * gp->row_data[y - 1].num_x_points
+                         / (float)gp->row_data[y].num_x_points);
 
                 if (upper > lower)
                 {
@@ -1041,17 +1057,17 @@ draw_blob (void)
                     for (x2 = lower; x2 <= upper; x2++)
                     {
                         x3 = x2;
-                        while (x3 < 0) x3 += row_data[y - 1].num_x_points;
-                        while (x3 >= row_data[y - 1].num_x_points)
-                            x3 -= row_data[y - 1].num_x_points;
+                        while (x3 < 0) x3 += gp->row_data[y - 1].num_x_points;
+                        while (x3 >= gp->row_data[y - 1].num_x_points)
+                            x3 -= gp->row_data[y - 1].num_x_points;
                         index2 = x3 + (y - 1) * x_resolution;
 
                         if (x2 < upper)
                         {
                             x3 = x2 + 1;
-                            while (x3 < 0) x3 += row_data[y - 1].num_x_points;
-                            while (x3 >= row_data[y - 1].num_x_points)
-                                x3 -= row_data[y - 1].num_x_points;
+                            while (x3 < 0) x3 += gp->row_data[y - 1].num_x_points;
+                            while (x3 >= gp->row_data[y - 1].num_x_points)
+                                x3 -= gp->row_data[y - 1].num_x_points;
                             index3 = x3 + (y - 1) * x_resolution;
                             if (x2 == lower)
                             {
@@ -1074,6 +1090,8 @@ draw_blob (void)
 static void
 draw_background (ModeInfo *mi)
 {
+    mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
+
     glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
     glEnable (GL_TEXTURE_2D);
     glDisable(GL_LIGHTING);
@@ -1092,13 +1110,13 @@ draw_background (ModeInfo *mi)
     glTexCoord2f (0.0, 0.0);
     glVertex2i (0, 0);
     
-    glTexCoord2f (0.0, -tex_height[current_texture]);
+    glTexCoord2f (0.0, -gp->tex_height[gp->current_texture]);
     glVertex2i (0, MI_HEIGHT(mi));
 
-    glTexCoord2f (tex_width[current_texture], -tex_height[current_texture]);
+    glTexCoord2f (gp->tex_width[gp->current_texture], -gp->tex_height[gp->current_texture]);
     glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
 
-    glTexCoord2f (tex_width[current_texture], 0.0);
+    glTexCoord2f (gp->tex_width[gp->current_texture], 0.0);
     glVertex2i (MI_WIDTH(mi), 0);
     glEnd();
 
@@ -1114,6 +1132,8 @@ draw_background (ModeInfo *mi)
 static GLvoid
 draw_scene(ModeInfo * mi)
 {
+    mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
+
     double fade = 0.0;
     double current_time;
     check_gl_error ("draw_scene");
@@ -1121,10 +1141,10 @@ draw_scene(ModeInfo * mi)
     glColor4d(1.0, 1.0, 1.0, 1.0);
 
     current_time = double_time();
-    switch (state)
+    switch (gp->state)
     {
     case TRANSITIONING:
-        fade = (current_time - state_start_time) / fade_time;
+        fade = (current_time - gp->state_start_time) / fade_time;
         break;
 
     case HOLDING:
@@ -1139,7 +1159,7 @@ draw_scene(ModeInfo * mi)
      */
     if (do_texture)
     {
-      glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
+      glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
     }
 
     if (do_paint_background && !do_wire)
@@ -1155,18 +1175,18 @@ draw_scene(ModeInfo * mi)
         /* When transitioning between two images paint the new image over the old
          * image with a varying alpha value to get a smooth fade.
          */
-        if (state == TRANSITIONING)
+        if (gp->state == TRANSITIONING)
         {
             glDisable (GL_DEPTH_TEST);
             glEnable (GL_BLEND);
             /* Select the texture to transition to */
-            glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
+            glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
             glColor4d (1.0, 1.0, 1.0, fade);
 
             draw_background (mi);
 
             /* Select the original texture to draw the blob */
-            glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
+            glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
             glEnable (GL_DEPTH_TEST);
         }
         /* Clear the depth buffer bit so the backgound is behind the blob */
@@ -1196,7 +1216,7 @@ draw_scene(ModeInfo * mi)
         fade = fade * 0.5;
     }
 
-    calc_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5, fade);
+    calc_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5, fade);
 
     glEnable(GL_LIGHTING);
     glEnable(GL_LIGHT0);
@@ -1219,7 +1239,7 @@ draw_scene(ModeInfo * mi)
         glDisable (GL_BLEND);
         glColor4d (1.0, 1.0, 1.0, 1.0);
     }
-    draw_blob();
+    draw_blob(gp);
 
     if (do_blend && do_colour)
     {
@@ -1232,34 +1252,34 @@ draw_scene(ModeInfo * mi)
      */
     if (do_texture && (hold_time > 0))
     {
-        switch (state)
+        switch (gp->state)
         {
         case TRANSITIONING:
             glClear(GL_DEPTH_BUFFER_BIT);
             glEnable (GL_BLEND);
             /* Select the texture to transition to */
-            glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
+            glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
             glColor4d (1.0, 1.0, 1.0, fade);
-            draw_blob ();
+            draw_blob (gp);
 
-            if ((current_time - state_start_time) > fade_time)
+            if ((current_time - gp->state_start_time) > fade_time)
             {
-                state = HOLDING;
-                state_start_time = current_time;
-                current_texture = 1 - current_texture;
+                gp->state = HOLDING;
+                gp->state_start_time = current_time;
+                gp->current_texture = 1 - gp->current_texture;
             }
             break;
 
         case HOLDING:
-            if ((current_time - state_start_time) > hold_time)
+            if ((current_time - gp->state_start_time) > hold_time)
             {
-                grab_texture (mi, 1 - current_texture);
-                state = TRANSITIONING;
+                grab_texture (mi, 1 - gp->current_texture);
+                gp->state = TRANSITIONING;
                 /* Get the time again rather than using the current time so
                  * that the time taken by the grab_texture function is not part
                  * of the fade time
                  */
-                state_start_time = double_time();
+                gp->state_start_time = double_time();
             }
             break;
         }
@@ -1268,18 +1288,23 @@ draw_scene(ModeInfo * mi)
 
 /******************************************************************************
  *
- * XScreensaver screen update entry
+ * XMirrorblob screen update entry
  */
-void
-draw_screensaver(ModeInfo * mi)
+ENTRYPOINT void
+draw_mirrorblob(ModeInfo * mi)
 {
-    screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
+    mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
     Display    *display = MI_DISPLAY(mi);
     Window      window = MI_WINDOW(mi);
 
     if (!gp->glx_context)
         return;
 
+    /* Wait for the first image; for subsequent images, load them in the
+       background while animating. */
+    if (gp->waiting_for_image_p && gp->first_image_p)
+      return;
+
     glXMakeCurrent(display, window, *(gp->glx_context));
     draw_scene(mi);
     if (mi->fps_p) do_fps (mi);
@@ -1288,10 +1313,10 @@ draw_screensaver(ModeInfo * mi)
 
 /******************************************************************************
  *
- * XScreensaver screen resize entry
+ * XMirrorblob screen resize entry
  */
-void
-reshape_screensaver(ModeInfo *mi, int width, int height)
+ENTRYPOINT void
+reshape_mirrorblob(ModeInfo *mi, int width, int height)
 {
     glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
     reset_projection(width, height);
@@ -1299,29 +1324,29 @@ reshape_screensaver(ModeInfo *mi, int width, int height)
 
 /******************************************************************************
  *
- * XScreensaver initialise entry
+ * XMirrorblob initialise entry
  */
-void
-init_screensaver(ModeInfo * mi)
+ENTRYPOINT void
+init_mirrorblob(ModeInfo * mi)
 {
     int screen = MI_SCREEN(mi);
 
-    screensaverstruct *gp;
+    mirrorblobstruct *gp;
 
-    if (Screensaver == NULL)
+    if (Mirrorblob == NULL)
     {
-        if ((Screensaver = (screensaverstruct *)
-             calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
+        if ((Mirrorblob = (mirrorblobstruct *)
+             calloc(MI_NUM_SCREENS(mi), sizeof (mirrorblobstruct))) == NULL)
         {
             return;
         }
     }
-    gp = &Screensaver[screen];
+    gp = &Mirrorblob[screen];
 
     gp->window = MI_WINDOW(mi);
     if ((gp->glx_context = init_GL(mi)) != NULL)
     {
-        reshape_screensaver(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+        reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
         initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
     }
     else
@@ -1329,30 +1354,40 @@ init_screensaver(ModeInfo * mi)
         MI_CLEARWINDOW(mi);
     }
 
-    initialise_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
-    state_start_time = double_time();
+    initialise_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
+    gp->state_start_time = double_time();
+
+    gp->freak = 0.0;
+    gp->v_freak = 0.0007;
+    gp->first_image_p = True;
 }
 
 /******************************************************************************
  *
- * XScreensaver cleanup entry
+ * XMirrorblob cleanup entry
  */
-void
-release_screensaver(ModeInfo * mi)
+ENTRYPOINT void
+release_mirrorblob(ModeInfo * mi)
 {
-    if (row_data) free(row_data);
-    if (field_data) free(field_data);
-    if (colours) free(colours);
-    if (tex_coords) free(tex_coords);
-    if (dots) free(dots);
-    if (wall_field) free(wall_field);
-    if (field) free(field);
-
-    if (Screensaver != NULL)
-    {
-        (void) free((void *) Screensaver);
-        Screensaver = NULL;
+  if (Mirrorblob != NULL) {
+    int i;
+    for (i = 0; i < MI_NUM_SCREENS(mi); i++) {
+      mirrorblobstruct *gp = &Mirrorblob[i];
+      if (gp->row_data) free(gp->row_data);
+      if (gp->field_data) free(gp->field_data);
+      if (gp->colours) free(gp->colours);
+      if (gp->tex_coords) free(gp->tex_coords);
+      if (gp->dots) free(gp->dots);
+      if (gp->wall_field) free(gp->wall_field);
+      if (gp->field) free(gp->field);
     }
-    FreeAllGL(mi);
+
+    free(Mirrorblob);
+    Mirrorblob = NULL;
+  }
+  FreeAllGL(mi);
 }
+
+XSCREENSAVER_MODULE ("MirrorBlob", mirrorblob)
+
 #endif