From 12012f135c833fef5e8480e7514737fc52f2cdae Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Fri, 24 Apr 2020 10:30:41 +1200 Subject: [PATCH] Refactor FlutterEngine usage in Linux shell (#17363) --- ci/licenses_golden/licenses_flutter | 7 + shell/platform/linux/BUILD.gn | 3 + shell/platform/linux/fl_engine.cc | 159 +++++++++++++ shell/platform/linux/fl_engine_private.h | 85 +++++++ shell/platform/linux/fl_renderer.cc | 139 +++++++++++ shell/platform/linux/fl_renderer.h | 114 +++++++++ shell/platform/linux/fl_renderer_x11.cc | 33 +++ shell/platform/linux/fl_renderer_x11.h | 39 ++++ shell/platform/linux/fl_view.cc | 219 +++--------------- .../linux/public/flutter_linux/fl_engine.h | 28 +++ .../linux/public/flutter_linux/fl_view.h | 11 + .../public/flutter_linux/flutter_linux.h | 1 + 12 files changed, 656 insertions(+), 182 deletions(-) create mode 100644 shell/platform/linux/fl_engine.cc create mode 100644 shell/platform/linux/fl_engine_private.h create mode 100644 shell/platform/linux/fl_renderer.cc create mode 100644 shell/platform/linux/fl_renderer.h create mode 100644 shell/platform/linux/fl_renderer_x11.cc create mode 100644 shell/platform/linux/fl_renderer_x11.h create mode 100644 shell/platform/linux/public/flutter_linux/fl_engine.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index cf0076fa9..762f60786 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1162,8 +1162,15 @@ FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_dart_project.cc +FILE: ../../../flutter/shell/platform/linux/fl_engine.cc +FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h +FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc +FILE: ../../../flutter/shell/platform/linux/fl_renderer.h +FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.cc +FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.h FILE: ../../../flutter/shell/platform/linux/fl_view.cc FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_engine.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/flutter_linux.h FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.cc diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index d5ac69df7..73da99327 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -57,6 +57,9 @@ source_set("flutter_linux") { sources = [ "fl_dart_project.cc", + "fl_engine.cc", + "fl_renderer.cc", + "fl_renderer_x11.cc", "fl_view.cc", ] diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc new file mode 100644 index 000000000..7d080340d --- /dev/null +++ b/shell/platform/linux/fl_engine.cc @@ -0,0 +1,159 @@ +// 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_engine.h" +#include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/fl_renderer.h" + +#include + +struct _FlEngine { + GObject parent_instance; + + FlDartProject* project; + FlRenderer* renderer; + FLUTTER_API_SYMBOL(FlutterEngine) engine; +}; + +G_DEFINE_QUARK(fl_engine_error_quark, fl_engine_error) + +G_DEFINE_TYPE(FlEngine, fl_engine, G_TYPE_OBJECT) + +// Callback from Flutter engine that are passed to the renderer + +static void* fl_engine_gl_proc_resolver(void* user_data, const char* name) { + FlEngine* self = static_cast(user_data); + return fl_renderer_get_proc_address(self->renderer, name); +} + +static bool fl_engine_gl_make_current(void* user_data) { + FlEngine* self = static_cast(user_data); + g_autoptr(GError) error = nullptr; + gboolean result = fl_renderer_make_current(self->renderer, &error); + if (!result) + g_warning("%s", error->message); + return result; +} + +static bool fl_engine_gl_clear_current(void* user_data) { + FlEngine* self = static_cast(user_data); + g_autoptr(GError) error = nullptr; + gboolean result = fl_renderer_clear_current(self->renderer, &error); + if (!result) + g_warning("%s", error->message); + return result; +} + +static uint32_t fl_engine_gl_fbo_callback(void* user_data) { + FlEngine* self = static_cast(user_data); + return fl_renderer_get_fbo(self->renderer); +} + +static bool fl_engine_gl_present(void* user_data) { + FlEngine* self = static_cast(user_data); + g_autoptr(GError) error = nullptr; + gboolean result = fl_renderer_present(self->renderer, &error); + if (!result) + g_warning("%s", error->message); + return result; +} + +static void fl_engine_dispose(GObject* object) { + FlEngine* self = FL_ENGINE(object); + + g_clear_object(&self->project); + g_clear_object(&self->renderer); + + FlutterEngineShutdown(self->engine); + + G_OBJECT_CLASS(fl_engine_parent_class)->dispose(object); +} + +static void fl_engine_class_init(FlEngineClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_engine_dispose; +} + +static void fl_engine_init(FlEngine* self) {} + +FlEngine* fl_engine_new(FlDartProject* project, FlRenderer* renderer) { + g_return_val_if_fail(FL_IS_DART_PROJECT(project), nullptr); + g_return_val_if_fail(FL_IS_RENDERER(renderer), nullptr); + + FlEngine* self = + static_cast(g_object_new(fl_engine_get_type(), nullptr)); + self->project = static_cast(g_object_ref(project)); + self->renderer = static_cast(g_object_ref(renderer)); + return self; +} + +gboolean fl_engine_start(FlEngine* self, GError** error) { + g_return_val_if_fail(FL_IS_ENGINE(self), FALSE); + + if (!fl_renderer_start(self->renderer, error)) + return FALSE; + + FlutterRendererConfig config = {}; + config.type = kOpenGL; + config.open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig); + config.open_gl.gl_proc_resolver = fl_engine_gl_proc_resolver; + config.open_gl.make_current = fl_engine_gl_make_current; + config.open_gl.clear_current = fl_engine_gl_clear_current; + config.open_gl.fbo_callback = fl_engine_gl_fbo_callback; + config.open_gl.present = fl_engine_gl_present; + + FlutterProjectArgs args = {}; + args.struct_size = sizeof(FlutterProjectArgs); + args.assets_path = fl_dart_project_get_assets_path(self->project); + args.icu_data_path = fl_dart_project_get_icu_data_path(self->project); + + FlutterEngineResult result = FlutterEngineInitialize( + FLUTTER_ENGINE_VERSION, &config, &args, self, &self->engine); + if (result != kSuccess) { + g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED, + "Failed to initialize Flutter engine"); + return FALSE; + } + + result = FlutterEngineRunInitialized(self->engine); + if (result != kSuccess) { + g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED, + "Failed to run Flutter engine"); + return FALSE; + } + + return TRUE; +} + +void fl_engine_send_window_metrics_event(FlEngine* self, + size_t width, + size_t height, + double pixel_ratio) { + g_return_if_fail(FL_IS_ENGINE(self)); + + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(FlutterWindowMetricsEvent); + event.width = width; + event.height = height; + event.pixel_ratio = pixel_ratio; + FlutterEngineSendWindowMetricsEvent(self->engine, &event); +} + +void fl_engine_send_mouse_pointer_event(FlEngine* self, + FlutterPointerPhase phase, + size_t timestamp, + double x, + double y, + int64_t buttons) { + g_return_if_fail(FL_IS_ENGINE(self)); + + FlutterPointerEvent fl_event = {}; + fl_event.struct_size = sizeof(fl_event); + fl_event.phase = phase; + fl_event.timestamp = timestamp; + fl_event.x = x; + fl_event.y = y; + fl_event.device_kind = kFlutterPointerDeviceKindMouse; + fl_event.buttons = buttons; + FlutterEngineSendPointerEvent(self->engine, &fl_event, 1); +} diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h new file mode 100644 index 000000000..32e4cc61e --- /dev/null +++ b/shell/platform/linux/fl_engine_private.h @@ -0,0 +1,85 @@ +// 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_ENGINE_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_ + +#include + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/fl_renderer.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" + +G_BEGIN_DECLS + +/** + * FlEngineError: + * Errors for #FlEngine objects to set on failures. + */ + +typedef enum { + FL_ENGINE_ERROR_FAILED, +} FlEngineError; + +GQuark fl_engine_error_quark(void) G_GNUC_CONST; + +/** + * fl_engine_new: + * @project: a #FlDartProject + * @renderer: a #FlRenderer + * + * Creates a new Flutter engine. + * + * Returns: a #FlEngine + */ +FlEngine* fl_engine_new(FlDartProject* project, FlRenderer* renderer); + +/** + * fl_engine_start: + * @engine: a #FlEngine + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Starts the Flutter engine. + * + * Returns: %TRUE on success + */ +gboolean fl_engine_start(FlEngine* engine, GError** error); + +/** + * fl_engine_send_window_metrics_event: + * @engine: a #FlEngine + * @width: width of the window in pixels. + * @height: height of the window in pixels. + * @pixel_ratio: scale factor for window. + * + * Sends a window metrics event to the engine. + */ +void fl_engine_send_window_metrics_event(FlEngine* engine, + size_t width, + size_t height, + double pixel_ratio); + +/** + * fl_engine_send_mouse_pointer_event: + * @engine: a #FlEngine + * @phase: mouse phase. + * @timestamp: time when event occurred in nanoseconds. + * @x: x location of mouse cursor. + * @y: y location of mouse cursor. + * @buttons: buttons that are pressed. + * + * Sends a mouse pointer event to the engine. + */ +void fl_engine_send_mouse_pointer_event(FlEngine* engine, + FlutterPointerPhase phase, + size_t timestamp, + double x, + double y, + int64_t buttons); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_ diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc new file mode 100644 index 000000000..c5a07be73 --- /dev/null +++ b/shell/platform/linux/fl_renderer.cc @@ -0,0 +1,139 @@ +// 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 "fl_renderer.h" + +#include "flutter/shell/platform/embedder/embedder.h" + +G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error) + +typedef struct { + EGLDisplay egl_display; + EGLSurface egl_surface; + EGLContext egl_context; +} FlRendererPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT) + +// Default implementation for the start virtual method. +// Provided so subclasses can chain up to here. +static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { + FlRendererPrivate* priv = + static_cast(fl_renderer_get_instance_private(self)); + + // Note the use of EGL_DEFAULT_DISPLAY rather than sharing an existing display + // connection (e.g. an X11 connection from GTK). This is because this EGL + // display is going to be accessed by a thread from Flutter. In the case + // of GTK/X11 the display connection is not thread safe and this would cause + // a crash. + // + priv->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + if (!eglInitialize(priv->egl_display, nullptr, nullptr)) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to initialze EGL"); + return FALSE; + } + + EGLint attributes[] = {EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, + EGL_NONE}; + EGLConfig egl_config; + EGLint n_config; + if (!eglChooseConfig(priv->egl_display, attributes, &egl_config, 1, + &n_config)) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to choose EGL config"); + return FALSE; + } + if (n_config == 0) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to find appropriate EGL config"); + return FALSE; + } + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to bind EGL OpenGL ES API"); + return FALSE; + } + + priv->egl_surface = FL_RENDERER_GET_CLASS(self)->create_surface( + self, priv->egl_display, egl_config); + EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + priv->egl_context = eglCreateContext(priv->egl_display, egl_config, + EGL_NO_CONTEXT, context_attributes); + EGLint value; + eglQueryContext(priv->egl_display, priv->egl_context, + EGL_CONTEXT_CLIENT_VERSION, &value); + + return TRUE; +} + +static void fl_renderer_class_init(FlRendererClass* klass) { + klass->start = fl_renderer_real_start; +} + +static void fl_renderer_init(FlRenderer* self) {} + +gboolean fl_renderer_start(FlRenderer* self, GError** error) { + return FL_RENDERER_GET_CLASS(self)->start(self, error); +} + +void* fl_renderer_get_proc_address(FlRenderer* self, const char* name) { + return reinterpret_cast(eglGetProcAddress(name)); +} + +gboolean fl_renderer_make_current(FlRenderer* self, GError** error) { + FlRendererPrivate* priv = + static_cast(fl_renderer_get_instance_private(self)); + + if (!eglMakeCurrent(priv->egl_display, priv->egl_surface, priv->egl_surface, + priv->egl_context)) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to make EGL context current"); + return FALSE; + } + + return TRUE; +} + +gboolean fl_renderer_clear_current(FlRenderer* self, GError** error) { + FlRendererPrivate* priv = + static_cast(fl_renderer_get_instance_private(self)); + + if (!eglMakeCurrent(priv->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to clear EGL context"); + return FALSE; + } + + return TRUE; +} + +guint32 fl_renderer_get_fbo(FlRenderer* self) { + // There is only one frame buffer object - always return that + return 0; +} + +gboolean fl_renderer_present(FlRenderer* self, GError** error) { + FlRendererPrivate* priv = + static_cast(fl_renderer_get_instance_private(self)); + + if (!eglSwapBuffers(priv->egl_display, priv->egl_surface)) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to swap EGL buffers"); + return FALSE; + } + + return TRUE; +} diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h new file mode 100644 index 000000000..2547736bb --- /dev/null +++ b/shell/platform/linux/fl_renderer.h @@ -0,0 +1,114 @@ +// 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_RENDERER_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_ + +#include + +#include + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" + +G_BEGIN_DECLS + +/** + * FlRendererError: + * Errors for #FlRenderer objects to set on failures. + */ + +typedef enum { + FL_RENDERER_ERROR_FAILED, +} FlRendererError; + +GQuark fl_renderer_error_quark(void) G_GNUC_CONST; + +G_DECLARE_DERIVABLE_TYPE(FlRenderer, fl_renderer, FL, RENDERER, GObject) + +/** + * FlRenderer: + * + * #FlRenderer is an abstract class that allows Flutter to draw pixels. + */ + +struct _FlRendererClass { + GObjectClass parent_class; + + // Virtual methods + gboolean (*start)(FlRenderer* renderer, GError** error); + EGLSurface (*create_surface)(FlRenderer* renderer, + EGLDisplay display, + EGLConfig config); +}; + +G_END_DECLS + +/** + * fl_renderer_start: + * @renderer: a #FlRenderer + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Returns: %TRUE if successfully started. + */ +gboolean fl_renderer_start(FlRenderer* self, GError** error); + +/** + * fl_renderer_get_proc_address: + * @renderer: a #FlRenderer + * @name: a function name + * + * Gets the rendering API function that matches the given name. + * + * Returns: a function pointer + */ +void* fl_renderer_get_proc_address(FlRenderer* renderer, const char* name); + +/** + * fl_renderer_make_current: + * @renderer: a #FlRenderer + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Makes the rendering context current. + * + * Returns %TRUE if successful + */ +gboolean fl_renderer_make_current(FlRenderer* renderer, GError** error); + +/** + * fl_renderer_clear_current: + * @renderer: a #FlRenderer + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Clears the current rendering context. + * + * Returns %TRUE if successful + */ +gboolean fl_renderer_clear_current(FlRenderer* renderer, GError** error); + +/** + * fl_renderer_get_fbo: + * @renderer: a #FlRenderer + * + * Gets the frame buffer object to render to. + * + * Returns: a frame buffer object index + */ +guint32 fl_renderer_get_fbo(FlRenderer* renderer); + +/** + * fl_renderer_present: + * @renderer: a #FlRenderer + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Presents the current frame. + * + * Returns %TRUE if successful + */ +gboolean fl_renderer_present(FlRenderer* renderer, GError** error); + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_ diff --git a/shell/platform/linux/fl_renderer_x11.cc b/shell/platform/linux/fl_renderer_x11.cc new file mode 100644 index 000000000..f885e74ef --- /dev/null +++ b/shell/platform/linux/fl_renderer_x11.cc @@ -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 "fl_renderer_x11.h" + +struct _FlRendererX11 { + FlRenderer parent_instance; + + Window xid; +}; + +G_DEFINE_TYPE(FlRendererX11, fl_renderer_x11, fl_renderer_get_type()) + +static EGLSurface fl_renderer_x11_create_surface(FlRenderer* renderer, + EGLDisplay display, + EGLConfig config) { + FlRendererX11* self = FL_RENDERER_X11(renderer); + return eglCreateWindowSurface(display, config, self->xid, nullptr); +} + +static void fl_renderer_x11_class_init(FlRendererX11Class* klass) { + FL_RENDERER_CLASS(klass)->create_surface = fl_renderer_x11_create_surface; +} + +static void fl_renderer_x11_init(FlRendererX11* self) {} + +FlRendererX11* fl_renderer_x11_new(Window xid) { + FlRendererX11* self = static_cast( + g_object_new(fl_renderer_x11_get_type(), nullptr)); + self->xid = xid; + return self; +} diff --git a/shell/platform/linux/fl_renderer_x11.h b/shell/platform/linux/fl_renderer_x11.h new file mode 100644 index 000000000..0aab6469a --- /dev/null +++ b/shell/platform/linux/fl_renderer_x11.h @@ -0,0 +1,39 @@ +// 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_RENDERER_X11_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_X11_H_ + +#include + +#include "flutter/shell/platform/linux/fl_renderer.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlRendererX11, + fl_renderer_x11, + FL, + RENDERER_X11, + FlRenderer) + +/** + * FlRendererX11: + * + * #FlRendererX11 is an implementation of a #FlRenderer that renders to X11 + * windows. + */ + +/** + * fl_renderer_x11_new: + * @xid: The X window to render to. + * + * Create an object that allows Flutter to render to X11 windows. + * + * Returns: a #FlRendererX11 + */ +FlRendererX11* fl_renderer_x11_new(Window xid); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_X11_H_ diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 229e5a215..07a1f9f7b 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -4,22 +4,19 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" -#include -#include -#include +#include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/fl_renderer_x11.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" + #include -#include "flutter/shell/platform/embedder/embedder.h" +#define NSEC_PER_MSEC 1000000 struct _FlView { GtkWidget parent_instance; - EGLDisplay egl_display; - EGLSurface egl_surface; - EGLContext egl_context; - - FlDartProject* flutter_project; - FLUTTER_API_SYMBOL(FlutterEngine) flutter_engine; + FlDartProject* project; + FlEngine* engine; int64_t button_state; }; @@ -27,139 +24,9 @@ enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST }; G_DEFINE_TYPE(FlView, fl_view, GTK_TYPE_WIDGET) -static gboolean initialize_egl(FlView* self) { - /* Note that we don't provide the XDisplay from GTK, this would make both - * GTK and EGL share the same X connection and this would crash when used by - * a Flutter thread. So the EGL display and GTK both have separate - * connections. - */ - self->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - - EGLint egl_major, egl_minor; - if (!eglInitialize(self->egl_display, &egl_major, &egl_minor)) { - g_warning("Failed to initialze EGL"); - return FALSE; - } - // TODO(robert-ancell): It would probably be useful to store the EGL version - // for debugging purposes - - EGLint attributes[] = {EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_ALPHA_SIZE, - 8, - EGL_NONE}; - EGLConfig egl_config; - EGLint n_config; - if (!eglChooseConfig(self->egl_display, attributes, &egl_config, 1, - &n_config)) { - g_warning("Failed to choose EGL config"); - return FALSE; - } - if (n_config == 0) { - g_warning("Failed to find appropriate EGL config"); - return FALSE; - } - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - g_warning("Failed to bind EGL OpenGL ES API"); - return FALSE; - } - - Window xid = gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(self))); - self->egl_surface = - eglCreateWindowSurface(self->egl_display, egl_config, xid, nullptr); - EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - self->egl_context = eglCreateContext(self->egl_display, egl_config, - EGL_NO_CONTEXT, context_attributes); - EGLint value; - eglQueryContext(self->egl_display, self->egl_context, - EGL_CONTEXT_CLIENT_VERSION, &value); - - return TRUE; -} - -static void* fl_view_gl_proc_resolver(void* user_data, const char* name) { - return reinterpret_cast(eglGetProcAddress(name)); -} - -static bool fl_view_gl_make_current(void* user_data) { - FlView* self = static_cast(user_data); - - if (!eglMakeCurrent(self->egl_display, self->egl_surface, self->egl_surface, - self->egl_context)) - g_warning("Failed to make EGL context current"); - - return true; -} - -static bool fl_view_gl_clear_current(void* user_data) { - FlView* self = static_cast(user_data); - - if (!eglMakeCurrent(self->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT)) - g_warning("Failed to make EGL context current"); - - return true; -} - -static uint32_t fl_view_gl_fbo_callback(void* user_data) { - /* There is only one frame buffer object - always return that */ - return 0; -} - -static bool fl_view_gl_present(void* user_data) { - FlView* self = static_cast(user_data); - - if (!eglSwapBuffers(self->egl_display, self->egl_surface)) - g_warning("Failed to swap EGL buffers"); - - return true; -} - -static gboolean run_flutter_engine(FlView* self) { - FlutterRendererConfig config = {}; - config.type = kOpenGL; - config.open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig); - config.open_gl.gl_proc_resolver = fl_view_gl_proc_resolver; - config.open_gl.make_current = fl_view_gl_make_current; - config.open_gl.clear_current = fl_view_gl_clear_current; - config.open_gl.fbo_callback = fl_view_gl_fbo_callback; - config.open_gl.present = fl_view_gl_present; - - g_autofree gchar* assets_path = - fl_dart_project_get_assets_path(self->flutter_project); - g_autofree gchar* icu_data_path = - fl_dart_project_get_icu_data_path(self->flutter_project); - - FlutterProjectArgs args = {}; - args.struct_size = sizeof(FlutterProjectArgs); - args.assets_path = assets_path; - args.icu_data_path = icu_data_path; - - FlutterEngineResult result = FlutterEngineInitialize( - FLUTTER_ENGINE_VERSION, &config, &args, self, &self->flutter_engine); - if (result != kSuccess) - return FALSE; - - result = FlutterEngineRunInitialized(self->flutter_engine); - if (result != kSuccess) - return FALSE; - - return TRUE; -} - -/* Convert a GDK button event into a Flutter event and send to the engine */ +// Convert a GDK button event into a Flutter event and send to the engine static gboolean fl_view_send_pointer_button_event(FlView* self, GdkEventButton* event) { - FlutterPointerEvent fl_event = {}; - fl_event.struct_size = sizeof(fl_event); - fl_event.timestamp = event->time * 1000; - int64_t button; switch (event->button) { case 1: @@ -175,31 +42,29 @@ static gboolean fl_view_send_pointer_button_event(FlView* self, return FALSE; } int old_button_state = self->button_state; + FlutterPointerPhase phase; if (event->type == GDK_BUTTON_PRESS) { // Drop the event if Flutter already thinks the button is down if ((self->button_state & button) != 0) return FALSE; self->button_state ^= button; - fl_event.phase = old_button_state == 0 ? kDown : kMove; + phase = old_button_state == 0 ? kDown : kMove; } else if (event->type == GDK_BUTTON_RELEASE) { // Drop the event if Flutter already thinks the button is up if ((self->button_state & button) == 0) return FALSE; self->button_state ^= button; - fl_event.phase = self->button_state == 0 ? kUp : kMove; + phase = self->button_state == 0 ? kUp : kMove; } - if (self->flutter_engine == nullptr) + if (self->engine == nullptr) return FALSE; - fl_event.x = event->x; - fl_event.y = event->y; - fl_event.device_kind = kFlutterPointerDeviceKindMouse; - fl_event.buttons = self->button_state; - FlutterEngineSendPointerEvent(self->flutter_engine, &fl_event, 1); - + fl_engine_send_mouse_pointer_event(self->engine, phase, + event->time * NSEC_PER_MSEC, event->x, + event->y, self->button_state); return TRUE; } @@ -211,7 +76,7 @@ static void fl_view_set_property(GObject* object, switch (prop_id) { case PROP_FLUTTER_PROJECT: - g_set_object(&self->flutter_project, + g_set_object(&self->project, static_cast(g_value_get_object(value))); break; default: @@ -228,7 +93,7 @@ static void fl_view_get_property(GObject* object, switch (prop_id) { case PROP_FLUTTER_PROJECT: - g_value_set_object(value, self->flutter_project); + g_value_set_object(value, self->project); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -239,17 +104,8 @@ static void fl_view_get_property(GObject* object, static void fl_view_dispose(GObject* object) { FlView* self = FL_VIEW(object); - FlutterEngineDeinitialize(self->flutter_engine); - FlutterEngineShutdown(self->flutter_engine); - - if (!eglDestroyContext(self->egl_display, self->egl_context)) - g_warning("Failed to destroy EGL context"); - if (!eglDestroySurface(self->egl_display, self->egl_surface)) - g_warning("Failed to destroy EGL surface"); - if (!eglTerminate(self->egl_display)) - g_warning("Failed to terminate EGL display"); - - g_clear_object(&self->flutter_project); + g_clear_object(&self->project); + g_clear_object(&self->engine); G_OBJECT_CLASS(fl_view_parent_class)->dispose(object); } @@ -282,8 +138,12 @@ static void fl_view_realize(GtkWidget* widget) { gtk_widget_register_window(widget, window); gtk_widget_set_window(widget, window); - if (initialize_egl(self)) - run_flutter_engine(self); + Window xid = gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(self))); + g_autoptr(FlRendererX11) renderer = fl_renderer_x11_new(xid); + self->engine = fl_engine_new(self->project, FL_RENDERER(renderer)); + g_autoptr(GError) error = nullptr; + if (!fl_engine_start(self->engine, &error)) + g_printerr("Failed to start Flutter engine: %s", error->message); } static void fl_view_size_allocate(GtkWidget* widget, @@ -297,13 +157,9 @@ static void fl_view_size_allocate(GtkWidget* widget, allocation->y, allocation->width, allocation->height); - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(FlutterWindowMetricsEvent); - event.width = allocation->width; - event.height = allocation->height; - event.pixel_ratio = - 1; // TODO(robert-ancell): This won't work on hidpi displays - FlutterEngineSendWindowMetricsEvent(self->flutter_engine, &event); + // TODO(robert-ancell): This pixel ratio won't work on hidpi displays + fl_engine_send_window_metrics_event(self->engine, allocation->width, + allocation->height, 1); } static gboolean fl_view_button_press_event(GtkWidget* widget, @@ -329,18 +185,12 @@ static gboolean fl_view_motion_notify_event(GtkWidget* widget, GdkEventMotion* event) { FlView* self = FL_VIEW(widget); - if (self->flutter_engine == nullptr) + if (self->engine == nullptr) return FALSE; - FlutterPointerEvent fl_event = {}; - fl_event.struct_size = sizeof(fl_event); - fl_event.timestamp = event->time * 1000; - fl_event.phase = self->button_state != 0 ? kMove : kHover; - fl_event.x = event->x; - fl_event.y = event->y; - fl_event.device_kind = kFlutterPointerDeviceKindMouse; - fl_event.buttons = self->button_state; - FlutterEngineSendPointerEvent(self->flutter_engine, &fl_event, 1); + fl_engine_send_mouse_pointer_event( + self->engine, self->button_state != 0 ? kMove : kHover, + event->time * NSEC_PER_MSEC, event->x, event->y, self->button_state); return TRUE; } @@ -370,3 +220,8 @@ G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) { return static_cast( g_object_new(fl_view_get_type(), "flutter-project", project, nullptr)); } + +G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* view) { + g_return_val_if_fail(FL_IS_VIEW(view), nullptr); + return view->engine; +} diff --git a/shell/platform/linux/public/flutter_linux/fl_engine.h b/shell/platform/linux/public/flutter_linux/fl_engine.h new file mode 100644 index 000000000..305bc455f --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_engine.h @@ -0,0 +1,28 @@ +// 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_ENGINE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include "fl_dart_project.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlEngine, fl_engine, FL, ENGINE, GObject) + +/** + * FlEngine: + * + * #FlEngine is an object that contains a running Flutter engine. + */ + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_view.h b/shell/platform/linux/public/flutter_linux/fl_view.h index 6b93daebf..d939ab039 100644 --- a/shell/platform/linux/public/flutter_linux/fl_view.h +++ b/shell/platform/linux/public/flutter_linux/fl_view.h @@ -12,6 +12,7 @@ #include #include "fl_dart_project.h" +#include "fl_engine.h" G_BEGIN_DECLS @@ -33,6 +34,16 @@ G_DECLARE_FINAL_TYPE(FlView, fl_view, FL, VIEW, GtkWidget) */ FlView* fl_view_new(FlDartProject* project); +/** + * fl_view_get_engine: + * @view: a #FlView + * + * Gets the engine being rendered in the view. + * + * Returns: a #FlEngine + */ +FlEngine* fl_view_get_engine(FlView* view); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_VIEW_H_ diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h index b63b05c24..c03784980 100644 --- a/shell/platform/linux/public/flutter_linux/flutter_linux.h +++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h @@ -8,6 +8,7 @@ #define __FLUTTER_LINUX_INSIDE__ #include +#include #include #undef __FLUTTER_LINUX_INSIDE__ -- GitLab