From 3becb59acdd178f5352a536b3db507cd3b8d3336 Mon Sep 17 00:00:00 2001 From: Paul Cornett Date: Sat, 30 Sep 2023 16:42:58 -0700 Subject: [PATCH] Enable using a dark theme when Gnome "dark style" is set The dark style setting does not cause a dark theme to be used automatically, so request it explicitly. See #23764. (cherry picked from commit 9b9ec141fbcd604aa8bfc83da6f16fc9bf5c32e0) Co-authored-by: Colin Kinloch --- docs/changes.txt | 1 + src/gtk/settings.cpp | 112 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/docs/changes.txt b/docs/changes.txt index 9dfe3a4f9c..e5c0ebb44b 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -295,6 +295,7 @@ wxGTK: - Fix GSource leak when using wxIdleEvent::RequestMore() (#23364). - Allow using libwebkit2gtk-4.1 and libsoup-3.0 (Scott Talbert, #23630). - Support Numpad Enter for ending wxListCtrl editing (Scott Talbert, #23762). +- Improve dark mode detection (Colin Kinloch and Paul Cornett, #23764). wxMSW: diff --git a/src/gtk/settings.cpp b/src/gtk/settings.cpp index 3047247737..647778e585 100644 --- a/src/gtk/settings.cpp +++ b/src/gtk/settings.cpp @@ -183,6 +183,64 @@ static void notify_gtk_font_name(GObject*, GParamSpec*, void*) } } +static bool UpdatePreferDark(GVariant* value) +{ + // 0: No preference, 1: Prefer dark appearance, 2: Prefer light appearance + gboolean preferDark = g_variant_get_uint32(value) == 1; + + GtkSettings* settings = gtk_settings_get_default(); + char* themeName; + gboolean preferDarkPrev; + g_object_get(settings, + "gtk-theme-name", &themeName, + "gtk-application-prefer-dark-theme", &preferDarkPrev, NULL); + + // We don't need to enable prefer-dark if the theme is already dark + if (strstr(themeName, "-dark") || strstr(themeName, "-Dark")) + preferDark = false; + g_free(themeName); + + const bool changed = preferDark != preferDarkPrev; + if (changed) + { + g_object_set(settings, + "gtk-application-prefer-dark-theme", preferDark, NULL); + } + return changed; +} + +// "g-signal" from GDBusProxy +extern "C" { +static void +proxy_g_signal(GDBusProxy*, const char*, const char* signal_name, GVariant* parameters, void*) +{ + if (strcmp(signal_name, "SettingChanged") != 0) + return; + + const char* nameSpace; + const char* key; + GVariant* value; + g_variant_get(parameters, "(&s&sv)", &nameSpace, &key, &value); + if (strcmp(nameSpace, "org.freedesktop.appearance") == 0 && + strcmp(key, "color-scheme") == 0) + { + if (UpdatePreferDark(value)) + { + for (int i = wxSYS_COLOUR_MAX; i--;) + gs_systemColorCache[i].UnRef(); + + for (auto* win: wxTopLevelWindows) + { + wxSysColourChangedEvent event; + event.SetEventObject(win); + win->HandleWindowEvent(event); + } + } + } + g_variant_unref(value); +} +} + // Some notes on using GtkStyleContext. Style information from a context // attached to a non-visible GtkWidget is not accurate. The context has an // internal visibility state, controlled by the widget, which it presumably @@ -1124,12 +1182,62 @@ bool wxSystemSettingsNative::HasFeature(wxSystemFeature index) class wxSystemSettingsModule: public wxModule { public: - virtual bool OnInit() wxOVERRIDE { return true; } + virtual bool OnInit() wxOVERRIDE; virtual void OnExit() wxOVERRIDE; + +#ifdef __WXGTK3__ + GDBusProxy* m_proxy; +#endif wxDECLARE_DYNAMIC_CLASS(wxSystemSettingsModule); }; wxIMPLEMENT_DYNAMIC_CLASS(wxSystemSettingsModule, wxModule); +bool wxSystemSettingsModule::OnInit() +{ +#ifdef __WXGTK3__ + // Gnome has gone to a dark style setting rather than a selectable dark + // theme, available via GSettings as the 'color-scheme' key under the + // 'org.gnome.desktop.interface' schema. It's also available via a "portal" + // (https://docs.flatpak.org/en/latest/portal-api-reference.html), which + // has the advantage of allowing the setting to be accessed from within a + // virtualized environment such as Flatpak. Since the setting does not + // change the theme, we propagate it to the GtkSettings + // 'gtk-application-prefer-dark-theme' property to get a dark theme. + + m_proxy = NULL; + + // GTK_THEME environment variable overrides other settings + if (getenv("GTK_THEME") == NULL) + { + m_proxy = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + NULL, NULL); + } + if (m_proxy) + { + g_signal_connect(m_proxy, "g-signal", G_CALLBACK(proxy_g_signal), NULL); + + GVariant* ret = g_dbus_proxy_call_sync(m_proxy, "Read", + g_variant_new("(ss)", "org.freedesktop.appearance", "color-scheme"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); + if (ret) + { + GVariant* child; + g_variant_get(ret, "(v)", &child); + GVariant* value = g_variant_get_variant(child); + UpdatePreferDark(value); + g_variant_unref(value); + g_variant_unref(child); + g_variant_unref(ret); + } + } +#endif // __WXGTK3__ + return true; +} + void wxSystemSettingsModule::OnExit() { #ifdef __WXGTK3__ @@ -1141,6 +1249,8 @@ void wxSystemSettingsModule::OnExit() g_signal_handlers_disconnect_by_func(settings, (void*)notify_gtk_font_name, NULL); } + if (m_proxy) + g_object_unref(m_proxy); #endif if (gs_tlw_parent) {