diff --git a/AUTHORS b/AUTHORS index 57248e0bacb..89e904a7d2a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -136,3 +136,4 @@ Jin Jeongsu Mairon Slusarz Ricardo Dalarme yiiim +letrungdo diff --git a/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler.cc b/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler.cc index efe76779408..9351f6e04d7 100644 --- a/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler.cc +++ b/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler.cc @@ -225,22 +225,24 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl( uint64_t result_logical_key; if (is_event_down) { + if (had_record && !was_down) { + // Windows delivered a new down event without a matching up event. This + // happens if the window lost focus before it could receive the key up. + // Synthesize an up event so the framework releases the key before + // processing the incoming down. + SendSynthesizeUpEvent(physical_key, last_logical_record); + last_logical_record_iter = pressingRecords_.find(physical_key); + had_record = last_logical_record_iter != pressingRecords_.end(); + last_logical_record = had_record ? last_logical_record_iter->second : 0; + } + if (had_record) { - if (was_down) { - // A normal repeated key. - type = kFlutterKeyEventTypeRepeat; - FML_DCHECK(had_record); - ConvertUtf32ToUtf8_(character_bytes, character); - eventual_logical_record = last_logical_record; - result_logical_key = last_logical_record; - } else { - // A non-repeated key has been pressed that has the exact physical key - // as a currently pressed one, usually indicating multiple keyboards are - // pressing keys with the same physical key, or the up event was lost - // during a loss of focus. The down event is ignored. - callback(true); - return; - } + // A normal repeated key. + type = kFlutterKeyEventTypeRepeat; + FML_DCHECK(had_record); + ConvertUtf32ToUtf8_(character_bytes, character); + eventual_logical_record = last_logical_record; + result_logical_key = last_logical_record; } else { // A normal down event (whether the system event is a repeat or not). type = kFlutterKeyEventTypeDown; diff --git a/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc b/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc index d8532c2b117..146aff79c11 100644 --- a/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +++ b/engine/src/flutter/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc @@ -747,17 +747,30 @@ TEST(KeyboardKeyEmbedderHandlerTest, RepeatedDownIsIgnored) { // KeyA's key up is missed. - // Press A again (should yield an empty event) + // Press A again (should synthesize an up event followed by a new down). last_handled = false; handler->KeyboardHook( kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false, [&last_handled](bool handled) { last_handled = handled; }); - EXPECT_EQ(last_handled, true); - EXPECT_EQ(results.size(), 1); + EXPECT_EQ(last_handled, false); + ASSERT_EQ(results.size(), 2u); + event = &results[0]; - EXPECT_EQ(event->physical, 0); - EXPECT_EQ(event->logical, 0); + EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, ""); + EXPECT_EQ(event->synthesized, true); EXPECT_EQ(event->callback, nullptr); + + event = &results[1]; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, kLogicalKeyA); + EXPECT_STREQ(event->character, "a"); + EXPECT_EQ(event->synthesized, false); + event->callback(true, event->user_data); + EXPECT_EQ(last_handled, true); results.clear(); } diff --git a/engine/src/flutter/shell/platform/windows/keyboard_unittests.cc b/engine/src/flutter/shell/platform/windows/keyboard_unittests.cc index b6ae283ea2a..281d436729a 100644 --- a/engine/src/flutter/shell/platform/windows/keyboard_unittests.cc +++ b/engine/src/flutter/shell/platform/windows/keyboard_unittests.cc @@ -1027,6 +1027,40 @@ TEST_F(KeyboardTest, RestartClearsKeyboardState) { EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0); } +// Press Enter, lose focus before the up event arrives, and press Enter again +// once focus returns. The second down event should not be dropped. +TEST_F(KeyboardTest, FreshKeyDownAfterMissedUpIsDelivered) { + KeyboardTester tester{GetContext()}; + tester.Responding(true); + + tester.InjectKeyboardChanges(std::vector{ + WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build( + kWmResultZero)}); + + tester.InjectKeyboardChanges(std::vector{ + WmKeyDownInfo{VK_RETURN, kScanCodeEnter, kNotExtended, kWasUp}.Build( + kWmResultZero)}); + + ASSERT_EQ(tester.key_calls.size(), 3u); + EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeDown, + kPhysicalEnter, kLogicalEnter, "", kNotSynthesized); + EXPECT_CALL_IS_EVENT(tester.key_calls[1], kFlutterKeyEventTypeUp, + kPhysicalEnter, kLogicalEnter, "", kSynthesized); + EXPECT_CALL_IS_EVENT(tester.key_calls[2], kFlutterKeyEventTypeDown, + kPhysicalEnter, kLogicalEnter, "", kNotSynthesized); + tester.clear_key_calls(); + + tester.InjectKeyboardChanges(std::vector{ + WmKeyUpInfo{VK_RETURN, kScanCodeEnter, kNotExtended}.Build( + kWmResultZero)}); + + ASSERT_EQ(tester.key_calls.size(), 1u); + EXPECT_CALL_IS_EVENT(tester.key_calls[0], kFlutterKeyEventTypeUp, + kPhysicalEnter, kLogicalEnter, "", kNotSynthesized); + tester.clear_key_calls(); + EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 0u); +} + // Press Shift-A. This is special because Win32 gives 'A' as character for the // KeyA press. TEST_F(KeyboardTest, ShiftLeftKeyA) {