diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c9a88a4807eaf..dacee2a2b942e 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1346,14 +1346,13 @@ FILE: ../../../flutter/shell/platform/glfw/system_utils.h FILE: ../../../flutter/shell/platform/glfw/system_utils_test.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h -FILE: ../../../flutter/shell/platform/linux/egl_utils.cc -FILE: ../../../flutter/shell/platform/linux/egl_utils.h -FILE: ../../../flutter/shell/platform/linux/egl_utils_test.cc FILE: ../../../flutter/shell/platform/linux/fl_accessibility_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_accessibility_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_accessible_node.cc FILE: ../../../flutter/shell/platform/linux/fl_accessible_node.h FILE: ../../../flutter/shell/platform/linux/fl_accessible_node_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_backing_store_provider.cc +FILE: ../../../flutter/shell/platform/linux/fl_backing_store_provider.h FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel_test.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_codec.cc @@ -1369,6 +1368,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h FILE: ../../../flutter/shell/platform/linux/fl_engine_test.cc FILE: ../../../flutter/shell/platform/linux/fl_event_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_event_channel_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_gl_area.cc +FILE: ../../../flutter/shell/platform/linux/fl_gl_area.h FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec.cc @@ -1398,12 +1399,10 @@ FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h FILE: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer.h +FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.cc +FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.h FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.h -FILE: ../../../flutter/shell/platform/linux/fl_renderer_wayland.cc -FILE: ../../../flutter/shell/platform/linux/fl_renderer_wayland.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_settings_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc @@ -1420,6 +1419,7 @@ FILE: ../../../flutter/shell/platform/linux/fl_value_test.cc FILE: ../../../flutter/shell/platform/linux/fl_view.cc FILE: ../../../flutter/shell/platform/linux/fl_view_accessible.cc FILE: ../../../flutter/shell/platform/linux/fl_view_accessible.h +FILE: ../../../flutter/shell/platform/linux/fl_view_private.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 853cfa9ac89ef..3706fcd1837c4 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -82,21 +82,19 @@ source_set("flutter_linux_sources") { "fl_standard_message_codec_private.h", ] - configs += [ - "//flutter/shell/platform/linux/config:gtk", - "//flutter/shell/platform/linux/config:wayland-client", - ] + configs += [ "//flutter/shell/platform/linux/config:gtk" ] sources = [ - "egl_utils.cc", "fl_accessibility_plugin.cc", "fl_accessible_node.cc", + "fl_backing_store_provider.cc", "fl_basic_message_channel.cc", "fl_binary_codec.cc", "fl_binary_messenger.cc", "fl_dart_project.cc", "fl_engine.cc", "fl_event_channel.cc", + "fl_gl_area.cc", "fl_json_message_codec.cc", "fl_json_method_codec.cc", "fl_key_event_plugin.cc", @@ -110,9 +108,8 @@ source_set("flutter_linux_sources") { "fl_plugin_registrar.cc", "fl_plugin_registry.cc", "fl_renderer.cc", + "fl_renderer_gl.cc", "fl_renderer_headless.cc", - "fl_renderer_wayland.cc", - "fl_renderer_x11.cc", "fl_settings_plugin.cc", "fl_standard_message_codec.cc", "fl_standard_method_codec.cc", @@ -140,10 +137,7 @@ source_set("flutter_linux_sources") { source_set("flutter_linux") { configs += [ "//flutter/shell/platform/linux/config:gtk", - "//flutter/shell/platform/linux/config:egl", - "//flutter/shell/platform/linux/config:wayland-egl", - "//flutter/shell/platform/linux/config:x11", - "//third_party/khronos:khronos_headers", + "//flutter/shell/platform/linux/config:epoxy", ] defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ] @@ -161,7 +155,6 @@ executable("flutter_linux_unittests") { testonly = true sources = [ - "egl_utils_test.cc", "fl_accessible_node_test.cc", "fl_basic_message_channel_test.cc", "fl_binary_codec_test.cc", @@ -181,19 +174,15 @@ executable("flutter_linux_unittests") { "fl_string_codec_test.cc", "fl_value_test.cc", "testing/fl_test.cc", - "testing/mock_egl.cc", "testing/mock_engine.cc", + "testing/mock_epoxy.cc", "testing/mock_renderer.cc", "testing/mock_text_input_plugin.cc", ] public_configs = [ "//flutter:config" ] - configs += [ - "//flutter/shell/platform/linux/config:gtk", - "//flutter/shell/platform/linux/config:wayland-egl", - "//flutter/shell/platform/linux/config:x11", - ] + configs += [ "//flutter/shell/platform/linux/config:gtk" ] defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES", diff --git a/shell/platform/linux/config/BUILD.gn b/shell/platform/linux/config/BUILD.gn index 0e41ed3ee4cdd..da0ac056bc567 100644 --- a/shell/platform/linux/config/BUILD.gn +++ b/shell/platform/linux/config/BUILD.gn @@ -17,10 +17,6 @@ pkg_config("egl") { packages = [ "egl" ] } -pkg_config("wayland-egl") { - packages = [ "wayland-egl" ] -} - -pkg_config("wayland-client") { - packages = [ "wayland-client" ] +pkg_config("epoxy") { + packages = [ "epoxy" ] } diff --git a/shell/platform/linux/egl_utils.cc b/shell/platform/linux/egl_utils.cc deleted file mode 100644 index f859fb55c5e7c..0000000000000 --- a/shell/platform/linux/egl_utils.cc +++ /dev/null @@ -1,282 +0,0 @@ -// 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 "egl_utils.h" - -#include - -// Converts an EGL decimal value to a string. -static gchar* egl_decimal_to_string(EGLint value) { - return g_strdup_printf("%d", value); -} - -// Converts an EGL hexadecimal value to a string. -static gchar* egl_hexadecimal_to_string(EGLint value) { - return g_strdup_printf("0x%x", value); -} - -// Converts an EGL enumerated value to a string. -static gchar* egl_enum_to_string(EGLint value) { - if (value == EGL_FALSE) - return g_strdup("EGL_FALSE"); - else if (value == EGL_LUMINANCE_BUFFER) - return g_strdup("EGL_LUMINANCE_BUFFER"); - else if (value == EGL_NONE) - return g_strdup("EGL_NONE"); - else if (value == EGL_NON_CONFORMANT_CONFIG) - return g_strdup("EGL_NON_CONFORMANT_CONFIG"); - else if (value == EGL_RGB_BUFFER) - return g_strdup("EGL_RGB_BUFFER"); - else if (value == EGL_SLOW_CONFIG) - return g_strdup("EGL_SLOW_CONFIG"); - else if (value == EGL_TRANSPARENT_RGB) - return g_strdup("EGL_TRANSPARENT_RGB"); - else if (value == EGL_TRUE) - return g_strdup("EGL_TRUE"); - else - return nullptr; -} - -// Ensures the given bit is not set in a bitfield. Returns TRUE if that bit was -// cleared. -static gboolean clear_bit(EGLint* field, EGLint bit) { - if ((*field & bit) == 0) - return FALSE; - - *field ^= bit; - return TRUE; -} - -// Converts an EGL renderable type bitfield to a string. -static gchar* egl_renderable_type_to_string(EGLint value) { - EGLint v = value; - g_autoptr(GPtrArray) strings = g_ptr_array_new_with_free_func(g_free); - if (clear_bit(&v, EGL_OPENGL_ES_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_OPENGL_ES_BIT")); - if (clear_bit(&v, EGL_OPENVG_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_OPENVG_BIT")); - if (clear_bit(&v, EGL_OPENGL_ES2_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_OPENGL_ES2_BIT")); - if (clear_bit(&v, EGL_OPENGL_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_OPENGL_BIT")); - if (clear_bit(&v, EGL_OPENGL_ES3_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_OPENGL_ES3_BIT")); - if (v != 0) - g_ptr_array_add(strings, egl_hexadecimal_to_string(v)); - g_ptr_array_add(strings, nullptr); - - return g_strjoinv("|", reinterpret_cast(strings->pdata)); -} - -// Converts an EGL surface type bitfield to a string. -static gchar* egl_surface_type_to_string(EGLint value) { - EGLint v = value; - g_autoptr(GPtrArray) strings = g_ptr_array_new_with_free_func(g_free); - if (clear_bit(&v, EGL_PBUFFER_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_PBUFFER_BIT")); - if (clear_bit(&v, EGL_PIXMAP_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_PIXMAP_BIT")); - if (clear_bit(&v, EGL_WINDOW_BIT)) - g_ptr_array_add(strings, g_strdup("EGL_WINDOW_BIT")); - if (v != 0) - g_ptr_array_add(strings, egl_hexadecimal_to_string(v)); - g_ptr_array_add(strings, nullptr); - - return g_strjoinv("|", reinterpret_cast(strings->pdata)); -} - -const gchar* egl_error_to_string(EGLint error) { - switch (error) { - case EGL_SUCCESS: - return "Success"; - case EGL_NOT_INITIALIZED: - return "Not Initialized"; - case EGL_BAD_ACCESS: - return "Bad Access"; - case EGL_BAD_ALLOC: - return "Bad Allocation"; - case EGL_BAD_ATTRIBUTE: - return "Bad Attribute"; - case EGL_BAD_CONTEXT: - return "Bad Context"; - case EGL_BAD_CONFIG: - return "Bad Configuration"; - case EGL_BAD_CURRENT_SURFACE: - return "Bad Current Surface"; - case EGL_BAD_DISPLAY: - return "Bad Display"; - case EGL_BAD_SURFACE: - return "Bad Surface"; - case EGL_BAD_MATCH: - return "Bad Match"; - case EGL_BAD_PARAMETER: - return "Bad Parameter"; - case EGL_BAD_NATIVE_PIXMAP: - return "Bad Native Pixmap"; - case EGL_BAD_NATIVE_WINDOW: - return "Bad Native Window"; - case EGL_CONTEXT_LOST: - return "Context Lost"; - default: - return "Unknown Error"; - } -} - -gchar* egl_config_to_string(EGLDisplay display, EGLConfig config) { - struct { - EGLint attribute; - const gchar* name; - gchar* (*to_string)(EGLint value); - } config_items[] = {{ - EGL_CONFIG_ID, - "EGL_CONFIG_ID", - egl_decimal_to_string, - }, - { - EGL_BUFFER_SIZE, - "EGL_BUFFER_SIZE", - egl_decimal_to_string, - }, - { - EGL_COLOR_BUFFER_TYPE, - "EGL_COLOR_BUFFER_TYPE", - egl_enum_to_string, - }, - { - EGL_TRANSPARENT_TYPE, - "EGL_TRANSPARENT_TYPE", - egl_enum_to_string, - }, - { - EGL_LEVEL, - "EGL_LEVEL", - egl_decimal_to_string, - }, - { - EGL_RED_SIZE, - "EGL_RED_SIZE", - egl_decimal_to_string, - }, - { - EGL_GREEN_SIZE, - "EGL_GREEN_SIZE", - egl_decimal_to_string, - }, - { - EGL_BLUE_SIZE, - "EGL_BLUE_SIZE", - egl_decimal_to_string, - }, - { - EGL_ALPHA_SIZE, - "EGL_ALPHA_SIZE", - egl_decimal_to_string, - }, - { - EGL_DEPTH_SIZE, - "EGL_DEPTH_SIZE", - egl_decimal_to_string, - }, - { - EGL_STENCIL_SIZE, - "EGL_STENCIL_SIZE", - egl_decimal_to_string, - }, - { - EGL_SAMPLES, - "EGL_SAMPLES", - egl_decimal_to_string, - }, - { - EGL_SAMPLE_BUFFERS, - "EGL_SAMPLE_BUFFERS", - egl_decimal_to_string, - }, - { - EGL_NATIVE_VISUAL_ID, - "EGL_NATIVE_VISUAL_ID", - egl_hexadecimal_to_string, - }, - { - EGL_NATIVE_VISUAL_TYPE, - "EGL_NATIVE_VISUAL_TYPE", - egl_hexadecimal_to_string, - }, - { - EGL_NATIVE_RENDERABLE, - "EGL_NATIVE_RENDERABLE", - egl_enum_to_string, - }, - { - EGL_CONFIG_CAVEAT, - "EGL_CONFIG_CAVEAT", - egl_enum_to_string, - }, - { - EGL_BIND_TO_TEXTURE_RGB, - "EGL_BIND_TO_TEXTURE_RGB", - egl_enum_to_string, - }, - { - EGL_BIND_TO_TEXTURE_RGBA, - "EGL_BIND_TO_TEXTURE_RGBA", - egl_enum_to_string, - }, - { - EGL_RENDERABLE_TYPE, - "EGL_RENDERABLE_TYPE", - egl_renderable_type_to_string, - }, - { - EGL_CONFORMANT, - "EGL_CONFORMANT", - egl_renderable_type_to_string, - }, - { - EGL_SURFACE_TYPE, - "EGL_SURFACE_TYPE", - egl_surface_type_to_string, - }, - { - EGL_MAX_PBUFFER_WIDTH, - "EGL_MAX_PBUFFER_WIDTH", - egl_decimal_to_string, - }, - { - EGL_MAX_PBUFFER_HEIGHT, - "EGL_MAX_PBUFFER_HEIGHT", - egl_decimal_to_string, - }, - { - EGL_MAX_PBUFFER_PIXELS, - "EGL_MAX_PBUFFER_PIXELS", - egl_decimal_to_string, - }, - { - EGL_MIN_SWAP_INTERVAL, - "EGL_MIN_SWAP_INTERVAL", - egl_decimal_to_string, - }, - { - EGL_MAX_SWAP_INTERVAL, - "EGL_MAX_SWAP_INTERVAL", - egl_decimal_to_string, - }, - {EGL_NONE, nullptr, nullptr}}; - - g_autoptr(GPtrArray) strings = g_ptr_array_new_with_free_func(g_free); - for (int i = 0; config_items[i].attribute != EGL_NONE; i++) { - EGLint value; - if (!eglGetConfigAttrib(display, config, config_items[i].attribute, &value)) - continue; - g_autofree gchar* value_string = config_items[i].to_string(value); - if (value_string == nullptr) - value_string = egl_hexadecimal_to_string(value); - g_ptr_array_add( - strings, g_strdup_printf("%s=%s", config_items[i].name, value_string)); - } - g_ptr_array_add(strings, nullptr); - - return g_strjoinv(" ", reinterpret_cast(strings->pdata)); -} diff --git a/shell/platform/linux/egl_utils.h b/shell/platform/linux/egl_utils.h deleted file mode 100644 index cc97174ea3714..0000000000000 --- a/shell/platform/linux/egl_utils.h +++ /dev/null @@ -1,38 +0,0 @@ -// 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_EGL_UTILS_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_EGL_UTILS_H_ - -#include - -#include - -G_BEGIN_DECLS - -/** - * egl_error_to_string: - * @error: an EGL error code. - * - * Converts an egl error code to a human readable string. e.g. "Bad Match". - * - * Returns: an error description. - */ -const gchar* egl_error_to_string(EGLint error); - -/** - * egl_config_to_string: - * @display: an EGL display. - * @config: an EGL configuration. - * - * Converts an EGL configuration to a human readable string. e.g. - * "EGL_CONFIG_ID=1 EGL_RED_SIZE=8...". - * - * Returns: a configuration description. - */ -gchar* egl_config_to_string(EGLDisplay display, EGLConfig config); - -G_END_DECLS - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_EGL_UTILS_H_ diff --git a/shell/platform/linux/egl_utils_test.cc b/shell/platform/linux/egl_utils_test.cc deleted file mode 100644 index 3be967d98b296..0000000000000 --- a/shell/platform/linux/egl_utils_test.cc +++ /dev/null @@ -1,58 +0,0 @@ -// 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 "gtest/gtest.h" - -#include "flutter/shell/platform/linux/egl_utils.h" - -TEST(EGLUtils, ErrorToString) { - const gchar* error_string = egl_error_to_string(EGL_SUCCESS); - EXPECT_STREQ(error_string, "Success"); -} - -TEST(EGLUtils, ErrorToStringUnknown) { - const gchar* error_string = egl_error_to_string(0xffffffff); - EXPECT_STREQ(error_string, "Unknown Error"); -} - -TEST(EGLUtils, ErrorToStringNegative) { - const gchar* error_string = egl_error_to_string(-1); - EXPECT_STREQ(error_string, "Unknown Error"); -} - -TEST(EGLUtils, ConfigToString) { - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EXPECT_TRUE(eglInitialize(display, nullptr, nullptr)); - EGLConfig config; - EXPECT_TRUE(eglChooseConfig(display, nullptr, &config, 1, nullptr)); - g_autofree gchar* config_string = egl_config_to_string(display, config); - EXPECT_STREQ( - config_string, - "EGL_CONFIG_ID=1 EGL_BUFFER_SIZE=32 EGL_COLOR_BUFFER_TYPE=EGL_RGB_BUFFER " - "EGL_TRANSPARENT_TYPE=EGL_NONE EGL_LEVEL=1 EGL_RED_SIZE=8 " - "EGL_GREEN_SIZE=8 EGL_BLUE_SIZE=8 EGL_ALPHA_SIZE=0 EGL_DEPTH_SIZE=0 " - "EGL_STENCIL_SIZE=0 EGL_SAMPLES=0 EGL_SAMPLE_BUFFERS=0 " - "EGL_NATIVE_VISUAL_ID=0x1 EGL_NATIVE_VISUAL_TYPE=0x0 " - "EGL_NATIVE_RENDERABLE=EGL_TRUE EGL_CONFIG_CAVEAT=EGL_NONE " - "EGL_BIND_TO_TEXTURE_RGB=EGL_TRUE EGL_BIND_TO_TEXTURE_RGBA=EGL_FALSE " - "EGL_RENDERABLE_TYPE=EGL_OPENGL_ES2_BIT " - "EGL_CONFORMANT=EGL_OPENGL_ES2_BIT " - "EGL_SURFACE_TYPE=EGL_PBUFFER_BIT|EGL_WINDOW_BIT " - "EGL_MAX_PBUFFER_WIDTH=1024 EGL_MAX_PBUFFER_HEIGHT=1024 " - "EGL_MAX_PBUFFER_PIXELS=1048576 EGL_MIN_SWAP_INTERVAL=0 " - "EGL_MAX_SWAP_INTERVAL=1000"); -} - -TEST(EGLUtils, ConfigToStringNullptr) { - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EXPECT_TRUE(eglInitialize(display, nullptr, nullptr)); - EGLConfig config; - 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); - EXPECT_STREQ(config_string2, ""); - g_autofree gchar* config_string3 = egl_config_to_string(nullptr, nullptr); - EXPECT_STREQ(config_string3, ""); -} diff --git a/shell/platform/linux/fl_backing_store_provider.cc b/shell/platform/linux/fl_backing_store_provider.cc new file mode 100644 index 0000000000000..d76b7ab6ceb40 --- /dev/null +++ b/shell/platform/linux/fl_backing_store_provider.cc @@ -0,0 +1,87 @@ +// 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_backing_store_provider.h" + +#include + +struct _FlBackingStoreProvider { + GObject parent_instance; + + uint32_t framebuffer_id; + uint32_t texture_id; + GdkRectangle geometry; +}; + +G_DEFINE_TYPE(FlBackingStoreProvider, fl_backing_store_provider, G_TYPE_OBJECT) + +static void fl_backing_store_provider_dispose(GObject* object) { + FlBackingStoreProvider* self = FL_BACKING_STORE_PROVIDER(object); + + glDeleteFramebuffers(1, &self->framebuffer_id); + glDeleteTextures(1, &self->texture_id); + + G_OBJECT_CLASS(fl_backing_store_provider_parent_class)->dispose(object); +} + +static void fl_backing_store_provider_class_init( + FlBackingStoreProviderClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_backing_store_provider_dispose; +} + +static void fl_backing_store_provider_init(FlBackingStoreProvider* self) {} + +FlBackingStoreProvider* fl_backing_store_provider_new(int width, int height) { + FlBackingStoreProvider* provider = FL_BACKING_STORE_PROVIDER( + g_object_new(fl_backing_store_provider_get_type(), nullptr)); + + provider->geometry = { + .x = 0, + .y = 0, + .width = width, + .height = height, + }; + + glGenTextures(1, &provider->texture_id); + glGenFramebuffers(1, &provider->framebuffer_id); + + glBindFramebuffer(GL_FRAMEBUFFER, provider->framebuffer_id); + + glBindTexture(GL_TEXTURE_2D, provider->texture_id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, provider->texture_id, 0); + + return provider; +} + +uint32_t fl_backing_store_provider_get_gl_framebuffer_id( + FlBackingStoreProvider* self) { + return self->framebuffer_id; +} + +uint32_t fl_backing_store_provider_get_gl_texture_id( + FlBackingStoreProvider* self) { + return self->texture_id; +} + +uint32_t fl_backing_store_provider_get_gl_target(FlBackingStoreProvider* self) { + return GL_TEXTURE_2D; +} + +uint32_t fl_backing_store_provider_get_gl_format(FlBackingStoreProvider* self) { + return GL_RGBA8; +} + +GdkRectangle fl_backing_store_provider_get_geometry( + FlBackingStoreProvider* self) { + return self->geometry; +} diff --git a/shell/platform/linux/fl_backing_store_provider.h b/shell/platform/linux/fl_backing_store_provider.h new file mode 100644 index 0000000000000..6f36daa83ab7d --- /dev/null +++ b/shell/platform/linux/fl_backing_store_provider.h @@ -0,0 +1,96 @@ +// 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_BACKING_STORE_PROVIDER_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_BACKING_STORE_PROVIDER_H_ + +#include + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlBackingStoreProvider, + fl_backing_store_provider, + FL, + BACKING_STORE_PROVIDER, + GObject) + +/** + * FlBackingStoreProvider: + * + * #FlBackingStoreProvider creates framebuffers and their backing textures + * for embedder compositor. + */ + +/** + * fl_backing_store_provider_new: + * @width: width of texture. + * @height: height of texture. + * + * Creates a new frame buffer. fl_renderer_make_current() must be called + * first. + * + * Returns: a new #FlBackingStoreProvider. + */ +FlBackingStoreProvider* fl_backing_store_provider_new(int width, int height); + +/** + * fl_backing_store_provider_get_gl_framebuffer_id: + * @provider: an #FlBackingStoreProvider. + * + * Gets created framebuffer id. + * + * Returns: gl framebuffer id, 0 if creation failed. + */ +uint32_t fl_backing_store_provider_get_gl_framebuffer_id( + FlBackingStoreProvider* provider); + +/** + * fl_backing_store_provider_get_gl_texture_id: + * @provider: an #FlBackingStoreProvider. + * + * Gets created texture id. + * + * Returns: gl texture id, 0 if creation failed. + */ +uint32_t fl_backing_store_provider_get_gl_texture_id( + FlBackingStoreProvider* provider); + +/** + * fl_backing_store_provider_get_gl_target: + * @provider: an #FlBackingStoreProvider. + * + * Gets target texture (example GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE). + * + * Returns: target texture. + */ +uint32_t fl_backing_store_provider_get_gl_target( + FlBackingStoreProvider* provider); + +/** + * fl_backing_store_provider_get_gl_format: + * @provider: an #FlBackingStoreProvider. + * + * Gets texture format (example GL_RGBA8). + * + * Returns: texture format. + */ +uint32_t fl_backing_store_provider_get_gl_format( + FlBackingStoreProvider* provider); + +/** + * fl_backing_store_provider_get_geometry: + * @provider: an #FlBackingStoreProvider. + * + * Gets geometry of framebuffer. + * + * Returns: geometry of backing store. + */ +GdkRectangle fl_backing_store_provider_get_geometry( + FlBackingStoreProvider* provider); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_BACKING_STORE_PROVIDER_H_ diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 76cc45a520536..2212b68ed6489 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -177,6 +177,33 @@ static void flutter_source_finalize(GSource* source) { } } +// Called when engine needs a backing store for a specific #FlutterLayer. +static bool compositor_create_backing_store_callback( + const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out, + void* user_data) { + g_return_val_if_fail(FL_IS_RENDERER(user_data), false); + return fl_renderer_create_backing_store(FL_RENDERER(user_data), config, + backing_store_out); +} + +// Called when the backing store is to be released. +static bool compositor_collect_backing_store_callback( + const FlutterBackingStore* renderer, + void* user_data) { + g_return_val_if_fail(FL_IS_RENDERER(user_data), false); + return fl_renderer_collect_backing_store(FL_RENDERER(user_data), renderer); +} + +// Called when embedder should composite contents of each layer onto the screen. +static bool compositor_present_layers_callback(const FlutterLayer** layers, + size_t layers_count, + void* user_data) { + g_return_val_if_fail(FL_IS_RENDERER(user_data), false); + return fl_renderer_present_layers(FL_RENDERER(user_data), layers, + layers_count); +} + // Table of functions for Flutter GLib main loop integration. static GSourceFuncs flutter_source_funcs = { nullptr, // prepare @@ -404,6 +431,7 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { FlutterCustomTaskRunners custom_task_runners = {}; custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners); custom_task_runners.platform_task_runner = &platform_task_runner; + custom_task_runners.render_task_runner = &platform_task_runner; g_autoptr(GPtrArray) command_line_args = fl_dart_project_get_switches(self->project); @@ -431,6 +459,16 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { args.dart_entrypoint_argv = reinterpret_cast(dart_entrypoint_args); + FlutterCompositor compositor = {}; + compositor.struct_size = sizeof(FlutterCompositor); + compositor.user_data = self->renderer; + compositor.create_backing_store_callback = + compositor_create_backing_store_callback; + compositor.collect_backing_store_callback = + compositor_collect_backing_store_callback; + compositor.present_layers_callback = compositor_present_layers_callback; + args.compositor = &compositor; + if (self->embedder_api.RunsAOTCompiledDartCode()) { FlutterEngineAOTDataSource source = {}; source.type = kFlutterEngineAOTDataSourceTypeElfPath; diff --git a/shell/platform/linux/fl_gl_area.cc b/shell/platform/linux/fl_gl_area.cc new file mode 100644 index 0000000000000..8d7200c351e05 --- /dev/null +++ b/shell/platform/linux/fl_gl_area.cc @@ -0,0 +1,132 @@ +// 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/fl_gl_area.h" + +#include + +struct _FlGLArea { + GtkWidget parent_instance; + + GdkGLContext* context; + + FlBackingStoreProvider* texture; +}; + +G_DEFINE_TYPE(FlGLArea, fl_gl_area, GTK_TYPE_WIDGET) + +static void fl_gl_area_dispose(GObject* gobject) { + FlGLArea* self = FL_GL_AREA(gobject); + + g_clear_object(&self->context); + g_clear_object(&self->texture); + + G_OBJECT_CLASS(fl_gl_area_parent_class)->dispose(gobject); +} + +// Implements GtkWidget::realize. +static void fl_gl_area_realize(GtkWidget* widget) { + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + + gtk_widget_set_realized(widget, TRUE); + + GdkWindowAttr attributes; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual(widget); + attributes.event_mask = gtk_widget_get_events(widget); + gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; + GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), + &attributes, attributes_mask); + gtk_widget_set_window(widget, window); + gtk_widget_register_window(widget, window); +} + +// Implements GtkWidget::unrealize. +static void fl_gl_area_unrealize(GtkWidget* widget) { + FlGLArea* self = FL_GL_AREA(widget); + gdk_gl_context_make_current(self->context); + g_clear_object(&self->texture); + + /* Make sure to unset the context if current */ + if (self->context == gdk_gl_context_get_current()) + gdk_gl_context_clear_current(); + + GTK_WIDGET_CLASS(fl_gl_area_parent_class)->unrealize(widget); +} + +// Implements GtkWidget::size_allocate. +static void fl_gl_area_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) { + gtk_widget_set_allocation(widget, allocation); + + if (gtk_widget_get_has_window(widget)) { + if (gtk_widget_get_realized(widget)) + gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, + allocation->y, allocation->width, + allocation->height); + } +} + +// Implements GtkWidget::draw. +static gboolean fl_gl_area_draw(GtkWidget* widget, cairo_t* cr) { + FlGLArea* self = FL_GL_AREA(widget); + + gdk_gl_context_make_current(self->context); + + gint scale = gtk_widget_get_scale_factor(widget); + + if (self->texture) { + uint32_t texture = + fl_backing_store_provider_get_gl_texture_id(self->texture); + GdkRectangle geometry = + fl_backing_store_provider_get_geometry(self->texture); + gdk_cairo_draw_from_gl(cr, gtk_widget_get_window(widget), texture, + GL_TEXTURE, scale, geometry.x, geometry.y, + geometry.width, geometry.height); + gdk_gl_context_make_current(self->context); + } + + return TRUE; +} + +static void fl_gl_area_class_init(FlGLAreaClass* klass) { + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + gobject_class->dispose = fl_gl_area_dispose; + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + widget_class->realize = fl_gl_area_realize; + widget_class->unrealize = fl_gl_area_unrealize; + widget_class->size_allocate = fl_gl_area_size_allocate; + widget_class->draw = fl_gl_area_draw; + + gtk_widget_class_set_accessible_role(widget_class, ATK_ROLE_DRAWING_AREA); +} + +static void fl_gl_area_init(FlGLArea* self) { + gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE); + gtk_widget_set_app_paintable(GTK_WIDGET(self), TRUE); +} + +GtkWidget* fl_gl_area_new(GdkGLContext* context) { + g_return_val_if_fail(GDK_IS_GL_CONTEXT(context), nullptr); + FlGLArea* area = + reinterpret_cast(g_object_new(fl_gl_area_get_type(), nullptr)); + area->context = context; + return GTK_WIDGET(area); +} + +void fl_gl_area_queue_render(FlGLArea* self, FlBackingStoreProvider* texture) { + g_return_if_fail(FL_IS_GL_AREA(self)); + + g_clear_object(&self->texture); + g_set_object(&self->texture, texture); + + gtk_widget_queue_draw(GTK_WIDGET(self)); +} diff --git a/shell/platform/linux/fl_gl_area.h b/shell/platform/linux/fl_gl_area.h new file mode 100644 index 0000000000000..36bdd7cdd6744 --- /dev/null +++ b/shell/platform/linux/fl_gl_area.h @@ -0,0 +1,43 @@ +// 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_GL_AREA_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_GL_AREA_H_ + +#include + +#include "flutter/shell/platform/linux/fl_backing_store_provider.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlGLArea, fl_gl_area, FL, GL_AREA, GtkWidget) + +/** + * FlGLArea: + * + * #FlGLArea is a OpenGL drawing area that shows Flutter backing store Layer. + */ + +/** + * fl_gl_area_new: + * @context: an #GdkGLContext. + * + * Creates a new #FlGLArea widget. + * + * Returns: the newly created #FlGLArea widget. + */ +GtkWidget* fl_gl_area_new(GdkGLContext* context); + +/** + * fl_gl_area_queue_render: + * @area: an #FlGLArea. + * @texture: queued OpenGL texture. + * + * Queues a texture to be drawn later. + */ +void fl_gl_area_queue_render(FlGLArea* area, FlBackingStoreProvider* texture); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_GL_AREA_H_ diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index f44bd7b527c60..1b3e44399b32a 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -4,19 +4,19 @@ #include "fl_renderer.h" +#include +#include + #include "flutter/shell/platform/embedder/embedder.h" -#include "flutter/shell/platform/linux/egl_utils.h" +#include "flutter/shell/platform/linux/fl_backing_store_provider.h" G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error) typedef struct { - EGLDisplay egl_display; - EGLConfig egl_config; - EGLSurface egl_surface; - EGLContext egl_context; + FlView* view; - EGLSurface resource_surface; - EGLContext resource_context; + GdkGLContext* main_context; + GdkGLContext* resource_context; } FlRendererPrivate; G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT) @@ -25,188 +25,35 @@ static void fl_renderer_class_init(FlRendererClass* klass) {} static void fl_renderer_init(FlRenderer* self) {} -// Initializes EGL and gets a valid EGL OpenGL ES configuration. -static gboolean setup_egl_display(FlRenderer* self, GError** error) { - FlRendererPrivate* priv = - static_cast(fl_renderer_get_instance_private(self)); - - if (priv->egl_display != EGL_NO_DISPLAY) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "EGL display already set up"); - return FALSE; - } - - priv->egl_display = FL_RENDERER_GET_CLASS(self)->create_display(self); - - if (priv->egl_display == EGL_NO_DISPLAY) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL display"); - return FALSE; - } - - 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}; - EGLint n_config; - 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)) { - EGLint egl_error = eglGetError(); // Must be before egl_config_to_string(). - g_autofree gchar* config_string = - 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(egl_error)); - return FALSE; - } - - return TRUE; -} - -// Creates a GDK window that can be rendered to using EGL. -static gboolean setup_gdk_window(FlRenderer* self, - GtkWidget* widget, - GError** error) { - FlRendererPrivate* priv = - static_cast(fl_renderer_get_instance_private(self)); - - if (priv->egl_display == EGL_NO_DISPLAY) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Can not set up GDK window: EGL display not created"); - return FALSE; - } - - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - - GdkWindowAttr window_attributes; - window_attributes.window_type = GDK_WINDOW_CHILD; - window_attributes.x = allocation.x; - window_attributes.y = allocation.y; - window_attributes.width = allocation.width; - window_attributes.height = allocation.height; - window_attributes.wclass = GDK_INPUT_OUTPUT; - window_attributes.event_mask = - gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | - GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | GDK_SMOOTH_SCROLL_MASK | GDK_KEY_PRESS_MASK | - GDK_KEY_RELEASE_MASK; - - gint window_attributes_mask = GDK_WA_X | GDK_WA_Y; - - if (FL_RENDERER_GET_CLASS(self)->setup_window_attr) { - if (!FL_RENDERER_GET_CLASS(self)->setup_window_attr( - self, widget, priv->egl_display, priv->egl_config, - &window_attributes, &window_attributes_mask, error)) { - return FALSE; - } - } - - GdkWindow* window = - gdk_window_new(gtk_widget_get_parent_window(widget), &window_attributes, - window_attributes_mask); - gtk_widget_register_window(widget, window); - gtk_widget_set_window(widget, window); - - return TRUE; -} - -// Creates the EGL surfaces that Flutter will render to. -static gboolean setup_egl_surfaces(FlRenderer* self, - GtkWidget* widget, - GError** error) { - FlRendererPrivate* priv = - static_cast(fl_renderer_get_instance_private(self)); - - if (priv->egl_surface != EGL_NO_SURFACE || - priv->resource_surface != EGL_NO_SURFACE) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "setup_egl_surfaces() called after surfaces already created"); - return FALSE; - } - - if (!FL_RENDERER_GET_CLASS(self)->create_surfaces( - self, widget, priv->egl_display, priv->egl_config, &priv->egl_surface, - &priv->resource_surface, error)) { - return FALSE; - } - - EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - - priv->egl_context = eglCreateContext(priv->egl_display, priv->egl_config, - EGL_NO_CONTEXT, context_attributes); - priv->resource_context = - eglCreateContext(priv->egl_display, priv->egl_config, priv->egl_context, - context_attributes); - if (priv->egl_context == EGL_NO_CONTEXT || - priv->resource_context == EGL_NO_CONTEXT) { - EGLint egl_error = eglGetError(); // Must be before egl_config_to_string(). - g_autofree gchar* config_string = - 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 contexts using configuration (%s): %s", - config_string, egl_error_to_string(egl_error)); - return FALSE; - } - - return TRUE; -} - -gboolean fl_renderer_start(FlRenderer* self, - GtkWidget* widget, - GError** error) { +gboolean fl_renderer_start(FlRenderer* self, FlView* view, GError** error) { g_return_val_if_fail(FL_IS_RENDERER(self), FALSE); + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + priv->view = view; + gboolean result = FL_RENDERER_GET_CLASS(self)->create_contexts( + self, GTK_WIDGET(view), &priv->main_context, &priv->resource_context, + error); - if (!setup_egl_display(self, error)) { - return FALSE; + if (result) { + gdk_gl_context_realize(priv->main_context, error); + gdk_gl_context_realize(priv->resource_context, error); } - if (!setup_gdk_window(self, widget, error)) { + if (*error != nullptr) return FALSE; - } - - if (!setup_egl_surfaces(self, widget, error)) { - return FALSE; - } - return TRUE; } -void fl_renderer_set_geometry(FlRenderer* self, - GdkRectangle* geometry, - gint scale) { - g_return_if_fail(FL_IS_RENDERER(self)); +FlView* fl_renderer_get_view(FlRenderer* self) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + return priv->view; +} - if (FL_RENDERER_GET_CLASS(self)->set_geometry) { - FL_RENDERER_GET_CLASS(self)->set_geometry(self, geometry, scale); - } +GdkGLContext* fl_renderer_get_context(FlRenderer* self) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + return priv->main_context; } void* fl_renderer_get_proc_address(FlRenderer* self, const char* name) { @@ -214,62 +61,27 @@ void* fl_renderer_get_proc_address(FlRenderer* self, const char* name) { } gboolean fl_renderer_make_current(FlRenderer* self, GError** error) { - FlRendererPrivate* priv = - static_cast(fl_renderer_get_instance_private(self)); - - if (priv->egl_surface == EGL_NO_SURFACE || - priv->egl_context == EGL_NO_CONTEXT) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to make EGL context current: No surface created"); - return FALSE; - } - - 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: %s", - egl_error_to_string(eglGetError())); - return FALSE; + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + if (priv->main_context) { + gdk_gl_context_make_current(priv->main_context); } return TRUE; } gboolean fl_renderer_make_resource_current(FlRenderer* self, GError** error) { - FlRendererPrivate* priv = - static_cast(fl_renderer_get_instance_private(self)); - - if (priv->resource_surface == EGL_NO_SURFACE || - priv->resource_context == EGL_NO_CONTEXT) { - g_set_error( - error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to make EGL resource context current: No surface created"); - return FALSE; - } - - if (!eglMakeCurrent(priv->egl_display, priv->resource_surface, - priv->resource_surface, priv->resource_context)) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to make EGL resource context current: %s", - egl_error_to_string(eglGetError())); - return FALSE; + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + if (priv->resource_context) { + gdk_gl_context_make_current(priv->resource_context); } 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: %s", - egl_error_to_string(eglGetError())); - return FALSE; - } - + gdk_gl_context_clear_current(); return TRUE; } @@ -279,15 +91,27 @@ guint32 fl_renderer_get_fbo(FlRenderer* self) { } gboolean fl_renderer_present(FlRenderer* self, GError** error) { - FlRendererPrivate* priv = - static_cast(fl_renderer_get_instance_private(self)); + return TRUE; +} - 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: %s", - egl_error_to_string(eglGetError())); - return FALSE; - } +gboolean fl_renderer_create_backing_store( + FlRenderer* self, + const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out) { + return FL_RENDERER_GET_CLASS(self)->create_backing_store(self, config, + backing_store_out); +} - return TRUE; +gboolean fl_renderer_collect_backing_store( + FlRenderer* self, + const FlutterBackingStore* backing_store) { + return FL_RENDERER_GET_CLASS(self)->collect_backing_store(self, + backing_store); +} + +gboolean fl_renderer_present_layers(FlRenderer* self, + const FlutterLayer** layers, + size_t layers_count) { + return FL_RENDERER_GET_CLASS(self)->present_layers(self, layers, + layers_count); } diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h index d5f969e3db88e..17cacd4c41480 100644 --- a/shell/platform/linux/fl_renderer.h +++ b/shell/platform/linux/fl_renderer.h @@ -5,12 +5,13 @@ #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" #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" +#include "flutter/shell/platform/embedder/embedder.h" + G_BEGIN_DECLS /** @@ -36,66 +37,72 @@ struct _FlRendererClass { GObjectClass parent_class; /** - * Virtual method called before creating a GdkWindow for the widget. - * Does not need to be implemented. + * Virtual method called when Flutter needs #GdkGLContext to render. * @renderer: an #FlRenderer. * @widget: the widget being rendered on. - * @display: display to create surfaces on. - * @config: EGL configuration. - * @window_attributes: window attributes to modify. - * @mask: (out): the window mask to use. + * @visible: (out): the GL context for visible surface. + * @resource: (out): the GL context for resource loading. * @error: (allow-none): #GError location to store the error occurring, or * %NULL to ignore. * - * Returns: %TRUE if the window is successfully set up. + * Returns: %TRUE if both contexts were created, %FALSE if there was an error. */ - gboolean (*setup_window_attr)(FlRenderer* renderer, - GtkWidget* widget, - EGLDisplay display, - EGLConfig config, - GdkWindowAttr* window_attributes, - gint* mask, - GError** error); + gboolean (*create_contexts)(FlRenderer* renderer, + GtkWidget* widget, + GdkGLContext** visible, + GdkGLContext** resource, + GError** error); /** - * Virtual method to create a new EGL display. + * Virtual method called when Flutter needs OpenGL proc address. + * @renderer: an #FlRenderer. + * @name: proc name. + * + * Returns: OpenGL proc address. */ - EGLDisplay (*create_display)(FlRenderer* renderer); + void* (*get_proc_address)(); /** - * Virtual method called when Flutter needs surfaces to render to. + * Virtual method called when Flutter needs a backing store for a specific + * #FlutterLayer. * @renderer: an #FlRenderer. - * @widget: the widget being rendered on. - * @display: display to create surfaces on. - * @config: EGL configuration. - * @visible: (out): the visible surface that is created. - * @resource: (out): the resource surface that is created. - * @error: (allow-none): #GError location to store the error occurring, or - * %NULL to ignore. + * @config: backing store config. + * @backing_store_out: saves created backing store. * - * Returns: %TRUE if both surfaces were created, %FALSE if there was an error. + * Returns %TRUE if successful. */ - gboolean (*create_surfaces)(FlRenderer* renderer, - GtkWidget* widget, - EGLDisplay display, - EGLConfig config, - EGLSurface* visible, - EGLSurface* resource, - GError** error); + gboolean (*create_backing_store)(FlRenderer* renderer, + const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out); + + /** + * Virtual method called when Flutter wants to release the backing store. + * @renderer: an #FlRenderer. + * @backing_store: backing store to be released. + * + * Returns %TRUE if successful. + */ + gboolean (*collect_backing_store)(FlRenderer* renderer, + const FlutterBackingStore* backing_store); /** - * Virtual method called when the EGL window needs to be resized. - * Does not need to be implemented. + * Virtual method called when Flutter wants to composite layers onto the + * screen. + * @renderer: an #FlRenderer. + * @layers: layers to be composited. + * @layers_count: number of layers. + * + * Returns %TRUE if successful. */ - void (*set_geometry)(FlRenderer* renderer, - GdkRectangle* geometry, - gint scale); + gboolean (*present_layers)(FlRenderer* renderer, + const FlutterLayer** layers, + size_t layers_count); }; /** * fl_renderer_start: * @renderer: an #FlRenderer. - * @widget: the widget Flutter is renderering to. + * @view: the view Flutter is renderering to. * @error: (allow-none): #GError location to store the error occurring, or %NULL * to ignore. * @@ -103,19 +110,23 @@ struct _FlRendererClass { * * Returns: %TRUE if successfully started. */ -gboolean fl_renderer_start(FlRenderer* renderer, - GtkWidget* widget, - GError** error); +gboolean fl_renderer_start(FlRenderer* renderer, FlView* view, GError** error); + +/** + * fl_renderer_get_view: + * @renderer: an #FlRenderer. + * + * Returns: targeted #FlView or %NULL if headless. + */ +FlView* fl_renderer_get_view(FlRenderer* renderer); /** - * fl_renderer_set_geometry: + * fl_renderer_get_context: * @renderer: an #FlRenderer. - * @geometry: New size and position (unscaled) of the EGL window. - * @scale: Scale of the window. + * + * Returns: GL context for GLAreas or %NULL if headless. */ -void fl_renderer_set_geometry(FlRenderer* renderer, - GdkRectangle* geometry, - gint scale); +GdkGLContext* fl_renderer_get_context(FlRenderer* renderer); /** * fl_renderer_get_proc_address: @@ -187,6 +198,50 @@ guint32 fl_renderer_get_fbo(FlRenderer* renderer); */ gboolean fl_renderer_present(FlRenderer* renderer, GError** error); +/** + * fl_renderer_create_backing_store: + * @renderer: an #FlRenderer. + * @config: backing store config. + * @backing_store_out: saves created backing store. + * + * Obtain a backing store for a specific #FlutterLayer. + * + * Returns %TRUE if successful. + */ +gboolean fl_renderer_create_backing_store( + FlRenderer* renderer, + const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out); + +/** + * fl_renderer_collect_backing_store: + * @renderer: an #FlRenderer. + * @backing_store: backing store to be released. + * + * A callback invoked by the engine to release the backing store. The + * embedder may collect any resources associated with the backing store. + * + * Returns %TRUE if successful. + */ +gboolean fl_renderer_collect_backing_store( + FlRenderer* renderer, + const FlutterBackingStore* backing_store); + +/** + * fl_renderer_present_layers: + * @renderer: an #FlRenderer. + * @layers: layers to be composited. + * @layers_count: number of layers. + * + * Callback invoked by the engine to composite the contents of each layer + * onto the screen. + * + * Returns %TRUE if successful. + */ +gboolean fl_renderer_present_layers(FlRenderer* renderer, + const FlutterLayer** layers, + size_t layers_count); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_ diff --git a/shell/platform/linux/fl_renderer_gl.cc b/shell/platform/linux/fl_renderer_gl.cc new file mode 100644 index 0000000000000..09be16586dba6 --- /dev/null +++ b/shell/platform/linux/fl_renderer_gl.cc @@ -0,0 +1,130 @@ +// 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/fl_renderer_gl.h" + +#include "flutter/shell/platform/linux/fl_backing_store_provider.h" +#include "flutter/shell/platform/linux/fl_view_private.h" + +struct _FlRendererGL { + FlRenderer parent_instance; +}; + +G_DEFINE_TYPE(FlRendererGL, fl_renderer_gl, fl_renderer_get_type()) + +// Implements FlRenderer::create_contexts. +static gboolean fl_renderer_gl_create_contexts(FlRenderer* renderer, + GtkWidget* widget, + GdkGLContext** visible, + GdkGLContext** resource, + GError** error) { + GdkWindow* window = gtk_widget_get_parent_window(widget); + + *visible = gdk_window_create_gl_context(window, error); + + if (*error != nullptr) + return FALSE; + + *resource = gdk_window_create_gl_context(window, error); + + if (*error != nullptr) + return FALSE; + return TRUE; +} + +// Implements FlRenderer::create_backing_store. +static gboolean fl_renderer_gl_create_backing_store( + FlRenderer* renderer, + const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out) { + g_autoptr(GError) error = nullptr; + gboolean result = fl_renderer_make_current(renderer, &error); + if (!result) { + g_warning("Failed to make renderer current when creating backing store: %s", + error->message); + return FALSE; + } + + FlBackingStoreProvider* provider = + fl_backing_store_provider_new(config->size.width, config->size.height); + if (!provider) { + g_warning("Failed to create backing store"); + return FALSE; + } + + uint32_t name = fl_backing_store_provider_get_gl_framebuffer_id(provider); + uint32_t format = fl_backing_store_provider_get_gl_format(provider); + + backing_store_out->type = kFlutterBackingStoreTypeOpenGL; + backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + backing_store_out->open_gl.framebuffer.user_data = provider; + backing_store_out->open_gl.framebuffer.name = name; + backing_store_out->open_gl.framebuffer.target = format; + + return TRUE; +} + +// Implements FlRenderer::collect_backing_store. +static gboolean fl_renderer_gl_collect_backing_store( + FlRenderer* renderer, + const FlutterBackingStore* backing_store) { + g_autoptr(GError) error = nullptr; + gboolean result = fl_renderer_make_current(renderer, &error); + if (!result) { + g_warning( + "Failed to make renderer current when collecting backing store: %s", + error->message); + return FALSE; + } + + // OpenGL context is required when destroying #FlBackingStoreProvider. + g_object_unref(backing_store->open_gl.framebuffer.user_data); + return TRUE; +} + +// Implements FlRenderer::present_layers. +static gboolean fl_renderer_gl_present_layers(FlRenderer* renderer, + const FlutterLayer** layers, + size_t layers_count) { + FlView* view = fl_renderer_get_view(renderer); + GdkGLContext* context = fl_renderer_get_context(renderer); + if (!view || !context) + return FALSE; + fl_view_begin_frame(view); + + for (size_t i = 0; i < layers_count; ++i) { + const FlutterLayer* layer = layers[i]; + switch (layer->type) { + case kFlutterLayerContentTypeBackingStore: { + const FlutterBackingStore* backing_store = layer->backing_store; + auto framebuffer = &backing_store->open_gl.framebuffer; + g_object_ref(context); + fl_view_add_gl_area( + view, context, + reinterpret_cast(framebuffer->user_data)); + } break; + case kFlutterLayerContentTypePlatformView: { + // Currently unsupported. + } break; + } + } + + fl_view_end_frame(view); + return TRUE; +} + +static void fl_renderer_gl_class_init(FlRendererGLClass* klass) { + FL_RENDERER_CLASS(klass)->create_contexts = fl_renderer_gl_create_contexts; + FL_RENDERER_CLASS(klass)->create_backing_store = + fl_renderer_gl_create_backing_store; + FL_RENDERER_CLASS(klass)->collect_backing_store = + fl_renderer_gl_collect_backing_store; + FL_RENDERER_CLASS(klass)->present_layers = fl_renderer_gl_present_layers; +} + +static void fl_renderer_gl_init(FlRendererGL* self) {} + +FlRendererGL* fl_renderer_gl_new() { + return FL_RENDERER_GL(g_object_new(fl_renderer_gl_get_type(), nullptr)); +} diff --git a/shell/platform/linux/fl_renderer_gl.h b/shell/platform/linux/fl_renderer_gl.h new file mode 100644 index 0000000000000..25216b49cab10 --- /dev/null +++ b/shell/platform/linux/fl_renderer_gl.h @@ -0,0 +1,31 @@ +// 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_GL_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_GL_H_ + +#include "flutter/shell/platform/linux/fl_renderer.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlRendererGL, fl_renderer_gl, FL, RENDERER_GL, FlRenderer) + +/** + * FlRendererGL: + * + * #FlRendererGL is an implementation of #FlRenderer that renders by OpenGL ES. + */ + +/** + * fl_renderer_gl_new: + * + * Creates an object that allows Flutter to render by OpenGL ES. + * + * Returns: a new #FlRendererGL. + */ +FlRendererGL* fl_renderer_gl_new(); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_GL_H_ diff --git a/shell/platform/linux/fl_renderer_headless.cc b/shell/platform/linux/fl_renderer_headless.cc index 21abad74dd251..19d2da931e8e2 100644 --- a/shell/platform/linux/fl_renderer_headless.cc +++ b/shell/platform/linux/fl_renderer_headless.cc @@ -10,19 +10,46 @@ struct _FlRendererHeadless { G_DEFINE_TYPE(FlRendererHeadless, fl_renderer_headless, fl_renderer_get_type()) -static gboolean fl_renderer_headless_create_surfaces(FlRenderer* renderer, +// Implements FlRenderer::create_contexts. +static gboolean fl_renderer_headless_create_contexts(FlRenderer* renderer, GtkWidget* widget, - EGLDisplay display, - EGLConfig config, - EGLSurface* visible, - EGLSurface* resource, + GdkGLContext** visible, + GdkGLContext** resource, GError** error) { return FALSE; } +// Implements FlRenderer::create_backing_store. +static gboolean fl_renderer_headless_create_backing_store( + FlRenderer* renderer, + const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out) { + return FALSE; +} + +// Implements FlRenderer::collect_backing_store. +static gboolean fl_renderer_headless_collect_backing_store( + FlRenderer* self, + const FlutterBackingStore* backing_store) { + return FALSE; +} + +// Implements FlRenderer::present_layers. +static gboolean fl_renderer_headless_present_layers(FlRenderer* self, + const FlutterLayer** layers, + size_t layers_count) { + return FALSE; +} + static void fl_renderer_headless_class_init(FlRendererHeadlessClass* klass) { - FL_RENDERER_CLASS(klass)->create_surfaces = - fl_renderer_headless_create_surfaces; + FL_RENDERER_CLASS(klass)->create_contexts = + fl_renderer_headless_create_contexts; + FL_RENDERER_CLASS(klass)->create_backing_store = + fl_renderer_headless_create_backing_store; + FL_RENDERER_CLASS(klass)->collect_backing_store = + fl_renderer_headless_collect_backing_store; + FL_RENDERER_CLASS(klass)->present_layers = + fl_renderer_headless_present_layers; } static void fl_renderer_headless_init(FlRendererHeadless* self) {} diff --git a/shell/platform/linux/fl_renderer_wayland.cc b/shell/platform/linux/fl_renderer_wayland.cc deleted file mode 100644 index de186bba2f903..0000000000000 --- a/shell/platform/linux/fl_renderer_wayland.cc +++ /dev/null @@ -1,297 +0,0 @@ -// 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. - -#define WL_EGL_PLATFORM - -#include "fl_renderer_wayland.h" -#include "flutter/shell/platform/linux/egl_utils.h" - -#include -#include -#include - -struct _FlRendererWayland { - FlRenderer parent_instance; - wl_registry* registry; - wl_subcompositor* subcompositor; - - struct { - wl_subsurface* subsurface; - wl_surface* surface; - wl_egl_window* egl_window; - GdkRectangle geometry; - gint scale; - } subsurface; - - // The resource surface will not be mapped, but needs to be a wl_surface - // because ONLY window EGL surfaces are supported on Wayland. - struct { - wl_surface* surface; - wl_egl_window* egl_window; - } resource; -}; - -G_DEFINE_TYPE(FlRendererWayland, fl_renderer_wayland, fl_renderer_get_type()) - -// wl_registry.global callback. -static void registry_handle_global(void* data, - wl_registry* registry, - uint32_t id, - const char* name, - uint32_t max_version) { - FlRendererWayland* self = FL_RENDERER_WAYLAND(data); - if (strcmp(name, wl_subcompositor_interface.name) == 0) { - uint32_t version = MIN( - static_cast(wl_subcompositor_interface.version), max_version); - self->subcompositor = static_cast( - wl_registry_bind(registry, id, &wl_subcompositor_interface, version)); - } -} - -// wl_registry.global_remove callback. -// Can be safely ignored unless we bind to globals that might be removed. -static void registry_handle_global_remove(void*, wl_registry*, uint32_t) {} - -static const wl_registry_listener registry_listener = { - .global = registry_handle_global, - .global_remove = registry_handle_global_remove, -}; - -// The first time this function is called, all Wayland globals are initialized -// (which blocks for a round trip to the Wayland compositor). -// Subsequent calls return immediately. -static void fl_renderer_wayland_lazy_init_wl(FlRendererWayland* self) { - if (self->registry) { - return; - } - - GdkWaylandDisplay* gdk_display = - GDK_WAYLAND_DISPLAY(gdk_display_get_default()); - g_return_if_fail(gdk_display); - - wl_display* display = gdk_wayland_display_get_wl_display(gdk_display); - self->registry = wl_display_get_registry(display); - wl_registry_add_listener(self->registry, ®istry_listener, self); - wl_display_roundtrip(display); -} - -// Implements GObject::dispose. -static void fl_renderer_wayland_dispose(GObject* object) { - FlRendererWayland* self = FL_RENDERER_WAYLAND(object); - - g_clear_pointer(&self->registry, wl_registry_destroy); - g_clear_pointer(&self->subcompositor, wl_subcompositor_destroy); - - g_clear_pointer(&self->subsurface.subsurface, wl_subsurface_destroy); - g_clear_pointer(&self->subsurface.egl_window, wl_egl_window_destroy); - g_clear_pointer(&self->subsurface.surface, wl_surface_destroy); - - g_clear_pointer(&self->resource.egl_window, wl_egl_window_destroy); - g_clear_pointer(&self->resource.surface, wl_surface_destroy); - - G_OBJECT_CLASS(fl_renderer_wayland_parent_class)->dispose(object); -} - -// Implements FlRenderer::create_display. -static EGLDisplay fl_renderer_wayland_create_display(FlRenderer* /*renderer*/) { - GdkWaylandDisplay* gdk_display = - GDK_WAYLAND_DISPLAY(gdk_display_get_default()); - g_return_val_if_fail(gdk_display, nullptr); - return eglGetDisplay(gdk_wayland_display_get_wl_display(gdk_display)); -} - -static void fl_renderer_wayland_on_window_map(FlRendererWayland* self, - GtkWidget* widget) { - if (self->subsurface.subsurface) { - g_error("fl_renderer_wayland_on_window_map: already has a subsurface"); - return; - } - - GdkWaylandDisplay* gdk_display = - GDK_WAYLAND_DISPLAY(gdk_display_get_default()); - - wl_compositor* compositor = - gdk_wayland_display_get_wl_compositor(gdk_display); - - fl_renderer_wayland_lazy_init_wl(self); - if (!self->subcompositor) { - g_error( - "fl_renderer_wayland_on_window_map: could not bind to " - "wl_subcompositor"); - return; - } - - GdkWaylandWindow* window = GDK_WAYLAND_WINDOW(gtk_widget_get_window(widget)); - if (!window) { - g_error("fl_renderer_wayland_on_window_map: not a Wayland window"); - return; - } - wl_surface* toplevel_surface = gdk_wayland_window_get_wl_surface(window); - if (!toplevel_surface) { - g_error("fl_renderer_wayland_on_window_map: could not get wl_surface"); - return; - } - - self->subsurface.subsurface = wl_subcompositor_get_subsurface( - self->subcompositor, self->subsurface.surface, toplevel_surface); - if (!self->subsurface.subsurface) { - g_error("fl_renderer_wayland_on_window_map: could not create subsurface"); - return; - } - - wl_subsurface_set_desync(self->subsurface.subsurface); - wl_subsurface_set_position(self->subsurface.subsurface, - self->subsurface.geometry.x, - self->subsurface.geometry.y); - - // Give the subsurface an empty input region so the main surface gets input. - wl_region* region = wl_compositor_create_region(compositor); - wl_surface_set_input_region(self->subsurface.surface, region); - wl_region_destroy(region); - - wl_surface_commit(self->subsurface.surface); -} - -static void fl_renderer_wayland_on_window_unmap(FlRendererWayland* self, - GtkWidget* widget) { - g_clear_pointer(&self->subsurface.subsurface, wl_subsurface_destroy); -} - -// Implements FlRenderer::create_surfaces. -static gboolean fl_renderer_wayland_create_surfaces(FlRenderer* renderer, - GtkWidget* widget, - EGLDisplay display, - EGLConfig config, - EGLSurface* visible, - EGLSurface* resource, - GError** error) { - FlRendererWayland* self = FL_RENDERER_WAYLAND(renderer); - - if (self->subsurface.surface || self->subsurface.egl_window || - self->resource.surface || self->resource.egl_window) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Surfaces already created"); - return FALSE; - } - - if (!GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Expected Wayland display"); - return FALSE; - } - GdkWaylandDisplay* gdk_display = - GDK_WAYLAND_DISPLAY(gdk_display_get_default()); - - wl_compositor* compositor = - gdk_wayland_display_get_wl_compositor(gdk_display); - if (!compositor) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "No wl_compositor"); - return FALSE; - } - - // Make sure size and scale is not <= 0 - self->subsurface.scale = MAX(self->subsurface.scale, 1); - self->subsurface.geometry.width = MAX(self->subsurface.geometry.width, 1); - self->subsurface.geometry.height = MAX(self->subsurface.geometry.height, 1); - - self->subsurface.surface = wl_compositor_create_surface(compositor); - self->resource.surface = wl_compositor_create_surface(compositor); - if (!self->subsurface.surface || !self->resource.surface) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create wl_surfaces"); - return FALSE; - } - wl_surface_set_buffer_scale(self->subsurface.surface, self->subsurface.scale); - - gint window_width = self->subsurface.geometry.width * self->subsurface.scale; - gint window_height = - self->subsurface.geometry.height * self->subsurface.scale; - self->subsurface.egl_window = wl_egl_window_create( - self->subsurface.surface, window_width, window_height); - self->resource.egl_window = - wl_egl_window_create(self->resource.surface, 1, 1); - if (!self->subsurface.egl_window || !self->resource.egl_window) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL windows"); - return FALSE; - } - - *visible = eglCreateWindowSurface(display, config, - self->subsurface.egl_window, nullptr); - *resource = eglCreateWindowSurface(display, config, self->resource.egl_window, - nullptr); - if (*visible == EGL_NO_SURFACE || *resource == EGL_NO_SURFACE) { - EGLint egl_error = eglGetError(); // must be before egl_config_to_string() - g_autofree gchar* config_string = egl_config_to_string(display, config); - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL surfaces using configuration (%s): %s", - config_string, egl_error_to_string(egl_error)); - return FALSE; - } - - GtkWidget* toplevel = gtk_widget_get_toplevel(widget); - if (!toplevel) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Renderer does not have a widget"); - return FALSE; - } - g_signal_connect_object(toplevel, "map", - G_CALLBACK(fl_renderer_wayland_on_window_map), self, - G_CONNECT_SWAPPED); - g_signal_connect_object(toplevel, "unmap", - G_CALLBACK(fl_renderer_wayland_on_window_unmap), self, - G_CONNECT_SWAPPED); - if (gtk_widget_get_mapped(toplevel)) { - fl_renderer_wayland_on_window_map(self, toplevel); - } - - return TRUE; -} - -// Implements FlRenderer::set_geometry. -static void fl_renderer_wayland_set_geometry(FlRenderer* renderer, - GdkRectangle* geometry, - gint scale) { - FlRendererWayland* self = FL_RENDERER_WAYLAND(renderer); - - if (scale != self->subsurface.scale && self->subsurface.surface) { - wl_surface_set_buffer_scale(self->subsurface.surface, scale); - } - - // NOTE: position is unscaled but size is scaled. - - if ((geometry->x != self->subsurface.geometry.x || - geometry->y != self->subsurface.geometry.y) && - self->subsurface.subsurface) { - wl_subsurface_set_position(self->subsurface.subsurface, geometry->x, - geometry->y); - } - - if ((geometry->width != self->subsurface.geometry.width || - geometry->height != self->subsurface.geometry.height || - scale != self->subsurface.scale) && - self->subsurface.egl_window) { - wl_egl_window_resize(self->subsurface.egl_window, geometry->width * scale, - geometry->height * scale, 0, 0); - } - - self->subsurface.geometry = *geometry; - self->subsurface.scale = scale; -} - -static void fl_renderer_wayland_class_init(FlRendererWaylandClass* klass) { - G_OBJECT_CLASS(klass)->dispose = fl_renderer_wayland_dispose; - FL_RENDERER_CLASS(klass)->create_display = fl_renderer_wayland_create_display; - FL_RENDERER_CLASS(klass)->create_surfaces = - fl_renderer_wayland_create_surfaces; - FL_RENDERER_CLASS(klass)->set_geometry = fl_renderer_wayland_set_geometry; -} - -static void fl_renderer_wayland_init(FlRendererWayland* self) {} - -FlRendererWayland* fl_renderer_wayland_new() { - return FL_RENDERER_WAYLAND( - g_object_new(fl_renderer_wayland_get_type(), nullptr)); -} diff --git a/shell/platform/linux/fl_renderer_wayland.h b/shell/platform/linux/fl_renderer_wayland.h deleted file mode 100644 index eda17587af92d..0000000000000 --- a/shell/platform/linux/fl_renderer_wayland.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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_WAYLAND_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_WAYLAND_H_ - -#include "flutter/shell/platform/linux/fl_renderer.h" - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE(FlRendererWayland, - fl_renderer_wayland, - FL, - RENDERER_WAYLAND, - FlRenderer) - -/** - * FlRendererWayland: - * - * #FlRendererWayland is an implementation of a #FlRenderer that renders to - * Wayland surfaces. - */ - -/** - * fl_renderer_wayland_new: - * - * Create an object that allows Flutter to render to Wayland surfaces. - * - * Returns: a #FlRendererWayland. - */ -FlRendererWayland* fl_renderer_wayland_new(); - -G_END_DECLS - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_WAYLAND_H_ diff --git a/shell/platform/linux/fl_renderer_x11.cc b/shell/platform/linux/fl_renderer_x11.cc deleted file mode 100644 index 2e9d7c147996a..0000000000000 --- a/shell/platform/linux/fl_renderer_x11.cc +++ /dev/null @@ -1,137 +0,0 @@ -// 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" -#ifdef GDK_WINDOWING_X11 - -#include - -#include "flutter/shell/platform/linux/egl_utils.h" - -struct _FlRendererX11 { - FlRenderer parent_instance; - - // Connection to the X server. - Display* display; -}; - -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); - - if (self->display != nullptr) { - XCloseDisplay(self->display); - self->display = nullptr; - } - - G_OBJECT_CLASS(fl_renderer_x11_parent_class)->dispose(object); -} - -// Implements FlRenderer::setup_window_attr. -static gboolean fl_renderer_x11_setup_window_attr( - FlRenderer* renderer, - GtkWidget* widget, - EGLDisplay display, - EGLConfig config, - GdkWindowAttr* window_attributes, - gint* mask, - GError** error) { - EGLint visual_id; - if (!eglGetConfigAttrib(display, 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 FALSE; - } - - GdkX11Screen* screen = GDK_X11_SCREEN(gtk_widget_get_screen(widget)); - if (!screen) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "View widget is not on an X11 screen"); - return FALSE; - } - - window_attributes->visual = gdk_x11_screen_lookup_visual(screen, visual_id); - if (window_attributes->visual == nullptr) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to find visual 0x%x", visual_id); - return FALSE; - } - - *mask |= GDK_WA_VISUAL; - - return TRUE; -} - -// Implements FlRenderer::create_display. -static EGLDisplay fl_renderer_x11_create_display(FlRenderer* renderer) { - FlRendererX11* self = FL_RENDERER_X11(renderer); - - // Create a dedicated connection to the X server because the EGL calls are - // made from Flutter on a different thread to GTK. Re-using the existing - // GTK X11 connection would crash as Xlib is not thread safe. - if (self->display == nullptr) { - Display* display = gdk_x11_get_default_xdisplay(); - self->display = XOpenDisplay(DisplayString(display)); - } - - return eglGetDisplay(self->display); -} - -// Implements FlRenderer::create_surfaces. -static gboolean fl_renderer_x11_create_surfaces(FlRenderer* renderer, - GtkWidget* widget, - EGLDisplay display, - EGLConfig config, - EGLSurface* visible, - EGLSurface* resource, - GError** error) { - GdkWindow* window = gtk_widget_get_window(widget); - if (!GDK_IS_X11_WINDOW(window)) { - g_set_error( - error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Can not create EGL surface: view doesn't have an X11 GDK window"); - return FALSE; - } - - *visible = eglCreateWindowSurface(display, config, - gdk_x11_window_get_xid(window), nullptr); - if (*visible == EGL_NO_SURFACE) { - EGLint egl_error = eglGetError(); // Must be before egl_config_to_string(). - g_autofree gchar* config_string = egl_config_to_string(display, 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(egl_error)); - return FALSE; - } - - const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; - *resource = eglCreatePbufferSurface(display, config, attribs); - if (*resource == EGL_NO_SURFACE) { - EGLint egl_error = eglGetError(); // Must be before egl_config_to_string(). - g_autofree gchar* config_string = egl_config_to_string(display, config); - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL resource using configuration (%s): %s", - config_string, egl_error_to_string(egl_error)); - return FALSE; - } - - return TRUE; -} - -static void fl_renderer_x11_class_init(FlRendererX11Class* klass) { - G_OBJECT_CLASS(klass)->dispose = fl_renderer_x11_dispose; - FL_RENDERER_CLASS(klass)->setup_window_attr = - fl_renderer_x11_setup_window_attr; - FL_RENDERER_CLASS(klass)->create_display = fl_renderer_x11_create_display; - FL_RENDERER_CLASS(klass)->create_surfaces = fl_renderer_x11_create_surfaces; -} - -static void fl_renderer_x11_init(FlRendererX11* self) {} - -FlRendererX11* fl_renderer_x11_new() { - return FL_RENDERER_X11(g_object_new(fl_renderer_x11_get_type(), nullptr)); -} - -#endif // GDK_WINDOWING_X11 diff --git a/shell/platform/linux/fl_renderer_x11.h b/shell/platform/linux/fl_renderer_x11.h deleted file mode 100644 index d8d3b60f25a6c..0000000000000 --- a/shell/platform/linux/fl_renderer_x11.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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 - -#ifdef GDK_WINDOWING_X11 -#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 #FlRenderer that renders to X11 - * windows. - */ - -/** - * fl_renderer_x11_new: - * - * Creates an object that allows Flutter to render to X11 windows. - * - * Returns: a new #FlRendererX11. - */ -FlRendererX11* fl_renderer_x11_new(); - -G_END_DECLS - -#endif // GDK_WINDOWING_X11 - -#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 4c63f4a0b3fb6..ea6f2f89ec634 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -4,10 +4,8 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif +#include "flutter/shell/platform/linux/fl_view_private.h" + #include #include "flutter/shell/platform/linux/fl_accessibility_plugin.h" @@ -16,8 +14,7 @@ #include "flutter/shell/platform/linux/fl_mouse_cursor_plugin.h" #include "flutter/shell/platform/linux/fl_platform_plugin.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" -#include "flutter/shell/platform/linux/fl_renderer_wayland.h" -#include "flutter/shell/platform/linux/fl_renderer_x11.h" +#include "flutter/shell/platform/linux/fl_renderer_gl.h" #include "flutter/shell/platform/linux/fl_text_input_plugin.h" #include "flutter/shell/platform/linux/fl_view_accessible.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" @@ -26,7 +23,7 @@ static constexpr int kMicrosecondsPerMillisecond = 1000; struct _FlView { - GtkWidget parent_instance; + GtkContainer parent_instance; // Project being run. FlDartProject* project; @@ -46,8 +43,21 @@ struct _FlView { FlMouseCursorPlugin* mouse_cursor_plugin; FlPlatformPlugin* platform_plugin; FlTextInputPlugin* text_input_plugin; + + GList* gl_area_list; + GList* used_area_list; + + GtkWidget* event_box; + + GList* children_list; + GList* pending_children_list; }; +typedef struct _FlViewChild { + GtkWidget* widget; + GdkRectangle geometry; +} FlViewChild; + enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST }; static void fl_view_plugin_registry_iface_init( @@ -56,7 +66,7 @@ static void fl_view_plugin_registry_iface_init( G_DEFINE_TYPE_WITH_CODE( FlView, fl_view, - GTK_TYPE_WIDGET, + GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(), fl_view_plugin_registry_iface_init)) @@ -127,7 +137,6 @@ static void fl_view_geometry_changed(FlView* self) { fl_engine_send_window_metrics_event( self->engine, allocation.width * scale_factor, allocation.height * scale_factor, scale_factor); - fl_renderer_set_geometry(self->renderer, &allocation, scale_factor); } // Implements FlPluginRegistry::get_registrar_for_plugin. @@ -145,27 +154,26 @@ static void fl_view_plugin_registry_iface_init( iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin; } -static FlRenderer* fl_view_get_renderer_for_display(GdkDisplay* display) { -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY(display)) { - return FL_RENDERER(fl_renderer_x11_new()); - } -#endif +static gboolean event_box_button_release_event(GtkWidget* widget, + GdkEventButton* event, + FlView* view); - if (GDK_IS_WAYLAND_DISPLAY(display)) { - return FL_RENDERER(fl_renderer_wayland_new()); - } +static gboolean event_box_button_press_event(GtkWidget* widget, + GdkEventButton* event, + FlView* view); - g_error("Unsupported GDK backend"); +static gboolean event_box_scroll_event(GtkWidget* widget, + GdkEventScroll* event, + FlView* view); - return nullptr; -} +static gboolean event_box_motion_notify_event(GtkWidget* widget, + GdkEventMotion* event, + FlView* view); static void fl_view_constructed(GObject* object) { FlView* self = FL_VIEW(object); - GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(self)); - self->renderer = fl_view_get_renderer_for_display(display); + self->renderer = FL_RENDERER(fl_renderer_gl_new()); self->engine = fl_engine_new(self->project, self->renderer); fl_engine_set_update_semantics_node_handler( self->engine, fl_view_update_semantics_node_cb, self, nullptr); @@ -178,6 +186,23 @@ static void fl_view_constructed(GObject* object) { fl_key_event_plugin_new(messenger, self->text_input_plugin); self->mouse_cursor_plugin = fl_mouse_cursor_plugin_new(messenger, self); self->platform_plugin = fl_platform_plugin_new(messenger); + + self->event_box = gtk_event_box_new(); + gtk_widget_set_parent(self->event_box, GTK_WIDGET(self)); + gtk_widget_show(self->event_box); + gtk_widget_add_events(self->event_box, + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | + GDK_SMOOTH_SCROLL_MASK); + + g_signal_connect(self->event_box, "button-press-event", + G_CALLBACK(event_box_button_press_event), self); + g_signal_connect(self->event_box, "button-release-event", + G_CALLBACK(event_box_button_release_event), self); + g_signal_connect(self->event_box, "scroll-event", + G_CALLBACK(event_box_scroll_event), self); + g_signal_connect(self->event_box, "motion-notify-event", + G_CALLBACK(event_box_motion_notify_event), self); } static void fl_view_set_property(GObject* object, @@ -241,6 +266,8 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->mouse_cursor_plugin); g_clear_object(&self->platform_plugin); g_clear_object(&self->text_input_plugin); + g_list_free_full(self->gl_area_list, g_object_unref); + self->gl_area_list = nullptr; G_OBJECT_CLASS(fl_view_parent_class)->dispose(object); } @@ -250,9 +277,28 @@ static void fl_view_realize(GtkWidget* widget) { FlView* self = FL_VIEW(widget); g_autoptr(GError) error = nullptr; + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + gtk_widget_set_realized(widget, TRUE); - if (!fl_renderer_start(self->renderer, widget, &error)) { + GdkWindowAttr attributes; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual(widget); + attributes.event_mask = + gtk_widget_get_events(widget) | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK; + gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; + GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), + &attributes, attributes_mask); + gtk_widget_set_window(widget, window); + gtk_widget_register_window(widget, window); + + if (!fl_renderer_start(self->renderer, self, &error)) { g_warning("Failed to start Flutter renderer: %s", error->message); return; } @@ -263,6 +309,52 @@ static void fl_view_realize(GtkWidget* widget) { } } +static void fl_view_get_preferred_width(GtkWidget* widget, + gint* minimum, + gint* natural) { + FlView* self = FL_VIEW(widget); + gint child_min, child_nat; + + *minimum = 0; + *natural = 0; + + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + FlViewChild* child = reinterpret_cast(iterator->data); + + if (!gtk_widget_get_visible(child->widget)) + continue; + + gtk_widget_get_preferred_width(child->widget, &child_min, &child_nat); + + *minimum = MAX(*minimum, child->geometry.x + child_min); + *natural = MAX(*natural, child->geometry.x + child_nat); + } +} + +static void fl_view_get_preferred_height(GtkWidget* widget, + gint* minimum, + gint* natural) { + FlView* self = FL_VIEW(widget); + gint child_min, child_nat; + + *minimum = 0; + *natural = 0; + + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + FlViewChild* child = reinterpret_cast(iterator->data); + + if (!gtk_widget_get_visible(child->widget)) + continue; + + gtk_widget_get_preferred_height(child->widget, &child_min, &child_nat); + + *minimum = MAX(*minimum, child->geometry.y + child_min); + *natural = MAX(*natural, child->geometry.y + child_nat); + } +} + // Implements GtkWidget::size-allocate. static void fl_view_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { @@ -270,15 +362,67 @@ static void fl_view_size_allocate(GtkWidget* widget, gtk_widget_set_allocation(widget, allocation); - if (gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) { - gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, - allocation->y, allocation->width, - allocation->height); + if (gtk_widget_get_has_window(widget)) { + if (gtk_widget_get_realized(widget)) + gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, + allocation->y, allocation->width, + allocation->height); + } + + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + FlViewChild* child = reinterpret_cast(iterator->data); + if (!gtk_widget_get_visible(child->widget)) + continue; + + GtkAllocation child_allocation = child->geometry; + GtkRequisition child_requisition; + gtk_widget_get_preferred_size(child->widget, &child_requisition, NULL); + + if (!gtk_widget_get_has_window(widget)) { + child_allocation.x += allocation->x; + child_allocation.y += allocation->y; + } + + if (child_allocation.width == 0 && child_allocation.height == 0) { + child_allocation.width = allocation->width; + child_allocation.height = allocation->height; + } + + gtk_widget_size_allocate(child->widget, &child_allocation); } + GtkAllocation event_box_allocation = { + .x = 0, + .y = 0, + .width = allocation->width, + .height = allocation->height, + }; + if (!gtk_widget_get_has_window(self->event_box)) { + event_box_allocation.x += allocation->x; + event_box_allocation.y += allocation->y; + } + gtk_widget_size_allocate(self->event_box, &event_box_allocation); + fl_view_geometry_changed(self); } +struct _DrawData { + GdkWindow* parent_window; + GdkWindow* last_window; +}; + +static void fl_view_draw_forall(GtkWidget* widget, gpointer user_data) { + _DrawData* data = reinterpret_cast<_DrawData*>(user_data); + GdkWindow* window = gtk_widget_get_window(widget); + if (window && window != data->parent_window) { + if (data->last_window) { + gdk_window_restack(window, data->last_window, TRUE); + } + data->last_window = window; + } +} + // Implements GtkWidget::draw. static gboolean fl_view_draw(GtkWidget* widget, cairo_t* cr) { FlView* self = FL_VIEW(widget); @@ -286,35 +430,42 @@ static gboolean fl_view_draw(GtkWidget* widget, cairo_t* cr) { // sending a window metrics event of the same geometry. Since the geometry // didn't change, only a frame will be scheduled. fl_view_geometry_changed(self); - return TRUE; -} -// Implements GtkWidget::button_press_event. -static gboolean fl_view_button_press_event(GtkWidget* widget, - GdkEventButton* event) { - FlView* self = FL_VIEW(widget); + struct _DrawData data = { + .parent_window = gtk_widget_get_window(GTK_WIDGET(self)), + .last_window = nullptr, + }; + gtk_container_forall(GTK_CONTAINER(self), fl_view_draw_forall, &data); + + return GTK_WIDGET_CLASS(fl_view_parent_class)->draw(widget, cr); +} + +static gboolean event_box_button_press_event(GtkWidget* widget, + GdkEventButton* event, + FlView* view) { // Flutter doesn't handle double and triple click events. if (event->type == GDK_DOUBLE_BUTTON_PRESS || event->type == GDK_TRIPLE_BUTTON_PRESS) { return FALSE; } - return fl_view_send_pointer_button_event(self, event); -} - -// Implements GtkWidget::button_release_event. -static gboolean fl_view_button_release_event(GtkWidget* widget, - GdkEventButton* event) { - FlView* self = FL_VIEW(widget); + if (!gtk_widget_has_focus(GTK_WIDGET(view))) { + gtk_widget_grab_focus(GTK_WIDGET(view)); + } - return fl_view_send_pointer_button_event(self, event); + return fl_view_send_pointer_button_event(view, event); } -// Implements GtkWidget::scroll_event. -static gboolean fl_view_scroll_event(GtkWidget* widget, GdkEventScroll* event) { - FlView* self = FL_VIEW(widget); +static gboolean event_box_button_release_event(GtkWidget* widget, + GdkEventButton* event, + FlView* view) { + return fl_view_send_pointer_button_event(view, event); +} +static gboolean event_box_scroll_event(GtkWidget* widget, + GdkEventScroll* event, + FlView* view) { // TODO(robert-ancell): Update to use GtkEventControllerScroll when we can // depend on GTK 3.24. @@ -338,30 +489,28 @@ static gboolean fl_view_scroll_event(GtkWidget* widget, GdkEventScroll* event) { scroll_delta_x *= kScrollOffsetMultiplier; scroll_delta_y *= kScrollOffsetMultiplier; - gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self)); + gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view)); fl_engine_send_mouse_pointer_event( - self->engine, self->button_state != 0 ? kMove : kHover, + view->engine, view->button_state != 0 ? kMove : kHover, event->time * kMicrosecondsPerMillisecond, event->x * scale_factor, event->y * scale_factor, scroll_delta_x, scroll_delta_y, - self->button_state); + view->button_state); return TRUE; } -// Implements GtkWidget::motion_notify_event. -static gboolean fl_view_motion_notify_event(GtkWidget* widget, - GdkEventMotion* event) { - FlView* self = FL_VIEW(widget); - - if (self->engine == nullptr) { +static gboolean event_box_motion_notify_event(GtkWidget* widget, + GdkEventMotion* event, + FlView* view) { + if (view->engine == nullptr) { return FALSE; } - gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self)); + gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(view)); fl_engine_send_mouse_pointer_event( - self->engine, self->button_state != 0 ? kMove : kHover, + view->engine, view->button_state != 0 ? kMove : kHover, event->time * kMicrosecondsPerMillisecond, event->x * scale_factor, - event->y * scale_factor, 0, 0, self->button_state); + event->y * scale_factor, 0, 0, view->button_state); return TRUE; } @@ -381,21 +530,104 @@ static gboolean fl_view_key_release_event(GtkWidget* widget, return fl_key_event_plugin_send_key_event(self->key_event_plugin, event); } +static void fl_view_put(FlView* self, + GtkWidget* widget, + GdkRectangle* geometry) { + FlViewChild* child = g_new(FlViewChild, 1); + child->widget = widget; + child->geometry = *geometry; + + gtk_widget_set_parent(widget, GTK_WIDGET(self)); + self->children_list = g_list_append(self->children_list, child); +} + +static void fl_view_add(GtkContainer* container, GtkWidget* widget) { + GdkRectangle geometry = { + .x = 0, + .y = 0, + .width = 0, + .height = 0, + }; + fl_view_put(FL_VIEW(container), widget, &geometry); +} + +static void fl_view_remove(GtkContainer* container, GtkWidget* widget) { + FlView* self = FL_VIEW(container); + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + FlViewChild* child = reinterpret_cast(iterator->data); + if (child->widget == widget) { + g_object_ref(widget); + gtk_widget_unparent(widget); + self->children_list = g_list_remove_link(self->children_list, iterator); + g_list_free(iterator); + g_free(child); + + break; + } + } + + if (widget == GTK_WIDGET(self->event_box)) { + g_clear_object(&self->event_box); + } +} + +static void fl_view_forall(GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) { + FlView* self = FL_VIEW(container); + for (GList* iterator = self->children_list; iterator; + iterator = iterator->next) { + FlViewChild* child = reinterpret_cast(iterator->data); + (*callback)(child->widget, callback_data); + } + + if (include_internals) { + (*callback)(self->event_box, callback_data); + } +} + +static GType fl_view_child_type(GtkContainer* container) { + return GTK_TYPE_WIDGET; +} + +static void fl_view_set_child_property(GtkContainer* container, + GtkWidget* child, + guint property_id, + const GValue* value, + GParamSpec* pspec) {} + +static void fl_view_get_child_property(GtkContainer* container, + GtkWidget* child, + guint property_id, + GValue* value, + GParamSpec* pspec) {} + static void fl_view_class_init(FlViewClass* klass) { - G_OBJECT_CLASS(klass)->constructed = fl_view_constructed; - G_OBJECT_CLASS(klass)->set_property = fl_view_set_property; - G_OBJECT_CLASS(klass)->get_property = fl_view_get_property; - G_OBJECT_CLASS(klass)->notify = fl_view_notify; - G_OBJECT_CLASS(klass)->dispose = fl_view_dispose; - GTK_WIDGET_CLASS(klass)->realize = fl_view_realize; - GTK_WIDGET_CLASS(klass)->size_allocate = fl_view_size_allocate; - GTK_WIDGET_CLASS(klass)->draw = fl_view_draw; - GTK_WIDGET_CLASS(klass)->button_press_event = fl_view_button_press_event; - GTK_WIDGET_CLASS(klass)->button_release_event = fl_view_button_release_event; - GTK_WIDGET_CLASS(klass)->scroll_event = fl_view_scroll_event; - GTK_WIDGET_CLASS(klass)->motion_notify_event = fl_view_motion_notify_event; - GTK_WIDGET_CLASS(klass)->key_press_event = fl_view_key_press_event; - GTK_WIDGET_CLASS(klass)->key_release_event = fl_view_key_release_event; + 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; + object_class->dispose = fl_view_dispose; + + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + widget_class->realize = fl_view_realize; + widget_class->get_preferred_width = fl_view_get_preferred_width; + widget_class->get_preferred_height = fl_view_get_preferred_height; + widget_class->size_allocate = fl_view_size_allocate; + widget_class->draw = fl_view_draw; + widget_class->key_press_event = fl_view_key_press_event; + widget_class->key_release_event = fl_view_key_release_event; + + GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass); + container_class->add = fl_view_add; + container_class->remove = fl_view_remove; + container_class->forall = fl_view_forall; + container_class->child_type = fl_view_child_type; + container_class->set_child_property = fl_view_set_child_property; + container_class->get_child_property = fl_view_get_child_property; g_object_class_install_property( G_OBJECT_CLASS(klass), PROP_FLUTTER_PROJECT, @@ -422,3 +654,95 @@ G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* view) { g_return_val_if_fail(FL_IS_VIEW(view), nullptr); return view->engine; } + +void fl_view_begin_frame(FlView* view) { + g_return_if_fail(FL_IS_VIEW(view)); + FlView* self = FL_VIEW(view); + + self->used_area_list = self->gl_area_list; + g_list_free_full(self->pending_children_list, g_free); + self->pending_children_list = nullptr; +} + +static void fl_view_add_pending_child(FlView* self, + GtkWidget* widget, + GdkRectangle* geometry) { + FlViewChild* child = g_new(FlViewChild, 1); + child->widget = widget; + if (geometry) + child->geometry = *geometry; + else + child->geometry = {0, 0, 0, 0}; + + self->pending_children_list = + g_list_append(self->pending_children_list, child); +} + +void fl_view_add_gl_area(FlView* view, + GdkGLContext* context, + FlBackingStoreProvider* texture) { + g_return_if_fail(FL_IS_VIEW(view)); + + FlGLArea* area; + if (view->used_area_list) { + area = reinterpret_cast(view->used_area_list->data); + view->used_area_list = view->used_area_list->next; + } else { + area = FL_GL_AREA(fl_gl_area_new(context)); + view->gl_area_list = g_list_append(view->gl_area_list, area); + } + + gtk_widget_show(GTK_WIDGET(area)); + fl_view_add_pending_child(view, GTK_WIDGET(area), nullptr); + fl_gl_area_queue_render(area, texture); +} + +void fl_view_add_widget(FlView* view, + GtkWidget* widget, + GdkRectangle* geometry) { + gtk_widget_show(widget); + fl_view_add_pending_child(view, widget, geometry); +} + +GList* find_child(GList* list, GtkWidget* widget) { + for (GList* i = list; i; i = i->next) { + FlViewChild* child = reinterpret_cast(i->data); + if (child && child->widget == widget) + return i; + } + return nullptr; +} + +void fl_view_end_frame(FlView* view) { + for (GList* pending_child = view->pending_children_list; pending_child; + pending_child = pending_child->next) { + FlViewChild* pending_view_child = + reinterpret_cast(pending_child->data); + GList* child = find_child(view->children_list, pending_view_child->widget); + + if (child) { + // existing child + g_free(child->data); + child->data = nullptr; + } else { + // newly added child + gtk_widget_set_parent(pending_view_child->widget, GTK_WIDGET(view)); + } + } + + for (GList* child = view->children_list; child; child = child->next) { + FlViewChild* view_child = reinterpret_cast(child->data); + if (view_child) { + // removed child + g_object_ref(view_child->widget); + gtk_widget_unparent(view_child->widget); + g_free(view_child); + child->data = nullptr; + } + } + + g_list_free(view->children_list); + view->children_list = view->pending_children_list; + view->pending_children_list = nullptr; + gtk_widget_queue_resize(GTK_WIDGET(view)); +} diff --git a/shell/platform/linux/fl_view_accessible.cc b/shell/platform/linux/fl_view_accessible.cc index 5695430dba461..d3c9f7c2eaaf3 100644 --- a/shell/platform/linux/fl_view_accessible.cc +++ b/shell/platform/linux/fl_view_accessible.cc @@ -7,13 +7,15 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" struct _FlViewAccessible { - GtkWidgetAccessible parent_instance; + GtkContainerAccessible parent_instance; // Semantics nodes keyed by ID GHashTable* semantics_nodes_by_id; }; -G_DEFINE_TYPE(FlViewAccessible, fl_view_accessible, GTK_TYPE_WIDGET_ACCESSIBLE) +G_DEFINE_TYPE(FlViewAccessible, + fl_view_accessible, + GTK_TYPE_CONTAINER_ACCESSIBLE) // Gets the ATK node for the given id. // If the node doesn't exist it will be created. diff --git a/shell/platform/linux/fl_view_accessible.h b/shell/platform/linux/fl_view_accessible.h index 612823272287c..e9ed68e929b33 100644 --- a/shell/platform/linux/fl_view_accessible.h +++ b/shell/platform/linux/fl_view_accessible.h @@ -19,7 +19,7 @@ G_DECLARE_FINAL_TYPE(FlViewAccessible, fl_view_accessible, FL, VIEW_ACCESSIBLE, - GtkWidgetAccessible) + GtkContainerAccessible) /** * FlViewAccessible: diff --git a/shell/platform/linux/fl_view_private.h b/shell/platform/linux/fl_view_private.h new file mode 100644 index 0000000000000..51c3107435966 --- /dev/null +++ b/shell/platform/linux/fl_view_private.h @@ -0,0 +1,55 @@ +// 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_VIEW_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_VIEW_PRIVATE_H_ + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" + +#include "flutter/shell/platform/linux/fl_gl_area.h" + +/** + * fl_view_begin_frame: + * @view: an #FlView. + * + * Reset children of #FlView a stacked #GtkContainer. + * This function is always paired with fl_view_end_frame. + */ +void fl_view_begin_frame(FlView* view); + +/** + * fl_view_add_gl_area: + * @view: an #FlView. + * @context: (transfer-full): a #GdkGLContext, for #FlGLArea to render. + * @texture: (transfer-full): texture for OpenGL area to render. + * + * Append an #FlGLArea at top of stacked children of #FlView. + * This function must be called after fl_view_begin_frame, and + * before fl_view_end_frame. + */ +void fl_view_add_gl_area(FlView* view, + GdkGLContext* context, + FlBackingStoreProvider* texture); + +/** + * fl_view_add_widget: + * @view: an #FlView. + * @widget: a #GtkWidget. + * @geometry: geometry of the widget. + * + * Append a #GtkWidget at top of stacked children of #FlView. + */ +void fl_view_add_widget(FlView* view, + GtkWidget* widget, + GdkRectangle* geometry); + +/** + * fl_view_end_frame: + * @view: an #FlView. + * + * Apply changes made by fl_view_add_gl_area and fl_view_add_widget. + */ +void fl_view_end_frame(FlView* view); + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_VIEW_PRIVATE_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_view.h b/shell/platform/linux/public/flutter_linux/fl_view.h index 32040c59eb6cb..f93f385a4af65 100644 --- a/shell/platform/linux/public/flutter_linux/fl_view.h +++ b/shell/platform/linux/public/flutter_linux/fl_view.h @@ -16,7 +16,7 @@ G_BEGIN_DECLS -G_DECLARE_FINAL_TYPE(FlView, fl_view, FL, VIEW, GtkWidget) +G_DECLARE_FINAL_TYPE(FlView, fl_view, FL, VIEW, GtkContainer) /** * FlView: diff --git a/shell/platform/linux/testing/mock_egl.cc b/shell/platform/linux/testing/mock_egl.cc deleted file mode 100644 index 0c81c2d26bf71..0000000000000 --- a/shell/platform/linux/testing/mock_egl.cc +++ /dev/null @@ -1,333 +0,0 @@ -// 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 - -typedef struct { - EGLint config_id; - EGLint buffer_size; - EGLint color_buffer_type; - EGLint transparent_type; - EGLint level; - EGLint red_size; - EGLint green_size; - EGLint blue_size; - EGLint alpha_size; - EGLint depth_size; - EGLint stencil_size; - EGLint samples; - EGLint sample_buffers; - EGLint native_visual_id; - EGLint native_visual_type; - EGLint native_renderable; - EGLint config_caveat; - EGLint bind_to_texture_rgb; - EGLint bind_to_texture_rgba; - EGLint renderable_type; - EGLint conformant; - EGLint surface_type; - EGLint max_pbuffer_width; - EGLint max_pbuffer_height; - EGLint max_pbuffer_pixels; - EGLint min_swap_interval; - EGLint max_swap_interval; -} MockConfig; - -typedef struct { -} 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; - -static bool check_display(EGLDisplay dpy) { - if (dpy == nullptr) { - mock_error = EGL_BAD_DISPLAY; - return false; - } - - return true; -} - -static bool check_initialized(EGLDisplay dpy) { - if (!display_initialized) { - mock_error = EGL_NOT_INITIALIZED; - return false; - } - - return true; -} - -static bool check_config(EGLConfig config) { - if (config == nullptr) { - mock_error = EGL_BAD_CONFIG; - return false; - } - - return true; -} - -static EGLBoolean bool_success() { - mock_error = EGL_SUCCESS; - return EGL_TRUE; -} - -static EGLBoolean bool_failure(EGLint error) { - mock_error = error; - return EGL_FALSE; -} - -EGLBoolean eglBindAPI(EGLenum api) { - return bool_success(); -} - -EGLBoolean eglChooseConfig(EGLDisplay dpy, - const EGLint* attrib_list, - EGLConfig* configs, - EGLint config_size, - EGLint* num_config) { - if (!check_display(dpy) || !check_initialized(dpy)) { - return EGL_FALSE; - } - - if (configs == nullptr) { - if (num_config != nullptr) { - *num_config = 1; - } - return bool_success(); - } - - EGLint n_returned = 0; - if (config_size >= 1) { - configs[0] = &mock_config; - n_returned++; - } - - if (num_config != nullptr) { - *num_config = n_returned; - } - - return bool_success(); -} - -EGLContext eglCreateContext(EGLDisplay dpy, - EGLConfig config, - EGLContext share_context, - const EGLint* attrib_list) { - if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { - return EGL_NO_CONTEXT; - } - - mock_error = EGL_SUCCESS; - return &mock_context; -} - -EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, - EGLConfig config, - const EGLint* attrib_list) { - if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { - return EGL_NO_SURFACE; - } - - mock_error = EGL_SUCCESS; - return &mock_surface; -} - -EGLSurface eglCreateWindowSurface(EGLDisplay dpy, - EGLConfig config, - EGLNativeWindowType win, - const EGLint* attrib_list) { - if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { - return EGL_NO_SURFACE; - } - - mock_error = EGL_SUCCESS; - return &mock_surface; -} - -EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, - EGLConfig config, - EGLint attribute, - EGLint* value) { - if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { - return EGL_FALSE; - } - - MockConfig* c = static_cast(config); - switch (attribute) { - case EGL_CONFIG_ID: - *value = c->config_id; - return bool_success(); - case EGL_BUFFER_SIZE: - *value = c->buffer_size; - return bool_success(); - case EGL_COLOR_BUFFER_TYPE: - *value = c->color_buffer_type; - return bool_success(); - case EGL_TRANSPARENT_TYPE: - *value = c->transparent_type; - return bool_success(); - case EGL_LEVEL: - *value = c->level; - return bool_success(); - case EGL_RED_SIZE: - *value = c->red_size; - return bool_success(); - case EGL_GREEN_SIZE: - *value = c->green_size; - return bool_success(); - case EGL_BLUE_SIZE: - *value = c->blue_size; - return bool_success(); - case EGL_ALPHA_SIZE: - *value = c->alpha_size; - return bool_success(); - case EGL_DEPTH_SIZE: - *value = c->depth_size; - return bool_success(); - case EGL_STENCIL_SIZE: - *value = c->stencil_size; - return bool_success(); - case EGL_SAMPLES: - *value = c->samples; - return bool_success(); - case EGL_SAMPLE_BUFFERS: - *value = c->sample_buffers; - return bool_success(); - case EGL_NATIVE_VISUAL_ID: - *value = c->native_visual_id; - return bool_success(); - case EGL_NATIVE_VISUAL_TYPE: - *value = c->native_visual_type; - return bool_success(); - case EGL_NATIVE_RENDERABLE: - *value = c->native_renderable; - return bool_success(); - case EGL_CONFIG_CAVEAT: - *value = c->config_caveat; - return bool_success(); - case EGL_BIND_TO_TEXTURE_RGB: - *value = c->bind_to_texture_rgb; - return bool_success(); - case EGL_BIND_TO_TEXTURE_RGBA: - *value = c->bind_to_texture_rgba; - return bool_success(); - case EGL_RENDERABLE_TYPE: - *value = c->renderable_type; - return bool_success(); - case EGL_CONFORMANT: - *value = c->conformant; - return bool_success(); - case EGL_SURFACE_TYPE: - *value = c->surface_type; - return bool_success(); - case EGL_MAX_PBUFFER_WIDTH: - *value = c->max_pbuffer_width; - return bool_success(); - case EGL_MAX_PBUFFER_HEIGHT: - *value = c->max_pbuffer_height; - return bool_success(); - case EGL_MAX_PBUFFER_PIXELS: - *value = c->max_pbuffer_pixels; - return bool_success(); - case EGL_MIN_SWAP_INTERVAL: - *value = c->min_swap_interval; - return bool_success(); - case EGL_MAX_SWAP_INTERVAL: - *value = c->max_swap_interval; - return bool_success(); - default: - return bool_failure(EGL_BAD_ATTRIBUTE); - } -} - -EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) { - return &mock_display; -} - -EGLint eglGetError() { - EGLint error = mock_error; - mock_error = EGL_SUCCESS; - return error; -} - -void (*eglGetProcAddress(const char* procname))(void) { - mock_error = EGL_SUCCESS; - return nullptr; -} - -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { - if (!check_display(dpy)) { - return EGL_FALSE; - } - - 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) { - *major = 1; - } - if (minor != nullptr) { - *minor = 5; - } - - return bool_success(); -} - -EGLBoolean eglMakeCurrent(EGLDisplay dpy, - EGLSurface draw, - EGLSurface read, - EGLContext ctx) { - if (!check_display(dpy) || !check_initialized(dpy)) { - return EGL_FALSE; - } - - return bool_success(); -} - -EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { - if (!check_display(dpy) || !check_initialized(dpy)) { - return EGL_FALSE; - } - - return bool_success(); -} diff --git a/shell/platform/linux/testing/mock_epoxy.cc b/shell/platform/linux/testing/mock_epoxy.cc new file mode 100644 index 0000000000000..a8012cd9888ef --- /dev/null +++ b/shell/platform/linux/testing/mock_epoxy.cc @@ -0,0 +1,474 @@ +// 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 +#include + +typedef struct { + EGLint config_id; + EGLint buffer_size; + EGLint color_buffer_type; + EGLint transparent_type; + EGLint level; + EGLint red_size; + EGLint green_size; + EGLint blue_size; + EGLint alpha_size; + EGLint depth_size; + EGLint stencil_size; + EGLint samples; + EGLint sample_buffers; + EGLint native_visual_id; + EGLint native_visual_type; + EGLint native_renderable; + EGLint config_caveat; + EGLint bind_to_texture_rgb; + EGLint bind_to_texture_rgba; + EGLint renderable_type; + EGLint conformant; + EGLint surface_type; + EGLint max_pbuffer_width; + EGLint max_pbuffer_height; + EGLint max_pbuffer_pixels; + EGLint min_swap_interval; + EGLint max_swap_interval; +} MockConfig; + +typedef struct { +} 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; + +static bool check_display(EGLDisplay dpy) { + if (dpy == nullptr) { + mock_error = EGL_BAD_DISPLAY; + return false; + } + + return true; +} + +static bool check_initialized(EGLDisplay dpy) { + if (!display_initialized) { + mock_error = EGL_NOT_INITIALIZED; + return false; + } + + return true; +} + +static bool check_config(EGLConfig config) { + if (config == nullptr) { + mock_error = EGL_BAD_CONFIG; + return false; + } + + return true; +} + +static EGLBoolean bool_success() { + mock_error = EGL_SUCCESS; + return EGL_TRUE; +} + +static EGLBoolean bool_failure(EGLint error) { + mock_error = error; + return EGL_FALSE; +} + +EGLBoolean _eglBindAPI(EGLenum api) { + return bool_success(); +} + +EGLBoolean _eglChooseConfig(EGLDisplay dpy, + const EGLint* attrib_list, + EGLConfig* configs, + EGLint config_size, + EGLint* num_config) { + if (!check_display(dpy) || !check_initialized(dpy)) { + return EGL_FALSE; + } + + if (configs == nullptr) { + if (num_config != nullptr) { + *num_config = 1; + } + return bool_success(); + } + + EGLint n_returned = 0; + if (config_size >= 1) { + configs[0] = &mock_config; + n_returned++; + } + + if (num_config != nullptr) { + *num_config = n_returned; + } + + return bool_success(); +} + +EGLContext _eglCreateContext(EGLDisplay dpy, + EGLConfig config, + EGLContext share_context, + const EGLint* attrib_list) { + if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { + return EGL_NO_CONTEXT; + } + + mock_error = EGL_SUCCESS; + return &mock_context; +} + +EGLSurface _eglCreatePbufferSurface(EGLDisplay dpy, + EGLConfig config, + const EGLint* attrib_list) { + if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { + return EGL_NO_SURFACE; + } + + mock_error = EGL_SUCCESS; + return &mock_surface; +} + +EGLSurface _eglCreateWindowSurface(EGLDisplay dpy, + EGLConfig config, + EGLNativeWindowType win, + const EGLint* attrib_list) { + if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { + return EGL_NO_SURFACE; + } + + mock_error = EGL_SUCCESS; + return &mock_surface; +} + +EGLBoolean _eglGetConfigAttrib(EGLDisplay dpy, + EGLConfig config, + EGLint attribute, + EGLint* value) { + if (!check_display(dpy) || !check_initialized(dpy) || !check_config(config)) { + return EGL_FALSE; + } + + MockConfig* c = static_cast(config); + switch (attribute) { + case EGL_CONFIG_ID: + *value = c->config_id; + return bool_success(); + case EGL_BUFFER_SIZE: + *value = c->buffer_size; + return bool_success(); + case EGL_COLOR_BUFFER_TYPE: + *value = c->color_buffer_type; + return bool_success(); + case EGL_TRANSPARENT_TYPE: + *value = c->transparent_type; + return bool_success(); + case EGL_LEVEL: + *value = c->level; + return bool_success(); + case EGL_RED_SIZE: + *value = c->red_size; + return bool_success(); + case EGL_GREEN_SIZE: + *value = c->green_size; + return bool_success(); + case EGL_BLUE_SIZE: + *value = c->blue_size; + return bool_success(); + case EGL_ALPHA_SIZE: + *value = c->alpha_size; + return bool_success(); + case EGL_DEPTH_SIZE: + *value = c->depth_size; + return bool_success(); + case EGL_STENCIL_SIZE: + *value = c->stencil_size; + return bool_success(); + case EGL_SAMPLES: + *value = c->samples; + return bool_success(); + case EGL_SAMPLE_BUFFERS: + *value = c->sample_buffers; + return bool_success(); + case EGL_NATIVE_VISUAL_ID: + *value = c->native_visual_id; + return bool_success(); + case EGL_NATIVE_VISUAL_TYPE: + *value = c->native_visual_type; + return bool_success(); + case EGL_NATIVE_RENDERABLE: + *value = c->native_renderable; + return bool_success(); + case EGL_CONFIG_CAVEAT: + *value = c->config_caveat; + return bool_success(); + case EGL_BIND_TO_TEXTURE_RGB: + *value = c->bind_to_texture_rgb; + return bool_success(); + case EGL_BIND_TO_TEXTURE_RGBA: + *value = c->bind_to_texture_rgba; + return bool_success(); + case EGL_RENDERABLE_TYPE: + *value = c->renderable_type; + return bool_success(); + case EGL_CONFORMANT: + *value = c->conformant; + return bool_success(); + case EGL_SURFACE_TYPE: + *value = c->surface_type; + return bool_success(); + case EGL_MAX_PBUFFER_WIDTH: + *value = c->max_pbuffer_width; + return bool_success(); + case EGL_MAX_PBUFFER_HEIGHT: + *value = c->max_pbuffer_height; + return bool_success(); + case EGL_MAX_PBUFFER_PIXELS: + *value = c->max_pbuffer_pixels; + return bool_success(); + case EGL_MIN_SWAP_INTERVAL: + *value = c->min_swap_interval; + return bool_success(); + case EGL_MAX_SWAP_INTERVAL: + *value = c->max_swap_interval; + return bool_success(); + default: + return bool_failure(EGL_BAD_ATTRIBUTE); + } +} + +EGLDisplay _eglGetDisplay(EGLNativeDisplayType display_id) { + return &mock_display; +} + +EGLint _eglGetError() { + EGLint error = mock_error; + mock_error = EGL_SUCCESS; + return error; +} + +void (*_eglGetProcAddress(const char* procname))(void) { + mock_error = EGL_SUCCESS; + return nullptr; +} + +EGLBoolean _eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { + if (!check_display(dpy)) { + return EGL_FALSE; + } + + 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) { + *major = 1; + } + if (minor != nullptr) { + *minor = 5; + } + + return bool_success(); +} + +EGLBoolean _eglMakeCurrent(EGLDisplay dpy, + EGLSurface draw, + EGLSurface read, + EGLContext ctx) { + if (!check_display(dpy) || !check_initialized(dpy)) { + return EGL_FALSE; + } + + return bool_success(); +} + +EGLBoolean _eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { + if (!check_display(dpy) || !check_initialized(dpy)) { + return EGL_FALSE; + } + + return bool_success(); +} + +static void _glBindFramebuffer(GLenum target, GLuint framebuffer) {} + +static void _glBindTexture(GLenum target, GLuint texture) {} + +void _glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {} + +void _glDeleteTextures(GLsizei n, const GLuint* textures) {} + +static void _glFramebufferTexture2D(GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level) {} + +static void _glGenTextures(GLsizei n, GLuint* textures) { + for (GLsizei i = 0; i < n; i++) { + textures[i] = 0; + } +} + +static void _glGenFramebuffers(GLsizei n, GLuint* framebuffers) { + for (GLsizei i = 0; i < n; i++) { + framebuffers[i] = 0; + } +} + +static void _glTexParameterf(GLenum target, GLenum pname, GLfloat param) {} + +static void _glTexParameteri(GLenum target, GLenum pname, GLint param) {} + +static void _glTexImage2D(GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void* pixels) {} + +#ifdef __GNUC__ +#define CONSTRUCT(_func) static void _func(void) __attribute__((constructor)); +#define DESTRUCT(_func) static void _func(void) __attribute__((destructor)); +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) +#define CONSTRUCT(_func) \ + static void _func(void); \ + static int _func##_wrapper(void) { \ + _func(); \ + return 0; \ + } \ + __pragma(section(".CRT$XCU", read)) __declspec(allocate( \ + ".CRT$XCU")) static int (*_array##_func)(void) = _func##_wrapper; + +#else +#error "You will need constructor support for your compiler" +#endif + +CONSTRUCT(library_init) + +EGLBoolean (*epoxy_eglBindAPI)(EGLenum api); +EGLBoolean (*epoxy_eglChooseConfig)(EGLDisplay dpy, + const EGLint* attrib_list, + EGLConfig* configs, + EGLint config_size, + EGLint* num_config); +EGLContext (*epoxy_eglCreateContext)(EGLDisplay dpy, + EGLConfig config, + EGLContext share_context, + const EGLint* attrib_list); +EGLSurface (*epoxy_eglCreatePbufferSurface)(EGLDisplay dpy, + EGLConfig config, + const EGLint* attrib_list); +EGLSurface (*epoxy_eglCreateWindowSurface)(EGLDisplay dpy, + EGLConfig config, + EGLNativeWindowType win, + const EGLint* attrib_list); +EGLBoolean (*epoxy_eglGetConfigAttrib)(EGLDisplay dpy, + EGLConfig config, + EGLint attribute, + EGLint* value); +EGLDisplay (*epoxy_eglGetDisplay)(EGLNativeDisplayType display_id); +EGLint (*epoxy_eglGetError)(); +void (*(*epoxy_eglGetProcAddress)(const char* procname))(void); +EGLBoolean (*epoxy_eglInitialize)(EGLDisplay dpy, EGLint* major, EGLint* minor); +EGLBoolean (*epoxy_eglMakeCurrent)(EGLDisplay dpy, + EGLSurface draw, + EGLSurface read, + EGLContext ctx); +EGLBoolean (*epoxy_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); + +void (*epoxy_glBindFramebuffer)(GLenum target, GLuint framebuffer); +void (*epoxy_glBindTexture)(GLenum target, GLuint texture); +void (*epoxy_glDeleteFramebuffers)(GLsizei n, const GLuint* framebuffers); +void (*epoxy_glDeleteTextures)(GLsizei n, const GLuint* textures); +void (*epoxy_glFramebufferTexture2D)(GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level); +void (*epoxy_glGenFramebuffers)(GLsizei n, GLuint* framebuffers); +void (*epoxy_glGenTextures)(GLsizei n, GLuint* textures); +void (*epoxy_glTexParameterf)(GLenum target, GLenum pname, GLfloat param); +void (*epoxy_glTexParameteri)(GLenum target, GLenum pname, GLint param); +void (*epoxy_glTexImage2D)(GLenum target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void* pixels); + +static void library_init() { + epoxy_eglBindAPI = _eglBindAPI; + epoxy_eglChooseConfig = _eglChooseConfig; + epoxy_eglCreateContext = _eglCreateContext; + epoxy_eglCreatePbufferSurface = _eglCreatePbufferSurface; + epoxy_eglCreateWindowSurface = _eglCreateWindowSurface; + epoxy_eglGetConfigAttrib = _eglGetConfigAttrib; + epoxy_eglGetDisplay = _eglGetDisplay; + epoxy_eglGetError = _eglGetError; + epoxy_eglGetProcAddress = _eglGetProcAddress; + epoxy_eglInitialize = _eglInitialize; + epoxy_eglMakeCurrent = _eglMakeCurrent; + epoxy_eglSwapBuffers = _eglSwapBuffers; + + epoxy_glBindFramebuffer = _glBindFramebuffer; + epoxy_glBindTexture = _glBindTexture; + epoxy_glDeleteFramebuffers = _glDeleteFramebuffers; + epoxy_glDeleteTextures = _glDeleteTextures; + epoxy_glFramebufferTexture2D = _glFramebufferTexture2D; + epoxy_glGenFramebuffers = _glGenFramebuffers; + epoxy_glGenTextures = _glGenTextures; + epoxy_glTexParameterf = _glTexParameterf; + epoxy_glTexParameteri = _glTexParameteri; + epoxy_glTexImage2D = _glTexImage2D; +} diff --git a/shell/platform/linux/testing/mock_renderer.cc b/shell/platform/linux/testing/mock_renderer.cc index ef8a0d413a2ae..ce801724ec3c8 100644 --- a/shell/platform/linux/testing/mock_renderer.cc +++ b/shell/platform/linux/testing/mock_renderer.cc @@ -10,28 +10,44 @@ struct _FlMockRenderer { G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type()) -// Implements FlRenderer::create_display. -static EGLDisplay fl_mock_renderer_create_display(FlRenderer* renderer) { - return eglGetDisplay(EGL_DEFAULT_DISPLAY); -} - -// Implements FlRenderer::create_surfaces. -static gboolean fl_mock_renderer_create_surfaces(FlRenderer* renderer, +// Implements FlRenderer::create_contexts. +static gboolean fl_mock_renderer_create_contexts(FlRenderer* renderer, GtkWidget* widget, - EGLDisplay display, - EGLConfig config, - EGLSurface* visible, - EGLSurface* resource, + GdkGLContext** visible, + GdkGLContext** resource, GError** error) { - *visible = eglCreateWindowSurface(display, config, 0, nullptr); - const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; - *resource = eglCreatePbufferSurface(display, config, attribs); + return TRUE; +} + +// Implements FlRenderer::create_backing_store. +static gboolean fl_mock_renderer_create_backing_store( + FlRenderer* renderer, + const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out) { + return TRUE; +} + +// Implements FlRenderer::collect_backing_store. +static gboolean fl_mock_renderer_collect_backing_store( + FlRenderer* self, + const FlutterBackingStore* backing_store) { + return TRUE; +} + +// Implements FlRenderer::present_layers. +static gboolean fl_mock_renderer_present_layers(FlRenderer* self, + const FlutterLayer** layers, + size_t layers_count) { return TRUE; } static void fl_mock_renderer_class_init(FlMockRendererClass* klass) { - FL_RENDERER_CLASS(klass)->create_display = fl_mock_renderer_create_display; - FL_RENDERER_CLASS(klass)->create_surfaces = fl_mock_renderer_create_surfaces; + FL_RENDERER_CLASS(klass)->create_contexts = fl_mock_renderer_create_contexts; + FL_RENDERER_CLASS(klass)->create_backing_store = + fl_mock_renderer_create_backing_store; + FL_RENDERER_CLASS(klass)->collect_backing_store = + fl_mock_renderer_collect_backing_store; + FL_RENDERER_CLASS(klass)->present_layers = fl_mock_renderer_present_layers; } static void fl_mock_renderer_init(FlMockRenderer* self) {}