diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 77ff734e..a7aad0a0 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -118,6 +118,8 @@ FRAMELESSHELPER_CORE_API void refreshWin32ThemeResources(const WId windowId, con FRAMELESSHELPER_CORE_API void enableNonClientAreaDpiScalingForWindow(const WId windowId); [[nodiscard]] FRAMELESSHELPER_CORE_API Global::DpiAwareness getDpiAwarenessForCurrentProcess(bool *highest = nullptr); +FRAMELESSHELPER_CORE_API void fixupChildWindowsDpiMessage(const WId windowId); +FRAMELESSHELPER_CORE_API void fixupDialogsDpiScaling(); #endif // Q_OS_WINDOWS #ifdef Q_OS_LINUX diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index d6e26a06..1dc9ab66 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -35,8 +35,6 @@ #include "winverhelper_p.h" #include "framelesshelper_windows.h" -EXTERN_C BOOL WINAPI EnableChildWindowDpiMessage2(const HWND hWnd, const BOOL fEnable); - FRAMELESSHELPER_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcFramelessHelperWin, "wangwenx190.framelesshelper.core.impl.win") @@ -75,7 +73,6 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW) FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow) -FRAMELESSHELPER_STRING_CONSTANT(EnableChildWindowDpiMessage) [[maybe_unused]] static constexpr const char kFallbackTitleBarErrorMessage[] = "FramelessHelper is unable to create the fallback title bar window, and thus the snap layout feature will be disabled" " unconditionally. You can ignore this error and continue running your application, nothing else will be affected, " @@ -102,12 +99,14 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) [[nodiscard]] static inline QString hwnd2str(const WId windowId) { + // NULL handle is allowed here. return FRAMELESSHELPER_STRING_LITERAL("0x") + QString::number(windowId, 16).toUpper(); } [[nodiscard]] static inline QString hwnd2str(const HWND hwnd) { + // NULL handle is allowed here. return hwnd2str(reinterpret_cast(hwnd)); } @@ -536,18 +535,10 @@ void FramelessHelperWin::addWindow(const SystemParameters ¶ms) Utils::updateWindowFrameMargins(windowId, false); // Tell DWM we don't use the window icon/caption/sysmenu, don't draw them. Utils::hideOriginalTitleBarElements(windowId); - // We don't need this hack on Win10 1607 and newer, the PMv2 DPI awareness - // mode will take care of it for us by default. - if (WindowsVersionHelper::isWin10OrGreater() - && !WindowsVersionHelper::isWin10RS1OrGreater()) { - // Without this hack, child windows can't get DPI change messages, - // which means only top level windows can scale to the correct size. - if (EnableChildWindowDpiMessage2(reinterpret_cast(windowId), TRUE) == FALSE) { - if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) { - WARNING << Utils::getSystemErrorMessage(kEnableChildWindowDpiMessage); - } - } - } + // Without this hack, the child windows can't get DPI change messages from + // Windows, which means only the top level windows can be scaled to the correct + // size, we of course don't want such thing from happening. + Utils::fixupChildWindowsDpiMessage(windowId); if (WindowsVersionHelper::isWin10RS1OrGreater()) { // Tell DWM we may need dark theme non-client area (title bar & frame border). FramelessHelper::Core::setApplicationOSThemeAware(); @@ -649,7 +640,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // Enable automatic DPI scaling for the non-client area of the window, // such as the caption bar, the scrollbars, and the menu bar. We need // to do this explicitly and manually here (only inside WM_NCCREATE). - // If we are using the PMv2 DPI awareness level, the non-client area + // If we are using the PMv2 DPI awareness mode, the non-client area // of the window will be scaled by the OS automatically, so there will // be no need to do this in that case. Utils::enableNonClientAreaDpiScalingForWindow(windowId); diff --git a/src/core/framelesshelpercore_global.cpp b/src/core/framelesshelpercore_global.cpp index 5136845e..559008ae 100644 --- a/src/core/framelesshelpercore_global.cpp +++ b/src/core/framelesshelpercore_global.cpp @@ -169,12 +169,17 @@ void initialize() #ifdef Q_OS_WINDOWS // This is equivalent to set the "dpiAware" and "dpiAwareness" field in // your manifest file. It works through out Windows Vista to Windows 11. - // It's highly recommended to enable the highest DPI awareness level + // It's highly recommended to enable the highest DPI awareness mode // (currently it's PerMonitor Version 2, or PMv2 for short) for any GUI // applications, to allow your user interface scale to an appropriate // size and still stay sharp, though you will have to do the calculation // and resize by yourself. Utils::tryToEnableHighestDpiAwarenessLevel(); + // This function need to be called before any dialogs are created, so + // to be safe we call it here. + // Without this hack, our native dialogs won't be able to respond to + // DPI change messages correctly, especially the non-client area. + Utils::fixupDialogsDpiScaling(); #endif // This attribute is known to be __NOT__ compatible with QGLWidget. diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index ca7df252..3f4ba0d0 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -1850,18 +1850,6 @@ void Utils::setAeroSnappingEnabled(const WId windowId, const bool enable) void Utils::tryToEnableHighestDpiAwarenessLevel() { - // We don't need this hack when running on Win10 1607 and newer, the PMv2 - // DPI awareness mode will take care of it for us by default. - if (WindowsVersionHelper::isWin10OrGreater() - && !WindowsVersionHelper::isWin10RS1OrGreater()) { - // This function need to be called before any dialogs are created, so - // to be safe we call it here. - if (EnablePerMonitorDialogScaling2() == FALSE) { - if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) { - WARNING << getSystemErrorMessage(kEnablePerMonitorDialogScaling); - } - } - } bool isHighestAlready = false; const DpiAwareness currentAwareness = getDpiAwarenessForCurrentProcess(&isHighestAlready); DEBUG << "Current DPI awareness mode:" << currentAwareness; @@ -2490,8 +2478,7 @@ void Utils::enableNonClientAreaDpiScalingForWindow(const WId windowId) if (!API_USER_AVAILABLE(EnableNonClientDpiScaling)) { return; } - // There's no need to enable automatic non-client area DPI scaling for PMv2, - // PMv2 will enable it for us by default. + // The PMv2 DPI awareness mode will take care of it for us. if (getDpiAwarenessForCurrentProcess() == DpiAwareness::PerMonitorVersion2) { return; } @@ -2607,4 +2594,49 @@ DpiAwareness Utils::getDpiAwarenessForCurrentProcess(bool *highest) return DpiAwareness::Unknown; } +void Utils::fixupChildWindowsDpiMessage(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + // This hack is only available on Windows 10 and newer, and starting from + // Win10 1607 it become useless due to the PMv2 DPI awareness mode already + // takes care of it for us. + if (!WindowsVersionHelper::isWin10OrGreater() + || (WindowsVersionHelper::isWin10RS1OrGreater() + && (getDpiAwarenessForCurrentProcess() == DpiAwareness::PerMonitorVersion2))) { + return; + } + const auto hwnd = reinterpret_cast(windowId); + if (EnableChildWindowDpiMessage2(hwnd, TRUE) != FALSE) { + return; + } + // This API is not available on current platform, it's fine. + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { + return; + } + WARNING << getSystemErrorMessage(kEnableChildWindowDpiMessage); +} + +void Utils::fixupDialogsDpiScaling() +{ + // This hack is only available on Windows 10 and newer, and starting from + // Win10 1607 it become useless due to the PMv2 DPI awareness mode already + // takes care of it for us. + if (!WindowsVersionHelper::isWin10OrGreater() + || (WindowsVersionHelper::isWin10RS1OrGreater() + && (getDpiAwarenessForCurrentProcess() == DpiAwareness::PerMonitorVersion2))) { + return; + } + if (EnablePerMonitorDialogScaling2() != FALSE) { + return; + } + // This API is not available on current platform, it's fine. + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { + return; + } + WARNING << getSystemErrorMessage(kEnablePerMonitorDialogScaling); +} + FRAMELESSHELPER_END_NAMESPACE