From 4df25523ba7830a9d475908c72dd356a4a518a5d Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 12 May 2020 14:44:06 +1200 Subject: [PATCH 1/5] Add FlPlatformPlugin --- ci/licenses_golden/licenses_flutter | 2 + shell/platform/linux/BUILD.gn | 1 + shell/platform/linux/fl_platform_plugin.cc | 172 +++++++++++++++++++++ shell/platform/linux/fl_platform_plugin.h | 37 +++++ shell/platform/linux/fl_view.cc | 4 + 5 files changed, 216 insertions(+) create mode 100644 shell/platform/linux/fl_platform_plugin.cc create mode 100644 shell/platform/linux/fl_platform_plugin.h 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..5fc26ddc4a211 --- /dev/null +++ b/shell/platform/linux/fl_platform_plugin.cc @@ -0,0 +1,172 @@ +// 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) + +// 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); + + if (text == nullptr) { + g_autoptr(GError) error = nullptr; + if (!fl_method_call_respond_error( + method_call, kClipboardRequestError, + "Failed to retrieve clipboard text from GTK", nullptr, &error)) + g_warning("Failed to send method call response: %s", error->message); + return; + } + + g_autoptr(FlValue) result = fl_value_new_map(); + fl_value_set_string_take(result, kTextKey, fl_value_new_string(text)); + + g_autoptr(GError) error = nullptr; + if (!fl_method_call_respond_success(method_call, result, &error)) + g_warning("Failed to send method call response: %s", error->message); +} + +// 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 void 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) { + g_autoptr(GError) error = nullptr; + if (!fl_method_call_respond_error(method_call, kBadArgumentsError, + "Expected string", nullptr, &error)) + g_warning("Failed to send method call response: %s", error->message); + return; + } + + const gchar* format = fl_value_get_string(args); + if (strcmp(format, kTextPlainFormat) != 0) { + g_autoptr(GError) error = nullptr; + if (!fl_method_call_respond_error(method_call, kUnknownClipboardFormatError, + "GTK clipboard API only supports text.", + nullptr, &error)) + g_warning("Failed to send method call response: %s", error->message); + return; + } + + GtkClipboard* clipboard = + gtk_clipboard_get_default(gdk_display_get_default()); + gtk_clipboard_request_text(clipboard, clipboard_text_cb, + g_object_ref(method_call)); +} + +// 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_not_implemented_response_new()); +} + +// 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) { + clipboard_get_data_async(self, method_call); + return; + } else if (strcmp(method, kSystemNavigatorPopMethod) == 0) { + response = system_navigator_pop(self); + } 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_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..eab449d0569fd --- /dev/null +++ b/shell/platform/linux/fl_platform_plugin.h @@ -0,0 +1,37 @@ +// 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 platform channel that implements the shell side + * of PlatformPlugins.platform from the Flutter services library. + */ + +/** + * fl_platform_plugin_new: + * @messenger: an #FlBinaryMessenger + * + * Creates a new platform channel. + * + * 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); From 3ee52db4bbb8490839b7adffa59b6545e690eee2 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 10 Jun 2020 10:07:06 +1200 Subject: [PATCH 2/5] Update plugin description --- shell/platform/linux/fl_platform_plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_platform_plugin.h b/shell/platform/linux/fl_platform_plugin.h index eab449d0569fd..6f1a81dae526a 100644 --- a/shell/platform/linux/fl_platform_plugin.h +++ b/shell/platform/linux/fl_platform_plugin.h @@ -18,7 +18,7 @@ G_DECLARE_FINAL_TYPE(FlPlatformPlugin, /** * FlPlatformPlugin: * - * #FlPlatformPlugin is a platform channel that implements the shell side + * #FlPlatformPlugin is a plugin that implements the shell side * of PlatformPlugins.platform from the Flutter services library. */ From e298acbc7623f8c90f0311041f6297e2484ea3d1 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 10 Jun 2020 10:07:44 +1200 Subject: [PATCH 3/5] Return success on pop --- shell/platform/linux/fl_platform_plugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_platform_plugin.cc b/shell/platform/linux/fl_platform_plugin.cc index 5fc26ddc4a211..b2c075c728e30 100644 --- a/shell/platform/linux/fl_platform_plugin.cc +++ b/shell/platform/linux/fl_platform_plugin.cc @@ -113,7 +113,7 @@ static FlMethodResponse* system_navigator_pop(FlPlatformPlugin* self) { g_application_quit(app); - return FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); } // Called when a method call is received from Flutter. From 3c1313fbb507ee6093eced2bbd4aade987e0b70e Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 10 Jun 2020 10:08:51 +1200 Subject: [PATCH 4/5] Update plugin description --- shell/platform/linux/fl_platform_plugin.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_platform_plugin.h b/shell/platform/linux/fl_platform_plugin.h index 6f1a81dae526a..bff6605b7678e 100644 --- a/shell/platform/linux/fl_platform_plugin.h +++ b/shell/platform/linux/fl_platform_plugin.h @@ -26,7 +26,8 @@ G_DECLARE_FINAL_TYPE(FlPlatformPlugin, * fl_platform_plugin_new: * @messenger: an #FlBinaryMessenger * - * Creates a new platform channel. + * Creates a new plugin that implements SystemChannels.platform from the + * Flutter services library. * * Returns: a new #FlPlatformPlugin */ From 6d6533d3a9a1626bf0890c3c5b4f1f0eff22ab0b Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 10 Jun 2020 10:27:52 +1200 Subject: [PATCH 5/5] Refactor out response error handling to one location --- shell/platform/linux/fl_platform_plugin.cc | 71 +++++++++++----------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/shell/platform/linux/fl_platform_plugin.cc b/shell/platform/linux/fl_platform_plugin.cc index b2c075c728e30..8d9ab91577f2a 100644 --- a/shell/platform/linux/fl_platform_plugin.cc +++ b/shell/platform/linux/fl_platform_plugin.cc @@ -29,27 +29,32 @@ struct _FlPlatformPlugin { 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); - if (text == nullptr) { - g_autoptr(GError) error = nullptr; - if (!fl_method_call_respond_error( - method_call, kClipboardRequestError, - "Failed to retrieve clipboard text from GTK", nullptr, &error)) - g_warning("Failed to send method call response: %s", error->message); - return; + 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)); } - g_autoptr(FlValue) result = fl_value_new_map(); - fl_value_set_string_take(result, kTextKey, fl_value_new_string(text)); - - g_autoptr(GError) error = nullptr; - if (!fl_method_call_respond_success(method_call, result, &error)) - g_warning("Failed to send method call response: %s", error->message); + send_response(method_call, response); } // Called when Flutter wants to copy to the clipboard. @@ -75,32 +80,29 @@ static FlMethodResponse* clipboard_set_data(FlPlatformPlugin* self, } // Called when Flutter wants to paste from the clipboard. -static void clipboard_get_data_async(FlPlatformPlugin* self, - FlMethodCall* method_call) { +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) { - g_autoptr(GError) error = nullptr; - if (!fl_method_call_respond_error(method_call, kBadArgumentsError, - "Expected string", nullptr, &error)) - g_warning("Failed to send method call response: %s", error->message); - return; + 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) { - g_autoptr(GError) error = nullptr; - if (!fl_method_call_respond_error(method_call, kUnknownClipboardFormatError, - "GTK clipboard API only supports text.", - nullptr, &error)) - g_warning("Failed to send method call response: %s", error->message); - return; + 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. @@ -126,20 +128,17 @@ static void method_call_cb(FlMethodChannel* channel, FlValue* args = fl_method_call_get_args(method_call); g_autoptr(FlMethodResponse) response = nullptr; - if (strcmp(method, kSetClipboardDataMethod) == 0) { + if (strcmp(method, kSetClipboardDataMethod) == 0) response = clipboard_set_data(self, args); - } else if (strcmp(method, kGetClipboardDataMethod) == 0) { - clipboard_get_data_async(self, method_call); - return; - } else if (strcmp(method, kSystemNavigatorPopMethod) == 0) { + 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 { + 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); + if (response != nullptr) + send_response(method_call, response); } static void fl_platform_plugin_dispose(GObject* object) {