diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 5c0032e668..51ebc66262 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -44394,8 +44394,6 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_layout_test.cc + ../.. ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_manager.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_manager.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_manager_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 @@ -47372,8 +47370,6 @@ FILE: ../../../flutter/shell/platform/linux/fl_keyboard_layout_test.cc FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager.cc FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager.h FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager_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 diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn index ba44674089..3ef0b715bc 100644 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ b/engine/src/flutter/shell/platform/linux/BUILD.gn @@ -82,7 +82,6 @@ source_set("flutter_linux_sources") { "fl_engine_private.h", "fl_keyboard_handler.h", "fl_keyboard_manager.h", - "fl_keyboard_pending_event.h", "fl_keyboard_view_delegate.h", "fl_key_event.h", "fl_key_channel_responder.h", @@ -122,7 +121,6 @@ source_set("flutter_linux_sources") { "fl_keyboard_handler.cc", "fl_keyboard_layout.cc", "fl_keyboard_manager.cc", - "fl_keyboard_pending_event.cc", "fl_keyboard_view_delegate.cc", "fl_message_codec.cc", "fl_method_call.cc", diff --git a/engine/src/flutter/shell/platform/linux/fl_key_event.cc b/engine/src/flutter/shell/platform/linux/fl_key_event.cc index b79e1b99a3..045f685985 100644 --- a/engine/src/flutter/shell/platform/linux/fl_key_event.cc +++ b/engine/src/flutter/shell/platform/linux/fl_key_event.cc @@ -111,20 +111,6 @@ GdkEvent* fl_key_event_get_origin(FlKeyEvent* self) { 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(self->is_press ? GDK_KEY_PRESS : GDK_KEY_RELEASE); - guint64 keycode = static_cast(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); diff --git a/engine/src/flutter/shell/platform/linux/fl_key_event.h b/engine/src/flutter/shell/platform/linux/fl_key_event.h index 80b5675bfc..959607be49 100644 --- a/engine/src/flutter/shell/platform/linux/fl_key_event.h +++ b/engine/src/flutter/shell/platform/linux/fl_key_event.h @@ -55,15 +55,4 @@ 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_ diff --git a/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.cc b/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.cc index f3c88265dd..1298a03c7c 100644 --- a/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.cc +++ b/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.cc @@ -13,7 +13,6 @@ #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_layout.h" -#include "flutter/shell/platform/linux/fl_keyboard_pending_event.h" #include "flutter/shell/platform/linux/key_mapping.h" // Turn on this flag to print complete layout data when switching IMEs. The data @@ -53,6 +52,35 @@ void debug_format_layout_data(std::string& debug_layout_data, } // namespace +typedef struct { + // Event being handled. + FlKeyEvent* event; + + // TRUE if the embedder has responded. + gboolean embedder_responded; + + // TRUE if the channel has responded. + gboolean channel_responded; + + // TRUE if this event is to be redispatched; + gboolean redispatch; + + // TRUE if either the embedder of channel handled this event (or both). + gboolean handled; +} HandleEventData; + +static HandleEventData* handle_event_data_new(FlKeyEvent* event) { + HandleEventData* data = + static_cast(g_new0(HandleEventData, 1)); + data->event = FL_KEY_EVENT(g_object_ref(event)); + return data; +} + +static void handle_event_data_free(HandleEventData* data) { + g_object_unref(data->event); + g_free(data); +} + struct _FlKeyboardManager { GObject parent_instance; @@ -66,9 +94,6 @@ struct _FlKeyboardManager { FlKeyboardManagerLookupKeyHandler lookup_key_handler; gpointer lookup_key_handler_user_data; - FlKeyboardManagerRedispatchEventHandler redispatch_handler; - gpointer redispatch_handler_user_data; - FlKeyboardManagerGetPressedStateHandler get_pressed_state_handler; gpointer get_pressed_state_handler_user_data; @@ -76,16 +101,7 @@ struct _FlKeyboardManager { FlKeyChannelResponder* key_channel_responder; - // An array of #FlKeyboardPendingEvent. - // - // Its elements are *not* unreferenced when removed. When FlKeyboardManager is - // disposed, this array will be set with a free_func so that the elements are - // unreferenced when removed. - GPtrArray* pending_responds; - - // An array of #FlKeyboardPendingEvent. - // - // Its elements are unreferenced when removed. + // Events in the process of being redispatched. GPtrArray* pending_redispatches; // Record the derived layout. @@ -120,77 +136,31 @@ static void keymap_keys_changed_cb(FlKeyboardManager* self) { self->derived_layout = fl_keyboard_layout_new(); } -// 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. -static gboolean g_ptr_array_find_with_equal_func1(GPtrArray* haystack, - gconstpointer needle, - GEqualFunc equal_func, - guint* index_) { - guint i; - g_return_val_if_fail(haystack != NULL, FALSE); - if (equal_func == NULL) { - equal_func = g_direct_equal; - } - for (i = 0; i < haystack->len; i++) { - if (equal_func(g_ptr_array_index(haystack, i), needle)) { - if (index_ != NULL) { - *index_ = i; - } - return TRUE; - } - } +static void complete_handle_event(FlKeyboardManager* self, GTask* task) { + HandleEventData* data = + static_cast(g_task_get_task_data(task)); - return FALSE; -} - -// 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(a)); - uint64_t hash = *reinterpret_cast(b); - return fl_keyboard_pending_event_get_hash(pending) == hash; -} - -// The callback used by a responder after the event was dispatched. -static void responder_handle_event_callback(FlKeyboardManager* self, - FlKeyboardPendingEvent* pending) { - g_autoptr(FlKeyboardViewDelegate) view_delegate = - FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate)); - if (view_delegate == nullptr) { + // Waiting for responses. + if (!data->embedder_responded || !data->channel_responded) { return; } - // All responders have replied. - if (fl_keyboard_pending_event_is_complete(pending)) { - g_ptr_array_remove(self->pending_responds, pending); - bool should_redispatch = - !fl_keyboard_pending_event_get_any_handled(pending) && - !fl_keyboard_view_delegate_text_filter_key_press( - view_delegate, fl_keyboard_pending_event_get_event(pending)); - if (should_redispatch) { - g_ptr_array_add(self->pending_redispatches, g_object_ref(pending)); - FlKeyEvent* event = fl_keyboard_pending_event_get_event(pending); - if (self->redispatch_handler != nullptr) { - self->redispatch_handler(event, self->redispatch_handler_user_data); - } else { - 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(fl_key_event_get_origin(event)); - } + // Redispatch if needed. + if (!data->handled) { + gboolean filtered = FALSE; + g_autoptr(FlKeyboardViewDelegate) view_delegate = + FL_KEYBOARD_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate)); + if (view_delegate != nullptr) { + filtered = fl_keyboard_view_delegate_text_filter_key_press(view_delegate, + data->event); + } + data->redispatch = !filtered; + if (data->redispatch) { + g_ptr_array_add(self->pending_redispatches, g_object_ref(data->event)); } } -} -static void complete_handle_event(FlKeyboardManager* self, GTask* task) { - FlKeyboardPendingEvent* pending = - FL_KEYBOARD_PENDING_EVENT(g_task_get_task_data(task)); - - if (fl_keyboard_pending_event_is_complete(pending)) { - g_task_return_boolean(task, TRUE); - } + g_task_return_boolean(task, TRUE); } static void responder_handle_embedder_event_callback(bool handled, @@ -198,10 +168,12 @@ static void responder_handle_embedder_event_callback(bool handled, g_autoptr(GTask) task = G_TASK(user_data); FlKeyboardManager* self = FL_KEYBOARD_MANAGER(g_task_get_source_object(task)); - FlKeyboardPendingEvent* pending = - FL_KEYBOARD_PENDING_EVENT(g_task_get_task_data(task)); - fl_keyboard_pending_event_mark_embedder_replied(pending, handled); - responder_handle_event_callback(self, pending); + HandleEventData* data = + static_cast(g_task_get_task_data(G_TASK(task))); + data->embedder_responded = TRUE; + if (handled) { + data->handled = TRUE; + } complete_handle_event(self, task); } @@ -212,6 +184,10 @@ static void responder_handle_channel_event_cb(GObject* object, g_autoptr(GTask) task = G_TASK(user_data); FlKeyboardManager* self = FL_KEYBOARD_MANAGER(g_task_get_source_object(task)); + HandleEventData* data = + static_cast(g_task_get_task_data(G_TASK(task))); + data->channel_responded = TRUE; + g_autoptr(GError) error = nullptr; gboolean handled; if (!fl_key_channel_responder_handle_event_finish( @@ -221,11 +197,9 @@ static void responder_handle_channel_event_cb(GObject* object, } handled = FALSE; } - - FlKeyboardPendingEvent* pending = - FL_KEYBOARD_PENDING_EVENT(g_task_get_task_data(task)); - fl_keyboard_pending_event_mark_channel_replied(pending, handled); - responder_handle_event_callback(self, pending); + if (handled) { + data->handled = TRUE; + } complete_handle_event(self, task); } @@ -346,8 +320,6 @@ static void fl_keyboard_manager_dispose(GObject* object) { g_clear_object(&self->key_embedder_responder); g_clear_object(&self->key_channel_responder); - 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_clear_object(&self->derived_layout); if (self->keymap_keys_changed_cb_id != 0) { @@ -377,7 +349,6 @@ static void fl_keyboard_manager_init(FlKeyboardManager* self) { } } - self->pending_responds = g_ptr_array_new(); self->pending_redispatches = g_ptr_array_new_with_free_func(g_object_unref); self->keymap = gdk_keymap_get_for_display(gdk_display_get_default()); @@ -450,19 +421,21 @@ gboolean fl_keyboard_manager_is_redispatched(FlKeyboardManager* self, FlKeyEvent* event) { g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), FALSE); - uint64_t hash = fl_key_event_hash(event); - - guint result_index; - gboolean found = g_ptr_array_find_with_equal_func1( - self->pending_redispatches, static_cast(&hash), - compare_pending_by_hash, &result_index); - if (found) { - // The removed object is freed due to `pending_redispatches`'s free_func. - g_ptr_array_remove_index_fast(self->pending_redispatches, result_index); - return TRUE; - } else { - return FALSE; + guint32 time = fl_key_event_get_time(event); + gboolean is_press = !!fl_key_event_get_is_press(event); + guint16 keycode = fl_key_event_get_keycode(event); + for (guint i = 0; i < self->pending_redispatches->len; i++) { + FlKeyEvent* e = + FL_KEY_EVENT(g_ptr_array_index(self->pending_redispatches, i)); + if (fl_key_event_get_time(e) == time && + !!fl_key_event_get_is_press(e) == is_press && + fl_key_event_get_keycode(e) == keycode) { + g_ptr_array_remove_index(self->pending_redispatches, i); + return TRUE; + } } + + return FALSE; } void fl_keyboard_manager_handle_event(FlKeyboardManager* self, @@ -477,9 +450,9 @@ void fl_keyboard_manager_handle_event(FlKeyboardManager* self, guarantee_layout(self, event); - FlKeyboardPendingEvent* pending = fl_keyboard_pending_event_new(event); - g_ptr_array_add(self->pending_responds, pending); - g_task_set_task_data(task, g_object_ref(pending), g_object_unref); + g_task_set_task_data( + task, handle_event_data_new(event), + reinterpret_cast(handle_event_data_free)); uint64_t specified_logical_key = fl_keyboard_layout_get_logical_key( self->derived_layout, fl_key_event_get_group(event), @@ -492,18 +465,21 @@ void fl_keyboard_manager_handle_event(FlKeyboardManager* self, self->cancellable, responder_handle_channel_event_cb, g_object_ref(task)); } -gboolean fl_keyboard_manager_handle_event_finish(FlKeyboardManager* self, - GAsyncResult* result, - GError** error) { +gboolean fl_keyboard_manager_handle_event_finish( + FlKeyboardManager* self, + GAsyncResult* result, + FlKeyEvent** redispatched_event, + GError** error) { g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), FALSE); g_return_val_if_fail(g_task_is_valid(result, self), FALSE); - return g_task_propagate_boolean(G_TASK(result), error); -} -gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager* self) { - g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), FALSE); - return self->pending_responds->len == 0 && - self->pending_redispatches->len == 0; + HandleEventData* data = + static_cast(g_task_get_task_data(G_TASK(result))); + if (redispatched_event != nullptr && data->redispatch) { + *redispatched_event = FL_KEY_EVENT(g_object_ref(data->event)); + } + + return g_task_propagate_boolean(G_TASK(result), error); } void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager* self, @@ -543,15 +519,6 @@ void fl_keyboard_manager_set_lookup_key_handler( self->lookup_key_handler_user_data = user_data; } -void fl_keyboard_manager_set_redispatch_handler( - FlKeyboardManager* self, - FlKeyboardManagerRedispatchEventHandler redispatch_handler, - gpointer user_data) { - g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self)); - self->redispatch_handler = redispatch_handler; - self->redispatch_handler_user_data = user_data; -} - void fl_keyboard_manager_set_get_pressed_state_handler( FlKeyboardManager* self, FlKeyboardManagerGetPressedStateHandler get_pressed_state_handler, diff --git a/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.h b/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.h index 6cce31df8e..ef9c51a704 100644 --- a/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.h +++ b/engine/src/flutter/shell/platform/linux/fl_keyboard_manager.h @@ -84,6 +84,7 @@ void fl_keyboard_manager_handle_event(FlKeyboardManager* manager, * fl_keyboard_manager_handle_event_finish: * @manager: an #FlKeyboardManager. * @result: a #GAsyncResult. + * @redispatched_event: FIXME * @error: (allow-none): #GError location to store the error occurring, or %NULL * to ignore. * @@ -91,20 +92,11 @@ void fl_keyboard_manager_handle_event(FlKeyboardManager* manager, * * Returns: %TRUE on success. */ -gboolean fl_keyboard_manager_handle_event_finish(FlKeyboardManager* manager, - GAsyncResult* result, - GError** error); - -/** - * fl_keyboard_manager_is_state_clear: - * @manager: the #FlKeyboardManager self. - * - * A debug-only method that queries whether the manager's various states are - * cleared, i.e. no pending events for redispatching or for responding. - * - * Returns: true if the manager's various states are cleared. - */ -gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager* manager); +gboolean fl_keyboard_manager_handle_event_finish( + FlKeyboardManager* manager, + GAsyncResult* result, + FlKeyEvent** redispatched_event, + GError** error); /** * fl_keyboard_manager_sync_modifier_if_needed: @@ -159,20 +151,6 @@ void fl_keyboard_manager_set_lookup_key_handler( FlKeyboardManagerLookupKeyHandler lookup_key_handler, gpointer user_data); -typedef void (*FlKeyboardManagerRedispatchEventHandler)(FlKeyEvent* event, - gpointer user_data); - -/** - * fl_keyboard_manager_set_redispatch_handler: - * @manager: the #FlKeyboardManager self. - * - * Set the handler for redispatches, for testing purposes only. - */ -void fl_keyboard_manager_set_redispatch_handler( - FlKeyboardManager* manager, - FlKeyboardManagerRedispatchEventHandler redispatch_handler, - gpointer user_data); - typedef GHashTable* (*FlKeyboardManagerGetPressedStateHandler)( gpointer user_data); diff --git a/engine/src/flutter/shell/platform/linux/fl_keyboard_manager_test.cc b/engine/src/flutter/shell/platform/linux/fl_keyboard_manager_test.cc index 07afe1c2c5..2f61e635b8 100644 --- a/engine/src/flutter/shell/platform/linux/fl_keyboard_manager_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_keyboard_manager_test.cc @@ -100,7 +100,6 @@ using ::flutter::testing::keycodes::kPhysicalMetaLeft; using ::flutter::testing::keycodes::kPhysicalShiftLeft; constexpr guint16 kKeyCodeKeyA = 0x26u; -constexpr guint16 kKeyCodeKeyB = 0x38u; constexpr guint16 kKeyCodeKeyM = 0x3au; constexpr guint16 kKeyCodeDigit1 = 0x0au; constexpr guint16 kKeyCodeMinus = 0x14u; @@ -174,52 +173,30 @@ static void fl_mock_view_set_text_filter_result(FlMockViewDelegate* self, self->text_filter_result = result; } -// Block until all GdkMainLoop messages are processed, which is basically used -// only for channel messages. -static void flush_channel_messages() { - GMainLoop* loop = g_main_loop_new(nullptr, 0); - g_idle_add( - [](gpointer data) { - g_autoptr(GMainLoop) loop = reinterpret_cast(data); - g_main_loop_quit(loop); - return FALSE; - }, - loop); - g_main_loop_run(loop); -} - -// Dispatch each of the given events, expect their results to be false -// (unhandled), and clear the event array. -// -// 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 redispatch_events_and_clear(FlKeyboardManager* manager, GPtrArray* events) { - guint event_count = events->len; - for (guint event_id = 0; event_id < event_count; event_id += 1) { - FlKeyEvent* event = FL_KEY_EVENT(g_ptr_array_index(events, event_id)); - EXPECT_TRUE(fl_keyboard_manager_is_redispatched(manager, event)); - } - g_ptr_array_set_size(events, 0); - return event_count; -} - -// Make sure that the keyboard can be disposed without crashes when there are -// unresolved pending events. -TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) { +TEST(FlKeyboardManagerTest, EngineNoResponseChannelHandled) { ::testing::NiceMock mock_keymap; - g_autoptr(FlDartProject) project = fl_dart_project_new(); - g_autoptr(FlEngine) engine = fl_engine_new(project); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + // Channel handles all events. + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + g_autoptr(FlValue) return_value = fl_value_new_map(); + fl_value_set_string_take(return_value, "handled", + fl_value_new_bool(TRUE)); + return fl_value_ref(return_value); + }, + nullptr); + + g_autoptr(FlEngine) engine = + FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", + FL_BINARY_MESSENGER(messenger), nullptr)); g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); - g_autoptr(GError) error = nullptr; - EXPECT_TRUE(fl_engine_start(engine, &error)); - EXPECT_EQ(error, nullptr); - - // Don't handle first event. + // Don't handle first event - async call never completes. fl_keyboard_manager_set_send_key_event_handler( manager, [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, @@ -227,7 +204,14 @@ TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) { nullptr); g_autoptr(FlKeyEvent) event1 = fl_key_event_new( 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); - fl_keyboard_manager_handle_event(manager, event1, nullptr, nullptr, nullptr); + gboolean first_event_completed = FALSE; + fl_keyboard_manager_handle_event( + manager, event1, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + gboolean* first_event_completed = static_cast(user_data); + *first_event_completed = TRUE; + }, + &first_event_completed); // Handle second event. fl_keyboard_manager_set_send_key_event_handler( @@ -238,25 +222,28 @@ TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) { nullptr); g_autoptr(FlKeyEvent) event2 = fl_key_event_new( 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); - fl_keyboard_manager_handle_event(manager, event2, nullptr, nullptr, nullptr); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + fl_keyboard_manager_handle_event( + manager, event2, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; + EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); + }, + loop); + g_main_loop_run(loop); + + EXPECT_FALSE(first_event_completed); // Passes if the cleanup does not crash. } -TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) { +TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledSync) { ::testing::NiceMock mock_keymap; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); - fl_mock_binary_messenger_set_json_message_channel( - messenger, "flutter/keyevent", - [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, - gpointer user_data) { - g_autoptr(FlValue) return_value = fl_value_new_map(); - fl_value_set_string_take(return_value, "handled", - fl_value_new_bool(FALSE)); - return fl_value_ref(return_value); - }, - nullptr); g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); g_autoptr(FlEngine) engine = @@ -268,144 +255,49 @@ TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) { manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, nullptr); - /// Test 1: One event that is handled by the framework - g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func( - reinterpret_cast(call_record_free)); + // Handle channel and embedder calls synchronously. + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + g_autoptr(FlValue) return_value = fl_value_new_map(); + fl_value_set_string_take(return_value, "handled", + fl_value_new_bool(FALSE)); + return fl_value_ref(return_value); + }, + nullptr); fl_keyboard_manager_set_send_key_event_handler( manager, [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, - void* callback_user_data, gpointer user_data) { - GPtrArray* call_records = static_cast(user_data); - g_ptr_array_add(call_records, - call_record_new(event, callback, callback_user_data)); - }, - call_records); - g_autoptr(GPtrArray) redispatched = - g_ptr_array_new_with_free_func(g_object_unref); - fl_keyboard_manager_set_redispatch_handler( - manager, - [](FlKeyEvent* event, gpointer user_data) { - GPtrArray* redispatched = static_cast(user_data); - g_ptr_array_add(redispatched, g_object_ref(event)); - }, - redispatched); + void* callback_user_data, + gpointer user_data) { callback(true, callback_user_data); }, + nullptr); - // Dispatch a key event - g_autoptr(FlKeyEvent) event1 = fl_key_event_new( + g_autoptr(FlKeyEvent) event = fl_key_event_new( 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); fl_keyboard_manager_handle_event( - manager, event1, nullptr, + manager, event, nullptr, [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); }, - nullptr); - flush_channel_messages(); - EXPECT_EQ(redispatched->len, 0u); - EXPECT_EQ(call_records->len, 1u); - EXPECT_KEY_EVENT(static_cast(g_ptr_array_index(call_records, 0)), - kFlutterKeyEventTypeDown, kPhysicalKeyA, kLogicalKeyA, "a", - false); + loop); + g_main_loop_run(loop); - call_record_respond( - static_cast(g_ptr_array_index(call_records, 0)), true); - flush_channel_messages(); - EXPECT_EQ(redispatched->len, 0u); - EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager)); - g_ptr_array_set_size(call_records, 0); - - /// Test 2: Two events that are unhandled by the framework + // Check duplicate event is not detected as redispatched. g_autoptr(FlKeyEvent) event2 = fl_key_event_new( - 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); - fl_keyboard_manager_handle_event( - manager, event2, nullptr, - [](GObject* object, GAsyncResult* result, gpointer user_data) { - EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); - }, - nullptr); - flush_channel_messages(); - EXPECT_EQ(redispatched->len, 0u); - EXPECT_EQ(call_records->len, 1u); - EXPECT_KEY_EVENT(static_cast(g_ptr_array_index(call_records, 0)), - kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, nullptr, - false); - - // Dispatch another key event - g_autoptr(FlKeyEvent) event3 = fl_key_event_new( - 0, TRUE, kKeyCodeKeyB, GDK_KEY_b, static_cast(0), 0); - fl_keyboard_manager_handle_event( - manager, event3, nullptr, - [](GObject* object, GAsyncResult* result, gpointer user_data) { - EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); - }, - nullptr); - flush_channel_messages(); - EXPECT_EQ(redispatched->len, 0u); - EXPECT_EQ(call_records->len, 2u); - EXPECT_KEY_EVENT(static_cast(g_ptr_array_index(call_records, 1)), - kFlutterKeyEventTypeDown, kPhysicalKeyB, kLogicalKeyB, "b", - false); - - // Resolve the second event first to test out-of-order response - call_record_respond( - static_cast(g_ptr_array_index(call_records, 1)), false); - EXPECT_EQ(redispatched->len, 1u); - EXPECT_EQ( - fl_key_event_get_keyval(FL_KEY_EVENT(g_ptr_array_index(redispatched, 0))), - 0x62u); - call_record_respond( - static_cast(g_ptr_array_index(call_records, 0)), false); - flush_channel_messages(); - 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_manager_is_state_clear(manager)); - g_ptr_array_set_size(call_records, 0); - - // Resolve redispatches - EXPECT_EQ(redispatch_events_and_clear(manager, redispatched), 2); - flush_channel_messages(); - EXPECT_EQ(call_records->len, 0u); - EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager)); - - /// Test 3: Dispatch the same event again to ensure that prevention from - /// redispatching only works once. - g_autoptr(FlKeyEvent) event4 = fl_key_event_new( - 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); - fl_keyboard_manager_handle_event( - manager, event4, nullptr, - [](GObject* object, GAsyncResult* result, gpointer user_data) { - EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); - }, - nullptr); - flush_channel_messages(); - EXPECT_EQ(redispatched->len, 0u); - EXPECT_EQ(call_records->len, 1u); - - call_record_respond( - static_cast(g_ptr_array_index(call_records, 0)), true); - EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager)); + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + EXPECT_FALSE(fl_keyboard_manager_is_redispatched(manager, event2)); } -TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) { +TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) { ::testing::NiceMock mock_keymap; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); - fl_mock_binary_messenger_set_json_message_channel( - messenger, "flutter/keyevent", - [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, - gpointer user_data) { - g_autoptr(FlValue) return_value = fl_value_new_map(); - fl_value_set_string_take(return_value, "handled", - fl_value_new_bool(FALSE)); - return fl_value_ref(return_value); - }, - nullptr); g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); g_autoptr(FlEngine) engine = @@ -417,84 +309,151 @@ TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) { manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, nullptr); - /// Test 1: One event that is handled by the framework - g_autoptr(GPtrArray) call_records = g_ptr_array_new_with_free_func( - reinterpret_cast(call_record_free)); + // Handle channel and embedder calls synchronously. + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + g_autoptr(FlValue) return_value = fl_value_new_map(); + fl_value_set_string_take(return_value, "handled", + fl_value_new_bool(TRUE)); + return fl_value_ref(return_value); + }, + nullptr); fl_keyboard_manager_set_send_key_event_handler( manager, [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, - void* callback_user_data, gpointer user_data) { - GPtrArray* call_records = static_cast(user_data); - g_ptr_array_add(call_records, - call_record_new(event, callback, callback_user_data)); - callback(true, callback_user_data); - }, - call_records); - g_autoptr(GPtrArray) redispatched = - g_ptr_array_new_with_free_func(g_object_unref); - fl_keyboard_manager_set_redispatch_handler( - manager, - [](FlKeyEvent* event, gpointer user_data) { - GPtrArray* redispatched = static_cast(user_data); - g_ptr_array_add(redispatched, g_object_ref(event)); - }, - redispatched); + void* callback_user_data, + gpointer user_data) { callback(false, callback_user_data); }, + nullptr); - // Dispatch a key event - g_autoptr(FlKeyEvent) event1 = fl_key_event_new( + g_autoptr(FlKeyEvent) event = fl_key_event_new( 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); fl_keyboard_manager_handle_event( - manager, event1, nullptr, + manager, event, nullptr, [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); + }, + loop); + g_main_loop_run(loop); + + // Check duplicate event is not detected as redispatched. + g_autoptr(FlKeyEvent) event2 = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + EXPECT_FALSE(fl_keyboard_manager_is_redispatched(manager, event2)); +} + +TEST(FlKeyboardManagerTest, EngineHandledChannelHandledSync) { + ::testing::NiceMock mock_keymap; + + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + + g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); + g_autoptr(FlEngine) engine = + FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", + FL_BINARY_MESSENGER(messenger), nullptr)); + g_autoptr(FlKeyboardManager) manager = + fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); + fl_keyboard_manager_set_lookup_key_handler( + manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, + nullptr); + + // Handle channel and embedder calls synchronously. + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + g_autoptr(FlValue) return_value = fl_value_new_map(); + fl_value_set_string_take(return_value, "handled", + fl_value_new_bool(TRUE)); + return fl_value_ref(return_value); }, nullptr); - flush_channel_messages(); - EXPECT_EQ(call_records->len, 1u); - EXPECT_KEY_EVENT(static_cast(g_ptr_array_index(call_records, 0)), - kFlutterKeyEventTypeDown, kPhysicalKeyA, kLogicalKeyA, "a", - false); - EXPECT_EQ(redispatched->len, 0u); - g_ptr_array_set_size(call_records, 0); - - EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager)); - g_ptr_array_set_size(redispatched, 0); - - /// Test 2: An event unhandled by the framework fl_keyboard_manager_set_send_key_event_handler( manager, [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, - void* callback_user_data, gpointer user_data) { - GPtrArray* call_records = static_cast(user_data); - g_ptr_array_add(call_records, - call_record_new(event, callback, callback_user_data)); - callback(false, callback_user_data); - }, - call_records); - g_autoptr(FlKeyEvent) event2 = fl_key_event_new( - 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + void* callback_user_data, + gpointer user_data) { callback(true, callback_user_data); }, + nullptr); + + g_autoptr(FlKeyEvent) event = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); fl_keyboard_manager_handle_event( - manager, event2, nullptr, + manager, event, nullptr, [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); + }, + loop); + g_main_loop_run(loop); + + // Check duplicate event is not detected as redispatched. + g_autoptr(FlKeyEvent) event2 = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + EXPECT_FALSE(fl_keyboard_manager_is_redispatched(manager, event2)); +} + +TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) { + ::testing::NiceMock mock_keymap; + + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + + g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); + g_autoptr(FlEngine) engine = + FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", + FL_BINARY_MESSENGER(messenger), nullptr)); + g_autoptr(FlKeyboardManager) manager = + fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); + fl_keyboard_manager_set_lookup_key_handler( + manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, + nullptr); + + // Handle channel and embedder calls synchronously. + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + g_autoptr(FlValue) return_value = fl_value_new_map(); + fl_value_set_string_take(return_value, "handled", + fl_value_new_bool(FALSE)); + return fl_value_ref(return_value); }, nullptr); - flush_channel_messages(); - EXPECT_EQ(call_records->len, 1u); - EXPECT_KEY_EVENT(static_cast(g_ptr_array_index(call_records, 0)), - kFlutterKeyEventTypeUp, kPhysicalKeyA, kLogicalKeyA, nullptr, - false); - EXPECT_EQ(redispatched->len, 1u); - g_ptr_array_set_size(call_records, 0); + fl_keyboard_manager_set_send_key_event_handler( + manager, + [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, + void* callback_user_data, + gpointer user_data) { callback(false, callback_user_data); }, + nullptr); - EXPECT_FALSE(fl_keyboard_manager_is_state_clear(manager)); + g_autoptr(FlKeyEvent) event = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + fl_keyboard_manager_handle_event( + manager, event, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; + EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_NE(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); + }, + loop); + g_main_loop_run(loop); - EXPECT_EQ(redispatch_events_and_clear(manager, redispatched), 1); - EXPECT_EQ(call_records->len, 0u); - - EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager)); + // Check duplicate event is detected as redispatched. + g_autoptr(FlKeyEvent) event2 = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + EXPECT_TRUE(fl_keyboard_manager_is_redispatched(manager, event2)); } static void channel_respond(FlMockBinaryMessenger* messenger, @@ -505,22 +464,10 @@ static void channel_respond(FlMockBinaryMessenger* messenger, fl_mock_binary_messenger_json_message_channel_respond(messenger, task, value); } -TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) { +TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledAsync) { ::testing::NiceMock mock_keymap; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); - g_autoptr(GPtrArray) channel_calls = - g_ptr_array_new_with_free_func(g_object_unref); - fl_mock_binary_messenger_set_json_message_channel( - messenger, "flutter/keyevent", - [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, - gpointer user_data) { - GPtrArray* call_records = static_cast(user_data); - g_ptr_array_add(call_records, g_object_ref(task)); - // Will respond async - return static_cast(nullptr); - }, - channel_calls); g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); g_autoptr(FlEngine) engine = @@ -532,6 +479,19 @@ TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) { manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, nullptr); + // Handle channel and embedder calls asynchronously. + g_autoptr(GPtrArray) channel_calls = + g_ptr_array_new_with_free_func(g_object_unref); + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + GPtrArray* channel_calls = static_cast(user_data); + g_ptr_array_add(channel_calls, g_object_ref(task)); + // Will respond async + return static_cast(nullptr); + }, + channel_calls); g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func( reinterpret_cast(call_record_free)); fl_keyboard_manager_set_send_key_event_handler( @@ -543,29 +503,21 @@ TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) { call_record_new(event, callback, callback_user_data)); }, embedder_call_records); - g_autoptr(GPtrArray) redispatched = - g_ptr_array_new_with_free_func(g_object_unref); - fl_keyboard_manager_set_redispatch_handler( - manager, - [](FlKeyEvent* event, gpointer user_data) { - GPtrArray* redispatched = static_cast(user_data); - g_ptr_array_add(redispatched, g_object_ref(event)); - }, - redispatched); - /// Test 1: One delegate responds true, the other false - - g_autoptr(FlKeyEvent) event1 = fl_key_event_new( + g_autoptr(FlKeyEvent) event = fl_key_event_new( 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); fl_keyboard_manager_handle_event( - manager, event1, nullptr, + manager, event, nullptr, [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); }, - nullptr); + loop); - EXPECT_EQ(redispatched->len, 0u); EXPECT_EQ(embedder_call_records->len, 1u); EXPECT_EQ(channel_calls->len, 1u); @@ -575,25 +527,195 @@ TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) { channel_respond(messenger, static_cast(g_ptr_array_index(channel_calls, 0)), FALSE); - flush_channel_messages(); - EXPECT_EQ(redispatched->len, 0u); + g_main_loop_run(loop); +} - EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager)); - g_ptr_array_set_size(embedder_call_records, 0); - g_ptr_array_set_size(channel_calls, 0); +TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledAsync) { + ::testing::NiceMock mock_keymap; - /// Test 2: All delegates respond false - g_autoptr(FlKeyEvent) event2 = fl_key_event_new( - 0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); - fl_keyboard_manager_handle_event( - manager, event2, nullptr, - [](GObject* object, GAsyncResult* result, gpointer user_data) { - EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); - }, + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + + g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); + g_autoptr(FlEngine) engine = + FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", + FL_BINARY_MESSENGER(messenger), nullptr)); + g_autoptr(FlKeyboardManager) manager = + fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); + fl_keyboard_manager_set_lookup_key_handler( + manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, nullptr); - EXPECT_EQ(redispatched->len, 0u); + // Handle channel and embedder calls asynchronously. + g_autoptr(GPtrArray) channel_calls = + g_ptr_array_new_with_free_func(g_object_unref); + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + GPtrArray* channel_calls = static_cast(user_data); + g_ptr_array_add(channel_calls, g_object_ref(task)); + // Will respond async + return static_cast(nullptr); + }, + channel_calls); + g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func( + reinterpret_cast(call_record_free)); + fl_keyboard_manager_set_send_key_event_handler( + manager, + [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, + void* callback_user_data, gpointer user_data) { + GPtrArray* call_records = static_cast(user_data); + g_ptr_array_add(call_records, + call_record_new(event, callback, callback_user_data)); + }, + embedder_call_records); + + g_autoptr(FlKeyEvent) event = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + fl_keyboard_manager_handle_event( + manager, event, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; + EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); + }, + loop); + + EXPECT_EQ(embedder_call_records->len, 1u); + EXPECT_EQ(channel_calls->len, 1u); + + call_record_respond( + static_cast(g_ptr_array_index(embedder_call_records, 0)), + false); + channel_respond(messenger, + static_cast(g_ptr_array_index(channel_calls, 0)), + TRUE); + g_main_loop_run(loop); +} + +TEST(FlKeyboardManagerTest, EngineHandledChannelHandledAsync) { + ::testing::NiceMock mock_keymap; + + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + + g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); + g_autoptr(FlEngine) engine = + FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", + FL_BINARY_MESSENGER(messenger), nullptr)); + g_autoptr(FlKeyboardManager) manager = + fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); + fl_keyboard_manager_set_lookup_key_handler( + manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, + nullptr); + + // Handle channel and embedder calls asynchronously. + g_autoptr(GPtrArray) channel_calls = + g_ptr_array_new_with_free_func(g_object_unref); + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + GPtrArray* channel_calls = static_cast(user_data); + g_ptr_array_add(channel_calls, g_object_ref(task)); + // Will respond async + return static_cast(nullptr); + }, + channel_calls); + g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func( + reinterpret_cast(call_record_free)); + fl_keyboard_manager_set_send_key_event_handler( + manager, + [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, + void* callback_user_data, gpointer user_data) { + GPtrArray* call_records = static_cast(user_data); + g_ptr_array_add(call_records, + call_record_new(event, callback, callback_user_data)); + }, + embedder_call_records); + + g_autoptr(FlKeyEvent) event = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + fl_keyboard_manager_handle_event( + manager, event, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; + EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); + }, + loop); + + EXPECT_EQ(embedder_call_records->len, 1u); + EXPECT_EQ(channel_calls->len, 1u); + + call_record_respond( + static_cast(g_ptr_array_index(embedder_call_records, 0)), + true); + channel_respond(messenger, + static_cast(g_ptr_array_index(channel_calls, 0)), + TRUE); + g_main_loop_run(loop); +} + +TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledAsync) { + ::testing::NiceMock mock_keymap; + + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + + g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); + g_autoptr(FlEngine) engine = + FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", + FL_BINARY_MESSENGER(messenger), nullptr)); + g_autoptr(FlKeyboardManager) manager = + fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); + fl_keyboard_manager_set_lookup_key_handler( + manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; }, + nullptr); + + // Handle channel and embedder calls asynchronously. + g_autoptr(GPtrArray) channel_calls = + g_ptr_array_new_with_free_func(g_object_unref); + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + GPtrArray* channel_calls = static_cast(user_data); + g_ptr_array_add(channel_calls, g_object_ref(task)); + // Will respond async + return static_cast(nullptr); + }, + channel_calls); + g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func( + reinterpret_cast(call_record_free)); + fl_keyboard_manager_set_send_key_event_handler( + manager, + [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, + void* callback_user_data, gpointer user_data) { + GPtrArray* call_records = static_cast(user_data); + g_ptr_array_add(call_records, + call_record_new(event, callback, callback_user_data)); + }, + embedder_call_records); + + g_autoptr(FlKeyEvent) event = fl_key_event_new( + 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + fl_keyboard_manager_handle_event( + manager, event, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; + EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_NE(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); + }, + loop); + EXPECT_EQ(embedder_call_records->len, 1u); EXPECT_EQ(channel_calls->len, 1u); @@ -603,34 +725,13 @@ TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) { channel_respond(messenger, static_cast(g_ptr_array_index(channel_calls, 0)), FALSE); - - g_ptr_array_set_size(embedder_call_records, 0); - g_ptr_array_set_size(channel_calls, 0); - - // Resolve redispatch - flush_channel_messages(); - EXPECT_EQ(redispatched->len, 1u); - EXPECT_EQ(redispatch_events_and_clear(manager, redispatched), 1); - EXPECT_EQ(embedder_call_records->len, 0u); - EXPECT_EQ(channel_calls->len, 0u); - - EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager)); + g_main_loop_run(loop); } -TEST(FlKeyboardManagerTest, TextInputHandlerReturnsFalse) { +TEST(FlKeyboardManagerTest, TextFilter) { ::testing::NiceMock mock_keymap; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); - fl_mock_binary_messenger_set_json_message_channel( - messenger, "flutter/keyevent", - [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, - gpointer user_data) { - g_autoptr(FlValue) return_value = fl_value_new_map(); - fl_value_set_string_take(return_value, "handled", - fl_value_new_bool(FALSE)); - return fl_value_ref(return_value); - }, - nullptr); g_autoptr(FlEngine) engine = FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_BINARY_MESSENGER(messenger), nullptr)); @@ -638,67 +739,18 @@ TEST(FlKeyboardManagerTest, TextInputHandlerReturnsFalse) { g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); - // Dispatch a key event. - fl_mock_view_set_text_filter_result(view, FALSE); - gboolean redispatched = FALSE; - fl_keyboard_manager_set_redispatch_handler( - manager, - [](FlKeyEvent* event, gpointer user_data) { - gboolean* redispatched = static_cast(user_data); - *redispatched = TRUE; - }, - &redispatched); - fl_keyboard_manager_set_send_key_event_handler( - manager, - [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, - void* callback_user_data, - gpointer user_data) { callback(false, callback_user_data); }, - nullptr); - g_autoptr(FlKeyEvent) event = fl_key_event_new( - 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); - fl_keyboard_manager_handle_event( - manager, event, nullptr, - [](GObject* object, GAsyncResult* result, gpointer user_data) { - EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); - }, - nullptr); - flush_channel_messages(); - // The event was redispatched because no one handles it. - EXPECT_TRUE(redispatched); -} - -TEST(FlKeyboardManagerTest, TextInputHandlerReturnsTrue) { - ::testing::NiceMock mock_keymap; - - g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); - fl_mock_binary_messenger_set_json_message_channel( - messenger, "flutter/keyevent", - [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, - gpointer user_data) { - g_autoptr(FlValue) return_value = fl_value_new_map(); - fl_value_set_string_take(return_value, "handled", - fl_value_new_bool(FALSE)); - return fl_value_ref(return_value); - }, - nullptr); - g_autoptr(FlEngine) engine = - FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", - FL_BINARY_MESSENGER(messenger), nullptr)); - g_autoptr(FlMockViewDelegate) view = fl_mock_view_delegate_new(); - g_autoptr(FlKeyboardManager) manager = - fl_keyboard_manager_new(engine, FL_KEYBOARD_VIEW_DELEGATE(view)); - - // Dispatch a key event. + // Event would have been redispatched, but is filtered. fl_mock_view_set_text_filter_result(view, TRUE); - gboolean redispatched = FALSE; - fl_keyboard_manager_set_redispatch_handler( - manager, - [](FlKeyEvent* event, gpointer user_data) { - gboolean* redispatched = static_cast(user_data); - *redispatched = TRUE; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", + [](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message, + gpointer user_data) { + g_autoptr(FlValue) return_value = fl_value_new_map(); + fl_value_set_string_take(return_value, "handled", + fl_value_new_bool(FALSE)); + return fl_value_ref(return_value); }, - &redispatched); + nullptr); fl_keyboard_manager_set_send_key_event_handler( manager, [](const FlutterKeyEvent* event, FlutterKeyEventCallback callback, @@ -707,16 +759,18 @@ TEST(FlKeyboardManagerTest, TextInputHandlerReturnsTrue) { nullptr); g_autoptr(FlKeyEvent) event = fl_key_event_new( 0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast(0), 0); + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); fl_keyboard_manager_handle_event( manager, event, nullptr, [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); + EXPECT_EQ(redispatched_event, nullptr); + g_main_loop_quit(static_cast(user_data)); }, - nullptr); - flush_channel_messages(); - // The event was not redispatched because handler handles it. - EXPECT_FALSE(redispatched); + loop); + g_main_loop_run(loop); } TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) { @@ -927,8 +981,9 @@ TEST(FlKeyboardManagerTest, GetPressedState) { fl_keyboard_manager_handle_event( manager, event, nullptr, [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatched_event = nullptr; EXPECT_TRUE(fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, nullptr)); + FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr)); }, nullptr); diff --git a/engine/src/flutter/shell/platform/linux/fl_keyboard_pending_event.cc b/engine/src/flutter/shell/platform/linux/fl_keyboard_pending_event.cc deleted file mode 100644 index 5a24ae85ac..0000000000 --- a/engine/src/flutter/shell/platform/linux/fl_keyboard_pending_event.cc +++ /dev/null @@ -1,103 +0,0 @@ -// 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; - - // True if the embedder responder has replied. - bool embedder_replied; - - // True if the channel responder has replied. - bool channel_replied; - - // True if this event was handled by the embedder responder. - bool embedder_handled; - - // True if this event was handled by the channel responder. - bool channel_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) { - 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->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_hash(FlKeyboardPendingEvent* self) { - g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), 0); - return self->hash; -} - -void fl_keyboard_pending_event_mark_embedder_replied( - FlKeyboardPendingEvent* self, - gboolean handled) { - g_return_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self)); - self->embedder_replied = true; - self->embedder_handled = handled; -} - -void fl_keyboard_pending_event_mark_channel_replied( - FlKeyboardPendingEvent* self, - gboolean handled) { - g_return_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self)); - self->channel_replied = true; - self->channel_handled = handled; -} - -gboolean fl_keyboard_pending_event_get_any_handled( - FlKeyboardPendingEvent* self) { - g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), FALSE); - return self->embedder_handled || self->channel_handled; -} - -gboolean fl_keyboard_pending_event_is_complete(FlKeyboardPendingEvent* self) { - g_return_val_if_fail(FL_IS_KEYBOARD_PENDING_EVENT(self), FALSE); - return self->embedder_replied && self->channel_replied; -} diff --git a/engine/src/flutter/shell/platform/linux/fl_keyboard_pending_event.h b/engine/src/flutter/shell/platform/linux/fl_keyboard_pending_event.h deleted file mode 100644 index 31827c3c76..0000000000 --- a/engine/src/flutter/shell/platform/linux/fl_keyboard_pending_event.h +++ /dev/null @@ -1,39 +0,0 @@ -// 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); - -FlKeyEvent* fl_keyboard_pending_event_get_event(FlKeyboardPendingEvent* event); - -uint64_t fl_keyboard_pending_event_get_hash(FlKeyboardPendingEvent* event); - -void fl_keyboard_pending_event_mark_embedder_replied( - FlKeyboardPendingEvent* event, - gboolean handled); - -void fl_keyboard_pending_event_mark_channel_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_ diff --git a/engine/src/flutter/shell/platform/linux/fl_view.cc b/engine/src/flutter/shell/platform/linux/fl_view.cc index 4d8dc4ef1f..ea634d4cea 100644 --- a/engine/src/flutter/shell/platform/linux/fl_view.cc +++ b/engine/src/flutter/shell/platform/linux/fl_view.cc @@ -684,13 +684,19 @@ static gboolean handle_key_event(FlView* self, GdkEventKey* key_event) { fl_keyboard_manager_handle_event( self->keyboard_manager, event, self->cancellable, [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(FlKeyEvent) redispatch_event = nullptr; g_autoptr(GError) error = nullptr; if (!fl_keyboard_manager_handle_event_finish( - FL_KEYBOARD_MANAGER(object), result, &error)) { + FL_KEYBOARD_MANAGER(object), result, &redispatch_event, + &error)) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warning("Failed to handle key event: %s", error->message); } } + + if (redispatch_event != nullptr) { + gdk_event_put(fl_key_event_get_origin(redispatch_event)); + } }, nullptr);