mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Refactor FlutterEngine usage in Linux shell (#17363)
This commit is contained in:
parent
371a8a05b0
commit
12012f135c
@ -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
|
||||
|
||||
@ -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",
|
||||
]
|
||||
|
||||
|
||||
159
shell/platform/linux/fl_engine.cc
Normal file
159
shell/platform/linux/fl_engine.cc
Normal file
@ -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 <gmodule.h>
|
||||
|
||||
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<FlEngine*>(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<FlEngine*>(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<FlEngine*>(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<FlEngine*>(user_data);
|
||||
return fl_renderer_get_fbo(self->renderer);
|
||||
}
|
||||
|
||||
static bool fl_engine_gl_present(void* user_data) {
|
||||
FlEngine* self = static_cast<FlEngine*>(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<FlEngine*>(g_object_new(fl_engine_get_type(), nullptr));
|
||||
self->project = static_cast<FlDartProject*>(g_object_ref(project));
|
||||
self->renderer = static_cast<FlRenderer*>(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);
|
||||
}
|
||||
85
shell/platform/linux/fl_engine_private.h
Normal file
85
shell/platform/linux/fl_engine_private.h
Normal file
@ -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 <glib-object.h>
|
||||
|
||||
#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_
|
||||
139
shell/platform/linux/fl_renderer.cc
Normal file
139
shell/platform/linux/fl_renderer.cc
Normal file
@ -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<FlRendererPrivate*>(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<void*>(eglGetProcAddress(name));
|
||||
}
|
||||
|
||||
gboolean fl_renderer_make_current(FlRenderer* self, GError** error) {
|
||||
FlRendererPrivate* priv =
|
||||
static_cast<FlRendererPrivate*>(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<FlRendererPrivate*>(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<FlRendererPrivate*>(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;
|
||||
}
|
||||
114
shell/platform/linux/fl_renderer.h
Normal file
114
shell/platform/linux/fl_renderer.h
Normal file
@ -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 <EGL/egl.h>
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#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_
|
||||
33
shell/platform/linux/fl_renderer_x11.cc
Normal file
33
shell/platform/linux/fl_renderer_x11.cc
Normal file
@ -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<FlRendererX11*>(
|
||||
g_object_new(fl_renderer_x11_get_type(), nullptr));
|
||||
self->xid = xid;
|
||||
return self;
|
||||
}
|
||||
39
shell/platform/linux/fl_renderer_x11.h
Normal file
39
shell/platform/linux/fl_renderer_x11.h
Normal file
@ -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 <gdk/gdkx.h>
|
||||
|
||||
#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_
|
||||
@ -4,22 +4,19 @@
|
||||
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#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 <gdk/gdkx.h>
|
||||
|
||||
#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<void*>(eglGetProcAddress(name));
|
||||
}
|
||||
|
||||
static bool fl_view_gl_make_current(void* user_data) {
|
||||
FlView* self = static_cast<FlView*>(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<FlView*>(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<FlView*>(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<FlDartProject*>(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<FlView*>(
|
||||
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;
|
||||
}
|
||||
|
||||
28
shell/platform/linux/public/flutter_linux/fl_engine.h
Normal file
28
shell/platform/linux/public/flutter_linux/fl_engine.h
Normal file
@ -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 <flutter_linux/flutter_linux.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#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_
|
||||
@ -12,6 +12,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#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_
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#define __FLUTTER_LINUX_INSIDE__
|
||||
|
||||
#include <flutter_linux/fl_dart_project.h>
|
||||
#include <flutter_linux/fl_engine.h>
|
||||
#include <flutter_linux/fl_view.h>
|
||||
|
||||
#undef __FLUTTER_LINUX_INSIDE__
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user