From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / glxfonts.c
index 3443e4f45e10406b34e9844b3515023bff93475a..1519006d4edb7c1726684dd94cda16e46d959261 100644 (file)
@@ -1,5 +1,4 @@
-/* glxfonts, Copyright (c) 2001-2004 Jamie Zawinski <jwz@jwz.org>
- * Loads X11 fonts for use with OpenGL.
+/* glxfonts, Copyright (c) 2001-2014 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
@@ -8,18 +7,38 @@
  * documentation.  No representations are made about the suitability of this
  * software for any purpose.  It is provided "as is" without express or 
  * implied warranty.
+ *
+ * Draws 2D text over the GL scene, e.g., the FPS displays.
+ * Also billboarding.
+ * The lower-level character rendering code is in texfont.c.
  */
 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
-#include "config.h"
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
-#include <GL/glx.h>
-#include <GL/glu.h>
+
+#ifdef HAVE_COCOA
+# include "jwxyz.h"
+/*# include <AGL/agl.h>*/
+#else
+# include <GL/glx.h>
+# include <GL/glu.h>
+#endif
+
+#ifdef HAVE_JWZGLES
+# include "jwzgles.h"
+#endif /* HAVE_JWZGLES */
+
 #include "resources.h"
 #include "glxfonts.h"
+#include "texfont.h"
+#include "fps.h"
+
 
 /* These are in xlock-gl.c */
 extern void clear_gl_error (void);
@@ -29,167 +48,190 @@ extern void check_gl_error (const char *type);
 extern char *progname;
 
 
-/* Loads the font named by the X resource "res".
-   Returns an XFontStruct.
-   Also converts the font to a set of GL lists and returns the first list.
-*/
-void
-load_font (Display *dpy, char *res, XFontStruct **font_ret, GLuint *dlist_ret)
-{
-  const char *font = get_string_resource (res, "Font");
-  const char *def1 = "-*-times-bold-r-normal-*-180-*";
-  const char *def2 = "fixed";
-  XFontStruct *f;
-  Font id;
-  int first, last;
-
-  if (!res || !*res) abort();
-  if (!font_ret && !dlist_ret) abort();
-
-  if (!font) font = def1;
+#undef DEBUG  /* Defining this causes check_gl_error() to be called inside
+                 time-critical sections, which could slow things down (since
+                 it might result in a round-trip, and stall of the pipeline.)
+               */
 
-  f = XLoadQueryFont(dpy, font);
-  if (!f && !!strcmp (font, def1))
-    {
-      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
-               progname, font, def1);
-      font = def1;
-      f = XLoadQueryFont(dpy, font);
-    }
-
-  if (!f && !!strcmp (font, def2))
-    {
-      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
-               progname, font, def2);
-      font = def2;
-      f = XLoadQueryFont(dpy, font);
-    }
-
-  if (!f)
-    {
-      fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
-               progname, font);
-      exit (1);
-    }
-
-  id = f->fid;
-  first = f->min_char_or_byte2;
-  last = f->max_char_or_byte2;
-  
-  if (dlist_ret)
-    {
-      clear_gl_error ();
-      *dlist_ret = glGenLists ((GLuint) last+1);
-      check_gl_error ("glGenLists");
-      glXUseXFont(id, first, last-first+1, *dlist_ret + first);
-      check_gl_error ("glXUseXFont");
-    }
-
-  if (font_ret)
-    *font_ret = f;
-}
-
-
-/* Width of the string in pixels.
- */
-int
-string_width (XFontStruct *f, const char *c)
-{
-  int w = 0;
-  while (*c)
-    {
-      int cc = *((unsigned char *) c);
-      w += (f->per_char
-            ? f->per_char[cc-f->min_char_or_byte2].rbearing
-            : f->min_bounds.rbearing);
-      c++;
-    }
-  return w;
-}
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
 
 
 /* Draws the string on the window at the given pixel position.
    Newlines and tab stops are honored.
-   Any text inside [] will be rendered as a subscript.
-   Assumes the font has been loaded as with load_font().
+   Any numbers inside [] will be rendered as a subscript.
+   Assumes the font has been loaded as with load_texture_font().
+
+   If width and height are 0, then instead the text is placed
+   into the 3D scene at the origin, billboarded to face the
+   viewer.
  */
 void
 print_gl_string (Display *dpy,
-                 XFontStruct *font,
-                 GLuint font_dlist,
+                 texture_font_data *data,
                  int window_width, int window_height,
                  GLfloat x, GLfloat y,
-                 const char *string)
+                 const char *string,
+                 Bool clear_background_p)
 {
-  GLfloat line_height = font->ascent + font->descent;
-  GLfloat sub_shift = (line_height * 0.3);
-  int cw = string_width (font, "m");
-  int tabs = cw * 7;
-
+  Bool in_scene_p = (window_width == 0);
+
+  int line_height = 0;
+  int lines = 0;
+  const char *c;
+  GLfloat color[4];
+
+  Bool tex_p   = glIsEnabled (GL_TEXTURE_2D);
+  Bool texs_p  = glIsEnabled (GL_TEXTURE_GEN_S);
+  Bool text_p  = glIsEnabled (GL_TEXTURE_GEN_T);
+  Bool light_p = glIsEnabled (GL_LIGHTING);
+  Bool blend_p = glIsEnabled (GL_BLEND);
+  Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
+  Bool cull_p  = glIsEnabled (GL_CULL_FACE);
+  Bool fog_p   = glIsEnabled (GL_FOG);
+  GLint oblend;
+  GLint ovp[4];
+
+#  ifndef HAVE_JWZGLES
+  GLint opoly[2];
+  glGetIntegerv (GL_POLYGON_MODE, opoly);
+#  endif
+
+  if (!in_scene_p)
+    glGetIntegerv (GL_VIEWPORT, ovp);
+
+  glGetIntegerv (GL_BLEND_DST, &oblend);
+  glGetFloatv (GL_CURRENT_COLOR, color);
+
+  for (c = string; *c; c++)
+    if (*c == '\n') lines++;
+
+  texture_string_width (data, "m", &line_height);
   y -= line_height;
 
-  glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
-                GL_ENABLE_BIT);     /* for various glDisable calls */
+
+  glEnable (GL_TEXTURE_2D);
+  glEnable (GL_BLEND);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glPolygonMode (GL_FRONT, GL_FILL);
+
+  glDisable (GL_TEXTURE_GEN_S);
+  glDisable (GL_TEXTURE_GEN_T);
   glDisable (GL_LIGHTING);
-  glDisable (GL_DEPTH_TEST);
-  glDisable (GL_TEXTURE_2D);
+  glDisable (GL_CULL_FACE);
+  glDisable (GL_FOG);
+
+  if (!in_scene_p)
+    glDisable (GL_DEPTH_TEST);
+
+  /* Each matrix mode has its own stack, so we need to push/pop
+     them separately.
+   */
+  glMatrixMode(GL_PROJECTION);
+  glPushMatrix();
   {
-    glMatrixMode(GL_PROJECTION);
+# ifdef DEBUG
+    check_gl_error ("glPushMatrix");
+# endif
+    if (!in_scene_p)
+      glLoadIdentity();
+
+    glMatrixMode(GL_MODELVIEW);
     glPushMatrix();
     {
-      glLoadIdentity();
+# ifdef DEBUG
+      check_gl_error ("glPushMatrix");
+# endif
+
+      if (!in_scene_p)
+        {
+          int rot = (int) current_device_rotation();
+          while (rot <= -180) rot += 360;
+          while (rot >   180) rot -= 360;
+
+          glLoadIdentity();
+          glViewport (0, 0, window_width, window_height);
+          glOrtho (0, window_width, 0, window_height, -1, 1);
+
+          if (rot > 135 || rot < -135)         /* 180 */
+            {
+              glTranslatef (window_width, window_height, 0);
+              glRotatef (180, 0, 0, 1);
+            }
+          else if (rot > 45)                   /* 90 */
+            {
+              glTranslatef (window_width, 0, 0);
+              glRotatef (90, 0, 0, 1);
+            }
+          else if (rot < -45)                  /* 270 */
+            {
+              glTranslatef(0, window_height, 0);
+              glRotatef (-90, 0, 0, 1);
+            }
+        }
+
+# ifdef DEBUG
+      check_gl_error ("glOrtho");
+# endif
+
+      /* Let's always dropshadow the FPS and Title text. */
+      if (! in_scene_p)
+        clear_background_p = True;
+
+
+      /* draw the text */
+
+      glTranslatef (x, y, 0);
 
-      glMatrixMode(GL_MODELVIEW);
-      glPushMatrix();
       {
-        unsigned int i;
-        int x2 = x;
-        Bool sub_p = False;
-        glLoadIdentity();
-
-        gluOrtho2D (0, window_width, 0, window_height);
-
-        glRasterPos2f (x, y);
-        for (i = 0; i < strlen(string); i++)
+        const XPoint offsets[] = {{ -1, -1 },
+                                  { -1,  1 },
+                                  {  1,  1 },
+                                  {  1, -1 },
+                                  {  0,  0 }};
+        int i;
+
+        glColor3f (0, 0, 0);
+        for (i = 0; i < countof(offsets); i++)
           {
-            char c = string[i];
-            if (c == '\n')
-              {
-                glRasterPos2f (x, (y -= line_height));
-                x2 = x;
-              }
-            else if (c == '\t')
-              {
-                x2 -= x;
-                x2 = ((x2 + tabs) / tabs) * tabs;  /* tab to tab stop */
-                x2 += x;
-                glRasterPos2f (x2, y);
-              }
-            else if (c == '[' && (isdigit (string[i+1])))
-              {
-                sub_p = True;
-                glRasterPos2f (x2, (y -= sub_shift));
-              }
-            else if (c == ']' && sub_p)
-              {
-                sub_p = False;
-                glRasterPos2f (x2, (y += sub_shift));
-              }
-            else
-              {
-                glCallList (font_dlist + (int)(c));
-                x2 += (font->per_char
-                       ? font->per_char[c - font->min_char_or_byte2].width
-                       : font->min_bounds.width);
-              }
+            if (! clear_background_p)
+              i = countof(offsets)-1;
+            if (offsets[i].x == 0)
+              glColor4fv (color);
+
+            glPushMatrix();
+            glTranslatef (offsets[i].x, offsets[i].y, 0);
+            print_texture_string (data, string);
+            glPopMatrix();
           }
       }
-      glPopMatrix();
+
+# ifdef DEBUG
+      check_gl_error ("print_texture_string");
+# endif
     }
-    glMatrixMode(GL_PROJECTION);
     glPopMatrix();
   }
-  glPopAttrib();
+  glMatrixMode(GL_PROJECTION);
+  glPopMatrix();
+
+  if (tex_p)   glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
+  if (texs_p)  glEnable (GL_TEXTURE_GEN_S);/*else glDisable(GL_TEXTURE_GEN_S);*/
+  if (text_p)  glEnable (GL_TEXTURE_GEN_T);/*else glDisable(GL_TEXTURE_GEN_T);*/
+  if (blend_p) glEnable (GL_BLEND); else glDisable (GL_BLEND);
+  if (light_p) glEnable (GL_LIGHTING); /*else glDisable (GL_LIGHTING);*/
+  if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
+  if (cull_p)  glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
+  if (fog_p)   glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
+
+  if (!in_scene_p)
+    glViewport (ovp[0], ovp[1], ovp[2], ovp[3]);
+
+  glBlendFunc (GL_SRC_ALPHA, oblend);
+
+# ifndef HAVE_JWZGLES
+  glPolygonMode (GL_FRONT, opoly[0]);
+# endif
 
   glMatrixMode(GL_MODELVIEW);
 }