Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions shell/platform/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
171 changes: 171 additions & 0 deletions shell/platform/linux/fl_platform_plugin.cc
Original file line number Diff line number Diff line change
@@ -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 <gtk/gtk.h>

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;
}
38 changes: 38 additions & 0 deletions shell/platform/linux/fl_platform_plugin.h
Original file line number Diff line number Diff line change
@@ -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_
4 changes: 4 additions & 0 deletions shell/platform/linux/fl_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -33,6 +34,7 @@ struct _FlView {

// Flutter system channel handlers.
FlKeyEventPlugin* key_event_plugin;
FlPlatformPlugin* platform_plugin;
FlTextInputPlugin* text_input_plugin;
};

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down