From 0321fcbd8bfffd50d61fecc91878635b65065792 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 3 Mar 2022 07:50:53 -0800 Subject: [PATCH 1/2] Synth up on non-repeat dup down --- .../Source/FlutterEmbedderKeyResponder.mm | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm index cf3e397a84612..476523af7e736 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm @@ -684,8 +684,19 @@ - (void)handleDownEvent:(NSEvent*)event callback:(FlutterKeyCallbackGuard*)callb // key up event to the window where the corresponding key down occurred. // However this might happen in add-to-app scenarios if the focus is changed // from the native view to the Flutter view amid the key tap. - [callback resolveTo:TRUE]; - return; + + // TODO + FlutterKeyEvent flutterEvent = { + .struct_size = sizeof(FlutterKeyEvent), + .timestamp = GetFlutterTimestampFrom(event.timestamp), + .type = kFlutterKeyEventTypeUp, + .physical = physicalKey, + .logical = logicalKey, + .character = nil, + .synthesized = true, + }; + [self sendSynthesizedFlutterEvent:flutterEvent guard:callback]; + pressedLogicalKey = nil; } if (pressedLogicalKey == nil) { From b16d9f4114fed3723c7a7e26a7c050e6eaa2c439 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 3 Mar 2022 08:10:11 -0800 Subject: [PATCH 2/2] Test --- .../Source/FlutterEmbedderKeyResponder.mm | 12 +++--- .../FlutterEmbedderKeyResponderUnittests.mm | 43 ++++++++----------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm index 476523af7e736..191c0aa8dbbc9 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm @@ -680,18 +680,18 @@ - (void)handleDownEvent:(NSEvent*)event callback:(FlutterKeyCallbackGuard*)callb bool isARepeat = event.isARepeat; NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)]; if (pressedLogicalKey != nil && !isARepeat) { - // Normally the key up events won't be missed since macOS always sends the - // key up event to the window where the corresponding key down occurred. - // However this might happen in add-to-app scenarios if the focus is changed + // This might happen in add-to-app scenarios if the focus is changed // from the native view to the Flutter view amid the key tap. - - // TODO + // + // This might also happen when a key event is forged (such as by an + // IME) using the same keyCode as an unreleased key. See + // https://github.com/flutter/flutter/issues/82673#issuecomment-988661079 FlutterKeyEvent flutterEvent = { .struct_size = sizeof(FlutterKeyEvent), .timestamp = GetFlutterTimestampFrom(event.timestamp), .type = kFlutterKeyEventTypeUp, .physical = physicalKey, - .logical = logicalKey, + .logical = [pressedLogicalKey unsignedLongLongValue], .character = nil, .synthesized = true, }; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponderUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponderUnittests.mm index 359bdc37e8012..ef5a7a34ce128 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponderUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponderUnittests.mm @@ -306,7 +306,7 @@ - (void)dealloc { [events removeAllObjects]; } -TEST(FlutterEmbedderKeyResponderUnittests, IgnoreDuplicateDownEvent) { +TEST(FlutterEmbedderKeyResponderUnittests, SynthesizeForDuplicateDownEvent) { __block NSMutableArray* events = [[NSMutableArray alloc] init]; __block BOOL last_handled = TRUE; FlutterKeyEvent* event; @@ -319,7 +319,7 @@ - (void)dealloc { userData:user_data]]; }]; - last_handled = FALSE; + last_handled = TRUE; [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA) callback:^(BOOL handled) { last_handled = handled; @@ -332,44 +332,35 @@ - (void)dealloc { EXPECT_EQ(event->logical, kLogicalKeyA); EXPECT_STREQ(event->character, "a"); EXPECT_EQ(event->synthesized, false); - EXPECT_EQ(last_handled, FALSE); - [[events lastObject] respond:TRUE]; EXPECT_EQ(last_handled, TRUE); + [[events lastObject] respond:FALSE]; + EXPECT_EQ(last_handled, FALSE); [events removeAllObjects]; - last_handled = FALSE; - [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA) + last_handled = TRUE; + [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"à", @"à", FALSE, kKeyCodeKeyA) callback:^(BOOL handled) { last_handled = handled; }]; - EXPECT_EQ([events count], 1u); - EXPECT_EQ(last_handled, TRUE); - event = [events lastObject].data; - EXPECT_EQ(event->physical, 0ull); - EXPECT_EQ(event->logical, 0ull); - EXPECT_FALSE([[events lastObject] hasCallback]); - EXPECT_EQ(last_handled, TRUE); - - [events removeAllObjects]; - - last_handled = FALSE; - [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA) - callback:^(BOOL handled) { - last_handled = handled; - }]; + EXPECT_EQ([events count], 2u); - EXPECT_EQ([events count], 1u); - event = [events lastObject].data; + event = [events firstObject].data; EXPECT_EQ(event->type, kFlutterKeyEventTypeUp); EXPECT_EQ(event->physical, kPhysicalKeyA); EXPECT_EQ(event->logical, kLogicalKeyA); - EXPECT_STREQ(event->character, nullptr); + EXPECT_STREQ(event->character, NULL); + EXPECT_EQ(event->synthesized, true); + + event = [events lastObject].data; + EXPECT_EQ(event->type, kFlutterKeyEventTypeDown); + EXPECT_EQ(event->physical, kPhysicalKeyA); + EXPECT_EQ(event->logical, 0xE0ull /* à */); + EXPECT_STREQ(event->character, "à"); EXPECT_EQ(event->synthesized, false); + [[events lastObject] respond:FALSE]; EXPECT_EQ(last_handled, FALSE); - [[events lastObject] respond:TRUE]; - EXPECT_EQ(last_handled, TRUE); [events removeAllObjects]; }