1 /* fps, Copyright (c) 2001-2007 Jamie Zawinski <jwz@jwz.org>
2 * Utility function to draw a frames-per-second display.
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
15 #endif /* HAVE_CONFIG_H */
17 #include "xlockmoreI.h"
20 # undef usleep /* conflicts with 10.5 headers... */
21 # include <OpenGL/gl.h>
22 # include <OpenGL/glu.h>
24 #else /* !HAVE_COCOA -- real Xlib */
28 #endif /* !HAVE_COCOA */
30 #undef DEBUG /* Defining this causes check_gl_error() to be called inside
31 time-critical sections, which could slow things down (since
32 it might result in a round-trip, and stall of the pipeline.)
35 extern void clear_gl_error (void);
36 extern void check_gl_error (const char *type);
38 extern GLfloat fps_1 (ModeInfo *mi);
39 extern void fps_2 (ModeInfo *mi);
40 extern void do_fps (ModeInfo *mi);
41 extern void fps_free (ModeInfo *mi);
53 struct timeval prev, now;
58 fps_init (ModeInfo *mi)
60 struct fps_state *st = mi->fps_state;
63 st = (struct fps_state *) calloc (1, sizeof(*st));
66 st->clear_p = get_boolean_resource (mi->dpy, "fpsSolid", "FPSSolid");
68 # ifndef HAVE_COCOA /* Xlib version */
75 font = get_string_resource (mi->dpy, "fpsFont", "Font");
77 if (!font) font = "-*-courier-bold-r-normal-*-180-*";
78 f = XLoadQueryFont (mi->dpy, font);
79 if (!f) f = XLoadQueryFont (mi->dpy, "fixed");
82 first = f->min_char_or_byte2;
83 last = f->max_char_or_byte2;
86 st->font_dlist = glGenLists ((GLuint) last+1);
87 check_gl_error ("glGenLists");
89 st->ascent = f->ascent;
90 st->descent = f->descent;
92 glXUseXFont (id, first, last-first+1, st->font_dlist + first);
93 check_gl_error ("glXUseXFont");
96 # else /* HAVE_COCOA */
99 AGLContext ctx = aglGetCurrentContext();
100 GLint id = 0; /* 0 = system font; 1 = application font */
101 Style face = 1; /* 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc. */
106 st->ascent = size * 0.9;
107 st->descent = size - st->ascent;
110 st->font_dlist = glGenLists ((GLuint) last+1);
111 check_gl_error ("glGenLists");
113 if (! aglUseFont (ctx, id, face, size,
114 first, last-first+1, st->font_dlist + first)) {
115 check_gl_error ("aglUseFont");
120 # endif /* HAVE_COCOA */
124 if (get_boolean_resource (mi->dpy, "fpsTop", "FPSTop"))
125 st->y = - (st->ascent + 10);
129 fps_free (ModeInfo *mi)
132 free (mi->fps_state);
137 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
139 struct fps_state *st = mi->fps_state;
140 const char *L2 = strchr (string, '\n');
144 y = mi->xgwa.height + y;
146 y -= (st->ascent + st->descent);
153 /* Sadly, this causes a stall of the graphics pipeline (as would the
154 equivalent calls to glGet*.) But there's no way around this, short
155 of having each caller set up the specific display matrix we need
156 here, which would kind of defeat the purpose of centralizing this
159 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
160 GL_ENABLE_BIT | /* for various glDisable calls */
161 GL_CURRENT_BIT | /* for glColor3f() */
162 GL_LIST_BIT); /* for glListBase() */
165 check_gl_error ("glPushAttrib");
168 /* disable lighting and texturing when drawing bitmaps!
169 (glPopAttrib() restores these.)
171 glDisable (GL_TEXTURE_2D);
172 glDisable (GL_LIGHTING);
173 glDisable (GL_BLEND);
174 glDisable (GL_DEPTH_TEST);
175 glDisable (GL_CULL_FACE);
177 /* glPopAttrib() does not restore matrix changes, so we must
178 push/pop the matrix stacks to be non-intrusive there.
180 glMatrixMode(GL_PROJECTION);
184 check_gl_error ("glPushMatrix");
188 /* Each matrix mode has its own stack, so we need to push/pop
190 glMatrixMode(GL_MODELVIEW);
194 check_gl_error ("glPushMatrix");
198 gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
200 check_gl_error ("gluOrtho2D");
203 /* clear the background */
206 int lines = L2 ? 2 : 1;
208 glRecti (x / 2, y - st->descent,
210 y + lines * (st->ascent + st->descent));
215 glRasterPos2f (x, y);
216 glListBase (st->font_dlist);
221 glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
222 glRasterPos2f (x, y + (st->ascent + st->descent));
223 glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
227 glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
231 check_gl_error ("fps_print_string");
236 glMatrixMode(GL_PROJECTION);
240 /* clean up after our state changes */
243 check_gl_error ("glPopAttrib");
251 struct fps_state *st = mi->fps_state;
256 strcpy (st->string, "FPS: (accumulating...)");
259 /* Every N frames (where N is approximately one second's worth of frames)
260 check the wall clock. We do this because checking the wall clock is
263 if (st->frame_count++ >= st->last_ifps)
265 # ifdef GETTIMEOFDAY_TWO_ARGS
267 gettimeofday(&st->now, &tzp);
269 gettimeofday(&st->now);
272 if (st->prev.tv_sec == 0)
276 /* If we've probed the wall-clock time, regenerate the string.
278 if (st->now.tv_sec != st->prev.tv_sec)
280 double uprev = st->prev.tv_sec + ((double) st->prev.tv_usec * 0.000001);
281 double unow = st->now.tv_sec + ((double) st->now.tv_usec * 0.000001);
282 double fps = st->frame_count / (unow - uprev);
289 sprintf (st->string, "FPS: %.02f", fps);
292 /* since there's no "-delay 0" in the Cocoa version,
293 don't bother mentioning the inter-frame delay. */
297 sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
298 while(*buf && buf[strlen(buf)-1] == '0')
299 buf[strlen(buf)-1] = 0;
300 if (buf[strlen(buf)-1] == '.')
301 buf[strlen(buf)-1] = 0;
302 sprintf(st->string + strlen(st->string),
303 " (including %s sec/frame delay)",
306 #endif /* HAVE_COCOA */
308 if (mi->polygon_count > 0)
310 unsigned long p = mi->polygon_count;
313 if (p >= (1024 * 1024)) p >>= 20, s = "M";
314 else if (p >= 2048) p >>= 10, s = "K";
317 strcat (st->string, "\nPolys: ");
319 sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s",
320 (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
322 sprintf (st->string + strlen(st->string), "%lu,%03lu%s",
323 (p / 1000), (p % 1000), s);
325 sprintf (st->string + strlen(st->string), "%lu%s", p, s);
335 struct fps_state *st = mi->fps_state;
336 fps_print_string (mi, st->x, st->y, st->string);
341 do_fps (ModeInfo *mi)
343 fps_1 (mi); /* Lazily compute current FPS value, about once a second. */
344 fps_2 (mi); /* Print the string every frame (else nothing shows up.) */