[feat] add live FPS to CLI status bar

This commit is contained in:
dijunkun
2026-10-31 00:36:27 +08:00
parent d687c0e2ea
commit 915cfe3a04
4 changed files with 22 additions and 3 deletions
+2
View File
@@ -59,6 +59,8 @@ Windows 的可见传统控制台优先读取当前字体尺寸;Windows Termina
快捷键行使用 `KEYS |`、`VIEW |` 等固定宽度标题,标题和按键组之间用竖线分隔;键名用 `<c>`、`<+/->` 等标签标识。宽窗口中的分组分隔线按列对齐,窄窗口保留标题和完整按键标签,按可用宽度合并或减少分组。
顶部状态栏以整数显示实时帧率(例如 `FPS 30`),约每秒按最近统计区间内完整输出到终端的帧数计算;没有新画面输出时更新为 `FPS 0`。该数值是实际提交速率,不是 `--fps` 目标值或终端 GPU 呈现频率;窄窗口优先省略画面参数和网络信息,保留帧率。
ANSI 在完整桌面缩略图中仍无法保留普通字号的文字。先增加终端列数(缩小终端字体或扩大窗口),阅读局部时用 `--zoom native`。只读全景中点击目标位置可直接进入该区域的细节模式;按 `d` 则以当前中心进入。细节模式依据视口和字符宽高比选择源区域,避免为了完整装入桌面而把文字缩小;ANSI 视野较小,借助方向键平移阅读。`--zoom 1..32` 是相对整桌面的倍率;在细节模式中 `+/-` 是相对原始细节尺寸的倍率。画面缩放完全在本地进行,控制鼠标坐标映射回原始桌面的正确区域。
auto 模式发送 Kitty 查询与设备属性查询,等待最多 250ms;没有有效 Kitty 回应就用 ANSI。显式 kitty 模式探测失败时报错。tmux/screen 中 auto 使用 ANSI,显式 kitty 报不支持;不修改用户终端配置。
+12
View File
@@ -411,6 +411,9 @@ int View(SessionOptions options, const std::string &device,
uint64_t view_revision = input.view_revision;
bool redraw = true;
uint64_t submitted = 0;
uint64_t fps_submitted = 0;
auto fps_sample_at = started;
long output_fps = 0;
std::optional<Clock::time_point> connected_at, first_frame_at,
first_output_at;
bool waiting_visible = false;
@@ -550,6 +553,14 @@ int View(SessionOptions options, const std::string &device,
size_query_tick = now + std::chrono::seconds(2);
}
if (output.Idle() && !shared_upload && now >= status_tick) {
const double fps_seconds =
std::chrono::duration<double>(now - fps_sample_at).count();
// Mode changes can redraw the status early; keep a full sampling window.
if (fps_seconds >= 1) {
output_fps = std::lround((submitted - fps_submitted) / fps_seconds);
fps_submitted = submitted;
fps_sample_at = now;
}
auto st = s.Status();
const auto last_state = st["state"].get<std::string>();
std::string progress =
@@ -577,6 +588,7 @@ int View(SessionOptions options, const std::string &device,
bar.error = input.error;
bar.control = input.control;
bar.command_mode = input.command_mode;
bar.fps = "FPS " + std::to_string(output_fps);
bar.view = std::string(kitty ? "KITTY"
: detail == AnsiDetail::Quadrant ? "QUAD"
: "HALF") +
+1 -1
View File
@@ -86,7 +86,7 @@ inline constexpr char kKittyDelete[] =
"\033_Ga=d,d=I,i=31,q=2\033\\\033_Ga=d,d=I,i=32,q=2\033\\";
bool KittyReply(const std::string &response);
struct StatusBar {
std::string progress, compact_progress, error, view, network;
std::string progress, compact_progress, error, view, network, fps;
bool control = false, command_mode = false;
};
// Three padded ASCII rows. Shortcuts are compacted as whole groups to fit.
+7 -2
View File
@@ -59,11 +59,16 @@ std::vector<std::string> StatusLines(const StatusBar &bar, int columns) {
const auto progress =
bar.error.empty() ? bar.progress : "Error: " + bar.error;
auto left = mode + " | " + progress;
auto right = bar.view + (bar.network.empty() ? "" : " | " + bar.network);
auto metrics = bar.fps;
if (!bar.network.empty())
metrics += (metrics.empty() ? "" : " | ") + bar.network;
auto right = bar.view + (metrics.empty() ? "" : " | " + metrics);
if (left.size() + right.size() + 2 > inner)
right = bar.network;
right = metrics;
if (left.size() + right.size() + 2 > inner && bar.error.empty())
left = mode + " | " + bar.compact_progress;
if (left.size() + right.size() + 2 > inner)
right = bar.fps;
if (left.size() + right.size() + 2 > inner)
right.clear();
std::string status = Clip(left, inner);