http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.04.2.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   const char *L2 = strchr (string, '\n');
75
76   if (y < 0)
77     {
78       y = mi->xgwa.height + y;
79       if (L2)
80         y -= (fps_ascent + fps_descent);
81     }
82
83 # ifdef DEBUG
84   clear_gl_error ();
85 # endif
86
87   /* Sadly, this causes a stall of the graphics pipeline (as would the
88      equivalent calls to glGet*.)  But there's no way around this, short
89      of having each caller set up the specific display matrix we need
90      here, which would kind of defeat the purpose of centralizing this
91      code in one file.
92    */
93   glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
94                GL_ENABLE_BIT |     /* for various glDisable calls */
95                GL_CURRENT_BIT |    /* for glColor3f() */
96                GL_LIST_BIT);       /* for glListBase() */
97   {
98 # ifdef DEBUG
99     check_gl_error ("glPushAttrib");
100 # endif
101
102     /* disable lighting and texturing when drawing bitmaps!
103        (glPopAttrib() restores these, I believe.)
104      */
105     glDisable(GL_TEXTURE_2D);
106     glDisable(GL_LIGHTING);
107     glDisable(GL_BLEND);
108     glDisable(GL_DEPTH_TEST);
109
110     /* glPopAttrib() does not restore matrix changes, so we must
111        push/pop the matrix stacks to be non-intrusive there.
112      */
113     glMatrixMode(GL_PROJECTION);
114     glPushMatrix();
115     {
116 # ifdef DEBUG
117       check_gl_error ("glPushMatrix");
118 # endif
119       glLoadIdentity();
120
121       /* Each matrix mode has its own stack, so we need to push/pop
122          them separately. */
123       glMatrixMode(GL_MODELVIEW);
124       glPushMatrix();
125       {
126 # ifdef DEBUG
127         check_gl_error ("glPushMatrix");
128 # endif
129         glLoadIdentity();
130
131         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
132 # ifdef DEBUG
133         check_gl_error ("gluOrtho2D");
134 # endif
135
136         /* clear the background */
137         if (fps_clear_p)
138           {
139             int lines = L2 ? 2 : 1;
140             glColor3f (0, 0, 0);
141             glRecti (x / 2, y - fps_descent,
142                      mi->xgwa.width - x,
143                      y + lines * (fps_ascent + fps_descent));
144           }
145
146         /* draw the text */
147         glColor3f (1, 1, 1);
148         glRasterPos2f (x, y);
149         glListBase (font_dlist);
150
151         if (L2)
152           {
153             L2++;
154             glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
155             glRasterPos2f (x, y + (fps_ascent + fps_descent));
156             glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
157           }
158         else
159           {
160             glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
161           }
162
163 # ifdef DEBUG
164         check_gl_error ("fps_print_string");
165 # endif
166       }
167       glPopMatrix();
168     }
169     glMatrixMode(GL_PROJECTION);
170     glPopMatrix();
171
172   }
173   /* clean up after our state changes */
174   glPopAttrib();
175 # ifdef DEBUG
176   check_gl_error ("glPopAttrib");
177 # endif
178 }
179
180
181 void
182 do_fps (ModeInfo *mi)
183 {
184   static Bool initted_p = False;
185   static int last_ifps = 0;
186   static int frame_count = 0;
187   static struct timeval prev = { 0, };
188   static struct timeval now  = { 0, };
189   static char msg [1024] = { 0, };
190
191   if (!initted_p)
192     {
193       initted_p = True;
194       fps_init (mi);
195       strcpy (msg, "FPS: (accumulating...)");
196     }
197
198   /* Every N frames (where N is approximately one second's worth of frames)
199      check the wall clock.  We do this because checking the wall clock is
200      a slow operation.
201    */
202   if (frame_count++ >= last_ifps)
203     {
204 # ifdef GETTIMEOFDAY_TWO_ARGS
205       struct timezone tzp;
206       gettimeofday(&now, &tzp);
207 # else
208       gettimeofday(&now);
209 # endif
210
211       if (prev.tv_sec == 0)
212         prev = now;
213     }
214
215   /* If we've probed the wall-clock time, regenerate the string.
216    */
217   if (now.tv_sec != prev.tv_sec)
218     {
219       double uprev = prev.tv_sec + ((double) prev.tv_usec * 0.000001);
220       double unow  =  now.tv_sec + ((double)  now.tv_usec * 0.000001);
221       double fps   = frame_count / (unow - uprev);
222
223       prev = now;
224       frame_count = 0;
225       last_ifps = fps;
226
227       sprintf (msg, "FPS: %.02f", fps);
228
229       if (mi->pause != 0)
230         {
231           char buf[40];
232           sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
233           while(*buf && buf[strlen(buf)-1] == '0')
234             buf[strlen(buf)-1] = 0;
235           if (buf[strlen(buf)-1] == '.')
236             buf[strlen(buf)-1] = 0;
237           sprintf(msg + strlen(msg), " (including %s sec/frame delay)",
238                   buf);
239         }
240
241       if (mi->polygon_count > 0)
242         {
243           unsigned long p = mi->polygon_count;
244           const char *s = "";
245 # if 0
246           if      (p >= (1024 * 1024)) p >>= 20, s = "M";
247           else if (p >= 2048)          p >>= 10, s = "K";
248 # endif
249
250           strcat (msg, "\nPolys: ");
251           if (p >= 1000000)
252             sprintf (msg + strlen(msg), "%lu,%03lu,%03lu%s",
253                      (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
254           else if (p >= 1000)
255             sprintf (msg + strlen(msg), "%lu,%03lu%s",
256                      (p / 1000), (p % 1000), s);
257           else
258             sprintf (msg + strlen(msg), "%lu%s", p, s);
259         }
260     }
261
262   /* Print the string every frame (or else nothing will show up.)
263    */
264   fps_print_string (mi, fps_text_x, fps_text_y, msg);
265 }