Skip to content

Commit

Permalink
Make fl_key_channel_responder_handle_event async (flutter#56959)
Browse files Browse the repository at this point in the history
Replace a callback with a more standard async call
  • Loading branch information
robert-ancell authored Dec 9, 2024
1 parent a8c08bc commit 37a7a14
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 125 deletions.
117 changes: 32 additions & 85 deletions shell/platform/linux/fl_key_channel_responder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,71 +9,6 @@

#include "flutter/shell/platform/linux/fl_key_event_channel.h"

/* Declare and define FlKeyChannelUserData */

/**
* FlKeyChannelUserData:
* The user_data used when #FlKeyChannelResponder sends message through the
* channel.
*/
G_DECLARE_FINAL_TYPE(FlKeyChannelUserData,
fl_key_channel_user_data,
FL,
KEY_CHANNEL_USER_DATA,
GObject);

struct _FlKeyChannelUserData {
GObject parent_instance;

// The current responder.
GWeakRef responder;
// The callback provided by the caller #FlKeyboardHandler.
FlKeyChannelResponderAsyncCallback callback;
// The user_data provided by the caller #FlKeyboardHandler.
gpointer user_data;
};

// Definition for FlKeyChannelUserData private class.
G_DEFINE_TYPE(FlKeyChannelUserData, fl_key_channel_user_data, G_TYPE_OBJECT)

// Dispose method for FlKeyChannelUserData private class.
static void fl_key_channel_user_data_dispose(GObject* object) {
g_return_if_fail(FL_IS_KEY_CHANNEL_USER_DATA(object));
FlKeyChannelUserData* self = FL_KEY_CHANNEL_USER_DATA(object);

g_weak_ref_clear(&self->responder);

G_OBJECT_CLASS(fl_key_channel_user_data_parent_class)->dispose(object);
}

// Class initialization method for FlKeyChannelUserData private class.
static void fl_key_channel_user_data_class_init(
FlKeyChannelUserDataClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_key_channel_user_data_dispose;
}

// Instance initialization method for FlKeyChannelUserData private class.
static void fl_key_channel_user_data_init(FlKeyChannelUserData* self) {}

// Creates a new FlKeyChannelUserData private class with all information.
//
// The callback and the user_data might be nullptr.
static FlKeyChannelUserData* fl_key_channel_user_data_new(
FlKeyChannelResponder* responder,
FlKeyChannelResponderAsyncCallback callback,
gpointer user_data) {
FlKeyChannelUserData* self = FL_KEY_CHANNEL_USER_DATA(
g_object_new(fl_key_channel_user_data_get_type(), nullptr));

g_weak_ref_init(&self->responder, responder);
self->callback = callback;
self->user_data = user_data;
return self;
}

/* Define FlKeyChannelResponder */

// Definition of the FlKeyChannelResponder GObject class.
struct _FlKeyChannelResponder {
GObject parent_instance;

Expand All @@ -87,20 +22,17 @@ G_DEFINE_TYPE(FlKeyChannelResponder, fl_key_channel_responder, G_TYPE_OBJECT)
static void handle_response(GObject* object,
GAsyncResult* result,
gpointer user_data) {
g_autoptr(FlKeyChannelUserData) data = FL_KEY_CHANNEL_USER_DATA(user_data);

g_autoptr(FlKeyChannelResponder) self =
FL_KEY_CHANNEL_RESPONDER(g_weak_ref_get(&data->responder));
if (self == nullptr) {
return;
}
g_autoptr(GTask) task = G_TASK(user_data);

gboolean handled = FALSE;
g_autoptr(GError) error = nullptr;
if (!fl_key_event_channel_send_finish(object, result, &handled, &error)) {
g_warning("Unable to retrieve framework response: %s", error->message);
}
data->callback(handled, data->user_data);

gboolean* return_value = g_new0(gboolean, 1);
*return_value = handled;
g_task_return_pointer(task, return_value, g_free);
}

// Disposes of an FlKeyChannelResponder instance.
Expand Down Expand Up @@ -136,12 +68,12 @@ FlKeyChannelResponder* fl_key_channel_responder_new(
return self;
}

void fl_key_channel_responder_handle_event(
FlKeyChannelResponder* self,
FlKeyEvent* event,
uint64_t specified_logical_key,
FlKeyChannelResponderAsyncCallback callback,
gpointer user_data) {
void fl_key_channel_responder_handle_event(FlKeyChannelResponder* self,
FlKeyEvent* event,
uint64_t specified_logical_key,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data) {
g_return_if_fail(event != nullptr);
g_return_if_fail(callback != nullptr);

Expand Down Expand Up @@ -198,10 +130,25 @@ void fl_key_channel_responder_handle_event(
state |= (shift_lock_pressed || caps_lock_pressed) ? GDK_LOCK_MASK : 0x0;
state |= num_lock_pressed ? GDK_MOD2_MASK : 0x0;

FlKeyChannelUserData* data =
fl_key_channel_user_data_new(self, callback, user_data);
fl_key_event_channel_send(self->channel, type, scan_code,
fl_key_event_get_keyval(event), state,
unicode_scalar_values, specified_logical_key,
nullptr, handle_response, data);
fl_key_event_channel_send(
self->channel, type, scan_code, fl_key_event_get_keyval(event), state,
unicode_scalar_values, specified_logical_key, nullptr, handle_response,
g_task_new(self, cancellable, callback, user_data));
}

gboolean fl_key_channel_responder_handle_event_finish(
FlKeyChannelResponder* self,
GAsyncResult* result,
gboolean* handled,
GError** error) {
g_return_val_if_fail(g_task_is_valid(result, self), FALSE);

g_autofree gboolean* return_value =
static_cast<gboolean*>(g_task_propagate_pointer(G_TASK(result), error));
if (return_value == nullptr) {
return FALSE;
}

*handled = *return_value;
return TRUE;
}
52 changes: 28 additions & 24 deletions shell/platform/linux/fl_key_channel_responder.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@ G_DECLARE_FINAL_TYPE(FlKeyChannelResponder,
KEY_CHANNEL_RESPONDER,
GObject);

/**
* FlKeyChannelResponderAsyncCallback:
* @event: whether the event has been handled.
* @user_data: the same value as user_data sent by
* #fl_key_responder_handle_event.
*
* The signature for a callback with which a #FlKeyChannelResponder
*asynchronously reports whether the responder handles the event.
**/
typedef void (*FlKeyChannelResponderAsyncCallback)(bool handled,
gpointer user_data);

/**
* FlKeyChannelResponder:
*
Expand All @@ -54,21 +42,37 @@ FlKeyChannelResponder* fl_key_channel_responder_new(
* @event: the event to be handled. Must not be null. The object is managed by
* callee and must not be assumed available after this function.
* @specified_logical_key:
* @callback: the callback to report the result. It should be called exactly
* once. Must not be null.
* @user_data: a value that will be sent back in the callback. Can be null.
* @cancellable: (allow-none): a #GCancellable or %NULL.
* @callback: (scope async): a #GAsyncReadyCallback to call when the event has
* been processed.
* @user_data: (closure): user data to pass to @callback.
*
* Let the responder handle an event.
*/
void fl_key_channel_responder_handle_event(FlKeyChannelResponder* responder,
FlKeyEvent* event,
uint64_t specified_logical_key,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data);

/**
* fl_key_channel_responder_handle_event_finish:
* @responder: an #FlKeyChannelResponder.
* @result: a #GAsyncResult.
* @handled: location to write if this event was handled by the platform.
* @error: (allow-none): #GError location to store the error occurring, or %NULL
* to ignore.
*
* Completes request started with fl_key_channel_responder_handle_event().
*
* Let the responder handle an event, expecting the responder to report
* whether to handle the event. The result will be reported by invoking
* `callback` exactly once, which might happen after
* `fl_key_channel_responder_handle_event` or during it.
* Returns %TRUE on success.
*/
void fl_key_channel_responder_handle_event(
gboolean fl_key_channel_responder_handle_event_finish(
FlKeyChannelResponder* responder,
FlKeyEvent* event,
uint64_t specified_logical_key,
FlKeyChannelResponderAsyncCallback callback,
gpointer user_data);
GAsyncResult* result,
gboolean* handled,
GError** error);

G_END_DECLS

Expand Down
42 changes: 30 additions & 12 deletions shell/platform/linux/fl_key_channel_responder_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ TEST(FlKeyChannelResponderTest, SendKeyEvent) {
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(
responder, event1, 0,
[](bool handled, gpointer user_data) {
responder, event1, 0, nullptr,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
gboolean handled;
EXPECT_TRUE(fl_key_channel_responder_handle_event_finish(
FL_KEY_CHANNEL_RESPONDER(object), result, &handled, nullptr));
EXPECT_FALSE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
Expand All @@ -73,8 +76,11 @@ TEST(FlKeyChannelResponderTest, SendKeyEvent) {
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
23456, FALSE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(
responder, event2, 0,
[](bool handled, gpointer user_data) {
responder, event2, 0, nullptr,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
gboolean handled;
EXPECT_TRUE(fl_key_channel_responder_handle_event_finish(
FL_KEY_CHANNEL_RESPONDER(object), result, &handled, nullptr));
EXPECT_FALSE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
Expand All @@ -97,8 +103,11 @@ void test_lock_event(guint key_code,
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
12345, TRUE, 0x04, key_code, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(
responder, event1, 0,
[](bool handled, gpointer user_data) {
responder, event1, 0, nullptr,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
gboolean handled;
EXPECT_TRUE(fl_key_channel_responder_handle_event_finish(
FL_KEY_CHANNEL_RESPONDER(object), result, &handled, nullptr));
EXPECT_FALSE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
Expand All @@ -109,8 +118,11 @@ void test_lock_event(guint key_code,
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
12346, FALSE, 0x04, key_code, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(
responder, event2, 0,
[](bool handled, gpointer user_data) {
responder, event2, 0, nullptr,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
gboolean handled;
EXPECT_TRUE(fl_key_channel_responder_handle_event_finish(
FL_KEY_CHANNEL_RESPONDER(object), result, &handled, nullptr));
EXPECT_FALSE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
Expand Down Expand Up @@ -162,8 +174,11 @@ TEST(FlKeyChannelResponderTest, TestKeyEventHandledByFramework) {
g_autoptr(FlKeyEvent) event = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(
responder, event, 0,
[](bool handled, gpointer user_data) {
responder, event, 0, nullptr,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
gboolean handled;
EXPECT_TRUE(fl_key_channel_responder_handle_event_finish(
FL_KEY_CHANNEL_RESPONDER(object), result, &handled, nullptr));
EXPECT_TRUE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
Expand All @@ -189,8 +204,11 @@ TEST(FlKeyChannelResponderTest, UseSpecifiedLogicalKey) {
g_autoptr(FlKeyEvent) event = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(
responder, event, 888,
[](bool handled, gpointer user_data) {
responder, event, 888, nullptr,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
gboolean handled;
EXPECT_TRUE(fl_key_channel_responder_handle_event_finish(
FL_KEY_CHANNEL_RESPONDER(object), result, &handled, nullptr));
EXPECT_TRUE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
Expand Down
25 changes: 21 additions & 4 deletions shell/platform/linux/fl_keyboard_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ struct _FlKeyboardManager {
GdkKeymap* keymap;
gulong keymap_keys_changed_cb_id; // Signal connection ID for
// keymap-keys-changed

GCancellable* cancellable;
};

G_DEFINE_TYPE(FlKeyboardManager, fl_keyboard_manager, G_TYPE_OBJECT);
Expand Down Expand Up @@ -274,18 +276,29 @@ static void responder_handle_embedder_event_callback(bool handled,
responder_handle_event_callback(self, data->pending);
}

static void responder_handle_channel_event_callback(bool handled,
gpointer user_data) {
static void responder_handle_channel_event_cb(GObject* object,
GAsyncResult* result,
gpointer user_data) {
g_autoptr(FlKeyboardManagerData) data = FL_KEYBOARD_MANAGER_DATA(user_data);

fl_keyboard_pending_event_mark_channel_replied(data->pending, handled);
g_autoptr(GError) error = nullptr;
gboolean handled;
if (!fl_key_channel_responder_handle_event_finish(
FL_KEY_CHANNEL_RESPONDER(object), result, &handled, &error)) {
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warning("Failed to handle key event in platform: %s", error->message);
}
return;
}

g_autoptr(FlKeyboardManager) self =
FL_KEYBOARD_MANAGER(g_weak_ref_get(&data->manager));
if (self == nullptr) {
return;
}

fl_keyboard_pending_event_mark_channel_replied(data->pending, handled);

responder_handle_event_callback(self, data->pending);
}

Expand Down Expand Up @@ -395,6 +408,8 @@ static void guarantee_layout(FlKeyboardManager* self, FlKeyEvent* event) {
static void fl_keyboard_manager_dispose(GObject* object) {
FlKeyboardManager* self = FL_KEYBOARD_MANAGER(object);

g_cancellable_cancel(self->cancellable);

g_weak_ref_clear(&self->engine);
g_weak_ref_clear(&self->view_delegate);

Expand All @@ -411,6 +426,7 @@ static void fl_keyboard_manager_dispose(GObject* object) {
g_signal_handler_disconnect(self->keymap, self->keymap_keys_changed_cb_id);
self->keymap_keys_changed_cb_id = 0;
}
g_clear_object(&self->cancellable);

G_OBJECT_CLASS(fl_keyboard_manager_parent_class)->dispose(object);
}
Expand Down Expand Up @@ -439,6 +455,7 @@ static void fl_keyboard_manager_init(FlKeyboardManager* self) {
self->keymap = gdk_keymap_get_for_display(gdk_display_get_default());
self->keymap_keys_changed_cb_id = g_signal_connect_swapped(
self->keymap, "keys-changed", G_CALLBACK(keymap_keys_changed_cb), self);
self->cancellable = g_cancellable_new();
}

FlKeyboardManager* fl_keyboard_manager_new(
Expand Down Expand Up @@ -499,7 +516,7 @@ gboolean fl_keyboard_manager_handle_event(FlKeyboardManager* self,
responder_handle_embedder_event_callback, g_object_ref(data));
fl_key_channel_responder_handle_event(
self->key_channel_responder, event, specified_logical_key,
responder_handle_channel_event_callback, g_object_ref(data));
self->cancellable, responder_handle_channel_event_cb, g_object_ref(data));

return TRUE;
}
Expand Down

0 comments on commit 37a7a14

Please sign in to comment.