Restore creation of engine before Linux widget is realized. (flutter/engine#53604)

Due to changes in the renderer in fc560d4 the engine was created once a
widget is realized, not when the widget is created. If a Flutter
application changed the default my_application.cc template to show the
Flutter widget after plugins are run then these plugins would not be
able to access the engine.

Solved by removing the GdkWindow from the renderer constructor and
setting in later when the widget is realized. This works because the
renderer is not used until the widget is realized.
    
Fixes https://github.com/flutter/flutter/issues/144873
This commit is contained in:
Robert Ancell 2024-07-02 17:18:07 +12:00 committed by GitHub
parent 8998424bbc
commit e3b2d11dba
14 changed files with 139 additions and 81 deletions

View File

@ -149,18 +149,6 @@ static void platform_message_handler_free(gpointer data) {
g_free(self); 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( static gboolean fl_binary_messenger_platform_message_cb(
FlEngine* engine, FlEngine* engine,
const gchar* channel, const gchar* channel,
@ -187,13 +175,6 @@ static gboolean fl_binary_messenger_platform_message_cb(
static void fl_binary_messenger_impl_dispose(GObject* object) { static void fl_binary_messenger_impl_dispose(GObject* object) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(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_weak_ref_clear(&self->engine);
g_clear_pointer(&self->platform_message_handlers, g_hash_table_unref); 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); 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( static void fl_binary_messenger_impl_class_init(
FlBinaryMessengerImplClass* klass) { FlBinaryMessengerImplClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_binary_messenger_impl_dispose; 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->send_on_channel_finish = send_on_channel_finish;
iface->resize_channel = resize_channel; iface->resize_channel = resize_channel;
iface->set_warns_on_channel_overflow = set_warns_on_channel_overflow; iface->set_warns_on_channel_overflow = set_warns_on_channel_overflow;
iface->shutdown = shutdown;
} }
static void fl_binary_messenger_impl_init(FlBinaryMessengerImpl* self) { 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); FL_IS_BINARY_MESSENGER_IMPL(self);
g_weak_ref_init(&self->engine, G_OBJECT(engine)); 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( fl_engine_set_platform_message_handler(
engine, fl_binary_messenger_platform_message_cb, self, NULL); 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( return FL_BINARY_MESSENGER_GET_IFACE(self)->set_warns_on_channel_overflow(
self, channel, warns); 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);
}

View File

@ -22,6 +22,14 @@ G_BEGIN_DECLS
*/ */
FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine); 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 G_END_DECLS
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_BINARY_MESSENGER_PRIVATE_H_ #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_BINARY_MESSENGER_PRIVATE_H_

View File

@ -634,7 +634,7 @@ static void kill_handler_notify_cb(gpointer was_called) {
TEST(FlBinaryMessengerTest, DeletingEngineClearsHandlers) { TEST(FlBinaryMessengerTest, DeletingEngineClearsHandlers) {
FlEngine* engine = make_mock_engine(); 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; gboolean was_killed = FALSE;
// Listen for messages from the engine. // Listen for messages from the engine.

View File

@ -399,6 +399,9 @@ static void fl_engine_dispose(GObject* object) {
self->aot_data = nullptr; 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->project);
g_clear_object(&self->renderer); g_clear_object(&self->renderer);
g_clear_object(&self->texture_registrar); g_clear_object(&self->texture_registrar);

View File

@ -39,6 +39,7 @@ static void fl_renderer_gdk_clear_current(FlRenderer* renderer) {
gdk_gl_context_clear_current(); gdk_gl_context_clear_current();
} }
// Implements FlRenderer::get_refresh_rate.
static gdouble fl_renderer_gdk_get_refresh_rate(FlRenderer* renderer) { static gdouble fl_renderer_gdk_get_refresh_rate(FlRenderer* renderer) {
FlRendererGdk* self = FL_RENDERER_GDK(renderer); FlRendererGdk* self = FL_RENDERER_GDK(renderer);
GdkDisplay* display = gdk_window_get_display(self->window); 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) {} static void fl_renderer_gdk_init(FlRendererGdk* self) {}
FlRendererGdk* fl_renderer_gdk_new(GdkWindow* window) { FlRendererGdk* fl_renderer_gdk_new() {
FlRendererGdk* self = FlRendererGdk* self =
FL_RENDERER_GDK(g_object_new(fl_renderer_gdk_get_type(), nullptr)); FL_RENDERER_GDK(g_object_new(fl_renderer_gdk_get_type(), nullptr));
self->window = window;
return self; 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) { 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); self->gdk_context = gdk_window_create_gl_context(self->window, error);
if (self->gdk_context == nullptr) { if (self->gdk_context == nullptr) {
return FALSE; return FALSE;

View File

@ -23,13 +23,22 @@ G_DECLARE_FINAL_TYPE(FlRendererGdk,
/** /**
* fl_renderer_gdk_new: * fl_renderer_gdk_new:
* @window: the window that is being rendered on.
* *
* Creates an object that allows Flutter to render by OpenGL ES. * Creates an object that allows Flutter to render by OpenGL ES.
* *
* Returns: a new #FlRendererGdk. * 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: * fl_renderer_gdk_create_contexts:

View File

@ -11,7 +11,7 @@ static constexpr int kMillisecondsPerMicrosecond = 1000;
struct _FlTaskRunner { struct _FlTaskRunner {
GObject parent_instance; GObject parent_instance;
FlEngine* engine; GWeakRef engine;
GMutex mutex; GMutex mutex;
GCond cond; GCond cond;
@ -51,12 +51,15 @@ static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner* self) {
g_mutex_unlock(&self->mutex); g_mutex_unlock(&self->mutex);
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine != nullptr) {
l = expired_tasks; l = expired_tasks;
while (l != nullptr && self->engine) { while (l != nullptr) {
FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data); FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
fl_engine_execute_task(self->engine, &task->task); fl_engine_execute_task(engine, &task->task);
l = l->next; l = l->next;
} }
}
g_list_free_full(expired_tasks, g_free); 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) { void fl_task_runner_dispose(GObject* object) {
FlTaskRunner* self = FL_TASK_RUNNER(object); FlTaskRunner* self = FL_TASK_RUNNER(object);
@ -133,11 +130,7 @@ void fl_task_runner_dispose(GObject* object) {
// main thread // main thread
g_assert(!self->blocking_main_thread); g_assert(!self->blocking_main_thread);
if (self->engine != nullptr) { g_weak_ref_clear(&self->engine);
g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self);
self->engine = nullptr;
}
g_mutex_clear(&self->mutex); g_mutex_clear(&self->mutex);
g_cond_clear(&self->cond); 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* fl_task_runner_new(FlEngine* engine) {
FlTaskRunner* res = FlTaskRunner* self =
FL_TASK_RUNNER(g_object_new(fl_task_runner_get_type(), nullptr)); FL_TASK_RUNNER(g_object_new(fl_task_runner_get_type(), nullptr));
res->engine = engine; g_weak_ref_init(&self->engine, G_OBJECT(engine));
g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, res); return self;
return res;
} }
void fl_task_runner_post_task(FlTaskRunner* self, void fl_task_runner_post_task(FlTaskRunner* self,

View File

@ -23,7 +23,7 @@ struct _FlTextureRegistrarImpl {
GObject parent_instance; GObject parent_instance;
// Weak reference to the engine this texture registrar is created for. // Weak reference to the engine this texture registrar is created for.
FlEngine* engine; GWeakRef engine;
// ID to assign to the next new texture. // ID to assign to the next new texture.
int64_t next_id; int64_t next_id;
@ -54,20 +54,6 @@ G_DEFINE_TYPE_WITH_CODE(
static void fl_texture_registrar_default_init( static void fl_texture_registrar_default_init(
FlTextureRegistrarInterface* iface) {} 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) { static void fl_texture_registrar_impl_dispose(GObject* object) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(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_clear_pointer(&self->textures, g_hash_table_unref);
g_mutex_unlock(&self->textures_mutex); g_mutex_unlock(&self->textures_mutex);
if (self->engine != nullptr) { g_weak_ref_clear(&self->engine);
g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self);
self->engine = nullptr;
}
g_mutex_clear(&self->textures_mutex); g_mutex_clear(&self->textures_mutex);
G_OBJECT_CLASS(fl_texture_registrar_impl_parent_class)->dispose(object); 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); FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar);
if (FL_IS_TEXTURE_GL(texture) || FL_IS_PIXEL_BUFFER_TEXTURE(texture)) { 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; return FALSE;
} }
@ -104,7 +88,7 @@ static gboolean register_texture(FlTextureRegistrar* registrar,
// https://github.com/flutter/flutter/issues/124009 int64_t id = // https://github.com/flutter/flutter/issues/124009 int64_t id =
// self->next_id++; // self->next_id++;
int64_t id = reinterpret_cast<int64_t>(texture); int64_t id = reinterpret_cast<int64_t>(texture);
if (fl_engine_register_external_texture(self->engine, id)) { if (fl_engine_register_external_texture(engine, id)) {
fl_texture_set_id(texture, id); fl_texture_set_id(texture, id);
g_mutex_lock(&self->textures_mutex); g_mutex_lock(&self->textures_mutex);
g_hash_table_insert(self->textures, GINT_TO_POINTER(id), g_hash_table_insert(self->textures, GINT_TO_POINTER(id),
@ -134,11 +118,12 @@ static gboolean mark_texture_frame_available(FlTextureRegistrar* registrar,
FlTexture* texture) { FlTexture* texture) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar); 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 FALSE;
} }
return fl_engine_mark_texture_frame_available(self->engine, return fl_engine_mark_texture_frame_available(engine,
fl_texture_get_id(texture)); fl_texture_get_id(texture));
} }
@ -146,12 +131,13 @@ static gboolean unregister_texture(FlTextureRegistrar* registrar,
FlTexture* texture) { FlTexture* texture) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(registrar); 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 FALSE;
} }
gboolean result = fl_engine_unregister_external_texture( gboolean result =
self->engine, fl_texture_get_id(texture)); fl_engine_unregister_external_texture(engine, fl_texture_get_id(texture));
g_mutex_lock(&self->textures_mutex); g_mutex_lock(&self->textures_mutex);
if (!g_hash_table_remove(self->textures, if (!g_hash_table_remove(self->textures,
@ -163,12 +149,25 @@ static gboolean unregister_texture(FlTextureRegistrar* registrar,
return result; 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( static void fl_texture_registrar_impl_iface_init(
FlTextureRegistrarInterface* iface) { FlTextureRegistrarInterface* iface) {
iface->register_texture = register_texture; iface->register_texture = register_texture;
iface->lookup_texture = lookup_texture; iface->lookup_texture = lookup_texture;
iface->mark_texture_frame_available = mark_texture_frame_available; iface->mark_texture_frame_available = mark_texture_frame_available;
iface->unregister_texture = unregister_texture; iface->unregister_texture = unregister_texture;
iface->shutdown = shutdown;
} }
static void fl_texture_registrar_impl_init(FlTextureRegistrarImpl* self) { static void fl_texture_registrar_impl_init(FlTextureRegistrarImpl* self) {
@ -213,6 +212,12 @@ G_MODULE_EXPORT gboolean fl_texture_registrar_unregister_texture(
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) { FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine) {
FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL( FlTextureRegistrarImpl* self = FL_TEXTURE_REGISTRAR_IMPL(
g_object_new(fl_texture_registrar_impl_get_type(), nullptr)); 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. // Added to stop compiler complaining about an unused function.
FL_IS_TEXTURE_REGISTRAR_IMPL(self); FL_IS_TEXTURE_REGISTRAR_IMPL(self);
self->engine = engine; g_weak_ref_init(&self->engine, G_OBJECT(engine));
g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self);
return FL_TEXTURE_REGISTRAR(self); return FL_TEXTURE_REGISTRAR(self);
} }

View File

@ -33,6 +33,14 @@ FlTextureRegistrar* fl_texture_registrar_new(FlEngine* engine);
FlTexture* fl_texture_registrar_lookup_texture(FlTextureRegistrar* registrar, FlTexture* fl_texture_registrar_lookup_texture(FlTextureRegistrar* registrar,
int64_t texture_id); 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 G_END_DECLS
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_PRIVATE_H_ #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXTURE_REGISTRAR_PRIVATE_H_

View File

@ -550,13 +550,8 @@ static gboolean window_state_event_cb(FlView* self, GdkEvent* event) {
} }
static GdkGLContext* create_context_cb(FlView* self) { static GdkGLContext* create_context_cb(FlView* self) {
self->renderer = fl_renderer_gdk_set_window(self->renderer,
fl_renderer_gdk_new(gtk_widget_get_parent_window(GTK_WIDGET(self))); 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);
// Must initialize the keymap before the keyboard. // Must initialize the keymap before the keyboard.
self->keymap = gdk_keymap_get_for_display(gdk_display_get_default()); 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); 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, static void fl_view_set_property(GObject* object,
guint prop_id, guint prop_id,
const GValue* value, const GValue* value,
@ -750,6 +756,7 @@ static gboolean fl_view_key_release_event(GtkWidget* widget,
static void fl_view_class_init(FlViewClass* klass) { static void fl_view_class_init(FlViewClass* klass) {
GObjectClass* object_class = G_OBJECT_CLASS(klass); GObjectClass* object_class = G_OBJECT_CLASS(klass);
object_class->constructed = fl_view_constructed;
object_class->set_property = fl_view_set_property; object_class->set_property = fl_view_set_property;
object_class->get_property = fl_view_get_property; object_class->get_property = fl_view_get_property;
object_class->notify = fl_view_notify; object_class->notify = fl_view_notify;

View File

@ -7,6 +7,19 @@
#include "gtest/gtest.h" #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) { TEST(FlViewTest, StateUpdateDoesNotHappenInInit) {
flutter::testing::fl_ensure_gtk_init(); flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new(); g_autoptr(FlDartProject) project = fl_dart_project_new();

View File

@ -104,6 +104,8 @@ struct _FlBinaryMessengerInterface {
void (*set_warns_on_channel_overflow)(FlBinaryMessenger* messenger, void (*set_warns_on_channel_overflow)(FlBinaryMessenger* messenger,
const gchar* channel, const gchar* channel,
bool warns); bool warns);
void (*shutdown)(FlBinaryMessenger* messenger);
}; };
struct _FlBinaryMessengerResponseHandleClass { struct _FlBinaryMessengerResponseHandleClass {

View File

@ -37,6 +37,8 @@ struct _FlTextureRegistrarInterface {
gboolean (*unregister_texture)(FlTextureRegistrar* registrar, gboolean (*unregister_texture)(FlTextureRegistrar* registrar,
FlTexture* texture); FlTexture* texture);
void (*shutdown)(FlTextureRegistrar* registrar);
}; };
/** /**

View File

@ -141,6 +141,8 @@ static void fl_mock_binary_messenger_set_warns_on_channel_overflow(
channel, warns); channel, warns);
} }
static void fl_mock_binary_messenger_shutdown(FlBinaryMessenger* messenger) {}
static void fl_mock_binary_messenger_iface_init( static void fl_mock_binary_messenger_iface_init(
FlBinaryMessengerInterface* iface) { FlBinaryMessengerInterface* iface) {
iface->set_message_handler_on_channel = 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->resize_channel = fl_mock_binary_messenger_resize_channel;
iface->set_warns_on_channel_overflow = iface->set_warns_on_channel_overflow =
fl_mock_binary_messenger_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) {} static void fl_mock_binary_messenger_init(FlMockBinaryMessenger* self) {}