From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / starwars.c
index 118d5d48e4749fe2802ee4562ba477c9719d3be8..1398c159c9bd28da58ef4664ae64c8e04059d69f 100644 (file)
@@ -1,5 +1,4 @@
-/*
- * starwars, Copyright (c) 1998-2006 Jamie Zawinski <jwz@jwz.org> and
+/* starwars, Copyright (c) 1998-2014 Jamie Zawinski <jwz@jwz.org> and
  * Claudio Matsuoka <claudio@helllabs.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
@@ -23,7 +22,7 @@
  *
  * For the fanboys:
  *
- *     starwars -program 'cat starwars.txt' -columns 25 -no-wrap -texture
+ *     starwars -program 'cat starwars.txt' -columns 25 -no-wrap
  */
 
 #ifdef HAVE_CONFIG_H
 # include <unistd.h>
 #endif
 
-#ifdef HAVE_UNAME
-# include <sys/utsname.h>
-#endif /* HAVE_UNAME */
-
-#ifndef HAVE_COCOA
-# include <X11/Intrinsic.h>
-#endif
-
-
+#include "starwars.h"
 #define DEFAULTS "*delay:    40000     \n" \
                 "*showFPS:  False     \n" \
                 "*fpsTop:   True      \n" \
-                "*font:   " DEF_FONT "\n"
+                "*usePty:   False     \n" \
+                "*font:   " DEF_FONT "\n" \
+                "*textLiteral: " DEF_TEXT "\n"
+
 
 # define refresh_sws 0
 # define sws_handle_event 0
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
 #include "xlockmore.h"
+#include "textclient.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 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 MAX_THICK_LINES   25
 #define FONT_WEIGHT       14
-#define KEEP_ASPECT
+
+#ifndef USE_IPHONE
+# define KEEP_ASPECT   /* Letterboxing looks dumb on iPhone. */
+#endif
 
 #include "texfont.h"
 #include "glutstroke.h"
@@ -102,11 +108,7 @@ typedef struct {
   GLuint text_list, star_list;
   texture_font_data *texfont;
   int polygon_count;
-
-  FILE *pipe;
-  XtInputId pipe_id;
-  XtIntervalId pipe_timer;
-  Time subproc_relaunch_delay;
+  text_data *tc;
 
   char *buf;
   int buf_size;
@@ -150,7 +152,7 @@ static XrmOptionDescRec opts[] = {
   {"-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"   },
@@ -176,10 +178,10 @@ static argtype vars[] = {
   {&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},
@@ -277,108 +279,9 @@ latin1_to_ascii (char *s)
     }
 }
 
-\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)
-{
-  XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
-  char *oprogram = get_string_resource (sc->dpy, "program", "Program");
-  char *program = (char *) malloc (strlen (oprogram) + 10);
-  strcpy (program, "( ");
-  strcat (program, oprogram);
-  strcat (program, " ) 2>&1");
-
-  if ((sc->pipe = popen (program, "r")))
-    {
-      sc->pipe_id =
-        XtAppAddInput (app, fileno (sc->pipe),
-                       (XtPointer) (XtInputReadMask | XtInputExceptMask),
-                       subproc_cb, (XtPointer) sc);
-    }
-  else
-    {
-      perror (program);
-    }
-}
-
-
-static void
-relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
-{
-  sws_configuration *sc = (sws_configuration *) closure;
-  if (!sc->pipe_timer) abort();
-  sc->pipe_timer = 0;
-  launch_text_generator (sc);
-}
-
-
-/* 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)
-{
-  XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
-  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. */
-          sc->pipe_timer = XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
-                                            relaunch_generator_timer,
-                                            (XtPointer) sc);
-        }
-    }
-}
-
 
 static int
-string_width (sws_configuration *sc, const char *s)
+sw_string_width (sws_configuration *sc, const char *s)
 {
   if (textures_p)
     return texture_string_width (sc->texfont, s, 0);
@@ -392,12 +295,11 @@ char_width (sws_configuration *sc, char c)
   char s[2];
   s[0] = c;
   s[1] = 0;
-  return string_width (sc, s);
+  return sw_string_width (sc, s);
 }
 
 
-/* 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)
@@ -409,6 +311,21 @@ get_more_lines (sws_configuration *sc)
   int col_pix = 0;
 
   char *s = sc->buf;
+
+  int target = sc->buf_size - sc->buf_tail - 2;
+
+  /* Fill as much as we can into sc->buf.
+   */
+  while (target > 0)
+    {
+      int c = textclient_getc (sc->tc);
+      if (c <= 0)
+        break;
+      sc->buf[sc->buf_tail++] = (char) c;
+      sc->buf[sc->buf_tail] = 0;
+      target--;
+    }
+
   while (sc->total_lines < max_lines)
     {
       int cw;
@@ -527,7 +444,7 @@ draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
       while (*s)
         {
           *c = *s++;
-          w = string_width (sc, c);
+          w = sw_string_width (sc, c);
           glBegin (GL_LINE_LOOP);
           glVertex3f (0, 0, 0);
           glVertex3f (w, 0, 0);
@@ -667,6 +584,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
     {
@@ -686,6 +604,19 @@ reshape_sws (ModeInfo *mi, int width, int height)
     gluLookAt (0.0, 0.0, 4600.0,
                0.0, 0.0, 0.0,
                0.0, 1.0, 0.0);
+
+    glRotatef(rot, 0, 0, 1);
+
+    /* 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
@@ -709,8 +640,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);
@@ -789,6 +720,8 @@ init_sws (ModeInfo *mi)
   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));
   }
 
@@ -804,16 +737,21 @@ init_sws (ModeInfo *mi)
 
       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
     {
@@ -859,7 +797,6 @@ init_sws (ModeInfo *mi)
   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)
@@ -881,7 +818,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));
@@ -907,6 +844,10 @@ 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);
+
+      /* Keep the stars pointing in the same direction after rotation */
+      glRotatef(current_device_rotation(), 0, 0, 1);
+
       glCallList (sc->star_list);
       if (textures_p) glEnable (GL_TEXTURE_2D);
     }
@@ -928,11 +869,6 @@ draw_sws (ModeInfo *mi)
   if (!sc->glx_context)
     return;
 
-#if 0
-  if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
-    XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
-#endif
-
   glDrawBuffer (GL_BACK);
   glXMakeCurrent (dpy, window, *(sc->glx_context));
 
@@ -943,14 +879,33 @@ draw_sws (ModeInfo *mi)
   glMatrixMode (GL_MODELVIEW);
   glPushMatrix ();
 
+# ifdef USE_IPHONE
+  /* 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.4, 0.4, 0.4);
       for (i = 0; i < 16; i++)
         {
           box (1, 1, 1);
@@ -1045,7 +1000,7 @@ draw_sws (ModeInfo *mi)
 
           if (alignment >= 0)
             {
-              int n = string_width (sc, line);
+              int n = sw_string_width (sc, line);
               xoff = 1.0 - (n * sc->font_scale);
             }
 
@@ -1078,12 +1033,8 @@ release_sws (ModeInfo *mi)
     int screen;
     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
       sws_configuration *sc = &scs[screen];
-      if (sc->pipe_id)
-        XtRemoveInput (sc->pipe_id);
-      if (sc->pipe)
-        pclose (sc->pipe);
-      if (sc->pipe_timer)
-        XtRemoveTimeOut (sc->pipe_timer);
+      if (sc->tc)
+        textclient_close (sc->tc);
 
       /* #### there's more to free here */
     }
@@ -1094,6 +1045,12 @@ release_sws (ModeInfo *mi)
 }
 
 
+#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 */