Files
StellarX-kaifa/开发记录/模块/Module-20261007-0005-阶段1输入采集与规范化实现.md
T

117 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 新增功能模块 / 模块重构
> 本记录描述通用输入系统阶段 1 的实际实现边界与验证证据。它不代表焦点、键盘路由或 TextBox 自绘已经完成。
## 基本信息
- 模块 ID:Module-20261007-0005
- 模块名称:阶段 1 输入采集、规范化与跨线程观察队列
- 状态:实现完成,自动回归与真实 IME / KEY1–6 人工兼容性回归通过
- 类型:新增模块 / 架构演进
- 所属系统 / 子系统:StellarX Core / Window / Native Input
- 版本 / 分支:`master`,未提交工作区
- 环境:Windows + Visual Studio 2022 + MSVC v143 + EasyX + MBCS 工程配置
- 负责人:Codex 协作实现
## 背景与目标
- 阶段 0 已确认 EasyX HWND/WndProc 和宿主 `peekmessage` 分属两个线程,当前窗口是 ANSI、中文布局为 CP936。
- 阶段 1 只建立“WndProc 采集与规范化 → 跨线程队列 → 宿主安全点观察排空”的地基,不实现 FocusManager、KeyboardRouter、消费白名单或控件键盘行为。
- 为保持现有行为,本阶段继续把 EasyX `EX_KEY` 交给旧 `Control::handleEvent()`;真正换轨留到阶段 3。
## 实现边界
### 已实现
- 公共事件模型:`KeyEvent`、`TextInputEvent`、`CompositionEvent`、`WindowActivationEvent`、`NativeInputEvent`。
- 每 Window `NativeInputContext` 和正确性优先的 `mutex + deque` 跨线程队列。
- 全局 atomic sequence、队列 pushed/popped/depth/maxDepth 统计和宿主观察统计。
- Key lParam 的 repeat、scan code、extended、alt context、previous state、system key 解析,以及左右 Ctrl/Shift/Alt 快照。
- 原始 `VK_PROCESSKEY` 单键所有权与 `imeComposingAtCapture` 生命周期快照的两轴模型。
- Unicode `WM_CHAR` 代理对、`WM_UNICHAR` UTF-32、ANSI/CP936 DBCS lead/trail 解码。
- `WM_IME_STARTCOMPOSITION/COMPOSITION/ENDCOMPOSITION` 状态机、缺失 START 时的惰性 `compositionId`。
- `GCS_RESULTSTR` 唯一正式提交,以及 `WM_IME_CHAR/WM_CHAR` 独立游标的邻接回声去重。
- `WM_SETFOCUS/WM_KILLFOCUS` 激活事件和重新获焦时的键盘布局快照。
- 主循环与模态循环在安全点观察排空;阶段 1 不调用控件路由。
- WndProc 分配失败/异常的无分配故障快照;宿主安全点 fail-stop,返回码为 `-2`。
- 非法文本序列、IMM 查询失败、残留半截解码状态的无原文累计诊断。
### 明确未实现
- FocusManager、FocusScope、Tab 顺序和焦点回退。
- KeyboardRouter、祖先冒泡、Dialog 默认动作和 `VK_PROCESSKEY` 下游抑制。
- 生产键盘消费白名单。
- Button、Table、TabControl、Dialog 的键盘接入。
- TextBox UTF-16 编辑内核、自绘、选区、剪贴板和 IME caret 定位。
- 将旧 `EX_KEY` 从 `Control::handleEvent()` 中移除。
## 关键契约
1. WndProc 线程只做状态快照、规范化和短临界区入队;不执行用户回调、控件 mutation 或文件 I/O。
2. 原始 `VK_PROCESSKEY` 只表示当前单键归 IME,不启动持久组合态,也不分配 `compositionId`。
3. 标准 IME 生命周期只由 START/COMPOSITION/END 维护;两轴均在采集时快照,延迟消费者不得重查全局输入状态。
4. `GCS_RESULTSTR` 只产生一次 `TextInputEvent`;紧邻且完整匹配的 IME/字符回声只观察,不重复入队。
5. END 转发旧窗口过程前不清回声,避免漏掉 END 处理中的嵌套或紧随回声。
6. 正常资源条件下队列不得丢输入;分配失败或队列异常必须显式 fail-stop。
7. 阶段 1 保留旧 EX_KEY 业务旁路;阶段 3 才把 EX_KEY 改为纯排空并启用新路由。EX_MOUSE 永久保留。
## 实现文件
- `include/StellarX/InputEvents.h`
- `src/StellarX/internal/SxNativeInputInternal.h`
- `src/StellarX/SxNativeInput.cpp`
- `include/StellarX/Window.h`
- `src/StellarX/Window.cpp`
- `src/StellarX/Dialog.cpp`
- `tests/StellarXTests.cpp`
- `tests/UiFixture.cpp`
- `scripts/run-ui-tests.ps1`
## 自动验证
### 逻辑测试
- Key lParam 和左右修饰键快照。
- `VK_PROCESSKEY` 不启动组合,且普通键在生命周期内不被改写为 IME 所有。
- IME 生命周期、Cancel/End 和 `compositionId`。
- CP936 lead/trail 合并。
- `WM_UNICHAR` 能力查询、UTF-32 代理对和非法 scalar 计数。
- GCS 结果回声双通道去重,以及 END 后紧随回声保持。
- CP936 不可表示结果只抑制实际替代字节回声,不误吞随后无物理键前导的程序化文本。
- 20,000 事件生产/消费跨线程严格顺序。
- 注入式分配失败 fail-stop。
- 宿主观察排空无积压。
### 真实 EasyX `input-shadow`
- 总计注入 4,000 条 F24 KeyDown/KeyUp,分为低于 EasyX 旧队列有效容量的持续小波次。
- 同时验证旧 `EX_KEY -> handleEvent()` 与新 NativeInputQueue;两面均必须完整看到 2,000 Down + 2,000 Up。
- 新队列必须 `pushed == popped`、depth 归零、sequence 严格、无 fault、无文本规范化异常。
- 两轮负载之间检查私有内存不出现线性增长,并验证窗口仍能响应鼠标点击。
- 脚本新增 `-OnlyScenario input-shadow`,默认不传参时仍执行完整 UI 套件。
## 当前证据
- Debug x64、x64 ASan、Release x64、Debug Win32 均为 27/27;KEY1–6 全部编译/链接成功。
- 完整 UI fixture 曾一轮通过;最终源码单独复跑 `input-shadow` 通过:旧面 4000/4000,新面 4000/4000,最大新队列深度 40,最终深度 0,内存增长 0,顺序严格且无故障。
- 最终完整 UI 的两次追加复跑分别在 `geometry`、`ownership` 的鼠标点击检查点超时,均未进入 `input-shadow`。该结果按既有桌面焦点/SendInput 抖动保留,不写成最终全套连续通过;阶段 1 核心输入场景已有最终源码独立证据。
- PowerShell 5.1 与 Codex 运行时 PowerShell 7.6.5 AST、`StellarX.Core.vcxproj` XML、`git diff --check` 均通过。
- 2026-10-07 微软拼音有效会话 `manual-20261007-192828-293-MicrosoftPinyin` 与搜狗有效会话 `manual-20261007-193100-289-Sogou` 均为 sequence 严格、零丢失、零原文泄漏、3 次正式结果与 `WM_IME_CHAR` 回声完整匹配,并观察到正常关闭和 `session_end`。
- 用户完成 KEY1–6 人工交互,当前快照未发现卡顿、重复提交、双触发或既有行为退化。人工用例中 KEY5/KEY6 的 `120 ms` Tooltip 覆盖已移除,恢复验证框架默认 `1000 ms`;KEY2 日志轮转阈值由 1 KiB 调整为 10 MiB。
- 上述用例调整后,正式 `NonUI` 再次确认四种单元测试配置均为 27/27,KEY1–6 全部编译链接成功。
- 输入探测夹具没有文本焦点叶子和 caret 锚点,候选框使用系统回退位置不属于阶段 1 失败;本记录不据此宣称 FocusManager、TextBox 或候选框定位已经集成。
## 风险与后续
- 当前微软拼音/搜狗 + ANSI EasyX + CP936 的结果/回声顺序有阶段 0 真实证据;其他 IME、窗口 A/W 属性和系统版本仍需保持可诊断回退。
- EasyX 旧 `EX_KEY` 队列在当前环境呈现约 63 条有效容量;压力测试必须使用持续小波次验证排空,不能把第三方瞬时溢出误判为新队列丢失。
- 阶段 3 是明确兼容性切换点:派生控件必须从 `handleEvent(EX_KEY)` 迁移到新的 routed key 接口;届时再补 `开发记录/MIGRATION.md` 条目。
- 阶段 1 的自动验证和用户人工兼容性回归已经收口;下一步可进入阶段 2 FocusManager 设计/实现,但仍不得提前混入阶段 3 KeyboardRouter 或阶段 5 TextBox 自绘。
## 关联记录
- [`Module-20261001-0001-通用输入焦点与键盘路由系统设计.md`](./Module-20261001-0001-通用输入焦点与键盘路由系统设计.md)
- [`Module-20261001-0002-阶段0A输入链路被动探测规格.md`](./Module-20261001-0002-阶段0A输入链路被动探测规格.md)
- [`Module-20261001-0003-阶段0C宽字符绘制与IME坐标探测规格.md`](./Module-20261001-0003-阶段0C宽字符绘制与IME坐标探测规格.md)
- [`Module-20261001-0004-阶段0B原生键盘消息隔离消费实验规格.md`](./Module-20261001-0004-阶段0B原生键盘消息隔离消费实验规格.md)