From 050055f52c207d445a481a2d62949a69579efa52 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 26 Apr 2022 19:39:03 +0200 Subject: [PATCH] [Linux] fix and test light vs. dark theme detection (flutter/engine#32618) --- .../ci/licenses_golden/licenses_flutter | 6 + .../src/flutter/shell/platform/linux/BUILD.gn | 13 ++ .../flutter/shell/platform/linux/fl_engine.cc | 3 +- .../shell/platform/linux/fl_gnome_settings.cc | 160 ++++++++++++++++++ .../shell/platform/linux/fl_gnome_settings.h | 29 ++++ .../platform/linux/fl_gnome_settings_test.cc | 112 ++++++++++++ .../shell/platform/linux/fl_settings.cc | 49 ++++++ .../shell/platform/linux/fl_settings.h | 106 ++++++++++++ .../platform/linux/fl_settings_plugin.cc | 130 +++----------- .../shell/platform/linux/fl_settings_plugin.h | 3 +- .../platform/linux/fl_settings_plugin_test.cc | 111 ++++++++++++ .../platform/linux/testing/gschemas/README.md | 21 +++ .../testing/gschemas/ubuntu-20.04.compiled | Bin 0 -> 35485 bytes .../platform/linux/testing/mock_settings.cc | 70 ++++++++ .../platform/linux/testing/mock_settings.h | 39 +++++ 15 files changed, 744 insertions(+), 108 deletions(-) create mode 100644 engine/src/flutter/shell/platform/linux/fl_gnome_settings.cc create mode 100644 engine/src/flutter/shell/platform/linux/fl_gnome_settings.h create mode 100644 engine/src/flutter/shell/platform/linux/fl_gnome_settings_test.cc create mode 100644 engine/src/flutter/shell/platform/linux/fl_settings.cc create mode 100644 engine/src/flutter/shell/platform/linux/fl_settings.h create mode 100644 engine/src/flutter/shell/platform/linux/fl_settings_plugin_test.cc create mode 100644 engine/src/flutter/shell/platform/linux/testing/gschemas/README.md create mode 100644 engine/src/flutter/shell/platform/linux/testing/gschemas/ubuntu-20.04.compiled create mode 100644 engine/src/flutter/shell/platform/linux/testing/mock_settings.cc create mode 100644 engine/src/flutter/shell/platform/linux/testing/mock_settings.h diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 3dc62641dd6..5ba213fbdab 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -2088,6 +2088,9 @@ 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_gnome_settings.cc +FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings.h +FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings_test.cc 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 @@ -2137,8 +2140,11 @@ 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_settings.cc +FILE: ../../../flutter/shell/platform/linux/fl_settings.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_settings_plugin_test.cc FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn index 9d218cd186f..a8c2a1c4023 100644 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ b/engine/src/flutter/shell/platform/linux/BUILD.gn @@ -107,6 +107,7 @@ source_set("flutter_linux_sources") { "fl_engine.cc", "fl_event_channel.cc", "fl_gl_area.cc", + "fl_gnome_settings.cc", "fl_json_message_codec.cc", "fl_json_method_codec.cc", "fl_key_channel_responder.cc", @@ -128,6 +129,7 @@ source_set("flutter_linux_sources") { "fl_renderer.cc", "fl_renderer_gl.cc", "fl_renderer_headless.cc", + "fl_settings.cc", "fl_settings_plugin.cc", "fl_standard_message_codec.cc", "fl_standard_method_codec.cc", @@ -175,6 +177,13 @@ test_fixtures("flutter_linux_fixtures") { fixtures = [] } +copy("flutter_linux_gschemas") { + testonly = true + + sources = [ "testing/gschemas/ubuntu-20.04.compiled" ] + outputs = [ "$target_gen_dir/assets/{{source_name_part}}/gschemas.compiled" ] +} + executable("flutter_linux_unittests") { testonly = true @@ -186,6 +195,7 @@ executable("flutter_linux_unittests") { "fl_dart_project_test.cc", "fl_engine_test.cc", "fl_event_channel_test.cc", + "fl_gnome_settings_test.cc", "fl_json_message_codec_test.cc", "fl_json_method_codec_test.cc", "fl_key_channel_responder_test.cc", @@ -197,6 +207,7 @@ executable("flutter_linux_unittests") { "fl_method_response_test.cc", "fl_pixel_buffer_texture_test.cc", "fl_plugin_registrar_test.cc", + "fl_settings_plugin_test.cc", "fl_standard_message_codec_test.cc", "fl_standard_method_codec_test.cc", "fl_string_codec_test.cc", @@ -210,6 +221,7 @@ executable("flutter_linux_unittests") { "testing/mock_epoxy.cc", "testing/mock_plugin_registrar.cc", "testing/mock_renderer.cc", + "testing/mock_settings.cc", "testing/mock_signal_handler.cc", "testing/mock_text_input_plugin.cc", "testing/mock_texture_registrar.cc", @@ -229,6 +241,7 @@ executable("flutter_linux_unittests") { deps = [ ":flutter_linux_fixtures", + ":flutter_linux_gschemas", ":flutter_linux_sources", "//flutter/runtime:libdart", "//flutter/shell/platform/embedder:embedder_headers", diff --git a/engine/src/flutter/shell/platform/linux/fl_engine.cc b/engine/src/flutter/shell/platform/linux/fl_engine.cc index 8684feae285..1f6b92a3cb7 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine.cc @@ -516,8 +516,9 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { setup_locales(self); + g_autoptr(FlSettings) settings = fl_settings_new(); self->settings_plugin = fl_settings_plugin_new(self->binary_messenger); - fl_settings_plugin_start(self->settings_plugin); + fl_settings_plugin_start(self->settings_plugin, settings); result = self->embedder_api.UpdateSemanticsEnabled(self->engine, TRUE); if (result != kSuccess) { diff --git a/engine/src/flutter/shell/platform/linux/fl_gnome_settings.cc b/engine/src/flutter/shell/platform/linux/fl_gnome_settings.cc new file mode 100644 index 00000000000..977c4c47b78 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_gnome_settings.cc @@ -0,0 +1,160 @@ +// 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_gnome_settings.h" + +#include +#include + +static constexpr char kDesktopInterfaceSchema[] = "org.gnome.desktop.interface"; +static constexpr char kDesktopTextScalingFactorKey[] = "text-scaling-factor"; +static constexpr char kDesktopClockFormatKey[] = "clock-format"; +static constexpr char kDesktopGtkThemeKey[] = "gtk-theme"; + +static constexpr char kClockFormat12Hour[] = "12h"; +static constexpr char kGtkThemeDarkSuffix[] = "-dark"; +static constexpr char kInterfaceSettings[] = "interface-settings"; + +struct _FlGnomeSettings { + GObject parent_instance; + + GSettings* interface_settings; +}; + +enum { PROP_0, PROP_INTERFACE_SETTINGS, PROP_LAST }; + +static void fl_gnome_settings_iface_init(FlSettingsInterface* iface); + +G_DEFINE_TYPE_WITH_CODE(FlGnomeSettings, + fl_gnome_settings, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(fl_settings_get_type(), + fl_gnome_settings_iface_init)) + +static FlClockFormat fl_gnome_settings_get_clock_format(FlSettings* settings) { + FlGnomeSettings* self = FL_GNOME_SETTINGS(settings); + + FlClockFormat clock_format = FL_CLOCK_FORMAT_24H; + + if (self->interface_settings != nullptr) { + g_autofree gchar* value = + g_settings_get_string(self->interface_settings, kDesktopClockFormatKey); + if (g_strcmp0(value, kClockFormat12Hour) == 0) { + clock_format = FL_CLOCK_FORMAT_12H; + } + } + return clock_format; +} + +static FlColorScheme fl_gnome_settings_get_color_scheme(FlSettings* settings) { + FlGnomeSettings* self = FL_GNOME_SETTINGS(settings); + + FlColorScheme color_scheme = FL_COLOR_SCHEME_LIGHT; + + if (self->interface_settings != nullptr) { + // check whether org.gnome.desktop.interface.gtk-theme ends with "-dark" + g_autofree gchar* value = + g_settings_get_string(self->interface_settings, kDesktopGtkThemeKey); + if (g_str_has_suffix(value, kGtkThemeDarkSuffix)) { + color_scheme = FL_COLOR_SCHEME_DARK; + } + } + return color_scheme; +} + +static gdouble fl_gnome_settings_get_text_scaling_factor(FlSettings* settings) { + FlGnomeSettings* self = FL_GNOME_SETTINGS(settings); + + gdouble scaling_factor = 1.0; + + if (self->interface_settings != nullptr) { + scaling_factor = g_settings_get_double(self->interface_settings, + kDesktopTextScalingFactorKey); + } + return scaling_factor; +} + +static void fl_gnome_settings_set_interface_settings(FlGnomeSettings* self, + GSettings* settings) { + g_return_if_fail(G_IS_SETTINGS(settings)); + + g_signal_connect_object(settings, "changed::clock-format", + G_CALLBACK(fl_settings_emit_changed), self, + G_CONNECT_SWAPPED); + g_signal_connect_object(settings, "changed::gtk-theme", + G_CALLBACK(fl_settings_emit_changed), self, + G_CONNECT_SWAPPED); + g_signal_connect_object(settings, "changed::text-scaling-factor", + G_CALLBACK(fl_settings_emit_changed), self, + G_CONNECT_SWAPPED); + + self->interface_settings = G_SETTINGS(g_object_ref(settings)); +} + +static void fl_gnome_settings_set_property(GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) { + FlGnomeSettings* self = FL_GNOME_SETTINGS(object); + switch (prop_id) { + case PROP_INTERFACE_SETTINGS: + fl_gnome_settings_set_interface_settings( + self, G_SETTINGS(g_value_get_object(value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void fl_gnome_settings_dispose(GObject* object) { + FlGnomeSettings* self = FL_GNOME_SETTINGS(object); + + g_clear_object(&self->interface_settings); + + G_OBJECT_CLASS(fl_gnome_settings_parent_class)->dispose(object); +} + +static void fl_gnome_settings_class_init(FlGnomeSettingsClass* klass) { + GObjectClass* object_class = G_OBJECT_CLASS(klass); + object_class->dispose = fl_gnome_settings_dispose; + object_class->set_property = fl_gnome_settings_set_property; + + g_object_class_install_property( + object_class, PROP_INTERFACE_SETTINGS, + g_param_spec_object( + kInterfaceSettings, kInterfaceSettings, kDesktopInterfaceSchema, + g_settings_get_type(), + static_cast(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); +} + +static void fl_gnome_settings_iface_init(FlSettingsInterface* iface) { + iface->get_clock_format = fl_gnome_settings_get_clock_format; + iface->get_color_scheme = fl_gnome_settings_get_color_scheme; + iface->get_text_scaling_factor = fl_gnome_settings_get_text_scaling_factor; +} + +static void fl_gnome_settings_init(FlGnomeSettings* self) {} + +static GSettings* create_settings(const gchar* schema_id) { + GSettings* settings = nullptr; + GSettingsSchemaSource* source = g_settings_schema_source_get_default(); + if (source != nullptr) { + g_autoptr(GSettingsSchema) schema = + g_settings_schema_source_lookup(source, schema_id, TRUE); + if (schema != nullptr) { + settings = g_settings_new_full(schema, nullptr, nullptr); + } + } + return settings; +} + +FlSettings* fl_gnome_settings_new() { + g_autoptr(GSettings) interface_settings = + create_settings(kDesktopInterfaceSchema); + return FL_SETTINGS(g_object_new(fl_gnome_settings_get_type(), + kInterfaceSettings, interface_settings, + nullptr)); +} diff --git a/engine/src/flutter/shell/platform/linux/fl_gnome_settings.h b/engine/src/flutter/shell/platform/linux/fl_gnome_settings.h new file mode 100644 index 00000000000..5aca6a583c9 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_gnome_settings.h @@ -0,0 +1,29 @@ +// 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_GNOME_SETTINGS_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_ + +#include "flutter/shell/platform/linux/fl_settings.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlGnomeSettings, + fl_gnome_settings, + FL, + GNOME_SETTINGS, + GObject); + +/** + * fl_gnome_settings_new: + * + * Creates a new settings instance for GNOME. + * + * Returns: a new #FlSettings. + */ +FlSettings* fl_gnome_settings_new(); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_ diff --git a/engine/src/flutter/shell/platform/linux/fl_gnome_settings_test.cc b/engine/src/flutter/shell/platform/linux/fl_gnome_settings_test.cc new file mode 100644 index 00000000000..f4cd92e0dda --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_gnome_settings_test.cc @@ -0,0 +1,112 @@ +// 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_gnome_settings.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "flutter/shell/platform/linux/testing/mock_settings.h" +#include "flutter/shell/platform/linux/testing/mock_signal_handler.h" +#include "flutter/testing/testing.h" + +#include +#define G_SETTINGS_ENABLE_BACKEND +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +class FlGnomeSettingsTest : public ::testing::Test { + protected: + void SetUp() override { + // force _g_io_modules_ensure_extension_points_registered() to get called + g_settings_backend_get_default(); + } +}; + +static GSettings* create_settings(const gchar* name, const gchar* schema_id) { + g_autofree gchar* path = + g_build_filename(flutter::testing::GetFixturesPath(), name, nullptr); + g_autoptr(GSettingsSchemaSource) source = + g_settings_schema_source_new_from_directory(path, nullptr, false, + nullptr); + g_autoptr(GSettingsSchema) schema = + g_settings_schema_source_lookup(source, schema_id, false); + g_autoptr(GSettingsBackend) backend = g_memory_settings_backend_new(); + return g_settings_new_full(schema, backend, nullptr); +} + +TEST_F(FlGnomeSettingsTest, ClockFormat) { + g_autoptr(GSettings) interface_settings = + create_settings("ubuntu-20.04", "org.gnome.desktop.interface"); + g_settings_set_string(interface_settings, "clock-format", "24h"); + + g_autoptr(FlSettings) settings = FL_SETTINGS( + g_object_new(fl_gnome_settings_get_type(), "interface_settings", + interface_settings, nullptr)); + EXPECT_EQ(fl_settings_get_clock_format(settings), FL_CLOCK_FORMAT_24H); + + flutter::testing::MockSignalHandler settings_changed(settings, "changed"); + EXPECT_SIGNAL(settings_changed).Times(1); + + g_settings_set_string(interface_settings, "clock-format", "12h"); + EXPECT_EQ(fl_settings_get_clock_format(settings), FL_CLOCK_FORMAT_12H); +} + +TEST_F(FlGnomeSettingsTest, GtkTheme) { + g_autoptr(GSettings) interface_settings = + create_settings("ubuntu-20.04", "org.gnome.desktop.interface"); + g_settings_set_string(interface_settings, "gtk-theme", "Yaru"); + + g_autoptr(FlSettings) settings = FL_SETTINGS( + g_object_new(fl_gnome_settings_get_type(), "interface_settings", + interface_settings, nullptr)); + EXPECT_EQ(fl_settings_get_color_scheme(settings), FL_COLOR_SCHEME_LIGHT); + + flutter::testing::MockSignalHandler settings_changed(settings, "changed"); + EXPECT_SIGNAL(settings_changed).Times(1); + + g_settings_set_string(interface_settings, "gtk-theme", "Yaru-dark"); + EXPECT_EQ(fl_settings_get_color_scheme(settings), FL_COLOR_SCHEME_DARK); +} + +TEST_F(FlGnomeSettingsTest, TextScalingFactor) { + g_autoptr(GSettings) interface_settings = + create_settings("ubuntu-20.04", "org.gnome.desktop.interface"); + g_settings_set_double(interface_settings, "text-scaling-factor", 1.0); + + g_autoptr(FlSettings) settings = FL_SETTINGS( + g_object_new(fl_gnome_settings_get_type(), "interface_settings", + interface_settings, nullptr)); + EXPECT_EQ(fl_settings_get_text_scaling_factor(settings), 1.0); + + flutter::testing::MockSignalHandler settings_changed(settings, "changed"); + EXPECT_SIGNAL(settings_changed).Times(1); + + g_settings_set_double(interface_settings, "text-scaling-factor", 1.5); + EXPECT_EQ(fl_settings_get_text_scaling_factor(settings), 1.5); +} + +TEST_F(FlGnomeSettingsTest, SignalHandlers) { + g_autoptr(GSettings) interface_settings = + create_settings("ubuntu-20.04", "org.gnome.desktop.interface"); + + g_autoptr(FlSettings) settings = FL_SETTINGS( + g_object_new(fl_gnome_settings_get_type(), "interface_settings", + interface_settings, nullptr)); + flutter::testing::MockSignalHandler settings_changed(settings, "changed"); + + EXPECT_SIGNAL(settings_changed).Times(3); + + g_settings_set_string(interface_settings, "clock-format", "12h"); + g_settings_set_string(interface_settings, "gtk-theme", "Yaru-dark"); + g_settings_set_double(interface_settings, "text-scaling-factor", 1.5); + + EXPECT_SIGNAL(settings_changed).Times(0); + + g_clear_object(&settings); + + // destroyed FlSettings object must have disconnected its signal handlers + g_settings_set_string(interface_settings, "clock-format", "24h"); + g_settings_set_string(interface_settings, "gtk-theme", "Yaru"); + g_settings_set_double(interface_settings, "text-scaling-factor", 2.0); +} diff --git a/engine/src/flutter/shell/platform/linux/fl_settings.cc b/engine/src/flutter/shell/platform/linux/fl_settings.cc new file mode 100644 index 00000000000..c129da3146d --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_settings.cc @@ -0,0 +1,49 @@ +// 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_settings.h" +#include "flutter/shell/platform/linux/fl_gnome_settings.h" + +G_DEFINE_INTERFACE(FlSettings, fl_settings, G_TYPE_OBJECT) + +enum { + SIGNAL_CHANGED, + SIGNAL_LAST_SIGNAL, +}; + +static guint signals[SIGNAL_LAST_SIGNAL]; + +static void fl_settings_default_init(FlSettingsInterface* iface) { + /** + * FlSettings::changed: + * @settings: an #FlSettings + * + * This signal is emitted after the settings have been changed. + */ + signals[SIGNAL_CHANGED] = + g_signal_new("changed", G_TYPE_FROM_INTERFACE(iface), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +FlClockFormat fl_settings_get_clock_format(FlSettings* self) { + return FL_SETTINGS_GET_IFACE(self)->get_clock_format(self); +} + +FlColorScheme fl_settings_get_color_scheme(FlSettings* self) { + return FL_SETTINGS_GET_IFACE(self)->get_color_scheme(self); +} + +gdouble fl_settings_get_text_scaling_factor(FlSettings* self) { + return FL_SETTINGS_GET_IFACE(self)->get_text_scaling_factor(self); +} + +void fl_settings_emit_changed(FlSettings* self) { + g_return_if_fail(FL_IS_SETTINGS(self)); + g_signal_emit(self, signals[SIGNAL_CHANGED], 0); +} + +FlSettings* fl_settings_new() { + // TODO(jpnurmi): add support for other desktop environments + return FL_SETTINGS(fl_gnome_settings_new()); +} diff --git a/engine/src/flutter/shell/platform/linux/fl_settings.h b/engine/src/flutter/shell/platform/linux/fl_settings.h new file mode 100644 index 00000000000..25f823614f8 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_settings.h @@ -0,0 +1,106 @@ +// 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_SETTINGS_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_H_ + +#include + +G_BEGIN_DECLS + +G_DECLARE_INTERFACE(FlSettings, fl_settings, FL, SETTINGS, GObject) + +/** + * FlClockFormat: + * @FL_CLOCK_FORMAT_12H: 12-hour clock format. + * @FL_CLOCK_FORMAT_24H: 24-hour clock format. + * + * Available clock formats. + */ +typedef enum { + FL_CLOCK_FORMAT_12H, + FL_CLOCK_FORMAT_24H, +} FlClockFormat; + +/** + * FlColorScheme: + * @FL_COLOR_SCHEME_LIGHT: Prefer light theme. + * @FL_COLOR_SCHEME_DARK: Prefer dark theme. + * + * Available color schemes. + */ +typedef enum { + FL_COLOR_SCHEME_LIGHT, + FL_COLOR_SCHEME_DARK, +} FlColorScheme; + +/** + * FlSettings: + * #FlSettings is and object that provides desktop settings. + */ +struct _FlSettingsInterface { + GTypeInterface parent; + FlClockFormat (*get_clock_format)(FlSettings* settings); + FlColorScheme (*get_color_scheme)(FlSettings* settings); + gdouble (*get_text_scaling_factor)(FlSettings* settings); +}; + +/** + * fl_settings_new: + * + * Creates a new settings instance. + * + * Returns: a new #FlSettings. + */ +FlSettings* fl_settings_new(); + +/** + * fl_settings_get_clock_format: + * @settings: an #FlSettings. + * + * Whether the clock displays in 24-hour or 12-hour format. + * + * This corresponds to `org.gnome.desktop.interface.clock-format` in GNOME. + * + * Returns: an #FlClockFormat. + */ +FlClockFormat fl_settings_get_clock_format(FlSettings* settings); + +/** + * fl_settings_get_color_scheme: + * @settings: an #FlSettings. + * + * The preferred color scheme for the user interface. + * + * This corresponds to `org.gnome.desktop.interface.color-scheme` in GNOME. + * + * Returns: an #FlColorScheme. + */ +FlColorScheme fl_settings_get_color_scheme(FlSettings* settings); + +/** + * fl_settings_get_text_scaling_factor: + * @settings: an #FlSettings. + * + * Factor used to enlarge or reduce text display, without changing font size. + * + * This corresponds to `org.gnome.desktop.interface.text-scaling-factor` in + * GNOME. + * + * Returns: a floating point number. + */ +gdouble fl_settings_get_text_scaling_factor(FlSettings* settings); + +/** + * fl_settings_emit_changed: + * @settings: an #FlSettings. + * + * Emits the "changed" signal. Used by FlSettings implementations to notify when + * the desktop settings have changed. + */ +void fl_settings_emit_changed(FlSettings* settings); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_H_ diff --git a/engine/src/flutter/shell/platform/linux/fl_settings_plugin.cc b/engine/src/flutter/shell/platform/linux/fl_settings_plugin.cc index 1cf50112f8e..89c80016786 100644 --- a/engine/src/flutter/shell/platform/linux/fl_settings_plugin.cc +++ b/engine/src/flutter/shell/platform/linux/fl_settings_plugin.cc @@ -5,8 +5,6 @@ #include "flutter/shell/platform/linux/fl_settings_plugin.h" #include -#include -#include #include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h" @@ -18,98 +16,42 @@ static constexpr char kPlatformBrightnessKey[] = "platformBrightness"; static constexpr char kPlatformBrightnessLight[] = "light"; static constexpr char kPlatformBrightnessDark[] = "dark"; -static constexpr char kDesktopInterfaceSchema[] = "org.gnome.desktop.interface"; -static constexpr char kDesktopTextScalingFactorKey[] = "text-scaling-factor"; -static constexpr char kDesktopClockFormatKey[] = "clock-format"; -static constexpr char kClockFormat24Hour[] = "24h"; - -enum class Brightness { Light, Dark }; - struct _FlSettingsPlugin { GObject parent_instance; FlBasicMessageChannel* channel; - GSettings* interface_settings; - - GArray* connections; + FlSettings* settings; }; G_DEFINE_TYPE(FlSettingsPlugin, fl_settings_plugin, G_TYPE_OBJECT) -// The color brightness calculation has been adapted from theme_data.dart: -// https://github.com/flutter/flutter/blob/8fe4cc79648a952f9c7e49a5248756c2ff98fa3b/packages/flutter/lib/src/material/theme_data.dart#L1470-L1488 - -// See . -static gdouble linearize_color_component(gdouble component) { - if (component <= 0.03928) { - return component / 12.92; +static const gchar* to_platform_brightness(FlColorScheme color_scheme) { + switch (color_scheme) { + case FL_COLOR_SCHEME_LIGHT: + return kPlatformBrightnessLight; + case FL_COLOR_SCHEME_DARK: + return kPlatformBrightnessDark; + default: + g_return_val_if_reached(nullptr); } - return pow((component + 0.055) / 1.055, 2.4); -} - -// See . -gdouble compute_luminance(GdkRGBA* color) { - gdouble r = linearize_color_component(color->red); - gdouble g = linearize_color_component(color->green); - gdouble b = linearize_color_component(color->blue); - return 0.2126 * r + 0.7152 * g + 0.0722 * b; -} - -static Brightness estimate_brightness_for_color(GdkRGBA* color) { - gdouble relative_luminance = compute_luminance(color); - - // See and - // . - const gdouble kThreshold = 0.15; - if ((relative_luminance + 0.05) * (relative_luminance + 0.05) > kThreshold) { - return Brightness::Light; - } - return Brightness::Dark; -} - -static bool is_dark_theme() { - // GTK doesn't have a specific flag for dark themes, so we check if the - // style text color is light or dark - GList* windows = gtk_window_list_toplevels(); - if (windows == nullptr) { - return false; - } - - GtkWidget* window = GTK_WIDGET(windows->data); - g_list_free(windows); - - GdkRGBA text_color; - GtkStyleContext* style = gtk_widget_get_style_context(window); - gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &text_color); - return estimate_brightness_for_color(&text_color) == Brightness::Light; } // Sends the current settings to the Flutter engine. static void update_settings(FlSettingsPlugin* self) { - gdouble scaling_factor = 1.0; - gboolean always_use_24hr = FALSE; - const gchar* platform_brightness = kPlatformBrightnessLight; - - if (self->interface_settings != nullptr) { - scaling_factor = g_settings_get_double(self->interface_settings, - kDesktopTextScalingFactorKey); - g_autofree gchar* clock_format = - g_settings_get_string(self->interface_settings, kDesktopClockFormatKey); - always_use_24hr = g_strcmp0(clock_format, kClockFormat24Hour) == 0; - } - - if (is_dark_theme()) { - platform_brightness = kPlatformBrightnessDark; - } + FlClockFormat clock_format = fl_settings_get_clock_format(self->settings); + FlColorScheme color_scheme = fl_settings_get_color_scheme(self->settings); + gdouble scaling_factor = fl_settings_get_text_scaling_factor(self->settings); g_autoptr(FlValue) message = fl_value_new_map(); fl_value_set_string_take(message, kTextScaleFactorKey, fl_value_new_float(scaling_factor)); - fl_value_set_string_take(message, kAlwaysUse24HourFormatKey, - fl_value_new_bool(always_use_24hr)); - fl_value_set_string_take(message, kPlatformBrightnessKey, - fl_value_new_string(platform_brightness)); + fl_value_set_string_take( + message, kAlwaysUse24HourFormatKey, + fl_value_new_bool(clock_format == FL_CLOCK_FORMAT_24H)); + fl_value_set_string_take( + message, kPlatformBrightnessKey, + fl_value_new_string(to_platform_brightness(color_scheme))); fl_basic_message_channel_send(self->channel, message, nullptr, nullptr, nullptr); } @@ -117,13 +59,8 @@ static void update_settings(FlSettingsPlugin* self) { static void fl_settings_plugin_dispose(GObject* object) { FlSettingsPlugin* self = FL_SETTINGS_PLUGIN(object); - for (guint i = 0; i < self->connections->len; i += 1) { - g_signal_handler_disconnect(self->interface_settings, - g_array_index(self->connections, gulong, i)); - } - g_array_unref(self->connections); g_clear_object(&self->channel); - g_clear_object(&self->interface_settings); + g_clear_object(&self->settings); G_OBJECT_CLASS(fl_settings_plugin_parent_class)->dispose(object); } @@ -143,36 +80,17 @@ FlSettingsPlugin* fl_settings_plugin_new(FlBinaryMessenger* messenger) { g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new(); self->channel = fl_basic_message_channel_new(messenger, kChannelName, FL_MESSAGE_CODEC(codec)); - self->connections = g_array_new(FALSE, FALSE, sizeof(gulong)); return self; } -void fl_settings_plugin_start(FlSettingsPlugin* self) { +void fl_settings_plugin_start(FlSettingsPlugin* self, FlSettings* settings) { g_return_if_fail(FL_IS_SETTINGS_PLUGIN(self)); + g_return_if_fail(FL_IS_SETTINGS(settings)); - // If we are on GNOME, get settings from GSettings. - GSettingsSchemaSource* source = g_settings_schema_source_get_default(); - if (source != nullptr) { - g_autoptr(GSettingsSchema) schema = - g_settings_schema_source_lookup(source, kDesktopInterfaceSchema, FALSE); - if (schema != nullptr) { - self->interface_settings = g_settings_new_full(schema, nullptr, nullptr); - gulong new_connections[] = { - g_signal_connect_object( - self->interface_settings, "changed::text-scaling-factor", - G_CALLBACK(update_settings), self, G_CONNECT_SWAPPED), - g_signal_connect_object( - self->interface_settings, "changed::clock-format", - G_CALLBACK(update_settings), self, G_CONNECT_SWAPPED), - g_signal_connect_object( - self->interface_settings, "changed::gtk-theme", - G_CALLBACK(update_settings), self, G_CONNECT_SWAPPED), - }; - g_array_append_vals(self->connections, new_connections, - sizeof(new_connections) / sizeof(gulong)); - } - } + self->settings = FL_SETTINGS(g_object_ref(settings)); + g_signal_connect_object(settings, "changed", G_CALLBACK(update_settings), + self, G_CONNECT_SWAPPED); update_settings(self); } diff --git a/engine/src/flutter/shell/platform/linux/fl_settings_plugin.h b/engine/src/flutter/shell/platform/linux/fl_settings_plugin.h index 26c146880d4..344a98dd8db 100644 --- a/engine/src/flutter/shell/platform/linux/fl_settings_plugin.h +++ b/engine/src/flutter/shell/platform/linux/fl_settings_plugin.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_PLUGIN_H_ #define FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_PLUGIN_H_ +#include "flutter/shell/platform/linux/fl_settings.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" G_BEGIN_DECLS @@ -38,7 +39,7 @@ FlSettingsPlugin* fl_settings_plugin_new(FlBinaryMessenger* messenger); * * Sends the current settings to the engine and updates when they change. */ -void fl_settings_plugin_start(FlSettingsPlugin* plugin); +void fl_settings_plugin_start(FlSettingsPlugin* plugin, FlSettings* settings); G_END_DECLS diff --git a/engine/src/flutter/shell/platform/linux/fl_settings_plugin_test.cc b/engine/src/flutter/shell/platform/linux/fl_settings_plugin_test.cc new file mode 100644 index 00000000000..20a47314c08 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_settings_plugin_test.cc @@ -0,0 +1,111 @@ +// 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_settings_plugin.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h" +#include "flutter/shell/platform/linux/testing/mock_settings.h" +#include "flutter/testing/testing.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +MATCHER_P2(HasSetting, key, value, "") { + g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new(); + g_autoptr(FlValue) message = + fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), arg, nullptr); + if (fl_value_equal(fl_value_lookup_string(message, key), value)) { + return true; + } + *result_listener << ::testing::PrintToString(message); + return false; +} + +#define EXPECT_SETTING(mock, messenger, key, value) \ + EXPECT_CALL(mock, fl_binary_messenger_send_on_channel( \ + messenger, ::testing::StrEq("flutter/settings"), \ + HasSetting(key, value), ::testing::A(), \ + ::testing::A(), \ + ::testing::A())) + +TEST(FlSettingsPluginTest, AlwaysUse24HourFormat) { + ::testing::NiceMock settings; + + ::testing::NiceMock mock_messenger; + g_autoptr(FlBinaryMessenger) messenger = + fl_binary_messenger_new_mock(&mock_messenger); + + g_autoptr(FlSettingsPlugin) plugin = fl_settings_plugin_new(messenger); + + g_autoptr(FlValue) use_12h = fl_value_new_bool(false); + g_autoptr(FlValue) use_24h = fl_value_new_bool(true); + + EXPECT_CALL(settings, fl_settings_get_clock_format( + ::testing::Eq(settings))) + .WillOnce(::testing::Return(FL_CLOCK_FORMAT_12H)) + .WillOnce(::testing::Return(FL_CLOCK_FORMAT_24H)); + + EXPECT_SETTING(mock_messenger, messenger, "alwaysUse24HourFormat", use_12h); + + fl_settings_plugin_start(plugin, settings); + + EXPECT_SETTING(mock_messenger, messenger, "alwaysUse24HourFormat", use_24h); + + fl_settings_emit_changed(settings); +} + +TEST(FlSettingsPluginTest, PlatformBrightness) { + ::testing::NiceMock settings; + + ::testing::NiceMock mock_messenger; + g_autoptr(FlBinaryMessenger) messenger = + fl_binary_messenger_new_mock(&mock_messenger); + + g_autoptr(FlSettingsPlugin) plugin = fl_settings_plugin_new(messenger); + + g_autoptr(FlValue) light = fl_value_new_string("light"); + g_autoptr(FlValue) dark = fl_value_new_string("dark"); + + EXPECT_CALL(settings, fl_settings_get_color_scheme( + ::testing::Eq(settings))) + .WillOnce(::testing::Return(FL_COLOR_SCHEME_LIGHT)) + .WillOnce(::testing::Return(FL_COLOR_SCHEME_DARK)); + + EXPECT_SETTING(mock_messenger, messenger, "platformBrightness", light); + + fl_settings_plugin_start(plugin, settings); + + EXPECT_SETTING(mock_messenger, messenger, "platformBrightness", dark); + + fl_settings_emit_changed(settings); +} + +TEST(FlSettingsPluginTest, TextScaleFactor) { + ::testing::NiceMock settings; + + ::testing::NiceMock mock_messenger; + g_autoptr(FlBinaryMessenger) messenger = + fl_binary_messenger_new_mock(&mock_messenger); + + g_autoptr(FlSettingsPlugin) plugin = fl_settings_plugin_new(messenger); + + g_autoptr(FlValue) one = fl_value_new_float(1.0); + g_autoptr(FlValue) two = fl_value_new_float(2.0); + + EXPECT_CALL(settings, fl_settings_get_text_scaling_factor( + ::testing::Eq(settings))) + .WillOnce(::testing::Return(1.0)) + .WillOnce(::testing::Return(2.0)); + + EXPECT_SETTING(mock_messenger, messenger, "textScaleFactor", one); + + fl_settings_plugin_start(plugin, settings); + + EXPECT_SETTING(mock_messenger, messenger, "textScaleFactor", two); + + fl_settings_emit_changed(settings); +} diff --git a/engine/src/flutter/shell/platform/linux/testing/gschemas/README.md b/engine/src/flutter/shell/platform/linux/testing/gschemas/README.md new file mode 100644 index 00000000000..8897df6ca64 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/testing/gschemas/README.md @@ -0,0 +1,21 @@ +# gsettings-desktop-schemas + +This directory contains a few variants of +[gsettings-desktop-schemas](https://packages.ubuntu.com/search?keywords=gsettings-desktop-schemas) +with different schemas for testing purposes. + +- [`ubuntu-20.04.compiled`](https://packages.ubuntu.com/focal/gsettings-desktop-schemas) + +### Add or update schemas + +```bash +# download gsettings-desktop-schemas package +wget http://archive.ubuntu.com/ubuntu/pool/main/g/gsettings-desktop-schemas/gsettings-desktop-schemas_.deb + +# extract schema sources (/usr/share/glib-2.0/schemas/*.gschema.xml & .override) +ar x gsettings-desktop-schemas_.deb +tar xf data.tar.zst + +# compile schemas (/usr/share/glib-2.0/schemas/gschemas.compiled) +glib-compile-schemas --targetdir path/to/testing/gschemas usr/share/glib-2.0/schemas/ +``` diff --git a/engine/src/flutter/shell/platform/linux/testing/gschemas/ubuntu-20.04.compiled b/engine/src/flutter/shell/platform/linux/testing/gschemas/ubuntu-20.04.compiled new file mode 100644 index 0000000000000000000000000000000000000000..1ad3a51de999858d248e04fa85fec96208afa885 GIT binary patch literal 35485 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 literal 0 HcmV?d00001 diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_settings.cc b/engine/src/flutter/shell/platform/linux/testing/mock_settings.cc new file mode 100644 index 00000000000..944b13f2eb8 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/testing/mock_settings.cc @@ -0,0 +1,70 @@ +// 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/testing/mock_settings.h" + +using namespace flutter::testing; + +G_DECLARE_FINAL_TYPE(FlMockSettings, + fl_mock_settings, + FL, + MOCK_SETTINGS, + GObject) + +struct _FlMockSettings { + GObject parent_instance; + MockSettings* mock; +}; + +static void fl_mock_settings_iface_init(FlSettingsInterface* iface); + +#define FL_UNUSED(x) (void)x; + +G_DEFINE_TYPE_WITH_CODE(FlMockSettings, + fl_mock_settings, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(fl_settings_get_type(), + fl_mock_settings_iface_init) + FL_UNUSED(FL_IS_MOCK_SETTINGS)) + +static void fl_mock_settings_class_init(FlMockSettingsClass* klass) {} + +static FlClockFormat fl_mock_settings_get_clock_format(FlSettings* settings) { + FlMockSettings* self = FL_MOCK_SETTINGS(settings); + return self->mock->fl_settings_get_clock_format(settings); +} + +static FlColorScheme fl_mock_settings_get_color_scheme(FlSettings* settings) { + FlMockSettings* self = FL_MOCK_SETTINGS(settings); + return self->mock->fl_settings_get_color_scheme(settings); +} + +static gdouble fl_mock_settings_get_text_scaling_factor(FlSettings* settings) { + FlMockSettings* self = FL_MOCK_SETTINGS(settings); + return self->mock->fl_settings_get_text_scaling_factor(settings); +} + +static void fl_mock_settings_iface_init(FlSettingsInterface* iface) { + iface->get_clock_format = fl_mock_settings_get_clock_format; + iface->get_color_scheme = fl_mock_settings_get_color_scheme; + iface->get_text_scaling_factor = fl_mock_settings_get_text_scaling_factor; +} + +static void fl_mock_settings_init(FlMockSettings* self) {} + +MockSettings::MockSettings() + : instance_( + FL_SETTINGS(g_object_new(fl_mock_settings_get_type(), nullptr))) { + FL_MOCK_SETTINGS(instance_)->mock = this; +} + +MockSettings::~MockSettings() { + if (instance_ != nullptr) { + g_clear_object(&instance_); + } +} + +MockSettings::operator FlSettings*() { + return instance_; +} diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_settings.h b/engine/src/flutter/shell/platform/linux/testing/mock_settings.h new file mode 100644 index 00000000000..79f77faab28 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/testing/mock_settings.h @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_SETTINGS_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_SETTINGS_H_ + +#include "flutter/shell/platform/linux/fl_settings.h" + +#include "gmock/gmock.h" + +namespace flutter { +namespace testing { + +// Mock for FlSettings. +class MockSettings { + public: + MockSettings(); + ~MockSettings(); + + operator FlSettings*(); + + MOCK_METHOD1(fl_settings_get_clock_format, + FlClockFormat(FlSettings* settings)); + + MOCK_METHOD1(fl_settings_get_color_scheme, + FlColorScheme(FlSettings* settings)); + + MOCK_METHOD1(fl_settings_get_text_scaling_factor, + gdouble(FlSettings* settings)); + + private: + FlSettings* instance_ = nullptr; +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_SETTINGS_H_