Refactor FlKeyboardHandler (flutter/engine#55601)

Refactor FlKeyboardHandler to be more GObject and less C++.

Done as we will need to move the handler from FlView to FlEngine to
support multi-view.
This commit is contained in:
Robert Ancell 2024-10-04 12:00:00 +13:00 committed by GitHub
parent 887fcf0563
commit 971dbd79fb
17 changed files with 806 additions and 786 deletions

View File

@ -44809,6 +44809,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_key_responder.h + ../../../flut
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_message_codec.cc + ../../../flutter/LICENSE
@ -47715,6 +47717,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_key_responder.h
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_pending_event.h
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.cc
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_view_delegate.h
FILE: ../../../flutter/shell/platform/linux/fl_message_codec.cc

View File

@ -81,6 +81,7 @@ source_set("flutter_linux_sources") {
"fl_dart_project_private.h",
"fl_engine_private.h",
"fl_keyboard_handler.h",
"fl_keyboard_pending_event.h",
"fl_keyboard_view_delegate.h",
"fl_key_event.h",
"fl_key_responder.h",
@ -116,6 +117,7 @@ source_set("flutter_linux_sources") {
"fl_key_event.cc",
"fl_key_responder.cc",
"fl_keyboard_handler.cc",
"fl_keyboard_pending_event.cc",
"fl_keyboard_view_delegate.cc",
"fl_message_codec.cc",
"fl_method_call.cc",

View File

@ -212,9 +212,11 @@ static void fl_key_channel_responder_handle_event(
g_return_if_fail(event != nullptr);
g_return_if_fail(callback != nullptr);
const gchar* type = event->is_press ? kTypeValueDown : kTypeValueUp;
int64_t scan_code = event->keycode;
int64_t unicode_scarlar_values = gdk_keyval_to_unicode(event->keyval);
const gchar* type =
fl_key_event_get_is_press(event) ? kTypeValueDown : kTypeValueUp;
int64_t scan_code = fl_key_event_get_keycode(event);
int64_t unicode_scarlar_values =
gdk_keyval_to_unicode(fl_key_event_get_keyval(event));
// For most modifier keys, GTK keeps track of the "pressed" state of the
// modifier keys. Flutter uses this information to keep modifier keys from
@ -239,20 +241,21 @@ static void fl_key_channel_responder_handle_event(
// interactions (for example, if shift-lock is on, tab traversal is broken).
// Remove lock states from state mask.
guint state = event->state & ~(GDK_LOCK_MASK | GDK_MOD2_MASK);
guint state =
fl_key_event_get_state(event) & ~(GDK_LOCK_MASK | GDK_MOD2_MASK);
static bool shift_lock_pressed = FALSE;
static bool caps_lock_pressed = FALSE;
static bool num_lock_pressed = FALSE;
switch (event->keyval) {
switch (fl_key_event_get_keyval(event)) {
case GDK_KEY_Num_Lock:
num_lock_pressed = event->is_press;
num_lock_pressed = fl_key_event_get_is_press(event);
break;
case GDK_KEY_Caps_Lock:
caps_lock_pressed = event->is_press;
caps_lock_pressed = fl_key_event_get_is_press(event);
break;
case GDK_KEY_Shift_Lock:
shift_lock_pressed = event->is_press;
shift_lock_pressed = fl_key_event_get_is_press(event);
break;
}
@ -269,7 +272,7 @@ static void fl_key_channel_responder_handle_event(
fl_value_set_string_take(message, kToolkitKey,
fl_value_new_string(kGtkToolkit));
fl_value_set_string_take(message, kKeyCodeKey,
fl_value_new_int(event->keyval));
fl_value_new_int(fl_key_event_get_keyval(event)));
fl_value_set_string_take(message, kModifiersKey, fl_value_new_int(state));
if (unicode_scarlar_values != 0) {
fl_value_set_string_take(message, kUnicodeScalarValuesKey,

View File

@ -29,33 +29,6 @@ static void responder_callback(bool handled, gpointer user_data) {
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
}
namespace {
// A global variable to store new event. It is a global variable so that it can
// be returned by #fl_key_event_new_by_mock for easy use.
FlKeyEvent _g_key_event;
} // namespace
// Create a new #FlKeyEvent with the given information.
//
// This event is passed to #fl_key_responder_handle_event,
// which assumes that the event is managed by callee.
// Therefore #fl_key_event_new_by_mock doesn't need to
// dynamically allocate, but reuses the same global object.
static FlKeyEvent* fl_key_event_new_by_mock(guint32 time_in_milliseconds,
bool is_press,
guint keyval,
guint16 keycode,
GdkModifierType state,
gboolean is_modifier) {
_g_key_event.is_press = is_press;
_g_key_event.time = time_in_milliseconds;
_g_key_event.state = state;
_g_key_event.keyval = keyval;
_g_key_event.keycode = keycode;
_g_key_event.origin = nullptr;
return &_g_key_event;
}
// Test sending a letter "A";
TEST(FlKeyChannelResponderTest, SendKeyEvent) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
@ -69,11 +42,9 @@ TEST(FlKeyChannelResponderTest, SendKeyEvent) {
g_autoptr(FlKeyResponder) responder =
FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger, &mock));
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12345, true, GDK_KEY_A, 0x04,
static_cast<GdkModifierType>(0), false),
responder_callback, loop);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, responder_callback, loop);
expected_value =
"{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, "
"modifiers: 0, unicodeScalarValues: 65}";
@ -82,11 +53,9 @@ TEST(FlKeyChannelResponderTest, SendKeyEvent) {
// Blocks here until echo_response_cb is called.
g_main_loop_run(loop);
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(23456, false, GDK_KEY_A, 0x04,
static_cast<GdkModifierType>(0), false),
responder_callback, loop);
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
23456, FALSE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event2, responder_callback, loop);
expected_value =
"{type: keyup, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, "
"modifiers: 0, unicodeScalarValues: 65}";
@ -110,11 +79,9 @@ void test_lock_event(guint key_code,
g_autoptr(FlKeyResponder) responder =
FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger, &mock));
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12345, true, key_code, 0x04,
static_cast<GdkModifierType>(0), false),
responder_callback, loop);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
12345, TRUE, 0x04, key_code, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, responder_callback, loop);
expected_value = down_expected;
expected_handled = FALSE;
@ -123,11 +90,9 @@ void test_lock_event(guint key_code,
expected_value = up_expected;
expected_handled = FALSE;
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12346, false, key_code, 0x04,
static_cast<GdkModifierType>(0), false),
responder_callback, loop);
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
12346, FALSE, 0x04, key_code, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event2, responder_callback, loop);
// Blocks here until echo_response_cb is called.
g_main_loop_run(loop);
@ -172,11 +137,9 @@ TEST(FlKeyChannelResponderTest, TestKeyEventHandledByFramework) {
g_autoptr(FlKeyResponder) responder =
FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger, &mock));
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12345, true, GDK_KEY_A, 0x04,
static_cast<GdkModifierType>(0), false),
responder_callback, loop);
g_autoptr(FlKeyEvent) event = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event, responder_callback, loop);
expected_handled = TRUE;
expected_value =
"{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, "
@ -198,11 +161,10 @@ TEST(FlKeyChannelResponderTest, UseSpecifiedLogicalKey) {
g_autoptr(FlKeyResponder) responder =
FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger, &mock));
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12345, true, GDK_KEY_A, 0x04,
static_cast<GdkModifierType>(0), false),
responder_callback, loop, 888);
g_autoptr(FlKeyEvent) event = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event, responder_callback, loop,
888);
expected_handled = TRUE;
expected_value =
"{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, "

View File

@ -297,16 +297,16 @@ static uint64_t apply_id_plane(uint64_t logical_id, uint64_t plane) {
return (logical_id & kValueMask) | plane;
}
static uint64_t event_to_physical_key(const FlKeyEvent* event) {
auto found = xkb_to_physical_key_map.find(event->keycode);
static uint64_t event_to_physical_key(FlKeyEvent* event) {
auto found = xkb_to_physical_key_map.find(fl_key_event_get_keycode(event));
if (found != xkb_to_physical_key_map.end()) {
return found->second;
}
return apply_id_plane(event->keycode, kGtkPlane);
return apply_id_plane(fl_key_event_get_keycode(event), kGtkPlane);
}
static uint64_t event_to_logical_key(const FlKeyEvent* event) {
guint keyval = event->keyval;
static uint64_t event_to_logical_key(FlKeyEvent* event) {
guint keyval = fl_key_event_get_keyval(event);
auto found = gtk_keyval_to_logical_key_map.find(keyval);
if (found != gtk_keyval_to_logical_key_map.end()) {
return found->second;
@ -319,14 +319,15 @@ static uint64_t event_to_logical_key(const FlKeyEvent* event) {
return apply_id_plane(keyval, kGtkPlane);
}
static uint64_t event_to_timestamp(const FlKeyEvent* event) {
return kMicrosecondsPerMillisecond * static_cast<double>(event->time);
static uint64_t event_to_timestamp(FlKeyEvent* event) {
return kMicrosecondsPerMillisecond *
static_cast<double>(fl_key_event_get_time(event));
}
// Returns a newly accocated UTF-8 string from event->keyval that must be
// freed later with g_free().
static char* event_to_character(const FlKeyEvent* event) {
gunichar unicodeChar = gdk_keyval_to_unicode(event->keyval);
// Returns a newly accocated UTF-8 string from fl_key_event_get_keyval(event)
// that must be freed later with g_free().
static char* event_to_character(FlKeyEvent* event) {
gunichar unicodeChar = gdk_keyval_to_unicode(fl_key_event_get_keyval(event));
glong items_written;
gchar* result = g_ucs4_to_utf8(&unicodeChar, 1, NULL, &items_written, NULL);
if (items_written == 0) {
@ -790,11 +791,11 @@ static void fl_key_embedder_responder_handle_event_impl(
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;
const bool is_down_event = fl_key_event_get_is_press(event);
SyncStateLoopContext sync_state_context;
sync_state_context.self = self;
sync_state_context.state = event->state;
sync_state_context.state = fl_key_event_get_state(event);
sync_state_context.timestamp = timestamp;
sync_state_context.is_down = is_down_event;
sync_state_context.event_logical_key = logical_key;

View File

@ -16,9 +16,6 @@ namespace {
constexpr gboolean kRelease = FALSE;
constexpr gboolean kPress = TRUE;
constexpr gboolean kIsModifier = TRUE;
constexpr gboolean kIsNotModifier = FALSE;
constexpr guint16 kKeyCodeDigit1 = 0x0au;
constexpr guint16 kKeyCodeKeyA = 0x26u;
constexpr guint16 kKeyCodeShiftLeft = 0x32u;
@ -100,33 +97,6 @@ static FlKeyEmbedderCallRecord* fl_key_embedder_call_record_new(
return self;
}
namespace {
// A global variable to store new event. It is a global variable so that it can
// be returned by #fl_key_event_new_by_mock for easy use.
FlKeyEvent _g_key_event;
} // namespace
// Create a new #FlKeyEvent with the given information.
//
// This event is passed to #fl_key_responder_handle_event,
// which assumes that the event is managed by callee.
// Therefore #fl_key_event_new_by_mock doesn't need to
// dynamically allocate, but reuses the same global object.
static FlKeyEvent* fl_key_event_new_by_mock(guint32 time_in_milliseconds,
bool is_press,
guint keyval,
guint16 keycode,
GdkModifierType state,
gboolean is_modifier) {
_g_key_event.is_press = is_press;
_g_key_event.time = time_in_milliseconds;
_g_key_event.state = state;
_g_key_event.keyval = keyval;
_g_key_event.keycode = keycode;
_g_key_event.origin = nullptr;
return &_g_key_event;
}
static gboolean g_expected_handled;
static gpointer g_expected_user_data;
@ -176,11 +146,11 @@ TEST(FlKeyEmbedderResponderTest, SendKeyEvent) {
// On a QWERTY keyboard, press key Q (physically key A), and release.
// Key down
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12345, kPress, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 =
fl_key_event_new(12345, kPress, kKeyCodeKeyA, GDK_KEY_a,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -196,11 +166,11 @@ TEST(FlKeyEmbedderResponderTest, SendKeyEvent) {
g_ptr_array_clear(g_call_records);
// Key up
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12346, kRelease, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(12346, kRelease, kKeyCodeKeyA, GDK_KEY_a,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -217,11 +187,11 @@ TEST(FlKeyEmbedderResponderTest, SendKeyEvent) {
// On an AZERTY keyboard, press key Q (physically key A), and release.
// Key down
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12347, kPress, GDK_KEY_q, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 =
fl_key_event_new(12347, kPress, kKeyCodeKeyA, GDK_KEY_q,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -237,11 +207,11 @@ TEST(FlKeyEmbedderResponderTest, SendKeyEvent) {
g_ptr_array_clear(g_call_records);
// Key up
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12348, kRelease, GDK_KEY_q, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 =
fl_key_event_new(12348, kRelease, kKeyCodeKeyA, GDK_KEY_q,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -272,11 +242,11 @@ TEST(FlKeyEmbedderResponderTest, UsesSpecifiedLogicalKey) {
// On an AZERTY keyboard, press physical key 1, and release.
// Key down
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(12345, kPress, GDK_KEY_ampersand, kKeyCodeDigit1,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data, kLogicalDigit1);
g_autoptr(FlKeyEvent) event =
fl_key_event_new(12345, kPress, kKeyCodeDigit1, GDK_KEY_ampersand,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event, verify_response_handled,
&user_data, kLogicalDigit1);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -306,11 +276,11 @@ TEST(FlKeyEmbedderResponderTest, PressShiftDuringLetterKeyTap) {
FlKeyEmbedderCallRecord* record;
// Press shift right
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_Shift_R, kKeyCodeShiftRight,
static_cast<GdkModifierType>(0), kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 =
fl_key_event_new(101, kPress, kKeyCodeShiftRight, GDK_KEY_Shift_R,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -324,11 +294,10 @@ TEST(FlKeyEmbedderResponderTest, PressShiftDuringLetterKeyTap) {
g_ptr_array_clear(g_call_records);
// Press key A
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_A, kKeyCodeKeyA,
GDK_SHIFT_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_SHIFT_MASK, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -342,11 +311,10 @@ TEST(FlKeyEmbedderResponderTest, PressShiftDuringLetterKeyTap) {
g_ptr_array_clear(g_call_records);
// Release shift right
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_Shift_R,
kKeyCodeShiftRight, GDK_SHIFT_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
103, kRelease, kKeyCodeShiftRight, GDK_KEY_Shift_R, GDK_SHIFT_MASK, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -360,11 +328,11 @@ TEST(FlKeyEmbedderResponderTest, PressShiftDuringLetterKeyTap) {
g_ptr_array_clear(g_call_records);
// Release key A
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(104, kRelease, GDK_KEY_A, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 =
fl_key_event_new(104, kRelease, kKeyCodeKeyA, GDK_KEY_A,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -399,11 +367,11 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
FlKeyEmbedderCallRecord* record;
// Press Numpad 1 (stage 0)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_KP_End, kKeyCodeNumpad1,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 =
fl_key_event_new(101, kPress, kKeyCodeNumpad1, GDK_KEY_KP_End,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -417,11 +385,11 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
g_ptr_array_clear(g_call_records);
// Press NumLock (stage 0 -> 1)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_Num_Lock, kKeyCodeNumLock,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(102, kPress, kKeyCodeNumLock, GDK_KEY_Num_Lock,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -435,11 +403,10 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
g_ptr_array_clear(g_call_records);
// Release numpad 1 (stage 1)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(104, kRelease, GDK_KEY_KP_1, kKeyCodeNumpad1,
GDK_MOD2_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
104, kRelease, kKeyCodeNumpad1, GDK_KEY_KP_1, GDK_MOD2_MASK, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -453,11 +420,10 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
g_ptr_array_clear(g_call_records);
// Release NumLock (stage 1 -> 2)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_Num_Lock, kKeyCodeNumLock,
GDK_MOD2_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
103, kRelease, kKeyCodeNumLock, GDK_KEY_Num_Lock, GDK_MOD2_MASK, 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -471,11 +437,10 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
g_ptr_array_clear(g_call_records);
// Press Numpad 1 (stage 2)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_KP_End, kKeyCodeNumpad1,
GDK_MOD2_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event5 = fl_key_event_new(
101, kPress, kKeyCodeNumpad1, GDK_KEY_KP_End, GDK_MOD2_MASK, 0);
fl_key_responder_handle_event(responder, event5, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -489,11 +454,10 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
g_ptr_array_clear(g_call_records);
// Press NumLock (stage 2 -> 3)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_Num_Lock, kKeyCodeNumLock,
GDK_MOD2_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event6 = fl_key_event_new(
102, kPress, kKeyCodeNumLock, GDK_KEY_Num_Lock, GDK_MOD2_MASK, 0);
fl_key_responder_handle_event(responder, event6, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -507,11 +471,10 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
g_ptr_array_clear(g_call_records);
// Release numpad 1 (stage 3)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(104, kRelease, GDK_KEY_KP_1, kKeyCodeNumpad1,
GDK_MOD2_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event7 = fl_key_event_new(
104, kRelease, kKeyCodeNumpad1, GDK_KEY_KP_1, GDK_MOD2_MASK, 0);
fl_key_responder_handle_event(responder, event7, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -525,11 +488,10 @@ TEST(FlKeyEmbedderResponderTest, TapNumPadKeysBetweenNumLockEvents) {
g_ptr_array_clear(g_call_records);
// Release NumLock (stage 3 -> 0)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_Num_Lock, kKeyCodeNumLock,
GDK_MOD2_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event8 = fl_key_event_new(
103, kRelease, kKeyCodeNumLock, GDK_KEY_Num_Lock, GDK_MOD2_MASK, 0);
fl_key_responder_handle_event(responder, event8, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -562,11 +524,10 @@ TEST(FlKeyEmbedderResponderTest, ReleaseShiftKeyBetweenDigitKeyEvents) {
GdkModifierType state = static_cast<GdkModifierType>(0);
// Press shift left
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_Shift_L, kKeyCodeShiftLeft,
state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
101, kPress, kKeyCodeShiftLeft, GDK_KEY_Shift_L, state, 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -582,11 +543,10 @@ TEST(FlKeyEmbedderResponderTest, ReleaseShiftKeyBetweenDigitKeyEvents) {
state = GDK_SHIFT_MASK;
// Press digit 1, which is '!' on a US keyboard
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_exclam, kKeyCodeDigit1,
state, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(102, kPress, kKeyCodeDigit1, GDK_KEY_exclam, state, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -600,11 +560,10 @@ TEST(FlKeyEmbedderResponderTest, ReleaseShiftKeyBetweenDigitKeyEvents) {
g_ptr_array_clear(g_call_records);
// Release shift
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_Shift_L,
kKeyCodeShiftLeft, state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
103, kRelease, kKeyCodeShiftLeft, GDK_KEY_Shift_L, state, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -620,11 +579,10 @@ TEST(FlKeyEmbedderResponderTest, ReleaseShiftKeyBetweenDigitKeyEvents) {
state = static_cast<GdkModifierType>(0);
// Release digit 1, which is "1" because shift has been released.
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(104, kRelease, GDK_KEY_1, kKeyCodeDigit1, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 =
fl_key_event_new(104, kRelease, kKeyCodeDigit1, GDK_KEY_1, state, 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -655,11 +613,11 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
FlKeyEmbedderCallRecord* record;
// Press CapsLock (stage 0 -> 1)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_Caps_Lock, kKeyCodeCapsLock,
static_cast<GdkModifierType>(0), kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 =
fl_key_event_new(101, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -673,11 +631,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
g_ptr_array_clear(g_call_records);
// Press key A (stage 1)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_A, kKeyCodeKeyA,
GDK_LOCK_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -691,11 +648,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
g_ptr_array_clear(g_call_records);
// Release CapsLock (stage 1 -> 2)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_Caps_Lock,
kKeyCodeCapsLock, GDK_LOCK_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
103, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -709,11 +665,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
g_ptr_array_clear(g_call_records);
// Release key A (stage 2)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(104, kRelease, GDK_KEY_A, kKeyCodeKeyA,
GDK_LOCK_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 = fl_key_event_new(104, kRelease, kKeyCodeKeyA,
GDK_KEY_A, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -727,11 +682,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
g_ptr_array_clear(g_call_records);
// Press CapsLock (stage 2 -> 3)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(105, kPress, GDK_KEY_Caps_Lock, kKeyCodeCapsLock,
GDK_LOCK_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event5 = fl_key_event_new(
105, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event5, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -745,11 +699,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
g_ptr_array_clear(g_call_records);
// Press key A (stage 3)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(106, kPress, GDK_KEY_A, kKeyCodeKeyA,
GDK_LOCK_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event6 =
fl_key_event_new(106, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event6, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -763,11 +716,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
g_ptr_array_clear(g_call_records);
// Release CapsLock (stage 3 -> 0)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(107, kRelease, GDK_KEY_Caps_Lock,
kKeyCodeCapsLock, GDK_LOCK_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event7 = fl_key_event_new(
107, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event7, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -781,11 +733,11 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEvents) {
g_ptr_array_clear(g_call_records);
// Release key A (stage 0)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(108, kRelease, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event8 =
fl_key_event_new(108, kRelease, kKeyCodeKeyA, GDK_KEY_a,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event8, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -816,11 +768,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
FlKeyEmbedderCallRecord* record;
// Press key A (stage 0)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
101, kPress, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -834,11 +785,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
g_ptr_array_clear(g_call_records);
// Press CapsLock (stage 0 -> 1)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_Caps_Lock, kKeyCodeCapsLock,
GDK_LOCK_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
102, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -852,11 +802,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
g_ptr_array_clear(g_call_records);
// Release CapsLock (stage 1 -> 2)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_Caps_Lock,
kKeyCodeCapsLock, GDK_LOCK_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
103, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -870,11 +819,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
g_ptr_array_clear(g_call_records);
// Release key A (stage 2)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(104, kRelease, GDK_KEY_A, kKeyCodeKeyA,
GDK_LOCK_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 = fl_key_event_new(104, kRelease, kKeyCodeKeyA,
GDK_KEY_A, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -888,11 +836,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
g_ptr_array_clear(g_call_records);
// Press key A (stage 2)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(105, kPress, GDK_KEY_A, kKeyCodeKeyA,
GDK_LOCK_MASK, kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event5 =
fl_key_event_new(105, kPress, kKeyCodeKeyA, GDK_KEY_A, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event5, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -906,11 +853,11 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
g_ptr_array_clear(g_call_records);
// Press CapsLock (stage 2 -> 3)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(106, kPress, GDK_KEY_Caps_Lock, kKeyCodeCapsLock,
static_cast<GdkModifierType>(0), kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event6 =
fl_key_event_new(106, kPress, kKeyCodeCapsLock, GDK_KEY_Caps_Lock,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event6, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -924,11 +871,10 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
g_ptr_array_clear(g_call_records);
// Release CapsLock (stage 3 -> 0)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(107, kRelease, GDK_KEY_Caps_Lock,
kKeyCodeCapsLock, GDK_LOCK_MASK, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event7 = fl_key_event_new(
107, kRelease, kKeyCodeCapsLock, GDK_KEY_Caps_Lock, GDK_LOCK_MASK, 0);
fl_key_responder_handle_event(responder, event7, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -942,11 +888,11 @@ TEST(FlKeyEmbedderResponderTest, TapLetterKeysBetweenCapsLockEventsReversed) {
g_ptr_array_clear(g_call_records);
// Release key A (stage 0)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(108, kRelease, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event8 =
fl_key_event_new(108, kRelease, kKeyCodeKeyA, GDK_KEY_a,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event8, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -973,11 +919,10 @@ TEST(FlKeyEmbedderResponderTest, TurnDuplicateDownEventsToRepeats) {
FlKeyEmbedderCallRecord* record;
// Press KeyA
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
101, kPress, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
@ -987,11 +932,10 @@ TEST(FlKeyEmbedderResponderTest, TurnDuplicateDownEventsToRepeats) {
// Another KeyA down events, which usually means a repeated event.
g_expected_handled = false;
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
102, kPress, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
@ -1007,11 +951,11 @@ TEST(FlKeyEmbedderResponderTest, TurnDuplicateDownEventsToRepeats) {
g_ptr_array_clear(g_call_records);
// Release KeyA
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_q, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 =
fl_key_event_new(103, kRelease, kKeyCodeKeyA, GDK_KEY_q,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1033,11 +977,11 @@ TEST(FlKeyEmbedderResponderTest, IgnoreAbruptUpEvent) {
// Release KeyA before it was even pressed.
g_expected_handled = true; // The empty event is always handled.
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_q, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event =
fl_key_event_new(103, kRelease, kKeyCodeKeyA, GDK_KEY_q,
static_cast<GdkModifierType>(0), 0);
fl_key_responder_handle_event(responder, event, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
@ -1069,11 +1013,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncPressingStateOnSelfEvents) {
GdkModifierType state = GDK_CONTROL_MASK;
// Send a ControlLeft up
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kRelease, GDK_KEY_Control_L,
kKeyCodeControlLeft, state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
101, kRelease, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1099,11 +1042,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncPressingStateOnSelfEvents) {
// Send a ControlLeft down.
state = static_cast<GdkModifierType>(0);
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_Control_L,
kKeyCodeControlLeft, state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
102, kPress, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
invoke_record_callback_and_verify(record, TRUE, &user_data);
@ -1113,11 +1055,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncPressingStateOnSelfEvents) {
state = static_cast<GdkModifierType>(0);
// Send another ControlLeft down
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kPress, GDK_KEY_Control_L,
kKeyCodeControlLeft, state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
103, kPress, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1141,11 +1082,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncPressingStateOnSelfEvents) {
// Send a ControlLeft up to clear up state.
state = GDK_CONTROL_MASK;
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(104, kRelease, GDK_KEY_Control_L,
kKeyCodeControlLeft, state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
104, kRelease, kKeyCodeControlLeft, GDK_KEY_Control_L, state, 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
invoke_record_callback_and_verify(record, TRUE, &user_data);
@ -1157,11 +1097,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncPressingStateOnSelfEvents) {
state = GDK_CONTROL_MASK;
// Send a ControlRight up.
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(105, kRelease, GDK_KEY_Control_R,
kKeyCodeControlRight, state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event5 = fl_key_event_new(
105, kRelease, kKeyCodeControlRight, GDK_KEY_Control_R, state, 0);
fl_key_responder_handle_event(responder, event5, verify_response_handled,
&user_data);
// A ControlLeft down is synthesized, with an empty event.
// Reason: The ControlLeft down is synthesized to synchronize the state
@ -1198,11 +1137,10 @@ TEST(FlKeyEmbedderResponderTest,
GdkModifierType state = GDK_CONTROL_MASK;
// Send a normal event (KeyA down)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_a, kKeyCodeKeyA, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 =
fl_key_event_new(101, kPress, kKeyCodeKeyA, GDK_KEY_a, state, 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1228,11 +1166,10 @@ TEST(FlKeyEmbedderResponderTest,
state = static_cast<GdkModifierType>(0);
// Send a normal event (KeyA up)
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kRelease, GDK_KEY_A, kKeyCodeKeyA, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(102, kRelease, kKeyCodeKeyA, GDK_KEY_A, state, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1259,11 +1196,10 @@ TEST(FlKeyEmbedderResponderTest,
// Press a key with physical CapsLock and logical ControlLeft.
state = static_cast<GdkModifierType>(0);
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_Control_L, kKeyCodeCapsLock,
state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(101, kPress, kKeyCodeCapsLock,
GDK_KEY_Control_L, state, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1281,11 +1217,10 @@ TEST(FlKeyEmbedderResponderTest,
state = static_cast<GdkModifierType>(0);
// Send a normal event (KeyA down).
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_A, kKeyCodeKeyA, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event4 =
fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, state, 0);
fl_key_responder_handle_event(responder, event4, verify_response_handled,
&user_data);
// The synthesized event should have physical CapsLock and logical
// ControlLeft.
@ -1328,11 +1263,10 @@ TEST(FlKeyEmbedderResponderTest,
// Press a key with physical CapsLock and logical ControlLeft.
GdkModifierType state = static_cast<GdkModifierType>(0);
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_Control_L, kKeyCodeCapsLock,
state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(101, kPress, kKeyCodeCapsLock,
GDK_KEY_Control_L, state, 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1350,11 +1284,10 @@ TEST(FlKeyEmbedderResponderTest,
state = static_cast<GdkModifierType>(0);
// Send a normal event (KeyA down).
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_A, kKeyCodeKeyA, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(102, kPress, kKeyCodeKeyA, GDK_KEY_A, state, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
// The synthesized event should have physical CapsLock and logical
// ControlLeft.
@ -1397,11 +1330,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncLockModeOnNonSelfEvents) {
GdkModifierType state = GDK_MOD2_MASK;
// Send a normal event
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_a, kKeyCodeKeyA, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 =
fl_key_event_new(101, kPress, kKeyCodeKeyA, GDK_KEY_a, state, 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1427,11 +1359,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncLockModeOnNonSelfEvents) {
state = static_cast<GdkModifierType>(0);
// Release key A
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kRelease, GDK_KEY_A, kKeyCodeKeyA, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 =
fl_key_event_new(102, kRelease, kKeyCodeKeyA, GDK_KEY_A, state, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 4u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1472,11 +1403,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncLockModeOnNonSelfEvents) {
// Release NumLock. Since the previous event should have synthesized NumLock
// to be released, this should result in only an empty event.
g_expected_handled = true;
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(103, kRelease, GDK_KEY_Num_Lock, kKeyCodeNumLock,
state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
103, kRelease, kKeyCodeNumLock, GDK_KEY_Num_Lock, state, 0);
fl_key_responder_handle_event(responder, event3, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 1u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1505,11 +1435,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncLockModeOnSelfEvents) {
GdkModifierType state = GDK_MOD2_MASK;
// NumLock down
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kPress, GDK_KEY_Num_Lock, kKeyCodeNumLock,
state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(101, kPress, kKeyCodeNumLock,
GDK_KEY_Num_Lock, state, 0);
fl_key_responder_handle_event(responder, event1, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 3u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1543,11 +1472,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizeForDesyncLockModeOnSelfEvents) {
state = GDK_MOD2_MASK;
// NumLock up
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(102, kPress, GDK_KEY_Num_Lock, kKeyCodeNumLock,
state, kIsModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(102, kPress, kKeyCodeNumLock,
GDK_KEY_Num_Lock, state, 0);
fl_key_responder_handle_event(responder, event2, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 4u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1606,11 +1534,10 @@ TEST(FlKeyEmbedderResponderTest, SynthesizationOccursOnIgnoredEvents) {
// Send a KeyA up event, which will be ignored.
g_expected_handled = true; // The ignored event is always handled.
fl_key_responder_handle_event(
responder,
fl_key_event_new_by_mock(101, kRelease, GDK_KEY_a, kKeyCodeKeyA, state,
kIsNotModifier),
verify_response_handled, &user_data);
g_autoptr(FlKeyEvent) event =
fl_key_event_new(101, kRelease, kKeyCodeKeyA, GDK_KEY_a, state, 0);
fl_key_responder_handle_event(responder, event, verify_response_handled,
&user_data);
EXPECT_EQ(g_call_records->len, 2u);
record = FL_KEY_EMBEDDER_CALL_RECORD(g_ptr_array_index(g_call_records, 0));
@ -1659,11 +1586,10 @@ TEST(FlKeyEmbedderResponderTest, HandlesShiftAltVersusGroupNext) {
GdkModifierType 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);
g_autoptr(FlKeyEvent) event =
fl_key_event_new(now_time, is_press, keycode, keyval, state, 0);
fl_key_responder_handle_event(responder, event, verify_response_handled,
&user_data);
};
FlKeyEmbedderCallRecord* record;
@ -1768,11 +1694,10 @@ TEST(FlKeyEmbedderResponderTest, HandlesShiftAltLeftIsMetaLeft) {
GdkModifierType 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);
g_autoptr(FlKeyEvent) event =
fl_key_event_new(now_time, is_press, keycode, keyval, state, 0);
fl_key_responder_handle_event(responder, event, verify_response_handled,
&user_data);
};
FlKeyEmbedderCallRecord* record;

View File

@ -4,12 +4,59 @@
#include "flutter/shell/platform/linux/fl_key_event.h"
struct _FlKeyEvent {
GObject parent_instance;
// Time in milliseconds.
guint32 time;
// True if is a press event, otherwise a release event.
gboolean is_press;
// Hardware keycode.
guint16 keycode;
// Keyval.
guint keyval;
// Modifier state.
GdkModifierType state;
// Keyboard group.
guint8 group;
// The original event.
GdkEvent* origin;
};
G_DEFINE_TYPE(FlKeyEvent, fl_key_event, G_TYPE_OBJECT)
FlKeyEvent* fl_key_event_new(guint32 time,
gboolean is_press,
guint16 keycode,
guint keyval,
GdkModifierType state,
guint8 group) {
FlKeyEvent* self =
FL_KEY_EVENT(g_object_new(fl_key_event_get_type(), nullptr));
self->time = time;
self->is_press = is_press;
self->keycode = keycode;
self->keyval = keyval;
self->state = state;
self->group = group;
return self;
}
FlKeyEvent* fl_key_event_new_from_gdk_event(GdkEvent* event) {
g_return_val_if_fail(event != nullptr, nullptr);
FlKeyEvent* self =
FL_KEY_EVENT(g_object_new(fl_key_event_get_type(), nullptr));
GdkEventType type = gdk_event_get_event_type(event);
g_return_val_if_fail(type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE,
nullptr);
FlKeyEvent* result = g_new(FlKeyEvent, 1);
guint16 keycode = 0;
gdk_event_get_keycode(event, &keycode);
@ -18,26 +65,76 @@ FlKeyEvent* fl_key_event_new_from_gdk_event(GdkEvent* event) {
GdkModifierType state = static_cast<GdkModifierType>(0);
gdk_event_get_state(event, &state);
result->time = gdk_event_get_time(event);
result->is_press = type == GDK_KEY_PRESS;
result->keycode = keycode;
result->keyval = keyval;
result->state = state;
result->group = event->key.group;
result->origin = event;
self->time = gdk_event_get_time(event);
self->is_press = type == GDK_KEY_PRESS;
self->keycode = keycode;
self->keyval = keyval;
self->state = state;
self->group = event->key.group;
self->origin = event;
return result;
return self;
}
void fl_key_event_dispose(FlKeyEvent* event) {
if (event->origin != nullptr) {
gdk_event_free(event->origin);
}
g_free(event);
guint32 fl_key_event_get_time(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), 0);
return self->time;
}
FlKeyEvent* fl_key_event_clone(const FlKeyEvent* event) {
FlKeyEvent* new_event = g_new(FlKeyEvent, 1);
*new_event = *event;
return new_event;
gboolean fl_key_event_get_is_press(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), FALSE);
return self->is_press;
}
guint16 fl_key_event_get_keycode(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), 0);
return self->keycode;
}
guint fl_key_event_get_keyval(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), 0);
return self->keyval;
}
GdkModifierType fl_key_event_get_state(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), static_cast<GdkModifierType>(0));
return self->state;
}
guint8 fl_key_event_get_group(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), 0);
return self->group;
}
GdkEvent* fl_key_event_get_origin(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), nullptr);
return self->origin;
}
uint64_t fl_key_event_hash(FlKeyEvent* self) {
g_return_val_if_fail(FL_IS_KEY_EVENT(self), 0);
// Combine the event timestamp, the type of event, and the hardware keycode
// (scan code) of the event to come up with a unique id for this event that
// can be derived solely from the event data itself, so that we can identify
// whether or not we have seen this event already.
guint64 type =
static_cast<uint64_t>(self->is_press ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
guint64 keycode = static_cast<uint64_t>(self->keycode);
return (self->time & 0xffffffff) | ((type & 0xffff) << 32) |
((keycode & 0xffff) << 48);
}
static void fl_key_event_dispose(GObject* object) {
FlKeyEvent* self = FL_KEY_EVENT(object);
g_clear_pointer(&self->origin, gdk_event_free);
G_OBJECT_CLASS(fl_key_event_parent_class)->dispose(object);
}
static void fl_key_event_class_init(FlKeyEventClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_key_event_dispose;
}
static void fl_key_event_init(FlKeyEvent* self) {}

View File

@ -5,8 +5,12 @@
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_KEY_EVENT_H_
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_KEY_EVENT_H_
#include <stdint.h>
#include <gdk/gdk.h>
G_DECLARE_FINAL_TYPE(FlKeyEvent, fl_key_event, FL, KEY_EVENT, GObject);
/**
* FlKeyEvent:
* A struct that stores information from GdkEvent.
@ -19,42 +23,47 @@
* object, so that Flutter can create an event object in unit tests even after
* migrating to GDK 4.0 which stops supporting creating GdkEvent.
*/
typedef struct _FlKeyEvent {
// Time in milliseconds.
guint32 time;
// True if is a press event, otherwise a release event.
bool is_press;
// Hardware keycode.
guint16 keycode;
// Keyval.
guint keyval;
// Modifier state.
GdkModifierType state;
// Keyboard group.
guint8 group;
// The original event.
GdkEvent* origin;
} FlKeyEvent;
FlKeyEvent* fl_key_event_new(guint32 time,
gboolean is_press,
guint16 keycode,
guint keyval,
GdkModifierType state,
guint8 group);
/**
* fl_key_event_new_from_gdk_event:
* @event: the #GdkEvent this #FlKeyEvent is based on. The #event must be a
* #GdkEventKey, and will be destroyed by #fl_key_event_dispose.
* @event: the #GdkEvent this #FlKeyEvent is based on.
*
* Create a new #FlKeyEvent based on a #GdkEvent.
*
* Returns: a new #FlKeyEvent. Must be freed with #fl_key_event_dispose.
* Returns: a new #FlKeyEvent.
*/
FlKeyEvent* fl_key_event_new_from_gdk_event(GdkEvent* event);
/**
* fl_key_event_dispose:
* @event: the event to dispose.
*
* Properly disposes the content of #event and then the pointer.
*/
void fl_key_event_dispose(FlKeyEvent* event);
guint32 fl_key_event_get_time(FlKeyEvent* event);
FlKeyEvent* fl_key_event_clone(const FlKeyEvent* source);
gboolean fl_key_event_get_is_press(FlKeyEvent* event);
guint16 fl_key_event_get_keycode(FlKeyEvent* event);
guint fl_key_event_get_keyval(FlKeyEvent* event);
GdkModifierType fl_key_event_get_state(FlKeyEvent* event);
guint8 fl_key_event_get_group(FlKeyEvent* event);
GdkEvent* fl_key_event_get_origin(FlKeyEvent* event);
/**
* fl_key_event_hash:
* @event: an #FlKeyEvent.
*
* Calculates a unique ID for a given FlKeyEvent object to use for
* identification of responses from the framework.
*
* Returns: a hash code.
*/
uint64_t fl_key_event_hash(FlKeyEvent* event);
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEY_EVENT_H_

View File

@ -11,6 +11,7 @@
#include "flutter/shell/platform/linux/fl_key_channel_responder.h"
#include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
#include "flutter/shell/platform/linux/fl_keyboard_pending_event.h"
#include "flutter/shell/platform/linux/key_mapping.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
@ -24,12 +25,6 @@ static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
/* Declarations of private classes */
G_DECLARE_FINAL_TYPE(FlKeyboardPendingEvent,
fl_keyboard_pending_event,
FL,
KEYBOARD_PENDING_EVENT,
GObject);
#define FL_TYPE_KEYBOARD_HANDLER_USER_DATA \
fl_keyboard_handler_user_data_get_type()
G_DECLARE_FINAL_TYPE(FlKeyboardHandlerUserData,
@ -94,10 +89,10 @@ void debug_format_layout_data(std::string& debug_layout_data,
} // namespace
static uint64_t get_logical_key_from_layout(const FlKeyEvent* event,
static uint64_t get_logical_key_from_layout(FlKeyEvent* event,
const DerivedLayout& layout) {
guint8 group = event->group;
guint16 keycode = event->keycode;
guint8 group = fl_key_event_get_group(event);
guint16 keycode = fl_key_event_get_keycode(event);
if (keycode >= kLayoutSize) {
return 0;
}
@ -109,93 +104,6 @@ static uint64_t get_logical_key_from_layout(const FlKeyEvent* event,
return 0;
}
/* Define FlKeyboardPendingEvent */
/**
* FlKeyboardPendingEvent:
* A record for events that have been received by the handler, but
* dispatched to other objects, whose results have yet to return.
*
* This object is used by both the "pending_responds" list and the
* "pending_redispatches" list.
*/
struct _FlKeyboardPendingEvent {
GObject parent_instance;
// The target event.
//
// This is freed by #FlKeyboardPendingEvent if not null.
std::unique_ptr<FlKeyEvent> event;
// Self-incrementing ID attached to an event sent to the framework.
//
// Used to identify pending responds.
uint64_t sequence_id;
// The number of responders that haven't replied.
size_t unreplied;
// Whether any replied responders reported true (handled).
bool any_handled;
// A value calculated out of critical event information that can be used
// to identify redispatched events.
uint64_t hash;
};
G_DEFINE_TYPE(FlKeyboardPendingEvent, fl_keyboard_pending_event, G_TYPE_OBJECT)
static void fl_keyboard_pending_event_dispose(GObject* object) {
// Redundant, but added so that we don't get a warning about unused function
// for FL_IS_KEYBOARD_PENDING_EVENT.
g_return_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(object));
FlKeyboardPendingEvent* self = FL_KEYBOARD_PENDING_EVENT(object);
if (self->event != nullptr) {
fl_key_event_dispose(self->event.release());
}
G_OBJECT_CLASS(fl_keyboard_pending_event_parent_class)->dispose(object);
}
static void fl_keyboard_pending_event_class_init(
FlKeyboardPendingEventClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_pending_event_dispose;
}
static void fl_keyboard_pending_event_init(FlKeyboardPendingEvent* self) {}
// Calculates a unique ID for a given FlKeyEvent object to use for
// identification of responses from the framework.
static uint64_t fl_keyboard_handler_get_event_hash(FlKeyEvent* event) {
// Combine the event timestamp, the type of event, and the hardware keycode
// (scan code) of the event to come up with a unique id for this event that
// can be derived solely from the event data itself, so that we can identify
// whether or not we have seen this event already.
guint64 type =
static_cast<uint64_t>(event->is_press ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
guint64 keycode = static_cast<uint64_t>(event->keycode);
return (event->time & 0xffffffff) | ((type & 0xffff) << 32) |
((keycode & 0xffff) << 48);
}
// Create a new FlKeyboardPendingEvent by providing the target event,
// the sequence ID, and the number of responders that will reply.
//
// This will acquire the ownership of the event.
static FlKeyboardPendingEvent* fl_keyboard_pending_event_new(
std::unique_ptr<FlKeyEvent> event,
uint64_t sequence_id,
size_t to_reply) {
FlKeyboardPendingEvent* self = FL_KEYBOARD_PENDING_EVENT(
g_object_new(fl_keyboard_pending_event_get_type(), nullptr));
self->event = std::move(event);
self->sequence_id = sequence_id;
self->unreplied = to_reply;
self->any_handled = false;
self->hash = fl_keyboard_handler_get_event_hash(self->event.get());
return self;
}
/* Define FlKeyboardHandlerUserData */
/**
@ -299,60 +207,6 @@ struct _FlKeyboardHandler {
G_DEFINE_TYPE(FlKeyboardHandler, fl_keyboard_handler, G_TYPE_OBJECT);
static void fl_keyboard_handler_dispose(GObject* object);
static void fl_keyboard_handler_class_init(FlKeyboardHandlerClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_handler_dispose;
}
static void fl_keyboard_handler_init(FlKeyboardHandler* self) {
self->derived_layout = std::make_unique<DerivedLayout>();
self->keycode_to_goals =
std::make_unique<std::map<uint16_t, const LayoutGoal*>>();
self->logical_to_mandatory_goals =
std::make_unique<std::map<uint64_t, const LayoutGoal*>>();
for (const LayoutGoal& goal : layout_goals) {
(*self->keycode_to_goals)[goal.keycode] = &goal;
if (goal.mandatory) {
(*self->logical_to_mandatory_goals)[goal.logical_key] = &goal;
}
}
self->responder_list = g_ptr_array_new_with_free_func(g_object_unref);
self->pending_responds = g_ptr_array_new();
self->pending_redispatches = g_ptr_array_new_with_free_func(g_object_unref);
self->last_sequence_id = 1;
}
static void fl_keyboard_handler_dispose(GObject* object) {
FlKeyboardHandler* self = FL_KEYBOARD_HANDLER(object);
if (self->view_delegate != nullptr) {
fl_keyboard_view_delegate_subscribe_to_layout_change(self->view_delegate,
nullptr);
g_object_remove_weak_pointer(
G_OBJECT(self->view_delegate),
reinterpret_cast<gpointer*>(&(self->view_delegate)));
self->view_delegate = nullptr;
}
self->derived_layout.reset();
self->keycode_to_goals.reset();
self->logical_to_mandatory_goals.reset();
g_ptr_array_free(self->responder_list, TRUE);
g_ptr_array_set_free_func(self->pending_responds, g_object_unref);
g_ptr_array_free(self->pending_responds, TRUE);
g_ptr_array_free(self->pending_redispatches, TRUE);
G_OBJECT_CLASS(fl_keyboard_handler_parent_class)->dispose(object);
}
/* Implement FlKeyboardHandler */
// This is an exact copy of g_ptr_array_find_with_equal_func. Somehow CI
// reports that can not find symbol g_ptr_array_find_with_equal_func, despite
// the fact that it runs well locally.
@ -377,22 +231,21 @@ static gboolean g_ptr_array_find_with_equal_func1(GPtrArray* haystack,
return FALSE;
}
// Compare a #FlKeyboardPendingEvent with the given sequence_id. The needle
// should be a pointer to uint64_t sequence_id.
static gboolean compare_pending_by_sequence_id(
gconstpointer pending,
gconstpointer needle_sequence_id) {
uint64_t sequence_id = *reinterpret_cast<const uint64_t*>(needle_sequence_id);
return static_cast<const FlKeyboardPendingEvent*>(pending)->sequence_id ==
sequence_id;
// Compare a #FlKeyboardPendingEvent with the given sequence_id.
static gboolean compare_pending_by_sequence_id(gconstpointer a,
gconstpointer b) {
FlKeyboardPendingEvent* pending =
FL_KEYBOARD_PENDING_EVENT(const_cast<gpointer>(a));
uint64_t sequence_id = *reinterpret_cast<const uint64_t*>(b);
return fl_keyboard_pending_event_get_sequence_id(pending) == sequence_id;
}
// Compare a #FlKeyboardPendingEvent with the given hash. The #needle should be
// a pointer to uint64_t hash.
static gboolean compare_pending_by_hash(gconstpointer pending,
gconstpointer needle_hash) {
uint64_t hash = *reinterpret_cast<const uint64_t*>(needle_hash);
return static_cast<const FlKeyboardPendingEvent*>(pending)->hash == hash;
// Compare a #FlKeyboardPendingEvent with the given hash.
static gboolean compare_pending_by_hash(gconstpointer a, gconstpointer b) {
FlKeyboardPendingEvent* pending =
FL_KEYBOARD_PENDING_EVENT(const_cast<gpointer>(a));
uint64_t hash = *reinterpret_cast<const uint64_t*>(b);
return fl_keyboard_pending_event_get_hash(pending) == hash;
}
// Try to remove a pending event from `pending_redispatches` with the target
@ -431,22 +284,22 @@ static void responder_handle_event_callback(bool handled,
FlKeyboardPendingEvent* pending = FL_KEYBOARD_PENDING_EVENT(
g_ptr_array_index(self->pending_responds, result_index));
g_return_if_fail(pending != nullptr);
g_return_if_fail(pending->unreplied > 0);
pending->unreplied -= 1;
pending->any_handled = pending->any_handled || handled;
fl_keyboard_pending_event_mark_replied(pending, handled);
// All responders have replied.
if (pending->unreplied == 0) {
if (fl_keyboard_pending_event_is_complete(pending)) {
g_object_unref(user_data_ptr);
gpointer removed =
g_ptr_array_remove_index_fast(self->pending_responds, result_index);
g_return_if_fail(removed == pending);
bool should_redispatch = !pending->any_handled &&
!fl_keyboard_view_delegate_text_filter_key_press(
self->view_delegate, pending->event.get());
bool should_redispatch =
!fl_keyboard_pending_event_get_any_handled(pending) &&
!fl_keyboard_view_delegate_text_filter_key_press(
self->view_delegate, fl_keyboard_pending_event_get_event(pending));
if (should_redispatch) {
g_ptr_array_add(self->pending_redispatches, pending);
fl_keyboard_view_delegate_redispatch_event(self->view_delegate,
std::move(pending->event));
fl_keyboard_view_delegate_redispatch_event(
self->view_delegate,
FL_KEY_EVENT(fl_keyboard_pending_event_get_event(pending)));
} else {
g_object_unref(pending);
}
@ -466,11 +319,11 @@ static uint16_t convert_key_to_char(FlKeyboardViewDelegate* view_delegate,
// Make sure that Flutter has derived the layout for the group of the event,
// if the event contains a goal keycode.
static void guarantee_layout(FlKeyboardHandler* self, FlKeyEvent* event) {
guint8 group = event->group;
guint8 group = fl_key_event_get_group(event);
if (self->derived_layout->find(group) != self->derived_layout->end()) {
return;
}
if (self->keycode_to_goals->find(event->keycode) ==
if (self->keycode_to_goals->find(fl_key_event_get_keycode(event)) ==
self->keycode_to_goals->end()) {
return;
}
@ -541,7 +394,7 @@ static void guarantee_layout(FlKeyboardHandler* self, FlKeyEvent* event) {
}
// Returns the keyboard pressed state.
FlMethodResponse* get_keyboard_state(FlKeyboardHandler* self) {
static FlMethodResponse* get_keyboard_state(FlKeyboardHandler* self) {
g_autoptr(FlValue) result = fl_value_new_map();
GHashTable* pressing_records =
@ -582,6 +435,67 @@ static void method_call_handler(FlMethodChannel* channel,
}
}
// The loop body to dispatch an event to a responder.
static void dispatch_to_responder(gpointer responder_data,
gpointer foreach_data_ptr) {
DispatchToResponderLoopContext* context =
reinterpret_cast<DispatchToResponderLoopContext*>(foreach_data_ptr);
FlKeyResponder* responder = FL_KEY_RESPONDER(responder_data);
fl_key_responder_handle_event(
responder, context->event, responder_handle_event_callback,
context->user_data, context->specified_logical_key);
}
static void fl_keyboard_handler_dispose(GObject* object) {
FlKeyboardHandler* self = FL_KEYBOARD_HANDLER(object);
if (self->view_delegate != nullptr) {
fl_keyboard_view_delegate_subscribe_to_layout_change(self->view_delegate,
nullptr);
g_object_remove_weak_pointer(
G_OBJECT(self->view_delegate),
reinterpret_cast<gpointer*>(&(self->view_delegate)));
self->view_delegate = nullptr;
}
self->derived_layout.reset();
self->keycode_to_goals.reset();
self->logical_to_mandatory_goals.reset();
g_ptr_array_free(self->responder_list, TRUE);
g_ptr_array_set_free_func(self->pending_responds, g_object_unref);
g_ptr_array_free(self->pending_responds, TRUE);
g_ptr_array_free(self->pending_redispatches, TRUE);
G_OBJECT_CLASS(fl_keyboard_handler_parent_class)->dispose(object);
}
static void fl_keyboard_handler_class_init(FlKeyboardHandlerClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_handler_dispose;
}
static void fl_keyboard_handler_init(FlKeyboardHandler* self) {
self->derived_layout = std::make_unique<DerivedLayout>();
self->keycode_to_goals =
std::make_unique<std::map<uint16_t, const LayoutGoal*>>();
self->logical_to_mandatory_goals =
std::make_unique<std::map<uint64_t, const LayoutGoal*>>();
for (const LayoutGoal& goal : layout_goals) {
(*self->keycode_to_goals)[goal.keycode] = &goal;
if (goal.mandatory) {
(*self->logical_to_mandatory_goals)[goal.logical_key] = &goal;
}
}
self->responder_list = g_ptr_array_new_with_free_func(g_object_unref);
self->pending_responds = g_ptr_array_new();
self->pending_redispatches = g_ptr_array_new_with_free_func(g_object_unref);
self->last_sequence_id = 1;
}
FlKeyboardHandler* fl_keyboard_handler_new(
FlBinaryMessenger* messenger,
FlKeyboardViewDelegate* view_delegate) {
@ -624,17 +538,6 @@ FlKeyboardHandler* fl_keyboard_handler_new(
return self;
}
// The loop body to dispatch an event to a responder.
static void dispatch_to_responder(gpointer responder_data,
gpointer foreach_data_ptr) {
DispatchToResponderLoopContext* context =
reinterpret_cast<DispatchToResponderLoopContext*>(foreach_data_ptr);
FlKeyResponder* responder = FL_KEY_RESPONDER(responder_data);
fl_key_responder_handle_event(
responder, context->event, responder_handle_event_callback,
context->user_data, context->specified_logical_key);
}
gboolean fl_keyboard_handler_handle_event(FlKeyboardHandler* self,
FlKeyEvent* event) {
g_return_val_if_fail(FL_IS_KEYBOARD_HANDLER(self), FALSE);
@ -643,18 +546,17 @@ gboolean fl_keyboard_handler_handle_event(FlKeyboardHandler* self,
guarantee_layout(self, event);
uint64_t incoming_hash = fl_keyboard_handler_get_event_hash(event);
uint64_t incoming_hash = fl_key_event_hash(event);
if (fl_keyboard_handler_remove_redispatched(self, incoming_hash)) {
return FALSE;
}
FlKeyboardPendingEvent* pending = fl_keyboard_pending_event_new(
std::unique_ptr<FlKeyEvent>(event), ++self->last_sequence_id,
self->responder_list->len);
event, ++self->last_sequence_id, self->responder_list->len);
g_ptr_array_add(self->pending_responds, pending);
FlKeyboardHandlerUserData* user_data =
fl_keyboard_handler_user_data_new(self, pending->sequence_id);
FlKeyboardHandlerUserData* user_data = fl_keyboard_handler_user_data_new(
self, fl_keyboard_pending_event_get_sequence_id(pending));
DispatchToResponderLoopContext data{
.event = event,
.specified_logical_key =

View File

@ -71,7 +71,7 @@ typedef std::function<void(AsyncKeyCallback callback)> ChannelCallHandler;
typedef std::function<void(const FlutterKeyEvent* event,
AsyncKeyCallback callback)>
EmbedderCallHandler;
typedef std::function<void(std::unique_ptr<FlKeyEvent>)> RedispatchHandler;
typedef std::function<void(FlKeyEvent*)> RedispatchHandler;
// A type that can record all kinds of effects that the keyboard handler
// triggers.
@ -377,11 +377,11 @@ static FlBinaryMessenger* fl_mock_view_keyboard_get_messenger(
static void fl_mock_view_keyboard_redispatch_event(
FlKeyboardViewDelegate* view_delegate,
std::unique_ptr<FlKeyEvent> event) {
FlKeyEvent* event) {
FlMockViewDelegatePrivate* priv =
FL_MOCK_VIEW_DELEGATE_GET_PRIVATE(view_delegate);
if (priv->redispatch_handler) {
priv->redispatch_handler(std::move(event));
priv->redispatch_handler(event);
}
}
@ -467,23 +467,6 @@ static void fl_mock_view_set_layout(FlMockViewDelegate* self,
/***** End FlMockViewDelegate *****/
// Create a new #FlKeyEvent with the given information.
static FlKeyEvent* fl_key_event_new_by_mock(bool is_press,
guint keyval,
guint16 keycode,
GdkModifierType state,
gboolean is_modifier,
guint8 group = 0) {
FlKeyEvent* event = g_new0(FlKeyEvent, 1);
event->is_press = is_press;
event->time = 0;
event->state = state;
event->keyval = keyval;
event->group = group;
event->keycode = keycode;
return event;
}
class KeyboardTester {
public:
KeyboardTester() {
@ -502,6 +485,7 @@ class KeyboardTester {
~KeyboardTester() {
g_clear_object(&view_);
g_clear_object(&handler_);
g_clear_pointer(&redispatched_events_, g_ptr_array_unref);
}
FlKeyboardHandler* handler() { return handler_; }
@ -520,21 +504,20 @@ class KeyboardTester {
// Returns the number of events redispatched. If any result is unexpected
// (handled), return a minus number `-x` instead, where `x` is the index of
// the first unexpected redispatch.
int redispatchEventsAndClear(
std::vector<std::unique_ptr<FlKeyEvent>>& events) {
size_t event_count = events.size();
int redispatchEventsAndClear(GPtrArray* events) {
guint event_count = events->len;
int first_error = -1;
during_redispatch_ = true;
for (size_t event_id = 0; event_id < event_count; event_id += 1) {
bool handled = fl_keyboard_handler_handle_event(
handler_, events[event_id].release());
for (guint event_id = 0; event_id < event_count; event_id += 1) {
FlKeyEvent* event = FL_KEY_EVENT(g_ptr_array_index(events, event_id));
bool handled = fl_keyboard_handler_handle_event(handler_, event);
EXPECT_FALSE(handled);
if (handled) {
first_error = first_error == -1 ? event_id : first_error;
}
}
during_redispatch_ = false;
events.clear();
g_ptr_array_set_size(events, 0);
return first_error < 0 ? event_count : -first_error;
}
@ -610,12 +593,11 @@ class KeyboardTester {
fl_mock_view_set_text_filter_result(view_, response);
}
void recordRedispatchedEventsTo(
std::vector<std::unique_ptr<FlKeyEvent>>& storage) {
fl_mock_view_set_redispatch_handler(
view_, [&storage](std::unique_ptr<FlKeyEvent> key) {
storage.push_back(std::move(key));
});
void recordRedispatchedEventsTo(GPtrArray* storage) {
redispatched_events_ = g_ptr_array_ref(storage);
fl_mock_view_set_redispatch_handler(view_, [this](FlKeyEvent* key) {
g_ptr_array_add(redispatched_events_, g_object_ref(key));
});
}
void setLayout(const MockLayoutData& layout) {
@ -625,6 +607,7 @@ class KeyboardTester {
private:
FlMockViewDelegate* view_;
FlKeyboardHandler* handler_;
GPtrArray* redispatched_events_ = nullptr;
bool during_redispatch_ = false;
static gboolean _flushChannelMessagesCb(gpointer data) {
@ -642,16 +625,14 @@ TEST(FlKeyboardHandlerTest, DisposeWithUnresolvedPends) {
// Record calls so that they aren't responded.
tester.recordEmbedderCallsTo(call_records);
fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
fl_keyboard_handler_handle_event(tester.handler(), event1);
tester.respondToEmbedderCallsWith(true);
fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
fl_keyboard_handler_handle_event(tester.handler(), event2);
tester.flushChannelMessages();
@ -661,7 +642,8 @@ TEST(FlKeyboardHandlerTest, DisposeWithUnresolvedPends) {
TEST(FlKeyboardHandlerTest, SingleDelegateWithAsyncResponds) {
KeyboardTester tester;
std::vector<CallRecord> call_records;
std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
g_autoptr(GPtrArray) redispatched =
g_ptr_array_new_with_free_func(g_object_unref);
gboolean handler_handled = false;
@ -670,55 +652,56 @@ TEST(FlKeyboardHandlerTest, SingleDelegateWithAsyncResponds) {
tester.recordRedispatchedEventsTo(redispatched);
// Dispatch a key event
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event1);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_EQ(call_records.size(), 1u);
EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
kLogicalKeyA, "a", false);
call_records[0].callback(true);
tester.flushChannelMessages();
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
call_records.clear();
/// Test 2: Two events that are unhandled by the framework
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event2);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_EQ(call_records.size(), 1u);
EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
kLogicalKeyA, nullptr, false);
// Dispatch another key event
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_b, kKeyCodeKeyB,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event3 = fl_key_event_new(
0, TRUE, kKeyCodeKeyB, GDK_KEY_b, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event3);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_EQ(call_records.size(), 2u);
EXPECT_KEY_EVENT(call_records[1], kFlutterKeyEventTypeDown, kPhysicalKeyB,
kLogicalKeyB, "b", false);
// Resolve the second event first to test out-of-order response
call_records[1].callback(false);
EXPECT_EQ(redispatched.size(), 1u);
EXPECT_EQ(redispatched[0]->keyval, 0x62u);
EXPECT_EQ(redispatched->len, 1u);
EXPECT_EQ(
fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 0))),
0x62u);
call_records[0].callback(false);
tester.flushChannelMessages();
EXPECT_EQ(redispatched.size(), 2u);
EXPECT_EQ(redispatched[1]->keyval, 0x61u);
EXPECT_EQ(redispatched->len, 2u);
EXPECT_EQ(
fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 1))),
0x61u);
EXPECT_FALSE(fl_keyboard_handler_is_state_clear(tester.handler()));
call_records.clear();
@ -731,13 +714,12 @@ TEST(FlKeyboardHandlerTest, SingleDelegateWithAsyncResponds) {
/// Test 3: Dispatch the same event again to ensure that prevention from
/// redispatching only works once.
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event4 = fl_key_event_new(
0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event4);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_EQ(call_records.size(), 1u);
call_records[0].callback(true);
@ -748,40 +730,39 @@ TEST(FlKeyboardHandlerTest, SingleDelegateWithSyncResponds) {
KeyboardTester tester;
gboolean handler_handled = false;
std::vector<CallRecord> call_records;
std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
g_autoptr(GPtrArray) redispatched =
g_ptr_array_new_with_free_func(g_object_unref);
/// Test 1: One event that is handled by the framework
tester.respondToEmbedderCallsWithAndRecordsTo(true, call_records);
tester.recordRedispatchedEventsTo(redispatched);
// Dispatch a key event
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event1);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(call_records.size(), 1u);
EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeDown, kPhysicalKeyA,
kLogicalKeyA, "a", false);
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
call_records.clear();
EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
redispatched.clear();
g_ptr_array_set_size(redispatched, 0);
/// Test 2: An event unhandled by the framework
tester.respondToEmbedderCallsWithAndRecordsTo(false, call_records);
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event2);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(call_records.size(), 1u);
EXPECT_KEY_EVENT(call_records[0], kFlutterKeyEventTypeUp, kPhysicalKeyA,
kLogicalKeyA, nullptr, false);
EXPECT_EQ(redispatched.size(), 1u);
EXPECT_EQ(redispatched->len, 1u);
call_records.clear();
EXPECT_FALSE(fl_keyboard_handler_is_state_clear(tester.handler()));
@ -795,7 +776,8 @@ TEST(FlKeyboardHandlerTest, SingleDelegateWithSyncResponds) {
TEST(FlKeyboardHandlerTest, WithTwoAsyncDelegates) {
KeyboardTester tester;
std::vector<CallRecord> call_records;
std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
g_autoptr(GPtrArray) redispatched =
g_ptr_array_new_with_free_func(g_object_unref);
gboolean handler_handled = false;
@ -805,13 +787,12 @@ TEST(FlKeyboardHandlerTest, WithTwoAsyncDelegates) {
/// Test 1: One delegate responds true, the other false
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event1);
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_EQ(call_records.size(), 2u);
EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
@ -820,19 +801,18 @@ TEST(FlKeyboardHandlerTest, WithTwoAsyncDelegates) {
call_records[0].callback(true);
call_records[1].callback(false);
tester.flushChannelMessages();
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
call_records.clear();
/// Test 2: All delegates respond false
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(false, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event2);
EXPECT_EQ(handler_handled, true);
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_EQ(call_records.size(), 2u);
EXPECT_EQ(call_records[0].type, CallRecord::kKeyCallEmbedder);
@ -845,7 +825,7 @@ TEST(FlKeyboardHandlerTest, WithTwoAsyncDelegates) {
// Resolve redispatch
tester.flushChannelMessages();
EXPECT_EQ(redispatched.size(), 1u);
EXPECT_EQ(redispatched->len, 1u);
EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
EXPECT_EQ(call_records.size(), 0u);
@ -854,20 +834,20 @@ TEST(FlKeyboardHandlerTest, WithTwoAsyncDelegates) {
TEST(FlKeyboardHandlerTest, TextInputHandlerReturnsFalse) {
KeyboardTester tester;
std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
g_autoptr(GPtrArray) redispatched =
g_ptr_array_new_with_free_func(g_object_unref);
gboolean handler_handled = false;
tester.recordRedispatchedEventsTo(redispatched);
tester.respondToTextInputWith(false);
// Dispatch a key event.
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
// The event was redispatched because no one handles it.
EXPECT_EQ(redispatched.size(), 1u);
EXPECT_EQ(redispatched->len, 1u);
// Resolve redispatched event.
EXPECT_EQ(tester.redispatchEventsAndClear(redispatched), 1);
@ -877,20 +857,20 @@ TEST(FlKeyboardHandlerTest, TextInputHandlerReturnsFalse) {
TEST(FlKeyboardHandlerTest, TextInputHandlerReturnsTrue) {
KeyboardTester tester;
std::vector<std::unique_ptr<FlKeyEvent>> redispatched;
g_autoptr(GPtrArray) redispatched =
g_ptr_array_new_with_free_func(g_object_unref);
gboolean handler_handled = false;
tester.recordRedispatchedEventsTo(redispatched);
tester.respondToTextInputWith(true);
// Dispatch a key event.
handler_handled = fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
handler_handled = fl_keyboard_handler_handle_event(tester.handler(), event);
tester.flushChannelMessages();
EXPECT_EQ(handler_handled, true);
// The event was not redispatched because handler handles it.
EXPECT_EQ(redispatched.size(), 0u);
EXPECT_EQ(redispatched->len, 0u);
EXPECT_TRUE(fl_keyboard_handler_is_state_clear(tester.handler()));
}
@ -902,14 +882,12 @@ TEST(FlKeyboardHandlerTest, CorrectLogicalKeyForLayouts) {
tester.recordEmbedderCallsTo(call_records);
auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
fl_keyboard_handler_handle_event(
tester.handler(), fl_key_event_new_by_mock(
true, keyval, keycode,
static_cast<GdkModifierType>(0), false, group));
fl_keyboard_handler_handle_event(
tester.handler(), fl_key_event_new_by_mock(
false, keyval, keycode,
static_cast<GdkModifierType>(0), false, group));
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
0, TRUE, keycode, keyval, static_cast<GdkModifierType>(0), group);
fl_keyboard_handler_handle_event(tester.handler(), event1);
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
0, FALSE, keycode, keyval, static_cast<GdkModifierType>(0), group);
fl_keyboard_handler_handle_event(tester.handler(), event2);
};
/* US keyboard layout */
@ -1033,10 +1011,9 @@ TEST(FlKeyboardHandlerTest, GetPressedState) {
tester.respondToTextInputWith(true);
// Dispatch a key event.
fl_keyboard_handler_handle_event(
tester.handler(),
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA,
static_cast<GdkModifierType>(0), false));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
fl_keyboard_handler_handle_event(tester.handler(), event);
GHashTable* pressedState =
fl_keyboard_handler_get_pressed_state(tester.handler());

View File

@ -0,0 +1,105 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/linux/fl_keyboard_pending_event.h"
/**
* FlKeyboardPendingEvent:
* A record for events that have been received by the handler, but
* dispatched to other objects, whose results have yet to return.
*
* This object is used by both the "pending_responds" list and the
* "pending_redispatches" list.
*/
struct _FlKeyboardPendingEvent {
GObject parent_instance;
// The target event.
FlKeyEvent* event;
// Unique ID to identify pending responds.
uint64_t sequence_id;
// The number of responders that haven't replied.
size_t unreplied;
// Whether any replied responders reported true (handled).
bool any_handled;
// A value calculated out of critical event information that can be used
// to identify redispatched events.
uint64_t hash;
};
G_DEFINE_TYPE(FlKeyboardPendingEvent, fl_keyboard_pending_event, G_TYPE_OBJECT)
static void fl_keyboard_pending_event_dispose(GObject* object) {
FlKeyboardPendingEvent* self = FL_KEYBOARD_PENDING_EVENT(object);
g_clear_object(&self->event);
G_OBJECT_CLASS(fl_keyboard_pending_event_parent_class)->dispose(object);
}
static void fl_keyboard_pending_event_class_init(
FlKeyboardPendingEventClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_pending_event_dispose;
}
static void fl_keyboard_pending_event_init(FlKeyboardPendingEvent* self) {}
// Creates a new FlKeyboardPendingEvent by providing the target event,
// the sequence ID, and the number of responders that will reply.
FlKeyboardPendingEvent* fl_keyboard_pending_event_new(FlKeyEvent* event,
uint64_t sequence_id,
size_t to_reply) {
FlKeyboardPendingEvent* self = FL_KEYBOARD_PENDING_EVENT(
g_object_new(fl_keyboard_pending_event_get_type(), nullptr));
self->event = FL_KEY_EVENT(g_object_ref(event));
self->sequence_id = sequence_id;
self->unreplied = to_reply;
self->any_handled = false;
self->hash = fl_key_event_hash(self->event);
return self;
}
FlKeyEvent* fl_keyboard_pending_event_get_event(FlKeyboardPendingEvent* self) {
g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), nullptr);
return self->event;
}
uint64_t fl_keyboard_pending_event_get_sequence_id(
FlKeyboardPendingEvent* self) {
g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), 0);
return self->sequence_id;
}
uint64_t fl_keyboard_pending_event_get_hash(FlKeyboardPendingEvent* self) {
g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), 0);
return self->hash;
}
void fl_keyboard_pending_event_mark_replied(FlKeyboardPendingEvent* self,
gboolean handled) {
g_return_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self));
g_return_if_fail(self->unreplied > 0);
self->unreplied -= 1;
if (handled) {
self->any_handled = TRUE;
}
}
gboolean fl_keyboard_pending_event_get_any_handled(
FlKeyboardPendingEvent* self) {
g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), FALSE);
return self->any_handled;
}
gboolean fl_keyboard_pending_event_is_complete(FlKeyboardPendingEvent* self) {
g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), FALSE);
return self->unreplied == 0;
}

View File

@ -0,0 +1,39 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_PENDING_EVENT_H_
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_PENDING_EVENT_H_
#include "fl_key_event.h"
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(FlKeyboardPendingEvent,
fl_keyboard_pending_event,
FL,
KEYBOARD_PENDING_EVENT,
GObject);
FlKeyboardPendingEvent* fl_keyboard_pending_event_new(FlKeyEvent* event,
uint64_t sequence_id,
size_t to_reply);
FlKeyEvent* fl_keyboard_pending_event_get_event(FlKeyboardPendingEvent* event);
uint64_t fl_keyboard_pending_event_get_sequence_id(
FlKeyboardPendingEvent* event);
uint64_t fl_keyboard_pending_event_get_hash(FlKeyboardPendingEvent* event);
void fl_keyboard_pending_event_mark_replied(FlKeyboardPendingEvent* event,
gboolean handled);
gboolean fl_keyboard_pending_event_get_any_handled(
FlKeyboardPendingEvent* event);
gboolean fl_keyboard_pending_event_is_complete(FlKeyboardPendingEvent* event);
G_END_DECLS
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_PENDING_EVENT_H_

View File

@ -39,14 +39,13 @@ FlBinaryMessenger* fl_keyboard_view_delegate_get_messenger(
return FL_KEYBOARD_VIEW_DELEGATE_GET_IFACE(self)->get_messenger(self);
}
void fl_keyboard_view_delegate_redispatch_event(
FlKeyboardViewDelegate* self,
std::unique_ptr<FlKeyEvent> event) {
void fl_keyboard_view_delegate_redispatch_event(FlKeyboardViewDelegate* self,
FlKeyEvent* event) {
g_return_if_fail(FL_IS_KEYBOARD_VIEW_DELEGATE(self));
g_return_if_fail(event != nullptr);
return FL_KEYBOARD_VIEW_DELEGATE_GET_IFACE(self)->redispatch_event(
self, std::move(event));
return FL_KEYBOARD_VIEW_DELEGATE_GET_IFACE(self)->redispatch_event(self,
event);
}
void fl_keyboard_view_delegate_subscribe_to_layout_change(

View File

@ -46,8 +46,7 @@ struct _FlKeyboardViewDelegateInterface {
FlBinaryMessenger* (*get_messenger)(FlKeyboardViewDelegate* delegate);
void (*redispatch_event)(FlKeyboardViewDelegate* delegate,
std::unique_ptr<FlKeyEvent> event);
void (*redispatch_event)(FlKeyboardViewDelegate* delegate, FlKeyEvent* event);
void (*subscribe_to_layout_change)(FlKeyboardViewDelegate* delegate,
KeyboardLayoutNotifier notifier);
@ -103,13 +102,10 @@ FlBinaryMessenger* fl_keyboard_view_delegate_get_messenger(
*
* Handles `FlKeyboardHandler`'s request to insert a GDK event to the system for
* redispatching.
*
* The ownership of event will be transferred to the view delegate. The view
* delegate is responsible to call fl_key_event_dispose.
*/
void fl_keyboard_view_delegate_redispatch_event(
FlKeyboardViewDelegate* delegate,
std::unique_ptr<FlKeyEvent> event);
FlKeyEvent* event);
void fl_keyboard_view_delegate_subscribe_to_layout_change(
FlKeyboardViewDelegate* delegate,

View File

@ -627,7 +627,8 @@ static gboolean fl_text_input_handler_filter_keypress_default(
}
if (gtk_im_context_filter_keypress(
priv->im_context, reinterpret_cast<GdkEventKey*>(event->origin))) {
priv->im_context,
reinterpret_cast<GdkEventKey*>(fl_key_event_get_origin(event)))) {
return TRUE;
}
@ -639,11 +640,11 @@ static gboolean fl_text_input_handler_filter_keypress_default(
gboolean do_action = FALSE;
// Handle navigation keys.
gboolean changed = FALSE;
if (event->is_press) {
switch (event->keyval) {
if (fl_key_event_get_is_press(event)) {
switch (fl_key_event_get_keyval(event)) {
case GDK_KEY_End:
case GDK_KEY_KP_End:
if (event->state & GDK_SHIFT_MASK) {
if (fl_key_event_get_state(event) & GDK_SHIFT_MASK) {
changed = priv->text_model->SelectToEnd();
} else {
changed = priv->text_model->MoveCursorToEnd();
@ -662,7 +663,7 @@ static gboolean fl_text_input_handler_filter_keypress_default(
break;
case GDK_KEY_Home:
case GDK_KEY_KP_Home:
if (event->state & GDK_SHIFT_MASK) {
if (fl_key_event_get_state(event) & GDK_SHIFT_MASK) {
changed = priv->text_model->SelectToBeginning();
} else {
changed = priv->text_model->MoveCursorToBeginning();

View File

@ -184,9 +184,8 @@ static void send_key_event(FlTextInputHandler* handler,
GdkEvent* gdk_event = gdk_event_new(GDK_KEY_PRESS);
gdk_event->key.keyval = keyval;
gdk_event->key.state = state;
FlKeyEvent* key_event = fl_key_event_new_from_gdk_event(gdk_event);
g_autoptr(FlKeyEvent) key_event = fl_key_event_new_from_gdk_event(gdk_event);
fl_text_input_handler_filter_keypress(handler, key_event);
fl_key_event_dispose(key_event);
}
TEST(FlTextInputHandlerTest, MessageHandler) {

View File

@ -357,13 +357,12 @@ static void fl_view_keyboard_delegate_iface_init(
};
iface->redispatch_event = [](FlKeyboardViewDelegate* view_delegate,
std::unique_ptr<FlKeyEvent> in_event) {
FlKeyEvent* event = in_event.release();
GdkEventType event_type = gdk_event_get_event_type(event->origin);
FlKeyEvent* event) {
GdkEventType event_type =
gdk_event_get_event_type(fl_key_event_get_origin(event));
g_return_if_fail(event_type == GDK_KEY_PRESS ||
event_type == GDK_KEY_RELEASE);
gdk_event_put(event->origin);
fl_key_event_dispose(event);
gdk_event_put(fl_key_event_get_origin(event));
};
iface->subscribe_to_layout_change = [](FlKeyboardViewDelegate* view_delegate,