diff --git a/src/corelib/kernel/qfunctions_win.cpp b/src/corelib/kernel/qfunctions_win.cpp index d5ce3e5894..b3a75ccf3a 100644 --- a/src/corelib/kernel/qfunctions_win.cpp +++ b/src/corelib/kernel/qfunctions_win.cpp @@ -43,7 +43,7 @@ QComHelper::~QComHelper() */ bool qt_win_hasPackageIdentity() { -#if defined(HAS_APPMODEL) +#if defined(HAS_APPMODEL) && 0 static const bool hasPackageIdentity = []() { UINT32 length = 0; switch (const auto result = GetCurrentPackageFullName(&length, nullptr)) { diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h index 1d14cffc89..320d5f2679 100644 --- a/src/corelib/thread/qfutex_p.h +++ b/src/corelib/thread/qfutex_p.h @@ -24,7 +24,7 @@ namespace QtDummyFutex { constexpr inline bool futexAvailable() { return false; } template inline bool futexWait(Atomic &, typename Atomic::Type, int = 0) - { Q_UNREACHABLE(); return false; } + { Q_UNREACHABLE_RETURN(false); } template inline void futexWakeOne(Atomic &) { Q_UNREACHABLE(); } template inline void futexWakeAll(Atomic &) @@ -113,30 +113,26 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE namespace QtWindowsFutex { -//#define QT_ALWAYS_USE_FUTEX -constexpr inline bool futexAvailable() { return true; } +constexpr inline bool futexAvailable() { return false; } 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) { return false; - //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; @@ -148,4 +144,4 @@ namespace QtFutex = QtDummyFutex; QT_END_NAMESPACE #endif -#endif // QFUTEX_P_H +#endif // QFUTEX_P_H \ No newline at end of file diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index ec6c711a4f..7b69a16780 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -675,6 +675,8 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC if (deadlineTimer.hasExpired()) return false; +#if defined(QT_ALWAYS_USE_FUTEX) + if (futexAvailable()) { if (Q_UNLIKELY(deadlineTimer.isForever())) { lockInternal(); @@ -701,6 +703,8 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC return false; } } + +#endif #if !defined(QT_ALWAYS_USE_FUTEX) while (!fastTryLock()) { @@ -909,7 +913,35 @@ void QMutexPrivate::derefWaiters(int value) noexcept QT_END_NAMESPACE #if defined(QT_ALWAYS_USE_FUTEX) -// nothing + // +#elif defined(Q_OS_WIN) + #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(QDeadlineTimer timeout) + { + if (timeout.isForever()) return (WaitForSingleObjectEx(event, INFINITE, FALSE) == WAIT_OBJECT_0); + auto a = timeout.remainingTime(); + if (a == 0) return false; + return (WaitForSingleObjectEx(event, a, FALSE) == WAIT_OBJECT_0); + } + + void QMutexPrivate::wakeUp() noexcept + { SetEvent(event); } + + QT_END_NAMESPACE #elif defined(Q_OS_DARWIN) # include "qmutex_mac.cpp" #else diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index aabb66fa55..4f1f503344 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -86,6 +86,8 @@ public: semaphore_t mach_semaphore; #elif defined(Q_OS_UNIX) sem_t semaphore; +#else + void* event; #endif }; diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp index b11a7051b6..a42cf12196 100644 --- a/src/corelib/thread/qsemaphore.cpp +++ b/src/corelib/thread/qsemaphore.cpp @@ -149,6 +149,7 @@ template bool futexSemaphoreTryAcquire_loop(QBasicAtomicInteger &u, quintptr curValue, quintptr nn, QDeadlineTimer timer) { + #if 0 using namespace std::chrono; int n = int(unsigned(nn)); @@ -187,6 +188,9 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger &u, quintptr curValu if (IsTimed && timer.hasExpired()) return false; } + #else + return false; + #endif } static constexpr QDeadlineTimer::ForeverConstant Expired = diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 667d73ddca..926c76d376 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -155,7 +155,7 @@ inline Int aligned(Int v, Int byteAlign) typedef HRESULT (__stdcall * Annoying_f)(UINT Flags, REFIID riid, void **ppFactory) ; -static IDXGIFactory1 *createDXGIFactory2() +IDXGIFactory1 *createDXGIFactory2() { IDXGIFactory1 *result = nullptr; @@ -174,6 +174,24 @@ static IDXGIFactory1 *createDXGIFactory2() return result; } +IDXGIFactory2 *createDXGIFactory2Ex(UINT factoryFlags) +{ + IDXGIFactory2 *result = nullptr; + + auto hDXGI = LoadLibraryW(L"DXGI.dll"); + auto pCreateDXGIFactory2 = reinterpret_cast(GetProcAddress(hDXGI, "CreateDXGIFactory2")); + if (!pCreateDXGIFactory2) { + return nullptr; + } + + const HRESULT hr = pCreateDXGIFactory2(factoryFlags, __uuidof(IDXGIFactory2), reinterpret_cast(&result)); + if (FAILED(hr)) { + qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", + qPrintable(QSystemError::windowsComString(hr))); + result = nullptr; + } + return result; +} bool QRhiD3D11::create(QRhi::Flags flags) { rhiFlags = flags; diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp index a508a3c1fa..4518fcb44f 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -147,6 +147,49 @@ QRhiD3D12::QRhiD3D12(QRhiD3D12InitParams *params, QRhiD3D12NativeHandles *import } } +typedef HRESULT (__stdcall * D3D12SerializeVersionedRootSignature2_f)( + const D3D12_VERSIONED_ROOT_SIGNATURE_DESC *pRootSignature, + ID3DBlob **ppBlob, + ID3DBlob **ppErrorBlob); + +HRESULT D3D12SerializeVersionedRootSignature2( + const D3D12_VERSIONED_ROOT_SIGNATURE_DESC *pRootSignature, + ID3DBlob **ppBlob, + ID3DBlob **ppErrorBlob +) +{ + auto hD3D12 = LoadLibraryW(L"D3D12.dll"); + auto pD3D12SerializeVersionedRootSignature = reinterpret_cast(GetProcAddress(hD3D12, "D3D12SerializeVersionedRootSignature")); + if (!pD3D12SerializeVersionedRootSignature) { + return (HRESULT)1; + } + + return pD3D12SerializeVersionedRootSignature(pRootSignature, ppBlob, ppErrorBlob); +} + +typedef HRESULT (__stdcall * D3D12CreateDevice_f)( + IUnknown *pAdapter, + D3D_FEATURE_LEVEL MinimumFeatureLevel, + REFIID riid, + void **ppDevice +); + +HRESULT D3D12CreateDevice2( + IUnknown *pAdapter, + D3D_FEATURE_LEVEL MinimumFeatureLevel, + REFIID riid, + void **ppDevice +) +{ + auto hD3D12 = LoadLibraryW(L"D3D12.dll"); + auto pD3D12CreateDevice = reinterpret_cast(GetProcAddress(hD3D12, "D3D12CreateDevice")); + if (!pD3D12CreateDevice) { + return E_FAIL; + } + + return pD3D12CreateDevice(pAdapter, MinimumFeatureLevel, riid, ppDevice); +} + template inline Int aligned(Int v, Int byteAlign) { @@ -172,6 +215,8 @@ static inline QD3D12RenderTargetData *rtData(QRhiRenderTarget *rt) Q_UNREACHABLE_RETURN(nullptr); } +IDXGIFactory2 *createDXGIFactory2Ex(UINT factoryFlags); + bool QRhiD3D12::create(QRhi::Flags flags) { rhiFlags = flags; @@ -179,6 +224,9 @@ bool QRhiD3D12::create(QRhi::Flags flags) UINT factoryFlags = 0; if (debugLayer) factoryFlags |= DXGI_CREATE_FACTORY_DEBUG; + + HRESULT hr {}; +#if 0 HRESULT hr = CreateDXGIFactory2(factoryFlags, __uuidof(IDXGIFactory2), reinterpret_cast(&dxgiFactory)); if (FAILED(hr)) { // retry without debug, if it was requested (to match D3D11 backend behavior) @@ -196,6 +244,17 @@ bool QRhiD3D12::create(QRhi::Flags flags) return false; } } +#endif + + this->dxgiFactory = createDXGIFactory2Ex(factoryFlags); + if (!this->dxgiFactory) { + if (debugLayer) { + this->dxgiFactory = createDXGIFactory2Ex(0); + } + if (!this->dxgiFactory) { + return false; + } + } supportsAllowTearing = false; IDXGIFactory5 *factory5 = nullptr; @@ -206,6 +265,7 @@ bool QRhiD3D12::create(QRhi::Flags flags) factory5->Release(); } +#if 0 if (debugLayer) { ID3D12Debug1 *debug = nullptr; if (SUCCEEDED(D3D12GetDebugInterface(__uuidof(ID3D12Debug1), reinterpret_cast(&debug)))) { @@ -214,6 +274,7 @@ bool QRhiD3D12::create(QRhi::Flags flags) debug->Release(); } } +#endif if (!importedDevice) { IDXGIAdapter1 *adapter; @@ -276,10 +337,10 @@ bool QRhiD3D12::create(QRhi::Flags flags) if (minimumFeatureLevel == 0) minimumFeatureLevel = MIN_FEATURE_LEVEL; - hr = D3D12CreateDevice(activeAdapter, - minimumFeatureLevel, - __uuidof(ID3D12Device2), - reinterpret_cast(&dev)); + hr = D3D12CreateDevice2(activeAdapter, + minimumFeatureLevel, + __uuidof(ID3D12Device2), + reinterpret_cast(&dev)); if (FAILED(hr)) { qWarning("Failed to create D3D12 device: %s", qPrintable(QSystemError::windowsComString(hr))); return false; @@ -2747,7 +2808,7 @@ bool QD3D12MipmapGenerator::create(QRhiD3D12 *rhiD) rsDesc.Desc_1_1.pStaticSamplers = &samplerDesc; ID3DBlob *signature = nullptr; - HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr); + HRESULT hr = D3D12SerializeVersionedRootSignature2(&rsDesc, &signature, nullptr); if (FAILED(hr)) { qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr))); return false; @@ -5007,7 +5068,7 @@ QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D1 rsDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAGS(rsFlags); ID3DBlob *signature = nullptr; - HRESULT hr = D3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr); + HRESULT hr = D3D12SerializeVersionedRootSignature2(&rsDesc, &signature, nullptr); if (FAILED(hr)) { qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr))); return {}; diff --git a/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/src/gui/text/windows/qwindowsfontdatabasebase.cpp index 9343e447ae..b857c6402a 100644 --- a/src/gui/text/windows/qwindowsfontdatabasebase.cpp +++ b/src/gui/text/windows/qwindowsfontdatabasebase.cpp @@ -17,10 +17,30 @@ # include "qwindowsfontenginedirectwrite_p.h" #endif +#include + +typedef BOOL(__stdcall *SystemParametersInfoForDpi_f)(UINT uiAction, UINT uiParam, PVOID pvParam, + UINT fWinIni, UINT dpi); + +inline BOOL SystemParametersInfoForDpi2( + UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni, + UINT dpi +) +{ + static auto pSystemParametersInfoForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "SystemParametersInfoForDpi")); + return pSystemParametersInfoForDpi + ? pSystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, dpi) + : SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni); +} + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; + // Helper classes for creating font engines directly from font data namespace { @@ -689,7 +709,7 @@ 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()); + SystemParametersInfoForDpi2(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, defaultVerticalDPI()); const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont); qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont; return systemFont; diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp index 72d5ae5c86..1ecdf98306 100644 --- a/src/network/kernel/qdnslookup_win.cpp +++ b/src/network/kernel/qdnslookup_win.cpp @@ -65,51 +65,53 @@ QT_BEGIN_NAMESPACE void QDnsLookupRunnable::query(QDnsLookupReply *reply) { - // Perform DNS query. - alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)]; - DNS_QUERY_REQUEST request = {}; - request.Version = 1; - request.QueryName = reinterpret_cast(requestName.constData()); - request.QueryType = requestType; - request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN; - + IP4_ARRAY srvList; + memset(&srvList, 0, sizeof(IP4_ARRAY)); if (!nameserver.isNull()) { - memset(dnsAddresses, 0, sizeof(dnsAddresses)); - request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY; - auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1]; - auto sa = new (addr[0].MaxSa) sockaddr; - request.pDnsServerList->MaxCount = sizeof(dnsAddresses); - request.pDnsServerList->AddrCount = 1; - // ### setting port 53 seems to cause some systems to fail - setSockaddr(sa, nameserver, port == DnsPort ? 0 : port); - request.pDnsServerList->Family = sa->sa_family; + if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) { + // The below code is referenced from: http://support.microsoft.com/kb/831226 + srvList.AddrCount = 1; + srvList.AddrArray[0] = htonl(nameserver.toIPv4Address()); + } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) { + // For supoprting IPv6 nameserver addresses, we'll need to switch + // from DnsQuey() to DnsQueryEx() as it supports passing an IPv6 + // address in the nameserver list + reply->error = QDnsLookup::ResolverError; + reply->errorString = tr("no IPv6"); + return; + } + } + + PDNS_RECORD dns_records = 0; + const DNS_STATUS status = DnsQuery_W(reinterpret_cast(requestName.constData()), requestType, DNS_QUERY_STANDARD, &srvList, &dns_records, NULL); + switch (status) { + case ERROR_SUCCESS: + break; + case DNS_ERROR_RCODE_FORMAT_ERROR: + reply->error = QDnsLookup::InvalidRequestError; + reply->errorString = tr("Server could not process query"); + return; + case DNS_ERROR_RCODE_SERVER_FAILURE: + reply->error = QDnsLookup::ServerFailureError; + reply->errorString = tr("Server failure"); + return; + case DNS_ERROR_RCODE_NAME_ERROR: + reply->error = QDnsLookup::NotFoundError; + reply->errorString = tr("Non existent domain"); + return; + case DNS_ERROR_RCODE_REFUSED: + reply->error = QDnsLookup::ServerRefusedError; + reply->errorString = tr("Server refused to answer"); + return; + default: + reply->error = QDnsLookup::InvalidReplyError; + reply->errorString = QSystemError(status, QSystemError::NativeError).toString(); + return; } - DNS_QUERY_RESULT results = {}; - results.Version = 1; - const DNS_STATUS status = DnsQueryEx(&request, &results, nullptr); - if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST) - return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1); - else if (status == ERROR_TIMEOUT) - return reply->makeTimeoutError(); - else if (status != ERROR_SUCCESS) - return reply->makeResolverSystemError(status); - - QStringView lastEncodedName; - QString cachedDecodedName; - auto extractAndCacheHost = [&](QStringView name) -> const QString & { - lastEncodedName = name; - cachedDecodedName = decodeLabel(name); - return cachedDecodedName; - }; - auto extractMaybeCachedHost = [&](QStringView name) -> const QString & { - return lastEncodedName == name ? cachedDecodedName : extractAndCacheHost(name); - }; - // Extract results. - for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) { - // warning: always assign name to the record before calling extractXxxHost() again - const QString &name = extractMaybeCachedHost(ptr->pName); + for (PDNS_RECORD ptr = dns_records; ptr != NULL; ptr = ptr->pNext) { + const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() ); if (ptr->wType == QDnsLookup::A) { QDnsHostAddressRecord record; record.d->name = name; @@ -129,12 +131,12 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply) QDnsDomainNameRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; - record.d->value = extractAndCacheHost(ptr->Data.Cname.pNameHost); + record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Cname.pNameHost).toLatin1()); reply->canonicalNameRecords.append(record); } else if (ptr->wType == QDnsLookup::MX) { QDnsMailExchangeRecord record; record.d->name = name; - record.d->exchange = decodeLabel(QStringView(ptr->Data.Mx.pNameExchange)); + record.d->exchange = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Mx.pNameExchange).toLatin1()); record.d->preference = ptr->Data.Mx.wPreference; record.d->timeToLive = ptr->dwTtl; reply->mailExchangeRecords.append(record); @@ -142,18 +144,18 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply) QDnsDomainNameRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; - record.d->value = decodeLabel(QStringView(ptr->Data.Ns.pNameHost)); + record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ns.pNameHost).toLatin1()); reply->nameServerRecords.append(record); } else if (ptr->wType == QDnsLookup::PTR) { QDnsDomainNameRecord record; record.d->name = name; record.d->timeToLive = ptr->dwTtl; - record.d->value = decodeLabel(QStringView(ptr->Data.Ptr.pNameHost)); + record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ptr.pNameHost).toLatin1()); reply->pointerRecords.append(record); } else if (ptr->wType == QDnsLookup::SRV) { QDnsServiceRecord record; record.d->name = name; - record.d->target = decodeLabel(QStringView(ptr->Data.Srv.pNameTarget)); + record.d->target = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Srv.pNameTarget).toLatin1()); record.d->port = ptr->Data.Srv.wPort; record.d->priority = ptr->Data.Srv.wPriority; record.d->timeToLive = ptr->dwTtl; @@ -164,13 +166,13 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply) record.d->name = name; record.d->timeToLive = ptr->dwTtl; for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) { - record.d->values << QStringView(ptr->Data.Txt.pStringArray[i]).toLatin1(); + record.d->values << QString::fromWCharArray((ptr->Data.Txt.pStringArray[i])).toLatin1();; } reply->textRecords.append(record); } } - DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList); + DnsRecordListFree(dns_records, DnsFreeRecordList); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp index a21e257d3c..ffe9fdb368 100644 --- a/src/plugins/platforms/windows/qwin10helpers.cpp +++ b/src/plugins/platforms/windows/qwin10helpers.cpp @@ -25,38 +25,6 @@ # include #endif -#ifndef HAS_UI_VIEW_SETTINGS_INTEROP -MIDL_INTERFACE("3694dbf9-8f68-44be-8ff5-195c98ede8a6") -IUIViewSettingsInterop : public IInspectable -{ -public: - virtual HRESULT STDMETHODCALLTYPE GetForWindow( - __RPC__in HWND hwnd, - __RPC__in REFIID riid, - __RPC__deref_out_opt void **ppv) = 0; -}; -#endif // !HAS_UI_VIEW_SETTINGS_INTEROP - -#ifndef HAS_UI_VIEW_SETTINGS -namespace ABI { -namespace Windows { -namespace UI { -namespace ViewManagement { - -enum UserInteractionMode { Mouse, Touch }; - -MIDL_INTERFACE("C63657F6-8850-470D-88F8-455E16EA2C26") -IUIViewSettings : public IInspectable -{ -public: - virtual HRESULT STDMETHODCALLTYPE get_UserInteractionMode(UserInteractionMode *value) = 0; -}; - -} // namespace ViewManagement -} // namespace UI -} // namespace Windows -} // namespace ABI -#endif // HAS_UI_VIEW_SETTINGS QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index b53ca5dd24..319d828d16 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -11,7 +11,7 @@ #include "qwindowspointerhandler.h" #include "qtwindowsglobal.h" #include "qwindowsmenu.h" -#include "qwindowsmime.h" +#include "qwindowsmimeregistry.h" #include "qwindowsinputcontext.h" #if QT_CONFIG(tabletevent) # include "qwindowstabletsupport.h" @@ -45,8 +45,11 @@ #include #include #include +#include +#include #include +#include #include #include @@ -67,7 +70,7 @@ typedef DPI_AWARENESS (__stdcall * GetAwarenessFromDpiAwarenessContext_f)(DPI_AW using namespace Qt::StringLiterals; -Q_LOGGING_CATEGORY(lcQpaWindows, "qt.qpa.windows") +Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window") Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl") Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") @@ -80,12 +83,90 @@ Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation") Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon") Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") +typedef BOOL (__stdcall *SystemParametersInfoForDpi_f)( + UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni, + UINT dpi +); + +BOOL SystemParametersInfoForDpi2( + UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni, + UINT dpi +) +{ + static auto pSystemParametersInfoForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "SystemParametersInfoForDpi")); + return pSystemParametersInfoForDpi + ? pSystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, dpi) + : SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni); +} + +bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, + unsigned dpi) +{ + return SystemParametersInfoForDpi2(action, param, out, 0, dpi) == TRUE; +} + +DPI_AWARENESS GetAwarenessFromDpiAwarenessContext2(DPI_AWARENESS_CONTEXT context) +{ + switch ((ULONG_PTR)context) + { + case 0x10: + case 0x11: + case 0x12: + case 0x80000010: + case 0x80000011: + case 0x80000012: + return (DPI_AWARENESS)((int)context & 3); + case (DPI_AWARENESS)(int)DPI_AWARENESS_CONTEXT_UNAWARE: + case (DPI_AWARENESS)(int)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE: + case (DPI_AWARENESS)(int)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE: + return DPI_AWARENESS(~(ULONG_PTR)context); + default: + return DPI_AWARENESS_INVALID; + } +} + +BOOL AreDpiAwarenessContextsEqual2(DPI_AWARENESS_CONTEXT ctx1, DPI_AWARENESS_CONTEXT ctx2) +{ + auto aware1 = GetAwarenessFromDpiAwarenessContext2(ctx1); + auto aware2 = GetAwarenessFromDpiAwarenessContext2(ctx2); + return aware1 != DPI_AWARENESS_INVALID && aware1 == aware2; +} + +[[nodiscard]] static inline QtWindows::DpiAwareness + dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT context) +{ + if (!IsValidDpiAwarenessContext(context)) + return QtWindows::DpiAwareness::Invalid; + if (AreDpiAwarenessContextsEqual2(context, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) + return QtWindows::DpiAwareness::Unaware_GdiScaled; + if (AreDpiAwarenessContextsEqual2(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + return QtWindows::DpiAwareness::PerMonitorVersion2; + if (AreDpiAwarenessContextsEqual2(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) + return QtWindows::DpiAwareness::PerMonitor; + if (AreDpiAwarenessContextsEqual2(context, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) + return QtWindows::DpiAwareness::System; + if (AreDpiAwarenessContextsEqual2(context, DPI_AWARENESS_CONTEXT_UNAWARE)) + return QtWindows::DpiAwareness::Unaware; + return QtWindows::DpiAwareness::Invalid; +} + int QWindowsContext::verbose = 0; #if !defined(LANG_SYRIAC) # define LANG_SYRIAC 0x5a #endif +class QDebug __cdecl operator<<(class QDebug e,enum QtWindows::DpiAwareness) +{ + return e; +} + static inline bool useRTL_Extensions() { // Since the IsValidLanguageGroup/IsValidLocale functions always return true on @@ -126,12 +207,12 @@ static inline bool sessionManagerInteractionBlocked() { return false; } static inline int windowDpiAwareness(HWND hwnd) { - auto pGetAwarenessFromDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetAwarenessFromDpiAwarenessContext")); + static auto pGetAwarenessFromDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetAwarenessFromDpiAwarenessContext")); if (!pGetAwarenessFromDpiAwarenessContext) { return false; } - auto pGetWindowDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetWindowDpiAwarenessContext")); + static auto pGetWindowDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetWindowDpiAwarenessContext")); if (!pGetWindowDpiAwarenessContext) { return false; } @@ -181,7 +262,7 @@ struct QWindowsContextPrivate { QWindowsKeyMapper m_keyMapper; QWindowsMouseHandler m_mouseHandler; QWindowsPointerHandler m_pointerHandler; - QWindowsMimeConverter m_mimeConverter; + QWindowsMimeRegistry m_mimeConverter; QWindowsScreenManager m_screenManager; QSharedPointer m_creationContext; #if QT_CONFIG(tabletevent) @@ -213,7 +294,7 @@ QWindowsContextPrivate::QWindowsContextPrivate() m_darkMode = QWindowsTheme::queryDarkMode(); if (FAILED(m_oleInitializeResult)) { qWarning() << "QWindowsContext: OleInitialize() failed: " - << QWindowsContext::comErrorString(m_oleInitializeResult); + << QSystemError::windowsComString(m_oleInitializeResult); } } @@ -286,7 +367,7 @@ void QWindowsContext::registerTouchWindows() { if (QGuiApplicationPrivate::is_app_running && (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) != 0) { - for (QWindowsWindow *w : qAsConst(d->m_windows)) + for (QWindowsWindow *w : std::as_const(d->m_windows)) w->registerTouchWindow(); } } @@ -378,68 +459,97 @@ void QWindowsContext::setTabletAbsoluteRange(int a) #endif } +QWindowsKeyMapper *QWindowsContext::keyMapper() const +{ + return &d->m_keyMapper; +} + void QWindowsContext::setDetectAltGrModifier(bool a) { d->m_keyMapper.setDetectAltGrModifier(a); } -int QWindowsContext::processDpiAwareness() +[[nodiscard]] static inline DPI_AWARENESS_CONTEXT + qtDpiAwarenessToDpiAwarenessContext(QtWindows::DpiAwareness dpiAwareness) { - PROCESS_DPI_AWARENESS result; + switch (dpiAwareness) { + case QtWindows::DpiAwareness::Invalid: + return nullptr; + case QtWindows::DpiAwareness::Unaware: + return DPI_AWARENESS_CONTEXT_UNAWARE; + case QtWindows::DpiAwareness::System: + return DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + case QtWindows::DpiAwareness::PerMonitor: + return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; + case QtWindows::DpiAwareness::PerMonitorVersion2: + return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + case QtWindows::DpiAwareness::Unaware_GdiScaled: + return DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED; + } + return nullptr; +} + +typedef DPI_AWARENESS_CONTEXT (__stdcall *GetThreadDpiAwarenessContext_f)(); +typedef HRESULT (__stdcall *GetProcessDpiAwarenessContext_f)(HANDLE hprocess, PROCESS_DPI_AWARENESS *value); + +QtWindows::DpiAwareness QWindowsContext::processDpiAwareness() +{ + PROCESS_DPI_AWARENESS awareness; - auto pGetDPIAwareness = reinterpret_cast(GetProcAddress(LoadLibraryW(L"api-ms-win-shcore-scaling-l1-1-1.dll"), "GetProcessDpiAwareness")); + static auto pGetDPIAwareness = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetProcessDpiAwareness")); if (!pGetDPIAwareness) { - return 0; + return (QtWindows::DpiAwareness)0; } - if (SUCCEEDED(pGetDPIAwareness(nullptr, &result))) { - return static_cast(result); + static auto pGetThreadDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetThreadDpiAwarenessContext")); + if (pGetThreadDpiAwarenessContext) { + return dpiAwarenessContextToQtDpiAwareness(pGetThreadDpiAwarenessContext()); } - return -1; + static auto pGetProcessDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetProcessDpiAwarenessContext")); + if (pGetProcessDpiAwarenessContext) { + if (SUCCEEDED(pGetProcessDpiAwarenessContext(NULL, &awareness))) { + return (QtWindows::DpiAwareness)awareness; + } + } + + if (SUCCEEDED(pGetDPIAwareness(nullptr, &awareness))) { + return (QtWindows::DpiAwareness)awareness; + } + + return (QtWindows::DpiAwareness)0; } - -void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness) +bool QWindowsContext::setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness) { - PROCESS_DPI_AWARENESS result; - - auto pSetProcessDpiAwareness = reinterpret_cast(GetProcAddress(LoadLibraryW(L"api-ms-win-shcore-scaling-l1-1-1.dll"), "SetProcessDpiAwareness")); + QWindowsContextPrivate::m_v2DpiAware = false; + + static auto pSetProcessDpiAwareness = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "SetProcessDpiAwareness")); if (!pSetProcessDpiAwareness) { - return; - } - - qCDebug(lcQpaWindows) << __FUNCTION__ << dpiAwareness; - const HRESULT hr = pSetProcessDpiAwareness(static_cast(dpiAwareness)); - // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).n - // Silence warning in that case unless debug is enabled. - if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) { - qWarning().noquote().nospace() << "SetProcessDpiAwareness(" - << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr) - << ", using " << QWindowsContext::processDpiAwareness(); - } -} - -bool QWindowsContext::setProcessDpiV2Awareness() -{ - auto pSetProcessDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "SetProcessDpiAwarenessContext")); - if (!pSetProcessDpiAwarenessContext) { return false; } - qCDebug(lcQpaWindows) << __FUNCTION__; - const BOOL ok = pSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - if (!ok) { - const HRESULT errorCode = GetLastError(); - qCWarning(lcQpaWindows).noquote().nospace() << "setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: " - << QWindowsContext::comErrorString(errorCode); - return false; + if (dpiAwareness != QtWindows::DpiAwareness::PerMonitorVersion2) { + const HRESULT hr = pSetProcessDpiAwareness(static_cast(dpiAwareness)); + if (FAILED(hr)) { + return false; + } else { + return true; + } + } + + static auto pSetProcessDpiAwarenessContext = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "SetProcessDpiAwarenessContext")); + if (pSetProcessDpiAwarenessContext) { + const BOOL ok = pSetProcessDpiAwarenessContext(qtDpiAwarenessToDpiAwarenessContext(dpiAwareness)); + if (ok) { + QWindowsContextPrivate::m_v2DpiAware = true; + } } - QWindowsContextPrivate::m_v2DpiAware = true; return true; } + bool QWindowsContext::isDarkMode() { return QWindowsContextPrivate::m_darkMode; @@ -574,7 +684,7 @@ QString QWindowsContext::registerWindowClass(const QWindow *w) if (icon) cname += "Icon"_L1; - return registerWindowClass(cname, qWindowsWndProc, style, GetSysColorBrush(COLOR_WINDOW), icon); + return registerWindowClass(cname, qWindowsWndProc, style, nullptr, icon); } QString QWindowsContext::registerWindowClass(QString cname, @@ -633,7 +743,7 @@ QString QWindowsContext::registerWindowClass(QString cname, qPrintable(cname)); d->m_registeredWindowClassNames.insert(cname); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << cname + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << ' ' << cname << " style=0x" << Qt::hex << style << Qt::dec << " brush=" << brush << " icon=" << icon << " atom=" << atom; return cname; @@ -643,7 +753,7 @@ void QWindowsContext::unregisterWindowClasses() { const auto appInstance = static_cast(GetModuleHandle(nullptr)); - for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) { + for (const QString &name : std::as_const(d->m_registeredWindowClassNames)) { if (!UnregisterClass(reinterpret_cast(name.utf16()), appInstance) && QWindowsContext::verbose) qErrnoWarning("UnregisterClass failed for '%s'", qPrintable(name)); } @@ -655,23 +765,6 @@ int QWindowsContext::screenDepth() const return GetDeviceCaps(d->m_displayContext, BITSPIXEL); } -QString QWindowsContext::windowsErrorMessage(unsigned long errorCode) -{ - QString rc = QString::fromLatin1("#%1: ").arg(errorCode); - char16_t *lpMsgBuf; - - const DWORD len = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, errorCode, 0, reinterpret_cast(&lpMsgBuf), 0, nullptr); - if (len) { - rc = QString::fromUtf16(lpMsgBuf, int(len)); - LocalFree(lpMsgBuf); - } else { - rc += QString::fromLatin1(""); - } - return rc; -} - void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w) { d->m_windows.insert(hwnd, w); @@ -834,7 +927,7 @@ bool QWindowsContext::isSessionLocked() return result; } -QWindowsMimeConverter &QWindowsContext::mimeConverter() const +QWindowsMimeRegistry &QWindowsContext::mimeConverter() const { return d->m_mimeConverter; } @@ -872,79 +965,6 @@ HWND QWindowsContext::createDummyWindow(const QString &classNameIn, HWND_MESSAGE, nullptr, static_cast(GetModuleHandle(nullptr)), nullptr); } -/*! - \brief Common COM error strings. -*/ - -QByteArray QWindowsContext::comErrorString(HRESULT hr) -{ - QByteArray result = QByteArrayLiteral("COM error 0x") - + QByteArray::number(quintptr(hr), 16) + ' '; - switch (hr) { - case S_OK: - result += QByteArrayLiteral("S_OK"); - break; - case S_FALSE: - result += QByteArrayLiteral("S_FALSE"); - break; - case E_UNEXPECTED: - result += QByteArrayLiteral("E_UNEXPECTED"); - break; - case E_ACCESSDENIED: - result += QByteArrayLiteral("E_ACCESSDENIED"); - break; - case CO_E_ALREADYINITIALIZED: - result += QByteArrayLiteral("CO_E_ALREADYINITIALIZED"); - break; - case CO_E_NOTINITIALIZED: - result += QByteArrayLiteral("CO_E_NOTINITIALIZED"); - break; - case RPC_E_CHANGED_MODE: - result += QByteArrayLiteral("RPC_E_CHANGED_MODE"); - break; - case OLE_E_WRONGCOMPOBJ: - result += QByteArrayLiteral("OLE_E_WRONGCOMPOBJ"); - break; - case CO_E_NOT_SUPPORTED: - result += QByteArrayLiteral("CO_E_NOT_SUPPORTED"); - break; - case E_NOTIMPL: - result += QByteArrayLiteral("E_NOTIMPL"); - break; - case E_INVALIDARG: - result += QByteArrayLiteral("E_INVALIDARG"); - break; - case E_NOINTERFACE: - result += QByteArrayLiteral("E_NOINTERFACE"); - break; - case E_POINTER: - result += QByteArrayLiteral("E_POINTER"); - break; - case E_HANDLE: - result += QByteArrayLiteral("E_HANDLE"); - break; - case E_ABORT: - result += QByteArrayLiteral("E_ABORT"); - break; - case E_FAIL: - result += QByteArrayLiteral("E_FAIL"); - break; - case RPC_E_WRONG_THREAD: - result += QByteArrayLiteral("RPC_E_WRONG_THREAD"); - break; - case RPC_E_THREAD_NOT_INIT: - result += QByteArrayLiteral("RPC_E_THREAD_NOT_INIT"); - break; - default: - break; - } - _com_error error(hr); - result += QByteArrayLiteral(" ("); - result += QString::fromWCharArray(error.ErrorMessage()).toUtf8(); - result += ')'; - return result; -} - void QWindowsContext::forceNcCalcSize(HWND hwnd) { // Force WM_NCCALCSIZE to adjust margin @@ -952,25 +972,6 @@ void QWindowsContext::forceNcCalcSize(HWND hwnd) SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); } -typedef BOOL (__stdcall *SystemParametersInfoForDpi_f)( - UINT uiAction, - UINT uiParam, - PVOID pvParam, - UINT fWinIni, - UINT dpi -); - -bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, - unsigned dpi) -{ - auto pSystemParametersInfoForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "SystemParametersInfoForDpi")); - - const BOOL result = dpi != 0 && pSystemParametersInfoForDpi - ? pSystemParametersInfoForDpi(action, param, out, 0, dpi) - : SystemParametersInfo(action, param, out, 0); - return result == TRUE; -} - bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out, const QPlatformScreen *screen) { @@ -1181,7 +1182,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, d->m_creationContext->applyToMinMaxInfo(reinterpret_cast(lParam)); return true; case QtWindows::ResizeEvent: - d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + d->m_creationContext->obtainedSize = QSize(LOWORD(lParam), HIWORD(lParam)); return true; case QtWindows::MoveEvent: d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); @@ -1224,7 +1225,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, if (wParam == DBT_DEVNODES_CHANGED) initTouch(); break; - case QtWindows::KeyboardLayoutChangeEvent: + case QtWindows::InputLanguageChangeEvent: if (QWindowsInputContext *wic = windowsInputContext()) wic->handleInputLanguageChanged(wParam, lParam); Q_FALLTHROUGH(); @@ -1254,7 +1255,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, platformWindow->handleMoved(); return true; case QtWindows::ResizeEvent: - platformWindow->handleResized(static_cast(wParam)); + platformWindow->handleResized(static_cast(wParam), lParam); return true; case QtWindows::QuerySizeHints: platformWindow->getSizeHints(reinterpret_cast(lParam)); @@ -1268,21 +1269,29 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::ExposeEvent: return platformWindow->handleWmPaint(hwnd, message, wParam, lParam, result); case QtWindows::NonClientMouseEvent: - if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) + if (!platformWindow->frameStrutEventsEnabled()) + break; + if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); else return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); case QtWindows::NonClientPointerEvent: - if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled()) + if (!platformWindow->frameStrutEventsEnabled()) + break; + if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result); break; case QtWindows::EnterSizeMoveEvent: platformWindow->setFlag(QWindowsWindow::ResizeMoveActive); + if (!IsZoomed(hwnd)) + platformWindow->updateRestoreGeometry(); return true; case QtWindows::ExitSizeMoveEvent: platformWindow->clearFlag(QWindowsWindow::ResizeMoveActive); platformWindow->checkForScreenChanged(); handleExitSizeMove(platformWindow->window()); + if (!IsZoomed(hwnd)) + platformWindow->updateRestoreGeometry(); return true; case QtWindows::ScrollEvent: if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer)) @@ -1328,6 +1337,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, QWindowSystemInterface::handleCloseEvent(platformWindow->window()); return true; case QtWindows::ThemeChanged: { + QWindowsThemeCache::clearThemeCache(platformWindow->handle()); // Switch from Aero to Classic changes margins. if (QWindowsTheme *theme = QWindowsTheme::instance()) theme->windowsThemeChanged(platformWindow->window()); @@ -1419,6 +1429,11 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; } #endif // !defined(QT_NO_SESSIONMANAGER) + case QtWindows::TaskbarButtonCreated: + // Apply application badge if this is the first time we have a taskbar + // button, or after Explorer restart. + QWindowsIntegration::instance()->updateApplicationBadge(); + break; default: break; } @@ -1461,7 +1476,7 @@ void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et, } if (nextActiveWindow != d->m_lastActiveWindow) { d->m_lastActiveWindow = nextActiveWindow; - QWindowSystemInterface::handleWindowActivated(nextActiveWindow, Qt::ActiveWindowFocusReason); + QWindowSystemInterface::handleFocusWindowChanged(nextActiveWindow, Qt::ActiveWindowFocusReason); } } @@ -1616,7 +1631,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); if (margins.left() >= 0) { if (platformWindow) { - qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins; + qCDebug(lcQpaWindow) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins; platformWindow->setFullFrameMargins(margins); } else { const QSharedPointer ctx = QWindowsContext::instance()->windowCreationContext(); diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 1089224433..99ecc4a7e7 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -33,7 +33,6 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen) class QWindow; class QPlatformScreen; class QPlatformWindow; -class QPlatformKeyMapper; class QWindowsMenuBar; class QWindowsScreenManager; class QWindowsTabletSupport; @@ -41,9 +40,11 @@ class QWindowsWindow; class QWindowsMimeRegistry; struct QWindowCreationContext; struct QWindowsContextPrivate; +class QWindowsKeyMapper; class QPoint; class QKeyEvent; class QPointingDevice; + class QWindowsContext { Q_DISABLE_COPY_MOVE(QWindowsContext) @@ -128,7 +129,7 @@ public: unsigned systemInfo() const; bool useRTLExtensions() const; - QPlatformKeyMapper *keyMapper() const; + QList possibleKeys(const QKeyEvent *e) const; HandleBaseWindowHash &windows(); @@ -138,6 +139,7 @@ public: QWindowsScreenManager &screenManager(); QWindowsTabletSupport *tabletSupport() const; + QWindowsKeyMapper *keyMapper() const; bool asyncExpose() const; void setAsyncExpose(bool value); diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 6543543888..74d1920e17 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -666,6 +666,7 @@ static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, return ::DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect); } +#if 0 if (msg.message == WM_POINTERUPDATE) { const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); @@ -711,7 +712,9 @@ static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, } } } - } else { + } else + #endif + { // Handle other messages. qWindowsWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 9bf19cfc36..fd4f8bbd9a 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -55,6 +55,8 @@ #include +#define QT_FEATURE_cpp_winrt 2 + #if !defined(QT_NO_OPENGL) # include "qwindowsglcontext.h" #endif @@ -243,8 +245,8 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication. if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) { m_context.setProcessDpiAwareness(dpiAwareness); - qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness - << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness(); + // qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness + // << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness(); } dpiAwarenessSet = true; } @@ -567,7 +569,8 @@ QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) co QPlatformKeyMapper *QWindowsIntegration::keyMapper() const { - return d->m_context.keyMapper(); + // TOOD: ? + return (QPlatformKeyMapper *)d->m_context.keyMapper(); } #if QT_CONFIG(clipboard) diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 22573bedae..4e14768cd3 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -60,6 +60,16 @@ QT_BEGIN_NAMESPACE static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state); +UINT GetDpiForWindow2(HWND hwnd); + +BOOL SystemParametersInfoForDpi2( + UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni, + UINT dpi); +int GetSystemMetricsForDpi2(int a, UINT dpi); + QWindowsKeyMapper::QWindowsKeyMapper() : m_useRTLExtensions(false), m_keyGrabber(nullptr) { @@ -88,17 +98,9 @@ QWindowsKeyMapper::~QWindowsKeyMapper()= default; #define VK_OEM_3 0xC0 #endif -// Get scancode from the given message -static constexpr quint32 getScancode(const MSG &msg) -{ - const auto keyFlags = HIWORD(msg.lParam); - quint32 scancode = LOBYTE(keyFlags); - // if extended-key flag is on, the scan code consists of a sequence of two bytes, - // where the first byte has a value of 0xe0. - if ((keyFlags & KF_EXTENDED) != 0) - scancode |= 0xE000; - return scancode; -} +// We not only need the scancode itself but also the extended bit of key messages. Thus we need +// the additional bit when masking the scancode. +enum { scancodeBitmask = 0x1ff }; // Key recorder ------------------------------------------------------------------------[ start ] -- struct KeyRecord { @@ -540,6 +542,33 @@ QDebug operator<<(QDebug d, const KeyboardLayoutItem &k) d << ')'; return d; } + +// Helpers to format a list of int as Qt key sequence +class formatKeys +{ +public: + explicit formatKeys(const QList &keys) : m_keys(keys) {} + +private: + friend QDebug operator<<(QDebug d, const formatKeys &keys); + const QList &m_keys; +}; + +QDebug operator<<(QDebug d, const formatKeys &k) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << '('; + for (int i =0, size = k.m_keys.size(); i < size; ++i) { + if (i) + d << ", "; + d << QKeySequence(k.m_keys.at(i)); + } + d << ')'; + return d; +} +#else // !QT_NO_DEBUG_STREAM +static int formatKeys(const QList &) { return 0; } #endif // QT_NO_DEBUG_STREAM /** @@ -637,7 +666,7 @@ void QWindowsKeyMapper::updateKeyMap(const MSG &msg) { unsigned char kbdBuffer[256]; // Will hold the complete keyboard state GetKeyboardState(kbdBuffer); - const quint32 scancode = getScancode(msg); + const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; updatePossibleKeyCodes(kbdBuffer, scancode, quint32(msg.wParam)); } @@ -730,40 +759,17 @@ static inline QString messageKeyText(const MSG &msg) return ch.isNull() ? QString() : QString(ch); } -UINT GetDpiForWindow2(HWND hwnd); - -typedef int (__stdcall *GetSystemMetricsForDpi_f)(int, UINT); -typedef int (__stdcall *GetSystemMetrics_f)(int); - [[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd) { const UINT dpi = GetDpiForWindow2(hwnd); - - auto pGetSystemMetricsForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetSystemMetricsForDpi")); - auto pGetSystemMetrics = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetSystemMetrics")); - - if (pGetSystemMetricsForDpi) - { - const int captionHeight = pGetSystemMetricsForDpi(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 = pGetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) - + pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); - return captionHeight + frameHeight; - } - else - { - const int captionHeight = pGetSystemMetrics(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 = pGetSystemMetrics(SM_CYSIZEFRAME) - + pGetSystemMetrics(SM_CXPADDEDBORDER); - return captionHeight + frameHeight; - } + const int captionHeight = GetSystemMetricsForDpi2(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 = GetSystemMetricsForDpi2(SM_CYSIZEFRAME, dpi) + + GetSystemMetricsForDpi2(SM_CXPADDEDBORDER, dpi); + return captionHeight + frameHeight; } [[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags) @@ -946,7 +952,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, m_seenAltGr = true; const UINT msgType = msg.message; - const quint32 scancode = getScancode(msg); + const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; auto vk_key = quint32(msg.wParam); quint32 nModifiers = 0; @@ -1343,7 +1349,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, return result; } -Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() const +Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() { Qt::KeyboardModifiers modifiers = Qt::NoModifier; if (GetKeyState(VK_SHIFT) < 0) @@ -1357,9 +1363,9 @@ Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() const return modifiers; } -QList QWindowsKeyMapper::possibleKeyCombinations(const QKeyEvent *e) const +QList QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const { - QList result; + QList result; const quint32 nativeVirtualKey = e->nativeVirtualKey(); @@ -1373,34 +1379,31 @@ QList QWindowsKeyMapper::possibleKeyCombinations(const QKeyEven quint32 baseKey = kbItem.qtKey[0]; Qt::KeyboardModifiers keyMods = e->modifiers(); if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) { - result << (Qt::Key_Enter | keyMods); + result << (Qt::Key_Enter | keyMods).toCombined(); return result; } - - // The base key is _always_ valid, of course - result << QKeyCombination::fromCombined(int(baseKey) + int(keyMods)); + result << int(baseKey) + int(keyMods); // The base key is _always_ valid, of course for (size_t i = 1; i < NumMods; ++i) { Qt::KeyboardModifiers neededMods = ModsTbl[i]; quint32 key = kbItem.qtKey[i]; if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) { const Qt::KeyboardModifiers missingMods = keyMods & ~neededMods; - const auto matchedKey = QKeyCombination::fromCombined(int(key) + int(missingMods)); - const auto it = std::find_if(result.begin(), result.end(), - [key](auto keyCombination) { - return keyCombination.key() == key; - }); + const int matchedKey = int(key) + int(missingMods); + const auto it = + std::find_if(result.begin(), result.end(), + [key] (int k) { return (k & ~Qt::KeyboardModifierMask) == key; }); // QTBUG-67200: Use the match with the least modifiers (prefer // Shift+9 over Alt + Shift + 9) resulting in more missing modifiers. if (it == result.end()) result << matchedKey; - else if (missingMods > it->keyboardModifiers()) + else if (missingMods > Qt::KeyboardModifiers(*it & Qt::KeyboardModifierMask)) *it = matchedKey; } } qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey=" << Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase - << e->modifiers() << kbItem << "\n returns" << result; + << e->modifiers() << kbItem << "\n returns" << formatKeys(result); return result; } diff --git a/src/plugins/platforms/windows/qwindowskeymapper.h b/src/plugins/platforms/windows/qwindowskeymapper.h index 72b2536ad7..e1b1eb5176 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.h +++ b/src/plugins/platforms/windows/qwindowskeymapper.h @@ -9,7 +9,6 @@ #include #include - QT_BEGIN_NAMESPACE class QKeyEvent; @@ -35,7 +34,7 @@ struct KeyboardLayoutItem { quint32 qtKey[NumQtKeys]; // Can by any Qt::Key_, or unicode character }; -class QWindowsKeyMapper : public QPlatformKeyMapper +class QWindowsKeyMapper : public QPlatformKeyMapper { Q_DISABLE_COPY_MOVE(QWindowsKeyMapper) public: @@ -55,8 +54,8 @@ public: QWindow *keyGrabber() const { return m_keyGrabber; } void setKeyGrabber(QWindow *w) { m_keyGrabber = w; } - Qt::KeyboardModifiers queryKeyboardModifiers() const override; - QList possibleKeyCombinations(const QKeyEvent *e) const override; + static Qt::KeyboardModifiers queryKeyboardModifiers(); + QList possibleKeys(const QKeyEvent *e) const; private: bool translateKeyEventInternal(QWindow *receiver, MSG msg, bool grab, LRESULT *lResult); diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp index 04bc0e404c..6da4853be2 100644 --- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -421,6 +421,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vTouchInfo, quint32 count) { + #if 0 Q_UNUSED(hwnd); auto *touchInfo = static_cast(vTouchInfo); @@ -547,6 +548,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, const auto *keyMapper = QWindowsContext::instance()->keyMapper(); QWindowSystemInterface::handleTouchEvent(window, m_touchDevice.data(), touchPoints, keyMapper->queryKeyboardModifiers()); + #endif return false; // Allow mouse messages to be generated. } diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index e2ab09afb9..ccac642b3a 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -44,6 +44,8 @@ #include +#define QT_FEATURE_cpp_winrt 2 + #if QT_CONFIG(cpp_winrt) # include @@ -58,6 +60,15 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; + +BOOL SystemParametersInfoForDpi2( + UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni, + UINT dpi +); + static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue) { BOOL result; @@ -768,7 +779,7 @@ void QWindowsTheme::refreshFonts() fixedFont.setStyleHint(QFont::TypeWriter); LOGFONT lfIconTitleFont; - SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi); + SystemParametersInfoForDpi2(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi); const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi); m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont()); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 9bf084f827..0b1d3e3a18 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1,9 +1,7 @@ // 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 -#ifndef WINVER -# define WINVER 0x0A00 // Enable touch functions for MinGW -#endif +#include #include "qwindowswindow.h" #include "qwindowscontext.h" @@ -29,6 +27,7 @@ #include #include #include +#include #include // QWINDOWSIZE_MAX #include #include @@ -48,13 +47,90 @@ QT_BEGIN_NAMESPACE typedef int (__stdcall *GetSystemMetricsForDpi_f)(int, UINT); -typedef int (__stdcall *GetSystemMetrics_f)(int); + typedef HRESULT(__stdcall *GetDpiForMonitor_f)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *pDpiX, UINT *pDpiY); + typedef UINT(__stdcall *GetDpiForWindow_f)(HWND hwnd); +typedef BOOL(__stdcall *AdjustWindowRectExForDpi_f)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, + DWORD dwExStyle, UINT dpi); + +typedef BOOL(__stdcall *AdjustWindowRectEx_f)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, + DWORD dwExStyle); + +typedef BOOL(__stdcall *SystemParametersInfoForDpi_f)(UINT uiAction, UINT uiParam, PVOID pvParam, + UINT fWinIni, UINT dpi); +int GetSystemMetricsForDpi2(int a, UINT dpi) +{ + auto pGetSystemMetricsForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetSystemMetricsForDpi")); + return pGetSystemMetricsForDpi ? pGetSystemMetricsForDpi(a, dpi) : GetSystemMetrics(a); +} + +HRESULT GetDpiForMonitor2(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *pDpiX, UINT *pDpiY) +{ + auto pGetDpiForMonitor = reinterpret_cast(GetProcAddress( + LoadLibraryW(L"USER32.dll"), "GetDpiForMonitor")); + if (pGetDpiForMonitor) { + return pGetDpiForMonitor(hmonitor, dpiType, pDpiX, pDpiY); + } + + auto hwnd = GetActiveWindow(); + if (HDC hDC = GetDC(hwnd)) { + auto dpiX = GetDeviceCaps(hDC, LOGPIXELSX); + auto dpiY = GetDeviceCaps(hDC, LOGPIXELSY); + ReleaseDC(hwnd, hDC); + *pDpiX = dpiX; + *pDpiY = dpiY; + return S_OK; + } else { + *pDpiX = 96; + *pDpiY = 96; + return S_OK; + } +} + +UINT GetDpiForWindow2(HWND hwnd) +{ + auto pGetDpiForWindow = reinterpret_cast( + GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetDpiForWindow")); + if (pGetDpiForWindow) { + return pGetDpiForWindow(hwnd); + } + + if (HDC hDC = GetDC(hwnd)) { + auto uRet = GetDeviceCaps(hDC, LOGPIXELSX); + ReleaseDC(hwnd, hDC); + return uRet; + } else { + return 96; + } +} + +static BOOL AdjustWindowRectExForDpi2(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, + UINT dpi) +{ + + static auto pAdjustWindowRectExForDpi = reinterpret_cast( + GetProcAddress(LoadLibraryW(L"user32.dll"), "AdjustWindowRectExForDpi")); + if (pAdjustWindowRectExForDpi) { + return pAdjustWindowRectExForDpi(lpRect, dwStyle, bMenu, dwExStyle, dpi); + } + + static auto pAdjustWindowRectEx = reinterpret_cast( + GetProcAddress(LoadLibraryW(L"user32.dll"), "AdjustWindowRectEx")); + if (pAdjustWindowRectEx) { + return pAdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle); + } + + return 0; +} + +BOOL SystemParametersInfoForDpi2(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, + UINT dpi); + using QWindowCreationContextPtr = QSharedPointer; enum { @@ -478,14 +554,21 @@ static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags) w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX); } +bool QWindowsWindow::hasNoNativeFrame(HWND hwnd, Qt::WindowFlags flags) +{ + const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE); + return (style & WS_CHILD) || (flags & Qt::FramelessWindowHint); +} + // Set the WS_EX_LAYERED flag on a HWND if required. This is required for // translucent backgrounds, not fully opaque windows and for // Qt::WindowTransparentForInput (in combination with WS_EX_TRANSPARENT). bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity) { const LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + // Native children are frameless by nature, so check for that as well. const bool needsLayered = (flags & Qt::WindowTransparentForInput) - || (hasAlpha && (flags & Qt::FramelessWindowHint)) || opacity < 1.0; + || (hasAlpha && hasNoNativeFrame(hwnd, flags)) || opacity < 1.0; const bool isLayered = (exStyle & WS_EX_LAYERED); if (needsLayered != isLayered) { if (needsLayered) { @@ -501,7 +584,7 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bo { if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) { const BYTE alpha = BYTE(qRound(255.0 * level)); - if (hasAlpha && !accelerated && (flags & Qt::FramelessWindowHint)) { + if (hasAlpha && !accelerated && QWindowsWindow::hasNoNativeFrame(hwnd, flags)) { // Non-GL windows with alpha: Use blend function to update. BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; UpdateLayeredWindow(hwnd, nullptr, nullptr, nullptr, nullptr, nullptr, 0, &blend, ULW_ALPHA); @@ -526,78 +609,17 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo [[nodiscard]] static inline int getResizeBorderThickness(const UINT dpi) { - auto pGetSystemMetricsForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetSystemMetricsForDpi")); - auto pGetSystemMetrics = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetSystemMetrics")); - - // 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. - - if (pGetSystemMetricsForDpi) - { - return pGetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) - + pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); - } - else if (pGetSystemMetrics) - { - return pGetSystemMetrics(SM_CXSIZEFRAME) - + pGetSystemMetrics(SM_CXPADDEDBORDER); - } else { - return 0; - } + return GetSystemMetricsForDpi2(SM_CXSIZEFRAME, dpi) + + GetSystemMetricsForDpi2(SM_CXPADDEDBORDER, dpi); } /*! Calculates the dimensions of the invisible borders within the window frames which only exist on Windows 10 and onwards. */ - -HRESULT GetDpiForMonitor2(HMONITOR hmonitor, - MONITOR_DPI_TYPE dpiType, - UINT *pDpiX, - UINT *pDpiY) -{ - auto pGetDpiForMonitor = reinterpret_cast(GetProcAddress(LoadLibraryW(L"api-ms-win-shcore-scaling-l1-1-1.dll"), "GetDpiForMonitor")); - if (pGetDpiForMonitor) - { - return pGetDpiForMonitor(hmonitor, dpiType, pDpiX, pDpiY); - } - - auto hwnd = GetActiveWindow(); - if (HDC hDC = GetDC(hwnd)) - { - auto dpiX = GetDeviceCaps(hDC, LOGPIXELSX); - auto dpiY = GetDeviceCaps(hDC, LOGPIXELSY); - ReleaseDC(hwnd, hDC); - *pDpiX = dpiX; - *pDpiY = dpiY; - return S_OK; - } - else - { - *pDpiX = 97; - *pDpiY = 97; - return S_OK; - } -} - -UINT GetDpiForWindow2(HWND hwnd) -{ - auto pGetDpiForWindow = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetDpiForWindow")); - if (pGetDpiForWindow) { - return pGetDpiForWindow(hwnd); - } - - if (HDC hDC = GetDC(hwnd)) - { - return GetDeviceCaps(hDC, LOGPIXELSX); - } - else - { - return 97; - } -} static QMargins invisibleMargins(QPoint screenPoint) { @@ -864,15 +886,8 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag style = WS_CHILD; } - // if (!testAttribute(Qt::WA_PaintUnclipped)) - // ### Commented out for now as it causes some problems, but - // this should be correct anyway, so dig some more into this -#ifdef Q_FLATTEN_EXPOSE - if (windowIsOpenGL(w)) // a bit incorrect since the is-opengl status may change from false to true at any time later on - style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // see SetPixelFormat -#else style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; -#endif + if (topLevel) { if ((type == Qt::Window || dialog || tool)) { if (!(flags & Qt::FramelessWindowHint)) { @@ -912,6 +927,10 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag // NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window if (flagsIn & Qt::WindowTransparentForInput) exStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT; + + // Currently only compatible with D3D surfaces, use it with care. + if (qEnvironmentVariableIntValue("QT_QPA_DISABLE_REDIRECTION_SURFACE")) + exStyle |= WS_EX_NOREDIRECTIONBITMAP; } } @@ -919,13 +938,16 @@ static inline bool shouldApplyDarkFrame(const QWindow *w) { if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint)) return false; - if (QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) - return true; + // the application has explicitly opted out of dark frames + if (!QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) + return false; + // if the application supports a dark border, and the palette is dark (window background color // is darker than the text), then turn dark-border support on, otherwise use a light border. - const QPalette defaultPalette; - return defaultPalette.color(QPalette::WindowText).lightness() - > defaultPalette.color(QPalette::Window).lightness(); + auto *dWindow = QWindowPrivate::get(const_cast(w)); + const QPalette windowPal = dWindow->windowPalette(); + return windowPal.color(QPalette::WindowText).lightness() + > windowPal.color(QPalette::Window).lightness(); } QWindowsWindowData @@ -956,11 +978,12 @@ QWindowsWindowData style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); - const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME)); + const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME)) + && !(result.flags & Qt::FramelessWindowHint); QMargins invMargins = topLevel && hasFrame && QWindowsGeometryHint::positionIncludesFrame(w) ? invisibleMargins(QPoint(context->frameX, context->frameY)) : QMargins(); - qCDebug(lcQpaWindows).nospace() + qCDebug(lcQpaWindow).nospace() << "CreateWindowEx: " << w << " class=" << windowClassName << " title=" << title << '\n' << *this << "\nrequested: " << rect << ": " << context->frameWidth << 'x' << context->frameHeight @@ -986,7 +1009,7 @@ QWindowsWindowData pos.x(), pos.y(), context->frameWidth, context->frameHeight, parentHandle, nullptr, appinst, nullptr); - qCDebug(lcQpaWindows).nospace() + qCDebug(lcQpaWindow).nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " << context->obtainedPos << context->obtainedSize << ' ' << context->margins; @@ -995,11 +1018,8 @@ QWindowsWindowData return result; } - if (QWindowsContext::isDarkMode() - && QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames) - && shouldApplyDarkFrame(w)) { + if (QWindowsContext::isDarkMode() && shouldApplyDarkFrame(w)) QWindowsWindow::setDarkBorderToWindow(result.hwnd, true); - } if (mirrorParentWidth != 0) { context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width() @@ -1009,6 +1029,7 @@ QWindowsWindowData QRect obtainedGeometry(context->obtainedPos, context->obtainedSize); result.geometry = obtainedGeometry; + result.restoreGeometry = frameGeometry(result.hwnd, topLevel); result.fullFrameMargins = context->margins; result.embedded = embedded; result.hasFrame = hasFrame; @@ -1029,7 +1050,7 @@ void WindowCreationData::applyWindowFlags(HWND hwnd) const const LONG_PTR newExStyle = exStyle; if (newExStyle != oldExStyle) SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << hwnd << *this + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << hwnd << *this << "\n Style from " << debugWinStyle(DWORD(oldStyle)) << "\n to " << debugWinStyle(DWORD(newStyle)) << "\n ExStyle from " << debugWinExStyle(DWORD(oldExStyle)) << " to " @@ -1093,49 +1114,6 @@ static QSize toNativeSizeConstrained(QSize dip, const QScreen *s) \internal */ -typedef BOOL (__stdcall *AdjustWindowRectExForDpi_f)( - LPRECT lpRect, - DWORD dwStyle, - BOOL bMenu, - DWORD dwExStyle, - UINT dpi -); - -typedef BOOL (__stdcall *AdjustWindowRectEx_f)( - LPRECT lpRect, - DWORD dwStyle, - BOOL bMenu, - DWORD dwExStyle -); - -static BOOL AdjustWindowRectExForDpi2(LPRECT lpRect, - DWORD dwStyle, - BOOL bMenu, - DWORD dwExStyle, - UINT dpi -) -{ - - auto pAdjustWindowRectExForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"user32.dll"), "AdjustWindowRectExForDpi")); - if (pAdjustWindowRectExForDpi) { - return pAdjustWindowRectExForDpi(lpRect, - dwStyle, - bMenu, - dwExStyle, - dpi); - } - - auto pAdjustWindowRectEx = reinterpret_cast(GetProcAddress(LoadLibraryW(L"user32.dll"), "AdjustWindowRectEx")); - if (pAdjustWindowRectEx) { - return pAdjustWindowRectEx(lpRect, - dwStyle, - bMenu, - dwExStyle); - } - - return 0; -} - QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD style, DWORD exStyle) { if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint)) @@ -1146,7 +1124,7 @@ QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD styl qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << " style=" << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase << ' ' << rect << ' ' << result; return result; @@ -1171,7 +1149,7 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyl } const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << " style=" << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase << " dpi=" << dpi << ' ' << rect << ' ' << result; @@ -1233,7 +1211,7 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co ncp->rgrc[0].right -= customMargins.right(); ncp->rgrc[0].bottom -= customMargins.bottom(); result = nullptr; - qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2] << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy; return true; @@ -1269,7 +1247,7 @@ void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, QSize minimumSize; QSize maximumSize; frameSizeConstraints(w, screen, margins, &minimumSize, &maximumSize); - qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min=" + qCDebug(lcQpaWindow).nospace() << '>' << __FUNCTION__ << '<' << " min=" << minimumSize.width() << ',' << minimumSize.height() << " max=" << maximumSize.width() << ',' << maximumSize.height() << " margins=" << margins @@ -1284,7 +1262,7 @@ void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, mmi->ptMaxTrackSize.x = maximumSize.width(); if (maximumSize.height() < QWINDOWSIZE_MAX) mmi->ptMaxTrackSize.y = maximumSize.height(); - qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " out " << *mmi; + qCDebug(lcQpaWindow).nospace() << '<' << __FUNCTION__ << " out " << *mmi; } void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, @@ -1380,7 +1358,7 @@ void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other window void QWindowsBaseWindow::raise_sys() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); const Qt::WindowType type = window()->type(); if (type == Qt::Popup || type == Qt::SubWindow // Special case for QTBUG-63121: MDI subwindows with WindowStaysOnTopHint @@ -1391,14 +1369,14 @@ void QWindowsBaseWindow::raise_sys() void QWindowsBaseWindow::lower_sys() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); if (!(window()->flags() & Qt::WindowStaysOnTopHint)) SetWindowPos(handle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } void QWindowsBaseWindow::setWindowTitle_sys(const QString &title) { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << title; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << title; SetWindowText(handle(), reinterpret_cast(title.utf16())); } @@ -1465,7 +1443,7 @@ void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow) const HWND newParent = newParentWindow ? reinterpret_cast(newParentWindow->winId()) : HWND(nullptr); const bool isTopLevel = !newParent; const DWORD oldStyle = style(); - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << "newParent=" + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << "newParent=" << newParentWindow << newParent << "oldStyle=" << debugWinStyle(oldStyle); SetParent(m_hwnd, newParent); if (wasTopLevel != isTopLevel) { // Top level window flags need to be set/cleared manually. @@ -1483,7 +1461,7 @@ void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow) void QWindowsForeignWindow::setVisible(bool visible) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << visible; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << visible; if (visible) ShowWindow(handle(), SW_SHOWNOACTIVATE); else @@ -1520,13 +1498,16 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen * requestedGeometry(geometry), obtainedPos(geometryIn.topLeft()), obtainedSize(geometryIn.size()), - margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)), - customMargins(cm) + margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)) { // Geometry of toplevels does not consider window frames. // TODO: No concept of WA_wasMoved yet that would indicate a // CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default' // for toplevels. + + if (!(w->flags() & Qt::FramelessWindowHint)) + customMargins = cm; + if (geometry.isValid() || !qt_window_private(const_cast(w))->resizeAutomatic) { frameX = geometry.x(); @@ -1545,7 +1526,7 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen * } } - qCDebug(lcQpaWindows).nospace() + qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << ' ' << w << ' ' << geometry << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w) << " frame=" << frameWidth << 'x' << frameHeight << '+' @@ -1629,6 +1610,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) QWindowsWindow::~QWindowsWindow() { setFlag(WithinDestroy); + QWindowsThemeCache::clearThemeCache(m_data.hwnd); if (testFlag(TouchRegistered)) UnregisterTouchWindow(m_data.hwnd); destroyWindow(); @@ -1681,7 +1663,7 @@ void QWindowsWindow::fireFullExpose(bool force) void QWindowsWindow::destroyWindow() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << m_data.hwnd; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << m_data.hwnd; if (m_data.hwnd) { // Stop event dispatching before Window is destroyed. setFlag(WithinDestroy); // Clear any transient child relationships as Windows will otherwise destroy them (QTBUG-35499, QTBUG-36666) @@ -1830,7 +1812,7 @@ QWindowsWindowData void QWindowsWindow::setVisible(bool visible) { const QWindow *win = window(); - qCDebug(lcQpaWindows) << __FUNCTION__ << this << win << m_data.hwnd << visible; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << win << m_data.hwnd << visible; if (m_data.hwnd) { if (visible) { show_sys(); @@ -2010,7 +1992,7 @@ void QWindowsWindow::show_sys() const void QWindowsWindow::setParent(const QPlatformWindow *newParent) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << newParent; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << newParent; if (m_data.hwnd) setParent_sys(newParent); @@ -2070,6 +2052,12 @@ void QWindowsWindow::handleCompositionSettingsChanged() } } +qreal QWindowsWindow::dpiRelativeScale(const UINT dpi) const +{ + return QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) / + QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi); +} + void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result) { // We want to keep QWindow's device independent size constant across the @@ -2077,11 +2065,19 @@ void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT * // by the change of DPI (e.g. 120 -> 144 = 1.2), also taking any scale // factor rounding into account. The win32 window size includes the margins; // add the margins for the new DPI to the window size. - const int dpi = int(wParam); - const qreal scale = QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) / - QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi); - const QMargins margins = QWindowsGeometryHint::frame(window(), style(), exStyle(), dpi); - const QSize windowSize = (geometry().size() * scale).grownBy(margins); + const UINT dpi = UINT(wParam); + const qreal scale = dpiRelativeScale(dpi); + const QMargins margins = fullFrameMargins(); + if (!(m_data.flags & Qt::FramelessWindowHint)) { + // We need to update the custom margins to match the current DPI, because + // we don't want our users manually hook into this message just to set a + // new margin, but here we can't call setCustomMargins() directly, that + // function will change the window geometry which conflicts with what we + // are currently doing. + m_data.customMargins *= scale; + } + + const QSize windowSize = (geometry().size() * scale).grownBy((margins * scale) + customMargins()); SIZE *size = reinterpret_cast(lParam); size->cx = windowSize.width(); size->cy = windowSize.height(); @@ -2091,11 +2087,17 @@ void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT * void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam) { const UINT dpi = HIWORD(wParam); + const qreal scale = dpiRelativeScale(dpi); setSavedDpi(dpi); + QWindowsThemeCache::clearThemeCache(hwnd); + // Send screen change first, so that the new screen is set during any following resize checkForScreenChanged(QWindowsWindow::FromDpiChange); + if (!IsZoomed(hwnd)) + m_data.restoreGeometry.setSize(m_data.restoreGeometry.size() * scale); + // We get WM_DPICHANGED in one of two situations: // // 1. The DPI change is a "spontaneous" DPI change as a result of e.g. @@ -2118,13 +2120,22 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM 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())); } void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd) { const UINT dpi = GetDpiForWindow2(hwnd); - const qreal scale = qreal(dpi) / qreal(savedDpi()); + const qreal scale = dpiRelativeScale(dpi); setSavedDpi(dpi); checkForScreenChanged(QWindowsWindow::FromDpiChange); @@ -2231,7 +2242,7 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) { const auto warning = msgUnableToSetGeometry(this, rectIn, m_data.geometry, - m_data.fullFrameMargins, m_data.customMargins); + fullFrameMargins(), customMargins()); qWarning("%s: %s", __FUNCTION__, qPrintable(warning)); } } else { @@ -2246,8 +2257,41 @@ void QWindowsWindow::handleMoved() handleGeometryChange(); } -void QWindowsWindow::handleResized(int wParam) +void QWindowsWindow::handleResized(int wParam, LPARAM lParam) { + /* Prevents borderless windows from covering the taskbar when maximized. */ + if ((m_data.flags.testFlag(Qt::FramelessWindowHint) + || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint))) + && IsZoomed(m_data.hwnd)) { + const int resizedWidth = LOWORD(lParam); + const int resizedHeight = HIWORD(lParam); + + const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO monitorInfo = {}; + monitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfoW(monitor, &monitorInfo); + + int correctLeft = monitorInfo.rcMonitor.left; + int correctTop = monitorInfo.rcMonitor.top; + int correctWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left; + int correctHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + + if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) { + const int borderWidth = invisibleMargins(m_data.hwnd).left(); + correctLeft -= borderWidth; + correctTop -= borderWidth; + correctWidth += borderWidth * 2; + correctHeight += borderWidth * 2; + } + + if (resizedWidth != correctWidth || resizedHeight != correctHeight) { + qCDebug(lcQpaWindow) << __FUNCTION__ << "correcting: " << resizedWidth << "x" + << resizedHeight << " -> " << correctWidth << "x" << correctHeight; + SetWindowPos(m_data.hwnd, nullptr, correctLeft, correctTop, correctWidth, correctHeight, + SWP_NOZORDER | SWP_NOACTIVATE); + } + } + switch (wParam) { case SIZE_MAXHIDE: // Some other window affected. case SIZE_MAXSHOW: @@ -2295,11 +2339,11 @@ void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode) return; // For screens with different DPI: postpone until WM_DPICHANGE // Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436). - if (mode == FromGeometryChange && currentScreen != nullptr - && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) { + const bool changingDpi = !equalDpi(QDpi(savedDpi(), savedDpi()), newScreen->logicalDpi()); + if (mode == FromGeometryChange && currentScreen != nullptr && changingDpi) return; - } - qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ + + qCDebug(lcQpaWindow).noquote().nospace() << __FUNCTION__ << ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString()) << "\"->\"" << newScreen->name() << '"'; updateFullFrameMargins(); @@ -2310,6 +2354,7 @@ void QWindowsWindow::handleGeometryChange() { const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); + updateFullFrameMargins(); QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE // which we no longer support in Qt 6) do not receive expose @@ -2327,6 +2372,9 @@ void QWindowsWindow::handleGeometryChange() if (testFlag(SynchronousGeometryChangeEvent)) QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); + if (!testFlag(ResizeMoveActive)) + updateRestoreGeometry(); + if (!wasSync) clearFlag(SynchronousGeometryChangeEvent); qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry; @@ -2337,7 +2385,7 @@ void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const const QMargins margins = fullFrameMargins(); const QRect frameGeometry = rect + margins; - qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << window() + qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << window() << "\n from " << geometry_sys() << " frame: " << margins << " to " <' << __FUNCTION__ << this << window() << "\n from: " + qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window() << "\n from: " << m_data.flags << "\n to: " << flags; const QRect oldGeometry = geometry(); if (m_data.flags != flags) { @@ -2472,7 +2520,7 @@ void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags) if (oldGeometry != newGeometry) handleGeometryChange(); - qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << "\n returns: " + qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << "\n returns: " << m_data.flags << " geometry " << oldGeometry << "->" << newGeometry; } @@ -2487,13 +2535,14 @@ QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt, QWindowsWindowData result = m_data; result.flags = creationData.flags; result.embedded = creationData.embedded; - result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME)); + result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME)) + && !(creationData.flags & Qt::FramelessWindowHint); return result; } void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << "\n from " << m_windowState << " to " << state; m_windowState = state; QWindowSystemInterface::handleWindowStateChanged(window(), state); @@ -2501,6 +2550,14 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) handleHidden(); QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now. } else { + if (state & Qt::WindowMaximized) { + WINDOWPLACEMENT windowPlacement{}; + windowPlacement.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(m_data.hwnd, &windowPlacement); + const RECT geometry = RECTfromQRect(m_data.restoreGeometry); + windowPlacement.rcNormalPosition = geometry; + SetWindowPlacement(m_data.hwnd, &windowPlacement); + } // QTBUG-17548: We send expose events when receiving WM_Paint, but for // layered windows and transient children, we won't receive any WM_Paint. QWindow *w = window(); @@ -2524,6 +2581,11 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) } } +void QWindowsWindow::updateRestoreGeometry() +{ + m_data.restoreGeometry = normalFrameGeometry(m_data.hwnd); +} + void QWindowsWindow::setWindowState(Qt::WindowStates state) { if (m_data.hwnd) { @@ -2560,7 +2622,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) const Qt::WindowStates oldState = m_windowState; if (oldState == newState) return; - qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() + qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window() << " from " << oldState << " to " << newState; const bool visible = isVisible(); @@ -2568,11 +2630,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) if (stateChange & Qt::WindowFullScreen) { if (newState & Qt::WindowFullScreen) { -#ifndef Q_FLATTEN_EXPOSE UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; -#else - UINT newStyle = WS_POPUP; -#endif // Save geometry and style to be restored when fullscreen // is turned off again, since on Windows, it is not a real // Window state but emulated by changing geometry and style. @@ -2686,12 +2744,12 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState) setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized); } } - qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << this << window() << newState; + qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << this << window() << newState; } void QWindowsWindow::setStyle(unsigned s) const { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << debugWinStyle(s); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinStyle(s); setFlag(WithinSetStyle); SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s); clearFlag(WithinSetStyle); @@ -2699,13 +2757,16 @@ void QWindowsWindow::setStyle(unsigned s) const void QWindowsWindow::setExStyle(unsigned s) const { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << debugWinExStyle(s); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinExStyle(s); SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s); } bool QWindowsWindow::windowEvent(QEvent *event) { switch (event->type()) { + case QEvent::ApplicationPaletteChange: + setDarkBorder(QWindowsContext::isDarkMode()); + break; case QEvent::WindowBlocked: // Blocked by another modal window. setEnabled(false); setFlag(BlockedByModal); @@ -2725,16 +2786,23 @@ bool QWindowsWindow::windowEvent(QEvent *event) void QWindowsWindow::propagateSizeHints() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); } bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins) { auto *windowPos = reinterpret_cast(message->lParam); + const QRect suggestedFrameGeometry(windowPos->x, windowPos->y, + windowPos->cx, windowPos->cy); + const QRect suggestedGeometry = suggestedFrameGeometry - margins; // Tell Windows to discard the entire contents of the client area, as re-using // parts of the client area would lead to jitter during resize. - windowPos->flags |= SWP_NOCOPYBITS; + // Check the suggestedGeometry against the current one to only discard during + // resize, and not a plain move. We also look for SWP_NOSIZE since that, too, + // implies an identical size, and comparing QRects wouldn't work with null cx/cy + if (!(windowPos->flags & SWP_NOSIZE) && suggestedGeometry.size() != qWindow->geometry().size()) + windowPos->flags |= SWP_NOCOPYBITS; if ((windowPos->flags & SWP_NOZORDER) == 0) { if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) { @@ -2750,9 +2818,6 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow * return false; if (windowPos->flags & SWP_NOSIZE) return false; - const QRect suggestedFrameGeometry(windowPos->x, windowPos->y, - windowPos->cx, windowPos->cy); - const QRect suggestedGeometry = suggestedFrameGeometry - margins; const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry); if (!correctedGeometryF.isValid()) return false; @@ -2774,8 +2839,10 @@ bool QWindowsWindow::handleGeometryChanging(MSG *message) const void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins) { + if (m_data.flags & Qt::FramelessWindowHint) + return; if (m_data.fullFrameMargins != newMargins) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins; m_data.fullFrameMargins = newMargins; } } @@ -2791,12 +2858,36 @@ void QWindowsWindow::updateFullFrameMargins() void QWindowsWindow::calculateFullFrameMargins() { + if (m_data.flags & Qt::FramelessWindowHint) + return; // Normally obtained from WM_NCCALCSIZE. This calculation only works // when no native menu is present. const auto systemMargins = testFlag(DisableNonClientScaling) ? QWindowsGeometryHint::frameOnPrimaryScreen(window(), m_data.hwnd) : frameMargins_sys(); - setFullFrameMargins(systemMargins + m_data.customMargins); + + // QTBUG-113736: systemMargins depends on AdjustWindowRectExForDpi. This doesn't take into + // account possible external modifications to the titlebar, as with ExtendsContentIntoTitleBar() + // from the Windows App SDK. We can fix this by comparing the WindowRect (which includes the + // frame) to the ClientRect. If a 'typical' frame is detected, i.e. only the titlebar has been + // modified, we can safely adjust the frame by deducting the bottom margin to the total Y + // difference between the two rects, to get the actual size of the titlebar and prevent + // unwanted client area slicing. + + RECT windowRect{}; + RECT clientRect{}; + GetWindowRect(handle(), &windowRect); + GetClientRect(handle(), &clientRect); + const int yDiff = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); + const bool typicalFrame = (systemMargins.left() == systemMargins.right()) + && (systemMargins.right() == systemMargins.bottom()); + + const QMargins adjustedMargins = typicalFrame ? + QMargins(systemMargins.left(), (yDiff - systemMargins.bottom()), + systemMargins.right(), systemMargins.bottom()) + : systemMargins + customMargins(); + + setFullFrameMargins(adjustedMargins); } QMargins QWindowsWindow::frameMargins() const @@ -2809,12 +2900,14 @@ QMargins QWindowsWindow::frameMargins() const QMargins QWindowsWindow::fullFrameMargins() const { + if (m_data.flags & Qt::FramelessWindowHint) + return {}; return m_data.fullFrameMargins; } void QWindowsWindow::setOpacity(qreal level) { - qCDebug(lcQpaWindows) << __FUNCTION__ << level; + qCDebug(lcQpaWindow) << __FUNCTION__ << level; if (!qFuzzyCompare(m_opacity, level)) { m_opacity = level; if (m_data.hwnd) @@ -2874,41 +2967,76 @@ void QWindowsWindow::setMask(const QRegion ®ion) void QWindowsWindow::requestActivateWindow() { - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); - // 'Active' state handling is based in focus since it needs to work for - // child windows as well. - if (m_data.hwnd) { - const DWORD currentThread = GetCurrentThreadId(); - bool attached = false; - DWORD foregroundThread = 0; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window(); - // QTBUG-14062, QTBUG-37435: Windows normally only flashes the taskbar entry - // when activating windows of inactive applications. Attach to the input of the - // currently active window while setting the foreground window to always activate - // the window when desired. - const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior(); - if (QGuiApplication::applicationState() != Qt::ApplicationActive - && activationBehavior == QWindowsApplication::AlwaysActivateWindow) { - if (const HWND foregroundWindow = GetForegroundWindow()) { - foregroundThread = GetWindowThreadProcessId(foregroundWindow, nullptr); - if (foregroundThread && foregroundThread != currentThread) - attached = AttachThreadInput(foregroundThread, currentThread, TRUE) == TRUE; - if (attached) { - if (!window()->flags().testFlag(Qt::WindowStaysOnBottomHint) - && !window()->flags().testFlag(Qt::WindowStaysOnTopHint) - && window()->type() != Qt::ToolTip) { - const UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER; - SetWindowPos(m_data.hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags); - SetWindowPos(m_data.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags); - } - } - } - } + if (!m_data.hwnd) + return; + + const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior(); + if (QGuiApplication::applicationState() == Qt::ApplicationActive + || activationBehavior != QWindowsApplication::AlwaysActivateWindow) { SetForegroundWindow(m_data.hwnd); SetFocus(m_data.hwnd); - if (attached) - AttachThreadInput(foregroundThread, currentThread, FALSE); + return; } + + // Force activate this window. The following code will bring the window to the + // foreground and activate it. If the window is hidden, it will show up. If + // the window is minimized, it will restore to the previous position. + + // But first we need some sanity checks. + if (m_data.flags & Qt::WindowStaysOnBottomHint) { + qCWarning(lcQpaWindow) << + "Windows with Qt::WindowStaysOnBottomHint can't be brought to the foreground."; + return; + } + if (m_data.flags & Qt::WindowStaysOnTopHint) { + qCWarning(lcQpaWindow) << + "Windows with Qt::WindowStaysOnTopHint will always be on the foreground."; + return; + } + if (window()->type() == Qt::ToolTip) { + qCWarning(lcQpaWindow) << "ToolTip windows should not be activated."; + return; + } + + // We need to show the window first, otherwise we won't be able to bring it to front. + if (!IsWindowVisible(m_data.hwnd)) + ShowWindow(m_data.hwnd, SW_SHOW); + + if (IsIconic(m_data.hwnd)) { + ShowWindow(m_data.hwnd, SW_RESTORE); + // When the window is restored, it will always become the foreground window. + // So return early here, we don't need the following code to bring it to front. + return; + } + + // OK, our window is not minimized, so now we will try to bring it to front manually. + const HWND oldForegroundWindow = GetForegroundWindow(); + if (!oldForegroundWindow) // It may be NULL, according to MS docs. + return; + + // First try to send a message to the current foreground window to check whether + // it is currently hanging or not. + if (SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0, + SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, nullptr) == 0) { + qCWarning(lcQpaWindow) << "The foreground window hangs, can't activate current window."; + return; + } + + const DWORD windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr); + const DWORD currentThreadId = GetCurrentThreadId(); + + AttachThreadInput(windowThreadProcessId, currentThreadId, TRUE); + const auto cleanup = qScopeGuard([windowThreadProcessId, currentThreadId](){ + AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE); + }); + + BringWindowToTop(m_data.hwnd); + + // Activate the window too. This will force us to the virtual desktop this + // window is on, if it's on another virtual desktop. + SetActiveWindow(m_data.hwnd); } bool QWindowsWindow::setKeyboardGrabEnabled(bool grab) @@ -2917,7 +3045,7 @@ bool QWindowsWindow::setKeyboardGrabEnabled(bool grab) qWarning("%s: No handle", __FUNCTION__); return false; } - qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << grab; + qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << grab; QWindowsContext *context = QWindowsContext::instance(); if (grab) { @@ -2931,7 +3059,7 @@ bool QWindowsWindow::setKeyboardGrabEnabled(bool grab) bool QWindowsWindow::setMouseGrabEnabled(bool grab) { - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << grab; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << grab; if (!m_data.hwnd) { qWarning("%s: No handle", __FUNCTION__); return false; @@ -3006,37 +3134,7 @@ void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled) void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const { QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi); - - // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the - // taskbar when maximized - if (m_data.flags.testFlag(Qt::FramelessWindowHint) - || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint))) { - if (QPlatformScreen *currentScreen = screen()) { - const QRect geometry = currentScreen->geometry(); - const QRect availableGeometry = currentScreen->availableGeometry(); - mmi->ptMaxSize.y = availableGeometry.height(); - - // Width, because you can have the taskbar on the sides too. - mmi->ptMaxSize.x = availableGeometry.width(); - - // If you have the taskbar on top, or on the left you don't want it at (0,0): - QPoint availablePositionDiff = availableGeometry.topLeft() - geometry.topLeft(); - mmi->ptMaxPosition.x = availablePositionDiff.x(); - mmi->ptMaxPosition.y = availablePositionDiff.y(); - if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) { - const int borderWidth = invisibleMargins(m_data.hwnd).left(); - mmi->ptMaxSize.x += borderWidth * 2; - mmi->ptMaxSize.y += borderWidth * 2; - mmi->ptMaxTrackSize = mmi->ptMaxSize; - mmi->ptMaxPosition.x -= borderWidth; - mmi->ptMaxPosition.y -= borderWidth; - } - } else { - qWarning("screen() returned a null screen"); - } - } - - qCDebug(lcQpaWindows) << __FUNCTION__ << window() << *mmi; + qCDebug(lcQpaWindow) << __FUNCTION__ << window() << *mmi; } bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *result) const @@ -3148,7 +3246,7 @@ void QWindowsWindow::setCursor(const CursorHandlePtr &c) } if (changed) { const bool apply = applyNewCursor(window()); - qCDebug(lcQpaWindows) << window() << __FUNCTION__ + qCDebug(lcQpaWindow) << window() << __FUNCTION__ << c->handle() << " doApply=" << apply; m_cursor = c; if (apply) @@ -3260,7 +3358,7 @@ static bool queryDarkBorder(HWND hwnd) SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result))) || SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result))); if (!ok) - qWarning("%s: Unable to retrieve dark window border setting.", __FUNCTION__); + qCWarning(lcQpaWindow, "%s: Unable to retrieve dark window border setting.", __FUNCTION__); return result == TRUE; } @@ -3271,14 +3369,18 @@ bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d) SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &darkBorder, sizeof(darkBorder))) || SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &darkBorder, sizeof(darkBorder))); if (!ok) - qWarning("%s: Unable to set dark window border.", __FUNCTION__); + qCWarning(lcQpaWindow, "%s: Unable to set %s window border.", __FUNCTION__, d ? "dark" : "light"); return ok; } void QWindowsWindow::setDarkBorder(bool d) { - if (shouldApplyDarkFrame(window()) && queryDarkBorder(m_data.hwnd) != d) - setDarkBorderToWindow(m_data.hwnd, d); + // respect explicit opt-out and incompatible palettes or styles + d = d && shouldApplyDarkFrame(window()); + if (queryDarkBorder(m_data.hwnd) == d) + return; + + setDarkBorderToWindow(m_data.hwnd, d); } QWindowsMenuBar *QWindowsWindow::menuBar() const @@ -3291,6 +3393,13 @@ void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb) m_menuBar = mb; } +QMargins QWindowsWindow::customMargins() const +{ + if (m_data.flags & Qt::FramelessWindowHint) + return {}; + return m_data.customMargins; +} + /*! \brief Sets custom margins to be added to the default margins determined by the windows style in the handling of the WM_NCCALCSIZE message. @@ -3303,6 +3412,10 @@ void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb) void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) { + if (m_data.flags & Qt::FramelessWindowHint) { + qCWarning(lcQpaWindow) << "You should not set custom margins for a frameless window."; + return; + } if (newCustomMargins != m_data.customMargins) { const QMargins oldCustomMargins = m_data.customMargins; m_data.customMargins = newCustomMargins; @@ -3311,7 +3424,7 @@ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) const QPoint topLeft = currentFrameGeometry.topLeft(); QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins; newFrame.moveTo(topLeft); - qCDebug(lcQpaWindows) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins + qCDebug(lcQpaWindow) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins << currentFrameGeometry << "->" << newFrame; SetWindowPos(m_data.hwnd, nullptr, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); } diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp index 405c213c1a..5d2ec74ed3 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -86,6 +86,36 @@ enum QSliderDirection { SlUp, SlDown, SlLeft, SlRight }; \internal */ +typedef BOOL (__stdcall *SystemParametersInfoForDpi_f)( + UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni, + UINT dpi +); + +BOOL SystemParametersInfoForDpi2( + UINT uiAction, + UINT uiParam, + PVOID pvParam, + UINT fWinIni, + UINT dpi +) +{ + auto pSystemParametersInfoForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "SystemParametersInfoForDpi")); + return dpi != 0 && pSystemParametersInfoForDpi + ? pSystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, dpi) + : SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni); +} + +typedef int (__stdcall *GetSystemMetricsForDpi_f)(int, UINT); + +int GetSystemMetricsForDpi2(int a, UINT dpi) +{ + auto pGetSystemMetricsForDpi = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.dll"), "GetSystemMetricsForDpi")); + return pGetSystemMetricsForDpi ? pGetSystemMetricsForDpi(a, dpi) : GetSystemMetrics(a); +} + QWindowsStylePrivate::QWindowsStylePrivate() = default; qreal QWindowsStylePrivate::appDevicePixelRatio() @@ -268,27 +298,27 @@ int QWindowsStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const switch (pm) { case QStyle::PM_DockWidgetFrameWidth: - return GetSystemMetricsForDpi(SM_CXFRAME, dpi); + return GetSystemMetricsForDpi2(SM_CXFRAME, dpi); case QStyle::PM_TitleBarHeight: { const int resizeBorderThickness = - GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + GetSystemMetricsForDpi2(SM_CXSIZEFRAME, dpi) + GetSystemMetricsForDpi2(SM_CXPADDEDBORDER, dpi); if (widget && (widget->windowType() == Qt::Tool)) - return GetSystemMetricsForDpi(SM_CYSMCAPTION, dpi) + resizeBorderThickness; - return GetSystemMetricsForDpi(SM_CYCAPTION, dpi) + resizeBorderThickness; + return GetSystemMetricsForDpi2(SM_CYSMCAPTION, dpi) + resizeBorderThickness; + return GetSystemMetricsForDpi2(SM_CYCAPTION, dpi) + resizeBorderThickness; } case QStyle::PM_ScrollBarExtent: { NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(NONCLIENTMETRICS); - if (SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0, dpi)) + if (SystemParametersInfoForDpi2(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0, dpi)) return qMax(ncm.iScrollHeight, ncm.iScrollWidth); } break; case QStyle::PM_MdiSubWindowFrameWidth: - return GetSystemMetricsForDpi(SM_CYFRAME, dpi); + return GetSystemMetricsForDpi2(SM_CYFRAME, dpi); default: break;