From ca806df6291d77b30649fd7df98623d44fe8aa81 Mon Sep 17 00:00:00 2001 From: ffiirree Date: Wed, 5 Apr 2023 21:30:49 +0800 Subject: [PATCH] [windows] #36, #37 1. monitor the registry event 2. automatically follow the theme of system --- src/core/config.cpp | 62 ++++++++++++++++++++-- src/core/config.h | 14 ++++- src/core/platform.h | 28 +++++++++- src/core/platform/platform_win.cpp | 84 +++++++++++++++++++++++++++--- src/core/platform/system_win.cpp | 2 +- src/main.cpp | 12 +---- src/setting/settingdialog.cpp | 2 +- 7 files changed, 179 insertions(+), 25 deletions(-) diff --git a/src/core/config.cpp b/src/core/config.cpp index 7db1963..3fcc4a5 100644 --- a/src/core/config.cpp +++ b/src/core/config.cpp @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -76,6 +77,11 @@ 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))); + }); + + monitor_system_theme(settings_["theme"].get() == "auto"); } void Config::save() @@ -91,12 +97,62 @@ void Config::save() file.close(); } -QString Config::theme() +std::string Config::theme() { - auto theme = Config::instance()["theme"].get(); + auto theme = Config::instance()["theme"].get(); if (theme == "auto") { - return QString::fromStdString(platform::system::theme_name(platform::system::theme())); + return platform::system::theme_name(platform::system::theme()); } return (theme == "dark") ? "dark" : "light"; +} + +void Config::monitor_system_theme(bool m) +{ +#ifdef _WIN32 + if (m && win_theme_monitor_ == nullptr) { + win_theme_monitor_ = platform::windows::monitor_regkey( + HKEY_CURRENT_USER, + R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)", + [this](auto) { + emit SYSTEM_THEME_CHANGED(static_cast(platform::system::theme())); + } + ); + } + + if(!m) { + win_theme_monitor_ = nullptr; + } +#endif +} + +void Config::set_theme(const std::string& theme) +{ + if (settings_["theme"].get() == theme) + return; + + set(settings_["theme"], theme); + + monitor_system_theme(theme == "auto"); + + load_theme(Config::theme()); +} + +void Config::load_theme(const std::string& theme) +{ + static std::string _theme = "unknown"; + if (_theme != theme) { + _theme = theme; + + LOAD_QSS(qApp, + { + ":/qss/capturer.qss", + ":/qss/capturer-" + QString::fromStdString(theme) + ".qss", + ":/qss/menu/menu.qss", + ":/qss/menu/menu-" + QString::fromStdString(theme) + ".qss", + ":/qss/setting/settingswindow.qss", + ":/qss/setting/settingswindow-" + QString::fromStdString(theme) + ".qss" + } + ); + } } \ No newline at end of file diff --git a/src/core/config.h b/src/core/config.h index 475f85a..023d937 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -4,6 +4,7 @@ #include #include "json.h" #include "utils.h" +#include "platform.h" #define IF_NULL_SET(X, default_value) st(if(X.is_null()) X = default_value;) @@ -23,7 +24,11 @@ class Config : public QObject QString getFilePath() const { return filepath_; } - static QString theme(); + static std::string theme(); + + // use this funtion to set theme + void set_theme(const std::string&); + static void load_theme(const std::string&); template void set(json& key, T value) { @@ -39,12 +44,19 @@ public slots: signals: void changed(); + void SYSTEM_THEME_CHANGED(int); private: Config(); + void monitor_system_theme(bool); + QString filepath_; json settings_ = json::parse("{}"); + +#ifdef _WIN32 + std::shared_ptr win_theme_monitor_{ nullptr }; +#endif // _WIN32 }; diff --git a/src/core/platform.h b/src/core/platform.h index e54a497..2132e59 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -9,6 +9,9 @@ #ifdef _WIN32 +#include +#include +#include #include #endif @@ -69,8 +72,29 @@ namespace platform inline const version_t WIN_11_21H2{ 10, 0, 22000, 194, "21H2" }; inline const version_t WIN_11_22H2{ 10, 0, 22621, 521, "22H2" }; - std::optional reg_read_dword(HKEY key, const char*, const char*); - std::optional reg_read_string(HKEY key, const char*, const char*); + std::optional reg_read_dword(HKEY key, const std::string&, const std::string&); + std::optional reg_read_string(HKEY key, const std::string&, const std::string&); + + class RegistryMonitor { + public: + RegistryMonitor(HKEY key, const std::string& subkey, std::function cb) { monitor(key, subkey, cb); } + ~RegistryMonitor() { stop(); } + + RegistryMonitor(const RegistryMonitor&) = delete; + RegistryMonitor& operator= (const RegistryMonitor&) = delete; + + int monitor(HKEY key, const std::string&, std::function); + void stop(); + + private: + HKEY key_; + HANDLE STOP_EVENT{ nullptr }; + HANDLE NOTIFY_EVENT{ nullptr }; + std::thread thread_; + std::atomic running_{ false }; + }; + + std::shared_ptr monitor_regkey(HKEY key, const std::string& subkey, std::function cb); } #elif __linux__ namespace linux { diff --git a/src/core/platform/platform_win.cpp b/src/core/platform/platform_win.cpp index 520a8bc..761ddd1 100644 --- a/src/core/platform/platform_win.cpp +++ b/src/core/platform/platform_win.cpp @@ -2,6 +2,7 @@ #include "platform.h" #include +#include "logging.h" namespace platform { @@ -10,11 +11,12 @@ namespace platform // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types // REG_DWORD : A 32-bit number. // REG_QWORD : A 64-bit number. - std::optional reg_read_dword(HKEY key, const char* subkey, const char* valuename) + std::optional reg_read_dword(HKEY key, const std::string& subkey, const std::string& valuename) { DWORD value = 0; DWORD size = sizeof(uint32_t); - if (RegGetValueA(key, subkey, valuename, RRF_RT_REG_DWORD, nullptr, &value, &size) == ERROR_SUCCESS) { + if (RegGetValue(key, util::to_utf16(subkey).c_str(), util::to_utf16(valuename).c_str(), + RRF_RT_REG_DWORD, nullptr, &value, &size) == ERROR_SUCCESS) { return value; } @@ -24,22 +26,92 @@ namespace platform // REG_SZ : A null - terminated string. // It's either a Unicode or an ANSI string, // depending on whether you use the Unicode or ANSI functions. - std::optional reg_read_string(HKEY key, const char* subkey, const char* valuename) + std::optional reg_read_string(HKEY key, const std::string& subkey, const std::string& valuename) { DWORD size = 0; - if (RegGetValueA(key, subkey, valuename, RRF_RT_REG_SZ, nullptr, nullptr, &size) != ERROR_SUCCESS) { + if (RegGetValue(key, util::to_utf16(subkey).c_str(), util::to_utf16(valuename).c_str(), + RRF_RT_REG_SZ, nullptr, nullptr, &size) != ERROR_SUCCESS) { return std::nullopt; } std::string value(size, {}); - if (RegGetValueA(key, subkey, valuename, RRF_RT_REG_SZ, nullptr, reinterpret_cast(&value[0]), &size) != ERROR_SUCCESS) { + if (RegGetValue(key, util::to_utf16(subkey).c_str(), util::to_utf16(valuename).c_str(), + RRF_RT_REG_SZ, nullptr, reinterpret_cast(&value[0]), &size) != ERROR_SUCCESS) { return std::nullopt; } return value; } + + int RegistryMonitor::monitor(HKEY key, const std::string& subkey, std::function callback) + { + if (::RegOpenKeyEx(key, platform::util::to_utf16(subkey).c_str(), 0, KEY_NOTIFY, &key_) != ERROR_SUCCESS) { + LOG(ERROR) << "failed to open the registry key : " << subkey; + return -1; + } + + if ((STOP_EVENT = ::CreateEvent(nullptr, TRUE, FALSE, L"Registry Stop Event")) == nullptr) { + LOG(ERROR) << "failed to create event for the registry key : " << subkey; + return -1; + } + + if ((NOTIFY_EVENT = ::CreateEvent(nullptr, FALSE, FALSE, L"Registry Notify Evnent")) == nullptr) { + LOG(ERROR) << "failed to create event for the registry key : " << subkey; + return -1; + } + + running_ = true; + thread_ = std::thread([=]() + { + const HANDLE events[] = { STOP_EVENT, NOTIFY_EVENT }; + + while (running_) { + if (::RegNotifyChangeKeyValue( + key_, TRUE, REG_LEGAL_CHANGE_FILTER, NOTIFY_EVENT, TRUE) != ERROR_SUCCESS) { + LOG(ERROR) << "failed to monitor the registry key : " << subkey; + ::SetEvent(STOP_EVENT); + } + + switch (::WaitForMultipleObjects(2, events, false, INFINITE)) + { + case WAIT_OBJECT_0 + 0: // STOP_EVENT + running_ = false; + break; + + case WAIT_OBJECT_0 + 1: // NOTIFIY_EVENT + callback(key_); + break; + + default: break; + } + } + } + ); + + return 0; + } + + void RegistryMonitor::stop() + { + running_ = false; + ::SetEvent(STOP_EVENT); + + if (thread_.joinable()) + thread_.join(); + + ::CloseHandle(NOTIFY_EVENT); + ::CloseHandle(STOP_EVENT); + ::RegCloseKey(key_); + + LOG(INFO) << "REG MONITOR DESTORY"; + } + + std::shared_ptr monitor_regkey(HKEY key, const std::string& subkey, std::function cb) + { + return std::make_shared(key, subkey, cb); + } } // namespace windows namespace util @@ -74,7 +146,7 @@ namespace platform std::wstring wstr(wlen, {}); MultiByteToWideChar(CP_UTF8, 0, mstr, static_cast(mlen), wstr.data(), static_cast(wstr.size())); - return wstr; + return std::move(wstr); } } // namespace util } diff --git a/src/core/platform/system_win.cpp b/src/core/platform/system_win.cpp index 5c17101..fe6914d 100644 --- a/src/core/platform/system_win.cpp +++ b/src/core/platform/system_win.cpp @@ -11,7 +11,7 @@ namespace platform::system if (os_version() >= platform::windows::WIN_10_1ST) { if (platform::windows::reg_read_dword( HKEY_CURRENT_USER, - "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)", "AppsUseLightTheme" ).value_or(1) == 0) { return theme_t::dark; diff --git a/src/main.cpp b/src/main.cpp index 01da098..255757c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include "version.h" #include "utils.h" @@ -34,16 +33,7 @@ int main(int argc, char *argv[]) // displays LOG(INFO) << "VIRTUAL SCREEN: " << platform::display::virtual_screen_geometry(); - LOAD_QSS(qApp, - { - ":/qss/capturer.qss", - ":/qss/capturer-" + Config::theme() + ".qss", - ":/qss/menu/menu.qss", - ":/qss/menu/menu-" + Config::theme() + ".qss", - ":/qss/setting/settingswindow.qss", - ":/qss/setting/settingswindow-" + Config::theme() + ".qss" - } - ); + Config::load_theme(Config::theme()); auto language = Config::instance()["language"].get(); LOG(INFO) << "LANGUAGE: " << language; diff --git a/src/setting/settingdialog.cpp b/src/setting/settingdialog.cpp index 17714f4..8f9e543 100644 --- a/src/setting/settingdialog.cpp +++ b/src/setting/settingdialog.cpp @@ -119,7 +119,7 @@ void SettingWindow::setupGeneralWidget() _3_2->addItem(tr("Light"), "light"); _3_2->setCurrentIndex(std::max(0, _3_2->findData(config["theme"].get()))); connect(_3_2, static_cast(&QComboBox::currentIndexChanged), this, [this, _3_2](int i) { - config.set(config["theme"], _3_2->currentData().toString()); + config.set_theme(_3_2->currentData().toString().toStdString()); }); layout->addWidget(new QLabel(tr("Theme")), 3, 0, 1, 1); layout->addWidget(_3_2, 3, 1, 1, 2);