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);
}
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);
}

View File

@ -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_

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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:

View File

@ -11,7 +11,7 @@ static constexpr int kMillisecondsPerMicrosecond = 1000;
struct _FlTaskRunner {
GObject parent_instance;
FlEngine* engine;
GWeakRef engine;
GMutex mutex;
GCond cond;
@ -51,12 +51,15 @@ static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner* self) {
g_mutex_unlock(&self->mutex);
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine != nullptr) {
l = expired_tasks;
while (l != nullptr && self->engine) {
while (l != nullptr) {
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;
}
}
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,

View File

@ -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<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);
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);
}

View File

@ -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_

View File

@ -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;

View File

@ -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();

View File

@ -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 {

View File

@ -37,6 +37,8 @@ struct _FlTextureRegistrarInterface {
gboolean (*unregister_texture)(FlTextureRegistrar* registrar,
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);
}
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) {}