diff --git a/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc b/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc index 3a1a998e59..16dcd2b86b 100644 --- a/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc +++ b/engine/src/flutter/shell/platform/linux/fl_binary_messenger.cc @@ -149,18 +149,6 @@ static void platform_message_handler_free(gpointer data) { g_free(self); } -static void engine_weak_notify_cb(gpointer user_data, - GObject* where_the_object_was) { - FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(user_data); - - // Disconnect any handlers. - // Take the reference in case a handler tries to modify this table. - g_autoptr(GHashTable) handlers = self->platform_message_handlers; - self->platform_message_handlers = g_hash_table_new_full( - g_str_hash, g_str_equal, g_free, platform_message_handler_free); - g_hash_table_remove_all(handlers); -} - static gboolean fl_binary_messenger_platform_message_cb( FlEngine* engine, const gchar* channel, @@ -187,13 +175,6 @@ static gboolean fl_binary_messenger_platform_message_cb( static void fl_binary_messenger_impl_dispose(GObject* object) { FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(object); - { - g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); - if (engine) { - g_object_weak_unref(G_OBJECT(engine), engine_weak_notify_cb, self); - } - } - g_weak_ref_clear(&self->engine); g_clear_pointer(&self->platform_message_handlers, g_hash_table_unref); @@ -383,6 +364,17 @@ static void set_warns_on_channel_overflow(FlBinaryMessenger* messenger, set_warns_on_channel_overflow_response_cb, nullptr); } +static void shutdown(FlBinaryMessenger* messenger) { + FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger); + + // Disconnect any handlers. + // Take the reference in case a handler tries to modify this table. + g_autoptr(GHashTable) handlers = self->platform_message_handlers; + self->platform_message_handlers = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, platform_message_handler_free); + g_hash_table_remove_all(handlers); +} + static void fl_binary_messenger_impl_class_init( FlBinaryMessengerImplClass* klass) { G_OBJECT_CLASS(klass)->dispose = fl_binary_messenger_impl_dispose; @@ -396,6 +388,7 @@ static void fl_binary_messenger_impl_iface_init( iface->send_on_channel_finish = send_on_channel_finish; iface->resize_channel = resize_channel; iface->set_warns_on_channel_overflow = set_warns_on_channel_overflow; + iface->shutdown = shutdown; } static void fl_binary_messenger_impl_init(FlBinaryMessengerImpl* self) { @@ -413,7 +406,6 @@ FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine) { FL_IS_BINARY_MESSENGER_IMPL(self); g_weak_ref_init(&self->engine, G_OBJECT(engine)); - g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self); fl_engine_set_platform_message_handler( engine, fl_binary_messenger_platform_message_cb, self, NULL); @@ -490,3 +482,9 @@ G_MODULE_EXPORT void fl_binary_messenger_set_warns_on_channel_overflow( return FL_BINARY_MESSENGER_GET_IFACE(self)->set_warns_on_channel_overflow( self, channel, warns); } + +void fl_binary_messenger_shutdown(FlBinaryMessenger* self) { + g_return_if_fail(FL_IS_BINARY_MESSENGER(self)); + + return FL_BINARY_MESSENGER_GET_IFACE(self)->shutdown(self); +} diff --git a/engine/src/flutter/shell/platform/linux/fl_binary_messenger_private.h b/engine/src/flutter/shell/platform/linux/fl_binary_messenger_private.h index ce70c51dc6..e444e8f94e 100644 --- a/engine/src/flutter/shell/platform/linux/fl_binary_messenger_private.h +++ b/engine/src/flutter/shell/platform/linux/fl_binary_messenger_private.h @@ -22,6 +22,14 @@ G_BEGIN_DECLS */ FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine); +/** + * fl_binary_messenger_shutdown: + * @messenger: an #FlBinaryMessenger. + * + * Shutdown the messenger closing any open channels. + */ +void fl_binary_messenger_shutdown(FlBinaryMessenger* messenger); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_BINARY_MESSENGER_PRIVATE_H_ diff --git a/engine/src/flutter/shell/platform/linux/fl_binary_messenger_test.cc b/engine/src/flutter/shell/platform/linux/fl_binary_messenger_test.cc index d27e07d8af..85cbed4de9 100644 --- a/engine/src/flutter/shell/platform/linux/fl_binary_messenger_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_binary_messenger_test.cc @@ -634,7 +634,7 @@ static void kill_handler_notify_cb(gpointer was_called) { TEST(FlBinaryMessengerTest, DeletingEngineClearsHandlers) { FlEngine* engine = make_mock_engine(); - g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); + FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine); gboolean was_killed = FALSE; // Listen for messages from the engine. diff --git a/engine/src/flutter/shell/platform/linux/fl_engine.cc b/engine/src/flutter/shell/platform/linux/fl_engine.cc index 42001f1dec..ace289a1b0 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine.cc @@ -399,6 +399,9 @@ static void fl_engine_dispose(GObject* object) { self->aot_data = nullptr; } + fl_binary_messenger_shutdown(self->binary_messenger); + fl_texture_registrar_shutdown(self->texture_registrar); + g_clear_object(&self->project); g_clear_object(&self->renderer); g_clear_object(&self->texture_registrar); diff --git a/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.cc b/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.cc index f48a6c78f1..2cbb46e37d 100644 --- a/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.cc +++ b/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.cc @@ -39,6 +39,7 @@ static void fl_renderer_gdk_clear_current(FlRenderer* renderer) { gdk_gl_context_clear_current(); } +// Implements FlRenderer::get_refresh_rate. static gdouble fl_renderer_gdk_get_refresh_rate(FlRenderer* renderer) { FlRendererGdk* self = FL_RENDERER_GDK(renderer); GdkDisplay* display = gdk_window_get_display(self->window); @@ -78,14 +79,22 @@ static void fl_renderer_gdk_class_init(FlRendererGdkClass* klass) { static void fl_renderer_gdk_init(FlRendererGdk* self) {} -FlRendererGdk* fl_renderer_gdk_new(GdkWindow* window) { +FlRendererGdk* fl_renderer_gdk_new() { FlRendererGdk* self = FL_RENDERER_GDK(g_object_new(fl_renderer_gdk_get_type(), nullptr)); - self->window = window; return self; } +void fl_renderer_gdk_set_window(FlRendererGdk* self, GdkWindow* window) { + g_return_if_fail(FL_IS_RENDERER_GDK(self)); + + g_assert(self->window == nullptr); + self->window = window; +} + gboolean fl_renderer_gdk_create_contexts(FlRendererGdk* self, GError** error) { + g_return_val_if_fail(FL_IS_RENDERER_GDK(self), FALSE); + self->gdk_context = gdk_window_create_gl_context(self->window, error); if (self->gdk_context == nullptr) { return FALSE; diff --git a/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.h b/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.h index e2b3f7bb1f..89a8ee212a 100644 --- a/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.h +++ b/engine/src/flutter/shell/platform/linux/fl_renderer_gdk.h @@ -23,13 +23,22 @@ G_DECLARE_FINAL_TYPE(FlRendererGdk, /** * fl_renderer_gdk_new: - * @window: the window that is being rendered on. * * Creates an object that allows Flutter to render by OpenGL ES. * * Returns: a new #FlRendererGdk. */ -FlRendererGdk* fl_renderer_gdk_new(GdkWindow* window); +FlRendererGdk* fl_renderer_gdk_new(); + +/** + * fl_renderer_gdk_set_window: + * @renderer: an #FlRendererGdk. + * @window: the window that is being rendered on. + * + * Set the window that is being rendered on. This is only called once when the + * window is available. + */ +void fl_renderer_gdk_set_window(FlRendererGdk* renderer, GdkWindow* window); /** * fl_renderer_gdk_create_contexts: diff --git a/engine/src/flutter/shell/platform/linux/fl_task_runner.cc b/engine/src/flutter/shell/platform/linux/fl_task_runner.cc index ce9938915c..147550c504 100644 --- a/engine/src/flutter/shell/platform/linux/fl_task_runner.cc +++ b/engine/src/flutter/shell/platform/linux/fl_task_runner.cc @@ -11,7 +11,7 @@ static constexpr int kMillisecondsPerMicrosecond = 1000; struct _FlTaskRunner { GObject parent_instance; - FlEngine* engine; + GWeakRef engine; GMutex mutex; GCond cond; @@ -51,11 +51,14 @@ static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner* self) { g_mutex_unlock(&self->mutex); - l = expired_tasks; - while (l != nullptr && self->engine) { - FlTaskRunnerTask* task = static_cast(l->data); - fl_engine_execute_task(self->engine, &task->task); - l = l->next; + g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); + if (engine != nullptr) { + l = expired_tasks; + while (l != nullptr) { + FlTaskRunnerTask* task = static_cast(l->data); + fl_engine_execute_task(engine, &task->task); + l = l->next; + } } g_list_free_full(expired_tasks, g_free); @@ -120,12 +123,6 @@ static void fl_task_runner_tasks_did_change_locked(FlTaskRunner* self) { } } -static void engine_weak_notify_cb(gpointer user_data, - GObject* where_the_object_was) { - FlTaskRunner* self = FL_TASK_RUNNER(user_data); - self->engine = nullptr; -} - void fl_task_runner_dispose(GObject* object) { FlTaskRunner* self = FL_TASK_RUNNER(object); @@ -133,11 +130,7 @@ void fl_task_runner_dispose(GObject* object) { // main thread g_assert(!self->blocking_main_thread); - if (self->engine != nullptr) { - g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self); - self->engine = nullptr; - } - + g_weak_ref_clear(&self->engine); g_mutex_clear(&self->mutex); g_cond_clear(&self->cond); @@ -159,11 +152,10 @@ static void fl_task_runner_init(FlTaskRunner* self) { } FlTaskRunner* fl_task_runner_new(FlEngine* engine) { - FlTaskRunner* res = + FlTaskRunner* self = FL_TASK_RUNNER(g_object_new(fl_task_runner_get_type(), nullptr)); - res->engine = engine; - g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, res); - return res; + g_weak_ref_init(&self->engine, G_OBJECT(engine)); + return self; } void fl_task_runner_post_task(FlTaskRunner* self, diff --git a/engine/src/flutter/shell/platform/linux/fl_texture_registrar.cc b/engine/src/flutter/shell/platform/linux/fl_texture_registrar.cc index 7ef0f85a04..1b373f4d14 100644 --- a/engine/src/flutter/shell/platform/linux/fl_texture_registrar.cc +++ b/engine/src/flutter/shell/platform/linux/fl_texture_registrar.cc @@ -23,7 +23,7 @@ struct _FlTextureRegistrarImpl { GObject parent_instance; // Weak reference to the engine this texture registrar is created for. - FlEngine* engine; + GWeakRef engine; // ID to assign to the next new texture. int64_t next_id; @@ -54,20 +54,6 @@ G_DEFINE_TYPE_WITH_CODE( static void fl_texture_registrar_default_init( FlTextureRegistrarInterface* iface) {} -static void engine_weak_notify_cb(gpointer user_data, - GObject* where_the_object_was) { - FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(user_data); - self->engine = nullptr; - - // Unregister any textures. - g_mutex_lock(&self->textures_mutex); - g_autoptr(GHashTable) textures = self->textures; - self->textures = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, - g_object_unref); - g_hash_table_remove_all(textures); - g_mutex_unlock(&self->textures_mutex); -} - static void fl_texture_registrar_impl_dispose(GObject* object) { FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(object); @@ -75,10 +61,7 @@ static void fl_texture_registrar_impl_dispose(GObject* object) { g_clear_pointer(&self->textures, g_hash_table_unref); g_mutex_unlock(&self->textures_mutex); - if (self->engine != nullptr) { - g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self); - self->engine = nullptr; - } + g_weak_ref_clear(&self->engine); g_mutex_clear(&self->textures_mutex); G_OBJECT_CLASS(fl_texture_registrar_impl_parent_class)->dispose(object); @@ -94,7 +77,8 @@ static gboolean register_texture(FlTextureRegistrar* registrar, FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar); if (FL_IS_TEXTURE_GL(texture) || FL_IS_PIXEL_BUFFER_TEXTURE(texture)) { - if (self->engine == nullptr) { + g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); + if (engine == nullptr) { return FALSE; } @@ -104,7 +88,7 @@ static gboolean register_texture(FlTextureRegistrar* registrar, // https://github.com/flutter/flutter/issues/124009 int64_t id = // self->next_id++; int64_t id = reinterpret_cast(texture); - if (fl_engine_register_external_texture(self->engine, id)) { + if (fl_engine_register_external_texture(engine, id)) { fl_texture_set_id(texture, id); g_mutex_lock(&self->textures_mutex); g_hash_table_insert(self->textures, GINT_TO_POINTER(id), @@ -134,11 +118,12 @@ static gboolean mark_texture_frame_available(FlTextureRegistrar* registrar, FlTexture* texture) { FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar); - if (self->engine == nullptr) { + g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); + if (engine == nullptr) { return FALSE; } - return fl_engine_mark_texture_frame_available(self->engine, + return fl_engine_mark_texture_frame_available(engine, fl_texture_get_id(texture)); } @@ -146,12 +131,13 @@ static gboolean unregister_texture(FlTextureRegistrar* registrar, FlTexture* texture) { FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar); - if (self->engine == nullptr) { + g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); + if (engine == nullptr) { return FALSE; } - gboolean result = fl_engine_unregister_external_texture( - self->engine, fl_texture_get_id(texture)); + gboolean result = + fl_engine_unregister_external_texture(engine, fl_texture_get_id(texture)); g_mutex_lock(&self->textures_mutex); if (!g_hash_table_remove(self->textures, @@ -163,12 +149,25 @@ static gboolean unregister_texture(FlTextureRegistrar* registrar, return result; } +static void shutdown(FlTextureRegistrar* registrar) { + FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar); + + // Unregister any textures. + g_mutex_lock(&self->textures_mutex); + g_autoptr(GHashTable) textures = self->textures; + self->textures = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, + g_object_unref); + g_hash_table_remove_all(textures); + g_mutex_unlock(&self->textures_mutex); +} + static void fl_texture_registrar_impl_iface_init( FlTextureRegistrarInterface* iface) { iface->register_texture = register_texture; iface->lookup_texture = lookup_texture; iface->mark_texture_frame_available = mark_texture_frame_available; iface->unregister_texture = unregister_texture; + iface->shutdown = shutdown; } static void fl_texture_registrar_impl_init(FlTextureRegistrarImpl* self) { @@ -213,6 +212,12 @@ G_MODULE_EXPORT gboolean fl_texture_registrar_unregister_texture( texture); } +void fl_texture_registrar_shutdown(FlTextureRegistrar* self) { + g_return_if_fail(FL_IS_TEXTURE_REGISTRAR(self)); + + return FL_TEXTURE_REGISTRAR_GET_IFACE(self)->shutdown(self); +} + FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine) { FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL( g_object_new(fl_texture_registrar_impl_get_type(), nullptr)); @@ -220,8 +225,7 @@ FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine) { // Added to stop compiler complaining about an unused function. FL_IS_TEXTURE_REGISTRAR_IMPL(self); - self->engine = engine; - g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self); + g_weak_ref_init(&self->engine, G_OBJECT(engine)); return FL_TEXTURE_REGISTRAR(self); } diff --git a/engine/src/flutter/shell/platform/linux/fl_texture_registrar_private.h b/engine/src/flutter/shell/platform/linux/fl_texture_registrar_private.h index 3f1e69b7fe..0ebbf48df4 100644 --- a/engine/src/flutter/shell/platform/linux/fl_texture_registrar_private.h +++ b/engine/src/flutter/shell/platform/linux/fl_texture_registrar_private.h @@ -33,6 +33,14 @@ FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine); FlTexture* fl_texture_registrar_lookup_texture(FlTextureRegistrar* registrar, int64_t texture_id); +/** + * fl_texture_registrar_shutdown: + * @registrar: an #FlTextureRegistrar. + * + * Shutdown the registrary and unregister any textures. + */ +void fl_texture_registrar_shutdown(FlTextureRegistrar* registrar); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_PRIVATE_H_ diff --git a/engine/src/flutter/shell/platform/linux/fl_view.cc b/engine/src/flutter/shell/platform/linux/fl_view.cc index 854749296b..150de42b88 100644 --- a/engine/src/flutter/shell/platform/linux/fl_view.cc +++ b/engine/src/flutter/shell/platform/linux/fl_view.cc @@ -550,13 +550,8 @@ static gboolean window_state_event_cb(FlView* self, GdkEvent* event) { } static GdkGLContext* create_context_cb(FlView* self) { - self->renderer = - fl_renderer_gdk_new(gtk_widget_get_parent_window(GTK_WIDGET(self))); - self->engine = fl_engine_new(self->project, FL_RENDERER(self->renderer)); - fl_engine_set_update_semantics_handler(self->engine, update_semantics_cb, - self, nullptr); - fl_engine_set_on_pre_engine_restart_handler( - self->engine, on_pre_engine_restart_cb, self, nullptr); + fl_renderer_gdk_set_window(self->renderer, + gtk_widget_get_parent_window(GTK_WIDGET(self))); // Must initialize the keymap before the keyboard. self->keymap = gdk_keymap_get_for_display(gdk_display_get_default()); @@ -654,6 +649,17 @@ static void size_allocate_cb(FlView* self) { handle_geometry_changed(self); } +static void fl_view_constructed(GObject* object) { + FlView* self = FL_VIEW(object); + + self->renderer = fl_renderer_gdk_new(); + self->engine = fl_engine_new(self->project, FL_RENDERER(self->renderer)); + fl_engine_set_update_semantics_handler(self->engine, update_semantics_cb, + self, nullptr); + fl_engine_set_on_pre_engine_restart_handler( + self->engine, on_pre_engine_restart_cb, self, nullptr); +} + static void fl_view_set_property(GObject* object, guint prop_id, const GValue* value, @@ -750,6 +756,7 @@ static gboolean fl_view_key_release_event(GtkWidget* widget, static void fl_view_class_init(FlViewClass* klass) { GObjectClass* object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fl_view_constructed; object_class->set_property = fl_view_set_property; object_class->get_property = fl_view_get_property; object_class->notify = fl_view_notify; diff --git a/engine/src/flutter/shell/platform/linux/fl_view_test.cc b/engine/src/flutter/shell/platform/linux/fl_view_test.cc index c36323cb04..db87bf8b2f 100644 --- a/engine/src/flutter/shell/platform/linux/fl_view_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_view_test.cc @@ -7,6 +7,19 @@ #include "gtest/gtest.h" +TEST(FlViewTest, GetEngine) { + flutter::testing::fl_ensure_gtk_init(); + g_autoptr(FlDartProject) project = fl_dart_project_new(); + g_autoptr(FlView) view = fl_view_new(project); + + // Check the engine is immediately available (i.e. before the widget is + // realized). + FlEngine* engine = fl_view_get_engine(view); + EXPECT_NE(engine, nullptr); + + g_object_ref_sink(view); +} + TEST(FlViewTest, StateUpdateDoesNotHappenInInit) { flutter::testing::fl_ensure_gtk_init(); g_autoptr(FlDartProject) project = fl_dart_project_new(); diff --git a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h index 4ac81862ed..bdff02d40d 100644 --- a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h +++ b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h @@ -104,6 +104,8 @@ struct _FlBinaryMessengerInterface { void (*set_warns_on_channel_overflow)(FlBinaryMessenger* messenger, const gchar* channel, bool warns); + + void (*shutdown)(FlBinaryMessenger* messenger); }; struct _FlBinaryMessengerResponseHandleClass { diff --git a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h index 0034d345e9..291f1644be 100644 --- a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h +++ b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_texture_registrar.h @@ -37,6 +37,8 @@ struct _FlTextureRegistrarInterface { gboolean (*unregister_texture)(FlTextureRegistrar* registrar, FlTexture* texture); + + void (*shutdown)(FlTextureRegistrar* registrar); }; /** diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_binary_messenger.cc b/engine/src/flutter/shell/platform/linux/testing/mock_binary_messenger.cc index ac1b05af33..8b22f2291b 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_binary_messenger.cc +++ b/engine/src/flutter/shell/platform/linux/testing/mock_binary_messenger.cc @@ -141,6 +141,8 @@ static void fl_mock_binary_messenger_set_warns_on_channel_overflow( channel, warns); } +static void fl_mock_binary_messenger_shutdown(FlBinaryMessenger* messenger) {} + static void fl_mock_binary_messenger_iface_init( FlBinaryMessengerInterface* iface) { iface->set_message_handler_on_channel = @@ -152,6 +154,7 @@ static void fl_mock_binary_messenger_iface_init( iface->resize_channel = fl_mock_binary_messenger_resize_channel; iface->set_warns_on_channel_overflow = fl_mock_binary_messenger_set_warns_on_channel_overflow; + iface->shutdown = fl_mock_binary_messenger_shutdown; } static void fl_mock_binary_messenger_init(FlMockBinaryMessenger* self) {}