75 lines
6.8 KiB
Markdown
75 lines
6.8 KiB
Markdown
# Fix-BUG-20261006-0002
|
||
|
||
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
|
||
|
||
## 关联信息
|
||
|
||
- Fix ID: Fix-BUG-20261006-0002
|
||
- 关联 BUG ID: BUG-20261006-0002
|
||
- 修复目标: 让阶段 0B 的真实 IME 实验具备可比较的位置基线、可命中的原生键规则和不因状态锁竞争主动丢记录的采集路径
|
||
- 状态:验证完成
|
||
- 负责人: Codex
|
||
- 分支 / 版本: `master`,未提交工作区
|
||
|
||
## 根因分析
|
||
|
||
- 根因 1: 0B 复用了只显示说明文字的 UiFixture 场景,没有把 0C 已验证的 caret point/line rect 写入当前 HWND 的 IME context。
|
||
- 根因 2: 规则按操作者语义配置为 `VK_ESCAPE`,但 IME 在 WndProc 前已处理组合态 KeyDown,窗口实际收到 `VK_PROCESSKEY`;scan code 1 才保留了本轮 Escape 的物理区分信息。
|
||
- 根因 3: 单环形缓冲在 safe-point 持锁复制,WndProc 为避免等待使用 `TryAcquireSRWLockExclusive`;宿主复制期间到达的原生消息会被主动丢弃。
|
||
|
||
## 修复方案
|
||
|
||
- 修复思路: 保持 0B 动作枚举只有 Forward/Consume 两值,仅修正诊断输入、位置基线和采集完整性,不新增生产路由行为。
|
||
- 关键改动:
|
||
- 0B 人工 runner 固定窗口为 `720×420`,传入客户区 caret `(225,282)` 与 line rect `(200,255)-(680,284)`。
|
||
- UiFixture 绘制与上述数值一致的 line region 和 caret marker,便于操作者判断候选 UI 是否仍落在窗口外。
|
||
- 在 `WM_SETFOCUS`、`WM_IME_SETCONTEXT`、`WM_IME_STARTCOMPOSITION`、`WM_IME_COMPOSITION` 观察点准备定位请求;锁外调用 `CFS_POINT` composition setter 与 `CFS_EXCLUDE` candidate setter,通过独立 `ime_position` 记录保存 context、两个 API 结果和坐标。
|
||
- Composition 请求 Consume 时,如果尚无一次两个 setter 同时成功的证据,立即 fail-open 为 `ime_position_unavailable`。
|
||
- 规则新增可选 `scanCode`。自动 F24 仍使用 any;人工 Escape 默认使用 `WM_KEYDOWN + VK_PROCESSKEY(229) + scanCode 1 + ComposingOnly`,界面指令仍要求操作者按 Escape。
|
||
- 分析器在指定 scan code 时同时过滤 WndProc 与 EasyX `EX_KEY`,并输出位置尝试/成功计数和 `imePositionReady`。
|
||
- 记录缓冲改为两个固定数组;safe-point 在锁内只交换 active buffer 和重置索引,文件写入在锁外完成。WndProc 使用极短阻塞临界区,不再因 `try-lock` 失败丢弃记录。
|
||
- 涉及文件 / 类 / 函数:
|
||
- `src/StellarX/SxInputProbe.cpp`
|
||
- `tests/UiFixture.cpp`
|
||
- `scripts/run-input-consume-probe.ps1`
|
||
- `scripts/run-input-consume-probe-manual.ps1`
|
||
- `scripts/analyze-input-probe.ps1`
|
||
- 影响的 API / 行为: 仅 `STELLARX_ENABLE_INPUT_PROBE` 且显式开启 0B 的诊断路径;公共 API 与默认生产窗口行为不变。
|
||
- 回滚点 / 开关: 不设置 `STELLARX_INPUT_PROBE_0B` 时不装载规则、不调用固定位置逻辑;默认构建不包含探测实现。
|
||
|
||
## 影响评估
|
||
|
||
- 正确性: scan code 消除同一 `VK_PROCESSKEY` 下字母、方向、Escape 等多类组合键的歧义;固定位置成功门槛防止把夹具定位失败算成消费副作用。
|
||
- 并发: 双缓冲消除宿主批量复制对 WndProc 的长时间占锁;文件写锁仍串行化多个 flush,状态锁序保持“文件写锁 → 状态锁”。
|
||
- 性能: WndProc 每条被观察消息增加一次短阻塞状态锁;Composition 生命周期增加有限的 IMM32 setter。两者只存在于诊断构建。
|
||
- 风险:
|
||
- 某些 IME 即使 setter 返回成功也可能自行调整候选 UI,因此仍需人工视觉确认。
|
||
- WndProc 阻塞临界区要求 safe-point 严禁在状态锁内做文件 I/O 或整批复制;后续维护若破坏此不变量可能重新引入卡顿。
|
||
- 首次人工复验发现持状态锁调用 IMM32 setter 会因同步重入 WndProc 自锁;已拆分为“锁内请求 → 锁外调用 → 锁内结果”并增加线程内递归定位保护,详见 `Fix-BUG-20261006-0003`。
|
||
- `VK_PROCESSKEY + scanCode 1` 是当前环境观察到的原生 Escape 形态,不得未经新证据外推到全部 IME/键盘布局。
|
||
|
||
## 验证结果
|
||
|
||
- 已完成:
|
||
- Debug x64 探测构建完成编译与链接;仅有 EasyX 头文件既有 C4201 警告。
|
||
- 失败截图已复制到对应忽略运行目录并记录 SHA-256。
|
||
- 默认 Debug x64 单元测试 15/15 通过。
|
||
- Windows PowerShell 5.1.22000.2538 与 PowerShell 7.6.5 对三份 0B 脚本均为 0 个 AST 解析错误。
|
||
- `git diff --check` 通过,仅输出仓库换行规范提示。
|
||
- 新分析器重放既有有效 F24 KeyDown A/B:Forward 仍为 `valid/present_once`,Consume 仍为 `valid/absent`,兼容未携带 `targetScanCode` 的既有 schema v2 日志。
|
||
- `BUG-20261006-0003` 修复后的微软拼音 Forward 人工复验为 `valid`:Escape 原生规则唯一命中并转发,18/18 次定位成功、零孤立结果、零丢失,人工截图确认候选 UI 位于固定锚点附近。
|
||
- 微软拼音 Consume 有效重跑为 `valid`:Escape 唯一命中并消费,旧过程未调用,对应 EasyX 键消失;7/7 次定位成功、零孤立结果、零丢失且无提交结果。
|
||
- 搜狗 Forward 运行确认候选 UI 可见且位置正常,但目标 HWND 未出现 START/COMPOSITION/END;Escape 到达时 tracked state 仍为 Idle,`ComposingOnly` 零命中,定位 6/6、零丢失、收口完整。
|
||
- 搜狗最终 Consume 请求按 `ime_condition_not_met` 正确运行时 fail-open:`experimentValidity=runtime_fail_open`、零消费、实际 Forward 一次、EasyX 键出现一次、定位 3/3、零丢失,最终 verdict 为 `MustForward`。
|
||
- 2026-10-07 默认 Debug x64 单元测试 15/15、UI fixture 1 轮和 F24 KeyDown A/B 新运行通过;既有 KeyUp 与 baseline mismatch 证据经当前分析器回放保持有效。
|
||
- 当前工具环境限制: 自动 F24 runner 已完成构建并打开夹具,但 Codex 非交互工具宿主无法把窗口切到前台,`ClickWindowForActivation` 在发键前失败;该运行没有形成新的 A/B 结论,不记为代码失败,也不覆盖既有有效证据。
|
||
- 首次微软拼音复验边界: 旧定位实现导致窗口未响应,失败运行保留于 `TestResults/InputProbe/0B/20261006-231604-636-4bc775a1-a3f8-41b9-b466-1cce740bc195/forward`;该运行只证明诊断死锁,不用于评价 Forward 或微软拼音。
|
||
- 验证边界: 自动 `SendInput` 不能替代真实 IME;搜狗不能被要求“规则唯一命中”,因为缺少可证明 Composition 状态时正确行为就是零命中并 fail-open。该 Fix 以固定定位链、原生键观察、零丢失和明确 `MustForward` 结论关闭,不把运行时 Forward 伪装成 Consume。
|
||
|
||
## 落地信息
|
||
|
||
- Commit: 待验证与用户确认后提交
|
||
- PR:无
|
||
- 发布版本:未发布
|
||
- 备注: 本修复不进入阶段 1,不实现 FocusManager、生产 InputCollector 或键盘路由。
|