Skip to content

[Windows] Fix Space input after Alt+Space with previous Microsoft IME - #192105

Merged
auto-submit[bot] merged 1 commit into
flutter:masterfrom
JIWON1923:fix/windows-ime-space-after-system-menu
Nov 12, 2026
Merged

auto-submit[bot] merged 1 commit into
flutter:masterfrom
JIWON1923:fix/windows-ime-space-after-system-menu

Conversation

@JIWON1923

Copy link
Copy Markdown
Contributor

Fixes #189495

Description

After Alt+Space opens the Windows system menu, the Space key-up does not always reach Flutter while the native menu is active. Flutter can therefore retain a stale pressed record that maps the physical Space key to the logical Space key.

With Use previous version of Microsoft IME enabled, the next physical Space press can first arrive as a fresh WM_KEYDOWN(VK_PROCESSKEY) with the Space scan code, followed by the translated WM_KEYDOWN(VK_SPACE) event.

The existing stale-state recovery synthesizes the missing Space key-up, but then continues processing the VK_PROCESSKEY event and records it as the logical key for physical Space. The following translated VK_SPACE event consequently inherits VK_PROCESSKEY as its sequence logical key and is filtered as an IME event. Space input then remains unavailable.

When this state is detected, the handler now synthesizes the missing key-up and filters the fresh VK_PROCESSKEY event without recording it. The following translated VK_SPACE event is then processed normally.

The observed failing event is WM_KEYDOWN(VK_PROCESSKEY). The implementation reuses the handler's existing definition of a key-down event, which also includes WM_SYSKEYDOWN, so the same pressed-state invariant is preserved for system key-down messages. The early path additionally requires an existing record for the same physical key and a previous-key-state bit indicating a fresh press.

Normal IME events without a stale pressed record, key-up events, and repeated key-down events continue through the existing paths.

Microsoft documents the previous IME setting here: Revert to a previous version of an Input Method Editor (IME)

Win32 behavior

Microsoft documents that:

  • WM_SYSKEYDOWN is posted when Alt is held while another key is pressed.
  • Bit 30 of WM_KEYDOWN indicates the previous key state, allowing a fresh down transition to be distinguished from an auto-repeat.
  • An IME changes the virtual-key value of an already processed key message to VK_PROCESSKEY, as described by ImmGetVirtualKey.
  • SC_KEYMENU identifies a window-menu request caused by a keystroke.

Here, 229 is VK_PROCESSKEY, 32 is VK_SPACE, and scan code 57 is the physical Space key.

Diagnostic log excerpts

Current Microsoft IME

The next Space press arrives directly as VK_SPACE. The existing stale-state recovery handles this case:

[flutter#189495][child-window] message=256 wparam=32 scan_code=57 extended=0 context=0 was_down=0 transition=0 space_async_down=1 alt_async_down=0
[flutter#189495][child-window] message=258 wparam=32 scan_code=57 extended=0 context=0 was_down=0 transition=0 space_async_down=1 alt_async_down=0
[flutter#189495][embedder:entry] action=256 key=32 scancode=57 character=32 extended=0 was_down=0 physical_key=458796 logical_key=32 had_record=1 last_logical_record=32 pressed_count=1
[flutter#189495][embedder:stale-record] physical_key=458796 last_logical_record=32 incoming_logical_key=32

Previous Microsoft IME before this change

A fresh VK_PROCESSKEY arrives first. The existing recovery replaces the logical record with 229, so the translated VK_SPACE event is also filtered:

[flutter#189495][window] WM_KEYDOWN message=256 wparam=229 scan_code=57 extended=0 context=0 was_down=0 transition=0 space_async_down=1 alt_async_down=0 has_focus=1 is_active=0 is_foreground=0
[flutter#189495][embedder:entry] action=256 key=229 scancode=57 character=0 extended=0 was_down=0 physical_key=458796 logical_key=229 had_record=1 last_logical_record=32 pressed_count=1
[flutter#189495][embedder:stale-record] physical_key=458796 last_logical_record=32 incoming_logical_key=229
[flutter#189495][window] WM_KEYDOWN message=256 wparam=32 scan_code=57 extended=0 context=0 was_down=0 transition=0 space_async_down=1 alt_async_down=0 has_focus=1 is_active=0 is_foreground=0
[flutter#189495][window] WM_CHAR message=258 wparam=32 scan_code=57 extended=0 context=0 was_down=0 transition=0 space_async_down=1 alt_async_down=0 has_focus=1 is_active=0 is_foreground=0
[flutter#189495][embedder:entry] action=256 key=32 scancode=57 character=32 extended=0 was_down=0 physical_key=458796 logical_key=32 had_record=1 last_logical_record=229 pressed_count=1
[flutter#189495][embedder:ime-filter] action=256 key=32 sequence_logical_key=229 had_record=1

Previous Microsoft IME after this change

The stale Space record is released while filtering the fresh VK_PROCESSKEY. The translated VK_SPACE event then arrives with no stale record and is processed as a normal key-down:

[flutter#189495][window] message=256 wparam=229 scan_code=57 extended=0 context=0 was_down=0 transition=0
[flutter#189495][embedder:entry] action=256 key=229 scancode=57 character=0 extended=0 was_down=0 physical_key=458796 logical_key=229 had_record=1 last_logical_record=32 pressed_count=1
[flutter#189495][embedder:synthesize-up] physical_key=458796 logical_key=32 pressed_count_before=1
[flutter#189495][embedder:synthesize-up] pressed_count_after=0
[flutter#189495][window] message=256 wparam=32 scan_code=57 extended=0 context=0 was_down=0 transition=0
[flutter#189495][window] message=258 wparam=32 scan_code=57 extended=0 context=0 was_down=0 transition=0
[flutter#189495][embedder:entry] action=256 key=32 scancode=57 character=32 extended=0 was_down=0 physical_key=458796 logical_key=32 had_record=0 last_logical_record=0 pressed_count=0
[flutter#189495][embedder:sequence] key=32 sequence_logical_key=32 ime_filter=0
[flutter#189495][embedder:decision] down result_logical_key=32

Screenshot

alt.space.mov

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@JIWON1923
JIWON1923 requested a review from a team as a code owner September 1, 2026 09:40
@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 Sep 1, 2026

@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 updates the Windows keyboard key embedder handler to release stale pressed key states when a fresh IME key-down event (VK_PROCESSKEY) is received, addressing scenarios where a native modal loop leaves a key recorded as pressed. A corresponding unit test, FreshImeKeyDownReleasesStalePressedKey, has been added to verify this behavior. There are no review comments, and I have no feedback to provide.

@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.

A small suggestion to clean this up, but looks good overall!

@mattkae

mattkae commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

@JIWON1923 Pinging you in case this got lost!

@JIWON1923

Copy link
Copy Markdown
Contributor Author

@JIWON1923 Pinging you in case this got lost!

Thanks for the ping, and sorry for the delayed response!

If I remember correctly, I kept this check outside the sequence_logical_key == VK_PROCESSKEY block because there was a case where the stale pressed record might not be released otherwise.

It's been a while, so I'd like to test this again to make sure. I'll take another look and get back to you!

@JIWON1923

JIWON1923 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

@mattkae

Thanks for the suggestion! I tried it locally and tested it with Use previous version of Microsoft IME enabled.

It turns out the suggested placement breaks the fix, and it also makes the new regression test FreshImeKeyDownReleasesStalePressedKey fail. I'd like to keep the current placement, and here is why.

Why the check can't move inside if (sequence_logical_key == VK_PROCESSKEY)

sequence_logical_key is taken from the existing pressed record whenever had_record is true. In this scenario that record still holds the stale Space entry, so when the fresh VK_PROCESSKEY arrives, sequence_logical_key is Space (32), not VK_PROCESSKEY (229):

[entry] key=229 scancode=57 had_record=1
        last_logical_record=32 sequence_logical_key=32
[dispatch] logical=229 eventual_record=229
[entry] key=32 last_logical_record=229 sequence_logical_key=229
[ime-filter] key=32
[text-suppressed] key=32 character=32 framework_handled=1

(Log tags are shortened here; these are the same log points used in the PR description.)

Because sequence_logical_key is 32, the sequence_logical_key == VK_PROCESSKEY branch is never entered for this event. Neither the synthesized key-up nor the IME filtering runs, so VK_PROCESSKEY is dispatched as an ordinary key-down and recorded as 229. The translated VK_SPACE event that follows then inherits 229 as its sequence logical key, is filtered as an IME event, and its WM_CHAR is suppressed.

In short, the branch we would move the check into is exactly the branch that the stale record prevents us from reaching. The check has to run before the sequence logical key is derived from the record, which is why it sits ahead of the IME filtering branch.

Manual verification

The same input sequence was used in all three recordings:

가나다 → Alt+Space → Esc → 가나다 → Space → Space

The decisive detail is how many of the two trailing spaces actually get inserted.

1. Notepad (baseline) — both spaces are inserted. The status bar ends at column 9 / 8 characters (6 Hangul syllables + 2 spaces).

default.mp4

2. Current implementation in this PR — matches Notepad. Both spaces are inserted.

default.mp4

3. With the suggested change — the first Space only commits the composing text and no space character is inserted. The second Space works normally, so the field ends with one space instead of two.

default.mp4

Given the above, would it be okay to keep the current implementation as it is? Please let me know if you would prefer a different approach.

@JIWON1923
JIWON1923 requested a review from mattkae October 20, 2026 01:02

@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.

Thanks for the thorough response! This looks good to me.

@loic-sharma

Copy link
Copy Markdown
Member

Thanks for the contribution!

@loic-sharma
loic-sharma force-pushed the fix/windows-ime-space-after-system-menu branch from 35d595b to eccd612 Compare October 21, 2026 23:26
@loic-sharma loic-sharma added CICD Run CI/CD autosubmit Merge PR when tree becomes green via auto submit App labels Oct 21, 2026
@flutter-dashboard

Copy link
Copy Markdown

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #192105 at sha eccd612

@flutter-dashboard flutter-dashboard Bot added will affect goldens Changes to golden files and removed CICD Run CI/CD labels Oct 22, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 23, 2026
@auto-submit

auto-submit Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/192105, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label.

@mattkae
mattkae force-pushed the fix/windows-ime-space-after-system-menu branch from 812df80 to 6338fc1 Compare October 23, 2026 20:18
@mattkae mattkae added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels Oct 23, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 23, 2026
@auto-submit

auto-submit Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/192105, because - The status or check suite Mac_arm64_verify_binaries has failed. Please fix the issues identified (or deflake) before re-applying this label.

@JIWON1923

Copy link
Copy Markdown
Contributor Author

@mattkae Could you re-apply autosubmit? Checks are all green now. Thanks!

@mattkae
mattkae force-pushed the fix/windows-ime-space-after-system-menu branch from 6338fc1 to 43cbd59 Compare November 12, 2026 15:09
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Nov 12, 2026
@mattkae mattkae added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels Nov 12, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Nov 12, 2026
Merged via the queue into flutter:master with commit 774b051 Nov 12, 2026
27 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 12, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Nov 13, 2026
…13653)

Manual roll Flutter from 9c427b5d7b1c to 1b9585481095 (32 revisions)

Manual roll requested by [email protected]

flutter/flutter@9c427b5...1b95854

2026-11-13 [email protected] Roll pub packages (flutter/flutter#196541)
2026-11-13 [email protected] Do not route PRs for review without cla, convert to draft (flutter/flutter#196341)
2026-11-13 [email protected] Roll Skia from d87a6c04da74 to 11f46cfe8838 (13 revisions) (flutter/flutter#196533)
2026-11-13 [email protected] [flutter_tools, flutter_test] Deprecate --experimental-faster-testing (flutter/flutter#196123)
2026-11-13 [email protected] [flutter_tools] Handle RPCError in ScreenshotCommand.runSkia (flutter/flutter#196132)
2026-11-13 [email protected] Roll Dart SDK from a8b961fd48bb to c91e8b6674e2 (2 revisions) (flutter/flutter#196502)
2026-11-13 [email protected] Roll Skia from 0c5ce4960cef to d87a6c04da74 (12 revisions) (flutter/flutter#196497)
2026-11-13 [email protected] Don't call `skia_image()`  for raster-owned images in `DlImage::Equals` (flutter/flutter#196473)
2026-11-13 [email protected] [flutter_tools] Require FlutterCommandRunner DI fields and extract findCommandName (flutter/flutter#196331)
2026-11-13 [email protected] [web] Unify ui.Canvas behind a shared EngineCanvas frontend (flutter/flutter#196348)
2026-11-13 [email protected] Link to MediaQuery.displayFeaturesOf from DisplayFeatures (flutter/flutter#193728)
2026-11-13 [email protected] Removes reference to unnecessary ensureSemantics call in doc (flutter/flutter#195831)
2026-11-13 [email protected] [flutter_tools] Register trackWidgetCreation in AppleBuildOptionsBundle (flutter/flutter#196078)
2026-11-12 [email protected] [Android] Skip the HardwareBuffer defect fallback for platform views (flutter/flutter#193441)
2026-11-12 [email protected] Fix supports_decal check in framebuffer_blend.frag (flutter/flutter#196227)
2026-11-12 [email protected] [ios] Update the screen in didUpdateEffectiveGeometry (flutter/flutter#195508)
2026-11-12 [email protected] [flutter_tools] Add `Cache-Control` headers for web test assets (flutter/flutter#196414)
2026-11-12 [email protected] Roll Skia from 3b71ec08218f to 0c5ce4960cef (16 revisions) (flutter/flutter#196472)
2026-11-12 [email protected] [web] Render bounded ImageFilter.blur on wimp (flutter/flutter#196081)
2026-11-12 [email protected] Roll Dart SDK from 69567bd3c427 to a8b961fd48bb (2 revisions) (flutter/flutter#196464)
2026-11-12 [email protected] [engine] Release the Skia OpenGL resource context on shutdown (flutter/flutter#193592)
2026-11-12 [email protected] [web] Fix decoding of still AVIF images on Chrome (flutter/flutter#195958)
2026-11-12 [email protected] Gate SurfaceControl context setting on API 34+ (flutter/flutter#196394)
2026-11-12 [email protected] [flutter_tools] Only pass on a valid root tool in DASH__TOOL (flutter/flutter#196228)
2026-11-12 [email protected] Support GL_TEXTURE_EXTERNAL_OES embedder textures in Impeller (flutter/flutter#195857)
2026-11-12 [email protected] Stop building linux/wasm_release in local_engine_builds (flutter/flutter#196345)
2026-11-12 [email protected] Split flame out of Linux customer_testing (flutter/flutter#196347)
2026-11-12 Jiwon Lee [Windows] Fix Space input after Alt+Space with previous Microsoft IME (flutter/flutter#192105)
2026-11-12 [email protected] [flutter_tools] Add ToolExtensionException, exit() guard, graceful shutdown, and base modifiers (flutter/flutter#195908)
2026-11-12 [email protected] Roll Packages from 653628e to 14f9933 (9 revisions) (flutter/flutter#196450)
2026-11-12 [email protected] Roll Dart SDK from e5add7a25b32 to 69567bd3c427 (2 revisions) (flutter/flutter#196437)
2026-11-12 [email protected] Roll Skia from 9aa2513a4ac9 to 3b71ec08218f (1 revision) (flutter/flutter#196436)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] 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

...
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 will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Windows] Space is no longer entered after opening and dismissing the system menu with Alt+Space

3 participants