From a2918c806fcd2e56f8c1d2bb98c1821e77714d4a Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 22 Oct 2024 15:25:42 +0200 Subject: [PATCH 1/3] Move platform handler from FlView to FlEngine --- shell/platform/linux/fl_engine.cc | 12 ++++++++++++ shell/platform/linux/fl_engine_private.h | 10 +++++++++- shell/platform/linux/fl_view.cc | 6 +----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index c56193e1d504b..2592c63b730d8 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -15,6 +15,7 @@ #include "flutter/shell/platform/linux/fl_dart_project_private.h" #include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h" +#include "flutter/shell/platform/linux/fl_platform_handler.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" #include "flutter/shell/platform/linux/fl_renderer.h" #include "flutter/shell/platform/linux/fl_renderer_gdk.h" @@ -42,6 +43,7 @@ struct _FlEngine { FlRenderer* renderer; FlBinaryMessenger* binary_messenger; FlSettingsHandler* settings_handler; + FlPlatformHandler* platform_handler; FlTextureRegistrar* texture_registrar; FlTaskRunner* task_runner; FlutterEngineAOTData aot_data; @@ -426,6 +428,7 @@ static void fl_engine_dispose(GObject* object) { g_clear_object(&self->texture_registrar); g_clear_object(&self->binary_messenger); g_clear_object(&self->settings_handler); + g_clear_object(&self->platform_handler); g_clear_object(&self->task_runner); if (self->platform_message_handler_destroy_notify) { @@ -604,6 +607,8 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { self->settings_handler = fl_settings_handler_new(self); fl_settings_handler_start(self->settings_handler, settings); + self->platform_handler = fl_platform_handler_new(self->binary_messenger); + result = self->embedder_api.UpdateSemanticsEnabled(self->engine, TRUE); if (result != kSuccess) { g_warning("Failed to enable accessibility features on Flutter engine"); @@ -1022,9 +1027,16 @@ void fl_engine_update_accessibility_features(FlEngine* self, int32_t flags) { } GPtrArray* fl_engine_get_switches(FlEngine* self) { + g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); + GPtrArray* switches = g_ptr_array_new_with_free_func(g_free); for (const auto& env_switch : flutter::GetSwitchesFromEnvironment()) { g_ptr_array_add(switches, g_strdup(env_switch.c_str())); } return switches; } + +void fl_engine_request_app_exit(FlEngine* self) { + g_return_if_fail(FL_IS_ENGINE(self)); + fl_platform_handler_request_app_exit(self->platform_handler); +} diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h index 6980ae4019db1..ed5d3197f04ec 100644 --- a/shell/platform/linux/fl_engine_private.h +++ b/shell/platform/linux/fl_engine_private.h @@ -418,7 +418,7 @@ void fl_engine_update_accessibility_features(FlEngine* engine, int32_t flags); /** * fl_engine_get_switches: - * @project: an #FlEngine. + * @engine: an #FlEngine. * * Determines the switches that should be passed to the Flutter engine. * @@ -426,6 +426,14 @@ void fl_engine_update_accessibility_features(FlEngine* engine, int32_t flags); */ GPtrArray* fl_engine_get_switches(FlEngine* engine); +/** + * fl_engine_request_app_exit: + * @engine: an #FlEngine. + * + * Request the application exits. + */ +void fl_engine_request_app_exit(FlEngine* engine); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_ diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 58d234601f2be..c39d11aa031cf 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -20,7 +20,6 @@ #include "flutter/shell/platform/linux/fl_keyboard_manager.h" #include "flutter/shell/platform/linux/fl_keyboard_view_delegate.h" #include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h" -#include "flutter/shell/platform/linux/fl_platform_handler.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" #include "flutter/shell/platform/linux/fl_renderer_gdk.h" #include "flutter/shell/platform/linux/fl_scrolling_manager.h" @@ -70,7 +69,6 @@ struct _FlView { FlKeyboardHandler* keyboard_handler; FlTextInputHandler* text_input_handler; FlMouseCursorHandler* mouse_cursor_handler; - FlPlatformHandler* platform_handler; GtkWidget* event_box; GtkGLArea* gl_area; @@ -128,7 +126,7 @@ static gboolean first_frame_idle_cb(gpointer user_data) { // Signal handler for GtkWidget::delete-event static gboolean window_delete_event_cb(FlView* self) { - fl_platform_handler_request_app_exit(self->platform_handler); + fl_engine_request_app_exit(self->engine); // Stop the event from propagating. return TRUE; } @@ -573,7 +571,6 @@ static GdkGLContext* create_context_cb(FlView* self) { FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine); init_scrolling(self); self->mouse_cursor_handler = fl_mouse_cursor_handler_new(messenger, self); - self->platform_handler = fl_platform_handler_new(messenger); g_autoptr(GError) error = nullptr; if (!fl_renderer_gdk_create_contexts(self->renderer, &error)) { @@ -702,7 +699,6 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->keyboard_manager); g_clear_object(&self->keyboard_handler); g_clear_object(&self->mouse_cursor_handler); - g_clear_object(&self->platform_handler); g_clear_object(&self->view_accessible); g_clear_object(&self->cancellable); From 388d50be03d92cca68db460a5db5f75482d904ab Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 22 Oct 2024 16:22:12 +0200 Subject: [PATCH 2/3] Move FlMouseCursorHandler from FlView to FlEngine --- shell/platform/linux/fl_engine.cc | 9 ++++ shell/platform/linux/fl_engine_private.h | 11 +++++ .../platform/linux/fl_mouse_cursor_handler.cc | 43 ++++++++++++------- .../platform/linux/fl_mouse_cursor_handler.h | 16 +++++-- shell/platform/linux/fl_view.cc | 42 +++++++++++++++--- 5 files changed, 95 insertions(+), 26 deletions(-) diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 2592c63b730d8..5f19d19ae7f2a 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -44,6 +44,7 @@ struct _FlEngine { FlBinaryMessenger* binary_messenger; FlSettingsHandler* settings_handler; FlPlatformHandler* platform_handler; + FlMouseCursorHandler* mouse_cursor_handler; FlTextureRegistrar* texture_registrar; FlTaskRunner* task_runner; FlutterEngineAOTData aot_data; @@ -429,6 +430,7 @@ static void fl_engine_dispose(GObject* object) { g_clear_object(&self->binary_messenger); g_clear_object(&self->settings_handler); g_clear_object(&self->platform_handler); + g_clear_object(&self->mouse_cursor_handler); g_clear_object(&self->task_runner); if (self->platform_message_handler_destroy_notify) { @@ -608,6 +610,8 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { fl_settings_handler_start(self->settings_handler, settings); self->platform_handler = fl_platform_handler_new(self->binary_messenger); + self->mouse_cursor_handler = + fl_mouse_cursor_handler_new(self->binary_messenger); result = self->embedder_api.UpdateSemanticsEnabled(self->engine, TRUE); if (result != kSuccess) { @@ -1040,3 +1044,8 @@ void fl_engine_request_app_exit(FlEngine* self) { g_return_if_fail(FL_IS_ENGINE(self)); fl_platform_handler_request_app_exit(self->platform_handler); } + +FlMouseCursorHandler* fl_engine_get_mouse_cursor_handler(FlEngine* self) { + g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); + return self->mouse_cursor_handler; +} diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h index ed5d3197f04ec..803d13591c750 100644 --- a/shell/platform/linux/fl_engine_private.h +++ b/shell/platform/linux/fl_engine_private.h @@ -8,6 +8,7 @@ #include #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h" #include "flutter/shell/platform/linux/fl_renderer.h" #include "flutter/shell/platform/linux/fl_task_runner.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" @@ -434,6 +435,16 @@ GPtrArray* fl_engine_get_switches(FlEngine* engine); */ void fl_engine_request_app_exit(FlEngine* engine); +/** + * fl_engine_get_mouse_cursor_handler: + * @engine: an #FlEngine. + * + * Gets the mouse cursor handler used by this engine. + * + * Returns: a #FlMouseCursorHandler. + */ +FlMouseCursorHandler* fl_engine_get_mouse_cursor_handler(FlEngine* engine); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_ diff --git a/shell/platform/linux/fl_mouse_cursor_handler.cc b/shell/platform/linux/fl_mouse_cursor_handler.cc index d8fdc8c395ab1..3b87567fa2083 100644 --- a/shell/platform/linux/fl_mouse_cursor_handler.cc +++ b/shell/platform/linux/fl_mouse_cursor_handler.cc @@ -4,7 +4,6 @@ #include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h" -#include #include #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h" @@ -22,11 +21,16 @@ struct _FlMouseCursorHandler { FlMethodChannel* channel; - GWeakRef view; - GHashTable* system_cursor_table; + + // The current cursor. + gchar* cursor_name; }; +enum { kSignalCursorChanged, kSignalLastSignal }; + +static guint fl_mouse_cursor_handler_signals[kSignalLastSignal]; + G_DEFINE_TYPE(FlMouseCursorHandler, fl_mouse_cursor_handler, G_TYPE_OBJECT) // Insert a new entry into a hashtable from strings to strings. @@ -109,14 +113,10 @@ FlMethodResponse* activate_system_cursor(FlMouseCursorHandler* self, cursor_name = kFallbackCursor; } - g_autoptr(FlView) view = FL_VIEW(g_weak_ref_get(&self->view)); - if (view != nullptr) { - GdkWindow* window = - gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(view))); - g_autoptr(GdkCursor) cursor = - gdk_cursor_new_from_name(gdk_window_get_display(window), cursor_name); - gdk_window_set_cursor(window, cursor); - } + g_free(self->cursor_name); + self->cursor_name = g_strdup(cursor_name); + + g_signal_emit(self, fl_mouse_cursor_handler_signals[kSignalCursorChanged], 0); return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); } @@ -147,8 +147,8 @@ static void fl_mouse_cursor_handler_dispose(GObject* object) { FlMouseCursorHandler* self = FL_MOUSE_CURSOR_HANDLER(object); g_clear_object(&self->channel); - g_weak_ref_clear(&self->view); g_clear_pointer(&self->system_cursor_table, g_hash_table_unref); + g_clear_pointer(&self->cursor_name, g_free); G_OBJECT_CLASS(fl_mouse_cursor_handler_parent_class)->dispose(object); } @@ -156,12 +156,18 @@ static void fl_mouse_cursor_handler_dispose(GObject* object) { static void fl_mouse_cursor_handler_class_init( FlMouseCursorHandlerClass* klass) { G_OBJECT_CLASS(klass)->dispose = fl_mouse_cursor_handler_dispose; + + fl_mouse_cursor_handler_signals[kSignalCursorChanged] = + g_signal_new("cursor-changed", fl_mouse_cursor_handler_get_type(), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } -static void fl_mouse_cursor_handler_init(FlMouseCursorHandler* self) {} +static void fl_mouse_cursor_handler_init(FlMouseCursorHandler* self) { + self->cursor_name = g_strdup(""); +} -FlMouseCursorHandler* fl_mouse_cursor_handler_new(FlBinaryMessenger* messenger, - FlView* view) { +FlMouseCursorHandler* fl_mouse_cursor_handler_new( + FlBinaryMessenger* messenger) { g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); FlMouseCursorHandler* self = FL_MOUSE_CURSOR_HANDLER( @@ -172,7 +178,12 @@ FlMouseCursorHandler* fl_mouse_cursor_handler_new(FlBinaryMessenger* messenger, fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec)); fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self, nullptr); - g_weak_ref_init(&self->view, view); return self; } + +const gchar* fl_mouse_cursor_handler_get_cursor_name( + FlMouseCursorHandler* self) { + g_return_val_if_fail(FL_IS_MOUSE_CURSOR_HANDLER(self), nullptr); + return self->cursor_name; +} diff --git a/shell/platform/linux/fl_mouse_cursor_handler.h b/shell/platform/linux/fl_mouse_cursor_handler.h index 7ec7b6412fa1b..88942a94c10f6 100644 --- a/shell/platform/linux/fl_mouse_cursor_handler.h +++ b/shell/platform/linux/fl_mouse_cursor_handler.h @@ -8,7 +8,6 @@ #include #include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" G_BEGIN_DECLS @@ -28,15 +27,24 @@ G_DECLARE_FINAL_TYPE(FlMouseCursorHandler, /** * fl_mouse_cursor_handler_new: * @messenger: an #FlBinaryMessenger. - * @view: an #FlView to control. * * Creates a new handler that implements SystemChannels.mouseCursor from the * Flutter services library. * * Returns: a new #FlMouseCursorHandler. */ -FlMouseCursorHandler* fl_mouse_cursor_handler_new(FlBinaryMessenger* messenger, - FlView* view); +FlMouseCursorHandler* fl_mouse_cursor_handler_new(FlBinaryMessenger* messenger); + +/** + * fl_mouse_cursor_handler_get_cursor_name: + * @handler: an #FlMouseCursorHandler. + * + * Get the name of the current mouse cursor. + * + * Returns: a mouse cursor name. + */ +const gchar* fl_mouse_cursor_handler_get_cursor_name( + FlMouseCursorHandler* handler); G_END_DECLS diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index c39d11aa031cf..f329eb3d6a4a0 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -19,7 +19,6 @@ #include "flutter/shell/platform/linux/fl_keyboard_handler.h" #include "flutter/shell/platform/linux/fl_keyboard_manager.h" #include "flutter/shell/platform/linux/fl_keyboard_view_delegate.h" -#include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" #include "flutter/shell/platform/linux/fl_renderer_gdk.h" #include "flutter/shell/platform/linux/fl_scrolling_manager.h" @@ -68,7 +67,6 @@ struct _FlView { // Flutter system channel handlers. FlKeyboardHandler* keyboard_handler; FlTextInputHandler* text_input_handler; - FlMouseCursorHandler* mouse_cursor_handler; GtkWidget* event_box; GtkGLArea* gl_area; @@ -79,6 +77,9 @@ struct _FlView { // Accessible tree from Flutter, exposed as an AtkPlug. FlViewAccessible* view_accessible; + // Signal subscripton for cursor changes. + guint cursor_changed_cb_id; + GCancellable* cancellable; }; @@ -258,6 +259,28 @@ static void check_pointer_inside(FlView* self, GdkEvent* event) { } } +// Called when the mouse cursor changes. +static void cursor_changed_cb(FlView* self) { + FlMouseCursorHandler* handler = + fl_engine_get_mouse_cursor_handler(self->engine); + const gchar* cursor_name = fl_mouse_cursor_handler_get_cursor_name(handler); + GdkWindow* window = + gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(self))); + g_autoptr(GdkCursor) cursor = + gdk_cursor_new_from_name(gdk_window_get_display(window), cursor_name); + gdk_window_set_cursor(window, cursor); +} + +// Set the mouse cursor. +static void setup_cursor(FlView* self) { + FlMouseCursorHandler* handler = + fl_engine_get_mouse_cursor_handler(self->engine); + + self->cursor_changed_cb_id = g_signal_connect_swapped( + handler, "cursor-changed", G_CALLBACK(cursor_changed_cb), self); + cursor_changed_cb(self); +} + // Updates the engine with the current window metrics. static void handle_geometry_changed(FlView* self) { GtkAllocation allocation; @@ -567,10 +590,7 @@ static GdkGLContext* create_context_cb(FlView* self) { fl_renderer_gdk_set_window(self->renderer, gtk_widget_get_parent_window(GTK_WIDGET(self))); - // Create system channel handlers. - FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine); init_scrolling(self); - self->mouse_cursor_handler = fl_mouse_cursor_handler_new(messenger, self); g_autoptr(GError) error = nullptr; if (!fl_renderer_gdk_create_contexts(self->renderer, &error)) { @@ -615,6 +635,8 @@ static void realize_cb(FlView* self) { return; } + setup_cursor(self); + handle_geometry_changed(self); self->view_accessible = fl_view_accessible_new(self->engine); @@ -677,6 +699,13 @@ static void fl_view_dispose(GObject* object) { fl_engine_set_update_semantics_handler(self->engine, nullptr, nullptr, nullptr); + FlMouseCursorHandler* handler = + fl_engine_get_mouse_cursor_handler(self->engine); + if (self->cursor_changed_cb_id != 0) { + g_signal_handler_disconnect(handler, self->cursor_changed_cb_id); + self->cursor_changed_cb_id = 0; + } + // Stop rendering. fl_renderer_remove_view(FL_RENDERER(self->renderer), self->view_id); @@ -698,7 +727,6 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->scrolling_manager); g_clear_object(&self->keyboard_manager); g_clear_object(&self->keyboard_handler); - g_clear_object(&self->mouse_cursor_handler); g_clear_object(&self->view_accessible); g_clear_object(&self->cancellable); @@ -852,6 +880,8 @@ G_MODULE_EXPORT FlView* fl_view_new_for_engine(FlEngine* engine) { fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id, FL_RENDERABLE(self)); + setup_cursor(self); + return self; } From a6b2fbacd0e5c79b80971e0318af43c86c2b7461 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 23 Oct 2024 13:36:52 +0200 Subject: [PATCH 3/3] Fix tests --- shell/platform/linux/fl_view_test.cc | 12 ++++++++++++ shell/platform/linux/testing/mock_window.cc | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/shell/platform/linux/fl_view_test.cc b/shell/platform/linux/fl_view_test.cc index 04c33ac55485b..e6a51057c68c1 100644 --- a/shell/platform/linux/fl_view_test.cc +++ b/shell/platform/linux/fl_view_test.cc @@ -82,6 +82,9 @@ TEST(FlViewTest, SecondaryView) { return kSuccess; })); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + FlView* secondary_view = fl_view_new_for_engine(engine); EXPECT_EQ(view_id, fl_view_get_id(secondary_view)); } @@ -103,6 +106,9 @@ TEST(FlViewTest, SecondaryViewError) { return kInvalidArguments; })); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + FlView* secondary_view = fl_view_new_for_engine(engine); EXPECT_EQ(view_id, fl_view_get_id(secondary_view)); } @@ -125,6 +131,9 @@ TEST(FlViewTest, ViewDestroy) { return kSuccess; })); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + FlView* secondary_view = fl_view_new_for_engine(engine); int64_t implicit_view_id = fl_view_get_id(implicit_view); @@ -155,6 +164,9 @@ TEST(FlViewTest, ViewDestroyError) { return kInvalidArguments; })); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + FlView* secondary_view = fl_view_new_for_engine(engine); gtk_widget_destroy(GTK_WIDGET(secondary_view)); diff --git a/shell/platform/linux/testing/mock_window.cc b/shell/platform/linux/testing/mock_window.cc index 9e86bc79cf45f..89902307ed38c 100644 --- a/shell/platform/linux/testing/mock_window.cc +++ b/shell/platform/linux/testing/mock_window.cc @@ -15,3 +15,18 @@ MockWindow::MockWindow() { GdkWindowState gdk_window_get_state(GdkWindow* window) { return mock->gdk_window_get_state(window); } + +GdkDisplay* gdk_window_get_display(GdkWindow* window) { + return nullptr; +} + +GdkMonitor* gdk_display_get_monitor_at_window(GdkDisplay* display, + GdkWindow* window) { + return nullptr; +} + +GdkCursor* gdk_cursor_new_from_name(GdkDisplay* display, const gchar* name) { + return nullptr; +} + +void gdk_window_set_cursor(GdkWindow* window, GdkCursor* cursor) {}