diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5287e39be53f2..81ee38f4c64a9 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1227,6 +1227,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_method_codec_private.h FILE: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_method_response.cc FILE: ../../../flutter/shell/platform/linux/fl_method_response_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_platform_plugin.cc +FILE: ../../../flutter/shell/platform/linux/fl_platform_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar.cc FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h FILE: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index adcfa6719d705..512dd834aceb7 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -89,6 +89,7 @@ source_set("flutter_linux_sources") { "fl_method_channel.cc", "fl_method_codec.cc", "fl_method_response.cc", + "fl_platform_plugin.cc", "fl_plugin_registrar.cc", "fl_plugin_registry.cc", "fl_renderer.cc", diff --git a/shell/platform/linux/fl_platform_plugin.cc b/shell/platform/linux/fl_platform_plugin.cc new file mode 100644 index 0000000000000..8d9ab91577f2a --- /dev/null +++ b/shell/platform/linux/fl_platform_plugin.cc @@ -0,0 +1,171 @@ +// 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_platform_plugin.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h" + +#include + +static constexpr char kChannelName[] = "flutter/platform"; +static constexpr char kBadArgumentsError[] = "Bad Arguments"; +static constexpr char kUnknownClipboardFormatError[] = + "Unknown Clipboard Format"; +static constexpr char kClipboardRequestError[] = "Clipboard Request Failed"; +static constexpr char kFailedError[] = "Failed"; +static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData"; +static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData"; +static constexpr char kSystemNavigatorPopMethod[] = "SystemNavigator.pop"; +static constexpr char kTextKey[] = "text"; + +static constexpr char kTextPlainFormat[] = "text/plain"; + +struct _FlPlatformPlugin { + GObject parent_instance; + + FlMethodChannel* channel; +}; + +G_DEFINE_TYPE(FlPlatformPlugin, fl_platform_plugin, G_TYPE_OBJECT) + +// Sends the method call response to Flutter. +static void send_response(FlMethodCall* method_call, + FlMethodResponse* response) { + 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); +} + +// Called when clipboard text received. +static void clipboard_text_cb(GtkClipboard* clipboard, + const gchar* text, + gpointer user_data) { + g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data); + + g_autoptr(FlMethodResponse) response = nullptr; + if (text != nullptr) { + g_autoptr(FlValue) result = fl_value_new_map(); + fl_value_set_string_take(result, kTextKey, fl_value_new_string(text)); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + } else { + response = FL_METHOD_RESPONSE(fl_method_error_response_new( + kClipboardRequestError, "Failed to retrieve clipboard text from GTK", + nullptr)); + } + + send_response(method_call, response); +} + +// Called when Flutter wants to copy to the clipboard. +static FlMethodResponse* clipboard_set_data(FlPlatformPlugin* 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* text_value = fl_value_lookup_string(args, kTextKey); + if (text_value == nullptr || + fl_value_get_type(text_value) != FL_VALUE_TYPE_STRING) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Missing clipboard text", nullptr)); + } + + GtkClipboard* clipboard = + gtk_clipboard_get_default(gdk_display_get_default()); + gtk_clipboard_set_text(clipboard, fl_value_get_string(text_value), -1); + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); +} + +// Called when Flutter wants to paste from the clipboard. +static FlMethodResponse* clipboard_get_data_async(FlPlatformPlugin* self, + FlMethodCall* method_call) { + FlValue* args = fl_method_call_get_args(method_call); + + if (fl_value_get_type(args) != FL_VALUE_TYPE_STRING) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Expected string", nullptr)); + } + + const gchar* format = fl_value_get_string(args); + if (strcmp(format, kTextPlainFormat) != 0) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kUnknownClipboardFormatError, "GTK clipboard API only supports text", + nullptr)); + } + + GtkClipboard* clipboard = + gtk_clipboard_get_default(gdk_display_get_default()); + gtk_clipboard_request_text(clipboard, clipboard_text_cb, + g_object_ref(method_call)); + + // Will response later. + return nullptr; +} + +// Called when Flutter wants to quit the application. +static FlMethodResponse* system_navigator_pop(FlPlatformPlugin* self) { + GApplication* app = g_application_get_default(); + if (app == nullptr) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kFailedError, "Unable to get GApplication", nullptr)); + } + + g_application_quit(app); + + 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) { + FlPlatformPlugin* self = FL_PLATFORM_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, kSetClipboardDataMethod) == 0) + response = clipboard_set_data(self, args); + else if (strcmp(method, kGetClipboardDataMethod) == 0) + response = clipboard_get_data_async(self, method_call); + else if (strcmp(method, kSystemNavigatorPopMethod) == 0) + response = system_navigator_pop(self); + else + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + + if (response != nullptr) + send_response(method_call, response); +} + +static void fl_platform_plugin_dispose(GObject* object) { + FlPlatformPlugin* self = FL_PLATFORM_PLUGIN(object); + + g_clear_object(&self->channel); + + G_OBJECT_CLASS(fl_platform_plugin_parent_class)->dispose(object); +} + +static void fl_platform_plugin_class_init(FlPlatformPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_platform_plugin_dispose; +} + +static void fl_platform_plugin_init(FlPlatformPlugin* self) {} + +FlPlatformPlugin* fl_platform_plugin_new(FlBinaryMessenger* messenger) { + g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); + + FlPlatformPlugin* self = + FL_PLATFORM_PLUGIN(g_object_new(fl_platform_plugin_get_type(), nullptr)); + + g_autoptr(FlJsonMethodCodec) codec = fl_json_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); + + return self; +} diff --git a/shell/platform/linux/fl_platform_plugin.h b/shell/platform/linux/fl_platform_plugin.h new file mode 100644 index 0000000000000..bff6605b7678e --- /dev/null +++ b/shell/platform/linux/fl_platform_plugin.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_PLUGIN_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_PLUGIN_H_ + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlPlatformPlugin, + fl_platform_plugin, + FL, + PLATFORM_PLUGIN, + GObject); + +/** + * FlPlatformPlugin: + * + * #FlPlatformPlugin is a plugin that implements the shell side + * of PlatformPlugins.platform from the Flutter services library. + */ + +/** + * fl_platform_plugin_new: + * @messenger: an #FlBinaryMessenger + * + * Creates a new plugin that implements SystemChannels.platform from the + * Flutter services library. + * + * Returns: a new #FlPlatformPlugin + */ +FlPlatformPlugin* fl_platform_plugin_new(FlBinaryMessenger* messenger); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_PLUGIN_H_ diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 6cdd74ac425e0..623ff02ab67d8 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -6,6 +6,7 @@ #include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_key_event_plugin.h" +#include "flutter/shell/platform/linux/fl_platform_plugin.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" #include "flutter/shell/platform/linux/fl_renderer_x11.h" #include "flutter/shell/platform/linux/fl_text_input_plugin.h" @@ -33,6 +34,7 @@ struct _FlView { // Flutter system channel handlers. FlKeyEventPlugin* key_event_plugin; + FlPlatformPlugin* platform_plugin; FlTextInputPlugin* text_input_plugin; }; @@ -129,6 +131,7 @@ static void fl_view_constructed(GObject* object) { // Create system channel handlers. FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine); self->key_event_plugin = fl_key_event_plugin_new(messenger); + self->platform_plugin = fl_platform_plugin_new(messenger); self->text_input_plugin = fl_text_input_plugin_new(messenger); } @@ -183,6 +186,7 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->renderer); g_clear_object(&self->engine); g_clear_object(&self->key_event_plugin); + g_clear_object(&self->platform_plugin); g_clear_object(&self->text_input_plugin); G_OBJECT_CLASS(fl_view_parent_class)->dispose(object);