http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / hacks / glx / fps.c
index 7da5123b7da09eea6d468fc643d875174bb1558c..6e9f0e79157f8fc014b6dc612e4714bde2c3676f 100644 (file)
@@ -1,4 +1,4 @@
-/* tube, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
+/* fps, Copyright (c) 2001-2007 Jamie Zawinski <jwz@jwz.org>
  * Utility function to draw a frames-per-second display.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * implied warranty.
  */
 
-#include "config.h"
-#include <stdlib.h>
-#include <stdio.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
 
-#include "screenhack.h"
 #include "xlockmoreI.h"
 
-#include <GL/gl.h>
-#include <GL/glu.h>
-#include <GL/glx.h>
+#ifdef HAVE_COCOA
+# undef usleep /* conflicts with 10.5 headers... */
+# include <OpenGL/gl.h>
+# include <OpenGL/glu.h>
+# include <AGL/agl.h>
+#else /* !HAVE_COCOA -- real Xlib */
+# include <GL/gl.h>
+# include <GL/glu.h>
+# include <GL/glx.h>
+#endif /* !HAVE_COCOA */
 
 #undef DEBUG  /* Defining this causes check_gl_error() to be called inside
                  time-critical sections, which could slow things down (since
 extern void clear_gl_error (void);
 extern void check_gl_error (const char *type);
 
-static int fps_text_x = 10;
-static int fps_text_y = 10;
-static int fps_ascent, fps_descent;
-static GLuint font_dlist;
-static Bool fps_clear_p = False;
+extern GLfloat fps_1 (ModeInfo *mi);
+extern void    fps_2 (ModeInfo *mi);
+extern void    do_fps (ModeInfo *mi);
+extern void    fps_free (ModeInfo *mi);
+
+struct fps_state {
+  int x, y;
+  int ascent, descent;
+  GLuint font_dlist;
+  Bool clear_p;
+  char string[1024];
+
+  int last_ifps;
+  GLfloat last_fps;
+  int frame_count;
+  struct timeval prev, now;
+};
+
 
 static void
 fps_init (ModeInfo *mi)
 {
-  const char *font = get_string_resource ("fpsFont", "Font");
-  XFontStruct *f;
-  Font id;
-  int first, last;
+  struct fps_state *st = mi->fps_state;
 
-  fps_clear_p = get_boolean_resource ("fpsSolid", "FPSSolid");
+  if (st) free (st);
+  st = (struct fps_state *) calloc (1, sizeof(*st));
+  mi->fps_state = st;
+
+  st->clear_p = get_boolean_resource (mi->dpy, "fpsSolid", "FPSSolid");
+
+# ifndef HAVE_COCOA /* Xlib version */
+  {
+    const char *font;
+    XFontStruct *f;
+    Font id;
+    int first, last;
 
-  if (!font) font = "-*-courier-bold-r-normal-*-180-*";
-  f = XLoadQueryFont(mi->dpy, font);
-  if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
+    font = get_string_resource (mi->dpy, "fpsFont", "Font");
 
-  id = f->fid;
-  first = f->min_char_or_byte2;
-  last = f->max_char_or_byte2;
+    if (!font) font = "-*-courier-bold-r-normal-*-180-*";
+    f = XLoadQueryFont (mi->dpy, font);
+    if (!f) f = XLoadQueryFont (mi->dpy, "fixed");
+
+    id = f->fid;
+    first = f->min_char_or_byte2;
+    last = f->max_char_or_byte2;
   
-  clear_gl_error ();
-  font_dlist = glGenLists ((GLuint) last+1);
-  check_gl_error ("glGenLists");
+    clear_gl_error ();
+    st->font_dlist = glGenLists ((GLuint) last+1);
+    check_gl_error ("glGenLists");
+
+    st->ascent = f->ascent;
+    st->descent = f->descent;
+
+    glXUseXFont (id, first, last-first+1, st->font_dlist + first);
+    check_gl_error ("glXUseXFont");
+  }
 
-  fps_ascent = f->ascent;
-  fps_descent = f->descent;
+# else  /* HAVE_COCOA */
 
-  if (get_boolean_resource ("fpsTop", "FPSTop"))
-    fps_text_y = - (f->ascent + 10);
+  {
+    AGLContext ctx = aglGetCurrentContext();
+    GLint id    = 0;   /* 0 = system font; 1 = application font */
+    Style face  = 1;   /* 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc. */
+    GLint size  = 24;
+    GLint first = 32;
+    GLint last  = 255;
+
+    st->ascent  = size * 0.9;
+    st->descent = size - st->ascent;
+
+    clear_gl_error ();
+    st->font_dlist = glGenLists ((GLuint) last+1);
+    check_gl_error ("glGenLists");
+
+    if (! aglUseFont (ctx, id, face, size, 
+                      first, last-first+1, st->font_dlist + first)) {
+      check_gl_error ("aglUseFont");
+      abort();
+    }
+  }
 
-  glXUseXFont(id, first, last-first+1, font_dlist + first);
-  check_gl_error ("glXUseXFont");
+# endif  /* HAVE_COCOA */
+
+  st->x = 10;
+  st->y = 10;
+  if (get_boolean_resource (mi->dpy, "fpsTop", "FPSTop"))
+    st->y = - (st->ascent + 10);
 }
 
+void
+fps_free (ModeInfo *mi)
+{
+  if (mi->fps_state)
+    free (mi->fps_state);
+  mi->fps_state = 0;
+}
 
 static void
 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
 {
+  struct fps_state *st = mi->fps_state;
+  const char *L2 = strchr (string, '\n');
+
   if (y < 0)
-    y = mi->xgwa.height + y;
+    {
+      y = mi->xgwa.height + y;
+      if (L2)
+        y -= (st->ascent + st->descent);
+    }
 
 # ifdef DEBUG
   clear_gl_error ();
@@ -84,22 +156,23 @@ fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
      here, which would kind of defeat the purpose of centralizing this
      code in one file.
    */
-  glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
-               GL_ENABLE_BIT |     /* for various glDisable calls */
-               GL_CURRENT_BIT |    /* for glColor3f() */
-               GL_LIST_BIT);       /* for glListBase() */
+  glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
+                GL_ENABLE_BIT |     /* for various glDisable calls */
+                GL_CURRENT_BIT |    /* for glColor3f() */
+                GL_LIST_BIT);       /* for glListBase() */
   {
 # ifdef DEBUG
     check_gl_error ("glPushAttrib");
 # endif
 
     /* disable lighting and texturing when drawing bitmaps!
-       (glPopAttrib() restores these, I believe.)
+       (glPopAttrib() restores these.)
      */
-    glDisable(GL_TEXTURE_2D);
-    glDisable(GL_LIGHTING);
-    glDisable(GL_BLEND);
-    glDisable(GL_DEPTH_TEST);
+    glDisable (GL_TEXTURE_2D);
+    glDisable (GL_LIGHTING);
+    glDisable (GL_BLEND);
+    glDisable (GL_DEPTH_TEST);
+    glDisable (GL_CULL_FACE);
 
     /* glPopAttrib() does not restore matrix changes, so we must
        push/pop the matrix stacks to be non-intrusive there.
@@ -128,19 +201,31 @@ fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
 # endif
 
         /* clear the background */
-        if (fps_clear_p)
+        if (st->clear_p)
           {
+            int lines = L2 ? 2 : 1;
             glColor3f (0, 0, 0);
-            glRecti (x / 2, y - fps_descent,
+            glRecti (x / 2, y - st->descent,
                      mi->xgwa.width - x,
-                     y + fps_ascent + fps_descent);
+                     y + lines * (st->ascent + st->descent));
           }
 
         /* draw the text */
         glColor3f (1, 1, 1);
         glRasterPos2f (x, y);
-        glListBase (font_dlist);
-        glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
+        glListBase (st->font_dlist);
+
+        if (L2)
+          {
+            L2++;
+            glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
+            glRasterPos2f (x, y + (st->ascent + st->descent));
+            glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
+          }
+        else
+          {
+            glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
+          }
 
 # ifdef DEBUG
         check_gl_error ("fps_print_string");
@@ -160,54 +245,52 @@ fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
 }
 
 
-void
-do_fps (ModeInfo *mi)
+GLfloat
+fps_1 (ModeInfo *mi)
 {
-  static Bool initted_p = False;
-  static int last_ifps = 0;
-  static int frame_count = 0;
-  static struct timeval prev = { 0, };
-  static struct timeval now  = { 0, };
-  static char msg [1024] = { 0, };
-
-  if (!initted_p)
+  struct fps_state *st = mi->fps_state;
+  if (!st)
     {
-      initted_p = True;
       fps_init (mi);
-      strcpy (msg, "FPS: (accumulating...)");
+      st = mi->fps_state;
+      strcpy (st->string, "FPS: (accumulating...)");
     }
 
   /* Every N frames (where N is approximately one second's worth of frames)
      check the wall clock.  We do this because checking the wall clock is
      a slow operation.
    */
-  if (frame_count++ >= last_ifps)
+  if (st->frame_count++ >= st->last_ifps)
     {
 # ifdef GETTIMEOFDAY_TWO_ARGS
       struct timezone tzp;
-      gettimeofday(&now, &tzp);
+      gettimeofday(&st->now, &tzp);
 # else
-      gettimeofday(&now);
+      gettimeofday(&st->now);
 # endif
 
-      if (prev.tv_sec == 0)
-        prev = now;
+      if (st->prev.tv_sec == 0)
+        st->prev = st->now;
     }
 
   /* If we've probed the wall-clock time, regenerate the string.
    */
-  if (now.tv_sec != prev.tv_sec)
+  if (st->now.tv_sec != st->prev.tv_sec)
     {
-      double uprev = prev.tv_sec + ((double) prev.tv_usec * 0.000001);
-      double unow  =  now.tv_sec + ((double)  now.tv_usec * 0.000001);
-      double fps   = frame_count / (unow - uprev);
+      double uprev = st->prev.tv_sec + ((double) st->prev.tv_usec * 0.000001);
+      double unow  =  st->now.tv_sec + ((double)  st->now.tv_usec * 0.000001);
+      double fps   = st->frame_count / (unow - uprev);
 
-      prev = now;
-      frame_count = 0;
-      last_ifps = fps;
+      st->prev = st->now;
+      st->frame_count = 0;
+      st->last_ifps = fps;
+      st->last_fps  = fps;
 
-      sprintf (msg, "FPS: %.02f", fps);
+      sprintf (st->string, "FPS: %.02f", fps);
 
+#ifndef HAVE_COCOA
+      /* since there's no "-delay 0" in the Cocoa version,
+         don't bother mentioning the inter-frame delay. */
       if (mi->pause != 0)
         {
           char buf[40];
@@ -216,12 +299,47 @@ do_fps (ModeInfo *mi)
             buf[strlen(buf)-1] = 0;
           if (buf[strlen(buf)-1] == '.')
             buf[strlen(buf)-1] = 0;
-          sprintf(msg + strlen(msg), " (including %s sec/frame delay)",
+          sprintf(st->string + strlen(st->string),
+                  " (including %s sec/frame delay)",
                   buf);
         }
+#endif /* HAVE_COCOA */
+
+      if (mi->polygon_count > 0)
+        {
+          unsigned long p = mi->polygon_count;
+          const char *s = "";
+# if 0
+          if      (p >= (1024 * 1024)) p >>= 20, s = "M";
+          else if (p >= 2048)          p >>= 10, s = "K";
+# endif
+
+          strcat (st->string, "\nPolys: ");
+          if (p >= 1000000)
+            sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s",
+                     (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
+          else if (p >= 1000)
+            sprintf (st->string + strlen(st->string), "%lu,%03lu%s",
+                     (p / 1000), (p % 1000), s);
+          else
+            sprintf (st->string + strlen(st->string), "%lu%s", p, s);
+        }
     }
 
-  /* Print the string every frame (or else nothing will show up.)
-   */
-  fps_print_string (mi, fps_text_x, fps_text_y, msg);
+  return st->last_fps;
+}
+
+void
+fps_2 (ModeInfo *mi)
+{
+  struct fps_state *st = mi->fps_state;
+  fps_print_string (mi, st->x, st->y, st->string);
+}
+
+
+void
+do_fps (ModeInfo *mi)
+{
+  fps_1 (mi);   /* Lazily compute current FPS value, about once a second. */
+  fps_2 (mi);   /* Print the string every frame (else nothing shows up.) */
 }