From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / starwars.c
index 0a7f4fd026dd770d93f5caf8cf7e14844404c177..9ee8cc3f05ed11e00cdb993ae1beeac83c0842d7 100644 (file)
@@ -1,5 +1,4 @@
-/*
- * starwars, Copyright (c) 1998-2005 Jamie Zawinski <jwz@jwz.org> and
+/* starwars, Copyright (c) 1998-2015 Jamie Zawinski <jwz@jwz.org> and
  * Claudio Matsuoka <claudio@helllabs.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  *
  * For the fanboys:
  *
- *     starwars -program 'cat starwars.txt' -columns 25 -no-wrap -texture
+ *     starwars -program 'cat starwars.txt' -columns 25 -no-wrap
  */
 
-#include <X11/Intrinsic.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
 
-extern XtAppContext app;
+#include <ctype.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "starwars.h"
+#define DEFAULTS "*delay:    40000     \n" \
+                "*showFPS:  False     \n" \
+                "*fpsTop:   True      \n" \
+                "*usePty:   False     \n" \
+                "*texFontCacheSize: 300\n" \
+                "*font:   " DEF_FONT "\n" \
+                "*textLiteral: " DEF_TEXT "\n" \
+                "*program: xscreensaver-text --cols 0"  /* don't wrap */
+
+# define refresh_sws 0
+# define release_sws 0
+# define sws_handle_event 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "textclient.h"
+#include "utf8wc.h"
+
+#ifdef USE_GL /* whole file */
+
+/* Should be in <GL/glext.h> */
+# ifndef  GL_TEXTURE_MAX_ANISOTROPY_EXT
+#  define GL_TEXTURE_MAX_ANISOTROPY_EXT     0x84FE
+# endif
+# ifndef  GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
+#  define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+# endif
 
-#define PROGCLASS      "StarWars"
-#define HACK_INIT      init_sws
-#define HACK_DRAW      draw_sws
-#define HACK_RESHAPE   reshape_sws
-#define sws_opts       xlockmore_opts
 
-#define DEF_PROGRAM    "xscreensaver-text --cols 0"  /* don't wrap */
 #define DEF_LINES      "125"
 #define DEF_STEPS      "35"
 #define DEF_SPIN       "0.03"
-#define DEF_FONT_SIZE  "-1"
+#define DEF_SIZE       "-1"
 #define DEF_COLUMNS    "-1"
-#define DEF_WRAP       "True"
-#define DEF_ALIGN      "Center"
+#define DEF_LINE_WRAP  "True"
+#define DEF_ALIGNMENT  "Center"
 #define DEF_SMOOTH     "True"
 #define DEF_THICK      "True"
 #define DEF_FADE       "True"
 #define DEF_TEXTURES   "True"
 #define DEF_DEBUG      "False"
 
-/* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
-   Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
-   Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
-   Utopia 400 needs  4 512x512 textures (1024x1024 bitmap).
-   Utopia 180 needs  1 512x512 texture.
-   Times  240 needs  1 512x512 texture.
- */
-#define DEF_FONT       "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
+#define DEF_FONT       "-*-utopia-bold-r-normal-*-*-360-*-*-*-*-*-*"
 
 #define TAB_WIDTH        8
 
 #define MAX_THICK_LINES   25
 #define FONT_WEIGHT       14
-#define KEEP_ASPECT
-
-#define DEFAULTS "*delay:    40000     \n" \
-                "*showFPS:  False     \n" \
-                "*fpsTop:   True      \n" \
-                "*font:   " DEF_FONT "\n" \
-
-#undef countof
-#define countof(x) (sizeof((x))/sizeof((*x)))
-
-#include "xlockmore.h"
 
-#ifdef USE_GL /* whole file */
+#ifndef HAVE_MOBILE
+# define KEEP_ASPECT   /* Letterboxing looks dumb on iPhone. */
+#endif
 
-#include <ctype.h>
-#include <GL/glu.h>
-#include <sys/stat.h>
 #include "texfont.h"
 #include "glutstroke.h"
 #include "glut_roman.h"
 #define GLUT_FONT (&glutStrokeRoman)
 
-#ifdef HAVE_UNAME
-# include <sys/utsname.h>
-#endif /* HAVE_UNAME */
-
-
 typedef struct {
+  Display *dpy;
   GLXContext *glx_context;
 
   GLuint text_list, star_list;
   texture_font_data *texfont;
-  int polygon_count;
-
-  FILE *pipe;
-  XtInputId pipe_id;
-  Time subproc_relaunch_delay;
+  text_data *tc;
 
   char *buf;
   int buf_size;
   int buf_tail;
 
   char **lines;
+  int *line_widths;
   int total_lines;
 
   double star_theta;
   double char_width;
   double line_height;
+  double descent;
   double font_scale;
   double intra_line_scroll;
 
@@ -123,7 +128,6 @@ typedef struct {
 
 static sws_configuration *scs = NULL;
 
-static char *program;
 static int max_lines;
 static int scroll_steps;
 static float star_spin;
@@ -139,11 +143,10 @@ static char *alignment_str;
 static int alignment;
 
 static XrmOptionDescRec opts[] = {
-  {"-program",     ".program",   XrmoptionSepArg, 0 },
   {"-lines",       ".lines",     XrmoptionSepArg, 0 },
   {"-steps",       ".steps",     XrmoptionSepArg, 0 },
   {"-spin",        ".spin",      XrmoptionSepArg, 0 },
-  {"-size",       ".fontSize",  XrmoptionSepArg, 0 },
+  {"-size",       ".size",      XrmoptionSepArg, 0 },
   {"-columns",    ".columns",   XrmoptionSepArg, 0 },
 /*{"-font",        ".font",      XrmoptionSepArg, 0 },*/
   {"-fade",        ".fade",      XrmoptionNoArg,  "True"   },
@@ -157,6 +160,7 @@ static XrmOptionDescRec opts[] = {
   {"-wrap",       ".lineWrap",  XrmoptionNoArg,  "True"   },
   {"-no-wrap",    ".lineWrap",  XrmoptionNoArg,  "False"  },
   {"-nowrap",     ".lineWrap",  XrmoptionNoArg,  "False"  },
+  {"-alignment",   ".alignment", XrmoptionSepArg, 0        },
   {"-left",        ".alignment", XrmoptionNoArg,  "Left"   },
   {"-right",       ".alignment", XrmoptionNoArg,  "Right"  },
   {"-center",      ".alignment", XrmoptionNoArg,  "Center" },
@@ -164,14 +168,13 @@ static XrmOptionDescRec opts[] = {
 };
 
 static argtype vars[] = {
-  {&program,        "program",   "Program",    DEF_PROGRAM,   t_String},
   {&max_lines,      "lines",     "Integer",    DEF_LINES,     t_Int},
   {&scroll_steps,   "steps",     "Integer",    DEF_STEPS,     t_Int},
   {&star_spin,      "spin",      "Float",      DEF_SPIN,      t_Float},
-  {&font_size,      "fontSize",  "Float",      DEF_FONT_SIZE, t_Float},
+  {&font_size,      "size",      "Float",      DEF_SIZE,      t_Float},
   {&target_columns, "columns",   "Integer",    DEF_COLUMNS,   t_Int},
-  {&wrap_p,         "lineWrap",  "Boolean",    DEF_WRAP,      t_Bool},
-  {&alignment_str,  "alignment", "Alignment",  DEF_ALIGN,     t_String},
+  {&wrap_p,         "lineWrap",  "Boolean",    DEF_LINE_WRAP, t_Bool},
+  {&alignment_str,  "alignment", "Alignment",  DEF_ALIGNMENT, t_String},
   {&smooth_p,       "smooth",    "Boolean",    DEF_SMOOTH,    t_Bool},
   {&thick_p,        "thick",     "Boolean",    DEF_THICK,     t_Bool},
   {&fade_p,         "fade",      "Boolean",    DEF_FADE,      t_Bool},
@@ -179,7 +182,7 @@ static argtype vars[] = {
   {&debug_p,        "debug",     "Boolean",    DEF_DEBUG,     t_Bool},
 };
 
-ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
 
 
@@ -244,242 +247,176 @@ strip (char *s, Bool leading, Bool trailing)
 }
 
 
-/* The GLUT font only has ASCII characters in them, so do what we can to
-   convert Latin1 characters to the nearest ASCII equivalent... 
- */
-static void
-latin1_to_ascii (char *s)
-{
-  unsigned char *us = (unsigned char *) s;
-  const unsigned char ascii[95] = {
-    '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
-    '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
-    '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
-    'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
-    'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
-    'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
-    'u', 'u', 'y', 'p', 'y' };
-  while (*us)
-    {
-      if (*us >= 161)
-        *us = ascii[*us - 161];
-      else if (*us > 127)
-        *us = '?';
-      us++;
-    }
-}
-
-\f
-/* Subprocess.
-   (This bit mostly cribbed from phosphor.c)
- */
-
-static void drain_input (sws_configuration *sc);
-
-static void
-subproc_cb (XtPointer closure, int *source, XtInputId *id)
-{
-  sws_configuration *sc = (sws_configuration *) closure;
-  drain_input (sc);
-}
-
-
-static void
-launch_text_generator (sws_configuration *sc)
+static int
+sw_string_width (sws_configuration *sc, const char *s)
 {
-  char *oprogram = get_string_resource ("program", "Program");
-  char *program = (char *) malloc (strlen (oprogram) + 10);
-  strcpy (program, "( ");
-  strcat (program, oprogram);
-  strcat (program, " ) 2>&1");
-
-  if ((sc->pipe = popen (program, "r")))
+  if (textures_p)
     {
-      sc->pipe_id =
-        XtAppAddInput (app, fileno (sc->pipe),
-                       (XtPointer) (XtInputReadMask | XtInputExceptMask),
-                       subproc_cb, (XtPointer) sc);
+      XCharStruct e;
+      texture_string_metrics (sc->texfont, s, &e, 0, 0);
+      return e.width;
     }
   else
-    {
-      perror (program);
-    }
-}
-
-
-static void
-relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
-{
-  sws_configuration *sc = (sws_configuration *) closure;
-  launch_text_generator (sc);
+    return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
 }
 
 
-/* When the subprocess has generated some output, this reads as much as it
-   can into sc->buf at sc->buf_tail.
- */
-static void
-drain_input (sws_configuration *sc)
+static int
+sw_string_width2 (sws_configuration *sc, const char *s, size_t size)
 {
-  if (sc->buf_tail < sc->buf_size - 2)
-    {
-      int target = sc->buf_size - sc->buf_tail - 2;
-      int n = (sc->pipe
-               ? read (fileno (sc->pipe),
-                       (void *) (sc->buf + sc->buf_tail),
-                       target)
-               : 0);
-      if (n > 0)
-        {
-          sc->buf_tail += n;
-          sc->buf[sc->buf_tail] = 0;
-        }
-      else
-        {
-          if (sc->pipe)
-            {
-              XtRemoveInput (sc->pipe_id);
-              sc->pipe_id = 0;
-              pclose (sc->pipe);
-              sc->pipe = 0;
-            }
-
-          /* If the process didn't print a terminating newline, add one. */
-          if (sc->buf_tail > 1 &&
-              sc->buf[sc->buf_tail-1] != '\n')
-            {
-              sc->buf[sc->buf_tail++] = '\n';
-              sc->buf[sc->buf_tail] = 0;
-            }
-
-          /* Then add one more, just for giggles. */
-          sc->buf[sc->buf_tail++] = '\n';
-          sc->buf[sc->buf_tail] = 0;
-
-          /* Set up a timer to re-launch the subproc in a bit. */
-          XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
-                           relaunch_generator_timer,
-                           (XtPointer) sc);
-        }
-    }
-}
+  char *s2 = (char *) malloc (size + 1);
+  int result;
 
+  strncpy (s2, s, size);
+  s2[size] = 0;
 
-static int
-string_width (sws_configuration *sc, const char *s)
-{
-  if (textures_p)
-    return texture_string_width (sc->texfont, s, 0);
-  else
-    return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
-}
+  result = sw_string_width (sc, s2);
 
-static int
-char_width (sws_configuration *sc, char c)
-{
-  char s[2];
-  s[0] = c;
-  s[1] = 0;
-  return string_width (sc, s);
+  free (s2);
+  return result;
 }
 
 
-/* Populates the sc->lines list with as many lines as are currently in
-   sc->buf (which was filled by drain_input().
+/* Populates the sc->lines list with as many lines as possible.
  */
 static void
 get_more_lines (sws_configuration *sc)
 {
   /* wrap anyway, if it's absurdly long. */
   int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
-  
-  int col = 0;
-  int col_pix = 0;
 
   char *s = sc->buf;
-  while (sc->total_lines < max_lines)
+
+  int target = sc->buf_size - sc->buf_tail - 2;
+
+  /* Fill as much as we can into sc->buf.
+   */
+  while (target > 0)
     {
-      int cw;
+      int c = textclient_getc (sc->tc);
+      if (c <= 0)
+        break;
+      sc->buf[sc->buf_tail++] = (char) c;
+      sc->buf[sc->buf_tail] = 0;
+      target--;
+    }
 
-      if (s >= sc->buf + sc->buf_tail)
-        /* Reached end of buffer before end of line.  Bail. */
-        return;
+  while (sc->total_lines < max_lines)
+    {
+      char *next_s = s;
+      unsigned counter = 0;
 
-      cw = char_width (sc, *s);
+      /* OS X is really slow to calcuate the bounds for a line of text,
+         so skip ahead a bit.
 
-      if (*s == '\r' || *s == '\n' ||
-          col_pix + cw >= wrap_pix)
+         Really though, the right thing to do is probably to wrap
+         CTLineCreateTruncatedLine, one way or another. */
+      for (;;)
         {
-          int L = s - sc->buf;
+          if (next_s >= sc->buf + sc->buf_tail)
+            break;
 
-          if (*s == '\r' || *s == '\n')
+          if (!counter)
             {
-              if (*s == '\r' && s[1] == '\n')  /* swallow CRLF too */
-                *s++ = 0;
+              if (s > sc->buf &&
+                  sw_string_width2 (sc, sc->buf, next_s - sc->buf) >= wrap_pix)
+                break;
 
-              *s++ = 0;
+              counter = 12; /* <-- Adjust to taste. */
+              s = next_s;
             }
-          else
+
+          if (*next_s == '\r' || *next_s == '\n')
+            break;
+
+          --counter;
+          ++next_s;
+        }
+
+      for (;;)
+        {
+          if (s >= sc->buf + sc->buf_tail)
+            /* Reached end of buffer before end of line.  Bail. */
+            return;
+
+          /* When checking pixel width, always measure the line from the
+             beginning, or else multi-byte UTF-8 characters, particularly
+             combining diacriticals, aren't measured right. */
+
+          if (*s == '\r' || *s == '\n' ||
+              (s > sc->buf && sw_string_width2 (sc, sc->buf, s - sc->buf) >= wrap_pix))
             {
-              /* We wrapped -- try to back up to the previous word boundary. */
-              char *s2 = s;
-              int n = 0;
-              while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
-                s2--, n++;
-              if (s2 > sc->buf)
+              int L = s - sc->buf;
+
+              if (*s == '\r' || *s == '\n')
                 {
-                  s = s2;
+                  if (*s == '\r' && s[1] == '\n')  /* swallow CRLF too */
+                    *s++ = 0;
+
                   *s++ = 0;
-                  L = s - sc->buf;
                 }
-            }
+              else
+                {
+                  /* We wrapped -- try to back up to the previous word boundary. */
+                  char *s2 = s;
+                  int n = 0;
+                  while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
+                    s2--, n++;
+                  if (s2 > sc->buf)
+                    {
+                      s = s2;
+                      *s++ = 0;
+                      L = s - sc->buf;
+                    }
+                }
 
-          sc->lines[sc->total_lines] = (char *) malloc (L+1);
-          memcpy (sc->lines[sc->total_lines], sc->buf, L);
-          sc->lines[sc->total_lines][L] = 0;
+              sc->lines[sc->total_lines] = (char *) malloc (L+1);
+              memcpy (sc->lines[sc->total_lines], sc->buf, L);
+              sc->lines[sc->total_lines][L] = 0;
 
-          if (!textures_p)
-            latin1_to_ascii (sc->lines[sc->total_lines]);
+              if (!textures_p)
+                {
+                  /* The GLUT font only has ASCII characters. */
+                  char *s1 = utf8_to_latin1 (sc->lines[sc->total_lines], True);
+                  free (sc->lines[sc->total_lines]);
+                  sc->lines[sc->total_lines] = s1;
+                }
 
-          {
-            char *t = sc->lines[sc->total_lines];
-            char *ut = untabify (t);
-            strip (ut, (alignment == 0), 1); /* if centering, strip
-                                                leading whitespace too */
-            sc->lines[sc->total_lines] = ut;
-            free (t);
-          }
+              {
+                char *t = sc->lines[sc->total_lines];
+                char *ut = untabify (t);
+                strip (ut, (alignment == 0), 1); /* if centering, strip
+                                                    leading whitespace too */
+                sc->lines[sc->total_lines] = ut;
+                free (t);
+              }
 
-          sc->total_lines++;
+              sc->line_widths[sc->total_lines] =
+                sw_string_width(sc, sc->lines[sc->total_lines]);
+
+              sc->total_lines++;
+
+              if (sc->buf_tail > (s - sc->buf))
+                {
+                  int i = sc->buf_tail - (s - sc->buf);
+                  memmove (sc->buf, s, i);
+                  sc->buf_tail = i;
+                  sc->buf[sc->buf_tail] = 0;
+                }
+              else
+                {
+                  sc->buf_tail = 0;
+                }
 
-          if (sc->buf_tail > (s - sc->buf))
-            {
-              int i = sc->buf_tail - (s - sc->buf);
-              memmove (sc->buf, s, i);
-              sc->buf_tail = i;
               sc->buf[sc->buf_tail] = 0;
+              s = sc->buf;
+
+              break;
             }
           else
             {
-              sc->buf_tail = 0;
+              s++;
             }
-
-          sc->buf[sc->buf_tail] = 0;
-          s = sc->buf;
-          col = 0;
-          col_pix = 0;
-        }
-      else
-        {
-          col++;
-          col_pix += cw;
-          if (*s == '\t')
-            {
-              int tab_pix = TAB_WIDTH * sc->char_width;
-              col     = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
-              col_pix = tab_pix   * ((col / tab_pix)   + 1);
-            }
-          s++;
         }
     }
 }
@@ -502,29 +439,45 @@ draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
 
   if (debug_p)
     {
-      GLfloat w;
       GLfloat h = sc->line_height / sc->font_scale;
-      char c[2];
-      c[1]=0;
-      s = os;
+      char **chars = utf8_split (os, 0);
+      int i, w = 0;
+      char *s2 = (char *) malloc (strlen(s) + 1);
+      *s2 = 0;
+
       if (textures_p) glDisable (GL_TEXTURE_2D);
       glLineWidth (1);
-      glColor3f (0.4, 0.4, 0.4);
-      glPushMatrix ();
-      glTranslatef (x, y, 0);
-      while (*s)
+
+      glColor3f (0.2, 0.2, 0.5);
+
+      glBegin (GL_LINES);
+
+      for (i = 0; chars[i]; i++)
         {
-          *c = *s++;
-          w = string_width (sc, c);
-          glBegin (GL_LINE_LOOP);
-          glVertex3f (0, 0, 0);
-          glVertex3f (w, 0, 0);
-          glVertex3f (w, h, 0);
-          glVertex3f (0, h, 0);
-          glEnd();
-          glTranslatef (w, 0, 0);
+          glVertex3f (x + w, y - sc->descent, 0);      /* char left */
+          glVertex3f (x + w, y - sc->descent + h, 0);
+          strcat (s2, chars[i]);
+          w = sw_string_width (sc, s2);
+          free (chars[i]);
         }
-      glPopMatrix ();
+
+      glVertex3f (x + w, y - sc->descent, 0);          /* char right */
+      glVertex3f (x + w, y - sc->descent + h, 0);
+
+      glVertex3f (x,     y - sc->descent + h, 0);      /* ascent */
+      glVertex3f (x + w, y - sc->descent + h, 0);
+
+      glVertex3f (x - sc->char_width,     y, 0);       /* baseline */
+      glVertex3f (x + sc->char_width + w, y, 0);
+
+      glVertex3f (x,     y - sc->descent, 0);          /* descent */
+      glVertex3f (x + w, y - sc->descent, 0);
+
+      glEnd();
+
+      free (s2);
+      free (chars);
+
       if (textures_p) glEnable (GL_TEXTURE_2D);
     }
 }
@@ -643,7 +596,7 @@ init_stars (ModeInfo *mi, int width, int height)
 
 /* Window management, etc
  */
-void
+ENTRYPOINT void
 reshape_sws (ModeInfo *mi, int width, int height)
 {
   sws_configuration *sc = &scs[MI_SCREEN(mi)];
@@ -655,6 +608,7 @@ reshape_sws (ModeInfo *mi, int width, int height)
     int w = mi->xgwa.width;
     int h = mi->xgwa.height;
     int yoff = 0;
+    GLfloat rot = current_device_rotation();
 
 #ifdef KEEP_ASPECT
     {
@@ -665,15 +619,28 @@ reshape_sws (ModeInfo *mi, int width, int height)
     }
 #endif
 
-    glMatrixMode (GL_PROJECTION);
     glViewport (0, yoff, w, h);
 
+    glMatrixMode (GL_PROJECTION);
+    glLoadIdentity();
+
     glMatrixMode (GL_MODELVIEW);
     glLoadIdentity ();
     gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
     gluLookAt (0.0, 0.0, 4600.0,
                0.0, 0.0, 0.0,
                0.0, 1.0, 0.0);
+
+    /* Horrible kludge to prevent the text from materializing already
+       on screen on iPhone in landscape mode.
+     */
+    if ((rot >  45 && rot <  135) ||
+        (rot < -45 && rot > -135))
+      {
+        GLfloat s = 1.1;
+        glScalef (s, s, s);
+      }
+
     glRotatef (-60.0, 1.0, 0.0, 0.0);
 
 #if 0
@@ -697,8 +664,8 @@ reshape_sws (ModeInfo *mi, int width, int height)
   {
     GLdouble mm[17], pm[17];
     GLint vp[5];
-    GLfloat x = 0.5, y1 = 0, z = 0;
-    GLfloat y2 = sc->line_height;
+    GLdouble x = 0.5, y1 = 0, z = 0;
+    GLdouble y2 = sc->line_height;
     GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
 
     glGetDoublev (GL_MODELVIEW_MATRIX, mm);
@@ -727,8 +694,6 @@ gl_init (ModeInfo *mi)
 {
   sws_configuration *sc = &scs[MI_SCREEN(mi)];
 
-  program = get_string_resource ("program", "Program");
-
   glDisable (GL_LIGHTING);
   glDisable (GL_DEPTH_TEST);
 
@@ -752,60 +717,70 @@ gl_init (ModeInfo *mi)
 }
 
 
-void 
+static void free_sws (ModeInfo *mi);
+
+
+ENTRYPOINT void 
 init_sws (ModeInfo *mi)
 {
   double font_height;
 
-  sws_configuration *sc;
+  sws_configuration *sc = 0;
 
-  if (!scs) {
-    scs = (sws_configuration *)
-      calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
-    if (!scs) {
-      fprintf(stderr, "%s: out of memory\n", progname);
-      exit(1);
-    }
+  MI_INIT (mi, scs, free_sws);
 
-    sc = &scs[MI_SCREEN(mi)];
-    sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
-  }
+  sc = &scs[MI_SCREEN(mi)];
 
+  sc->dpy = MI_DISPLAY(mi);
   sc = &scs[MI_SCREEN(mi)];
+  /* Unchecked malloc. :( */
+  sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
+  sc->line_widths = (int *) calloc (max_lines+1, sizeof(int));
 
   if ((sc->glx_context = init_GL(mi)) != NULL) {
     gl_init(mi);
     reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+    clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
+
     init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
   }
 
   if (textures_p)
     {
-      int cw, lh;
+      XCharStruct e;
+      int cw, ascent, descent;
       sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
-      cw = texture_string_width (sc->texfont, "n", &lh);
+      texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
+      cw = e.width;
       sc->char_width = cw;
-      font_height = lh;
+      font_height = ascent + descent;
+      sc->descent = descent;
       glEnable(GL_ALPHA_TEST);
       glEnable (GL_TEXTURE_2D);
 
       check_gl_error ("loading font");
 
-# ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
       /* "Anistropic filtering helps for quadrilateral-angled textures.
          A sharper image is accomplished by interpolating and filtering
          multiple samples from one or more mipmaps to better approximate
          very distorted textures.  This is the next level of filtering
          after trilinear filtering." */
-      if (smooth_p)
-        glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
-      clear_gl_error();
-# endif
+      if (smooth_p && 
+          strstr ((char *) glGetString(GL_EXTENSIONS),
+                  "GL_EXT_texture_filter_anisotropic"))
+      {
+        GLfloat anisotropic = 0.0;
+        glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
+        if (anisotropic >= 1.0)
+          glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 
+                           anisotropic);
+      }
     }
   else
     {
       font_height = GLUT_FONT->top - GLUT_FONT->bottom;
       sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
+      sc->descent = 0;
     }
   
   sc->font_scale = 1.0 / sc->char_width;
@@ -836,17 +811,19 @@ init_sws (ModeInfo *mi)
   sc->font_scale /= target_columns;
   sc->line_height = font_height * sc->font_scale;
 
-
-  /* Buffer only two lines of text.
+  /* Buffer only a few lines of text.
      If the buffer is too big, there's a significant delay between
      when the program launches and when the text appears, which can be
      irritating for time-sensitive output (clock, current music, etc.)
+
+     I'd like to buffer only 2 lines, but we need to assume that we
+     could get a whole line of N-byte Unicrud, and if we fill the buffer
+     before hitting the end of the line, we stall.
    */
-  sc->buf_size = target_columns * 2;
+  sc->buf_size = target_columns * 2 * 4;
   if (sc->buf_size < 80) sc->buf_size = 80;
   sc->buf = (char *) calloc (1, sc->buf_size);
 
-  sc->subproc_relaunch_delay = 2 * 1000;   /* 2 seconds */
   sc->total_lines = max_lines-1;
 
   if (random() & 1)
@@ -868,7 +845,7 @@ init_sws (ModeInfo *mi)
       exit (1);
     }
 
-  launch_text_generator (sc);
+  sc->tc = textclient_open (sc->dpy);
 
   /* one more reshape, after line_height has been computed */
   reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
@@ -894,6 +871,7 @@ draw_stars (ModeInfo *mi)
                -100.0, 100.0);
       glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
       if (textures_p) glDisable (GL_TEXTURE_2D);
+
       glCallList (sc->star_list);
       if (textures_p) glEnable (GL_TEXTURE_2D);
     }
@@ -903,10 +881,11 @@ draw_stars (ModeInfo *mi)
   glPopMatrix ();
 }
 
-void
+ENTRYPOINT void
 draw_sws (ModeInfo *mi)
 {
   sws_configuration *sc = &scs[MI_SCREEN(mi)];
+/*  XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
   Display *dpy = MI_DISPLAY(mi);
   Window window = MI_WINDOW(mi);
   int i;
@@ -914,9 +893,6 @@ draw_sws (ModeInfo *mi)
   if (!sc->glx_context)
     return;
 
-  if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
-    XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
-
   glDrawBuffer (GL_BACK);
   glXMakeCurrent (dpy, window, *(sc->glx_context));
 
@@ -927,14 +903,33 @@ draw_sws (ModeInfo *mi)
   glMatrixMode (GL_MODELVIEW);
   glPushMatrix ();
 
+# ifdef HAVE_MOBILE
+  /* Need to do this every time to get device rotation right */
+  reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+# endif
+
   if (debug_p)
     {
       int i;
       glPushMatrix ();
       if (textures_p) glDisable (GL_TEXTURE_2D);
       glLineWidth (1);
-      glColor3f (0.4, 0.4, 0.4);
       glTranslatef (0,-1, 0);
+
+      glColor3f(1, 0, 0);      /* Red line is where text appears */
+      glPushMatrix();
+      glTranslatef(0, -0.028, 0);
+      glLineWidth (4);
+      glBegin(GL_LINES);
+      glVertex3f(-0.5,  1, 0);
+      glVertex3f( 0.5,  1, 0);
+      glVertex3f(-0.5, -1, 0);
+      glVertex3f( 0.5, -1, 0);
+      glEnd();
+      glLineWidth (1);
+      glPopMatrix();
+
+      glColor3f (0.2, 0.2, 0.2);
       for (i = 0; i < 16; i++)
         {
           box (1, 1, 1);
@@ -943,14 +938,79 @@ draw_sws (ModeInfo *mi)
         }
       if (textures_p) glEnable (GL_TEXTURE_2D);
       glPopMatrix ();
+      check_gl_error ("debug render");
     }
 
   /* Scroll to current position */
   glTranslatef (0.0, sc->intra_line_scroll, 0.0);
 
   glColor3f (1.0, 1.0, 0.4);
-  glCallList (sc->text_list);
-  mi->polygon_count = sc->polygon_count;
+
+  mi->polygon_count = 0;
+
+  glPushMatrix ();
+  glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
+  for (i = 0; i < sc->total_lines; i++)
+    {
+      double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
+      int offscreen_lines = 2;
+
+      double x = -0.5;
+      double y =  ((sc->total_lines - (i + offscreen_lines) - 1)
+                   * sc->line_height);
+      double xoff = 0;
+      char *line = sc->lines[i];
+
+      if (debug_p)
+        {
+          double xx = x * 1.4;  /* a little more to the left */
+          char n[20];
+          sprintf(n, "%d:", i);
+          glColor3f (1.0, 1.0, 1.0);
+          draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
+        }
+
+      if (!line || !*line)
+        continue;
+
+      if (sc->line_thickness != 1 && !textures_p)
+        {
+          int max_thick_lines = MAX_THICK_LINES;
+          GLfloat thinnest_line = 1.0;
+          GLfloat thickest_line = sc->line_thickness;
+          GLfloat range = thickest_line - thinnest_line;
+          GLfloat thickness;
+
+          int j = sc->total_lines - i - 1;
+
+          if (j > max_thick_lines)
+            thickness = thinnest_line;
+          else
+            thickness = (thinnest_line +
+                         (range * ((max_thick_lines - j) /
+                                   (GLfloat) max_thick_lines)));
+
+          glLineWidth (thickness);
+        }
+
+      if (alignment >= 0)
+        {
+          int n = sc->line_widths[i];
+          xoff = 1.0 - (n * sc->font_scale);
+        }
+
+      if (alignment == 0)
+        xoff /= 2;
+
+      glColor3f (fade, fade, 0.5 * fade);
+      draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
+                   line);
+      if (textures_p)
+        mi->polygon_count += strlen (line);
+    }
+  glPopMatrix ();
+
+
 
   sc->intra_line_scroll += sc->line_height / scroll_steps;
 
@@ -965,8 +1025,10 @@ draw_sws (ModeInfo *mi)
       /* Scroll the contents of the lines array toward 0. */
       if (sc->total_lines > 0)
         {
-          for (i = 1; i < sc->total_lines; i++)
+          for (i = 1; i < sc->total_lines; i++) {
             sc->lines[i-1] = sc->lines[i];
+            sc->line_widths[i-1] = sc->line_widths[i];
+          }
           sc->lines[--sc->total_lines] = 0;
         }
 
@@ -978,72 +1040,6 @@ draw_sws (ModeInfo *mi)
            here so that new text still pulls in from the bottom of
            the screen, isntead of just appearing. */
         sc->total_lines = max_lines;
-
-      glDeleteLists (sc->text_list, 1);
-      sc->text_list = glGenLists (1);
-      glNewList (sc->text_list, GL_COMPILE);
-      sc->polygon_count = 0;
-      glPushMatrix ();
-      glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
-      for (i = 0; i < sc->total_lines; i++)
-        {
-          double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
-          int offscreen_lines = 2;
-
-          double x = -0.5;
-          double y =  ((sc->total_lines - (i + offscreen_lines) - 1)
-                       * sc->line_height);
-          double xoff = 0;
-          char *line = sc->lines[i];
-
-          if (debug_p)
-            {
-              double xx = x * 1.4;  /* a little more to the left */
-              char n[20];
-              sprintf(n, "%d:", i);
-              draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
-            }
-
-          if (!line || !*line)
-            continue;
-
-          if (sc->line_thickness != 1 && !textures_p)
-            {
-              int max_thick_lines = MAX_THICK_LINES;
-              GLfloat thinnest_line = 1.0;
-              GLfloat thickest_line = sc->line_thickness;
-              GLfloat range = thickest_line - thinnest_line;
-              GLfloat thickness;
-
-              int j = sc->total_lines - i - 1;
-
-              if (j > max_thick_lines)
-                thickness = thinnest_line;
-              else
-                thickness = (thinnest_line +
-                             (range * ((max_thick_lines - j) /
-                                       (GLfloat) max_thick_lines)));
-
-              glLineWidth (thickness);
-            }
-
-          if (alignment >= 0)
-            {
-              int n = string_width (sc, line);
-              xoff = 1.0 - (n * sc->font_scale);
-            }
-
-          if (alignment == 0)
-            xoff /= 2;
-
-          glColor3f (fade, fade, 0.5 * fade);
-          draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
-                       line);
-          if (textures_p)
-            sc->polygon_count += strlen (line);
-        }
-      glPopMatrix ();
-      glEndList ();
     }
 
   glPopMatrix ();
@@ -1055,4 +1051,23 @@ draw_sws (ModeInfo *mi)
   sc->star_theta += star_spin;
 }
 
+static void
+free_sws (ModeInfo *mi)
+{
+  sws_configuration *sc = &scs[MI_SCREEN(mi)];
+  if (sc->tc)
+    textclient_close (sc->tc);
+
+  /* #### there's more to free here */
+}
+
+
+#ifdef __GNUC__
+ __extension__ /* don't warn about "string length is greater than the length
+                  ISO C89 compilers are required to support" when including
+                  "starwars.txt" in the defaults... */
+#endif
+
+XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)
+
 #endif /* USE_GL */