Use the X visual from the EGL configuration when making an FlView. (#19438)

This commit is contained in:
Robert Ancell 2020-07-03 09:37:51 +12:00 committed by GitHub
parent f8bbcc396b
commit acd026ed3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 217 additions and 120 deletions

View File

@ -23,9 +23,9 @@ TEST(EGLUtils, ErrorToStringNegative) {
TEST(EGLUtils, ConfigToString) {
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, nullptr, nullptr);
EXPECT_TRUE(eglInitialize(display, nullptr, nullptr));
EGLConfig config;
eglChooseConfig(display, nullptr, &config, 1, nullptr);
EXPECT_TRUE(eglChooseConfig(display, nullptr, &config, 1, nullptr));
g_autofree gchar* config_string = egl_config_to_string(display, config);
EXPECT_STREQ(
config_string,
@ -46,9 +46,9 @@ TEST(EGLUtils, ConfigToString) {
TEST(EGLUtils, ConfigToStringNullptr) {
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, nullptr, nullptr);
EXPECT_TRUE(eglInitialize(display, nullptr, nullptr));
EGLConfig config;
eglChooseConfig(display, nullptr, &config, 1, nullptr);
EXPECT_TRUE(eglChooseConfig(display, nullptr, &config, 1, nullptr));
g_autofree gchar* config_string1 = egl_config_to_string(nullptr, config);
EXPECT_STREQ(config_string1, "");
g_autofree gchar* config_string2 = egl_config_to_string(display, nullptr);

View File

@ -15,10 +15,13 @@
static FlEngine* make_mock_engine() {
g_autoptr(FlDartProject) project = fl_dart_project_new();
g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new();
g_autoptr(GError) renderer_error = nullptr;
EXPECT_TRUE(fl_renderer_setup(FL_RENDERER(renderer), &renderer_error));
EXPECT_EQ(renderer_error, nullptr);
g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer));
g_autoptr(GError) error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &error));
EXPECT_EQ(error, nullptr);
g_autoptr(GError) engine_error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &engine_error));
EXPECT_EQ(engine_error, nullptr);
return static_cast<FlEngine*>(g_object_ref(engine));
}

View File

@ -14,10 +14,13 @@
static FlEngine* make_mock_engine() {
g_autoptr(FlDartProject) project = fl_dart_project_new();
g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new();
g_autoptr(GError) renderer_error = nullptr;
EXPECT_TRUE(fl_renderer_setup(FL_RENDERER(renderer), &renderer_error));
EXPECT_EQ(renderer_error, nullptr);
g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer));
g_autoptr(GError) error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &error));
EXPECT_EQ(error, nullptr);
g_autoptr(GError) engine_error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &engine_error));
EXPECT_EQ(engine_error, nullptr);
return static_cast<FlEngine*>(g_object_ref(engine));
}

View File

@ -17,10 +17,13 @@
static FlEngine* make_mock_engine() {
g_autoptr(FlDartProject) project = fl_dart_project_new();
g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new();
g_autoptr(GError) renderer_error = nullptr;
EXPECT_TRUE(fl_renderer_setup(FL_RENDERER(renderer), &renderer_error));
EXPECT_EQ(renderer_error, nullptr);
g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer));
g_autoptr(GError) error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &error));
EXPECT_EQ(error, nullptr);
g_autoptr(GError) engine_error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &engine_error));
EXPECT_EQ(engine_error, nullptr);
return static_cast<FlEngine*>(g_object_ref(engine));
}

View File

@ -11,6 +11,7 @@ G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error)
typedef struct {
EGLDisplay egl_display;
EGLConfig egl_config;
EGLSurface egl_surface;
EGLContext egl_context;
@ -30,7 +31,7 @@ static void create_resource_surface(FlRenderer* self, EGLConfig config) {
EGL_NONE};
priv->resource_surface = eglCreatePbufferSurface(priv->egl_display, config,
resource_context_attribs);
if (priv->resource_surface == nullptr) {
if (priv->resource_surface == EGL_NO_SURFACE) {
g_warning("Failed to create EGL resource surface: %s",
egl_error_to_string(eglGetError()));
return;
@ -38,14 +39,16 @@ static void create_resource_surface(FlRenderer* self, EGLConfig config) {
priv->resource_context = eglCreateContext(
priv->egl_display, config, priv->egl_context, context_attributes);
if (priv->resource_context == nullptr)
if (priv->resource_context == EGL_NO_CONTEXT)
g_warning("Failed to create EGL resource context: %s",
egl_error_to_string(eglGetError()));
}
// 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) {
static void fl_renderer_class_init(FlRendererClass* klass) {}
static void fl_renderer_init(FlRenderer* self) {}
gboolean fl_renderer_setup(FlRenderer* self, GError** error) {
FlRendererPrivate* priv =
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
@ -73,71 +76,91 @@ static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) {
EGL_ALPHA_SIZE,
8,
EGL_NONE};
EGLConfig egl_config;
EGLint n_config;
if (!eglChooseConfig(priv->egl_display, attributes, &egl_config, 1,
if (!eglChooseConfig(priv->egl_display, attributes, &priv->egl_config, 1,
&n_config)) {
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
"Failed to choose EGL config: %s",
egl_error_to_string(eglGetError()));
return FALSE;
}
if (n_config == 0) {
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
"Failed to find appropriate EGL config: %s",
egl_error_to_string(eglGetError()));
return FALSE;
}
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
g_autofree gchar* config_string =
egl_config_to_string(priv->egl_display, egl_config);
egl_config_to_string(priv->egl_display, priv->egl_config);
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
"Failed to bind EGL OpenGL ES API using configuration (%s): %s",
config_string, egl_error_to_string(eglGetError()));
return FALSE;
}
return TRUE;
}
GdkVisual* fl_renderer_get_visual(FlRenderer* self,
GdkScreen* screen,
GError** error) {
FlRendererPrivate* priv =
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
EGLint visual_id;
if (!eglGetConfigAttrib(priv->egl_display, priv->egl_config,
EGL_NATIVE_VISUAL_ID, &visual_id)) {
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
"Failed to determine EGL configuration visual");
return nullptr;
}
GdkVisual* visual =
FL_RENDERER_GET_CLASS(self)->get_visual(self, screen, visual_id);
if (visual == nullptr) {
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
"Failed to find visual 0x%x", visual_id);
return nullptr;
}
return visual;
}
gboolean fl_renderer_start(FlRenderer* self, GError** error) {
FlRendererPrivate* priv =
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
priv->egl_surface = FL_RENDERER_GET_CLASS(self)->create_surface(
self, priv->egl_display, egl_config);
if (priv->egl_surface == nullptr) {
self, priv->egl_display, priv->egl_config);
if (priv->egl_surface == EGL_NO_SURFACE) {
g_autofree gchar* config_string =
egl_config_to_string(priv->egl_display, egl_config);
egl_config_to_string(priv->egl_display, priv->egl_config);
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
"Failed to create EGL surface using configuration (%s): %s",
config_string, egl_error_to_string(eglGetError()));
return FALSE;
}
EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
priv->egl_context = eglCreateContext(priv->egl_display, egl_config,
priv->egl_context = eglCreateContext(priv->egl_display, priv->egl_config,
EGL_NO_CONTEXT, context_attributes);
if (priv->egl_context == nullptr) {
if (priv->egl_context == EGL_NO_CONTEXT) {
g_autofree gchar* config_string =
egl_config_to_string(priv->egl_display, egl_config);
egl_config_to_string(priv->egl_display, priv->egl_config);
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
"Failed to create EGL context using configuration (%s): %s",
config_string, egl_error_to_string(eglGetError()));
return FALSE;
}
create_resource_surface(self, egl_config);
EGLint value;
eglQueryContext(priv->egl_display, priv->egl_context,
EGL_CONTEXT_CLIENT_VERSION, &value);
create_resource_surface(self, priv->egl_config);
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));
}
@ -161,7 +184,8 @@ gboolean fl_renderer_make_resource_current(FlRenderer* self, GError** error) {
FlRendererPrivate* priv =
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
if (priv->resource_surface == nullptr || priv->resource_context == nullptr)
if (priv->resource_surface == EGL_NO_SURFACE ||
priv->resource_context == EGL_NO_CONTEXT)
return FALSE;
if (!eglMakeCurrent(priv->egl_display, priv->resource_surface,

View File

@ -7,7 +7,7 @@
#include <EGL/egl.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"
@ -35,23 +35,51 @@ G_DECLARE_DERIVABLE_TYPE(FlRenderer, fl_renderer, FL, RENDERER, GObject)
struct _FlRendererClass {
GObjectClass parent_class;
// Virtual method called when Flutter has set up EGL and is ready for the
// renderer to start.
gboolean (*start)(FlRenderer* renderer, GError** error);
// Virtual method called to get the visual that matches the given ID.
GdkVisual* (*get_visual)(FlRenderer* renderer,
GdkScreen* screen,
EGLint visual_id);
// Virtual method called when flutter needs a surface to render to.
// Virtual method called when Flutter needs a surface to render to.
EGLSurface (*create_surface)(FlRenderer* renderer,
EGLDisplay display,
EGLConfig config);
};
/**
* fl_renderer_setup:
* @renderer: an #FlRenderer.
* @error: (allow-none): #GError location to store the error occurring, or %NULL
* to ignore.
*
* Set up the renderer.
*
* Returns: %TRUE if successfully setup.
*/
gboolean fl_renderer_setup(FlRenderer* self, GError** error);
/**
* fl_renderer_get_visual:
* @renderer: an #FlRenderer.
* @screen: the screen being rendered on.
* @error: (allow-none): #GError location to store the error occurring, or %NULL
* to ignore.
*
* Gets the visual required to render on.
*
* Returns: a #GdkVisual.
*/
GdkVisual* fl_renderer_get_visual(FlRenderer* self,
GdkScreen* screen,
GError** error);
/**
* fl_renderer_start:
* @renderer: an #FlRenderer.
* @error: (allow-none): #GError location to store the error occurring, or %NULL
* to ignore.
*
* Start the renderer. EGL must be set up before this call.
* Start the renderer.
*
* Returns: %TRUE if successfully started.
*/

View File

@ -7,19 +7,38 @@
struct _FlRendererX11 {
FlRenderer parent_instance;
Window xid;
GdkX11Window* window;
};
G_DEFINE_TYPE(FlRendererX11, fl_renderer_x11, fl_renderer_get_type())
static void fl_renderer_x11_dispose(GObject* object) {
FlRendererX11* self = FL_RENDERER_X11(object);
g_clear_object(&self->window);
G_OBJECT_CLASS(fl_renderer_x11_parent_class)->dispose(object);
}
// Implments FlRenderer::get_visual.
static GdkVisual* fl_renderer_x11_get_visual(FlRenderer* renderer,
GdkScreen* screen,
EGLint visual_id) {
return gdk_x11_screen_lookup_visual(GDK_X11_SCREEN(screen), visual_id);
}
// Implments FlRenderer::create_surface.
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);
return eglCreateWindowSurface(display, config,
gdk_x11_window_get_xid(self->window), nullptr);
}
static void fl_renderer_x11_class_init(FlRendererX11Class* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_renderer_x11_dispose;
FL_RENDERER_CLASS(klass)->get_visual = fl_renderer_x11_get_visual;
FL_RENDERER_CLASS(klass)->create_surface = fl_renderer_x11_create_surface;
}
@ -29,7 +48,7 @@ FlRendererX11* fl_renderer_x11_new() {
return FL_RENDERER_X11(g_object_new(fl_renderer_x11_get_type(), nullptr));
}
void fl_renderer_x11_set_xid(FlRendererX11* self, Window xid) {
void fl_renderer_x11_set_window(FlRendererX11* self, GdkX11Window* window) {
g_return_if_fail(FL_IS_RENDERER_X11(self));
self->xid = xid;
self->window = GDK_X11_WINDOW(g_object_ref(window));
}

View File

@ -34,13 +34,13 @@ G_DECLARE_FINAL_TYPE(FlRendererX11,
FlRendererX11* fl_renderer_x11_new();
/**
* fl_renderer_x11_set_xid:
* fl_renderer_x11_set_window:
* @renderer: an #FlRendererX11.
* @xid: The X window being rendered to.
* @window: the X window being rendered to.
*
* Sets the X11 window that is being rendered to.
*/
void fl_renderer_x11_set_xid(FlRendererX11* renderer, Window xid);
void fl_renderer_x11_set_window(FlRendererX11* renderer, GdkX11Window* window);
G_END_DECLS

View File

@ -202,6 +202,10 @@ static void fl_view_realize(GtkWidget* widget) {
gtk_widget_set_realized(widget, TRUE);
g_autoptr(GError) error = nullptr;
if (!fl_renderer_setup(FL_RENDERER(self->renderer), &error))
g_warning("Failed to setup renderer: %s", error->message);
GtkAllocation allocation;
gtk_widget_get_allocation(widget, &allocation);
@ -212,7 +216,8 @@ static void fl_view_realize(GtkWidget* widget) {
window_attributes.width = allocation.width;
window_attributes.height = allocation.height;
window_attributes.wclass = GDK_INPUT_OUTPUT;
window_attributes.visual = gtk_widget_get_visual(widget);
window_attributes.visual = fl_renderer_get_visual(
FL_RENDERER(self->renderer), gtk_widget_get_screen(widget), nullptr);
window_attributes.event_mask =
gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK |
GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
@ -227,10 +232,9 @@ static void fl_view_realize(GtkWidget* widget) {
gtk_widget_register_window(widget, window);
gtk_widget_set_window(widget, window);
Window xid = gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(self)));
fl_renderer_x11_set_xid(self->renderer, xid);
fl_renderer_x11_set_window(
self->renderer, GDK_X11_WINDOW(gtk_widget_get_window(GTK_WIDGET(self))));
g_autoptr(GError) error = nullptr;
if (!fl_engine_start(self->engine, &error))
g_warning("Failed to start Flutter engine: %s", error->message);
}

View File

@ -35,11 +35,19 @@ typedef struct {
} MockConfig;
typedef struct {
bool initialized;
MockConfig config;
} MockDisplay;
typedef struct {
} MockContext;
typedef struct {
} MockSurface;
static bool display_initialized = false;
static MockDisplay mock_display;
static MockConfig mock_config;
static MockContext mock_context;
static MockSurface mock_surface;
static EGLint mock_error = EGL_SUCCESS;
@ -49,8 +57,11 @@ static bool check_display(EGLDisplay dpy) {
return false;
}
MockDisplay* display = static_cast<MockDisplay*>(dpy);
if (!display->initialized) {
return true;
}
static bool check_initialized(EGLDisplay dpy) {
if (!display_initialized) {
mock_error = EGL_NOT_INITIALIZED;
return false;
}
@ -86,7 +97,8 @@ EGLBoolean eglChooseConfig(EGLDisplay dpy,
EGLConfig* configs,
EGLint config_size,
EGLint* num_config) {
MockDisplay* display = static_cast<MockDisplay*>(dpy);
if (!check_display(dpy) || !check_initialized(dpy))
return EGL_FALSE;
if (configs == nullptr) {
if (num_config != nullptr)
@ -96,7 +108,8 @@ EGLBoolean eglChooseConfig(EGLDisplay dpy,
EGLint n_returned = 0;
if (config_size >= 1) {
configs[0] = &display->config;
configs[0] = &mock_config;
n_returned++;
}
if (num_config != nullptr)
@ -109,39 +122,39 @@ EGLContext eglCreateContext(EGLDisplay dpy,
EGLConfig config,
EGLContext share_context,
const EGLint* attrib_list) {
if (!check_display(dpy) || !check_config(config))
if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config))
return EGL_NO_CONTEXT;
mock_error = EGL_SUCCESS;
return EGL_NO_CONTEXT;
return &mock_context;
}
EGLSurface eglCreatePbufferSurface(EGLDisplay dpy,
EGLConfig config,
const EGLint* attrib_list) {
if (!check_display(dpy) || !check_config(config))
if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config))
return EGL_NO_SURFACE;
mock_error = EGL_SUCCESS;
return EGL_NO_SURFACE;
return &mock_surface;
}
EGLSurface eglCreateWindowSurface(EGLDisplay dpy,
EGLConfig config,
EGLNativeWindowType win,
const EGLint* attrib_list) {
if (!check_display(dpy) || !check_config(config))
if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config))
return EGL_NO_SURFACE;
mock_error = EGL_SUCCESS;
return EGL_NO_SURFACE;
return &mock_surface;
}
EGLBoolean eglGetConfigAttrib(EGLDisplay dpy,
EGLConfig config,
EGLint attribute,
EGLint* value) {
if (!check_display(dpy) || !check_config(config))
if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config))
return EGL_FALSE;
MockConfig* c = static_cast<MockConfig*>(config);
@ -248,38 +261,38 @@ void (*eglGetProcAddress(const char* procname))(void) {
}
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) {
MockDisplay* display = static_cast<MockDisplay*>(dpy);
if (!check_display(dpy))
return EGL_FALSE;
if (!display->initialized) {
MockConfig* c = &display->config;
c->config_id = 1;
c->buffer_size = 32;
c->color_buffer_type = EGL_RGB_BUFFER;
c->transparent_type = EGL_NONE;
c->level = 1;
c->red_size = 8;
c->green_size = 8;
c->blue_size = 8;
c->alpha_size = 0;
c->depth_size = 0;
c->stencil_size = 0;
c->samples = 0;
c->sample_buffers = 0;
c->native_visual_id = 1;
c->native_visual_type = 0;
c->native_renderable = EGL_TRUE;
c->config_caveat = EGL_NONE;
c->bind_to_texture_rgb = EGL_TRUE;
c->bind_to_texture_rgba = EGL_FALSE;
c->renderable_type = EGL_OPENGL_ES2_BIT;
c->conformant = EGL_OPENGL_ES2_BIT;
c->surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
c->max_pbuffer_width = 1024;
c->max_pbuffer_height = 1024;
c->max_pbuffer_pixels = 1024 * 1024;
c->min_swap_interval = 0;
c->max_swap_interval = 1000;
display->initialized = true;
if (!display_initialized) {
mock_config.config_id = 1;
mock_config.buffer_size = 32;
mock_config.color_buffer_type = EGL_RGB_BUFFER;
mock_config.transparent_type = EGL_NONE;
mock_config.level = 1;
mock_config.red_size = 8;
mock_config.green_size = 8;
mock_config.blue_size = 8;
mock_config.alpha_size = 0;
mock_config.depth_size = 0;
mock_config.stencil_size = 0;
mock_config.samples = 0;
mock_config.sample_buffers = 0;
mock_config.native_visual_id = 1;
mock_config.native_visual_type = 0;
mock_config.native_renderable = EGL_TRUE;
mock_config.config_caveat = EGL_NONE;
mock_config.bind_to_texture_rgb = EGL_TRUE;
mock_config.bind_to_texture_rgba = EGL_FALSE;
mock_config.renderable_type = EGL_OPENGL_ES2_BIT;
mock_config.conformant = EGL_OPENGL_ES2_BIT;
mock_config.surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
mock_config.max_pbuffer_width = 1024;
mock_config.max_pbuffer_height = 1024;
mock_config.max_pbuffer_pixels = 1024 * 1024;
mock_config.min_swap_interval = 0;
mock_config.max_swap_interval = 1000;
display_initialized = true;
}
if (major != nullptr)
@ -294,24 +307,14 @@ EGLBoolean eglMakeCurrent(EGLDisplay dpy,
EGLSurface draw,
EGLSurface read,
EGLContext ctx) {
if (!check_display(dpy))
return EGL_FALSE;
return bool_success();
}
EGLBoolean eglQueryContext(EGLDisplay dpy,
EGLContext ctx,
EGLint attribute,
EGLint* value) {
if (!check_display(dpy))
if (!check_display(dpy) || !check_initialized(dpy))
return EGL_FALSE;
return bool_success();
}
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
if (!check_display(dpy))
if (!check_display(dpy) || !check_initialized(dpy))
return EGL_FALSE;
return bool_success();

View File

@ -10,13 +10,23 @@ struct _FlMockRenderer {
G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type())
// Implements FlRenderer::start
static gboolean fl_mock_renderer_start(FlRenderer* renderer, GError** error) {
return TRUE;
// Implements FlRenderer::get_visual.
static GdkVisual* fl_mock_renderer_get_visual(FlRenderer* renderer,
GdkScreen* screen,
EGLint visual_id) {
return static_cast<GdkVisual*>(g_object_new(GDK_TYPE_VISUAL, nullptr));
}
// Implements FlRenderer::create_surface.
static EGLSurface fl_mock_renderer_create_surface(FlRenderer* renderer,
EGLDisplay display,
EGLConfig config) {
return eglCreateWindowSurface(display, config, 0, nullptr);
}
static void fl_mock_renderer_class_init(FlMockRendererClass* klass) {
FL_RENDERER_CLASS(klass)->start = fl_mock_renderer_start;
FL_RENDERER_CLASS(klass)->get_visual = fl_mock_renderer_get_visual;
FL_RENDERER_CLASS(klass)->create_surface = fl_mock_renderer_create_surface;
}
static void fl_mock_renderer_init(FlMockRenderer* self) {}