From 70d36f06db290bf83e5d458a185eb7d7f36f8dd0 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 24 Aug 2021 12:48:58 +0100 Subject: [PATCH] Linux: Implement dark mode detection --- .../native/juce_linux_Windowing.cpp | 47 +++++++++++++++++-- .../native/x11/juce_linux_XWindowSystem.cpp | 31 ++++++++++++ .../native/x11/juce_linux_XWindowSystem.h | 2 + 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 8fc5b397ed..16ed033f57 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -534,16 +534,53 @@ bool Desktop::canUseSemiTransparentWindows() noexcept return XWindowSystem::getInstance()->canUseSemiTransparentWindows(); } -bool Desktop::isDarkModeActive() const +class Desktop::NativeDarkModeChangeDetectorImpl : private XWindowSystemUtilities::XSettings::Listener { - return false; -} +public: + NativeDarkModeChangeDetectorImpl() + { + const auto* windowSystem = XWindowSystem::getInstance(); -class Desktop::NativeDarkModeChangeDetectorImpl { public: NativeDarkModeChangeDetectorImpl() = default; }; + if (auto* xSettings = windowSystem->getXSettings()) + xSettings->addListener (this); + + darkModeEnabled = windowSystem->isDarkModeActive(); + } + + ~NativeDarkModeChangeDetectorImpl() override + { + if (auto* windowSystem = XWindowSystem::getInstanceWithoutCreating()) + if (auto* xSettings = windowSystem->getXSettings()) + xSettings->removeListener (this); + } + + bool isDarkModeEnabled() const noexcept { return darkModeEnabled; } + +private: + void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override + { + if (settingThatHasChanged.name == XWindowSystem::getThemeNameSettingName()) + { + const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, XWindowSystem::getInstance()->isDarkModeActive()); + + if (darkModeEnabled != wasDarkModeEnabled) + Desktop::getInstance().darkModeChanged(); + } + } + + bool darkModeEnabled = false; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) +}; std::unique_ptr Desktop::createNativeDarkModeChangeDetectorImpl() { - return nullptr; + return std::make_unique(); +} + +bool Desktop::isDarkModeActive() const +{ + return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled(); } static bool screenSaverAllowed = true; diff --git a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp index df47b8a361..623fd4f4bc 100644 --- a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp +++ b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp @@ -2018,6 +2018,37 @@ bool XWindowSystem::canUseARGBImages() const return canUseARGB; } +bool XWindowSystem::isDarkModeActive() const +{ + const auto themeName = [this]() -> String + { + if (xSettings != nullptr) + { + const auto themeNameSetting = xSettings->getSetting (getThemeNameSettingName()); + + if (themeNameSetting.isValid() + && themeNameSetting.stringValue.isNotEmpty()) + { + return themeNameSetting.stringValue; + } + } + + ChildProcess gsettings; + + if (File ("/usr/bin/gsettings").existsAsFile() + && gsettings.start ("/usr/bin/gsettings get org.gnome.desktop.interface gtk-theme", ChildProcess::wantStdOut)) + { + if (gsettings.waitForProcessToFinish (200)) + return gsettings.readAllProcessOutput(); + } + + return {}; + }(); + + return (themeName.isNotEmpty() + && (themeName.containsIgnoreCase ("dark") || themeName.containsIgnoreCase ("black"))); +} + Image XWindowSystem::createImage (bool isSemiTransparent, int width, int height, bool argb) const { auto visualAndDepth = displayVisuals->getBestVisualForWindow (isSemiTransparent); diff --git a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h index c8ee0a83c0..2c1191a6c0 100644 --- a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h +++ b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h @@ -197,6 +197,7 @@ public: bool canUseSemiTransparentWindows() const; bool canUseARGBImages() const; + bool isDarkModeActive() const; int getNumPaintsPendingForWindow (::Window); void processPendingPaintsForWindow (::Window); @@ -238,6 +239,7 @@ public: bool isX11Available() const noexcept { return xIsAvailable; } static String getWindowScalingFactorSettingName() { return "Gdk/WindowScalingFactor"; } + static String getThemeNameSettingName() { return "Net/ThemeName"; } //============================================================================== void handleWindowMessage (LinuxComponentPeer*, XEvent&) const;