2e1ba3f715a2f4125c5564c85ece9d2b4fc3a744
[xscreensaver] / hacks / fps.c
1 /* fps, Copyright (c) 2001-2008 Jamie Zawinski <jwz@jwz.org>
2  * Draw a frames-per-second display (Xlib and OpenGL).
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 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif /* HAVE_CONFIG_H */
16
17 #include "screenhackI.h"
18 #include "fpsI.h"
19
20 fps_state *
21 fps_init (Display *dpy, Window window)
22 {
23   fps_state *st;
24   const char *font;
25   XFontStruct *f;
26   int first, last;
27
28   if (! get_boolean_resource (dpy, "doFPS", "DoFPS"))
29     return 0;
30
31   st = (fps_state *) calloc (1, sizeof(*st));
32
33   st->dpy = dpy;
34   st->window = window;
35   st->clear_p = get_boolean_resource (dpy, "fpsSolid", "FPSSolid");
36
37   font = get_string_resource (dpy, "fpsFont", "Font");
38
39   if (!font) font = "-*-courier-bold-r-normal-*-180-*";
40   f = XLoadQueryFont (dpy, font);
41   if (!f) f = XLoadQueryFont (dpy, "fixed");
42
43   first = f->min_char_or_byte2;
44   last = f->max_char_or_byte2;
45
46   {
47     XWindowAttributes xgwa;
48     XGCValues gcv;
49     XGetWindowAttributes (dpy, window, &xgwa);
50     gcv.font = f->fid;
51     gcv.foreground = 
52       get_pixel_resource (st->dpy, xgwa.colormap, "foreground", "Foreground");
53     st->draw_gc = XCreateGC (dpy, window, GCFont|GCForeground, &gcv);
54     gcv.foreground =
55       get_pixel_resource (st->dpy, xgwa.colormap, "background", "Background");
56     st->erase_gc = XCreateGC (dpy, window, GCFont|GCForeground, &gcv);
57   }
58
59   st->font = f;
60   st->x = 10;
61   st->y = 10;
62   if (get_boolean_resource (dpy, "fpsTop", "FPSTop"))
63     st->y = - (st->font->ascent + st->font->descent + 10);
64
65   strcpy (st->string, "FPS: ... ");
66
67   return st;
68 }
69
70 void
71 fps_free (fps_state *st)
72 {
73   if (st->draw_gc)  XFreeGC (st->dpy, st->draw_gc);
74   if (st->erase_gc) XFreeGC (st->dpy, st->erase_gc);
75   if (st->font) XFreeFont (st->dpy, st->font);
76   free (st);
77 }
78
79
80 void
81 fps_slept (fps_state *st, unsigned long usecs)
82 {
83   st->slept += usecs;
84 }
85
86
87 double
88 fps_compute (fps_state *st, unsigned long polys)
89 {
90   if (! st) return 0;  /* too early? */
91
92   /* Every N frames (where N is approximately one second's worth of frames)
93      check the wall clock.  We do this because checking the wall clock is
94      a slow operation.
95    */
96   if (st->frame_count++ >= st->last_ifps)
97     {
98 # ifdef GETTIMEOFDAY_TWO_ARGS
99       struct timezone tzp;
100       gettimeofday(&st->this_frame_end, &tzp);
101 # else
102       gettimeofday(&st->this_frame_end);
103 # endif
104
105       if (st->prev_frame_end.tv_sec == 0)
106         st->prev_frame_end = st->this_frame_end;
107     }
108
109   /* If we've probed the wall-clock time, regenerate the string.
110    */
111   if (st->this_frame_end.tv_sec != st->prev_frame_end.tv_sec)
112     {
113       double uprev_frame_end = (st->prev_frame_end.tv_sec +
114                                 ((double) st->prev_frame_end.tv_usec
115                                  * 0.000001));
116       double uthis_frame_end = (st->this_frame_end.tv_sec +
117                                 ((double) st->this_frame_end.tv_usec
118                                  * 0.000001));
119       double fps = st->frame_count / (uthis_frame_end - uprev_frame_end);
120       double idle = (((double) st->slept * 0.000001) /
121                      (uthis_frame_end - uprev_frame_end));
122       double load = 100 * (1 - idle);
123
124       if (load < 0) load = 0;  /* well that's obviously nonsense... */
125
126       st->prev_frame_end = st->this_frame_end;
127       st->frame_count = 0;
128       st->slept       = 0;
129       st->last_ifps   = fps;
130       st->last_fps    = fps;
131
132       sprintf (st->string, (polys 
133                             ? "FPS:   %.1f \nLoad:  %.1f%% "
134                             : "FPS:  %.1f \nLoad: %.1f%% "),
135                fps, load);
136
137       if (polys > 0)
138         {
139           const char *s = "";
140 # if 0
141           if      (polys >= (1024 * 1024)) polys >>= 20, s = "M";
142           else if (polys >= 2048)          polys >>= 10, s = "K";
143 # endif
144
145           strcat (st->string, "\nPolys: ");
146           if (polys >= 1000000)
147             sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s ",
148                      (polys / 1000000), ((polys / 1000) % 1000),
149                      (polys % 1000), s);
150           else if (polys >= 1000)
151             sprintf (st->string + strlen(st->string), "%lu,%03lu%s ",
152                      (polys / 1000), (polys % 1000), s);
153           else
154             sprintf (st->string + strlen(st->string), "%lu%s ", polys, s);
155         }
156     }
157
158   return st->last_fps;
159 }
160
161
162
163 /* Width (and optionally height) of the string in pixels.
164  */
165 static int
166 string_width (XFontStruct *f, const char *c, int *height_ret)
167 {
168   int x = 0;
169   int max_w = 0;
170   int h = f->ascent + f->descent;
171   while (*c)
172     {
173       int cc = *((unsigned char *) c);
174       if (*c == '\n')
175         {
176           if (x > max_w) max_w = x;
177           x = 0;
178           h += f->ascent + f->descent;
179         }
180       else
181         x += (f->per_char
182               ? f->per_char[cc-f->min_char_or_byte2].width
183               : f->min_bounds.rbearing);
184       c++;
185     }
186   if (x > max_w) max_w = x;
187   if (height_ret) *height_ret = h;
188
189   return max_w;
190 }
191
192
193 /* This function is used only in Xlib mode.  For GL mode, see glx/fps-glx.c.
194  */
195 void
196 fps_draw (fps_state *st)
197 {
198   XWindowAttributes xgwa;
199   const char *string = st->string;
200   const char *s;
201   int x = st->x;
202   int y = st->y;
203   int lines = 1;
204   int lh = st->font->ascent + st->font->descent;
205
206   XGetWindowAttributes (st->dpy, st->window, &xgwa);
207
208   for (s = string; *s; s++) 
209     if (*s == '\n') lines++;
210
211   if (y < 0)
212     y = -y + (lines-1) * lh;
213   else
214     y = xgwa.height - y;
215
216   y -= lh * (lines-1) + st->font->descent;
217
218   /* clear the background */
219   if (st->clear_p)
220     {
221       int w, h;
222       int lh = st->font->ascent + st->font->descent;
223       w = string_width (st->font, string, &h);
224       XFillRectangle (st->dpy, st->window, st->erase_gc,
225                       x - st->font->descent,
226                       y - lh,
227                       w + 2*st->font->descent,
228                       h + 2*st->font->descent);
229     }
230
231   /* draw the text */
232   while (lines)
233     {
234       s = strchr (string, '\n');
235       if (! s) s = string + strlen(string);
236       XDrawString (st->dpy, st->window, st->draw_gc,
237                    x, y, string, s - string);
238       string = s;
239       string++;
240       lines--;
241       y += lh;
242     }
243 }