| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/shell/platform/linux/fl_mouse_cursor_plugin.h" |
| |
| #include <gtk/gtk.h> |
| #include <cstring> |
| |
| #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h" |
| #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" |
| |
| static constexpr char kChannelName[] = "flutter/mousecursor"; |
| static constexpr char kBadArgumentsError[] = "Bad Arguments"; |
| static constexpr char kActivateSystemCursorMethod[] = "activateSystemCursor"; |
| static constexpr char kKindKey[] = "kind"; |
| |
| static constexpr char kFallbackCursor[] = "default"; |
| |
| struct _FlMouseCursorPlugin { |
| GObject parent_instance; |
| |
| FlMethodChannel* channel; |
| |
| FlView* view; |
| |
| GHashTable* system_cursor_table; |
| }; |
| |
| G_DEFINE_TYPE(FlMouseCursorPlugin, fl_mouse_cursor_plugin, G_TYPE_OBJECT) |
| |
| // Insert a new entry into a hashtable from strings to strings. |
| // |
| // Returns whether the newly added value was already in the hash table or not. |
| static bool define_system_cursor(GHashTable* table, |
| const gchar* key, |
| const gchar* value) { |
| return g_hash_table_insert( |
| table, reinterpret_cast<gpointer>(const_cast<gchar*>(key)), |
| reinterpret_cast<gpointer>(const_cast<gchar*>(value))); |
| } |
| |
| // Populate the hash table so that it maps from Flutter's cursor kinds to GTK's |
| // cursor values. |
| // |
| // The table must have been created as a hashtable from strings to strings. |
| static void populate_system_cursor_table(GHashTable* table) { |
| // The following mapping must be kept in sync with Flutter framework's |
| // mouse_cursor.dart. |
| define_system_cursor(table, "alias", "alias"); |
| define_system_cursor(table, "allScroll", "all-scroll"); |
| define_system_cursor(table, "basic", "default"); |
| define_system_cursor(table, "cell", "cell"); |
| define_system_cursor(table, "click", "pointer"); |
| define_system_cursor(table, "contextMenu", "context-menu"); |
| define_system_cursor(table, "copy", "copy"); |
| define_system_cursor(table, "forbidden", "not-allowed"); |
| define_system_cursor(table, "grab", "grab"); |
| define_system_cursor(table, "grabbing", "grabbing"); |
| define_system_cursor(table, "help", "help"); |
| define_system_cursor(table, "move", "move"); |
| define_system_cursor(table, "none", "none"); |
| define_system_cursor(table, "noDrop", "no-drop"); |
| define_system_cursor(table, "precise", "crosshair"); |
| define_system_cursor(table, "progress", "progress"); |
| define_system_cursor(table, "text", "text"); |
| define_system_cursor(table, "resizeColumn", "col-resize"); |
| define_system_cursor(table, "resizeDown", "s-resize"); |
| define_system_cursor(table, "resizeDownLeft", "sw-resize"); |
| define_system_cursor(table, "resizeDownRight", "se-resize"); |
| define_system_cursor(table, "resizeLeft", "w-resize"); |
| define_system_cursor(table, "resizeLeftRight", "ew-resize"); |
| define_system_cursor(table, "resizeRight", "e-resize"); |
| define_system_cursor(table, "resizeRow", "row-resize"); |
| define_system_cursor(table, "resizeUp", "n-resize"); |
| define_system_cursor(table, "resizeUpDown", "ns-resize"); |
| define_system_cursor(table, "resizeUpLeft", "nw-resize"); |
| define_system_cursor(table, "resizeUpRight", "ne-resize"); |
| define_system_cursor(table, "resizeUpLeftDownRight", "nwse-resize"); |
| define_system_cursor(table, "resizeUpRightDownLeft", "nesw-resize"); |
| define_system_cursor(table, "verticalText", "vertical-text"); |
| define_system_cursor(table, "wait", "wait"); |
| define_system_cursor(table, "zoomIn", "zoom-in"); |
| define_system_cursor(table, "zoomOut", "zoom-out"); |
| } |
| |
| // Sets the mouse cursor. |
| FlMethodResponse* activate_system_cursor(FlMouseCursorPlugin* self, |
| FlValue* args) { |
| if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { |
| return FL_METHOD_RESPONSE(fl_method_error_response_new( |
| kBadArgumentsError, "Argument map missing or malformed", nullptr)); |
| } |
| |
| FlValue* kind_value = fl_value_lookup_string(args, kKindKey); |
| const gchar* kind = nullptr; |
| if (fl_value_get_type(kind_value) == FL_VALUE_TYPE_STRING) { |
| kind = fl_value_get_string(kind_value); |
| } |
| |
| if (self->system_cursor_table == nullptr) { |
| self->system_cursor_table = g_hash_table_new(g_str_hash, g_str_equal); |
| populate_system_cursor_table(self->system_cursor_table); |
| } |
| |
| const gchar* cursor_name = reinterpret_cast<const gchar*>( |
| g_hash_table_lookup(self->system_cursor_table, kind)); |
| if (cursor_name == nullptr) { |
| cursor_name = kFallbackCursor; |
| } |
| |
| GdkWindow* window = |
| gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(self->view))); |
| g_autoptr(GdkCursor) cursor = |
| gdk_cursor_new_from_name(gdk_window_get_display(window), cursor_name); |
| gdk_window_set_cursor(window, cursor); |
| |
| return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); |
| } |
| |
| // Called when a method call is received from Flutter. |
| static void method_call_cb(FlMethodChannel* channel, |
| FlMethodCall* method_call, |
| gpointer user_data) { |
| FlMouseCursorPlugin* self = FL_MOUSE_CURSOR_PLUGIN(user_data); |
| |
| const gchar* method = fl_method_call_get_name(method_call); |
| FlValue* args = fl_method_call_get_args(method_call); |
| |
| g_autoptr(FlMethodResponse) response = nullptr; |
| if (strcmp(method, kActivateSystemCursorMethod) == 0) { |
| response = activate_system_cursor(self, args); |
| } else { |
| response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); |
| } |
| |
| g_autoptr(GError) error = nullptr; |
| if (!fl_method_call_respond(method_call, response, &error)) { |
| g_warning("Failed to send method call response: %s", error->message); |
| } |
| } |
| |
| static void fl_mouse_cursor_plugin_dispose(GObject* object) { |
| FlMouseCursorPlugin* self = FL_MOUSE_CURSOR_PLUGIN(object); |
| |
| g_clear_object(&self->channel); |
| if (self->view != nullptr) { |
| g_object_remove_weak_pointer(G_OBJECT(self->view), |
| reinterpret_cast<gpointer*>(&(self->view))); |
| self->view = nullptr; |
| } |
| g_clear_pointer(&self->system_cursor_table, g_hash_table_unref); |
| |
| G_OBJECT_CLASS(fl_mouse_cursor_plugin_parent_class)->dispose(object); |
| } |
| |
| static void fl_mouse_cursor_plugin_class_init(FlMouseCursorPluginClass* klass) { |
| G_OBJECT_CLASS(klass)->dispose = fl_mouse_cursor_plugin_dispose; |
| } |
| |
| static void fl_mouse_cursor_plugin_init(FlMouseCursorPlugin* self) {} |
| |
| FlMouseCursorPlugin* fl_mouse_cursor_plugin_new(FlBinaryMessenger* messenger, |
| FlView* view) { |
| g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); |
| |
| FlMouseCursorPlugin* self = FL_MOUSE_CURSOR_PLUGIN( |
| g_object_new(fl_mouse_cursor_plugin_get_type(), nullptr)); |
| |
| g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); |
| self->channel = |
| fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec)); |
| fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self, |
| nullptr); |
| self->view = view; |
| if (view != nullptr) { |
| g_object_add_weak_pointer(G_OBJECT(view), |
| reinterpret_cast<gpointer*>(&(self->view))); |
| } |
| |
| return self; |
| } |