diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 681dd8f3e0496..4cf987fba6623 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -54,6 +54,9 @@ struct _FlEngine { // Implements the flutter/platform channel. FlPlatformHandler* platform_handler; + // Implements the flutter/mousecursor channel. + FlMouseCursorHandler* mouse_cursor_handler; + // Manages textures rendered by native code. FlTextureRegistrar* texture_registrar; @@ -449,6 +452,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) { @@ -629,6 +633,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) { @@ -1051,3 +1057,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 f32557902567f..761b9bbd30b13 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" @@ -424,6 +425,16 @@ void fl_engine_update_accessibility_features(FlEngine* engine, int32_t flags); */ 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 92a172b5953f3..a5aca2279a0c4 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -16,7 +16,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_pointer_manager.h" #include "flutter/shell/platform/linux/fl_renderer_gdk.h" @@ -68,11 +67,13 @@ struct _FlView { // Flutter system channel handlers. FlKeyboardHandler* keyboard_handler; FlTextInputHandler* text_input_handler; - FlMouseCursorHandler* mouse_cursor_handler; // Accessible tree from Flutter, exposed as an AtkPlug. FlViewAccessible* view_accessible; + // Signal subscripton for cursor changes. + guint cursor_changed_cb_id; + GCancellable* cancellable; }; @@ -184,6 +185,28 @@ static gboolean get_mouse_button(GdkEvent* event, int64_t* button) { } } +// 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; @@ -457,10 +480,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)) { @@ -505,6 +525,8 @@ static void realize_cb(FlView* self) { return; } + setup_cursor(self); + handle_geometry_changed(self); self->view_accessible = fl_view_accessible_new(self->engine); @@ -567,6 +589,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); @@ -589,7 +618,6 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->pointer_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); @@ -750,6 +778,8 @@ G_MODULE_EXPORT FlView* fl_view_new_for_engine(FlEngine* engine) { self->pointer_manager = fl_pointer_manager_new(self->view_id, engine); + setup_cursor(self); + return self; } 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) {}