Added missing newline in NEDsim error message.
[screensavers] / screenhack / fps.c
CommitLineData
3144ee8a
AT
1/* fps, Copyright (c) 2001-2019 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
21fps_state *
22fps_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 = load_font_retry (dpy, font);
48 if (!f) abort();
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 HAVE_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
84void
85fps_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
94void
95fps_slept (fps_state *st, unsigned long usecs)
96{
97 st->slept += usecs;
98}
99
100
101double
102fps_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 const char *s = "";
174 unsigned long ldepth = depth;
175# if 0
176 if (depth >= (1024 * 1024 * 1024))
177 ldepth = depth / (1024 * 1024 * 1024), s = "G";
178 else if (depth >= (1024 * 1024)) ldepth >>= 20, s = "M";
179 else if (depth >= 2048) ldepth >>= 10, s = "K";
180# endif
181 strcat (st->string, "\nDepth: ");
182 if (ldepth >= 1000000000)
183 sprintf (st->string + strlen(st->string),
184 "%lu,%03lu,%03lu,%03lu%s ",
185 (ldepth / 1000000000),
186 ((ldepth / 1000000) % 1000),
187 ((ldepth / 1000) % 1000),
188 (ldepth % 1000), s);
189 else if (ldepth >= 1000000)
190 sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s ",
191 (ldepth / 1000000), ((ldepth / 1000) % 1000),
192 (ldepth % 1000), s);
193 else if (ldepth >= 1000)
194 sprintf (st->string + strlen(st->string), "%lu,%03lu%s ",
195 (ldepth / 1000), (ldepth % 1000), s);
196 else if (*s)
197 sprintf (st->string + strlen(st->string), "%lu%s ",
198 ldepth, s);
199 else
200 {
201 int L;
202 sprintf (st->string + strlen(st->string), "%.1f", depth);
203 L = strlen (st->string);
204 /* Remove trailing ".0" in case depth is not a fraction. */
205 if (st->string[L-2] == '.' && st->string[L-1] == '0')
206 st->string[L-2] = 0;
207 }
208 }
209 }
210
211 return st->last_fps;
212}
213
214
215
216/* Width (and optionally height) of the string in pixels.
217 */
218static int
219string_width (XFontStruct *f, const char *c, int *height_ret)
220{
221 int x = 0;
222 int max_w = 0;
223 int h = f->ascent + f->descent;
224 while (*c)
225 {
226 int cc = *((unsigned char *) c);
227 if (*c == '\n')
228 {
229 if (x > max_w) max_w = x;
230 x = 0;
231 h += f->ascent + f->descent;
232 }
233 else
234 x += (f->per_char
235 ? f->per_char[cc-f->min_char_or_byte2].width
236 : f->min_bounds.rbearing);
237 c++;
238 }
239 if (x > max_w) max_w = x;
240 if (height_ret) *height_ret = h;
241
242 return max_w;
243}
244
245
246/* This function is used only in Xlib mode. For GL mode, see glx/fps-gl.c.
247 */
248void
249fps_draw (fps_state *st)
250{
251 XWindowAttributes xgwa;
252 const char *string = st->string;
253 const char *s;
254 int x = st->x;
255 int y = st->y;
256 int lines = 1;
257 int lh = st->font->ascent + st->font->descent;
258
259 XGetWindowAttributes (st->dpy, st->window, &xgwa);
260
261 for (s = string; *s; s++)
262 if (*s == '\n') lines++;
263
264 if (y < 0)
265 y = -y + (lines-1) * lh;
266 else
267 y = xgwa.height - y;
268
269 y -= lh * (lines-1) + st->font->descent;
270
271 /* clear the background */
272 if (st->clear_p)
273 {
274 int w, h;
275 w = string_width (st->font, string, &h);
276 XFillRectangle (st->dpy, st->window, st->erase_gc,
277 x - st->font->descent,
278 y - lh,
279 w + 2*st->font->descent,
280 h + 2*st->font->descent);
281 }
282
283 /* draw the text */
284 while (lines)
285 {
286 s = strchr (string, '\n');
287 if (! s) s = string + strlen(string);
288 XDrawString (st->dpy, st->window, st->draw_gc,
289 x, y, string, (int) (s - string));
290 string = s;
291 string++;
292 lines--;
293 y += lh;
294 }
295}