http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.02.tar.gz
[xscreensaver] / hacks / glx / fps.c
1 /* tube, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
2  * Utility function to draw a frames-per-second display.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #include "config.h"
14 #include <stdlib.h>
15 #include <stdio.h>
16
17 #include "screenhack.h"
18 #include "xlockmoreI.h"
19
20 #include <GL/gl.h>
21 #include <GL/glu.h>
22 #include <GL/glx.h>
23
24 #undef DEBUG  /* Defining this causes check_gl_error() to be called inside
25                  time-critical sections, which could slow things down (since
26                  it might result in a round-trip, and stall of the pipeline.)
27                */
28
29 extern void clear_gl_error (void);
30 extern void check_gl_error (const char *type);
31
32 static int fps_text_x = 10;
33 static int fps_text_y = 10;
34 static int fps_ascent, fps_descent;
35 static GLuint font_dlist;
36 static Bool fps_clear_p = False;
37
38 static void
39 fps_init (ModeInfo *mi)
40 {
41   const char *font = get_string_resource ("fpsFont", "Font");
42   XFontStruct *f;
43   Font id;
44   int first, last;
45
46   fps_clear_p = get_boolean_resource ("fpsSolid", "FPSSolid");
47
48   if (!font) font = "-*-courier-bold-r-normal-*-180-*";
49   f = XLoadQueryFont(mi->dpy, font);
50   if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
51
52   id = f->fid;
53   first = f->min_char_or_byte2;
54   last = f->max_char_or_byte2;
55   
56   clear_gl_error ();
57   font_dlist = glGenLists ((GLuint) last+1);
58   check_gl_error ("glGenLists");
59
60   fps_ascent = f->ascent;
61   fps_descent = f->descent;
62
63   if (get_boolean_resource ("fpsTop", "FPSTop"))
64     fps_text_y = - (f->ascent + 10);
65
66   glXUseXFont(id, first, last-first+1, font_dlist + first);
67   check_gl_error ("glXUseXFont");
68 }
69
70
71 static void
72 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
73 {
74   if (y < 0)
75     y = mi->xgwa.height + y;
76
77 # ifdef DEBUG
78   clear_gl_error ();
79 # endif
80
81   /* Sadly, this causes a stall of the graphics pipeline (as would the
82      equivalent calls to glGet*.)  But there's no way around this, short
83      of having each caller set up the specific display matrix we need
84      here, which would kind of defeat the purpose of centralizing this
85      code in one file.
86    */
87   glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
88                GL_ENABLE_BIT |     /* for various glDisable calls */
89                GL_CURRENT_BIT |    /* for glColor3f() */
90                GL_LIST_BIT);       /* for glListBase() */
91   {
92 # ifdef DEBUG
93     check_gl_error ("glPushAttrib");
94 # endif
95
96     /* disable lighting and texturing when drawing bitmaps!
97        (glPopAttrib() restores these, I believe.)
98      */
99     glDisable(GL_TEXTURE_2D);
100     glDisable(GL_LIGHTING);
101     glDisable(GL_BLEND);
102     glDisable(GL_DEPTH_TEST);
103
104     /* glPopAttrib() does not restore matrix changes, so we must
105        push/pop the matrix stacks to be non-intrusive there.
106      */
107     glMatrixMode(GL_PROJECTION);
108     glPushMatrix();
109     {
110 # ifdef DEBUG
111       check_gl_error ("glPushMatrix");
112 # endif
113       glLoadIdentity();
114
115       /* Each matrix mode has its own stack, so we need to push/pop
116          them separately. */
117       glMatrixMode(GL_MODELVIEW);
118       glPushMatrix();
119       {
120 # ifdef DEBUG
121         check_gl_error ("glPushMatrix");
122 # endif
123         glLoadIdentity();
124
125         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
126 # ifdef DEBUG
127         check_gl_error ("gluOrtho2D");
128 # endif
129
130         /* clear the background */
131         if (fps_clear_p)
132           {
133             glColor3f (0, 0, 0);
134             glRecti (x / 2, y - fps_descent,
135                      mi->xgwa.width - x,
136                      y + fps_ascent + fps_descent);
137           }
138
139         /* draw the text */
140         glColor3f (1, 1, 1);
141         glRasterPos2f (x, y);
142         glListBase (font_dlist);
143         glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
144
145 # ifdef DEBUG
146         check_gl_error ("fps_print_string");
147 # endif
148       }
149       glPopMatrix();
150     }
151     glMatrixMode(GL_PROJECTION);
152     glPopMatrix();
153
154   }
155   /* clean up after our state changes */
156   glPopAttrib();
157 # ifdef DEBUG
158   check_gl_error ("glPopAttrib");
159 # endif
160 }
161
162
163 void
164 do_fps (ModeInfo *mi)
165 {
166   static Bool initted_p = False;
167   static int last_ifps = 0;
168   static int frame_count = 0;
169   static struct timeval prev = { 0, };
170   static struct timeval now  = { 0, };
171   static char msg [1024] = { 0, };
172
173   if (!initted_p)
174     {
175       initted_p = True;
176       fps_init (mi);
177       strcpy (msg, "FPS: (accumulating...)");
178     }
179
180   /* Every N frames (where N is approximately one second's worth of frames)
181      check the wall clock.  We do this because checking the wall clock is
182      a slow operation.
183    */
184   if (frame_count++ >= last_ifps)
185     {
186 # ifdef GETTIMEOFDAY_TWO_ARGS
187       struct timezone tzp;
188       gettimeofday(&now, &tzp);
189 # else
190       gettimeofday(&now);
191 # endif
192
193       if (prev.tv_sec == 0)
194         prev = now;
195     }
196
197   /* If we've probed the wall-clock time, regenerate the string.
198    */
199   if (now.tv_sec != prev.tv_sec)
200     {
201       double uprev = prev.tv_sec + ((double) prev.tv_usec * 0.000001);
202       double unow  =  now.tv_sec + ((double)  now.tv_usec * 0.000001);
203       double fps   = frame_count / (unow - uprev);
204
205       prev = now;
206       frame_count = 0;
207       last_ifps = fps;
208
209       sprintf (msg, "FPS: %.02f", fps);
210
211       if (mi->pause != 0)
212         {
213           char buf[40];
214           sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
215           while(*buf && buf[strlen(buf)-1] == '0')
216             buf[strlen(buf)-1] = 0;
217           if (buf[strlen(buf)-1] == '.')
218             buf[strlen(buf)-1] = 0;
219           sprintf(msg + strlen(msg), " (including %s sec/frame delay)",
220                   buf);
221         }
222     }
223
224   /* Print the string every frame (or else nothing will show up.)
225    */
226   fps_print_string (mi, fps_text_x, fps_text_y, msg);
227 }