From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / dnalogo.c
index d6c5514830875f26af237260d669a24737e0b35c..f9a55ef92110418e17858aa47eac1394f578f9cd 100644 (file)
@@ -1,4 +1,4 @@
-/* DNA Logo, Copyright (c) 2001-2011 Jamie Zawinski <jwz@jwz.org>
+/* DNA Logo, Copyright (c) 2001-2016 Jamie Zawinski <jwz@jwz.org>
  *
  *      DNA Lounge
  *
@@ -8,7 +8,7 @@
  *      San Francisco, CA
  *      94103
  *
- *      http://www.dnalounge.com/
+ *      https://www.dnalounge.com/
  *      http://www.dnapizza.com/
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
                        "*frameDepth:       0.01    \n" \
                        "*frameThickness:   0.03    \n" \
                        "*triangleSize:     0.045   \n" \
+                       "*cwFacets:         3       \n" \
+                       "*cwDiscFacets:     64      \n" \
+                       "*cwSpread:         0.5     \n" \
+                       "*cwLineWidth:      0.18    \n" \
+                       "*cwThickness:      0.15    \n" \
+                       "*cwCapSize:        0.4     \n" \
+                       "*text:             CODEWORD\n" \
                        "*speed:            1.0     \n" \
                        "*mode:             both"  "\n" \
-                       ".foreground:       #00AA00 \n" \
+                       ".background:       #000000\n" \
+                       ".foreground:       #00AA00\n" \
+                       ".cwForeground:     #FCA816\n" \
+                       ".cwBackground:     #943225\n" \
+                       "*cwFont:           " CWFONT "\n" \
                        "*geometry:         =640x640\n" \
 
+# if defined(HAVE_COCOA) || defined(HAVE_ANDROID)
+#  define CWFONT "Yearling 28, OCR A Std 24"
+# else
+#  define CWFONT "-*-helvetica-medium-r-normal-*-*-240-*-*-*-*-*-*"
+# endif
+
 # define refresh_logo 0
 # define release_logo 0
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
+#undef DXF_OUTPUT_HACK
+
 #ifdef DXF_OUTPUT_HACK   /* When this is defined, instead of rendering
                             to the screen, we write a DXF CAD file to stdout.
                             This is a kludge of shocking magnitude...
 #include "xlockmore.h"
 #include "normals.h"
 #include "tube.h"
+#include "sphere.h"
 #include "rotator.h"
 #include "gltrackball.h"
+#include "utf8wc.h"
+#include "texfont.h"
 
 #ifdef USE_GL /* whole file */
 
 #endif
 
 typedef enum {
-  HELIX_IN, HELIX, HELIX_OUT, 
+  HELIX_IN, HELIX, HELIX_OUT,
   PIZZA_IN, PIZZA, PIZZA_OUT,
-  BOTH
+  HELIX_AND_PIZZA,
+  CODEWORD_IN, CODEWORD, CODEWORD_OUT, CODEWORD_BLANK
 } glyph_mode;
 
 typedef struct {
   Bool spinning_p;
-  GLfloat position;     /* 0.0 - 1.0 */
-  GLfloat speed;        /* how far along the path (may be negative) */
-  GLfloat probability;  /* relative likelyhood to start spinning */
+  GLfloat position;            /* 0.0 - 1.0 */
+  GLfloat position_eased;      /* 0.0 - 1.0, eased in and out */
+  GLfloat easement;            /* portion of path that is eased. <= 0.5 */
+  GLfloat speed;               /* how far along the path (may be negative) */
+  GLfloat probability;         /* relative likelyhood to start spinning */
 } spinner;
 
 typedef struct {
@@ -125,13 +150,27 @@ typedef struct {
   GLfloat frame_thickness;
   GLfloat triangle_size;
 
+  int codeword_facets, codeword_disc_facets;
+  GLfloat codeword_spread, codeword_line_width, codeword_thickness;
+  GLfloat codeword_cap_size;
+  const char *codeword_text;
+  char *codeword_text_out;
+  int *codeword_text_points;
+  XYZ *codeword_path;
+  int codeword_path_npoints;
+  int codeword_nguides;
+  XYZ *codeword_guides;
+  GLfloat codeword_color[4], codeword_bg[4];
+  texture_font_data *font;
+
   GLfloat speed;
   glyph_mode mode;
   glyph_mode anim_state;
   GLfloat anim_ratio;
 
   spinner gasket_spinnerx, gasket_spinnery, gasket_spinnerz;
-  spinner scene_spinnerx,  scene_spinnery;
+  spinner scene_spinnerx,  scene_spinnery;     /* for DNA */
+  rotator *scene_rot;                          /* for Codeword */
   spinner helix_spinnerz;
   spinner pizza_spinnery, pizza_spinnerz;
   spinner frame_spinner;
@@ -146,11 +185,14 @@ typedef struct {
 static logo_configuration *dcs = NULL;
 
 static XrmOptionDescRec opts[] = {
-  { "-speed",  ".speed",  XrmoptionSepArg, 0       },
-  { "-mode",   ".mode",   XrmoptionSepArg, 0       },
-  { "-pizza",  ".mode",   XrmoptionNoArg,  "pizza" },
-  { "-helix",  ".mode",   XrmoptionNoArg,  "helix" },
-  { "-both",   ".mode",   XrmoptionNoArg,  "both"  },
+  { "-speed",    ".speed",  XrmoptionSepArg, 0          },
+  { "-mode",     ".mode",   XrmoptionSepArg, 0          },
+  { "-pizza",    ".mode",   XrmoptionNoArg,  "pizza"    },
+  { "-helix",    ".mode",   XrmoptionNoArg,  "helix"    },
+  { "-both",     ".mode",   XrmoptionNoArg,  "both"     },
+  { "-codeword", ".mode",   XrmoptionNoArg,  "codeword" },
+  { "-cw",       ".mode",   XrmoptionNoArg,  "codeword" },
+  { "-text",     ".text",   XrmoptionSepArg, 0          },
 };
 
 ENTRYPOINT ModeSpecOpt logo_opts = {countof(opts), opts, 0, NULL, NULL};
@@ -417,15 +459,20 @@ dxf_glDrawArrays (GLenum mode, GLint first, GLsizei count)
 }
 
 
-# define XYZ tube_XYZ /* avoid conflict with normals.h */
-# include "tube.c"    /* Yes, I really am including a C file. */
+# define XYZ tube_XYZ   /* avoid conflict with normals.h */
+# include "tube.c"      /* Yes, I really am including a C file. */
+# undef XYZ
+# define XYZ sphere_XYZ
+# define unit_sphere unit_sphere_dxf
+# define unit_dome unit_dome_dxf
+# include "sphere.c"
 # undef XYZ
 
 #endif /* DXF_OUTPUT_HACK */
 
 
 \f
-/* Calculate the angle (in degrees) between two vectors.
+/* Calculate the angle (in radians) between two vectors.
  */
 static GLfloat
 vector_angle (double ax, double ay, double az,
@@ -451,7 +498,30 @@ vector_angle (double ax, double ay, double az,
   if (cc > 1) cc = 1;  /* avoid fp rounding error (1.000001 => sqrt error) */
   angle = acos (cc);
 
-  return (angle * M_PI / 180);
+  return (angle);
+}
+
+static void
+normalize (XYZ *p)
+{
+  GLfloat d = sqrt (p->x*p->x + p->y*p->y + p->z*p->z);
+  if (d != 0)
+    {
+      p->x /= d;
+      p->y /= d;
+      p->z /= d;
+    }
+  else
+    {
+      p->x = p->y = p->z = 0;
+    }
+}
+
+
+static double
+dot (const XYZ u, const XYZ v)
+{
+  return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
 }
 
 \f
@@ -947,7 +1017,7 @@ make_gasket (logo_configuration *dc, int wire)
 
             GLfloat anglea = vector_angle (nx, ny, 0, nxa, nya, 0);
             GLfloat anglez = vector_angle (nx, ny, 0, nxz, nyz, 0);
-            GLfloat pointy = 0.005;
+            GLfloat pointy = 0.6;
 
             if (anglea > pointy)
               {
@@ -1450,7 +1520,6 @@ make_pizza (logo_configuration *dc, int facetted, int wire)
   GLfloat th, x, y, s;
   int i, j, k;
   int endpoints;
-  int endedge1;
 
 # ifdef HAVE_TESS
   tess_out TO, *to = &TO;
@@ -1502,7 +1571,6 @@ make_pizza (logo_configuration *dc, int facetted, int wire)
         points[j++] = edge[i*2];
         points[j++] = 0;
       }
-    endedge1 = i;
   }
 
   s = 0.798;  /* radius of end of slice, before crust gap */
@@ -1690,21 +1758,39 @@ make_pizza (logo_configuration *dc, int facetted, int wire)
 # else  /* !HAVE_TESS */
   if (! wire)
     {
+      glTranslatef(0, 0, thick2);
+      glNormal3f (0, 0, 1);
+      glFrontFace (GL_CW);
+
+      /* Sadly, jwzgl's glVertexPointer seems not to be recordable inside
+         display lists. */
+#  if 0
       glDisableClientState (GL_COLOR_ARRAY);
       glDisableClientState (GL_NORMAL_ARRAY);
       glDisableClientState (GL_TEXTURE_COORD_ARRAY);
       glEnableClientState (GL_VERTEX_ARRAY);
       glVertexPointer (3, GL_FLOAT, 0, dnapizza_triangles);
-
-      glTranslatef(0, 0, thick2);
-      glNormal3f (0, 0, 1);
-      glFrontFace (GL_CW);
       glDrawArrays (GL_TRIANGLES, 0, countof (dnapizza_triangles) / 3);
+#  else
+      glBegin (GL_TRIANGLES);
+      for (i = 0; i < countof (dnapizza_triangles); i += 3)
+        glVertex3fv (dnapizza_triangles + i);
+      glEnd();
+#  endif
 
       glTranslatef(0, 0, -thick2*2);
       glNormal3f (0, 0, -1);
       glFrontFace (GL_CCW);
+
+#  if 0
       glDrawArrays (GL_TRIANGLES, 0, countof (dnapizza_triangles) / 3);
+#  else
+      int i;
+      glBegin (GL_TRIANGLES);
+      for (i = 0; i < countof (dnapizza_triangles); i += 3)
+        glVertex3fv (dnapizza_triangles + i);
+      glEnd();
+#  endif
 
       glTranslatef(0, 0, thick2);
     }
@@ -1830,229 +1916,1112 @@ make_pizza (logo_configuration *dc, int facetted, int wire)
 }
 
 
-
-\f
-/* Window management, etc
+/* Upcase string, convert Unicrud to ASCII, remove any non-letters.
  */
-ENTRYPOINT void
-reshape_logo (ModeInfo *mi, int width, int height)
+static char *
+codeword_simplify_text (const char *s0)
 {
-  GLfloat h = (GLfloat) height / (GLfloat) width;
-
-  glViewport (0, 0, (GLint) width, (GLint) height);
-
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-  gluPerspective (30.0, 1/h, 1.0, 100.0);
-
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
-  gluLookAt( 0.0, 0.0, 30.0,
-             0.0, 0.0, 0.0,
-             0.0, 1.0, 0.0);
-
-  glClear(GL_COLOR_BUFFER_BIT);
+  char *s1 = utf8_to_latin1 ((s0 ? s0 : ""), True);
+  int L = strlen(s1);
+  char *s2 = (char *) malloc (L + 10);
+  char *s3 = s2;
+  int i;
+  for (i = 0; i < L; i++)
+    {
+      char c = s1[i];
+      if (c >= 'a' && c <= 'z')
+        c -= 'a'-'A';
+      if (c >= 'A' && c <= 'Z')
+        *s3++ = c;
+    }
+  *s3 = 0;
+  if (! *s2)
+    strcpy (s2, "CODEWORD");
+  return s2;
 }
 
 
 static void
-gl_init (ModeInfo *mi)
+make_codeword_path (ModeInfo *mi)
 {
-/*  logo_configuration *dc = &dcs[MI_SCREEN(mi)]; */
-  int wire = MI_IS_WIREFRAME(mi);
-
-  GLfloat position[]  = {0, 0, 0, 0};
-  GLfloat direction[] = {3, -1, -3};
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+  int letters = strlen (dc->codeword_text);
 
-  position[0] = -direction[0];
-  position[1] = -direction[1];
-  position[2] = -direction[2];
+  GLfloat rtick = dc->codeword_spread;
+  GLfloat iradius = rtick * dc->codeword_cap_size;
 
-  if (!wire)
-    {
-      glLightfv(GL_LIGHT0, GL_POSITION, position);
-      glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction);
-      glShadeModel(GL_SMOOTH);
-      glEnable(GL_NORMALIZE);
-      glEnable(GL_CULL_FACE);
-      glEnable(GL_LIGHTING);
-      glEnable(GL_LIGHT0);
-      glEnable(GL_DEPTH_TEST);
-    }
-}
+  int dial = 0;
+  int letter;
+  GLfloat last_r = 0;
 
+  GLfloat inner_circum = M_PI * 2 * (iradius + rtick * 2);
+  GLfloat outer_circum = M_PI * 2 * (iradius + rtick * (letters + 1));
+  GLfloat facet_length = inner_circum / (26 * dc->codeword_facets);
+  int outer_facets = ceil (outer_circum / facet_length);
 
-ENTRYPOINT void 
-init_logo (ModeInfo *mi)
-{
-  logo_configuration *dc;
-  int do_gasket = get_boolean_resource(mi->dpy, "doGasket", "Boolean");
-  int do_helix = get_boolean_resource(mi->dpy, "doHelix", "Boolean");
-  int do_ladder = (do_helix && 
-                   get_boolean_resource(mi->dpy, "doLadder", "Boolean"));
-  int do_frame = get_boolean_resource(mi->dpy, "doFrame", "Boolean");
-  GLfloat helix_rot = 147.0;
+  int *histo = (int *) calloc (letters * 26, sizeof(*histo));
+  XYZ *points = (XYZ *) calloc (letters * outer_facets, sizeof (*points));
+  int npoints = 0;
 
-  if (!do_gasket && !do_helix)
+  for (letter = -1; letter < letters; letter++)
     {
-      fprintf (stderr, "%s: no helix or gasket?\n", progname);
-      exit (1);
-    }
-
-  if (!dcs) {
-    dcs = (logo_configuration *)
-      calloc (MI_NUM_SCREENS(mi), sizeof (logo_configuration));
-    if (!dcs) {
-      fprintf(stderr, "%s: out of memory\n", progname);
-      exit(1);
-    }
-  }
+      if (letter == -1)                        /* Inner starting point */
+        {
+          points[npoints].x = iradius;
+          points[npoints].y = 0;
+          last_r = iradius;
+          npoints++;
+        }
+      else                             /* Add arc for this letter */
+        {
+          int direction = (letter & 1 ? -1 : 1);
+          int v = (dc->codeword_text[letter] - 'A' + 1);
+          int dial1 = dial + v * direction;
+
+          GLfloat th;
+          GLfloat th0 = M_PI * 2 / 26 * dial;
+          GLfloat th1 = M_PI * 2 / 26 * dial1;
+          GLfloat r  = iradius + rtick * (letter + 2);
+          GLfloat circum = M_PI * 2 * r;
+          GLfloat arc_length = circum * v / 26;
+          int arc_facets = ceil (fabs (arc_length / facet_length));
+          GLfloat facet_th = (th1 - th0) / arc_facets;
+
+          if (arc_facets > outer_facets) abort();
+
+          /* Let's put some intermediate facets on the crossbars too,
+             so that the animation doesn't speed up on those. */
+          {
+            GLfloat rr;
+            for (rr = last_r + facet_length;
+                 rr <= r - facet_length;
+                 rr += facet_length)
+              {
+                points[npoints].x = rr * cos (th0);
+                points[npoints].y = rr * sin (th0);
+                npoints++;
+              }
+            last_r = r;
+          }
 
-  dc = &dcs[MI_SCREEN(mi)];
 
-  if ((dc->glx_context = init_GL(mi)) != NULL) {
-    gl_init(mi);
-    reshape_logo (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
-  }
+          for (th = th0;
+               (th0 < th1
+                ? th <= th1 + facet_th
+                : th >= th1 + facet_th);
+               th += facet_th)
+            {
+              GLfloat th2 = th;
+              if (th0 < th1 && th > th1)
+                th2 = th1;
+              if (th0 > th1 && th < th1)
+                th2 = th1;
+              points[npoints].x = r * cos (th2);
+              points[npoints].y = r * sin (th2);
+
+              /* Ugh, add point only if it differs from prev.
+                 Not sure how this happens. */
+              if (npoints == 0 ||
+                  points[npoints-1].x != points[npoints].x ||
+                  points[npoints-1].y != points[npoints].y)
+                npoints++;
+            }
 
-  dc->wall_facets    = get_integer_resource(mi->dpy, "wallFacets",  "Integer");
-  dc->bar_facets     = get_integer_resource(mi->dpy, "barFacets",   "Integer");
-  dc->clockwise      = get_boolean_resource(mi->dpy, "clockwise",   "Boolean");
-  dc->turns          = get_float_resource(mi->dpy, "turns",         "Float");
-  dc->turn_spacing   = get_float_resource(mi->dpy, "turnSpacing",   "Float");
-  dc->bar_spacing    = get_float_resource(mi->dpy, "barSpacing",    "Float");
-  dc->wall_height    = get_float_resource(mi->dpy, "wallHeight",    "Float");
-  dc->wall_thickness = get_float_resource(mi->dpy, "wallThickness", "Float");
-  dc->bar_thickness  = get_float_resource(mi->dpy, "barThickness",  "Float");
-  dc->wall_taper     = get_float_resource(mi->dpy, "wallTaper",     "Float");
+          /* Mark up the histo array to find the outer border. */
+          {
+            int i;
+            for (i = dial;
+                 (direction > 0
+                  ? i <= dial1
+                  : i >= dial1);
+                  i += direction)
+              {
+                int x = (i + 26) % 26;
+                int y;
+                for (y = 0; y <= letter; y++)
+                  histo[y * 26 + x]++;
+              }
+          }
 
-  dc->gasket_size      = get_float_resource(mi->dpy,"gasketSize",     "Float");
-  dc->gasket_depth     = get_float_resource(mi->dpy,"gasketDepth",    "Float");
-  dc->gasket_thickness = get_float_resource(mi->dpy,"gasketThickness","Float");
+          dc->codeword_text_points[letter] = npoints;
 
-  dc->frame_size      = get_float_resource(mi->dpy, "frameSize",      "Float");
-  dc->frame_depth     = get_float_resource(mi->dpy, "frameDepth",     "Float");
-  dc->frame_thickness = get_float_resource(mi->dpy, "frameThickness", "Float");
-  dc->triangle_size   = get_float_resource(mi->dpy, "triangleSize",   "Float");
+          dial = dial1;
+        }
+    }
 
-  dc->speed          = get_float_resource(mi->dpy,   "speed",         "Float");
+  if (npoints >= letters * outer_facets) abort();
 
-  {
-    char *s = get_string_resource (MI_DISPLAY (mi), "mode", "String");
-    if (!s || !*s || !strcasecmp (s, "helix"))
-      dc->mode = HELIX;
-    else if (!strcasecmp (s, "pizza"))
-      dc->mode = PIZZA;
-    else if (!strcasecmp (s, "both"))
-      dc->mode = BOTH;
-    else
+# if 0
+  { /* Print histo */
+    int x, y;
+    for (y = 0; y < letters; y++)
       {
-        fprintf (stderr, "%s: mode must be helix, pizza or both, not \"%s\"\n", 
-                 progname, s);
-        exit (1);
+        fprintf (stderr, "%2d: ", y);
+        for (x = 0; x < 26; x++)
+          fprintf (stderr, "%x", histo[y * 26 + x]);
+        fprintf (stderr, "\n");
       }
-    if (s) free (s);
-
-    dc->anim_state = (dc->mode == BOTH
-                      ? ((random() & 1) ? HELIX : PIZZA)
-                      : dc->mode);
-    dc->anim_ratio = 0;
+    fprintf (stderr, "\n");
   }
+# endif
 
-  {
-    XColor xcolor;
 
-    char *color_name = get_string_resource (mi->dpy, "foreground", "Foreground");
-    char *s2;
-    for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
-      if (*s2 == ' ' || *s2 == '\t')
-        *s2 = 0;
-      else
-        break;
+  /* Find a gap in the outer edge, to draw guide dots. */
+  {
+    int x, y;
+    int last_row = letters;
+    int start_dial = -1, end_dial = -1;
 
-    if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
+    for (y = letters-1; y >= 0; y--)
       {
-        fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
-        exit (1);
+        for (x = 0; x < 26; x++)
+          {
+            if (histo[y * 26 + x] == 0)
+              {
+                if (last_row != y)
+                  start_dial = end_dial = -1;
+                last_row = y;
+                if (start_dial == -1)
+                  start_dial = x;
+                end_dial = x;
+              }
+          }
       }
 
-    dc->color[0] = xcolor.red   / 65535.0;
-    dc->color[1] = xcolor.green / 65535.0;
-    dc->color[2] = xcolor.blue  / 65535.0;
-    dc->color[3] = 1.0;
+    if (last_row < letters-1 && start_dial >= 0)
+      {
+        GLfloat r = iradius + rtick * (last_row + 2);
+        int i;
+
+        dc->codeword_nguides = 0;
+        dc->codeword_guides = (XYZ *) 
+          calloc (end_dial - start_dial + 1, sizeof (*dc->codeword_guides));
+        for (i = start_dial; i <= end_dial; i++)
+          {
+            GLfloat th = i * M_PI * 2 / 26;
+            GLfloat x = r * cos (th);
+            GLfloat y = r * sin (th);
+            dc->codeword_guides[dc->codeword_nguides].x = x;
+            dc->codeword_guides[dc->codeword_nguides].y = y;
+            dc->codeword_nguides++;
+          }
+      }
+    free (histo);
+    histo = 0;
   }
 
-  dc->trackball = gltrackball_init ();
+  dc->codeword_path_npoints = npoints;
+  dc->codeword_path = points;
+}
 
-  dc->gasket_spinnerx.probability = 0.1;
-  dc->gasket_spinnery.probability = 0.1;
-  dc->gasket_spinnerz.probability = 1.0;
 
-  dc->helix_spinnerz.probability  = 0.6;
+static int
+draw_codeword_cap (ModeInfo *mi)
+{
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+  int wire = MI_IS_WIREFRAME(mi);
+  int polys = 0;
 
-  dc->pizza_spinnerz.probability  = 0.6;
-  dc->pizza_spinnery.probability  = 0.6;
+  int segments  = dc->codeword_disc_facets;
+  GLfloat size  = dc->codeword_spread * dc->codeword_cap_size;
+  GLfloat width = dc->codeword_line_width / 2;
+  GLfloat thick = dc->codeword_thickness  / 2;
+  GLfloat r1 = size + width/2;
+  GLfloat r2 = size - width/2;
+  GLfloat facet, th, z;
 
-  dc->scene_spinnerx.probability  = 0.1;
-  dc->scene_spinnery.probability  = 0.0;
+  if (wire) segments = 12;
+  facet = M_PI * 2 / segments;
 
-  dc->frame_spinner.probability   = 5.0;
+  glPushMatrix();
 
-  /* start the frame off-screen */
-  dc->frame_spinner.spinning_p = True;
-  dc->frame_spinner.position = 0.3;
-  dc->frame_spinner.speed = 0.001;
+  /* Top and bottom */
 
-  if (dc->speed > 0)    /* start off with the gasket in motion */
+  for (z = -thick; z <= thick; z += thick*2)
     {
-      dc->gasket_spinnerz.spinning_p = True;
-      dc->gasket_spinnerz.speed = (0.002
-                                   * ((random() & 1) ? 1 : -1)
-                                   * dc->speed);
-    }
+      glNormal3f (0, 0, (z < 0 ? -1 : 1));
+      glFrontFace (z < 0 ? GL_CCW : GL_CW);
 
-# ifdef DXF_OUTPUT_HACK
-  {
-    dc->frame_depth = dc->gasket_depth;
-    dxf_layer = 1;
-    dxf_color = 3;
-    dxf_start();
-    glPushMatrix();
-    glRotatef(90, 1, 0, 0);
-    glRotatef(90, 0, 0, 1);
-    make_pizza (dc, 0, 0);
+      glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+      for (th = 0; th <= M_PI*2; th += facet)
+        {
+          GLfloat x = cos (th);
+          GLfloat y = sin (th);
+          glVertex3f (r1 * x, r1 * y, z);
+          glVertex3f (r2 * x, r2 * y, z);
+        }
+      glEnd();
 
-    glPushMatrix();
-    glRotatef(helix_rot, 0, 0, 1);
-    make_ladder (dc, 0, 0);
-    make_helix  (dc, 0, 0);
-    glRotatef (180, 0, 0, 1);
-    make_helix  (dc, 0, 0);
-    glPopMatrix();
+      if (wire)
+        {
+          glBegin (GL_LINE_LOOP);
+          for (th = 0; th <= M_PI*2; th += facet)
+            {
+              GLfloat x = cos (th);
+              GLfloat y = sin (th);
+              glVertex3f (r1 * x, r1 * y, z);
+            }
+          glEnd();
+          glBegin (GL_LINE_LOOP);
+          for (th = 0; th <= M_PI*2; th += facet)
+            {
+              GLfloat x = cos (th);
+              GLfloat y = sin (th);
+              glVertex3f (r2 * x, r2 * y, z);
+            }
+          glEnd();
+        }
+    }
 
-    dxf_layer++;
-    make_gasket (dc, 0);
-    dxf_layer++;
-    make_frame (dc, 0);
-    glPopMatrix();
-    dxf_end();
-  }
-# endif
+  /* Inside and outside */
 
-  glPushMatrix();
-  dc->helix_list = glGenLists (1);
-  glNewList (dc->helix_list, GL_COMPILE);
-  glRotatef(helix_rot, 0, 0, 1);
-  if (do_ladder) dc->polys[0] += make_ladder (dc, 0, 0);
-  if (do_helix)  dc->polys[0] += make_helix  (dc, 0, 0);
-  glRotatef(180, 0, 0, 1);
-  if (do_helix)  dc->polys[0] += make_helix  (dc, 0, 0);
-  glEndList ();
-  glPopMatrix();
+  for (z = -1; z <= 1; z += 2)
+    {
+      glFrontFace (z < 0 ? GL_CCW : GL_CW);
 
-  glPushMatrix();
-  dc->helix_list_wire = glGenLists (1);
-  glNewList (dc->helix_list_wire, GL_COMPILE);
+      glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+      for (th = 0; th <= M_PI*2; th += facet)
+        {
+          GLfloat th1 = th + facet;
+          GLfloat x0 = cos (th);
+          GLfloat y0 = sin (th);
+          GLfloat x1 = cos (th1);
+          GLfloat y1 = sin (th1);
+          GLfloat r = z < 0 ? r1 : r2;
+
+          if (z < 0)
+            do_normal (r * x0, r * y0,  thick,
+                       r * x0, r * y0, -thick,
+                       r * x1, r * y1, -thick);
+          else
+            do_normal (r * x1, r * y1,  thick,
+                       r * x1, r * y1, -thick,
+                       r * x0, r * y0, -thick);
+
+          glVertex3f (r * x0, r * y0,  thick);
+          glVertex3f (r * x0, r * y0, -thick);
+        }
+      glEnd();
+    }
+
+  glPopMatrix();
+
+  return polys;
+}
+
+
+static int
+draw_codeword_guides (ModeInfo *mi, GLfloat anim_ratio)
+{
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+  int wire = MI_IS_WIREFRAME(mi);
+  int polys = 0;
+
+  int segments = dc->codeword_disc_facets;
+  GLfloat s = dc->codeword_line_width / 2;
+  GLfloat each = 1.0 / dc->codeword_nguides;
+  int i;
+
+  if (wire) segments = 6;
+
+  for (i = 0; i < dc->codeword_nguides; i++)
+    {
+      GLfloat ratio;
+      if      (anim_ratio <  i*each)     ratio = 0;
+      else if (anim_ratio >= (i+1)*each) ratio = 1;
+      else    ratio = (anim_ratio - i*each) / each;
+
+      if (ratio <= 0) continue;
+      if (ratio == 0) ratio = 0.001;
+
+      glPushMatrix();
+      glTranslatef (dc->codeword_guides[i].x,
+                    dc->codeword_guides[i].y,
+                    dc->codeword_guides[i].z);
+
+      glScalef (ratio, ratio, ratio);
+
+      /* If the line width and thickness are pretty close to each other,
+         use spheres. Otherwise use tubes. 
+       */
+      if (dc->codeword_thickness < dc->codeword_line_width * 1.3 &&
+          dc->codeword_thickness > dc->codeword_line_width / 1.3)
+        {
+          glScalef (s, s, s);
+          glFrontFace (GL_CCW);
+          polys += unit_sphere (segments, segments, wire);
+        }
+      else
+        {
+          polys += tube (0, 0, -dc->codeword_thickness / 2,
+                         0, 0,  dc->codeword_thickness / 2,
+                         s, 0, segments, True, True, wire);
+        }
+
+      glPopMatrix();
+    }
+
+  return polys;
+}
+
+
+/* Compute the characters at which the cursor is currently pointing,
+   and render it on the logo. 
+ */
+static void
+codeword_text_output (ModeInfo *mi, GLfloat anim_ratio)
+{
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+  int i;
+  int L = strlen (dc->codeword_text);
+  int point = dc->codeword_path_npoints * anim_ratio;
+  Bool hit = False;
+
+  if (dc->anim_state == CODEWORD_BLANK)
+    point = 0;
+
+  for (i = 0; i < L; i++)
+    {
+      if (point >= dc->codeword_text_points[i])
+        dc->codeword_text_out[i] = dc->codeword_text[i];
+      else if (hit)
+        dc->codeword_text_out[i] = 0;
+      else
+        {
+          int steps = dc->codeword_text[i] - 'A' + 1;
+          int last = (i > 0 ? dc->codeword_text_points[i-1] : 0);
+          double ratio = ((point - last) /
+                          (double) (dc->codeword_text_points[i] - last));
+          char chr = 'A' + (ratio * steps);
+          if (ratio < 0.1) chr = 0;
+          dc->codeword_text_out[i] = chr;
+          hit = True;
+        }
+    }
+  dc->codeword_text_out[i] = 0;
+
+  if (*dc->codeword_text_out &&
+      !strcmp (dc->codeword_text, "CODEWORD"))
+    {
+      int i;
+      int L2 = strlen (dc->codeword_text_out);
+      GLfloat ss = 0.01;
+      int ascent, descent;
+
+      glPushMatrix();
+      glColor4fv (dc->codeword_color);
+      glRotatef (90, 0, 1, 0);
+      glRotatef (-90, 0, 0, 1);
+
+      for (i = 0; i < L2; i++)
+        {
+          XCharStruct e;
+          char buf[2];
+          glPushMatrix();
+          glRotatef ((i + 0.5) * 360 / 26.0, 0, 0, 1);
+
+# if 0
+          {
+            GLfloat th;
+            glDisable(GL_LIGHTING);
+            glBegin(GL_LINES);
+            glVertex3f(0,0,0);
+            glVertex3f(0,-4,0);
+            glEnd();
+            glBegin(GL_LINE_STRIP);
+            for (th = M_PI * 1.45; th < M_PI * 1.55; th += 0.1)
+              {
+                GLfloat r = 3.85;
+                glVertex3f (r * cos(th), r * sin(th), 0);
+              }
+            glEnd();
+            if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
+          }
+# endif
+
+          glTranslatef (0, -dc->codeword_spread * (L - 1), 0);
+          glScalef (ss, ss, ss);
+          buf[0] = dc->codeword_text_out[i] + ('a' - 'A');
+          buf[1] = 0;
+          texture_string_metrics (dc->font, buf, &e, &ascent, &descent);
+
+# ifdef HAVE_MOBILE
+          /* #### Magic magic magic WTF... */
+          glScalef (0.5, 0.5, 0.5);
+# endif
+
+          glTranslatef (-e.width * 1.0,
+                        -(ascent + descent + e.descent * 2.4), /* #### WTF */
+                          0);
+
+          glScalef (2, 2, 2);
+
+# if 0
+          glDisable(GL_LIGHTING);
+          glBegin(GL_LINE_LOOP);
+          glVertex3f(0, 0, 0);
+          glVertex3f(e.width, 0, 0);
+          glVertex3f(e.width, e.ascent + e.descent, 0);
+          glVertex3f(0, e.ascent + e.descent, 0);
+          glEnd();
+          if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
+# endif
+
+          glDisable(GL_CULL_FACE);  /* tell texfont.c to draw both sides */
+          print_texture_string (dc->font, buf);
+          glEnable(GL_CULL_FACE);
+
+          glPopMatrix();
+        }
+      glPopMatrix();
+    }
+}
+
+
+/* Convert the precomputed path to a thick line of polygons.
+   We could actually precompute all of these polygons too,
+   but it's fast enough.
+ */
+static int
+draw_codeword_path (ModeInfo *mi)
+{
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+  int wire = MI_IS_WIREFRAME(mi);
+  int polys = 0;
+
+  GLfloat anim_ratio = (dc->anim_state == CODEWORD_IN ?      dc->anim_ratio :
+                        dc->anim_state == CODEWORD_OUT ? 1 - dc->anim_ratio :
+                        dc->anim_state == CODEWORD_BLANK ? 0 :
+                        1);
+  int last_anim_point = 0;
+
+  GLfloat width = dc->codeword_line_width / 2;
+  GLfloat thick = dc->codeword_thickness  / 2;
+  int i, k;
+  GLfloat j;
+
+  int quad_size = (dc->codeword_path_npoints + 1) * 2;
+  XYZ *quads = (XYZ *) calloc (quad_size, sizeof(*quads));
+  XYZ *norms = (XYZ *) calloc (quad_size, sizeof(*norms));
+  int nquads = 0;
+
+  for (i = 0; i < dc->codeword_path_npoints; i++)
+    {
+      XYZ p1 = dc->codeword_path[i];
+      XYZ p2 = (i < dc->codeword_path_npoints-1
+                ? dc->codeword_path[i+1]
+                : dc->codeword_path[i-1]);
+      XYZ p1a, p1b;
+
+      XYZ n;          /* normal of the first line segment */
+      n.x = -(p2.y - p1.y);
+      n.y =  (p2.x - p1.x);
+      n.z =  0;
+      normalize (&n);
+
+      if (i == 0)
+        {
+          p1a.x = p1.x - width / 2 * n.x;
+          p1a.y = p1.y - width / 2 * n.y;
+          p1a.z = 0;
+
+          p1b.x = p1.x + width / 2 * n.x;
+          p1b.y = p1.y + width / 2 * n.y;
+          p1b.z = 0;
+        }
+      else if (i == dc->codeword_path_npoints - 1)
+        {
+          p1b.x = p1.x - width / 2 * n.x;
+          p1b.y = p1.y - width / 2 * n.y;
+          p1b.z = 0;
+
+          p1a.x = p1.x + width / 2 * n.x;
+          p1a.y = p1.y + width / 2 * n.y;
+          p1a.z = 0;
+        }
+      else
+        {
+          XYZ p0 = dc->codeword_path[i-1];
+
+          XYZ t, t0, t1;  /* tangent of corner between two line segments */
+          XYZ m;         /* miter line: normal of tangent */
+          GLfloat d;     /* length of miter */
+
+          t0.x = p2.x - p1.x;
+          t0.y = p2.y - p1.y;
+          t0.z = p2.z - p1.z;
+          normalize (&t0);
+
+          t1.x = p1.x - p0.x;
+          t1.y = p1.y - p0.y;
+          t1.z = p1.z - p0.z;
+          normalize (&t1);
+
+          t.x = t0.x + t1.x;
+          t.y = t0.y + t1.y;
+          t.z = t0.z + t1.z;
+          normalize (&t);
+
+          m.x = -t.y;
+          m.y =  t.x;
+          m.z =  0;
+
+          /* find length of miter by projecting it on one of the normals */
+          d = width / 2 / dot (m, n);
+
+          p1a.x = p1.x - d * m.x;
+          p1a.y = p1.y - d * m.y;
+          p1a.z = 0;
+
+          p1b.x = p1.x + d * m.x;
+          p1b.y = p1.y + d * m.y;
+          p1b.z = 0;
+        }
+
+      quads[nquads++] = p1a;
+      quads[nquads++] = p1b;
+
+      if (nquads >= quad_size) abort();
+
+      if (i / (double) dc->codeword_path_npoints > anim_ratio)
+        break;
+
+      last_anim_point = i;
+    }
+
+
+  /* Compute normals for each point along the interior edge */
+  for (k = 0; k <= 1; k++)
+    {
+      for (i = k; i < nquads-2; i += 2)
+        {
+          XYZ p1a = quads[i];
+          XYZ p2a = quads[i+2];
+          XYZ p1b = p1a;
+          XYZ p2b = p2a;
+          p1a.z =  thick;  /* a: top */
+          p1b.z = -thick;  /* b: bottom */
+          p2a.z =  thick;
+          p2b.z = -thick;
+
+          norms[i] = (k == 0
+                      ? calc_normal (p1a, p1b, p2b)
+                      : calc_normal (p2a, p2b, p1a));
+        }
+    }
+
+  glPushMatrix();
+  glColor4fv (dc->codeword_color);
+  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dc->codeword_color);
+
+# ifdef HAVE_MOBILE  /* Make the whole thing fit on the phone screen */
+  {
+    GLfloat size = MI_WIDTH(mi) < MI_HEIGHT(mi) ? MI_WIDTH(mi) : MI_HEIGHT(mi);
+    glScalef (0.9, 0.9, 0.9);
+    if (size <= 768)  /* iPad retina / iPhone 6 */
+      glScalef (0.7, 0.7, 0.7);
+  }
+# endif
+
+  codeword_text_output (mi, anim_ratio);
+
+  glRotatef (90, 1, 0, 0);
+  glRotatef (90, 0, 1, 0);
+  glRotatef (-90, 0, 0, 1);
+  glScalef (0.8, 0.8, 0.8);
+
+  glNormal3f (0, 0, -1);
+
+  if (anim_ratio <= 0)
+    {
+      polys += draw_codeword_cap (mi);
+      goto DONE;
+    }
+
+# if 0
+  glColor3f (1, 0, 0);
+  glBegin(GL_LINE_STRIP);
+  for (i = 0; i < dc->codeword_path_npoints; i++)
+    {
+      glVertex3f (dc->codeword_path[i].x,
+                  dc->codeword_path[i].y,
+                  dc->codeword_path[i].z);
+      polys++;
+    }
+  glEnd();
+  glColor4fv (dc->codeword_color);
+# endif
+
+  if (wire)
+    {
+      int k;
+      GLfloat j;
+      for (i = 0; i <= 1; i++)
+        for (j = -thick; j <= thick; j += thick*2)
+          {
+            glBegin (GL_LINE_STRIP);
+            for (k = i; k < nquads; k += 2)
+              {
+                glVertex3f (quads[k].x, quads[k].y, j);
+                polys++;
+              }
+            glEnd();
+          }
+    }
+
+  /* Top and bottom */
+
+  for (j = -thick; j <= thick; j += thick*2)
+    {
+      if (j < 0)
+        {
+          glNormal3f (0, 0, -1);
+          glFrontFace (GL_CW);
+        }
+      else
+        {
+          glNormal3f (0, 0, 1);
+          glFrontFace (GL_CCW);
+        }
+      glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+      for (i = 0; i < nquads; i += 2)
+        {
+          glVertex3f (quads[i+1].x, quads[i+1].y, j);
+          glVertex3f (quads[i].x,   quads[i].y,   j);
+          polys++;
+        }
+      glEnd();
+    }
+
+  /* Edges */
+
+  for (k = 0; k <= 1; k++)
+    {
+      if (k > 0)
+        {
+          glNormal3f (0, 0, -1);
+          glFrontFace (GL_CW);
+        }
+      else
+        {
+          glNormal3f (0, 0, 1);
+          glFrontFace (GL_CCW);
+        }
+
+      glBegin (wire ? GL_LINES : GL_QUADS);
+      for (i = k; i < nquads; i += 2)
+        {
+          XYZ p1a = quads[i];
+          XYZ p2a = (i < nquads-2) ? quads[i+2] : p1a;
+          XYZ p1b = p1a;
+          XYZ p2b = p2a;
+
+          XYZ n1 = norms[i];
+          XYZ n2 = (i < nquads-2) ? norms[i+2] : n1;
+
+          /* If the two normals are very similar, smooth the face.
+             If they are different, it's a sharp turn, and use the
+             same normal for both edges (not quite right, but close).
+          */
+          GLfloat angle = vector_angle (n1.x, n1.y, n1.z,
+                                        n2.x, n2.y, n2.z);
+          GLfloat pointy = 0.8;
+
+          p1a.z =  thick;
+          p1b.z = -thick;
+          p2a.z =  thick;
+          p2b.z = -thick;
+
+          glNormal3f (n1.x, n1.y, n1.z);
+          glVertex3f (p1a.x, p1a.y, p1a.z);
+          glVertex3f (p1b.x, p1b.y, p1b.z);
+
+          if (angle < pointy)
+            glNormal3f (n2.x, n2.y, n2.z);
+          glVertex3f (p2b.x, p2b.y, p2b.z);
+          glVertex3f (p2a.x, p2a.y, p2a.z);
+          polys++;
+        }
+      glEnd();
+    }
+
+
+  /* Only draw the guides when the path is almost complete;
+     fade them in and out based on completeness. */
+  {
+    GLfloat size = 0.95;
+    GLfloat r = (anim_ratio > size
+                 ? (anim_ratio - size) / (1 - size)
+                 : 0);
+    polys += draw_codeword_guides (mi, r);
+  }
+
+
+  /* Draw the start and end caps */
+  {
+    int i;
+    GLfloat x, y, z, x2, y2, z2, X, Y, Z;
+    GLfloat r = dc->codeword_spread * dc->codeword_cap_size;
+
+    i = 0;
+    x = dc->codeword_path[i].x;
+    y = dc->codeword_path[i].y;
+    z = dc->codeword_path[i].z;
+
+    x -= r;
+
+    glPushMatrix();
+    glTranslatef (x, y, z);
+    polys += draw_codeword_cap (mi);
+    glPopMatrix();
+
+    /* end cap */
+
+    i = last_anim_point + 1;
+    if (i > dc->codeword_path_npoints - 1)
+      i = dc->codeword_path_npoints - 1;
+
+    x = dc->codeword_path[i].x;
+    y = dc->codeword_path[i].y;
+    z = dc->codeword_path[i].z;
+
+    i--;
+    x2 = dc->codeword_path[i].x;
+    y2 = dc->codeword_path[i].y;
+    z2 = dc->codeword_path[i].z;
+
+    X = (x2 - x);
+    Y = (y2 - y);
+    Z = (z2 - z);
+
+    glPushMatrix();
+    glTranslatef (x, y, z);
+    glRotatef (-atan2 (X, Y)               * (180 / M_PI), 0, 0, 1);
+    glRotatef ( atan2 (Z, sqrt(X*X + Y*Y)) * (180 / M_PI), 1, 0, 0);
+    glTranslatef (0, -r, 0);
+    polys += draw_codeword_cap (mi);
+    glPopMatrix();
+  }
+
+ DONE:
+
+  glPopMatrix();
+
+  free (quads);
+  free (norms);
+
+  return polys;
+}
+
+
+\f
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_logo (ModeInfo *mi, int width, int height)
+{
+  GLfloat h = (GLfloat) height / (GLfloat) width;
+
+  glViewport (0, 0, (GLint) width, (GLint) height);
+
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  gluPerspective (30.0, 1/h, 1.0, 100.0);
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  gluLookAt( 0.0, 0.0, 30.0,
+             0.0, 0.0, 0.0,
+             0.0, 1.0, 0.0);
+
+# ifdef HAVE_MOBILE    /* Keep it the same relative size when rotated. */
+  {
+    int o = (int) current_device_rotation();
+    if (o != 0 && o != 180 && o != -180)
+      glScalef (1/h, 1/h, 1/h);  /* #### Why does this change the lighting? */
+  }
+# endif
+
+  glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+static void
+gl_init (ModeInfo *mi)
+{
+/*  logo_configuration *dc = &dcs[MI_SCREEN(mi)]; */
+  int wire = MI_IS_WIREFRAME(mi);
+
+  GLfloat position[]  = {0, 0, 0, 0};
+  GLfloat direction[] = {3, -1, -3};
+
+  position[0] = -direction[0];
+  position[1] = -direction[1];
+  position[2] = -direction[2];
+
+  if (!wire)
+    {
+      glLightfv(GL_LIGHT0, GL_POSITION, position);
+      glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction);
+      glShadeModel(GL_SMOOTH);
+      glEnable(GL_NORMALIZE);
+      glEnable(GL_CULL_FACE);
+      glEnable(GL_LIGHTING);
+      glEnable(GL_LIGHT0);
+      glEnable(GL_DEPTH_TEST);
+    }
+}
+
+
+ENTRYPOINT void 
+init_logo (ModeInfo *mi)
+{
+  logo_configuration *dc;
+  int do_gasket = get_boolean_resource(mi->dpy, "doGasket", "Boolean");
+  int do_helix = get_boolean_resource(mi->dpy, "doHelix", "Boolean");
+  int do_ladder = (do_helix && 
+                   get_boolean_resource(mi->dpy, "doLadder", "Boolean"));
+  int do_frame = get_boolean_resource(mi->dpy, "doFrame", "Boolean");
+  GLfloat helix_rot = 147.0;
+
+  if (!do_gasket && !do_helix)
+    {
+      fprintf (stderr, "%s: no helix or gasket?\n", progname);
+      exit (1);
+    }
+
+  MI_INIT (mi, dcs, NULL);
+
+  dc = &dcs[MI_SCREEN(mi)];
+
+  if ((dc->glx_context = init_GL(mi)) != NULL) {
+    gl_init(mi);
+    reshape_logo (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+  }
+
+  dc->wall_facets    = get_integer_resource(mi->dpy, "wallFacets",  "Integer");
+  dc->bar_facets     = get_integer_resource(mi->dpy, "barFacets",   "Integer");
+  dc->clockwise      = get_boolean_resource(mi->dpy, "clockwise",   "Boolean");
+  dc->turns          = get_float_resource(mi->dpy, "turns",         "Float");
+  dc->turn_spacing   = get_float_resource(mi->dpy, "turnSpacing",   "Float");
+  dc->bar_spacing    = get_float_resource(mi->dpy, "barSpacing",    "Float");
+  dc->wall_height    = get_float_resource(mi->dpy, "wallHeight",    "Float");
+  dc->wall_thickness = get_float_resource(mi->dpy, "wallThickness", "Float");
+  dc->bar_thickness  = get_float_resource(mi->dpy, "barThickness",  "Float");
+  dc->wall_taper     = get_float_resource(mi->dpy, "wallTaper",     "Float");
+
+  dc->gasket_size      = get_float_resource(mi->dpy,"gasketSize",     "Float");
+  dc->gasket_depth     = get_float_resource(mi->dpy,"gasketDepth",    "Float");
+  dc->gasket_thickness = get_float_resource(mi->dpy,"gasketThickness","Float");
+
+  dc->frame_size      = get_float_resource(mi->dpy, "frameSize",      "Float");
+  dc->frame_depth     = get_float_resource(mi->dpy, "frameDepth",     "Float");
+  dc->frame_thickness = get_float_resource(mi->dpy, "frameThickness", "Float");
+  dc->triangle_size   = get_float_resource(mi->dpy, "triangleSize",   "Float");
+
+  dc->speed           = get_float_resource(mi->dpy, "speed",          "Float");
+  dc->codeword_text   = get_string_resource(mi->dpy, "text",         "String");
+  dc->codeword_text   = codeword_simplify_text (dc->codeword_text);
+  dc->codeword_text_out =
+    calloc (strlen(dc->codeword_text) + 1, sizeof(*dc->codeword_text_out));
+  dc->codeword_text_points =
+    (int *) calloc (strlen(dc->codeword_text) + 1,
+                    sizeof(*dc->codeword_text_points));
+
+  dc->codeword_facets = get_integer_resource(mi->dpy, "cwFacets",  "Integer");
+  dc->codeword_disc_facets = get_integer_resource(mi->dpy,
+                                                  "cwDiscFacets",  "Integer");
+  dc->codeword_spread = get_float_resource(mi->dpy, "cwSpread",    "Float");
+  dc->codeword_line_width = get_float_resource(mi->dpy, "cwLineWidth", "Float");
+  dc->codeword_thickness  = get_float_resource(mi->dpy, "cwThickness", "Float");
+  dc->codeword_cap_size = get_float_resource(mi->dpy, "cwCapSize",     "Float");
+
+  {
+    char *s = get_string_resource (MI_DISPLAY (mi), "mode", "String");
+    if (!s || !*s || !strcasecmp (s, "helix"))
+      dc->mode = HELIX;
+    else if (!strcasecmp (s, "pizza"))
+      dc->mode = PIZZA;
+    else if (!strcasecmp (s, "both"))
+      dc->mode = HELIX_AND_PIZZA;
+    else if (!strcasecmp (s, "codeword"))
+      dc->mode = CODEWORD_IN;
+    else
+      {
+        fprintf (stderr,
+               "%s: mode must be helix, pizza, both or codeword, not \"%s\"\n", 
+                 progname, s);
+        exit (1);
+      }
+    if (s) free (s);
+
+    dc->anim_state = (dc->mode == HELIX_AND_PIZZA
+                      ? ((random() & 1) ? HELIX : PIZZA)
+                      : dc->mode);
+    dc->anim_ratio = 0;
+  }
+
+  if (dc->mode == CODEWORD_IN)
+    dc->font = load_texture_font (MI_DISPLAY(mi), "cwFont");
+
+  {
+    XColor xcolor;
+
+    char *color_name =
+      get_string_resource (mi->dpy, "foreground", "Foreground");
+    char *s2;
+    for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
+      if (*s2 == ' ' || *s2 == '\t')
+        *s2 = 0;
+      else
+        break;
+
+    if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
+      {
+        fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
+        exit (1);
+      }
+
+    dc->color[0] = xcolor.red   / 65535.0;
+    dc->color[1] = xcolor.green / 65535.0;
+    dc->color[2] = xcolor.blue  / 65535.0;
+    dc->color[3] = 1.0;
+
+    color_name = get_string_resource (mi->dpy, "cwForeground", "Foreground");
+    for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
+      if (*s2 == ' ' || *s2 == '\t')
+        *s2 = 0;
+      else
+        break;
+
+    if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
+      {
+        fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
+        exit (1);
+      }
+
+    dc->codeword_color[0] = xcolor.red   / 65535.0;
+    dc->codeword_color[1] = xcolor.green / 65535.0;
+    dc->codeword_color[2] = xcolor.blue  / 65535.0;
+    dc->codeword_color[3] = 1.0;
+
+    color_name = get_string_resource (mi->dpy, "cwBackground", "Background");
+    for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
+      if (*s2 == ' ' || *s2 == '\t')
+        *s2 = 0;
+      else
+        break;
+
+    if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
+      {
+        fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
+        exit (1);
+      }
+
+    dc->codeword_bg[0] = xcolor.red   / 65535.0;
+    dc->codeword_bg[1] = xcolor.green / 65535.0;
+    dc->codeword_bg[2] = xcolor.blue  / 65535.0;
+    dc->codeword_bg[3] = 1.0;
+  }
+
+  dc->trackball = gltrackball_init (False);
+
+  dc->gasket_spinnerx.probability = 0.1;
+  dc->gasket_spinnery.probability = 0.1;
+  dc->gasket_spinnerz.probability = 1.0;
+  dc->gasket_spinnerx.easement    = 0.08;
+  dc->gasket_spinnery.easement    = 0.08;
+  dc->gasket_spinnerz.easement    = 0.08;
+
+  dc->helix_spinnerz.probability  = 0.6;
+  dc->helix_spinnerz.easement     = 0.2;
+
+  dc->pizza_spinnerz.probability  = 0.6;
+  dc->pizza_spinnery.probability  = 0.6;
+  dc->pizza_spinnerz.easement     = 0.2;
+  dc->pizza_spinnery.easement     = 0.2;
+
+  dc->frame_spinner.probability   = 5.0;
+  dc->frame_spinner.easement      = 0.2;
+
+  dc->scene_spinnerx.probability  = 0.1;
+  dc->scene_spinnery.probability  = 0.0;
+  dc->scene_spinnerx.easement     = 0.1;
+  dc->scene_spinnery.easement     = 0.1;
+
+  if (dc->mode == CODEWORD_IN)
+    {
+      double tilt_speed = 0.003;
+      dc->scene_rot = make_rotator (0, 0, 0, 0, tilt_speed, True);
+    }
+
+  /* start the frame off-screen */
+  dc->frame_spinner.spinning_p = True;
+  dc->frame_spinner.position = 0.3;
+  dc->frame_spinner.speed = 0.001;
+
+  if (dc->speed > 0)    /* start off with the gasket in motion */
+    {
+      dc->gasket_spinnerz.spinning_p = True;
+      dc->gasket_spinnerz.speed = (0.002
+                                   * ((random() & 1) ? 1 : -1)
+                                   * dc->speed);
+    }
+
+# ifdef DXF_OUTPUT_HACK
+  {
+#  if 0
+    dc->frame_depth = dc->gasket_depth;
+    dxf_layer = 1;
+    dxf_color = 3;
+    dxf_start();
+    glPushMatrix();
+    glRotatef(90, 1, 0, 0);
+    glRotatef(90, 0, 0, 1);
+    make_pizza (dc, 0, 0);
+
+    glPushMatrix();
+    glRotatef(helix_rot, 0, 0, 1);
+    make_ladder (dc, 0, 0);
+    make_helix  (dc, 0, 0);
+    glRotatef (180, 0, 0, 1);
+    make_helix  (dc, 0, 0);
+    glPopMatrix();
+
+    dxf_layer++;
+    make_gasket (dc, 0);
+    dxf_layer++;
+    make_frame (dc, 0);
+    glPopMatrix();
+    dxf_end();
+#  else
+    dxf_start();
+    glPushMatrix();
+    glRotatef(90, 1, 0, 0);
+    glRotatef(90, 0, 0, 1);
+    dc->anim_state = CODEWORD;
+    make_codeword_path (mi);
+    draw_codeword_path (mi);
+    glPopMatrix();
+    dxf_end();
+#  endif
+  }
+# endif
+
+  glPushMatrix();
+  dc->helix_list = glGenLists (1);
+  glNewList (dc->helix_list, GL_COMPILE);
+  glRotatef(helix_rot, 0, 0, 1);
+  if (do_ladder) dc->polys[0] += make_ladder (dc, 0, 0);
+  if (do_helix)  dc->polys[0] += make_helix  (dc, 0, 0);
+  glRotatef(180, 0, 0, 1);
+  if (do_helix)  dc->polys[0] += make_helix  (dc, 0, 0);
+  glEndList ();
+  glPopMatrix();
+
+  glPushMatrix();
+  dc->helix_list_wire = glGenLists (1);
+  glNewList (dc->helix_list_wire, GL_COMPILE);
 /*  glRotatef(helix_rot, 0, 0, 1); wtf? */
   if (do_ladder) dc->polys[1] += make_ladder (dc, 1, 1);
   if (do_helix)  dc->polys[1] += make_helix  (dc, 1, 1);
@@ -2107,6 +3076,9 @@ init_logo (ModeInfo *mi)
   if (do_frame) dc->polys[6] += make_frame (dc, 1);
   glEndList ();
 
+  make_codeword_path (mi);
+
+
   /* When drawing both solid and wireframe objects,
      make sure the wireframe actually shows up! */
   glEnable (GL_POLYGON_OFFSET_FILL);
@@ -2119,44 +3091,48 @@ logo_handle_event (ModeInfo *mi, XEvent *event)
 {
   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
 
-  if (event->xany.type == ButtonPress &&
-      event->xbutton.button == Button1)
-    {
-      dc->button_down_p = True;
-      gltrackball_start (dc->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)
-    {
-      dc->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 (dc->trackball, event->xbutton.button, 10,
-                              !!event->xbutton.state);
-      return True;
-    }
-  else if (event->xany.type == MotionNotify &&
-           dc->button_down_p)
+  if (gltrackball_event_handler (event, dc->trackball,
+                                 MI_WIDTH (mi), MI_HEIGHT (mi),
+                                 &dc->button_down_p))
+    return True;
+  else if (event->xany.type == KeyPress)
     {
-      gltrackball_track (dc->trackball,
-                         event->xmotion.x, event->xmotion.y,
-                         MI_WIDTH (mi), MI_HEIGHT (mi));
-      return True;
+      KeySym keysym;
+      char c = 0;
+      XLookupString (&event->xkey, &c, 1, &keysym, 0);
+      if (c == ' ' || c == '\t')
+        {
+          switch (dc->anim_state) {
+          case HELIX:    
+            dc->anim_state = HELIX_OUT;
+            dc->anim_ratio = 0.0;
+            return True;
+          case PIZZA:
+            dc->anim_state = PIZZA_OUT;
+            dc->anim_ratio = 0.0;
+            return True;
+          case CODEWORD:
+            dc->anim_state = CODEWORD_OUT;
+            dc->anim_ratio = 0.0;
+            return True;
+          default:
+            break;
+          }
+        }
     }
 
   return False;
 }
 
 
+static GLfloat
+spinner_ease (GLfloat x)
+{
+  /* Smooth curve up, ending at slope = 1. */
+  return cos ((x/2 + 1) * M_PI) + 1;
+}
+
+
 static void
 tick_spinner (ModeInfo *mi, spinner *s)
 {
@@ -2168,12 +3144,20 @@ tick_spinner (ModeInfo *mi, spinner *s)
   if (s->spinning_p)
     {
       s->position += s->speed;
-      if (s->position >=  1.0 || s->position <= -1.0)
-          
+      if (s->position >=  1.0 || s->position <= 0.0)
         {
           s->position = 0;
+          s->position_eased = 0;
           s->spinning_p = False;
         }
+      else if (s->easement > 0 && s->position <= s->easement)
+        s->position_eased = (s->easement *
+                             spinner_ease (s->position / s->easement));
+      else if (s->easement > 0 && s->position >= 1-s->easement)
+        s->position_eased = (1 - s->easement *
+                             spinner_ease ((1 - s->position) / s->easement));
+      else
+        s->position_eased = s->position;
     }
   else if (s->probability &&
            (random() % (int) (PROBABILITY_SCALE / s->probability)) == 0)
@@ -2185,7 +3169,10 @@ tick_spinner (ModeInfo *mi, spinner *s)
         s->speed = dc->speed * (frand(ss/3) + frand(ss/3) + frand(ss/3));
       } while (s->speed <= 0);
       if (random() & 1)
-        s->speed = -s->speed;
+        {
+          s->speed = -s->speed;
+          s->position = 1.0;
+        }
     }
 }
 
@@ -2214,6 +3201,7 @@ draw_logo (ModeInfo *mi)
   GLfloat specular[]  = {0.8, 0.8, 0.8, 1.0};
   GLfloat shininess   = 50.0;
   Bool pizza_p;
+  Bool codeword_p;
 
   if (!dc->glx_context)
     return;
@@ -2242,7 +3230,7 @@ draw_logo (ModeInfo *mi)
   switch (dc->anim_state)
     {
     case HELIX:
-      if (dc->mode == BOTH &&
+      if (dc->mode == HELIX_AND_PIZZA &&
           (random() % (int) (PROBABILITY_SCALE / 0.2)) == 0)
         dc->anim_state = HELIX_OUT;
       break;
@@ -2266,7 +3254,7 @@ draw_logo (ModeInfo *mi)
       break;
 
     case PIZZA:
-      if (dc->mode == BOTH &&
+      if (dc->mode == HELIX_AND_PIZZA &&
           (random() % (int) (PROBABILITY_SCALE / 0.2)) == 0)
         dc->anim_state = PIZZA_OUT;
       break;
@@ -2289,6 +3277,55 @@ draw_logo (ModeInfo *mi)
         }
       break;
 
+
+    case CODEWORD_IN:
+      dc->scene_spinnerx.probability = 0.2;
+      dc->scene_spinnery.probability = 0.05;
+      if (! dc->button_down_p)
+        dc->anim_ratio += 0.004 * dc->speed;
+      if (dc->anim_ratio >= 1.0)
+        {
+          dc->anim_state = CODEWORD;
+          dc->anim_ratio = frand (0.5);
+        }
+      break;
+
+    case CODEWORD:
+      dc->scene_spinnerx.probability = 0.5;
+      dc->scene_spinnery.probability = 0.2;
+      if (! dc->button_down_p)
+        dc->anim_ratio += (0.0005 + frand(0.002)) * dc->speed;
+      if (dc->anim_ratio >= 1.0)
+        {
+          dc->anim_ratio = 0.0;
+          dc->anim_state = CODEWORD_OUT;
+        }
+      break;
+
+    case CODEWORD_OUT:
+      dc->scene_spinnerx.probability = 0;
+      dc->scene_spinnery.probability = 0;
+      if (! dc->button_down_p)
+        dc->anim_ratio += 0.02 * dc->speed;
+      if (dc->anim_ratio >= 1.0)
+        {
+          dc->anim_ratio = 0.0;
+          dc->anim_state = CODEWORD_BLANK;
+        }
+      break;
+
+    case CODEWORD_BLANK:
+      dc->scene_spinnerx.probability = 0;
+      dc->scene_spinnery.probability = 0;
+      if (! dc->button_down_p)
+        dc->anim_ratio += 0.01 * dc->speed;
+      if (dc->anim_ratio >= 1.0)
+        {
+          dc->anim_ratio = 0.0;
+          dc->anim_state = CODEWORD_IN;
+        }
+      break;
+
     default:
       abort();
       break;
@@ -2298,154 +3335,182 @@ draw_logo (ModeInfo *mi)
              dc->anim_state == PIZZA_IN ||
              dc->anim_state == PIZZA_OUT);
 
+  codeword_p = (dc->anim_state == CODEWORD ||
+                dc->anim_state == CODEWORD_IN ||
+                dc->anim_state == CODEWORD_OUT ||
+                dc->anim_state == CODEWORD_BLANK);
+
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
   glPushMatrix ();
   glRotatef(current_device_rotation(), 0, 0, 1);
   {
-    GLfloat scale = 0;
-    glScalef(3, 3, 3);
-    glScalef (0.6, 0.6, 0.6);
+    GLfloat scale = 1.8;
+    glScalef(scale, scale, scale);
 
     glColor3f(dc->color[0], dc->color[1], dc->color[2]);
 
-    /* Draw frame before trackball rotation */
-    {
-      GLfloat p = (dc->frame_spinner.position >= 0
-                   ? dc->frame_spinner.position
-                   : -dc->frame_spinner.position);
-      GLfloat size = (p > 0.5 ? 1-p : p);
-      scale = 1 + (size * 10);
-      glPushMatrix();
-      /* gltrackball_rotate (dc->trackball); */
-      glRotatef(90, 1, 0, 0);
-      glRotatef(90, 0, 0, 1);
 
-      glScalef (1, scale, scale);
-      if (wire)
-        {
-          glDisable (GL_LIGHTING);
-          glCallList (dc->frame_list_wire);
-          mi->polygon_count += dc->polys[6];
-        }
-      else if (dc->wire_overlay != 0)
-        {
-          glCallList (dc->frame_list);
-          glDisable (GL_LIGHTING);
-          glColor3fv (dc->color);
-          glCallList (dc->frame_list_wire);
-          mi->polygon_count += dc->polys[6];
-          if (!wire) glEnable (GL_LIGHTING);
-        }
-      else
-        {
-          glCallList (dc->frame_list);
-          mi->polygon_count += dc->polys[5];
-        }
-      glPopMatrix();
-    }
+    /* Draw frame before trackball rotation */
+    if (! codeword_p)
+      {
+        GLfloat p = (dc->frame_spinner.position_eased >= 0
+                     ? dc->frame_spinner.position_eased
+                     : -dc->frame_spinner.position_eased);
+        GLfloat size = (p > 0.5 ? 1-p : p);
+        scale = 1 + (size * 10);
+        glPushMatrix();
+        /* gltrackball_rotate (dc->trackball); */
+        glRotatef(90, 1, 0, 0);
+        glRotatef(90, 0, 0, 1);
+
+        glScalef (1, scale, scale);
+        if (wire)
+          {
+            glDisable (GL_LIGHTING);
+            glCallList (dc->frame_list_wire);
+            mi->polygon_count += dc->polys[6];
+          }
+        else if (dc->wire_overlay != 0)
+          {
+            glCallList (dc->frame_list);
+            glDisable (GL_LIGHTING);
+            glColor3fv (dc->color);
+            glCallList (dc->frame_list_wire);
+            mi->polygon_count += dc->polys[6];
+            if (!wire) glEnable (GL_LIGHTING);
+          }
+        else
+          {
+            glCallList (dc->frame_list);
+            mi->polygon_count += dc->polys[5];
+          }
+        glPopMatrix();
+      }
 
     gltrackball_rotate (dc->trackball);
 
     glRotatef(90, 1, 0, 0);
     glRotatef(90, 0, 0, 1);
 
-    glRotatef (360 * sin (M_PI/2 * dc->scene_spinnerx.position), 0, 1, 0);
-    glRotatef (360 * sin (M_PI/2 * dc->scene_spinnery.position), 0, 0, 1);
-
-    glPushMatrix();
-    {
-      glRotatef (360 * sin (M_PI/2 * dc->gasket_spinnerx.position), 0, 1, 0);
-      glRotatef (360 * sin (M_PI/2 * dc->gasket_spinnery.position), 0, 0, 1);
-      glRotatef (360 * sin (M_PI/2 * dc->gasket_spinnerz.position), 1, 0, 0);
-
-      memcpy (gcolor, dc->color, sizeof (dc->color));
-      if (dc->wire_overlay != 0)
-        {
-          gcolor[0]   = gcolor[1]   = gcolor[2]   = 0;
-          specular[0] = specular[1] = specular[2] = 0;
-          shininess = 0;
-        }
-      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gcolor);
-      glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,  specular);
-      glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS, shininess);
+    if (! codeword_p)
+      {
+        glRotatef (360 * dc->scene_spinnerx.position_eased, 0, 1, 0);
+        glRotatef (360 * dc->scene_spinnery.position_eased, 0, 0, 1);
 
-      if (wire)
-        {
-          glDisable (GL_LIGHTING);
-          glCallList (dc->gasket_list_wire);
-          mi->polygon_count += dc->polys[4];
-        }
-      else if (dc->wire_overlay != 0)
-        {
-          glCallList (dc->gasket_list);
-          glDisable (GL_LIGHTING);
-          glColor3fv (dc->color);
-          glCallList (dc->gasket_list_wire);
-          mi->polygon_count += dc->polys[4];
-          if (!wire) glEnable (GL_LIGHTING);
-          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gcolor);
-        }
-      else
-        {
-          glCallList (dc->gasket_list);
-          mi->polygon_count += dc->polys[3];
-        }
-    }
-    glPopMatrix();
+        glPushMatrix();
 
-    if (pizza_p)
-      {
-        glRotatef (360 * sin (M_PI/2 * dc->pizza_spinnery.position), 1, 0, 0);
-        glRotatef (360 * sin (M_PI/2 * dc->pizza_spinnerz.position), 0, 0, 1);
-      }
-    else
-      {
-        glRotatef (360 * sin (M_PI/2 * dc->helix_spinnerz.position), 0, 0, 1);
-      }
+        glRotatef (360 * dc->gasket_spinnerx.position_eased, 0, 1, 0);
+        glRotatef (360 * dc->gasket_spinnery.position_eased, 0, 0, 1);
+        glRotatef (360 * dc->gasket_spinnerz.position_eased, 1, 0, 0);
 
-    scale = ((dc->anim_state == PIZZA_IN || dc->anim_state == HELIX_IN)
-             ? dc->anim_ratio
-             : ((dc->anim_state == PIZZA_OUT || dc->anim_state == HELIX_OUT)
-                ? 1.0 - dc->anim_ratio
-                : 1.0));
-    if (scale <= 0) scale = 0.001;
-    glScalef (scale, scale, scale);
+        memcpy (gcolor, dc->color, sizeof (dc->color));
+        if (dc->wire_overlay != 0)
+          {
+            gcolor[0]   = gcolor[1]   = gcolor[2]   = 0;
+            specular[0] = specular[1] = specular[2] = 0;
+            shininess = 0;
+          }
+        glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gcolor);
+        glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,  specular);
+        glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS, shininess);
 
-    if (wire)
-      {
-        glDisable (GL_LIGHTING);
-        if (pizza_p)
-          glCallList (dc->pizza_list_wire);
+        if (wire)
+          {
+            glDisable (GL_LIGHTING);
+            glCallList (dc->gasket_list_wire);
+            mi->polygon_count += dc->polys[4];
+          }
+        else if (dc->wire_overlay != 0)
+          {
+            glCallList (dc->gasket_list);
+            glDisable (GL_LIGHTING);
+            glColor3fv (dc->color);
+            glCallList (dc->gasket_list_wire);
+            mi->polygon_count += dc->polys[4];
+            if (!wire) glEnable (GL_LIGHTING);
+            glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gcolor);
+          }
         else
-          glCallList (dc->helix_list_wire);
-        mi->polygon_count += dc->polys[1];
-      }
-    else if (dc->wire_overlay != 0)
-      {
+          {
+            glCallList (dc->gasket_list);
+            mi->polygon_count += dc->polys[3];
+          }
+        glPopMatrix();
+
         if (pizza_p)
-          glCallList (dc->pizza_list_facetted);
+          {
+            glRotatef (360 * dc->pizza_spinnery.position_eased, 1, 0, 0);
+            glRotatef (360 * dc->pizza_spinnerz.position_eased, 0, 0, 1);
+          }
         else
-          glCallList (dc->helix_list_facetted);
+          {
+            glRotatef (360 * dc->helix_spinnerz.position_eased, 0, 0, 1);
+          }
 
-        glDisable (GL_LIGHTING);
-        glColor3fv (dc->color);
+        scale = ((dc->anim_state == PIZZA_IN || dc->anim_state == HELIX_IN)
+                 ? dc->anim_ratio
+                 : ((dc->anim_state == PIZZA_OUT || dc->anim_state == HELIX_OUT)
+                    ? 1.0 - dc->anim_ratio
+                    : 1.0));
+        if (scale <= 0) scale = 0.001;
+        glScalef (scale, scale, scale);
 
-        if (pizza_p)
-          glCallList (dc->pizza_list_wire);
-        else
-          glCallList (dc->helix_list_wire);
+        if (wire)
+          {
+            glDisable (GL_LIGHTING);
+            if (pizza_p)
+              glCallList (dc->pizza_list_wire);
+            else
+              glCallList (dc->helix_list_wire);
+            mi->polygon_count += dc->polys[1];
+          }
+        else if (dc->wire_overlay != 0)
+          {
+            if (pizza_p)
+              glCallList (dc->pizza_list_facetted);
+            else
+              glCallList (dc->helix_list_facetted);
+
+            glDisable (GL_LIGHTING);
+            glColor3fv (dc->color);
+
+            if (pizza_p)
+              glCallList (dc->pizza_list_wire);
+            else
+              glCallList (dc->helix_list_wire);
 
-        mi->polygon_count += dc->polys[2];
-        if (!wire) glEnable (GL_LIGHTING);
+            mi->polygon_count += dc->polys[2];
+            if (!wire) glEnable (GL_LIGHTING);
+          }
+        else
+          {
+            if (pizza_p)
+              glCallList (dc->pizza_list);
+            else
+              glCallList (dc->helix_list);
+            mi->polygon_count += dc->polys[0];
+          }
       }
-    else
+    else       /* codeword_p */
       {
-        if (pizza_p)
-          glCallList (dc->pizza_list);
-        else
-          glCallList (dc->helix_list);
-        mi->polygon_count += dc->polys[0];
+# if 0
+        double max = 70;  /* face front */
+        double x, y, z;
+        get_position (dc->scene_rot, &x, &y, &z, !dc->button_down_p);
+        glRotatef (max/2 - x*max, 0, 0, 1);
+        glRotatef (max/2 - y*max, 0, 1, 0);
+        /* glRotatef (max/2 - z*max, 1, 0, 0); */
+# else
+        glRotatef (360 * dc->scene_spinnerx.position_eased, 0, 1, 0);
+        glRotatef (360 * dc->scene_spinnery.position_eased, 0, 0, 1);
+# endif
+
+        glClearColor (dc->codeword_bg[0],
+                      dc->codeword_bg[1],
+                      dc->codeword_bg[2],
+                      dc->codeword_bg[3]);
+        mi->polygon_count += draw_codeword_path (mi);
       }
   }
   glPopMatrix();