From 5f4262d2e1c63e9640a4aa96d73e85bd131dd256 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 12 Apr 2022 13:25:16 +0200 Subject: [PATCH 1/2] [Linux] resort to "-dark" suffix for detecting platform brightness Some users have reported that toggling between light and dark system theme results in Flutter detecting the opposite theme. This is because the window text color, which is used to calculate the brightness, may not have been yet refreshed when the theme change signal is emitted. This commit replaces the unreliable (and untestable) window text color -based theme detection with the simpler alternative proposed in flutter/engine#25535 to check for a "-dark" suffix in the theme name, which is used by convention in dark GTK theme names (Yaru vs. Yaru-dark, Adwaita vs. Adwaita-dark, Pop vs. Pop-dark, ...). Ref: flutter/flutter#101438 --- shell/platform/linux/fl_settings_plugin.cc | 62 +++------------------- 1 file changed, 7 insertions(+), 55 deletions(-) diff --git a/shell/platform/linux/fl_settings_plugin.cc b/shell/platform/linux/fl_settings_plugin.cc index 1cf50112f8efa..fedec5e4d1464 100644 --- a/shell/platform/linux/fl_settings_plugin.cc +++ b/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" @@ -21,9 +19,9 @@ 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 kDesktopGtkThemeKey[] = "gtk-theme"; static constexpr char kClockFormat24Hour[] = "24h"; - -enum class Brightness { Light, Dark }; +static constexpr char kGtkThemeDarkSuffix[] = "-dark"; struct _FlSettingsPlugin { GObject parent_instance; @@ -37,54 +35,6 @@ struct _FlSettingsPlugin { 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; - } - 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; @@ -97,10 +47,12 @@ static void update_settings(FlSettingsPlugin* self) { 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; + g_autofree gchar* gtk_theme = + g_settings_get_string(self->interface_settings, kDesktopGtkThemeKey); + if (g_str_has_suffix(gtk_theme, kGtkThemeDarkSuffix)) { + platform_brightness = kPlatformBrightnessDark; + } } g_autoptr(FlValue) message = fl_value_new_map(); From 9ddd2ca0a168b64888a8b13205cf3ecfdc3bacf1 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 12 Apr 2022 13:14:50 +0200 Subject: [PATCH 2/2] [Linux] make desktop settings testable FlSettings is an interface that provides desktop settings and notifies changes, whereas FlSettingsPlugin merely communicates the settings to the framework. This makes it straightforward to test both separately. As a bonus, it will be easy to add FlSettings implementations for other DEs too. --- ci/licenses_golden/licenses_flutter | 6 + shell/platform/linux/BUILD.gn | 13 ++ 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 ++++++++++++ shell/platform/linux/fl_settings_plugin.cc | 88 +++------- 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 shell/platform/linux/testing/mock_settings.cc | 70 ++++++++ shell/platform/linux/testing/mock_settings.h | 39 +++++ 15 files changed, 747 insertions(+), 63 deletions(-) create mode 100644 shell/platform/linux/fl_gnome_settings.cc create mode 100644 shell/platform/linux/fl_gnome_settings.h create mode 100644 shell/platform/linux/fl_gnome_settings_test.cc create mode 100644 shell/platform/linux/fl_settings.cc create mode 100644 shell/platform/linux/fl_settings.h create mode 100644 shell/platform/linux/fl_settings_plugin_test.cc create mode 100644 shell/platform/linux/testing/gschemas/README.md create mode 100644 shell/platform/linux/testing/gschemas/ubuntu-20.04.compiled create mode 100644 shell/platform/linux/testing/mock_settings.cc create mode 100644 shell/platform/linux/testing/mock_settings.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e1dec7770725b..45dc550c37d46 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2027,6 +2027,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 @@ -2076,8 +2079,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/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 9d218cd186fe4..a8c2a1c4023d2 100644 --- a/shell/platform/linux/BUILD.gn +++ b/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/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 8684feae285f3..1f6b92a3cb72e 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/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/shell/platform/linux/fl_gnome_settings.cc b/shell/platform/linux/fl_gnome_settings.cc new file mode 100644 index 0000000000000..977c4c47b780a --- /dev/null +++ b/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/shell/platform/linux/fl_gnome_settings.h b/shell/platform/linux/fl_gnome_settings.h new file mode 100644 index 0000000000000..5aca6a583c9a8 --- /dev/null +++ b/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/shell/platform/linux/fl_gnome_settings_test.cc b/shell/platform/linux/fl_gnome_settings_test.cc new file mode 100644 index 0000000000000..f4cd92e0ddafb --- /dev/null +++ b/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/shell/platform/linux/fl_settings.cc b/shell/platform/linux/fl_settings.cc new file mode 100644 index 0000000000000..c129da3146d26 --- /dev/null +++ b/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/shell/platform/linux/fl_settings.h b/shell/platform/linux/fl_settings.h new file mode 100644 index 0000000000000..25f823614f858 --- /dev/null +++ b/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/shell/platform/linux/fl_settings_plugin.cc b/shell/platform/linux/fl_settings_plugin.cc index fedec5e4d1464..89c800167860c 100644 --- a/shell/platform/linux/fl_settings_plugin.cc +++ b/shell/platform/linux/fl_settings_plugin.cc @@ -16,52 +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 kDesktopGtkThemeKey[] = "gtk-theme"; -static constexpr char kClockFormat24Hour[] = "24h"; -static constexpr char kGtkThemeDarkSuffix[] = "-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) +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); + } +} + // 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; - - g_autofree gchar* gtk_theme = - g_settings_get_string(self->interface_settings, kDesktopGtkThemeKey); - if (g_str_has_suffix(gtk_theme, kGtkThemeDarkSuffix)) { - 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); } @@ -69,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); } @@ -95,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/shell/platform/linux/fl_settings_plugin.h b/shell/platform/linux/fl_settings_plugin.h index 26c146880d4cb..344a98dd8db91 100644 --- a/shell/platform/linux/fl_settings_plugin.h +++ b/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/shell/platform/linux/fl_settings_plugin_test.cc b/shell/platform/linux/fl_settings_plugin_test.cc new file mode 100644 index 0000000000000..20a47314c08d0 --- /dev/null +++ b/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/shell/platform/linux/testing/gschemas/README.md b/shell/platform/linux/testing/gschemas/README.md new file mode 100644 index 0000000000000..8897df6ca64c0 --- /dev/null +++ b/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/shell/platform/linux/testing/gschemas/ubuntu-20.04.compiled b/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/shell/platform/linux/testing/mock_settings.cc b/shell/platform/linux/testing/mock_settings.cc new file mode 100644 index 0000000000000..944b13f2eb880 --- /dev/null +++ b/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/shell/platform/linux/testing/mock_settings.h b/shell/platform/linux/testing/mock_settings.h new file mode 100644 index 0000000000000..79f77faab28d0 --- /dev/null +++ b/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_