qt 6.5.3 win7 compatibility with fallbacks to newer behaviour on later windows, qrhi3d11 not tested

This commit is contained in:
kleuter 2023-11-01 22:16:22 +01:00
parent 7018d9e6c8
commit a948a5cdf9
19 changed files with 913 additions and 285 deletions

View File

@ -12,6 +12,7 @@
#include <qt_windows.h> #include <qt_windows.h>
#include <shlobj.h> #include <shlobj.h>
#include <VersionHelpers.h>
#include <intshcut.h> #include <intshcut.h>
#include <qvarlengtharray.h> #include <qvarlengtharray.h>
@ -60,19 +61,25 @@ static inline void appendTestMode(QString &path)
static bool isProcessLowIntegrity() static bool isProcessLowIntegrity()
{ {
if (!IsWindows8OrGreater())
return false;
// same as GetCurrentProcessToken() // same as GetCurrentProcessToken()
const auto process_token = HANDLE(quintptr(-4)); const auto process_token = HANDLE(quintptr(-4));
QVarLengthArray<char,256> token_info_buf(256); QVarLengthArray<char,256> token_info_buf(256);
auto* token_info = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_info_buf.data()); auto* token_info = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_info_buf.data());
DWORD token_info_length = token_info_buf.size(); DWORD token_info_length = token_info_buf.size();
if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_info, token_info_length, &token_info_length)) { if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_info, token_info_length, &token_info_length)
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// grow buffer and retry GetTokenInformation // grow buffer and retry GetTokenInformation
token_info_buf.resize(token_info_length); token_info_buf.resize(token_info_length);
token_info = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_info_buf.data()); token_info = reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_info_buf.data());
if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_info, token_info_length, &token_info_length)) if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_info, token_info_length, &token_info_length))
return false; // assume "normal" process return false; // assume "normal" process
} }
else
return false;
// The GetSidSubAuthorityCount return-code is undefined on failure, so // The GetSidSubAuthorityCount return-code is undefined on failure, so
// there's no point in checking before dereferencing // there's no point in checking before dereferencing

View File

@ -354,10 +354,15 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
ok = t->fastTimerId; ok = t->fastTimerId;
} }
if (!ok) { typedef BOOL (WINAPI *SetCoalescableTimerFunc) (HWND, UINT_PTR, UINT, TIMERPROC, ULONG);
static SetCoalescableTimerFunc mySetCoalescableTimerFunc =
(SetCoalescableTimerFunc)::GetProcAddress(::GetModuleHandle(L"User32"), "SetCoalescableTimer");
if (!ok && mySetCoalescableTimerFunc) {
// user normal timers for (Very)CoarseTimers, or if no more multimedia timers available // user normal timers for (Very)CoarseTimers, or if no more multimedia timers available
ok = SetCoalescableTimer(internalHwnd, t->timerId, interval, nullptr, tolerance); ok = mySetCoalescableTimerFunc(internalHwnd, t->timerId, interval, nullptr, tolerance);
} }
if (!ok) if (!ok)
ok = SetTimer(internalHwnd, t->timerId, interval, nullptr); ok = SetTimer(internalHwnd, t->timerId, interval, nullptr);

View File

@ -42,11 +42,18 @@ QComHelper::~QComHelper()
https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/modernize-packaged-apps https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/modernize-packaged-apps
*/ */
bool qt_win_hasPackageIdentity() bool qt_win_hasPackageIdentity()
{
typedef BOOL (WINAPI *GetCurrentPackageFullNameFunc) (UINT32 *, PWSTR);
static GetCurrentPackageFullNameFunc myGetCurrentPackageFullName =
(GetCurrentPackageFullNameFunc)::GetProcAddress(::GetModuleHandle(L"kernel32"), "GetCurrentPackageFullName");
if (myGetCurrentPackageFullName)
{ {
#if defined(HAS_APPMODEL) #if defined(HAS_APPMODEL)
static const bool hasPackageIdentity = []() { static const bool hasPackageIdentity = []() {
UINT32 length = 0; UINT32 length = 0;
switch (const auto result = GetCurrentPackageFullName(&length, nullptr)) { switch (const auto result = myGetCurrentPackageFullName(&length, nullptr)) {
case ERROR_INSUFFICIENT_BUFFER: case ERROR_INSUFFICIENT_BUFFER:
return true; return true;
case APPMODEL_ERROR_NO_PACKAGE: case APPMODEL_ERROR_NO_PACKAGE:
@ -62,4 +69,7 @@ bool qt_win_hasPackageIdentity()
#endif #endif
} }
return false;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -107,39 +107,6 @@ namespace QtLinuxFutex {
} }
namespace QtFutex = QtLinuxFutex; namespace QtFutex = QtLinuxFutex;
QT_END_NAMESPACE QT_END_NAMESPACE
#elif defined(Q_OS_WIN)
# include <qt_windows.h>
QT_BEGIN_NAMESPACE
namespace QtWindowsFutex {
#define QT_ALWAYS_USE_FUTEX
constexpr inline bool futexAvailable() { return true; }
template <typename Atomic>
inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
{
QtTsan::futexRelease(&futex);
WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), INFINITE);
QtTsan::futexAcquire(&futex);
}
template <typename Atomic>
inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
{
BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(nstimeout / 1000 / 1000));
return r || GetLastError() != ERROR_TIMEOUT;
}
template <typename Atomic> inline void futexWakeAll(Atomic &futex)
{
WakeByAddressAll(&futex);
}
template <typename Atomic> inline void futexWakeOne(Atomic &futex)
{
WakeByAddressSingle(&futex);
}
}
namespace QtFutex = QtWindowsFutex;
QT_END_NAMESPACE
#else #else
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE

View File

@ -869,6 +869,8 @@ QT_END_NAMESPACE
// nothing // nothing
#elif defined(Q_OS_MAC) #elif defined(Q_OS_MAC)
# include "qmutex_mac.cpp" # include "qmutex_mac.cpp"
#elif defined(Q_OS_WIN)
# include "qmutex_win.cpp"
#else #else
# include "qmutex_unix.cpp" # include "qmutex_unix.cpp"
#endif #endif

View File

@ -91,6 +91,8 @@ public:
bool wakeup; bool wakeup;
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t cond; pthread_cond_t cond;
#elif defined(Q_OS_WIN)
Qt::HANDLE event;
#endif #endif
}; };

View File

@ -0,0 +1,30 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmutex.h"
#include <qatomic.h>
#include "qmutex_p.h"
#include <qt_windows.h>
QT_BEGIN_NAMESPACE
QMutexPrivate::QMutexPrivate()
{
event = CreateEvent(0, FALSE, FALSE, 0);
if (!event)
qWarning("QMutexPrivate::QMutexPrivate: Cannot create event");
}
QMutexPrivate::~QMutexPrivate()
{ CloseHandle(event); }
bool QMutexPrivate::wait(int timeout)
{
return (WaitForSingleObjectEx(event, timeout < 0 ? INFINITE : timeout, FALSE) == WAIT_OBJECT_0);
}
void QMutexPrivate::wakeUp() noexcept
{ SetEvent(event); }
QT_END_NAMESPACE

View File

@ -13,6 +13,8 @@
#include <d3dcompiler.h> #include <d3dcompiler.h>
#include <VersionHelpers.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
@ -131,13 +133,22 @@ inline Int aligned(Int v, Int byteAlign)
static IDXGIFactory1 *createDXGIFactory2() static IDXGIFactory1 *createDXGIFactory2()
{ {
typedef HRESULT(WINAPI* CreateDXGIFactory2Func) (UINT flags, REFIID riid, void** factory);
static CreateDXGIFactory2Func myCreateDXGIFactory2 =
(CreateDXGIFactory2Func)::GetProcAddress(::GetModuleHandle(L"dxgi"), "CreateDXGIFactory2");
IDXGIFactory1 *result = nullptr; IDXGIFactory1 *result = nullptr;
const HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&result));
if (myCreateDXGIFactory2)
{
const HRESULT hr = myCreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&result));
if (FAILED(hr)) { if (FAILED(hr)) {
qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s",
qPrintable(QSystemError::windowsComString(hr))); qPrintable(QSystemError::windowsComString(hr)));
result = nullptr; result = nullptr;
} }
}
return result; return result;
} }
@ -4914,6 +4925,15 @@ static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
bool QD3D11SwapChain::createOrResize() bool QD3D11SwapChain::createOrResize()
{ {
if (IsWindows10OrGreater())
{
// continue
}
else
{
return createOrResizeWin7();
}
// Can be called multiple times due to window resizes - that is not the // Can be called multiple times due to window resizes - that is not the
// same as a simple destroy+create (as with other resources). Just need to // same as a simple destroy+create (as with other resources). Just need to
// resize the buffers then. // resize the buffers then.
@ -5203,6 +5223,295 @@ bool QD3D11SwapChain::createOrResize()
return true; return true;
} }
bool QD3D11SwapChain::createOrResizeWin7()
{
// Can be called multiple times due to window resizes - that is not the
// same as a simple destroy+create (as with other resources). Just need to
// resize the buffers then.
const bool needsRegistration = !window || window != m_window;
// except if the window actually changes
if (window && window != m_window)
destroy();
window = m_window;
m_currentPixelSize = surfacePixelSize();
pixelSize = m_currentPixelSize;
if (pixelSize.isEmpty())
return false;
QRHI_RES_RHI(QRhiD3D11);
bool useFlipModel = rhiD->supportsFlipSwapchain;
// Take a shortcut for alpha: whatever the platform plugin does to enable
// transparency for our QWindow will be sufficient on the legacy (DISCARD)
// path. For FLIP_* we'd need to use DirectComposition (create a
// IDCompositionDevice/Target/Visual), avoid that for now. (this though
// means HDR and semi-transparent windows cannot be combined)
if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
useFlipModel = false;
if (window->requestedFormat().alphaBufferSize() <= 0)
qWarning("Swapchain says surface has alpha but the window has no alphaBufferSize set. "
"This may lead to problems.");
}
swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
swapChainFlags = 0;
// A non-flip swapchain can do Present(0) as expected without
// ALLOW_TEARING, and ALLOW_TEARING is not compatible with it at all so the
// flag must not be set then. Whereas for flip we should use it, if
// supported, to get better results for 'unthrottled' presentation.
if (swapInterval == 0 && useFlipModel && rhiD->supportsAllowTearing)
swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
if (!swapChain) {
HWND hwnd = reinterpret_cast<HWND>(window->winId());
sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
colorFormat = DEFAULT_FORMAT;
srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT;
HRESULT hr;
if (useFlipModel) {
DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR
DXGI_OUTPUT_DESC1 hdrOutputDesc;
if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) {
// https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
switch (m_format) {
case HDRExtendedSrgbLinear:
colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
srgbAdjustedColorFormat = colorFormat;
break;
case HDR10:
colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
srgbAdjustedColorFormat = colorFormat;
break;
default:
break;
}
} else {
// This happens also when Use HDR is set to Off in the Windows
// Display settings. Show a helpful warning, but continue with the
// default non-HDR format.
qWarning("The output associated with the window is not HDR capable "
"(or Use HDR is Off in the Display Settings), ignoring HDR format request");
}
}
// We use a FLIP model swapchain which implies a buffer count of 2
// (as opposed to the old DISCARD with back buffer count == 1).
// This makes no difference for the rest of the stuff except that
// automatic MSAA is unsupported and needs to be implemented via a
// custom multisample render target and an explicit resolve.
DXGI_SWAP_CHAIN_DESC1 desc;
memset(&desc, 0, sizeof(desc));
desc.Width = UINT(pixelSize.width());
desc.Height = UINT(pixelSize.height());
desc.Format = colorFormat;
desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = BUFFER_COUNT;
// Normally we'd want FLIP_DISCARD, but that comes with the default
// SCALING_STRETCH, as SCALING_NONE is documented to be only
// available for FLIP_SEQUENTIAl. The problem with stretch is that
// Qt Quick and similar apps typically running in resizable windows
// will not like how that looks in practice: the content will
// appear to be "jumping" around during a window resize. So choose
// sequential/none by default.
if (rhiD->forceFlipDiscard) {
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
} else {
desc.Scaling = DXGI_SCALING_NONE;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
}
// Do not bother with AlphaMode, if won't work unless we go through
// DirectComposition. Instead, we just take the other (DISCARD)
// path for now when alpha is requested.
desc.Flags = swapChainFlags;
IDXGIFactory2 *fac = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory);
IDXGISwapChain1 *sc1;
hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
// If failed and we tried a HDR format, then try with SDR. This
// matches other backends, such as Vulkan where if the format is
// not supported, the default one is used instead.
if (FAILED(hr) && m_format != SDR) {
colorFormat = DEFAULT_FORMAT;
desc.Format = DEFAULT_FORMAT;
hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1);
}
if (SUCCEEDED(hr)) {
swapChain = sc1;
if (m_format != SDR) {
IDXGISwapChain3 *sc3 = nullptr;
if (SUCCEEDED(sc1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast<void **>(&sc3)))) {
hr = sc3->SetColorSpace1(hdrColorSpace);
if (FAILED(hr))
qWarning("Failed to set color space on swapchain: %s",
qPrintable(QSystemError::windowsComString(hr)));
sc3->Release();
} else {
qWarning("IDXGISwapChain3 not available, HDR swapchain will not work as expected");
}
}
}
} else {
// Fallback: use DISCARD mode. Regardless, keep on using our manual
// resolve for symmetry with the FLIP_* code path when MSAA is
// requested. This has no HDR support.
DXGI_SWAP_CHAIN_DESC desc;
memset(&desc, 0, sizeof(desc));
desc.BufferDesc.Width = UINT(pixelSize.width());
desc.BufferDesc.Height = UINT(pixelSize.height());
desc.BufferDesc.RefreshRate.Numerator = 60;
desc.BufferDesc.RefreshRate.Denominator = 1;
desc.BufferDesc.Format = colorFormat;
desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 1;
desc.OutputWindow = hwnd;
desc.Windowed = true;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
desc.Flags = swapChainFlags;
hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain);
}
if (FAILED(hr)) {
qWarning("Failed to create D3D11 swapchain: %s",
qPrintable(QSystemError::windowsComString(hr)));
return false;
}
rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
} else {
releaseBuffers();
const UINT count = useFlipModel ? BUFFER_COUNT : 1;
HRESULT hr = swapChain->ResizeBuffers(count, UINT(pixelSize.width()), UINT(pixelSize.height()),
colorFormat, swapChainFlags);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
qWarning("Device loss detected in ResizeBuffers()");
rhiD->deviceLost = true;
return false;
} else if (FAILED(hr)) {
qWarning("Failed to resize D3D11 swapchain: %s",
qPrintable(QSystemError::windowsComString(hr)));
return false;
}
}
// This looks odd (for FLIP_*, esp. compared with backends for Vulkan
// & co.) but the backbuffer is always at index 0, with magic underneath.
// Some explanation from
// https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements
//
// "In Direct3D 11, applications could call GetBuffer( 0, … ) only once.
// Every call to Present implicitly changed the resource identity of the
// returned interface. Direct3D 12 no longer supports that implicit
// resource identity change, due to the CPU overhead required and the
// flexible resource descriptor design. As a result, the application must
// manually call GetBuffer for every each buffer created with the
// swapchain."
// So just query index 0 once (per resize) and be done with it.
HRESULT hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&backBufferTex));
if (FAILED(hr)) {
qWarning("Failed to query swapchain backbuffer: %s",
qPrintable(QSystemError::windowsComString(hr)));
return false;
}
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.Format = srgbAdjustedColorFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv);
if (FAILED(hr)) {
qWarning("Failed to create rtv for swapchain backbuffer: %s",
qPrintable(QSystemError::windowsComString(hr)));
return false;
}
// Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer.
for (int i = 0; i < BUFFER_COUNT; ++i) {
if (sampleDesc.Count > 1) {
if (!newColorBuffer(pixelSize, srgbAdjustedColorFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
return false;
}
}
if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
m_depthStencil->sampleCount(), m_sampleCount);
}
if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
m_depthStencil->setPixelSize(pixelSize);
if (!m_depthStencil->create())
qWarning("Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
pixelSize.width(), pixelSize.height());
} else {
qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
pixelSize.width(), pixelSize.height());
}
}
currentFrameSlot = 0;
frameCount = 0;
ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr;
rt.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget
QD3D11SwapChainRenderTarget *rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rt);
rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
rtD->d.pixelSize = pixelSize;
rtD->d.dpr = float(window->devicePixelRatio());
rtD->d.sampleCount = int(sampleDesc.Count);
rtD->d.colorAttCount = 1;
rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
if (rhiD->hasGpuFrameTimeCallback()) {
D3D11_QUERY_DESC queryDesc = {};
for (int i = 0; i < BUFFER_COUNT; ++i) {
if (!timestampDisjointQuery[i]) {
queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
hr = rhiD->dev->CreateQuery(&queryDesc, &timestampDisjointQuery[i]);
if (FAILED(hr)) {
qWarning("Failed to create timestamp disjoint query: %s",
qPrintable(QSystemError::windowsComString(hr)));
break;
}
}
queryDesc.Query = D3D11_QUERY_TIMESTAMP;
for (int j = 0; j < 2; ++j) {
const int idx = BUFFER_COUNT * i + j; // one pair per buffer (frame)
if (!timestampQuery[idx]) {
hr = rhiD->dev->CreateQuery(&queryDesc, &timestampQuery[idx]);
if (FAILED(hr)) {
qWarning("Failed to create timestamp query: %s",
qPrintable(QSystemError::windowsComString(hr)));
break;
}
}
}
}
// timestamp queries are optional so we can go on even if they failed
}
if (needsRegistration)
rhiD->registerResource(this);
return true;
}
void QRhiD3D11::DeviceCurse::initResources() void QRhiD3D11::DeviceCurse::initResources()
{ {
framesLeft = framesToActivate; framesLeft = framesToActivate;

View File

@ -569,6 +569,8 @@ struct QD3D11SwapChain : public QRhiSwapChain
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
bool createOrResize() override; bool createOrResize() override;
bool createOrResizeWin7();
void releaseBuffers(); void releaseBuffers();
bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc, bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const; ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const;
@ -740,6 +742,8 @@ public:
IDXGIFactory1 *dxgiFactory = nullptr; IDXGIFactory1 *dxgiFactory = nullptr;
IDCompositionDevice *dcompDevice = nullptr; IDCompositionDevice *dcompDevice = nullptr;
bool supportsAllowTearing = false; bool supportsAllowTearing = false;
bool supportsFlipSwapchain = false; // win7
bool forceFlipDiscard = false; // win7
bool deviceLost = false; bool deviceLost = false;
QRhiD3D11NativeHandles nativeHandlesStruct; QRhiD3D11NativeHandles nativeHandlesStruct;
QRhiDriverInfo driverInfoStruct; QRhiDriverInfo driverInfoStruct;

View File

@ -685,7 +685,16 @@ QFont QWindowsFontDatabaseBase::systemDefaultFont()
// Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610) // Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610)
NONCLIENTMETRICS ncm = {}; NONCLIENTMETRICS ncm = {};
ncm.cbSize = sizeof(ncm); ncm.cbSize = sizeof(ncm);
SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, defaultVerticalDPI());
typedef BOOL (WINAPI *SystemParametersInfoForDpiFunc) (UINT, UINT, PVOID, UINT, UINT);
static SystemParametersInfoForDpiFunc mySystemParametersInfoForDpi =
(SystemParametersInfoForDpiFunc)::GetProcAddress(::GetModuleHandle(L"user32"), "SystemParametersInfoForDpi");
if (mySystemParametersInfoForDpi)
mySystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, defaultVerticalDPI());
else
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont); const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont; qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont;
return systemFont; return systemFont;

View File

@ -58,10 +58,37 @@ public:
} // namespace ABI } // namespace ABI
#endif // HAS_UI_VIEW_SETTINGS #endif // HAS_UI_VIEW_SETTINGS
// based on SDL approach
// see SDL_windows_gaming_input.c, SDL_windows.c
void * WIN_LoadComBaseFunction(const char *name)
{
static bool s_bLoaded = false;
static HMODULE s_hComBase = NULL;
if (!s_bLoaded) {
s_hComBase = ::LoadLibraryEx(L"combase.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
s_bLoaded = true;
}
if (s_hComBase) {
return ::GetProcAddress(s_hComBase, name);
} else {
return NULL;
}
}
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// Return tablet mode, note: Does not work for GetDesktopWindow(). // Return tablet mode, note: Does not work for GetDesktopWindow().
bool qt_windowsIsTabletMode(HWND hwnd) bool qt_windowsIsTabletMode(HWND hwnd)
{
typedef HRESULT (WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING* string);
typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)WIN_LoadComBaseFunction("WindowsCreateStringReference");
RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)WIN_LoadComBaseFunction("RoGetActivationFactory");
if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc)
{ {
bool result = false; bool result = false;
@ -69,14 +96,14 @@ bool qt_windowsIsTabletMode(HWND hwnd)
HSTRING_HEADER uiViewSettingsIdRefHeader; HSTRING_HEADER uiViewSettingsIdRefHeader;
HSTRING uiViewSettingsIdHs = nullptr; HSTRING uiViewSettingsIdHs = nullptr;
const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1); const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1);
if (FAILED(WindowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs))) if (FAILED(WindowsCreateStringReferenceFunc(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs)))
return false; return false;
IUIViewSettingsInterop *uiViewSettingsInterop = nullptr; IUIViewSettingsInterop *uiViewSettingsInterop = nullptr;
// __uuidof(IUIViewSettingsInterop); // __uuidof(IUIViewSettingsInterop);
const GUID uiViewSettingsInteropRefId = {0x3694dbf9, 0x8f68, 0x44be,{0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}}; const GUID uiViewSettingsInteropRefId = {0x3694dbf9, 0x8f68, 0x44be,{0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}};
HRESULT hr = RoGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId, HRESULT hr = RoGetActivationFactoryFunc(uiViewSettingsIdHs, uiViewSettingsInteropRefId,
reinterpret_cast<void **>(&uiViewSettingsInterop)); reinterpret_cast<void **>(&uiViewSettingsInterop));
if (FAILED(hr)) if (FAILED(hr))
return false; return false;
@ -97,4 +124,7 @@ bool qt_windowsIsTabletMode(HWND hwnd)
return result; return result;
} }
return false;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -44,6 +44,7 @@
#include <QtCore/qsysinfo.h> #include <QtCore/qsysinfo.h>
#include <QtCore/qscopedpointer.h> #include <QtCore/qscopedpointer.h>
#include <QtCore/quuid.h> #include <QtCore/quuid.h>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h> #include <QtCore/private/qwinregistry_p.h>
#include <QtCore/private/qfactorycacheregistration_p.h> #include <QtCore/private/qfactorycacheregistration_p.h>
#include <QtCore/private/qsystemerror_p.h> #include <QtCore/private/qsystemerror_p.h>
@ -120,15 +121,17 @@ static inline bool sessionManagerInteractionBlocked() { return false; }
static inline int windowDpiAwareness(HWND hwnd) static inline int windowDpiAwareness(HWND hwnd)
{ {
return static_cast<int>(GetAwarenessFromDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd))); return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext
? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext(QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd))
: -1;
} }
// Note: This only works within WM_NCCREATE // Note: This only works within WM_NCCREATE
static bool enableNonClientDpiScaling(HWND hwnd) static bool enableNonClientDpiScaling(HWND hwnd)
{ {
bool result = false; bool result = false;
if (windowDpiAwareness(hwnd) == 2) { if (QWindowsContext::user32dll.enableNonClientDpiScaling && windowDpiAwareness(hwnd) == 2) {
result = EnableNonClientDpiScaling(hwnd) != FALSE; result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE;
if (!result) { if (!result) {
const DWORD errorCode = GetLastError(); const DWORD errorCode = GetLastError();
qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)", qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
@ -138,6 +141,77 @@ static bool enableNonClientDpiScaling(HWND hwnd)
return result; return result;
} }
/*!
\class QWindowsUser32DLL
\brief Struct that contains dynamically resolved symbols of User32.dll.
The stub libraries shipped with the MinGW compiler miss some of the
functions. They need to be retrieved dynamically.
In addition, touch-related functions are available only from Windows onwards.
These need to resolved dynamically for Q_CC_MSVC as well.
\sa QWindowsShell32DLL
\internal
*/
void QWindowsUser32DLL::init()
{
QSystemLibrary library(QStringLiteral("user32"));
setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware");
setProcessDpiAwarenessContext = (SetProcessDpiAwarenessContext)library.resolve("SetProcessDpiAwarenessContext");
getThreadDpiAwarenessContext = (GetThreadDpiAwarenessContext)library.resolve("GetThreadDpiAwarenessContext");
areDpiAwarenessContextsEqual = (AreDpiAwarenessContextsEqual)library.resolve("AreDpiAwarenessContextsEqual");
addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener");
removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener");
getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences");
setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences");
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8) {
enableMouseInPointer = (EnableMouseInPointer)library.resolve("EnableMouseInPointer");
getPointerType = (GetPointerType)library.resolve("GetPointerType");
getPointerInfo = (GetPointerInfo)library.resolve("GetPointerInfo");
getPointerDeviceRects = (GetPointerDeviceRects)library.resolve("GetPointerDeviceRects");
getPointerTouchInfo = (GetPointerTouchInfo)library.resolve("GetPointerTouchInfo");
getPointerFrameTouchInfo = (GetPointerFrameTouchInfo)library.resolve("GetPointerFrameTouchInfo");
getPointerFrameTouchInfoHistory = (GetPointerFrameTouchInfoHistory)library.resolve("GetPointerFrameTouchInfoHistory");
getPointerPenInfo = (GetPointerPenInfo)library.resolve("GetPointerPenInfo");
getPointerPenInfoHistory = (GetPointerPenInfoHistory)library.resolve("GetPointerPenInfoHistory");
skipPointerFrameMessages = (SkipPointerFrameMessages)library.resolve("SkipPointerFrameMessages");
}
if (QOperatingSystemVersion::current()
>= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) {
adjustWindowRectEx = (AdjustWindowRectEx)library.resolve("AdjustWindowRectEx");
adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi");
enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi");
getDpiForWindow = (GetDpiForWindow)library.resolve("GetDpiForWindow");
getSystemMetricsForDpi = (GetSystemMetricsForDpi)library.resolve("GetSystemMetricsForDpi");
}
}
bool QWindowsUser32DLL::supportsPointerApi()
{
return enableMouseInPointer && getPointerType && getPointerInfo && getPointerDeviceRects
&& getPointerTouchInfo && getPointerFrameTouchInfo && getPointerFrameTouchInfoHistory
&& getPointerPenInfo && getPointerPenInfoHistory && skipPointerFrameMessages;
}
void QWindowsShcoreDLL::init()
{
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1)
return;
QSystemLibrary library(QStringLiteral("SHCore"));
getProcessDpiAwareness = (GetProcessDpiAwareness)library.resolve("GetProcessDpiAwareness");
setProcessDpiAwareness = (SetProcessDpiAwareness)library.resolve("SetProcessDpiAwareness");
getDpiForMonitor = (GetDpiForMonitor)library.resolve("GetDpiForMonitor");
}
QWindowsUser32DLL QWindowsContext::user32dll;
QWindowsShcoreDLL QWindowsContext::shcoredll;
QWindowsContext *QWindowsContext::m_instance = nullptr; QWindowsContext *QWindowsContext::m_instance = nullptr;
/*! /*!
@ -181,6 +255,9 @@ bool QWindowsContextPrivate::m_v2DpiAware = false;
QWindowsContextPrivate::QWindowsContextPrivate() QWindowsContextPrivate::QWindowsContextPrivate()
: m_oleInitializeResult(OleInitialize(nullptr)) : m_oleInitializeResult(OleInitialize(nullptr))
{ {
QWindowsContext::user32dll.init();
QWindowsContext::shcoredll.init();
if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice()) if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice())
m_systemInfo |= QWindowsContext::SI_SupportsTouch; m_systemInfo |= QWindowsContext::SI_SupportsTouch;
m_displayContext = GetDC(nullptr); m_displayContext = GetDC(nullptr);
@ -299,6 +376,12 @@ bool QWindowsContext::initPointer(unsigned integrationOptions)
if (integrationOptions & QWindowsIntegration::DontUseWMPointer) if (integrationOptions & QWindowsIntegration::DontUseWMPointer)
return false; return false;
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8)
return false;
if (!QWindowsContext::user32dll.supportsPointerApi())
return false;
d->m_systemInfo |= QWindowsContext::SI_SupportsPointer; d->m_systemInfo |= QWindowsContext::SI_SupportsPointer;
return true; return true;
} }
@ -369,7 +452,8 @@ void QWindowsContext::setDetectAltGrModifier(bool a)
int QWindowsContext::processDpiAwareness() int QWindowsContext::processDpiAwareness()
{ {
PROCESS_DPI_AWARENESS result; PROCESS_DPI_AWARENESS result;
if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) { if (QWindowsContext::shcoredll.getProcessDpiAwareness
&& SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) {
return static_cast<int>(result); return static_cast<int>(result);
} }
return -1; return -1;
@ -381,23 +465,34 @@ void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiA
if (processDpiAwareness() == int(dpiAwareness)) if (processDpiAwareness() == int(dpiAwareness))
return; return;
const HRESULT hr = SetProcessDpiAwareness(static_cast<PROCESS_DPI_AWARENESS>(dpiAwareness)); if (QWindowsContext::shcoredll.isValid()) {
const HRESULT hr = QWindowsContext::shcoredll.setProcessDpiAwareness(static_cast<PROCESS_DPI_AWARENESS>(dpiAwareness));
if (FAILED(hr)) { if (FAILED(hr)) {
qCWarning(lcQpaWindow).noquote().nospace() << "SetProcessDpiAwareness(" qCWarning(lcQpaWindow).noquote().nospace() << "SetProcessDpiAwareness("
<< dpiAwareness << ") failed: " << QSystemError::windowsComString(hr) << ", using " << dpiAwareness << ") failed: " << QSystemError::windowsComString(hr) << ", using "
<< QWindowsContext::processDpiAwareness() << "\nQt's fallback DPI awareness is " << QWindowsContext::processDpiAwareness() << "\nQt's fallback DPI awareness is "
<< "PROCESS_DPI_AWARENESS. If you know what you are doing consider an override in qt.conf"; << "PROCESS_DPI_AWARENESS. If you know what you are doing consider an override in qt.conf";
} }
} else {
if (dpiAwareness != QtWindows::ProcessDpiUnaware && QWindowsContext::user32dll.setProcessDPIAware) {
if (!QWindowsContext::user32dll.setProcessDPIAware())
qErrnoWarning("SetProcessDPIAware() failed");
}
}
} }
bool QWindowsContext::setProcessDpiV2Awareness() bool QWindowsContext::setProcessDpiV2Awareness()
{ {
qCDebug(lcQpaWindow) << __FUNCTION__; qCDebug(lcQpaWindow) << __FUNCTION__;
auto dpiContext = GetThreadDpiAwarenessContext(); if (QWindowsContext::user32dll.getThreadDpiAwarenessContext) {
if (AreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) auto dpiContext = QWindowsContext::user32dll.getThreadDpiAwarenessContext();
if (QWindowsContext::user32dll.areDpiAwarenessContextsEqual &&
QWindowsContext::user32dll.areDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
return true; return true;
}
const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); if (QWindowsContext::user32dll.setProcessDpiAwarenessContext) {
const BOOL ok = QWindowsContext::user32dll.setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
if (!ok) { if (!ok) {
const DWORD dwError = GetLastError(); const DWORD dwError = GetLastError();
qCWarning(lcQpaWindow).noquote().nospace() qCWarning(lcQpaWindow).noquote().nospace()
@ -409,9 +504,13 @@ bool QWindowsContext::setProcessDpiV2Awareness()
return false; return false;
} }
QWindowsContextPrivate::m_v2DpiAware = true; QWindowsContextPrivate::m_v2DpiAware = true;
return true; return true;
} }
return false;
}
bool QWindowsContext::isDarkMode() bool QWindowsContext::isDarkMode()
{ {
return QWindowsContextPrivate::m_darkMode; return QWindowsContextPrivate::m_darkMode;
@ -837,8 +936,8 @@ void QWindowsContext::forceNcCalcSize(HWND hwnd)
bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out,
unsigned dpi) unsigned dpi)
{ {
const BOOL result = dpi != 0 const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0
? SystemParametersInfoForDpi(action, param, out, 0, dpi) ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi)
: SystemParametersInfo(action, param, out, 0); : SystemParametersInfo(action, param, out, 0);
return result == TRUE; return result == TRUE;
} }

View File

@ -14,6 +14,7 @@
#define STRICT_TYPED_ITEMIDS #define STRICT_TYPED_ITEMIDS
#include <shlobj.h> #include <shlobj.h>
#include <shlwapi.h> #include <shlwapi.h>
#include <shellscalingapi.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -44,6 +45,94 @@ class QPoint;
class QKeyEvent; class QKeyEvent;
class QPointingDevice; class QPointingDevice;
struct QWindowsUser32DLL
{
inline void init();
inline bool supportsPointerApi();
typedef BOOL (WINAPI *EnableMouseInPointer)(BOOL);
typedef BOOL (WINAPI *GetPointerType)(UINT32, PVOID);
typedef BOOL (WINAPI *GetPointerInfo)(UINT32, PVOID);
typedef BOOL (WINAPI *GetPointerDeviceRects)(HANDLE, RECT *, RECT *);
typedef BOOL (WINAPI *GetPointerTouchInfo)(UINT32, PVOID);
typedef BOOL (WINAPI *GetPointerFrameTouchInfo)(UINT32, UINT32 *, PVOID);
typedef BOOL (WINAPI *GetPointerFrameTouchInfoHistory)(UINT32, UINT32 *, UINT32 *, PVOID);
typedef BOOL (WINAPI *GetPointerPenInfo)(UINT32, PVOID);
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 *);
typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD);
typedef BOOL (WINAPI *AdjustWindowRectEx)(LPRECT,DWORD,BOOL,DWORD);
typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT);
typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND);
typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND);
typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int);
typedef BOOL (WINAPI *SystemParametersInfoForDpi)(UINT, UINT, PVOID, UINT, UINT);
typedef int (WINAPI *GetDpiForWindow)(HWND);
typedef BOOL (WINAPI *GetSystemMetricsForDpi)(INT, UINT);
typedef BOOL (WINAPI *AreDpiAwarenessContextsEqual)(int, HANDLE);
typedef int (WINAPI *GetThreadDpiAwarenessContext)();
// Windows pointer functions (Windows 8 or later).
EnableMouseInPointer enableMouseInPointer = nullptr;
GetPointerType getPointerType = nullptr;
GetPointerInfo getPointerInfo = nullptr;
GetPointerDeviceRects getPointerDeviceRects = nullptr;
GetPointerTouchInfo getPointerTouchInfo = nullptr;
GetPointerFrameTouchInfo getPointerFrameTouchInfo = nullptr;
GetPointerFrameTouchInfoHistory getPointerFrameTouchInfoHistory = nullptr;
GetPointerPenInfo getPointerPenInfo = nullptr;
GetPointerPenInfoHistory getPointerPenInfoHistory = nullptr;
SkipPointerFrameMessages skipPointerFrameMessages = nullptr;
// Windows Vista onwards
SetProcessDPIAware setProcessDPIAware = nullptr;
// Windows 10 version 1607 onwards
GetDpiForWindow getDpiForWindow = nullptr;
GetThreadDpiAwarenessContext getThreadDpiAwarenessContext = nullptr;
// Windows 10 version 1703 onwards
SetProcessDpiAwarenessContext setProcessDpiAwarenessContext = nullptr;
AreDpiAwarenessContextsEqual areDpiAwarenessContextsEqual = 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;
RemoveClipboardFormatListener removeClipboardFormatListener = nullptr;
// Rotation API
GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr;
SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr;
AdjustWindowRectEx adjustWindowRectEx = nullptr;
AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr;
EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr;
GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr;
GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr;
SystemParametersInfoForDpi systemParametersInfoForDpi = nullptr;
GetSystemMetricsForDpi getSystemMetricsForDpi = nullptr;
};
// Shell scaling library (Windows 8.1 onwards)
struct QWindowsShcoreDLL
{
void init();
inline bool isValid() const { return getProcessDpiAwareness && setProcessDpiAwareness && getDpiForMonitor; }
typedef HRESULT (WINAPI *GetProcessDpiAwareness)(HANDLE,PROCESS_DPI_AWARENESS *);
typedef HRESULT (WINAPI *SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
typedef HRESULT (WINAPI *GetDpiForMonitor)(HMONITOR,int,UINT *,UINT *);
GetProcessDpiAwareness getProcessDpiAwareness = nullptr;
SetProcessDpiAwareness setProcessDpiAwareness = nullptr;
GetDpiForMonitor getDpiForMonitor = nullptr;
};
class QWindowsContext class QWindowsContext
{ {
Q_DISABLE_COPY_MOVE(QWindowsContext) Q_DISABLE_COPY_MOVE(QWindowsContext)
@ -137,6 +226,9 @@ public:
QWindowsScreenManager &screenManager(); QWindowsScreenManager &screenManager();
QWindowsTabletSupport *tabletSupport() const; QWindowsTabletSupport *tabletSupport() const;
static QWindowsUser32DLL user32dll;
static QWindowsShcoreDLL shcoredll;
bool asyncExpose() const; bool asyncExpose() const;
void setAsyncExpose(bool value); void setAsyncExpose(bool value);

View File

@ -670,7 +670,7 @@ static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource,
const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
POINTER_INFO pointerInfo{}; POINTER_INFO pointerInfo{};
if (!GetPointerInfo(pointerId, &pointerInfo)) if (!QWindowsContext::user32dll.getPointerInfo || !QWindowsContext::user32dll.getPointerInfo(pointerId, &pointerInfo))
return E_FAIL; return E_FAIL;
if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) { if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) {

View File

@ -751,16 +751,29 @@ static inline QString messageKeyText(const MSG &msg)
[[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd) [[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd)
{ {
const UINT dpi = GetDpiForWindow(hwnd); if (QWindowsContext::user32dll.getDpiForWindow && QWindowsContext::user32dll.getSystemMetricsForDpi)
const int captionHeight = GetSystemMetricsForDpi(SM_CYCAPTION, dpi); {
const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd);
const int captionHeight = QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYCAPTION, dpi);
if (IsZoomed(hwnd)) if (IsZoomed(hwnd))
return captionHeight; return captionHeight;
// The frame height should also be taken into account if the window // The frame height should also be taken into account if the window
// is not maximized. // is not maximized.
const int frameHeight = GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) const int frameHeight = QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYSIZEFRAME, dpi)
+ GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
return captionHeight + frameHeight; return captionHeight + frameHeight;
} }
else
{
const int captionHeight = GetSystemMetrics(SM_CYCAPTION);
if (IsZoomed(hwnd))
return captionHeight;
// The frame height should also be taken into account if the window
// is not maximized.
const int frameHeight = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
return captionHeight + frameHeight;
}
}
[[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags) [[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags)
{ {

View File

@ -48,7 +48,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
*result = 0; *result = 0;
const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
if (!GetPointerType(pointerId, &m_pointerType)) { if (!QWindowsContext::user32dll.getPointerType(pointerId, &m_pointerType)) {
qWarning() << "GetPointerType() failed:" << qt_error_string(); qWarning() << "GetPointerType() failed:" << qt_error_string();
return false; return false;
} }
@ -62,12 +62,12 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
} }
case QT_PT_TOUCH: { case QT_PT_TOUCH: {
quint32 pointerCount = 0; quint32 pointerCount = 0;
if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) { if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) {
qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
return false; return false;
} }
QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount); QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount);
if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) { if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
return false; return false;
} }
@ -80,7 +80,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
// dispatch any skipped frames if event compression is disabled by the app // dispatch any skipped frames if event compression is disabled by the app
if (historyCount > 1 && !QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) { if (historyCount > 1 && !QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) {
touchInfo.resize(pointerCount * historyCount); touchInfo.resize(pointerCount * historyCount);
if (!GetPointerFrameTouchInfoHistory(pointerId, if (!QWindowsContext::user32dll.getPointerFrameTouchInfoHistory(pointerId,
&historyCount, &historyCount,
&pointerCount, &pointerCount,
touchInfo.data())) { touchInfo.data())) {
@ -101,7 +101,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
} }
case QT_PT_PEN: { case QT_PT_PEN: {
POINTER_PEN_INFO penInfo; POINTER_PEN_INFO penInfo;
if (!GetPointerPenInfo(pointerId, &penInfo)) { if (!QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo)) {
qWarning() << "GetPointerPenInfo() failed:" << qt_error_string(); qWarning() << "GetPointerPenInfo() failed:" << qt_error_string();
return false; return false;
} }
@ -113,7 +113,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
|| !QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents))) { || !QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents))) {
QVarLengthArray<POINTER_PEN_INFO, 10> penInfoHistory(historyCount); QVarLengthArray<POINTER_PEN_INFO, 10> penInfoHistory(historyCount);
if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) { if (!QWindowsContext::user32dll.getPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) {
qWarning() << "GetPointerPenInfoHistory() failed:" << qt_error_string(); qWarning() << "GetPointerPenInfoHistory() failed:" << qt_error_string();
return false; return false;
} }
@ -441,8 +441,6 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (id != -1) if (id != -1)
m_lastTouchPoints.remove(id); m_lastTouchPoints.remove(id);
} }
// Send LeaveEvent to reset hover when the last finger leaves the touch screen (QTBUG-62912)
QWindowSystemInterface::handleEnterLeaveEvent(nullptr, window);
} }
// Only handle down/up/update, ignore others like WM_POINTERENTER, WM_POINTERLEAVE, etc. // Only handle down/up/update, ignore others like WM_POINTERENTER, WM_POINTERLEAVE, etc.
@ -519,7 +517,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
inputIds.insert(touchPoint.id); inputIds.insert(touchPoint.id);
// Avoid getting repeated messages for this frame if there are multiple pointerIds // Avoid getting repeated messages for this frame if there are multiple pointerIds
SkipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId); QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
} }
// Some devices send touches for each finger in a different message/frame, instead of consolidating // Some devices send touches for each finger in a different message/frame, instead of consolidating
@ -565,7 +563,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
auto *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo); auto *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
RECT pRect, dRect; RECT pRect, dRect;
if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
return false; return false;
const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice; const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice;

View File

@ -42,10 +42,12 @@ static inline QDpi deviceDPI(HDC hdc)
static inline QDpi monitorDPI(HMONITOR hMonitor) static inline QDpi monitorDPI(HMONITOR hMonitor)
{ {
if (QWindowsContext::shcoredll.isValid()) {
UINT dpiX; UINT dpiX;
UINT dpiY; UINT dpiY;
if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
return QDpi(dpiX, dpiY); return QDpi(dpiX, dpiY);
}
return {0, 0}; return {0, 0};
} }
@ -564,6 +566,7 @@ QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScre
bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o) bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o)
{ {
bool result = false; bool result = false;
if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) {
ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
switch (o) { switch (o) {
case Qt::PrimaryOrientation: case Qt::PrimaryOrientation:
@ -581,15 +584,17 @@ bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o)
orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED; orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
break; break;
} }
result = SetDisplayAutoRotationPreferences(orientationPreference); result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference);
}
return result; return result;
} }
Qt::ScreenOrientation QWindowsScreen::orientationPreference() Qt::ScreenOrientation QWindowsScreen::orientationPreference()
{ {
Qt::ScreenOrientation result = Qt::PrimaryOrientation; Qt::ScreenOrientation result = Qt::PrimaryOrientation;
ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) {
if (GetDisplayAutoRotationPreferences(&orientationPreference)) { DWORD orientationPreference = ORIENTATION_PREFERENCE_NONE;
if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences(&orientationPreference)) {
switch (orientationPreference) { switch (orientationPreference) {
case ORIENTATION_PREFERENCE_NONE: case ORIENTATION_PREFERENCE_NONE:
break; break;
@ -607,6 +612,7 @@ Qt::ScreenOrientation QWindowsScreen::orientationPreference()
break; break;
} }
} }
}
return result; return result;
} }

View File

@ -42,6 +42,8 @@
#include <algorithm> #include <algorithm>
#include <VersionHelpers.h>
#if QT_CONFIG(cpp_winrt) #if QT_CONFIG(cpp_winrt)
# include <QtCore/private/qt_winrtbase_p.h> # include <QtCore/private/qt_winrtbase_p.h>
@ -232,11 +234,15 @@ static void populateLightSystemBasePalette(QPalette &result)
QColor accent = getSysColor(COLOR_HIGHLIGHT); QColor accent = getSysColor(COLOR_HIGHLIGHT);
#if QT_CONFIG(cpp_winrt) #if QT_CONFIG(cpp_winrt)
if (IsWindows10OrGreater())
{
// respect the Windows 11 accent color // respect the Windows 11 accent color
using namespace winrt::Windows::UI::ViewManagement; using namespace winrt::Windows::UI::ViewManagement;
const auto settings = UISettings(); const auto settings = UISettings();
accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
}
#endif #endif
const QColor btnFace = background; const QColor btnFace = background;
@ -271,41 +277,45 @@ static void populateLightSystemBasePalette(QPalette &result)
static void populateDarkSystemBasePalette(QPalette &result) static void populateDarkSystemBasePalette(QPalette &result)
{ {
QColor foreground = Qt::white;
QColor background = QColor(0x1E, 0x1E, 0x1E);
QColor accent = QColor(0x00, 0x55, 0xff);
QColor accentDark = accent.darker(120);
QColor accentDarker = accentDark.darker(120);
QColor accentDarkest = accentDarker.darker(120);
QColor accentLight = accent.lighter(120);
QColor accentLighter = accentLight.lighter(120);
QColor accentLightest = accentLighter.lighter(120);
QColor linkColor = Qt::blue;
#if QT_CONFIG(cpp_winrt) #if QT_CONFIG(cpp_winrt)
if (IsWindows10OrGreater())
{
using namespace winrt::Windows::UI::ViewManagement; using namespace winrt::Windows::UI::ViewManagement;
const auto settings = UISettings(); const auto settings = UISettings();
// We have to craft a palette from these colors. The settings.UIElementColor(UIElementType) API // We have to craft a palette from these colors. The settings.UIElementColor(UIElementType) API
// returns the old system colors, not the dark mode colors. If the background is black (which it // returns the old system colors, not the dark mode colors. If the background is black (which it
// usually), then override it with a dark gray instead so that we can go up and down the lightness. // usually), then override it with a dark gray instead so that we can go up and down the lightness.
const QColor foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground)); foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground));
const QColor background = [&settings]() -> QColor { background = [&settings]() -> QColor {
auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background)); auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background));
if (systemBackground == Qt::black) if (systemBackground == Qt::black)
systemBackground = QColor(0x1E, 0x1E, 0x1E); systemBackground = QColor(0x1E, 0x1E, 0x1E);
return systemBackground; return systemBackground;
}(); }();
accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
linkColor = accent;
}
const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
const QColor accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
const QColor accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
const QColor accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
const QColor accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
const QColor linkColor = accent;
#else
const QColor foreground = Qt::white;
const QColor background = QColor(0x1E, 0x1E, 0x1E);
const QColor accent = QColor(0x00, 0x55, 0xff);
const QColor accentDark = accent.darker(120);
const QColor accentDarker = accentDark.darker(120);
const QColor accentDarkest = accentDarker.darker(120);
const QColor accentLight = accent.lighter(120);
const QColor accentLighter = accentLight.lighter(120);
const QColor accentLightest = accentLighter.lighter(120);
const QColor linkColor = Qt::blue;
#endif #endif
const QColor buttonColor = background.lighter(200); const QColor buttonColor = background.lighter(200);
result.setColor(QPalette::All, QPalette::WindowText, foreground); result.setColor(QPalette::All, QPalette::WindowText, foreground);
@ -532,6 +542,8 @@ void QWindowsTheme::refreshPalettes()
m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light); m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light);
if (!light) { if (!light) {
#if QT_CONFIG(cpp_winrt) #if QT_CONFIG(cpp_winrt)
if (IsWindows10OrGreater())
{
using namespace winrt::Windows::UI::ViewManagement; using namespace winrt::Windows::UI::ViewManagement;
const auto settings = UISettings(); const auto settings = UISettings();
const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
@ -541,6 +553,18 @@ void QWindowsTheme::refreshPalettes()
m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, accent); m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, accent);
m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, accentLight); m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, accentLight);
m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, accentDarkest); m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, accentDarkest);
}
else
{
m_palettes[ButtonPalette] = new QPalette(*m_palettes[SystemPalette]);
m_palettes[ButtonPalette]->setColor(QPalette::Button, QColor(0x666666u));
const QColor checkBoxBlue(0x0078d7u);
m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]);
m_palettes[CheckBoxPalette]->setColor(QPalette::Base, checkBoxBlue);
m_palettes[CheckBoxPalette]->setColor(QPalette::Button, checkBoxBlue);
m_palettes[CheckBoxPalette]->setColor(QPalette::ButtonText, Qt::white);
}
#else #else
m_palettes[ButtonPalette] = new QPalette(*m_palettes[SystemPalette]); m_palettes[ButtonPalette] = new QPalette(*m_palettes[SystemPalette]);
m_palettes[ButtonPalette]->setColor(QPalette::Button, QColor(0x666666u)); m_palettes[ButtonPalette]->setColor(QPalette::Button, QColor(0x666666u));
@ -645,8 +669,14 @@ void QWindowsTheme::refreshFonts()
fixedFont.setStyleHint(QFont::TypeWriter); fixedFont.setStyleHint(QFont::TypeWriter);
LOGFONT lfIconTitleFont; LOGFONT lfIconTitleFont;
SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi); QFont iconTitleFont;
const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi); if (QWindowsContext::user32dll.systemParametersInfoForDpi) {
QWindowsContext::user32dll.systemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi);
iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi);
} else {
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont);
}
m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont()); m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont());
m_fonts[MenuFont] = new QFont(menuFont); m_fonts[MenuFont] = new QFont(menuFont);

View File

@ -523,11 +523,14 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo
[[nodiscard]] static inline int getResizeBorderThickness(const UINT dpi) [[nodiscard]] static inline int getResizeBorderThickness(const UINT dpi)
{ {
if (QWindowsContext::user32dll.getSystemMetricsForDpi) {
// The width of the padded border will always be 0 if DWM composition is // The width of the padded border will always be 0 if DWM composition is
// disabled, but since it will always be enabled and can't be programtically // disabled, but since it will always be enabled and can't be programtically
// disabled from Windows 8, we are safe to go. // disabled from Windows 8, we are safe to go.
return GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) return QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi)
+ GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
}
return 0;
} }
/*! /*!
@ -537,24 +540,31 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo
static QMargins invisibleMargins(QPoint screenPoint) static QMargins invisibleMargins(QPoint screenPoint)
{ {
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) {
POINT pt = {screenPoint.x(), screenPoint.y()}; POINT pt = {screenPoint.x(), screenPoint.y()};
if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) { if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
if (QWindowsContext::shcoredll.isValid()) {
UINT dpiX; UINT dpiX;
UINT dpiY; UINT dpiY;
if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
const int gap = getResizeBorderThickness(dpiX); const int gap = getResizeBorderThickness(dpiX);
return QMargins(gap, 0, gap, gap); return QMargins(gap, 0, gap, gap);
} }
} }
}
}
return QMargins(); return QMargins();
} }
[[nodiscard]] static inline QMargins invisibleMargins(const HWND hwnd) [[nodiscard]] static inline QMargins invisibleMargins(const HWND hwnd)
{ {
const UINT dpi = GetDpiForWindow(hwnd); if (QWindowsContext::user32dll.getDpiForWindow) {
const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd);
const int gap = getResizeBorderThickness(dpi); const int gap = getResizeBorderThickness(dpi);
return QMargins(gap, 0, gap, gap); return QMargins(gap, 0, gap, gap);
} }
return QMargins();
}
/*! /*!
\class WindowCreationData \class WindowCreationData
@ -1038,7 +1048,8 @@ QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD styl
return {}; return {};
RECT rect = {0,0,0,0}; RECT rect = {0,0,0,0};
style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE) if (QWindowsContext::user32dll.adjustWindowRectEx &&
QWindowsContext::user32dll.adjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE)
qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__);
const QMargins result(qAbs(rect.left), qAbs(rect.top), const QMargins result(qAbs(rect.left), qAbs(rect.top),
qAbs(rect.right), qAbs(rect.bottom)); qAbs(rect.right), qAbs(rect.bottom));
@ -1062,7 +1073,8 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyl
return {}; return {};
RECT rect = {0,0,0,0}; RECT rect = {0,0,0,0};
style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
if (AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) { if (QWindowsContext::user32dll.adjustWindowRectExForDpi &&
QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) {
qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__); qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__);
} }
const QMargins result(qAbs(rect.left), qAbs(rect.top), const QMargins result(qAbs(rect.left), qAbs(rect.top),
@ -1556,7 +1568,8 @@ void QWindowsWindow::initialize()
QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry); QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry);
} }
} }
QWindowsWindow::setSavedDpi(GetDpiForWindow(handle())); QWindowsWindow::setSavedDpi(QWindowsContext::user32dll.getDpiForWindow ?
QWindowsContext::user32dll.getDpiForWindow(handle()) : 96);
} }
QSurfaceFormat QWindowsWindow::format() const QSurfaceFormat QWindowsWindow::format() const
@ -2003,15 +2016,14 @@ void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *
void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam) void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
{ {
const UINT dpi = HIWORD(wParam); if (QWindowsContext::user32dll.getDpiForWindow) {
const qreal scale = dpiRelativeScale(dpi); const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd);
const qreal scale = qreal(dpi) / qreal(savedDpi());
setSavedDpi(dpi); setSavedDpi(dpi);
// Send screen change first, so that the new screen is set during any following resize // Send screen change first, so that the new screen is set during any following resize
checkForScreenChanged(QWindowsWindow::FromDpiChange); checkForScreenChanged(QWindowsWindow::FromDpiChange);
if (!IsZoomed(hwnd))
m_data.restoreGeometry.setSize(m_data.restoreGeometry.size() * scale);
// We get WM_DPICHANGED in one of two situations: // We get WM_DPICHANGED in one of two situations:
// //
// 1. The DPI change is a "spontaneous" DPI change as a result of e.g. // 1. The DPI change is a "spontaneous" DPI change as a result of e.g.
@ -2045,10 +2057,12 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
// a new scale factor. // a new scale factor.
setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window())); setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
} }
}
void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd) void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd)
{ {
const UINT dpi = GetDpiForWindow(hwnd); if (QWindowsContext::user32dll.getDpiForWindow) {
const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd);
const qreal scale = dpiRelativeScale(dpi); const qreal scale = dpiRelativeScale(dpi);
setSavedDpi(dpi); setSavedDpi(dpi);
@ -2060,6 +2074,7 @@ void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd)
QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale); QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale);
setGeometry(scaledGeometry); setGeometry(scaledGeometry);
} }
}
static QRect normalFrameGeometry(HWND hwnd) static QRect normalFrameGeometry(HWND hwnd)
{ {