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