From fed0dff4c7ee78690111d3b928bf938d130bef13 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Wed, 3 Jun 2026 14:39:33 -0500 Subject: [PATCH] Read dark-mode setting from the desktop portal There are numerous ways to make a GTK theme dark The GtkSettings value is deprecated: https://docs.gtk.org/gtk4/property.Settings.gtk-application-prefer-dark-theme.html The GSettings value isn't deprecated but was never a documented standard outside of GNOME and has the downside of not being accurate inside of any sandbox. The portal setting is a standardized value that multiple desktops set. This was problematic for the `wig` project which uses libadwaita, so the platform and application can be completely out of sync regarding dark mode. libadwaita also respects the portal value. libportal was used for convenience, but it's just some basic dbus usage if that's preferred. --- .gitignore | 2 ++ meson.build | 13 ++++++++++ src/meson.build | 2 +- src/wpe-display-gtk.c | 51 ++++++++++++++++++-------------------- subprojects/libportal.wrap | 8 ++++++ 5 files changed, 48 insertions(+), 28 deletions(-) create mode 100644 .gitignore create mode 100644 subprojects/libportal.wrap diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8ca369 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/subprojects/libportal-*/ +/subprojects/packagecache/ diff --git a/meson.build b/meson.build index a61990d..115ede5 100644 --- a/meson.build +++ b/meson.build @@ -16,6 +16,19 @@ wpe_platform_dep = dependency('wpe-platform-2.0', version: '>= 2.51.3') gmodule_dep = dependency('gmodule-2.0', version: '>= 2.70.0') gtk_dep = dependency('gtk4', version: '>= 4.16.0') epoxy_dep = dependency('epoxy', version: '>= 1.4') +libportal_dep = dependency('libportal', version: '>= 0.8.0', + fallback: ['libportal', 'libportal_dep'], + default_options: [ + 'c_std=gnu11', + 'introspection=false', + 'vapi=false', + 'docs=false', + 'backend-gtk4=disabled', + 'backend-qt6=disabled', + 'backend-qt5=disabled', + 'backend-gtk3=disabled', + ] +) wpe_platform_module_dir = wpe_platform_dep.get_variable('moduledir', pkgconfig_define: ['libdir', join_paths(prefix, libdir)]) diff --git a/src/meson.build b/src/meson.build index a72629d..bb2ce8e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -35,7 +35,7 @@ configure_file( libwpeplatformgtk = shared_library( 'wpeplatform-gtk', sources: libwpeplatformgtk_sources, - dependencies: [ wpe_platform_dep, gtk_dep, epoxy_dep ], + dependencies: [ wpe_platform_dep, gtk_dep, epoxy_dep, libportal_dep ], gnu_symbol_visibility: 'hidden', install: true ) diff --git a/src/wpe-display-gtk.c b/src/wpe-display-gtk.c index 28d1bde..30ee669 100644 --- a/src/wpe-display-gtk.c +++ b/src/wpe-display-gtk.c @@ -30,6 +30,8 @@ #include "wpe-toplevel-gtk.h" #include "wpe-view-gtk.h" #include +#include +#include #ifdef GDK_WINDOWING_WAYLAND #include @@ -53,7 +55,7 @@ struct _WPEDisplayGtk { WPEClipboard *clipboard; GPtrArray *screens; - GSettings *desktop_settings; + XdpSettings *portal_settings; }; G_DEFINE_DYNAMIC_TYPE_EXTENDED(WPEDisplayGtk, wpe_display_gtk, WPE_TYPE_DISPLAY, G_TYPE_FLAG_FINAL, {}) @@ -65,7 +67,7 @@ static void wpe_display_gtk_finalize(GObject *object) g_clear_pointer(&display_gtk->screens, g_ptr_array_unref); g_clear_object(&display_gtk->keymap); g_clear_object(&display_gtk->clipboard); - g_clear_object(&display_gtk->desktop_settings); + g_clear_object(&display_gtk->portal_settings); G_OBJECT_CLASS(wpe_display_gtk_parent_class)->finalize(object); } @@ -121,26 +123,6 @@ static WPESettingsSubpixelLayout wpe_subpixel_layout(const char *subpixel_layout return WPE_SETTINGS_SUBPIXEL_LAYOUT_RGB; } -static void color_scheme_settings_changed(GSettings *desktop_settings) -{ - gboolean use_dark_mode = g_settings_get_enum(desktop_settings, "color-scheme") == 1; - g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", use_dark_mode, NULL); -} - -static void wpe_display_gtk_setup_dark_mode(WPEDisplayGtk *display_gtk) -{ - if (g_file_test("/.flatpak-info", G_FILE_TEST_EXISTS)) - return; - - g_autoptr(GSettingsSchema) schema = g_settings_schema_source_lookup(g_settings_schema_source_get_default(), "org.gnome.desktop.interface", TRUE); - if (!schema || !g_settings_schema_has_key(schema, "color-scheme")) - return; - - display_gtk->desktop_settings = g_settings_new("org.gnome.desktop.interface"); - color_scheme_settings_changed(display_gtk->desktop_settings); - g_signal_connect(display_gtk->desktop_settings, "changed::color-scheme", G_CALLBACK(color_scheme_settings_changed), NULL); -} - static void gtk_settings_changed (WPEDisplayGtk *display_gtk) { WPESettings *settings = wpe_display_get_settings(WPE_DISPLAY(display_gtk)); @@ -152,7 +134,6 @@ static void gtk_settings_changed (WPEDisplayGtk *display_gtk) int font_antialias, font_hinting; char *font_hinting_style, *subpixel_layout; int font_dpi; - gboolean dark_theme; g_object_get(gtk_settings, "gtk-double-click-time", &double_click_time, "gtk-double-click-distance", &double_click_distance, @@ -164,7 +145,6 @@ static void gtk_settings_changed (WPEDisplayGtk *display_gtk) "gtk-xft-hintstyle", &font_hinting_style, "gtk-xft-rgba", &subpixel_layout, "gtk-xft-dpi", &font_dpi, - "gtk-application-prefer-dark-theme", &dark_theme, NULL); wpe_settings_set_value(settings, WPE_SETTING_DOUBLE_CLICK_TIME, g_variant_new_uint32(double_click_time), WPE_SETTINGS_SOURCE_PLATFORM, NULL); @@ -177,13 +157,21 @@ static void gtk_settings_changed (WPEDisplayGtk *display_gtk) wpe_settings_set_value(settings, WPE_SETTING_FONT_SUBPIXEL_LAYOUT, g_variant_new_byte(wpe_subpixel_layout(subpixel_layout)), WPE_SETTINGS_SOURCE_PLATFORM, NULL); } wpe_settings_set_value(settings, WPE_SETTING_FONT_DPI, g_variant_new_double(font_dpi), WPE_SETTINGS_SOURCE_PLATFORM, NULL); - wpe_settings_set_value(settings, WPE_SETTING_DARK_MODE, g_variant_new_boolean(dark_theme), WPE_SETTINGS_SOURCE_PLATFORM, NULL); g_free(font_name); g_free(font_hinting_style); g_free(subpixel_layout); } +static void xdp_settings_changed(WPEDisplayGtk *display_gtk, char *namespace_, char *key, GVariant *value, XdpSettings *portal_settings) +{ + // https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html + if (!g_strcmp0(namespace_, "org.freedesktop.appearance") && !g_strcmp0(key, "color-scheme")) { + WPESettings *settings = wpe_display_get_settings(WPE_DISPLAY(display_gtk)); + wpe_settings_set_value(settings, WPE_SETTING_DARK_MODE, g_variant_new_boolean(g_variant_get_uint32(value) == 1), WPE_SETTINGS_SOURCE_PLATFORM, NULL); + } +} + static void wpe_display_gtk_setup_settings(WPEDisplayGtk *display_gtk) { GtkSettings *gtk_settings = gtk_settings_get_default(); @@ -196,9 +184,19 @@ static void wpe_display_gtk_setup_settings(WPEDisplayGtk *display_gtk) g_signal_connect_swapped(gtk_settings, "notify::gtk-xft-hintstyle", G_CALLBACK(gtk_settings_changed), display_gtk); g_signal_connect_swapped(gtk_settings, "notify::gtk-xft-rgba", G_CALLBACK(gtk_settings_changed), display_gtk); g_signal_connect_swapped(gtk_settings, "notify::gtk-xft-dpi", G_CALLBACK(gtk_settings_changed), display_gtk); - g_signal_connect_swapped(gtk_settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(gtk_settings_changed), display_gtk); gtk_settings_changed(display_gtk); + + XdpPortal *portal = xdp_portal_new(); + display_gtk->portal_settings = xdp_portal_get_settings(portal); + g_object_unref(portal); + + guint dark_mode = xdp_settings_read_uint(display_gtk->portal_settings, "org.freedesktop.appearance", "color-scheme", NULL, NULL); + + WPESettings *wpe_settings = wpe_display_get_settings(WPE_DISPLAY(display_gtk)); + wpe_settings_set_value(wpe_settings, WPE_SETTING_DARK_MODE, g_variant_new_boolean(dark_mode == 1), WPE_SETTINGS_SOURCE_PLATFORM, NULL); + + g_signal_connect_swapped(display_gtk->portal_settings, "changed", G_CALLBACK(xdp_settings_changed), display_gtk); } static gboolean wpe_display_gtk_connect(WPEDisplay *display, GError **error) @@ -247,7 +245,6 @@ G_GNUC_END_IGNORE_DEPRECATIONS } wpe_display_gtk_setup_screens(display_gtk); - wpe_display_gtk_setup_dark_mode(display_gtk); wpe_display_gtk_setup_settings(display_gtk); return TRUE; diff --git a/subprojects/libportal.wrap b/subprojects/libportal.wrap new file mode 100644 index 0000000..6f1eed9 --- /dev/null +++ b/subprojects/libportal.wrap @@ -0,0 +1,8 @@ +[wrap-file] +directory = libportal-0.9.1 +source_url = https://github.com/flatpak/libportal/releases/download/0.9.1/libportal-0.9.1.tar.xz +source_filename = libportal-0.9.1.tar.xz +source_hash = de801ee349ed3c255a9af3c01b1a401fab5b3fc1c35eb2fd7dfb35d4b8194d7f + +[provide] +dependency_names = libportal