Skip to content

Commit c3815be

Browse files
letrungdookorohelijah
authored andcommitted
Fix Windows Enter key after focus loss (flutter#178523)
Fix issue: flutter#169447 Problem: On Windows, pressing Enter after refocusing the app could drop the first keydown because the embedder still thought the key was pressed, causing a double-press requirement and HardwareKeyboard assertions. Fix: In KeyboardKeyEmbedderHandler, synthesize a key-up whenever Win32 reports a “fresh” down for a key still tracked as pressed, so Flutter sees a release before the new down. This keeps modifier/lock-key synchronization intact. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
1 parent 37e7741 commit c3815be

File tree

4 files changed

+70
-20
lines changed

4 files changed

+70
-20
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,4 @@ Jin Jeongsu <[email protected]>
136136
Mairon Slusarz <[email protected]>
137137
Ricardo Dalarme <[email protected]>
138138
139+
letrungdo <[email protected]>

engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler.cc

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -225,22 +225,24 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
225225
uint64_t result_logical_key;
226226

227227
if (is_event_down) {
228+
if (had_record && !was_down) {
229+
// Windows delivered a new down event without a matching up event. This
230+
// happens if the window lost focus before it could receive the key up.
231+
// Synthesize an up event so the framework releases the key before
232+
// processing the incoming down.
233+
SendSynthesizeUpEvent(physical_key, last_logical_record);
234+
last_logical_record_iter = pressingRecords_.find(physical_key);
235+
had_record = last_logical_record_iter != pressingRecords_.end();
236+
last_logical_record = had_record ? last_logical_record_iter->second : 0;
237+
}
238+
228239
if (had_record) {
229-
if (was_down) {
230-
// A normal repeated key.
231-
type = kFlutterKeyEventTypeRepeat;
232-
FML_DCHECK(had_record);
233-
ConvertUtf32ToUtf8_(character_bytes, character);
234-
eventual_logical_record = last_logical_record;
235-
result_logical_key = last_logical_record;
236-
} else {
237-
// A non-repeated key has been pressed that has the exact physical key
238-
// as a currently pressed one, usually indicating multiple keyboards are
239-
// pressing keys with the same physical key, or the up event was lost
240-
// during a loss of focus. The down event is ignored.
241-
callback(true);
242-
return;
243-
}
240+
// A normal repeated key.
241+
type = kFlutterKeyEventTypeRepeat;
242+
FML_DCHECK(had_record);
243+
ConvertUtf32ToUtf8_(character_bytes, character);
244+
eventual_logical_record = last_logical_record;
245+
result_logical_key = last_logical_record;
244246
} else {
245247
// A normal down event (whether the system event is a repeat or not).
246248
type = kFlutterKeyEventTypeDown;

engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -747,17 +747,30 @@ TEST(KeyboardKeyEmbedderHandlerTest, RepeatedDownIsIgnored) {
747747

748748
// KeyA's key up is missed.
749749

750-
// Press A again (should yield an empty event)
750+
// Press A again (should synthesize an up event followed by a new down).
751751
last_handled = false;
752752
handler->KeyboardHook(
753753
kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false,
754754
[&last_handled](bool handled) { last_handled = handled; });
755-
EXPECT_EQ(last_handled, true);
756-
EXPECT_EQ(results.size(), 1);
755+
EXPECT_EQ(last_handled, false);
756+
ASSERT_EQ(results.size(), 2u);
757+
757758
event = &results[0];
758-
EXPECT_EQ(event->physical, 0);
759-
EXPECT_EQ(event->logical, 0);
759+
EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
760+
EXPECT_EQ(event->physical, kPhysicalKeyA);
761+
EXPECT_EQ(event->logical, kLogicalKeyA);
762+
EXPECT_STREQ(event->character, "");
763+
EXPECT_EQ(event->synthesized, true);
760764
EXPECT_EQ(event->callback, nullptr);
765+
766+
event = &results[1];
767+
EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
768+
EXPECT_EQ(event->physical, kPhysicalKeyA);
769+
EXPECT_EQ(event->logical, kLogicalKeyA);
770+
EXPECT_STREQ(event->character, "a");
771+
EXPECT_EQ(event->synthesized, false);
772+
event->callback(true, event->user_data);
773+
EXPECT_EQ(last_handled, true);
761774
results.clear();
762775
}
763776

engine/src/flutter/shell/platform/windows/keyboard_unittests.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,40 @@ TEST_F(KeyboardTest, RestartClearsKeyboardState) {
10271027
EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0);
10281028
}
10291029

1030+
// Press Enter, lose focus before the up event arrives, and press Enter again
1031+
// once focus returns. The second down event should not be dropped.
1032+
TEST_F(KeyboardTest, FreshKeyDownAfterMissedUpIsDelivered) {
1033+
KeyboardTester tester{GetContext()};
1034+
tester.Responding(true);
1035+
1036+
tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1037+
WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
1038+
kWmResultZero)});
1039+
1040+
tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1041+
WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build(
1042+
kWmResultZero)});
1043+
1044+
ASSERT_EQ(tester.key_calls.size(), 3u);
1045+
EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown,
1046+
kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1047+
EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp,
1048+
kPhysicalEnter, kLogicalEnter, "", kSynthesized);
1049+
EXPECT_CALL_IS_EVENT(tester.key_calls[2], kFlutterKeyEventTypeDown,
1050+
kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1051+
tester.clear_key_calls();
1052+
1053+
tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
1054+
WmKeyUpInfo{VK_RETURN, kScanCodeEnter, kNotExtended}.Build(
1055+
kWmResultZero)});
1056+
1057+
ASSERT_EQ(tester.key_calls.size(), 1u);
1058+
EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp,
1059+
kPhysicalEnter, kLogicalEnter, "", kNotSynthesized);
1060+
tester.clear_key_calls();
1061+
EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0u);
1062+
}
1063+
10301064
// Press Shift-A. This is special because Win32 gives 'A' as character for the
10311065
// KeyA press.
10321066
TEST_F(KeyboardTest, ShiftLeftKeyA) {

0 commit comments

Comments
 (0)