diff --git a/src/core/config.cpp b/src/core/config.cpp index 3fcc4a5..73bd52a 100644 --- a/src/core/config.cpp +++ b/src/core/config.cpp @@ -77,8 +77,10 @@ Config::Config() settings_["devices"]["speakers"] = Devices::speakers()[0]; connect(this, &Config::changed, this, &Config::save); - connect(this, &Config::SYSTEM_THEME_CHANGED, this, [this](int theme) { - load_theme(platform::system::theme_name(static_cast(theme))); + connect(this, &Config::SYSTEM_THEME_CHANGED, this, [this](int theme) { + if (settings_["theme"].get() == "auto") { + load_theme(platform::system::theme_name(static_cast(theme))); + } }); monitor_system_theme(settings_["theme"].get() == "auto"); @@ -110,8 +112,8 @@ std::string Config::theme() void Config::monitor_system_theme(bool m) { #ifdef _WIN32 - if (m && win_theme_monitor_ == nullptr) { - win_theme_monitor_ = platform::windows::monitor_regkey( + if (m && theme_monitor_ == nullptr) { + theme_monitor_ = platform::windows::monitor_regkey( HKEY_CURRENT_USER, R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)", [this](auto) { @@ -121,8 +123,51 @@ void Config::monitor_system_theme(bool m) } if(!m) { - win_theme_monitor_ = nullptr; + theme_monitor_ = nullptr; } +#elif __linux__ + if (m && theme_monitor_ == nullptr) { + if (platform::system::desktop() == platform::system::desktop_t::GNOME || + platform::system::desktop() == platform::system::desktop_t::Unity) { + + auto color_scheme = platform::linux::exec("gsettings get org.gnome.desktop.interface color-scheme").value_or(""); + if (!color_scheme.empty() && color_scheme != "\t") { // TODO + theme_monitor_ = platform::linux::monitor_gsettings("org.gnome.desktop.interface", "color-scheme", [this](auto str){ + platform::system::theme_t _theme = platform::system::theme_t::dark; + if (str.find("dark") != std::string::npos) { + _theme = platform::system::theme_t::dark; + } + if (str.find("light") != std::string::npos) { + _theme = platform::system::theme_t::light; + } + emit SYSTEM_THEME_CHANGED(static_cast(_theme)); + }); + + return; + } + + auto gtk_theme = platform::linux::exec("gsettings get org.gnome.desktop.interface gtk-theme").value_or(""); + if (!gtk_theme.empty() && gtk_theme != "\t") { + theme_monitor_ = platform::linux::monitor_gsettings("org.gnome.desktop.interface", "gtk-theme", [this](auto str){ + platform::system::theme_t _theme = platform::system::theme_t::dark; + if (str.find("dark") != std::string::npos) { + _theme = platform::system::theme_t::dark; + } + if (str.find("light") != std::string::npos) { + _theme = platform::system::theme_t::light; + } + emit SYSTEM_THEME_CHANGED(static_cast(_theme)); + }); + + return; + } + } + } + + // TODO: pclose can not exit + // if (!m) { + // theme_monitor_ = nullptr; + // } #endif } diff --git a/src/core/config.h b/src/core/config.h index 023d937..3be4a07 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -55,7 +55,9 @@ public slots: json settings_ = json::parse("{}"); #ifdef _WIN32 - std::shared_ptr win_theme_monitor_{ nullptr }; + std::shared_ptr theme_monitor_{ nullptr }; +#elif __linux__ + std::shared_ptr theme_monitor_{ nullptr }; #endif // _WIN32 }; diff --git a/src/core/platform.h b/src/core/platform.h index 2132e59..89a149a 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -6,12 +6,12 @@ #include #include #include +#include +#include +#include #ifdef _WIN32 -#include -#include -#include #include #endif @@ -77,7 +77,7 @@ namespace platform class RegistryMonitor { public: - RegistryMonitor(HKEY key, const std::string& subkey, std::function cb) { monitor(key, subkey, cb); } + RegistryMonitor() = default; ~RegistryMonitor() { stop(); } RegistryMonitor(const RegistryMonitor&) = delete; @@ -97,8 +97,27 @@ namespace platform std::shared_ptr monitor_regkey(HKEY key, const std::string& subkey, std::function cb); } #elif __linux__ - namespace linux { + namespace linux + { std::optional exec(const char *); + + class GSettingsMonitor { + public: + GSettingsMonitor() = default; + ~GSettingsMonitor() { stop(); } + + GSettingsMonitor(const GSettingsMonitor&) = delete; + GSettingsMonitor& operator= (const GSettingsMonitor&) = delete; + + int monitor(const std::string&, const std::string&, std::function); + void stop(); + + private: + std::thread thread_; + std::atomic running_{ false }; + }; + + std::shared_ptr monitor_gsettings(const std::string&, const std::string&, std::function); } #endif // _WIN32 @@ -121,6 +140,22 @@ namespace platform namespace system { + enum class desktop_t + { + unknown, + Windows, // Windows + KDE, // K Desktop Environment, based on Qt + GNOME, // GNU Network Object Model Environment + Unity, // based on GNOME + MATE, // forked from GNOME 2 + Cinnamon, // forked from GNOME 3 + Xfce, + DeepinDE, // based on Qt + Enlightenment, + LXQT, + Lumina + }; + enum class theme_t { dark, light @@ -143,6 +178,8 @@ namespace platform theme_t theme(); std::string theme_name(theme_t); + desktop_t desktop(); + std::string os_name(); std::string kernel_name(); diff --git a/src/core/platform/platform_linux.cpp b/src/core/platform/platform_linux.cpp index 274378e..dd9aaf5 100644 --- a/src/core/platform/platform_linux.cpp +++ b/src/core/platform/platform_linux.cpp @@ -1,6 +1,11 @@ #ifdef __linux__ #include "platform.h" +#include +#include "logging.h" + + +using namespace std::chrono_literals; namespace platform { @@ -22,6 +27,55 @@ namespace platform pclose(pipe); return result; } + + int GSettingsMonitor::monitor(const std::string& key, const std::string& subkey, std::function callback) + { + const std::string cmd = "gsettings monitor " + key + " " + subkey; + + running_ = true; + thread_ = std::thread([=](){ + while (running_) { + FILE* pipe = popen(cmd.c_str(), "r"); + + if (!pipe) { + LOG(ERROR) << "failed to open : " << cmd; + std::this_thread::sleep_for(100ms); + continue; + } + + char buffer[256]{}; + while (running_ && (fgets(buffer, sizeof(buffer), pipe) != nullptr)) { + callback(buffer); + } + + pclose(pipe); // TODO: stuck if the cmd process does not exit + + // exit unexpectedly + if (running_) { + LOG(WARNING) << "exit unexpectedly, try monitor the '" << cmd << "' again after 250ms"; + std::this_thread::sleep_for(250ms); + } + } + }); + + return 0; + } + + void GSettingsMonitor::stop() + { + running_ = false; + + if (thread_.joinable()) + thread_.join(); + } + + std::shared_ptr monitor_gsettings(const std::string& key, const std::string& subkey, std::function cb) + { + auto monitor = std::make_shared(); + monitor->monitor(key, subkey, cb); + return monitor; + } + } // namespace linux namespace util diff --git a/src/core/platform/platform_win.cpp b/src/core/platform/platform_win.cpp index 761ddd1..e11b596 100644 --- a/src/core/platform/platform_win.cpp +++ b/src/core/platform/platform_win.cpp @@ -110,7 +110,9 @@ namespace platform std::shared_ptr monitor_regkey(HKEY key, const std::string& subkey, std::function cb) { - return std::make_shared(key, subkey, cb); + auto monitor = std::make_shared(); + monitor->monitor(key, subkey, cb); + return monitor; } } // namespace windows diff --git a/src/core/platform/system_linux.cpp b/src/core/platform/system_linux.cpp index 4322713..89c9983 100644 --- a/src/core/platform/system_linux.cpp +++ b/src/core/platform/system_linux.cpp @@ -21,19 +21,48 @@ namespace platform::system return version; } - theme_t theme() + desktop_t desktop() { const char * de = std::getenv("XDG_CURRENT_DESKTOP"); // GNOME - if (std::string_view(de).find("GNOME") != std::string::npos) { - auto result = platform::linux::exec("gsettings get org.gnome.desktop.interface color-scheme").value_or(""); - if (result.find("dark") != std::string::npos) { - return theme_t::dark; - } - if (result.find("light") != std::string::npos) { - return theme_t::light; + if (std::string_view(de).find("GNOME") != std::string::npos || + std::string_view(de).find("gnome") != std::string::npos) { + return desktop_t::GNOME; + } + // Unity + if (std::string_view(de).find("Unity") != std::string::npos || + std::string_view(de).find("unity") != std::string::npos) { + return desktop_t::GNOME; + } + return desktop_t::unknown; + } + + + theme_t theme() + { + if (desktop() == desktop_t::GNOME || desktop() == desktop_t::Unity) { + + auto color_scheme = platform::linux::exec("gsettings get org.gnome.desktop.interface color-scheme").value_or(""); + if (!color_scheme.empty()) { + if (color_scheme.find("dark") != std::string::npos) { + return theme_t::dark; + } + if (color_scheme.find("light") != std::string::npos) { + return theme_t::light; + } } + + auto gtk_theme = platform::linux::exec("gsettings get org.gnome.desktop.interface gtk-theme").value_or(""); + if (!gtk_theme.empty()) { + if (gtk_theme.find("dark") != std::string::npos) { + return theme_t::dark; + } + if (gtk_theme.find("light") != std::string::npos) { + return theme_t::light; + } + } } + // TODO : other desktop env return theme_t::dark; } diff --git a/src/core/platform/system_win.cpp b/src/core/platform/system_win.cpp index fe6914d..6497752 100644 --- a/src/core/platform/system_win.cpp +++ b/src/core/platform/system_win.cpp @@ -6,6 +6,11 @@ namespace platform::system { extern "C" NTSYSAPI NTSTATUS NTAPI RtlGetVersion(_Out_ PRTL_OSVERSIONINFOW lpVersionInformation); + desktop_t desktop() + { + return desktop_t::Windows; + } + theme_t theme() { if (os_version() >= platform::windows::WIN_10_1ST) {