From 14f42719547ba899e52f7b8e43f4b1d9038591f2 Mon Sep 17 00:00:00 2001 From: jason Date: Wed, 1 Apr 2026 07:37:15 +0900 Subject: [PATCH] Fix Korean IME arrow key inserting character at wrong position (#20039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a regression introduced in v1.24 where pressing an arrow key during Korean IME composition caused the committed character to be inserted at the wrong cursor position. Before #19738, the Korean IME activated through IMM32 (excluded from TSF by `TF_TMAE_UIELEMENTENABLEDONLY`) and was not affected by `TermControl::_KeyHandler` forwarding keys to the PTY during composition. After #19738, the Korean IME activates through TSF, making a missing composition guard in `_KeyHandler` visible as a bug. The sequence of events that causes the bug: 1. User presses Left arrow during active Korean IME composition (e.g. composing `가`). 2. `_KeyHandler` calls `_TrySendKeyEvent(VK_LEFT)` which enqueues `\x1b[D` to the PTY input queue. The cursor moves. 3. TSF processes the key. The Korean IME sees the arrow and ends the composition. 4. `OnEndComposition` schedules `_doCompositionUpdate` with `TF_ES_ASYNC`. 5. The async session fires, reads finalized text `가`, calls `HandleOutput("가")`. 6. PTY processes `[\x1b[D, "가"]`: cursor moves left first, then `가` is inserted at the wrong (already-moved) position. The fix adds a guard before `_TrySendKeyEvent`, which mirrors the existing behavior in conhost (windowio.cpp). When TSF has an active composition, key events are not converted into input. The Korean IME re-injects navigation and confirmation keys after the composition ends, at which point `HasActiveComposition()` returns false and they are forwarded normally. **Historical note:** This guard was not needed before PR #17067 (v1.22) because the old implementation used WinRT `CoreTextServices` via XAML (`TSFInputControl.xaml`). The XAML framework intercepted composition key events before `_KeyHandler`. The new custom Win32 TSF context in #17067 no longer does this. The bug was latent from v1.22 but only became visible for Korean in v1.24 when #19738 removed `TF_TMAE_UIELEMENTENABLEDONLY`. ## Validation Steps Performed 1. Open Windows Terminal with Korean IME (Dubeolsik layout). 2. Type `rk` to begin composing `가` (composition active, syllable not yet committed). 3. Press the Left arrow key. 4. Before fix: `가` is inserted one cell to the left of the intended position. 5. After fix: `가` is inserted at the correct position, then cursor moves left. Also verified: - Normal Korean text input (typing without arrow keys) still works correctly. - Arrow key navigation when no composition is active still works correctly. - English and other IME input is not affected. Closes #20038 Refs #19738 --- src/cascadia/TerminalControl/TermControl.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index eb5032ef85..9f632d276d 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1795,6 +1795,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation return true; } + // While TSF has an active composition, key events must not be forwarded + // to the PTY directly. The IME re-injects navigation/confirmation keys + // after composition ends, at which point HasActiveComposition() == false + // and they are forwarded normally. This mirrors conhost v1 behavior in + // src/interactivity/win32/windowio.cpp. + if (GetTSFHandle().HasActiveComposition()) + { + return true; + } + if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown)) { return true;