/* fps, Copyright (c) 2001-2019 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
#endif /* HAVE_CONFIG_H */
fps_init (Display
*dpy
, Window window
)
if (! get_boolean_resource (dpy
, "doFPS", "DoFPS"))
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
->clear_p
= get_boolean_resource (dpy
, "fpsSolid", "FPSSolid");
font
= get_string_resource (dpy
, "fpsFont", "Font");
font
= "-*-courier-bold-r-normal-*-*-180-*-*-*-*-*-*"; /* also texfont.c */
f
= load_font_retry (dpy
, font
);
XGetWindowAttributes (dpy
, window
, &xgwa
);
get_pixel_resource (st
->dpy
, xgwa
.colormap
, "foreground", "Foreground");
st
->draw_gc
= XCreateGC (dpy
, window
, GCFont
|GCForeground
, &gcv
);
get_pixel_resource (st
->dpy
, xgwa
.colormap
, "background", "Background");
st
->erase_gc
= XCreateGC (dpy
, window
, GCFont
|GCForeground
, &gcv
);
st
->y
= - (st
->font
->ascent
+ st
->font
->descent
+ 10);
/* 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
->y
+= 48 * (top_p
? -1 : 1);
strcpy (st
->string
, "FPS: ... ");
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
);
fps_slept (fps_state
*st
, unsigned long usecs
)
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
if (st
->frame_count
++ >= st
->last_ifps
)
# ifdef GETTIMEOFDAY_TWO_ARGS
gettimeofday(&st
->this_frame_end
, &tzp
);
gettimeofday(&st
->this_frame_end
);
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
double uthis_frame_end
= (st
->this_frame_end
.tv_sec
+
((double) st
->this_frame_end
.tv_usec
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
;
sprintf (st
->string
, (polys
? "FPS: %.1f \nLoad: %.1f%% "
: "FPS: %.1f \nLoad: %.1f%% "),
if (polys
>= (1024 * 1024)) polys
>>= 20, s
= "M";
else if (polys
>= 2048) polys
>>= 10, s
= "K";
strcat (st
->string
, "\nPolys: ");
sprintf (st
->string
+ strlen(st
->string
), "%lu,%03lu,%03lu%s ",
(polys
/ 1000000), ((polys
/ 1000) % 1000),
sprintf (st
->string
+ strlen(st
->string
), "%lu,%03lu%s ",
(polys
/ 1000), (polys
% 1000), s
);
sprintf (st
->string
+ strlen(st
->string
), "%lu%s ", polys
, s
);
unsigned long ldepth
= depth
;
if (depth
>= (1024 * 1024 * 1024))
ldepth
= depth
/ (1024 * 1024 * 1024), s
= "G";
else if (depth
>= (1024 * 1024)) ldepth
>>= 20, s
= "M";
else if (depth
>= 2048) ldepth
>>= 10, s
= "K";
strcat (st
->string
, "\nDepth: ");
if (ldepth
>= 1000000000)
sprintf (st
->string
+ strlen(st
->string
),
"%lu,%03lu,%03lu,%03lu%s ",
((ldepth
/ 1000000) % 1000),
((ldepth
/ 1000) % 1000),
else if (ldepth
>= 1000000)
sprintf (st
->string
+ strlen(st
->string
), "%lu,%03lu,%03lu%s ",
(ldepth
/ 1000000), ((ldepth
/ 1000) % 1000),
sprintf (st
->string
+ strlen(st
->string
), "%lu,%03lu%s ",
(ldepth
/ 1000), (ldepth
% 1000), s
);
sprintf (st
->string
+ strlen(st
->string
), "%lu%s ",
sprintf (st
->string
+ strlen(st
->string
), "%.1f", depth
);
/* Remove trailing ".0" in case depth is not a fraction. */
if (st
->string
[L
-2] == '.' && st
->string
[L
-1] == '0')
/* Width (and optionally height) of the string in pixels.
string_width (XFontStruct
*f
, const char *c
, int *height_ret
)
int h
= f
->ascent
+ f
->descent
;
int cc
= *((unsigned char *) c
);
if (x
> max_w
) max_w
= x
;
h
+= f
->ascent
+ f
->descent
;
? f
->per_char
[cc
-f
->min_char_or_byte2
].width
: f
->min_bounds
.rbearing
);
if (x
> max_w
) max_w
= x
;
if (height_ret
) *height_ret
= h
;
/* This function is used only in Xlib mode. For GL mode, see glx/fps-gl.c.
const char *string
= st
->string
;
int lh
= st
->font
->ascent
+ st
->font
->descent
;
XGetWindowAttributes (st
->dpy
, st
->window
, &xgwa
);
for (s
= string
; *s
; s
++)
y
-= lh
* (lines
-1) + st
->font
->descent
;
/* clear the background */
w
= string_width (st
->font
, string
, &h
);
XFillRectangle (st
->dpy
, st
->window
, st
->erase_gc
,
h
+ 2*st
->font
->descent
);
s
= strchr (string
, '\n');
if (! s
) s
= string
+ strlen(string
);
XDrawString (st
->dpy
, st
->window
, st
->draw_gc
,
x
, y
, string
, (int) (s
- string
));