Keyboard tidy ups (#162054)

Make async calls use standard GLib async API.
Remove test specific methods.
Smaller cleanups.
This commit is contained in:
Robert Ancell 2025-01-24 04:45:46 +13:00 committed by GitHub
parent 2919003513
commit b56b5b6a23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 1461 additions and 699 deletions

View File

@ -545,6 +545,15 @@ G_MODULE_EXPORT FlEngine* fl_engine_new(FlDartProject* project) {
return fl_engine_new_with_renderer(project, FL_RENDERER(renderer));
}
FlEngine* fl_engine_new_with_binary_messenger(
FlBinaryMessenger* binary_messenger) {
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlEngine* self = fl_engine_new(project);
g_object_unref(self->binary_messenger);
self->binary_messenger = FL_BINARY_MESSENGER(g_object_ref(binary_messenger));
return self;
}
G_MODULE_EXPORT FlEngine* fl_engine_new_headless(FlDartProject* project) {
g_autoptr(FlRendererHeadless) renderer = fl_renderer_headless_new();
return fl_engine_new_with_renderer(project, FL_RENDERER(renderer));

View File

@ -61,6 +61,17 @@ typedef void (*FlEngineUpdateSemanticsHandler)(
const FlutterSemanticsUpdate2* update,
gpointer user_data);
/**
* fl_engine_new_with_binary_messenger:
* @binary_messenger: an #FlBinaryMessenger.
*
* Creates a new engine with a custom binary messenger. Used for testing.
*
* Returns: a new #FlEngine.
*/
FlEngine* fl_engine_new_with_binary_messenger(
FlBinaryMessenger* binary_messenger);
/**
* fl_engine_new_with_renderer:
* @project: an #FlDartProject.

View File

@ -73,55 +73,6 @@ static uint64_t to_lower(uint64_t n) {
return n;
}
/**
* FlKeyEmbedderUserData:
* The user_data used when #FlKeyEmbedderResponder sends message through the
* embedder.SendKeyEvent API.
*/
G_DECLARE_FINAL_TYPE(FlKeyEmbedderUserData,
fl_key_embedder_user_data,
FL,
KEY_EMBEDDER_USER_DATA,
GObject);
struct _FlKeyEmbedderUserData {
GObject parent_instance;
FlKeyEmbedderResponderAsyncCallback callback;
gpointer user_data;
};
G_DEFINE_TYPE(FlKeyEmbedderUserData, fl_key_embedder_user_data, G_TYPE_OBJECT)
static void fl_key_embedder_user_data_dispose(GObject* object);
static void fl_key_embedder_user_data_class_init(
FlKeyEmbedderUserDataClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_key_embedder_user_data_dispose;
}
static void fl_key_embedder_user_data_init(FlKeyEmbedderUserData* self) {}
static void fl_key_embedder_user_data_dispose(GObject* object) {
// The following line suppresses a warning for unused function
// FL_IS_KEY_EMBEDDER_USER_DATA.
g_return_if_fail(FL_IS_KEY_EMBEDDER_USER_DATA(object));
}
// Creates a new FlKeyChannelUserData private class with all information.
//
// The callback and the user_data might be nullptr.
static FlKeyEmbedderUserData* fl_key_embedder_user_data_new(
FlKeyEmbedderResponderAsyncCallback callback,
gpointer user_data) {
FlKeyEmbedderUserData* self = FL_KEY_EMBEDDER_USER_DATA(
g_object_new(fl_key_embedder_user_data_get_type(), nullptr));
self->callback = callback;
self->user_data = user_data;
return self;
}
namespace {
typedef enum {
@ -135,8 +86,8 @@ typedef enum {
struct _FlKeyEmbedderResponder {
GObject parent_instance;
EmbedderSendKeyEvent send_key_event;
void* send_key_event_user_data;
// Engine sending key events to.
GWeakRef engine;
// Internal record for states of whether a key is pressed.
//
@ -190,6 +141,8 @@ struct _FlKeyEmbedderResponder {
// It is a map from primary physical keys to lock bits. Both keys and values
// are directly stored uint64s. This table is freed by the responder.
GHashTable* logical_key_to_lock_bit;
GCancellable* cancellable;
};
static void fl_key_embedder_responder_dispose(GObject* object);
@ -203,17 +156,23 @@ static void fl_key_embedder_responder_class_init(
}
// Initializes an FlKeyEmbedderResponder instance.
static void fl_key_embedder_responder_init(FlKeyEmbedderResponder* self) {}
static void fl_key_embedder_responder_init(FlKeyEmbedderResponder* self) {
self->cancellable = g_cancellable_new();
}
// Disposes of an FlKeyEmbedderResponder instance.
static void fl_key_embedder_responder_dispose(GObject* object) {
FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(object);
g_cancellable_cancel(self->cancellable);
g_weak_ref_clear(&self->engine);
g_clear_pointer(&self->pressing_records, g_hash_table_unref);
g_clear_pointer(&self->mapping_records, g_hash_table_unref);
g_clear_pointer(&self->modifier_bit_to_checked_keys, g_hash_table_unref);
g_clear_pointer(&self->lock_bit_to_checked_keys, g_hash_table_unref);
g_clear_pointer(&self->logical_key_to_lock_bit, g_hash_table_unref);
g_clear_object(&self->cancellable);
G_OBJECT_CLASS(fl_key_embedder_responder_parent_class)->dispose(object);
}
@ -234,14 +193,11 @@ static void initialize_logical_key_to_lock_bit_loop_body(gpointer lock_bit,
}
// Creates a new FlKeyEmbedderResponder instance.
FlKeyEmbedderResponder* fl_key_embedder_responder_new(
EmbedderSendKeyEvent send_key_event,
void* send_key_event_user_data) {
FlKeyEmbedderResponder* fl_key_embedder_responder_new(FlEngine* engine) {
FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(
g_object_new(fl_key_embedder_responder_get_type(), nullptr));
self->send_key_event = send_key_event;
self->send_key_event_user_data = send_key_event_user_data;
g_weak_ref_init(&self->engine, engine);
self->pressing_records = g_hash_table_new(g_direct_hash, g_direct_equal);
self->mapping_records = g_hash_table_new(g_direct_hash, g_direct_equal);
@ -311,16 +267,6 @@ static char* event_to_character(FlKeyEvent* event) {
return result;
}
// Handles a response from the embedder API to a key event sent to the framework
// earlier.
static void handle_response(bool handled, gpointer user_data) {
g_autoptr(FlKeyEmbedderUserData) data = FL_KEY_EMBEDDER_USER_DATA(user_data);
g_return_if_fail(data->callback != nullptr);
data->callback(handled, data->user_data);
}
// Sends a synthesized event to the framework with no demand for callback.
static void synthesize_simple_event(FlKeyEmbedderResponder* self,
FlutterKeyEventType type,
@ -336,8 +282,11 @@ static void synthesize_simple_event(FlKeyEmbedderResponder* self,
out_event.character = nullptr;
out_event.synthesized = true;
self->sent_any_events = true;
self->send_key_event(&out_event, nullptr, nullptr,
self->send_key_event_user_data);
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine != nullptr) {
fl_engine_send_key_event(engine, &out_event, self->cancellable, nullptr,
nullptr);
}
}
namespace {
@ -750,13 +699,9 @@ static void fl_key_embedder_responder_handle_event_impl(
FlKeyEmbedderResponder* responder,
FlKeyEvent* event,
uint64_t specified_logical_key,
FlKeyEmbedderResponderAsyncCallback callback,
gpointer user_data) {
GTask* task) {
FlKeyEmbedderResponder* self = FL_KEY_EMBEDDER_RESPONDER(responder);
g_return_if_fail(event != nullptr);
g_return_if_fail(callback != nullptr);
const uint64_t logical_key = specified_logical_key != 0
? specified_logical_key
: event_to_logical_key(event);
@ -811,7 +756,9 @@ static void fl_key_embedder_responder_handle_event_impl(
// The physical key has been released before. It might indicate a missed
// event due to loss of focus, or multiple keyboards pressed keys with the
// same physical key. Ignore the up event.
callback(true, user_data);
gboolean* return_value = g_new0(gboolean, 1);
*return_value = TRUE;
g_task_return_pointer(task, return_value, g_free);
return;
} else {
out_event.type = kFlutterKeyEventTypeUp;
@ -825,41 +772,85 @@ static void fl_key_embedder_responder_handle_event_impl(
if (is_down_event) {
update_mapping_record(self, physical_key, logical_key);
}
FlKeyEmbedderUserData* response_data =
fl_key_embedder_user_data_new(callback, user_data);
self->sent_any_events = true;
self->send_key_event(&out_event, handle_response, response_data,
self->send_key_event_user_data);
}
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine != nullptr) {
fl_engine_send_key_event(
engine, &out_event, self->cancellable,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
g_autoptr(GTask) task = G_TASK(user_data);
void fl_key_embedder_responder_handle_event(
FlKeyEmbedderResponder* self,
FlKeyEvent* event,
uint64_t specified_logical_key,
FlKeyEmbedderResponderAsyncCallback callback,
gpointer user_data) {
self->sent_any_events = false;
fl_key_embedder_responder_handle_event_impl(
self, event, specified_logical_key, callback, user_data);
if (!self->sent_any_events) {
self->send_key_event(&kEmptyEvent, nullptr, nullptr,
self->send_key_event_user_data);
gboolean handled;
g_autoptr(GError) error = nullptr;
if (!fl_engine_send_key_event_finish(FL_ENGINE(object), result,
&handled, &error)) {
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
return;
}
g_warning("Failed to handle key event: %s", error->message);
handled = FALSE;
}
gboolean* return_value = g_new0(gboolean, 1);
*return_value = handled;
g_task_return_pointer(task, return_value, g_free);
},
g_object_ref(task));
}
}
void fl_key_embedder_responder_handle_event(FlKeyEmbedderResponder* self,
FlKeyEvent* event,
uint64_t specified_logical_key,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data) {
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data);
self->sent_any_events = false;
fl_key_embedder_responder_handle_event_impl(self, event,
specified_logical_key, task);
if (!self->sent_any_events) {
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine != nullptr) {
fl_engine_send_key_event(engine, &kEmptyEvent, self->cancellable, nullptr,
nullptr);
}
}
}
gboolean fl_key_embedder_responder_handle_event_finish(
FlKeyEmbedderResponder* 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;
}
void fl_key_embedder_responder_sync_modifiers_if_needed(
FlKeyEmbedderResponder* responder,
FlKeyEmbedderResponder* self,
guint state,
double event_time) {
g_return_if_fail(FL_IS_KEY_EMBEDDER_RESPONDER(self));
const double timestamp = event_time * kMicrosecondsPerMillisecond;
SyncStateLoopContext sync_state_context;
sync_state_context.self = responder;
sync_state_context.self = self;
sync_state_context.state = state;
sync_state_context.timestamp = timestamp;
// Update pressing states.
g_hash_table_foreach(responder->modifier_bit_to_checked_keys,
g_hash_table_foreach(self->modifier_bit_to_checked_keys,
synchronize_pressed_states_loop_body,
&sync_state_context);
}

View File

@ -8,34 +8,6 @@
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_key_event.h"
// The signature of a function that FlKeyEmbedderResponder calls on every key
// event.
//
// The `user_data` is opaque data managed by the object that creates
// FlKeyEmbedderResponder, and is registered along with this callback
// via `fl_key_embedder_responder_new`.
//
// The `callback_user_data` is opaque data managed by FlKeyEmbedderResponder.
// Instances of the EmbedderSendKeyEvent callback are required to invoke
// `callback` with the `callback_user_data` parameter after the `event` has been
// processed.
typedef void (*EmbedderSendKeyEvent)(const FlutterKeyEvent* event,
FlutterKeyEventCallback callback,
void* callback_user_data,
void* send_key_event_user_data);
/**
* FlKeyEmbedderResponderAsyncCallback:
* @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 #FlKeyEmbedderResponder
*asynchronously reports whether the responder handles the event.
**/
typedef void (*FlKeyEmbedderResponderAsyncCallback)(bool handled,
gpointer user_data);
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(FlKeyEmbedderResponder,
@ -59,16 +31,10 @@ G_DECLARE_FINAL_TYPE(FlKeyEmbedderResponder,
* the event.
*
* Creates a new #FlKeyEmbedderResponder.
* @send_key_event: a function that is called on every key event.
* @send_key_event_user_data: an opaque pointer that will be sent back as the
* last argument of send_key_event, created and managed by the object that holds
* FlKeyEmbedderResponder.
*
* Returns: a new #FlKeyEmbedderResponder.
*/
FlKeyEmbedderResponder* fl_key_embedder_responder_new(
EmbedderSendKeyEvent send_key_event,
void* send_key_event_user_data);
FlKeyEmbedderResponder* fl_key_embedder_responder_new(FlEngine* engine);
/**
* fl_key_embedder_responder_handle_event:
@ -76,21 +42,38 @@ FlKeyEmbedderResponder* fl_key_embedder_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 view is
* added.
* @user_data: (closure): user data to pass to @callback.
*
* 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_embedder_responder_handle_event` or during it.
* Let the responder handle an event, expecting the responder to report whether
* to handle the event.
*/
void fl_key_embedder_responder_handle_event(
void fl_key_embedder_responder_handle_event(FlKeyEmbedderResponder* responder,
FlKeyEvent* event,
uint64_t specified_logical_key,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
/**
* fl_key_embedder_responder_handle_event_finish:
* @responder: an #FlKeyEmbedderResponder.
* @result: a #GAsyncResult.
* @handled: location to write if this event was handled by the embedder.
* @error: (allow-none): #GError location to store the error occurring, or %NULL
* to ignore.
*
* Completes request started with fl_key_embedder_responder_handle_event().
*
* Returns %TRUE on success.
*/
gboolean fl_key_embedder_responder_handle_event_finish(
FlKeyEmbedderResponder* responder,
FlKeyEvent* event,
uint64_t specified_logical_key,
FlKeyEmbedderResponderAsyncCallback callback,
gpointer user_data);
GAsyncResult* result,
gboolean* handled,
GError** error);
/**
* fl_key_embedder_responder_sync_modifiers_if_needed:

View File

@ -5,6 +5,7 @@
#include "flutter/shell/platform/linux/fl_keyboard_handler.h"
#include "flutter/shell/platform/linux/fl_keyboard_channel.h"
#include "flutter/shell/platform/linux/key_mapping.h"
struct _FlKeyboardHandler {
GObject parent_instance;
@ -29,8 +30,8 @@ static FlValue* get_keyboard_state(gpointer user_data) {
g_hash_table_foreach(
pressing_records,
[](gpointer key, gpointer value, gpointer user_data) {
int64_t physical_key = reinterpret_cast<int64_t>(key);
int64_t logical_key = reinterpret_cast<int64_t>(value);
int64_t physical_key = gpointer_to_uint64(key);
int64_t logical_key = gpointer_to_uint64(value);
FlValue* fl_value_map = reinterpret_cast<FlValue*>(user_data);
fl_value_set_take(fl_value_map, fl_value_new_int(physical_key),
@ -66,9 +67,8 @@ FlKeyboardHandler* fl_keyboard_handler_new(
g_object_new(fl_keyboard_handler_get_type(), nullptr));
self->keyboard_manager = FL_KEYBOARD_MANAGER(g_object_ref(keyboard_manager));
// Setup the flutter/keyboard channel.
self->channel =
fl_keyboard_channel_new(messenger, &keyboard_channel_vtable, self);
return self;
}

View File

@ -4,40 +4,69 @@
#include "flutter/shell/platform/linux/fl_keyboard_handler.h"
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
#include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_method_codec_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h"
#include "flutter/shell/platform/linux/testing/mock_keymap.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
static constexpr char kKeyboardChannelName[] = "flutter/keyboard";
static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
static constexpr uint64_t kMockPhysicalKey = 42;
static constexpr uint64_t kMockLogicalKey = 42;
using ::flutter::testing::keycodes::kLogicalKeyA;
using ::flutter::testing::keycodes::kPhysicalKeyA;
constexpr guint16 kKeyCodeKeyA = 0x26u;
TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) {
::testing::NiceMock<flutter::testing::MockKeymap> mock_keymap;
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_get_pressed_state_handler(
manager,
[](gpointer user_data) {
GHashTable* result = g_hash_table_new(g_direct_hash, g_direct_equal);
g_hash_table_insert(result,
reinterpret_cast<gpointer>(kMockPhysicalKey),
reinterpret_cast<gpointer>(kMockLogicalKey));
return result;
},
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
g_autoptr(FlKeyboardHandler) handler =
fl_keyboard_handler_new(FL_BINARY_MESSENGER(messenger), manager);
EXPECT_NE(handler, nullptr);
// Send key event to set pressed state.
fl_mock_binary_messenger_set_json_message_channel(
messenger, "flutter/keyevent",
[](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
gpointer user_data) {
FlValue* response = fl_value_new_map();
fl_value_set_string_take(response, "handled", fl_value_new_bool(FALSE));
return response;
},
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
callback(false, user_data);
return kSuccess;
}));
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
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) {
g_autoptr(FlKeyEvent) redispatched_event = nullptr;
EXPECT_TRUE(fl_keyboard_manager_handle_event_finish(
FL_KEYBOARD_MANAGER(object), result, &redispatched_event, nullptr));
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
loop);
g_main_loop_run(loop);
gboolean called = FALSE;
fl_mock_binary_messenger_invoke_standard_method(
messenger, kKeyboardChannelName, kGetKeyboardStateMethod, nullptr,
@ -49,14 +78,13 @@ TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) {
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
g_autoptr(FlValue) expected_result = fl_value_new_map();
fl_value_set_take(expected_result, fl_value_new_int(kMockPhysicalKey),
fl_value_new_int(kMockLogicalKey));
fl_value_set_take(expected_result, fl_value_new_int(kPhysicalKeyA),
fl_value_new_int(kLogicalKeyA));
EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response)),
expected_result));
},
&called);
EXPECT_TRUE(called);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
EXPECT_TRUE(called);
}

View File

@ -9,7 +9,6 @@
#include <memory>
#include <string>
#include "flutter/shell/platform/linux/fl_engine_private.h"
#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"
@ -84,17 +83,9 @@ static void handle_event_data_free(HandleEventData* data) {
struct _FlKeyboardManager {
GObject parent_instance;
GWeakRef engine;
FlKeyboardManagerSendKeyEventHandler send_key_event_handler;
gpointer send_key_event_handler_user_data;
FlKeyboardManagerLookupKeyHandler lookup_key_handler;
gpointer lookup_key_handler_user_data;
FlKeyboardManagerGetPressedStateHandler get_pressed_state_handler;
gpointer get_pressed_state_handler_user_data;
// Key events that have been redispatched.
GPtrArray* redispatched_key_events;
@ -166,14 +157,25 @@ static void complete_handle_event(FlKeyboardManager* self, GTask* task) {
g_task_return_boolean(task, TRUE);
}
static void responder_handle_embedder_event_callback(bool handled,
gpointer user_data) {
static void responder_handle_embedder_event_cb(GObject* object,
GAsyncResult* result,
gpointer user_data) {
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->embedder_responded = TRUE;
g_autoptr(GError) error = nullptr;
gboolean handled;
if (!fl_key_embedder_responder_handle_event_finish(
FL_KEY_EMBEDDER_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 embedder: %s", error->message);
}
handled = FALSE;
}
if (handled) {
data->handled = TRUE;
}
@ -309,8 +311,6 @@ static void fl_keyboard_manager_dispose(GObject* object) {
g_cancellable_cancel(self->cancellable);
g_weak_ref_clear(&self->engine);
self->keycode_to_goals.reset();
self->logical_to_mandatory_goals.reset();
@ -357,51 +357,7 @@ FlKeyboardManager* fl_keyboard_manager_new(FlEngine* engine) {
FlKeyboardManager* self = FL_KEYBOARD_MANAGER(
g_object_new(fl_keyboard_manager_get_type(), nullptr));
g_weak_ref_init(&self->engine, engine);
self->key_embedder_responder = fl_key_embedder_responder_new(
[](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
void* callback_user_data, void* send_key_event_user_data) {
FlKeyboardManager* self = FL_KEYBOARD_MANAGER(send_key_event_user_data);
if (self->send_key_event_handler != nullptr) {
self->send_key_event_handler(event, callback, callback_user_data,
self->send_key_event_handler_user_data);
} else {
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine != nullptr) {
typedef struct {
FlutterKeyEventCallback callback;
void* callback_user_data;
} SendKeyEventData;
SendKeyEventData* data = g_new0(SendKeyEventData, 1);
data->callback = callback;
data->callback_user_data = callback_user_data;
fl_engine_send_key_event(
engine, event, self->cancellable,
[](GObject* object, GAsyncResult* result, gpointer user_data) {
g_autofree SendKeyEventData* data =
static_cast<SendKeyEventData*>(user_data);
gboolean handled = FALSE;
g_autoptr(GError) error = nullptr;
if (!fl_engine_send_key_event_finish(
FL_ENGINE(object), result, &handled, &error)) {
if (g_error_matches(error, G_IO_ERROR,
G_IO_ERROR_CANCELLED)) {
return;
}
g_warning("Failed to send key event: %s", error->message);
}
if (data->callback != nullptr) {
data->callback(handled, data->callback_user_data);
}
},
data);
}
}
},
self);
self->key_embedder_responder = fl_key_embedder_responder_new(engine);
self->key_channel_responder =
fl_key_channel_responder_new(fl_engine_get_binary_messenger(engine));
@ -444,7 +400,8 @@ void fl_keyboard_manager_handle_event(FlKeyboardManager* self,
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(task));
self->cancellable, responder_handle_embedder_event_cb,
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(task));
@ -477,22 +434,8 @@ void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager* self,
GHashTable* fl_keyboard_manager_get_pressed_state(FlKeyboardManager* self) {
g_return_val_if_fail(FL_IS_KEYBOARD_MANAGER(self), nullptr);
if (self->get_pressed_state_handler != nullptr) {
return self->get_pressed_state_handler(
self->get_pressed_state_handler_user_data);
} else {
return fl_key_embedder_responder_get_pressed_state(
self->key_embedder_responder);
}
}
void fl_keyboard_manager_set_send_key_event_handler(
FlKeyboardManager* self,
FlKeyboardManagerSendKeyEventHandler send_key_event_handler,
gpointer user_data) {
g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
self->send_key_event_handler = send_key_event_handler;
self->send_key_event_handler_user_data = user_data;
return fl_key_embedder_responder_get_pressed_state(
self->key_embedder_responder);
}
void fl_keyboard_manager_set_lookup_key_handler(
@ -503,12 +446,3 @@ void fl_keyboard_manager_set_lookup_key_handler(
self->lookup_key_handler = lookup_key_handler;
self->lookup_key_handler_user_data = user_data;
}
void fl_keyboard_manager_set_get_pressed_state_handler(
FlKeyboardManager* self,
FlKeyboardManagerGetPressedStateHandler get_pressed_state_handler,
gpointer user_data) {
g_return_if_fail(FL_IS_KEYBOARD_MANAGER(self));
self->get_pressed_state_handler = get_pressed_state_handler;
self->get_pressed_state_handler_user_data = user_data;
}

View File

@ -115,23 +115,6 @@ void fl_keyboard_manager_sync_modifier_if_needed(FlKeyboardManager* manager,
*/
GHashTable* fl_keyboard_manager_get_pressed_state(FlKeyboardManager* manager);
typedef void (*FlKeyboardManagerSendKeyEventHandler)(
const FlutterKeyEvent* event,
FlutterKeyEventCallback callback,
void* callback_user_data,
gpointer user_data);
/**
* fl_keyboard_manager_set_send_key_event_handler:
* @manager: the #FlKeyboardManager self.
*
* Set the handler for sending events, for testing purposes only.
*/
void fl_keyboard_manager_set_send_key_event_handler(
FlKeyboardManager* manager,
FlKeyboardManagerSendKeyEventHandler send_key_event_handler,
gpointer user_data);
typedef guint (*FlKeyboardManagerLookupKeyHandler)(const GdkKeymapKey* key,
gpointer user_data);
@ -146,20 +129,6 @@ void fl_keyboard_manager_set_lookup_key_handler(
FlKeyboardManagerLookupKeyHandler lookup_key_handler,
gpointer user_data);
typedef GHashTable* (*FlKeyboardManagerGetPressedStateHandler)(
gpointer user_data);
/**
* fl_keyboard_manager_set_get_pressed_state_handler:
* @manager: the #FlKeyboardManager self.
*
* Set the handler for gettting the keyboard state, for testing purposes only.
*/
void fl_keyboard_manager_set_get_pressed_state_handler(
FlKeyboardManager* manager,
FlKeyboardManagerGetPressedStateHandler get_pressed_state_handler,
gpointer user_data);
G_END_DECLS
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_MANAGER_H_

View File

@ -8,6 +8,7 @@
#include <vector>
#include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/key_mapping.h"
#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h"
@ -15,16 +16,6 @@
#include "gtest/gtest.h"
// Define compound `expect` in macros. If they were defined in functions, the
// stacktrace wouldn't print where the function is called in the unit tests.
#define EXPECT_KEY_EVENT(RECORD, TYPE, PHYSICAL, LOGICAL, CHAR, SYNTHESIZED) \
EXPECT_EQ((RECORD)->event_type, (TYPE)); \
EXPECT_EQ((RECORD)->event_physical, (PHYSICAL)); \
EXPECT_EQ((RECORD)->event_logical, (LOGICAL)); \
EXPECT_STREQ((RECORD)->event_character, (CHAR)); \
EXPECT_EQ((RECORD)->event_synthesized, (SYNTHESIZED));
#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
EXPECT_EQ(static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)) \
->event_type, \
@ -134,16 +125,16 @@ TEST(FlKeyboardManagerTest, EngineNoResponseChannelHandled) {
nullptr);
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Don't handle first event - async call never completes.
fl_keyboard_manager_set_send_key_event_handler(
manager,
[](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
void* callback_user_data, gpointer user_data) {},
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback,
void* user_data) { return kSuccess; }));
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
gboolean first_event_completed = FALSE;
@ -156,12 +147,12 @@ TEST(FlKeyboardManagerTest, EngineNoResponseChannelHandled) {
&first_event_completed);
// Handle second event.
fl_keyboard_manager_set_send_key_event_handler(
manager,
[](const FlutterKeyEvent* event, FlutterKeyEventCallback callback,
void* callback_user_data,
gpointer user_data) { callback(true, callback_user_data); },
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
callback(true, user_data);
return kSuccess;
}));
g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
0, FALSE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
@ -188,12 +179,10 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledSync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls synchronously.
fl_mock_binary_messenger_set_json_message_channel(
@ -206,12 +195,12 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledSync) {
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) { callback(true, callback_user_data); },
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
callback(true, user_data);
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -235,12 +224,10 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls synchronously.
fl_mock_binary_messenger_set_json_message_channel(
@ -253,12 +240,12 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledSync) {
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) { callback(false, callback_user_data); },
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
callback(false, user_data);
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -282,12 +269,10 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelHandledSync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls synchronously.
fl_mock_binary_messenger_set_json_message_channel(
@ -300,12 +285,12 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelHandledSync) {
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) { callback(true, callback_user_data); },
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
callback(true, user_data);
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -329,12 +314,10 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls synchronously.
fl_mock_binary_messenger_set_json_message_channel(
@ -347,12 +330,12 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledSync) {
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) { callback(false, callback_user_data); },
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
callback(false, user_data);
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -384,12 +367,10 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledAsync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls asynchronously.
g_autoptr(GPtrArray) channel_calls =
@ -406,15 +387,14 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelNotHandledAsync) {
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);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([&embedder_call_records](
auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
g_ptr_array_add(embedder_call_records,
call_record_new(event, callback, user_data));
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -448,12 +428,10 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledAsync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls asynchronously.
g_autoptr(GPtrArray) channel_calls =
@ -470,15 +448,14 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelHandledAsync) {
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);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([&embedder_call_records](
auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
g_ptr_array_add(embedder_call_records,
call_record_new(event, callback, user_data));
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -512,12 +489,10 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelHandledAsync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls asynchronously.
g_autoptr(GPtrArray) channel_calls =
@ -534,15 +509,14 @@ TEST(FlKeyboardManagerTest, EngineHandledChannelHandledAsync) {
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);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([&embedder_call_records](
auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
g_ptr_array_add(embedder_call_records,
call_record_new(event, callback, user_data));
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -576,12 +550,10 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledAsync) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Handle channel and embedder calls asynchronously.
g_autoptr(GPtrArray) channel_calls =
@ -598,15 +570,14 @@ TEST(FlKeyboardManagerTest, EngineNotHandledChannelNotHandledAsync) {
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);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([&embedder_call_records](
auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
g_ptr_array_add(embedder_call_records,
call_record_new(event, callback, user_data));
return kSuccess;
}));
g_autoptr(FlKeyEvent) event = fl_key_event_new(
0, TRUE, kKeyCodeKeyA, GDK_KEY_a, static_cast<GdkModifierType>(0), 0);
@ -641,20 +612,18 @@ TEST(FlKeyboardManagerTest, CorrectLogicalKeyForLayouts) {
g_autoptr(FlEngine) engine = fl_engine_new(project);
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
g_autoptr(GPtrArray) 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);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent,
([&call_records](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
g_ptr_array_add(call_records,
call_record_new(event, callback, callback_user_data));
},
call_records);
fl_keyboard_manager_set_lookup_key_handler(
manager, [](const GdkKeymapKey* key, gpointer user_data) { return 0u; },
nullptr);
call_record_new(event, callback, user_data));
return kSuccess;
}));
auto sendTap = [&](guint8 keycode, guint keyval, guint8 group) {
g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
@ -776,17 +745,18 @@ TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
g_autoptr(FlEngine) engine = fl_engine_new(project);
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
g_autoptr(GPtrArray) 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);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent,
([&call_records](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
g_ptr_array_add(call_records,
call_record_new(event, callback, callback_user_data));
},
call_records);
call_record_new(event, callback, user_data));
return kSuccess;
}));
auto verifyModifierIsSynthesized = [&](GdkModifierType mask,
uint64_t physical, uint64_t logical) {
@ -794,16 +764,23 @@ TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
guint state = mask;
fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1000);
EXPECT_EQ(call_records->len, 1u);
EXPECT_KEY_EVENT(
static_cast<CallRecord*>(g_ptr_array_index(call_records, 0)),
kFlutterKeyEventTypeDown, physical, logical, NULL, true);
CallRecord* record =
static_cast<CallRecord*>(g_ptr_array_index(call_records, 0));
EXPECT_EQ(record->event_type, kFlutterKeyEventTypeDown);
EXPECT_EQ(record->event_physical, physical);
EXPECT_EQ(record->event_logical, logical);
EXPECT_STREQ(record->event_character, NULL);
EXPECT_EQ(record->event_synthesized, true);
// Modifier is released.
state = state ^ mask;
fl_keyboard_manager_sync_modifier_if_needed(manager, state, 1001);
EXPECT_EQ(call_records->len, 2u);
EXPECT_KEY_EVENT(
static_cast<CallRecord*>(g_ptr_array_index(call_records, 1)),
kFlutterKeyEventTypeUp, physical, logical, NULL, true);
record = static_cast<CallRecord*>(g_ptr_array_index(call_records, 1));
EXPECT_EQ(record->event_type, kFlutterKeyEventTypeUp);
EXPECT_EQ(record->event_physical, physical);
EXPECT_EQ(record->event_logical, logical);
EXPECT_STREQ(record->event_character, NULL);
EXPECT_EQ(record->event_synthesized, true);
g_ptr_array_set_size(call_records, 0);
};
@ -826,27 +803,49 @@ TEST(FlKeyboardManagerTest, SynthesizeModifiersIfNeeded) {
TEST(FlKeyboardManagerTest, GetPressedState) {
::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();
g_autoptr(FlEngine) engine =
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlKeyboardManager) manager = fl_keyboard_manager_new(engine);
EXPECT_TRUE(fl_engine_start(engine, nullptr));
// Dispatch a key event.
fl_mock_binary_messenger_set_json_message_channel(
messenger, "flutter/keyevent",
[](FlMockBinaryMessenger* messenger, GTask* task, FlValue* message,
gpointer user_data) {
FlValue* response = fl_value_new_map();
fl_value_set_string_take(response, "handled", fl_value_new_bool(FALSE));
return response;
},
nullptr);
fl_engine_get_embedder_api(engine)->SendKeyEvent = MOCK_ENGINE_PROC(
SendKeyEvent, ([](auto engine, const FlutterKeyEvent* event,
FlutterKeyEventCallback callback, void* user_data) {
callback(false, user_data);
return kSuccess;
}));
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));
},
nullptr);
loop);
g_main_loop_run(loop);
GHashTable* pressedState = fl_keyboard_manager_get_pressed_state(manager);
EXPECT_EQ(g_hash_table_size(pressedState), 1u);
GHashTable* pressed_state = fl_keyboard_manager_get_pressed_state(manager);
EXPECT_EQ(g_hash_table_size(pressed_state), 1u);
gpointer physical_key =
g_hash_table_lookup(pressedState, uint64_to_gpointer(kPhysicalKeyA));
g_hash_table_lookup(pressed_state, uint64_to_gpointer(kPhysicalKeyA));
EXPECT_EQ(gpointer_to_uint64(physical_key), kLogicalKeyA);
}

View File

@ -19,8 +19,7 @@ TEST(FlSettingsHandlerTest, AlwaysUse24HourFormat) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine);
EXPECT_CALL(settings, fl_settings_get_clock_format(
@ -78,8 +77,7 @@ TEST(FlSettingsHandlerTest, PlatformBrightness) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine);
EXPECT_CALL(settings, fl_settings_get_color_scheme(
@ -135,8 +133,7 @@ TEST(FlSettingsHandlerTest, TextScaleFactor) {
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr));
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine);
EXPECT_CALL(settings, fl_settings_get_text_scaling_factor(