Final code of version 2.0.0!

Some minor issues are known to exist and they'll get fixed before 2.1 is officially released.

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-04-23 12:19:38 +08:00
parent 8460995d7f
commit a0a9b8d108
13 changed files with 280 additions and 112 deletions

View File

@ -16,7 +16,23 @@
## Screenshots ## Screenshots
TODO ### Windows
![Light](./doc/win_light.png)
![Dark](./doc/win_dark.png)
### Linux
![Light](./doc/linux_light.png)
![Dark](./doc/linux_dark.png)
### macOS
![Light](./doc/mac_light.png)
![Dark](./doc/mac_dark.png)
## Roadmap ## Roadmap
@ -28,9 +44,39 @@ TODO
- [ ] Windows: Maximize button docking feature introduced in Windows 11 - [ ] Windows: Maximize button docking feature introduced in Windows 11
- [ ] More feature requests are welcome! - [ ] More feature requests are welcome!
## Feedback ## Build
Please write down your feature requests and bug reports in here: <https://github.com/wangwenx190/framelesshelper/issues/104>. Thanks! ```bash
git clone https://github.com/wangwenx190/framelesshelper.git
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=<YOUR_QT_SDK_DIR_PATH> -DCMAKE_BUILD_TYPE=Release -GNinja ../framelesshelper
cmake --build . --config Release --target all --parallel
```
**Note**: On Linux you need to install the GTK3 and X11 development packages first.
## Use
For Qt Widgets applications: subclass `FramelessWidget` or `FramelessMainWindow`.
For Qt Quick applications: use `FramelessWindow` instead of `Window`.
Please refer to the demo applications to see more detailed usages: [examples](./examples/)
## Platform Notes
### Windows
- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape.
### Linux
- FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland.
### macOS
- The three system buttons on the title bar can't be made hidden for Qt Widgets applications, for some unknown reason.
## License ## License

BIN
doc/linux_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

BIN
doc/linux_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

BIN
doc/mac_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

BIN
doc/mac_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 KiB

BIN
doc/win_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

BIN
doc/win_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

View File

@ -61,9 +61,10 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8Point1OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8Point1OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101607OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_1607OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101809OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_1809OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101903OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_1903OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_2004OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin11OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin11OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isDwmCompositionEnabled(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isDwmCompositionEnabled();
FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId windowId); FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId windowId);

View File

@ -245,12 +245,12 @@ void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemPar
} }
Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true); Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true);
Utils::updateWindowFrameMargins(windowId, false); Utils::updateWindowFrameMargins(windowId, false);
if (Utils::isWin101607OrGreater()) { if (Utils::isWin10_1607OrGreater()) {
const bool dark = Utils::shouldAppsUseDarkMode(); const bool dark = Utils::shouldAppsUseDarkMode();
if (!(settings.options & Option::DontTouchWindowFrameBorderColor)) { if (!(settings.options & Option::DontTouchWindowFrameBorderColor)) {
Utils::updateWindowFrameBorderColor(windowId, dark); Utils::updateWindowFrameBorderColor(windowId, dark);
} }
if (Utils::isWin101809OrGreater()) { if (Utils::isWin10_1809OrGreater()) {
if (settings.options & Option::SyncNativeControlsThemeWithSystem) { if (settings.options & Option::SyncNativeControlsThemeWithSystem) {
Utils::updateGlobalWin32ControlsTheme(windowId, dark); Utils::updateGlobalWin32ControlsTheme(windowId, dark);
} }
@ -821,7 +821,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
} }
bool systemThemeChanged = ((uMsg == WM_THEMECHANGED) || (uMsg == WM_SYSCOLORCHANGE) bool systemThemeChanged = ((uMsg == WM_THEMECHANGED) || (uMsg == WM_SYSCOLORCHANGE)
|| (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED)); || (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED));
if (Utils::isWin101607OrGreater()) { if (Utils::isWin10_1607OrGreater()) {
if (uMsg == WM_SETTINGCHANGE) { if (uMsg == WM_SETTINGCHANGE) {
if ((wParam == 0) && (QString::fromWCharArray(reinterpret_cast<LPCWSTR>(lParam)) if ((wParam == 0) && (QString::fromWCharArray(reinterpret_cast<LPCWSTR>(lParam))
.compare(qThemeSettingChangeEventName, Qt::CaseInsensitive) == 0)) { .compare(qThemeSettingChangeEventName, Qt::CaseInsensitive) == 0)) {
@ -830,7 +830,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
if (!(data.settings.options & Option::DontTouchWindowFrameBorderColor)) { if (!(data.settings.options & Option::DontTouchWindowFrameBorderColor)) {
Utils::updateWindowFrameBorderColor(windowId, dark); Utils::updateWindowFrameBorderColor(windowId, dark);
} }
if (Utils::isWin101809OrGreater()) { if (Utils::isWin10_1809OrGreater()) {
if (data.settings.options & Option::SyncNativeControlsThemeWithSystem) { if (data.settings.options & Option::SyncNativeControlsThemeWithSystem) {
Utils::updateGlobalWin32ControlsTheme(windowId, dark); Utils::updateGlobalWin32ControlsTheme(windowId, dark);
} }

View File

@ -204,6 +204,9 @@ void FramelessWindowsManagerPrivate::initialize()
m_colorizationArea = Utils::getDwmColorizationArea(); m_colorizationArea = Utils::getDwmColorizationArea();
m_accentColor = Utils::getDwmColorizationColor(); m_accentColor = Utils::getDwmColorizationColor();
#endif #endif
#ifdef Q_OS_LINUX
m_accentColor = Utils::getWmThemeColor();
#endif
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
m_accentColor = Utils::getControlsAccentColor(); m_accentColor = Utils::getControlsAccentColor();
#endif #endif

View File

@ -34,21 +34,21 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; using namespace Global;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOP = 1; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOP = 1;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7;
static constexpr const auto _NET_WM_MOVERESIZE_MOVE = 8; [[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_MOVE = 8;
static constexpr const char WM_MOVERESIZE_OPERATION_NAME[] = "_NET_WM_MOVERESIZE"; [[maybe_unused]] static constexpr const char WM_MOVERESIZE_OPERATION_NAME[] = "_NET_WM_MOVERESIZE";
static constexpr const char GTK_THEME_NAME_ENV_VAR[] = "GTK_THEME"; [[maybe_unused]] static constexpr const char GTK_THEME_NAME_ENV_VAR[] = "GTK_THEME";
static constexpr const char GTK_THEME_NAME_PROP[] = "gtk-theme-name"; [[maybe_unused]] static constexpr const char GTK_THEME_NAME_PROP[] = "gtk-theme-name";
static constexpr const char GTK_THEME_PREFER_DARK_PROP[] = "gtk-application-prefer-dark-theme"; [[maybe_unused]] static constexpr const char GTK_THEME_PREFER_DARK_PROP[] = "gtk-application-prefer-dark-theme";
FRAMELESSHELPER_STRING_CONSTANT2(GTK_THEME_DARK_REGEX, "[:-]dark") FRAMELESSHELPER_STRING_CONSTANT2(GTK_THEME_DARK_REGEX, "[:-]dark")
template<typename T> template<typename T>
@ -80,7 +80,8 @@ template<typename T>
return result; return result;
} }
[[nodiscard]] static inline int qtEdgesToWmMoveOrResizeOperation(const Qt::Edges edges) [[maybe_unused]] [[nodiscard]] static inline int
qtEdgesToWmMoveOrResizeOperation(const Qt::Edges edges)
{ {
if (edges == Qt::Edges{}) { if (edges == Qt::Edges{}) {
return -1; return -1;
@ -112,7 +113,8 @@ template<typename T>
return -1; return -1;
} }
static inline void doStartSystemMoveResize(const WId windowId, const QPoint &globalPos, const int edges) [[maybe_unused]] static inline void
doStartSystemMoveResize(const WId windowId, const QPoint &globalPos, const int edges)
{ {
Q_ASSERT(windowId); Q_ASSERT(windowId);
Q_ASSERT(edges >= 0); Q_ASSERT(edges >= 0);
@ -161,11 +163,16 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)
if (!window) { if (!window) {
return; return;
} }
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemMove();
#else
// Qt always gives us logical coordinates, however, the native APIs // Qt always gives us logical coordinates, however, the native APIs
// are expecting device coordinates. // are expecting device coordinates.
const qreal dpr = window->devicePixelRatio(); const qreal dpr = window->devicePixelRatio();
const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint(); const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint();
doStartSystemMoveResize(window->winId(), globalPos2, _NET_WM_MOVERESIZE_MOVE); doStartSystemMoveResize(window->winId(), globalPos2, _NET_WM_MOVERESIZE_MOVE);
#endif
} }
void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos) void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos)
@ -177,6 +184,10 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
if (edges == Qt::Edges{}) { if (edges == Qt::Edges{}) {
return; return;
} }
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemResize(edges);
#else
const int section = qtEdgesToWmMoveOrResizeOperation(edges); const int section = qtEdgesToWmMoveOrResizeOperation(edges);
if (section < 0) { if (section < 0) {
return; return;
@ -186,6 +197,7 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
const qreal dpr = window->devicePixelRatio(); const qreal dpr = window->devicePixelRatio();
const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint(); const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint();
doStartSystemMoveResize(window->winId(), globalPos2, section); doStartSystemMoveResize(window->winId(), globalPos2, section);
#endif
} }
bool Utils::isTitleBarColorized() bool Utils::isTitleBarColorized()

View File

@ -141,29 +141,6 @@ Q_GLOBAL_STATIC(NSWindowProxyHash, g_nswindowOverrideHash);
return [nsview window]; return [nsview window];
} }
static inline void mac_windowStartNativeDrag(const WId windowId, const QPoint &globalPos)
{
Q_ASSERT(windowId);
if (!windowId) {
return;
}
const NSWindow * const nswindow = mac_getNSWindow(windowId);
Q_ASSERT(nswindow);
if (!nswindow) {
return;
}
const CGEventRef clickDown = CGEventCreateMouseEvent(
NULL, kCGEventLeftMouseDown, CGPointMake(globalPos.x(), globalPos.y()), kCGMouseButtonLeft);
NSEvent * const nsevent = [NSEvent eventWithCGEvent:clickDown];
Q_ASSERT(nsevent);
if (!nsevent) {
CFRelease(clickDown);
return;
}
[nswindow performWindowDragWithEvent:nsevent];
CFRelease(clickDown);
}
SystemTheme Utils::getSystemTheme() SystemTheme Utils::getSystemTheme()
{ {
// ### TODO: how to detect high contrast mode on macOS? // ### TODO: how to detect high contrast mode on macOS?
@ -199,14 +176,43 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)
if (!window) { if (!window) {
return; return;
} }
mac_windowStartNativeDrag(window->winId(), globalPos); #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemMove();
#else
const NSWindow * const nswindow = mac_getNSWindow(window->winId());
Q_ASSERT(nswindow);
if (!nswindow) {
return;
}
const CGEventRef clickDown = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown,
CGPointMake(globalPos.x(), globalPos.y()), kCGMouseButtonLeft);
NSEvent * const nsevent = [NSEvent eventWithCGEvent:clickDown];
Q_ASSERT(nsevent);
if (!nsevent) {
CFRelease(clickDown);
return;
}
[nswindow performWindowDragWithEvent:nsevent];
CFRelease(clickDown);
#endif
} }
void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos) void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos)
{ {
Q_UNUSED(window); Q_ASSERT(window);
Q_UNUSED(edges); if (!window) {
return;
}
if (edges == Qt::Edges{}) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos); Q_UNUSED(globalPos);
window->startSystemResize(edges);
#else
Q_UNUSED(globalPos);
#endif
} }
QColor Utils::getControlsAccentColor() QColor Utils::getControlsAccentColor()

View File

@ -113,9 +113,25 @@ FRAMELESSHELPER_STRING_CONSTANT(SystemParametersInfoW)
FRAMELESSHELPER_STRING_CONSTANT2(SetWindowLongPtrW, "SetWindowLongW") FRAMELESSHELPER_STRING_CONSTANT2(SetWindowLongPtrW, "SetWindowLongW")
#endif #endif
FRAMELESSHELPER_STRING_CONSTANT(ReleaseCapture) FRAMELESSHELPER_STRING_CONSTANT(ReleaseCapture)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowTheme)
FRAMELESSHELPER_STRING_CONSTANT(SetProcessDpiAwarenessContext)
FRAMELESSHELPER_STRING_CONSTANT(SetProcessDpiAwareness)
FRAMELESSHELPER_STRING_CONSTANT(SetProcessDPIAware)
FRAMELESSHELPER_STRING_CONSTANT(GetDpiForMonitor)
FRAMELESSHELPER_STRING_CONSTANT(MonitorFromPoint)
FRAMELESSHELPER_STRING_CONSTANT(D2D1CreateFactory)
FRAMELESSHELPER_STRING_CONSTANT(ReloadSystemMetrics)
FRAMELESSHELPER_STRING_CONSTANT(GetDC)
FRAMELESSHELPER_STRING_CONSTANT(ReleaseDC)
FRAMELESSHELPER_STRING_CONSTANT(GetDeviceCaps)
FRAMELESSHELPER_STRING_CONSTANT(DwmSetWindowAttribute)
FRAMELESSHELPER_STRING_CONSTANT(EnableMenuItem)
FRAMELESSHELPER_STRING_CONSTANT(SetMenuDefaultItem)
FRAMELESSHELPER_STRING_CONSTANT(HiliteMenuItem)
FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu)
#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) [[maybe_unused]] [[nodiscard]] static inline bool
[[nodiscard]] static inline bool isWindowsVersionOrGreater(const DWORD dwMajor, const DWORD dwMinor, const DWORD dwBuild) isWindowsVersionOrGreater(const DWORD dwMajor, const DWORD dwMinor, const DWORD dwBuild)
{ {
OSVERSIONINFOEXW osvi; OSVERSIONINFOEXW osvi;
SecureZeroMemory(&osvi, sizeof(osvi)); SecureZeroMemory(&osvi, sizeof(osvi));
@ -128,9 +144,8 @@ FRAMELESSHELPER_STRING_CONSTANT(ReleaseCapture)
VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op); VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op);
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op); VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op);
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, op); VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, op);
return (VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, dwlConditionMask) != FALSE); return (VerifyVersionInfoW(&osvi, (VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER), dwlConditionMask) != FALSE);
} }
#endif
[[nodiscard]] static inline QString __getSystemErrorMessage(const QString &function, const DWORD code) [[nodiscard]] static inline QString __getSystemErrorMessage(const QString &function, const DWORD code)
{ {
@ -186,7 +201,8 @@ FRAMELESSHELPER_STRING_CONSTANT(ReleaseCapture)
} }
} }
[[nodiscard]] static inline DWORD qtEdgesToWin32Orientation(const Qt::Edges edges) [[maybe_unused]] [[nodiscard]] static inline
DWORD qtEdgesToWin32Orientation(const Qt::Edges edges)
{ {
if (edges == Qt::Edges{}) { if (edges == Qt::Edges{}) {
return 0; return 0;
@ -563,12 +579,18 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const QPoint &
const QPoint adjustment = (maxOrFull ? QPoint(0, 0) : offset); const QPoint adjustment = (maxOrFull ? QPoint(0, 0) : offset);
const int xPos = (pos.x() + adjustment.x()); const int xPos = (pos.x() + adjustment.x());
const int yPos = (pos.y() + adjustment.y()); const int yPos = (pos.y() + adjustment.y());
const int ret = TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() const int result = TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft()
? TPM_RIGHTALIGN : TPM_LEFTALIGN)), xPos, yPos, 0, hWnd, nullptr); ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), xPos, yPos, 0, hWnd, nullptr);
if (ret != 0) { // TrackPopupMenu returns 0: the user cancelled the menu, or some error occurred.
if (PostMessageW(hWnd, WM_SYSCOMMAND, ret, 0) == FALSE) { if (result == 0) {
qWarning() << getSystemErrorMessage(kPostMessageW); const DWORD dwError = GetLastError();
if (dwError != ERROR_SUCCESS) {
qWarning() << __getSystemErrorMessage(kTrackPopupMenu, dwError);
} }
return;
}
if (PostMessageW(hWnd, WM_SYSCOMMAND, result, 0) == FALSE) {
qWarning() << getSystemErrorMessage(kPostMessageW);
} }
} }
@ -698,7 +720,7 @@ void Utils::syncWmPaintWithDwm()
} }
} }
bool Utils::isWin101607OrGreater() bool Utils::isWin10_1607OrGreater()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)); static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393));
@ -708,7 +730,7 @@ bool Utils::isWin101607OrGreater()
return result; return result;
} }
bool Utils::isWin101809OrGreater() bool Utils::isWin10_1809OrGreater()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_1809); static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_1809);
@ -720,7 +742,7 @@ bool Utils::isWin101809OrGreater()
return result; return result;
} }
bool Utils::isWin101903OrGreater() bool Utils::isWin10_1903OrGreater()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_1903); static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_1903);
@ -732,6 +754,18 @@ bool Utils::isWin101903OrGreater()
return result; return result;
} }
bool Utils::isWin10_2004OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_2004);
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 19041));
#else
static const bool result = isWindowsVersionOrGreater(10, 0, 19041);
#endif
return result;
}
bool Utils::isHighContrastModeEnabled() bool Utils::isHighContrastModeEnabled()
{ {
HIGHCONTRASTW hc; HIGHCONTRASTW hc;
@ -750,12 +784,17 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal)
reinterpret_cast<decltype(&GetDpiForMonitor)>( reinterpret_cast<decltype(&GetDpiForMonitor)>(
QSystemLibrary::resolve(kshcore, "GetDpiForMonitor")); QSystemLibrary::resolve(kshcore, "GetDpiForMonitor"));
if (pGetDpiForMonitor) { if (pGetDpiForMonitor) {
const HMONITOR monitor = MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY); const HMONITOR monitor = MonitorFromPoint(POINT{50, 50}, MONITOR_DEFAULTTOPRIMARY);
if (monitor) { if (monitor) {
UINT dpiX = 0, dpiY = 0; UINT dpiX = 0, dpiY = 0;
if (SUCCEEDED(pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { const HRESULT hr = pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
if (SUCCEEDED(hr)) {
return (horizontal ? dpiX : dpiY); return (horizontal ? dpiX : dpiY);
} else {
qWarning() << __getSystemErrorMessage(kGetDpiForMonitor, hr);
} }
} else {
qWarning() << getSystemErrorMessage(kMonitorFromPoint);
} }
} }
static const auto pD2D1CreateFactory = static const auto pD2D1CreateFactory =
@ -763,29 +802,42 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal)
QSystemLibrary::resolve(kd2d1, "D2D1CreateFactory")); QSystemLibrary::resolve(kd2d1, "D2D1CreateFactory"));
if (pD2D1CreateFactory) { if (pD2D1CreateFactory) {
CComPtr<ID2D1Factory> d2dFactory = nullptr; CComPtr<ID2D1Factory> d2dFactory = nullptr;
if (SUCCEEDED(pD2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), HRESULT hr = pD2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory),
nullptr, reinterpret_cast<void **>(&d2dFactory)))) { nullptr, reinterpret_cast<void **>(&d2dFactory));
if (SUCCEEDED(d2dFactory->ReloadSystemMetrics())) { if (SUCCEEDED(hr)) {
hr = d2dFactory->ReloadSystemMetrics();
if (SUCCEEDED(hr)) {
FLOAT dpiX = 0.0, dpiY = 0.0; FLOAT dpiX = 0.0, dpiY = 0.0;
QT_WARNING_PUSH QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED QT_WARNING_DISABLE_DEPRECATED
d2dFactory->GetDesktopDpi(&dpiX, &dpiY); d2dFactory->GetDesktopDpi(&dpiX, &dpiY);
QT_WARNING_POP QT_WARNING_POP
return (horizontal ? quint32(qRound(dpiX)) : quint32(qRound(dpiY))); return (horizontal ? quint32(qRound(dpiX)) : quint32(qRound(dpiY)));
} else {
qWarning() << __getSystemErrorMessage(kReloadSystemMetrics, hr);
} }
} else {
qWarning() << __getSystemErrorMessage(kD2D1CreateFactory, hr);
} }
} }
const HDC hdc = GetDC(nullptr); const HDC hdc = GetDC(nullptr);
if (hdc) { if (hdc) {
bool valid = false;
const int dpiX = GetDeviceCaps(hdc, LOGPIXELSX); const int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
const int dpiY = GetDeviceCaps(hdc, LOGPIXELSY); const int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(nullptr, hdc); if ((dpiX > 0) && (dpiY > 0)) {
if (horizontal && (dpiX > 0)) { valid = true;
return dpiX; } else {
qWarning() << getSystemErrorMessage(kGetDeviceCaps);
} }
if (!horizontal && (dpiY > 0)) { if (ReleaseDC(nullptr, hdc) == 0) {
return dpiY; qWarning() << getSystemErrorMessage(kReleaseDC);
} }
if (valid) {
return (horizontal ? dpiX : dpiY);
}
} else {
qWarning() << getSystemErrorMessage(kGetDC);
} }
return USER_DEFAULT_SCREEN_DPI; return USER_DEFAULT_SCREEN_DPI;
} }
@ -820,9 +872,14 @@ quint32 Utils::getWindowDpi(const WId windowId, const bool horizontal)
const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor) { if (monitor) {
UINT dpiX = 0, dpiY = 0; UINT dpiX = 0, dpiY = 0;
if (SUCCEEDED(pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { const HRESULT hr = pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
if (SUCCEEDED(hr)) {
return (horizontal ? dpiX : dpiY); return (horizontal ? dpiX : dpiY);
} else {
qWarning() << __getSystemErrorMessage(kGetDpiForMonitor, hr);
} }
} else {
qWarning() << getSystemErrorMessage(kMonitorFromWindow);
} }
} }
return getPrimaryScreenDpi(horizontal); return getPrimaryScreenDpi(horizontal);
@ -916,7 +973,7 @@ void Utils::updateWindowFrameBorderColor(const WId windowId, const bool dark)
return; return;
} }
// There's no global dark theme before Win10 1607. // There's no global dark theme before Win10 1607.
if (!isWin101607OrGreater()) { if (!isWin10_1607OrGreater()) {
return; return;
} }
static const auto pDwmSetWindowAttribute = static const auto pDwmSetWindowAttribute =
@ -926,11 +983,12 @@ void Utils::updateWindowFrameBorderColor(const WId windowId, const bool dark)
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(windowId); const auto hwnd = reinterpret_cast<HWND>(windowId);
const DWORD mode = (isWin10_2004OrGreater() ? _DWMWA_USE_IMMERSIVE_DARK_MODE : _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1);
const BOOL value = (dark ? TRUE : FALSE); const BOOL value = (dark ? TRUE : FALSE);
// Whether dark window frame is available or not depends on the runtime system version, const HRESULT hr = pDwmSetWindowAttribute(hwnd, mode, &value, sizeof(value));
// it's totally OK if it's not available, so just ignore the errors. if (FAILED(hr)) {
pDwmSetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, &value, sizeof(value)); qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
pDwmSetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value)); }
} }
void Utils::fixupQtInternals(const WId windowId) void Utils::fixupQtInternals(const WId windowId)
@ -946,7 +1004,12 @@ void Utils::fixupQtInternals(const WId windowId)
qWarning() << getSystemErrorMessage(kGetClassLongPtrW); qWarning() << getSystemErrorMessage(kGetClassLongPtrW);
return; return;
} }
const DWORD newClassStyle = (oldClassStyle | CS_HREDRAW | CS_VREDRAW); // CS_HREDRAW/CS_VREDRAW will trigger a repaint event when the window size changes
// horizontally/vertically, which will cause flicker and jitter during window
// resizing, mostly for the applications which do all the painting by themselves.
// So we remove these flags from the window class here, Qt by default won't add them
// but let's make it extra safe.
const DWORD newClassStyle = (oldClassStyle & ~(CS_HREDRAW | CS_VREDRAW));
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
if (SetClassLongPtrW(hwnd, GCL_STYLE, static_cast<LONG_PTR>(newClassStyle)) == 0) { if (SetClassLongPtrW(hwnd, GCL_STYLE, static_cast<LONG_PTR>(newClassStyle)) == 0) {
qWarning() << getSystemErrorMessage(kSetClassLongPtrW); qWarning() << getSystemErrorMessage(kSetClassLongPtrW);
@ -964,9 +1027,7 @@ void Utils::fixupQtInternals(const WId windowId)
// windows, for example, it will affect DWM's default policy. And Qt will also not add // windows, for example, it will affect DWM's default policy. And Qt will also not add
// the "WS_OVERLAPPED" flag to the windows in some cases, which also causes some trouble // the "WS_OVERLAPPED" flag to the windows in some cases, which also causes some trouble
// for us. To avoid some weird bugs, we do the correction here: remove the WS_POPUP flag // for us. To avoid some weird bugs, we do the correction here: remove the WS_POPUP flag
// and add the WS_OVERLAPPED flag, unconditionally. If your window really don't need this // and add the WS_OVERLAPPED flag, unconditionally.
// correction, it also means you should not use this framework, because without this
// correction, our core frameless functionality will be broken in some degree.
const DWORD newWindowStyle = ((oldWindowStyle & ~WS_POPUP) | WS_OVERLAPPED); const DWORD newWindowStyle = ((oldWindowStyle & ~WS_POPUP) | WS_OVERLAPPED);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast<LONG_PTR>(newWindowStyle)) == 0) { if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast<LONG_PTR>(newWindowStyle)) == 0) {
@ -983,6 +1044,9 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)
if (!window) { if (!window) {
return; return;
} }
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemMove();
#else
if (ReleaseCapture() == FALSE) { if (ReleaseCapture() == FALSE) {
qWarning() << getSystemErrorMessage(kReleaseCapture); qWarning() << getSystemErrorMessage(kReleaseCapture);
return; return;
@ -991,6 +1055,7 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)
if (PostMessageW(hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0) == FALSE) { if (PostMessageW(hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0) == FALSE) {
qWarning() << getSystemErrorMessage(kPostMessageW); qWarning() << getSystemErrorMessage(kPostMessageW);
} }
#endif
} }
void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos) void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos)
@ -1003,6 +1068,9 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
if (edges == Qt::Edges{}) { if (edges == Qt::Edges{}) {
return; return;
} }
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemResize(edges);
#else
if (ReleaseCapture() == FALSE) { if (ReleaseCapture() == FALSE) {
qWarning() << getSystemErrorMessage(kReleaseCapture); qWarning() << getSystemErrorMessage(kReleaseCapture);
return; return;
@ -1011,6 +1079,7 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
if (PostMessageW(hwnd, WM_SYSCOMMAND, qtEdgesToWin32Orientation(edges), 0) == FALSE) { if (PostMessageW(hwnd, WM_SYSCOMMAND, qtEdgesToWin32Orientation(edges), 0) == FALSE) {
qWarning() << getSystemErrorMessage(kPostMessageW); qWarning() << getSystemErrorMessage(kPostMessageW);
} }
#endif
} }
bool Utils::isWindowFrameBorderVisible() bool Utils::isWindowFrameBorderVisible()
@ -1082,7 +1151,7 @@ void Utils::installSystemMenuHook(const WId windowId, const Options options, con
qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); qWarning() << getSystemErrorMessage(kSetWindowLongPtrW);
return; return;
} }
//triggerFrameChange(windowId); //triggerFrameChange(windowId); // Crash
Win32UtilsHelperData data = {}; Win32UtilsHelperData data = {};
data.originalWindowProc = originalWindowProc; data.originalWindowProc = originalWindowProc;
data.options = options; data.options = options;
@ -1112,7 +1181,7 @@ void Utils::uninstallSystemMenuHook(const WId windowId)
qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); qWarning() << getSystemErrorMessage(kSetWindowLongPtrW);
return; return;
} }
//triggerFrameChange(windowId); //triggerFrameChange(windowId); // Crash
g_utilsHelper()->data.remove(windowId); g_utilsHelper()->data.remove(windowId);
} }
@ -1138,6 +1207,8 @@ void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId windowId,
const Qt::WindowFlags newWindowFlags = (enable ? (originalWindowFlags | Qt::FramelessWindowHint) const Qt::WindowFlags newWindowFlags = (enable ? (originalWindowFlags | Qt::FramelessWindowHint)
: (originalWindowFlags & ~Qt::FramelessWindowHint)); : (originalWindowFlags & ~Qt::FramelessWindowHint));
setWindowFlags(newWindowFlags); setWindowFlags(newWindowFlags);
// The trick is to restore the window style. Qt mainly adds the "WS_EX_LAYERED"
// flag to the extended window style.
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(hwnd, GWL_STYLE, originalWindowStyle) == 0) { if (SetWindowLongPtrW(hwnd, GWL_STYLE, originalWindowStyle) == 0) {
qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); qWarning() << getSystemErrorMessage(kSetWindowLongPtrW);
@ -1159,6 +1230,7 @@ void Utils::setAeroSnappingEnabled(const WId windowId, const bool enable)
qWarning() << getSystemErrorMessage(kGetWindowLongPtrW); qWarning() << getSystemErrorMessage(kGetWindowLongPtrW);
return; return;
} }
// The key is the existence of the "WS_THICKFRAME" flag.
const DWORD newWindowStyle = [enable, oldWindowStyle]() -> DWORD { const DWORD newWindowStyle = [enable, oldWindowStyle]() -> DWORD {
if (enable) { if (enable) {
return ((oldWindowStyle & ~WS_POPUP) | WS_THICKFRAME); return ((oldWindowStyle & ~WS_POPUP) | WS_THICKFRAME);
@ -1180,21 +1252,36 @@ void Utils::tryToEnableHighestDpiAwarenessLevel()
reinterpret_cast<decltype(&SetProcessDpiAwarenessContext)>( reinterpret_cast<decltype(&SetProcessDpiAwarenessContext)>(
QSystemLibrary::resolve(kuser32, "SetProcessDpiAwarenessContext")); QSystemLibrary::resolve(kuser32, "SetProcessDpiAwarenessContext"));
if (pSetProcessDpiAwarenessContext) { if (pSetProcessDpiAwarenessContext) {
if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) != FALSE) { const auto SetProcessDpiAwarenessContext2 = [](const DPI_AWARENESS_CONTEXT context) -> bool {
Q_ASSERT(context);
if (!context) {
return false;
}
if (pSetProcessDpiAwarenessContext(context) != FALSE) {
return true;
}
const DWORD dwError = GetLastError();
// "ERROR_ACCESS_DENIED" means set externally (mostly due to manifest file).
// Any attempt to change the DPI awareness level through API will always fail,
// so we treat this situation as succeeded.
if (dwError == ERROR_ACCESS_DENIED) {
qDebug() << "FramelessHelper doesn't have access to change this process's DPI awareness level,"
" most likely due to it has been setted externally.";
return true;
}
qWarning() << __getSystemErrorMessage(kSetProcessDpiAwarenessContext, dwError);
return false;
};
if (SetProcessDpiAwarenessContext2(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
return; return;
} }
const DWORD dwError = GetLastError(); if (SetProcessDpiAwarenessContext2(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) {
// "ERROR_ACCESS_DENIED" means set externally (mostly due to manifest file).
if (dwError == ERROR_ACCESS_DENIED) {
return; return;
} }
if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) != FALSE) { if (SetProcessDpiAwarenessContext2(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) {
return; return;
} }
if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE) != FALSE) { if (SetProcessDpiAwarenessContext2(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) {
return;
}
if (pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED) != FALSE) {
return; return;
} }
} }
@ -1202,24 +1289,35 @@ void Utils::tryToEnableHighestDpiAwarenessLevel()
reinterpret_cast<decltype(&SetProcessDpiAwareness)>( reinterpret_cast<decltype(&SetProcessDpiAwareness)>(
QSystemLibrary::resolve(kshcore, "SetProcessDpiAwareness")); QSystemLibrary::resolve(kshcore, "SetProcessDpiAwareness"));
if (pSetProcessDpiAwareness) { if (pSetProcessDpiAwareness) {
// This enum value is our own extension, so don't check for "E_ACCESSDENIED" const auto SetProcessDpiAwareness2 = [](const PROCESS_DPI_AWARENESS pda) -> bool {
// because it won't appear in anywhere outside of our own code. const HRESULT hr = pSetProcessDpiAwareness(pda);
if (SUCCEEDED(pSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE_V2))) { if (SUCCEEDED(hr)) {
return true;
}
// "E_ACCESSDENIED" means set externally (mostly due to manifest file).
// Any attempt to change the DPI awareness level through API will always fail,
// so we treat this situation as succeeded.
if (hr == E_ACCESSDENIED) {
qDebug() << "FramelessHelper doesn't have access to change this process's DPI awareness level,"
" most likely due to it has been setted externally.";
return true;
}
qWarning() << __getSystemErrorMessage(kSetProcessDpiAwareness, hr);
return false;
};
if (SetProcessDpiAwareness2(PROCESS_PER_MONITOR_DPI_AWARE_V2)) {
return; return;
} }
const HRESULT hr = pSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); if (SetProcessDpiAwareness2(PROCESS_PER_MONITOR_DPI_AWARE)) {
if (SUCCEEDED(hr)) {
return; return;
} }
// "E_ACCESSDENIED" means set externally (mostly due to manifest file). if (SetProcessDpiAwareness2(PROCESS_SYSTEM_DPI_AWARE)) {
if (hr == E_ACCESSDENIED) {
return;
}
if (SUCCEEDED(pSetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE))) {
return; return;
} }
} }
SetProcessDPIAware(); if (SetProcessDPIAware() == FALSE) {
qWarning() << getSystemErrorMessage(kSetProcessDPIAware);
}
} }
SystemTheme Utils::getSystemTheme() SystemTheme Utils::getSystemTheme()
@ -1227,7 +1325,7 @@ SystemTheme Utils::getSystemTheme()
if (isHighContrastModeEnabled()) { if (isHighContrastModeEnabled()) {
return SystemTheme::HighContrast; return SystemTheme::HighContrast;
} }
if (isWin101607OrGreater() && shouldAppsUseDarkMode()) { if (isWin10_1607OrGreater() && shouldAppsUseDarkMode()) {
return SystemTheme::Dark; return SystemTheme::Dark;
} }
return SystemTheme::Light; return SystemTheme::Light;
@ -1240,7 +1338,7 @@ void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark)
return; return;
} }
// There's no global dark theme for common Win32 controls before Win10 1809. // There's no global dark theme for common Win32 controls before Win10 1809.
if (!isWin101809OrGreater()) { if (!isWin10_1809OrGreater()) {
return; return;
} }
static const auto pSetWindowTheme = static const auto pSetWindowTheme =
@ -1250,14 +1348,16 @@ void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark)
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(windowId); const auto hwnd = reinterpret_cast<HWND>(windowId);
// The result depends on the runtime system version, no need to check. const HRESULT hr = pSetWindowTheme(hwnd, (dark ? kSystemDarkThemeResourceName : kSystemLightThemeResourceName), nullptr);
pSetWindowTheme(hwnd, (dark ? kSystemDarkThemeResourceName : kSystemLightThemeResourceName), nullptr); if (FAILED(hr)) {
qWarning() << __getSystemErrorMessage(kSetWindowTheme, hr);
}
} }
bool Utils::shouldAppsUseDarkMode_windows() bool Utils::shouldAppsUseDarkMode_windows()
{ {
// The global dark mode was first introduced in Windows 10 1607. // The global dark mode was first introduced in Windows 10 1607.
if (!isWin101607OrGreater()) { if (!isWin10_1607OrGreater()) {
return false; return false;
} }
const auto resultFromRegistry = []() -> bool { const auto resultFromRegistry = []() -> bool {
@ -1268,7 +1368,7 @@ bool Utils::shouldAppsUseDarkMode_windows()
static const auto pShouldAppsUseDarkMode = static const auto pShouldAppsUseDarkMode =
reinterpret_cast<BOOL(WINAPI *)(VOID)>( reinterpret_cast<BOOL(WINAPI *)(VOID)>(
QSystemLibrary::resolve(kuxtheme, MAKEINTRESOURCEA(132))); QSystemLibrary::resolve(kuxtheme, MAKEINTRESOURCEA(132)));
if (pShouldAppsUseDarkMode && !isWin101903OrGreater()) { if (pShouldAppsUseDarkMode && !isWin10_1903OrGreater()) {
return (pShouldAppsUseDarkMode() != FALSE); return (pShouldAppsUseDarkMode() != FALSE);
} }
// Starting from Windows 10 1903, "ShouldAppsUseDarkMode()" always return "TRUE" // Starting from Windows 10 1903, "ShouldAppsUseDarkMode()" always return "TRUE"