diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 1abdcd0172038..3fdaef624cd6b 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -195,6 +195,9 @@ static void render_with_textures(FlRenderer* self, int width, int height) { GLint saved_array_buffer_binding; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glUseProgram(priv->program); for (guint i = 0; i < priv->framebuffers->len; i++) { @@ -236,6 +239,8 @@ static void render_with_textures(FlRenderer* self, int width, int height) { glDeleteBuffers(1, &vertex_buffer); } + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, saved_texture_binding); glBindVertexArray(saved_vao_binding); glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding); @@ -443,13 +448,17 @@ void fl_renderer_setup(FlRenderer* self) { } } -void fl_renderer_render(FlRenderer* self, int width, int height) { +void fl_renderer_render(FlRenderer* self, + int width, + int height, + const GdkRGBA* background_color) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); g_return_if_fail(FL_IS_RENDERER(self)); - glClearColor(0.0, 0.0, 0.0, 1.0); + glClearColor(background_color->red, background_color->green, + background_color->blue, background_color->alpha); glClear(GL_COLOR_BUFFER_BIT); if (priv->has_gl_framebuffer_blit) { diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h index c8af204906f68..648c535f0eaa9 100644 --- a/shell/platform/linux/fl_renderer.h +++ b/shell/platform/linux/fl_renderer.h @@ -209,10 +209,14 @@ void fl_renderer_setup(FlRenderer* renderer); * @renderer: an #FlRenderer. * @width: width of the window in pixels. * @height: height of the window in pixels. + * @background_color: color to use for background. * * Performs OpenGL commands to render current Flutter view. */ -void fl_renderer_render(FlRenderer* renderer, int width, int height); +void fl_renderer_render(FlRenderer* renderer, + int width, + int height, + const GdkRGBA* background_color); /** * fl_renderer_cleanup: diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc index 1426e4ec8b136..015c9d2cb9010 100644 --- a/shell/platform/linux/fl_renderer_test.cc +++ b/shell/platform/linux/fl_renderer_test.cc @@ -12,6 +12,36 @@ #include +TEST(FlRendererTest, BackgroundColor) { + ::testing::NiceMock epoxy; + + ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); + EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30)); + ON_CALL(epoxy, glGetString(GL_VENDOR)) + .WillByDefault( + ::testing::Return(reinterpret_cast("Intel"))); + EXPECT_CALL(epoxy, glClearColor(0.2, 0.3, 0.4, 0.5)); + + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); + fl_renderer_setup(FL_RENDERER(renderer)); + fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); + FlutterBackingStoreConfig config = { + .struct_size = sizeof(FlutterBackingStoreConfig), + .size = {.width = 1024, .height = 1024}}; + FlutterBackingStore backing_store; + fl_renderer_create_backing_store(FL_RENDERER(renderer), &config, + &backing_store); + const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer), + .type = kFlutterLayerContentTypeBackingStore, + .backing_store = &backing_store, + .size = {.width = 1024, .height = 1024}}; + const FlutterLayer* layers[] = {&layer0}; + fl_renderer_present_layers(FL_RENDERER(renderer), 0, layers, 1); + GdkRGBA background_color = { + .red = 0.2, .green = 0.3, .blue = 0.4, .alpha = 0.5}; + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024, &background_color); +} + TEST(FlRendererTest, RestoresGLState) { ::testing::NiceMock epoxy; @@ -44,7 +74,9 @@ TEST(FlRendererTest, RestoresGLState) { fl_renderer_present_layers(FL_RENDERER(renderer), 0, layers.data(), layers.size()); - fl_renderer_render(FL_RENDERER(renderer), kWidth, kHeight); + GdkRGBA background_color = { + .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}; + fl_renderer_render(FL_RENDERER(renderer), kWidth, kHeight, &background_color); GLuint texture_2d_binding; glGetIntegerv(GL_TEXTURE_BINDING_2D, @@ -82,8 +114,7 @@ TEST(FlRendererTest, BlitFramebuffer) { EXPECT_CALL(epoxy, glBlitFramebuffer); - g_autoptr(FlMockRenderer) renderer = - fl_mock_renderer_new(&renderer_get_refresh_rate); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); fl_renderer_setup(FL_RENDERER(renderer)); fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); FlutterBackingStoreConfig config = { @@ -98,7 +129,9 @@ TEST(FlRendererTest, BlitFramebuffer) { .size = {.width = 1024, .height = 1024}}; const FlutterLayer* layers[] = {&layer0}; fl_renderer_present_layers(FL_RENDERER(renderer), 0, layers, 1); - fl_renderer_render(FL_RENDERER(renderer), 1024, 1024); + GdkRGBA background_color = { + .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}; + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024, &background_color); } TEST(FlRendererTest, BlitFramebufferExtension) { @@ -116,8 +149,7 @@ TEST(FlRendererTest, BlitFramebufferExtension) { EXPECT_CALL(epoxy, glBlitFramebuffer); - g_autoptr(FlMockRenderer) renderer = - fl_mock_renderer_new(&renderer_get_refresh_rate); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); fl_renderer_setup(FL_RENDERER(renderer)); fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); FlutterBackingStoreConfig config = { @@ -132,7 +164,9 @@ TEST(FlRendererTest, BlitFramebufferExtension) { .size = {.width = 1024, .height = 1024}}; const FlutterLayer* layers[] = {&layer0}; fl_renderer_present_layers(FL_RENDERER(renderer), 0, layers, 1); - fl_renderer_render(FL_RENDERER(renderer), 1024, 1024); + GdkRGBA background_color = { + .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}; + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024, &background_color); } TEST(FlRendererTest, NoBlitFramebuffer) { @@ -145,8 +179,7 @@ TEST(FlRendererTest, NoBlitFramebuffer) { ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20)); - g_autoptr(FlMockRenderer) renderer = - fl_mock_renderer_new(&renderer_get_refresh_rate); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); fl_renderer_setup(FL_RENDERER(renderer)); fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); FlutterBackingStoreConfig config = { @@ -161,7 +194,9 @@ TEST(FlRendererTest, NoBlitFramebuffer) { .size = {.width = 1024, .height = 1024}}; const FlutterLayer* layers[] = {&layer0}; fl_renderer_present_layers(FL_RENDERER(renderer), 0, layers, 1); - fl_renderer_render(FL_RENDERER(renderer), 1024, 1024); + GdkRGBA background_color = { + .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}; + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024, &background_color); } TEST(FlRendererTest, BlitFramebufferNvidia) { @@ -175,8 +210,7 @@ TEST(FlRendererTest, BlitFramebufferNvidia) { ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30)); - g_autoptr(FlMockRenderer) renderer = - fl_mock_renderer_new(&renderer_get_refresh_rate); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); fl_renderer_setup(FL_RENDERER(renderer)); fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); FlutterBackingStoreConfig config = { @@ -191,5 +225,7 @@ TEST(FlRendererTest, BlitFramebufferNvidia) { .size = {.width = 1024, .height = 1024}}; const FlutterLayer* layers[] = {&layer0}; fl_renderer_present_layers(FL_RENDERER(renderer), 0, layers, 1); - fl_renderer_render(FL_RENDERER(renderer), 1024, 1024); + GdkRGBA background_color = { + .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}; + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024, &background_color); } diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 77414b07cba45..1874b265b2529 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -46,6 +46,9 @@ struct _FlView { // Rendering output. FlRendererGdk* renderer; + // Background color. + GdkRGBA* background_color; + // Pointer button state recorded for sending status updates. int64_t button_state; @@ -607,7 +610,7 @@ static gboolean render_cb(FlView* self, GdkGLContext* context) { int height = gtk_widget_get_allocated_height(GTK_WIDGET(self->gl_area)); gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self->gl_area)); fl_renderer_render(FL_RENDERER(self->renderer), width * scale_factor, - height * scale_factor); + height * scale_factor, self->background_color); return TRUE; } @@ -654,6 +657,7 @@ static void fl_view_dispose(GObject* object) { g_clear_object(&self->engine); g_clear_object(&self->renderer); + g_clear_pointer(&self->background_color, gdk_rgba_free); g_clear_object(&self->window_state_monitor); g_clear_object(&self->scrolling_manager); g_clear_object(&self->keyboard_handler); @@ -706,6 +710,10 @@ static void fl_view_init(FlView* self) { // https://github.com/flutter/flutter/issues/138178 self->view_id = flutter::kFlutterImplicitViewId; + GdkRGBA default_background = { + .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}; + self->background_color = gdk_rgba_copy(&default_background); + self->event_box = gtk_event_box_new(); gtk_widget_set_hexpand(self->event_box, TRUE); gtk_widget_set_vexpand(self->event_box, TRUE); @@ -743,6 +751,7 @@ static void fl_view_init(FlView* self) { self); self->gl_area = GTK_GL_AREA(gtk_gl_area_new()); + gtk_gl_area_set_has_alpha(self->gl_area, TRUE); gtk_widget_show(GTK_WIDGET(self->gl_area)); gtk_container_add(GTK_CONTAINER(self->event_box), GTK_WIDGET(self->gl_area)); @@ -785,6 +794,13 @@ G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* self) { return self->engine; } +G_MODULE_EXPORT void fl_view_set_background_color(FlView* self, + const GdkRGBA* color) { + g_return_if_fail(FL_IS_VIEW(self)); + gdk_rgba_free(self->background_color); + self->background_color = gdk_rgba_copy(color); +} + void fl_view_redraw(FlView* self) { g_return_if_fail(FL_IS_VIEW(self)); gtk_widget_queue_draw(GTK_WIDGET(self->gl_area)); diff --git a/shell/platform/linux/public/flutter_linux/fl_view.h b/shell/platform/linux/public/flutter_linux/fl_view.h index 663e3d1500891..843e23c9f0a3e 100644 --- a/shell/platform/linux/public/flutter_linux/fl_view.h +++ b/shell/platform/linux/public/flutter_linux/fl_view.h @@ -69,6 +69,15 @@ FlView* fl_view_new_for_engine(FlEngine* engine); */ FlEngine* fl_view_get_engine(FlView* view); +/** + * fl_view_set_background_color: + * @view: an #FlView. + * @color: a background color. + * + * Set the background color for Flutter (defaults to black). + */ +void fl_view_set_background_color(FlView* view, const GdkRGBA* color); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_PUBLIC_FLUTTER_LINUX_FL_VIEW_H_ diff --git a/shell/platform/linux/testing/mock_epoxy.cc b/shell/platform/linux/testing/mock_epoxy.cc index 5f68e1d54c90f..b6a2cc38ea76f 100644 --- a/shell/platform/linux/testing/mock_epoxy.cc +++ b/shell/platform/linux/testing/mock_epoxy.cc @@ -382,6 +382,10 @@ GLuint _glCreateProgram() { void _glCompileShader(GLuint shader) {} +void _glClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { + mock->glClearColor(r, g, b, a); +} + GLuint _glCreateShader(GLenum shaderType) { return 0; } @@ -594,6 +598,7 @@ static void library_init() { epoxy_glBindTexture = _glBindTexture; epoxy_glBlitFramebuffer = _glBlitFramebuffer; epoxy_glCompileShader = _glCompileShader; + epoxy_glClearColor = _glClearColor; epoxy_glCreateProgram = _glCreateProgram; epoxy_glCreateShader = _glCreateShader; epoxy_glDeleteFramebuffers = _glDeleteFramebuffers; diff --git a/shell/platform/linux/testing/mock_epoxy.h b/shell/platform/linux/testing/mock_epoxy.h index 02eef6c239df9..846d43420ba1f 100644 --- a/shell/platform/linux/testing/mock_epoxy.h +++ b/shell/platform/linux/testing/mock_epoxy.h @@ -20,6 +20,7 @@ class MockEpoxy { MOCK_METHOD(bool, epoxy_has_gl_extension, (const char* extension)); MOCK_METHOD(bool, epoxy_is_desktop_gl, ()); MOCK_METHOD(int, epoxy_gl_version, ()); + MOCK_METHOD(void, glClearColor, (GLfloat r, GLfloat g, GLfloat b, GLfloat a)); MOCK_METHOD(void, glBlitFramebuffer, (GLint srcX0,