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

Commit 2b1eaf1

Browse files
committed
[Windows] Synthesize modifier keys events on pointer events
1 parent 6e91204 commit 2b1eaf1

22 files changed

+380
-46
lines changed

shell/platform/windows/flutter_window.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@ void FlutterWindow::OnPaint() {
156156
void FlutterWindow::OnPointerMove(double x,
157157
double y,
158158
FlutterPointerDeviceKind device_kind,
159-
int32_t device_id) {
160-
binding_handler_delegate_->OnPointerMove(x, y, device_kind, device_id);
159+
int32_t device_id,
160+
int modifiers_state) {
161+
binding_handler_delegate_->OnPointerMove(x, y, device_kind, device_id,
162+
modifiers_state);
161163
}
162164

163165
void FlutterWindow::OnPointerDown(double x,

shell/platform/windows/flutter_window.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class FlutterWindow : public Window, public WindowBindingHandler {
4141
void OnPointerMove(double x,
4242
double y,
4343
FlutterPointerDeviceKind device_kind,
44-
int32_t device_id) override;
44+
int32_t device_id,
45+
int modifiers_state) override;
4546

4647
// |Window|
4748
void OnPointerDown(double x,

shell/platform/windows/flutter_window_unittests.cc

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ class SpyKeyboardKeyHandler : public KeyboardHandlerBase {
4242
ON_CALL(*this, KeyboardHook(_, _, _, _, _, _, _))
4343
.WillByDefault(Invoke(real_implementation_.get(),
4444
&KeyboardKeyHandler::KeyboardHook));
45+
ON_CALL(*this, SyncModifiersIfNeeded(_))
46+
.WillByDefault(Invoke(real_implementation_.get(),
47+
&KeyboardKeyHandler::SyncModifiersIfNeeded));
4548
}
4649

4750
MOCK_METHOD7(KeyboardHook,
@@ -53,6 +56,8 @@ class SpyKeyboardKeyHandler : public KeyboardHandlerBase {
5356
bool was_down,
5457
KeyEventCallback callback));
5558

59+
MOCK_METHOD1(SyncModifiersIfNeeded, void(int modifiers_state));
60+
5661
private:
5762
std::unique_ptr<KeyboardKeyHandler> real_implementation_;
5863
};
@@ -266,15 +271,15 @@ TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
266271
// Move
267272
EXPECT_CALL(delegate,
268273
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
269-
kDefaultPointerDeviceId))
274+
kDefaultPointerDeviceId, 0))
270275
.Times(1);
271276
EXPECT_CALL(delegate,
272277
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
273-
kDefaultPointerDeviceId))
278+
kDefaultPointerDeviceId, 0))
274279
.Times(1);
275280
EXPECT_CALL(delegate,
276281
OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
277-
kDefaultPointerDeviceId))
282+
kDefaultPointerDeviceId, 0))
278283
.Times(1);
279284

280285
// Down
@@ -323,7 +328,7 @@ TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
323328
.Times(1);
324329

325330
win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
326-
kDefaultPointerDeviceId);
331+
kDefaultPointerDeviceId, 0);
327332
win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
328333
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
329334
win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
@@ -333,7 +338,7 @@ TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
333338

334339
// Touch
335340
win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
336-
kDefaultPointerDeviceId);
341+
kDefaultPointerDeviceId, 0);
337342
win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
338343
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
339344
win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
@@ -343,7 +348,7 @@ TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
343348

344349
// Pen
345350
win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
346-
kDefaultPointerDeviceId);
351+
kDefaultPointerDeviceId, 0);
347352
win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
348353
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
349354
win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,

shell/platform/windows/flutter_windows_view.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ void FlutterWindowsView::OnWindowRepaint() {
166166
void FlutterWindowsView::OnPointerMove(double x,
167167
double y,
168168
FlutterPointerDeviceKind device_kind,
169-
int32_t device_id) {
169+
int32_t device_id,
170+
int modifiers_state) {
171+
keyboard_key_handler_->SyncModifiersIfNeeded(modifiers_state);
170172
SendPointerMove(x, y, GetOrCreatePointerState(device_kind, device_id));
171173
}
172174

@@ -285,8 +287,6 @@ void FlutterWindowsView::OnResetImeComposing() {
285287

286288
void FlutterWindowsView::InitializeKeyboard() {
287289
auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
288-
// TODO(cbracken): This can be inlined into KeyboardKeyEmedderHandler once
289-
// UWP code is removed. https://github.com/flutter/flutter/issues/102172.
290290
KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
291291
KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
292292
[](UINT virtual_key, bool extended) {

shell/platform/windows/flutter_windows_view.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
112112
void OnPointerMove(double x,
113113
double y,
114114
FlutterPointerDeviceKind device_kind,
115-
int32_t device_id) override;
115+
int32_t device_id,
116+
int modifiers_state) override;
116117

117118
// |WindowBindingHandlerDelegate|
118119
void OnPointerDown(double x,

shell/platform/windows/keyboard_handler_base.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class KeyboardHandlerBase {
2929
bool extended,
3030
bool was_down,
3131
KeyEventCallback callback) = 0;
32+
33+
// If needed, synthesize modifier keys events by comparing the
34+
// given modifiers state to the known pressing state..
35+
virtual void SyncModifiersIfNeeded(int modifiers_state) = 0;
3236
};
3337

3438
} // namespace flutter

shell/platform/windows/keyboard_key_channel_handler.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ KeyboardKeyChannelHandler::KeyboardKeyChannelHandler(
109109

110110
KeyboardKeyChannelHandler::~KeyboardKeyChannelHandler() = default;
111111

112+
void KeyboardKeyChannelHandler::SyncModifiersIfNeeded(int modifiers_state) {
113+
// Do nothing
114+
}
115+
112116
void KeyboardKeyChannelHandler::KeyboardHook(
113117
int key,
114118
int scancode,

shell/platform/windows/keyboard_key_channel_handler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class KeyboardKeyChannelHandler
3838
bool was_down,
3939
std::function<void(bool)> callback);
4040

41+
void SyncModifiersIfNeeded(int modifiers_state);
42+
4143
private:
4244
// The Flutter system channel for key event messages.
4345
std::unique_ptr<flutter::BasicMessageChannel<rapidjson::Document>> channel_;

shell/platform/windows/keyboard_key_embedder_handler.cc

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
187187
const bool is_event_down = action == WM_KEYDOWN || action == WM_SYSKEYDOWN;
188188

189189
bool event_key_can_be_repeat = true;
190-
UpdateLastSeenCritialKey(key, physical_key, sequence_logical_key);
190+
UpdateLastSeenCriticalKey(key, physical_key, sequence_logical_key);
191191
// Synchronize the toggled states of critical keys (such as whether CapsLocks
192192
// is enabled). Toggled states can only be changed upon a down event, so if
193193
// the recorded toggled state does not match the true state, this function
@@ -197,16 +197,18 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
197197
// After this function, all critical keys will have their toggled state
198198
// updated to the true state, while the critical keys whose toggled state have
199199
// been changed will be pressed regardless of their true pressed state.
200-
// Updating the pressed state will be done by SynchronizeCritialPressedStates.
201-
SynchronizeCritialToggledStates(key, is_event_down, &event_key_can_be_repeat);
200+
// Updating the pressed state will be done by
201+
// SynchronizeCriticalPressedStates.
202+
SynchronizeCriticalToggledStates(key, is_event_down,
203+
&event_key_can_be_repeat);
202204
// Synchronize the pressed states of critical keys (such as whether CapsLocks
203205
// is pressed).
204206
//
205207
// After this function, all critical keys except for the target key will have
206208
// their toggled state and pressed state matched with their true states. The
207209
// target key's pressed state will be updated immediately after this.
208-
SynchronizeCritialPressedStates(key, physical_key, is_event_down,
209-
event_key_can_be_repeat);
210+
SynchronizeCriticalPressedStates(key, physical_key, is_event_down,
211+
event_key_can_be_repeat);
210212

211213
// Reassess the last logical record in case pressingRecords_ was modified
212214
// by the above synchronization methods.
@@ -317,8 +319,8 @@ void KeyboardKeyEmbedderHandler::KeyboardHookImpl(
317319
// received despite that GetKeyState says that CapsLock is not pressed. In
318320
// such case, post-event synchronization will synthesize a CapsLock up event
319321
// after the main event.
320-
SynchronizeCritialPressedStates(key, physical_key, is_event_down,
321-
event_key_can_be_repeat);
322+
SynchronizeCriticalPressedStates(key, physical_key, is_event_down,
323+
event_key_can_be_repeat);
322324
}
323325

324326
void KeyboardKeyEmbedderHandler::KeyboardHook(
@@ -349,7 +351,7 @@ void KeyboardKeyEmbedderHandler::KeyboardHook(
349351
}
350352
}
351353

352-
void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(
354+
void KeyboardKeyEmbedderHandler::UpdateLastSeenCriticalKey(
353355
int virtual_key,
354356
uint64_t physical_key,
355357
uint64_t logical_key) {
@@ -360,7 +362,7 @@ void KeyboardKeyEmbedderHandler::UpdateLastSeenCritialKey(
360362
}
361363
}
362364

363-
void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
365+
void KeyboardKeyEmbedderHandler::SynchronizeCriticalToggledStates(
364366
int event_virtual_key,
365367
bool is_event_down,
366368
bool* event_key_can_be_repeat) {
@@ -415,7 +417,7 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialToggledStates(
415417
}
416418
}
417419

418-
void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates(
420+
void KeyboardKeyEmbedderHandler::SynchronizeCriticalPressedStates(
419421
int event_virtual_key,
420422
int event_physical_key,
421423
bool is_event_down,
@@ -492,6 +494,72 @@ void KeyboardKeyEmbedderHandler::SynchronizeCritialPressedStates(
492494
}
493495
}
494496

497+
void KeyboardKeyEmbedderHandler::SyncModifiersIfNeeded(int modifiers_state) {
498+
// TODO(bleroux): consider exposing these constants in flutter_key_map.g.cc?
499+
const uint64_t physical_shift_left =
500+
windowsToPhysicalMap_.at(kScanCodeShiftLeft);
501+
const uint64_t physical_shift_right =
502+
windowsToPhysicalMap_.at(kScanCodeShiftRight);
503+
const uint64_t logical_shift_left =
504+
windowsToLogicalMap_.at(kKeyCodeShiftLeft);
505+
const uint64_t physical_control_left =
506+
windowsToPhysicalMap_.at(kScanCodeControlLeft);
507+
const uint64_t physical_control_right =
508+
windowsToPhysicalMap_.at(kScanCodeControlRight);
509+
const uint64_t logical_control_left =
510+
windowsToLogicalMap_.at(kKeyCodeControlLeft);
511+
512+
bool shift_pressed = (modifiers_state & kShift) != 0;
513+
SynthesizeIfNeeded(physical_shift_left, physical_shift_right,
514+
logical_shift_left, shift_pressed);
515+
bool control_pressed = (modifiers_state & kControl) != 0;
516+
SynthesizeIfNeeded(physical_control_left, physical_control_right,
517+
logical_control_left, control_pressed);
518+
}
519+
520+
void KeyboardKeyEmbedderHandler::SynthesizeIfNeeded(uint64_t physical_left,
521+
uint64_t physical_right,
522+
uint64_t logical_left,
523+
bool is_pressed) {
524+
auto pressing_record_iter_left = pressingRecords_.find(physical_left);
525+
bool left_pressed = pressing_record_iter_left != pressingRecords_.end();
526+
auto pressing_record_iter_right = pressingRecords_.find(physical_right);
527+
bool right_pressed = pressing_record_iter_right != pressingRecords_.end();
528+
bool already_pressed = left_pressed || right_pressed;
529+
bool synthesize_down = is_pressed && !already_pressed;
530+
bool synthesize_up = !is_pressed && already_pressed;
531+
532+
if (synthesize_down) {
533+
SendSynthesizeDownEvent(physical_left, logical_left);
534+
}
535+
536+
if (synthesize_up && left_pressed) {
537+
uint64_t known_logical = pressing_record_iter_left->second;
538+
SendSynthesizeUpEvent(physical_left, known_logical);
539+
}
540+
541+
if (synthesize_up && right_pressed) {
542+
uint64_t known_logical = pressing_record_iter_right->second;
543+
SendSynthesizeUpEvent(physical_right, known_logical);
544+
}
545+
}
546+
547+
void KeyboardKeyEmbedderHandler::SendSynthesizeDownEvent(uint64_t physical,
548+
uint64_t logical) {
549+
SendEvent(
550+
SynthesizeSimpleEvent(kFlutterKeyEventTypeDown, physical, logical, ""),
551+
nullptr, nullptr);
552+
pressingRecords_[physical] = logical;
553+
};
554+
555+
void KeyboardKeyEmbedderHandler::SendSynthesizeUpEvent(uint64_t physical,
556+
uint64_t logical) {
557+
SendEvent(
558+
SynthesizeSimpleEvent(kFlutterKeyEventTypeUp, physical, logical, ""),
559+
nullptr, nullptr);
560+
pressingRecords_.erase(physical);
561+
};
562+
495563
void KeyboardKeyEmbedderHandler::HandleResponse(bool handled, void* user_data) {
496564
PendingResponse* pending = reinterpret_cast<PendingResponse*>(user_data);
497565
auto callback = std::move(pending->callback);
@@ -516,8 +584,6 @@ void KeyboardKeyEmbedderHandler::InitCriticalKeys(
516584
};
517585
};
518586

519-
// TODO(dkwingsmt): Consider adding more critical keys here.
520-
// https://github.com/flutter/flutter/issues/76736
521587
critical_keys_.emplace(VK_LSHIFT,
522588
createCheckedKey(VK_LSHIFT, false, true, false));
523589
critical_keys_.emplace(VK_RSHIFT,
@@ -532,7 +598,6 @@ void KeyboardKeyEmbedderHandler::InitCriticalKeys(
532598
createCheckedKey(VK_RMENU, true, true, false));
533599
critical_keys_.emplace(VK_LWIN, createCheckedKey(VK_LWIN, true, true, false));
534600
critical_keys_.emplace(VK_RWIN, createCheckedKey(VK_RWIN, true, true, false));
535-
536601
critical_keys_.emplace(VK_CAPITAL,
537602
createCheckedKey(VK_CAPITAL, false, true, true));
538603
critical_keys_.emplace(VK_SCROLL,

shell/platform/windows/keyboard_key_embedder_handler.h

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class KeyboardKeyEmbedderHandler
7070
bool was_down,
7171
std::function<void(bool)> callback) override;
7272

73+
void SyncModifiersIfNeeded(int modifiers_state) override;
74+
7375
private:
7476
struct PendingResponse {
7577
std::function<void(bool, uint64_t)> callback;
@@ -109,27 +111,40 @@ class KeyboardKeyEmbedderHandler
109111
// Assign |critical_keys_| with basic information.
110112
void InitCriticalKeys(MapVirtualKeyToScanCode map_virtual_key_to_scan_code);
111113
// Update |critical_keys_| with last seen logical and physical key.
112-
void UpdateLastSeenCritialKey(int virtual_key,
113-
uint64_t physical_key,
114-
uint64_t logical_key);
114+
void UpdateLastSeenCriticalKey(int virtual_key,
115+
uint64_t physical_key,
116+
uint64_t logical_key);
115117
// Check each key's state from |get_key_state_| and synthesize events
116118
// if their toggling states have been desynchronized.
117-
void SynchronizeCritialToggledStates(int event_virtual_key,
118-
bool is_event_down,
119-
bool* event_key_can_be_repeat);
119+
void SynchronizeCriticalToggledStates(int event_virtual_key,
120+
bool is_event_down,
121+
bool* event_key_can_be_repeat);
120122
// Check each key's state from |get_key_state_| and synthesize events
121123
// if their pressing states have been desynchronized.
122-
void SynchronizeCritialPressedStates(int event_virtual_key,
123-
int event_physical_key,
124-
bool is_event_down,
125-
bool event_key_can_be_repeat);
124+
void SynchronizeCriticalPressedStates(int event_virtual_key,
125+
int event_physical_key,
126+
bool is_event_down,
127+
bool event_key_can_be_repeat);
126128

127129
// Wraps perform_send_event_ with state tracking. Use this instead of
128130
// |perform_send_event_| to send events to the framework.
129131
void SendEvent(const FlutterKeyEvent& event,
130132
FlutterKeyEventCallback callback,
131133
void* user_data);
132134

135+
// Send a synthesized down event and update pressing records.
136+
void SendSynthesizeDownEvent(uint64_t physical, uint64_t logical);
137+
138+
// Send a synthesized up event and update pressing records.
139+
void SendSynthesizeUpEvent(uint64_t physical, uint64_t logical);
140+
141+
// Send a synthesized up or down event depending on the current pressing
142+
// state.
143+
void SynthesizeIfNeeded(uint64_t physical_left,
144+
uint64_t physical_right,
145+
uint64_t logical_left,
146+
bool is_pressed);
147+
133148
std::function<void(const FlutterKeyEvent&, FlutterKeyEventCallback, void*)>
134149
perform_send_event_;
135150
GetKeyStateHandler get_key_state_;

0 commit comments

Comments
 (0)