Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 3bba62b

Browse files
Make fl_engine_send_key_event into a standard async function. (#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.
1 parent e291650 commit 3bba62b

File tree

4 files changed

+223
-7
lines changed

4 files changed

+223
-7
lines changed

shell/platform/linux/fl_engine.cc

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,17 +1095,51 @@ void fl_engine_send_pointer_pan_zoom_event(FlEngine* self,
10951095
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
10961096
}
10971097

1098+
static void send_key_event_cb(bool handled, void* user_data) {
1099+
g_autoptr(GTask) task = G_TASK(user_data);
1100+
gboolean* return_value = g_new0(gboolean, 1);
1101+
*return_value = handled;
1102+
g_task_return_pointer(task, return_value, g_free);
1103+
}
1104+
10981105
void fl_engine_send_key_event(FlEngine* self,
10991106
const FlutterKeyEvent* event,
1100-
FlutterKeyEventCallback callback,
1101-
void* user_data) {
1107+
GCancellable* cancellable,
1108+
GAsyncReadyCallback callback,
1109+
gpointer user_data) {
11021110
g_return_if_fail(FL_IS_ENGINE(self));
11031111

1112+
g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data);
1113+
11041114
if (self->engine == nullptr) {
1115+
g_task_return_new_error(task, fl_engine_error_quark(),
1116+
FL_ENGINE_ERROR_FAILED, "No engine");
11051117
return;
11061118
}
11071119

1108-
self->embedder_api.SendKeyEvent(self->engine, event, callback, user_data);
1120+
if (self->embedder_api.SendKeyEvent(self->engine, event, send_key_event_cb,
1121+
g_object_ref(task)) != kSuccess) {
1122+
g_task_return_new_error(task, fl_engine_error_quark(),
1123+
FL_ENGINE_ERROR_FAILED, "Failed to send key event");
1124+
g_object_unref(task);
1125+
}
1126+
}
1127+
1128+
gboolean fl_engine_send_key_event_finish(FlEngine* self,
1129+
GAsyncResult* result,
1130+
gboolean* handled,
1131+
GError** error) {
1132+
g_return_val_if_fail(FL_IS_ENGINE(self), FALSE);
1133+
g_return_val_if_fail(g_task_is_valid(result, self), FALSE);
1134+
1135+
g_autofree gboolean* return_value =
1136+
static_cast<gboolean*>(g_task_propagate_pointer(G_TASK(result), error));
1137+
if (return_value == nullptr) {
1138+
return FALSE;
1139+
}
1140+
1141+
*handled = *return_value;
1142+
return TRUE;
11091143
}
11101144

11111145
void fl_engine_dispatch_semantics_action(FlEngine* self,

shell/platform/linux/fl_engine_private.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,37 @@ void fl_engine_send_pointer_pan_zoom_event(FlEngine* engine,
369369

370370
/**
371371
* fl_engine_send_key_event:
372+
* @engine: an #FlEngine.
373+
* @event: key event to send.
374+
* @cancellable: (allow-none): a #GCancellable or %NULL.
375+
* @callback: (scope async): a #GAsyncReadyCallback to call when the request is
376+
* satisfied.
377+
* @user_data: (closure): user data to pass to @callback.
378+
*
379+
* Send a key event to the engine.
372380
*/
373381
void fl_engine_send_key_event(FlEngine* engine,
374382
const FlutterKeyEvent* event,
375-
FlutterKeyEventCallback callback,
376-
void* user_data);
383+
GCancellable* cancellable,
384+
GAsyncReadyCallback callback,
385+
gpointer user_data);
386+
387+
/**
388+
* fl_engine_send_key_event_finish:
389+
* @engine: an #FlEngine.
390+
* @result: a #GAsyncResult.
391+
* @handled: location to write if this event was handled by the engine.
392+
* @error: (allow-none): #GError location to store the error occurring, or %NULL
393+
* to ignore.
394+
*
395+
* Completes request started with fl_engine_send_key_event().
396+
*
397+
* Returns: %TRUE on success.
398+
*/
399+
gboolean fl_engine_send_key_event_finish(FlEngine* engine,
400+
GAsyncResult* result,
401+
gboolean* handled,
402+
GError** error);
377403

378404
/**
379405
* fl_engine_dispatch_semantics_action:

shell/platform/linux/fl_engine_test.cc

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,4 +748,132 @@ TEST(FlEngineTest, RemoveViewEngineError) {
748748
g_main_loop_run(loop);
749749
}
750750

751+
TEST(FlEngineTest, SendKeyEvent) {
752+
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
753+
754+
g_autoptr(FlEngine) engine = make_mock_engine();
755+
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
756+
757+
bool called;
758+
embedder_api->SendKeyEvent = MOCK_ENGINE_PROC(
759+
SendKeyEvent,
760+
([&called](auto engine, const FlutterKeyEvent* event,
761+
FlutterKeyEventCallback callback, void* user_data) {
762+
called = true;
763+
EXPECT_EQ(event->timestamp, 1234);
764+
EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
765+
EXPECT_EQ(event->physical, static_cast<uint64_t>(42));
766+
EXPECT_EQ(event->logical, static_cast<uint64_t>(123));
767+
EXPECT_TRUE(event->synthesized);
768+
EXPECT_EQ(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
769+
callback(TRUE, user_data);
770+
return kSuccess;
771+
}));
772+
773+
FlutterKeyEvent event = {.struct_size = sizeof(FlutterKeyEvent),
774+
.timestamp = 1234,
775+
.type = kFlutterKeyEventTypeUp,
776+
.physical = 42,
777+
.logical = 123,
778+
.character = nullptr,
779+
.synthesized = true,
780+
.device_type = kFlutterKeyEventDeviceTypeKeyboard};
781+
fl_engine_send_key_event(
782+
engine, &event, nullptr,
783+
[](GObject* object, GAsyncResult* result, gpointer user_data) {
784+
gboolean handled;
785+
g_autoptr(GError) error = nullptr;
786+
EXPECT_TRUE(fl_engine_send_key_event_finish(FL_ENGINE(object), result,
787+
&handled, &error));
788+
EXPECT_EQ(error, nullptr);
789+
EXPECT_TRUE(handled);
790+
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
791+
},
792+
loop);
793+
794+
g_main_loop_run(loop);
795+
EXPECT_TRUE(called);
796+
}
797+
798+
TEST(FlEngineTest, SendKeyEventNotHandled) {
799+
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
800+
801+
g_autoptr(FlEngine) engine = make_mock_engine();
802+
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
803+
804+
bool called;
805+
embedder_api->SendKeyEvent = MOCK_ENGINE_PROC(
806+
SendKeyEvent,
807+
([&called](auto engine, const FlutterKeyEvent* event,
808+
FlutterKeyEventCallback callback, void* user_data) {
809+
called = true;
810+
callback(FALSE, user_data);
811+
return kSuccess;
812+
}));
813+
814+
FlutterKeyEvent event = {.struct_size = sizeof(FlutterKeyEvent),
815+
.timestamp = 1234,
816+
.type = kFlutterKeyEventTypeUp,
817+
.physical = 42,
818+
.logical = 123,
819+
.character = nullptr,
820+
.synthesized = true,
821+
.device_type = kFlutterKeyEventDeviceTypeKeyboard};
822+
fl_engine_send_key_event(
823+
engine, &event, nullptr,
824+
[](GObject* object, GAsyncResult* result, gpointer user_data) {
825+
gboolean handled;
826+
g_autoptr(GError) error = nullptr;
827+
EXPECT_TRUE(fl_engine_send_key_event_finish(FL_ENGINE(object), result,
828+
&handled, &error));
829+
EXPECT_EQ(error, nullptr);
830+
EXPECT_FALSE(handled);
831+
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
832+
},
833+
loop);
834+
835+
g_main_loop_run(loop);
836+
EXPECT_TRUE(called);
837+
}
838+
839+
TEST(FlEngineTest, SendKeyEventError) {
840+
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
841+
842+
g_autoptr(FlEngine) engine = make_mock_engine();
843+
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
844+
845+
bool called;
846+
embedder_api->SendKeyEvent = MOCK_ENGINE_PROC(
847+
SendKeyEvent,
848+
([&called](auto engine, const FlutterKeyEvent* event,
849+
FlutterKeyEventCallback callback, void* user_data) {
850+
called = true;
851+
return kInvalidArguments;
852+
}));
853+
854+
FlutterKeyEvent event = {.struct_size = sizeof(FlutterKeyEvent),
855+
.timestamp = 1234,
856+
.type = kFlutterKeyEventTypeUp,
857+
.physical = 42,
858+
.logical = 123,
859+
.character = nullptr,
860+
.synthesized = true,
861+
.device_type = kFlutterKeyEventDeviceTypeKeyboard};
862+
fl_engine_send_key_event(
863+
engine, &event, nullptr,
864+
[](GObject* object, GAsyncResult* result, gpointer user_data) {
865+
gboolean handled;
866+
g_autoptr(GError) error = nullptr;
867+
EXPECT_FALSE(fl_engine_send_key_event_finish(FL_ENGINE(object), result,
868+
&handled, &error));
869+
EXPECT_NE(error, nullptr);
870+
EXPECT_STREQ(error->message, "Failed to send key event");
871+
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
872+
},
873+
loop);
874+
875+
g_main_loop_run(loop);
876+
EXPECT_TRUE(called);
877+
}
878+
751879
// NOLINTEND(clang-analyzer-core.StackAddressEscape)

shell/platform/linux/fl_keyboard_manager.cc

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <memory>
1010
#include <string>
1111

12+
#include "flutter/shell/platform/linux/fl_engine_private.h"
1213
#include "flutter/shell/platform/linux/fl_key_channel_responder.h"
1314
#include "flutter/shell/platform/linux/fl_key_embedder_responder.h"
1415
#include "flutter/shell/platform/linux/fl_keyboard_layout.h"
@@ -479,8 +480,35 @@ FlKeyboardManager* fl_keyboard_manager_new(
479480
} else {
480481
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
481482
if (engine != nullptr) {
482-
fl_engine_send_key_event(engine, event, callback,
483-
callback_user_data);
483+
typedef struct {
484+
FlutterKeyEventCallback callback;
485+
void* callback_user_data;
486+
} SendKeyEventData;
487+
SendKeyEventData* data = g_new0(SendKeyEventData, 1);
488+
data->callback = callback;
489+
data->callback_user_data = callback_user_data;
490+
fl_engine_send_key_event(
491+
engine, event, self->cancellable,
492+
[](GObject* object, GAsyncResult* result, gpointer user_data) {
493+
g_autofree SendKeyEventData* data =
494+
static_cast<SendKeyEventData*>(user_data);
495+
gboolean handled = FALSE;
496+
g_autoptr(GError) error = nullptr;
497+
if (!fl_engine_send_key_event_finish(
498+
FL_ENGINE(object), result, &handled, &error)) {
499+
if (g_error_matches(error, G_IO_ERROR,
500+
G_IO_ERROR_CANCELLED)) {
501+
return;
502+
}
503+
504+
g_warning("Failed to send key event: %s", error->message);
505+
}
506+
507+
if (data->callback != nullptr) {
508+
data->callback(handled, data->callback_user_data);
509+
}
510+
},
511+
data);
484512
}
485513
}
486514
},

0 commit comments

Comments
 (0)