跳转到内容

输入回放的记录与播放

回放(replay)功能记录并保存的是人类在键盘上执行的输入操作,而非终端的屏幕输出本身。 在播放已记录的操作时,系统通过 PTY(伪终端)重新启动指定命令,并严格按照预定的时间轴注入按键输入,进而重新捕获由此产生的屏幕画面。 与直接回放预录输出数据的 asciicast 不同,输入回放能够完整复现命令本身的真实执行过程。

按键录制数据(回放文件)中绝不会直接记录特定操作系统的内部数据结构,例如 Windows 控制台事件结构体或 Unix 终端控制标志。 为了实现跨操作系统与不同键盘布局的可靠复现,系统使用抽象的通用模型保存按键语义。

常见按键统一记录为规范化名称,例如 ArrowUp、Home、Delete、F1~F12 等;Shift、Alt、Ctrl、Meta(Windows 键或 Command 键)则作为独立的修饰键标志位保存。 输入的可见字符直接保存为 Unicode 文本而非硬件扫描码(scancode),包括表情符号在内的 代理对(Surrogate Pair:用两个 16 位值表示一个字符的 UTF-16 表达形式)也被视作单一逻辑按键。 对于无法纳入常规按键模型的特殊字节序列,系统提供了 raw 事件直接存储原始字节数据。

回放文件中的每个事件均持有表示从录制开始算起绝对秒数的 time,或表示相对于前一事件增量秒数的 tick。

在读取文件时,带有 time 的事件直接采用该绝对值,仅包含 tick 的事件则依次累加到此前累积的时间戳上,从而将全部事件归一化为单一的单调绝对时间轴。 此外,元数据中还记录了总耗时(duration)。 考虑到所有按键发送完毕后子进程往往会停留在 Shell 提示符等待用户退出的情况,系统将总耗时额外增加 1 秒的时间点作为安全超时上限,以平稳结束进程。

输入流的数据分块容错与转义解析

Section titled “输入流的数据分块容错与转义解析”

在实时录制交互输入时,从宿主终端接收到的字节流会立即透传写入子进程的 PTY。 与此同时,负责将其转换为录制模型的解析通路采用了有状态的 UTF-8 解码器。

键盘发送的转义序列常常由于操作系统的读取分块边界而被切断(例如首个 chunk 仅包含起始的 ESC,而后续的 CSI 参数落入下一个 read)。 在此类情况下,系统会将未完成的转义序列暂存在内部缓冲区中,与下一次到达的字节流拼接后再继续解析。 这防止了因读取边界被切断而将不完整的转义序列误判为孤立按键(如误判为单按了 ESC 键)。

在执行播放时,已保存的按键名称与修饰键组合会被逆向还原为终端应用程序可理解的 VT 字节序列(转义序列)。

Enter、Tab、方向键及功能键生成标准的 VT 控制序列;Ctrl 键与英文字母的组合转换为对应的 ASCII 控制字符(0x01~0x1A);Alt 键组合则在序列前方附加 ESC 前缀;普通打印文字则编码为 UTF-8 字节数组。 生成的字节流被推入与交互式捕获完全相同的 PTY 写入流中,因此对于被执行的程序而言,完全无法区分这是自动化回放还是人类的手工敲击。

回放流在异步等待直至达到每个事件的预定发送时间后,才将对应字节流写入 PTY。

然而,若由于宿主机系统负载过高或命令本身的处理阻塞导致当前执行时间已落后于预定时间轴,调度器不会插入额外的等待时间,而是立即发送下一个事件。 这使得回放过程在严格保持事件执行顺序的前提下,以最短时间追赶上原本的目标时间线。 此外,即便是单次按键生成的字节序列超出了读取端的缓冲区容量,回放流也会通过内部偏移量分块输出,确保输入数据绝不发生丢失。