Two host-timing paths could hide or displace the candidate window between ordinary keystrokes. Both get much more likely while the machine is busy, which is when the flicker is reported. TS_E_NOLAYOUT from ITfContextView::GetTextExt means "not laid out yet, ask again", but _GetTextExt reported it as a valid measurement at INVALID_Y. Server then parks the host off-screen for that frame and the next successful measurement pulls it back. Chromium-based hosts answer TS_E_NOLAYOUT whenever their renderer is behind, so under load this repeats per keystroke. Keep the anchor the session already has instead, once it has one; composition start still falls back to the sentinel rather than reusing a stale anchor from another document. TF_LC_DESTROY was treated as candidate-session teardown for every process except a hardcoded Telegram.exe. Hosts that transiently destroy and recreate their context view mid-composition therefore got a HideCandidate between keystrokes, which clears the live composition on the Server side: the window blinks and the pending candidate can no longer be committed. Gate on a live composition instead of a process name. Commit, cancel, focus loss, and presenter cleanup all end the composition first, so they still tear the session down through their own paths.
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/ 下是本机开发用的生成、构建、注册和签名脚本,从本目录运行。
引擎契约来自 ../vendor/MetasequoiaImeEngine,需要 git submodule update --init --recursive。