close
Skip to content

Fix Korean IME arrow key inserting character at wrong position#20039

Merged
DHowett merged 1 commit into
microsoft:mainfrom
drvoss:fix/korean-ime-composition-key-forward
Mar 31, 2026
Merged

Fix Korean IME arrow key inserting character at wrong position#20039
DHowett merged 1 commit into
microsoft:mainfrom
drvoss:fix/korean-ime-composition-key-forward

Conversation

@drvoss
Copy link
Copy Markdown
Contributor

@drvoss drvoss commented Mar 31, 2026

Summary of the Pull Request

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.

References and Relevant Issues

Closes #20038
Related to #19738 (Remove TF_TMAE_UIELEMENTENABLEDONLY).

Detailed Description of the Pull Request / Additional comments

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:

cpp if (GetTSFHandle().HasActiveComposition()) { return true; }

This mirrors the existing behavior in conhost v1 (src/interactivity/win32/windowio.cpp lines 361-364). When TSF has an active composition, key events are suppressed from the PTY. 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.

PR Checklist

When an arrow key is pressed during an active Korean IME composition,
_KeyHandler was forwarding the key to the PTY immediately via
_TrySendKeyEvent. This moved the cursor before the async TSF edit
session had a chance to commit the finalized text, causing the
committed character to land at the wrong position.

The fix adds a guard that mirrors the existing behavior in the conhost
v1 key handler (src/interactivity/win32/windowio.cpp lines 361-364):
suppress all key events from being forwarded to the PTY while TSF has
an active composition. The 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 microsoft#17067 (v1.22)
because the old TSF implementation used WinRT CoreTextServices via
XAML (TSFInputControl.xaml/.cpp). In that model the XAML framework
handled composition key events before they reached _KeyHandler, so
_KeyHandler never received them during composition. The new custom
Win32 TSF context introduced in microsoft#17067 no longer intercepts keys at
the XAML level, making _KeyHandler responsible for this check.

The bug was not visible in v1.22-v1.23 for Korean because the Korean
IME was excluded from the TSF path by TF_TMAE_UIELEMENTENABLEDONLY.
It became visible in v1.24 when PR microsoft#19738 removed that flag, causing
the Korean IME to activate through TSF for the first time.

Closes microsoft#20038
Copy link
Copy Markdown
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lhecker this makes sense to me - you agree?

@DHowett
Copy link
Copy Markdown
Member

DHowett commented Mar 31, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@DHowett DHowett enabled auto-merge (squash) March 31, 2026 22:07
Copy link
Copy Markdown
Member

@lhecker lhecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for investigating this for us!

@DHowett DHowett merged commit 14f4271 into microsoft:main Mar 31, 2026
14 of 16 checks passed
DHowett pushed a commit that referenced this pull request Mar 31, 2026
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

(cherry picked from commit 14f4271)
Service-Card-Id: PVTI_lADOAF3p4s4BQX0-zgo0JpM
Service-Version: 1.25
DHowett pushed a commit that referenced this pull request Mar 31, 2026
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

(cherry picked from commit 14f4271)
Service-Card-Id: PVTI_lADOAF3p4s4BBcTlzgo0JpQ
Service-Version: 1.24
DHowett pushed a commit that referenced this pull request Apr 17, 2026
This commit fixes a regression in v1.24, where composing Korean text
in-between existing characters causes text to the right to be hidden.
This is problematic for the Korean IME, because it commonly inserts
the composed syllable between existing characters.

The fix compresses (removes/absorbs) available whitespace to the right
of the composition to make space for any existing text. This means
that a composition now virtually acts in insert mode.

Closes #20040
Refs #19738
Refs #20039

## Validation Steps Performed

1. Korean IME: compose `ㄷ` between `가` and `나` → display shows `가ㄷ나`,
`나` visible.
2. TUI box (vim prompt with padding): compose inside padded text box →
border preserved in place.
3. Composition at end of line (no chars to right): unaffected.
4. English and other IME input: not affected (no active composition
row).

Co-authored-by: jason <drvoss@users.noreply.github.com>
Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
DHowett pushed a commit that referenced this pull request Apr 17, 2026
This commit fixes a regression in v1.24, where composing Korean text
in-between existing characters causes text to the right to be hidden.
This is problematic for the Korean IME, because it commonly inserts
the composed syllable between existing characters.

The fix compresses (removes/absorbs) available whitespace to the right
of the composition to make space for any existing text. This means
that a composition now virtually acts in insert mode.

Closes #20040
Refs #19738
Refs #20039

## Validation Steps Performed

1. Korean IME: compose `ㄷ` between `가` and `나` → display shows `가ㄷ나`,
`나` visible.
2. TUI box (vim prompt with padding): compose inside padded text box →
border preserved in place.
3. Composition at end of line (no chars to right): unaffected.
4. English and other IME input: not affected (no active composition
row).

Co-authored-by: jason <drvoss@users.noreply.github.com>
Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
(cherry picked from commit 7a83c0f)
Service-Card-Id: PVTI_lADOAF3p4s4BQX0-zgo0JpY
Service-Version: 1.25
DHowett pushed a commit that referenced this pull request Apr 30, 2026
This commit fixes a regression in v1.24, where composing Korean text
in-between existing characters causes text to the right to be hidden.
This is problematic for the Korean IME, because it commonly inserts
the composed syllable between existing characters.

The fix compresses (removes/absorbs) available whitespace to the right
of the composition to make space for any existing text. This means
that a composition now virtually acts in insert mode.

Closes #20040
Refs #19738
Refs #20039

1. Korean IME: compose `ㄷ` between `가` and `나` → display shows `가ㄷ나`,
`나` visible.
2. TUI box (vim prompt with padding): compose inside padded text box →
border preserved in place.
3. Composition at end of line (no chars to right): unaffected.
4. English and other IME input: not affected (no active composition
row).

Co-authored-by: jason <drvoss@users.noreply.github.com>
Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
(cherry picked from commit 7a83c0f)
Service-Card-Id: PVTI_lADOAF3p4s4BBcTlzgo0Jpc
Service-Version: 1.24
Qmoony pushed a commit to Qmoony/terminal that referenced this pull request May 11, 2026
…osoft#20041)

This commit fixes a regression in v1.24, where composing Korean text
in-between existing characters causes text to the right to be hidden.
This is problematic for the Korean IME, because it commonly inserts
the composed syllable between existing characters.

The fix compresses (removes/absorbs) available whitespace to the right
of the composition to make space for any existing text. This means
that a composition now virtually acts in insert mode.

Closes microsoft#20040
Refs microsoft#19738
Refs microsoft#20039

## Validation Steps Performed

1. Korean IME: compose `ㄷ` between `가` and `나` → display shows `가ㄷ나`,
`나` visible.
2. TUI box (vim prompt with padding): compose inside padded text box →
border preserved in place.
3. Composition at end of line (no chars to right): unaffected.
4. English and other IME input: not affected (no active composition
row).

Co-authored-by: jason <drvoss@users.noreply.github.com>
Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Korean IME (v1.24+): Arrow key during composition inserts character at wrong position

3 participants