From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / fliptext.c
index 3d134b473d0343779bc84fd1d97fc07d659dc70a..d35706cb2a5ad0325c22c865f9341f72f2271c43 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * fliptext, Copyright (c) 2005 Jamie Zawinski <jwz@jwz.org>
+ * fliptext, Copyright (c) 2005-2015 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * implied warranty.
  */
 
-#include <X11/Intrinsic.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
 
-extern XtAppContext app;
-
-#define PROGCLASS      "FlipText"
-#define HACK_INIT      init_fliptext
-#define HACK_DRAW      draw_fliptext
-#define HACK_RESHAPE   reshape_fliptext
-#define fliptext_opts  xlockmore_opts
-
-#define DEF_PROGRAM    "xscreensaver-text --cols 0"  /* don't wrap */
-#define DEF_LINES      "8"
-#define DEF_FONT_SIZE  "20"
-#define DEF_COLUMNS    "80"
-#define DEF_ALIGN      "random"
+#define DEF_FONT       "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-*-*"
 #define DEF_COLOR      "#00CCFF"
-#define DEF_SPEED       "1.0"
-
-/* 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 TAB_WIDTH        8
-
-#define FONT_WEIGHT       14
-#define KEEP_ASPECT
 
 #define DEFAULTS "*delay:        10000      \n" \
                 "*showFPS:      False      \n" \
                 "*wireframe:    False      \n" \
+                "*usePty:       False      \n" \
+                "*texFontCacheSize: 60     \n" \
                 "*font:       " DEF_FONT  "\n" \
                 ".foreground: " DEF_COLOR "\n" \
+                "*program: xscreensaver-text --cols 0"  /* don't wrap */
 
+# define refresh_fliptext 0
+# define release_fliptext 0
+# define fliptext_handle_event 0
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
@@ -55,17 +36,29 @@ extern XtAppContext app;
 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
 
 #include "xlockmore.h"
+#include "texfont.h"
+#include "textclient.h"
 
 #ifdef USE_GL /* whole file */
 
-#include <ctype.h>
-#include <GL/glu.h>
-#include <sys/stat.h>
-#include "texfont.h"
+/* 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
 
-#ifdef HAVE_UNAME
-# include <sys/utsname.h>
-#endif /* HAVE_UNAME */
+
+#define DEF_LINES      "8"
+#define DEF_FONT_SIZE  "20"
+#define DEF_COLUMNS    "80"
+#define DEF_ALIGNMENT  "random"
+#define DEF_SPEED       "1.0"
+#define TAB_WIDTH        8
+
+#define FONT_WEIGHT       14
+#define KEEP_ASPECT
 
 typedef enum { NEW, HESITATE, IN, LINGER, OUT, DEAD } line_state;
 typedef enum { SCROLL_BOTTOM, SCROLL_TOP, SPIN } line_anim_type;
@@ -89,13 +82,13 @@ typedef struct {
 
 
 typedef struct {
+  Display *dpy;
   GLXContext *glx_context;
 
   texture_font_data *texfont;
+  text_data *tc;
 
-  FILE *pipe;
-  XtInputId pipe_id;
-  Time subproc_relaunch_delay;
+  int alignment;
 
   char *buf;
   int buf_size;
@@ -124,36 +117,34 @@ typedef struct {
 
 static fliptext_configuration *scs = NULL;
 
-static char *program;
 static int max_lines, min_lines;
 static float font_size;
 static int target_columns;
 static char *alignment_str;
-static int alignment, alignment_random_p;
+static int alignment_random_p;
 static GLfloat speed;
 
 static XrmOptionDescRec opts[] = {
-  {"-program",     ".program",   XrmoptionSepArg, 0 },
   {"-lines",       ".lines",     XrmoptionSepArg, 0 },
   {"-size",       ".fontSize",  XrmoptionSepArg, 0 },
   {"-columns",    ".columns",   XrmoptionSepArg, 0 },
   {"-speed",       ".speed",     XrmoptionSepArg, 0 },
 /*{"-font",        ".font",      XrmoptionSepArg, 0 },*/
+  {"-alignment",   ".alignment", XrmoptionSepArg, 0 },
   {"-left",        ".alignment", XrmoptionNoArg,  "Left"   },
   {"-right",       ".alignment", XrmoptionNoArg,  "Right"  },
   {"-center",      ".alignment", XrmoptionNoArg,  "Center" },
 };
 
 static argtype vars[] = {
-  {&program,        "program",   "Program",    DEF_PROGRAM,   t_String},
   {&max_lines,      "lines",     "Integer",    DEF_LINES,     t_Int},
   {&font_size,      "fontSize",  "Float",      DEF_FONT_SIZE, t_Float},
   {&target_columns, "columns",   "Integer",    DEF_COLUMNS,   t_Int},
-  {&alignment_str,  "alignment", "Alignment",  DEF_ALIGN,     t_String},
+  {&alignment_str,  "alignment", "Alignment",  DEF_ALIGNMENT, t_String},
   {&speed,         "speed",     "Speed",      DEF_SPEED,     t_Float},
 };
 
-ModeSpecOpt fliptext_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt fliptext_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
 
 
@@ -218,109 +209,15 @@ strip (char *s, Bool leading, Bool trailing)
 }
 
 
-\f
-/* Subprocess.
-   (This bit mostly cribbed from phosphor.c)
- */
-
-static void drain_input (fliptext_configuration *sc);
-
-static void
-subproc_cb (XtPointer closure, int *source, XtInputId *id)
-{
-  fliptext_configuration *sc = (fliptext_configuration *) closure;
-  drain_input (sc);
-}
-
-
-static void
-launch_text_generator (fliptext_configuration *sc)
-{
-  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")))
-    {
-      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)
-{
-  fliptext_configuration *sc = (fliptext_configuration *) closure;
-  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 (fliptext_configuration *sc)
-{
-  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);
-        }
-    }
-}
-
-
 static int
 char_width (fliptext_configuration *sc, char c)
 {
+  XCharStruct e;
   char s[2];
   s[0] = c;
   s[1] = 0;
-  return texture_string_width (sc->texfont, s, 0);
+  texture_string_metrics (sc->texfont, s, &e, 0, 0);
+  return e.width;
 }
 
 
@@ -335,8 +232,23 @@ get_one_line (fliptext_configuration *sc)
   int wrap_pix = sc->font_wrap_pixels;
   int col = 0;
   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, but stop at newline.
+   */
+  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--;
+      if (c == '\r' || c == '\n')
+        break;
+    }
+
   while (!result)
     {
       int cw;
@@ -382,8 +294,8 @@ get_one_line (fliptext_configuration *sc)
           {
             char *t = result;
             char *ut = untabify (t);
-            strip (ut, (alignment == 0), 1); /* if centering, strip
-                                                leading whitespace too */
+            strip (ut, (sc->alignment == 0), 1); /* if centering, strip
+                                                    leading whitespace too */
             result = ut;
             free (t);
           }
@@ -442,6 +354,7 @@ blank_p (const char *s)
 static line *
 make_line (fliptext_configuration *sc, Bool skip_blanks_p)
 {
+  XCharStruct e;
   line *ln;
   char *s;
 
@@ -458,9 +371,12 @@ make_line (fliptext_configuration *sc, Bool skip_blanks_p)
   ln = (line *) calloc (1, sizeof(*ln));
   ln->text = s;
   ln->state = NEW;
-  ln->width = sc->font_scale * texture_string_width (sc->texfont, s, 0);
+  texture_string_metrics (sc->texfont, s, &e, 0, 0);
+  ln->width = sc->font_scale * e.width;
   ln->height = sc->font_scale * sc->line_height;
 
+  memcpy (ln->color, sc->color, sizeof(ln->color));
+
   sc->nlines++;
   if (sc->lines_size <= sc->nlines)
     {
@@ -515,9 +431,9 @@ draw_line (ModeInfo *mi, line *line)
 
   glRotatef (line->cth, 0, 1, 0);
 
-  if (alignment == 1)
+  if (sc->alignment == 1)
     glTranslatef (-line->width, 0, 0);
-  else if (alignment == 0)
+  else if (sc->alignment == 0)
     glTranslatef (-line->width/2, 0, 0);
 
   glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
@@ -536,8 +452,11 @@ draw_line (ModeInfo *mi, line *line)
       glColor3f (0.4, 0.4, 0.4);
       while (*s)
         {
+          XCharStruct e;
           *c = *s++;
-          w = texture_string_width (sc->texfont, c, &h);
+          texture_string_metrics (sc->texfont, c, &e, 0, 0);
+          w = e.width;
+          h = e.ascent + e.descent;
           glBegin (GL_LINE_LOOP);
           glVertex3f (0, 0, 0);
           glVertex3f (w, 0, 0);
@@ -568,10 +487,10 @@ draw_line (ModeInfo *mi, line *line)
 static void
 tick_line (fliptext_configuration *sc, line *line)
 {
-  int stagger = 60;            /* frames of delay between line spin-outs */
+  int stagger = 30;            /* frames of delay between line spin-outs */
   int slide   = 600;           /* frames in a slide in/out */
   int linger  = 0;             /* frames to pause with no motion */
-  double i;
+  double i, ii;
 
   if (line->state >= DEAD) abort();
   if (++line->step >= line->steps)
@@ -582,6 +501,9 @@ tick_line (fliptext_configuration *sc, line *line)
       if (linger == 0 && line->state == LINGER)
         line->state++;
 
+      if (sc->anim_type != SPIN)
+        stagger *= 2;
+
       switch (line->state)
         {
         case HESITATE:                 /* entering state HESITATE */
@@ -602,7 +524,7 @@ tick_line (fliptext_configuration *sc, line *line)
           break;
 
         case IN:
-          memset (line->color, 0, sizeof (line->color));
+          line->color[3] = 0;
           switch (sc->anim_type)
             {
             case SCROLL_BOTTOM:                /* entering state BOTTOM IN */
@@ -646,7 +568,6 @@ tick_line (fliptext_configuration *sc, line *line)
           break;
 
         case OUT:
-          memcpy (sc->color, line->color, sizeof(line->color));
           switch (sc->anim_type)
             {
             case SCROLL_BOTTOM:                /* entering state BOTTOM OUT */
@@ -684,7 +605,6 @@ tick_line (fliptext_configuration *sc, line *line)
           break;
 
         case LINGER:
-          memcpy (sc->color, line->color, sizeof(line->color));
           line->from = line->to;
           line->steps = linger;
           break;
@@ -701,19 +621,20 @@ tick_line (fliptext_configuration *sc, line *line)
     case IN:
     case OUT:
       i = (double) line->step / line->steps;
-      if (line->state == IN) i = 1-i;
-      i = i * i;
-      if (line->state == IN) i = 1-i;
-
-      line->color[0] = sc->color[0] * (line->state == IN ? i : 1-i);
-      line->color[1] = sc->color[1] * (line->state == IN ? i : 1-i);
-      line->color[2] = sc->color[2] * (line->state == IN ? i : 1-i);
-      line->color[3] = sc->color[3] * (line->state == IN ? i : 1-i);
-
-      line->current.x = line->from.x + (i * (line->to.x - line->from.x));
-      line->current.y = line->from.y + (i * (line->to.y - line->from.y));
-      line->current.z = line->from.z + (i * (line->to.z - line->from.z));
-      line->cth = line->fth + (i * (line->tth - line->fth));
+
+      /* Move along the path exponentially, slow side towards the middle. */
+      if (line->state == OUT)
+        ii = i * i;
+      else
+        ii = 1 - ((1-i) * (1-i));
+
+      line->current.x = line->from.x + (ii * (line->to.x - line->from.x));
+      line->current.y = line->from.y + (ii * (line->to.y - line->from.y));
+      line->current.z = line->from.z + (ii * (line->to.z - line->from.z));
+      line->cth = line->fth + (ii * (line->tth - line->fth));
+
+      if (line->state == OUT) ii = 1-ii;
+      line->color[3] = sc->color[3] * ii;
       break;
 
     case HESITATE:
@@ -741,7 +662,7 @@ reset_lines (ModeInfo *mi)
   sc->rotation.y = 5 - BELLRAND(10);
   sc->rotation.z = 5 - BELLRAND(10);
 
-  switch (random() % 7)
+  switch (random() % 8)
     {
     case 0:  sc->anim_type = SCROLL_TOP;    break;
     case 1:  sc->anim_type = SCROLL_BOTTOM; break;
@@ -766,17 +687,20 @@ reset_lines (ModeInfo *mi)
     maxh = maxy - miny;
       
   if (alignment_random_p)
-    alignment = (random() % 3) - 1;
+    sc->alignment = (random() % 3) - 1;
 
-  if      (alignment == -1) maxx -= maxw;
-  else if (alignment ==  1) minx += maxw;
-  else                      minx += maxw/2, maxx -= maxw/2;
+  if      (sc->alignment == -1) maxx -= maxw;
+  else if (sc->alignment ==  1) minx += maxw;
+  else                          minx += maxw/2, maxx -= maxw/2;
 
   miny += maxh/2;
   maxy -= maxh/2;
 
   sc->mid.x = minx + frand (maxx - minx);
-  sc->mid.y = miny + frand (maxy - miny);
+  if (sc->anim_type == SPIN)
+    sc->mid.y = miny + BELLRAND (maxy - miny);
+  else
+    sc->mid.y = miny + frand (maxy - miny);
 
   sc->in.x  = BELLRAND(sc->right_margin * 2) - sc->right_margin;
   sc->out.x = BELLRAND(sc->right_margin * 2) - sc->right_margin;
@@ -839,7 +763,7 @@ parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
 
 /* Window management, etc
  */
-void
+ENTRYPOINT void
 reshape_fliptext (ModeInfo *mi, int width, int height)
 {
   fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
@@ -864,38 +788,36 @@ reshape_fliptext (ModeInfo *mi, int width, int height)
 }
 
 
-void 
+static void free_fliptext (ModeInfo *mi);
+
+
+ENTRYPOINT void 
 init_fliptext (ModeInfo *mi)
 {
   int wire = MI_IS_WIREFRAME(mi);
 
   fliptext_configuration *sc;
 
-  if (!scs) {
-    scs = (fliptext_configuration *)
-      calloc (MI_NUM_SCREENS(mi), sizeof (fliptext_configuration));
-    if (!scs) {
-      fprintf(stderr, "%s: out of memory\n", progname);
-      exit(1);
-    }
-
-    sc = &scs[MI_SCREEN(mi)];
-    sc->lines = (line **) calloc (max_lines+1, sizeof(char *));
-  }
+  MI_INIT(mi, scs, free_fliptext);
 
   sc = &scs[MI_SCREEN(mi)];
+  sc->lines = (line **) calloc (max_lines+1, sizeof(char *));
+
+  sc->dpy = MI_DISPLAY(mi);
 
   if ((sc->glx_context = init_GL(mi)) != NULL) {
     reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+    clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
   }
 
-  program = get_string_resource ("program", "Program");
-
   {
-    int cw, lh;
+    XCharStruct e;
+    int cw, lh, ascent, descent;
     sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
     check_gl_error ("loading font");
-    cw = texture_string_width (sc->texfont, "n", &lh);
+    texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
+    cw = e.width;
+    lh = ascent + descent;
     sc->char_width = cw;
     sc->line_height = lh;
   }
@@ -907,15 +829,20 @@ init_fliptext (ModeInfo *mi)
       glEnable (GL_ALPHA_TEST);
       glEnable (GL_TEXTURE_2D);
 
-# 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." */
-      glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
-      clear_gl_error();
-# endif
+      if (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);
+      }
     }
   
   /* The default font is (by fiat) "18 points".
@@ -940,19 +867,17 @@ init_fliptext (ModeInfo *mi)
   sc->buf_size = target_columns * max_lines;
   sc->buf = (char *) calloc (1, sc->buf_size);
 
-  sc->subproc_relaunch_delay = 2 * 1000;   /* 2 seconds */
-
   alignment_random_p = False;
   if (!alignment_str || !*alignment_str ||
       !strcasecmp(alignment_str, "left"))
-    alignment = -1;
+    sc->alignment = -1;
   else if (!strcasecmp(alignment_str, "center") ||
            !strcasecmp(alignment_str, "middle"))
-    alignment = 0;
+    sc->alignment = 0;
   else if (!strcasecmp(alignment_str, "right"))
-    alignment = 1;
+    sc->alignment = 1;
   else if (!strcasecmp(alignment_str, "random"))
-    alignment = -1, alignment_random_p = True;
+    sc->alignment = -1, alignment_random_p = True;
 
   else
     {
@@ -962,7 +887,7 @@ init_fliptext (ModeInfo *mi)
       exit (1);
     }
 
-  launch_text_generator (sc);
+  sc->tc = textclient_open (sc->dpy);
 
   if (max_lines < 1) max_lines = 1;
   min_lines = max_lines * 0.66;
@@ -970,7 +895,7 @@ init_fliptext (ModeInfo *mi)
   if (min_lines < 1) min_lines = 1;
 
   parse_color (mi, "foreground",
-               get_string_resource("foreground", "Foreground"),
+               get_string_resource(mi->dpy, "foreground", "Foreground"),
                sc->color);
 
   sc->top_margin = (sc->char_width * 100);
@@ -979,10 +904,11 @@ init_fliptext (ModeInfo *mi)
 }
 
 
-void
+ENTRYPOINT void
 draw_fliptext (ModeInfo *mi)
 {
   fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
+/*  XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
   Display *dpy = MI_DISPLAY(mi);
   Window window = MI_WINDOW(mi);
   int i;
@@ -990,14 +916,19 @@ draw_fliptext (ModeInfo *mi)
   if (!sc->glx_context)
     return;
 
+  glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sc->glx_context));
+
+#if 0
   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+#endif
 
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
   mi->polygon_count = 0;
 
   glPushMatrix();
+  glRotatef(current_device_rotation(), 0, 0, 1);
   {
     GLfloat s = 3.0 / (sc->top_margin - sc->bottom_margin);
     glScalef(s, s, s);
@@ -1051,4 +982,17 @@ draw_fliptext (ModeInfo *mi)
   glXSwapBuffers(dpy, window);
 }
 
+static void
+free_fliptext (ModeInfo *mi)
+{
+  fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
+  if (sc->tc)
+    textclient_close (sc->tc);
+  free(sc->lines);
+
+  /* #### there's more to free here */
+}
+
+XSCREENSAVER_MODULE ("FlipText", fliptext)
+
 #endif /* USE_GL */