Repository navigation
[Windows] Fix Korean 3-beolsik IME dropping space, digits and punctuation - #194270
Open
sujeong000 wants to merge 2 commits into
Open
sujeong000 wants to merge 2 commits into
sujeong000 wants to merge 2 commits into
Conversation
…ITION
IMM32 does not guarantee the order of WM_IME_COMPOSITION (GCS_RESULTSTR)
and WM_IME_ENDCOMPOSITION. Most IMEs send the result first, and
FlutterWindow relies on that: WM_IME_ENDCOMPOSITION commits the
composing text and destroys the IME window, and DefWindowProc is
suppressed for these messages so that the result is not inserted twice.
The Microsoft Korean IME in the 3-beolsik (390 / Final) layouts consumes
every key and, when a space, digit or punctuation key ends a
composition, sends WM_IME_ENDCOMPOSITION *before* the GCS_RESULTSTR
(for example "겨 "). With nothing being composed it sends
WM_IME_ENDCOMPOSITION followed by the result (" ") without any
WM_IME_STARTCOMPOSITION. In both cases the result string could not be
read after the composition had ended and was dropped, and because
DefWindowProc was suppressed there was no WM_CHAR fallback either, so
the typed character was lost.
When WM_IME_ENDCOMPOSITION arrives while a GCS_RESULTSTR message is
already queued, defer ending the composition until that result has been
applied. A result that arrives with no composition in progress but with
a deferred end is delivered as text input. A result that arrives after a
non-deferred end (the IME re-sends the committed text when the window
loses focus) is ignored so that it is not inserted twice. The result
string is now readable without an active IME window; the composing
string keeps its previous behaviour.
IMEs that send the result before WM_IME_ENDCOMPOSITION never take the
deferred path and behave as before.
Fixes flutter#140537
Contributor
There was a problem hiding this comment.
Code Review
This pull request modifies the Windows platform shell to defer ending IME compositions when a result string is queued, resolving issues with certain IME layouts like Microsoft Korean 3-beolsik. It introduces state variables to track composition status, utilizes Win32PeekMessage to detect queued results, and updates TextInputManager to allow reading result strings after composition ends. Unit tests are added to verify these behaviors. The review feedback suggests replacing std::optional::value() with operator* when retrieving the committed text to avoid exception-handling overhead and adhere to the Google C++ Style Guide.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #140537
Problem
With the Microsoft Korean IME set to a 3-beolsik layout (390 or Final), a space, digit or punctuation character typed into a
TextFieldon Windows is silently dropped, both while Hangul is being composed and when nothing is being composed. 2-beolsik is unaffected.Screen Recordings
Microsoft Korean IME, 3-beolsik 390, windows 10 22H2
Before - the space, digit and period are dropped.
before.mov
After - all characters are inserted.
after.mov
Cause
IMM32 does not guarantee the relative order of
WM_IME_COMPOSITIONwithGCS_RESULTSTRandWM_IME_ENDCOMPOSITION: the WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION and WM_IME_STARTCOMPOSITION pages describe each message in isolation, and Microsoft's own sample inserts the result string whenGCS_RESULTSTRis set without referring toWM_IME_ENDCOMPOSITIONat all. Most IMEs send the result first, andFlutterWindowis written for that order:OnImeEndCompositioncommits the composing text and destroys the IME window, andDefWindowProcis suppressed for these messages so that the result is not inserted twice viaWM_IME_CHAR/WM_CHAR.In the 3-beolsik layouts the IME consumes every key (
WM_KEYDOWNwithVK_PROCESSKEY) and emits the characters itself. Captured with a Win32 message probe on Windows 10 22H2 (19045), Microsoft Korean IME, 3-beolsik 390:After
WM_IME_ENDCOMPOSITIONthe composing text had already been committed andTextInputManager::GetStringrefused to read the result (ime_active_was false), so the result string was dropped. Native controls are unaffected becauseDefWindowProcconverts the result toWM_IME_CHAR/WM_CHARregardless of order; Flutter suppresses that path.Fix
FlutterWindow::OnImeEndCompositionpeeks the message queue (PM_NOREMOVE, the same approachKeyboardManageralready uses to look for a followingWM_CHAR) and, if aWM_IME_COMPOSITIONwithGCS_RESULTSTRis already queued, defers ending the composition until that result has been processed. IMEs that send the result first see no change: nothing is queued, so the composition ends immediately as before.FlutterWindow::OnImeCompositionapplies a result to the composition when one is in progress (unchanged); delivers it viaOnTextwhen there is no composition but the end was deferred (the bare-space case above); and ignores a result that arrives after a non-deferred end. The last case happens when the window loses focus during composition: the Korean IME sendsWM_IME_ENDCOMPOSITIONwith nothing queued and then re-sends the committed text as a result, which previously was dropped only as a side effect of theime_active_check.TextInputManager::GetResultStringno longer requires an active IME window.GetComposingStringkeeps the previous check, so composing-string handling is unchanged.Tests
MockWindow.OnImeEndCompositionWaitsForQueuedResult: the end is deferred until the queued result, and the result is applied to the composition beforeOnComposeEnd.MockWindow.OnImeCompositionResultWithoutCompositionIsText: end + result with no composition is delivered viaOnText.MockWindow.OnImeCompositionResultAfterEndCompositionIsIgnored: the focus-loss re-send is not inserted again.OnImeCompositionResultandOnImeCompositionResultAndComposenow start a composition first, matching the real message order.MockWindowgained aWin32PeekMessagemock.Pre-launch Checklist
///).