Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 756344a

Browse files
authored
[macOS, Keyboard] Duplicate down events are no longer ignored, but kept and preceded by up events (#31800)
1 parent 6c67716 commit 756344a

File tree

2 files changed

+33
-31
lines changed

2 files changed

+33
-31
lines changed

shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -680,12 +680,23 @@ - (void)handleDownEvent:(NSEvent*)event callback:(FlutterKeyCallbackGuard*)callb
680680
bool isARepeat = event.isARepeat;
681681
NSNumber* pressedLogicalKey = _pressingRecords[@(physicalKey)];
682682
if (pressedLogicalKey != nil && !isARepeat) {
683-
// Normally the key up events won't be missed since macOS always sends the
684-
// key up event to the window where the corresponding key down occurred.
685-
// However this might happen in add-to-app scenarios if the focus is changed
683+
// This might happen in add-to-app scenarios if the focus is changed
686684
// from the native view to the Flutter view amid the key tap.
687-
[callback resolveTo:TRUE];
688-
return;
685+
//
686+
// This might also happen when a key event is forged (such as by an
687+
// IME) using the same keyCode as an unreleased key. See
688+
// https://github.com/flutter/flutter/issues/82673#issuecomment-988661079
689+
FlutterKeyEvent flutterEvent = {
690+
.struct_size = sizeof(FlutterKeyEvent),
691+
.timestamp = GetFlutterTimestampFrom(event.timestamp),
692+
.type = kFlutterKeyEventTypeUp,
693+
.physical = physicalKey,
694+
.logical = [pressedLogicalKey unsignedLongLongValue],
695+
.character = nil,
696+
.synthesized = true,
697+
};
698+
[self sendSynthesizedFlutterEvent:flutterEvent guard:callback];
699+
pressedLogicalKey = nil;
689700
}
690701

691702
if (pressedLogicalKey == nil) {

shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponderUnittests.mm

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ - (void)dealloc {
306306
[events removeAllObjects];
307307
}
308308

309-
TEST(FlutterEmbedderKeyResponderUnittests, IgnoreDuplicateDownEvent) {
309+
TEST(FlutterEmbedderKeyResponderUnittests, SynthesizeForDuplicateDownEvent) {
310310
__block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
311311
__block BOOL last_handled = TRUE;
312312
FlutterKeyEvent* event;
@@ -319,7 +319,7 @@ - (void)dealloc {
319319
userData:user_data]];
320320
}];
321321

322-
last_handled = FALSE;
322+
last_handled = TRUE;
323323
[responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
324324
callback:^(BOOL handled) {
325325
last_handled = handled;
@@ -332,44 +332,35 @@ - (void)dealloc {
332332
EXPECT_EQ(event->logical, kLogicalKeyA);
333333
EXPECT_STREQ(event->character, "a");
334334
EXPECT_EQ(event->synthesized, false);
335-
EXPECT_EQ(last_handled, FALSE);
336-
[[events lastObject] respond:TRUE];
337335
EXPECT_EQ(last_handled, TRUE);
336+
[[events lastObject] respond:FALSE];
337+
EXPECT_EQ(last_handled, FALSE);
338338

339339
[events removeAllObjects];
340340

341-
last_handled = FALSE;
342-
[responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
341+
last_handled = TRUE;
342+
[responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"à", @"à", FALSE, kKeyCodeKeyA)
343343
callback:^(BOOL handled) {
344344
last_handled = handled;
345345
}];
346346

347-
EXPECT_EQ([events count], 1u);
348-
EXPECT_EQ(last_handled, TRUE);
349-
event = [events lastObject].data;
350-
EXPECT_EQ(event->physical, 0ull);
351-
EXPECT_EQ(event->logical, 0ull);
352-
EXPECT_FALSE([[events lastObject] hasCallback]);
353-
EXPECT_EQ(last_handled, TRUE);
354-
355-
[events removeAllObjects];
356-
357-
last_handled = FALSE;
358-
[responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
359-
callback:^(BOOL handled) {
360-
last_handled = handled;
361-
}];
347+
EXPECT_EQ([events count], 2u);
362348

363-
EXPECT_EQ([events count], 1u);
364-
event = [events lastObject].data;
349+
event = [events firstObject].data;
365350
EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
366351
EXPECT_EQ(event->physical, kPhysicalKeyA);
367352
EXPECT_EQ(event->logical, kLogicalKeyA);
368-
EXPECT_STREQ(event->character, nullptr);
353+
EXPECT_STREQ(event->character, NULL);
354+
EXPECT_EQ(event->synthesized, true);
355+
356+
event = [events lastObject].data;
357+
EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
358+
EXPECT_EQ(event->physical, kPhysicalKeyA);
359+
EXPECT_EQ(event->logical, 0xE0ull /* à */);
360+
EXPECT_STREQ(event->character, "à");
369361
EXPECT_EQ(event->synthesized, false);
362+
[[events lastObject] respond:FALSE];
370363
EXPECT_EQ(last_handled, FALSE);
371-
[[events lastObject] respond:TRUE];
372-
EXPECT_EQ(last_handled, TRUE);
373364

374365
[events removeAllObjects];
375366
}

0 commit comments

Comments
 (0)