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

Commit dce3fe6

Browse files
committed
[Linux] Synthesize modifier keys events on pointer events
1 parent a71c0c6 commit dce3fe6

6 files changed

+107
-1
lines changed

shell/platform/linux/fl_key_embedder_responder.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,3 +868,20 @@ static void fl_key_embedder_responder_handle_event(
868868
self->send_key_event(&kEmptyEvent, nullptr, nullptr);
869869
}
870870
}
871+
872+
void fl_key_embedder_responder_sync_modifiers_if_needed(
873+
FlKeyEmbedderResponder* responder,
874+
guint state,
875+
double event_time) {
876+
const double timestamp = event_time * kMicrosecondsPerMillisecond;
877+
878+
SyncStateLoopContext sync_state_context;
879+
sync_state_context.self = responder;
880+
sync_state_context.state = state;
881+
sync_state_context.timestamp = timestamp;
882+
883+
// Update pressing states.
884+
g_hash_table_foreach(responder->modifier_bit_to_checked_keys,
885+
synchronize_pressed_states_loop_body,
886+
&sync_state_context);
887+
}

shell/platform/linux/fl_key_embedder_responder.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ G_DECLARE_FINAL_TYPE(FlKeyEmbedderResponder,
5050
FlKeyEmbedderResponder* fl_key_embedder_responder_new(
5151
EmbedderSendKeyEvent send_key_event);
5252

53+
/**
54+
* fl_key_embedder_responder_sync_modifiers_if_needed:
55+
* @responder: the #FlKeyEmbedderResponder self.
56+
* @state: the state of the modifiers mask.
57+
* @event_time: the time attribute of the incoming GDK event.
58+
*
59+
* If needed, synthesize modifier keys up and down event by comparing their
60+
* current pressing states with the given modifiers mask.
61+
*/
62+
void fl_key_embedder_responder_sync_modifiers_if_needed(
63+
FlKeyEmbedderResponder* responder,
64+
guint state,
65+
double event_time);
66+
5367
G_END_DECLS
5468

5569
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEY_EMBEDDER_RESPONDER_H_

shell/platform/linux/fl_keyboard_manager.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,14 @@ gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager* self) {
610610
return self->pending_responds->len == 0 &&
611611
self->pending_redispatches->len == 0;
612612
}
613+
614+
void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager* self,
615+
guint state,
616+
double event_time) {
617+
// The embedder responder is the first element in
618+
// FlKeyboardManager.responder_list.
619+
FlKeyEmbedderResponder* responder =
620+
FL_KEY_EMBEDDER_RESPONDER(g_ptr_array_index(self->responder_list, 0));
621+
fl_key_embedder_responder_sync_modifiers_if_needed(responder, state,
622+
event_time);
623+
}

shell/platform/linux/fl_keyboard_manager.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ gboolean fl_keyboard_manager_handle_event(FlKeyboardManager* manager,
7070
*/
7171
gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager* manager);
7272

73+
/**
74+
* fl_keyboard_manager_sync_modifier_if_needed:
75+
* @manager: the #FlKeyboardManager self.
76+
* @state: the state of the modifiers mask.
77+
* @event_time: the time attribute of the incoming GDK event.
78+
*
79+
* If needed, synthesize modifier keys up and down event by comparing their
80+
* current pressing states with the given modifiers mask.
81+
*/
82+
void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager* manager,
83+
guint state,
84+
double event_time);
85+
7386
G_END_DECLS
7487

7588
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_MANAGER_H_

shell/platform/linux/fl_keyboard_manager_test.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,28 @@
3232
call_records.clear()
3333

3434
namespace {
35+
using ::flutter::testing::keycodes::kLogicalAltLeft;
3536
using ::flutter::testing::keycodes::kLogicalBracketLeft;
3637
using ::flutter::testing::keycodes::kLogicalComma;
38+
using ::flutter::testing::keycodes::kLogicalControlLeft;
3739
using ::flutter::testing::keycodes::kLogicalDigit1;
3840
using ::flutter::testing::keycodes::kLogicalKeyA;
3941
using ::flutter::testing::keycodes::kLogicalKeyB;
4042
using ::flutter::testing::keycodes::kLogicalKeyM;
4143
using ::flutter::testing::keycodes::kLogicalKeyQ;
44+
using ::flutter::testing::keycodes::kLogicalMetaLeft;
4245
using ::flutter::testing::keycodes::kLogicalMinus;
4346
using ::flutter::testing::keycodes::kLogicalParenthesisRight;
4447
using ::flutter::testing::keycodes::kLogicalSemicolon;
48+
using ::flutter::testing::keycodes::kLogicalShiftLeft;
4549
using ::flutter::testing::keycodes::kLogicalUnderscore;
4650

51+
using ::flutter::testing::keycodes::kPhysicalAltLeft;
52+
using ::flutter::testing::keycodes::kPhysicalControlLeft;
4753
using ::flutter::testing::keycodes::kPhysicalKeyA;
4854
using ::flutter::testing::keycodes::kPhysicalKeyB;
55+
using ::flutter::testing::keycodes::kPhysicalMetaLeft;
56+
using ::flutter::testing::keycodes::kPhysicalShiftLeft;
4957

5058
// Hardware key codes.
5159
typedef std::function<void(bool handled)> AsyncKeyCallback;
@@ -880,6 +888,44 @@ TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
880888
VERIFY_DOWN(kLogicalBracketLeft, "[");
881889
}
882890

891+
TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
892+
KeyboardTester tester;
893+
std::vector<CallRecord> call_records;
894+
tester.recordEmbedderCallsTo(call_records);
895+
896+
auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
897+
uint64_t physical, uint64_t logical) {
898+
// Modifier is pressed.
899+
guint state = mask;
900+
fl_keyboard_manager_sync_modifier_if_needed(tester.manager(), state, 1000);
901+
EXPECT_EQ(call_records.size(), 1u);
902+
EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, physical,
903+
logical, NULL, true);
904+
// Modifier is released.
905+
state = state ^ mask;
906+
fl_keyboard_manager_sync_modifier_if_needed(tester.manager(), state, 1001);
907+
EXPECT_EQ(call_records.size(), 2u);
908+
EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeUp, physical, logical,
909+
NULL, true);
910+
call_records.clear();
911+
};
912+
913+
// No modifiers pressed.
914+
guint state = 0;
915+
fl_keyboard_manager_sync_modifier_if_needed(tester.manager(), state, 1000);
916+
EXPECT_EQ(call_records.size(), 0u);
917+
call_records.clear();
918+
919+
// Press and release each modifier once.
920+
verifyModifierIsSynthesized(GDK_CONTROL_MASK, kPhysicalControlLeft,
921+
kLogicalControlLeft);
922+
verifyModifierIsSynthesized(GDK_META_MASK, kPhysicalMetaLeft,
923+
kLogicalMetaLeft);
924+
verifyModifierIsSynthesized(GDK_MOD1_MASK, kPhysicalAltLeft, kLogicalAltLeft);
925+
verifyModifierIsSynthesized(GDK_SHIFT_MASK, kPhysicalShiftLeft,
926+
kLogicalShiftLeft);
927+
}
928+
883929
// The following layout data is generated using DEBUG_PRINT_LAYOUT.
884930

885931
const MockGroupLayoutData kLayoutUs0{{

shell/platform/linux/fl_view.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ static gboolean send_pointer_button_event(FlView* self, GdkEventButton* event) {
164164
fl_scrolling_manager_set_last_mouse_position(self->scrolling_manager,
165165
event->x * scale_factor,
166166
event->y * scale_factor);
167+
fl_keyboard_manager_sync_modifier_if_needed(self->keyboard_manager,
168+
event->state, event->time);
167169
fl_engine_send_mouse_pointer_event(
168170
self->engine, phase, event->time * kMicrosecondsPerMillisecond,
169171
event->x * scale_factor, event->y * scale_factor, 0, 0,
@@ -172,7 +174,7 @@ static gboolean send_pointer_button_event(FlView* self, GdkEventButton* event) {
172174
return TRUE;
173175
}
174176

175-
// Geneartes a mouse pointer event if the pointer appears inside the window.
177+
// Generates a mouse pointer event if the pointer appears inside the window.
176178
static void check_pointer_inside(FlView* view, GdkEvent* event) {
177179
if (!view->pointer_inside) {
178180
view->pointer_inside = TRUE;
@@ -402,6 +404,9 @@ static gboolean motion_notify_event_cb(GtkWidget* widget,
402404
check_pointer_inside(view, reinterpret_cast<GdkEvent*>(event));
403405

404406
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view));
407+
408+
fl_keyboard_manager_sync_modifier_if_needed(view->keyboard_manager,
409+
event->state, event->time);
405410
fl_engine_send_mouse_pointer_event(
406411
view->engine, view->button_state != 0 ? kMove : kHover,
407412
event->time * kMicrosecondsPerMillisecond, event->x * scale_factor,

0 commit comments

Comments
 (0)