diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 425a3152820ef..8ce5b14b11c12 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1185,6 +1185,7 @@ FILE: ../../../flutter/shell/platform/glfw/platform_handler.h FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h +FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger.cc @@ -1207,6 +1208,7 @@ FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_value.cc FILE: ../../../flutter/shell/platform/linux/fl_value_test.cc FILE: ../../../flutter/shell/platform/linux/fl_view.cc +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index cae44d2c3566d..5b3f8652d8825 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -44,6 +44,7 @@ if (build_glfw_shell) { } _public_headers = [ + "public/flutter_linux/fl_basic_message_channel.h", "public/flutter_linux/fl_binary_codec.h", "public/flutter_linux/fl_binary_messenger.h", "public/flutter_linux/fl_dart_project.h", @@ -64,6 +65,7 @@ source_set("flutter_linux") { public = _public_headers sources = [ + "fl_basic_message_channel.cc", "fl_binary_codec.cc", "fl_binary_messenger.cc", "fl_dart_project.cc", diff --git a/shell/platform/linux/fl_basic_message_channel.cc b/shell/platform/linux/fl_basic_message_channel.cc new file mode 100644 index 0000000000000..5559c4a99100f --- /dev/null +++ b/shell/platform/linux/fl_basic_message_channel.cc @@ -0,0 +1,206 @@ +// 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/public/flutter_linux/fl_basic_message_channel.h" + +#include + +struct _FlBasicMessageChannel { + GObject parent_instance; + + // Messenger to communicate on + FlBinaryMessenger* messenger; + + // Channel name + gchar* name; + + // Codec to en/decode messages + FlMessageCodec* codec; + + // Function called when a message is received + FlBasicMessageChannelMessageHandler message_handler; + gpointer message_handler_data; +}; + +// Wrap the binary messenger handle for type safety and to make the API +// consistent +struct _FlBasicMessageChannelResponseHandle { + FlBinaryMessengerResponseHandle* response_handle; +}; + +static FlBasicMessageChannelResponseHandle* response_handle_new( + FlBinaryMessengerResponseHandle* response_handle) { + FlBasicMessageChannelResponseHandle* handle = + static_cast( + g_malloc0(sizeof(FlBasicMessageChannelResponseHandle))); + handle->response_handle = response_handle; + + return handle; +} + +static void response_handle_free(FlBasicMessageChannelResponseHandle* handle) { + g_free(handle); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlBasicMessageChannelResponseHandle, + response_handle_free); + +// Added here to stop the compiler from optimising this function away +G_MODULE_EXPORT GType fl_basic_message_channel_get_type(); + +G_DEFINE_TYPE(FlBasicMessageChannel, fl_basic_message_channel, G_TYPE_OBJECT) + +// Called when a binary message is received on this channel +static void message_cb(FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + FlBasicMessageChannel* self = FL_BASIC_MESSAGE_CHANNEL(user_data); + + if (self->message_handler == nullptr) { + fl_binary_messenger_send_response(messenger, response_handle, nullptr, + nullptr); + return; + } + + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message_value = + fl_message_codec_decode_message(self->codec, message, &error); + if (message_value == nullptr) { + g_warning("Failed to decode message: %s", error->message); + fl_binary_messenger_send_response(messenger, response_handle, nullptr, + nullptr); + } + + self->message_handler(self, message_value, + response_handle_new(response_handle), + self->message_handler_data); +} + +// Called when a response is received to a sent message +static void message_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + GTask* task = G_TASK(user_data); + g_task_return_pointer(task, result, g_object_unref); +} + +static void fl_basic_message_channel_dispose(GObject* object) { + FlBasicMessageChannel* self = FL_BASIC_MESSAGE_CHANNEL(object); + + if (self->messenger != nullptr) + fl_binary_messenger_set_message_handler_on_channel( + self->messenger, self->name, nullptr, nullptr); + + g_clear_object(&self->messenger); + g_clear_pointer(&self->name, g_free); + g_clear_object(&self->codec); + + G_OBJECT_CLASS(fl_basic_message_channel_parent_class)->dispose(object); +} + +static void fl_basic_message_channel_class_init( + FlBasicMessageChannelClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_basic_message_channel_dispose; +} + +static void fl_basic_message_channel_init(FlBasicMessageChannel* self) {} + +G_MODULE_EXPORT FlBasicMessageChannel* fl_basic_message_channel_new( + FlBinaryMessenger* messenger, + const gchar* name, + FlMessageCodec* codec) { + g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); + g_return_val_if_fail(name != nullptr, nullptr); + g_return_val_if_fail(FL_IS_MESSAGE_CODEC(codec), nullptr); + + FlBasicMessageChannel* self = FL_BASIC_MESSAGE_CHANNEL( + g_object_new(fl_basic_message_channel_get_type(), nullptr)); + + self->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger)); + self->name = g_strdup(name); + self->codec = FL_MESSAGE_CODEC(g_object_ref(codec)); + + fl_binary_messenger_set_message_handler_on_channel( + self->messenger, self->name, message_cb, self); + + return self; +} + +G_MODULE_EXPORT void fl_basic_message_channel_set_message_handler( + FlBasicMessageChannel* self, + FlBasicMessageChannelMessageHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self)); + + self->message_handler = handler; + self->message_handler_data = user_data; +} + +G_MODULE_EXPORT gboolean fl_basic_message_channel_respond( + FlBasicMessageChannel* self, + FlBasicMessageChannelResponseHandle* response_handle, + FlValue* message, + GError** error) { + g_return_val_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self), FALSE); + g_return_val_if_fail(response_handle != nullptr, FALSE); + + // Take reference to ensure it is freed + g_autoptr(FlBasicMessageChannelResponseHandle) owned_response_handle = + response_handle; + + g_autoptr(GBytes) data = + fl_message_codec_encode_message(self->codec, message, error); + if (data == nullptr) + return FALSE; + + return fl_binary_messenger_send_response( + self->messenger, owned_response_handle->response_handle, data, error); +} + +G_MODULE_EXPORT void fl_basic_message_channel_send(FlBasicMessageChannel* self, + FlValue* message, + GCancellable* cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self)); + g_return_if_fail(message != nullptr); + + g_autoptr(GTask) task = + callback != nullptr ? g_task_new(self, cancellable, callback, user_data) + : nullptr; + + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) data = + fl_message_codec_encode_message(self->codec, message, &error); + if (data == nullptr) { + if (task != nullptr) + g_task_return_error(task, error); + return; + } + + fl_binary_messenger_send_on_channel( + self->messenger, self->name, data, cancellable, + callback != nullptr ? message_response_cb : nullptr, + g_steal_pointer(&task)); +} + +G_MODULE_EXPORT FlValue* fl_basic_message_channel_send_on_channel_finish( + FlBasicMessageChannel* self, + GAsyncResult* result, + GError** error) { + g_return_val_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self), nullptr); + g_return_val_if_fail(g_task_is_valid(result, self), nullptr); + + g_autoptr(GTask) task = G_TASK(result); + GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr)); + + g_autoptr(GBytes) message = + fl_binary_messenger_send_on_channel_finish(self->messenger, r, error); + if (message == nullptr) + return nullptr; + + return fl_message_codec_decode_message(self->codec, message, error); +} diff --git a/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h b/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h new file mode 100644 index 0000000000000..a26332bb0e62a --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h @@ -0,0 +1,143 @@ +// 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_BASIC_MESSAGE_CHANNEL_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_BASIC_MESSAGE_CHANNEL_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +#include "fl_binary_messenger.h" +#include "fl_message_codec.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlBasicMessageChannel, + fl_basic_message_channel, + FL, + BASIC_MESSAGE_CHANNEL, + GObject) + +/** + * FlBasicMessageChannel: + * + * #FlBasicMessageChannel is an object that allows sending and receiving + * messages to/from Dart code over platform channels. + * + * #FlBasicMessageChannel matches the BasicMessageChannel class in the Flutter + * services library. + */ + +/** + * FlBasicMessageChannelResponseHandle: + * + * A handle used to respond to messages. + */ +typedef struct _FlBasicMessageChannelResponseHandle + FlBasicMessageChannelResponseHandle; + +/** + * FlBasicMessageChannelMessageHandler: + * @channel: a #FlBasicMessageChannel + * @message: message received + * @response_handle: (transfer full): a handle to respond to the message with + * @user_data: (closure): data provided when registering this handler + * + * Function called when a message is received. + */ +typedef void (*FlBasicMessageChannelMessageHandler)( + FlBasicMessageChannel* channel, + FlValue* message, + FlBasicMessageChannelResponseHandle* response_handle, + gpointer user_data); + +/** + * fl_basic_message_channel_new: + * @messenger: a #FlBinaryMessenger + * @name: a channel name + * @codec: the message codec + * + * Create a new basic message channel. @codec must match the codec used on the + * Dart end of the channel. + * + * Returns: a new #FlBasicMessageChannel. + */ +FlBasicMessageChannel* fl_basic_message_channel_new( + FlBinaryMessenger* messenger, + const gchar* name, + FlMessageCodec* codec); + +/** + * fl_basic_message_channel_set_message_handler: + * @channel: a #FlBasicMessageChannel + * @handler: (allow-none): function to call when a message is received on this + * channel or %NULL to disable the handler. + * @user_data: (closure): user data to pass to @handler + * + * Set the function called when a message is received. + */ +void fl_basic_message_channel_set_message_handler( + FlBasicMessageChannel* channel, + FlBasicMessageChannelMessageHandler handler, + gpointer user_data); + +/** + * fl_basic_message_channel_send_response: + * @channel: a #FlBasicMessageChannel + * @response_handle: (transfer full): handle that was provided in a + * #FlBasicMessageChannelMessageHandler + * @response: (allow-none): response to send or %NULL for an empty response + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore + * + * Respond to a message. + * + * Returns: %TRUE on success. + */ +gboolean fl_basic_message_channel_send_response( + FlBasicMessageChannel* channel, + FlBasicMessageChannelResponseHandle* response_handle, + FlValue* response, + GError** error); + +/** + * fl_basic_message_channel_send: + * @channel: a #FlBasicMessageChannel + * @message: message to send, must match what the #FlMessageCodec supports + * @cancellable: (allow-none): a #GCancellable or %NULL + * @callback: (scope async): (allow-none): a #GAsyncReadyCallback to call when + * the request is satisfied or %NULL to ignore the response. + * @user_data: (closure): user data to pass to @callback + * + * Asynchronously send a message. + */ +void fl_basic_message_channel_send(FlBasicMessageChannel* channel, + FlValue* message, + GCancellable* cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +/** + * fl_basic_message_channel_send_finish: + * @channel: a #FlBasicMessageChannel + * @result: a #GAsyncResult + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Complete request started with fl_basic_message_channel_send(). + * + * Returns: message response on success or %NULL on error. + */ +FlValue* fl_basic_message_channel_send_on_channel_finish( + FlBasicMessageChannel* channel, + GAsyncResult* result, + GError** error); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_BASIC_MESSAGE_CHANNEL_H_ diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h index 62449e81abf6f..cb869b0b5c151 100644 --- a/shell/platform/linux/public/flutter_linux/flutter_linux.h +++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h @@ -7,6 +7,7 @@ #define __FLUTTER_LINUX_INSIDE__ +#include #include #include #include