Skip to content

Commit

Permalink
Make fl_keyboard_manager_handle_event async (#161637)
Browse files Browse the repository at this point in the history
In this way we can tell when this call completes, which should help
testing and refactoring.
  • Loading branch information
robert-ancell authored Jan 16, 2025
1 parent 82a58ee commit a6045c9
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 137 deletions.
169 changes: 60 additions & 109 deletions engine/src/flutter/shell/platform/linux/fl_keyboard_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,6 @@
// is used in unit tests.
#define DEBUG_PRINT_LAYOUT

/* Declarations of private classes */

G_DECLARE_FINAL_TYPE(FlKeyboardManagerData,
fl_keyboard_manager_data,
FL,
KEYBOARD_MANAGER_DATA,
GObject);

/* End declarations */

namespace {

static bool is_eascii(uint16_t character) {
Expand Down Expand Up @@ -63,55 +53,6 @@ void debug_format_layout_data(std::string& debug_layout_data,

} // namespace

/* Define FlKeyboardManagerData */

/**
* FlKeyboardManagerData:
* The user_data used when #FlKeyboardManager sends event to
* responders.
*/

struct _FlKeyboardManagerData {
GObject parent_instance;

// The owner manager.
GWeakRef manager;

FlKeyboardPendingEvent* pending;
};

G_DEFINE_TYPE(FlKeyboardManagerData, fl_keyboard_manager_data, G_TYPE_OBJECT)

static void fl_keyboard_manager_data_dispose(GObject* object) {
g_return_if_fail(FL_IS_KEYBOARD_MANAGER_DATA(object));
FlKeyboardManagerData* self = FL_KEYBOARD_MANAGER_DATA(object);

g_weak_ref_clear(&self->manager);

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

static void fl_keyboard_manager_data_class_init(
FlKeyboardManagerDataClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_manager_data_dispose;
}

static void fl_keyboard_manager_data_init(FlKeyboardManagerData* self) {}

// Creates a new FlKeyboardManagerData private class with all information.
static FlKeyboardManagerData* fl_keyboard_manager_data_new(
FlKeyboardManager* manager,
FlKeyboardPendingEvent* pending) {
FlKeyboardManagerData* self = FL_KEYBOARD_MANAGER_DATA(
g_object_new(fl_keyboard_manager_data_get_type(), nullptr));

g_weak_ref_init(&self->manager, manager);
self->pending = FL_KEYBOARD_PENDING_EVENT(g_object_ref(pending));
return self;
}

/* Define FlKeyboardManager */

struct _FlKeyboardManager {
GObject parent_instance;

Expand Down Expand Up @@ -211,25 +152,6 @@ static gboolean compare_pending_by_hash(gconstpointer a, gconstpointer b) {
return fl_keyboard_pending_event_get_hash(pending) == hash;
}

// Try to remove a pending event from `pending_redispatches` with the target
// hash.
//
// Returns true if the event is found and removed.
static bool fl_keyboard_manager_remove_redispatched(FlKeyboardManager* self,
uint64_t hash) {
guint result_index;
gboolean found = g_ptr_array_find_with_equal_func1(
self->pending_redispatches, static_cast<const uint64_t*>(&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;
}
}

// The callback used by a responder after the event was dispatched.
static void responder_handle_event_callback(FlKeyboardManager* self,
FlKeyboardPendingEvent* pending) {
Expand Down Expand Up @@ -262,25 +184,33 @@ static void responder_handle_event_callback(FlKeyboardManager* self,
}
}

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);
}
}

static void responder_handle_embedder_event_callback(bool handled,
gpointer user_data) {
g_autoptr(FlKeyboardManagerData) data = FL_KEYBOARD_MANAGER_DATA(user_data);

fl_keyboard_pending_event_mark_embedder_replied(data->pending, handled);
g_autoptr(GTask) task = G_TASK(user_data);
FlKeyboardManager* self = FL_KEYBOARD_MANAGER(g_task_get_source_object(task));

g_autoptr(FlKeyboardManager) self =
FL_KEYBOARD_MANAGER(g_weak_ref_get(&data->manager));
if (self == nullptr) {
return;
}
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);

responder_handle_event_callback(self, data->pending);
complete_handle_event(self, task);
}

static void responder_handle_channel_event_cb(GObject* object,
GAsyncResult* result,
gpointer user_data) {
g_autoptr(FlKeyboardManagerData) data = FL_KEYBOARD_MANAGER_DATA(user_data);
g_autoptr(GTask) task = G_TASK(user_data);
FlKeyboardManager* self = FL_KEYBOARD_MANAGER(g_task_get_source_object(task));

g_autoptr(GError) error = nullptr;
gboolean handled;
Expand All @@ -289,18 +219,15 @@ static void responder_handle_channel_event_cb(GObject* object,
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;
handled = FALSE;
}

fl_keyboard_pending_event_mark_channel_replied(data->pending, handled);
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);

responder_handle_event_callback(self, data->pending);
complete_handle_event(self, task);
}

static uint16_t convert_key_to_char(FlKeyboardManager* self,
Expand Down Expand Up @@ -519,34 +446,58 @@ FlKeyboardManager* fl_keyboard_manager_new(
return self;
}

gboolean fl_keyboard_manager_handle_event(FlKeyboardManager* self,
FlKeyEvent* event) {
gboolean fl_keyboard_manager_is_redispatched(FlKeyboardManager* self,
FlKeyEvent* event) {
g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), FALSE);
g_return_val_if_fail(event != nullptr, FALSE);

guarantee_layout(self, event);
uint64_t hash = fl_key_event_hash(event);

uint64_t incoming_hash = fl_key_event_hash(event);
if (fl_keyboard_manager_remove_redispatched(self, incoming_hash)) {
guint result_index;
gboolean found = g_ptr_array_find_with_equal_func1(
self->pending_redispatches, static_cast<const uint64_t*>(&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;
}
}

FlKeyboardPendingEvent* pending = fl_keyboard_pending_event_new(event);
void fl_keyboard_manager_handle_event(FlKeyboardManager* self,
FlKeyEvent* event,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data) {
g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
g_return_if_fail(event != nullptr);

g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data);

guarantee_layout(self, event);

FlKeyboardPendingEvent* pending = fl_keyboard_pending_event_new(event);
g_ptr_array_add(self->pending_responds, pending);
g_autoptr(FlKeyboardManagerData) data =
fl_keyboard_manager_data_new(self, pending);
g_task_set_task_data(task, g_object_ref(pending), g_object_unref);

uint64_t specified_logical_key = fl_keyboard_layout_get_logical_key(
self->derived_layout, fl_key_event_get_group(event),
fl_key_event_get_keycode(event));
fl_key_embedder_responder_handle_event(
self->key_embedder_responder, event, specified_logical_key,
responder_handle_embedder_event_callback, g_object_ref(data));
responder_handle_embedder_event_callback, g_object_ref(task));
fl_key_channel_responder_handle_event(
self->key_channel_responder, event, specified_logical_key,
self->cancellable, responder_handle_channel_event_cb, g_object_ref(data));
self->cancellable, responder_handle_channel_event_cb, g_object_ref(task));
}

return TRUE;
gboolean fl_keyboard_manager_handle_event_finish(FlKeyboardManager* self,
GAsyncResult* result,
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) {
Expand Down
40 changes: 37 additions & 3 deletions engine/src/flutter/shell/platform/linux/fl_keyboard_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,52 @@ FlKeyboardManager* fl_keyboard_manager_new(
FlEngine* engine,
FlKeyboardViewDelegate* view_delegate);

/**
* fl_keyboard_manager_is_redispatched:
* @manager: an #FlKeyboardManager.
* @event: an event received from the system.
*
* Checks if an event was redispacthed from this manager.
*
* Returns: %TRUE if the event is redispatched.
*/
gboolean fl_keyboard_manager_is_redispatched(FlKeyboardManager* manager,
FlKeyEvent* event);

/**
* fl_keyboard_manager_handle_event:
* @manager: the #FlKeyboardManager self.
* @manager: an #FlKeyboardManager.
* @event: the event to be dispatched. It is usually a wrap of a GdkEventKey.
* This event will be managed and released by #FlKeyboardManager.
* @cancellable: (allow-none): a #GCancellable or %NULL.
* @callback: (scope async): a #GAsyncReadyCallback to call when the view is
* added.
* @user_data: (closure): user data to pass to @callback.
*
* Make the manager process a system key event. This might eventually send
* messages to the framework, trigger text input effects, or redispatch the
* event back to the system.
*/
gboolean fl_keyboard_manager_handle_event(FlKeyboardManager* manager,
FlKeyEvent* event);
void fl_keyboard_manager_handle_event(FlKeyboardManager* manager,
FlKeyEvent* event,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data);

/**
* fl_keyboard_manager_handle_event_finish:
* @manager: an #FlKeyboardManager.
* @result: a #GAsyncResult.
* @error: (allow-none): #GError location to store the error occurring, or %NULL
* to ignore.
*
* Completes request started with fl_keyboard_manager_handle_event().
*
* Returns: %TRUE on success.
*/
gboolean fl_keyboard_manager_handle_event_finish(FlKeyboardManager* manager,
GAsyncResult* result,
GError** error);

/**
* fl_keyboard_manager_is_state_clear:
Expand Down
Loading

0 comments on commit a6045c9

Please sign in to comment.