summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
authorYuhang Zhao <2546789017@qq.com>2022-11-17 15:14:24 +0800
committerYuhang Zhao <2546789017@qq.com>2023-01-23 23:39:16 +0800
commit5e0d9a077d28802988182319ae257e9102f0344e (patch)
tree769da6ec229dc7e5230dbc9f866126096b773c80 /src/plugins/platforms
parent21baa7623025308a39577e7582c023b1d3ae11d6 (diff)
Windows QPA: rework how we set dpi awareness
Qt6's minimum supported platform is Win10 1809, so it should be quite safe to use DPI_AWARENESS_CONTEXT APIs because they were introduced in Win10 1607. This patch removes the use of the PROCESS_DPI_AWARENESS APIs because they are old (introduced in Win8.1) and most importantly, they can't handle the new PMv2 and GdiScaled awareness mode. This refactor also fixed a bug: previously Qt is using GetProcessDpiAwareness() to get the dpi awareness mode of the current process, however, that API can't return PMv2, which means even if we are in PMv2 mode, it will still return PMv1 (I've confirmed that locally), and thus Qt is mishandling such cases. Eg: when judging whether to enable non-client area dpi scaling or not. Change-Id: I8a8946ba63c863f8c19c27998af2bac97db37ec7 Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r--src/plugins/platforms/windows/qtwindowsglobal.h17
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp165
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h7
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp28
4 files changed, 137 insertions, 80 deletions
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index d39e923644..22787bd63e 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -150,13 +150,14 @@ enum WindowsEventType // Simplify event types
Q_DECLARE_MIXED_ENUM_OPERATORS(bool, WindowsEventTypeFlags, WindowsEventType);
Q_DECLARE_MIXED_ENUM_OPERATORS(bool, WindowsEventType, WindowsEventTypeFlags);
-// Matches Process_DPI_Awareness (Windows 8.1 onwards), used for SetProcessDpiAwareness()
-enum ProcessDpiAwareness
+enum class DpiAwareness
{
- ProcessDpiUnaware,
- ProcessSystemDpiAware,
- ProcessPerMonitorDpiAware,
- ProcessPerMonitorV2DpiAware // Qt extension (not in Process_DPI_Awareness)
+ Invalid = -1,
+ Unaware,
+ System,
+ PerMonitor,
+ PerMonitorVersion2,
+ Unaware_GdiScaled
};
} // namespace QtWindows
@@ -326,6 +327,10 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
return QtWindows::UnknownEvent;
}
+#ifndef QT_NO_DEBUG_STREAM
+extern QDebug operator<<(QDebug, QtWindows::DpiAwareness);
+#endif
+
QT_END_NAMESPACE
#endif // QTWINDOWSGLOBAL_H
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index c92c935dfb..3fe91d2935 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -118,26 +118,6 @@ static inline bool sessionManagerInteractionBlocked()
static inline bool sessionManagerInteractionBlocked() { return false; }
#endif
-static inline int windowDpiAwareness(HWND hwnd)
-{
- return static_cast<int>(GetAwarenessFromDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd)));
-}
-
-// Note: This only works within WM_NCCREATE
-static bool enableNonClientDpiScaling(HWND hwnd)
-{
- bool result = false;
- if (windowDpiAwareness(hwnd) == 2) {
- result = EnableNonClientDpiScaling(hwnd) != FALSE;
- if (!result) {
- const DWORD errorCode = GetLastError();
- qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
- hwnd, errorCode);
- }
- }
- return result;
-}
-
QWindowsContext *QWindowsContext::m_instance = nullptr;
/*!
@@ -366,49 +346,117 @@ void QWindowsContext::setDetectAltGrModifier(bool a)
d->m_keyMapper.setDetectAltGrModifier(a);
}
-int QWindowsContext::processDpiAwareness()
-{
- PROCESS_DPI_AWARENESS result;
- if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) {
- return static_cast<int>(result);
+[[nodiscard]] static inline QtWindows::DpiAwareness
+ dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT context)
+{
+ // IsValidDpiAwarenessContext() will handle the NULL pointer case.
+ if (!IsValidDpiAwarenessContext(context))
+ return QtWindows::DpiAwareness::Invalid;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED))
+ return QtWindows::DpiAwareness::Unaware_GdiScaled;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
+ return QtWindows::DpiAwareness::PerMonitorVersion2;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
+ return QtWindows::DpiAwareness::PerMonitor;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE))
+ return QtWindows::DpiAwareness::System;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE))
+ return QtWindows::DpiAwareness::Unaware;
+ return QtWindows::DpiAwareness::Invalid;
+}
+
+QtWindows::DpiAwareness QWindowsContext::windowDpiAwareness(HWND hwnd)
+{
+ if (!hwnd)
+ return QtWindows::DpiAwareness::Invalid;
+ const auto context = GetWindowDpiAwarenessContext(hwnd);
+ return dpiAwarenessContextToQtDpiAwareness(context);
+}
+
+QtWindows::DpiAwareness QWindowsContext::processDpiAwareness()
+{
+ // Although we have GetDpiAwarenessContextForProcess(), however,
+ // it's only available on Win10 1903+, which is a little higher
+ // than Qt's minimum supported version (1809), so we can't use it.
+ // Luckily, MS docs said GetThreadDpiAwarenessContext() will also
+ // return the default DPI_AWARENESS_CONTEXT for the process if
+ // SetThreadDpiAwarenessContext() was never called. So we can use
+ // it as an equivalent.
+ const auto context = GetThreadDpiAwarenessContext();
+ return dpiAwarenessContextToQtDpiAwareness(context);
+}
+
+[[nodiscard]] static inline DPI_AWARENESS_CONTEXT
+ qtDpiAwarenessToDpiAwarenessContext(QtWindows::DpiAwareness dpiAwareness)
+{
+ switch (dpiAwareness) {
+ case QtWindows::DpiAwareness::Invalid:
+ return nullptr;
+ case QtWindows::DpiAwareness::Unaware:
+ return DPI_AWARENESS_CONTEXT_UNAWARE;
+ case QtWindows::DpiAwareness::System:
+ return DPI_AWARENESS_CONTEXT_SYSTEM_AWARE;
+ case QtWindows::DpiAwareness::PerMonitor:
+ return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
+ case QtWindows::DpiAwareness::PerMonitorVersion2:
+ return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
+ case QtWindows::DpiAwareness::Unaware_GdiScaled:
+ return DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED;
}
- return -1;
+ return nullptr;
}
-void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness)
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, QtWindows::DpiAwareness dpiAwareness)
{
- qCDebug(lcQpaWindow) << __FUNCTION__ << dpiAwareness;
- if (processDpiAwareness() == int(dpiAwareness))
- return;
-
- const HRESULT hr = SetProcessDpiAwareness(static_cast<PROCESS_DPI_AWARENESS>(dpiAwareness));
- if (FAILED(hr)) {
- qCWarning(lcQpaWindow).noquote().nospace() << "SetProcessDpiAwareness("
- << dpiAwareness << ") failed: " << QSystemError::windowsComString(hr) << ", using "
- << QWindowsContext::processDpiAwareness() << "\nQt's fallback DPI awareness is "
- << "PROCESS_DPI_AWARENESS. If you know what you are doing consider an override in qt.conf";
+ const QDebugStateSaver saver(d);
+ d.nospace().noquote() << "QtWindows::DpiAwareness::";
+ switch (dpiAwareness) {
+ case QtWindows::DpiAwareness::Invalid:
+ d.nospace().noquote() << "Invalid";
+ break;
+ case QtWindows::DpiAwareness::Unaware:
+ d.nospace().noquote() << "Unaware";
+ break;
+ case QtWindows::DpiAwareness::System:
+ d.nospace().noquote() << "System";
+ break;
+ case QtWindows::DpiAwareness::PerMonitor:
+ d.nospace().noquote() << "PerMonitor";
+ break;
+ case QtWindows::DpiAwareness::PerMonitorVersion2:
+ d.nospace().noquote() << "PerMonitorVersion2";
+ break;
+ case QtWindows::DpiAwareness::Unaware_GdiScaled:
+ d.nospace().noquote() << "Unaware_GdiScaled";
+ break;
}
+ return d;
}
+#endif
-bool QWindowsContext::setProcessDpiV2Awareness()
+bool QWindowsContext::setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness)
{
- qCDebug(lcQpaWindow) << __FUNCTION__;
- auto dpiContext = GetThreadDpiAwarenessContext();
- if (AreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
+ qCDebug(lcQpaWindow) << __FUNCTION__ << dpiAwareness;
+ if (processDpiAwareness() == dpiAwareness)
return true;
-
- const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
- if (!ok) {
- const DWORD dwError = GetLastError();
+ const auto context = qtDpiAwarenessToDpiAwarenessContext(dpiAwareness);
+ if (!IsValidDpiAwarenessContext(context)) {
+ qCWarning(lcQpaWindow) << dpiAwareness << "is not supported by current system.";
+ return false;
+ }
+ if (!SetProcessDpiAwarenessContext(context)) {
qCWarning(lcQpaWindow).noquote().nospace()
- << "SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: "
- << QSystemError::windowsComString(HRESULT(dwError)) << "\nQt's default DPI awareness "
- << "context is DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. If you know what you "
- << "are doing you can overwrite this default using qt.conf "
- << "(https://doc.qt.io/qt-6/highdpi.html#configuring-windows)";
+ << "SetProcessDpiAwarenessContext() failed: "
+ << QSystemError::windowsString()
+ << "\nQt's default DPI awareness context is "
+ << "DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. If you know what you "
+ << "are doing, you can overwrite this default using qt.conf "
+ << "(https://doc.qt.io/qt-6/highdpi.html#configuring-windows).";
return false;
}
- QWindowsContextPrivate::m_v2DpiAware = true;
+ QWindowsContextPrivate::m_v2DpiAware
+ = processDpiAwareness() == QtWindows::DpiAwareness::PerMonitorVersion2;
return true;
}
@@ -921,6 +969,21 @@ static inline bool isInputMessage(UINT m)
|| (m >= WM_KEYFIRST && m <= WM_KEYLAST);
}
+// Note: This only works within WM_NCCREATE
+static bool enableNonClientDpiScaling(HWND hwnd)
+{
+ bool result = false;
+ if (QWindowsContext::windowDpiAwareness(hwnd) == QtWindows::DpiAwareness::PerMonitor) {
+ result = EnableNonClientDpiScaling(hwnd) != FALSE;
+ if (!result) {
+ const DWORD errorCode = GetLastError();
+ qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
+ hwnd, errorCode);
+ }
+ }
+ return result;
+}
+
/*!
\brief Main windows procedure registered for windows.
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 6b3010f33c..21e841a886 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -115,9 +115,10 @@ public:
QSharedPointer<QWindowCreationContext> windowCreationContext() const;
static void setTabletAbsoluteRange(int a);
- void setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness);
- static int processDpiAwareness();
- bool setProcessDpiV2Awareness();
+
+ bool setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness);
+ static QtWindows::DpiAwareness processDpiAwareness();
+ static QtWindows::DpiAwareness windowDpiAwareness(HWND hwnd);
static bool isDarkMode();
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 51f040e719..ce34697edd 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -152,7 +152,7 @@ bool parseIntOption(const QString &parameter,const QLatin1StringView &option,
const auto valueRef = QStringView{parameter}.right(valueLength);
const int value = valueRef.toInt(&ok);
if (ok) {
- if (value >= minimumValue && value <= maximumValue)
+ if (value >= int(minimumValue) && value <= int(maximumValue))
*target = static_cast<IntType>(value);
else {
qWarning() << "Value" << value << "for option" << option << "out of range"
@@ -169,7 +169,7 @@ using DarkModeHandling = QNativeInterface::Private::QWindowsApplication::DarkMod
static inline unsigned parseOptions(const QStringList &paramList,
int *tabletAbsoluteRange,
- QtWindows::ProcessDpiAwareness *dpiAwareness,
+ QtWindows::DpiAwareness *dpiAwareness,
DarkModeHandling *darkModeHandling)
{
unsigned options = 0;
@@ -200,7 +200,8 @@ static inline unsigned parseOptions(const QStringList &paramList,
options |= QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch;
} else if (parseIntOption(param, "verbose"_L1, 0, INT_MAX, &QWindowsContext::verbose)
|| parseIntOption(param, "tabletabsoluterange"_L1, 0, INT_MAX, tabletAbsoluteRange)
- || parseIntOption(param, "dpiawareness"_L1, QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorV2DpiAware, dpiAwareness)) {
+ || parseIntOption(param, "dpiawareness"_L1, QtWindows::DpiAwareness::Invalid,
+ QtWindows::DpiAwareness::PerMonitorVersion2, dpiAwareness)) {
} else if (param == u"menus=native") {
options |= QWindowsIntegration::AlwaysUseNativeMenus;
} else if (param == u"menus=none") {
@@ -230,7 +231,7 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
static bool dpiAwarenessSet = false;
// Default to per-monitor-v2 awareness (if available)
- QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorV2DpiAware;
+ QtWindows::DpiAwareness dpiAwareness = QtWindows::DpiAwareness::PerMonitorVersion2;
int tabletAbsoluteRange = -1;
DarkModeHandling darkModeHandling = DarkModeHandlingFlag::DarkModeWindowFrames
@@ -249,22 +250,9 @@ 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)) {
- if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware) {
- // DpiAwareV2 requires using new API
- if (m_context.setProcessDpiV2Awareness()) {
- qCDebug(lcQpaWindow, "DpiAwareness: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2");
- dpiAwarenessSet = true;
- } else {
- // fallback to old API
- dpiAwareness = QtWindows::ProcessPerMonitorDpiAware;
- }
- }
-
- if (!dpiAwarenessSet) {
- m_context.setProcessDpiAwareness(dpiAwareness);
- qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness
- << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
- }
+ m_context.setProcessDpiAwareness(dpiAwareness);
+ qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness
+ << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
}
dpiAwarenessSet = true;
}