1 /* tube, Copyright (c) 2001, 2006 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 # include <OpenGL/gl.h>
21 # include <OpenGL/glu.h>
23 #else /* !HAVE_COCOA -- real Xlib */
27 #endif /* !HAVE_COCOA */
29 #undef DEBUG /* Defining this causes check_gl_error() to be called inside
30 time-critical sections, which could slow things down (since
31 it might result in a round-trip, and stall of the pipeline.)
34 extern void clear_gl_error (void);
35 extern void check_gl_error (const char *type);
37 extern GLfloat fps_1 (ModeInfo *mi);
38 extern void fps_2 (ModeInfo *mi);
39 extern void do_fps (ModeInfo *mi);
40 extern void fps_free (ModeInfo *mi);
52 struct timeval prev, now;
57 fps_init (ModeInfo *mi)
59 struct fps_state *st = mi->fps_state;
62 st = (struct fps_state *) calloc (1, sizeof(*st));
65 st->clear_p = get_boolean_resource (mi->dpy, "fpsSolid", "FPSSolid");
67 # ifndef HAVE_COCOA /* Xlib version */
74 font = get_string_resource (mi->dpy, "fpsFont", "Font");
76 if (!font) font = "-*-courier-bold-r-normal-*-180-*";
77 f = XLoadQueryFont (mi->dpy, font);
78 if (!f) f = XLoadQueryFont (mi->dpy, "fixed");
81 first = f->min_char_or_byte2;
82 last = f->max_char_or_byte2;
85 st->font_dlist = glGenLists ((GLuint) last+1);
86 check_gl_error ("glGenLists");
88 st->ascent = f->ascent;
89 st->descent = f->descent;
91 glXUseXFont (id, first, last-first+1, st->font_dlist + first);
92 check_gl_error ("glXUseXFont");
95 # else /* HAVE_COCOA */
98 AGLContext ctx = aglGetCurrentContext();
99 GLint id = 0; /* 0 = system font; 1 = application font */
100 Style face = 1; /* 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc. */
105 st->ascent = size * 0.9;
106 st->descent = size - st->ascent;
109 st->font_dlist = glGenLists ((GLuint) last+1);
110 check_gl_error ("glGenLists");
112 if (! aglUseFont (ctx, id, face, size,
113 first, last-first+1, st->font_dlist + first)) {
114 check_gl_error ("aglUseFont");
119 # endif /* HAVE_COCOA */
123 if (get_boolean_resource (mi->dpy, "fpsTop", "FPSTop"))
124 st->y = - (st->ascent + 10);
128 fps_free (ModeInfo *mi)
131 free (mi->fps_state);
136 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
138 struct fps_state *st = mi->fps_state;
139 const char *L2 = strchr (string, '\n');
143 y = mi->xgwa.height + y;
145 y -= (st->ascent + st->descent);
152 /* Sadly, this causes a stall of the graphics pipeline (as would the
153 equivalent calls to glGet*.) But there's no way around this, short
154 of having each caller set up the specific display matrix we need
155 here, which would kind of defeat the purpose of centralizing this
158 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
159 GL_ENABLE_BIT | /* for various glDisable calls */
160 GL_CURRENT_BIT | /* for glColor3f() */
161 GL_LIST_BIT); /* for glListBase() */
164 check_gl_error ("glPushAttrib");
167 /* disable lighting and texturing when drawing bitmaps!
168 (glPopAttrib() restores these.)
170 glDisable (GL_TEXTURE_2D);
171 glDisable (GL_LIGHTING);
172 glDisable (GL_BLEND);
173 glDisable (GL_DEPTH_TEST);
174 glDisable (GL_CULL_FACE);
176 /* glPopAttrib() does not restore matrix changes, so we must
177 push/pop the matrix stacks to be non-intrusive there.
179 glMatrixMode(GL_PROJECTION);
183 check_gl_error ("glPushMatrix");
187 /* Each matrix mode has its own stack, so we need to push/pop
189 glMatrixMode(GL_MODELVIEW);
193 check_gl_error ("glPushMatrix");
197 gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
199 check_gl_error ("gluOrtho2D");
202 /* clear the background */
205 int lines = L2 ? 2 : 1;
207 glRecti (x / 2, y - st->descent,
209 y + lines * (st->ascent + st->descent));
214 glRasterPos2f (x, y);
215 glListBase (st->font_dlist);
220 glCallLists (strlen(L2), GL_UNSIGNED_BYTE, L2);
221 glRasterPos2f (x, y + (st->ascent + st->descent));
222 glCallLists (L2 - string - 1, GL_UNSIGNED_BYTE, string);
226 glCallLists (strlen(string), GL_UNSIGNED_BYTE, string);
230 check_gl_error ("fps_print_string");
235 glMatrixMode(GL_PROJECTION);
239 /* clean up after our state changes */
242 check_gl_error ("glPopAttrib");
250 struct fps_state *st = mi->fps_state;
255 strcpy (st->string, "FPS: (accumulating...)");
258 /* Every N frames (where N is approximately one second's worth of frames)
259 check the wall clock. We do this because checking the wall clock is
262 if (st->frame_count++ >= st->last_ifps)
264 # ifdef GETTIMEOFDAY_TWO_ARGS
266 gettimeofday(&st->now, &tzp);
268 gettimeofday(&st->now);
271 if (st->prev.tv_sec == 0)
275 /* If we've probed the wall-clock time, regenerate the string.
277 if (st->now.tv_sec != st->prev.tv_sec)
279 double uprev = st->prev.tv_sec + ((double) st->prev.tv_usec * 0.000001);
280 double unow = st->now.tv_sec + ((double) st->now.tv_usec * 0.000001);
281 double fps = st->frame_count / (unow - uprev);
288 sprintf (st->string, "FPS: %.02f", fps);
291 /* since there's no "-delay 0" in the Cocoa version,
292 don't bother mentioning the inter-frame delay. */
296 sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
297 while(*buf && buf[strlen(buf)-1] == '0')
298 buf[strlen(buf)-1] = 0;
299 if (buf[strlen(buf)-1] == '.')
300 buf[strlen(buf)-1] = 0;
301 sprintf(st->string + strlen(st->string),
302 " (including %s sec/frame delay)",
305 #endif /* HAVE_COCOA */
307 if (mi->polygon_count > 0)
309 unsigned long p = mi->polygon_count;
312 if (p >= (1024 * 1024)) p >>= 20, s = "M";
313 else if (p >= 2048) p >>= 10, s = "K";
316 strcat (st->string, "\nPolys: ");
318 sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s",
319 (p / 1000000), ((p / 1000) % 1000), (p % 1000), s);
321 sprintf (st->string + strlen(st->string), "%lu,%03lu%s",
322 (p / 1000), (p % 1000), s);
324 sprintf (st->string + strlen(st->string), "%lu%s", p, s);
334 struct fps_state *st = mi->fps_state;
335 fps_print_string (mi, st->x, st->y, st->string);
340 do_fps (ModeInfo *mi)
342 fps_1 (mi); /* Lazily compute current FPS value, about once a second. */
343 fps_2 (mi); /* Print the string every frame (else nothing shows up.) */