Skip to content

Commit b257966

Browse files
authored
[Window, Keyboard] Fix repeat events of modifier keys (#35046)
1 parent a8c04a1 commit b257966

File tree

3 files changed

+36
-7
lines changed

3 files changed

+36
-7
lines changed

shell/platform/windows/keyboard_key_embedder_handler.cc

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
186186

187187
const bool is_event_down = action == WM_KEYDOWN || action == WM_SYSKEYDOWN;
188188

189+
bool event_key_can_be_repeat = true;
189190
UpdateLastSeenCritialKey(key, physical_key, sequence_logical_key);
190191
// Synchronize the toggled states of critical keys (such as whether CapsLocks
191192
// is enabled). Toggled states can only be changed upon a down event, so if
@@ -197,14 +198,15 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
197198
// updated to the true state, while the critical keys whose toggled state have
198199
// been changed will be pressed regardless of their true pressed state.
199200
// Updating the pressed state will be done by SynchronizeCritialPressedStates.
200-
SynchronizeCritialToggledStates(key, is_event_down);
201+
SynchronizeCritialToggledStates(key, is_event_down, &event_key_can_be_repeat);
201202
// Synchronize the pressed states of critical keys (such as whether CapsLocks
202203
// is pressed).
203204
//
204205
// After this function, all critical keys except for the target key will have
205206
// their toggled state and pressed state matched with their true states. The
206207
// target key's pressed state will be updated immediately after this.
207-
SynchronizeCritialPressedStates(key, physical_key, is_event_down);
208+
SynchronizeCritialPressedStates(key, physical_key, is_event_down,
209+
event_key_can_be_repeat);
208210

209211
// The resulting event's `type`.
210212
FlutterKeyEventType type;
@@ -340,7 +342,8 @@ void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(
340342

341343
void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
342344
int event_virtual_key,
343-
bool is_event_down) {
345+
bool is_event_down,
346+
bool* event_key_can_be_repeat) {
344347
// NowState ----------------> PreEventState --------------> TrueState
345348
// Synchronization Event
346349
for (auto& kv : critical_keys_) {
@@ -385,6 +388,7 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
385388
key_info.physical_key,
386389
key_info.logical_key, empty_character),
387390
nullptr, nullptr);
391+
*event_key_can_be_repeat = false;
388392
}
389393
key_info.toggled_on = true_toggled;
390394
}
@@ -394,7 +398,8 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
394398
void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates(
395399
int event_virtual_key,
396400
int event_physical_key,
397-
bool is_event_down) {
401+
bool is_event_down,
402+
bool event_key_can_be_repeat) {
398403
// During an incoming event, there might be a synthesized Flutter event for
399404
// each key of each pressing goal, followed by an eventual main Flutter
400405
// event.
@@ -424,8 +429,18 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates(
424429
if (is_event_down) {
425430
// For down events, this key is the event key if they have the same
426431
// virtual key, because virtual key represents "functionality."
432+
//
433+
// In that case, normally Flutter should synthesize nothing since the
434+
// resulting event can adapt to the current state by dispatching either
435+
// a down or a repeat event. However, in certain cases (when Flutter has
436+
// just synchronized the key's toggling state) the event must not be a
437+
// repeat event.
427438
if (virtual_key == event_virtual_key) {
428-
pre_event_pressed = false;
439+
if (event_key_can_be_repeat) {
440+
continue;
441+
} else {
442+
pre_event_pressed = false;
443+
}
429444
}
430445
} else {
431446
// For up events, this key is the event key if they have the same

shell/platform/windows/keyboard_key_embedder_handler.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,14 @@ class KeyboardKeyEmbedderHandler
115115
// Check each key's state from |get_key_state_| and synthesize events
116116
// if their toggling states have been desynchronized.
117117
void SynchronizeCritialToggledStates(int event_virtual_key,
118-
bool is_event_down);
118+
bool is_event_down,
119+
bool* event_key_can_be_repeat);
119120
// Check each key's state from |get_key_state_| and synthesize events
120121
// if their pressing states have been desynchronized.
121122
void SynchronizeCritialPressedStates(int event_virtual_key,
122123
int event_physical_key,
123-
bool is_event_down);
124+
bool is_event_down,
125+
bool event_key_can_be_repeat);
124126

125127
// Wraps perform_send_event_ with state tracking. Use this instead of
126128
// |perform_send_event_| to send events to the framework.

shell/platform/windows/keyboard_unittests.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,18 @@ TEST(KeyboardTest, ShiftLeftUnhandled) {
696696
clear_key_calls();
697697
EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
698698

699+
// Hold ShiftLeft
700+
tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
701+
WmKeyDownInfo{VK_SHIFT, kScanCodeShiftLeft, kNotExtended, kWasDown}.Build(
702+
kWmResultZero)});
703+
704+
EXPECT_EQ(key_calls.size(), 1);
705+
EXPECT_CALL_IS_EVENT(key_calls[0], kFlutterKeyEventTypeRepeat,
706+
kPhysicalShiftLeft, kLogicalShiftLeft, "",
707+
kNotSynthesized);
708+
clear_key_calls();
709+
EXPECT_EQ(tester.RedispatchedMessageCountAndClear(), 1);
710+
699711
// Release ShiftLeft
700712
tester.InjectKeyboardChanges(std::vector<KeyboardChange>{
701713
KeyStateChange{VK_LSHIFT, false, true},

0 commit comments

Comments
 (0)