Skip to content

[windows] OS hook 与主窗口前台热键双事件源并存,听写生命周期存在失配风险 #154

@Cooper-X-Oak

Description

@Cooper-X-Oak

治理归属

  • 族群:E 族群 / helper-window 与输入交互契约
  • canonical PR:docs(windows): 跟踪双热键事件源生命周期 #155
  • 主修范围:Windows 双热键事件源、input source ownership、state-machine precedence、mixed-source regression
  • 不默认并入:主窗口外观、capsule geometry、helper-window hidden-state 语义、startup visible/ready 视觉暴露
  • 参考:
    • docs/windows-window-governance-board.zh-CN.md
    • docs/2026-05-02-window-capability-family-audit.md
    • docs/github-tracking/windows-window-family-canonical-map.md

现象 / Symptom

Windows 的听写生命周期目前有两条事件入口:

  • OS-level low-level keyboard hook
  • 主窗口聚焦时的 renderer-level window hotkey forwarder

macOS/Linux 则只有 backend/global listener 这一条主入口。也就是说,Windows 当前不是“同一状态机的同一输入源”,而是“双事件源共同驱动同一个 lifecycle state machine”。

这类设计即使暂时没有稳定复现的 field bug,也属于高风险不一致:

  • press / release 次序可能来自不同来源
  • focus 切换时可能只收到一半边沿
  • hold mode 与 toggle mode 都可能出现 Windows-only stuck / double-trigger / phase drift

证据 / Evidence

  • openless-all/app/src/App.tsx:63-80
    • Windows 前端在 keydown/keyup 上主动转发 window-local hotkey event
  • openless-all/app/src-tauri/src/coordinator.rs:739-779
    • backend 额外提供 handle_window_hotkey_event 入口
  • openless-all/app/src-tauri/src/hotkey.rs
    • Windows 还有独立的 WH_KEYBOARD_LL low-level hook
  • openless-all/app/src-tauri/src/coordinator.rs:645-709
    • 当前只靠 hotkey_trigger_held 做共享 edge dedupe,没有源级别的 precedence contract

5 Whys / 根因分析

  1. 为什么这是生命周期问题,而不只是“多一条 fallback”?
    • 因为它不是只读 telemetry,而是第二条会真正触发 start/stop/cancel 的状态机输入源。
  2. 为什么双事件源会带来风险?
    • 因为同一个 session phase 可能由 source A 按下、source B 抬起,形成部分边沿丢失或重复。
  3. 为什么这个问题主要在 Windows?
    • 因为只有 Windows 同时保留了 OS hook 和 renderer forwarder 两套路径。
  4. 为什么这偏离了 macOS 的原始设计意图?
    • 原始意图是“用户一个手势对应一个稳定的生命周期转换”;Windows 当前更像“一个手势可能穿过两条不同的输入链路”。
  5. 为什么之前没有完全暴露?
    • 因为现有 smoke 主要验证“某条路径能工作”,没有验证 mixed-source ordering、focus switch 和 hold/release 失配。

平台边界 / Platform Scope

  • 直接风险范围:Windows-only 实现风险。
  • 问题层面:input source ownership、state-machine edge contract、focus-sensitive lifecycle driving。
  • 全平台风险判断:症状只在 Windows 这条实现链上产生,但它影响的是核心 dictation lifecycle,而不是纯 Windows UI 细节。

认领 / Ownership

影响 / Impact

  • 可能导致 Windows-only 的听写开始/结束失配
  • 会增加“热键偶发不稳定、卡住、重复触发”的排障成本
  • 会妨碍后续把生命周期问题与 UI 问题拆开判断

建议接受标准 / Proposed Acceptance Criteria

  • 明确 Windows 上谁是 dictation lifecycle 的唯一 owner source,或明确定义 source precedence
  • 补一组 mixed-source smoke / tests:OS press + window release、window press + OS release、focus switch during hold
  • 保证 hold / toggle 两种模式在 Windows 上都不会因双来源而 strand session phase
  • 文档化为什么 Windows 需要额外前台窗口路径,以及它与 macOS/Linux 的边界差异

TODO / 不确定项

  • 是否应最终把窗口前台路径下沉回 backend hotkey adapter,而不是继续暴露一条 renderer IPC lifecycle input
  • 是否需要把该问题与现有前台热键兼容性问题单独区分,避免“能用”和“生命周期一致”被混为一谈

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingwindowsWindows-specific issue

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions