http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / glx / gflux.c
index 2286200242d4c14ea11cdafbe7e883aabd97e305..bbe655f8ca36d89dcc29c599927d2ca230480a2d 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- emacs friendly */
-
 /* gflux - creates a fluctuating 3D grid 
  * requires OpenGL or MesaGL
  * 
- * Copyright (c) Josiah Pease, 2000
+ * Copyright (c) Josiah Pease, 2000, 2003
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * the above copyright notice appear in all copies and that both that
  * 04 July 2000 : majour bug hunt, xscreensaver code rewritten
  * 08 July 2000 : texture mapping, rotation and zoom added
  * 21 July 2000 : cleaned up code from bug hunts, manpage written
+ * 24 November 2000 : fixed x co-ord calculation in solid - textured
+ * 05 March 2001 : put back non pnmlib code with #ifdefs
+ * 11 May 2002 : fixed image problems with large images
  */
 
 
-/*-
- * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
- * otherwise caddr_t is not defined correctly
- */
-
-#include <X11/Intrinsic.h>
-
-
 #ifdef STANDALONE
 # define PROGCLASS                                             "gflux"
 # define HACK_INIT                                             init_gflux
 # define HACK_DRAW                                             draw_gflux
-# define gflux_opts                            xlockmore_opts
-#define DEFAULTS                        "*squares:      19      \n" \
-                                        "*resolution:   0       \n" \
-                                        "*draw:         0       \n" \
+# define HACK_RESHAPE                                  reshape_gflux
+# define HACK_HANDLE_EVENT                             gflux_handle_event
+# define EVENT_MASK                                            PointerMotionMask
+# define gflux_opts                                            xlockmore_opts
+#define DEFAULTS                        "*delay:               20000   \n" \
+                                                                               "*showFPS:      False   \n" \
+                                        "*mode:         light" "\n" \
+                                        "*squares:      19      \n" \
+                                                                               "*resolution:   0       \n" \
                                         "*flat:         0       \n" \
                                         "*speed:        0.05    \n" \
                                         "*rotationx:    0.01    \n" \
                                         "*rotationz:    0.1     \n" \
                                         "*waves:        3       \n" \
                                         "*waveChange:   50      \n" \
-                                        "*waveHeight:   1.0     \n" \
-                                        "*waveFreq:    3.0     \n" \
-                                        "*zoom:         1.0     \n" 
+                                        "*waveHeight:  0.8      \n" \
+                                        "*waveFreq:    3.0      \n" \
+                                        "*zoom:        1.0      \n" \
+                                        "*useSHM:      True     \n" 
 
 
 # include "xlockmore.h"                                /* from the xscreensaver distribution */
 
 #ifdef USE_GL /* whole file */
 
-#ifdef HAVE_XPM
-# include <X11/xpm.h>
-# ifndef PIXEL_ALREADY_TYPEDEFED
-# define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
-# endif
-#endif
-
 #ifdef HAVE_XMU
 # ifndef VMS
 #  include <X11/Xmu/Drawing.h>
 
 #include <math.h>
 
-static enum {wire=0,solid,light,checker,textured} _draw; /* draw style */
+#include "grab-ximage.h"
+#include "gltrackball.h"
+
+
+static enum {wire=0,solid,light,checker,grab} _draw; /* draw style */
 static int _squares = 19;                                 /* grid size */
 static int _resolution = 4;                    /* wireframe resolution */
 static int _flat = 0;
@@ -115,40 +112,44 @@ static int _waveChange = 50;
 static float _waveHeight = 1.0;
 static float _waveFreq = 3.0;
 
+static trackball_state *trackball;
+static Bool button_down_p = False;
+
 #define WIDTH 320
 #define HEIGHT 240
 
 static XrmOptionDescRec opts[] = {
-    {"-squares", ".gflux.squares", XrmoptionSepArg, (caddr_t) NULL},
-    {"-resolution", ".gflux.resolution", XrmoptionSepArg, (caddr_t) NULL},
-    {"-draw", ".gflux.draw", XrmoptionSepArg, (caddr_t) NULL},
-    {"-flat", ".gflux.flat", XrmoptionSepArg, (caddr_t) NULL},
-    {"-speed", ".gflux.speed", XrmoptionSepArg, (caddr_t) NULL},
-    {"-rotationx", ".gflux.rotationx", XrmoptionSepArg, (caddr_t) NULL},
-    {"-rotationy", ".gflux.rotationy", XrmoptionSepArg, (caddr_t) NULL},
-    {"-rotationz", ".gflux.rotationz", XrmoptionSepArg, (caddr_t) NULL},
-    {"-waves", ".gflux.waves", XrmoptionSepArg, (caddr_t) NULL},
-    {"-waveChange", ".gflux.waveChange", XrmoptionSepArg, (caddr_t) NULL},
-    {"-waveHeight", ".gflux.waveHeight", XrmoptionSepArg, (caddr_t) NULL},
-    {"-waveFreq", ".gflux.waveFreq", XrmoptionSepArg, (caddr_t) NULL},
-    {"-zoom", ".gflux.zoom", XrmoptionSepArg, (caddr_t) NULL},
+    {"-squares", ".gflux.squares", XrmoptionSepArg, 0},
+    {"-resolution", ".gflux.resolution", XrmoptionSepArg, 0},
+/*    {"-draw", ".gflux.draw", XrmoptionSepArg, 0},*/
+    {"-mode", ".gflux.mode", XrmoptionSepArg, 0},
+    {"-flat", ".gflux.flat", XrmoptionSepArg, 0},
+    {"-speed", ".gflux.speed", XrmoptionSepArg, 0},
+    {"-rotationx", ".gflux.rotationx", XrmoptionSepArg, 0},
+    {"-rotationy", ".gflux.rotationy", XrmoptionSepArg, 0},
+    {"-rotationz", ".gflux.rotationz", XrmoptionSepArg, 0},
+    {"-waves", ".gflux.waves", XrmoptionSepArg, 0},
+    {"-waveChange", ".gflux.waveChange", XrmoptionSepArg, 0},
+    {"-waveHeight", ".gflux.waveHeight", XrmoptionSepArg, 0},
+    {"-waveFreq", ".gflux.waveFreq", XrmoptionSepArg, 0},
+    {"-zoom", ".gflux.zoom", XrmoptionSepArg, 0},
 };
 
 
 static argtype vars[] = {
-    {(caddr_t *) & _squares, "squares", "Squares", "19", t_Int},
-    {(caddr_t *) & _resolution, "resolution", "Resolution", "4", t_Int},
-    {(caddr_t *) & _draw, "draw", "Draw", "0", t_Int},
-    {(caddr_t *) & _flat, "flat", "Flat", "0", t_Int},
-    {(caddr_t *) & _speed, "speed", "Speed", "0.05", t_Float},
-    {(caddr_t *) & _rotationx, "rotationx", "Rotationx", "0.01", t_Float},
-    {(caddr_t *) & _rotationy, "rotationy", "Rotationy", "0.0", t_Float},
-    {(caddr_t *) & _rotationz, "rotationz", "Rotationz", "0.1", t_Float},
-    {(caddr_t *) & _waves, "waves", "Waves", "3", t_Int},
-    {(caddr_t *) & _waveChange, "waveChange", "WaveChange", "50", t_Int},
-    {(caddr_t *) & _waveHeight, "waveHeight", "WaveHeight", "1.0", t_Float},
-    {(caddr_t *) & _waveFreq, "waveFreq", "WaveFreq", "3.0", t_Float},
-    {(caddr_t *) & _zoom, "zoom", "Zoom", "1.0", t_Float},
+    {&_squares, "squares", "Squares", "19", t_Int},
+    {&_resolution, "resolution", "Resolution", "4", t_Int},
+/*    {&_draw, "draw", "Draw", "2", t_Int},*/
+    {&_flat, "flat", "Flat", "0", t_Int},
+    {&_speed, "speed", "Speed", "0.05", t_Float},
+    {&_rotationx, "rotationx", "Rotationx", "0.01", t_Float},
+    {&_rotationy, "rotationy", "Rotationy", "0.0", t_Float},
+    {&_rotationz, "rotationz", "Rotationz", "0.1", t_Float},
+    {&_waves, "waves", "Waves", "3", t_Int},
+    {&_waveChange, "waveChange", "WaveChange", "50", t_Int},
+    {&_waveHeight, "waveHeight", "WaveHeight", "1.0", t_Float},
+    {&_waveFreq, "waveFreq", "WaveFreq", "3.0", t_Float},
+    {&_zoom, "zoom", "Zoom", "1.0", t_Float},
 };
 
 
@@ -156,7 +157,7 @@ static OptionStruct desc[] =
 {
     {"-squares num", "size of grid in squares (19)"},
     {"-resolution num", "detail of lines making grid, wireframe only (4)"},
-    {"-draw num", "draw method to use: 0=wireframe 1=solid 2=lit (0)"},
+/*    {"-draw num", "draw method to use: 0=wireframe 1=solid 2=lit (0)"},*/
     {"-flat num", "shading method, not wireframe: 0=smooth 1=flat (0)"},
     {"-speed num", "speed of waves (0.05)"},
     {"-rotationx num", "speed of xrotation (0.01)"},
@@ -181,6 +182,7 @@ ModStruct   gflux_description =
 
 /* structure for holding the gflux data */
 typedef struct {
+    ModeInfo *modeinfo;
     int screen_width, screen_height;
     GLXContext *glx_context;
     Window window;
@@ -191,18 +193,18 @@ typedef struct {
     double dispy[MAXWAVES];
     double dispx[MAXWAVES];
     GLfloat colour[3];
-    int imageWidth;
-    int imageHeight;
-    GLubyte *image;
-    GLint texName;
+    GLuint texName;
+    GLfloat tex_xscale;
+    GLfloat tex_yscale;
+    XRectangle img_geom;
+    int img_width, img_height;
     void (*drawFunc)(void);
 } gfluxstruct;
 static gfluxstruct *gflux = NULL;
 
 /* prototypes */
 void initLighting(void);
-void initTexture(void);
-void loadTexture(void);
+void grabTexture(void);
 void createTexture(void);
 void displaySolid(void);            /* drawFunc implementations */
 void displayLight(void);
@@ -224,6 +226,51 @@ double getGrid(double,double,double);
 /* BEGINNING OF FUNCTIONS */
 
 
+Bool
+gflux_handle_event (ModeInfo *mi, XEvent *event)
+{
+  if (event->xany.type == ButtonPress &&
+      event->xbutton.button == Button1)
+    {
+      button_down_p = True;
+      gltrackball_start (trackball,
+                         event->xbutton.x, event->xbutton.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+  else if (event->xany.type == ButtonRelease &&
+           event->xbutton.button == Button1)
+    {
+      button_down_p = False;
+      return True;
+    }
+  else if (event->xany.type == ButtonPress &&
+           (event->xbutton.button == Button4 ||
+            event->xbutton.button == Button5))
+    {
+      gltrackball_mousewheel (trackball, event->xbutton.button, 10,
+                              !!event->xbutton.state);
+      return True;
+    }
+  else if (event->xany.type == MotionNotify &&
+           button_down_p)
+    {
+      gltrackball_track (trackball,
+                         event->xmotion.x, event->xmotion.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+
+  return False;
+}
+
+
+static void
+userRot(void)
+{
+  gltrackball_rotate (trackball);
+}
+
 /* draw the gflux once */
 void draw_gflux(ModeInfo * mi)
 {
@@ -237,6 +284,7 @@ void draw_gflux(ModeInfo * mi)
 
     calcGrid();
     gflux->drawFunc();
+    if (mi->fps_p) do_fps (mi);
     glXSwapBuffers(display, window);
 }
 
@@ -252,8 +300,8 @@ void resetProjection(void) {
 }
 
 /* Standard reshape function */
-static void
-reshape(int width, int height)
+void
+reshape_gflux(ModeInfo *mi, int width, int height)
 {
     glViewport( 0, 0, width, height );
     resetProjection();
@@ -261,10 +309,14 @@ reshape(int width, int height)
 
 
 /* main OpenGL initialization routine */
-void initializeGL(GLsizei width, GLsizei height) 
+void initializeGL(ModeInfo *mi, GLsizei width, GLsizei height) 
 {
-  reshape(width, height);
+  reshape_gflux(mi, width, height);
   glViewport( 0, 0, width, height ); 
+
+  gflux->tex_xscale = 1.0;  /* maybe changed later */
+  gflux->tex_yscale = 1.0;
+
   switch(_draw) {
     case solid :
       gflux->drawFunc = (displaySolid);
@@ -279,14 +331,12 @@ void initializeGL(GLsizei width, GLsizei height)
       gflux->drawFunc = (displayTexture);
       glEnable(GL_DEPTH_TEST);
       createTexture();
-      initTexture();
       initLighting();
     break;
-       case textured :
+       case grab :
       gflux->drawFunc = (displayTexture);
       glEnable(GL_DEPTH_TEST);
-      loadTexture();
-      initTexture();
+      grabTexture();
       initLighting();
     break;
     case wire :
@@ -315,10 +365,31 @@ void init_gflux(ModeInfo * mi)
     }
     gp = &gflux[screen];
 
+    trackball = gltrackball_init ();
+
+    {
+      char *s = get_string_resource ("mode", "Mode");
+      if (!s || !*s)                       _draw = wire;
+      else if (!strcasecmp (s, "wire"))    _draw = wire;
+      else if (!strcasecmp (s, "solid"))   _draw = solid;
+      else if (!strcasecmp (s, "light"))   _draw = light;
+      else if (!strcasecmp (s, "checker")) _draw = checker;
+      else if (!strcasecmp (s, "grab"))    _draw = grab;
+      else
+        {
+          fprintf (stderr,
+                   "%s: mode must be one of: wire, solid, "
+                   "light, checker, or grab; not \"%s\"\n",
+                   progname, s);
+          exit (1);
+        }
+    }
+
+    gp->modeinfo = mi;
     gp->window = MI_WINDOW(mi);
     if ((gp->glx_context = init_GL(mi)) != NULL) {
-        reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
-        initializeGL(MI_WIDTH(mi), MI_HEIGHT(mi));
+        reshape_gflux(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+        initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
     } else {
         MI_CLEARWINDOW(mi);
     }
@@ -327,7 +398,6 @@ void init_gflux(ModeInfo * mi)
 /* cleanup code */
 void release_gflux(ModeInfo * mi)
 {
-    if(gflux->image!=NULL) free(gflux->image);
     if(gflux->glx_context!=NULL) free(gflux->glx_context);
     if (gflux != NULL) {
         (void) free((void *) gflux);
@@ -336,124 +406,58 @@ void release_gflux(ModeInfo * mi)
     FreeAllGL(mi);
 }
 
-/* loads several different types of PNM image from stdin */
-#define presult(A,B,C) (*(result+(A)*(gflux->imageWidth)*4+(B)*4+(C)))
-void loadTexture(void)
-{
-    int i, j, levels, width, height;
-    int red,green,blue;
-    char s[4];
-    int ppmType=0;
-    FILE *file = stdin;
-    GLubyte *result;
-
-    fgets(s,4,file);
-
-    if(!strncmp(s,"P6",2)) ppmType=6;
-    if(!strncmp(s,"P5",2)) ppmType=5;
-    if(!strncmp(s,"P3",2)) ppmType=3;
-    if(!strncmp(s,"P2",2)) ppmType=2;
-    if(!ppmType)exit(1);
-
-    while((i=getc(file))=='#')
-    {
-        while(getc(file)!='\n');
-    }
-    ungetc(i,file);
-
-    fscanf(file,"%d %d %d",&width,&height,&levels);
-
-    result = malloc(sizeof(GLubyte)*4*width*height);
-    gflux->imageWidth = width;
-       gflux->imageHeight = height;
-
-    switch(ppmType) {
-        case 2 :    /* ASCII grey */
-            for(i=0;i<height;i++) {
-                for(j=0;j<width;j++) {
-                    fscanf(file,"%d",&red);
-                    presult(j,i,0) = red;
-                    presult(j,i,1) = red;
-                    presult(j,i,2) = red;
-                }
-            }
-            break;
-        case 3 :    /* ASCII rgb */
-            for(i=0;i<height;i++) {
-                for(j=0;j<width;j++) {
-                    fscanf(file,"%d %d %d",&red,&green,&blue);
-                    presult(j,i,0) = red;
-                    presult(j,i,1) = green;
-                    presult(j,i,2) = blue;
-                }
-            }
-            break;
-        case 5 :    /* Binary grey */
-            getc(file); /* seems nessessary */
-            for(i=0;i<height;i++) {
-                for(j=0;j<width;j++) {
-                    red = getc(file);
-                    presult(j,i,0) = red;
-                    presult(j,i,1) = red;
-                    presult(j,i,2) = red;
-                }
-            }
-        break;
-        case 6 :    /* Binary rgb */
-            getc(file); /* seems nessessary */
-            for(i=0;i<height;i++) {
-                for(j=0;j<width;j++) {
-                    red = getc(file);
-                    green = getc(file);
-                    blue = getc(file);
-                    presult(j,i,0) = red;
-                    presult(j,i,1) = green;
-                    presult(j,i,2) = blue;
-                }
-            }
-        break;
-    }
-       gflux->image = result;
-}
 
-/* creates an image for texture mapping */
+
 void createTexture(void)
 {
-    int i,j,c;
-    GLubyte *result;
-
-       gflux->imageHeight = gflux->imageWidth = 8;
-
-       result = malloc(sizeof(GLubyte)*4*gflux->imageHeight*gflux->imageWidth);
-    for(i=0;i<gflux->imageHeight;i++) {
-        for(j=0;j<gflux->imageWidth;j++) {
-            c = (((i)%2 ^ (j)%2) ? 100 : 200 );
-            presult(i,j,0) = (GLubyte) c;
-            presult(i,j,1) = (GLubyte) c;
-            presult(i,j,2) = (GLubyte) c;
-            presult(i,j,3) = (GLubyte) 255;
-        }
-    }
-       gflux->image = result;
+  int size = 4;
+  unsigned int data[] = { 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA,
+                          0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF,
+                          0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA,
+                          0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF };
+
+  gflux->tex_xscale = size;
+  gflux->tex_yscale = size;
+
+  glGenTextures (1, &gflux->texName);
+  glBindTexture (GL_TEXTURE_2D, gflux->texName);
+
+  glTexImage2D (GL_TEXTURE_2D, 0, 3, size, size, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 }
-#undef presult
 
-/* specifies image as texture */    
-void initTexture(void)
+
+void
+grabTexture(void)
 {
-       glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-       glGenTextures(1, &gflux->texName);
-       glBindTexture(GL_TEXTURE_2D, gflux->texName);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    clear_gl_error();
-       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gflux->imageWidth,
-                       gflux->imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, gflux->image);
-    check_gl_error("texture");
+  Bool mipmap_p = True;
+  int iw, ih, tw, th;
+
+  if (MI_IS_WIREFRAME(gflux->modeinfo))
+    return;
+
+  if (! screen_to_texture (gflux->modeinfo->xgwa.screen,
+                           gflux->modeinfo->window, 0, 0, mipmap_p,
+                           NULL, &gflux->img_geom, &iw, &ih, &tw, &th))
+    exit (1);
+
+  gflux->tex_xscale =  (GLfloat) iw / tw;
+  gflux->tex_yscale = -(GLfloat) ih / th;
+  gflux->img_width  = iw;
+  gflux->img_height = ih;
+   
+  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));
 }
 
+
 void initLighting(void)
 {
     static float ambientA[] = {0.0, 0.0, 0.0, 1.0};
@@ -502,6 +506,37 @@ void displayTexture(void)
     double dx = 2.0/((double)_squares);
     double dy = 2.0/((double)_squares);
 
+    double du = 2.0/((double)_squares);
+    double dv = 2.0/((double)_squares);
+
+    double xs = gflux->tex_xscale;
+    double ys = gflux->tex_yscale;
+
+    double minx, miny, maxx, maxy;
+    double minu, minv;
+
+#if 0
+    minx = (GLfloat) gflux->img_geom.x / gflux->img_width;
+    miny = (GLfloat) gflux->img_geom.y / gflux->img_height;
+    maxx = ((GLfloat) (gflux->img_geom.x + gflux->img_geom.width) /
+            gflux->img_width);
+    maxy = ((GLfloat) (gflux->img_geom.y + gflux->img_geom.height) /
+            gflux->img_height);
+    minu = minx;
+    minv = miny;
+    minx = (minx * 2) - 1;
+    miny = (miny * 2) - 1;
+    maxx = (maxx * 2) - 1;
+    maxy = (maxy * 2) - 1;
+#else
+    minx = -1;
+    miny = -1;
+    maxx = 1;
+    maxy = 1;
+    minv = 0;
+    minu = 0;
+#endif
+
        glMatrixMode (GL_TEXTURE);
        glLoadIdentity ();
        glTranslatef(-1,-1,0);
@@ -509,23 +544,26 @@ void displayTexture(void)
        glMatrixMode (GL_MODELVIEW);
 
     glLoadIdentity();
+    userRot();
     glRotatef(anglex,1,0,0);
     glRotatef(angley,0,1,0);
     glRotatef(anglez,0,0,1);
     glScalef(1,1,(GLfloat)_waveHeight);
     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        glEnable(GL_TEXTURE_2D);
+
+    clear_gl_error();
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glBindTexture(GL_TEXTURE_2D, gflux->texName);
-       glColor3f(0.5,0.5,0.5);
+    check_gl_error("texture binding");
 
-    for(x=-1,u=0;x<=1;x+=dx,u+=dx) {
+       glColor3f(0.5,0.5,0.5);
+    for(x = minx, u = minu; x < maxx - 0.01; x += dx, u += du) {
         glBegin(GL_QUAD_STRIP);
-        for(y=-1,v=0;y<=1;y+=dy,v+=dx) {
+        for (y = miny, v = minv; y <= maxy + 0.01; y += dy, v += dv) {
             z = getGrid(x,y,time);
-        /*  genColour(z);
-            glColor3fv(gflux->colour);
-        */  glTexCoord2f(u,v);
+            glTexCoord2f(u*xs,v*ys);
             glNormal3f(
                 getGrid(x+dx,y,time)-getGrid(x-dx,y,time),
                 getGrid(x,y+dy,time)-getGrid(x,y-dy,time),
@@ -534,9 +572,7 @@ void displayTexture(void)
             glVertex3f(x,y,z);
 
             z = getGrid(x+dx,y,time);
-        /*  genColour(z);
-            glColor3fv(gflux->colour);
-        */  glTexCoord2f(u+dx,v);
+            glTexCoord2f((u+du)*xs,v*ys);
             glNormal3f(
                 getGrid(x+dx+dx,y,time)-getGrid(x,y,time),
                 getGrid(x+dx,y+dy,time)-getGrid(x+dx,y-dy,time),
@@ -547,10 +583,35 @@ void displayTexture(void)
         glEnd();
     }
 
-    time -= _speed;
-    anglex -= _rotationx;
-    angley -= _rotationy;
-    anglez -= _rotationz;
+    /* Draw a border around the grid.
+     */
+    glColor3f(0.4, 0.4, 0.4);
+    glDisable(GL_TEXTURE_2D);
+    glEnable (GL_LINE_SMOOTH);
+
+    glBegin(GL_LINE_LOOP);
+    y = miny;
+    for (x = minx; x <= maxx; x += dx)
+      glVertex3f (x, y, getGrid (x, y, time));
+    x = maxx;
+    for (y = miny; y <= maxy; y += dy)
+      glVertex3f (x, y, getGrid (x, y, time));
+    y = maxy;
+    for (x = maxx; x >= minx; x -= dx)
+      glVertex3f (x, y, getGrid (x, y, time));
+    x = minx;
+    for (y = maxy; y >= miny; y -= dy)
+      glVertex3f (x, y, getGrid (x, y, time));
+
+    glEnd();
+    glEnable(GL_TEXTURE_2D);
+
+    if (! button_down_p) {
+      time -= _speed;
+      anglex -= _rotationx;
+      angley -= _rotationy;
+      anglez -= _rotationz;
+    }
 }
 void displaySolid(void)
 {
@@ -568,10 +629,11 @@ void displaySolid(void)
     glRotatef(anglex,1,0,0);
     glRotatef(angley,0,1,0);
     glRotatef(anglez,0,0,1);
+    userRot();
     glScalef(1,1,(GLfloat)_waveHeight);
     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
-    for(x=-1;x<=1;x+=dx) {
+    for(x=-1;x<0.9999;x+=dx) {
         glBegin(GL_QUAD_STRIP);
         for(y=-1;y<=1;y+=dy) {
             z = getGrid(x,y,time);
@@ -587,10 +649,12 @@ void displaySolid(void)
         glEnd();
     }
 
-    time -= _speed;
-    anglex -= _rotationx;
-    angley -= _rotationy;
-    anglez -= _rotationz;
+    if (! button_down_p) {
+      time -= _speed;
+      anglex -= _rotationx;
+      angley -= _rotationy;
+      anglez -= _rotationz;
+    }
 
 }
 
@@ -610,10 +674,11 @@ void displayLight(void)
     glRotatef(anglex,1,0,0);
     glRotatef(angley,0,1,0);
     glRotatef(anglez,0,0,1);
+    userRot();
     glScalef(1,1,(GLfloat)_waveHeight);
     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
-    for(x=-1;x<=1;x+=dx) {
+    for(x=-1;x<0.9999;x+=dx) {
         glBegin(GL_QUAD_STRIP);
         for(y=-1;y<=1;y+=dy) {
             z = getGrid(x,y,time);
@@ -639,10 +704,12 @@ void displayLight(void)
         glEnd();
     }
 
-    time -= _speed;
-    anglex -= _rotationx;
-    angley -= _rotationy;
-    anglez -= _rotationz;
+    if (! button_down_p) {
+      time -= _speed;
+      anglex -= _rotationx;
+      angley -= _rotationy;
+      anglez -= _rotationz;
+    }
 }
 
 void displayWire(void)
@@ -663,6 +730,7 @@ void displayWire(void)
     glRotatef(anglex,1,0,0);
     glRotatef(angley,0,1,0);
     glRotatef(anglez,0,0,1);
+    userRot();
     glScalef(1,1,(GLfloat)_waveHeight);
     glClear(GL_COLOR_BUFFER_BIT);
 
@@ -687,10 +755,12 @@ void displayWire(void)
         glEnd();
     }
 
-    time -= _speed;
-    anglex -= _rotationx;
-    angley -= _rotationy;
-    anglez -= _rotationz;
+    if (! button_down_p) {
+      time -= _speed;
+      anglex -= _rotationx;
+      angley -= _rotationy;
+      anglez -= _rotationz;
+    }
 }
 
 /* generates new ripples */
@@ -700,12 +770,14 @@ void calcGrid(void)
     double tmp;
     static int newWave;
 
+    if (button_down_p) return;
+
     tmp = 1.0/((double)_waveChange);
     if(!(counter%_waveChange)) {
         newWave = ((int)(counter*tmp))%_waves;
-        gflux->dispx[newWave] = 1.0 - ((double)random())/RAND_MAX;
-        gflux->dispy[newWave] = 1.0 - ((double)random())/RAND_MAX;
-        gflux->freq[newWave] = _waveFreq * ((float)random())/RAND_MAX;
+        gflux->dispx[newWave] = -frand(1.0);
+        gflux->dispy[newWave] = -frand(1.0);
+        gflux->freq[newWave] = _waveFreq * frand(1.0);
         gflux->wa[newWave] = 0.0;
     }
     counter++;