forked from firka/flutter
[Lixux] fix pressing Shift+AltLeft throws (flutter/engine#37079)
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user