Make fl_engine_send_key_event into a standard async function. (flutter/engine#57112)
Add missing tests for this function. Note this makes FlKeyboardManager a bit more complex, but this is planned to be simplified in a future refactor.
This commit is contained in:
parent
600671f790
commit
d9cdbeebea
@ -1095,17 +1095,51 @@ void fl_engine_send_pointer_pan_zoom_event(FlEngine* self,
|
|||||||
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
|
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void send_key_event_cb(bool handled, void* user_data) {
|
||||||
|
g_autoptr(GTask) task = G_TASK(user_data);
|
||||||
|
gboolean* return_value = g_new0(gboolean, 1);
|
||||||
|
*return_value = handled;
|
||||||
|
g_task_return_pointer(task, return_value, g_free);
|
||||||
|
}
|
||||||
|
|
||||||
void fl_engine_send_key_event(FlEngine* self,
|
void fl_engine_send_key_event(FlEngine* self,
|
||||||
const FlutterKeyEvent* event,
|
const FlutterKeyEvent* event,
|
||||||
FlutterKeyEventCallback callback,
|
GCancellable* cancellable,
|
||||||
void* user_data) {
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data) {
|
||||||
g_return_if_fail(FL_IS_ENGINE(self));
|
g_return_if_fail(FL_IS_ENGINE(self));
|
||||||
|
|
||||||
|
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data);
|
||||||
|
|
||||||
if (self->engine == nullptr) {
|
if (self->engine == nullptr) {
|
||||||
|
g_task_return_new_error(task, fl_engine_error_quark(),
|
||||||
|
FL_ENGINE_ERROR_FAILED, "No engine");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->embedder_api.SendKeyEvent(self->engine, event, callback, user_data);
|
if (self->embedder_api.SendKeyEvent(self->engine, event, send_key_event_cb,
|
||||||
|
g_object_ref(task)) != kSuccess) {
|
||||||
|
g_task_return_new_error(task, fl_engine_error_quark(),
|
||||||
|
FL_ENGINE_ERROR_FAILED, "Failed to send key event");
|
||||||
|
g_object_unref(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean fl_engine_send_key_event_finish(FlEngine* self,
|
||||||
|
GAsyncResult* result,
|
||||||
|
gboolean* handled,
|
||||||
|
GError** error) {
|
||||||
|
g_return_val_if_fail(FL_IS_ENGINE(self), FALSE);
|
||||||
|
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_engine_dispatch_semantics_action(FlEngine* self,
|
void fl_engine_dispatch_semantics_action(FlEngine* self,
|
||||||
|
@ -369,11 +369,37 @@ void fl_engine_send_pointer_pan_zoom_event(FlEngine* engine,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* fl_engine_send_key_event:
|
* fl_engine_send_key_event:
|
||||||
|
* @engine: an #FlEngine.
|
||||||
|
* @event: key event to send.
|
||||||
|
* @cancellable: (allow-none): a #GCancellable or %NULL.
|
||||||
|
* @callback: (scope async): a #GAsyncReadyCallback to call when the request is
|
||||||
|
* satisfied.
|
||||||
|
* @user_data: (closure): user data to pass to @callback.
|
||||||
|
*
|
||||||
|
* Send a key event to the engine.
|
||||||
*/
|
*/
|
||||||
void fl_engine_send_key_event(FlEngine* engine,
|
void fl_engine_send_key_event(FlEngine* engine,
|
||||||
const FlutterKeyEvent* event,
|
const FlutterKeyEvent* event,
|
||||||
FlutterKeyEventCallback callback,
|
GCancellable* cancellable,
|
||||||
void* user_data);
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_engine_send_key_event_finish:
|
||||||
|
* @engine: an #FlEngine.
|
||||||
|
* @result: a #GAsyncResult.
|
||||||
|
* @handled: location to write if this event was handled by the engine.
|
||||||
|
* @error: (allow-none): #GError location to store the error occurring, or %NULL
|
||||||
|
* to ignore.
|
||||||
|
*
|
||||||
|
* Completes request started with fl_engine_send_key_event().
|
||||||
|
*
|
||||||
|
* Returns: %TRUE on success.
|
||||||
|
*/
|
||||||
|
gboolean fl_engine_send_key_event_finish(FlEngine* engine,
|
||||||
|
GAsyncResult* result,
|
||||||
|
gboolean* handled,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fl_engine_dispatch_semantics_action:
|
* fl_engine_dispatch_semantics_action:
|
||||||
|
@ -748,4 +748,132 @@ TEST(FlEngineTest, RemoveViewEngineError) {
|
|||||||
g_main_loop_run(loop);
|
g_main_loop_run(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FlEngineTest, SendKeyEvent) {
|
||||||
|
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
|
||||||
|
|
||||||
|
g_autoptr(FlEngine) engine = make_mock_engine();
|
||||||
|
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
|
||||||
|
|
||||||
|
bool called;
|
||||||
|
embedder_api->SendKeyEvent = MOCK_ENGINE_PROC(
|
||||||
|
SendKeyEvent,
|
||||||
|
([&called](auto engine, const FlutterKeyEvent* event,
|
||||||
|
FlutterKeyEventCallback callback, void* user_data) {
|
||||||
|
called = true;
|
||||||
|
EXPECT_EQ(event->timestamp, 1234);
|
||||||
|
EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
|
||||||
|
EXPECT_EQ(event->physical, static_cast<uint64_t>(42));
|
||||||
|
EXPECT_EQ(event->logical, static_cast<uint64_t>(123));
|
||||||
|
EXPECT_TRUE(event->synthesized);
|
||||||
|
EXPECT_EQ(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
|
||||||
|
callback(TRUE, user_data);
|
||||||
|
return kSuccess;
|
||||||
|
}));
|
||||||
|
|
||||||
|
FlutterKeyEvent event = {.struct_size = sizeof(FlutterKeyEvent),
|
||||||
|
.timestamp = 1234,
|
||||||
|
.type = kFlutterKeyEventTypeUp,
|
||||||
|
.physical = 42,
|
||||||
|
.logical = 123,
|
||||||
|
.character = nullptr,
|
||||||
|
.synthesized = true,
|
||||||
|
.device_type = kFlutterKeyEventDeviceTypeKeyboard};
|
||||||
|
fl_engine_send_key_event(
|
||||||
|
engine, &event, nullptr,
|
||||||
|
[](GObject* object, GAsyncResult* result, gpointer user_data) {
|
||||||
|
gboolean handled;
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
EXPECT_TRUE(fl_engine_send_key_event_finish(FL_ENGINE(object), result,
|
||||||
|
&handled, &error));
|
||||||
|
EXPECT_EQ(error, nullptr);
|
||||||
|
EXPECT_TRUE(handled);
|
||||||
|
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
|
||||||
|
},
|
||||||
|
loop);
|
||||||
|
|
||||||
|
g_main_loop_run(loop);
|
||||||
|
EXPECT_TRUE(called);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlEngineTest, SendKeyEventNotHandled) {
|
||||||
|
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
|
||||||
|
|
||||||
|
g_autoptr(FlEngine) engine = make_mock_engine();
|
||||||
|
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
|
||||||
|
|
||||||
|
bool called;
|
||||||
|
embedder_api->SendKeyEvent = MOCK_ENGINE_PROC(
|
||||||
|
SendKeyEvent,
|
||||||
|
([&called](auto engine, const FlutterKeyEvent* event,
|
||||||
|
FlutterKeyEventCallback callback, void* user_data) {
|
||||||
|
called = true;
|
||||||
|
callback(FALSE, user_data);
|
||||||
|
return kSuccess;
|
||||||
|
}));
|
||||||
|
|
||||||
|
FlutterKeyEvent event = {.struct_size = sizeof(FlutterKeyEvent),
|
||||||
|
.timestamp = 1234,
|
||||||
|
.type = kFlutterKeyEventTypeUp,
|
||||||
|
.physical = 42,
|
||||||
|
.logical = 123,
|
||||||
|
.character = nullptr,
|
||||||
|
.synthesized = true,
|
||||||
|
.device_type = kFlutterKeyEventDeviceTypeKeyboard};
|
||||||
|
fl_engine_send_key_event(
|
||||||
|
engine, &event, nullptr,
|
||||||
|
[](GObject* object, GAsyncResult* result, gpointer user_data) {
|
||||||
|
gboolean handled;
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
EXPECT_TRUE(fl_engine_send_key_event_finish(FL_ENGINE(object), result,
|
||||||
|
&handled, &error));
|
||||||
|
EXPECT_EQ(error, nullptr);
|
||||||
|
EXPECT_FALSE(handled);
|
||||||
|
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
|
||||||
|
},
|
||||||
|
loop);
|
||||||
|
|
||||||
|
g_main_loop_run(loop);
|
||||||
|
EXPECT_TRUE(called);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FlEngineTest, SendKeyEventError) {
|
||||||
|
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
|
||||||
|
|
||||||
|
g_autoptr(FlEngine) engine = make_mock_engine();
|
||||||
|
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
|
||||||
|
|
||||||
|
bool called;
|
||||||
|
embedder_api->SendKeyEvent = MOCK_ENGINE_PROC(
|
||||||
|
SendKeyEvent,
|
||||||
|
([&called](auto engine, const FlutterKeyEvent* event,
|
||||||
|
FlutterKeyEventCallback callback, void* user_data) {
|
||||||
|
called = true;
|
||||||
|
return kInvalidArguments;
|
||||||
|
}));
|
||||||
|
|
||||||
|
FlutterKeyEvent event = {.struct_size = sizeof(FlutterKeyEvent),
|
||||||
|
.timestamp = 1234,
|
||||||
|
.type = kFlutterKeyEventTypeUp,
|
||||||
|
.physical = 42,
|
||||||
|
.logical = 123,
|
||||||
|
.character = nullptr,
|
||||||
|
.synthesized = true,
|
||||||
|
.device_type = kFlutterKeyEventDeviceTypeKeyboard};
|
||||||
|
fl_engine_send_key_event(
|
||||||
|
engine, &event, nullptr,
|
||||||
|
[](GObject* object, GAsyncResult* result, gpointer user_data) {
|
||||||
|
gboolean handled;
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
EXPECT_FALSE(fl_engine_send_key_event_finish(FL_ENGINE(object), result,
|
||||||
|
&handled, &error));
|
||||||
|
EXPECT_NE(error, nullptr);
|
||||||
|
EXPECT_STREQ(error->message, "Failed to send key event");
|
||||||
|
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
|
||||||
|
},
|
||||||
|
loop);
|
||||||
|
|
||||||
|
g_main_loop_run(loop);
|
||||||
|
EXPECT_TRUE(called);
|
||||||
|
}
|
||||||
|
|
||||||
// NOLINTEND(clang-analyzer-core.StackAddressEscape)
|
// NOLINTEND(clang-analyzer-core.StackAddressEscape)
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#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_channel_responder.h"
|
||||||
#include "flutter/shell/platform/linux/fl_key_embedder_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_layout.h"
|
||||||
@ -479,8 +480,35 @@ FlKeyboardManager* fl_keyboard_manager_new(
|
|||||||
} else {
|
} else {
|
||||||
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
|
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
|
||||||
if (engine != nullptr) {
|
if (engine != nullptr) {
|
||||||
fl_engine_send_key_event(engine, event, callback,
|
typedef struct {
|
||||||
callback_user_data);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user