Refactor event redispatching (#161701)
Instead of doing the redispatching from inside FlKeyboardManager, return the event asynchronously to the code that provided the event in the first case. That code has all the context on how to do the redispatch - this will be more complicated in a multi-view case.
This commit is contained in:
parent
12518fac13
commit
f2ce662fd2
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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<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);
|
||||
|
||||
|
@ -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_
|
||||
|
@ -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<HandleEventData*>(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<HandleEventData*>(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<gpointer>(a));
|
||||
uint64_t hash = *reinterpret_cast<const uint64_t*>(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<HandleEventData*>(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<HandleEventData*>(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<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;
|
||||
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<GDestroyNotify>(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<HandleEventData*>(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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<GMainLoop*>(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<flutter::testing::MockKeymap> 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<GdkModifierType>(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<gboolean*>(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<GdkModifierType>(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<GMainLoop*>(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<flutter::testing::MockKeymap> 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<GDestroyNotify>(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<GPtrArray*>(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<GPtrArray*>(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<GdkModifierType>(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<GMainLoop*>(user_data));
|
||||
},
|
||||
nullptr);
|
||||
flush_channel_messages();
|
||||
EXPECT_EQ(redispatched->len, 0u);
|
||||
EXPECT_EQ(call_records->len, 1u);
|
||||
EXPECT_KEY_EVENT(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)),
|
||||
kFlutterKeyEventTypeDown, kPhysicalKeyA, kLogicalKeyA, "a",
|
||||
false);
|
||||
loop);
|
||||
g_main_loop_run(loop);
|
||||
|
||||
call_record_respond(
|
||||
static_cast<CallRecord*>(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<GdkModifierType>(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<CallRecord*>(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<GdkModifierType>(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<CallRecord*>(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<CallRecord*>(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<CallRecord*>(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<GdkModifierType>(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<CallRecord*>(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<GdkModifierType>(0), 0);
|
||||
EXPECT_FALSE(fl_keyboard_manager_is_redispatched(manager, event2));
|
||||
}
|
||||
|
||||
TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
|
||||
TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) {
|
||||
::testing::NiceMock<flutter::testing::MockKeymap> 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<GDestroyNotify>(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<GPtrArray*>(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<GPtrArray*>(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<GdkModifierType>(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<GMainLoop*>(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<GdkModifierType>(0), 0);
|
||||
EXPECT_FALSE(fl_keyboard_manager_is_redispatched(manager, event2));
|
||||
}
|
||||
|
||||
TEST(FlKeyboardManagerTest, EngineHandledChannelHandledSync) {
|
||||
::testing::NiceMock<flutter::testing::MockKeymap> 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<CallRecord*>(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<GPtrArray*>(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<GdkModifierType>(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<GdkModifierType>(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<GMainLoop*>(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<GdkModifierType>(0), 0);
|
||||
EXPECT_FALSE(fl_keyboard_manager_is_redispatched(manager, event2));
|
||||
}
|
||||
|
||||
TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) {
|
||||
::testing::NiceMock<flutter::testing::MockKeymap> 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<CallRecord*>(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<GdkModifierType>(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<GMainLoop*>(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<GdkModifierType>(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<flutter::testing::MockKeymap> 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<GPtrArray*>(user_data);
|
||||
g_ptr_array_add(call_records, g_object_ref(task));
|
||||
// Will respond async
|
||||
return static_cast<FlValue*>(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<GPtrArray*>(user_data);
|
||||
g_ptr_array_add(channel_calls, g_object_ref(task));
|
||||
// Will respond async
|
||||
return static_cast<FlValue*>(nullptr);
|
||||
},
|
||||
channel_calls);
|
||||
g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
|
||||
reinterpret_cast<GDestroyNotify>(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<GPtrArray*>(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<GdkModifierType>(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<GMainLoop*>(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<GTask*>(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<flutter::testing::MockKeymap> mock_keymap;
|
||||
|
||||
/// Test 2: All delegates respond false
|
||||
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
|
||||
0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(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<GPtrArray*>(user_data);
|
||||
g_ptr_array_add(channel_calls, g_object_ref(task));
|
||||
// Will respond async
|
||||
return static_cast<FlValue*>(nullptr);
|
||||
},
|
||||
channel_calls);
|
||||
g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
|
||||
reinterpret_cast<GDestroyNotify>(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<GPtrArray*>(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<GdkModifierType>(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<GMainLoop*>(user_data));
|
||||
},
|
||||
loop);
|
||||
|
||||
EXPECT_EQ(embedder_call_records->len, 1u);
|
||||
EXPECT_EQ(channel_calls->len, 1u);
|
||||
|
||||
call_record_respond(
|
||||
static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
|
||||
false);
|
||||
channel_respond(messenger,
|
||||
static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
|
||||
TRUE);
|
||||
g_main_loop_run(loop);
|
||||
}
|
||||
|
||||
TEST(FlKeyboardManagerTest, EngineHandledChannelHandledAsync) {
|
||||
::testing::NiceMock<flutter::testing::MockKeymap> 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<GPtrArray*>(user_data);
|
||||
g_ptr_array_add(channel_calls, g_object_ref(task));
|
||||
// Will respond async
|
||||
return static_cast<FlValue*>(nullptr);
|
||||
},
|
||||
channel_calls);
|
||||
g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
|
||||
reinterpret_cast<GDestroyNotify>(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<GPtrArray*>(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<GdkModifierType>(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<GMainLoop*>(user_data));
|
||||
},
|
||||
loop);
|
||||
|
||||
EXPECT_EQ(embedder_call_records->len, 1u);
|
||||
EXPECT_EQ(channel_calls->len, 1u);
|
||||
|
||||
call_record_respond(
|
||||
static_cast<CallRecord*>(g_ptr_array_index(embedder_call_records, 0)),
|
||||
true);
|
||||
channel_respond(messenger,
|
||||
static_cast<GTask*>(g_ptr_array_index(channel_calls, 0)),
|
||||
TRUE);
|
||||
g_main_loop_run(loop);
|
||||
}
|
||||
|
||||
TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledAsync) {
|
||||
::testing::NiceMock<flutter::testing::MockKeymap> 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<GPtrArray*>(user_data);
|
||||
g_ptr_array_add(channel_calls, g_object_ref(task));
|
||||
// Will respond async
|
||||
return static_cast<FlValue*>(nullptr);
|
||||
},
|
||||
channel_calls);
|
||||
g_autoptr(GPtrArray) embedder_call_records = g_ptr_array_new_with_free_func(
|
||||
reinterpret_cast<GDestroyNotify>(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<GPtrArray*>(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<GdkModifierType>(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<GMainLoop*>(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<GTask*>(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<flutter::testing::MockKeymap> 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<gboolean*>(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<GdkModifierType>(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<flutter::testing::MockKeymap> 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<gboolean*>(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<GdkModifierType>(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<GMainLoop*>(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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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_
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user