# 新增功能模块 / 模块重构 > 本记录描述通用输入系统阶段 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)