Skip to content

macOS: Fix Korean composing text commit key handling#4478

Open
jayychoi wants to merge 1 commit intorust-windowing:masterfrom
jayychoi:fix/korean-ime-commit-handling
Open

macOS: Fix Korean composing text commit key handling#4478
jayychoi wants to merge 1 commit intorust-windowing:masterfrom
jayychoi:fix/korean-ime-commit-handling

Conversation

@jayychoi
Copy link

@jayychoi jayychoi commented Feb 7, 2026

  • Tested on all platforms changed
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality

Summary

A minimal fix that addresses a major pain point in macOS Korean IME handling.

Fixes two interrelated bugs reported downstream in Alacritty:

  1. Double space input: With Korean IME active and no text being composed, pressing Space once inserts two spaces
    When using CJK input method in MacOS, space is inserted twice per 1 stroke. alacritty/alacritty#8079
  2. Key loss: After committing composed text, trigger keys of ASCII characters were silently dropped.
    Keyboard input does not work with CJK IME (macOS, 0.12.1) alacritty/alacritty#6942

Root Cause

Bug 1: Double Space

When Space is pressed with Korean IME active but no active composition, macOS calls setMarkedText(" ") followed by insertText twice within the same interpretKeyEvents. The first insertText call set ImeState::Committed but did not clear marked_text. The second call still saw hasMarkedText() == true and emitted a duplicate Ime::Commit(" ").

Bug 2: Key Loss

doCommandBySelector had a blanket early-return guard when ImeState == Committed:

if self.ivars().ime_state.get() == ImeState::Committed {
    return;
}

This was originally added to prevent Enter from being sent twice when used to confirm IME input. However, it was too aggressive — it swallowed ASCII key commands after a commit, preventing forward_key_to_app from being set. This caused trigger keys to be silently dropped.

Fix

In insertText:

  • Clear marked_text immediately after committing, so subsequent insertText calls within the same interpretKeyEvents no longer see stale preedit state.
  • Add an else if Committed branch that sets forward_key_to_app = true for post-commit characters, forwarding them as regular key events instead of duplicate IME commits.

In doCommandBySelector:

  • Remove the Committed early-return guard. With marked_text properly cleared in insertText, the double-input issue this guard was meant to prevent no longer occurs.

Test Results (Korean IME)

Tested with the ime example (cargo run --example ime). Below are before/after comparisons for each Korean IME scenario.

Space on empty field (Bug 1: double space)

Before (🔴 double space)After (✅ single space)
Preedit:  , caret Some((1, 1))
Preedit: , caret None
 |                          ← 1st space
Preedit: , caret None
  |                         ← 2nd space
                             (duplicate!)
Preedit:  , caret Some((1, 1))
Preedit: , caret None
 |                          ← 1 space only ✓

"한" + Space commit (already worked)

Before (✅)After (✅)
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: 한 , caret Some((4, 4))
Preedit: , caret None
한 |
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: 한 , caret Some((4, 4))
Preedit: , caret None
한 |

"한" + Enter commit (already worked)

Before (✅)After (✅)
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: , caret None
한|
한                          ← newline ✓
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: , caret None
한|
한                          ← newline ✓

"한" + digit (Bug 2: key loss)

Before (🔴 digit dropped)After (✅ digit forwarded)
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: , caret None
한|                     ← "5" silently dropped!
Preedit: , caret None
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: , caret None
한|
한5|                    ← digit forwarded ✓

"한" + ASCII (Bug 2: key loss)

Before (🔴 ASCII dropped)After (✅ ASCII forwarded)
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: , caret None
한|                     ← ";" silently dropped!
Preedit: , caret None
Preedit: ㅎ, caret Some((3, 3))
Preedit: 하, caret Some((3, 3))
Preedit: 한, caret Some((3, 3))
Preedit: , caret None
한|
한;|                    ← ASCII forwarded ✓

Impact on Other CJK IMEs

These changes only affect the Korean IME code path. Japanese and Chinese IMEs are not affected because:

  • Japanese/Chinese IMEs consume confirmation keys (Enter/Space) internally — they never reach doCommandBySelector or trigger a second insertText within the same interpretKeyEvents call.
  • The Committed state is reset to Ground at the end of each keyDown, so the next keypress always starts from Ground regardless of IME.

Japanese and Chinese IMEs were tested and confirmed no regression (nihon→にほん, nihao→你好, emoji selection, etc.).

Fix space being inserted twice.
Fix ASCII characters being dropped when typed during preedit state.
@jayychoi jayychoi requested a review from madsmtm as a code owner February 7, 2026 13:50
@madsmtm madsmtm added the DS - appkit Affects the AppKit/macOS backend label Feb 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DS - appkit Affects the AppKit/macOS backend

Development

Successfully merging this pull request may close these issues.

2 participants