diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 1e3a5bdd4..1a004d958 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -374,6 +374,8 @@ if (WIN32) target_sources(ui PRIVATE qt_winrawinputfilter.hpp qt_winrawinputfilter.cpp + qt_vmmanager_windarkmodefilter.hpp + qt_vmmanager_windarkmodefilter.cpp qt_winmanagerfilter.hpp qt_winmanagerfilter.cpp ) diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 3dd63389e..6a2ba1a3a 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -66,6 +66,7 @@ extern "C" { # include "qt_rendererstack.hpp" # include "qt_winrawinputfilter.hpp" # include "qt_winmanagerfilter.hpp" +# include "qt_vmmanager_windarkmodefilter.hpp" # include <86box/win.h> # include # include @@ -514,10 +515,6 @@ main_thread_fn() static std::thread *main_thread; -#ifdef Q_OS_WINDOWS -extern bool windows_is_light_theme(); -#endif - int main(int argc, char *argv[]) { @@ -548,7 +545,7 @@ main(int argc, char *argv[]) } QApplication::setAttribute(Qt::AA_NativeWindows); - if (!windows_is_light_theme()) { + if (!util::isWindowsLightTheme()) { QFile f(":qdarkstyle/dark/darkstyle.qss"); if (!f.exists()) { @@ -558,6 +555,10 @@ main(int argc, char *argv[]) QTextStream ts(&f); qApp->setStyleSheet(ts.readAll()); } + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::white); + palette.setColor(QPalette::LinkVisited, Qt::lightGray); + qApp->setPalette(palette); } #endif @@ -632,8 +633,19 @@ main(int argc, char *argv[]) // QApplication::setApplicationDisplayName("86Box VM Manager"); // vmm.show(); // vmm.exec(); +#ifdef Q_OS_WINDOWS + auto darkModeFilter = std::unique_ptr(new WindowsDarkModeFilter()); + if (darkModeFilter) { + qApp->installNativeEventFilter(darkModeFilter.get()); + } + QTimer::singleShot(0, [&darkModeFilter] { +#else QTimer::singleShot(0, [] { +#endif const auto vmm_main_window = new VMManagerMainWindow(); +#ifdef Q_OS_WINDOWS + darkModeFilter.get()->setWindow(vmm_main_window); +#endif vmm_main_window->show(); }); QApplication::exec(); diff --git a/src/qt/qt_styleoverride.cpp b/src/qt/qt_styleoverride.cpp index 1319f0dae..2da0342bb 100644 --- a/src/qt/qt_styleoverride.cpp +++ b/src/qt/qt_styleoverride.cpp @@ -15,6 +15,7 @@ * Copyright 2022 Teemu Korhonen */ #include "qt_styleoverride.hpp" +#include "qt_util.hpp" #include #include @@ -64,8 +65,7 @@ StyleOverride::polish(QWidget *widget) } widget->setWindowFlag(Qt::WindowContextHelpButtonHint, false); #ifdef Q_OS_WINDOWS - extern bool windows_is_light_theme(); - BOOL DarkMode = !windows_is_light_theme(); + BOOL DarkMode = !util::isWindowsLightTheme(); DwmSetWindowAttribute((HWND)widget->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&DarkMode, sizeof(DarkMode)); #endif } diff --git a/src/qt/qt_util.cpp b/src/qt/qt_util.cpp index bc9b9f1f8..201b61b3f 100644 --- a/src/qt/qt_util.cpp +++ b/src/qt/qt_util.cpp @@ -27,6 +27,7 @@ #include "qt_util.hpp" #ifdef Q_OS_WINDOWS +# include # include # ifndef DWMWA_WINDOW_CORNER_PREFERENCE # define DWMWA_WINDOW_CORNER_PREFERENCE 33 @@ -62,6 +63,36 @@ screenOfWidget(QWidget *widget) } #ifdef Q_OS_WINDOWS + +bool +isWindowsLightTheme(void) { + // based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application + + // The value is expected to be a REG_DWORD, which is a signed 32-bit little-endian + auto buffer = std::vector(4); + auto cbData = static_cast(buffer.size() * sizeof(char)); + auto res = RegGetValueW( + HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + L"AppsUseLightTheme", + RRF_RT_REG_DWORD, // expected value type + nullptr, + buffer.data(), + &cbData); + + if (res != ERROR_SUCCESS) { + return 1; + } + + // convert bytes written to our buffer to an int, assuming little-endian + auto i = int(buffer[3] << 24 | + buffer[2] << 16 | + buffer[1] << 8 | + buffer[0]); + + return i == 1; +} + void setWin11RoundedCorners(WId hwnd, bool enable) { diff --git a/src/qt/qt_util.hpp b/src/qt/qt_util.hpp index de3457a88..a2ca44425 100644 --- a/src/qt/qt_util.hpp +++ b/src/qt/qt_util.hpp @@ -15,6 +15,7 @@ QString DlgFilter(QStringList extensions, bool last = false); /* Returns screen the widget is on */ QScreen *screenOfWidget(QWidget *widget); #ifdef Q_OS_WINDOWS +bool isWindowsLightTheme(void); void setWin11RoundedCorners(WId hwnd, bool enable); #endif QString currentUuid(); diff --git a/src/qt/qt_vmmanager_details.cpp b/src/qt/qt_vmmanager_details.cpp index 56cfa8758..65608cf0a 100644 --- a/src/qt/qt_vmmanager_details.cpp +++ b/src/qt/qt_vmmanager_details.cpp @@ -19,12 +19,19 @@ #include #include +#include "qt_util.hpp" #include "qt_vmmanager_details.hpp" #include "ui_qt_vmmanager_details.h" +#define TOOLBUTTON_STYLESHEET_LIGHT "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(midlight)} QToolButton:pressed {background: palette(mid)}" #ifdef Q_OS_WINDOWS -extern bool windows_is_light_theme(); +# define TOOLBUTTON_STYLESHEET_DARK "QToolButton {padding: 5px}" +# define SCREENSHOTBORDER_STYLESHEET_DARK "QLabel { border: 1px solid gray }" +#else +# define TOOLBUTTON_STYLESHEET_DARK "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(dark)} QToolButton:pressed {background: palette(mid)}" #endif +#define SCROLLAREA_STYLESHEET_LIGHT "QWidget {background-color: palette(light)} QScrollBar{ background-color: none }" +#define SYSTEMLABEL_STYLESHEET_LIGHT "background-color: palette(midlight);" using namespace VMManager; @@ -100,18 +107,14 @@ VMManagerDetails::VMManagerDetails(QWidget *parent) : QString toolButtonStyleSheet; // Simple method to try and determine if light mode is enabled #ifdef Q_OS_WINDOWS - const bool lightMode = windows_is_light_theme(); + const bool lightMode = util::isWindowsLightTheme(); #else const bool lightMode = QApplication::palette().window().color().value() > QApplication::palette().windowText().color().value(); #endif if (lightMode) { - toolButtonStyleSheet = "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(midlight)} QToolButton:pressed {background: palette(mid)}"; + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_LIGHT; } else { -#ifndef Q_OS_WINDOWS - toolButtonStyleSheet = "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(dark)} QToolButton:pressed {background: palette(mid)}"; -#else - toolButtonStyleSheet = "QToolButton {padding: 5px}"; -#endif + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_DARK; } ui->ssNavTBHolder->setStyleSheet(toolButtonStyleSheet); @@ -150,6 +153,17 @@ VMManagerDetails::VMManagerDetails(QWidget *parent) : ui->notesTextEdit->setEnabled(false); +#ifdef Q_OS_WINDOWS + connect(this, &VMManagerDetails::styleUpdated, systemSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, videoSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, storageSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, audioSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, networkSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, inputSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, portsSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, otherSection, &VMManagerDetailSection::updateStyle); +#endif + sysconfig = new VMManagerSystem(); } @@ -163,12 +177,11 @@ VMManagerDetails::updateData(VMManagerSystem *passed_sysconfig) { // Set the scrollarea background but also set the scroll bar to none. Otherwise it will also // set the scrollbar background to the same. #ifdef Q_OS_WINDOWS - extern bool windows_is_light_theme(); - if (windows_is_light_theme()) + if (util::isWindowsLightTheme()) #endif { - ui->scrollArea->setStyleSheet("QWidget {background-color: palette(light)} QScrollBar{ background-color: none }"); - ui->systemLabel->setStyleSheet("background-color: palette(midlight);"); + ui->scrollArea->setStyleSheet(SCROLLAREA_STYLESHEET_LIGHT); + ui->systemLabel->setStyleSheet(SYSTEMLABEL_STYLESHEET_LIGHT); } // Margins are a little different on macos #ifdef Q_OS_MACOS @@ -331,8 +344,8 @@ VMManagerDetails::updateScreenshots(VMManagerSystem *passed_sysconfig) { ui->screenshot->setEnabled(false); ui->screenshot->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); #ifdef Q_OS_WINDOWS - if (!windows_is_light_theme()) { - ui->screenshot->setStyleSheet("QLabel { border: 1px solid gray }"); + if (!util::isWindowsLightTheme()) { + ui->screenshot->setStyleSheet(SCREENSHOTBORDER_STYLESHEET_DARK); } else { ui->screenshot->setStyleSheet(""); } @@ -390,6 +403,32 @@ VMManagerDetails::updateWindowStatus() updateProcessStatus(); } +#ifdef Q_OS_WINDOWS +void +VMManagerDetails::updateStyle() +{ + QString toolButtonStyleSheet; + const bool lightMode = util::isWindowsLightTheme(); + if (lightMode) { + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_LIGHT; + ui->scrollArea->setStyleSheet(SCROLLAREA_STYLESHEET_LIGHT); + ui->systemLabel->setStyleSheet(SYSTEMLABEL_STYLESHEET_LIGHT); + if (!ui->screenshot->isEnabled()) + ui->screenshot->setStyleSheet(""); + } else { + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_DARK; + ui->scrollArea->setStyleSheet(""); + ui->systemLabel->setStyleSheet(""); + if (!ui->screenshot->isEnabled()) + ui->screenshot->setStyleSheet(SCREENSHOTBORDER_STYLESHEET_DARK); + } + ui->ssNavTBHolder->setStyleSheet(toolButtonStyleSheet); + ui->toolButtonHolder->setStyleSheet(toolButtonStyleSheet); + + emit styleUpdated(); +} +#endif + QWidget * VMManagerDetails::createHorizontalLine(const int leftSpacing, const int rightSpacing) { diff --git a/src/qt/qt_vmmanager_details.hpp b/src/qt/qt_vmmanager_details.hpp index 0a6c2d35a..ac7cda35b 100644 --- a/src/qt/qt_vmmanager_details.hpp +++ b/src/qt/qt_vmmanager_details.hpp @@ -42,8 +42,18 @@ public: void updateProcessStatus(); void updateWindowStatus(); + +#ifdef Q_OS_WINDOWS + void updateStyle(); +#endif + // CollapseButton *systemCollapseButton; +#ifdef Q_OS_WINDOWS +signals: + void styleUpdated(); +#endif + private: Ui::VMManagerDetails *ui; VMManagerSystem *sysconfig; diff --git a/src/qt/qt_vmmanager_detailsection.cpp b/src/qt/qt_vmmanager_detailsection.cpp index 043342f1e..ab9a4b5ff 100644 --- a/src/qt/qt_vmmanager_detailsection.cpp +++ b/src/qt/qt_vmmanager_detailsection.cpp @@ -19,6 +19,15 @@ #include "ui_qt_vmmanager_detailsection.h" #include +#include "qt_util.hpp" + +#define HEADER_STYLESHEET_LIGHT "background-color: palette(midlight);" +#ifdef Q_OS_WINDOWS +# define HEADER_STYLESHEET_DARK "background-color: #616161;" +# define BACKGROUND_STYLESHEET_DARK "background-color: #272727;" +#else +# define HEADER_STYLESHEET_DARK "background-color: palette(mid);" +#endif const QString VMManagerDetailSection::sectionSeparator = ";"; using namespace VMManager; @@ -40,21 +49,18 @@ VMManagerDetailSection(const QString §ionName) // Simple method to try and determine if light mode is enabled on the host #ifdef Q_OS_WINDOWS - extern bool windows_is_light_theme(); - const bool lightMode = windows_is_light_theme(); + const bool lightMode = util::isWindowsLightTheme(); #else const bool lightMode = QApplication::palette().window().color().value() > QApplication::palette().windowText().color().value(); #endif // Alternate layout - if ( lightMode) { - ui->collapseButtonHolder->setStyleSheet("background-color: palette(midlight);"); + if (lightMode) { + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_LIGHT); } else { #ifdef Q_OS_WINDOWS - ui->outerFrame->setStyleSheet("background-color: #272727;"); - ui->collapseButtonHolder->setStyleSheet("background-color: #616161;"); -#else - ui->collapseButtonHolder->setStyleSheet("background-color: palette(mid);"); + ui->outerFrame->setStyleSheet(BACKGROUND_STYLESHEET_DARK); #endif + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_DARK); } const auto sectionLabel = new QLabel(sectionName); sectionLabel->setStyleSheet(sectionLabel->styleSheet().append("font-weight: bold;")); @@ -214,6 +220,21 @@ VMManagerDetailSection::clear() ui->detailFrame->setLayout(frameGridLayout); } +#ifdef Q_OS_WINDOWS +void +VMManagerDetailSection::updateStyle() +{ + const bool lightMode = util::isWindowsLightTheme(); + if (lightMode) { + ui->outerFrame->setStyleSheet(""); + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_LIGHT); + } else { + ui->outerFrame->setStyleSheet(BACKGROUND_STYLESHEET_DARK); + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_DARK); + } +} +#endif + // QT for Linux and Windows doesn't have the same default margins as QT on MacOS. // For consistency in appearance we'll have to return the margins on a per-OS basis QMargins diff --git a/src/qt/qt_vmmanager_detailsection.hpp b/src/qt/qt_vmmanager_detailsection.hpp index a7da9470e..3df7ce64c 100644 --- a/src/qt/qt_vmmanager_detailsection.hpp +++ b/src/qt/qt_vmmanager_detailsection.hpp @@ -74,6 +74,10 @@ public: static const QString sectionSeparator; +#ifdef Q_OS_WINDOWS +public slots: + void updateStyle(); +#endif private: enum class MarginSection { diff --git a/src/qt/qt_vmmanager_listviewdelegate.cpp b/src/qt/qt_vmmanager_listviewdelegate.cpp index c5bcbbdd9..c5d2e1dc9 100644 --- a/src/qt/qt_vmmanager_listviewdelegate.cpp +++ b/src/qt/qt_vmmanager_listviewdelegate.cpp @@ -18,13 +18,10 @@ #include +#include "qt_util.hpp" #include "qt_vmmanager_listviewdelegate.hpp" #include "qt_vmmanager_model.hpp" -#ifdef Q_OS_WINDOWS -extern bool windows_is_light_theme(); -#endif - // Thanks to scopchanov https://github.com/scopchanov/SO-MessageLog // from https://stackoverflow.com/questions/53105343/is-it-possible-to-add-a-custom-widget-into-a-qlistview @@ -52,7 +49,7 @@ void VMManagerListViewDelegate::paint(QPainter *painter, const QStyleOptionViewI const QModelIndex &index) const { bool windows_light_mode = true; #ifdef Q_OS_WINDOWS - windows_light_mode = windows_is_light_theme(); + windows_light_mode = util::isWindowsLightTheme(); #endif QStyleOptionViewItem opt(option); initStyleOption(&opt, index); diff --git a/src/qt/qt_vmmanager_main.cpp b/src/qt/qt_vmmanager_main.cpp index 8859d5cec..f55c90389 100644 --- a/src/qt/qt_vmmanager_main.cpp +++ b/src/qt/qt_vmmanager_main.cpp @@ -349,7 +349,7 @@ illegal_chars: }); // Initial default details view - vm_details = new VMManagerDetails(); + vm_details = new VMManagerDetails(ui->detailsArea); ui->detailsArea->layout()->addWidget(vm_details); const QItemSelectionModel *selection_model = ui->listView->selectionModel(); @@ -785,6 +785,14 @@ VMManagerMain::onLanguageUpdated() vm_details->updateData(selected_sysconfig); } +#ifdef Q_OS_WINDOWS +void +VMManagerMain::onDarkModeUpdated() +{ + vm_details->updateStyle(); +} +#endif + int VMManagerMain::getActiveMachineCount() { diff --git a/src/qt/qt_vmmanager_main.hpp b/src/qt/qt_vmmanager_main.hpp index 8ec1129ae..be43da705 100644 --- a/src/qt/qt_vmmanager_main.hpp +++ b/src/qt/qt_vmmanager_main.hpp @@ -80,6 +80,9 @@ public slots: void modelDataChange(); void onPreferencesUpdated(); void onLanguageUpdated(); +#ifdef Q_OS_WINDOWS + void onDarkModeUpdated(); +#endif void onConfigUpdated(const QString &uuid); int getActiveMachineCount(); diff --git a/src/qt/qt_vmmanager_mainwindow.cpp b/src/qt/qt_vmmanager_mainwindow.cpp index af9f3082c..11eba670b 100644 --- a/src/qt/qt_vmmanager_mainwindow.cpp +++ b/src/qt/qt_vmmanager_mainwindow.cpp @@ -116,6 +116,9 @@ VMManagerMainWindow(QWidget *parent) // Inform the main view when preferences are updated connect(this, &VMManagerMainWindow::preferencesUpdated, vmm, &VMManagerMain::onPreferencesUpdated); connect(this, &VMManagerMainWindow::languageUpdated, vmm, &VMManagerMain::onLanguageUpdated); +#ifdef Q_OS_WINDOWS + connect(this, &VMManagerMainWindow::darkModeUpdated, vmm, &VMManagerMain::onDarkModeUpdated); +#endif } @@ -178,6 +181,15 @@ VMManagerMainWindow::updateLanguage() emit languageUpdated(); } + +#ifdef Q_OS_WINDOWS +void +VMManagerMainWindow::updateDarkMode() +{ + emit darkModeUpdated(); +} +#endif + void VMManagerMainWindow::changeEvent(QEvent *event) { diff --git a/src/qt/qt_vmmanager_mainwindow.hpp b/src/qt/qt_vmmanager_mainwindow.hpp index 2f2d34d25..bde74765c 100644 --- a/src/qt/qt_vmmanager_mainwindow.hpp +++ b/src/qt/qt_vmmanager_mainwindow.hpp @@ -37,6 +37,9 @@ public: signals: void preferencesUpdated(); void languageUpdated(); +#ifdef Q_OS_WINDOWS + void darkModeUpdated(); +#endif private: Ui::VMManagerMainWindow *ui; @@ -48,6 +51,9 @@ public slots: void setStatusLeft(const QString &text) const; void setStatusRight(const QString &text) const; void updateLanguage(); +#ifdef Q_OS_WINDOWS + void updateDarkMode(); +#endif private slots: void vmmSelectionChanged(const QModelIndex ¤tSelection, QProcess::ProcessState processState) const; diff --git a/src/qt/qt_vmmanager_windarkmodefilter.cpp b/src/qt/qt_vmmanager_windarkmodefilter.cpp new file mode 100644 index 000000000..195419ad9 --- /dev/null +++ b/src/qt/qt_vmmanager_windarkmodefilter.cpp @@ -0,0 +1,99 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Generic Windows native event filter for dark mode handling + * + * + * + * Authors: Teemu Korhonen + * Cacodemon345 + * + * Copyright 2021 Teemu Korhonen + * Copyright 2024-2025 Cacodemon345. + */ + +#include "qt_vmmanager_windarkmodefilter.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +#include <86box/86box.h> +#include <86box/plat.h> + +#include "qt_util.hpp" + +static bool NewDarkMode = FALSE; + +void +WindowsDarkModeFilter::setWindow(VMManagerMainWindow *window) +{ + this->window = window; +} + +bool +WindowsDarkModeFilter::nativeEventFilter(const QByteArray &eventType, void *message, result_t *result) +{ + if ((window != nullptr) && (eventType == "windows_generic_MSG")) { + MSG *msg = static_cast(message); + + if ((msg != nullptr) && (msg->message == WM_SETTINGCHANGE)) { + if ((((void *) msg->lParam) != nullptr) && + (wcscmp(L"ImmersiveColorSet", (wchar_t*)msg->lParam) == 0)) { + + bool OldDarkMode = NewDarkMode; + + if (!util::isWindowsLightTheme()) { + QFile f(":qdarkstyle/dark/darkstyle.qss"); + + if (!f.exists()) + printf("Unable to set stylesheet, file not found\n"); + else { + f.open(QFile::ReadOnly | QFile::Text); + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + } + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::white); + palette.setColor(QPalette::LinkVisited, Qt::lightGray); + qApp->setPalette(palette); + window->resize(window->size()); + + NewDarkMode = TRUE; + } else { + qApp->setStyleSheet(""); + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::blue); + palette.setColor(QPalette::LinkVisited, Qt::magenta); + qApp->setPalette(palette); + window->resize(window->size()); + NewDarkMode = FALSE; + } + window->updateDarkMode(); + + if (NewDarkMode != OldDarkMode) QTimer::singleShot(1000, [this] () { + BOOL DarkMode = NewDarkMode; + DwmSetWindowAttribute((HWND) window->winId(), + DWMWA_USE_IMMERSIVE_DARK_MODE, + (LPCVOID) &DarkMode, + sizeof(DarkMode)); + }); + } + } + } + + return false; +} diff --git a/src/qt/qt_vmmanager_windarkmodefilter.hpp b/src/qt/qt_vmmanager_windarkmodefilter.hpp new file mode 100644 index 000000000..4f6b28a6e --- /dev/null +++ b/src/qt/qt_vmmanager_windarkmodefilter.hpp @@ -0,0 +1,47 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Header file for Windows dark mode native messages filter + * + * + * + * Authors: Teemu Korhonen + * + * Copyright 2022 Teemu Korhonen + */ + +#ifndef QT_WINDOWSDARKMODEEVENTFILTER_HPP +#define QT_WINDOWSDARKMODEEVENTFILTER_HPP + +#include +#include +#include +#include +#include + +#include "qt_vmmanager_mainwindow.hpp" + +#if QT_VERSION_MAJOR >= 6 +# define result_t qintptr +#else +# define result_t long +#endif + +class WindowsDarkModeFilter : public QObject, public QAbstractNativeEventFilter { + Q_OBJECT + +public: + WindowsDarkModeFilter() = default; + void setWindow(VMManagerMainWindow *window); + bool nativeEventFilter(const QByteArray &eventType, void *message, result_t *result) override; + +private: + VMManagerMainWindow *window; +}; + +#endif // QT_WINDOWSDARKMODEEVENTFILTER_HPP diff --git a/src/qt/qt_winrawinputfilter.cpp b/src/qt/qt_winrawinputfilter.cpp index eca77d15d..9b7adce1d 100644 --- a/src/qt/qt_winrawinputfilter.cpp +++ b/src/qt/qt_winrawinputfilter.cpp @@ -71,38 +71,11 @@ extern void win_keyboard_handle(uint32_t scancode, int up, int e0, int e1); #include #include "qt_rendererstack.hpp" +#include "qt_util.hpp" #include "ui_qt_mainwindow.h" static bool NewDarkMode = FALSE; -bool windows_is_light_theme() { - // based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application - - // The value is expected to be a REG_DWORD, which is a signed 32-bit little-endian - auto buffer = std::vector(4); - auto cbData = static_cast(buffer.size() * sizeof(char)); - auto res = RegGetValueW( - HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", - L"AppsUseLightTheme", - RRF_RT_REG_DWORD, // expected value type - nullptr, - buffer.data(), - &cbData); - - if (res != ERROR_SUCCESS) { - return 1; - } - - // convert bytes written to our buffer to an int, assuming little-endian - auto i = int(buffer[3] << 24 | - buffer[2] << 16 | - buffer[1] << 8 | - buffer[0]); - - return i == 1; -} - struct { HANDLE done_event = 0, ready_event = 0; @@ -365,7 +338,7 @@ WindowsRawInputFilter::nativeEventFilter(const QByteArray &eventType, void *mess } #endif - if (!windows_is_light_theme()) { + if (!util::isWindowsLightTheme()) { QFile f(":qdarkstyle/dark/darkstyle.qss"); if (!f.exists()) @@ -375,9 +348,17 @@ WindowsRawInputFilter::nativeEventFilter(const QByteArray &eventType, void *mess QTextStream ts(&f); qApp->setStyleSheet(ts.readAll()); } + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::white); + palette.setColor(QPalette::LinkVisited, Qt::lightGray); + qApp->setPalette(palette); NewDarkMode = TRUE; } else { qApp->setStyleSheet(""); + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::blue); + palette.setColor(QPalette::LinkVisited, Qt::magenta); + qApp->setPalette(palette); NewDarkMode = FALSE; }