画面呈现协议

在 0.50.0 版本加入.

同步更新(私有模式 2026)让程序一次提交完整画面,避免撕裂。但程序无法知道该帧何时到达屏幕,因此动画、实时笔迹或视频程序只能用定时器控制节奏,可能浪费工作在从未显示的帧上,也可能落后于显示器。画面呈现协议补足另一半:终端每次呈现包含程序输出的画面后发送一条报告。它与 数位板指针协议 一样,是小巧、低干扰、易于实现的开放终端扩展,不改变 TTY 的本质。

消息封装

帧使用带有 frame 选择器的 OSC 777::

<OSC> 777 ; frame ; v=1 : op=query : id=42 <ST>

帧遵循 OSC 777 扩展封装 规定的 OSC 777 共用规则:字段语法、拒绝哪些帧、单位和时钟。协议版本 v 为 1,op 为 query、cap、mode 或 event。

能力探测

程序询问终端是否支持本协议::

<OSC> 777 ; frame ; v=1 : op=query : id=42 <ST>

id 是程序自行选择的非负 31 位整数,回复原样返回,以便匹配查询。支持协议的终端回答::

<OSC> 777 ; frame ; v=1 : op=cap : id=42 : caps=1 : mode=0 : period=8333 <ST>

caps 是终端报告能力的位集合,当前始终为 1,表示呈现事件。mode 为下述模式的当前状态。period 表示窗口所在显示器的刷新周期,单位微秒;终端不知道时为 0。窗口移到另一显示器时它会变化,有需要的程序可定期重新查询,或直接使用每条事件携带的值。不支持协议的终端会忽略查询,因此程序必须设置超时,并回退到定时器。

模式

报告默认关闭,按终端窗口开启::

<OSC> 777 ; frame ; v=1 : op=mode : mode=1 <ST>

用 mode=0 关闭。开启模式会重置序号计数器。模式仅作用于当前窗口,并由 RIS 重置。程序必须在退出、挂起或失败时关闭,恢复时重新开启。多路复用器与其他终端状态一起保存它,并在脱离连接时关闭。终端可允许用户针对全部或部分程序拒绝模式;被拒绝时读回为关闭,不产生画面报告,因此程序应读回状态,而不是假定请求已获准。

不带 mode 字段的 mode 请求读取状态而不修改,回复为::

<OSC> 777 ; frame ; v=1 : op=mode : mode=1 <ST>

程序重连后恢复时,应读回模式,确认是否仍然有效,而非假定状态保留。

报告

模式开启时,终端每次向显示器提交包含自上次报告以来程序新写入内容的画面,都会发送一条报告::

<OSC> 777 ; frame ; v=1 : op=event : seq=17 : t=408211 : tu=73 : period=8333 <ST>

seq 从模式启用时起计数。t 为画面交给显示系统的时刻,单位毫秒,使用全部终端扩展共用的时钟(见 OSC 777 扩展封装),可与触发该帧的输入事件的 t 对照。tu 是 t 之后的微秒数,范围 0 至 999,仅在终端能测量到该精度时出现。这不是画面真正显示的时刻,实际显示发生在下一次刷新或更晚;后续协议版本可能补充实际显示时刻。period 与 cap 回复相同,以微秒表示刷新周期。

每条报告都代表一帧,因此终端或中继绝不将其合并或丢弃,见 OSC 777 扩展封装。

无论包含多少输出,一帧只报告一次;程序不写入内容就不会收到报告,因此空闲程序不会被唤醒。开启模式的命令本身也是输出,所以第一条报告会在下一次画面提交后到达,并直接提供周期,无须额外查询。即使输出未改变可见内容,仍可能产生报告,因为终端统计的是输出,而非可见变化。窗口被隐藏或位于非活动标签页时不会呈现画面,也不会发送报告,因此程序绝不能无限阻塞等待报告:应以几个刷新周期为超时,随后继续运行。

配合同步更新控制绘制节奏

预期用法是在渲染循环中,将一帧放在 CSI ? 2026 h 与 CSI ? 2026 l 之间提交,等待报告后再开始下一帧。每次画面呈现对应一条报告,程序的绘制速度就与显示速度一致,不会过快;相邻报告的 t 之差可得实际帧率。渲染快于显示的程序应丢弃或合并帧,而非排队,因为终端每次呈现时只显示最新输出。

报告像按键一样写入程序输入。等待报告时程序仍必须读取输入,因为按键等其他输入也会通过同一字节流到达。