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>
This commit is contained in:
parent
21baa76230
commit
5e0d9a077d
@ -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
|
||||
|
@ -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()
|
||||
[[nodiscard]] static inline QtWindows::DpiAwareness
|
||||
dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT context)
|
||||
{
|
||||
PROCESS_DPI_AWARENESS result;
|
||||
if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) {
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
return -1;
|
||||
// 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;
|
||||
}
|
||||
|
||||
void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness)
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
QDebug operator<<(QDebug d, QtWindows::DpiAwareness dpiAwareness)
|
||||
{
|
||||
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::setProcessDpiAwareness(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";
|
||||
}
|
||||
}
|
||||
|
||||
bool QWindowsContext::setProcessDpiV2Awareness()
|
||||
{
|
||||
qCDebug(lcQpaWindow) << __FUNCTION__;
|
||||
auto dpiContext = GetThreadDpiAwarenessContext();
|
||||
if (AreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
|
||||
if (processDpiAwareness() == dpiAwareness)
|
||||
return true;
|
||||
|
||||
const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
if (!ok) {
|
||||
const DWORD dwError = GetLastError();
|
||||
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)";
|
||||
const auto context = qtDpiAwarenessToDpiAwarenessContext(dpiAwareness);
|
||||
if (!IsValidDpiAwarenessContext(context)) {
|
||||
qCWarning(lcQpaWindow) << dpiAwareness << "is not supported by current system.";
|
||||
return false;
|
||||
}
|
||||
QWindowsContextPrivate::m_v2DpiAware = true;
|
||||
if (!SetProcessDpiAwarenessContext(context)) {
|
||||
qCWarning(lcQpaWindow).noquote().nospace()
|
||||
<< "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
|
||||
= 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.
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -152,7 +152,7 @@ bool parseIntOption(const QString ¶meter,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 ¶mList,
|
||||
int *tabletAbsoluteRange,
|
||||
QtWindows::ProcessDpiAwareness *dpiAwareness,
|
||||
QtWindows::DpiAwareness *dpiAwareness,
|
||||
DarkModeHandling *darkModeHandling)
|
||||
{
|
||||
unsigned options = 0;
|
||||
@ -200,7 +200,8 @@ static inline unsigned parseOptions(const QStringList ¶mList,
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user