From 2bc8aafcc4a5966e594cd16bed5530d3ff4ef0fd Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Wed, 3 Feb 2021 23:44:49 +0800 Subject: [PATCH 01/14] Add platform view support for Linux shell --- ci/licenses_golden/licenses_flutter | 6 + shell/platform/linux/BUILD.gn | 4 + shell/platform/linux/fl_gesture_helper.cc | 156 +++++++++ shell/platform/linux/fl_gesture_helper.h | 102 ++++++ shell/platform/linux/fl_platform_views.cc | 58 ++++ .../linux/fl_platform_views_plugin.cc | 310 ++++++++++++++++++ .../platform/linux/fl_platform_views_plugin.h | 72 ++++ shell/platform/linux/fl_plugin_registrar.cc | 14 + shell/platform/linux/fl_renderer.h | 9 - shell/platform/linux/fl_renderer_gl.cc | 23 +- shell/platform/linux/fl_view.cc | 37 ++- shell/platform/linux/fl_view_private.h | 15 +- .../public/flutter_linux/fl_platform_views.h | 137 ++++++++ .../flutter_linux/fl_plugin_registrar.h | 17 + 14 files changed, 945 insertions(+), 15 deletions(-) create mode 100644 shell/platform/linux/fl_gesture_helper.cc create mode 100644 shell/platform/linux/fl_gesture_helper.h create mode 100644 shell/platform/linux/fl_platform_views.cc create mode 100644 shell/platform/linux/fl_platform_views_plugin.cc create mode 100644 shell/platform/linux/fl_platform_views_plugin.h create mode 100644 shell/platform/linux/public/flutter_linux/fl_platform_views.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7dd9efbb4bb40..c5dd34993f24c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1378,6 +1378,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h FILE: ../../../flutter/shell/platform/linux/fl_engine_test.cc FILE: ../../../flutter/shell/platform/linux/fl_event_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_event_channel_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_gesture_helper.cc +FILE: ../../../flutter/shell/platform/linux/fl_gesture_helper.h FILE: ../../../flutter/shell/platform/linux/fl_gl_area.cc FILE: ../../../flutter/shell/platform/linux/fl_gl_area.h FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec.cc @@ -1404,6 +1406,9 @@ FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_platform_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_platform_plugin.h +FILE: ../../../flutter/shell/platform/linux/fl_platform_views.cc +FILE: ../../../flutter/shell/platform/linux/fl_platform_views_plugin.cc +FILE: ../../../flutter/shell/platform/linux/fl_platform_views_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 @@ -1443,6 +1448,7 @@ FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_call. FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_platform_views.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 3706fcd1837c4..e62d466a10708 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -55,6 +55,7 @@ _public_headers = [ "public/flutter_linux/fl_method_channel.h", "public/flutter_linux/fl_method_codec.h", "public/flutter_linux/fl_method_response.h", + "public/flutter_linux/fl_platform_views.h", "public/flutter_linux/fl_plugin_registrar.h", "public/flutter_linux/fl_plugin_registry.h", "public/flutter_linux/fl_standard_message_codec.h", @@ -94,6 +95,7 @@ source_set("flutter_linux_sources") { "fl_dart_project.cc", "fl_engine.cc", "fl_event_channel.cc", + "fl_gesture_helper.cc", "fl_gl_area.cc", "fl_json_message_codec.cc", "fl_json_method_codec.cc", @@ -105,6 +107,8 @@ source_set("flutter_linux_sources") { "fl_method_response.cc", "fl_mouse_cursor_plugin.cc", "fl_platform_plugin.cc", + "fl_platform_views.cc", + "fl_platform_views_plugin.cc", "fl_plugin_registrar.cc", "fl_plugin_registry.cc", "fl_renderer.cc", diff --git a/shell/platform/linux/fl_gesture_helper.cc b/shell/platform/linux/fl_gesture_helper.cc new file mode 100644 index 0000000000000..b5971f46be2b9 --- /dev/null +++ b/shell/platform/linux/fl_gesture_helper.cc @@ -0,0 +1,156 @@ +// 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_gesture_helper.h" + +struct _FlGestureHelper { + GObject parent_instance; + + GList* event_list; + GtkWidget* grabbed_widget; + GtkWidget* hover_widget; + int64_t pointer_id; + int32_t pressed_buttons; + bool end; +}; + +G_DEFINE_TYPE(FlGestureHelper, fl_gesture_helper, G_TYPE_OBJECT) + +static void fl_gesture_helper_class_init(FlGestureHelperClass* klass) {} + +static void fl_gesture_helper_init(FlGestureHelper* self) {} + +FlGestureHelper* fl_gesture_helper_new() { + return FL_GESTURE_HELPER(g_object_new(fl_gesture_helper_get_type(), nullptr)); +} + +static void free_event(gpointer data) { + gdk_event_free(reinterpret_cast(data)); +} + +static void send_button_event(FlGestureHelper* self, + GdkEventButton* event, + GtkWidget* widget) { + if (event->type == GDK_BUTTON_PRESS) { + self->end = false; + self->pressed_buttons |= 1 << event->button; + } else if (event->type == GDK_BUTTON_RELEASE) { + self->end = true; + self->pressed_buttons &= ~(1 << event->button); + } + + GdkWindow* window = gtk_widget_get_window(widget); + gint origin_x, origin_y; + gdk_window_get_origin(window, &origin_x, &origin_y); + GdkEvent button; + button.button.type = event->type; + g_object_ref(window); + button.button.window = window; + button.button.send_event = event->send_event; + button.button.time = event->time; + button.button.x = event->x_root - origin_x; + button.button.y = event->y_root - origin_y; + button.button.axes = event->axes; + button.button.state = event->state; + button.button.button = event->button; + button.button.device = event->device; + button.button.x_root = event->x_root; + button.button.y_root = event->y_root; + gtk_widget_event(widget, &button); +} + +static void send_motion_event(FlGestureHelper* self, + GdkEventMotion* event, + GtkWidget* widget) { + if (self->end) + return; + + GdkWindow* window = gtk_widget_get_window(widget); + gint origin_x, origin_y; + gdk_window_get_origin(window, &origin_x, &origin_y); + + GdkEvent motion; + motion.motion.type = event->type; + g_object_ref(window); + motion.motion.window = window; + motion.motion.send_event = event->send_event; + motion.motion.time = event->time; + motion.motion.x = event->x_root - origin_x; + motion.motion.y = event->y_root - origin_y; + motion.motion.axes = event->axes; + motion.motion.state = event->state; + motion.motion.is_hint = event->is_hint; + motion.motion.device = event->device; + motion.motion.x_root = event->x_root; + motion.motion.y_root = event->y_root; + gtk_widget_event(widget, &motion); +} + +static void clear_state(FlGestureHelper* self) { + g_clear_object(&self->grabbed_widget); + g_list_free_full(self->event_list, free_event); + self->event_list = nullptr; +} + +void fl_gesture_helper_button_press(FlGestureHelper* self, GdkEvent* event) { + clear_state(self); + self->pointer_id++; + + self->event_list = g_list_append(self->event_list, gdk_event_copy(event)); +} + +void fl_gesture_helper_button_release(FlGestureHelper* self, GdkEvent* event) { + if (self->grabbed_widget) { + send_button_event(self, &event->button, self->grabbed_widget); + + clear_state(self); + } else { + self->event_list = g_list_append(self->event_list, gdk_event_copy(event)); + } +} + +void fl_gesture_helper_button_motion(FlGestureHelper* self, GdkEvent* event) { + if (self->grabbed_widget) { + // We have grabbed the widget, we directly distribute the event to the + // widget. + if (!self->end) { + send_motion_event(self, &event->motion, self->grabbed_widget); + } + } else { + if (self->pressed_buttons) { + // Or if there are some mouse buttons pressed, delay the event. + self->event_list = g_list_append(self->event_list, gdk_event_copy(event)); + } else if (self->hover_widget) { + // Or mouse is hovering. + send_motion_event(self, &event->motion, self->hover_widget); + } + } +} + +void fl_gesture_helper_accept_gesture(FlGestureHelper* self, + GtkWidget* widget, + int64_t pointer_id) { + g_set_object(&self->grabbed_widget, widget); + + // send stored events to grabbed widget. + for (GList* event = self->event_list; event; event = event->next) { + GdkEvent* gdk_event = reinterpret_cast(event->data); + if (gdk_event->type == GDK_BUTTON_PRESS || + gdk_event->type == GDK_BUTTON_RELEASE) { + send_button_event(self, &gdk_event->button, self->grabbed_widget); + } else if (gdk_event->type == GDK_MOTION_NOTIFY) { + if (self->pressed_buttons && !self->end) { + send_motion_event(self, &gdk_event->motion, self->grabbed_widget); + } + } + } +} + +void fl_gesture_helper_enter(FlGestureHelper* self, GtkWidget* widget) { + g_set_object(&self->hover_widget, widget); +} + +void fl_gesture_helper_exit(FlGestureHelper* self, GtkWidget* widget) { + g_clear_object(&self->hover_widget); +} diff --git a/shell/platform/linux/fl_gesture_helper.h b/shell/platform/linux/fl_gesture_helper.h new file mode 100644 index 0000000000000..39a6bc47f6bd2 --- /dev/null +++ b/shell/platform/linux/fl_gesture_helper.h @@ -0,0 +1,102 @@ +// 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_GESTURE_HELPER_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_GESTURE_HELPER_H_ + +#include + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlGestureHelper, + fl_gesture_helper, + FL, + GESTURE_HELPER, + GObject) + +/** + * FlGestureHelper: + * + * #FlGestureHelper captures and redistributes mouse events to embedded platform + * views. + */ + +/** + * fl_gesture_helper_new: + * + * Creates a new #FlGestureHelper object. + * + * Returns: a new #FlGestureHelper. + */ +FlGestureHelper* fl_gesture_helper_new(); + +/** + * fl_gesture_helper_button_press: + * @gesture_helper: an #FlGestureHelper. + * @event: a #GdkEventButton event. + * + * Redistrubutes @event to a embedded platform view. + */ +void fl_gesture_helper_button_press(FlGestureHelper* gesture_helper, + GdkEvent* event); + +/** + * fl_gesture_helper_button_release: + * @gesture_helper: an #FlGestureHelper. + * @event: a #GdkEventButton event. + * + * Redistributes @event to a embeeded platform view. + */ +void fl_gesture_helper_button_release(FlGestureHelper* gesture_helper, + GdkEvent* event); + +/** + * fl_gesture_helper_button_motion: + * @gesture_helper: an #FlGestureHelper. + * @event: a #GdkEventMotion event. + * + * Redistributes a #GdkEventMotion event to embedded platform views. + */ +void fl_gesture_helper_button_motion(FlGestureHelper* gesture_helper, + GdkEvent* event); + +/** + * fl_gesture_helper_accept_gesture: + * @gesture_helper: an #FlGestureHelper. + * @widget: the widget to distribute current gesture to. + * @pointer_id: touch sequence id. + * + * Distributes all delayed and following pointer events of current gesture to + * @widget. + */ +void fl_gesture_helper_accept_gesture(FlGestureHelper* gesture_helper, + GtkWidget* widget, + int64_t pointer_id); + +/** + * fl_gesture_helper_enter: + * @gesture_helper: an #FlGestureHelper. + * @widget: the widget that a mouse pointer enters into. + * + * Notify that a mouse pointer enters into @widget, and distribute following + * motion events to @widget. + */ +void fl_gesture_helper_enter(FlGestureHelper* gesture_helper, + GtkWidget* widget); + +/** + * fl_gesture_helper_exit: + * @gesture_helper: an #FlGestureHelper. + * @widget: the widget that a mouse pointer exits from. + * + * Notify that a mouse pointer exits from @widget, and stop distributing motion + * events to @widget. + */ +void fl_gesture_helper_exit(FlGestureHelper* gesture_helper, GtkWidget* widget); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_GESTURE_HELPER_H_ diff --git a/shell/platform/linux/fl_platform_views.cc b/shell/platform/linux/fl_platform_views.cc new file mode 100644 index 0000000000000..18de311d66a9f --- /dev/null +++ b/shell/platform/linux/fl_platform_views.cc @@ -0,0 +1,58 @@ +// 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_platform_views.h" + +#include + +// Added here to stop the compiler from optimizing this function away. +G_MODULE_EXPORT GType fl_platform_view_get_type(); + +G_DEFINE_TYPE(FlPlatformView, fl_platform_view, G_TYPE_OBJECT) + +static void fl_platform_view_class_init(FlPlatformViewClass* klass) {} + +static void fl_platform_view_init(FlPlatformView* self) {} + +G_MODULE_EXPORT GtkWidget* fl_platform_view_get_view(FlPlatformView* self) { + g_return_val_if_fail(FL_IS_PLATFORM_VIEW(self), nullptr); + + GtkWidget* widget = FL_PLATFORM_VIEW_GET_CLASS(self)->get_view(self); + if (widget && !GTK_IS_WIDGET(widget)) { + g_critical("fl_platform_view::get_view should return GtkWidget"); + } + return widget; +} + +// Added here to stop the compiler from optimizing this function away. +G_MODULE_EXPORT GType fl_platform_view_factory_get_type(); + +G_DEFINE_INTERFACE(FlPlatformViewFactory, + fl_platform_view_factory, + G_TYPE_OBJECT) + +static void fl_platform_view_factory_default_init( + FlPlatformViewFactoryInterface* iface) {} + +G_MODULE_EXPORT FlPlatformView* fl_platform_view_factory_create_platform_view( + FlPlatformViewFactory* self, + int64_t view_identifier, + FlValue* args) { + g_return_val_if_fail(FL_IS_PLATFORM_VIEW_FACTORY(self), nullptr); + + return FL_PLATFORM_VIEW_FACTORY_GET_IFACE(self)->create_platform_view( + self, view_identifier, args); +} + +G_MODULE_EXPORT FlMessageCodec* +fl_platform_view_factory_get_create_arguments_codec( + FlPlatformViewFactory* self) { + g_return_val_if_fail(FL_IS_PLATFORM_VIEW_FACTORY(self), nullptr); + + if (!FL_PLATFORM_VIEW_FACTORY_GET_IFACE(self)->get_create_arguments_codec) + return nullptr; + + return FL_PLATFORM_VIEW_FACTORY_GET_IFACE(self)->get_create_arguments_codec( + self); +} diff --git a/shell/platform/linux/fl_platform_views_plugin.cc b/shell/platform/linux/fl_platform_views_plugin.cc new file mode 100644 index 0000000000000..aa0c1707cf0a4 --- /dev/null +++ b/shell/platform/linux/fl_platform_views_plugin.cc @@ -0,0 +1,310 @@ +// 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_views_plugin.h" + +#include +#include + +#include "flutter/shell/platform/linux/fl_gesture_helper.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" + +static constexpr char kChannelName[] = "flutter/platform_views"; +static constexpr char kBadArgumentsError[] = "Bad Arguments"; +static constexpr char kCreateMethod[] = "create"; +static constexpr char kDisposeMethod[] = "dispose"; +static constexpr char kAcceptGestureMethod[] = "acceptGesture"; +static constexpr char kRejectGestureMethod[] = "rejectGesture"; +static constexpr char kEnterMethod[] = "enter"; +static constexpr char kExitMethod[] = "exit"; + +struct _FlPlatformViewsPlugin { + GObject parent_instance; + + FlMethodChannel* channel; + + FlGestureHelper* gesture_helper; + + GHashTable* factories; // string -> FlPlatformViewFactory* + + GHashTable* platform_views; // int -> FlPlatformView* +}; + +G_DEFINE_TYPE(FlPlatformViewsPlugin, fl_platform_views_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); + } +} + +static FlPlatformView* get_platform_view(FlPlatformViewsPlugin* self, + int64_t id) { + gpointer p = g_hash_table_lookup(self->platform_views, GINT_TO_POINTER(id)); + if (p && FL_IS_PLATFORM_VIEW(p)) { + return FL_PLATFORM_VIEW(p); + } else { + return nullptr; + } +} + +// Creates a new platform view. +static FlMethodResponse* platform_views_create(FlPlatformViewsPlugin* 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* id_value = fl_value_lookup_string(args, "id"); + if (id_value == nullptr || fl_value_get_type(id_value) != FL_VALUE_TYPE_INT) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Missing platform view id", nullptr)); + } + int64_t id = fl_value_get_int(id_value); + + FlValue* view_type_value = fl_value_lookup_string(args, "viewType"); + if (view_type_value == nullptr || + fl_value_get_type(view_type_value) != FL_VALUE_TYPE_STRING) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Missing platform view viewType", nullptr)); + } + const gchar* view_type = fl_value_get_string(view_type_value); + + FlValue* params_value = fl_value_lookup_string(args, "params"); + + FlPlatformView* platform_view = get_platform_view(self, id); + if (platform_view) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Platform view id already exists", nullptr)); + } + + gpointer factory_pointer = g_hash_table_lookup(self->factories, view_type); + if (!factory_pointer) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "View type does not exist", nullptr)); + } + FlPlatformViewFactory* factory = FL_PLATFORM_VIEW_FACTORY(factory_pointer); + + g_autoptr(FlMessageCodec) codec = + fl_platform_view_factory_get_create_arguments_codec(factory); + FlValue* creation_params = nullptr; + if (codec && params_value) { + const uint8_t* creation_params_bytes = + fl_value_get_uint8_list(params_value); + size_t creation_params_length = fl_value_get_length(params_value); + g_autoptr(GBytes) bytes = + g_bytes_new_static(creation_params_bytes, creation_params_length); + + GError* error = nullptr; + creation_params = fl_message_codec_decode_message(codec, bytes, &error); + } + + platform_view = fl_platform_view_factory_create_platform_view( + factory, id, creation_params); + if (!FL_IS_PLATFORM_VIEW(platform_view)) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Invalid platform view", nullptr)); + } + + g_hash_table_insert(self->platform_views, GINT_TO_POINTER(id), platform_view); + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); +} + +// Called when Flutter considers this gesture applied to a platform view. +static FlMethodResponse* platform_views_accept_gesture( + FlPlatformViewsPlugin* self, + FlValue* args) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Argument map missing or malformed", nullptr)); + } + + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); + int64_t pointer_id = fl_value_get_int(fl_value_get_list_value(args, 1)); + FlPlatformView* platform_view = + fl_platform_views_plugin_get_platform_view(self, view_id); + if (!platform_view) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Platform view not found", nullptr)); + } + + GtkWidget* widget = fl_platform_view_get_view(platform_view); + if (widget) + fl_gesture_helper_accept_gesture(self->gesture_helper, widget, pointer_id); + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); +} + +// Reject gesture captured by the platform view. +static FlMethodResponse* platform_views_reject_gesture( + FlPlatformViewsPlugin* self, + FlValue* args) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Argument map missing or malformed", nullptr)); + } + + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); + FlPlatformView* platform_view = + fl_platform_views_plugin_get_platform_view(self, view_id); + if (!platform_view) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Platform view not found", nullptr)); + } + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); +} + +// Mark that a mouse pointer has entered into the platform view. +static FlMethodResponse* platform_views_enter(FlPlatformViewsPlugin* self, + FlValue* args) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Argument map missing or malformed", nullptr)); + } + + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); + FlPlatformView* platform_view = + fl_platform_views_plugin_get_platform_view(self, view_id); + if (!platform_view) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Platform view not found", nullptr)); + } + + GtkWidget* widget = fl_platform_view_get_view(platform_view); + if (widget) + fl_gesture_helper_enter(self->gesture_helper, widget); + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); +} + +// Mark that a mouse pointer has exited from the platform view. +static FlMethodResponse* platform_views_exit(FlPlatformViewsPlugin* self, + FlValue* args) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Argument map missing or malformed", nullptr)); + } + + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); + FlPlatformView* platform_view = + fl_platform_views_plugin_get_platform_view(self, view_id); + if (!platform_view) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Platform view not found", nullptr)); + } + + GtkWidget* widget = fl_platform_view_get_view(platform_view); + if (widget) + fl_gesture_helper_exit(self->gesture_helper, widget); + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); +} + +// Disposes the platform view. +static FlMethodResponse* platform_views_dispose(FlPlatformViewsPlugin* self, + FlValue* args) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_INT) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Argument map missing or malformed", nullptr)); + } + + int64_t id = fl_value_get_int(args); + + if (!g_hash_table_remove(self->platform_views, GINT_TO_POINTER(id))) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Platform view does not exist", nullptr)); + } + + 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) { + FlPlatformViewsPlugin* self = FL_PLATFORM_VIEWS_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, kCreateMethod) == 0) { + response = platform_views_create(self, args); + } else if (strcmp(method, kDisposeMethod) == 0) { + response = platform_views_dispose(self, args); + } else if (strcmp(method, kAcceptGestureMethod) == 0) { + response = platform_views_accept_gesture(self, args); + } else if (strcmp(method, kRejectGestureMethod) == 0) { + response = platform_views_reject_gesture(self, args); + } else if (strcmp(method, kEnterMethod) == 0) { + response = platform_views_enter(self, args); + } else if (strcmp(method, kExitMethod) == 0) { + response = platform_views_exit(self, args); + } else { + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + } + + if (response != nullptr) { + send_response(method_call, response); + } +} + +static void fl_platform_views_plugin_dispose(GObject* object) { + FlPlatformViewsPlugin* self = FL_PLATFORM_VIEWS_PLUGIN(object); + + g_clear_object(&self->channel); + g_hash_table_destroy(self->factories); + g_hash_table_destroy(self->platform_views); + self->factories = self->platform_views = nullptr; + + G_OBJECT_CLASS(fl_platform_views_plugin_parent_class)->dispose(object); +} + +static void fl_platform_views_plugin_class_init( + FlPlatformViewsPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_platform_views_plugin_dispose; +} + +static void fl_platform_views_plugin_init(FlPlatformViewsPlugin* self) {} + +FlPlatformViewsPlugin* fl_platform_views_plugin_new( + FlBinaryMessenger* messenger, + FlGestureHelper* gesture_helper) { + g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); + + FlPlatformViewsPlugin* self = FL_PLATFORM_VIEWS_PLUGIN( + g_object_new(fl_platform_views_plugin_get_type(), nullptr)); + + g_autoptr(FlStandardMethodCodec) codec = fl_standard_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); + + self->gesture_helper = gesture_helper; + self->factories = g_hash_table_new(g_str_hash, g_str_equal); + self->platform_views = g_hash_table_new(g_direct_hash, g_direct_equal); + + return self; +} + +gboolean fl_platform_views_plugin_register_view_factory( + FlPlatformViewsPlugin* self, + FlPlatformViewFactory* factory, + const gchar* view_type) { + return g_hash_table_insert(self->factories, g_strdup(view_type), factory); +} + +FlPlatformView* fl_platform_views_plugin_get_platform_view( + FlPlatformViewsPlugin* self, + int view_identifier) { + return (FlPlatformView*)g_hash_table_lookup(self->platform_views, + GINT_TO_POINTER(view_identifier)); +} diff --git a/shell/platform/linux/fl_platform_views_plugin.h b/shell/platform/linux/fl_platform_views_plugin.h new file mode 100644 index 0000000000000..a6112f2e8f72e --- /dev/null +++ b/shell/platform/linux/fl_platform_views_plugin.h @@ -0,0 +1,72 @@ +// 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_VIEWS_PLUGIN_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_VIEWS_PLUGIN_H_ + +#include "flutter/shell/platform/linux/fl_gesture_helper.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_platform_views.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlPlatformViewsPlugin, + fl_platform_views_plugin, + FL, + PLATFORM_VIEWS_PLUGIN, + GObject); + +/** + * FlPlatformViewsPlugin: + * + * #FlPlatformViewsPlugin is a plugin that implements the shell side + * of SystemChannels.platform_views from the Flutter services library. + */ + +/** + * fl_platform_views_plugin_new: + * @messenger: an #FlBinaryMessenger. + * @gesture_helper: an #FlGestureHelper for accepting gestures by Dart side. + * + * Creates a new plugin that implements SystemChannels.platform_views from the + * Flutter services library. + * + * Returns: a new #FlPlatformViewsPlugin. + */ +FlPlatformViewsPlugin* fl_platform_views_plugin_new( + FlBinaryMessenger* messenger, + FlGestureHelper* gesture_helper); + +/** + * fl_platform_views_plugin_register_view_factory: + * @plugin: an #FlPlatformViewsPlugin. + * @factory: the view factory that will be registered. + * @view_type: A unique identifier for the factory. The Dart code of the Flutter + * app can use this identifier to request creation of a #GtkWidget + * by the registered factory. + * + * Register platform view factory. + * + * Returns: %TRUE if view facotory has been successfully registered, %FALSE if + * @view_type has been occupied. + */ +gboolean fl_platform_views_plugin_register_view_factory( + FlPlatformViewsPlugin* plugin, + FlPlatformViewFactory* factory, + const gchar* view_type); + +/** + * fl_platform_views_plugin_get_platform_view: + * @plugin: an #FlPlatformViewsPlugin. + * @view_identifier: the identifier of platform view. + * + * Returns: an #FlPlatformView, or %NULL if @view_identifier is unknown. + */ +FlPlatformView* fl_platform_views_plugin_get_platform_view( + FlPlatformViewsPlugin* plugin, + int view_identifier); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_VIEWS_PLUGIN_H_ diff --git a/shell/platform/linux/fl_plugin_registrar.cc b/shell/platform/linux/fl_plugin_registrar.cc index 5207497b2fb30..0b8b365b56036 100644 --- a/shell/platform/linux/fl_plugin_registrar.cc +++ b/shell/platform/linux/fl_plugin_registrar.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" +#include "flutter/shell/platform/linux/fl_view_private.h" #include @@ -71,3 +72,16 @@ G_MODULE_EXPORT FlView* fl_plugin_registrar_get_view(FlPluginRegistrar* self) { return self->view; } + +G_MODULE_EXPORT void fl_plugin_registrar_register_view_factory( + FlPluginRegistrar* self, + FlPlatformViewFactory* factory, + const gchar* view_type) { + g_return_if_fail(FL_IS_PLUGIN_REGISTRAR(self)); + + if (self->view) { + FlPlatformViewsPlugin* plugin = + fl_view_get_platform_views_plugin(self->view); + fl_platform_views_plugin_register_view_factory(plugin, factory, view_type); + } +} diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h index 17cacd4c41480..0f5f343bb761c 100644 --- a/shell/platform/linux/fl_renderer.h +++ b/shell/platform/linux/fl_renderer.h @@ -53,15 +53,6 @@ struct _FlRendererClass { GdkGLContext** resource, GError** error); - /** - * Virtual method called when Flutter needs OpenGL proc address. - * @renderer: an #FlRenderer. - * @name: proc name. - * - * Returns: OpenGL proc address. - */ - void* (*get_proc_address)(); - /** * Virtual method called when Flutter needs a backing store for a specific * #FlutterLayer. diff --git a/shell/platform/linux/fl_renderer_gl.cc b/shell/platform/linux/fl_renderer_gl.cc index 09be16586dba6..9d1b4b4c01c61 100644 --- a/shell/platform/linux/fl_renderer_gl.cc +++ b/shell/platform/linux/fl_renderer_gl.cc @@ -5,6 +5,7 @@ #include "flutter/shell/platform/linux/fl_renderer_gl.h" #include "flutter/shell/platform/linux/fl_backing_store_provider.h" +#include "flutter/shell/platform/linux/fl_platform_views_plugin.h" #include "flutter/shell/platform/linux/fl_view_private.h" struct _FlRendererGL { @@ -105,7 +106,27 @@ static gboolean fl_renderer_gl_present_layers(FlRenderer* renderer, reinterpret_cast(framebuffer->user_data)); } break; case kFlutterLayerContentTypePlatformView: { - // Currently unsupported. + FlPlatformViewsPlugin* plugin = fl_view_get_platform_views_plugin(view); + FlPlatformView* platform_view = + fl_platform_views_plugin_get_platform_view( + plugin, layer->platform_view->identifier); + GtkWidget* widget = fl_platform_view_get_view(platform_view); + if (!widget) + continue; + GdkRectangle geometry = { + .x = static_cast(layer->offset.x), + .y = static_cast(layer->offset.y), + .width = static_cast(layer->size.width), + .height = static_cast(layer->size.height), + }; + GPtrArray* mutations = + g_ptr_array_new_full(layer->platform_view->mutations_count, g_free); + for (size_t i = 0; i < layer->platform_view->mutations_count; i++) { + FlutterPlatformViewMutation* mutation = + g_new(FlutterPlatformViewMutation, 1); + g_ptr_array_add(mutations, mutation); + } + fl_view_add_widget(view, widget, &geometry, mutations); } break; } } diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index ea6f2f89ec634..721a426115a7b 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -10,9 +10,11 @@ #include "flutter/shell/platform/linux/fl_accessibility_plugin.h" #include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/fl_gesture_helper.h" #include "flutter/shell/platform/linux/fl_key_event_plugin.h" #include "flutter/shell/platform/linux/fl_mouse_cursor_plugin.h" #include "flutter/shell/platform/linux/fl_platform_plugin.h" +#include "flutter/shell/platform/linux/fl_platform_views_plugin.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" #include "flutter/shell/platform/linux/fl_renderer_gl.h" #include "flutter/shell/platform/linux/fl_text_input_plugin.h" @@ -43,6 +45,9 @@ struct _FlView { FlMouseCursorPlugin* mouse_cursor_plugin; FlPlatformPlugin* platform_plugin; FlTextInputPlugin* text_input_plugin; + FlPlatformViewsPlugin* platform_views_plugin; + + FlGestureHelper* gesture_helper; GList* gl_area_list; GList* used_area_list; @@ -56,6 +61,7 @@ struct _FlView { typedef struct _FlViewChild { GtkWidget* widget; GdkRectangle geometry; + GPtrArray* mutations; } FlViewChild; enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST }; @@ -175,6 +181,8 @@ static void fl_view_constructed(GObject* object) { self->renderer = FL_RENDERER(fl_renderer_gl_new()); self->engine = fl_engine_new(self->project, self->renderer); + self->gesture_helper = fl_gesture_helper_new(); + fl_engine_set_update_semantics_node_handler( self->engine, fl_view_update_semantics_node_cb, self, nullptr); @@ -186,6 +194,8 @@ static void fl_view_constructed(GObject* object) { fl_key_event_plugin_new(messenger, self->text_input_plugin); self->mouse_cursor_plugin = fl_mouse_cursor_plugin_new(messenger, self); self->platform_plugin = fl_platform_plugin_new(messenger); + self->platform_views_plugin = + fl_platform_views_plugin_new(messenger, self->gesture_helper); self->event_box = gtk_event_box_new(); gtk_widget_set_parent(self->event_box, GTK_WIDGET(self)); @@ -266,6 +276,7 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->mouse_cursor_plugin); g_clear_object(&self->platform_plugin); g_clear_object(&self->text_input_plugin); + g_clear_object(&self->platform_views_plugin); g_list_free_full(self->gl_area_list, g_object_unref); self->gl_area_list = nullptr; @@ -450,6 +461,9 @@ static gboolean event_box_button_press_event(GtkWidget* widget, return FALSE; } + fl_gesture_helper_button_press(view->gesture_helper, + reinterpret_cast(event)); + if (!gtk_widget_has_focus(GTK_WIDGET(view))) { gtk_widget_grab_focus(GTK_WIDGET(view)); } @@ -460,6 +474,8 @@ static gboolean event_box_button_press_event(GtkWidget* widget, static gboolean event_box_button_release_event(GtkWidget* widget, GdkEventButton* event, FlView* view) { + fl_gesture_helper_button_release(view->gesture_helper, + reinterpret_cast(event)); return fl_view_send_pointer_button_event(view, event); } @@ -506,6 +522,9 @@ static gboolean event_box_motion_notify_event(GtkWidget* widget, return FALSE; } + fl_gesture_helper_button_motion(view->gesture_helper, + reinterpret_cast(event)); + gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view)); fl_engine_send_mouse_pointer_event( view->engine, view->button_state != 0 ? kMove : kHover, @@ -561,6 +580,8 @@ static void fl_view_remove(GtkContainer* container, GtkWidget* widget) { gtk_widget_unparent(widget); self->children_list = g_list_remove_link(self->children_list, iterator); g_list_free(iterator); + + g_ptr_array_unref(child->mutations); g_free(child); break; @@ -655,6 +676,11 @@ G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* view) { return view->engine; } +FlPlatformViewsPlugin* fl_view_get_platform_views_plugin(FlView* self) { + g_return_val_if_fail(FL_IS_VIEW(self), FALSE); + return self->platform_views_plugin; +} + void fl_view_begin_frame(FlView* view) { g_return_if_fail(FL_IS_VIEW(view)); FlView* self = FL_VIEW(view); @@ -666,13 +692,15 @@ void fl_view_begin_frame(FlView* view) { static void fl_view_add_pending_child(FlView* self, GtkWidget* widget, - GdkRectangle* geometry) { + GdkRectangle* geometry, + GPtrArray* mutations) { FlViewChild* child = g_new(FlViewChild, 1); child->widget = widget; if (geometry) child->geometry = *geometry; else child->geometry = {0, 0, 0, 0}; + child->mutations = mutations; self->pending_children_list = g_list_append(self->pending_children_list, child); @@ -693,15 +721,16 @@ void fl_view_add_gl_area(FlView* view, } gtk_widget_show(GTK_WIDGET(area)); - fl_view_add_pending_child(view, GTK_WIDGET(area), nullptr); + fl_view_add_pending_child(view, GTK_WIDGET(area), nullptr, nullptr); fl_gl_area_queue_render(area, texture); } void fl_view_add_widget(FlView* view, GtkWidget* widget, - GdkRectangle* geometry) { + GdkRectangle* geometry, + GPtrArray* mutations) { gtk_widget_show(widget); - fl_view_add_pending_child(view, widget, geometry); + fl_view_add_pending_child(view, widget, geometry, mutations); } GList* find_child(GList* list, GtkWidget* widget) { diff --git a/shell/platform/linux/fl_view_private.h b/shell/platform/linux/fl_view_private.h index 51c3107435966..765001783aef5 100644 --- a/shell/platform/linux/fl_view_private.h +++ b/shell/platform/linux/fl_view_private.h @@ -8,6 +8,17 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" #include "flutter/shell/platform/linux/fl_gl_area.h" +#include "flutter/shell/platform/linux/fl_platform_views_plugin.h" + +/** + * fl_view_get_platform_views_plugin(FlView* view); + * @view: an #FlView. + * + * Get #FlPlatformViewsPlugin related to @view. + * + * Returns: an #FlPlatformViewsPlugin. + */ +FlPlatformViewsPlugin* fl_view_get_platform_views_plugin(FlView* view); /** * fl_view_begin_frame: @@ -37,12 +48,14 @@ void fl_view_add_gl_area(FlView* view, * @view: an #FlView. * @widget: a #GtkWidget. * @geometry: geometry of the widget. + * @mutations: (transfer-full): the mutations applied to the widget. * * Append a #GtkWidget at top of stacked children of #FlView. */ void fl_view_add_widget(FlView* view, GtkWidget* widget, - GdkRectangle* geometry); + GdkRectangle* geometry, + GPtrArray* mutations); /** * fl_view_end_frame: diff --git a/shell/platform/linux/public/flutter_linux/fl_platform_views.h b/shell/platform/linux/public/flutter_linux/fl_platform_views.h new file mode 100644 index 0000000000000..e17fc10c3e5b3 --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_platform_views.h @@ -0,0 +1,137 @@ +// 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_VIEWS_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_VIEWS_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include "fl_message_codec.h" +#include "fl_value.h" + +G_BEGIN_DECLS + +G_DECLARE_DERIVABLE_TYPE(FlPlatformView, + fl_platform_view, + FL, + PLATFORM_VIEW, + GObject) + +/** + * FlPlatformView: + * + * #FlPlatformView is an abstract class that wraps a #GtkWidget for embedding in + * the Flutter hierarchy. + */ + +struct _FlPlatformViewClass { + GObjectClass parent_class; + + /** + * Virtual method called when Flutter needs the #GtkWidget for embedding. + * @platform_view: an #FlPlatformView. + * + * Returns: (allow-none): an #GtkWidget or %NULL if no widgets can be + * provided. + */ + GtkWidget* (*get_view)(FlPlatformView* platform_view); +}; + +/** + * fl_platform_view_get_view: + * @platform_view: an #FlPlatformView. + * + * Returns: (allow-none): an #GtkWidget or %NULL if no widgets can be provided. + */ +GtkWidget* fl_platform_view_get_view(FlPlatformView* platform_view); + +// + +G_DECLARE_INTERFACE(FlPlatformViewFactory, + fl_platform_view_factory, + FL, + PLATFORM_VIEW_FACTORY, + GObject) + +/** + * FlPlatformViewFactory: + * + * #FlPlatformViewFactory vends #FlPlatformView objects for embedding. + */ + +struct _FlPlatformViewFactoryInterface { + GTypeInterface parent_interface; + + /** + * FlPlatformViewFactory::create_platform_view: + * @factory: an #FlPlatformViewFactory. + * @view_identifier: a unique identifier for this #GtkWidget. + * @args: parameters for creating the #GtkWidget sent from the Dart side of + * the Flutter app. If get_create_arguments_codec is not implemented, + * or if no creation arguments were sent from the Dart code, this will + * be null. Otherwise this will be the value sent from the Dart code as + * decoded by get_create_arguments_codec. + * + * Creates an #FlPlatformView for embedding. + * + * Returns: (transfer full): an #FlPlatformView. + */ + FlPlatformView* (*create_platform_view)(FlPlatformViewFactory* factory, + int64_t view_identifier, + FlValue* args); + + /** + * FlPlatformViewFactory::get_create_arguments_codec: + * @factory: an #FlPlatformViewFactory. + * + * Creates an #FlMessageCodec if needed. Only needs to be implemented if + * create_platform_view needs arguments. + * + * Returns: (transfer full): an #FlMessageCodec for decoding the args + * parameter of create_platform_view, or %NULL if no creation arguments will + * be sent. + */ + FlMessageCodec* (*get_create_arguments_codec)(FlPlatformViewFactory* self); +}; + +/** + * fl_platform_view_factory_create_platform_view: + * @factory: an #FlPlatformViewFactory. + * @view_identifier: a unique identifier for this #GtkWidget. + * @args: parameters for creating the #GtkWidget sent from the Dart side of + * the Flutter app. If get_create_arguments_codec is not implemented, or + * if no creation arguments were sent from the Dart code, this will be + * null. Otherwise this will be the value sent from the Dart code as + * decoded by get_create_arguments_codec. + * + * Creates an #FlPlatformView for embedding. + * + * Returns: (transfer full): an #FlPlatformView. + */ +FlPlatformView* fl_platform_view_factory_create_platform_view( + FlPlatformViewFactory* factory, + int64_t view_identifier, + FlValue* args); + +/** + * fl_platform_view_factory_get_create_arguments_codec: + * @factory: an #FlPlatformViewFactory. + * + * Creates an #FlMessageCodec if needed. Only needs to be implemented if + * create_platform_view needs arguments. + * + * Returns: (transfer full): an #FlMessageCodec for decoding the args + * parameter of create_platform_view, or %NULL if no creation arguments will + * be sent. + */ +FlMessageCodec* fl_platform_view_factory_get_create_arguments_codec( + FlPlatformViewFactory* factory); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_VIEWS_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h index 5ac073c1844ae..fcb1b85542e08 100644 --- a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h +++ b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h @@ -12,6 +12,7 @@ #include #include "fl_binary_messenger.h" +#include "fl_platform_views.h" #include "fl_view.h" G_BEGIN_DECLS @@ -49,6 +50,22 @@ FlBinaryMessenger* fl_plugin_registrar_get_messenger( */ FlView* fl_plugin_registrar_get_view(FlPluginRegistrar* registrar); +/** + * fl_plugin_registrar_register_view_factory: + * @registrar: an #FlPluginRegistrar. + * @factory: the view factory that will be registered. + * @view_type: A unique identifier for the factory. The Dart code of the Flutter + * app can use this identifier to request creation of a #GtkWidget + * by the registered factory. + * + * Registers a #FlPlatformViewFactory for creation of platform views. + * Plugins can expose a GtkWidget for embedding in Flutter apps by registering a + * view factory. + */ +void fl_plugin_registrar_register_view_factory(FlPluginRegistrar* registrar, + FlPlatformViewFactory* factory, + const gchar* view_type); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PLUGIN_REGISTRAR_H_ From df150a646ffac0756eaa51575653a1d4ed567ebd Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 4 Feb 2021 15:12:59 +0800 Subject: [PATCH 02/14] Fix docs --- shell/platform/linux/fl_gesture_helper.cc | 62 +++++++------------ .../linux/fl_platform_views_plugin.cc | 44 ++++++------- .../platform/linux/fl_platform_views_plugin.h | 9 +-- shell/platform/linux/fl_plugin_registrar.cc | 3 + shell/platform/linux/fl_view.cc | 1 + .../flutter_linux/fl_plugin_registrar.h | 2 +- 6 files changed, 53 insertions(+), 68 deletions(-) diff --git a/shell/platform/linux/fl_gesture_helper.cc b/shell/platform/linux/fl_gesture_helper.cc index b5971f46be2b9..97ae09ff35b10 100644 --- a/shell/platform/linux/fl_gesture_helper.cc +++ b/shell/platform/linux/fl_gesture_helper.cc @@ -30,38 +30,31 @@ static void free_event(gpointer data) { } static void send_button_event(FlGestureHelper* self, - GdkEventButton* event, + GdkEvent* event, GtkWidget* widget) { if (event->type == GDK_BUTTON_PRESS) { self->end = false; - self->pressed_buttons |= 1 << event->button; + self->pressed_buttons |= 1 << event->button.button; } else if (event->type == GDK_BUTTON_RELEASE) { self->end = true; - self->pressed_buttons &= ~(1 << event->button); + self->pressed_buttons &= ~(1 << event->button.button); + } else { + return; } GdkWindow* window = gtk_widget_get_window(widget); gint origin_x, origin_y; gdk_window_get_origin(window, &origin_x, &origin_y); - GdkEvent button; - button.button.type = event->type; - g_object_ref(window); - button.button.window = window; - button.button.send_event = event->send_event; - button.button.time = event->time; - button.button.x = event->x_root - origin_x; - button.button.y = event->y_root - origin_y; - button.button.axes = event->axes; - button.button.state = event->state; - button.button.button = event->button; - button.button.device = event->device; - button.button.x_root = event->x_root; - button.button.y_root = event->y_root; - gtk_widget_event(widget, &button); + GdkEvent* new_event = gdk_event_copy(event); + GdkEventButton* button = &new_event->button; + button->x = event->button.x_root - origin_x; + button->y = event->button.y_root - origin_y; + gtk_widget_event(widget, new_event); + gdk_event_free(new_event); } static void send_motion_event(FlGestureHelper* self, - GdkEventMotion* event, + GdkEvent* event, GtkWidget* widget) { if (self->end) return; @@ -70,21 +63,12 @@ static void send_motion_event(FlGestureHelper* self, gint origin_x, origin_y; gdk_window_get_origin(window, &origin_x, &origin_y); - GdkEvent motion; - motion.motion.type = event->type; - g_object_ref(window); - motion.motion.window = window; - motion.motion.send_event = event->send_event; - motion.motion.time = event->time; - motion.motion.x = event->x_root - origin_x; - motion.motion.y = event->y_root - origin_y; - motion.motion.axes = event->axes; - motion.motion.state = event->state; - motion.motion.is_hint = event->is_hint; - motion.motion.device = event->device; - motion.motion.x_root = event->x_root; - motion.motion.y_root = event->y_root; - gtk_widget_event(widget, &motion); + GdkEvent* new_event = gdk_event_copy(event); + GdkEventMotion* motion = &new_event->motion; + motion->x = event->motion.x_root - origin_x; + motion->y = event->motion.y_root - origin_y; + gtk_widget_event(widget, new_event); + gdk_event_free(new_event); } static void clear_state(FlGestureHelper* self) { @@ -102,7 +86,7 @@ void fl_gesture_helper_button_press(FlGestureHelper* self, GdkEvent* event) { void fl_gesture_helper_button_release(FlGestureHelper* self, GdkEvent* event) { if (self->grabbed_widget) { - send_button_event(self, &event->button, self->grabbed_widget); + send_button_event(self, event, self->grabbed_widget); clear_state(self); } else { @@ -115,7 +99,7 @@ void fl_gesture_helper_button_motion(FlGestureHelper* self, GdkEvent* event) { // We have grabbed the widget, we directly distribute the event to the // widget. if (!self->end) { - send_motion_event(self, &event->motion, self->grabbed_widget); + send_motion_event(self, event, self->grabbed_widget); } } else { if (self->pressed_buttons) { @@ -123,7 +107,7 @@ void fl_gesture_helper_button_motion(FlGestureHelper* self, GdkEvent* event) { self->event_list = g_list_append(self->event_list, gdk_event_copy(event)); } else if (self->hover_widget) { // Or mouse is hovering. - send_motion_event(self, &event->motion, self->hover_widget); + send_motion_event(self, event, self->hover_widget); } } } @@ -138,10 +122,10 @@ void fl_gesture_helper_accept_gesture(FlGestureHelper* self, GdkEvent* gdk_event = reinterpret_cast(event->data); if (gdk_event->type == GDK_BUTTON_PRESS || gdk_event->type == GDK_BUTTON_RELEASE) { - send_button_event(self, &gdk_event->button, self->grabbed_widget); + send_button_event(self, gdk_event, self->grabbed_widget); } else if (gdk_event->type == GDK_MOTION_NOTIFY) { if (self->pressed_buttons && !self->end) { - send_motion_event(self, &gdk_event->motion, self->grabbed_widget); + send_motion_event(self, gdk_event, self->grabbed_widget); } } } diff --git a/shell/platform/linux/fl_platform_views_plugin.cc b/shell/platform/linux/fl_platform_views_plugin.cc index aa0c1707cf0a4..d6e66889803c3 100644 --- a/shell/platform/linux/fl_platform_views_plugin.cc +++ b/shell/platform/linux/fl_platform_views_plugin.cc @@ -25,6 +25,7 @@ struct _FlPlatformViewsPlugin { FlMethodChannel* channel; + // gesture_helper is not owned here. FlGestureHelper* gesture_helper; GHashTable* factories; // string -> FlPlatformViewFactory* @@ -43,16 +44,6 @@ static void send_response(FlMethodCall* method_call, } } -static FlPlatformView* get_platform_view(FlPlatformViewsPlugin* self, - int64_t id) { - gpointer p = g_hash_table_lookup(self->platform_views, GINT_TO_POINTER(id)); - if (p && FL_IS_PLATFORM_VIEW(p)) { - return FL_PLATFORM_VIEW(p); - } else { - return nullptr; - } -} - // Creates a new platform view. static FlMethodResponse* platform_views_create(FlPlatformViewsPlugin* self, FlValue* args) { @@ -78,18 +69,19 @@ static FlMethodResponse* platform_views_create(FlPlatformViewsPlugin* self, FlValue* params_value = fl_value_lookup_string(args, "params"); - FlPlatformView* platform_view = get_platform_view(self, id); + FlPlatformView* platform_view = + fl_platform_views_plugin_get_platform_view(self, id); if (platform_view) { return FL_METHOD_RESPONSE(fl_method_error_response_new( kBadArgumentsError, "Platform view id already exists", nullptr)); } - gpointer factory_pointer = g_hash_table_lookup(self->factories, view_type); - if (!factory_pointer) { + FlPlatformViewFactory* factory = + FL_PLATFORM_VIEW_FACTORY(g_hash_table_lookup(self->factories, view_type)); + if (!factory) { return FL_METHOD_RESPONSE(fl_method_error_response_new( kBadArgumentsError, "View type does not exist", nullptr)); } - FlPlatformViewFactory* factory = FL_PLATFORM_VIEW_FACTORY(factory_pointer); g_autoptr(FlMessageCodec) codec = fl_platform_view_factory_get_create_arguments_codec(factory); @@ -121,9 +113,10 @@ static FlMethodResponse* platform_views_create(FlPlatformViewsPlugin* self, static FlMethodResponse* platform_views_accept_gesture( FlPlatformViewsPlugin* self, FlValue* args) { - if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST || + fl_value_get_length(args) != 2) { return FL_METHOD_RESPONSE(fl_method_error_response_new( - kBadArgumentsError, "Argument map missing or malformed", nullptr)); + kBadArgumentsError, "Argument list missing or malformed", nullptr)); } int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); @@ -145,7 +138,8 @@ static FlMethodResponse* platform_views_accept_gesture( static FlMethodResponse* platform_views_reject_gesture( FlPlatformViewsPlugin* self, FlValue* args) { - if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST || + fl_value_get_length(args) != 1) { return FL_METHOD_RESPONSE(fl_method_error_response_new( kBadArgumentsError, "Argument map missing or malformed", nullptr)); } @@ -164,9 +158,10 @@ static FlMethodResponse* platform_views_reject_gesture( // Mark that a mouse pointer has entered into the platform view. static FlMethodResponse* platform_views_enter(FlPlatformViewsPlugin* self, FlValue* args) { - if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST || + fl_value_get_length(args) != 1) { return FL_METHOD_RESPONSE(fl_method_error_response_new( - kBadArgumentsError, "Argument map missing or malformed", nullptr)); + kBadArgumentsError, "Argument list missing or malformed", nullptr)); } int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); @@ -187,9 +182,10 @@ static FlMethodResponse* platform_views_enter(FlPlatformViewsPlugin* self, // Mark that a mouse pointer has exited from the platform view. static FlMethodResponse* platform_views_exit(FlPlatformViewsPlugin* self, FlValue* args) { - if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST || + fl_value_get_length(args) != 1) { return FL_METHOD_RESPONSE(fl_method_error_response_new( - kBadArgumentsError, "Argument map missing or malformed", nullptr)); + kBadArgumentsError, "Argument list missing or malformed", nullptr)); } int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); @@ -304,7 +300,7 @@ gboolean fl_platform_views_plugin_register_view_factory( FlPlatformView* fl_platform_views_plugin_get_platform_view( FlPlatformViewsPlugin* self, - int view_identifier) { - return (FlPlatformView*)g_hash_table_lookup(self->platform_views, - GINT_TO_POINTER(view_identifier)); + int64_t view_identifier) { + return reinterpret_cast(g_hash_table_lookup( + self->platform_views, GINT_TO_POINTER(view_identifier))); } diff --git a/shell/platform/linux/fl_platform_views_plugin.h b/shell/platform/linux/fl_platform_views_plugin.h index a6112f2e8f72e..5199e22a9fe5a 100644 --- a/shell/platform/linux/fl_platform_views_plugin.h +++ b/shell/platform/linux/fl_platform_views_plugin.h @@ -26,8 +26,9 @@ G_DECLARE_FINAL_TYPE(FlPlatformViewsPlugin, /** * fl_platform_views_plugin_new: - * @messenger: an #FlBinaryMessenger. - * @gesture_helper: an #FlGestureHelper for accepting gestures by Dart side. + * @messenger: (transfer-none): an #FlBinaryMessenger. + * @gesture_helper: (transfer-none): an #FlGestureHelper for accepting gestures + * by Dart side. * * Creates a new plugin that implements SystemChannels.platform_views from the * Flutter services library. @@ -41,7 +42,7 @@ FlPlatformViewsPlugin* fl_platform_views_plugin_new( /** * fl_platform_views_plugin_register_view_factory: * @plugin: an #FlPlatformViewsPlugin. - * @factory: the view factory that will be registered. + * @factory: (transfer-full): the view factory that will be registered. * @view_type: A unique identifier for the factory. The Dart code of the Flutter * app can use this identifier to request creation of a #GtkWidget * by the registered factory. @@ -65,7 +66,7 @@ gboolean fl_platform_views_plugin_register_view_factory( */ FlPlatformView* fl_platform_views_plugin_get_platform_view( FlPlatformViewsPlugin* plugin, - int view_identifier); + int64_t view_identifier); G_END_DECLS diff --git a/shell/platform/linux/fl_plugin_registrar.cc b/shell/platform/linux/fl_plugin_registrar.cc index 0b8b365b56036..4b211d22beaf3 100644 --- a/shell/platform/linux/fl_plugin_registrar.cc +++ b/shell/platform/linux/fl_plugin_registrar.cc @@ -83,5 +83,8 @@ G_MODULE_EXPORT void fl_plugin_registrar_register_view_factory( FlPlatformViewsPlugin* plugin = fl_view_get_platform_views_plugin(self->view); fl_platform_views_plugin_register_view_factory(plugin, factory, view_type); + } else { + g_warning("Cannot register view factory before FlView is initialized"); + g_object_unref(factory); } } diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 721a426115a7b..48f3c9f4dcbc1 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -277,6 +277,7 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->platform_plugin); g_clear_object(&self->text_input_plugin); g_clear_object(&self->platform_views_plugin); + g_clear_object(&self->gesture_helper); g_list_free_full(self->gl_area_list, g_object_unref); self->gl_area_list = nullptr; diff --git a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h index fcb1b85542e08..887e7ff20430a 100644 --- a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h +++ b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h @@ -53,7 +53,7 @@ FlView* fl_plugin_registrar_get_view(FlPluginRegistrar* registrar); /** * fl_plugin_registrar_register_view_factory: * @registrar: an #FlPluginRegistrar. - * @factory: the view factory that will be registered. + * @factory: (transfer-full): the view factory that will be registered. * @view_type: A unique identifier for the factory. The Dart code of the Flutter * app can use this identifier to request creation of a #GtkWidget * by the registered factory. From 14dc15259a1e79bc26de50d3f00f2899f69829bb Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 4 Feb 2021 15:21:53 +0800 Subject: [PATCH 03/14] Add scroll event propagation --- shell/platform/linux/fl_gesture_helper.cc | 22 ++++++++++++++++++++++ shell/platform/linux/fl_gesture_helper.h | 9 +++++++++ shell/platform/linux/fl_view.cc | 3 +++ 3 files changed, 34 insertions(+) diff --git a/shell/platform/linux/fl_gesture_helper.cc b/shell/platform/linux/fl_gesture_helper.cc index 97ae09ff35b10..4841918841083 100644 --- a/shell/platform/linux/fl_gesture_helper.cc +++ b/shell/platform/linux/fl_gesture_helper.cc @@ -71,6 +71,21 @@ static void send_motion_event(FlGestureHelper* self, gdk_event_free(new_event); } +static void send_scroll_event(FlGestureHelper* self, + GdkEvent* event, + GtkWidget* widget) { + GdkWindow* window = gtk_widget_get_window(widget); + gint origin_x, origin_y; + gdk_window_get_origin(window, &origin_x, &origin_y); + + GdkEvent* new_event = gdk_event_copy(event); + GdkEventScroll* scroll = &new_event->scroll; + scroll->x = event->scroll.x_root - origin_x; + scroll->y = event->scroll.y_root - origin_y; + gtk_widget_event(widget, new_event); + gdk_event_free(new_event); +} + static void clear_state(FlGestureHelper* self) { g_clear_object(&self->grabbed_widget); g_list_free_full(self->event_list, free_event); @@ -112,6 +127,13 @@ void fl_gesture_helper_button_motion(FlGestureHelper* self, GdkEvent* event) { } } +void fl_gesture_helper_scroll(FlGestureHelper* self, GdkEvent* event) { + if (self->hover_widget) { + // If mouse is hovering. + send_scroll_event(self, event, self->hover_widget); + } +} + void fl_gesture_helper_accept_gesture(FlGestureHelper* self, GtkWidget* widget, int64_t pointer_id) { diff --git a/shell/platform/linux/fl_gesture_helper.h b/shell/platform/linux/fl_gesture_helper.h index 39a6bc47f6bd2..80145d94ffc88 100644 --- a/shell/platform/linux/fl_gesture_helper.h +++ b/shell/platform/linux/fl_gesture_helper.h @@ -63,6 +63,15 @@ void fl_gesture_helper_button_release(FlGestureHelper* gesture_helper, void fl_gesture_helper_button_motion(FlGestureHelper* gesture_helper, GdkEvent* event); +/** + * fl_gesture_helper_scroll: + * @gesture_helper: an #FlGestureHelper. + * @event: a #GdkEventScroll event. + * + * Redistributes a #GdkEventScroll event to embedded platform views. + */ +void fl_gesture_helper_scroll(FlGestureHelper* gesture_helper, GdkEvent* event); + /** * fl_gesture_helper_accept_gesture: * @gesture_helper: an #FlGestureHelper. diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 48f3c9f4dcbc1..3dbd4e6a6bc67 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -500,6 +500,9 @@ static gboolean event_box_scroll_event(GtkWidget* widget, return FALSE; } + fl_gesture_helper_scroll(view->gesture_helper, + reinterpret_cast(event)); + // The multiplier is taken from the Chromium source // (ui/events/x/events_x_utils.cc). const int kScrollOffsetMultiplier = 53; From d3c96eeb87e5446a8e4e16b5b6102f987ef92232 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Fri, 5 Feb 2021 23:33:21 +0800 Subject: [PATCH 04/14] Add platform views plugin test --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/linux/BUILD.gn | 2 + .../linux/fl_platform_views_plugin.cc | 7 +- .../platform/linux/fl_platform_views_plugin.h | 2 +- .../linux/fl_platform_views_plugin_test.cc | 180 ++++++++++++++++++ .../flutter_linux/fl_plugin_registrar.h | 2 +- .../linux/testing/mock_platform_views.cc | 103 ++++++++++ .../linux/testing/mock_platform_views.h | 33 ++++ 8 files changed, 326 insertions(+), 4 deletions(-) create mode 100644 shell/platform/linux/fl_platform_views_plugin_test.cc create mode 100644 shell/platform/linux/testing/mock_platform_views.cc create mode 100644 shell/platform/linux/testing/mock_platform_views.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c5dd34993f24c..6ad60f33985ea 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1409,6 +1409,7 @@ FILE: ../../../flutter/shell/platform/linux/fl_platform_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_platform_views.cc FILE: ../../../flutter/shell/platform/linux/fl_platform_views_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_platform_views_plugin.h +FILE: ../../../flutter/shell/platform/linux/fl_platform_views_plugin_test.cc 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 e62d466a10708..51e3740be423e 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -173,6 +173,7 @@ executable("flutter_linux_unittests") { "fl_method_channel_test.cc", "fl_method_codec_test.cc", "fl_method_response_test.cc", + "fl_platform_views_plugin_test.cc", "fl_standard_message_codec_test.cc", "fl_standard_method_codec_test.cc", "fl_string_codec_test.cc", @@ -180,6 +181,7 @@ executable("flutter_linux_unittests") { "testing/fl_test.cc", "testing/mock_engine.cc", "testing/mock_epoxy.cc", + "testing/mock_platform_views.cc", "testing/mock_renderer.cc", "testing/mock_text_input_plugin.cc", ] diff --git a/shell/platform/linux/fl_platform_views_plugin.cc b/shell/platform/linux/fl_platform_views_plugin.cc index d6e66889803c3..57eafb0f4e0a0 100644 --- a/shell/platform/linux/fl_platform_views_plugin.cc +++ b/shell/platform/linux/fl_platform_views_plugin.cc @@ -285,8 +285,10 @@ FlPlatformViewsPlugin* fl_platform_views_plugin_new( nullptr); self->gesture_helper = gesture_helper; - self->factories = g_hash_table_new(g_str_hash, g_str_equal); - self->platform_views = g_hash_table_new(g_direct_hash, g_direct_equal); + self->factories = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); + self->platform_views = g_hash_table_new_full(g_direct_hash, g_direct_equal, + nullptr, g_object_unref); return self; } @@ -295,6 +297,7 @@ gboolean fl_platform_views_plugin_register_view_factory( FlPlatformViewsPlugin* self, FlPlatformViewFactory* factory, const gchar* view_type) { + g_object_ref(factory); return g_hash_table_insert(self->factories, g_strdup(view_type), factory); } diff --git a/shell/platform/linux/fl_platform_views_plugin.h b/shell/platform/linux/fl_platform_views_plugin.h index 5199e22a9fe5a..7e2b51ae5e0ed 100644 --- a/shell/platform/linux/fl_platform_views_plugin.h +++ b/shell/platform/linux/fl_platform_views_plugin.h @@ -42,7 +42,7 @@ FlPlatformViewsPlugin* fl_platform_views_plugin_new( /** * fl_platform_views_plugin_register_view_factory: * @plugin: an #FlPlatformViewsPlugin. - * @factory: (transfer-full): the view factory that will be registered. + * @factory: (transfer-none): the view factory that will be registered. * @view_type: A unique identifier for the factory. The Dart code of the Flutter * app can use this identifier to request creation of a #GtkWidget * by the registered factory. diff --git a/shell/platform/linux/fl_platform_views_plugin_test.cc b/shell/platform/linux/fl_platform_views_plugin_test.cc new file mode 100644 index 0000000000000..db94e391ca9c0 --- /dev/null +++ b/shell/platform/linux/fl_platform_views_plugin_test.cc @@ -0,0 +1,180 @@ +// 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_views_plugin.h" + +#include "gtest/gtest.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_method_channel.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "flutter/shell/platform/linux/testing/mock_platform_views.h" + +static void invoke_response_cb(GObject* source_object, + GAsyncResult* res, + gpointer user_data) { + g_main_loop_quit(reinterpret_cast(user_data)); +} + +static void invoke_method(GMainLoop* loop, + FlBinaryMessenger* messenger, + const gchar* channel_name, + const gchar* method_name, + FlValue* args, + GAsyncReadyCallback cb) { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + g_autoptr(FlValue) invoke_args = fl_value_new_list(); + fl_value_append_take(invoke_args, fl_value_new_string(channel_name)); + fl_value_append_take(invoke_args, fl_value_new_string(method_name)); + fl_value_append_take(invoke_args, args); + fl_method_channel_invoke_method(channel, "InvokeMethod", invoke_args, nullptr, + cb, loop); +} + +static void create_platform_view(GMainLoop* loop, + FlBinaryMessenger* messenger, + int64_t view_id, + const gchar* view_type, + FlValue* args, + GAsyncReadyCallback cb) { + FlValue* create_args = fl_value_new_map(); + fl_value_set_string_take(create_args, "id", fl_value_new_int(view_id)); + fl_value_set_string_take(create_args, "viewType", + fl_value_new_string(view_type)); + if (args) + fl_value_set_string_take(create_args, "params", args); + invoke_method(loop, messenger, "flutter/platform_views", "create", + create_args, cb); +} + +static void dispose_platform_view(GMainLoop* loop, + FlBinaryMessenger* messenger, + int64_t view_id, + GAsyncReadyCallback cb) { + invoke_method(loop, messenger, "flutter/platform_views", "dispose", + fl_value_new_int(view_id), cb); +} + +static FlPlatformView* expected_platform_view; +static constexpr int64_t expected_view_identifier = 1; + +TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithUnknownViewType) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); + g_autoptr(FlGestureHelper) gesture_helper = fl_gesture_helper_new(); + g_autoptr(FlPlatformViewsPlugin) plugin = + fl_platform_views_plugin_new(messenger, gesture_helper); + + create_platform_view(loop, messenger, 1, "unknown_view_type", nullptr, + invoke_response_cb); + + // Blocks here until invoke_response_cb is called. + g_main_loop_run(loop); + + EXPECT_EQ(nullptr, fl_platform_views_plugin_get_platform_view( + plugin, expected_view_identifier)); +} + +static FlPlatformView* create_platform_view_with_no_codec( + FlPlatformViewFactory* factory, + int64_t view_identifier, + FlValue* args) { + EXPECT_EQ(view_identifier, expected_view_identifier); + EXPECT_EQ(args, nullptr); + + return expected_platform_view = + FL_PLATFORM_VIEW(fl_mock_platform_view_new(nullptr)); +} + +TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithNoCodec) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); + g_autoptr(FlGestureHelper) gesture_helper = fl_gesture_helper_new(); + g_autoptr(FlPlatformViewsPlugin) plugin = + fl_platform_views_plugin_new(messenger, gesture_helper); + FlMockViewFactory* factory = + fl_mock_view_factory_new(create_platform_view_with_no_codec, nullptr); + fl_platform_views_plugin_register_view_factory( + plugin, FL_PLATFORM_VIEW_FACTORY(factory), "some_view_type"); + + expected_platform_view = nullptr; + create_platform_view(loop, messenger, expected_view_identifier, + "some_view_type", nullptr, invoke_response_cb); + + // Blocks here until flutter/platform_views::create is called. + g_main_loop_run(loop); + + EXPECT_EQ(expected_platform_view, fl_platform_views_plugin_get_platform_view( + plugin, expected_view_identifier)); + + dispose_platform_view(loop, messenger, expected_view_identifier, + invoke_response_cb); + + // Blocks here until flutter/platform_views::dispose is called. + g_main_loop_run(loop); +} + +static FlPlatformView* create_platform_view_with_codec( + FlPlatformViewFactory* factory, + int64_t view_identifier, + FlValue* args) { + EXPECT_EQ(view_identifier, expected_view_identifier); + EXPECT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(args), "Hello World!"); + + return expected_platform_view = + FL_PLATFORM_VIEW(fl_mock_platform_view_new(nullptr)); +} + +static FlMessageCodec* get_create_arguments_codec( + FlPlatformViewFactory* factory) { + return FL_MESSAGE_CODEC(fl_standard_message_codec_new()); +} + +TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithCodec) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(GError) error = nullptr; + g_autoptr(FlEngine) engine = make_mock_engine(); + g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); + g_autoptr(FlGestureHelper) gesture_helper = fl_gesture_helper_new(); + g_autoptr(FlPlatformViewsPlugin) plugin = + fl_platform_views_plugin_new(messenger, gesture_helper); + g_autoptr(FlMockViewFactory) factory = fl_mock_view_factory_new( + create_platform_view_with_codec, get_create_arguments_codec); + g_autoptr(FlMessageCodec) codec = + FL_MESSAGE_CODEC(fl_standard_message_codec_new()); + fl_platform_views_plugin_register_view_factory( + plugin, FL_PLATFORM_VIEW_FACTORY(factory), "some_view_type"); + + expected_platform_view = nullptr; + g_autoptr(FlValue) args = fl_value_new_string("Hello World!"); + g_autoptr(GBytes) bytes = + fl_message_codec_encode_message(codec, args, &error); + EXPECT_EQ(error, nullptr); + create_platform_view( + loop, messenger, expected_view_identifier, "some_view_type", + fl_value_new_uint8_list_from_bytes(bytes), invoke_response_cb); + + // Blocks here until invoke_method_response is called. + g_main_loop_run(loop); + + EXPECT_EQ(expected_platform_view, fl_platform_views_plugin_get_platform_view( + plugin, expected_view_identifier)); + + dispose_platform_view(loop, messenger, expected_view_identifier, + invoke_response_cb); + + // Blocks here until flutter/platform_views::dispose is called. + g_main_loop_run(loop); +} diff --git a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h index 887e7ff20430a..83f94f647d5a0 100644 --- a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h +++ b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h @@ -53,7 +53,7 @@ FlView* fl_plugin_registrar_get_view(FlPluginRegistrar* registrar); /** * fl_plugin_registrar_register_view_factory: * @registrar: an #FlPluginRegistrar. - * @factory: (transfer-full): the view factory that will be registered. + * @factory: (transfer-none): the view factory that will be registered. * @view_type: A unique identifier for the factory. The Dart code of the Flutter * app can use this identifier to request creation of a #GtkWidget * by the registered factory. diff --git a/shell/platform/linux/testing/mock_platform_views.cc b/shell/platform/linux/testing/mock_platform_views.cc new file mode 100644 index 0000000000000..cca8adde67530 --- /dev/null +++ b/shell/platform/linux/testing/mock_platform_views.cc @@ -0,0 +1,103 @@ +// 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/testing/mock_platform_views.h" + +struct _FlMockPlatformView { + FlPlatformView parent_instance; + + int64_t view_identifier; + + GtkWidget* (*get_view)(FlPlatformView* self); +}; + +G_DEFINE_TYPE(FlMockPlatformView, + fl_mock_platform_view, + fl_platform_view_get_type()) + +static GtkWidget* fl_mock_platform_view_get_view( + FlPlatformView* platform_view) { + g_return_val_if_fail(FL_IS_MOCK_PLATFORM_VIEW(platform_view), nullptr); + FlMockPlatformView* self = FL_MOCK_PLATFORM_VIEW(platform_view); + if (!self->get_view) + return nullptr; + else + return self->get_view(FL_PLATFORM_VIEW(self)); +} + +static void fl_mock_platform_view_class_init(FlMockPlatformViewClass* klass) { + FL_PLATFORM_VIEW_CLASS(klass)->get_view = fl_mock_platform_view_get_view; +} + +static void fl_mock_platform_view_init(FlMockPlatformView* self) {} + +FlMockPlatformView* fl_mock_platform_view_new( + GtkWidget* (*get_view)(FlPlatformView* self)) { + FlMockPlatformView* self = FL_MOCK_PLATFORM_VIEW( + g_object_new(fl_mock_platform_view_get_type(), nullptr)); + self->get_view = get_view; + return self; +} + +struct _FlMockViewFactory { + GObject parent_instance; + + FlPlatformView* (*create_platform_view)(FlPlatformViewFactory* self, + int64_t view_identifier, + FlValue* args); + FlMessageCodec* (*get_create_arguments_codec)(FlPlatformViewFactory* self); +}; + +static void fl_mock_view_factory_fl_platform_view_factory_iface_init( + FlPlatformViewFactoryInterface* iface); + +G_DEFINE_TYPE_WITH_CODE( + FlMockViewFactory, + fl_mock_view_factory, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE( + fl_platform_view_factory_get_type(), + fl_mock_view_factory_fl_platform_view_factory_iface_init)) + +static FlPlatformView* fl_mock_view_factory_create_platform_view( + FlPlatformViewFactory* factory, + int64_t view_identifier, + FlValue* args) { + FlMockViewFactory* self = FL_MOCK_VIEW_FACTORY(factory); + return self->create_platform_view(factory, view_identifier, args); +} + +static FlMessageCodec* fl_mock_view_factory_get_create_arguments_codec( + FlPlatformViewFactory* factory) { + FlMockViewFactory* self = FL_MOCK_VIEW_FACTORY(factory); + if (!self->get_create_arguments_codec) + return nullptr; + else + return self->get_create_arguments_codec(factory); +} + +static void fl_mock_view_factory_fl_platform_view_factory_iface_init( + FlPlatformViewFactoryInterface* iface) { + iface->create_platform_view = fl_mock_view_factory_create_platform_view; + iface->get_create_arguments_codec = + fl_mock_view_factory_get_create_arguments_codec; +} + +static void fl_mock_view_factory_class_init(FlMockViewFactoryClass* klass) {} + +static void fl_mock_view_factory_init(FlMockViewFactory* self) {} + +// Creates a mock platform_view_factory +FlMockViewFactory* fl_mock_view_factory_new( + FlPlatformView* (*create_platform_view)(FlPlatformViewFactory* self, + int64_t view_identifier, + FlValue* args), + FlMessageCodec* (*get_create_arguments_codec)( + FlPlatformViewFactory* self)) { + FlMockViewFactory* self = FL_MOCK_VIEW_FACTORY( + g_object_new(fl_mock_view_factory_get_type(), nullptr)); + self->create_platform_view = create_platform_view; + self->get_create_arguments_codec = get_create_arguments_codec; + return self; +} diff --git a/shell/platform/linux/testing/mock_platform_views.h b/shell/platform/linux/testing/mock_platform_views.h new file mode 100644 index 0000000000000..5e919f10a39ec --- /dev/null +++ b/shell/platform/linux/testing/mock_platform_views.h @@ -0,0 +1,33 @@ +// 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_platform_views.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlMockPlatformView, + fl_mock_platform_view, + FL, + MOCK_PLATFORM_VIEW, + FlPlatformView) + +FlMockPlatformView* fl_mock_platform_view_new( + GtkWidget* (*get_view)(FlPlatformView* self)); + +int64_t fl_mock_platform_view_get_view_identifier( + FlMockPlatformView* platform_view); + +G_DECLARE_FINAL_TYPE(FlMockViewFactory, + fl_mock_view_factory, + FL, + MOCK_VIEW_FACTORY, + GObject) + +FlMockViewFactory* fl_mock_view_factory_new( + FlPlatformView* (*create_platform_view)(FlPlatformViewFactory* self, + int64_t view_identifier, + FlValue* args), + FlMessageCodec* (*get_create_arguments_codec)(FlPlatformViewFactory* self)); + +G_END_DECLS From 92fc86b46a519bbed122afd6dd8b1dbe1a53f2fe Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sun, 7 Feb 2021 19:46:07 +0800 Subject: [PATCH 05/14] Apply clipping mutators to platform views --- shell/platform/linux/BUILD.gn | 1 + shell/platform/linux/fl_clipping_view.cc | 185 ++++++++++++++++++ shell/platform/linux/fl_clipping_view.h | 51 +++++ shell/platform/linux/fl_renderer_gl.cc | 1 + shell/platform/linux/fl_view.cc | 231 ++++++++--------------- 5 files changed, 315 insertions(+), 154 deletions(-) create mode 100644 shell/platform/linux/fl_clipping_view.cc create mode 100644 shell/platform/linux/fl_clipping_view.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 51e3740be423e..9335d20461144 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -92,6 +92,7 @@ source_set("flutter_linux_sources") { "fl_basic_message_channel.cc", "fl_binary_codec.cc", "fl_binary_messenger.cc", + "fl_clipping_view.cc", "fl_dart_project.cc", "fl_engine.cc", "fl_event_channel.cc", diff --git a/shell/platform/linux/fl_clipping_view.cc b/shell/platform/linux/fl_clipping_view.cc new file mode 100644 index 0000000000000..65d733a6fc7b0 --- /dev/null +++ b/shell/platform/linux/fl_clipping_view.cc @@ -0,0 +1,185 @@ +// 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_clipping_view.h" + +#include "flutter/shell/platform/embedder/embedder.h" + +struct _FlClippingView { + GtkEventBox parent_instance; + + GdkRectangle geometry; + GPtrArray* mutations; +}; + +G_DEFINE_TYPE(FlClippingView, fl_clipping_view, GTK_TYPE_EVENT_BOX) + +static void fl_clipping_view_dispose(GObject* gobject) { + FlClippingView* self = FL_CLIPPING_VIEW(gobject); + + if (self->mutations) { + g_ptr_array_unref(self->mutations); + self->mutations = nullptr; + } + + G_OBJECT_CLASS(fl_clipping_view_parent_class)->dispose(gobject); +} + +// Implements GtkWidget::size-allocate. +static void fl_clipping_view_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) { + FlClippingView* self = FL_CLIPPING_VIEW(widget); + + gtk_widget_set_allocation(widget, allocation); + + if (gtk_widget_get_has_window(widget)) { + if (gtk_widget_get_realized(widget)) + gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, + allocation->y, allocation->width, + allocation->height); + } + + GtkWidget* child = gtk_bin_get_child(GTK_BIN(self)); + if (!child || !gtk_widget_get_visible(child)) + return; + + GtkAllocation child_allocation = self->geometry; + if (!gtk_widget_get_has_window(widget)) { + child_allocation.x += allocation->x; + child_allocation.y += allocation->y; + } + gtk_widget_size_allocate(child, &child_allocation); +} + +// Implements GtkWidget::draw. +static gboolean fl_clipping_view_draw(GtkWidget* widget, cairo_t* cr) { + FlClippingView* self = FL_CLIPPING_VIEW(widget); + + // We currently can only clip widgets that have no GdkWindow. + + cairo_save(cr); + if (self->mutations) { + for (guint i = 0; i < self->mutations->len; i++) { + FlutterPlatformViewMutation* mutation = + reinterpret_cast( + self->mutations->pdata[i]); + switch (mutation->type) { + case kFlutterPlatformViewMutationTypeOpacity: { + // opacitiy is applied in fl_clipping_view_reset (). + } break; + case kFlutterPlatformViewMutationTypeClipRect: { + cairo_rectangle(cr, mutation->clip_rect.left, mutation->clip_rect.top, + mutation->clip_rect.right - mutation->clip_rect.left, + mutation->clip_rect.bottom - mutation->clip_rect.top); + cairo_clip(cr); + } break; + case kFlutterPlatformViewMutationTypeClipRoundedRect: { + FlutterSize* top_left_radii = + &mutation->clip_rounded_rect.upper_left_corner_radius; + FlutterSize* top_right_radii = + &mutation->clip_rounded_rect.upper_right_corner_radius; + FlutterSize* bottom_left_radii = + &mutation->clip_rounded_rect.lower_left_corner_radius; + FlutterSize* bottom_right_radii = + &mutation->clip_rounded_rect.lower_right_corner_radius; + FlutterRect* rect = &mutation->clip_rounded_rect.rect; + cairo_move_to(cr, rect->left + top_left_radii->width, rect->top); + + cairo_line_to(cr, rect->right - top_right_radii->width, rect->top); + cairo_curve_to(cr, rect->right, rect->top, // + rect->right, rect->top + top_right_radii->height, + rect->right, rect->top + top_right_radii->height); + + cairo_line_to(cr, rect->right, + rect->bottom - bottom_right_radii->height); + cairo_curve_to(cr, rect->right, rect->bottom, + rect->right - bottom_right_radii->width, rect->bottom, + rect->right - bottom_right_radii->width, rect->bottom); + + cairo_line_to(cr, rect->left + bottom_left_radii->width, + rect->bottom); + cairo_curve_to(cr, rect->left, rect->bottom, // + rect->left, rect->bottom - bottom_left_radii->height, + rect->left, rect->bottom - bottom_left_radii->height); + + cairo_line_to(cr, rect->left, rect->top + top_left_radii->height); + cairo_curve_to(cr, rect->left, rect->top, + rect->left + top_left_radii->width, rect->top, + rect->left + top_left_radii->width, rect->top); + + cairo_close_path(cr); + cairo_clip(cr); + } break; + case kFlutterPlatformViewMutationTypeTransformation: { + cairo_matrix_t matrix = { + .xx = mutation->transformation.scaleX, + .yx = mutation->transformation.skewY, + .xy = mutation->transformation.skewX, + .yy = mutation->transformation.scaleY, + .x0 = mutation->transformation.transX, + .y0 = mutation->transformation.transY, + }; + cairo_transform(cr, &matrix); + } break; + } + } + } + cairo_translate(cr, self->geometry.x, -self->geometry.y); + gboolean result = + GTK_WIDGET_CLASS(fl_clipping_view_parent_class)->draw(widget, cr); + cairo_restore(cr); + return result; +} + +static void fl_clipping_view_constructed(GObject* object) { + gtk_event_box_set_visible_window(GTK_EVENT_BOX(object), TRUE); +} + +static void fl_clipping_view_class_init(FlClippingViewClass* klass) { + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + gobject_class->constructed = fl_clipping_view_constructed; + gobject_class->dispose = fl_clipping_view_dispose; + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + widget_class->draw = fl_clipping_view_draw; + widget_class->size_allocate = fl_clipping_view_size_allocate; +} + +static void fl_clipping_view_init(FlClippingView* self) {} + +GtkWidget* fl_clipping_view_new() { + return GTK_WIDGET(g_object_new(fl_clipping_view_get_type(), nullptr)); +} + +void fl_clipping_view_reset(FlClippingView* self, + GtkWidget* child, + GdkRectangle* geometry, + GPtrArray* mutations) { + g_return_if_fail(FL_IS_CLIPPING_VIEW(self)); + + GtkWidget* old_child = gtk_bin_get_child(GTK_BIN(self)); + if (old_child != child) { + if (old_child) + gtk_container_remove(GTK_CONTAINER(self), old_child); + + g_object_ref(child); + gtk_container_add(GTK_CONTAINER(self), child); + } + if (self->mutations) { + g_ptr_array_unref(self->mutations); + } + self->geometry = *geometry; + self->mutations = mutations; + + if (mutations) { + for (guint i = 0; i < mutations->len; i++) { + FlutterPlatformViewMutation* mutation = + reinterpret_cast( + self->mutations->pdata[i]); + if (mutation->type == kFlutterPlatformViewMutationTypeOpacity) { + gtk_widget_set_opacity(child, mutation->opacity); + } + } + } +} diff --git a/shell/platform/linux/fl_clipping_view.h b/shell/platform/linux/fl_clipping_view.h new file mode 100644 index 0000000000000..931e0aaeca9d1 --- /dev/null +++ b/shell/platform/linux/fl_clipping_view.h @@ -0,0 +1,51 @@ +// 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_CLIPPING_VIEW_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_CLIPPING_VIEW_H_ + +#include + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlClippingView, + fl_clipping_view, + FL, + CLIPPING_VIEW, + GtkEventBox) + +/** + * FlClippingView: + * + * #FlClippingView is a GTK widget that draws its child with mutators. + */ + +/** + * fl_clipping_view_new: + * + * Creates a new #FlClippingView widget. + * + * Returns: the newly created #FlClippingView widget. + */ +GtkWidget* fl_clipping_view_new(); + +/** + * fl_clipping_view_reset: + * @clipping_view: an #FlClippingView. + * @child: (transfer-none): widget to apply mutators. + * @geometry: geometry of widget. + * @mutations: (transfer-full): mutations to be applied. + * + * Reset child widget and its mutations. + */ +void fl_clipping_view_reset(FlClippingView* clipping_view, + GtkWidget* child, + GdkRectangle* geometry, + GPtrArray* mutations); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_CLIPPING_VIEW_H_ diff --git a/shell/platform/linux/fl_renderer_gl.cc b/shell/platform/linux/fl_renderer_gl.cc index 9d1b4b4c01c61..67730e95ca958 100644 --- a/shell/platform/linux/fl_renderer_gl.cc +++ b/shell/platform/linux/fl_renderer_gl.cc @@ -124,6 +124,7 @@ static gboolean fl_renderer_gl_present_layers(FlRenderer* renderer, for (size_t i = 0; i < layer->platform_view->mutations_count; i++) { FlutterPlatformViewMutation* mutation = g_new(FlutterPlatformViewMutation, 1); + *mutation = *layer->platform_view->mutations[i]; g_ptr_array_add(mutations, mutation); } fl_view_add_widget(view, widget, &geometry, mutations); diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 3dbd4e6a6bc67..855a87071cfc5 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -6,9 +6,13 @@ #include "flutter/shell/platform/linux/fl_view_private.h" +#include #include +#include +#include #include "flutter/shell/platform/linux/fl_accessibility_plugin.h" +#include "flutter/shell/platform/linux/fl_clipping_view.h" #include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_gesture_helper.h" #include "flutter/shell/platform/linux/fl_key_event_plugin.h" @@ -52,18 +56,15 @@ struct _FlView { GList* gl_area_list; GList* used_area_list; + std::map* current_clipping_views; + std::map* last_clipping_views; + GtkWidget* event_box; - GList* children_list; - GList* pending_children_list; + std::vector* children_list; + std::vector* pending_children_list; }; -typedef struct _FlViewChild { - GtkWidget* widget; - GdkRectangle geometry; - GPtrArray* mutations; -} FlViewChild; - enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST }; static void fl_view_plugin_registry_iface_init( @@ -197,6 +198,11 @@ static void fl_view_constructed(GObject* object) { self->platform_views_plugin = fl_platform_views_plugin_new(messenger, self->gesture_helper); + self->children_list = new std::vector(); + self->pending_children_list = new std::vector(); + self->current_clipping_views = new std::map(); + self->last_clipping_views = new std::map(); + self->event_box = gtk_event_box_new(); gtk_widget_set_parent(self->event_box, GTK_WIDGET(self)); gtk_widget_show(self->event_box); @@ -281,6 +287,11 @@ static void fl_view_dispose(GObject* object) { g_list_free_full(self->gl_area_list, g_object_unref); self->gl_area_list = nullptr; + delete self->children_list; + delete self->pending_children_list; + delete self->current_clipping_views; + delete self->last_clipping_views; + G_OBJECT_CLASS(fl_view_parent_class)->dispose(object); } @@ -321,52 +332,6 @@ static void fl_view_realize(GtkWidget* widget) { } } -static void fl_view_get_preferred_width(GtkWidget* widget, - gint* minimum, - gint* natural) { - FlView* self = FL_VIEW(widget); - gint child_min, child_nat; - - *minimum = 0; - *natural = 0; - - for (GList* iterator = self->children_list; iterator; - iterator = iterator->next) { - FlViewChild* child = reinterpret_cast(iterator->data); - - if (!gtk_widget_get_visible(child->widget)) - continue; - - gtk_widget_get_preferred_width(child->widget, &child_min, &child_nat); - - *minimum = MAX(*minimum, child->geometry.x + child_min); - *natural = MAX(*natural, child->geometry.x + child_nat); - } -} - -static void fl_view_get_preferred_height(GtkWidget* widget, - gint* minimum, - gint* natural) { - FlView* self = FL_VIEW(widget); - gint child_min, child_nat; - - *minimum = 0; - *natural = 0; - - for (GList* iterator = self->children_list; iterator; - iterator = iterator->next) { - FlViewChild* child = reinterpret_cast(iterator->data); - - if (!gtk_widget_get_visible(child->widget)) - continue; - - gtk_widget_get_preferred_height(child->widget, &child_min, &child_nat); - - *minimum = MAX(*minimum, child->geometry.y + child_min); - *natural = MAX(*natural, child->geometry.y + child_nat); - } -} - // Implements GtkWidget::size-allocate. static void fl_view_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { @@ -381,35 +346,27 @@ static void fl_view_size_allocate(GtkWidget* widget, allocation->height); } - for (GList* iterator = self->children_list; iterator; - iterator = iterator->next) { - FlViewChild* child = reinterpret_cast(iterator->data); - if (!gtk_widget_get_visible(child->widget)) - continue; + GtkAllocation default_child_allocation = { + .x = 0, + .y = 0, + .width = allocation->width, + .height = allocation->height, + }; - GtkAllocation child_allocation = child->geometry; - GtkRequisition child_requisition; - gtk_widget_get_preferred_size(child->widget, &child_requisition, NULL); + for (GtkWidget* child : *self->children_list) { + if (!gtk_widget_get_visible(child)) + continue; + GtkAllocation child_allocation = default_child_allocation; if (!gtk_widget_get_has_window(widget)) { child_allocation.x += allocation->x; child_allocation.y += allocation->y; } - if (child_allocation.width == 0 && child_allocation.height == 0) { - child_allocation.width = allocation->width; - child_allocation.height = allocation->height; - } - - gtk_widget_size_allocate(child->widget, &child_allocation); + gtk_widget_size_allocate(child, &child_allocation); } - GtkAllocation event_box_allocation = { - .x = 0, - .y = 0, - .width = allocation->width, - .height = allocation->height, - }; + GtkAllocation event_box_allocation = default_child_allocation; if (!gtk_widget_get_has_window(self->event_box)) { event_box_allocation.x += allocation->x; event_box_allocation.y += allocation->y; @@ -553,41 +510,23 @@ static gboolean fl_view_key_release_event(GtkWidget* widget, return fl_key_event_plugin_send_key_event(self->key_event_plugin, event); } -static void fl_view_put(FlView* self, - GtkWidget* widget, - GdkRectangle* geometry) { - FlViewChild* child = g_new(FlViewChild, 1); - child->widget = widget; - child->geometry = *geometry; - +static void fl_view_put(FlView* self, GtkWidget* widget) { gtk_widget_set_parent(widget, GTK_WIDGET(self)); - self->children_list = g_list_append(self->children_list, child); + self->children_list->push_back(widget); } static void fl_view_add(GtkContainer* container, GtkWidget* widget) { - GdkRectangle geometry = { - .x = 0, - .y = 0, - .width = 0, - .height = 0, - }; - fl_view_put(FL_VIEW(container), widget, &geometry); + fl_view_put(FL_VIEW(container), widget); } static void fl_view_remove(GtkContainer* container, GtkWidget* widget) { FlView* self = FL_VIEW(container); - for (GList* iterator = self->children_list; iterator; - iterator = iterator->next) { - FlViewChild* child = reinterpret_cast(iterator->data); - if (child->widget == widget) { - g_object_ref(widget); + for (auto child = self->children_list->begin(); + child != self->children_list->end(); child++) { + if (*child == widget) { + g_object_ref(widget); // children_list does not own widgets. gtk_widget_unparent(widget); - self->children_list = g_list_remove_link(self->children_list, iterator); - g_list_free(iterator); - - g_ptr_array_unref(child->mutations); - g_free(child); - + self->children_list->erase(child); break; } } @@ -602,10 +541,8 @@ static void fl_view_forall(GtkContainer* container, GtkCallback callback, gpointer callback_data) { FlView* self = FL_VIEW(container); - for (GList* iterator = self->children_list; iterator; - iterator = iterator->next) { - FlViewChild* child = reinterpret_cast(iterator->data); - (*callback)(child->widget, callback_data); + for (GtkWidget* child : *self->children_list) { + (*callback)(child, callback_data); } if (include_internals) { @@ -639,8 +576,6 @@ static void fl_view_class_init(FlViewClass* klass) { GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); widget_class->realize = fl_view_realize; - widget_class->get_preferred_width = fl_view_get_preferred_width; - widget_class->get_preferred_height = fl_view_get_preferred_height; widget_class->size_allocate = fl_view_size_allocate; widget_class->draw = fl_view_draw; widget_class->key_press_event = fl_view_key_press_event; @@ -690,24 +625,11 @@ void fl_view_begin_frame(FlView* view) { FlView* self = FL_VIEW(view); self->used_area_list = self->gl_area_list; - g_list_free_full(self->pending_children_list, g_free); - self->pending_children_list = nullptr; + self->pending_children_list->clear(); } -static void fl_view_add_pending_child(FlView* self, - GtkWidget* widget, - GdkRectangle* geometry, - GPtrArray* mutations) { - FlViewChild* child = g_new(FlViewChild, 1); - child->widget = widget; - if (geometry) - child->geometry = *geometry; - else - child->geometry = {0, 0, 0, 0}; - child->mutations = mutations; - - self->pending_children_list = - g_list_append(self->pending_children_list, child); +static void fl_view_add_pending_child(FlView* self, GtkWidget* child) { + self->pending_children_list->push_back(child); } void fl_view_add_gl_area(FlView* view, @@ -725,7 +647,7 @@ void fl_view_add_gl_area(FlView* view, } gtk_widget_show(GTK_WIDGET(area)); - fl_view_add_pending_child(view, GTK_WIDGET(area), nullptr, nullptr); + fl_view_add_pending_child(view, GTK_WIDGET(area)); fl_gl_area_queue_render(area, texture); } @@ -733,49 +655,50 @@ void fl_view_add_widget(FlView* view, GtkWidget* widget, GdkRectangle* geometry, GPtrArray* mutations) { - gtk_widget_show(widget); - fl_view_add_pending_child(view, widget, geometry, mutations); -} - -GList* find_child(GList* list, GtkWidget* widget) { - for (GList* i = list; i; i = i->next) { - FlViewChild* child = reinterpret_cast(i->data); - if (child && child->widget == widget) - return i; + FlClippingView* clipping_view; + if (view->last_clipping_views->count(widget)) { + clipping_view = view->last_clipping_views->at(widget); + view->last_clipping_views->erase(widget); + } else { + clipping_view = FL_CLIPPING_VIEW(fl_clipping_view_new()); } - return nullptr; + view->current_clipping_views->insert({widget, clipping_view}); + fl_clipping_view_reset(clipping_view, widget, geometry, mutations); + + gtk_widget_show_all(GTK_WIDGET(clipping_view)); + fl_view_add_pending_child(view, GTK_WIDGET(clipping_view)); } -void fl_view_end_frame(FlView* view) { - for (GList* pending_child = view->pending_children_list; pending_child; - pending_child = pending_child->next) { - FlViewChild* pending_view_child = - reinterpret_cast(pending_child->data); - GList* child = find_child(view->children_list, pending_view_child->widget); +void fl_view_end_frame(FlView* self) { + for (GtkWidget* pending_child : *self->pending_children_list) { + auto child = std::find(self->children_list->begin(), + self->children_list->end(), pending_child); - if (child) { + if (child != self->children_list->end()) { // existing child - g_free(child->data); - child->data = nullptr; + *child = nullptr; } else { // newly added child - gtk_widget_set_parent(pending_view_child->widget, GTK_WIDGET(view)); + gtk_widget_set_parent(pending_child, GTK_WIDGET(self)); } } - for (GList* child = view->children_list; child; child = child->next) { - FlViewChild* view_child = reinterpret_cast(child->data); - if (view_child) { + for (GtkWidget* child : *self->children_list) { + if (child) { // removed child - g_object_ref(view_child->widget); - gtk_widget_unparent(view_child->widget); - g_free(view_child); - child->data = nullptr; + g_object_ref(child); + gtk_widget_unparent(child); } } - g_list_free(view->children_list); - view->children_list = view->pending_children_list; - view->pending_children_list = nullptr; - gtk_widget_queue_resize(GTK_WIDGET(view)); + self->children_list->clear(); + std::swap(self->children_list, self->pending_children_list); + + for (auto& it : *self->last_clipping_views) { + g_object_unref(it.second); + } + self->last_clipping_views->clear(); + std::swap(self->last_clipping_views, self->current_clipping_views); + + gtk_widget_queue_resize(GTK_WIDGET(self)); } From f14f743299ed4afaa54d00832435ba2ef5b3bfe2 Mon Sep 17 00:00:00 2001 From: Yuhui Huang Date: Fri, 12 Feb 2021 10:32:33 +0800 Subject: [PATCH 06/14] Update licenses_flutter --- ci/licenses_golden/licenses_flutter | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a41bc24bb04ca..1467a662b69da 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1372,6 +1372,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_binary_codec_test.cc 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_binary_messenger_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_clipping_view.cc +FILE: ../../../flutter/shell/platform/linux/fl_clipping_view.h FILE: ../../../flutter/shell/platform/linux/fl_dart_project.cc FILE: ../../../flutter/shell/platform/linux/fl_dart_project_private.h FILE: ../../../flutter/shell/platform/linux/fl_dart_project_test.cc From d81f161574f2b8279ab31c60da3c78ba76c1104c Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Mon, 22 Feb 2021 22:06:02 +0800 Subject: [PATCH 07/14] Support setDirection --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/linux/fl_platform_views.cc | 23 +++++++++- .../linux/fl_platform_views_plugin.cc | 42 ++++++++++++++++++- .../linux/fl_platform_views_plugin_test.cc | 38 ++++++++++++++--- .../linux/fl_platform_views_private.h | 24 +++++++++++ 5 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 shell/platform/linux/fl_platform_views_private.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1467a662b69da..5cddd116fc14c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1414,6 +1414,7 @@ FILE: ../../../flutter/shell/platform/linux/fl_platform_views.cc FILE: ../../../flutter/shell/platform/linux/fl_platform_views_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_platform_views_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_platform_views_plugin_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_platform_views_private.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/fl_platform_views.cc b/shell/platform/linux/fl_platform_views.cc index 18de311d66a9f..b8292e25b33e1 100644 --- a/shell/platform/linux/fl_platform_views.cc +++ b/shell/platform/linux/fl_platform_views.cc @@ -4,12 +4,18 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_platform_views.h" +#include "flutter/shell/platform/linux/fl_platform_views_private.h" + #include // Added here to stop the compiler from optimizing this function away. G_MODULE_EXPORT GType fl_platform_view_get_type(); -G_DEFINE_TYPE(FlPlatformView, fl_platform_view, G_TYPE_OBJECT) +typedef struct { + GtkTextDirection direction; +} FlPlatformViewPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FlPlatformView, fl_platform_view, G_TYPE_OBJECT) static void fl_platform_view_class_init(FlPlatformViewClass* klass) {} @@ -22,9 +28,24 @@ G_MODULE_EXPORT GtkWidget* fl_platform_view_get_view(FlPlatformView* self) { if (widget && !GTK_IS_WIDGET(widget)) { g_critical("fl_platform_view::get_view should return GtkWidget"); } + + if (widget) { + FlPlatformViewPrivate* priv = reinterpret_cast( + fl_platform_view_get_instance_private(self)); + gtk_widget_set_direction(widget, priv->direction); + } return widget; } +void fl_platform_view_set_direction(FlPlatformView* self, + GtkTextDirection direction) { + FlPlatformViewPrivate* priv = reinterpret_cast( + fl_platform_view_get_instance_private(self)); + priv->direction = direction; + // apply GtkTextDirection to widget. + fl_platform_view_get_view(self); +} + // Added here to stop the compiler from optimizing this function away. G_MODULE_EXPORT GType fl_platform_view_factory_get_type(); diff --git a/shell/platform/linux/fl_platform_views_plugin.cc b/shell/platform/linux/fl_platform_views_plugin.cc index 57eafb0f4e0a0..51b0cb738a783 100644 --- a/shell/platform/linux/fl_platform_views_plugin.cc +++ b/shell/platform/linux/fl_platform_views_plugin.cc @@ -8,6 +8,7 @@ #include #include "flutter/shell/platform/linux/fl_gesture_helper.h" +#include "flutter/shell/platform/linux/fl_platform_views_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" @@ -17,6 +18,7 @@ static constexpr char kCreateMethod[] = "create"; static constexpr char kDisposeMethod[] = "dispose"; static constexpr char kAcceptGestureMethod[] = "acceptGesture"; static constexpr char kRejectGestureMethod[] = "rejectGesture"; +static constexpr char kSetDirectionMethod[] = "setDirection"; static constexpr char kEnterMethod[] = "enter"; static constexpr char kExitMethod[] = "exit"; @@ -67,6 +69,15 @@ static FlMethodResponse* platform_views_create(FlPlatformViewsPlugin* self, } const gchar* view_type = fl_value_get_string(view_type_value); + FlValue* direction_value = fl_value_lookup_string(args, "direction"); + if (direction_value == nullptr || + fl_value_get_type(direction_value) != FL_VALUE_TYPE_INT) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Missing platform view direction", nullptr)); + } + GtkTextDirection direction = + static_cast(fl_value_get_int(direction_value)); + FlValue* params_value = fl_value_lookup_string(args, "params"); FlPlatformView* platform_view = @@ -103,6 +114,7 @@ static FlMethodResponse* platform_views_create(FlPlatformViewsPlugin* self, return FL_METHOD_RESPONSE(fl_method_error_response_new( kBadArgumentsError, "Invalid platform view", nullptr)); } + fl_platform_view_set_direction(platform_view, direction); g_hash_table_insert(self->platform_views, GINT_TO_POINTER(id), platform_view); @@ -141,10 +153,34 @@ static FlMethodResponse* platform_views_reject_gesture( if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST || fl_value_get_length(args) != 1) { return FL_METHOD_RESPONSE(fl_method_error_response_new( - kBadArgumentsError, "Argument map missing or malformed", nullptr)); + kBadArgumentsError, "Argument list missing or malformed", nullptr)); + } + + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); + FlPlatformView* platform_view = + fl_platform_views_plugin_get_platform_view(self, view_id); + if (!platform_view) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Platform view not found", nullptr)); + } + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); +} + +// Set GtkTextDirection of platform view. +static FlMethodResponse* platform_views_set_direction( + FlPlatformViewsPlugin* self, + FlValue* args) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST || + fl_value_get_length(args) != 2) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Argument list missing or malformed", nullptr)); } int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); + GtkTextDirection direction = static_cast( + fl_value_get_int(fl_value_get_list_value(args, 1))); + FlPlatformView* platform_view = fl_platform_views_plugin_get_platform_view(self, view_id); if (!platform_view) { @@ -152,6 +188,8 @@ static FlMethodResponse* platform_views_reject_gesture( kBadArgumentsError, "Platform view not found", nullptr)); } + fl_platform_view_set_direction(platform_view, direction); + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); } @@ -239,6 +277,8 @@ static void method_call_cb(FlMethodChannel* channel, response = platform_views_accept_gesture(self, args); } else if (strcmp(method, kRejectGestureMethod) == 0) { response = platform_views_reject_gesture(self, args); + } else if (strcmp(method, kSetDirectionMethod) == 0) { + response = platform_views_set_direction(self, args); } else if (strcmp(method, kEnterMethod) == 0) { response = platform_views_enter(self, args); } else if (strcmp(method, kExitMethod) == 0) { diff --git a/shell/platform/linux/fl_platform_views_plugin_test.cc b/shell/platform/linux/fl_platform_views_plugin_test.cc index db94e391ca9c0..459b6ea7e40d9 100644 --- a/shell/platform/linux/fl_platform_views_plugin_test.cc +++ b/shell/platform/linux/fl_platform_views_plugin_test.cc @@ -20,6 +20,18 @@ static void invoke_response_cb(GObject* source_object, g_main_loop_quit(reinterpret_cast(user_data)); } +// Called when a the test engine notifies us that a response a sent. +static void response_cb(FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + fl_binary_messenger_send_response(messenger, response_handle, nullptr, + nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + static void invoke_method(GMainLoop* loop, FlBinaryMessenger* messenger, const gchar* channel_name, @@ -47,6 +59,8 @@ static void create_platform_view(GMainLoop* loop, fl_value_set_string_take(create_args, "id", fl_value_new_int(view_id)); fl_value_set_string_take(create_args, "viewType", fl_value_new_string(view_type)); + fl_value_set_string_take(create_args, "direction", + fl_value_new_int(GTK_TEXT_DIR_LTR)); if (args) fl_value_set_string_take(create_args, "params", args); invoke_method(loop, messenger, "flutter/platform_views", "create", @@ -61,6 +75,10 @@ static void dispose_platform_view(GMainLoop* loop, fl_value_new_int(view_id), cb); } +GtkWidget* get_view(FlPlatformView* self) { + return nullptr; +} + static FlPlatformView* expected_platform_view; static constexpr int64_t expected_view_identifier = 1; @@ -91,7 +109,7 @@ static FlPlatformView* create_platform_view_with_no_codec( EXPECT_EQ(args, nullptr); return expected_platform_view = - FL_PLATFORM_VIEW(fl_mock_platform_view_new(nullptr)); + FL_PLATFORM_VIEW(fl_mock_platform_view_new(get_view)); } TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithNoCodec) { @@ -99,6 +117,10 @@ TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithNoCodec) { g_autoptr(FlEngine) engine = make_mock_engine(); g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); + + fl_binary_messenger_set_message_handler_on_channel( + messenger, "test/responses", response_cb, loop, nullptr); + g_autoptr(FlGestureHelper) gesture_helper = fl_gesture_helper_new(); g_autoptr(FlPlatformViewsPlugin) plugin = fl_platform_views_plugin_new(messenger, gesture_helper); @@ -109,7 +131,7 @@ TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithNoCodec) { expected_platform_view = nullptr; create_platform_view(loop, messenger, expected_view_identifier, - "some_view_type", nullptr, invoke_response_cb); + "some_view_type", nullptr, nullptr); // Blocks here until flutter/platform_views::create is called. g_main_loop_run(loop); @@ -133,7 +155,7 @@ static FlPlatformView* create_platform_view_with_codec( EXPECT_STREQ(fl_value_get_string(args), "Hello World!"); return expected_platform_view = - FL_PLATFORM_VIEW(fl_mock_platform_view_new(nullptr)); + FL_PLATFORM_VIEW(fl_mock_platform_view_new(get_view)); } static FlMessageCodec* get_create_arguments_codec( @@ -147,6 +169,10 @@ TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithCodec) { g_autoptr(GError) error = nullptr; g_autoptr(FlEngine) engine = make_mock_engine(); g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); + + fl_binary_messenger_set_message_handler_on_channel( + messenger, "test/responses", response_cb, loop, nullptr); + g_autoptr(FlGestureHelper) gesture_helper = fl_gesture_helper_new(); g_autoptr(FlPlatformViewsPlugin) plugin = fl_platform_views_plugin_new(messenger, gesture_helper); @@ -162,9 +188,9 @@ TEST(FlPlatformViewsPluginTest, CreatePlatformViewWithCodec) { g_autoptr(GBytes) bytes = fl_message_codec_encode_message(codec, args, &error); EXPECT_EQ(error, nullptr); - create_platform_view( - loop, messenger, expected_view_identifier, "some_view_type", - fl_value_new_uint8_list_from_bytes(bytes), invoke_response_cb); + create_platform_view(loop, messenger, expected_view_identifier, + "some_view_type", + fl_value_new_uint8_list_from_bytes(bytes), nullptr); // Blocks here until invoke_method_response is called. g_main_loop_run(loop); diff --git a/shell/platform/linux/fl_platform_views_private.h b/shell/platform/linux/fl_platform_views_private.h new file mode 100644 index 0000000000000..a63bde3bfdec6 --- /dev/null +++ b/shell/platform/linux/fl_platform_views_private.h @@ -0,0 +1,24 @@ +// 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_VIEWS_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_VIEWS_PRIVATE_H_ + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_platform_views.h" + +G_BEGIN_DECLS + +/** + * fl_platform_view_set_direction: + * @platform_view: an #FlPlatformView. + * @direction: new #GtkTextDirection for platform view. + * + * Set text direction. + */ +void fl_platform_view_set_direction(FlPlatformView* platform_view, + GtkTextDirection direction); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_VIEWS_PRIVATE_H_ From fca32cf3de5f298c6ce7ba44be5e64ec6ce2cb7b Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Mon, 8 Mar 2021 21:02:58 +0800 Subject: [PATCH 08/14] fix: transformation --- shell/platform/linux/fl_clipping_view.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_clipping_view.cc b/shell/platform/linux/fl_clipping_view.cc index 65d733a6fc7b0..cc9c384a6cc47 100644 --- a/shell/platform/linux/fl_clipping_view.cc +++ b/shell/platform/linux/fl_clipping_view.cc @@ -125,7 +125,7 @@ static gboolean fl_clipping_view_draw(GtkWidget* widget, cairo_t* cr) { } } } - cairo_translate(cr, self->geometry.x, -self->geometry.y); + cairo_translate(cr, -self->geometry.x, -self->geometry.y); gboolean result = GTK_WIDGET_CLASS(fl_clipping_view_parent_class)->draw(widget, cr); cairo_restore(cr); From 6647f90094d5a40eb2a866563150dff55a5db73c Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 24 Apr 2021 22:43:40 +0800 Subject: [PATCH 09/14] Add comments --- shell/platform/linux/fl_platform_views.cc | 16 ++-- .../linux/fl_platform_views_plugin.cc | 2 +- .../platform/linux/fl_platform_views_plugin.h | 5 +- .../public/flutter_linux/fl_platform_views.h | 91 ++++++++++++++++++- 4 files changed, 101 insertions(+), 13 deletions(-) diff --git a/shell/platform/linux/fl_platform_views.cc b/shell/platform/linux/fl_platform_views.cc index b8292e25b33e1..b1b2a788d4918 100644 --- a/shell/platform/linux/fl_platform_views.cc +++ b/shell/platform/linux/fl_platform_views.cc @@ -25,25 +25,23 @@ G_MODULE_EXPORT GtkWidget* fl_platform_view_get_view(FlPlatformView* self) { g_return_val_if_fail(FL_IS_PLATFORM_VIEW(self), nullptr); GtkWidget* widget = FL_PLATFORM_VIEW_GET_CLASS(self)->get_view(self); - if (widget && !GTK_IS_WIDGET(widget)) { + if (!widget || !GTK_IS_WIDGET(widget)) { g_critical("fl_platform_view::get_view should return GtkWidget"); + return nullptr; } - if (widget) { - FlPlatformViewPrivate* priv = reinterpret_cast( - fl_platform_view_get_instance_private(self)); - gtk_widget_set_direction(widget, priv->direction); - } + FlPlatformViewPrivate* priv = static_cast( + fl_platform_view_get_instance_private(self)); + gtk_widget_set_direction(widget, priv->direction); + return widget; } void fl_platform_view_set_direction(FlPlatformView* self, GtkTextDirection direction) { - FlPlatformViewPrivate* priv = reinterpret_cast( + FlPlatformViewPrivate* priv = static_cast( fl_platform_view_get_instance_private(self)); priv->direction = direction; - // apply GtkTextDirection to widget. - fl_platform_view_get_view(self); } // Added here to stop the compiler from optimizing this function away. diff --git a/shell/platform/linux/fl_platform_views_plugin.cc b/shell/platform/linux/fl_platform_views_plugin.cc index 51b0cb738a783..ca84d2a546a43 100644 --- a/shell/platform/linux/fl_platform_views_plugin.cc +++ b/shell/platform/linux/fl_platform_views_plugin.cc @@ -37,7 +37,7 @@ struct _FlPlatformViewsPlugin { G_DEFINE_TYPE(FlPlatformViewsPlugin, fl_platform_views_plugin, G_TYPE_OBJECT) -// Sends the method call response to Flutter. +// Sends the method call response to Flutter Framework. static void send_response(FlMethodCall* method_call, FlMethodResponse* response) { g_autoptr(GError) error = nullptr; diff --git a/shell/platform/linux/fl_platform_views_plugin.h b/shell/platform/linux/fl_platform_views_plugin.h index 7e2b51ae5e0ed..e462af12efac1 100644 --- a/shell/platform/linux/fl_platform_views_plugin.h +++ b/shell/platform/linux/fl_platform_views_plugin.h @@ -21,7 +21,10 @@ G_DECLARE_FINAL_TYPE(FlPlatformViewsPlugin, * FlPlatformViewsPlugin: * * #FlPlatformViewsPlugin is a plugin that implements the shell side - * of SystemChannels.platform_views from the Flutter services library. + * of SystemChannels.platform_views from the Flutter services library. This also + * manages shell-side platform views registration, processes platform views + * construction and destruction request from Framework side, transmit gesture + * events to widgets. */ /** diff --git a/shell/platform/linux/public/flutter_linux/fl_platform_views.h b/shell/platform/linux/public/flutter_linux/fl_platform_views.h index e17fc10c3e5b3..c73a678f7c1ad 100644 --- a/shell/platform/linux/public/flutter_linux/fl_platform_views.h +++ b/shell/platform/linux/public/flutter_linux/fl_platform_views.h @@ -27,6 +27,43 @@ G_DECLARE_DERIVABLE_TYPE(FlPlatformView, * * #FlPlatformView is an abstract class that wraps a #GtkWidget for embedding in * the Flutter hierarchy. + * + * The following example shows how to implement an #FlPlatformView. + * |[ + * // Type definition, destructor, init and class_init are omitted. + * struct _WebViewPlatformView { // extends FlPlatformView + * FlPlatformView parent_instance; + * int64_t view_identifier; + * WebKitWebView *webview; // holds reference to your widget. + * }; + * + * G_DEFINE_TYPE(WebViewPlatformView, + * webview_platform_view, + * fl_platform_view_get_type ()) + * + * static GtkWidget * + * webview_plaform_view_get_view (FlPlatformView *platform_view) { + * // Simply return the #GtkWidget you created in constructor. + * // DO NOT create widgets in this function, which will be called + * // every frame. + * WebViewPlatformView *self = WEBVIEW_PLATFORM_VIEW (platform_view); + * return GTK_WIDGET (self->webview); + * } + * + * // Constructor should be called by your custom #FlPlatformViewFactory. + * WebViewPlatformView * + * webview_platform_view_new (FlBinaryMessenger *messenger, + * int64_t view_identifier, + * FlValue *args) { + * // Initializes your #GtkWidget, and stores it in #FlPlatformView. + * WebKitWebView *webview = webview_web_view_new (); + * + * WebViewPlatformView *view = WEBVIEW_PLATFORM_VIEW ( + * g_object_new (webview_platform_view_get_type (), NULL)); + * view->webview = webview; + * return view; + * } + * ]| */ struct _FlPlatformViewClass { @@ -50,8 +87,6 @@ struct _FlPlatformViewClass { */ GtkWidget* fl_platform_view_get_view(FlPlatformView* platform_view); -// - G_DECLARE_INTERFACE(FlPlatformViewFactory, fl_platform_view_factory, FL, @@ -62,6 +97,58 @@ G_DECLARE_INTERFACE(FlPlatformViewFactory, * FlPlatformViewFactory: * * #FlPlatformViewFactory vends #FlPlatformView objects for embedding. + * + * The following example shows how to implement an #FlPlatformViewFactory. + * In common cases, #FlPlatformViewFactory may not hold references to your + * own #FlPlatformView. Flutter will manage life-cycle of platform views + * created by your factory. + * + * For header of your subclass: + * |[ + * // platform_view_factory.h + * G_DECLARE_FINAL_TYPE(WebViewFactory, + * webview_factory, + * WEBVIEW, + * FACTORY, + * GObject) + * + * // Creates your own platform view factory. Messenger can be used for + * // Dart-side code to control single #FlPlatformView (or #GtkWidget). + * WebViewFactory * + * webview_factory_new(FlBinaryMessenger *messenger); + * ]| + * + * For source of your subclass: + * |[ + * // platform_view_factory.c + * // Type definitions and webview_factory_new are omitted. + * + * static FlPlatformView * + * webview_factory_create_platform_view (FlPlatformViewFactory *factory, + * int64_t view_identifier, + * FlValue *args) { + * // Creates a new instance of your custom #FlPlatformView implementation. + * // Flutter will take the reference, and release it when Dart-side widget + * // is destructed. + * WebViewFactory *self = WEBVIEW_FACTORY (factory); + * return FL_PLATFORM_VIEW ( + * webview_platform_view_new (self->messenger, view_identifier, args)); + * } + * + * static FlMessageCodec * + * webview_factory_get_create_arguments_codec (FlPlatformViewFactory *self) { + * // Simply creates a new instance of message codec. + * return FL_MESSAGE_CODEC (fl_standard_message_codec_new ()); + * } + * + * static void + * webview_factory_fl_platform_view_factory_iface_init ( + * FlPlatformViewFactoryInterface* iface) { + * iface->create_platform_view = webview_factory_create_platform_view; + * iface->get_create_arguments_codec = + * webview_factory_get_create_arguments_codec; + * } + * ]| */ struct _FlPlatformViewFactoryInterface { From a9a8a224acc93ca8bb816cc19ae26f68d590f37b Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 6 May 2021 23:12:06 +0800 Subject: [PATCH 10/14] fix: nits --- shell/platform/linux/fl_clipping_view.cc | 14 +-- shell/platform/linux/fl_clipping_view.h | 5 +- shell/platform/linux/fl_renderer_gl.cc | 5 +- shell/platform/linux/fl_view.cc | 100 ++++++++++-------- shell/platform/linux/fl_view_private.h | 2 +- .../public/flutter_linux/fl_platform_views.h | 2 +- .../flutter_linux/fl_plugin_registrar.h | 2 +- 7 files changed, 69 insertions(+), 61 deletions(-) diff --git a/shell/platform/linux/fl_clipping_view.cc b/shell/platform/linux/fl_clipping_view.cc index cc9c384a6cc47..4889d810513fe 100644 --- a/shell/platform/linux/fl_clipping_view.cc +++ b/shell/platform/linux/fl_clipping_view.cc @@ -18,10 +18,7 @@ G_DEFINE_TYPE(FlClippingView, fl_clipping_view, GTK_TYPE_EVENT_BOX) static void fl_clipping_view_dispose(GObject* gobject) { FlClippingView* self = FL_CLIPPING_VIEW(gobject); - if (self->mutations) { - g_ptr_array_unref(self->mutations); - self->mutations = nullptr; - } + g_clear_pointer(&self->mutations, g_ptr_array_unref); G_OBJECT_CLASS(fl_clipping_view_parent_class)->dispose(gobject); } @@ -160,17 +157,16 @@ void fl_clipping_view_reset(FlClippingView* self, GtkWidget* old_child = gtk_bin_get_child(GTK_BIN(self)); if (old_child != child) { - if (old_child) + if (old_child) { gtk_container_remove(GTK_CONTAINER(self), old_child); + } g_object_ref(child); gtk_container_add(GTK_CONTAINER(self), child); } - if (self->mutations) { - g_ptr_array_unref(self->mutations); - } self->geometry = *geometry; - self->mutations = mutations; + g_clear_pointer(&self->mutations, g_ptr_array_unref); + self->mutations = g_ptr_array_ref(mutations); if (mutations) { for (guint i = 0; i < mutations->len; i++) { diff --git a/shell/platform/linux/fl_clipping_view.h b/shell/platform/linux/fl_clipping_view.h index 931e0aaeca9d1..8a5f7d41360a7 100644 --- a/shell/platform/linux/fl_clipping_view.h +++ b/shell/platform/linux/fl_clipping_view.h @@ -20,7 +20,8 @@ G_DECLARE_FINAL_TYPE(FlClippingView, /** * FlClippingView: * - * #FlClippingView is a GTK widget that draws its child with mutators. + * #FlClippingView is a GTK widget that applies clipping mutations set by + * Framework side to child platform view. */ /** @@ -37,7 +38,7 @@ GtkWidget* fl_clipping_view_new(); * @clipping_view: an #FlClippingView. * @child: (transfer-none): widget to apply mutators. * @geometry: geometry of widget. - * @mutations: (transfer-full): mutations to be applied. + * @mutations: the clipping mutations to be applied. * * Reset child widget and its mutations. */ diff --git a/shell/platform/linux/fl_renderer_gl.cc b/shell/platform/linux/fl_renderer_gl.cc index 55e0694b920d0..59aaf4268d19e 100644 --- a/shell/platform/linux/fl_renderer_gl.cc +++ b/shell/platform/linux/fl_renderer_gl.cc @@ -115,15 +115,16 @@ static gboolean fl_renderer_gl_present_layers(FlRenderer* renderer, fl_platform_views_plugin_get_platform_view( plugin, layer->platform_view->identifier); GtkWidget* widget = fl_platform_view_get_view(platform_view); - if (!widget) + if (!widget) { continue; + } GdkRectangle geometry = { .x = static_cast(layer->offset.x), .y = static_cast(layer->offset.y), .width = static_cast(layer->size.width), .height = static_cast(layer->size.height), }; - GPtrArray* mutations = + g_autoptr(GPtrArray) mutations = g_ptr_array_new_full(layer->platform_view->mutations_count, g_free); for (size_t i = 0; i < layer->platform_view->mutations_count; i++) { FlutterPlatformViewMutation* mutation = diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 34b8de01444c9..64f14a1780a19 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -56,13 +56,17 @@ struct _FlView { GList* gl_area_list; GList* used_area_list; - std::map* current_clipping_views; - std::map* last_clipping_views; + // FlClippingView keyed by GtkWidget + GHashTable* current_clipping_views; + // FlClippingView keyed by GtkWidget + GHashTable* last_clipping_views; GtkWidget* event_box; - std::vector* children_list; - std::vector* pending_children_list; + // GtkWidgets currently on screen. + GList* children_list; + // GtkWidgets on screen in next frame. + GList* pending_children_list; }; enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST }; @@ -198,10 +202,10 @@ static void fl_view_constructed(GObject* object) { self->platform_views_plugin = fl_platform_views_plugin_new(messenger, self->gesture_helper); - self->children_list = new std::vector(); - self->pending_children_list = new std::vector(); - self->current_clipping_views = new std::map(); - self->last_clipping_views = new std::map(); + self->current_clipping_views = g_hash_table_new_full( + g_direct_hash, g_direct_equal, nullptr, g_object_unref); + self->last_clipping_views = g_hash_table_new_full( + g_direct_hash, g_direct_equal, nullptr, g_object_unref); self->event_box = gtk_event_box_new(); gtk_widget_set_parent(self->event_box, GTK_WIDGET(self)); @@ -287,10 +291,10 @@ static void fl_view_dispose(GObject* object) { g_list_free_full(self->gl_area_list, g_object_unref); self->gl_area_list = nullptr; - delete self->children_list; - delete self->pending_children_list; - delete self->current_clipping_views; - delete self->last_clipping_views; + g_clear_pointer(&self->children_list, g_list_free); + g_clear_pointer(&self->pending_children_list, g_list_free); + g_clear_pointer(&self->current_clipping_views, g_hash_table_unref); + g_clear_pointer(&self->last_clipping_views, g_hash_table_unref); G_OBJECT_CLASS(fl_view_parent_class)->dispose(object); } @@ -353,7 +357,9 @@ static void fl_view_size_allocate(GtkWidget* widget, .height = allocation->height, }; - for (GtkWidget* child : *self->children_list) { + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + GtkWidget* child = reinterpret_cast(iterator->data); if (!gtk_widget_get_visible(child)) continue; @@ -494,7 +500,7 @@ static gboolean fl_view_key_release_event(GtkWidget* widget, static void fl_view_put(FlView* self, GtkWidget* widget) { gtk_widget_set_parent(widget, GTK_WIDGET(self)); - self->children_list->push_back(widget); + self->children_list = g_list_append(self->children_list, widget); } static void fl_view_add(GtkContainer* container, GtkWidget* widget) { @@ -503,12 +509,13 @@ static void fl_view_add(GtkContainer* container, GtkWidget* widget) { static void fl_view_remove(GtkContainer* container, GtkWidget* widget) { FlView* self = FL_VIEW(container); - for (auto child = self->children_list->begin(); - child != self->children_list->end(); child++) { - if (*child == widget) { + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + GtkWidget* child = reinterpret_cast(iterator->data); + if (child == widget) { g_object_ref(widget); // children_list does not own widgets. gtk_widget_unparent(widget); - self->children_list->erase(child); + self->children_list = g_list_delete_link(self->children_list, iterator); break; } } @@ -523,7 +530,9 @@ static void fl_view_forall(GtkContainer* container, GtkCallback callback, gpointer callback_data) { FlView* self = FL_VIEW(container); - for (GtkWidget* child : *self->children_list) { + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + GtkWidget* child = reinterpret_cast(iterator->data); (*callback)(child, callback_data); } @@ -606,11 +615,12 @@ void fl_view_begin_frame(FlView* view) { FlView* self = FL_VIEW(view); self->used_area_list = self->gl_area_list; - self->pending_children_list->clear(); + g_clear_pointer(&self->pending_children_list, g_list_free); } static void fl_view_add_pending_child(FlView* self, GtkWidget* child) { - self->pending_children_list->push_back(child); + self->pending_children_list = + g_list_append(self->pending_children_list, child); } void fl_view_add_gl_area(FlView* view, @@ -637,13 +647,14 @@ void fl_view_add_widget(FlView* view, GdkRectangle* geometry, GPtrArray* mutations) { FlClippingView* clipping_view; - if (view->last_clipping_views->count(widget)) { - clipping_view = view->last_clipping_views->at(widget); - view->last_clipping_views->erase(widget); + if (g_hash_table_contains(view->last_clipping_views, widget)) { + clipping_view = reinterpret_cast( + g_hash_table_lookup(view->last_clipping_views, widget)); + g_hash_table_steal(view->last_clipping_views, widget); } else { clipping_view = FL_CLIPPING_VIEW(fl_clipping_view_new()); } - view->current_clipping_views->insert({widget, clipping_view}); + g_hash_table_insert(view->current_clipping_views, widget, clipping_view); fl_clipping_view_reset(clipping_view, widget, geometry, mutations); gtk_widget_show_all(GTK_WIDGET(clipping_view)); @@ -651,42 +662,41 @@ void fl_view_add_widget(FlView* view, } void fl_view_end_frame(FlView* self) { - for (GtkWidget* pending_child : *self->pending_children_list) { - auto child = std::find(self->children_list->begin(), - self->children_list->end(), pending_child); - - if (child != self->children_list->end()) { + for (GList* pending_child = self->pending_children_list; pending_child; + pending_child = pending_child->next) { + GtkWidget* pending_view_child = + reinterpret_cast(pending_child->data); + GList* child = g_list_find(self->children_list, pending_view_child); + if (child) { // existing child - *child = nullptr; + // We remove existing children in children_list, so children_list will + // finally contains all children to be removed. + self->children_list = g_list_delete_link(self->children_list, child); } else { // newly added child - gtk_widget_set_parent(pending_child, GTK_WIDGET(self)); + gtk_widget_set_parent(pending_view_child, GTK_WIDGET(self)); } } - for (GtkWidget* child : *self->children_list) { - if (child) { - // removed child - g_object_ref(child); - gtk_widget_unparent(child); - } + for (GList* child = self->children_list; child; child = child->next) { + GtkWidget* view_child = reinterpret_cast(child->data); + // removed child + g_object_ref(view_child); + gtk_widget_unparent(view_child); } - self->children_list->clear(); + g_clear_pointer(&self->children_list, g_list_free); std::swap(self->children_list, self->pending_children_list); - for (auto& it : *self->last_clipping_views) { - g_object_unref(it.second); - } - self->last_clipping_views->clear(); + g_hash_table_remove_all(self->last_clipping_views); std::swap(self->last_clipping_views, self->current_clipping_views); struct _ReorderData data = { - .parent_window = gtk_widget_get_window(GTK_WIDGET(view)), + .parent_window = gtk_widget_get_window(GTK_WIDGET(self)), .last_window = nullptr, }; - gtk_container_forall(GTK_CONTAINER(view), fl_view_reorder_forall, &data); + gtk_container_forall(GTK_CONTAINER(self), fl_view_reorder_forall, &data); gtk_widget_queue_draw(GTK_WIDGET(self)); } diff --git a/shell/platform/linux/fl_view_private.h b/shell/platform/linux/fl_view_private.h index 765001783aef5..f472f8cb7cb2d 100644 --- a/shell/platform/linux/fl_view_private.h +++ b/shell/platform/linux/fl_view_private.h @@ -48,7 +48,7 @@ void fl_view_add_gl_area(FlView* view, * @view: an #FlView. * @widget: a #GtkWidget. * @geometry: geometry of the widget. - * @mutations: (transfer-full): the mutations applied to the widget. + * @mutations: the clipping mutations applied to the widget. * * Append a #GtkWidget at top of stacked children of #FlView. */ diff --git a/shell/platform/linux/public/flutter_linux/fl_platform_views.h b/shell/platform/linux/public/flutter_linux/fl_platform_views.h index c73a678f7c1ad..0023ba497d94f 100644 --- a/shell/platform/linux/public/flutter_linux/fl_platform_views.h +++ b/shell/platform/linux/public/flutter_linux/fl_platform_views.h @@ -143,7 +143,7 @@ G_DECLARE_INTERFACE(FlPlatformViewFactory, * * static void * webview_factory_fl_platform_view_factory_iface_init ( - * FlPlatformViewFactoryInterface* iface) { + * FlPlatformViewFactoryInterface *iface) { * iface->create_platform_view = webview_factory_create_platform_view; * iface->get_create_arguments_codec = * webview_factory_get_create_arguments_codec; diff --git a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h index 83f94f647d5a0..bc7eb914b0102 100644 --- a/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h +++ b/shell/platform/linux/public/flutter_linux/fl_plugin_registrar.h @@ -53,7 +53,7 @@ FlView* fl_plugin_registrar_get_view(FlPluginRegistrar* registrar); /** * fl_plugin_registrar_register_view_factory: * @registrar: an #FlPluginRegistrar. - * @factory: (transfer-none): the view factory that will be registered. + * @factory: (transfer none): the view factory that will be registered. * @view_type: A unique identifier for the factory. The Dart code of the Flutter * app can use this identifier to request creation of a #GtkWidget * by the registered factory. From bed4f49787455fc853bc592907bdb3892de62626 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 6 May 2021 23:23:36 +0800 Subject: [PATCH 11/14] fix: nits --- .../linux/fl_platform_views_plugin.cc | 25 +++++++++++++++++++ shell/platform/linux/fl_renderer_gl.cc | 3 +++ 2 files changed, 28 insertions(+) diff --git a/shell/platform/linux/fl_platform_views_plugin.cc b/shell/platform/linux/fl_platform_views_plugin.cc index ca84d2a546a43..91d11e79fbb00 100644 --- a/shell/platform/linux/fl_platform_views_plugin.cc +++ b/shell/platform/linux/fl_platform_views_plugin.cc @@ -14,6 +14,7 @@ static constexpr char kChannelName[] = "flutter/platform_views"; static constexpr char kBadArgumentsError[] = "Bad Arguments"; +static constexpr char kBadPreconditionError[] = "Illegal State"; static constexpr char kCreateMethod[] = "create"; static constexpr char kDisposeMethod[] = "dispose"; static constexpr char kAcceptGestureMethod[] = "acceptGesture"; @@ -131,6 +132,11 @@ static FlMethodResponse* platform_views_accept_gesture( kBadArgumentsError, "Argument list missing or malformed", nullptr)); } + if (!self->gesture_helper) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadPreconditionError, "Gesture helper destroyed", nullptr)); + } + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); int64_t pointer_id = fl_value_get_int(fl_value_get_list_value(args, 1)); FlPlatformView* platform_view = @@ -202,6 +208,11 @@ static FlMethodResponse* platform_views_enter(FlPlatformViewsPlugin* self, kBadArgumentsError, "Argument list missing or malformed", nullptr)); } + if (!self->gesture_helper) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadPreconditionError, "Gesture helper destroyed", nullptr)); + } + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); FlPlatformView* platform_view = fl_platform_views_plugin_get_platform_view(self, view_id); @@ -226,6 +237,11 @@ static FlMethodResponse* platform_views_exit(FlPlatformViewsPlugin* self, kBadArgumentsError, "Argument list missing or malformed", nullptr)); } + if (!self->gesture_helper) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadPreconditionError, "Gesture helper destroyed", nullptr)); + } + int64_t view_id = fl_value_get_int(fl_value_get_list_value(args, 0)); FlPlatformView* platform_view = fl_platform_views_plugin_get_platform_view(self, view_id); @@ -300,6 +316,12 @@ static void fl_platform_views_plugin_dispose(GObject* object) { g_hash_table_destroy(self->platform_views); self->factories = self->platform_views = nullptr; + if (self->gesture_helper != nullptr) { + g_object_remove_weak_pointer( + G_OBJECT(self), reinterpret_cast(&(self->gesture_helper))); + self->gesture_helper = nullptr; + } + G_OBJECT_CLASS(fl_platform_views_plugin_parent_class)->dispose(object); } @@ -325,6 +347,9 @@ FlPlatformViewsPlugin* fl_platform_views_plugin_new( nullptr); self->gesture_helper = gesture_helper; + g_object_add_weak_pointer( + G_OBJECT(self), reinterpret_cast(&(self->gesture_helper))); + self->factories = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); self->platform_views = g_hash_table_new_full(g_direct_hash, g_direct_equal, diff --git a/shell/platform/linux/fl_renderer_gl.cc b/shell/platform/linux/fl_renderer_gl.cc index 59aaf4268d19e..07bd078b3bdce 100644 --- a/shell/platform/linux/fl_renderer_gl.cc +++ b/shell/platform/linux/fl_renderer_gl.cc @@ -124,6 +124,9 @@ static gboolean fl_renderer_gl_present_layers(FlRenderer* renderer, .width = static_cast(layer->size.width), .height = static_cast(layer->size.height), }; + + // Makes a copy of mutation array from embedder API so we can use it out + // of this method. g_autoptr(GPtrArray) mutations = g_ptr_array_new_full(layer->platform_view->mutations_count, g_free); for (size_t i = 0; i < layer->platform_view->mutations_count; i++) { From ca9e391b764f8d248dd85950bc1d51275624a32e Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 20 May 2021 21:25:16 +0800 Subject: [PATCH 12/14] Fix leaking error --- shell/platform/linux/fl_platform_views_plugin.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_platform_views_plugin.cc b/shell/platform/linux/fl_platform_views_plugin.cc index 91d11e79fbb00..35df82113cf0f 100644 --- a/shell/platform/linux/fl_platform_views_plugin.cc +++ b/shell/platform/linux/fl_platform_views_plugin.cc @@ -105,8 +105,14 @@ static FlMethodResponse* platform_views_create(FlPlatformViewsPlugin* self, g_autoptr(GBytes) bytes = g_bytes_new_static(creation_params_bytes, creation_params_length); - GError* error = nullptr; + g_autoptr(GError) error = nullptr; creation_params = fl_message_codec_decode_message(codec, bytes, &error); + if (!creation_params) { + g_warning("Failed to decode creation params, error message: %s", + error->message); + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Creation params cannot be parsed", nullptr)); + } } platform_view = fl_platform_view_factory_create_platform_view( From e4b3fdecf12a43ae3fac13665aa7191c22892526 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 20 May 2021 21:25:29 +0800 Subject: [PATCH 13/14] Fix no dispose --- shell/platform/linux/fl_gesture_helper.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_gesture_helper.cc b/shell/platform/linux/fl_gesture_helper.cc index 4841918841083..8b118fca33f8f 100644 --- a/shell/platform/linux/fl_gesture_helper.cc +++ b/shell/platform/linux/fl_gesture_helper.cc @@ -17,7 +17,21 @@ struct _FlGestureHelper { G_DEFINE_TYPE(FlGestureHelper, fl_gesture_helper, G_TYPE_OBJECT) -static void fl_gesture_helper_class_init(FlGestureHelperClass* klass) {} +static void fl_gesture_helper_dispose(GObject* object) { + FlGestureHelper* self = FL_GESTURE_HELPER(object); + + g_list_free_full(self->event_list, free_event); + self->event_list = nullptr; + + g_clear_object(&self->grabbed_widget); + g_clear_object(&self->hover_widget); + + G_OBJECT_CLASS(fl_gesture_helper_parent_class)->dispose(object); +} + +static void fl_gesture_helper_class_init(FlGestureHelperClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_gesture_helper_dispose; +} static void fl_gesture_helper_init(FlGestureHelper* self) {} From 6eeb3211fd33c34ab692fa75f7164bb269c3142a Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Thu, 20 May 2021 21:25:45 +0800 Subject: [PATCH 14/14] Fix: clipping_view's mutations cannot be null --- shell/platform/linux/fl_clipping_view.cc | 15 +++++++-------- shell/platform/linux/fl_clipping_view.h | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/shell/platform/linux/fl_clipping_view.cc b/shell/platform/linux/fl_clipping_view.cc index 4889d810513fe..44b803c5dc785 100644 --- a/shell/platform/linux/fl_clipping_view.cc +++ b/shell/platform/linux/fl_clipping_view.cc @@ -154,6 +154,7 @@ void fl_clipping_view_reset(FlClippingView* self, GdkRectangle* geometry, GPtrArray* mutations) { g_return_if_fail(FL_IS_CLIPPING_VIEW(self)); + g_return_if_fail(mutations != nullptr); GtkWidget* old_child = gtk_bin_get_child(GTK_BIN(self)); if (old_child != child) { @@ -168,14 +169,12 @@ void fl_clipping_view_reset(FlClippingView* self, g_clear_pointer(&self->mutations, g_ptr_array_unref); self->mutations = g_ptr_array_ref(mutations); - if (mutations) { - for (guint i = 0; i < mutations->len; i++) { - FlutterPlatformViewMutation* mutation = - reinterpret_cast( - self->mutations->pdata[i]); - if (mutation->type == kFlutterPlatformViewMutationTypeOpacity) { - gtk_widget_set_opacity(child, mutation->opacity); - } + for (guint i = 0; i < mutations->len; i++) { + FlutterPlatformViewMutation* mutation = + reinterpret_cast( + self->mutations->pdata[i]); + if (mutation->type == kFlutterPlatformViewMutationTypeOpacity) { + gtk_widget_set_opacity(child, mutation->opacity); } } } diff --git a/shell/platform/linux/fl_clipping_view.h b/shell/platform/linux/fl_clipping_view.h index 8a5f7d41360a7..2c84a51675ec9 100644 --- a/shell/platform/linux/fl_clipping_view.h +++ b/shell/platform/linux/fl_clipping_view.h @@ -36,7 +36,7 @@ GtkWidget* fl_clipping_view_new(); /** * fl_clipping_view_reset: * @clipping_view: an #FlClippingView. - * @child: (transfer-none): widget to apply mutators. + * @child: widget to apply mutators. * @geometry: geometry of widget. * @mutations: the clipping mutations to be applied. *