diff --git a/src/corelib/io/qstandardpaths_win.cpp b/src/corelib/io/qstandardpaths_win.cpp index 13b8fe22..9e2607c3 100644 --- a/src/corelib/io/qstandardpaths_win.cpp +++ b/src/corelib/io/qstandardpaths_win.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -60,20 +61,26 @@ static inline void appendTestMode(QString &path) static bool isProcessLowIntegrity() { + if (!IsWindows8OrGreater()) + return false; + // same as GetCurrentProcessToken() const auto process_token = HANDLE(quintptr(-4)); QVarLengthArray token_info_buf(256); auto* token_info = reinterpret_cast(token_info_buf.data()); 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 token_info_buf.resize(token_info_length); token_info = reinterpret_cast(token_info_buf.data()); if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_info, token_info_length, &token_info_length)) return false; // assume "normal" process } - + else + return false; + // The GetSidSubAuthorityCount return-code is undefined on failure, so // there's no point in checking before dereferencing DWORD integrity_level = *GetSidSubAuthority(token_info->Label.Sid, *GetSidSubAuthorityCount(token_info->Label.Sid) - 1); diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index 1c54c975..26467f34 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -354,10 +354,15 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) 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 - ok = SetCoalescableTimer(internalHwnd, t->timerId, interval, nullptr, tolerance); + ok = mySetCoalescableTimerFunc(internalHwnd, t->timerId, interval, nullptr, tolerance); } + if (!ok) ok = SetTimer(internalHwnd, t->timerId, interval, nullptr); diff --git a/src/corelib/kernel/qfunctions_win.cpp b/src/corelib/kernel/qfunctions_win.cpp index d5ce3e58..70b67ea0 100644 --- a/src/corelib/kernel/qfunctions_win.cpp +++ b/src/corelib/kernel/qfunctions_win.cpp @@ -43,23 +43,33 @@ QComHelper::~QComHelper() */ 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) - static const bool hasPackageIdentity = []() { - UINT32 length = 0; - switch (const auto result = GetCurrentPackageFullName(&length, nullptr)) { - case ERROR_INSUFFICIENT_BUFFER: - return true; - case APPMODEL_ERROR_NO_PACKAGE: - return false; - default: - qWarning("Failed to resolve package identity (error code %ld)", result); - return false; - } - }(); - return hasPackageIdentity; + + static const bool hasPackageIdentity = []() { + UINT32 length = 0; + switch (const auto result = myGetCurrentPackageFullName(&length, nullptr)) { + case ERROR_INSUFFICIENT_BUFFER: + return true; + case APPMODEL_ERROR_NO_PACKAGE: + return false; + default: + qWarning("Failed to resolve package identity (error code %ld)", result); + return false; + } + }(); + return hasPackageIdentity; #else - return false; + return false; #endif + } + + return false; } QT_END_NAMESPACE diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h index 48f03f5e..efb14851 100644 --- a/src/corelib/thread/qfutex_p.h +++ b/src/corelib/thread/qfutex_p.h @@ -107,39 +107,6 @@ namespace QtLinuxFutex { } namespace QtFutex = QtLinuxFutex; QT_END_NAMESPACE - -#elif defined(Q_OS_WIN) -# include - -QT_BEGIN_NAMESPACE -namespace QtWindowsFutex { -#define QT_ALWAYS_USE_FUTEX -constexpr inline bool futexAvailable() { return true; } - -template -inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue) -{ - QtTsan::futexRelease(&futex); - WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), INFINITE); - QtTsan::futexAcquire(&futex); -} -template -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 inline void futexWakeAll(Atomic &futex) -{ - WakeByAddressAll(&futex); -} -template inline void futexWakeOne(Atomic &futex) -{ - WakeByAddressSingle(&futex); -} -} -namespace QtFutex = QtWindowsFutex; -QT_END_NAMESPACE #else QT_BEGIN_NAMESPACE diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index 7b4aac95..ae1a3a73 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -869,6 +869,8 @@ QT_END_NAMESPACE // nothing #elif defined(Q_OS_MAC) # include "qmutex_mac.cpp" +#elif defined(Q_OS_WIN) +# include "qmutex_win.cpp" #else # include "qmutex_unix.cpp" #endif diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 565de31c..21f3fb0d 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -91,6 +91,8 @@ public: bool wakeup; pthread_mutex_t mutex; pthread_cond_t cond; +#elif defined(Q_OS_WIN) + Qt::HANDLE event; #endif }; diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp new file mode 100644 index 00000000..87d0289f --- /dev/null +++ b/src/corelib/thread/qmutex_win.cpp @@ -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 +#include "qmutex_p.h" +#include + +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 \ No newline at end of file diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 95d7ff14..22c46928 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -13,6 +13,8 @@ #include +#include + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -131,13 +133,22 @@ inline Int aligned(Int v, Int byteAlign) 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; - const HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast(&result)); - if (FAILED(hr)) { - qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", - qPrintable(QSystemError::windowsComString(hr))); - result = nullptr; + + if (myCreateDXGIFactory2) + { + const HRESULT hr = myCreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast(&result)); + if (FAILED(hr)) { + qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", + qPrintable(QSystemError::windowsComString(hr))); + result = nullptr; + } } + return result; } @@ -4864,20 +4875,38 @@ bool QRhiD3D11::ensureDirectCompositionDevice() if (dcompDevice) return true; - qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)"); + typedef HRESULT(WINAPI* DCompositionCreateDeviceFunc) (IDXGIDevice *dxgiDevice, REFIID iid, void **dcompositionDevice); + static DCompositionCreateDeviceFunc myDCompositionCreateDevice = + (DCompositionCreateDeviceFunc)::GetProcAddress(::GetModuleHandle(L"dcomp"), "DCompositionCreateDevice"); - HRESULT hr = DCompositionCreateDevice(nullptr, __uuidof(IDCompositionDevice), reinterpret_cast(&dcompDevice)); - if (FAILED(hr)) { - qWarning("Failed to Direct Composition device: %s", - qPrintable(QSystemError::windowsComString(hr))); - return false; + if (myDCompositionCreateDevice) + { + qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)"); + + HRESULT hr = myDCompositionCreateDevice(nullptr, __uuidof(IDCompositionDevice), reinterpret_cast(&dcompDevice)); + if (FAILED(hr)) { + qWarning("Failed to Direct Composition device: %s", + qPrintable(QSystemError::windowsComString(hr))); + return false; + } + + return true; } - - return true; + + return false; } bool QD3D11SwapChain::createOrResize() { + if (IsWindows10OrGreater()) + { + // continue + } + else + { + return 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. @@ -5167,6 +5196,295 @@ bool QD3D11SwapChain::createOrResize() 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(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(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(&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(&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, ×tampDisjointQuery[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, ×tampQuery[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() { framesLeft = framesToActivate; diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index d53f7f80..e1d7559f 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -569,6 +569,8 @@ struct QD3D11SwapChain : public QRhiSwapChain QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; + bool createOrResizeWin7(); + void releaseBuffers(); bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc, ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const; @@ -740,6 +742,8 @@ public: IDXGIFactory1 *dxgiFactory = nullptr; IDCompositionDevice *dcompDevice = nullptr; bool supportsAllowTearing = false; + bool supportsFlipSwapchain = false; // win7 + bool forceFlipDiscard = false; // win7 bool deviceLost = false; QRhiD3D11NativeHandles nativeHandlesStruct; QRhiDriverInfo driverInfoStruct; diff --git a/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/src/gui/text/windows/qwindowsfontdatabasebase.cpp index f9b36b48..18155cb0 100644 --- a/src/gui/text/windows/qwindowsfontdatabasebase.cpp +++ b/src/gui/text/windows/qwindowsfontdatabasebase.cpp @@ -685,7 +685,16 @@ QFont QWindowsFontDatabaseBase::systemDefaultFont() // Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610) NONCLIENTMETRICS 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); qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont; return systemFont; diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp index 026e81cb..cbe4fa89 100644 --- a/src/plugins/platforms/windows/qwin10helpers.cpp +++ b/src/plugins/platforms/windows/qwin10helpers.cpp @@ -58,43 +58,73 @@ public: } // namespace ABI #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 // Return tablet mode, note: Does not work for GetDesktopWindow(). bool qt_windowsIsTabletMode(HWND hwnd) { - bool result = false; + 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); - const wchar_t uiViewSettingsId[] = L"Windows.UI.ViewManagement.UIViewSettings"; - HSTRING_HEADER uiViewSettingsIdRefHeader; - HSTRING uiViewSettingsIdHs = nullptr; - const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1); - if (FAILED(WindowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs))) - return false; + WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)WIN_LoadComBaseFunction("WindowsCreateStringReference"); + RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)WIN_LoadComBaseFunction("RoGetActivationFactory"); - IUIViewSettingsInterop *uiViewSettingsInterop = nullptr; - // __uuidof(IUIViewSettingsInterop); - const GUID uiViewSettingsInteropRefId = {0x3694dbf9, 0x8f68, 0x44be,{0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}}; + if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) + { + bool result = false; - HRESULT hr = RoGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId, - reinterpret_cast(&uiViewSettingsInterop)); - if (FAILED(hr)) - return false; + const wchar_t uiViewSettingsId[] = L"Windows.UI.ViewManagement.UIViewSettings"; + HSTRING_HEADER uiViewSettingsIdRefHeader; + HSTRING uiViewSettingsIdHs = nullptr; + const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1); + if (FAILED(WindowsCreateStringReferenceFunc(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs))) + return false; - // __uuidof(ABI::Windows::UI::ViewManagement::IUIViewSettings); - const GUID uiViewSettingsRefId = {0xc63657f6, 0x8850, 0x470d,{0x88, 0xf8, 0x45, 0x5e, 0x16, 0xea, 0x2c, 0x26}}; - ABI::Windows::UI::ViewManagement::IUIViewSettings *viewSettings = nullptr; - hr = uiViewSettingsInterop->GetForWindow(hwnd, uiViewSettingsRefId, - reinterpret_cast(&viewSettings)); - if (SUCCEEDED(hr)) { - ABI::Windows::UI::ViewManagement::UserInteractionMode currentMode; - hr = viewSettings->get_UserInteractionMode(¤tMode); - if (SUCCEEDED(hr)) - result = currentMode == 1; // Touch, 1 - viewSettings->Release(); + IUIViewSettingsInterop *uiViewSettingsInterop = nullptr; + // __uuidof(IUIViewSettingsInterop); + const GUID uiViewSettingsInteropRefId = {0x3694dbf9, 0x8f68, 0x44be,{0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}}; + + HRESULT hr = RoGetActivationFactoryFunc(uiViewSettingsIdHs, uiViewSettingsInteropRefId, + reinterpret_cast(&uiViewSettingsInterop)); + if (FAILED(hr)) + return false; + + // __uuidof(ABI::Windows::UI::ViewManagement::IUIViewSettings); + const GUID uiViewSettingsRefId = {0xc63657f6, 0x8850, 0x470d,{0x88, 0xf8, 0x45, 0x5e, 0x16, 0xea, 0x2c, 0x26}}; + ABI::Windows::UI::ViewManagement::IUIViewSettings *viewSettings = nullptr; + hr = uiViewSettingsInterop->GetForWindow(hwnd, uiViewSettingsRefId, + reinterpret_cast(&viewSettings)); + if (SUCCEEDED(hr)) { + ABI::Windows::UI::ViewManagement::UserInteractionMode currentMode; + hr = viewSettings->get_UserInteractionMode(¤tMode); + if (SUCCEEDED(hr)) + result = currentMode == 1; // Touch, 1 + viewSettings->Release(); + } + uiViewSettingsInterop->Release(); + return result; } - uiViewSettingsInterop->Release(); - return result; + + return false; } -QT_END_NAMESPACE +QT_END_NAMESPACE \ No newline at end of file diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 9b72efae..59ce1144 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -120,15 +121,17 @@ static inline bool sessionManagerInteractionBlocked() { return false; } static inline int windowDpiAwareness(HWND hwnd) { - return static_cast(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 static bool enableNonClientDpiScaling(HWND hwnd) { bool result = false; - if (windowDpiAwareness(hwnd) == 2) { - result = EnableNonClientDpiScaling(hwnd) != FALSE; + if (QWindowsContext::user32dll.enableNonClientDpiScaling && windowDpiAwareness(hwnd) == 2) { + result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE; if (!result) { const DWORD errorCode = GetLastError(); qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)", @@ -138,6 +141,77 @@ static bool enableNonClientDpiScaling(HWND hwnd) 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; /*! @@ -181,6 +255,9 @@ bool QWindowsContextPrivate::m_v2DpiAware = false; QWindowsContextPrivate::QWindowsContextPrivate() : m_oleInitializeResult(OleInitialize(nullptr)) { + QWindowsContext::user32dll.init(); + QWindowsContext::shcoredll.init(); + if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice()) m_systemInfo |= QWindowsContext::SI_SupportsTouch; m_displayContext = GetDC(nullptr); @@ -299,6 +376,12 @@ bool QWindowsContext::initPointer(unsigned integrationOptions) if (integrationOptions & QWindowsIntegration::DontUseWMPointer) return false; + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) + return false; + + if (!QWindowsContext::user32dll.supportsPointerApi()) + return false; + d->m_systemInfo |= QWindowsContext::SI_SupportsPointer; return true; } @@ -368,8 +451,9 @@ void QWindowsContext::setDetectAltGrModifier(bool a) int QWindowsContext::processDpiAwareness() { - PROCESS_DPI_AWARENESS result; - if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) { + int result; + if (QWindowsContext::shcoredll.getProcessDpiAwareness + && SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) { return static_cast(result); } return -1; @@ -381,34 +465,46 @@ void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiA if (processDpiAwareness() == int(dpiAwareness)) return; - const HRESULT hr = SetProcessDpiAwareness(static_cast(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"; + if (QWindowsContext::shcoredll.isValid()) { + const HRESULT hr = QWindowsContext::shcoredll.setProcessDpiAwareness(static_cast(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"; + } + } else { + if (dpiAwareness != QtWindows::ProcessDpiUnaware && QWindowsContext::user32dll.setProcessDPIAware) { + if (!QWindowsContext::user32dll.setProcessDPIAware()) + qErrnoWarning("SetProcessDPIAware() failed"); + } } } bool QWindowsContext::setProcessDpiV2Awareness() { qCDebug(lcQpaWindow) << __FUNCTION__; - auto dpiContext = GetThreadDpiAwarenessContext(); - if (AreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) - 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)"; - return false; + if (QWindowsContext::user32dll.getThreadDpiAwarenessContext) { + auto dpiContext = QWindowsContext::user32dll.getThreadDpiAwarenessContext(); + if (QWindowsContext::user32dll.areDpiAwarenessContextsEqual && + QWindowsContext::user32dll.areDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + return true; + } + + if (QWindowsContext::user32dll.setProcessDpiAwarenessContext) { + const BOOL ok = QWindowsContext::user32dll.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)"; + return false; + } + QWindowsContextPrivate::m_v2DpiAware = true; } - QWindowsContextPrivate::m_v2DpiAware = true; return true; } @@ -837,8 +933,8 @@ void QWindowsContext::forceNcCalcSize(HWND hwnd) bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi) { - const BOOL result = dpi != 0 - ? SystemParametersInfoForDpi(action, param, out, 0, dpi) + const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0 + ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi) : SystemParametersInfo(action, param, out, 0); return result == TRUE; } diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 6b3010f3..de2da558 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -44,6 +44,94 @@ class QPoint; class QKeyEvent; 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,int *); + typedef HRESULT (WINAPI *SetProcessDpiAwareness)(int); + typedef HRESULT (WINAPI *GetDpiForMonitor)(HMONITOR,int,UINT *,UINT *); + + GetProcessDpiAwareness getProcessDpiAwareness = nullptr; + SetProcessDpiAwareness setProcessDpiAwareness = nullptr; + GetDpiForMonitor getDpiForMonitor = nullptr; +}; + class QWindowsContext { Q_DISABLE_COPY_MOVE(QWindowsContext) @@ -137,6 +225,9 @@ public: QWindowsScreenManager &screenManager(); QWindowsTabletSupport *tabletSupport() const; + static QWindowsUser32DLL user32dll; + static QWindowsShcoreDLL shcoredll; + bool asyncExpose() const; void setAsyncExpose(bool value); diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 48e0bba4..b58eccce 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -670,7 +670,7 @@ static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); POINTER_INFO pointerInfo{}; - if (!GetPointerInfo(pointerId, &pointerInfo)) + if (!QWindowsContext::user32dll.getPointerInfo || !QWindowsContext::user32dll.getPointerInfo(pointerId, &pointerInfo)) return E_FAIL; if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) { diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 3089e745..4b219c34 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -751,15 +751,28 @@ static inline QString messageKeyText(const MSG &msg) [[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd) { - const UINT dpi = GetDpiForWindow(hwnd); - const int captionHeight = GetSystemMetricsForDpi(SM_CYCAPTION, dpi); - if (IsZoomed(hwnd)) - return captionHeight; - // The frame height should also be taken into account if the window - // is not maximized. - const int frameHeight = GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) - + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); - return captionHeight + frameHeight; + if (QWindowsContext::user32dll.getDpiForWindow && QWindowsContext::user32dll.getSystemMetricsForDpi) + { + const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd); + const int captionHeight = QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYCAPTION, dpi); + if (IsZoomed(hwnd)) + return captionHeight; + // The frame height should also be taken into account if the window + // is not maximized. + const int frameHeight = QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) + + QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + 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) diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index 88f02347..6a8dfdde 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -48,7 +48,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q *result = 0; 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(); return false; } @@ -62,12 +62,12 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q } case QT_PT_TOUCH: { quint32 pointerCount = 0; - if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) { + if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) { qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); return false; } QVarLengthArray touchInfo(pointerCount); - if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) { + if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) { qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); return false; } @@ -80,10 +80,10 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q // dispatch any skipped frames if event compression is disabled by the app if (historyCount > 1 && !QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) { touchInfo.resize(pointerCount * historyCount); - if (!GetPointerFrameTouchInfoHistory(pointerId, - &historyCount, - &pointerCount, - touchInfo.data())) { + if (!QWindowsContext::user32dll.getPointerFrameTouchInfoHistory(pointerId, + &historyCount, + &pointerCount, + touchInfo.data())) { qWarning() << "GetPointerFrameTouchInfoHistory() failed:" << qt_error_string(); return false; } @@ -101,7 +101,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q } case QT_PT_PEN: { POINTER_PEN_INFO penInfo; - if (!GetPointerPenInfo(pointerId, &penInfo)) { + if (!QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo)) { qWarning() << "GetPointerPenInfo() failed:" << qt_error_string(); return false; } @@ -113,7 +113,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q || !QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents))) { QVarLengthArray penInfoHistory(historyCount); - if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) { + if (!QWindowsContext::user32dll.getPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) { qWarning() << "GetPointerPenInfoHistory() failed:" << qt_error_string(); return false; } @@ -517,7 +517,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, inputIds.insert(touchPoint.id); // 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 @@ -563,7 +563,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin auto *penInfo = static_cast(vPenInfo); RECT pRect, dRect; - if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) + if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) return false; const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice; diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 5414fc27..02cfea67 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -35,10 +35,12 @@ static inline QDpi deviceDPI(HDC hdc) static inline QDpi monitorDPI(HMONITOR hMonitor) { - UINT dpiX; - UINT dpiY; - if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) - return QDpi(dpiX, dpiY); + if (QWindowsContext::shcoredll.isValid()) { + UINT dpiX; + UINT dpiY; + if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) + return QDpi(dpiX, dpiY); + } return {0, 0}; } @@ -425,47 +427,51 @@ QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScre bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o) { bool result = false; - ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; - switch (o) { - case Qt::PrimaryOrientation: - break; - case Qt::PortraitOrientation: - orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT; - break; - case Qt::LandscapeOrientation: - orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE; - break; - case Qt::InvertedPortraitOrientation: - orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED; - break; - case Qt::InvertedLandscapeOrientation: - orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED; - break; + if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) { + ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; + switch (o) { + case Qt::PrimaryOrientation: + break; + case Qt::PortraitOrientation: + orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT; + break; + case Qt::LandscapeOrientation: + orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE; + break; + case Qt::InvertedPortraitOrientation: + orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED; + break; + case Qt::InvertedLandscapeOrientation: + orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED; + break; + } + result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference); } - result = SetDisplayAutoRotationPreferences(orientationPreference); return result; } Qt::ScreenOrientation QWindowsScreen::orientationPreference() { Qt::ScreenOrientation result = Qt::PrimaryOrientation; - ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; - if (GetDisplayAutoRotationPreferences(&orientationPreference)) { - switch (orientationPreference) { - case ORIENTATION_PREFERENCE_NONE: - break; - case ORIENTATION_PREFERENCE_LANDSCAPE: - result = Qt::LandscapeOrientation; - break; - case ORIENTATION_PREFERENCE_PORTRAIT: - result = Qt::PortraitOrientation; - break; - case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED: - result = Qt::InvertedLandscapeOrientation; - break; - case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED: - result = Qt::InvertedPortraitOrientation; - break; + if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) { + DWORD orientationPreference = ORIENTATION_PREFERENCE_NONE; + if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences(&orientationPreference)) { + switch (orientationPreference) { + case ORIENTATION_PREFERENCE_NONE: + break; + case ORIENTATION_PREFERENCE_LANDSCAPE: + result = Qt::LandscapeOrientation; + break; + case ORIENTATION_PREFERENCE_PORTRAIT: + result = Qt::PortraitOrientation; + break; + case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED: + result = Qt::InvertedLandscapeOrientation; + break; + case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED: + result = Qt::InvertedPortraitOrientation; + break; + } } } return result; diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 6fba9e55..2b64c50b 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -42,6 +42,8 @@ #include +#include + #if QT_CONFIG(cpp_winrt) # include @@ -232,11 +234,15 @@ void QWindowsTheme::populateLightSystemBasePalette(QPalette &result) QColor accent = getSysColor(COLOR_HIGHLIGHT); #if QT_CONFIG(cpp_winrt) - // respect the Windows 11 accent color - using namespace winrt::Windows::UI::ViewManagement; - const auto settings = UISettings(); - accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + if (IsWindows10OrGreater()) + { + // respect the Windows 11 accent color + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + } + #endif const QColor btnFace = background; @@ -271,41 +277,45 @@ void QWindowsTheme::populateLightSystemBasePalette(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) - using namespace winrt::Windows::UI::ViewManagement; - const auto settings = UISettings(); - // 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 - // 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)); - const QColor background = [&settings]() -> QColor { - auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background)); - if (systemBackground == Qt::black) - systemBackground = QColor(0x1E, 0x1E, 0x1E); - return systemBackground; - }(); + if (IsWindows10OrGreater()) + { + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + // 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 + // usually), then override it with a dark gray instead so that we can go up and down the lightness. + foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground)); + background = [&settings]() -> QColor { + auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background)); + if (systemBackground == Qt::black) + systemBackground = QColor(0x1E, 0x1E, 0x1E); + 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 + const QColor buttonColor = background.lighter(200); result.setColor(QPalette::All, QPalette::WindowText, foreground); @@ -562,15 +572,29 @@ void QWindowsTheme::refreshPalettes() m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light); if (!light) { #if QT_CONFIG(cpp_winrt) - using namespace winrt::Windows::UI::ViewManagement; - const auto settings = UISettings(); - const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); - const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); - const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); - m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]); - m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, accent); - m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, accentLight); - m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, accentDarkest); + if (IsWindows10OrGreater()) + { + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); + const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); + m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, accent); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, accentLight); + 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 m_palettes[ButtonPalette] = new QPalette(*m_palettes[SystemPalette]); m_palettes[ButtonPalette]->setColor(QPalette::Button, QColor(0x666666u)); @@ -637,8 +661,14 @@ void QWindowsTheme::refreshFonts() fixedFont.setStyleHint(QFont::TypeWriter); LOGFONT lfIconTitleFont; - SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi); - const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi); + QFont iconTitleFont; + 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[MenuFont] = new QFont(menuFont); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 1239a5b8..4baec139 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -516,11 +516,14 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo [[nodiscard]] static inline int getResizeBorderThickness(const UINT dpi) { - // 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 from Windows 8, we are safe to go. - return GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) - + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + if (QWindowsContext::user32dll.getSystemMetricsForDpi) { + // 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 from Windows 8, we are safe to go. + return QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + + QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + } + return 0; } /*! @@ -530,13 +533,17 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo static QMargins invisibleMargins(QPoint screenPoint) { - POINT pt = {screenPoint.x(), screenPoint.y()}; - if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) { - UINT dpiX; - UINT dpiY; - if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { - const int gap = getResizeBorderThickness(dpiX); - return QMargins(gap, 0, gap, gap); + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) { + POINT pt = {screenPoint.x(), screenPoint.y()}; + if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) { + if (QWindowsContext::shcoredll.isValid()) { + UINT dpiX; + UINT dpiY; + if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { + const int gap = getResizeBorderThickness(dpiX); + return QMargins(gap, 0, gap, gap); + } + } } } return QMargins(); @@ -544,9 +551,12 @@ static QMargins invisibleMargins(QPoint screenPoint) [[nodiscard]] static inline QMargins invisibleMargins(const HWND hwnd) { - const UINT dpi = GetDpiForWindow(hwnd); - const int gap = getResizeBorderThickness(dpi); - return QMargins(gap, 0, gap, gap); + if (QWindowsContext::user32dll.getDpiForWindow) { + const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd); + const int gap = getResizeBorderThickness(dpi); + return QMargins(gap, 0, gap, gap); + } + return QMargins(); } /*! @@ -1030,7 +1040,8 @@ QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD styl return {}; RECT rect = {0,0,0,0}; 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__); const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); @@ -1054,7 +1065,8 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyl return {}; RECT rect = {0,0,0,0}; 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__); } const QMargins result(qAbs(rect.left), qAbs(rect.top), @@ -1548,7 +1560,8 @@ void QWindowsWindow::initialize() QWindowSystemInterface::handleGeometryChange(w, obtainedGeometry); } } - QWindowsWindow::setSavedDpi(GetDpiForWindow(handle())); + QWindowsWindow::setSavedDpi(QWindowsContext::user32dll.getDpiForWindow ? + QWindowsContext::user32dll.getDpiForWindow(handle()) : 96); } QSurfaceFormat QWindowsWindow::format() const @@ -1994,59 +2007,64 @@ void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT * void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam) { - const UINT dpi = HIWORD(wParam); - setSavedDpi(dpi); + if (QWindowsContext::user32dll.getDpiForWindow) { + const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd); + const qreal scale = qreal(dpi) / qreal(savedDpi()); + setSavedDpi(dpi); - // Send screen change first, so that the new screen is set during any following resize - checkForScreenChanged(QWindowsWindow::FromDpiChange); + // Send screen change first, so that the new screen is set during any following resize + checkForScreenChanged(QWindowsWindow::FromDpiChange); - // We get WM_DPICHANGED in one of two situations: - // - // 1. The DPI change is a "spontaneous" DPI change as a result of e.g. - // the user dragging the window to a new screen. In this case Windows - // first sends WM_GETDPISCALEDSIZE, where we set the new window size, - // followed by this event where we apply the suggested window geometry - // to the native window. This will make sure the window tracks the mouse - // cursor during screen change, and also that the window size is scaled - // according to the DPI change. - // - // 2. The DPI change is a result of a setGeometry() call. In this case - // Qt has already scaled the window size for the new DPI. Further, Windows - // does not call WM_GETDPISCALEDSIZE, and also applies its own scaling - // to the already scaled window size. Since there is no need to set the - // window geometry again, and the provided geometry is incorrect, we omit - // making the SetWindowPos() call. - if (!m_inSetgeometry) { - updateFullFrameMargins(); - const auto prcNewWindow = reinterpret_cast(lParam); - SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, - prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); - // If the window does not have a frame, WM_MOVE and WM_SIZE won't be - // called which prevents the content from being scaled appropriately - // after a DPI change. - if (m_data.flags & Qt::FramelessWindowHint) - handleGeometryChange(); + // We get WM_DPICHANGED in one of two situations: + // + // 1. The DPI change is a "spontaneous" DPI change as a result of e.g. + // the user dragging the window to a new screen. In this case Windows + // first sends WM_GETDPISCALEDSIZE, where we set the new window size, + // followed by this event where we apply the suggested window geometry + // to the native window. This will make sure the window tracks the mouse + // cursor during screen change, and also that the window size is scaled + // according to the DPI change. + // + // 2. The DPI change is a result of a setGeometry() call. In this case + // Qt has already scaled the window size for the new DPI. Further, Windows + // does not call WM_GETDPISCALEDSIZE, and also applies its own scaling + // to the already scaled window size. Since there is no need to set the + // window geometry again, and the provided geometry is incorrect, we omit + // making the SetWindowPos() call. + if (!m_inSetgeometry) { + updateFullFrameMargins(); + const auto prcNewWindow = reinterpret_cast(lParam); + SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); + // If the window does not have a frame, WM_MOVE and WM_SIZE won't be + // called which prevents the content from being scaled appropriately + // after a DPI change. + if (m_data.flags & Qt::FramelessWindowHint) + handleGeometryChange(); + } + + // Re-apply mask now that we have a new DPI, which have resulted in + // a new scale factor. + setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window())); } - - // Re-apply mask now that we have a new DPI, which have resulted in - // a new scale factor. - setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window())); } void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd) { - const UINT dpi = GetDpiForWindow(hwnd); - const qreal scale = qreal(dpi) / qreal(savedDpi()); - setSavedDpi(dpi); + if (QWindowsContext::user32dll.getDpiForWindow) { + const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd); + const qreal scale = qreal(dpi) / qreal(savedDpi()); + setSavedDpi(dpi); - checkForScreenChanged(QWindowsWindow::FromDpiChange); + checkForScreenChanged(QWindowsWindow::FromDpiChange); - // Child windows do not get WM_GETDPISCALEDSIZE messages to inform - // Windows about the new size, so we need to manually scale them. - QRect currentGeometry = geometry(); - QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale); - setGeometry(scaledGeometry); + // Child windows do not get WM_GETDPISCALEDSIZE messages to inform + // Windows about the new size, so we need to manually scale them. + QRect currentGeometry = geometry(); + QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale); + setGeometry(scaledGeometry); + } } static QRect normalFrameGeometry(HWND hwnd)