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