diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4d3cf3cbb2ce1..81f5b6327b281 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1172,6 +1172,8 @@ 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_binary_messenger.cc +FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger_private.h FILE: ../../../flutter/shell/platform/linux/fl_dart_project.cc FILE: ../../../flutter/shell/platform/linux/fl_engine.cc FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h @@ -1180,6 +1182,7 @@ FILE: ../../../flutter/shell/platform/linux/fl_renderer.h FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.h FILE: ../../../flutter/shell/platform/linux/fl_view.cc +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_engine.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 73da99327c3ea..cb0a85d6549df 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -43,7 +43,9 @@ if (build_glfw_shell) { } _public_headers = [ + "public/flutter_linux/fl_binary_messenger.h", "public/flutter_linux/fl_dart_project.h", + "public/flutter_linux/fl_engine.h", "public/flutter_linux/fl_view.h", "public/flutter_linux/flutter_linux.h", ] @@ -56,6 +58,7 @@ source_set("flutter_linux") { public = _public_headers sources = [ + "fl_binary_messenger.cc", "fl_dart_project.cc", "fl_engine.cc", "fl_renderer.cc", diff --git a/shell/platform/linux/fl_binary_messenger.cc b/shell/platform/linux/fl_binary_messenger.cc new file mode 100644 index 0000000000000..40fd541b9d37c --- /dev/null +++ b/shell/platform/linux/fl_binary_messenger.cc @@ -0,0 +1,199 @@ +// 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_binary_messenger.h" +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" + +#include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" + +#include + +struct _FlBinaryMessenger { + GObject parent_instance; + + FlEngine* engine; + + // PlatformMessageHandler keyed by channel name + GHashTable* platform_message_handlers; +}; + +G_DEFINE_TYPE(FlBinaryMessenger, fl_binary_messenger, G_TYPE_OBJECT) + +typedef struct { + FlBinaryMessengerCallback callback; + gpointer user_data; +} PlatformMessageHandler; + +PlatformMessageHandler* platform_message_handler_new( + FlBinaryMessengerCallback callback, + gpointer user_data) { + PlatformMessageHandler* handler = static_cast( + g_malloc0(sizeof(PlatformMessageHandler))); + handler->callback = callback; + handler->user_data = user_data; + return handler; +} + +void platform_message_handler_free(gpointer data) { + PlatformMessageHandler* handler = static_cast(data); + g_free(handler); +} + +struct _FlBinaryMessengerResponseHandle { + const FlutterPlatformMessageResponseHandle* response_handle; +}; + +static void engine_weak_notify_cb(gpointer user_data, GObject* object) { + FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data); + self->engine = nullptr; +} + +static FlBinaryMessengerResponseHandle* response_handle_new( + const FlutterPlatformMessageResponseHandle* response_handle) { + FlBinaryMessengerResponseHandle* handle = + static_cast( + g_malloc0(sizeof(FlBinaryMessengerResponseHandle))); + handle->response_handle = response_handle; + + return handle; +} + +static void response_handle_free(FlBinaryMessengerResponseHandle* handle) { + g_free(handle); +} + +static gboolean fl_binary_messenger_platform_message_callback( + FlEngine* engine, + const gchar* channel, + GBytes* message, + const FlutterPlatformMessageResponseHandle* response_handle, + void* user_data) { + FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data); + + FlBinaryMessengerResponseHandle* handle = + response_handle_new(response_handle); + + PlatformMessageHandler* handler = static_cast( + g_hash_table_lookup(self->platform_message_handlers, channel)); + if (handler == nullptr) + return FALSE; + + handler->callback(self, channel, message, handle, handler->user_data); + + return TRUE; +} + +static void fl_binary_messenger_dispose(GObject* object) { + FlBinaryMessenger* self = FL_BINARY_MESSENGER(object); + + if (self->engine != nullptr) { + g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self); + self->engine = nullptr; + } + + g_clear_pointer(&self->platform_message_handlers, g_hash_table_unref); + + G_OBJECT_CLASS(fl_binary_messenger_parent_class)->dispose(object); +} + +static void fl_binary_messenger_class_init(FlBinaryMessengerClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_binary_messenger_dispose; +} + +static void fl_binary_messenger_init(FlBinaryMessenger* self) { + self->platform_message_handlers = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, platform_message_handler_free); +} + +FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine) { + g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr); + + FlBinaryMessenger* self = FL_BINARY_MESSENGER( + g_object_new(fl_binary_messenger_get_type(), nullptr)); + + self->engine = engine; + g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self); + + fl_engine_set_platform_message_handler( + engine, fl_binary_messenger_platform_message_callback, self); + + return self; +} + +G_MODULE_EXPORT void fl_binary_messenger_set_message_handler_on_channel( + FlBinaryMessenger* self, + const gchar* channel, + FlBinaryMessengerCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_BINARY_MESSENGER(self)); + g_return_if_fail(channel != nullptr); + g_return_if_fail(callback != nullptr); + + PlatformMessageHandler* handler = + platform_message_handler_new(callback, user_data); + g_hash_table_replace(self->platform_message_handlers, g_strdup(channel), + handler); +} + +G_MODULE_EXPORT gboolean fl_binary_messenger_send_response( + FlBinaryMessenger* self, + FlBinaryMessengerResponseHandle* response_handle, + GBytes* response, + GError** error) { + g_return_val_if_fail(FL_IS_BINARY_MESSENGER(self), FALSE); + g_return_val_if_fail(response_handle != nullptr, FALSE); + + if (self->engine == nullptr) + return TRUE; + + gboolean result = fl_engine_send_platform_message_response( + self->engine, response_handle->response_handle, response, error); + response_handle_free(response_handle); + + return result; +} + +static void platform_message_ready_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + GTask* task = G_TASK(user_data); + g_task_return_pointer(task, result, g_object_unref); +} + +G_MODULE_EXPORT void fl_binary_messenger_send_on_channel( + FlBinaryMessenger* self, + const gchar* channel, + GBytes* message, + GCancellable* cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_BINARY_MESSENGER(self)); + g_return_if_fail(channel != nullptr); + + if (self->engine == nullptr) + return; + + fl_engine_send_platform_message( + self->engine, channel, message, cancellable, + callback != nullptr ? platform_message_ready_cb : nullptr, + callback != nullptr ? g_task_new(self, cancellable, callback, user_data) + : nullptr); +} + +G_MODULE_EXPORT GBytes* fl_binary_messenger_send_on_channel_finish( + FlBinaryMessenger* self, + GAsyncResult* result, + GError** error) { + g_return_val_if_fail(FL_IS_BINARY_MESSENGER(self), FALSE); + g_return_val_if_fail(g_task_is_valid(result, self), FALSE); + + g_autoptr(GTask) task = G_TASK(result); + GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr)); + + if (self->engine == nullptr) + return nullptr; + + return fl_engine_send_platform_message_finish(self->engine, r, error); +} diff --git a/shell/platform/linux/fl_binary_messenger_private.h b/shell/platform/linux/fl_binary_messenger_private.h new file mode 100644 index 0000000000000..88212c1745b05 --- /dev/null +++ b/shell/platform/linux/fl_binary_messenger_private.h @@ -0,0 +1,26 @@ +// 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_BINARY_MESSENGER_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_BINARY_MESSENGER_PRIVATE_H_ + +#include + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" + +G_BEGIN_DECLS + +/** + * fl_binary_messenger_new: + * @engine: The #FlEngine to communicate with. + * + * Create a new #FlBinaryMessenger. + * + * Returns: a new #FlBinaryMessenger. + */ +FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_BINARY_MESSENGER_PRIVATE_H_ diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index fb733369d3357..7e144f8904d20 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -4,6 +4,8 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" #include "flutter/shell/platform/linux/fl_engine_private.h" + +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/fl_renderer.h" #include @@ -21,7 +23,12 @@ struct _FlEngine { FlDartProject* project; FlRenderer* renderer; + FlBinaryMessenger* binary_messenger; FLUTTER_API_SYMBOL(FlutterEngine) engine; + + // Function to call when a platform message is received + FlEnginePlatformMessageCallback platform_message_callback; + gpointer platform_message_callback_data; }; G_DEFINE_QUARK(fl_engine_error_quark, fl_engine_error) @@ -119,13 +126,41 @@ static void fl_engine_post_task_callback(FlutterTask task, g_source_attach(source, nullptr); } +static void fl_engine_platform_message_callback( + const FlutterPlatformMessage* message, + void* user_data) { + FlEngine* self = FL_ENGINE(user_data); + + gboolean handled = FALSE; + if (self->platform_message_callback != nullptr) { + g_autoptr(GBytes) data = + g_bytes_new(message->message, message->message_size); + handled = self->platform_message_callback( + self, message->channel, data, message->response_handle, + self->platform_message_callback_data); + } + + if (!handled) + fl_engine_send_platform_message_response(self, message->response_handle, + nullptr, nullptr); +} + +static void fl_engine_platform_message_response_callback(const uint8_t* data, + size_t data_length, + void* user_data) { + g_autoptr(GTask) task = G_TASK(user_data); + g_task_return_pointer(task, g_bytes_new(data, data_length), + (GDestroyNotify)g_bytes_unref); +} + static void fl_engine_dispose(GObject* object) { FlEngine* self = FL_ENGINE(object); + FlutterEngineShutdown(self->engine); + g_clear_object(&self->project); g_clear_object(&self->renderer); - - FlutterEngineShutdown(self->engine); + g_clear_object(&self->binary_messenger); G_OBJECT_CLASS(fl_engine_parent_class)->dispose(object); } @@ -136,6 +171,8 @@ static void fl_engine_class_init(FlEngineClass* klass) { static void fl_engine_init(FlEngine* self) { self->thread = g_thread_self(); + + self->binary_messenger = fl_binary_messenger_new(self); } FlEngine* fl_engine_new(FlDartProject* project, FlRenderer* renderer) { @@ -180,6 +217,7 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { args.struct_size = sizeof(FlutterProjectArgs); args.assets_path = fl_dart_project_get_assets_path(self->project); args.icu_data_path = fl_dart_project_get_icu_data_path(self->project); + args.platform_message_callback = fl_engine_platform_message_callback; args.custom_task_runners = &custom_task_runners; FlutterEngineResult result = FlutterEngineInitialize( @@ -200,6 +238,99 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { return TRUE; } +void fl_engine_set_platform_message_handler( + FlEngine* self, + FlEnginePlatformMessageCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_ENGINE(self)); + g_return_if_fail(callback != nullptr); + + self->platform_message_callback = callback; + self->platform_message_callback_data = user_data; +} + +gboolean fl_engine_send_platform_message_response( + FlEngine* self, + const FlutterPlatformMessageResponseHandle* handle, + GBytes* response, + GError** error) { + g_return_val_if_fail(FL_IS_ENGINE(self), FALSE); + g_return_val_if_fail(handle != nullptr, FALSE); + + gsize data_length = 0; + const uint8_t* data = nullptr; + if (response != nullptr) + data = + static_cast(g_bytes_get_data(response, &data_length)); + FlutterEngineResult result = FlutterEngineSendPlatformMessageResponse( + self->engine, handle, data, data_length); + + if (result != kSuccess) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to send platorm message response"); + return FALSE; + } + + return TRUE; +} + +void fl_engine_send_platform_message(FlEngine* self, + const gchar* channel, + GBytes* message, + GCancellable* cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_ENGINE(self)); + + GTask* task = nullptr; + FlutterPlatformMessageResponseHandle* response_handle = nullptr; + if (callback != nullptr) { + task = g_task_new(self, cancellable, callback, user_data); + + FlutterEngineResult result = FlutterPlatformMessageCreateResponseHandle( + self->engine, fl_engine_platform_message_response_callback, task, + &response_handle); + if (result != kSuccess) { + g_task_return_new_error(task, fl_engine_error_quark(), + FL_ENGINE_ERROR_FAILED, + "Failed to create response handle"); + g_object_unref(task); + return; + } + } + + FlutterPlatformMessage fl_message = {}; + fl_message.struct_size = sizeof(fl_message); + fl_message.channel = channel; + fl_message.message = + message != nullptr + ? static_cast(g_bytes_get_data(message, nullptr)) + : nullptr; + fl_message.message_size = message != nullptr ? g_bytes_get_size(message) : 0; + fl_message.response_handle = response_handle; + FlutterEngineResult result = + FlutterEngineSendPlatformMessage(self->engine, &fl_message); + + if (result != kSuccess && task != nullptr) { + g_task_return_new_error(task, fl_engine_error_quark(), + FL_ENGINE_ERROR_FAILED, + "Failed to send platform messages"); + g_object_unref(task); + } + + if (response_handle != nullptr) + FlutterPlatformMessageReleaseResponseHandle(self->engine, response_handle); +} + +GBytes* fl_engine_send_platform_message_finish(FlEngine* self, + GAsyncResult* result, + GError** error) { + g_return_val_if_fail(FL_IS_ENGINE(self), FALSE); + g_return_val_if_fail(g_task_is_valid(result, self), FALSE); + + return static_cast(g_task_propagate_pointer(G_TASK(result), error)); +} + void fl_engine_send_window_metrics_event(FlEngine* self, size_t width, size_t height, @@ -232,3 +363,9 @@ void fl_engine_send_mouse_pointer_event(FlEngine* self, fl_event.buttons = buttons; FlutterEngineSendPointerEvent(self->engine, &fl_event, 1); } + +G_MODULE_EXPORT FlBinaryMessenger* fl_engine_get_binary_messenger( + FlEngine* self) { + g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); + return self->binary_messenger; +} diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h index f4d49572e5fdf..98b432689b13d 100644 --- a/shell/platform/linux/fl_engine_private.h +++ b/shell/platform/linux/fl_engine_private.h @@ -25,6 +25,25 @@ typedef enum { GQuark fl_engine_error_quark(void) G_GNUC_CONST; +/** + * FlEnginePlatformMessageCallback: + * @engine: a #FlEngine + * @channel: channel message received on. + * @message: message content received from Dart + * @response_handle: a handle to respond to the message with + * @user_data: (closure): data provided when registering this callback + * + * Function called when platform messages are received. + * + * Returns: %TRUE if message has been accepted. + */ +typedef gboolean (*FlEnginePlatformMessageCallback)( + FlEngine* engine, + const gchar* channel, + GBytes* message, + const FlutterPlatformMessageResponseHandle* response_handle, + gpointer user_data); + /** * fl_engine_new: * @project: a #FlDartProject @@ -36,6 +55,23 @@ GQuark fl_engine_error_quark(void) G_GNUC_CONST; */ FlEngine* fl_engine_new(FlDartProject* project, FlRenderer* renderer); +/** + * fl_engine_set_platform_message_handler: + * @engine: a #FlEngine + * @callback: function to call when a platform message is received + * @user_data: (closure): user data to pass to @callback + * + * Register a callback to handle platform messages. Call + * fl_engine_send_platform_message_response() when this message should be + * responded to. Ownership of #FlutterPlatformMessageResponseHandle is + * transferred to the caller, and the call must be responded to to avoid + * memory leaks. + */ +void fl_engine_set_platform_message_handler( + FlEngine* engine, + FlEnginePlatformMessageCallback callback, + gpointer user_data); + /** * fl_engine_start: * @engine: a #FlEngine @@ -80,6 +116,58 @@ void fl_engine_send_mouse_pointer_event(FlEngine* engine, double y, int64_t buttons); +/** + * fl_engine_send_platform_message_response: + * @engine: a #FlEngine + * @response_handle: handle that was provided in the callback. + * @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 platform message. + * + * Returns: %TRUE on success. + */ +gboolean fl_engine_send_platform_message_response( + FlEngine* engine, + const FlutterPlatformMessageResponseHandle* handle, + GBytes* response, + GError** error); + +/** + * fl_engine_send_platform_message: + * @engine: a #FlEngine + * @channel: channel to send to + * @message: (allow-none): message buffer to send or %NULL for an empty message + * @cancellable: (allow-none): a #GCancellable or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is + * satisfied + * @user_data: (closure): user data to pass to @callback + * + * Asynchronously send a platform message. + */ +void fl_engine_send_platform_message(FlEngine* engine, + const gchar* channel, + GBytes* message, + GCancellable* cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +/** + * fl_engine_send_platform_message_finish: + * @engine: a #FlEngine + * @result: a #GAsyncResult + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Complete request started with fl_engine_send_platform_message(). + * + * Returns: message response on success or %NULL on error. + */ +GBytes* fl_engine_send_platform_message_finish(FlEngine* engine, + GAsyncResult* result, + GError** error); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_ diff --git a/shell/platform/linux/fl_renderer_x11.cc b/shell/platform/linux/fl_renderer_x11.cc index f885e74ef3ae0..9f9108cefce4f 100644 --- a/shell/platform/linux/fl_renderer_x11.cc +++ b/shell/platform/linux/fl_renderer_x11.cc @@ -25,9 +25,11 @@ static void fl_renderer_x11_class_init(FlRendererX11Class* klass) { static void fl_renderer_x11_init(FlRendererX11* self) {} -FlRendererX11* fl_renderer_x11_new(Window xid) { - FlRendererX11* self = static_cast( - g_object_new(fl_renderer_x11_get_type(), nullptr)); +FlRendererX11* fl_renderer_x11_new() { + return FL_RENDERER_X11(g_object_new(fl_renderer_x11_get_type(), nullptr)); +} + +void fl_renderer_x11_set_xid(FlRendererX11* self, Window xid) { + g_return_if_fail(FL_IS_RENDERER_X11(self)); self->xid = xid; - return self; } diff --git a/shell/platform/linux/fl_renderer_x11.h b/shell/platform/linux/fl_renderer_x11.h index 0aab6469ad4c1..ca36341727d68 100644 --- a/shell/platform/linux/fl_renderer_x11.h +++ b/shell/platform/linux/fl_renderer_x11.h @@ -26,13 +26,21 @@ G_DECLARE_FINAL_TYPE(FlRendererX11, /** * fl_renderer_x11_new: - * @xid: The X window to render to. * * Create an object that allows Flutter to render to X11 windows. * * Returns: a #FlRendererX11 */ -FlRendererX11* fl_renderer_x11_new(Window xid); +FlRendererX11* fl_renderer_x11_new(); + +/** + * fl_renderer_x11_set_xid: + * @renderer: a #FlRendererX11 + * @xid: The X window being rendered to + * + * Set the X11 window that is being rendered to. + */ +void fl_renderer_x11_set_xid(FlRendererX11* renderer, Window xid); G_END_DECLS diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 6a60fecfc39b8..49aa6d830f5a4 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -16,6 +16,7 @@ struct _FlView { GtkWidget parent_instance; FlDartProject* project; + FlRendererX11* renderer; FlEngine* engine; int64_t button_state; }; @@ -68,6 +69,13 @@ static gboolean fl_view_send_pointer_button_event(FlView* self, return TRUE; } +static void fl_view_constructed(GObject* object) { + FlView* self = FL_VIEW(object); + + self->renderer = fl_renderer_x11_new(); + self->engine = fl_engine_new(self->project, FL_RENDERER(self->renderer)); +} + static void fl_view_set_property(GObject* object, guint prop_id, const GValue* value, @@ -105,6 +113,7 @@ static void fl_view_dispose(GObject* object) { FlView* self = FL_VIEW(object); g_clear_object(&self->project); + g_clear_object(&self->renderer); g_clear_object(&self->engine); G_OBJECT_CLASS(fl_view_parent_class)->dispose(object); @@ -139,8 +148,8 @@ static void fl_view_realize(GtkWidget* widget) { gtk_widget_set_window(widget, window); Window xid = gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(self))); - g_autoptr(FlRendererX11) renderer = fl_renderer_x11_new(xid); - self->engine = fl_engine_new(self->project, FL_RENDERER(renderer)); + fl_renderer_x11_set_xid(self->renderer, xid); + g_autoptr(GError) error = nullptr; if (!fl_engine_start(self->engine, &error)) g_warning("Failed to start Flutter engine: %s", error->message); @@ -197,6 +206,7 @@ static gboolean fl_view_motion_notify_event(GtkWidget* widget, } static void fl_view_class_init(FlViewClass* klass) { + G_OBJECT_CLASS(klass)->constructed = fl_view_constructed; G_OBJECT_CLASS(klass)->set_property = fl_view_set_property; G_OBJECT_CLASS(klass)->get_property = fl_view_get_property; G_OBJECT_CLASS(klass)->dispose = fl_view_dispose; diff --git a/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h b/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h new file mode 100644 index 0000000000000..f37aff43095df --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h @@ -0,0 +1,128 @@ +// 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_BINARY_MESSENGER_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_BINARY_MESSENGER_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlBinaryMessenger, + fl_binary_messenger, + FL, + BINARY_MESSENGER, + GObject) + +/** + * FlBinaryMessenger: + * + * #FlBinaryMessenger is an object that allows sending and receiving of platform + * messages with a #FlEngine. + */ + +/** + * FlBinaryMessengerResponseHandle: + * + * A handle used to respond to platform messages. + */ +typedef struct _FlBinaryMessengerResponseHandle FlBinaryMessengerResponseHandle; + +/** + * FlBinaryMessengerCallback: + * @messenger: a #FlBinaryMessenger + * @channel: channel message received on + * @message: message content received from Dart + * @response_handle: a handle to respond to the message with + * @user_data: (closure): data provided when registering this callback + * + * Function called when platform messages are received. The receiver must + * respond to the message to avoid leaking the handle, see the documentation on + * the code that generated the callback as to which function to call. + */ +typedef void (*FlBinaryMessengerCallback)( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data); + +/** + * fl_binary_messenger_set_platform_message_handler: + * @binary_messenger: a #FlBinaryMessenger + * @channel: channel to listen on + * @callback: function to call when a message is received on this channel + * @user_data: (closure): user data to pass to @callback + * + * Set the function called when a platform message is received on the given + * channel. Call fl_binary_messenger_send_response() when the message is + * handled. Ownership of #FlBinaryMessengerResponseHandle is transferred to the + * caller, and the call must be responded to to avoid memory leaks. + */ +void fl_binary_messenger_set_message_handler_on_channel( + FlBinaryMessenger* messenger, + const gchar* channel, + FlBinaryMessengerCallback callback, + gpointer user_data); + +/** + * fl_binary_messenger_send_response: + * @binary_messenger: a #FlBinaryMessenger + * @response_handle: handle that was provided in a #FlBinaryMessengerCallback + * @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 platform message. + * + * Returns: %TRUE on success. + */ +gboolean fl_binary_messenger_send_response( + FlBinaryMessenger* messenger, + FlBinaryMessengerResponseHandle* response_handle, + GBytes* response, + GError** error); + +/** + * fl_binary_messenger_send_on_channel: + * @binary_messenger: a #FlBinaryMessenger + * @channel: channel to send to + * @message: (allow-none): message buffer to send or %NULL for an empty message + * @cancellable: (allow-none): a #GCancellable or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request is + * satisfied + * @user_data: (closure): user data to pass to @callback + * + * Asynchronously send a platform message. + */ +void fl_binary_messenger_send_on_channel(FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + GCancellable* cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +/** + * fl_binary_messenger_send_on_channel_finish: + * @binary_messenger: a #FlBinaryMessenger + * @result: a #GAsyncResult + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Complete request started with fl_binary_messenger_send_on_channel(). + * + * Returns: message response on success or %NULL on error. + */ +GBytes* fl_binary_messenger_send_on_channel_finish(FlBinaryMessenger* messenger, + GAsyncResult* result, + GError** error); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_BINARY_MESSENGER_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_engine.h b/shell/platform/linux/public/flutter_linux/fl_engine.h index 305bc455feb6d..9473717979d83 100644 --- a/shell/platform/linux/public/flutter_linux/fl_engine.h +++ b/shell/platform/linux/public/flutter_linux/fl_engine.h @@ -11,6 +11,7 @@ #include +#include "fl_binary_messenger.h" #include "fl_dart_project.h" G_BEGIN_DECLS @@ -23,6 +24,16 @@ G_DECLARE_FINAL_TYPE(FlEngine, fl_engine, FL, ENGINE, GObject) * #FlEngine is an object that contains a running Flutter engine. */ +/** + * fl_engine_get_binary_messenger: + * @engine: a #FlEngine + * + * Get the messenger to communicate with this engine. + * + * Returns: a #FlBinaryMessenger + */ +FlBinaryMessenger* fl_engine_get_binary_messenger(FlEngine* engine); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_H_ diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h index c037849802ff0..74e8a142496dd 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