http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / glx / lament.c
index 8a2360958900f81b257f6518e750c5bafd28f3ac..4c80da596e0e83d02be624c0632730f8f0a9a97c 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1998 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1998-2004 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 
      *  Needs music.  ("Hellraiser Themes" by Coil: TORSO CD161; also
         duplicated on the "Unnatural History 2" compilation, WORLN M04699.)
-
-     *  I'm not totally happy with the spinning motion; I like the
-        acceleration and deceleration, but it often feels like it's going too
-        fast, or not naturally enough, or something.
-
-     *  However, the motion is better than that used by gears, superquadrics,
-        etc.; so maybe I should make them all share the same motion code.
  */
 
-#include <X11/Intrinsic.h>
-
-#define PROGCLASS      "Lament"
-#define HACK_INIT      init_lament
-#define HACK_DRAW      draw_lament
-#define lament_opts    xlockmore_opts
-#define DEFAULTS       "*delay:        10000   \n"     \
-                       "*wireframe:    False   \n"     \
-                       "*texture:      True    \n"
+#define DEFAULTS       "*delay:        20000   \n"     \
+                       "*showFPS:      False   \n"     \
+                       "*wireframe:    False   \n"
+# define refresh_lament 0
+# define release_lament 0
 #include "xlockmore.h"
 
 #ifdef USE_GL /* whole file */
 #define DEF_TEXTURE "True"
 
 static int do_texture;
+
 static XrmOptionDescRec opts[] = {
-  {"-texture", ".lament.texture", XrmoptionNoArg, (caddr_t) "true" },
-  {"+texture", ".lament.texture", XrmoptionNoArg, (caddr_t) "false" },
+  {"-texture", ".lament.texture", XrmoptionNoArg, "true" },
+  {"+texture", ".lament.texture", XrmoptionNoArg, "false" },
 };
 
 static argtype vars[] = {
-  {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
+  {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
 };
 
-ModeSpecOpt lament_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt lament_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
+#include "normals.h"
 #include "xpm-ximage.h"
+#include "rotator.h"
+#include "gltrackball.h"
 #include "../images/lament.xpm"
 
 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
@@ -138,12 +131,26 @@ typedef enum {
 
 } lament_type;
 
-static GLfloat exterior_color[] = { 0.70, 0.60, 0.00, 1.00 };
-static GLfloat interior_color[] = { 0.25, 0.25, 0.20, 1.00 };
+static const GLfloat exterior_color[] =
+ { 0.33, 0.22, 0.03, 1.00,  /* ambient    */
+   0.78, 0.57, 0.11, 1.00,  /* specular   */
+   0.99, 0.91, 0.81, 1.00,  /* diffuse    */
+   27.80                   /* shininess  */
+ };
+static const GLfloat interior_color[] =
+ { 0.20, 0.20, 0.15, 1.00,  /* ambient    */
+   0.40, 0.40, 0.32, 1.00,  /* specular   */
+   0.99, 0.99, 0.81, 1.00,  /* diffuse    */
+   50.80                    /* shininess  */
+ };
 
 
 typedef struct {
   GLXContext *glx_context;
+  rotator *rot;
+  double rotx, roty, rotz;
+  trackball_state *trackball;
+  Bool button_down_p;
 
   GLuint box;                     /* display list IDs */
   GLuint star1, star2;
@@ -151,10 +158,6 @@ typedef struct {
   GLuint lid_0, lid_1, lid_2, lid_3, lid_4;
   GLuint taser_base, taser_lifter, taser_slider;
 
-  GLfloat rotx, roty, rotz;       /* current object rotation */
-  GLfloat dx, dy, dz;             /* current rotational velocity */
-  GLfloat ddx, ddy, ddz;          /* current rotational acceleration */
-  GLfloat d_max;                  /* max velocity */
   XImage *texture;                /* image bits */
   GLuint texids[6];               /* texture map IDs */
   lament_type type;               /* which mode of the object is current */
@@ -162,6 +165,9 @@ typedef struct {
   int anim_pause;                 /* countdown before animating again */
   GLfloat anim_r, anim_y, anim_z;  /* relative position during anims */
 
+  int state, nstates;
+  lament_type *states;
+
 } lament_configuration;
 
 static lament_configuration *lcs = NULL;
@@ -183,100 +189,21 @@ parse_image_data(ModeInfo *mi)
                               lament_faces);
 }
 
-
 \f
-/* Computing normal vectors (thanks to Nat Friedman <ndf@mit.edu>)
+/* Shorthand utilities for making faces, with proper normals.
  */
 
-typedef struct vector {
-  GLfloat x, y, z;
-} vector;
-
-typedef struct plane {
-  vector p1, p2, p3;
-} plane;
-
-static void
-vector_set(vector *v, GLfloat x, GLfloat y, GLfloat z)
-{
-  v->x = x;
-  v->y = y;
-  v->z = z;
-}
-
-static void
-vector_cross(vector v1, vector v2, vector *v3)
-{
-  v3->x = (v1.y * v2.z) - (v1.z * v2.y);
-  v3->y = (v1.z * v2.x) - (v1.x * v2.z);
-  v3->z = (v1.x * v2.y) - (v1.y * v2.x);
-}
-
-static void
-vector_subtract(vector v1, vector v2, vector *res)
-{
-  res->x = v1.x - v2.x;
-  res->y = v1.y - v2.y;
-  res->z = v1.z - v2.z;
-}
-
 static void
-plane_normal(plane p, vector *n)
+set_colors (const GLfloat *color)
 {
-  vector v1, v2;
-  vector_subtract(p.p1, p.p2, &v1);
-  vector_subtract(p.p1, p.p3, &v2);
-  vector_cross(v2, v1, n);
+  glMaterialfv(GL_FRONT, GL_AMBIENT, color+0);
+  glMaterialfv(GL_FRONT, GL_DIFFUSE, color+4);
+  glMaterialfv(GL_FRONT, GL_SPECULAR, color+8);
+  glMaterialfv(GL_FRONT, GL_SHININESS, color+12);
 }
 
 static void
-do_normal(GLfloat x1, GLfloat y1, GLfloat z1,
-         GLfloat x2, GLfloat y2, GLfloat z2,
-         GLfloat x3, GLfloat y3, GLfloat z3)
-{
-  plane plane;
-  vector n;
-  vector_set(&plane.p1, x1, y1, z1);
-  vector_set(&plane.p2, x2, y2, z2);
-  vector_set(&plane.p3, x3, y3, z3);
-  plane_normal(plane, &n);
-  n.x = -n.x; n.y = -n.y; n.z = -n.z;
-
-  glNormal3f(n.x, n.y, n.z);
-
-#ifdef DEBUG
-  /* Draw a line in the direction of this face's normal. */
-  {
-    GLfloat ax = n.x > 0 ? n.x : -n.x;
-    GLfloat ay = n.y > 0 ? n.y : -n.y;
-    GLfloat az = n.z > 0 ? n.z : -n.z;
-    GLfloat mx = (x1 + x2 + x3) / 3;
-    GLfloat my = (y1 + y2 + y3) / 3;
-    GLfloat mz = (z1 + z2 + z3) / 3;
-    GLfloat xx, yy, zz;
-
-    GLfloat max = ax > ay ? ax : ay;
-    if (az > max) max = az;
-    max *= 2;
-    xx = n.x / max;
-    yy = n.y / max;
-    zz = n.z / max;
-
-    glBegin(GL_LINE_LOOP);
-    glVertex3f(mx, my, mz);
-    glVertex3f(mx+xx, my+yy, mz+zz);
-    glEnd();
-  }
-#endif /* DEBUG */
-}
-
-
-\f
-/* Shorthand utilities for making faces, with proper normals.
- */
-
-static void
-face3(GLint texture, GLfloat *color, Bool wire,
+face3(GLint texture, const GLfloat *color, Bool wire,
       GLfloat s1, GLfloat t1, GLfloat x1, GLfloat y1, GLfloat z1,
       GLfloat s2, GLfloat t2, GLfloat x2, GLfloat y2, GLfloat z2,
       GLfloat s3, GLfloat t3, GLfloat x3, GLfloat y3, GLfloat z3)
@@ -284,7 +211,8 @@ face3(GLint texture, GLfloat *color, Bool wire,
 #ifdef HAVE_GLBINDTEXTURE
   glBindTexture(GL_TEXTURE_2D, texture);
 #endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
+  set_colors(color);
+
   do_normal(x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
   glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLES);
   glTexCoord2f(s1, t1); glVertex3f(x1, y1, z1);
@@ -294,7 +222,7 @@ face3(GLint texture, GLfloat *color, Bool wire,
 }
 
 static void
-face4(GLint texture, GLfloat *color, Bool wire,
+face4(GLint texture, const GLfloat *color, Bool wire,
       GLfloat s1, GLfloat t1, GLfloat x1, GLfloat y1, GLfloat z1,
       GLfloat s2, GLfloat t2, GLfloat x2, GLfloat y2, GLfloat z2,
       GLfloat s3, GLfloat t3, GLfloat x3, GLfloat y3, GLfloat z3,
@@ -303,7 +231,7 @@ face4(GLint texture, GLfloat *color, Bool wire,
 #ifdef HAVE_GLBINDTEXTURE
   glBindTexture(GL_TEXTURE_2D, texture);
 #endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
+  set_colors(color);
   do_normal(x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
   glTexCoord2f(s1, t1); glVertex3f(x1, y1, z1);
@@ -314,7 +242,7 @@ face4(GLint texture, GLfloat *color, Bool wire,
 }
 
 static void
-face5(GLint texture, GLfloat *color, Bool wire,
+face5(GLint texture, const GLfloat *color, Bool wire,
       GLfloat s1, GLfloat t1, GLfloat x1, GLfloat y1, GLfloat z1,
       GLfloat s2, GLfloat t2, GLfloat x2, GLfloat y2, GLfloat z2,
       GLfloat s3, GLfloat t3, GLfloat x3, GLfloat y3, GLfloat z3,
@@ -324,7 +252,7 @@ face5(GLint texture, GLfloat *color, Bool wire,
 #ifdef HAVE_GLBINDTEXTURE
   glBindTexture(GL_TEXTURE_2D, texture);
 #endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
+  set_colors(color);
   do_normal(x1, y1, z1,  x2, y2, z2,  x3, y3, z3);
   glBegin(wire ? GL_LINE_LOOP : GL_POLYGON);
   glTexCoord2f(s1, t1); glVertex3f(x1, y1, z1);
@@ -541,7 +469,7 @@ star(ModeInfo *mi, Bool top, Bool wire)
 #ifdef HAVE_GLBINDTEXTURE
   glBindTexture(GL_TEXTURE_2D, lc->texids[top ? FACE_U : FACE_D]);
 #endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
+  set_colors(exterior_color);
 
   i = 1;
   do_normal(points[i+0][0], points[i+0][1], 0,
@@ -563,9 +491,9 @@ star(ModeInfo *mi, Bool top, Bool wire)
 #ifdef HAVE_GLBINDTEXTURE
   glBindTexture(GL_TEXTURE_2D, 0);
 #endif /* HAVE_GLBINDTEXTURE */
-  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, interior_color);
+  set_colors(interior_color);
 
-  i = countof(points) - 3;
+  i = countof(points) - 9;
   do_normal(points[i+0][0], points[i+0][1], 0,
            points[i+4][0], points[i+4][1], 0,
            points[i+8][0], points[i+8][1], 0);
@@ -712,7 +640,6 @@ tetra(ModeInfo *mi, Bool wire)
          0.0, 0.0,      0.5, -0.5,  0.5,
          0.0, 0.0,      0.5,  0.5, -0.5,
          0.0, 0.0,     -0.5, -0.5, -0.5);
-    glEnd();
   }
   glEndList();
 
@@ -1010,7 +937,7 @@ taser(ModeInfo *mi, Bool wire)
 #ifdef HAVE_GLBINDTEXTURE
       glBindTexture(GL_TEXTURE_2D, lc->texids[FACE_E]);
 #endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
+      set_colors(exterior_color);
 
       do_normal(0, body_face_points[(i*5)+0][0], body_face_points[(i*5)+0][1],
                0, body_face_points[(i*5)+1][0], body_face_points[(i*5)+1][1],
@@ -1135,7 +1062,7 @@ taser(ModeInfo *mi, Bool wire)
 #ifdef HAVE_GLBINDTEXTURE
       glBindTexture(GL_TEXTURE_2D, lc->texids[FACE_E]);
 #endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
+      set_colors(exterior_color);
 
       do_normal(
          0, lifter_face_points[(i*5)+0][0], lifter_face_points[(i*5)+0][1],
@@ -1273,7 +1200,7 @@ taser(ModeInfo *mi, Bool wire)
 #ifdef HAVE_GLBINDTEXTURE
       glBindTexture(GL_TEXTURE_2D, lc->texids[FACE_E]);
 #endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
+      set_colors(exterior_color);
 
       do_normal(
           0, slider_face_points[(i*5)+0][0], slider_face_points[(i*5)+0][1],
@@ -1302,7 +1229,7 @@ taser(ModeInfo *mi, Bool wire)
 #ifdef HAVE_GLBINDTEXTURE
       glBindTexture(GL_TEXTURE_2D, 0);
 #endif /* HAVE_GLBINDTEXTURE */
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, interior_color);
+      set_colors(interior_color);
 
       do_normal(
           0, slider_face_points[(i*5)+2][0], slider_face_points[(i*5)+2][1],
@@ -1385,6 +1312,49 @@ taser(ModeInfo *mi, Bool wire)
 /* Rendering and animating object models
  */
 
+ENTRYPOINT Bool
+lament_handle_event (ModeInfo *mi, XEvent *event)
+{
+  lament_configuration *lc = &lcs[MI_SCREEN(mi)];
+
+  if (event->xany.type == ButtonPress &&
+      event->xbutton.button == Button1)
+    {
+      lc->button_down_p = True;
+      gltrackball_start (lc->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)
+    {
+      lc->button_down_p = False;
+      return True;
+    }
+  else if (event->xany.type == ButtonPress &&
+           (event->xbutton.button == Button4 ||
+            event->xbutton.button == Button5 ||
+            event->xbutton.button == Button6 ||
+            event->xbutton.button == Button7))
+    {
+      gltrackball_mousewheel (lc->trackball, event->xbutton.button, 5,
+                              !!event->xbutton.state);
+      return True;
+    }
+  else if (event->xany.type == MotionNotify &&
+           lc->button_down_p)
+    {
+      gltrackball_track (lc->trackball,
+                         event->xmotion.x, event->xmotion.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+
+  return False;
+}
+
+
 static void
 draw(ModeInfo *mi)
 {
@@ -1397,24 +1367,14 @@ draw(ModeInfo *mi)
     glClear(GL_COLOR_BUFFER_BIT);
 
   glPushMatrix();
-  {
-    GLfloat x = lc->rotx;
-    GLfloat y = lc->roty;
-    GLfloat z = lc->rotz;
-
-#if 0
- x=0.75; y=0; z=0; 
-#endif
 
-    if (x < 0) x = 1 - (x + 1);
-    if (y < 0) y = 1 - (y + 1);
-    if (z < 0) z = 1 - (z + 1);
+  gltrackball_rotate (lc->trackball);
 
-    /* Make into the screen be +Y right be +X, and up be +Z. */
-    glRotatef(-90.0, 1.0, 0.0, 0.0);
+  /* Make into the screen be +Y right be +X, and up be +Z. */
+  glRotatef(-90.0, 1.0, 0.0, 0.0);
 
-    /* Scale it up. */
-    glScalef(4.0, 4.0, 4.0);
+  /* Scale it up. */
+  glScalef(4.0, 4.0, 4.0);
 
 #ifdef DEBUG
     glPushMatrix();
@@ -1440,9 +1400,12 @@ draw(ModeInfo *mi)
     glPushMatrix();
     {
       /* Apply rotation to the object. */
-      glRotatef(x * 360, 1.0, 0.0, 0.0);
-      glRotatef(y * 360, 0.0, 1.0, 0.0);
-      glRotatef(z * 360, 0.0, 0.0, 1.0);
+      if (lc->type != LAMENT_LID_ZOOM)
+        get_rotation (lc->rot, &lc->rotx, &lc->roty, &lc->rotz,
+                      !lc->button_down_p);
+      glRotatef (lc->rotx * 360, 1.0, 0.0, 0.0);
+      glRotatef (lc->roty * 360, 0.0, 1.0, 0.0);
+      glRotatef (lc->rotz * 360, 0.0, 0.0, 1.0);
 
       switch (lc->type)
        {
@@ -1552,11 +1515,29 @@ draw(ModeInfo *mi)
     }
     glPopMatrix();
 
-  }
   glPopMatrix();
 }
 
 
+/* Rather than just picking states randomly, pick an ordering randomly, do it,
+   and then re-randomize.  That way one can be assured of seeing all states in
+   a short time period, though not always in the same order (it's frustrating
+   to see it pick the same state 5x in a row.)
+ */
+static void
+shuffle_states (lament_configuration *lc)
+{
+  int i;
+  for (i = 0; i < lc->nstates; i++)
+    {
+      int a = random() % lc->nstates;
+      lament_type swap = lc->states[a];
+      lc->states[a] = lc->states[i];
+      lc->states[i] = swap;
+    }
+}
+
+
 static void
 animate(ModeInfo *mi)
 {
@@ -1569,38 +1550,13 @@ animate(ModeInfo *mi)
     {
     case LAMENT_BOX:
       {
-       /* Rather than just picking states randomly, pick an ordering randomly,
-          do it, and then re-randomize.  That way one can be assured of seeing
-          all states in a short time period, though not always in the same
-          order (it's frustrating to see it pick the same state 5x in a row.)
-        */
-       static lament_type states[] = {
-         LAMENT_STAR_OUT, LAMENT_STAR_OUT,
-         LAMENT_TETRA_UNE, LAMENT_TETRA_USW,
-         LAMENT_TETRA_DWN, LAMENT_TETRA_DSE,
-         LAMENT_LID_OPEN, LAMENT_LID_OPEN, LAMENT_LID_OPEN,
-         LAMENT_TASER_OUT, LAMENT_TASER_OUT,
-         LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX,
-         LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX, LAMENT_BOX,
-       };
-       static int state = countof(states);
-
-       if (state < countof(states))
-         {
-           lc->type = states[state++];
-         }
-       else
-         {
-           int i;
-           state = 0;
-           for (i = 0; i < countof(states); i++)
-             {
-               int a = random() % countof(states);
-               lament_type swap = states[a];
-               states[a] = states[i];
-               states[i] = swap;
-             }
-         }
+        lc->state++;
+        if (lc->state >= lc->nstates)
+          {
+            shuffle_states (lc);
+            lc->state = 0;
+          }
+        lc->type = lc->states[lc->state];
 
        if (lc->type == LAMENT_BOX)
          lc->anim_pause = pause3;
@@ -1746,9 +1702,6 @@ animate(ModeInfo *mi)
        {
          lc->anim_r = 0.0;
          lc->anim_z = 0.0;
-         lc->rotx = frand(1.0) * RANDSIGN();
-         lc->roty = frand(1.0) * RANDSIGN();
-         lc->rotz = frand(1.0) * RANDSIGN();
          lc->type = LAMENT_BOX;
        }
       break;
@@ -1802,79 +1755,12 @@ animate(ModeInfo *mi)
 }
 
 
-static void
-rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
-{
-  double ppos = *pos;
-
-  /* tick position */
-  if (ppos < 0)
-    ppos = -(ppos + *v);
-  else
-    ppos += *v;
-
-  if (ppos > 1.0)
-    ppos -= 1.0;
-  else if (ppos < 0)
-    ppos += 1.0;
-
-  if (ppos < 0) abort();
-  if (ppos > 1.0) abort();
-  *pos = (*pos > 0 ? ppos : -ppos);
-
-  /* accelerate */
-  *v += *dv;
-
-  /* clamp velocity */
-  if (*v > max_v || *v < -max_v)
-    {
-      *dv = -*dv;
-    }
-  /* If it stops, start it going in the other direction. */
-  else if (*v < 0)
-    {
-      if (random() % 4)
-       {
-         *v = 0;
-
-         /* keep going in the same direction */
-         if (random() % 2)
-           *dv = 0;
-         else if (*dv < 0)
-           *dv = -*dv;
-       }
-      else
-       {
-         /* reverse gears */
-         *v = -*v;
-         *dv = -*dv;
-         *pos = -*pos;
-       }
-    }
-
-  /* Alter direction of rotational acceleration randomly. */
-  if (! (random() % 120))
-    *dv = -*dv;
-
-  /* Change acceleration very occasionally. */
-  if (! (random() % 200))
-    {
-      if (*dv == 0)
-       *dv = 0.00001;
-      else if (random() & 1)
-       *dv *= 1.2;
-      else
-       *dv *= 0.8;
-    }
-}
-
-
 \f
 /* Window management, etc
  */
 
-static void
-reshape(int width, int height)
+ENTRYPOINT void
+reshape_lament(ModeInfo *mi, int width, int height)
 {
   int target_size = 180;
   int win_size = (width > height ? height : width);
@@ -1900,7 +1786,7 @@ reshape(int width, int height)
      Note that the image-map bits we have are 128x128.  Therefore, if the
      image is magnified a lot, it looks pretty blocky.  So it's better to
      have a 128x128 animation on a 1280x1024 screen that looks good, than
-     a 1024x1024 animation that looks really pixellated.
+     a 1024x1024 animation that looks really pixelated.
    */
   if (win_size > target_size * 1.5)
     {
@@ -1927,31 +1813,26 @@ gl_init(ModeInfo *mi)
 
   if (!wire)
     {
-      static GLfloat pos0[]  = { -4.0, 2.0, 5.0, 1.0 };
-      static GLfloat pos1[]  = { 12.0, 5.0, 1.0, 1.0 };
-      static GLfloat local[] = { 0.0 };
-      static GLfloat ambient[] = { 0.3, 0.3, 0.3, 1.0 };
-      static GLfloat spec[] = { 1.0, 1.0, 1.0, 1.0 };
-      static GLfloat shine[] = { 100.0 };
+      static const GLfloat pos0[]  = { -4.0,  2.0, 5.0, 1.0 };
+      static const GLfloat pos1[]  = {  6.0, -1.0, 3.0, 1.0 };
+
+      static const GLfloat amb0[]  = { 0.7, 0.7, 0.7, 1.0 };
+/*    static const GLfloat amb1[]  = { 0.7, 0.0, 0.0, 1.0 }; */
+      static const GLfloat dif0[]  = { 1.0, 1.0, 1.0, 1.0 };
+      static const GLfloat dif1[]  = { 0.3, 0.1, 0.1, 1.0 };
 
       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
 
-      glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
-      glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
-
-      glLightfv(GL_LIGHT0, GL_SPECULAR, spec);
-      glLightfv(GL_LIGHT1, GL_SPECULAR, spec);
-
-      glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local);
-      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
-      glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
-      glMaterialfv(GL_FRONT, GL_SHININESS, shine);
+      glLightfv(GL_LIGHT0, GL_AMBIENT,  amb0);
+/*    glLightfv(GL_LIGHT1, GL_AMBIENT,  amb1); */
+      glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif0);
+      glLightfv(GL_LIGHT1, GL_DIFFUSE,  dif1);
+      set_colors(exterior_color);
 
       glEnable(GL_LIGHTING);
       glEnable(GL_LIGHT0);
-      glEnable(GL_LIGHT1);
-      glDisable(GL_LIGHT1);
+/*    glEnable(GL_LIGHT1); */
 
       glEnable(GL_DEPTH_TEST);
       glEnable(GL_TEXTURE_2D);
@@ -1975,12 +1856,17 @@ gl_init(ModeInfo *mi)
        {
          int height = lc->texture->width;      /* assume square */
          glBindTexture(GL_TEXTURE_2D, lc->texids[i]);
-         glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, exterior_color);
+         set_colors(exterior_color);
+
+          clear_gl_error();
          glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                       lc->texture->width, height, 0,
-                      GL_RGBA, GL_UNSIGNED_BYTE,
+                      GL_RGBA,
+                       /* GL_UNSIGNED_BYTE, */
+                       GL_UNSIGNED_INT_8_8_8_8_REV,
                       (lc->texture->data +
                        (lc->texture->bytes_per_line * height * i)));
+          check_gl_error("texture");
 
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
@@ -2025,7 +1911,48 @@ gl_init(ModeInfo *mi)
 }
 
 
-void
+# ifdef HAVE_MESA_GL
+
+# include <signal.h>
+
+static RETSIGTYPE
+lament_signal_kludge (int sig)
+{
+  signal (sig, SIG_DFL);
+  fprintf (stderr,
+           "\n"
+           "%s: dying with signal %d (%s).\n"
+           "\n"
+           "\tThis is almost certainly a bug in the Mesa GL library,\n"
+           "\tespecially if the stack trace in the core file mentions\n"
+           "\t`lambda_textured_triangle' or `render_quad'.\n"
+           "\n"
+           "\tFirst make sure that you have the latest version of Mesa.\n"
+           "\tIf that doesn't fix it, then I encourage you to report this\n"
+           "\tbug to the Mesa maintainers at <http://www.mesa3d.org/>.\n"
+           "\n",
+           progname,
+           sig,
+           (sig == SIGILL ? "SIGILL" :
+            sig == SIGFPE ? "SIGFPE" :
+            sig == SIGBUS ? "SIGBUS" :
+            sig == SIGSEGV ? "SIGSEGV" : "???"));
+  fflush (stderr);
+  kill (getpid (), sig);
+}
+
+static void
+handle_signals (void)
+{
+  signal (SIGILL,  lament_signal_kludge);
+  signal (SIGFPE,  lament_signal_kludge);
+  signal (SIGBUS,  lament_signal_kludge);
+  signal (SIGSEGV, lament_signal_kludge);
+}
+# endif /* HAVE_MESA_GL */
+
+
+ENTRYPOINT void
 init_lament(ModeInfo *mi)
 {
   lament_configuration *lc;
@@ -2042,40 +1969,55 @@ init_lament(ModeInfo *mi)
 
   lc = &lcs[MI_SCREEN(mi)];
 
-  lc->rotx = frand(1.0) * RANDSIGN();
-  lc->roty = frand(1.0) * RANDSIGN();
-  lc->rotz = frand(1.0) * RANDSIGN();
-
-  /* bell curve from 0-1.5 degrees, avg 0.75 */
-  lc->dx = (frand(1) + frand(1) + frand(1)) / (360*2);
-  lc->dy = (frand(1) + frand(1) + frand(1)) / (360*2);
-  lc->dz = (frand(1) + frand(1) + frand(1)) / (360*2);
-
-  lc->d_max = lc->dx * 2;
-
-  lc->ddx = 0.00006 + frand(0.00003);
-  lc->ddy = 0.00006 + frand(0.00003);
-  lc->ddz = 0.00006 + frand(0.00003);
-
-  lc->ddx = 0.00001;
-  lc->ddy = 0.00001;
-  lc->ddz = 0.00001;
+  {
+    double rot_speed = 0.5;
+    lc->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
+    lc->trackball = gltrackball_init ();
+  }
 
   lc->type = LAMENT_BOX;
   lc->anim_pause = 300 + (random() % 100);
 
   if ((lc->glx_context = init_GL(mi)) != NULL)
     {
-      reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
+      reshape_lament(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
       gl_init(mi);
     }
+
+  lc->states = (lament_type *) calloc (50, sizeof (*lc->states));
+  lc->nstates = 0;
+  lc->states[lc->nstates++] = LAMENT_STAR_OUT;
+  lc->states[lc->nstates++] = LAMENT_STAR_OUT;
+  lc->states[lc->nstates++] = LAMENT_TETRA_UNE;
+  lc->states[lc->nstates++] = LAMENT_TETRA_USW;
+  lc->states[lc->nstates++] = LAMENT_TETRA_DWN;
+  lc->states[lc->nstates++] = LAMENT_TETRA_DSE;
+  lc->states[lc->nstates++] = LAMENT_LID_OPEN;
+  lc->states[lc->nstates++] = LAMENT_LID_OPEN;
+  lc->states[lc->nstates++] = LAMENT_LID_OPEN;
+  lc->states[lc->nstates++] = LAMENT_TASER_OUT;
+  lc->states[lc->nstates++] = LAMENT_TASER_OUT;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  lc->states[lc->nstates++] = LAMENT_BOX;
+  shuffle_states (lc);
+
+# ifdef HAVE_MESA_GL
+  handle_signals ();
+# endif /* HAVE_MESA_GL */
 }
 
 
-void
+ENTRYPOINT void
 draw_lament(ModeInfo *mi)
 {
-  static int tick = 0;
   lament_configuration *lc = &lcs[MI_SCREEN(mi)];
   Display *dpy = MI_DISPLAY(mi);
   Window window = MI_WINDOW(mi);
@@ -2087,26 +2029,17 @@ draw_lament(ModeInfo *mi)
 
   glXMakeCurrent(dpy, window, *(lc->glx_context));
   draw(mi);
+  if (mi->fps_p) do_fps (mi);
+
   glFinish();
   glXSwapBuffers(dpy, window);
 
-  if (lc->type != LAMENT_LID_ZOOM)
-    {
-      rotate(&lc->rotx, &lc->dx, &lc->ddx, lc->d_max);
-      rotate(&lc->roty, &lc->dy, &lc->ddy, lc->d_max);
-      rotate(&lc->rotz, &lc->dz, &lc->ddz, lc->d_max);
-    }
-
   if (lc->anim_pause)
     lc->anim_pause--;
   else
     animate(mi);
-
-  if (++tick > 500)
-    {
-      tick = 0;
-      reshape(MI_WIDTH(mi), MI_HEIGHT(mi));
-    }
 }
 
+XSCREENSAVER_MODULE ("Lament", lament)
+
 #endif /* USE_GL */