[Lixux] fix pressing Shift+AltLeft throws (flutter/engine#37079)

This commit is contained in:
Bruno Leroux
2022-10-29 05:51:57 +02:00
committed by GitHub
parent 33ba266371
commit f6c5601c2f
2 changed files with 146 additions and 1 deletions

View File

@@ -375,6 +375,15 @@ typedef struct {
double timestamp;
} SyncStateLoopContext;
// Context variables for the foreach call used to find the physical key from
// a modifier logical key.
typedef struct {
bool known_modifier_physical_key;
uint64_t logical_key;
uint64_t physical_key_from_event;
uint64_t corrected_physical_key;
} ModifierLogicalToPhysicalContext;
} // namespace
// Update the pressing record.
@@ -696,6 +705,69 @@ static void synchronize_lock_states_loop_body(gpointer key,
}
}
// Find if a given physical key is the primary physical of one of the known
// modifier keys.
//
// This is used as the body of a loop over #modifier_bit_to_checked_keys.
static void is_known_modifier_physical_key_loop_body(gpointer key,
gpointer value,
gpointer user_data) {
ModifierLogicalToPhysicalContext* context =
reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
FlKeyEmbedderCheckedKey* checked_key =
reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
if (checked_key->primary_physical_key == context->physical_key_from_event) {
context->known_modifier_physical_key = true;
}
}
// Return the primary physical key of a known modifier key which matches the
// given logical key.
//
// This is used as the body of a loop over #modifier_bit_to_checked_keys.
static void find_physical_from_logical_loop_body(gpointer key,
gpointer value,
gpointer user_data) {
ModifierLogicalToPhysicalContext* context =
reinterpret_cast<ModifierLogicalToPhysicalContext*>(user_data);
FlKeyEmbedderCheckedKey* checked_key =
reinterpret_cast<FlKeyEmbedderCheckedKey*>(value);
if (checked_key->primary_logical_key == context->logical_key ||
checked_key->secondary_logical_key == context->logical_key) {
context->corrected_physical_key = checked_key->primary_physical_key;
}
}
static uint64_t corrected_modifier_physical_key(
GHashTable* modifier_bit_to_checked_keys,
uint64_t physical_key_from_event,
uint64_t logical_key) {
ModifierLogicalToPhysicalContext logical_to_physical_context;
logical_to_physical_context.known_modifier_physical_key = false;
logical_to_physical_context.physical_key_from_event = physical_key_from_event;
logical_to_physical_context.logical_key = logical_key;
// If no match is found, defaults to the physical key retrieved from the
// event.
logical_to_physical_context.corrected_physical_key = physical_key_from_event;
// Check if the physical key is one of the known modifier physical key.
g_hash_table_foreach(modifier_bit_to_checked_keys,
is_known_modifier_physical_key_loop_body,
&logical_to_physical_context);
// If the physical key matches a known modifier key, find the modifier
// physical key from the logical key.
if (logical_to_physical_context.known_modifier_physical_key) {
g_hash_table_foreach(modifier_bit_to_checked_keys,
find_physical_from_logical_loop_body,
&logical_to_physical_context);
}
return logical_to_physical_context.corrected_physical_key;
}
static void fl_key_embedder_responder_handle_event_impl(
FlKeyResponder* responder,
FlKeyEvent* event,
@@ -707,10 +779,12 @@ static void fl_key_embedder_responder_handle_event_impl(
g_return_if_fail(event != nullptr);
g_return_if_fail(callback != nullptr);
const uint64_t physical_key = event_to_physical_key(event);
const uint64_t logical_key = specified_logical_key != 0
? specified_logical_key
: event_to_logical_key(event);
const uint64_t physical_key_from_event = event_to_physical_key(event);
const uint64_t physical_key = corrected_modifier_physical_key(
self->modifier_bit_to_checked_keys, physical_key_from_event, logical_key);
const double timestamp = event_to_timestamp(event);
const bool is_down_event = event->is_press;

View File

@@ -23,6 +23,7 @@ constexpr guint16 kKeyCodeDigit1 = 0x0au;
constexpr guint16 kKeyCodeKeyA = 0x26u;
constexpr guint16 kKeyCodeShiftLeft = 0x32u;
constexpr guint16 kKeyCodeShiftRight = 0x3Eu;
constexpr guint16 kKeyCodeAltLeft = 0x40u;
constexpr guint16 kKeyCodeAltRight = 0x6Cu;
constexpr guint16 kKeyCodeNumpad1 = 0x57u;
constexpr guint16 kKeyCodeNumLock = 0x4Du;
@@ -1734,3 +1735,73 @@ TEST(FlKeyEmbedderResponderTest, HandlesShiftAltVersusGroupNext) {
clear_g_call_records();
g_object_unref(responder);
}
// Shift + AltLeft results in GDK event whose keyval is MetaLeft but whose
// keycode is either AltLeft or Shift keycode (depending on which one was
// released last). The physical key is usually deduced from the keycode, but in
// this case (Shift + AltLeft) a correction is needed otherwise the physical
// key won't be the MetaLeft one.
// Regression test for https://github.com/flutter/flutter/issues/96082
TEST(FlKeyEmbedderResponderTest, HandlesShiftAltLeftIsMetaLeft) {
EXPECT_EQ(g_call_records, nullptr);
g_call_records = g_ptr_array_new_with_free_func(g_object_unref);
FlKeyResponder* responder = FL_KEY_RESPONDER(
fl_key_embedder_responder_new(record_calls_in(g_call_records)));
g_expected_handled = true;
guint32 now_time = 1;
// A convenient shorthand to simulate events.
auto send_key_event = [responder, &now_time](bool is_press, guint keyval,
guint16 keycode, int state) {
now_time += 1;
int user_data = 123; // Arbitrary user data
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(now_time, is_press, keyval, keycode, state,
kIsModifier),
verify_response_handled, &user_data);
};
FlKeyEmbedderCallRecord* record;
// ShiftLeft + AltLeft
send_key_event(kPress, GDK_KEY_Shift_L, kKeyCodeShiftLeft, 0x2000000);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
EXPECT_EQ(record->event->physical, kPhysicalShiftLeft);
EXPECT_EQ(record->event->logical, kLogicalShiftLeft);
EXPECT_EQ(record->event->synthesized, false);
send_key_event(kPress, GDK_KEY_Meta_L, kKeyCodeAltLeft, 0x2000001);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 1));
EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
EXPECT_EQ(record->event->physical, kPhysicalMetaLeft);
EXPECT_EQ(record->event->logical, kLogicalMetaLeft);
EXPECT_EQ(record->event->synthesized, false);
send_key_event(kRelease, GDK_KEY_Meta_L, kKeyCodeAltLeft, 0x2002000);
send_key_event(kRelease, GDK_KEY_Shift_L, kKeyCodeShiftLeft, 0x2000000);
g_ptr_array_clear(g_call_records);
// ShiftRight + AltLeft
send_key_event(kPress, GDK_KEY_Shift_R, kKeyCodeShiftRight, 0x2000000);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
EXPECT_EQ(record->event->physical, kPhysicalShiftRight);
EXPECT_EQ(record->event->logical, kLogicalShiftRight);
EXPECT_EQ(record->event->synthesized, false);
send_key_event(kPress, GDK_KEY_Meta_L, kKeyCodeAltLeft, 0x2000001);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 1));
EXPECT_EQ(record->event->type, kFlutterKeyEventTypeDown);
EXPECT_EQ(record->event->physical, kPhysicalMetaLeft);
EXPECT_EQ(record->event->logical, kLogicalMetaLeft);
EXPECT_EQ(record->event->synthesized, false);
clear_g_call_records();
g_object_unref(responder);
}