问题:成对标点上屏后靠 PostMessage + SendInput(VK_LEFT) 把光标送回中
间。(〈《""' 全是 Shift 组合键,而 PostMessage 是异步的,合成的
VK_LEFT 常常在物理 Shift 仍按住时抵达宿主,于是变成 Shift+Left——右
半边标点被选中。机器差异只是竞态窗口大小的差异,【】这类不需要 Shift
的键从不复现,可作对照。另外按右半边标点会再插一个,得到(内容));
而引号方向由左右交替维护,右引号根本打不出来。
改动:
- 修饰键闸门:送键前用 GetAsyncKeyState 查 Shift/Ctrl/Alt/Win 是否物
理按下,按住就挂 15ms 重试定时器推迟,而不是合成一次 Shift 抬起
(WindInput 的源码注释记录了那条路走不通)。Win 键同样要拦,Win+Left
是贴靠窗口的快捷键,箭头落进按住的 Win 组合会重排桌面。带 2s 超时
和焦点会话校验,过期即放弃并清栈。
- 自生成按键标记:合成的 INPUT 带 dwExtraInfo =
PAIRED_PUNCTUATION_SENDINPUT_EXTRA_INFO,四个按键 sink 入口和
_BareShiftKeyboardHookProc 在开头识别后放行,IME 不会把自己发出去
的键再处理一遍,跳过配对时也就不会清掉正在走的那个栈。
- 配对栈:参照 WindInput 的 pair_tracker,记录 {左半, 右半, 焦点令牌},
深度上限 16。按右半边标点时先读文档确认光标右边确实是那个字符,确
认后跳过而不是再插一个;焦点切换、中英与标点模式切换、任何结构性
按键(方向键、Backspace、回车等)都清栈。
- 引号在成对模式下每次按都开一对新的,不再走 GetPunctuation 的左右交
替,右引号因此可达。
- 消息窗口销毁时走 _CancelPairedPunctuationCaretMove 而不是裸
KillTimer:重试定时器额外挂着 _pairedCaretRetryTimerActive,只杀定
时器会把标志停在 true,消息窗口重建后就再也装不上定时器,等于延迟
送键这条路被静默废掉。
已知限制:连续按住 Shift 跨两个成对标点键(如 " 接 ()会得到 ""()
而不是 "()"。这是延迟送键设计的固有取舍,WindInput 同样如此;要消
掉需走 TSF SetSelection 兜底,或合成一次 Shift 抬起。
Backspace 整对删除未实现:判断左右两半必须读文档,而读文档需要编辑
cookie,不吃键的 Backspace 路径没有。不验证就合成 VK_DELETE,在栈因
鼠标点击而失效时会删掉无关字符。
验证:Debug 与 Release 的 x64、x86 四种组合均编译通过;clang-format
对改动文件无差异;本机实测成对标点上屏后光标落在成对标点中间。
windows —— TSF 文本服务
水杉输入法在 Windows 上的前端:一个纯 TSF 方案的文本服务 DLL,注入宿主进程,负责按键预判、焦点与线程归属、composition 的 edit session 写入,以及与 Server 之间的命名管道协议。候选窗、工具栏、设置页和实际的候选计算都在 ../server/,本目录只做宿主适配。
产品介绍、安装和功能说明见仓库根目录的 README;本目录的代码地图、TSF/COM 硬约定和改动要求见 AGENTS.md。
构建
从仓库根目录配置,源码目录是本目录:
cmake -S windows -B windows/build -A x64 `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" `
-DVCPKG_TARGET_TRIPLET=x64-windows-static
cmake --build windows/build --config Release --parallel
ctest --test-dir windows/build -C Release --output-on-failure
Win32 用 -A Win32 和 x86-windows-static;安装包两种架构都要。scripts/ 下是本机开发用的生成、构建、注册和签名脚本,从本目录运行。
引擎契约来自 ../engine/contracts/,是本仓的目录,普通检出就有。TSF 只引用这些头文件,不链接引擎运行库。