From c35643dba3efabaf3fa036895f152bf5b8725f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 1 Dec 2020 15:24:23 +0100 Subject: [PATCH] Windows: Add support for PerMonitorV2 DPI awareness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for opting in to PerMonitorV2 DPI awareness on the command line: -platform windows:dpiawareness=3 This mode is supported on Windows 10 and up. Setting it requires using the new SetProcessDpiAwarenessContext API, which can be resolved from user32.dll. Task-number: QTBUG-68712 Change-Id: I37821e27a67e08c2e9fef25e494cfd7abed13314 Reviewed-by: Tor Arne Vestbø --- .../platforms/windows/qtwindowsglobal.h | 11 +++++++++- .../platforms/windows/qwindowscontext.cpp | 21 +++++++++++++++++++ .../platforms/windows/qwindowscontext.h | 5 +++++ .../platforms/windows/qwindowsintegration.cpp | 19 ++++++++++++----- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index 985f13bdc5..573a8d07c8 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -76,6 +76,14 @@ # define WM_POINTERHWHEEL 0x024F #endif // WM_POINTERUPDATE +#if !defined(_DPI_AWARENESS_CONTEXTS_) +# define DPI_AWARENESS_CONTEXT_UNAWARE ((HANDLE)-1) +# define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((HANDLE)-2) +# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((HANDLE)-3) +# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE)-4) +# define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((HANDLE)-5) +#endif + QT_BEGIN_NAMESPACE namespace QtWindows @@ -167,7 +175,8 @@ enum ProcessDpiAwareness { ProcessDpiUnaware, ProcessSystemDpiAware, - ProcessPerMonitorDpiAware + ProcessPerMonitorDpiAware, + ProcessPerMonitorV2DpiAware // Qt extension (not in Process_DPI_Awareness) }; } // namespace QtWindows diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 67d38a95ce..475dca4020 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -192,6 +192,7 @@ void QWindowsUser32DLL::init() { QSystemLibrary library(QStringLiteral("user32")); setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware"); + setProcessDpiAwarenessContext = (SetProcessDpiAwarenessContext)library.resolve("SetProcessDpiAwarenessContext"); addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener"); removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener"); @@ -278,9 +279,11 @@ struct QWindowsContextPrivate { HPOWERNOTIFY m_powerNotification = nullptr; HWND m_powerDummyWindow = nullptr; static bool m_darkMode; + static bool m_v2DpiAware; }; bool QWindowsContextPrivate::m_darkMode = false; +bool QWindowsContextPrivate::m_v2DpiAware = false; QWindowsContextPrivate::QWindowsContextPrivate() : m_oleInitializeResult(OleInitialize(nullptr)) @@ -505,6 +508,23 @@ void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiA } } +void QWindowsContext::setProcessDpiV2Awareness() +{ + qCDebug(lcQpaWindows) << __FUNCTION__; + const BOOL ok = QWindowsContext::user32dll.setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + if (ok) { + QWindowsContextPrivate::m_v2DpiAware = true; + } else { + const HRESULT errorCode = GetLastError(); + // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin). + // Silence warning in that case unless debug is enabled. + if (errorCode != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled()) { + qWarning().noquote().nospace() << "setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: " + << QWindowsContext::comErrorString(errorCode); + } + } +} + bool QWindowsContext::isDarkMode() { return QWindowsContextPrivate::m_darkMode; @@ -1098,6 +1118,7 @@ static inline bool resizeOnDpiChanged(const QWindow *w) bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window) { return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 + && !QWindowsContextPrivate::m_v2DpiAware // V2 implies NonClientDpiScaling; no need to enable && window->isTopLevel() && !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid() #if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index aacdafebb6..99b4bb6c77 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -98,6 +98,7 @@ struct QWindowsUser32DLL typedef BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32, UINT32 *, PVOID); typedef BOOL (WINAPI *SkipPointerFrameMessages)(UINT32); typedef BOOL (WINAPI *SetProcessDPIAware)(); + typedef BOOL (WINAPI *SetProcessDpiAwarenessContext)(HANDLE); typedef BOOL (WINAPI *AddClipboardFormatListener)(HWND); typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND); typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *); @@ -123,6 +124,9 @@ struct QWindowsUser32DLL // Windows Vista onwards SetProcessDPIAware setProcessDPIAware = nullptr; + // Windows 10 version 1703 onwards + SetProcessDpiAwarenessContext setProcessDpiAwarenessContext = nullptr; + // Clipboard listeners are present on Windows Vista onwards // but missing in MinGW 4.9 stub libs. Can be removed in MinGW 5. AddClipboardFormatListener addClipboardFormatListener = nullptr; @@ -227,6 +231,7 @@ public: static void setTabletAbsoluteRange(int a); void setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness); static int processDpiAwareness(); + void setProcessDpiV2Awareness(); static bool isDarkMode(); diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 74e6985980..ed401bac30 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -219,7 +219,7 @@ static inline unsigned parseOptions(const QStringList ¶mList, options |= QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch; } else if (parseIntOption(param, QLatin1String("verbose"), 0, INT_MAX, &QWindowsContext::verbose) || parseIntOption(param, QLatin1String("tabletabsoluterange"), 0, INT_MAX, tabletAbsoluteRange) - || parseIntOption(param, QLatin1String("dpiawareness"), QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorDpiAware, dpiAwareness)) { + || parseIntOption(param, QLatin1String("dpiawareness"), QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorV2DpiAware, dpiAwareness)) { } else if (param == u"menus=native") { options |= QWindowsIntegration::AlwaysUseNativeMenus; } else if (param == u"menus=none") { @@ -263,10 +263,19 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication. if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { - m_context.setProcessDpiAwareness(dpiAwareness); - qCDebug(lcQpaWindows) - << __FUNCTION__ << "DpiAwareness=" << dpiAwareness - << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness(); + + // DpiAwareV2 requires using new API + bool hasDpiAwarenessContext = QWindowsContext::user32dll.setProcessDpiAwarenessContext != nullptr; + if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware && hasDpiAwarenessContext) { + m_context.setProcessDpiV2Awareness(); + qCDebug(lcQpaWindows) + << __FUNCTION__ << "DpiAwareness: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2"; + } else { + m_context.setProcessDpiAwareness(dpiAwareness); + qCDebug(lcQpaWindows) + << __FUNCTION__ << "DpiAwareness=" << dpiAwareness + << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness(); + } } dpiAwarenessSet = true; }