Skip to content

Fix Windows Korean IME caret position during composition#186353

Merged
auto-submit[bot] merged 2 commits into
flutter:masterfrom
CHOIgoung:fix-windows-korean-ime-caret-position
Jun 1, 2026
Merged

Fix Windows Korean IME caret position during composition#186353
auto-submit[bot] merged 2 commits into
flutter:masterfrom
CHOIgoung:fix-windows-korean-ime-caret-position

Conversation

@CHOIgoung

@CHOIgoung CHOIgoung commented May 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #140739

Summary

This fixes a Windows Korean IME caret positioning issue where the caret could appear before the active composing syllable while entering Korean text.

The Windows embedder previously queried the IME composing cursor position for every WM_IME_COMPOSITION update. However, Microsoft Korean IME can send composition updates with GCS_COMPSTR or GCS_RESULTSTR without the GCS_CURSORPOS flag. In that case, the cursor position is not explicitly part of the message, so querying it can produce a missing or stale cursor value. That stale value can then be converted into Flutter's text selection and place the visual caret before the end of the active composing range.

Recordings

Before this fix, the caret can appear before the active Korean composing syllable:

Before fix: Windows Korean IME caret appears before the active composing syllable

After this fix, the caret stays at the end of the active composing syllable:

After fix: Windows Korean IME caret stays at the composition end

Root Cause

For Korean input such as 가나달, the engine receives WM_IME_COMPOSITION updates for the active syllable. Local runtime logging showed that Microsoft Korean IME sends composition text updates without GCS_CURSORPOS:

[IME] OnImeComposition message=271, wparam=45796, lparam=24600, has_result=0, has_comp=1, has_cursor=0
[IME] GCS_CURSORPOS absent; fallback_cursor_pos=1
[IME] composing text="다", text_length=1, compose_change_cursor_pos=1
[IME] ComposeChangeHook input_text="다", input_length=1, cursor_pos=1, before_text="가나", before_selection=(2, 2), before_composing=(2, 2), after_text="가나다", after_selection=(3, 3), after_composing=(2, 3)
[IME] SendStateUpdate text="가나다", selection=(3, 3), composing=(2, 3)

[IME] OnImeComposition message=271, wparam=45804, lparam=24600, has_result=0, has_comp=1, has_cursor=0
[IME] GCS_CURSORPOS absent; fallback_cursor_pos=1
[IME] composing text="달", text_length=1, compose_change_cursor_pos=1
[IME] ComposeChangeHook input_text="달", input_length=1, cursor_pos=1, before_text="가나다", before_selection=(3, 3), before_composing=(2, 3), after_text="가나달", after_selection=(3, 3), after_composing=(2, 3)
[IME] SendStateUpdate text="가나달", selection=(3, 3), composing=(2, 3)

In the problematic path, has_comp=1 but has_cursor=0. The composing text is present, but the IME did not provide an explicit composing cursor position.

Fix

This change makes the Windows embedder read the IME composing cursor only when GCS_CURSORPOS is present in lParam.

When GCS_CURSORPOS is absent, the embedder falls back to the end of the current composition text. If GCS_CURSORPOS is present but returns an out-of-range value, it also falls back to the composition end.

This keeps explicit IME cursor positions working for IMEs that provide them, while avoiding stale or missing cursor values for Korean composition updates.

Validation

  • Added regression coverage for Korean composition updates without GCS_CURSORPOS.
  • Added coverage that explicit GCS_CURSORPOS is still used when present.
  • Added coverage that out-of-range cursor positions fall back to the composition end.
  • Ran:
flutter_windows_unittests.exe --gtest_filter=MockWindow.OnImeComposition*

Result:

[==========] Running 7 tests from 1 test suite.
[  PASSED  ] 7 tests.
  • Manually verified Korean IME input for 가나달 with a local diagnostic build. The final editing state showed the caret at the end of the composing range:
text="가나달", selection=(3, 3), composing=(2, 3)

@google-cla

google-cla Bot commented May 11, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-windows Building on or for Windows specifically a: desktop Running on desktop team-windows Owned by the Windows platform team labels May 11, 2026
@CHOIgoung CHOIgoung marked this pull request as ready for review May 12, 2026 01:59
@CHOIgoung CHOIgoung requested a review from a team as a code owner May 12, 2026 01:59

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a GetCursorPositionForComposition helper function in the Windows platform shell to manage IME composition cursor positions, ensuring they default to the end of the text when an explicit position is missing or invalid. Corresponding unit tests and a regression test were added to verify this behavior. Feedback suggests replacing the long data type with int in the new helper function to comply with the Google C++ Style Guide.

Comment thread engine/src/flutter/shell/platform/windows/flutter_window.cc Outdated
@loic-sharma loic-sharma added a: text input Entering text in a text field or keyboard related problems CICD Run CI/CD labels May 12, 2026
Comment thread engine/src/flutter/shell/platform/windows/flutter_window.cc

@loic-sharma loic-sharma left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the pull request!

@loic-sharma

Copy link
Copy Markdown
Member

@mattkae Could you do the second review for this?

@Renzo-Olivares Renzo-Olivares left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM but will defer to current reviewers.

@github-actions github-actions Bot removed a: text input Entering text in a text field or keyboard related problems CICD Run CI/CD labels May 18, 2026
@CHOIgoung

Copy link
Copy Markdown
Contributor Author

The review threads appear resolved and the PR is approved, but Google testing failed with "Engine artifacts not found" while tree-status is broken.

Could someone rerun the external testing or advise if there is anything else needed from my side?

@mattkae

mattkae commented May 19, 2026

Copy link
Copy Markdown
Contributor

The review threads appear resolved and the PR is approved, but Google testing failed with "Engine artifacts not found" while tree-status is broken.

Could someone rerun the external testing or advise if there is anything else needed from my side?

CC: @loic-sharma

Also - I left a comment for Loic to read above :)

@mattkae mattkae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks good, but I am skeptical of the potential side-effects of the int cast. long seems more appropriate longterm there, since that is what the Win32 API returns to us.

@loic-sharma loic-sharma added the CICD Run CI/CD label May 21, 2026
@loic-sharma

Copy link
Copy Markdown
Member

This looks good, but I am skeptical of the potential side-effects of the int cast. long seems more appropriate longterm there, since that is what the Win32 API returns to us.

Good catch. It looks like the underlying TextInputModel and TextRange both use size_t. I'm not sure why Flutter Windows casts to an int, that looks like a mistake that we should fix. However, given that's a preexisting problem, I'm OK with fixing that in a separate PR.

@loic-sharma loic-sharma added the autosubmit Merge PR when tree becomes green via auto submit App label May 26, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 26, 2026
@auto-submit

auto-submit Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/186353, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR.

@loic-sharma

Copy link
Copy Markdown
Member

@CHOIgoung could you rebase this PR off the latest master commit? Once you do that, I'll rerun tests and get this landed! :)

@CHOIgoung CHOIgoung force-pushed the fix-windows-korean-ime-caret-position branch from 7f46b4d to bc46f13 Compare May 27, 2026 00:34
@github-actions github-actions Bot removed the CICD Run CI/CD label May 27, 2026
@loic-sharma loic-sharma added CICD Run CI/CD autosubmit Merge PR when tree becomes green via auto submit App labels May 27, 2026
@auto-submit auto-submit Bot added this pull request to the merge queue Jun 1, 2026
Merged via the queue into flutter:master with commit 2fc403d Jun 1, 2026
205 of 206 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jun 1, 2026
@80psh0601

Copy link
Copy Markdown

큰 일 하셨습니다 !!!

@sandapilarius

Copy link
Copy Markdown

감사합니다. 2년이 걸렸네요.
지금은 flutter를 떠나 있지만 데스크탑 앱을 만들 프로젝트를 진행한다면 flutter도 고려할께요.
너무 수고 많으셨습니다.

auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Jun 2, 2026
flutter/flutter@54e199a...701665b

2026-06-02 engine-flutter-autoroll@skia.org Roll Skia from c97e939eb5c9 to 279b17fe9fc1 (16 revisions) (flutter/flutter#187425)
2026-06-02 bdero@google.com [Flutter GPU] Add block-compressed texture format support (BC, ETC2, ASTC LDR) (flutter/flutter#187281)
2026-06-02 bdero@google.com [Impeller] Allow attaching specific texture mip levels and slices (flutter/flutter#187066)
2026-06-02 bdero@google.com [Impeller] Fix GLES command submission status before context is current (flutter/flutter#187293)
2026-06-02 engine-flutter-autoroll@skia.org Roll Dart SDK from 3cdc25e8ffe9 to d39850bf4a01 (9 revisions) (flutter/flutter#187409)
2026-06-01 jason-simmons@users.noreply.github.com [Impeller] Use glVertexAttribDivisor on GLES3 and glVertexAttribDivisorEXT on GLES2 with the extension (flutter/flutter#187313)
2026-06-01 matt.boetger@gmail.com [Android] Add Javadoc documentation to TextInputChannel (flutter/flutter#186018)
2026-06-01 mvincentong@gmail.com Read FLTEnableWideGamut from Dart bundle (flutter/flutter#186509)
2026-06-01 matt.boetger@gmail.com [flutter_tools] Remove obsolete AndroidX console warning during Gradle builds (flutter/flutter#186077)
2026-06-01 kjlubick@users.noreply.github.com [skia] Update gni file list name hsw -> ml3 (flutter/flutter#184892)
2026-06-01 zhongliu88889@gmail.com [web] Always sync slider input attrs regardless of gesture mode (flutter/flutter#187217)
2026-06-01 zhongliu88889@gmail.com [flutter_driver] Don't throw when stderr is unavailable on web (flutter/flutter#187190)
2026-06-01 116356835+AbdeMohlbi@users.noreply.github.com Remove unused code in `FlutterPluginUtils.kt` (flutter/flutter#187012)
2026-06-01 taak140@gmail.com [flutter_tools] Fix `flutter drive --chrome-binary` being ignored on web (flutter/flutter#185481)
2026-06-01 davidmartos96@gmail.com Eager failure when building and no XCode build settings (flutter/flutter#184726)
2026-06-01 goung123@gmail.com Fix Windows Korean IME caret position during composition (flutter/flutter#186353)
2026-06-01 okorohelijah@google.com iOS: update provisioning profile for 2026-2027 cert (flutter/flutter#187280)
2026-06-01 154381524+flutteractionsbot@users.noreply.github.com Sync CHANGELOG.md from stable (flutter/flutter#187380)
2026-06-01 jason-simmons@users.noreply.github.com Reland "Move dart-lang/ai to a top level third party dependency in engine (#187268)" (flutter/flutter#187378)
2026-06-01 stuartmorgan@google.com Add vector_math to Framework triage (flutter/flutter#187389)
2026-06-01 engine-flutter-autoroll@skia.org Roll Packages from e930ced to f5d50ca (4 revisions) (flutter/flutter#187381)
2026-06-01 mr_nadeem_iqbal@yahoo.com [flutter_tools] Reject archive entries that escape into a sibling directory by name prefix (#185794) (flutter/flutter#186647)
2026-06-01 bkonyi@google.com [flutter_tools] Fix widget_preview unawaited async write race condition (flutter/flutter#187177)
2026-06-01 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#187375)
2026-06-01 engine-flutter-autoroll@skia.org Roll Skia from 0aee4675e0ad to c97e939eb5c9 (7 revisions) (flutter/flutter#187371)
2026-06-01 mr_nadeem_iqbal@yahoo.com docs: Stack.clipBehavior = Clip.none does not extend hit testing (#160787) (flutter/flutter#186643)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
creatorpiyush pushed a commit to creatorpiyush/packages that referenced this pull request Jun 10, 2026
…r#11822)

flutter/flutter@54e199a...701665b

2026-06-02 engine-flutter-autoroll@skia.org Roll Skia from c97e939eb5c9 to 279b17fe9fc1 (16 revisions) (flutter/flutter#187425)
2026-06-02 bdero@google.com [Flutter GPU] Add block-compressed texture format support (BC, ETC2, ASTC LDR) (flutter/flutter#187281)
2026-06-02 bdero@google.com [Impeller] Allow attaching specific texture mip levels and slices (flutter/flutter#187066)
2026-06-02 bdero@google.com [Impeller] Fix GLES command submission status before context is current (flutter/flutter#187293)
2026-06-02 engine-flutter-autoroll@skia.org Roll Dart SDK from 3cdc25e8ffe9 to d39850bf4a01 (9 revisions) (flutter/flutter#187409)
2026-06-01 jason-simmons@users.noreply.github.com [Impeller] Use glVertexAttribDivisor on GLES3 and glVertexAttribDivisorEXT on GLES2 with the extension (flutter/flutter#187313)
2026-06-01 matt.boetger@gmail.com [Android] Add Javadoc documentation to TextInputChannel (flutter/flutter#186018)
2026-06-01 mvincentong@gmail.com Read FLTEnableWideGamut from Dart bundle (flutter/flutter#186509)
2026-06-01 matt.boetger@gmail.com [flutter_tools] Remove obsolete AndroidX console warning during Gradle builds (flutter/flutter#186077)
2026-06-01 kjlubick@users.noreply.github.com [skia] Update gni file list name hsw -> ml3 (flutter/flutter#184892)
2026-06-01 zhongliu88889@gmail.com [web] Always sync slider input attrs regardless of gesture mode (flutter/flutter#187217)
2026-06-01 zhongliu88889@gmail.com [flutter_driver] Don't throw when stderr is unavailable on web (flutter/flutter#187190)
2026-06-01 116356835+AbdeMohlbi@users.noreply.github.com Remove unused code in `FlutterPluginUtils.kt` (flutter/flutter#187012)
2026-06-01 taak140@gmail.com [flutter_tools] Fix `flutter drive --chrome-binary` being ignored on web (flutter/flutter#185481)
2026-06-01 davidmartos96@gmail.com Eager failure when building and no XCode build settings (flutter/flutter#184726)
2026-06-01 goung123@gmail.com Fix Windows Korean IME caret position during composition (flutter/flutter#186353)
2026-06-01 okorohelijah@google.com iOS: update provisioning profile for 2026-2027 cert (flutter/flutter#187280)
2026-06-01 154381524+flutteractionsbot@users.noreply.github.com Sync CHANGELOG.md from stable (flutter/flutter#187380)
2026-06-01 jason-simmons@users.noreply.github.com Reland "Move dart-lang/ai to a top level third party dependency in engine (#187268)" (flutter/flutter#187378)
2026-06-01 stuartmorgan@google.com Add vector_math to Framework triage (flutter/flutter#187389)
2026-06-01 engine-flutter-autoroll@skia.org Roll Packages from e930ced to f5d50ca (4 revisions) (flutter/flutter#187381)
2026-06-01 mr_nadeem_iqbal@yahoo.com [flutter_tools] Reject archive entries that escape into a sibling directory by name prefix (#185794) (flutter/flutter#186647)
2026-06-01 bkonyi@google.com [flutter_tools] Fix widget_preview unawaited async write race condition (flutter/flutter#187177)
2026-06-01 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#187375)
2026-06-01 engine-flutter-autoroll@skia.org Roll Skia from 0aee4675e0ad to c97e939eb5c9 (7 revisions) (flutter/flutter#187371)
2026-06-01 mr_nadeem_iqbal@yahoo.com docs: Stack.clipBehavior = Clip.none does not extend hit testing (#160787) (flutter/flutter#186643)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
via-guy pushed a commit to via-guy/flutter that referenced this pull request Jun 26, 2026
)

Fixes flutter#140739

## Summary

This fixes a Windows Korean IME caret positioning issue where the caret
could appear before the active composing syllable while entering Korean
text.

The Windows embedder previously queried the IME composing cursor
position for every `WM_IME_COMPOSITION` update. However, Microsoft
Korean IME can send composition updates with `GCS_COMPSTR` or
`GCS_RESULTSTR` without the `GCS_CURSORPOS` flag. In that case, the
cursor position is not explicitly part of the message, so querying it
can produce a missing or stale cursor value. That stale value can then
be converted into Flutter's text selection and place the visual caret
before the end of the active composing range.

## Recordings

Before this fix, the caret can appear before the active Korean composing
syllable:

![Before fix: Windows Korean IME caret appears before the active
composing
syllable](https://raw.githubusercontent.com/CHOIgoung/flutter/pr-assets-ime-caret-20260512-010049/windows-korean-ime-caret-before-fix.gif)

After this fix, the caret stays at the end of the active composing
syllable:

![After fix: Windows Korean IME caret stays at the composition
end](https://raw.githubusercontent.com/CHOIgoung/flutter/pr-assets-ime-caret-20260512-010049/windows-korean-ime-caret-position.gif)

## Root Cause

For Korean input such as `가나달`, the engine receives `WM_IME_COMPOSITION`
updates for the active syllable. Local runtime logging showed that
Microsoft Korean IME sends composition text updates without
`GCS_CURSORPOS`:

```text
[IME] OnImeComposition message=271, wparam=45796, lparam=24600, has_result=0, has_comp=1, has_cursor=0
[IME] GCS_CURSORPOS absent; fallback_cursor_pos=1
[IME] composing text="다", text_length=1, compose_change_cursor_pos=1
[IME] ComposeChangeHook input_text="다", input_length=1, cursor_pos=1, before_text="가나", before_selection=(2, 2), before_composing=(2, 2), after_text="가나다", after_selection=(3, 3), after_composing=(2, 3)
[IME] SendStateUpdate text="가나다", selection=(3, 3), composing=(2, 3)

[IME] OnImeComposition message=271, wparam=45804, lparam=24600, has_result=0, has_comp=1, has_cursor=0
[IME] GCS_CURSORPOS absent; fallback_cursor_pos=1
[IME] composing text="달", text_length=1, compose_change_cursor_pos=1
[IME] ComposeChangeHook input_text="달", input_length=1, cursor_pos=1, before_text="가나다", before_selection=(3, 3), before_composing=(2, 3), after_text="가나달", after_selection=(3, 3), after_composing=(2, 3)
[IME] SendStateUpdate text="가나달", selection=(3, 3), composing=(2, 3)
```

In the problematic path, `has_comp=1` but `has_cursor=0`. The composing
text is present, but the IME did not provide an explicit composing
cursor position.

## Fix

This change makes the Windows embedder read the IME composing cursor
only when `GCS_CURSORPOS` is present in `lParam`.

When `GCS_CURSORPOS` is absent, the embedder falls back to the end of
the current composition text. If `GCS_CURSORPOS` is present but returns
an out-of-range value, it also falls back to the composition end.

This keeps explicit IME cursor positions working for IMEs that provide
them, while avoiding stale or missing cursor values for Korean
composition updates.

## Validation

- Added regression coverage for Korean composition updates without
`GCS_CURSORPOS`.
- Added coverage that explicit `GCS_CURSORPOS` is still used when
present.
- Added coverage that out-of-range cursor positions fall back to the
composition end.
- Ran:

```text
flutter_windows_unittests.exe --gtest_filter=MockWindow.OnImeComposition*
```

Result:

```text
[==========] Running 7 tests from 1 test suite.
[  PASSED  ] 7 tests.
```

- Manually verified Korean IME input for `가나달` with a local diagnostic
build. The final editing state showed the caret at the end of the
composing range:

```text
text="가나달", selection=(3, 3), composing=(2, 3)
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-windows Building on or for Windows specifically team-windows Owned by the Windows platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When entering Korean, the text cursor is behind single space.

7 participants