summaryrefslogtreecommitdiffstats
path: root/hacks/fps.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/fps.c')
-rw-r--r--hacks/fps.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/hacks/fps.c b/hacks/fps.c
new file mode 100644
index 0000000..a24f623
--- /dev/null
+++ b/hacks/fps.c
@@ -0,0 +1,268 @@
+/* fps, Copyright (c) 2001-2018 Jamie Zawinski <jwz@jwz.org>
+ * Draw a frames-per-second display (Xlib and OpenGL).
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <time.h>
+#include "screenhackI.h"
+#include "fpsI.h"
+
+fps_state *
+fps_init (Display *dpy, Window window)
+{
+ fps_state *st;
+ const char *font;
+ XFontStruct *f;
+ Bool top_p;
+ XWindowAttributes xgwa;
+
+ if (! get_boolean_resource (dpy, "doFPS", "DoFPS"))
+ return 0;
+
+ if (!strcasecmp (progname, "BSOD")) return 0; /* Never worked right */
+
+ top_p = get_boolean_resource (dpy, "fpsTop", "FPSTop");
+
+ st = (fps_state *) calloc (1, sizeof(*st));
+
+ st->dpy = dpy;
+ st->window = window;
+ st->clear_p = get_boolean_resource (dpy, "fpsSolid", "FPSSolid");
+
+ font = get_string_resource (dpy, "fpsFont", "Font");
+
+ if (!font)
+ font = "-*-courier-bold-r-normal-*-*-180-*-*-*-*-*-*"; /* also texfont.c */
+ f = load_font_retry (dpy, font);
+ if (!f) abort();
+
+ {
+ XGCValues gcv;
+ XGetWindowAttributes (dpy, window, &xgwa);
+ gcv.font = f->fid;
+ gcv.foreground =
+ get_pixel_resource (st->dpy, xgwa.colormap, "foreground", "Foreground");
+ st->draw_gc = XCreateGC (dpy, window, GCFont|GCForeground, &gcv);
+ gcv.foreground =
+ get_pixel_resource (st->dpy, xgwa.colormap, "background", "Background");
+ st->erase_gc = XCreateGC (dpy, window, GCFont|GCForeground, &gcv);
+ }
+
+ st->font = f;
+ st->x = 10;
+ st->y = 10;
+ if (top_p)
+ st->y = - (st->font->ascent + st->font->descent + 10);
+
+# ifdef USE_IPHONE
+ /* Don't hide the FPS display under the iPhone X bezel.
+ #### This is the worst of all possible ways to do this! But how else?
+ */
+ if (xgwa.width == 2436 || xgwa.height == 2436)
+ {
+ st->x += 48;
+ st->y += 48 * (top_p ? -1 : 1);
+ }
+# endif
+
+ strcpy (st->string, "FPS: ... ");
+
+ return st;
+}
+
+void
+fps_free (fps_state *st)
+{
+ if (st->draw_gc) XFreeGC (st->dpy, st->draw_gc);
+ if (st->erase_gc) XFreeGC (st->dpy, st->erase_gc);
+ if (st->font) XFreeFont (st->dpy, st->font);
+ free (st);
+}
+
+
+void
+fps_slept (fps_state *st, unsigned long usecs)
+{
+ st->slept += usecs;
+}
+
+
+double
+fps_compute (fps_state *st, unsigned long polys, double depth)
+{
+ if (! st) return 0; /* too early? */
+
+ /* Every N frames (where N is approximately one second's worth of frames)
+ check the wall clock. We do this because checking the wall clock is
+ a slow operation.
+ */
+ if (st->frame_count++ >= st->last_ifps)
+ {
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&st->this_frame_end, &tzp);
+# else
+ gettimeofday(&st->this_frame_end);
+# endif
+
+ if (st->prev_frame_end.tv_sec == 0)
+ st->prev_frame_end = st->this_frame_end;
+ }
+
+ /* If we've probed the wall-clock time, regenerate the string.
+ */
+ if (st->this_frame_end.tv_sec != st->prev_frame_end.tv_sec)
+ {
+ double uprev_frame_end = (st->prev_frame_end.tv_sec +
+ ((double) st->prev_frame_end.tv_usec
+ * 0.000001));
+ double uthis_frame_end = (st->this_frame_end.tv_sec +
+ ((double) st->this_frame_end.tv_usec
+ * 0.000001));
+ double fps = st->frame_count / (uthis_frame_end - uprev_frame_end);
+ double idle = (((double) st->slept * 0.000001) /
+ (uthis_frame_end - uprev_frame_end));
+ double load = 100 * (1 - idle);
+
+ if (load < 0) load = 0; /* well that's obviously nonsense... */
+
+ st->prev_frame_end = st->this_frame_end;
+ st->frame_count = 0;
+ st->slept = 0;
+ st->last_ifps = fps;
+ st->last_fps = fps;
+
+ sprintf (st->string, (polys
+ ? "FPS: %.1f \nLoad: %.1f%% "
+ : "FPS: %.1f \nLoad: %.1f%% "),
+ fps, load);
+
+ if (polys > 0)
+ {
+ const char *s = "";
+# if 0
+ if (polys >= (1024 * 1024)) polys >>= 20, s = "M";
+ else if (polys >= 2048) polys >>= 10, s = "K";
+# endif
+
+ strcat (st->string, "\nPolys: ");
+ if (polys >= 1000000)
+ sprintf (st->string + strlen(st->string), "%lu,%03lu,%03lu%s ",
+ (polys / 1000000), ((polys / 1000) % 1000),
+ (polys % 1000), s);
+ else if (polys >= 1000)
+ sprintf (st->string + strlen(st->string), "%lu,%03lu%s ",
+ (polys / 1000), (polys % 1000), s);
+ else
+ sprintf (st->string + strlen(st->string), "%lu%s ", polys, s);
+ }
+
+ if (depth >= 0.0)
+ {
+ unsigned long L = strlen (st->string);
+ char *s = st->string + L;
+ strcat (s, "\nDepth: ");
+ sprintf (s + strlen(s), "%.1f", depth);
+ L = strlen (s);
+ /* Remove trailing ".0" in case depth is not a fraction. */
+ if (s[L-2] == '.' && s[L-1] == '0')
+ s[L-2] = 0;
+ }
+ }
+
+ return st->last_fps;
+}
+
+
+
+/* Width (and optionally height) of the string in pixels.
+ */
+static int
+string_width (XFontStruct *f, const char *c, int *height_ret)
+{
+ int x = 0;
+ int max_w = 0;
+ int h = f->ascent + f->descent;
+ while (*c)
+ {
+ int cc = *((unsigned char *) c);
+ if (*c == '\n')
+ {
+ if (x > max_w) max_w = x;
+ x = 0;
+ h += f->ascent + f->descent;
+ }
+ else
+ x += (f->per_char
+ ? f->per_char[cc-f->min_char_or_byte2].width
+ : f->min_bounds.rbearing);
+ c++;
+ }
+ if (x > max_w) max_w = x;
+ if (height_ret) *height_ret = h;
+
+ return max_w;
+}
+
+
+/* This function is used only in Xlib mode. For GL mode, see glx/fps-gl.c.
+ */
+void
+fps_draw (fps_state *st)
+{
+ XWindowAttributes xgwa;
+ const char *string = st->string;
+ const char *s;
+ int x = st->x;
+ int y = st->y;
+ int lines = 1;
+ int lh = st->font->ascent + st->font->descent;
+
+ XGetWindowAttributes (st->dpy, st->window, &xgwa);
+
+ for (s = string; *s; s++)
+ if (*s == '\n') lines++;
+
+ if (y < 0)
+ y = -y + (lines-1) * lh;
+ else
+ y = xgwa.height - y;
+
+ y -= lh * (lines-1) + st->font->descent;
+
+ /* clear the background */
+ if (st->clear_p)
+ {
+ int w, h;
+ w = string_width (st->font, string, &h);
+ XFillRectangle (st->dpy, st->window, st->erase_gc,
+ x - st->font->descent,
+ y - lh,
+ w + 2*st->font->descent,
+ h + 2*st->font->descent);
+ }
+
+ /* draw the text */
+ while (lines)
+ {
+ s = strchr (string, '\n');
+ if (! s) s = string + strlen(string);
+ XDrawString (st->dpy, st->window, st->draw_gc,
+ x, y, string, (int) (s - string));
+ string = s;
+ string++;
+ lines--;
+ y += lh;
+ }
+}