diff --git a/qtbase/src/corelib/io/qstandardpaths_win.cpp b/qtbase/src/corelib/io/qstandardpaths_win.cpp index 9e2607c3..aade9d5f 100644 --- a/qtbase/src/corelib/io/qstandardpaths_win.cpp +++ b/qtbase/src/corelib/io/qstandardpaths_win.cpp @@ -112,8 +112,10 @@ static GUID writableSpecialFolderId(QStandardPaths::StandardLocation type) FOLDERID_LocalAppData, // AppConfigLocation ("Local" path) FOLDERID_Public, // PublicShareLocation FOLDERID_Templates, // TemplatesLocation + GUID(), // StateLocation + GUID(), // GenericStateLocation }; - static_assert(sizeof(folderIds) / sizeof(folderIds[0]) == size_t(QStandardPaths::TemplatesLocation + 1)); + static_assert(sizeof(folderIds) / sizeof(folderIds[0]) == size_t(QStandardPaths::GenericStateLocation + 1)); // folders for low integrity processes static const GUID folderIds_li[] = { @@ -137,6 +139,8 @@ static GUID writableSpecialFolderId(QStandardPaths::StandardLocation type) FOLDERID_LocalAppDataLow,// AppConfigLocation ("Local" path) FOLDERID_Public, // PublicShareLocation FOLDERID_Templates, // TemplatesLocation + GUID(), // StateLocation + GUID(), // GenericStateLocation }; static_assert(sizeof(folderIds_li) == sizeof(folderIds)); @@ -191,6 +195,23 @@ QString QStandardPaths::writableLocation(StandardLocation type) result = QDir::tempPath(); break; + case StateLocation: + result = sHGetKnownFolderPath(writableSpecialFolderId(AppLocalDataLocation)); + if (!result.isEmpty()) { + appendTestMode(result); + appendOrganizationAndApp(result); + result += "/State"_L1; + } + break; + + case GenericStateLocation: + result = sHGetKnownFolderPath(writableSpecialFolderId(GenericDataLocation)); + if (!result.isEmpty()) { + appendTestMode(result); + result += "/State"_L1; + } + break; + default: result = sHGetKnownFolderPath(writableSpecialFolderId(type)); if (!result.isEmpty() && isConfigLocation(type)) { diff --git a/qtbase/src/corelib/kernel/qeventdispatcher_win.cpp b/qtbase/src/corelib/kernel/qeventdispatcher_win.cpp index 30ea3bfe..8459cd5c 100644 --- a/qtbase/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/qtbase/src/corelib/kernel/qeventdispatcher_win.cpp @@ -7,7 +7,6 @@ #include "qcoreapplication.h" #include #include "qoperatingsystemversion.h" -#include "qpair.h" #include "qset.h" #include "qsocketnotifier.h" #include "qvarlengtharray.h" @@ -136,7 +135,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read }; QSNDict *dict = sn_vec[type]; - QSockNot *sn = dict ? dict->value(wp) : 0; + QSockNot *sn = dict ? dict->value(qintptr(wp)) : 0; if (sn == nullptr) { d->postActivateSocketNotifiers(); } else { @@ -414,7 +413,7 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId) } } -void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket, long event) +void QEventDispatcherWin32Private::doWsaAsyncSelect(qintptr socket, long event) { Q_ASSERT(internalHwnd); // BoundsChecker may emit a warning for WSAAsyncSelect when event == 0 @@ -563,7 +562,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); - int sockfd = notifier->socket(); + qintptr sockfd = notifier->socket(); int type = notifier->type(); #ifndef QT_NO_DEBUG if (sockfd < 0) { @@ -587,7 +586,7 @@ void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) const char *t[] = { "Read", "Write", "Exception" }; /* Variable "socket" below is a function pointer. */ qWarning("QSocketNotifier: Multiple socket notifiers for " - "same socket %d and type %s", sockfd, t[type]); + "same socket %" PRIdQINTPTR " and type %s", sockfd, t[type]); } QSockNot *sn = new QSockNot; @@ -631,7 +630,7 @@ void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); #ifndef QT_NO_DEBUG - int sockfd = notifier->socket(); + qintptr sockfd = notifier->socket(); if (sockfd < 0) { qWarning("QEventDispatcherWin32::unregisterSocketNotifier: invalid socket identifier"); return; @@ -648,7 +647,7 @@ void QEventDispatcherWin32::doUnregisterSocketNotifier(QSocketNotifier *notifier { Q_D(QEventDispatcherWin32); int type = notifier->type(); - int sockfd = notifier->socket(); + qintptr sockfd = notifier->socket(); Q_ASSERT(sockfd >= 0); QSFDict::iterator it = d->active_fd.find(sockfd); @@ -911,3 +910,5 @@ HWND QEventDispatcherWin32::internalHwnd() } QT_END_NAMESPACE + +#include "moc_qeventdispatcher_win_p.cpp" diff --git a/qtbase/src/corelib/kernel/qfunctions_win.cpp b/qtbase/src/corelib/kernel/qfunctions_win.cpp index 70b67ea0..a74d3979 100644 --- a/qtbase/src/corelib/kernel/qfunctions_win.cpp +++ b/qtbase/src/corelib/kernel/qfunctions_win.cpp @@ -51,21 +51,21 @@ bool qt_win_hasPackageIdentity() { #if defined(HAS_APPMODEL) - static const bool hasPackageIdentity = []() { - UINT32 length = 0; - switch (const auto result = myGetCurrentPackageFullName(&length, nullptr)) { - case ERROR_INSUFFICIENT_BUFFER: - return true; - case APPMODEL_ERROR_NO_PACKAGE: - return false; - default: - qWarning("Failed to resolve package identity (error code %ld)", result); - return false; - } - }(); - return hasPackageIdentity; + static const bool hasPackageIdentity = []() { + UINT32 length = 0; + switch (const auto result = myGetCurrentPackageFullName(&length, nullptr)) { + case ERROR_INSUFFICIENT_BUFFER: + return true; + case APPMODEL_ERROR_NO_PACKAGE: + return false; + default: + qWarning("Failed to resolve package identity (error code %ld)", result); + return false; + } + }(); + return hasPackageIdentity; #else - return false; + return false; #endif } diff --git a/qtbase/src/corelib/thread/qfutex_p.h b/qtbase/src/corelib/thread/qfutex_p.h index efb14851..f6a8dda6 100644 --- a/qtbase/src/corelib/thread/qfutex_p.h +++ b/qtbase/src/corelib/thread/qfutex_p.h @@ -15,15 +15,15 @@ // We mean it. // +#include #include -#include QT_BEGIN_NAMESPACE namespace QtDummyFutex { constexpr inline bool futexAvailable() { return false; } template - inline bool futexWait(Atomic &, typename Atomic::Type, int = 0) + inline bool futexWait(Atomic &, typename Atomic::Type, QDeadlineTimer = {}) { Q_UNREACHABLE_RETURN(false); } template inline void futexWakeOne(Atomic &) { Q_UNREACHABLE(); } @@ -33,82 +33,16 @@ namespace QtDummyFutex { QT_END_NAMESPACE -#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE) +#if defined(Q_OS_DARWIN) +# include "qfutex_mac_p.h" +#elif defined(Q_OS_FREEBSD) +# include "qfutex_freebsd_p.h" +#elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE) // use Linux mutexes everywhere except for LSB builds -# include -# include -# include -# include -# include -# include -# define QT_ALWAYS_USE_FUTEX - -// if not defined in linux/futex.h -# define FUTEX_PRIVATE_FLAG 128 // added in v2.6.22 - -// RISC-V does not supply __NR_futex -# ifndef __NR_futex -# define __NR_futex __NR_futex_time64 -# endif - -QT_BEGIN_NAMESPACE -namespace QtLinuxFutex { - constexpr inline bool futexAvailable() { return true; } - inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0, - int *addr2 = nullptr, int val3 = 0) noexcept - { - QtTsan::futexRelease(addr, addr2); - - // we use __NR_futex because some libcs (like Android's bionic) don't - // provide SYS_futex etc. - int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3); - - QtTsan::futexAcquire(addr, addr2); - - return result; - } - template int *addr(T *ptr) - { - int *int_addr = reinterpret_cast(ptr); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - if (sizeof(T) > sizeof(int)) - int_addr++; //We want a pointer to the least significant half -#endif - return int_addr; - } - - template - inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue) - { - _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue)); - } - template - inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout) - { - struct timespec ts; - ts.tv_sec = nstimeout / 1000 / 1000 / 1000; - ts.tv_nsec = nstimeout % (1000 * 1000 * 1000); - int r = _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue), quintptr(&ts)); - return r == 0 || errno != ETIMEDOUT; - } - template inline void futexWakeOne(Atomic &futex) - { - _q_futex(addr(&futex), FUTEX_WAKE, 1); - } - template inline void futexWakeAll(Atomic &futex) - { - _q_futex(addr(&futex), FUTEX_WAKE, INT_MAX); - } - template inline - void futexWakeOp(Atomic &futex1, int wake1, int wake2, Atomic &futex2, quint32 op) - { - _q_futex(addr(&futex1), FUTEX_WAKE_OP, wake1, wake2, addr(&futex2), op); - } -} -namespace QtFutex = QtLinuxFutex; -QT_END_NAMESPACE +# include "qfutex_linux_p.h" +//#elif defined(Q_OS_WIN) +//# include "qfutex_win_p.h" #else - QT_BEGIN_NAMESPACE namespace QtFutex = QtDummyFutex; QT_END_NAMESPACE diff --git a/qtbase/src/corelib/thread/qmutex.cpp b/qtbase/src/corelib/thread/qmutex.cpp index 0e941218..58f42791 100644 --- a/qtbase/src/corelib/thread/qmutex.cpp +++ b/qtbase/src/corelib/thread/qmutex.cpp @@ -8,7 +8,6 @@ #include "qmutex.h" #include #include "qatomic.h" -#include "qelapsedtimer.h" #include "qfutex_p.h" #include "qthread.h" #include "qmutex_p.h" @@ -673,12 +672,11 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT */ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXCEPT { - qint64 remainingTime = deadlineTimer.remainingTimeNSecs(); - if (remainingTime == 0) + if (deadlineTimer.hasExpired()) return false; if (futexAvailable()) { - if (Q_UNLIKELY(remainingTime < 0)) { // deadlineTimer.isForever() + if (Q_UNLIKELY(deadlineTimer.isForever())) { lockInternal(); return true; } @@ -689,8 +687,8 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) return true; - Q_FOREVER { - if (!futexWait(d_ptr, dummyFutexValue(), remainingTime)) + for (;;) { + if (!futexWait(d_ptr, dummyFutexValue(), deadlineTimer)) return false; // We got woken up, so must try to acquire the mutex. We must set @@ -699,9 +697,7 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) return true; - // calculate the remaining time - remainingTime = deadlineTimer.remainingTimeNSecs(); - if (remainingTime <= 0) + if (deadlineTimer.hasExpired()) return false; } } @@ -713,7 +709,7 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC continue; if (copy == dummyLocked()) { - if (remainingTime == 0) + if (deadlineTimer.hasExpired()) return false; // The mutex is locked but does not have a QMutexPrivate yet. // we need to allocate a QMutexPrivate @@ -728,7 +724,7 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC } QMutexPrivate *d = static_cast(copy); - if (remainingTime == 0 && !d->possiblyUnlocked.loadRelaxed()) + if (deadlineTimer.hasExpired() && !d->possiblyUnlocked.loadRelaxed()) return false; // At this point we have a pointer to a QMutexPrivate. But the other thread @@ -790,7 +786,6 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC Q_ASSERT(d == d_ptr.loadRelaxed()); return true; } else { - Q_ASSERT(remainingTime >= 0); // timed out d->derefWaiters(1); //There may be a race in which the mutex is unlocked right after we timed out, @@ -915,10 +910,10 @@ QT_END_NAMESPACE #if defined(QT_ALWAYS_USE_FUTEX) // nothing -#elif defined(Q_OS_MAC) +#elif defined(Q_OS_DARWIN) # include "qmutex_mac.cpp" #elif defined(Q_OS_WIN) # include "qmutex_win.cpp" #else # include "qmutex_unix.cpp" -#endif \ No newline at end of file +#endif diff --git a/qtbase/src/gui/rhi/qrhid3d11.cpp b/qtbase/src/gui/rhi/qrhid3d11.cpp index d5b60b7e..7d2a99ed 100644 --- a/qtbase/src/gui/rhi/qrhid3d11.cpp +++ b/qtbase/src/gui/rhi/qrhid3d11.cpp @@ -95,22 +95,47 @@ using namespace Qt::StringLiterals; /*! \variable QRhiD3D11NativeHandles::dev + + Points to a + \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11device}{ID3D11Device} + or left set to \nullptr if no existing device is to be imported. + + \note When importing a device, both the device and the device context must be set to valid objects. */ /*! \variable QRhiD3D11NativeHandles::context + + Points to a \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11devicecontext}{ID3D11DeviceContext} + or left set to \nullptr if no existing device context is to be imported. + + \note When importing a device, both the device and the device context must be set to valid objects. */ /*! \variable QRhiD3D11NativeHandles::featureLevel + + Specifies the feature level passed to + \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice}{D3D11CreateDevice()}. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. When not set, the default rules outlined in the D3D + documentation apply. */ /*! \variable QRhiD3D11NativeHandles::adapterLuidLow + + The low part of the local identifier (LUID) of the DXGI adapter to use. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. */ /*! \variable QRhiD3D11NativeHandles::adapterLuidHigh + + The high part of the local identifier (LUID) of the DXGI adapter to use. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. */ // help mingw with its ancient sdk headers @@ -259,9 +284,7 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) { activeAdapter = adapter; adapterLuid = desc.AdapterLuid; - driverInfoStruct.deviceName = name.toUtf8(); - driverInfoStruct.deviceId = desc.DeviceId; - driverInfoStruct.vendorId = desc.VendorId; + QRhiD3D::fillDriverInfo(&driverInfoStruct, desc); qCDebug(QRHI_LOG_INFO, " using this adapter"); } else { adapter->Release(); @@ -304,21 +327,24 @@ bool QRhiD3D11::create(QRhi::Flags flags) return false; } + const bool supports11_1 = SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast(&context))); + ctx->Release(); + if (!supports11_1) { + qWarning("ID3D11DeviceContext1 not supported"); + return false; + } + // Test if creating a Shader Model 5.0 vertex shader works; we want to // fail already in create() if that's not the case. ID3D11VertexShader *testShader = nullptr; if (SUCCEEDED(dev->CreateVertexShader(g_testVertexShader, sizeof(g_testVertexShader), nullptr, &testShader))) { testShader->Release(); } else { - qWarning("D3D11 smoke test failed (failed to create vertex shader)"); - ctx->Release(); - return false; - } - - const bool supports11_1 = SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast(&context))); - ctx->Release(); - if (!supports11_1) { - qWarning("ID3D11DeviceContext1 not supported"); + static const char *msg = "D3D11 smoke test: Failed to create vertex shader"; + if (flags.testFlag(QRhi::SuppressSmokeTestWarnings)) + qCDebug(QRHI_LOG_INFO, "%s", msg); + else + qWarning("%s", msg); return false; } @@ -328,11 +354,19 @@ bool QRhiD3D11::create(QRhi::Flags flags) // still not support this D3D_FEATURE_LEVEL_11_1 feature. (e.g. // because it only does 11_0) if (!features.ConstantBufferOffsetting) { - qWarning("Constant buffer offsetting is not supported by the driver"); + static const char *msg = "D3D11 smoke test: Constant buffer offsetting is not supported by the driver"; + if (flags.testFlag(QRhi::SuppressSmokeTestWarnings)) + qCDebug(QRHI_LOG_INFO, "%s", msg); + else + qWarning("%s", msg); return false; } } else { - qWarning("Failed to query D3D11_FEATURE_D3D11_OPTIONS"); + static const char *msg = "D3D11 smoke test: Failed to query D3D11_FEATURE_D3D11_OPTIONS"; + if (flags.testFlag(QRhi::SuppressSmokeTestWarnings)) + qCDebug(QRHI_LOG_INFO, "%s", msg); + else + qWarning("%s", msg); return false; } } else { @@ -342,12 +376,14 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (SUCCEEDED(dev->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgiDev)))) { IDXGIAdapter *adapter = nullptr; if (SUCCEEDED(dxgiDev->GetAdapter(&adapter))) { - DXGI_ADAPTER_DESC desc; - adapter->GetDesc(&desc); - adapterLuid = desc.AdapterLuid; - driverInfoStruct.deviceName = QString::fromUtf16(reinterpret_cast(desc.Description)).toUtf8(); - driverInfoStruct.deviceId = desc.DeviceId; - driverInfoStruct.vendorId = desc.VendorId; + IDXGIAdapter1 *adapter1 = nullptr; + if (SUCCEEDED(adapter->QueryInterface(__uuidof(IDXGIAdapter1), reinterpret_cast(&adapter1)))) { + DXGI_ADAPTER_DESC1 desc; + adapter1->GetDesc1(&desc); + adapterLuid = desc.AdapterLuid; + QRhiD3D::fillDriverInfo(&driverInfoStruct, desc); + adapter1->Release(); + } adapter->Release(); } dxgiDev->Release(); @@ -358,11 +394,6 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (FAILED(context->QueryInterface(__uuidof(ID3DUserDefinedAnnotation), reinterpret_cast(&annotations)))) annotations = nullptr; - if (flags.testFlag(QRhi::EnableTimestamps)) { - ofr.timestamps.prepare(2, this); - // timestamp queries are optional so we can go on even if they failed - } - deviceLost = false; nativeHandlesStruct.dev = dev; @@ -388,7 +419,16 @@ void QRhiD3D11::destroy() clearShaderCache(); - ofr.timestamps.destroy(); + if (ofr.tsDisjointQuery) { + ofr.tsDisjointQuery->Release(); + ofr.tsDisjointQuery = nullptr; + } + for (int i = 0; i < 2; ++i) { + if (ofr.tsQueries[i]) { + ofr.tsQueries[i]->Release(); + ofr.tsQueries[i] = nullptr; + } + } if (annotations) { annotations->Release(); @@ -592,6 +632,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ThreeDimensionalTextureMipmaps: return true; + case QRhi::MultiView: + return false; default: Q_UNREACHABLE(); return false; @@ -1262,7 +1304,6 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb) void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb) { QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); - // no timestampSwapChain, in order to avoid timestamp mess executeCommandBuffer(cbD); cbD->resetCommands(); } @@ -1285,6 +1326,19 @@ double QRhiD3D11::lastCompletedGpuTime(QRhiCommandBuffer *cb) return cbD->lastGpuTime; } +static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt) +{ + switch (rt->resourceType()) { + case QRhiResource::SwapChainRenderTarget: + return &QRHI_RES(QD3D11SwapChainRenderTarget, rt)->d; + case QRhiResource::TextureRenderTarget: + return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d; + default: + Q_UNREACHABLE(); + return nullptr; + } +} + QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) { Q_UNUSED(flags); @@ -1301,12 +1355,22 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF finishActiveReadbacks(); - if (swapChainD->timestamps.active[currentFrameSlot]) { + if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) { double elapsedSec = 0; - if (swapChainD->timestamps.tryQueryTimestamps(currentFrameSlot, context, &elapsedSec)) + if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, context, &elapsedSec)) swapChainD->cb.lastGpuTime = elapsedSec; } + ID3D11Query *tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2]; + ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex]; + const bool recordTimestamps = tsStart && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]; + + QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::BeginFrame; + cmd.args.beginFrame.tsQuery = recordTimestamps ? tsStart : nullptr; + cmd.args.beginFrame.tsDisjointQuery = recordTimestamps ? tsDisjoint : nullptr; + cmd.args.beginFrame.swapchainData = rtData(&swapChainD->rt); + return QRhi::FrameOpSuccess; } @@ -1316,17 +1380,13 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame Q_ASSERT(contextState.currentSwapChain = swapChainD); const int currentFrameSlot = swapChainD->currentFrameSlot; - ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[currentFrameSlot]; - const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot; - ID3D11Query *tsStart = swapChainD->timestamps.query[tsIdx]; - ID3D11Query *tsEnd = swapChainD->timestamps.query[tsIdx + 1]; - const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !swapChainD->timestamps.active[currentFrameSlot]; + QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::EndFrame; + cmd.args.endFrame.tsQuery = nullptr; // done later manually, see below + cmd.args.endFrame.tsDisjointQuery = nullptr; // send all commands to the context - if (recordTimestamps) - executeCommandBuffer(&swapChainD->cb, swapChainD); - else - executeCommandBuffer(&swapChainD->cb); + executeCommandBuffer(&swapChainD->cb); if (swapChainD->sampleDesc.Count > 1) { context->ResolveSubresource(swapChainD->backBufferTex, 0, @@ -1334,11 +1394,15 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame swapChainD->colorFormat); } - // this is here because we want to include the time spent on the resolve as well + // this is here because we want to include the time spent on the ResolveSubresource as well + ID3D11Query *tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1]; + ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex]; + const bool recordTimestamps = tsEnd && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]; if (recordTimestamps) { context->End(tsEnd); context->End(tsDisjoint); - swapChainD->timestamps.active[currentFrameSlot] = true; + swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true; + swapChainD->currentTimestampPairIndex = (swapChainD->currentTimestampPairIndex + 1) % QD3D11SwapChainTimestamps::TIMESTAMP_PAIRS; } if (!flags.testFlag(QRhi::SkipPresent)) { @@ -1383,12 +1447,36 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi: ofr.cbWrapper.resetState(); *cb = &ofr.cbWrapper; - if (ofr.timestamps.active[ofr.timestampIdx]) { - double elapsedSec = 0; - if (ofr.timestamps.tryQueryTimestamps(ofr.timestampIdx, context, &elapsedSec)) - ofr.cbWrapper.lastGpuTime = elapsedSec; + if (rhiFlags.testFlag(QRhi::EnableTimestamps)) { + D3D11_QUERY_DESC queryDesc = {}; + if (!ofr.tsDisjointQuery) { + queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; + HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsDisjointQuery); + if (FAILED(hr)) { + qWarning("Failed to create timestamp disjoint query: %s", + qPrintable(QSystemError::windowsComString(hr))); + return QRhi::FrameOpError; + } + } + queryDesc.Query = D3D11_QUERY_TIMESTAMP; + for (int i = 0; i < 2; ++i) { + if (!ofr.tsQueries[i]) { + HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsQueries[i]); + if (FAILED(hr)) { + qWarning("Failed to create timestamp query: %s", + qPrintable(QSystemError::windowsComString(hr))); + return QRhi::FrameOpError; + } + } + } } + QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::BeginFrame; + cmd.args.beginFrame.tsQuery = ofr.tsQueries[0] ? ofr.tsQueries[0] : nullptr; + cmd.args.beginFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr; + cmd.args.beginFrame.swapchainData = nullptr; + return QRhi::FrameOpSuccess; } @@ -1397,25 +1485,39 @@ QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags) Q_UNUSED(flags); ofr.active = false; - ID3D11Query *tsDisjoint = ofr.timestamps.disjointQuery[ofr.timestampIdx]; - ID3D11Query *tsStart = ofr.timestamps.query[ofr.timestampIdx * 2]; - ID3D11Query *tsEnd = ofr.timestamps.query[ofr.timestampIdx * 2 + 1]; - const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !ofr.timestamps.active[ofr.timestampIdx]; - if (recordTimestamps) { - context->Begin(tsDisjoint); - context->End(tsStart); // record timestamp; no Begin() for D3D11_QUERY_TIMESTAMP - } + QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::EndFrame; + cmd.args.endFrame.tsQuery = ofr.tsQueries[1] ? ofr.tsQueries[1] : nullptr; + cmd.args.endFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr; executeCommandBuffer(&ofr.cbWrapper); context->Flush(); finishActiveReadbacks(); - if (recordTimestamps) { - context->End(tsEnd); - context->End(tsDisjoint); - ofr.timestamps.active[ofr.timestampIdx] = true; - ofr.timestampIdx = (ofr.timestampIdx + 1) % 2; + if (ofr.tsQueries[0]) { + quint64 timestamps[2]; + D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj; + HRESULT hr; + bool ok = true; + do { + hr = context->GetData(ofr.tsDisjointQuery, &dj, sizeof(dj), 0); + } while (hr == S_FALSE); + ok &= hr == S_OK; + do { + hr = context->GetData(ofr.tsQueries[1], ×tamps[1], sizeof(quint64), 0); + } while (hr == S_FALSE); + ok &= hr == S_OK; + do { + hr = context->GetData(ofr.tsQueries[0], ×tamps[0], sizeof(quint64), 0); + } while (hr == S_FALSE); + ok &= hr == S_OK; + if (ok) { + if (!dj.Disjoint && dj.Frequency) { + const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f; + ofr.cbWrapper.lastGpuTime = elapsedMs / 1000.0; + } + } } return QRhi::FrameOpSuccess; @@ -1558,7 +1660,7 @@ QRhi::FrameOpResult QRhiD3D11::finish() } else { Q_ASSERT(contextState.currentSwapChain); Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass); - executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess + executeCommandBuffer(&contextState.currentSwapChain->cb); contextState.currentSwapChain->cb.resetCommands(); } } @@ -1933,19 +2035,6 @@ void QRhiD3D11::finishActiveReadbacks() f(); } -static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt) -{ - switch (rt->resourceType()) { - case QRhiResource::SwapChainRenderTarget: - return &QRHI_RES(QD3D11SwapChainRenderTarget, rt)->d; - case QRhiResource::TextureRenderTarget: - return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d; - default: - Q_UNREACHABLE(); - return nullptr; - } -} - void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { Q_ASSERT(QRHI_RES(QD3D11CommandBuffer, cb)->recordingPass == QD3D11CommandBuffer::NoPass); @@ -2651,7 +2740,7 @@ void QRhiD3D11::resetShaderResources() currentShaderMask &= ~StageU##MaskBit; \ } -void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain) +void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD) { quint32 stencilRef = 0; float blendConstants[] = { 1, 1, 1, 1 }; @@ -2664,26 +2753,30 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain * }; int currentShaderMask = 0xFF; - if (timestampSwapChain) { - const int currentFrameSlot = timestampSwapChain->currentFrameSlot; - ID3D11Query *tsDisjoint = timestampSwapChain->timestamps.disjointQuery[currentFrameSlot]; - const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot; - ID3D11Query *tsStart = timestampSwapChain->timestamps.query[tsIdx]; - if (tsDisjoint && tsStart && !timestampSwapChain->timestamps.active[currentFrameSlot]) { - // The timestamps seem to include vsync time with Present(1), except - // when running on a non-primary gpu. This is not ideal. So try working - // it around by issuing a semi-fake OMSetRenderTargets early and - // writing the first timestamp only afterwards. - context->Begin(tsDisjoint); - QD3D11RenderTargetData *rtD = rtData(×tampSwapChain->rt); - context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); - context->End(tsStart); // just record a timestamp, no Begin needed - } - } - for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) { const QD3D11CommandBuffer::Command &cmd(*it); switch (cmd.cmd) { + case QD3D11CommandBuffer::Command::BeginFrame: + if (cmd.args.beginFrame.tsDisjointQuery) + context->Begin(cmd.args.beginFrame.tsDisjointQuery); + if (cmd.args.beginFrame.tsQuery) { + if (cmd.args.beginFrame.swapchainData) { + // The timestamps seem to include vsync time with Present(1), except + // when running on a non-primary gpu. This is not ideal. So try working + // it around by issuing a semi-fake OMSetRenderTargets early and + // writing the first timestamp only afterwards. + QD3D11RenderTargetData *rtD = cmd.args.beginFrame.swapchainData; + context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); + } + context->End(cmd.args.beginFrame.tsQuery); // no Begin() for D3D11_QUERY_TIMESTAMP + } + break; + case QD3D11CommandBuffer::Command::EndFrame: + if (cmd.args.endFrame.tsQuery) + context->End(cmd.args.endFrame.tsQuery); + if (cmd.args.endFrame.tsDisjointQuery) + context->End(cmd.args.endFrame.tsDisjointQuery); + break; case QD3D11CommandBuffer::Command::ResetShaderResources: resetShaderResources(); break; @@ -4702,14 +4795,13 @@ void QD3D11CommandBuffer::destroy() // nothing to do here } -bool QD3D11Timestamps::prepare(int pairCount, QRhiD3D11 *rhiD) +bool QD3D11SwapChainTimestamps::prepare(QRhiD3D11 *rhiD) { // Creates the query objects if not yet done, but otherwise calling this // function is expected to be a no-op. - Q_ASSERT(pairCount <= MAX_TIMESTAMP_PAIRS); D3D11_QUERY_DESC queryDesc = {}; - for (int i = 0; i < pairCount; ++i) { + for (int i = 0; i < TIMESTAMP_PAIRS; ++i) { if (!disjointQuery[i]) { queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &disjointQuery[i]); @@ -4721,7 +4813,7 @@ bool QD3D11Timestamps::prepare(int pairCount, QRhiD3D11 *rhiD) } queryDesc.Query = D3D11_QUERY_TIMESTAMP; for (int j = 0; j < 2; ++j) { - const int idx = pairCount * i + j; + const int idx = 2 * i + j; if (!query[idx]) { HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &query[idx]); if (FAILED(hr)) { @@ -4732,20 +4824,19 @@ bool QD3D11Timestamps::prepare(int pairCount, QRhiD3D11 *rhiD) } } } - this->pairCount = pairCount; return true; } -void QD3D11Timestamps::destroy() +void QD3D11SwapChainTimestamps::destroy() { - for (int i = 0; i < MAX_TIMESTAMP_PAIRS; ++i) { + for (int i = 0; i < TIMESTAMP_PAIRS; ++i) { active[i] = false; if (disjointQuery[i]) { disjointQuery[i]->Release(); disjointQuery[i] = nullptr; } for (int j = 0; j < 2; ++j) { - const int idx = MAX_TIMESTAMP_PAIRS * i + j; + const int idx = TIMESTAMP_PAIRS * i + j; if (query[idx]) { query[idx]->Release(); query[idx] = nullptr; @@ -4754,26 +4845,21 @@ void QD3D11Timestamps::destroy() } } -bool QD3D11Timestamps::tryQueryTimestamps(int idx, ID3D11DeviceContext *context, double *elapsedSec) +bool QD3D11SwapChainTimestamps::tryQueryTimestamps(int pairIndex, ID3D11DeviceContext *context, double *elapsedSec) { bool result = false; - if (!active[idx]) + if (!active[pairIndex]) return result; - ID3D11Query *tsDisjoint = disjointQuery[idx]; - const int tsIdx = pairCount * idx; - ID3D11Query *tsStart = query[tsIdx]; - ID3D11Query *tsEnd = query[tsIdx + 1]; + ID3D11Query *tsDisjoint = disjointQuery[pairIndex]; + ID3D11Query *tsStart = query[pairIndex * 2]; + ID3D11Query *tsEnd = query[pairIndex * 2 + 1]; quint64 timestamps[2]; D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj; bool ok = true; ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; ok &= context->GetData(tsEnd, ×tamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; - // this above is often not ready, not even in frame_where_recorded+2, - // not clear why. so make the whole thing async and do not touch the - // queries until they are finally all available in frame this+2 or - // this+4 or ... ok &= context->GetData(tsStart, ×tamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; if (ok) { @@ -4782,16 +4868,14 @@ bool QD3D11Timestamps::tryQueryTimestamps(int idx, ID3D11DeviceContext *context, *elapsedSec = elapsedMs / 1000.0; result = true; } - active[idx] = false; - } // else leave active set, will retry in a subsequent beginFrame or similar + active[pairIndex] = false; + } // else leave active set, will retry in a subsequent beginFrame return result; } QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi) - : QRhiSwapChain(rhi), - rt(rhi, this), - cb(rhi) + : QRhiSwapChain(rhi), rt(rhi, this), rtRight(rhi, this), cb(rhi) { backBufferTex = nullptr; backBufferRtv = nullptr; @@ -4812,6 +4896,10 @@ void QD3D11SwapChain::releaseBuffers() backBufferRtv->Release(); backBufferRtv = nullptr; } + if (backBufferRtvRight) { + backBufferRtvRight->Release(); + backBufferRtvRight = nullptr; + } if (backBufferTex) { backBufferTex->Release(); backBufferTex = nullptr; @@ -4869,50 +4957,17 @@ QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget() return &rt; } +QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer) +{ + return targetBuffer == StereoTargetBuffer::LeftBuffer? &rt: &rtRight; +} + QSize QD3D11SwapChain::surfacePixelSize() { Q_ASSERT(m_window); return m_window->size() * m_window->devicePixelRatio(); } -static bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result) -{ - bool ok = false; - QRect wr = w->geometry(); - wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio()); - const QPoint center = wr.center(); - IDXGIOutput *currentOutput = nullptr; - IDXGIOutput *output = nullptr; - for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) { - DXGI_OUTPUT_DESC desc; - output->GetDesc(&desc); - const RECT r = desc.DesktopCoordinates; - const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); - if (dr.contains(center)) { - currentOutput = output; - break; - } else { - output->Release(); - } - } - if (currentOutput) { - ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(result))); - currentOutput->Release(); - } - return ok; -} - -static bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result) -{ - bool ok = false; - IDXGIOutput6 *out6 = nullptr; - if (output6ForWindow(w, adapter, &out6)) { - ok = SUCCEEDED(out6->GetDesc1(result)); - out6->Release(); - } - return ok; -} - bool QD3D11SwapChain::isFormatSupported(Format f) { if (f == SDR) @@ -4925,7 +4980,7 @@ bool QD3D11SwapChain::isFormatSupported(Format f) QRHI_RES_RHI(QRhiD3D11); DXGI_OUTPUT_DESC1 desc1; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) { + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) { if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10; } @@ -4936,14 +4991,16 @@ bool QD3D11SwapChain::isFormatSupported(Format f) QRhiSwapChainHdrInfo QD3D11SwapChain::hdrInfo() { QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo(); + // Must use m_window, not window, given this may be called before createOrResize(). if (m_window) { QRHI_RES_RHI(QRhiD3D11); DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { - info.isHardCodedDefaults = false; + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits; info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance; info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance; + info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits + info.sdrWhiteLevel = QRhiD3D::sdrWhiteLevelInNits(hdrOutputDesc); } } return info; @@ -5016,11 +5073,13 @@ bool QD3D11SwapChain::createOrResize() { return createOrResizeWin7(); } + // Can be called multiple times due to window resizes - that is not the // same as a simple destroy+create (as with other resources). Just need to // resize the buffers then. const bool needsRegistration = !window || window != m_window; + const bool stereo = m_window->format().stereo(); // except if the window actually changes if (window && window != m_window) @@ -5078,7 +5137,7 @@ bool QD3D11SwapChain::createOrResize() DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { switch (m_format) { @@ -5120,6 +5179,7 @@ bool QD3D11SwapChain::createOrResize() desc.Flags = swapChainFlags; desc.Scaling = rhiD->useLegacySwapchainModel ? DXGI_SCALING_STRETCH : DXGI_SCALING_NONE; desc.SwapEffect = rhiD->useLegacySwapchainModel ? DXGI_SWAP_EFFECT_DISCARD : DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.Stereo = stereo; if (dcompVisual) { // With DirectComposition setting AlphaMode to STRAIGHT fails the @@ -5238,6 +5298,19 @@ bool QD3D11SwapChain::createOrResize() return false; } + if (stereo) { + // Create a second render target view for the right eye + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.FirstArraySlice = 1; + rtvDesc.Texture2DArray.ArraySize = 1; + hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtvRight); + if (FAILED(hr)) { + qWarning("Failed to create rtv for swapchain backbuffer (right eye): %s", + qPrintable(QSystemError::windowsComString(hr))); + return false; + } + } + // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer. for (int i = 0; i < BUFFER_COUNT; ++i) { if (sampleDesc.Count > 1) { @@ -5276,8 +5349,20 @@ bool QD3D11SwapChain::createOrResize() rtD->d.colorAttCount = 1; rtD->d.dsAttCount = m_depthStencil ? 1 : 0; + if (stereo) { + rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rtRight); + rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc); + rtD->d.pixelSize = pixelSize; + rtD->d.dpr = float(window->devicePixelRatio()); + rtD->d.sampleCount = int(sampleDesc.Count); + rtD->d.colorAttCount = 1; + rtD->d.dsAttCount = m_depthStencil ? 1 : 0; + rtD->d.rtv[0] = backBufferRtvRight; + rtD->d.dsv = ds ? ds->dsv : nullptr; + } + if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps)) { - timestamps.prepare(BUFFER_COUNT, rhiD); + timestamps.prepare(rhiD); // timestamp queries are optional so we can go on even if they failed } diff --git a/qtbase/src/gui/rhi/qrhid3d11_p.h b/qtbase/src/gui/rhi/qrhid3d11_p.h index ce4114c1..86b810cc 100644 --- a/qtbase/src/gui/rhi/qrhid3d11_p.h +++ b/qtbase/src/gui/rhi/qrhid3d11_p.h @@ -356,6 +356,8 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer struct Command { enum Cmd { + BeginFrame, + EndFrame, ResetShaderResources, SetRenderTarget, Clear, @@ -385,6 +387,15 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer // QRhi*/QD3D11* references should be kept at minimum (so no // QRhiTexture/Buffer/etc. pointers). union Args { + struct { + ID3D11Query *tsQuery; + ID3D11Query *tsDisjointQuery; + QD3D11RenderTargetData *swapchainData; + } beginFrame; + struct { + ID3D11Query *tsQuery; + ID3D11Query *tsDisjointQuery; + } endFrame; struct { QRhiRenderTarget *rt; } setRenderTarget; @@ -556,17 +567,15 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer } }; -static const int QD3D11_SWAPCHAIN_BUFFER_COUNT = 2; - -struct QD3D11Timestamps +struct QD3D11SwapChainTimestamps { - static const int MAX_TIMESTAMP_PAIRS = QD3D11_SWAPCHAIN_BUFFER_COUNT; - bool active[MAX_TIMESTAMP_PAIRS] = {}; - ID3D11Query *disjointQuery[MAX_TIMESTAMP_PAIRS] = {}; - ID3D11Query *query[MAX_TIMESTAMP_PAIRS * 2] = {}; - int pairCount = 0; + static const int TIMESTAMP_PAIRS = 2; - bool prepare(int pairCount, QRhiD3D11 *rhiD); + bool active[TIMESTAMP_PAIRS] = {}; + ID3D11Query *disjointQuery[TIMESTAMP_PAIRS] = {}; + ID3D11Query *query[TIMESTAMP_PAIRS * 2] = {}; + + bool prepare(QRhiD3D11 *rhiD); void destroy(); bool tryQueryTimestamps(int idx, ID3D11DeviceContext *context, double *elapsedSec); }; @@ -579,6 +588,7 @@ struct QD3D11SwapChain : public QRhiSwapChain QRhiCommandBuffer *currentFrameCommandBuffer() override; QRhiRenderTarget *currentFrameRenderTarget() override; + QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override; QSize surfacePixelSize() override; bool isFormatSupported(Format f) override; @@ -587,7 +597,7 @@ struct QD3D11SwapChain : public QRhiSwapChain QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; bool createOrResizeWin7(); - + void releaseBuffers(); bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc, ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const; @@ -595,6 +605,7 @@ struct QD3D11SwapChain : public QRhiSwapChain QWindow *window = nullptr; QSize pixelSize; QD3D11SwapChainRenderTarget rt; + QD3D11SwapChainRenderTarget rtRight; QD3D11CommandBuffer cb; DXGI_FORMAT colorFormat; DXGI_FORMAT srgbAdjustedColorFormat; @@ -602,7 +613,8 @@ struct QD3D11SwapChain : public QRhiSwapChain UINT swapChainFlags = 0; ID3D11Texture2D *backBufferTex; ID3D11RenderTargetView *backBufferRtv; - static const int BUFFER_COUNT = QD3D11_SWAPCHAIN_BUFFER_COUNT; + ID3D11RenderTargetView *backBufferRtvRight = nullptr; + static const int BUFFER_COUNT = 2; ID3D11Texture2D *msaaTex[BUFFER_COUNT]; ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT]; DXGI_SAMPLE_DESC sampleDesc; @@ -612,7 +624,8 @@ struct QD3D11SwapChain : public QRhiSwapChain UINT swapInterval = 1; IDCompositionTarget *dcompTarget = nullptr; IDCompositionVisual *dcompVisual = nullptr; - QD3D11Timestamps timestamps; + QD3D11SwapChainTimestamps timestamps; + int currentTimestampPairIndex = 0; }; class QRhiD3D11 : public QRhiImplementation @@ -737,7 +750,7 @@ public: const uint *dynOfsPairs, int dynOfsPairCount, bool offsetOnlyChange); void resetShaderResources(); - void executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain = nullptr); + void executeCommandBuffer(QD3D11CommandBuffer *cbD); DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount) const; void finishActiveReadbacks(); void reportLiveObjects(ID3D11Device *device); @@ -780,8 +793,8 @@ public: OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { } bool active = false; QD3D11CommandBuffer cbWrapper; - QD3D11Timestamps timestamps; - int timestampIdx = 0; + ID3D11Query *tsQueries[2] = {}; + ID3D11Query *tsDisjointQuery = nullptr; } ofr; struct TextureReadback { diff --git a/qtbase/src/gui/rhi/qrhid3d12.cpp b/qtbase/src/gui/rhi/qrhid3d12.cpp index 5e5fee4e..4e8dba9c 100644 --- a/qtbase/src/gui/rhi/qrhid3d12.cpp +++ b/qtbase/src/gui/rhi/qrhid3d12.cpp @@ -2,10 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qrhid3d12_p.h" -#include "qshader.h" -#include #include -#include #include #include #include "qrhid3dhelpers_p.h" @@ -16,6 +13,8 @@ #define QRHI_D3D12_HAS_OLD_PIX #endif +#ifdef __ID3D12Device2_INTERFACE_DEFINED__ + QT_BEGIN_NAMESPACE /* @@ -84,28 +83,54 @@ QT_BEGIN_NAMESPACE /*! \variable QRhiD3D12NativeHandles::dev + + Points to a + \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12device}{ID3D12Device} + or left set to \nullptr if no existing device is to be imported. */ /*! \variable QRhiD3D12NativeHandles::minimumFeatureLevel + + Specifies the \b minimum feature level passed to + \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-d3d12createdevice}{D3D12CreateDevice()}. + When not set, \c{D3D_FEATURE_LEVEL_11_0} is used. See + \l{https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-feature-levels}{this + page} for details. + + Relevant only when QRhi creates the device, ignored when importing a device + and device context. */ /*! \variable QRhiD3D12NativeHandles::adapterLuidLow + + The low part of the local identifier (LUID) of the DXGI adapter to use. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. */ /*! \variable QRhiD3D12NativeHandles::adapterLuidHigh + + The high part of the local identifier (LUID) of the DXGI adapter to use. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. */ /*! \variable QRhiD3D12NativeHandles::commandQueue + + When set, must point to a + \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12commandqueue}{ID3D12CommandQueue}. + It allows to optionally import a command queue as well, in addition to a + device. */ /*! \class QRhiD3D12CommandBufferNativeHandles \inmodule QtGui - \brief Holds the ID3D12GraphicsCommandList object that is backing a QRhiCommandBuffer. + \brief Holds the ID3D12GraphicsCommandList1 object that is backing a QRhiCommandBuffer. \note The command list object is only guaranteed to be valid, and in recording state, while recording a frame. That is, between a @@ -129,8 +154,14 @@ QRhiD3D12::QRhiD3D12(QRhiD3D12InitParams *params, QRhiD3D12NativeHandles *import debugLayer = params->enableDebugLayer; if (importParams) { if (importParams->dev) { - dev = reinterpret_cast(importParams->dev); - importedDevice = true; + ID3D12Device *d3d12Device = reinterpret_cast(importParams->dev); + if (SUCCEEDED(d3d12Device->QueryInterface(__uuidof(ID3D12Device2), reinterpret_cast(&dev)))) { + // get rid of the ref added by QueryInterface + d3d12Device->Release(); + importedDevice = true; + } else { + qWarning("ID3D12Device2 not supported, cannot import device"); + } } if (importParams->commandQueue) { cmdQueue = reinterpret_cast(importParams->commandQueue); @@ -273,9 +304,7 @@ bool QRhiD3D12::create(QRhi::Flags flags) if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) { activeAdapter = adapter; adapterLuid = desc.AdapterLuid; - driverInfoStruct.deviceName = name.toUtf8(); - driverInfoStruct.deviceId = desc.DeviceId; - driverInfoStruct.vendorId = desc.VendorId; + QRhiD3D::fillDriverInfo(&driverInfoStruct, desc); qCDebug(QRHI_LOG_INFO, " using this adapter"); } else { adapter->Release(); @@ -291,7 +320,7 @@ bool QRhiD3D12::create(QRhi::Flags flags) hr = myD3D12CreateDevice(activeAdapter, minimumFeatureLevel, - __uuidof(ID3D12Device), + __uuidof(ID3D12Device2), reinterpret_cast(&dev)); if (FAILED(hr)) { qWarning("Failed to create D3D12 device: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -305,16 +334,20 @@ bool QRhiD3D12::create(QRhi::Flags flags) for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); - adapter->Release(); if (desc.AdapterLuid.LowPart == adapterLuid.LowPart && desc.AdapterLuid.HighPart == adapterLuid.HighPart) { - driverInfoStruct.deviceName = QString::fromUtf16(reinterpret_cast(desc.Description)).toUtf8(); - driverInfoStruct.deviceId = desc.DeviceId; - driverInfoStruct.vendorId = desc.VendorId; + activeAdapter = adapter; + QRhiD3D::fillDriverInfo(&driverInfoStruct, desc); break; + } else { + adapter->Release(); } } + if (!activeAdapter) { + qWarning("No adapter"); + return false; + } qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); } @@ -416,6 +449,9 @@ bool QRhiD3D12::create(QRhi::Flags flags) qWarning("Could not create host-visible staging area"); return false; } + QString decoratedName = QLatin1String("Small staging area buffer/"); + decoratedName += QString::number(i); + smallStagingAreas[i].mem.buffer->SetName(reinterpret_cast(decoratedName.utf16())); } if (!shaderVisibleCbvSrvUavHeap.create(dev, @@ -426,6 +462,49 @@ bool QRhiD3D12::create(QRhi::Flags flags) return false; } + if (flags.testFlag(QRhi::EnableTimestamps)) { + static bool wantsStablePowerState = qEnvironmentVariableIntValue("QT_D3D_STABLE_POWER_STATE"); + // + // https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-setstablepowerstate + // + // NB! This is a _global_ setting, affecting other processes (and 3D + // APIs such as Vulkan), as long as this application is running. Hence + // making it an env.var. for now. Never enable it in production. But + // extremely useful for the GPU timings with NVIDIA at least; the + // timestamps become stable and smooth, making the number readable and + // actually useful e.g. in Quick 3D's DebugView when this is enabled. + // (otherwise the number's all over the place) + // + // See also + // https://developer.nvidia.com/blog/advanced-api-performance-setstablepowerstate/ + // for possible other approaches. + // + if (wantsStablePowerState) + dev->SetStablePowerState(TRUE); + + hr = cmdQueue->GetTimestampFrequency(×tampTicksPerSecond); + if (FAILED(hr)) { + qWarning("Failed to query timestamp frequency: %s", + qPrintable(QSystemError::windowsComString(hr))); + return false; + } + if (!timestampQueryHeap.create(dev, QD3D12_FRAMES_IN_FLIGHT * 2, D3D12_QUERY_HEAP_TYPE_TIMESTAMP)) { + qWarning("Failed to create timestamp query pool"); + return false; + } + const quint32 readbackBufSize = QD3D12_FRAMES_IN_FLIGHT * 2 * sizeof(quint64); + if (!timestampReadbackArea.create(this, readbackBufSize, D3D12_HEAP_TYPE_READBACK)) { + qWarning("Failed to create timestamp readback buffer"); + return false; + } + timestampReadbackArea.mem.buffer->SetName(L"Timestamp readback buffer"); + memset(timestampReadbackArea.mem.p, 0, readbackBufSize); + } + + D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {}; + if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3)))) + caps.multiView = options3.ViewInstancingTier != D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED; + deviceLost = false; offscreenActive = false; @@ -454,6 +533,9 @@ void QRhiD3D12::destroy() } } + timestampQueryHeap.destroy(); + timestampReadbackArea.destroy(); + shaderVisibleCbvSrvUavHeap.destroy(); for (int i = 0; i < QD3D12_FRAMES_IN_FLIGHT; ++i) @@ -591,7 +673,7 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const return false; #endif case QRhi::Timestamps: - return false; // ### + return true; case QRhi::Instancing: return true; case QRhi::CustomInstanceStepRate: @@ -664,6 +746,8 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ThreeDimensionalTextureMipmaps: return false; // we generate mipmaps ourselves with compute and this is not implemented + case QRhi::MultiView: + return caps.multiView; } return false; } @@ -820,15 +904,18 @@ void QRhiD3D12::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline } cbD->cmdList->IASetPrimitiveTopology(psD->topology); + + if (psD->viewInstanceMask) + cbD->cmdList->SetViewInstanceMask(psD->viewInstanceMask); } } -void QRhiD3D12::visitUniformBuffer(QD3D12Stage s, - const QRhiShaderResourceBinding::Data::UniformBufferData &d, - int, - int binding, - int dynamicOffsetCount, - const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) +void QD3D12CommandBuffer::visitUniformBuffer(QD3D12Stage s, + const QRhiShaderResourceBinding::Data::UniformBufferData &d, + int, + int binding, + int dynamicOffsetCount, + const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) { QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, d.buf); quint32 offset = d.offset; @@ -841,29 +928,30 @@ void QRhiD3D12::visitUniformBuffer(QD3D12Stage s, } } } - visitorData.cbufs[s].append({ bufD->handles[currentFrameSlot], offset }); + QRHI_RES_RHI(QRhiD3D12); + visitorData.cbufs[s].append({ bufD->handles[rhiD->currentFrameSlot], offset }); } -void QRhiD3D12::visitTexture(QD3D12Stage s, - const QRhiShaderResourceBinding::TextureAndSampler &d, - int) +void QD3D12CommandBuffer::visitTexture(QD3D12Stage s, + const QRhiShaderResourceBinding::TextureAndSampler &d, + int) { QD3D12Texture *texD = QRHI_RES(QD3D12Texture, d.tex); visitorData.srvs[s].append(texD->srv); } -void QRhiD3D12::visitSampler(QD3D12Stage s, - const QRhiShaderResourceBinding::TextureAndSampler &d, - int) +void QD3D12CommandBuffer::visitSampler(QD3D12Stage s, + const QRhiShaderResourceBinding::TextureAndSampler &d, + int) { QD3D12Sampler *samplerD = QRHI_RES(QD3D12Sampler, d.sampler); visitorData.samplers[s].append(samplerD->lookupOrCreateShaderVisibleDescriptor()); } -void QRhiD3D12::visitStorageBuffer(QD3D12Stage s, - const QRhiShaderResourceBinding::Data::StorageBufferData &d, - QD3D12ShaderResourceVisitor::StorageOp, - int) +void QD3D12CommandBuffer::visitStorageBuffer(QD3D12Stage s, + const QRhiShaderResourceBinding::Data::StorageBufferData &d, + QD3D12ShaderResourceVisitor::StorageOp, + int) { QD3D12Buffer *bufD = QRHI_RES(QD3D12Buffer, d.buf); // SPIRV-Cross generated HLSL uses RWByteAddressBuffer @@ -876,10 +964,10 @@ void QRhiD3D12::visitStorageBuffer(QD3D12Stage s, visitorData.uavs[s].append({ bufD->handles[0], uavDesc }); } -void QRhiD3D12::visitStorageImage(QD3D12Stage s, - const QRhiShaderResourceBinding::Data::StorageImageData &d, - QD3D12ShaderResourceVisitor::StorageOp, - int) +void QD3D12CommandBuffer::visitStorageImage(QD3D12Stage s, + const QRhiShaderResourceBinding::Data::StorageImageData &d, + QD3D12ShaderResourceVisitor::StorageOp, + int) { QD3D12Texture *texD = QRHI_RES(QD3D12Texture, d.tex); const bool isCube = texD->m_flags.testFlag(QRhiTexture::CubeMap); @@ -925,8 +1013,8 @@ void QRhiD3D12::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind QD3D12ShaderResourceBindings *srbD = QRHI_RES(QD3D12ShaderResourceBindings, srb); - for (int i = 0, ie = srbD->sortedBindings.size(); i != ie; ++i) { - const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings[i]); + for (int i = 0, ie = srbD->m_bindings.size(); i != ie; ++i) { + const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->m_bindings[i]); switch (b->type) { case QRhiShaderResourceBinding::UniformBuffer: { @@ -1038,14 +1126,15 @@ void QRhiD3D12::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind QD3D12ShaderResourceVisitor visitor(srbD, stageData, gfxPsD ? 5 : 1); + QD3D12CommandBuffer::VisitorData &visitorData(cbD->visitorData); visitorData = {}; using namespace std::placeholders; - visitor.uniformBuffer = std::bind(&QRhiD3D12::visitUniformBuffer, this, _1, _2, _3, _4, dynamicOffsetCount, dynamicOffsets); - visitor.texture = std::bind(&QRhiD3D12::visitTexture, this, _1, _2, _3); - visitor.sampler = std::bind(&QRhiD3D12::visitSampler, this, _1, _2, _3); - visitor.storageBuffer = std::bind(&QRhiD3D12::visitStorageBuffer, this, _1, _2, _3, _4); - visitor.storageImage = std::bind(&QRhiD3D12::visitStorageImage, this, _1, _2, _3, _4); + visitor.uniformBuffer = std::bind(&QD3D12CommandBuffer::visitUniformBuffer, cbD, _1, _2, _3, _4, dynamicOffsetCount, dynamicOffsets); + visitor.texture = std::bind(&QD3D12CommandBuffer::visitTexture, cbD, _1, _2, _3); + visitor.sampler = std::bind(&QD3D12CommandBuffer::visitSampler, cbD, _1, _2, _3); + visitor.storageBuffer = std::bind(&QD3D12CommandBuffer::visitStorageBuffer, cbD, _1, _2, _3, _4); + visitor.storageImage = std::bind(&QD3D12CommandBuffer::visitStorageImage, cbD, _1, _2, _3, _4); visitor.visit(); @@ -1401,8 +1490,24 @@ void QRhiD3D12::endExternal(QRhiCommandBuffer *cb) double QRhiD3D12::lastCompletedGpuTime(QRhiCommandBuffer *cb) { - Q_UNUSED(cb); - return 0; + QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb); + return cbD->lastGpuTime; +} + +static void calculateGpuTime(QD3D12CommandBuffer *cbD, + int timestampPairStartIndex, + const quint8 *readbackBufPtr, + quint64 timestampTicksPerSecond) +{ + const size_t byteOffset = timestampPairStartIndex * sizeof(quint64); + const quint64 *p = reinterpret_cast(readbackBufPtr + byteOffset); + const quint64 startTime = *p++; + const quint64 endTime = *p; + if (startTime < endTime) { + const quint64 ticks = endTime - startTime; + const double timeSec = ticks / double(timestampTicksPerSecond); + cbD->lastGpuTime = timeSec; + } } QRhi::FrameOpResult QRhiD3D12::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) @@ -1448,6 +1553,16 @@ QRhi::FrameOpResult QRhiD3D12::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF swapChainD->rtWrapper.d.dsv = swapChainD->ds ? swapChainD->ds->dsv.cpuHandle : D3D12_CPU_DESCRIPTOR_HANDLE { 0 }; + if (swapChainD->stereo) { + swapChainD->rtWrapperRight.d.rtv[0] = swapChainD->sampleDesc.Count > 1 + ? swapChainD->msaaRtvs[swapChainD->currentBackBufferIndex].cpuHandle + : swapChainD->rtvsRight[swapChainD->currentBackBufferIndex].cpuHandle; + + swapChainD->rtWrapperRight.d.dsv = + swapChainD->ds ? swapChainD->ds->dsv.cpuHandle : D3D12_CPU_DESCRIPTOR_HANDLE{ 0 }; + } + + // Time to release things that are marked for currentFrameSlot since due to // the wait above we know that the previous commands on the GPU for this // slot must have finished already. @@ -1465,6 +1580,20 @@ QRhi::FrameOpResult QRhiD3D12::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls + if (timestampQueryHeap.isValid() && timestampTicksPerSecond) { + // Read the timestamps for the previous frame for this slot. (the + // ResolveQuery() should have completed by now due to the wait above) + const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT; + calculateGpuTime(cbD, + timestampPairStartIndex, + timestampReadbackArea.mem.p, + timestampTicksPerSecond); + // Write the start timestamp for this frame for this slot. + cbD->cmdList->EndQuery(timestampQueryHeap.heap, + D3D12_QUERY_TYPE_TIMESTAMP, + timestampPairStartIndex); + } + return QRhi::FrameOpSuccess; } @@ -1489,7 +1618,20 @@ QRhi::FrameOpResult QRhiD3D12::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame barrierGen.addTransitionBarrier(backBufferResourceHandle, D3D12_RESOURCE_STATE_PRESENT); barrierGen.enqueueBufferedTransitionBarriers(cbD); - ID3D12GraphicsCommandList *cmdList = cbD->cmdList; + if (timestampQueryHeap.isValid()) { + const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT; + cbD->cmdList->EndQuery(timestampQueryHeap.heap, + D3D12_QUERY_TYPE_TIMESTAMP, + timestampPairStartIndex + 1); + cbD->cmdList->ResolveQueryData(timestampQueryHeap.heap, + D3D12_QUERY_TYPE_TIMESTAMP, + timestampPairStartIndex, + 2, + timestampReadbackArea.mem.buffer, + timestampPairStartIndex * sizeof(quint64)); + } + + ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList; HRESULT hr = cmdList->Close(); if (FAILED(hr)) { qWarning("Failed to close command list: %s", @@ -1577,6 +1719,12 @@ QRhi::FrameOpResult QRhiD3D12::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi: bindShaderVisibleHeaps(cbD); + if (timestampQueryHeap.isValid() && timestampTicksPerSecond) { + cbD->cmdList->EndQuery(timestampQueryHeap.heap, + D3D12_QUERY_TYPE_TIMESTAMP, + currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT); + } + offscreenActive = true; *cb = cbD; @@ -1590,7 +1738,20 @@ QRhi::FrameOpResult QRhiD3D12::endOffscreenFrame(QRhi::EndFrameFlags flags) offscreenActive = false; QD3D12CommandBuffer *cbD = offscreenCb[currentFrameSlot]; - ID3D12GraphicsCommandList *cmdList = cbD->cmdList; + if (timestampQueryHeap.isValid()) { + const int timestampPairStartIndex = currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT; + cbD->cmdList->EndQuery(timestampQueryHeap.heap, + D3D12_QUERY_TYPE_TIMESTAMP, + timestampPairStartIndex + 1); + cbD->cmdList->ResolveQueryData(timestampQueryHeap.heap, + D3D12_QUERY_TYPE_TIMESTAMP, + timestampPairStartIndex, + 2, + timestampReadbackArea.mem.buffer, + timestampPairStartIndex * sizeof(quint64)); + } + + ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList; HRESULT hr = cmdList->Close(); if (FAILED(hr)) { qWarning("Failed to close command list: %s", @@ -1610,6 +1771,14 @@ QRhi::FrameOpResult QRhiD3D12::endOffscreenFrame(QRhi::EndFrameFlags flags) // previous) frame is safe since we waited for completion above. finishActiveReadbacks(true); + // the timestamp query results should be available too, given the wait + if (timestampQueryHeap.isValid()) { + calculateGpuTime(cbD, + currentFrameSlot * QD3D12_FRAMES_IN_FLIGHT, + timestampReadbackArea.mem.p, + timestampTicksPerSecond); + } + return QRhi::FrameOpSuccess; } @@ -1631,7 +1800,7 @@ QRhi::FrameOpResult QRhiD3D12::finish() Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass); - ID3D12GraphicsCommandList *cmdList = cbD->cmdList; + ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList; HRESULT hr = cmdList->Close(); if (FAILED(hr)) { qWarning("Failed to close command list: %s", @@ -1816,13 +1985,16 @@ void QRhiD3D12::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource barrierGen.addTransitionBarrier(dstTexD->handle, D3D12_RESOURCE_STATE_RESOLVE_DEST); barrierGen.enqueueBufferedTransitionBarriers(cbD); - const UINT srcSubresource = calcSubresource(0, UINT(colorAtt.layer()), 1); - const UINT dstSubresource = calcSubresource(UINT(colorAtt.resolveLevel()), - UINT(colorAtt.resolveLayer()), - dstTexD->mipLevelCount); - cbD->cmdList->ResolveSubresource(dstRes->resource, dstSubresource, - srcRes->resource, srcSubresource, - dstTexD->dxgiFormat); + const UINT resolveCount = colorAtt.multiViewCount() >= 2 ? colorAtt.multiViewCount() : 1; + for (UINT resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) { + const UINT srcSubresource = calcSubresource(0, UINT(colorAtt.layer()) + resolveIdx, 1); + const UINT dstSubresource = calcSubresource(UINT(colorAtt.resolveLevel()), + UINT(colorAtt.resolveLayer()) + resolveIdx, + dstTexD->mipLevelCount); + cbD->cmdList->ResolveSubresource(dstRes->resource, dstSubresource, + srcRes->resource, srcSubresource, + dstTexD->dxgiFormat); + } } } @@ -2071,6 +2243,36 @@ void QD3D12CpuDescriptorPool::release(const QD3D12Descriptor &descriptor, quint3 quint64(descriptor.cpuHandle.ptr)); } +bool QD3D12QueryHeap::create(ID3D12Device *device, + quint32 queryCount, + D3D12_QUERY_HEAP_TYPE heapType) +{ + capacity = queryCount; + + D3D12_QUERY_HEAP_DESC heapDesc = {}; + heapDesc.Type = heapType; + heapDesc.Count = capacity; + + HRESULT hr = device->CreateQueryHeap(&heapDesc, __uuidof(ID3D12QueryHeap), reinterpret_cast(&heap)); + if (FAILED(hr)) { + qWarning("Failed to create query heap: %s", qPrintable(QSystemError::windowsComString(hr))); + heap = nullptr; + capacity = 0; + return false; + } + + return true; +} + +void QD3D12QueryHeap::destroy() +{ + if (heap) { + heap->Release(); + heap = nullptr; + } + capacity = 0; +} + bool QD3D12StagingArea::create(QRhiD3D12 *rhi, quint32 capacity, D3D12_HEAP_TYPE heapType) { Q_ASSERT(heapType == D3D12_HEAP_TYPE_UPLOAD || heapType == D3D12_HEAP_TYPE_READBACK); @@ -2411,8 +2613,8 @@ static inline QPair mapBinding(int binding, const QShader::NativeResou void QD3D12ShaderResourceVisitor::visit() { - for (int bindingIdx = 0, bindingCount = srb->sortedBindings.count(); bindingIdx != bindingCount; ++bindingIdx) { - const QRhiShaderResourceBinding &b(srb->sortedBindings[bindingIdx]); + for (int bindingIdx = 0, bindingCount = srb->m_bindings.count(); bindingIdx != bindingCount; ++bindingIdx) { + const QRhiShaderResourceBinding &b(srb->m_bindings[bindingIdx]); const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b); for (int stageIdx = 0; stageIdx < stageCount; ++stageIdx) { @@ -2566,6 +2768,7 @@ bool QD3D12MipmapGenerator::create(QRhiD3D12 *rhiD) // b0 rootParams[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; rootParams[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParams[0].Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC; // t0 descriptorRanges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; @@ -2944,7 +3147,7 @@ DXGI_SAMPLE_DESC QRhiD3D12::effectiveSampleDesc(int sampleCount, DXGI_FORMAT for return desc; } -bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList **cmdList) +bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList) { ID3D12CommandAllocator *cmdAlloc = cmdAllocators[currentFrameSlot]; if (!*cmdList) { @@ -2952,7 +3155,7 @@ bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList ** D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, - __uuidof(ID3D12GraphicsCommandList), + __uuidof(ID3D12GraphicsCommandList1), reinterpret_cast(cmdList)); if (FAILED(hr)) { qWarning("Failed to create command list: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -4469,19 +4672,21 @@ bool QD3D12TextureRenderTarget::create() qWarning("Could not look up texture handle for render target"); return false; } + const bool isMultiView = it->multiViewCount() >= 2; + UINT layerCount = isMultiView ? UINT(it->multiViewCount()) : 1; D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags()); if (texD->flags().testFlag(QRhiTexture::CubeMap)) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture2DArray.ArraySize = 1; + rtvDesc.Texture2DArray.ArraySize = layerCount; } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) { if (texD->flags().testFlag(QRhiTexture::TextureArray)) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY; rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture1DArray.ArraySize = 1; + rtvDesc.Texture1DArray.ArraySize = layerCount; } else { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level()); @@ -4490,18 +4695,18 @@ bool QD3D12TextureRenderTarget::create() if (texD->sampleDesc.Count > 1) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture2DMSArray.ArraySize = 1; + rtvDesc.Texture2DMSArray.ArraySize = layerCount; } else { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture2DArray.ArraySize = 1; + rtvDesc.Texture2DArray.ArraySize = layerCount; } } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer()); - rtvDesc.Texture3D.WSize = 1; + rtvDesc.Texture3D.WSize = layerCount; } else { if (texD->sampleDesc.Count > 1) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; @@ -4634,8 +4839,6 @@ QD3D12ShaderResourceBindings::~QD3D12ShaderResourceBindings() void QD3D12ShaderResourceBindings::destroy() { - sortedBindings.clear(); - QRHI_RES_RHI(QRhiD3D12); if (rhiD) rhiD->unregisterResource(this); @@ -4643,20 +4846,14 @@ void QD3D12ShaderResourceBindings::destroy() bool QD3D12ShaderResourceBindings::create() { - if (!sortedBindings.isEmpty()) - destroy(); - QRHI_RES_RHI(QRhiD3D12); if (!rhiD->sanityCheckShaderResourceBindings(this)) return false; rhiD->updateLayoutDesc(this); - std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings)); - std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan); - hasDynamicOffset = false; - for (const QRhiShaderResourceBinding &b : sortedBindings) { + for (const QRhiShaderResourceBinding &b : std::as_const(m_bindings)) { const QRhiShaderResourceBinding::Data *bd = QRhiImplementation::shaderResourceBindingData(b); if (bd->type == QRhiShaderResourceBinding::UniformBuffer && bd->u.ubuf.hasDynamicOffset) { hasDynamicOffset = true; @@ -4679,11 +4876,7 @@ bool QD3D12ShaderResourceBindings::create() void QD3D12ShaderResourceBindings::updateResources(UpdateFlags flags) { - sortedBindings.clear(); - std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings)); - if (!flags.testFlag(BindingsAreSorted)) - std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan); - + Q_UNUSED(flags); generation += 1; } @@ -4701,6 +4894,7 @@ void QD3D12ShaderResourceBindings::visitUniformBuffer(QD3D12Stage s, rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; rootParam.ShaderVisibility = qd3d12_stageToVisibility(s); rootParam.Descriptor.ShaderRegister = shaderRegister; + rootParam.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC; visitorData.cbParams[s].append(rootParam); } @@ -4788,6 +4982,9 @@ QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D1 { QRHI_RES_RHI(QRhiD3D12); + if (!myD3D12SerializeVersionedRootSignature) + return {}; + // It's not just that the root signature has to be tied to the pipeline // (cannot just freely create it like e.g. with Vulkan where one just // creates a descriptor layout 1:1 with the QRhiShaderResourceBindings' @@ -4871,13 +5068,6 @@ QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D1 } rsDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAGS(rsFlags); - if (!myD3D12SerializeVersionedRootSignature) - myD3D12SerializeVersionedRootSignature = - (D3D12SerializeVersionedRootSignatureFunc)::GetProcAddress(::GetModuleHandle(L"D3d12"), "D3D12SerializeVersionedRootSignature"); - - if (!myD3D12SerializeVersionedRootSignature) - return {}; - ID3DBlob *signature = nullptr; HRESULT hr = myD3D12SerializeVersionedRootSignature(&rsDesc, &signature, nullptr); if (FAILED(hr)) { @@ -4899,9 +5089,14 @@ QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D1 return QD3D12RootSignature::addToPool(&rhiD->rootSignaturePool, rootSig); } -// For now we mirror exactly what's done in the D3D11 backend, meaning we use -// the old shader compiler (so like fxc, not dxc) to generate shader model 5.0 -// output. Some day this should be moved to the new compiler and DXIL. +// For shader model < 6.0 we do the same as the D3D11 backend: use the old +// compiler (D3DCompile) to generate DXBC, just as qsb does (when -c is passed) +// by invoking fxc, not dxc. For SM >= 6.0 we have to use the new compiler and +// work with DXIL. And that involves IDxcCompiler and needs the presence of +// dxcompiler.dll and dxil.dll at runtime. Plus there's a chance we have +// ancient SDK headers when not using MSVC. So this is heavily optional, +// meaning support for dxc can be disabled both at build time (no dxcapi.h) and +// at run time (no DLLs). static inline void makeHlslTargetString(char target[7], const char stage[3], int version) { @@ -4916,9 +5111,139 @@ static inline void makeHlslTargetString(char target[7], const char stage[3], int target[6] = '\0'; } +enum class HlslCompileFlag +{ + WithDebugInfo = 0x01 +}; + +static QByteArray legacyCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error) +{ + static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile(); + if (!d3dCompile) { + qWarning("Unable to resolve function D3DCompile()"); + return QByteArray(); + } + + ID3DBlob *bytecode = nullptr; + ID3DBlob *errors = nullptr; + UINT d3dCompileFlags = 0; + if (flags & int(HlslCompileFlag::WithDebugInfo)) + d3dCompileFlags |= D3DCOMPILE_DEBUG; + + HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()), + nullptr, nullptr, nullptr, + hlslSource.entryPoint().constData(), target, d3dCompileFlags, 0, &bytecode, &errors); + if (FAILED(hr) || !bytecode) { + qWarning("HLSL shader compilation failed: 0x%x", uint(hr)); + if (errors) { + *error = QString::fromUtf8(static_cast(errors->GetBufferPointer()), + int(errors->GetBufferSize())); + errors->Release(); + } + return QByteArray(); + } + + QByteArray result; + result.resize(int(bytecode->GetBufferSize())); + memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size())); + bytecode->Release(); + return result; +} + +#ifdef QRHI_D3D12_HAS_DXC + +#ifndef DXC_CP_UTF8 +#define DXC_CP_UTF8 65001 +#endif + +#ifndef DXC_ARG_DEBUG +#define DXC_ARG_DEBUG L"-Zi" +#endif + +static QByteArray dxcCompile(const QShaderCode &hlslSource, const char *target, int flags, QString *error) +{ + static std::pair dxc = QRhiD3D::createDxcCompiler(); + IDxcCompiler *compiler = dxc.first; + if (!compiler) { + qWarning("Unable to instantiate IDxcCompiler. Likely no dxcompiler.dll and dxil.dll present. " + "Use windeployqt or try https://github.com/microsoft/DirectXShaderCompiler/releases"); + return QByteArray(); + } + IDxcLibrary *library = dxc.second; + if (!library) + return QByteArray(); + + IDxcBlobEncoding *sourceBlob = nullptr; + HRESULT hr = library->CreateBlobWithEncodingOnHeapCopy(hlslSource.shader().constData(), + UINT32(hlslSource.shader().size()), + DXC_CP_UTF8, + &sourceBlob); + if (FAILED(hr)) { + qWarning("Failed to create source blob for dxc: 0x%x (%s)", + uint(hr), + qPrintable(QSystemError::windowsComString(hr))); + return QByteArray(); + } + + const QString entryPointStr = QString::fromLatin1(hlslSource.entryPoint()); + const QString targetStr = QString::fromLatin1(target); + + QVarLengthArray argPtrs; + QString debugArg; + if (flags & int(HlslCompileFlag::WithDebugInfo)) { + debugArg = QString::fromUtf16(reinterpret_cast(DXC_ARG_DEBUG)); + argPtrs.append(reinterpret_cast(debugArg.utf16())); + } + + IDxcOperationResult *result = nullptr; + hr = compiler->Compile(sourceBlob, + nullptr, + reinterpret_cast(entryPointStr.utf16()), + reinterpret_cast(targetStr.utf16()), + argPtrs.data(), argPtrs.count(), + nullptr, 0, + nullptr, + &result); + sourceBlob->Release(); + if (SUCCEEDED(hr)) + result->GetStatus(&hr); + if (FAILED(hr)) { + qWarning("HLSL shader compilation failed: 0x%x (%s)", + uint(hr), + qPrintable(QSystemError::windowsComString(hr))); + if (result) { + IDxcBlobEncoding *errorsBlob = nullptr; + if (SUCCEEDED(result->GetErrorBuffer(&errorsBlob))) { + if (errorsBlob) { + *error = QString::fromUtf8(static_cast(errorsBlob->GetBufferPointer()), + int(errorsBlob->GetBufferSize())); + errorsBlob->Release(); + } + } + } + return QByteArray(); + } + + IDxcBlob *bytecode = nullptr; + if FAILED(result->GetResult(&bytecode)) { + qWarning("No result from IDxcCompiler: 0x%x (%s)", + uint(hr), + qPrintable(QSystemError::windowsComString(hr))); + return QByteArray(); + } + + QByteArray ba; + ba.resize(int(bytecode->GetBufferSize())); + memcpy(ba.data(), bytecode->GetBufferPointer(), size_t(ba.size())); + bytecode->Release(); + return ba; +} + +#endif // QRHI_D3D12_HAS_DXC + static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, - UINT flags, + int flags, QString *error, QShaderKey *usedShaderKey) { @@ -4975,33 +5300,17 @@ static QByteArray compileHlslShaderSource(const QShader &shader, break; } - static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile(); - if (!d3dCompile) { - qWarning("Unable to resolve function D3DCompile()"); - return QByteArray(); + if (key.sourceVersion().version() >= 60) { +#ifdef QRHI_D3D12_HAS_DXC + return dxcCompile(hlslSource, target, flags, error); +#else + qWarning("Attempted to runtime-compile HLSL source code for shader model >= 6.0 " + "but the Qt build has no support for DXC. " + "Rebuild Qt with a recent Windows SDK or switch to an MSVC build."); +#endif } - ID3DBlob *bytecode = nullptr; - ID3DBlob *errors = nullptr; - HRESULT hr = d3dCompile(hlslSource.shader().constData(), SIZE_T(hlslSource.shader().size()), - nullptr, nullptr, nullptr, - hlslSource.entryPoint().constData(), target, flags, 0, &bytecode, &errors); - if (FAILED(hr) || !bytecode) { - qWarning("HLSL shader compilation failed: 0x%x", uint(hr)); - if (errors) { - *error = QString::fromUtf8(static_cast(errors->GetBufferPointer()), - int(errors->GetBufferSize())); - errors->Release(); - } - return QByteArray(); - } - - QByteArray result; - result.resize(int(bytecode->GetBufferSize())); - memcpy(result.data(), bytecode->GetBufferPointer(), size_t(result.size())); - bytecode->Release(); - - return result; + return legacyCompile(hlslSource, target, flags, error); } static inline UINT8 toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c) @@ -5291,16 +5600,16 @@ bool QD3D12GraphicsPipeline::create() } else { QString error; QShaderKey shaderKey; - UINT compileFlags = 0; + int compileFlags = 0; if (m_flags.testFlag(CompileShadersWithDebugInfo)) - compileFlags |= D3DCOMPILE_DEBUG; + compileFlags |= int(HlslCompileFlag::WithDebugInfo); const QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), compileFlags, &error, &shaderKey); if (bytecode.isEmpty()) { - qWarning("HLSL compute shader compilation failed: %s", qPrintable(error)); + qWarning("HLSL graphics shader compilation failed: %s", qPrintable(error)); return false; } @@ -5330,83 +5639,26 @@ bool QD3D12GraphicsPipeline::create() QD3D12RenderPassDescriptor *rpD = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc); const DXGI_SAMPLE_DESC sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, DXGI_FORMAT(rpD->colorFormat[0])); - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.pRootSignature = rootSig; - for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) { - const int d3dStage = qd3d12_stage(shaderStage.type()); - switch (d3dStage) { - case VS: - psoDesc.VS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.VS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case HS: - psoDesc.HS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.HS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case DS: - psoDesc.DS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.DS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case GS: - psoDesc.GS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.GS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case PS: - psoDesc.PS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.PS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - default: - Q_UNREACHABLE(); - break; - } - } + struct { + QD3D12PipelineStateSubObject rootSig; + QD3D12PipelineStateSubObject inputLayout; + QD3D12PipelineStateSubObject primitiveTopology; + QD3D12PipelineStateSubObject VS; + QD3D12PipelineStateSubObject HS; + QD3D12PipelineStateSubObject DS; + QD3D12PipelineStateSubObject GS; + QD3D12PipelineStateSubObject PS; + QD3D12PipelineStateSubObject rasterizerState; + QD3D12PipelineStateSubObject depthStencilState; + QD3D12PipelineStateSubObject blendState; + QD3D12PipelineStateSubObject rtFormats; + QD3D12PipelineStateSubObject dsFormat; + QD3D12PipelineStateSubObject sampleDesc; + QD3D12PipelineStateSubObject sampleMask; + QD3D12PipelineStateSubObject viewInstancingDesc; + } stream; - psoDesc.BlendState.IndependentBlendEnable = m_targetBlends.count() > 1; - for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) { - const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]); - D3D12_RENDER_TARGET_BLEND_DESC blend = {}; - blend.BlendEnable = b.enable; - blend.SrcBlend = toD3DBlendFactor(b.srcColor, true); - blend.DestBlend = toD3DBlendFactor(b.dstColor, true); - blend.BlendOp = toD3DBlendOp(b.opColor); - blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false); - blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false); - blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha); - blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite); - psoDesc.BlendState.RenderTarget[i] = blend; - } - if (m_targetBlends.isEmpty()) { - D3D12_RENDER_TARGET_BLEND_DESC blend = {}; - blend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; - psoDesc.BlendState.RenderTarget[0] = blend; - } - - psoDesc.SampleMask = 0xFFFFFFFF; - - psoDesc.RasterizerState.FillMode = toD3DFillMode(m_polygonMode); - psoDesc.RasterizerState.CullMode = toD3DCullMode(m_cullMode); - psoDesc.RasterizerState.FrontCounterClockwise = m_frontFace == CCW; - psoDesc.RasterizerState.DepthBias = m_depthBias; - psoDesc.RasterizerState.SlopeScaledDepthBias = m_slopeScaledDepthBias; - psoDesc.RasterizerState.DepthClipEnable = TRUE; - psoDesc.RasterizerState.MultisampleEnable = sampleDesc.Count > 1; - - psoDesc.DepthStencilState.DepthEnable = m_depthTest; - psoDesc.DepthStencilState.DepthWriteMask = m_depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; - psoDesc.DepthStencilState.DepthFunc = toD3DCompareOp(m_depthOp); - psoDesc.DepthStencilState.StencilEnable = m_stencilTest; - if (m_stencilTest) { - psoDesc.DepthStencilState.StencilReadMask = UINT8(m_stencilReadMask); - psoDesc.DepthStencilState.StencilWriteMask = UINT8(m_stencilWriteMask); - psoDesc.DepthStencilState.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp); - psoDesc.DepthStencilState.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp); - psoDesc.DepthStencilState.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp); - psoDesc.DepthStencilState.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp); - psoDesc.DepthStencilState.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp); - psoDesc.DepthStencilState.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp); - psoDesc.DepthStencilState.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp); - psoDesc.DepthStencilState.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp); - } + stream.rootSig.object = rootSig; QVarLengthArray inputDescs; QByteArrayList matrixSliceSemantics; @@ -5444,24 +5696,113 @@ bool QD3D12GraphicsPipeline::create() inputDescs.append(desc); } } - if (!inputDescs.isEmpty()) { - psoDesc.InputLayout.pInputElementDescs = inputDescs.constData(); - psoDesc.InputLayout.NumElements = inputDescs.count(); - } - psoDesc.PrimitiveTopologyType = toD3DTopologyType(m_topology); + stream.inputLayout.object.NumElements = inputDescs.count(); + stream.inputLayout.object.pInputElementDescs = inputDescs.isEmpty() ? nullptr : inputDescs.constData(); + + stream.primitiveTopology.object = toD3DTopologyType(m_topology); topology = toD3DTopology(m_topology, m_patchControlPointCount); - psoDesc.NumRenderTargets = rpD->colorAttachmentCount; + for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) { + const int d3dStage = qd3d12_stage(shaderStage.type()); + switch (d3dStage) { + case VS: + stream.VS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.VS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case HS: + stream.HS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.HS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case DS: + stream.DS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.DS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case GS: + stream.GS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.GS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case PS: + stream.PS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.PS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + default: + Q_UNREACHABLE(); + break; + } + } + + stream.rasterizerState.object.FillMode = toD3DFillMode(m_polygonMode); + stream.rasterizerState.object.CullMode = toD3DCullMode(m_cullMode); + stream.rasterizerState.object.FrontCounterClockwise = m_frontFace == CCW; + stream.rasterizerState.object.DepthBias = m_depthBias; + stream.rasterizerState.object.SlopeScaledDepthBias = m_slopeScaledDepthBias; + stream.rasterizerState.object.DepthClipEnable = TRUE; + stream.rasterizerState.object.MultisampleEnable = sampleDesc.Count > 1; + + stream.depthStencilState.object.DepthEnable = m_depthTest; + stream.depthStencilState.object.DepthWriteMask = m_depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; + stream.depthStencilState.object.DepthFunc = toD3DCompareOp(m_depthOp); + stream.depthStencilState.object.StencilEnable = m_stencilTest; + if (m_stencilTest) { + stream.depthStencilState.object.StencilReadMask = UINT8(m_stencilReadMask); + stream.depthStencilState.object.StencilWriteMask = UINT8(m_stencilWriteMask); + stream.depthStencilState.object.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp); + stream.depthStencilState.object.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp); + stream.depthStencilState.object.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp); + stream.depthStencilState.object.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp); + stream.depthStencilState.object.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp); + stream.depthStencilState.object.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp); + stream.depthStencilState.object.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp); + stream.depthStencilState.object.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp); + } + + stream.blendState.object.IndependentBlendEnable = m_targetBlends.count() > 1; + for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) { + const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]); + D3D12_RENDER_TARGET_BLEND_DESC blend = {}; + blend.BlendEnable = b.enable; + blend.SrcBlend = toD3DBlendFactor(b.srcColor, true); + blend.DestBlend = toD3DBlendFactor(b.dstColor, true); + blend.BlendOp = toD3DBlendOp(b.opColor); + blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false); + blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false); + blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha); + blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite); + stream.blendState.object.RenderTarget[i] = blend; + } + if (m_targetBlends.isEmpty()) { + D3D12_RENDER_TARGET_BLEND_DESC blend = {}; + blend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + stream.blendState.object.RenderTarget[0] = blend; + } + + stream.rtFormats.object.NumRenderTargets = rpD->colorAttachmentCount; for (int i = 0; i < rpD->colorAttachmentCount; ++i) - psoDesc.RTVFormats[i] = DXGI_FORMAT(rpD->colorFormat[i]); - psoDesc.DSVFormat = rpD->hasDepthStencil ? DXGI_FORMAT(rpD->dsFormat) : DXGI_FORMAT_UNKNOWN; - psoDesc.SampleDesc = sampleDesc; + stream.rtFormats.object.RTFormats[i] = DXGI_FORMAT(rpD->colorFormat[i]); + + stream.dsFormat.object = rpD->hasDepthStencil ? DXGI_FORMAT(rpD->dsFormat) : DXGI_FORMAT_UNKNOWN; + + stream.sampleDesc.object = sampleDesc; + + stream.sampleMask.object = 0xFFFFFFFF; + + viewInstanceMask = 0; + const bool isMultiView = m_multiViewCount >= 2; + stream.viewInstancingDesc.object.ViewInstanceCount = isMultiView ? m_multiViewCount : 0; + QVarLengthArray viewInstanceLocations; + if (isMultiView) { + for (int i = 0; i < m_multiViewCount; ++i) { + viewInstanceMask |= (1 << i); + viewInstanceLocations.append({ 0, UINT(i) }); + } + stream.viewInstancingDesc.object.pViewInstanceLocations = viewInstanceLocations.constData(); + } + + const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream }; ID3D12PipelineState *pso = nullptr; - HRESULT hr = rhiD->dev->CreateGraphicsPipelineState(&psoDesc, - __uuidof(ID3D12PipelineState), - reinterpret_cast(&pso)); + HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast(&pso)); if (FAILED(hr)) { qWarning("Failed to create graphics pipeline state: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -5525,9 +5866,9 @@ bool QD3D12ComputePipeline::create() } else { QString error; QShaderKey shaderKey; - UINT compileFlags = 0; + int compileFlags = 0; if (m_flags.testFlag(CompileShadersWithDebugInfo)) - compileFlags |= D3DCOMPILE_DEBUG; + compileFlags |= int(HlslCompileFlag::WithDebugInfo); const QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), compileFlags, @@ -5560,14 +5901,16 @@ bool QD3D12ComputePipeline::create() return false; } - D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.pRootSignature = rootSig; - psoDesc.CS.pShaderBytecode = shaderBytecode.constData(); - psoDesc.CS.BytecodeLength = shaderBytecode.size(); + struct { + QD3D12PipelineStateSubObject rootSig; + QD3D12PipelineStateSubObject CS; + } stream; + stream.rootSig.object = rootSig; + stream.CS.object.pShaderBytecode = shaderBytecode.constData(); + stream.CS.object.BytecodeLength = shaderBytecode.size(); + const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream }; ID3D12PipelineState *pso = nullptr; - HRESULT hr = rhiD->dev->CreateComputePipelineState(&psoDesc, - __uuidof(ID3D12PipelineState), - reinterpret_cast(&pso)); + HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast(&pso)); if (FAILED(hr)) { qWarning("Failed to create compute pipeline state: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -5719,6 +6062,7 @@ int QD3D12SwapChainRenderTarget::sampleCount() const QD3D12SwapChain::QD3D12SwapChain(QRhiImplementation *rhi) : QRhiSwapChain(rhi), rtWrapper(rhi, this), + rtWrapperRight(rhi, this), cbWrapper(rhi) { } @@ -5775,6 +6119,8 @@ void QD3D12SwapChain::releaseBuffers() for (UINT i = 0; i < BUFFER_COUNT; ++i) { rhiD->resourcePool.remove(colorBuffers[i]); rhiD->rtvPool.release(rtvs[i], 1); + if (stereo) + rhiD->rtvPool.release(rtvsRight[i], 1); if (!msaaBuffers[i].isNull()) rhiD->resourcePool.remove(msaaBuffers[i]); if (msaaRtvs[i].isValid()) @@ -5809,50 +6155,17 @@ QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget() return &rtWrapper; } +QRhiRenderTarget *QD3D12SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer) +{ + return !stereo || targetBuffer == StereoTargetBuffer::LeftBuffer ? &rtWrapper : &rtWrapperRight; +} + QSize QD3D12SwapChain::surfacePixelSize() { Q_ASSERT(m_window); return m_window->size() * m_window->devicePixelRatio(); } -static bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result) -{ - bool ok = false; - QRect wr = w->geometry(); - wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio()); - const QPoint center = wr.center(); - IDXGIOutput *currentOutput = nullptr; - IDXGIOutput *output = nullptr; - for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) { - DXGI_OUTPUT_DESC desc; - output->GetDesc(&desc); - const RECT r = desc.DesktopCoordinates; - const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); - if (dr.contains(center)) { - currentOutput = output; - break; - } else { - output->Release(); - } - } - if (currentOutput) { - ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(result))); - currentOutput->Release(); - } - return ok; -} - -static bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result) -{ - bool ok = false; - IDXGIOutput6 *out6 = nullptr; - if (output6ForWindow(w, adapter, &out6)) { - ok = SUCCEEDED(out6->GetDesc1(result)); - out6->Release(); - } - return ok; -} - bool QD3D12SwapChain::isFormatSupported(Format f) { if (f == SDR) @@ -5865,7 +6178,7 @@ bool QD3D12SwapChain::isFormatSupported(Format f) QRHI_RES_RHI(QRhiD3D12); DXGI_OUTPUT_DESC1 desc1; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) { + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) { if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10; } @@ -5876,14 +6189,16 @@ bool QD3D12SwapChain::isFormatSupported(Format f) QRhiSwapChainHdrInfo QD3D12SwapChain::hdrInfo() { QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo(); + // Must use m_window, not window, given this may be called before createOrResize(). if (m_window) { QRHI_RES_RHI(QRhiD3D12); DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { - info.isHardCodedDefaults = false; + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits; info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance; info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance; + info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits + info.sdrWhiteLevel = QRhiD3D::sdrWhiteLevelInNits(hdrOutputDesc); } } return info; @@ -5926,7 +6241,7 @@ void QD3D12SwapChain::chooseFormats() hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR DXGI_OUTPUT_DESC1 hdrOutputDesc; QRHI_RES_RHI(QRhiD3D12); - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { switch (m_format) { @@ -5976,6 +6291,7 @@ bool QD3D12SwapChain::createOrResize() HWND hwnd = reinterpret_cast(window->winId()); HRESULT hr; QRHI_RES_RHI(QRhiD3D12); + stereo = m_window->format().stereo() && rhiD->dxgiFactory->IsWindowedStereoEnabled(); if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) { if (rhiD->ensureDirectCompositionDevice()) { @@ -6018,6 +6334,7 @@ bool QD3D12SwapChain::createOrResize() desc.Flags = swapChainFlags; desc.Scaling = DXGI_SCALING_NONE; desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.Stereo = stereo; if (dcompVisual) { // With DirectComposition setting AlphaMode to STRAIGHT fails the @@ -6131,6 +6448,16 @@ bool QD3D12SwapChain::createOrResize() rtvDesc.Format = srgbAdjustedColorFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvs[i].cpuHandle); + + if (stereo) { + rtvsRight[i] = rhiD->rtvPool.allocate(1); + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = srgbAdjustedColorFormat; + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.ArraySize = 1; + rtvDesc.Texture2DArray.FirstArraySlice = 1; + rhiD->dev->CreateRenderTargetView(colorBuffer, &rtvDesc, rtvsRight[i].cpuHandle); + } } if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) { @@ -6203,6 +6530,15 @@ bool QD3D12SwapChain::createOrResize() rtD->d.colorAttCount = 1; rtD->d.dsAttCount = m_depthStencil ? 1 : 0; + rtWrapperRight.setRenderPassDescriptor(m_renderPassDesc); + QD3D12SwapChainRenderTarget *rtDr = QRHI_RES(QD3D12SwapChainRenderTarget, &rtWrapperRight); + rtDr->d.rp = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc); + rtDr->d.pixelSize = pixelSize; + rtDr->d.dpr = float(window->devicePixelRatio()); + rtDr->d.sampleCount = int(sampleDesc.Count); + rtDr->d.colorAttCount = 1; + rtDr->d.dsAttCount = m_depthStencil ? 1 : 0; + if (needsRegistration) { rhiD->swapchains.insert(this); rhiD->registerResource(this); @@ -6212,3 +6548,5 @@ bool QD3D12SwapChain::createOrResize() } QT_END_NAMESPACE + +#endif // __ID3D12Device2_INTERFACE_DEFINED__ diff --git a/qtbase/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/qtbase/src/gui/text/windows/qwindowsfontdatabasebase.cpp index 18155cb0..fe2070a0 100644 --- a/qtbase/src/gui/text/windows/qwindowsfontdatabasebase.cpp +++ b/qtbase/src/gui/text/windows/qwindowsfontdatabasebase.cpp @@ -359,10 +359,10 @@ namespace { { } - inline void addKey(const void *key, const QByteArray &fontData) + inline void addKey(const QByteArray &fontData) { - Q_ASSERT(!m_fontDatas.contains(key)); - m_fontDatas.insert(key, fontData); + if (!m_fontDatas.contains(fontData.data())) + m_fontDatas.insert(fontData.data(), fontData); } inline void removeKey(const void *key) @@ -378,6 +378,11 @@ namespace { UINT32 fontFileReferenceKeySize, OUT IDWriteFontFileStream **fontFileStream) override; + void clear() + { + m_fontDatas.clear(); + } + private: ULONG m_referenceCount; QHash m_fontDatas; @@ -435,53 +440,63 @@ namespace { return S_OK; } - class CustomFontFileLoader - { - public: - CustomFontFileLoader(IDWriteFactory *factory) - { - m_directWriteFactory = factory; - - if (m_directWriteFactory) { - m_directWriteFactory->AddRef(); - - m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); - m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); - } - } - - ~CustomFontFileLoader() - { - if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr) - m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); - - if (m_directWriteFactory != nullptr) - m_directWriteFactory->Release(); - } - - void addKey(const void *key, const QByteArray &fontData) - { - if (m_directWriteFontFileLoader != nullptr) - m_directWriteFontFileLoader->addKey(key, fontData); - } - - void removeKey(const void *key) - { - if (m_directWriteFontFileLoader != nullptr) - m_directWriteFontFileLoader->removeKey(key); - } - - IDWriteFontFileLoader *loader() const - { - return m_directWriteFontFileLoader; - } - - private: - IDWriteFactory *m_directWriteFactory = nullptr; - DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr; - }; } // Anonymous namespace +class QCustomFontFileLoader +{ +public: + QCustomFontFileLoader(IDWriteFactory *factory) + { + m_directWriteFactory = factory; + + if (m_directWriteFactory) { + m_directWriteFactory->AddRef(); + + m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); + m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); + } + } + + ~QCustomFontFileLoader() + { + clear(); + + if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr) + m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); + + if (m_directWriteFactory != nullptr) + m_directWriteFactory->Release(); + } + + void addKey(const QByteArray &fontData) + { + if (m_directWriteFontFileLoader != nullptr) + m_directWriteFontFileLoader->addKey(fontData); + } + + void removeKey(const void *key) + { + if (m_directWriteFontFileLoader != nullptr) + m_directWriteFontFileLoader->removeKey(key); + } + + IDWriteFontFileLoader *loader() const + { + return m_directWriteFontFileLoader; + } + + void clear() + { + if (m_directWriteFontFileLoader != nullptr) + m_directWriteFontFileLoader->clear(); + } + +private: + IDWriteFactory *m_directWriteFactory = nullptr; + DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr; +}; + + #endif // directwrite && direct2d @@ -550,8 +565,12 @@ void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory IUnknown *result = nullptr; # if QT_CONFIG(directwrite3) - DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result); + DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result); + + if (result == nullptr) + DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result); # endif + if (result == nullptr) DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result); @@ -700,28 +719,47 @@ QFont QWindowsFontDatabaseBase::systemDefaultFont() return systemFont; } -#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d) -IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) const +void QWindowsFontDatabaseBase::invalidate() { +#if QT_CONFIG(directwrite) + m_fontFileLoader.reset(nullptr); +#endif +} + +#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d) +IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) +{ + QList faces = createDirectWriteFaces(fontData, false); + Q_ASSERT(faces.size() <= 1); + + return faces.isEmpty() ? nullptr : faces.first(); +} + +QList QWindowsFontDatabaseBase::createDirectWriteFaces(const QByteArray &fontData, + bool queryVariations) const +{ + QList ret; QSharedPointer fontEngineData = data(); if (fontEngineData->directWriteFactory == nullptr) { qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()"; - return nullptr; + return ret; } - CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory); - fontFileLoader.addKey(this, fontData); + if (m_fontFileLoader == nullptr) + m_fontFileLoader.reset(new QCustomFontFileLoader(fontEngineData->directWriteFactory)); + + m_fontFileLoader->addKey(fontData); IDWriteFontFile *fontFile = nullptr; - const void *key = this; + const void *key = fontData.data(); HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key, sizeof(void *), - fontFileLoader.loader(), + m_fontFileLoader->loader(), &fontFile); if (FAILED(hres)) { qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__); - return nullptr; + return ret; } BOOL isSupportedFontType; @@ -731,25 +769,65 @@ IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArra fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); if (!isSupportedFontType) { fontFile->Release(); - return nullptr; + return ret; } +#if QT_CONFIG(directwrite3) + IDWriteFactory5 *factory5 = nullptr; + if (queryVariations && SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5), + reinterpret_cast(&factory5)))) { + + IDWriteFontSetBuilder1 *builder; + if (SUCCEEDED(factory5->CreateFontSetBuilder(&builder))) { + if (SUCCEEDED(builder->AddFontFile(fontFile))) { + IDWriteFontSet *fontSet; + if (SUCCEEDED(builder->CreateFontSet(&fontSet))) { + int count = fontSet->GetFontCount(); + qCDebug(lcQpaFonts) << "Found" << count << "variations in font file"; + for (int i = 0; i < count; ++i) { + IDWriteFontFaceReference *ref; + if (SUCCEEDED(fontSet->GetFontFaceReference(i, &ref))) { + IDWriteFontFace3 *face; + if (SUCCEEDED(ref->CreateFontFace(&face))) { + ret.append(face); + } + ref->Release(); + } + } + fontSet->Release(); + } + } + + builder->Release(); + } + + factory5->Release(); + } +#else + Q_UNUSED(queryVariations); +#endif + // ### Currently no support for .ttc, but we could easily return a list here. - IDWriteFontFace *directWriteFontFace = nullptr; - hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType, - 1, - &fontFile, - 0, - DWRITE_FONT_SIMULATIONS_NONE, - &directWriteFontFace); - if (FAILED(hres)) { - qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__); - fontFile->Release(); - return nullptr; + if (ret.isEmpty()) { + IDWriteFontFace *directWriteFontFace = nullptr; + hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType, + 1, + &fontFile, + 0, + DWRITE_FONT_SIMULATIONS_NONE, + &directWriteFontFace); + if (FAILED(hres)) { + qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__); + fontFile->Release(); + return ret; + } else { + ret.append(directWriteFontFace); + } } fontFile->Release(); - return directWriteFontFace; + + return ret; } #endif // directwrite && direct2d @@ -769,7 +847,10 @@ QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qr if (fontEngineData->directWriteFactory == nullptr) return nullptr; - IDWriteFontFace *directWriteFontFace = createDirectWriteFace(fontData); + IDWriteFontFace * directWriteFontFace = createDirectWriteFace(fontData); + if (directWriteFontFace == nullptr) + return nullptr; + fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace, pixelSize, fontEngineData); diff --git a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp index 2e9692bc..1d2b8eb9 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp @@ -46,10 +46,13 @@ #include #include #include -#include +#if QT_CONFIG(cpp_winrt) +# include +#endif #include #include +#include #include #include @@ -58,6 +61,8 @@ #include #include +#include "vxkex.h" + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -276,6 +281,8 @@ QWindowsContext::~QWindowsContext() if (d->m_powerDummyWindow) DestroyWindow(d->m_powerDummyWindow); + d->m_screenManager.destroyWindow(); + unregisterWindowClasses(); if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) { #ifdef QT_USE_FACTORY_CACHE_REGISTRATION @@ -445,11 +452,25 @@ void QWindowsContext::setDetectAltGrModifier(bool a) return QtWindows::DpiAwareness::System; if (QWindowsContext::user32dll.areDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE)) return QtWindows::DpiAwareness::Unaware; - - return QtWindows::DpiAwareness::Invalid; + } + else + { + // IsValidDpiAwarenessContext() will handle the NULL pointer case. + if (!vxkex::IsValidDpiAwarenessContext(context)) + return QtWindows::DpiAwareness::Invalid; + if (vxkex::AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) + return QtWindows::DpiAwareness::Unaware_GdiScaled; + if (vxkex::AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + return QtWindows::DpiAwareness::PerMonitorVersion2; + if (vxkex::AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) + return QtWindows::DpiAwareness::PerMonitor; + if (vxkex::AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) + return QtWindows::DpiAwareness::System; + if (vxkex::AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE)) + return QtWindows::DpiAwareness::Unaware; } - return QtWindows::DpiAwareness::Unaware; // Windows 7 + return QtWindows::DpiAwareness::Invalid; } QtWindows::DpiAwareness QWindowsContext::windowDpiAwareness(HWND hwnd) @@ -457,31 +478,26 @@ QtWindows::DpiAwareness QWindowsContext::windowDpiAwareness(HWND hwnd) if (!hwnd) return QtWindows::DpiAwareness::Invalid; - if (QWindowsContext::user32dll.getWindowDpiAwarenessContext) - { - const auto context = QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd); - return dpiAwarenessContextToQtDpiAwareness(context); - } + const auto context = QWindowsContext::user32dll.getWindowDpiAwarenessContext ? + QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd) : + vxkex::GetWindowDpiAwarenessContext(hwnd); - return dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT_UNAWARE); + return dpiAwarenessContextToQtDpiAwareness(context); } QtWindows::DpiAwareness QWindowsContext::processDpiAwareness() { - if (QWindowsContext::user32dll.getThreadDpiAwarenessContext) - { - // Although we have GetDpiAwarenessContextForProcess(), however, - // it's only available on Win10 1903+, which is a little higher - // than Qt's minimum supported version (1809), so we can't use it. - // Luckily, MS docs said GetThreadDpiAwarenessContext() will also - // return the default DPI_AWARENESS_CONTEXT for the process if - // SetThreadDpiAwarenessContext() was never called. So we can use - // it as an equivalent. - const auto context = QWindowsContext::user32dll.getThreadDpiAwarenessContext(); - return dpiAwarenessContextToQtDpiAwareness(context); - } - - return dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT_UNAWARE); + // Although we have GetDpiAwarenessContextForProcess(), however, + // it's only available on Win10 1903+, which is a little higher + // than Qt's minimum supported version (1809), so we can't use it. + // Luckily, MS docs said GetThreadDpiAwarenessContext() will also + // return the default DPI_AWARENESS_CONTEXT for the process if + // SetThreadDpiAwarenessContext() was never called. So we can use + // it as an equivalent. + const DPI_AWARENESS_CONTEXT context = QWindowsContext::user32dll.getThreadDpiAwarenessContext ? + QWindowsContext::user32dll.getThreadDpiAwarenessContext() : + vxkex::GetThreadDpiAwarenessContext(); + return dpiAwarenessContextToQtDpiAwareness(context); } [[nodiscard]] static inline DPI_AWARENESS_CONTEXT @@ -541,28 +557,32 @@ bool QWindowsContext::setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwarenes return true; const auto context = qtDpiAwarenessToDpiAwarenessContext(dpiAwareness); - if (QWindowsContext::user32dll.isValidDpiAwarenessContext && QWindowsContext::user32dll.setProcessDpiAwarenessContext) - { - if (!QWindowsContext::user32dll.isValidDpiAwarenessContext(context)) { - qCWarning(lcQpaWindow) << dpiAwareness << "is not supported by current system."; - return false; - } - if (!QWindowsContext::user32dll.setProcessDpiAwarenessContext(context)) { - qCWarning(lcQpaWindow).noquote().nospace() - << "SetProcessDpiAwarenessContext() failed: " - << QSystemError::windowsString() - << "\nQt's default DPI awareness context is " - << "DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. If you know what you " - << "are doing, you can overwrite this default using qt.conf " - << "(https://doc.qt.io/qt-6/highdpi.html#configuring-windows)."; - return false; - } - QWindowsContextPrivate::m_v2DpiAware - = processDpiAwareness() == QtWindows::DpiAwareness::PerMonitorVersion2; - return true; + BOOL bResultIsValid = QWindowsContext::user32dll.isValidDpiAwarenessContext ? + QWindowsContext::user32dll.isValidDpiAwarenessContext(context) : + vxkex::IsValidDpiAwarenessContext(context); + + if (!bResultIsValid) { + qCWarning(lcQpaWindow) << dpiAwareness << "is not supported by current system."; + return false; } - return false; // Windows 7 + BOOL bResultSet = QWindowsContext::user32dll.setProcessDpiAwarenessContext ? + QWindowsContext::user32dll.setProcessDpiAwarenessContext(context) : + vxkex::SetProcessDpiAwarenessContext(context); + + if (!bResultSet) { + qCWarning(lcQpaWindow).noquote().nospace() + << "SetProcessDpiAwarenessContext() failed: " + << QSystemError::windowsString() + << "\nQt's default DPI awareness context is " + << "DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. If you know what you " + << "are doing, you can overwrite this default using qt.conf " + << "(https://doc.qt.io/qt-6/highdpi.html#configuring-windows)."; + return false; + } + QWindowsContextPrivate::m_v2DpiAware + = processDpiAwareness() == QtWindows::DpiAwareness::PerMonitorVersion2; + return true; } bool QWindowsContext::isDarkMode() @@ -585,9 +605,9 @@ bool QWindowsContext::useRTLExtensions() const return d->m_keyMapper.useRTLExtensions(); } -QList QWindowsContext::possibleKeys(const QKeyEvent *e) const +QPlatformKeyMapper *QWindowsContext::keyMapper() const { - return d->m_keyMapper.possibleKeys(e); + return &d->m_keyMapper; } QWindowsContext::HandleBaseWindowHash &QWindowsContext::windows() @@ -994,7 +1014,7 @@ bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void { const BOOL result = (QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0) ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi) - : SystemParametersInfo(action, param, out, 0); + : vxkex::SystemParametersInfoForDpi(action, param, out, 0, dpi); return result == TRUE; } @@ -1080,8 +1100,11 @@ static inline bool isInputMessage(UINT m) static bool enableNonClientDpiScaling(HWND hwnd) { bool result = false; - if (QWindowsContext::user32dll.enableNonClientDpiScaling && QWindowsContext::windowDpiAwareness(hwnd) == QtWindows::DpiAwareness::PerMonitor) { - result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE; + if (QWindowsContext::windowDpiAwareness(hwnd) == QtWindows::DpiAwareness::PerMonitor) { + result = QWindowsContext::user32dll.enableNonClientDpiScaling ? + (QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE) : + (vxkex::EnableNonClientDpiScaling(hwnd) != FALSE); + if (!result) { const DWORD errorCode = GetLastError(); qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)", @@ -1267,7 +1290,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(); @@ -1379,6 +1402,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()); @@ -1517,7 +1541,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); } } @@ -1547,7 +1571,7 @@ bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg) } QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos, - QWindowsKeyMapper::queryKeyboardModifiers()); + keyMapper()->queryKeyboardModifiers()); return true; } #endif @@ -1568,7 +1592,7 @@ void QWindowsContext::handleExitSizeMove(QWindow *window) const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons(); if (currentButtons == appButtons) return; - const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const Qt::KeyboardModifiers keyboardModifiers = keyMapper()->queryKeyboardModifiers(); const QPoint globalPos = QWindowsCursor::mousePosition(); const QPlatformWindow *platWin = window->handle(); const QPoint localPos = platWin->mapFromGlobal(globalPos); diff --git a/qtbase/src/plugins/platforms/windows/qwindowscontext.h b/qtbase/src/plugins/platforms/windows/qwindowscontext.h index d35c58de..b0c16593 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowscontext.h +++ b/qtbase/src/plugins/platforms/windows/qwindowscontext.h @@ -34,6 +34,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen) class QWindow; class QPlatformScreen; class QPlatformWindow; +class QPlatformKeyMapper; class QWindowsMenuBar; class QWindowsScreenManager; class QWindowsTabletSupport; @@ -217,7 +218,7 @@ public: unsigned systemInfo() const; bool useRTLExtensions() const; - QList possibleKeys(const QKeyEvent *e) const; + QPlatformKeyMapper *keyMapper() const; HandleBaseWindowHash &windows(); diff --git a/qtbase/src/plugins/platforms/windows/qwindowsdrag.cpp b/qtbase/src/plugins/platforms/windows/qwindowsdrag.cpp index b91608de..072906c5 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -28,9 +28,13 @@ #include #include #include +#include +#include #include +#include "vxkex.h" + QT_BEGIN_NAMESPACE /*! @@ -167,7 +171,7 @@ static Qt::MouseButtons lastButtons = Qt::NoButton; \internal */ -class QWindowsOleDropSource : public QWindowsComBase +class QWindowsOleDropSource : public QComObject { public: enum Mode { @@ -526,7 +530,8 @@ QWindowsOleDropTarget::DragLeave() qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window; - lastModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + lastModifiers = keyMapper->queryKeyboardModifiers(); lastButtons = QWindowsMouseHandler::queryMouseButtons(); QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction, @@ -611,7 +616,6 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, */ bool QWindowsDrag::m_canceled = false; -bool QWindowsDrag::m_dragging = false; QWindowsDrag::QWindowsDrag() = default; @@ -671,7 +675,11 @@ static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); POINTER_INFO pointerInfo{}; - if (!QWindowsContext::user32dll.getPointerInfo || !QWindowsContext::user32dll.getPointerInfo(pointerId, &pointerInfo)) + BOOL bResultPointerInfo = QWindowsContext::user32dll.getPointerInfo ? + QWindowsContext::user32dll.getPointerInfo(pointerId, &pointerInfo) : + vxkex::GetPointerInfo(pointerId, &pointerInfo); + + if (!bResultPointerInfo) return E_FAIL; if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) { @@ -739,10 +747,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) const DWORD allowedEffects = translateToWinDragEffects(possibleActions); qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x" << Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::dec; - // Indicate message handlers we are in DoDragDrop() event loop. - QWindowsDrag::m_dragging = true; const HRESULT r = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); - QWindowsDrag::m_dragging = false; const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect(); if (r == DRAGDROP_S_DROP) { if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) { diff --git a/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp b/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp index 319b8ab3..622a062f 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -17,6 +17,8 @@ #include #include +#include "vxkex.h" + #if defined(WM_APPCOMMAND) # ifndef FAPPCOMMAND_MOUSE # define FAPPCOMMAND_MOUSE 0x8000 @@ -88,9 +90,17 @@ QWindowsKeyMapper::~QWindowsKeyMapper()= default; #define VK_OEM_3 0xC0 #endif -// 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 }; +// 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; +} // Key recorder ------------------------------------------------------------------------[ start ] -- struct KeyRecord { @@ -532,33 +542,6 @@ 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 /** @@ -656,7 +639,7 @@ void QWindowsKeyMapper::updateKeyMap(const MSG &msg) { unsigned char kbdBuffer[256]; // Will hold the complete keyboard state GetKeyboardState(kbdBuffer); - const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; + const quint32 scancode = getScancode(msg); updatePossibleKeyCodes(kbdBuffer, scancode, quint32(msg.wParam)); } @@ -751,28 +734,18 @@ static inline QString messageKeyText(const MSG &msg) [[nodiscard]] static inline int getTitleBarHeight(const HWND hwnd) { - if (QWindowsContext::user32dll.getDpiForWindow && QWindowsContext::user32dll.getSystemMetricsForDpi) - { - const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd); - const int captionHeight = QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYCAPTION, dpi); - if (IsZoomed(hwnd)) - return captionHeight; - // The frame height should also be taken into account if the window - // is not maximized. - const int frameHeight = QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) - + QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); - return captionHeight + frameHeight; - } - else - { - const int captionHeight = GetSystemMetrics(SM_CYCAPTION); - if (IsZoomed(hwnd)) - return captionHeight; - // The frame height should also be taken into account if the window - // is not maximized. - const int frameHeight = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER); - return captionHeight + frameHeight; - } + const BOOL bNewAPI = (QWindowsContext::user32dll.getSystemMetricsForDpi != nullptr); + const UINT dpi = bNewAPI ? QWindowsContext::user32dll.getDpiForWindow(hwnd) : vxkex::GetDpiForWindow(hwnd); + const int captionHeight = bNewAPI ? QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYCAPTION, dpi) : vxkex::GetSystemMetricsForDpi(SM_CYCAPTION, dpi); + if (IsZoomed(hwnd)) + return captionHeight; + // The frame height should also be taken into account if the window + // is not maximized. + const int frameHeight = bNewAPI ? + (QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) + QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)) + : (vxkex::GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) + vxkex::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)); + + return captionHeight + frameHeight; } [[nodiscard]] static inline bool isSystemMenuOffsetNeeded(const Qt::WindowFlags flags) @@ -955,7 +928,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, m_seenAltGr = true; const UINT msgType = msg.message; - const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; + const quint32 scancode = getScancode(msg); auto vk_key = quint32(msg.wParam); quint32 nModifiers = 0; @@ -1352,7 +1325,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg, return result; } -Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() +Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() const { Qt::KeyboardModifiers modifiers = Qt::NoModifier; if (GetKeyState(VK_SHIFT) < 0) @@ -1366,9 +1339,9 @@ Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() return modifiers; } -QList QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const +QList QWindowsKeyMapper::possibleKeyCombinations(const QKeyEvent *e) const { - QList result; + QList result; const quint32 nativeVirtualKey = e->nativeVirtualKey(); @@ -1382,31 +1355,34 @@ QList QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const quint32 baseKey = kbItem.qtKey[0]; Qt::KeyboardModifiers keyMods = e->modifiers(); if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) { - result << (Qt::Key_Enter | keyMods).toCombined(); + result << (Qt::Key_Enter | keyMods); return result; } - result << int(baseKey) + int(keyMods); // The base key is _always_ valid, of course + + // The base key is _always_ valid, of course + result << QKeyCombination::fromCombined(int(baseKey) + int(keyMods)); 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 int matchedKey = int(key) + int(missingMods); - const auto it = - std::find_if(result.begin(), result.end(), - [key] (int k) { return (k & ~Qt::KeyboardModifierMask) == key; }); + 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; + }); // 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 > Qt::KeyboardModifiers(*it & Qt::KeyboardModifierMask)) + else if (missingMods > it->keyboardModifiers()) *it = matchedKey; } } qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey=" << Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase - << e->modifiers() << kbItem << "\n returns" << formatKeys(result); + << e->modifiers() << kbItem << "\n returns" << result; return result; } diff --git a/qtbase/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/qtbase/src/plugins/platforms/windows/qwindowspointerhandler.cpp index 26a2b027..f7bf7578 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowspointerhandler.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowspointerhandler.cpp @@ -27,6 +27,8 @@ #include +#include "vxkex.h" + QT_BEGIN_NAMESPACE enum { @@ -48,7 +50,11 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q *result = 0; const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam); - if (!QWindowsContext::user32dll.getPointerType(pointerId, &m_pointerType)) { + BOOL bResultPt = QWindowsContext::user32dll.getPointerType ? + QWindowsContext::user32dll.getPointerType(pointerId, &m_pointerType) : + vxkex::GetPointerType(pointerId, &m_pointerType); + + if (!bResultPt) { qWarning() << "GetPointerType() failed:" << qt_error_string(); return false; } @@ -62,12 +68,21 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q } case QT_PT_TOUCH: { quint32 pointerCount = 0; - if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) { + BOOL bResultPointerTouchInfo = QWindowsContext::user32dll.getPointerFrameTouchInfo ? + QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr) : + vxkex::GetPointerFrameTouchInfo(pointerId, &pointerCount, nullptr); + + if (!bResultPointerTouchInfo) { qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); return false; } QVarLengthArray touchInfo(pointerCount); - if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) { + + bResultPointerTouchInfo = QWindowsContext::user32dll.getPointerFrameTouchInfo ? + QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data()) : + vxkex::GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data()); + + if (!bResultPointerTouchInfo) { qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string(); return false; } @@ -80,10 +95,12 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q // dispatch any skipped frames if event compression is disabled by the app if (historyCount > 1 && !QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) { touchInfo.resize(pointerCount * historyCount); - if (!QWindowsContext::user32dll.getPointerFrameTouchInfoHistory(pointerId, - &historyCount, - &pointerCount, - touchInfo.data())) { + + BOOL bResultTouchHistory = QWindowsContext::user32dll.getPointerFrameTouchInfoHistory ? + QWindowsContext::user32dll.getPointerFrameTouchInfoHistory(pointerId, &historyCount, &pointerCount, touchInfo.data()) : + vxkex::GetPointerFrameTouchInfoHistory(pointerId, &historyCount, &pointerCount, touchInfo.data()); + + if (!bResultTouchHistory) { qWarning() << "GetPointerFrameTouchInfoHistory() failed:" << qt_error_string(); return false; } @@ -101,7 +118,11 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q } case QT_PT_PEN: { POINTER_PEN_INFO penInfo; - if (!QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo)) { + + BOOL bResultPenInfo = QWindowsContext::user32dll.getPointerPenInfo ? + QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo) : vxkex::GetPointerPenInfo(pointerId, &penInfo); + + if (!bResultPenInfo) { qWarning() << "GetPointerPenInfo() failed:" << qt_error_string(); return false; } @@ -113,7 +134,11 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q || !QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents))) { QVarLengthArray penInfoHistory(historyCount); - if (!QWindowsContext::user32dll.getPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) { + BOOL bResultPenInfoHistory = QWindowsContext::user32dll.getPointerPenInfoHistory ? + QWindowsContext::user32dll.getPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data()) : + vxkex::GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data()); + + if (!bResultPenInfoHistory) { qWarning() << "GetPointerPenInfoHistory() failed:" << qt_error_string(); return false; } @@ -428,8 +453,9 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, return false; if (msg.message == WM_POINTERCAPTURECHANGED) { + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); QWindowSystemInterface::handleTouchCancelEvent(window, m_touchDevice.data(), - QWindowsKeyMapper::queryKeyboardModifiers()); + keyMapper->queryKeyboardModifiers()); m_lastTouchPoints.clear(); return true; } @@ -519,7 +545,10 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, inputIds.insert(touchPoint.id); // Avoid getting repeated messages for this frame if there are multiple pointerIds - QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId); + if (QWindowsContext::user32dll.skipPointerFrameMessages) + QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId); + else + vxkex::SkipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId); } // Some devices send touches for each finger in a different message/frame, instead of consolidating @@ -539,8 +568,9 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd, if (allStates == QEventPoint::State::Released) m_touchInputIDToTouchPointID.clear(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); QWindowSystemInterface::handleTouchEvent(window, m_touchDevice.data(), touchPoints, - QWindowsKeyMapper::queryKeyboardModifiers()); + keyMapper->queryKeyboardModifiers()); return false; // Allow mouse messages to be generated. } @@ -565,7 +595,12 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin auto *penInfo = static_cast(vPenInfo); RECT pRect, dRect; - if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect)) + + BOOL bResultDeviceRects = QWindowsContext::user32dll.getPointerDeviceRects ? + QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect) : + vxkex::GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect); + + if (!bResultDeviceRects) return false; const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice; @@ -673,7 +708,8 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin wumPlatformWindow->applyCursor(); } } - const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers(); QWindowSystemInterface::handleTabletEvent(target, device.data(), localPos, hiResGlobalPos, mouseButtons, @@ -762,7 +798,8 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, : QWindowsGeometryHint::mapFromGlobal(targetHwnd, globalPos); } - const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers(); + const auto *keyMapper = QWindowsContext::instance()->keyMapper(); + const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers(); QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos); if (et == QtWindows::MouseWheelEvent) diff --git a/qtbase/src/plugins/platforms/windows/qwindowsscreen.cpp b/qtbase/src/plugins/platforms/windows/qwindowsscreen.cpp index a8f65209..23486b24 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -32,6 +32,8 @@ #include #include +#include "vxkex.h" + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -43,12 +45,18 @@ static inline QDpi deviceDPI(HDC hdc) static inline QDpi monitorDPI(HMONITOR hMonitor) { - if (QWindowsContext::shcoredll.isValid()) { - UINT dpiX; - UINT dpiY; - if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) - return QDpi(dpiX, dpiY); - } + UINT dpiX; + UINT dpiY; + + HRESULT hr = S_OK; + + if (QWindowsContext::shcoredll.isValid()) + hr = QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + else + hr = vxkex::GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + + if (SUCCEEDED(hr)) + return QDpi(dpiX, dpiY); return {0, 0}; } @@ -579,51 +587,58 @@ QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScre bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o) { bool result = false; - if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) { - ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; - switch (o) { - case Qt::PrimaryOrientation: - break; - case Qt::PortraitOrientation: - orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT; - break; - case Qt::LandscapeOrientation: - orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE; - break; - case Qt::InvertedPortraitOrientation: - orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED; - break; - case Qt::InvertedLandscapeOrientation: - orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED; - break; - } - result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference); + ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; + switch (o) { + case Qt::PrimaryOrientation: + break; + case Qt::PortraitOrientation: + orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT; + break; + case Qt::LandscapeOrientation: + orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE; + break; + case Qt::InvertedPortraitOrientation: + orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED; + break; + case Qt::InvertedLandscapeOrientation: + orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED; + break; } + if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) + result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference); + else + result = vxkex::SetDisplayAutoRotationPreferences(orientationPreference); return result; } Qt::ScreenOrientation QWindowsScreen::orientationPreference() { Qt::ScreenOrientation result = Qt::PrimaryOrientation; - if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) { - DWORD orientationPreference = ORIENTATION_PREFERENCE_NONE; - if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences(&orientationPreference)) { - switch (orientationPreference) { - case ORIENTATION_PREFERENCE_NONE: - break; - case ORIENTATION_PREFERENCE_LANDSCAPE: - result = Qt::LandscapeOrientation; - break; - case ORIENTATION_PREFERENCE_PORTRAIT: - result = Qt::PortraitOrientation; - break; - case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED: - result = Qt::InvertedLandscapeOrientation; - break; - case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED: - result = Qt::InvertedPortraitOrientation; - break; - } + ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE; + + BOOL bResult = TRUE; + + if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) + bResult = QWindowsContext::user32dll.getDisplayAutoRotationPreferences((DWORD *)&orientationPreference); + else + bResult = vxkex::GetDisplayAutoRotationPreferences(&orientationPreference); + + if (bResult) { + switch (orientationPreference) { + case ORIENTATION_PREFERENCE_NONE: + break; + case ORIENTATION_PREFERENCE_LANDSCAPE: + result = Qt::LandscapeOrientation; + break; + case ORIENTATION_PREFERENCE_PORTRAIT: + result = Qt::PortraitOrientation; + break; + case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED: + result = Qt::InvertedLandscapeOrientation; + break; + case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED: + result = Qt::InvertedPortraitOrientation; + break; } } return result; @@ -704,11 +719,15 @@ void QWindowsScreenManager::initialize() handleScreenChanges(); } -QWindowsScreenManager::~QWindowsScreenManager() +void QWindowsScreenManager::destroyWindow() { + qCDebug(lcQpaScreen) << "Destroying display change observer" << m_displayChangeObserver; DestroyWindow(m_displayChangeObserver); + m_displayChangeObserver = nullptr; } +QWindowsScreenManager::~QWindowsScreenManager() = default; + bool QWindowsScreenManager::isSingleScreen() { return QWindowsContext::instance()->screenManager().screens().size() < 2; diff --git a/qtbase/src/plugins/platforms/windows/qwindowstheme.cpp b/qtbase/src/plugins/platforms/windows/qwindowstheme.cpp index e61447a2..2873f7b4 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowstheme.cpp @@ -7,6 +7,7 @@ #include "qwindowsmenu.h" #include "qwindowsdialoghelpers.h" #include "qwindowscontext.h" +#include "qwindowsiconengine.h" #include "qwindowsintegration.h" #if QT_CONFIG(systemtrayicon) # include "qwindowssystemtrayicon.h" @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +44,7 @@ #include -#include +#include "vxkex.h" #if QT_CONFIG(cpp_winrt) # include @@ -81,11 +83,11 @@ static inline QColor mixColors(const QColor &c1, const QColor &c2) (c1.blue() + c2.blue()) / 2}; } -static inline QColor getSysColor(int index) -{ - COLORREF cr = GetSysColor(index); - return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); -} +enum AccentColorLevel { + AccentColorDarkest, + AccentColorNormal, + AccentColorLightest +}; #if QT_CONFIG(cpp_winrt) static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color) @@ -94,6 +96,54 @@ static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color) } #endif +[[maybe_unused]] [[nodiscard]] static inline QColor qt_accentColor(AccentColorLevel level) +{ + QColor accent; + QColor accentLight; + QColor accentDarkest; + +#if QT_CONFIG(cpp_winrt) + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) + { + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); + accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); + } +#endif + + if (!accent.isValid()) + { + const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); + if (!registry.isValid()) + return {}; + const QVariant value = registry.value(L"AccentColor"); + if (!value.isValid()) + return {}; + // The retrieved value is in the #AABBGGRR format, we need to + // convert it to the #AARRGGBB format which Qt expects. + const QColor abgr = QColor::fromRgba(qvariant_cast(value)); + if (!abgr.isValid()) + return {}; + accent = QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); + accentLight = accent.lighter(120); + accentDarkest = accent.darker(120 * 120 * 120); + } + + if (level == AccentColorDarkest) + return accentDarkest; + else if (level == AccentColorLightest) + return accentLight; + return accent; +} + +static inline QColor getSysColor(int index) +{ + COLORREF cr = GetSysColor(index); + return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); +} + // QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system // models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the // behavior by running it in a thread. @@ -223,44 +273,17 @@ static QColor placeHolderColor(QColor textColor) return textColor; } -[[maybe_unused]] [[nodiscard]] static inline QColor qt_accentColor() -{ - const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); - if (!registry.isValid()) - return {}; - const QVariant value = registry.value(L"AccentColor"); - if (!value.isValid()) - return {}; - // The retrieved value is in the #AABBGGRR format, we need to - // convert it to the #AARRGGBB format which Qt expects. - const QColor abgr = QColor::fromRgba(qvariant_cast(value)); - if (!abgr.isValid()) - return {}; - return QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); -} - /* This is used when the theme is light mode, and when the theme is dark but the application doesn't support dark mode. In the latter case, we need to check. */ static void populateLightSystemBasePalette(QPalette &result) { - QColor background = getSysColor(COLOR_BTNFACE); - QColor textColor = getSysColor(COLOR_WINDOWTEXT); - QColor accent = qt_accentColor(); - QColor accentDarkest = accent.darker(120 * 120 * 120); + const QColor background = getSysColor(COLOR_BTNFACE); + const QColor textColor = getSysColor(COLOR_WINDOWTEXT); -#if QT_CONFIG(cpp_winrt) - if (IsWindows10OrGreater()) - { - // respect the Windows 11 accent color - using namespace winrt::Windows::UI::ViewManagement; - const auto settings = UISettings(); - - accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); - accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); - } -#endif + const QColor accent = qt_accentColor(AccentColorNormal); + const QColor accentDarkest = qt_accentColor(AccentColorDarkest); const QColor linkColor = accent; const QColor btnFace = background; @@ -298,7 +321,7 @@ static void populateDarkSystemBasePalette(QPalette &result) { QColor foreground = Qt::white; QColor background = QColor(0x1E, 0x1E, 0x1E); - QColor accent = qt_accentColor(); + QColor accent = qt_accentColor(AccentColorNormal); QColor accentDark = accent.darker(120); QColor accentDarker = accentDark.darker(120); QColor accentDarkest = accentDarker.darker(120); @@ -307,7 +330,7 @@ static void populateDarkSystemBasePalette(QPalette &result) QColor accentLightest = accentLighter.lighter(120); #if QT_CONFIG(cpp_winrt) - if (IsWindows10OrGreater()) + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) { using namespace winrt::Windows::UI::ViewManagement; const auto settings = UISettings(); @@ -472,7 +495,10 @@ static inline QStringList iconThemeSearchPaths() static inline QStringList styleNames() { - return { QStringLiteral("WindowsVista"), QStringLiteral("Windows") }; + QStringList styles = { QStringLiteral("WindowsVista"), QStringLiteral("Windows") }; + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) + styles.prepend(QStringLiteral("Windows11")); + return styles; } static inline int uiEffects() @@ -537,6 +563,8 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const Qt::ColorScheme QWindowsTheme::colorScheme() const { + if (queryHighContrast()) + return Qt::ColorScheme::Unknown; return QWindowsContext::isDarkMode() ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; } @@ -559,24 +587,10 @@ void QWindowsTheme::refreshPalettes() m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette], light)); m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light); if (!light) { - QColor accent = qt_accentColor(); - QColor accentLight = accent.lighter(120); - QColor accentDarkest = accent.darker(120 * 120 * 120); - -#if QT_CONFIG(cpp_winrt) - if (IsWindows10OrGreater()) - { - using namespace winrt::Windows::UI::ViewManagement; - const auto settings = UISettings(); - accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); - accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); - accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); - } -#endif m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]); - m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, accent); - m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, accentLight); - m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, accentDarkest); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, qt_accentColor(AccentColorNormal)); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, qt_accentColor(AccentColorLightest)); + m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, qt_accentColor(AccentColorDarkest)); m_palettes[RadioButtonPalette] = new QPalette(*m_palettes[CheckBoxPalette]); } } @@ -586,15 +600,15 @@ QPalette QWindowsTheme::systemPalette(Qt::ColorScheme colorScheme) QPalette result = standardPalette(); switch (colorScheme) { - case Qt::ColorScheme::Light: - populateLightSystemBasePalette(result); - break; - case Qt::ColorScheme::Dark: - populateDarkSystemBasePalette(result); - break; - default: - qFatal("Unknown color scheme"); - break; + case Qt::ColorScheme::Light: + populateLightSystemBasePalette(result); + break; + case Qt::ColorScheme::Dark: + populateDarkSystemBasePalette(result); + break; + default: + qFatal("Unknown color scheme"); + break; } if (result.window() != result.base()) { @@ -675,14 +689,11 @@ void QWindowsTheme::refreshFonts() fixedFont.setStyleHint(QFont::TypeWriter); LOGFONT lfIconTitleFont; - QFont iconTitleFont; - if (QWindowsContext::user32dll.systemParametersInfoForDpi) { + if (QWindowsContext::user32dll.systemParametersInfoForDpi) QWindowsContext::user32dll.systemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi); - iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi); - } else { - SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); - iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont); - } + else + vxkex::SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi); + const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi); m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont()); m_fonts[MenuFont] = new QFont(menuFont); @@ -856,15 +867,18 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSiz } if (stockId != SIID_INVALID) { - QPixmap pixmap; SHSTOCKICONINFO iconInfo; memset(&iconInfo, 0, sizeof(iconInfo)); iconInfo.cbSize = sizeof(iconInfo); - stockFlags |= (pixmapSize.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON); - if (SHGetStockIconInfo(stockId, SHGFI_ICON | stockFlags, &iconInfo) == S_OK) { - pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon); - DestroyIcon(iconInfo.hIcon); - return pixmap; + stockFlags |= SHGSI_ICONLOCATION; + if (SHGetStockIconInfo(stockId, stockFlags, &iconInfo) == S_OK) { + const auto iconSize = pixmapSize.width(); + HICON icon; + if (SHDefExtractIcon(iconInfo.szPath, iconInfo.iIcon, 0, &icon, nullptr, iconSize) == S_OK) { + QPixmap pixmap = qt_pixmapFromWinHICON(icon); + DestroyIcon(icon); + return pixmap; + } } } @@ -1088,6 +1102,11 @@ QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOpt return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions)); } +QIconEngine *QWindowsTheme::createIconEngine(const QString &iconName) const +{ + return new QWindowsIconEngine(iconName); +} + static inline bool doUseNativeMenus() { const unsigned options = QWindowsIntegration::instance()->options(); diff --git a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp b/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp index 4218df86..59e5ace4 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowswindow.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include // QWINDOWSIZE_MAX #include #include @@ -43,6 +44,8 @@ #include +#include "vxkex.h" + QT_BEGIN_NAMESPACE using QWindowCreationContextPtr = QSharedPointer; @@ -526,8 +529,8 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo return QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + QWindowsContext::user32dll.getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); } - - return GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER); + else + return vxkex::GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + vxkex::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); } /*! @@ -537,16 +540,22 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo static QMargins invisibleMargins(QPoint screenPoint) { - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) { - POINT pt = {screenPoint.x(), screenPoint.y()}; - if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) { - if (QWindowsContext::shcoredll.isValid()) { - UINT dpiX; - UINT dpiY; - if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { - const int gap = getResizeBorderThickness(dpiX); - return QMargins(gap, 0, gap, gap); - } + POINT pt = {screenPoint.x(), screenPoint.y()}; + if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) { + if (QWindowsContext::shcoredll.isValid()) { + UINT dpiX; + UINT dpiY; + + HRESULT hr = S_OK; + + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) + hr = QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + else + hr = vxkex::GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + + if (SUCCEEDED(hr)) { + const int gap = getResizeBorderThickness(dpiX); + return QMargins(gap, 0, gap, gap); } } } @@ -555,12 +564,10 @@ static QMargins invisibleMargins(QPoint screenPoint) [[nodiscard]] static inline QMargins invisibleMargins(const HWND hwnd) { - if (QWindowsContext::user32dll.getDpiForWindow) { - const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd); - const int gap = getResizeBorderThickness(dpi); - return QMargins(gap, 0, gap, gap); - } - return QMargins(); + const UINT dpi = (QWindowsContext::user32dll.getDpiForWindow) ? QWindowsContext::user32dll.getDpiForWindow(hwnd) : vxkex::GetDpiForWindow(hwnd); + + const int gap = getResizeBorderThickness(dpi); + return QMargins(gap, 0, gap, gap); } /*! @@ -848,6 +855,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; } } @@ -1074,10 +1085,17 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyl return {}; RECT rect = {0,0,0,0}; style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. - if (QWindowsContext::user32dll.adjustWindowRectExForDpi && - QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) { - qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__); + if (QWindowsContext::user32dll.adjustWindowRectExForDpi) + { + if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) + qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__); } + else + { + if (vxkex::AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) + qErrnoWarning("%s: vxkex::AdjustWindowRectExForDpi failed", __FUNCTION__); + } + const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << " style=" @@ -1364,6 +1382,8 @@ QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd) , m_hwnd(hwnd) , m_topLevelStyle(0) { + if (QPlatformWindow::parent()) + setParent(QPlatformWindow::parent()); } void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow) @@ -1539,6 +1559,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(); @@ -1568,7 +1589,7 @@ void QWindowsWindow::initialize() } } QWindowsWindow::setSavedDpi(QWindowsContext::user32dll.getDpiForWindow ? - QWindowsContext::user32dll.getDpiForWindow(handle()) : 96); + QWindowsContext::user32dll.getDpiForWindow(handle()) : vxkex::GetDpiForWindow(handle())); } QSurfaceFormat QWindowsWindow::format() const @@ -2018,6 +2039,9 @@ 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); @@ -2060,20 +2084,17 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam) void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd) { - if (QWindowsContext::user32dll.getDpiForWindow) - { - const UINT dpi = QWindowsContext::user32dll.getDpiForWindow(hwnd); - const qreal scale = dpiRelativeScale(dpi); - setSavedDpi(dpi); + const UINT dpi = QWindowsContext::user32dll.getDpiForWindow ? QWindowsContext::user32dll.getDpiForWindow(hwnd) : vxkex::GetDpiForWindow(hwnd); + const qreal scale = dpiRelativeScale(dpi); + setSavedDpi(dpi); - checkForScreenChanged(QWindowsWindow::FromDpiChange); + checkForScreenChanged(QWindowsWindow::FromDpiChange); - // Child windows do not get WM_GETDPISCALEDSIZE messages to inform - // Windows about the new size, so we need to manually scale them. - QRect currentGeometry = geometry(); - QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale); - setGeometry(scaledGeometry); - } + // Child windows do not get WM_GETDPISCALEDSIZE messages to inform + // Windows about the new size, so we need to manually scale them. + QRect currentGeometry = geometry(); + QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale); + setGeometry(scaledGeometry); } static QRect normalFrameGeometry(HWND hwnd) @@ -2485,6 +2506,11 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state) GetWindowPlacement(m_data.hwnd, &windowPlacement); const RECT geometry = RECTfromQRect(m_data.restoreGeometry); windowPlacement.rcNormalPosition = geometry; + // Even if the window is hidden, windowPlacement's showCmd is not SW_HIDE, so change it + // manually to avoid unhiding a hidden window with the subsequent call to + // SetWindowPlacement(). + if (!isVisible()) + windowPlacement.showCmd = SW_HIDE; SetWindowPlacement(m_data.hwnd, &windowPlacement); } // QTBUG-17548: We send expose events when receiving WM_Paint, but for diff --git a/qtbase/src/plugins/platforms/windows/vxkex.h b/qtbase/src/plugins/platforms/windows/vxkex.h new file mode 100644 index 00000000..2f4adb96 --- /dev/null +++ b/qtbase/src/plugins/platforms/windows/vxkex.h @@ -0,0 +1,430 @@ +#pragma once + +#include +#include +#include +#include + +#define MDT_MAXIMUM_DPI 3 + +namespace vxkex { + +static INT GetSystemMetricsForDpi( + IN INT Index, + IN UINT Dpi) +{ + INT Value; + + Value = GetSystemMetrics(Index); + + switch (Index) { + case SM_CXVSCROLL: + case SM_CYHSCROLL: + case SM_CYCAPTION: + case SM_CYVTHUMB: + case SM_CXHTHUMB: + case SM_CXICON: + case SM_CYICON: + case SM_CXCURSOR: + case SM_CYCURSOR: + case SM_CYMENU: + case SM_CYVSCROLL: + case SM_CXHSCROLL: + case SM_CXMIN: + case SM_CXMINTRACK: + case SM_CYMIN: + case SM_CYMINTRACK: + case SM_CXSIZE: + case SM_CXFRAME: + case SM_CYFRAME: + case SM_CXICONSPACING: + case SM_CYICONSPACING: + case SM_CXSMICON: + case SM_CYSMICON: + case SM_CYSMCAPTION: + case SM_CXSMSIZE: + case SM_CYSMSIZE: + case SM_CXMENUSIZE: + case SM_CYMENUSIZE: + case SM_CXMENUCHECK: + case SM_CYMENUCHECK: + // These are pixel values that have to be scaled according to DPI. + Value *= Dpi; + Value /= USER_DEFAULT_SCREEN_DPI; + break; + } + + return Value; +} + +static BOOL SystemParametersInfoForDpi( + IN UINT Action, + IN UINT Parameter, + IN OUT PVOID Data, + IN UINT WinIni, + IN UINT Dpi) +{ + switch (Action) { + case SPI_GETICONTITLELOGFONT: + return SystemParametersInfo(Action, Parameter, Data, 0); + case SPI_GETICONMETRICS: + { + BOOL Success; + PICONMETRICS IconMetrics; + + Success = SystemParametersInfo(Action, Parameter, Data, 0); + + if (Success) { + IconMetrics = (PICONMETRICS) Data; + + IconMetrics->iHorzSpacing *= Dpi; + IconMetrics->iVertSpacing *= Dpi; + IconMetrics->iHorzSpacing /= USER_DEFAULT_SCREEN_DPI; + IconMetrics->iVertSpacing /= USER_DEFAULT_SCREEN_DPI; + } + + return Success; + } + case SPI_GETNONCLIENTMETRICS: + { + BOOL Success; + PNONCLIENTMETRICS NonClientMetrics; + + Success = SystemParametersInfo(Action, Parameter, Data, 0); + + if (Success) { + NonClientMetrics = (PNONCLIENTMETRICS) Data; + + NonClientMetrics->iBorderWidth *= Dpi; + NonClientMetrics->iScrollWidth *= Dpi; + NonClientMetrics->iScrollHeight *= Dpi; + NonClientMetrics->iCaptionWidth *= Dpi; + NonClientMetrics->iCaptionHeight *= Dpi; + NonClientMetrics->iSmCaptionWidth *= Dpi; + NonClientMetrics->iSmCaptionHeight *= Dpi; + NonClientMetrics->iMenuWidth *= Dpi; + NonClientMetrics->iMenuHeight *= Dpi; + NonClientMetrics->iPaddedBorderWidth *= Dpi; + + NonClientMetrics->iBorderWidth /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iScrollWidth /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iScrollHeight /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iCaptionWidth /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iCaptionHeight /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iSmCaptionWidth /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iSmCaptionHeight /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iMenuWidth /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iMenuHeight /= USER_DEFAULT_SCREEN_DPI; + NonClientMetrics->iPaddedBorderWidth /= USER_DEFAULT_SCREEN_DPI; + } + + return Success; + } + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } +} + +static HRESULT GetScaleFactorForMonitor( + IN HMONITOR Monitor, + OUT DEVICE_SCALE_FACTOR *ScaleFactor) +{ + HDC DeviceContext; + ULONG LogPixelsX; + + DeviceContext = GetDC(NULL); + if (!DeviceContext) { + *ScaleFactor = SCALE_100_PERCENT; + return S_OK; + } + + LogPixelsX = GetDeviceCaps(DeviceContext, LOGPIXELSX); + ReleaseDC(NULL, DeviceContext); + + *ScaleFactor = (DEVICE_SCALE_FACTOR) (9600 / LogPixelsX); + return S_OK; +} + +static HRESULT GetDpiForMonitor( + IN HMONITOR Monitor, + IN MONITOR_DPI_TYPE DpiType, + OUT UINT * DpiX, + OUT UINT * DpiY) +{ + HDC DeviceContext; + + if (DpiType >= MDT_MAXIMUM_DPI) { + return E_INVALIDARG; + } + + if (!DpiX || !DpiY) { + return E_INVALIDARG; + } + + if (!IsProcessDPIAware()) { + *DpiX = USER_DEFAULT_SCREEN_DPI; + *DpiY = USER_DEFAULT_SCREEN_DPI; + return S_OK; + } + + DeviceContext = GetDC(NULL); + if (!DeviceContext) { + *DpiX = USER_DEFAULT_SCREEN_DPI; + *DpiY = USER_DEFAULT_SCREEN_DPI; + return S_OK; + } + + *DpiX = GetDeviceCaps(DeviceContext, LOGPIXELSX); + *DpiY = GetDeviceCaps(DeviceContext, LOGPIXELSY); + + if (DpiType == MDT_EFFECTIVE_DPI) { + DEVICE_SCALE_FACTOR ScaleFactor; + + // We have to multiply the DPI values by the scaling factor. + vxkex::GetScaleFactorForMonitor(Monitor, &ScaleFactor); + + *DpiX *= ScaleFactor; + *DpiY *= ScaleFactor; + *DpiX /= 100; + *DpiY /= 100; + } + + ReleaseDC(NULL, DeviceContext); + return S_OK; +} + +static UINT GetDpiForSystem( + VOID) +{ + HDC DeviceContext; + ULONG LogPixelsX; + + if (!IsProcessDPIAware()) { + return 96; + } + + DeviceContext = GetDC(NULL); + if (!DeviceContext) { + return 96; + } + + LogPixelsX = GetDeviceCaps(DeviceContext, LOGPIXELSX); + ReleaseDC(NULL, DeviceContext); + + return LogPixelsX; +} + +static UINT GetDpiForWindow( + IN HWND Window) +{ + if (!IsWindow(Window)) { + return 0; + } + + return vxkex::GetDpiForSystem(); +} + +static BOOL AdjustWindowRectExForDpi( + IN OUT LPRECT Rect, + IN ULONG WindowStyle, + IN BOOL HasMenu, + IN ULONG WindowExStyle, + IN ULONG Dpi) +{ + // I'm not sure how to implement this function properly. + // If it turns out to be important, I'll have to do some testing + // on a Win10 VM. + + return AdjustWindowRectEx( + Rect, + WindowStyle, + HasMenu, + WindowExStyle); +} + +static BOOL SetDisplayAutoRotationPreferences( + IN ORIENTATION_PREFERENCE Orientation) +{ + return TRUE; +} + +static BOOL GetDisplayAutoRotationPreferences( + OUT ORIENTATION_PREFERENCE * Orientation) +{ + *Orientation = ORIENTATION_PREFERENCE_NONE; + return TRUE; +} + +// scaling.c + +static BOOL SetProcessDpiAwarenessContext( + IN DPI_AWARENESS_CONTEXT DpiContext) +{ + switch ((ULONG_PTR)DpiContext) { + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE: + //NOTHING; + break; + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE: + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE: + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: + SetProcessDPIAware(); + break; + default: + return FALSE; + } + + return TRUE; +} + +static BOOL AreDpiAwarenessContextsEqual( + IN DPI_AWARENESS_CONTEXT Value1, + IN DPI_AWARENESS_CONTEXT Value2) +{ + return (Value1 == Value2); +} + +static BOOL IsValidDpiAwarenessContext( + IN DPI_AWARENESS_CONTEXT Value) +{ + switch ((ULONG_PTR)Value) { + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE: + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED: + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE: + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE: + case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: + return TRUE; + default: + return FALSE; + } +} + +static BOOL EnableNonClientDpiScaling( + IN HWND Window) +{ + return TRUE; +} + +static DPI_AWARENESS_CONTEXT GetThreadDpiAwarenessContext( + VOID) +{ + if (IsProcessDPIAware()) { + return DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + } else { + return DPI_AWARENESS_CONTEXT_UNAWARE; + } +} + +static DPI_AWARENESS_CONTEXT GetWindowDpiAwarenessContext( + IN HWND Window) +{ + ULONG WindowThreadId; + ULONG WindowProcessId; + + WindowThreadId = GetWindowThreadProcessId(Window, &WindowProcessId); + if (!WindowThreadId) { + return 0; + } + + // looks like there's a bug in vxkex, here should be == instead of = + // and if is always true + // anyway I don't want to deal with Windows kernel mode structures here + + if (1) { //if (WindowProcessId = (ULONG) NtCurrentTeb()->ClientId.UniqueProcess) { + return vxkex::GetThreadDpiAwarenessContext(); + } + + return DPI_AWARENESS_CONTEXT_UNAWARE; +} + +// pointer.c + +static BOOL GetPointerType( + IN UINT32 PointerId, + OUT POINTER_INPUT_TYPE *PointerType) +{ + *PointerType = PT_MOUSE; + return TRUE; +} + +static BOOL GetPointerFrameTouchInfo( + IN UINT32 PointerId, + IN OUT UINT32 *PointerCount, + OUT LPVOID TouchInfo) +{ + return FALSE; +} + +static BOOL GetPointerFrameTouchInfoHistory( + IN UINT32 PointerId, + IN OUT UINT32 *EntriesCount, + IN OUT UINT32 *PointerCount, + OUT LPVOID TouchInfo) +{ + return FALSE; +} + +static BOOL GetPointerPenInfo( + IN UINT32 PointerId, + OUT LPVOID PenInfo) +{ + return FALSE; +} + +static BOOL GetPointerPenInfoHistory( + IN UINT32 PointerId, + IN OUT UINT32 *EntriesCount, + OUT LPVOID PenInfo) +{ + return FALSE; +} + +static BOOL SkipPointerFrameMessages( + IN UINT32 PointerId) +{ + return TRUE; +} + +static BOOL GetPointerDeviceRects( + IN HANDLE Device, + OUT LPRECT PointerDeviceRect, + OUT LPRECT DisplayRect) +{ + PointerDeviceRect->top = 0; + PointerDeviceRect->left = 0; + PointerDeviceRect->bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN); + PointerDeviceRect->right = GetSystemMetrics(SM_CXVIRTUALSCREEN); + + DisplayRect->top = 0; + DisplayRect->left = 0; + DisplayRect->bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN); + DisplayRect->right = GetSystemMetrics(SM_CXVIRTUALSCREEN); + + return TRUE; +} + +static BOOL GetPointerInfo( + IN DWORD PointerId, + OUT POINTER_INFO *PointerInfo) +{ + PointerInfo->pointerType = PT_MOUSE; + PointerInfo->pointerId = PointerId; + PointerInfo->frameId = 0; + PointerInfo->pointerFlags = POINTER_FLAG_NONE; + PointerInfo->sourceDevice = NULL; + PointerInfo->hwndTarget = NULL; + GetCursorPos(&PointerInfo->ptPixelLocation); + GetCursorPos(&PointerInfo->ptHimetricLocation); + GetCursorPos(&PointerInfo->ptPixelLocationRaw); + GetCursorPos(&PointerInfo->ptHimetricLocationRaw); + PointerInfo->dwTime = 0; + PointerInfo->historyCount = 1; + PointerInfo->InputData = 0; + PointerInfo->dwKeyStates = 0; + PointerInfo->PerformanceCount = 0; + PointerInfo->ButtonChangeType = POINTER_CHANGE_NONE; + + return TRUE; +} + +} // namespace vxkex diff --git a/qtbase/src/widgets/styles/qwindowsstyle.cpp b/qtbase/src/widgets/styles/qwindowsstyle.cpp new file mode 100644 index 00000000..76ced203 --- /dev/null +++ b/qtbase/src/widgets/styles/qwindowsstyle.cpp @@ -0,0 +1,2365 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsstyle_p.h" +#include "qwindowsstyle_p_p.h" + +#if QT_CONFIG(style_windows) || defined(QT_PLUGIN) + +#include "qapplication.h" +#include "qbitmap.h" +#include "qdrawutil.h" // for now +#include "qevent.h" +#if QT_CONFIG(menu) +#include "qmenu.h" +#endif +#if QT_CONFIG(menubar) +#include "qmenubar.h" +#include +#endif +#include "qpaintengine.h" +#include "qpainter.h" +#if QT_CONFIG(rubberband) +#include "qrubberband.h" +#endif +#include "qstyleoption.h" +#if QT_CONFIG(tabbar) +#include "qtabbar.h" +#endif +#include "qwidget.h" +#include "qdebug.h" +#if QT_CONFIG(mainwindow) +#include "qmainwindow.h" +#endif +#include "qfile.h" +#include "qtextstream.h" +#include "qpixmapcache.h" +#if QT_CONFIG(wizard) +#include "qwizard.h" +#endif +#if QT_CONFIG(listview) +#include "qlistview.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if QT_CONFIG(animation) +#include +#endif + +#include + +#include "../../plugins/platforms/windows/vxkex.h" + +QT_BEGIN_NAMESPACE + +#if defined(Q_OS_WIN) + +QT_BEGIN_INCLUDE_NAMESPACE +#include "qt_windows.h" +QT_END_INCLUDE_NAMESPACE +# ifndef COLOR_GRADIENTACTIVECAPTION +# define COLOR_GRADIENTACTIVECAPTION 27 +# endif +# ifndef COLOR_GRADIENTINACTIVECAPTION +# define COLOR_GRADIENTINACTIVECAPTION 28 +# endif + +Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &); +#endif //Q_OS_WIN + +QT_BEGIN_INCLUDE_NAMESPACE +#include +QT_END_INCLUDE_NAMESPACE + +enum QSliderDirection { SlUp, SlDown, SlLeft, SlRight }; + +/* + \internal +*/ + +QWindowsStylePrivate::QWindowsStylePrivate() = default; + +qreal QWindowsStylePrivate::appDevicePixelRatio() +{ + return qApp->devicePixelRatio(); +} + +// Returns \c true if the toplevel parent of \a widget has seen the Alt-key +bool QWindowsStylePrivate::hasSeenAlt(const QWidget *widget) const +{ + widget = widget->window(); + return seenAlt.contains(widget); +} + +/*! + \reimp +*/ +bool QWindowsStyle::eventFilter(QObject *o, QEvent *e) +{ + // Records Alt- and Focus events + if (!o->isWidgetType()) + return QObject::eventFilter(o, e); + + QWidget *widget = qobject_cast(o); + Q_D(QWindowsStyle); + switch(e->type()) { + case QEvent::KeyPress: + if (static_cast(e)->key() == Qt::Key_Alt) { + widget = widget->window(); + + // Alt has been pressed - find all widgets that care + const QList children = widget->findChildren(); + auto ignorable = [](QWidget *w) { + return w->isWindow() || !w->isVisible() + || w->style()->styleHint(SH_UnderlineShortcut, nullptr, w); + }; + // Update states before repainting + d->seenAlt.append(widget); + d->alt_down = true; + + // Repaint all relevant widgets + for (QWidget *w : children) { + if (!ignorable(w)) + w->update(); + } + } + break; + case QEvent::KeyRelease: + if (static_cast(e)->key() == Qt::Key_Alt) { + widget = widget->window(); + + // Update state and repaint the menu bars. + d->alt_down = false; +#if QT_CONFIG(menubar) + const QList menuBars = widget->findChildren(); + for (QWidget *w : menuBars) + w->update(); +#endif + } + break; + case QEvent::Close: + // Reset widget when closing + d->seenAlt.removeAll(widget); + d->seenAlt.removeAll(widget->window()); + break; + default: + break; + } + return QCommonStyle::eventFilter(o, e); +} + +/*! + \class QWindowsStyle + \brief The QWindowsStyle class provides a Microsoft Windows-like look and feel. + + \ingroup appearance + \inmodule QtWidgets + \internal + + This style is Qt's default GUI style on Windows. + + \image qwindowsstyle.png + \sa QWindowsVistaStyle, QMacStyle, QFusionStyle +*/ + +/*! + Constructs a QWindowsStyle object. +*/ +QWindowsStyle::QWindowsStyle() : QCommonStyle(*new QWindowsStylePrivate) +{ +} + +/*! + \internal + + Constructs a QWindowsStyle object. +*/ +QWindowsStyle::QWindowsStyle(QWindowsStylePrivate &dd) : QCommonStyle(dd) +{ +} + + +/*! Destroys the QWindowsStyle object. */ +QWindowsStyle::~QWindowsStyle() +{ +} + +#ifdef Q_OS_WIN +static inline QRgb colorref2qrgb(COLORREF col) +{ + return qRgb(GetRValue(col), GetGValue(col), GetBValue(col)); +} +#endif + +/*! \reimp */ +void QWindowsStyle::polish(QApplication *app) +{ + QCommonStyle::polish(app); + QWindowsStylePrivate *d = const_cast(d_func()); + // We only need the overhead when shortcuts are sometimes hidden + if (!proxy()->styleHint(SH_UnderlineShortcut, nullptr) && app) + app->installEventFilter(this); + + const auto &palette = QGuiApplication::palette(); + d->activeGradientCaptionColor = palette.highlight().color(); + d->activeCaptionColor = d->activeGradientCaptionColor; + d->inactiveGradientCaptionColor = palette.dark().color(); + d->inactiveCaptionColor = d->inactiveGradientCaptionColor; + d->inactiveCaptionText = palette.window().color(); + +#if defined(Q_OS_WIN) //fetch native title bar colors + if (app->desktopSettingsAware()){ + DWORD activeCaption = GetSysColor(COLOR_ACTIVECAPTION); + DWORD gradientActiveCaption = GetSysColor(COLOR_GRADIENTACTIVECAPTION); + DWORD inactiveCaption = GetSysColor(COLOR_INACTIVECAPTION); + DWORD gradientInactiveCaption = GetSysColor(COLOR_GRADIENTINACTIVECAPTION); + DWORD inactiveCaptionText = GetSysColor(COLOR_INACTIVECAPTIONTEXT); + d->activeCaptionColor = colorref2qrgb(activeCaption); + d->activeGradientCaptionColor = colorref2qrgb(gradientActiveCaption); + d->inactiveCaptionColor = colorref2qrgb(inactiveCaption); + d->inactiveGradientCaptionColor = colorref2qrgb(gradientInactiveCaption); + d->inactiveCaptionText = colorref2qrgb(inactiveCaptionText); + } +#endif +} + +/*! \reimp */ +void QWindowsStyle::unpolish(QApplication *app) +{ + QCommonStyle::unpolish(app); + app->removeEventFilter(this); +} + +/*! \reimp */ +void QWindowsStyle::polish(QWidget *widget) +{ + QCommonStyle::polish(widget); +} + +/*! \reimp */ +void QWindowsStyle::unpolish(QWidget *widget) +{ + QCommonStyle::unpolish(widget); +} + +/*! + \reimp +*/ +void QWindowsStyle::polish(QPalette &pal) +{ + QCommonStyle::polish(pal); +} + +typedef BOOL (WINAPI *GetSystemMetricsForDpiFunc)(int, UINT); +typedef BOOL (WINAPI *SystemParametersInfoForDpiFunc)(UINT, UINT, PVOID, UINT, UINT); + +int QWindowsStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *, const QWidget *widget) +{ +#if defined(Q_OS_WIN) + // The pixel metrics are in device indepentent pixels; + // hardcode DPI to 1x 96 DPI. + const int dpi = 96; + + static GetSystemMetricsForDpiFunc myGetSystemMetricsForDpi = + (GetSystemMetricsForDpiFunc)::GetProcAddress(::GetModuleHandle(L"User32"), "GetSystemMetricsForDpi"); + + static SystemParametersInfoForDpiFunc mySystemParametersInfoForDpi = + (SystemParametersInfoForDpiFunc)::GetProcAddress(::GetModuleHandle(L"User32"), "SystemParametersInfoForDpi"); + + switch (pm) { + case QStyle::PM_DockWidgetFrameWidth: + return myGetSystemMetricsForDpi ? myGetSystemMetricsForDpi(SM_CXFRAME, dpi) : vxkex::GetSystemMetricsForDpi(SM_CXFRAME, dpi); + + case QStyle::PM_TitleBarHeight: { + const int resizeBorderThickness = myGetSystemMetricsForDpi ? + (myGetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + myGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)) : + (vxkex::GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + vxkex::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)) ; + if (widget && (widget->windowType() == Qt::Tool)) + return myGetSystemMetricsForDpi ? + (myGetSystemMetricsForDpi(SM_CYSMCAPTION, dpi) + resizeBorderThickness) : + (vxkex::GetSystemMetricsForDpi(SM_CYSMCAPTION, dpi) + resizeBorderThickness); + return myGetSystemMetricsForDpi ? + (myGetSystemMetricsForDpi(SM_CYCAPTION, dpi) + resizeBorderThickness) : + (vxkex::GetSystemMetricsForDpi(SM_CYCAPTION, dpi) + resizeBorderThickness); + } + + case QStyle::PM_ScrollBarExtent: + { + NONCLIENTMETRICS ncm; + ncm.cbSize = sizeof(NONCLIENTMETRICS); + BOOL bResult = mySystemParametersInfoForDpi + ? mySystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0, dpi) + : vxkex::SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0, dpi); + if (bResult) + return qMax(ncm.iScrollHeight, ncm.iScrollWidth); + } + break; + + case QStyle::PM_MdiSubWindowFrameWidth: + return myGetSystemMetricsForDpi ? myGetSystemMetricsForDpi(SM_CYFRAME, dpi) : vxkex::GetSystemMetricsForDpi(SM_CYFRAME, dpi); + + default: + break; + } +#else // Q_OS_WIN + Q_UNUSED(pm); + Q_UNUSED(widget); +#endif + return QWindowsStylePrivate::InvalidMetric; +} + +int QWindowsStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm) +{ + switch (pm) { + case QStyle::PM_ToolBarItemSpacing: + return 0; + case QStyle::PM_ButtonDefaultIndicator: + case QStyle::PM_ButtonShiftHorizontal: + case QStyle::PM_ButtonShiftVertical: + case QStyle::PM_MenuHMargin: + case QStyle::PM_MenuVMargin: + case QStyle::PM_ToolBarItemMargin: + return 1; + case QStyle::PM_DockWidgetSeparatorExtent: + return 4; +#if QT_CONFIG(tabbar) + case QStyle::PM_TabBarTabShiftHorizontal: + return 0; + case QStyle::PM_TabBarTabShiftVertical: + return 2; +#endif + +#if QT_CONFIG(slider) + case QStyle::PM_SliderLength: + return 11; +#endif // QT_CONFIG(slider) + +#if QT_CONFIG(menu) + case QStyle::PM_MenuBarHMargin: + case QStyle::PM_MenuBarVMargin: + case QStyle::PM_MenuBarPanelWidth: + return 0; + case QStyle::PM_SmallIconSize: + return 16; + case QStyle::PM_LargeIconSize: + return 32; + case QStyle::PM_DockWidgetTitleMargin: + return 2; + case QStyle::PM_DockWidgetTitleBarButtonMargin: + case QStyle::PM_DockWidgetFrameWidth: + return 4; + +#endif // QT_CONFIG(menu) + case QStyle::PM_ToolBarHandleExtent: + return 10; + default: + break; + } + return QWindowsStylePrivate::InvalidMetric; +} + +static QScreen *screenOf(const QWidget *w) +{ + if (w) { + if (auto screen = qt_widget_private(const_cast(w))->associatedScreen()) + return screen; + } + return QGuiApplication::primaryScreen(); +} + +// Calculate the overall scale factor to obtain Qt Device Independent +// Pixels from a native Windows size. +qreal QWindowsStylePrivate::nativeMetricScaleFactor(const QWidget *widget) +{ + return qreal(1) / QHighDpiScaling::factor(screenOf(widget)); +} + +/*! + \reimp +*/ +int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWidget *widget) const +{ + int ret = QWindowsStylePrivate::pixelMetricFromSystemDp(pm, opt, widget); + if (ret != QWindowsStylePrivate::InvalidMetric) + return ret; + + ret = QWindowsStylePrivate::fixedPixelMetric(pm); + if (ret != QWindowsStylePrivate::InvalidMetric) + return int(QStyleHelper::dpiScaled(ret, opt)); + + ret = 0; + + switch (pm) { + case PM_MaximumDragDistance: + ret = QCommonStyle::pixelMetric(PM_MaximumDragDistance, opt, widget); + if (ret == -1) + ret = 60; + break; + +#if QT_CONFIG(slider) + // Returns the number of pixels to use for the business part of the + // slider (i.e., the non-tickmark portion). The remaining space is shared + // equally between the tickmark regions. + case PM_SliderControlThickness: + if (const QStyleOptionSlider *sl = qstyleoption_cast(opt)) { + int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width(); + int ticks = sl->tickPosition; + int n = 0; + if (ticks & QSlider::TicksAbove) + ++n; + if (ticks & QSlider::TicksBelow) + ++n; + if (!n) { + ret = space; + break; + } + + int thick = 6; // Magic constant to get 5 + 16 + 5 + if (ticks != QSlider::TicksBothSides && ticks != QSlider::NoTicks) + thick += proxy()->pixelMetric(PM_SliderLength, sl, widget) / 4; + + space -= thick; + if (space > 0) + thick += (space * 2) / (n + 2); + ret = thick; + } + break; +#endif // QT_CONFIG(slider) + + case PM_IconViewIconSize: + ret = proxy()->pixelMetric(PM_LargeIconSize, opt, widget); + break; + + case PM_SplitterWidth: + ret = QStyleHelper::dpiScaled(4, opt); + break; + + default: + ret = QCommonStyle::pixelMetric(pm, opt, widget); + break; + } + + return ret; +} + +/*! + \reimp + */ +QPixmap QWindowsStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, + const QWidget *widget) const +{ +#if defined(Q_OS_WIN) + QPixmap desktopIcon; + switch(standardPixmap) { + case SP_DriveCDIcon: + case SP_DriveDVDIcon: + case SP_DriveNetIcon: + case SP_DriveHDIcon: + case SP_DriveFDIcon: + case SP_FileIcon: + case SP_FileLinkIcon: + case SP_DirLinkIcon: + case SP_DirClosedIcon: + case SP_DesktopIcon: + case SP_ComputerIcon: + case SP_DirOpenIcon: + case SP_FileDialogNewFolder: + case SP_DirHomeIcon: + case SP_TrashIcon: + case SP_VistaShield: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + QPlatformTheme::StandardPixmap sp = static_cast(standardPixmap); + desktopIcon = theme->standardPixmap(sp, QSizeF(16, 16)); + } + break; + case SP_MessageBoxInformation: + case SP_MessageBoxWarning: + case SP_MessageBoxCritical: + case SP_MessageBoxQuestion: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + QPlatformTheme::StandardPixmap sp = static_cast(standardPixmap); + desktopIcon = theme->standardPixmap(sp, QSizeF()); + } + break; + default: + break; + } + if (!desktopIcon.isNull()) { + return desktopIcon; + } +#endif // Q_OS_WIN + return QCommonStyle::standardPixmap(standardPixmap, opt, widget); +} + +/*! \reimp */ +int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWidget *widget, + QStyleHintReturn *returnData) const +{ + int ret = 0; + + switch (hint) { + case SH_EtchDisabledText: { + const QPalette pal = opt ? opt->palette + : widget ? widget->palette() + : QPalette(); + ret = pal.window().color().lightness() > pal.text().color().lightness() + ? 1 : 0; + break; + } + case SH_Slider_SnapToValue: + case SH_PrintDialog_RightAlignButtons: + case SH_FontDialog_SelectAssociatedText: + case SH_Menu_AllowActiveAndDisabled: + case SH_MenuBar_AltKeyNavigation: + case SH_MenuBar_MouseTracking: + case SH_Menu_MouseTracking: + case SH_ComboBox_ListMouseTracking: + case SH_Slider_StopMouseOverSlider: + case SH_MainWindow_SpaceBelowMenuBar: + ret = 1; + + break; + case SH_ItemView_ShowDecorationSelected: +#if QT_CONFIG(listview) + if (qobject_cast(widget)) + ret = 1; +#endif + break; + case SH_ItemView_ChangeHighlightOnFocus: + ret = 1; + break; + case SH_ToolBox_SelectedPageTitleBold: + ret = 0; + break; + +#if defined(Q_OS_WIN) + case SH_UnderlineShortcut: + { + ret = 1; + BOOL cues = false; + SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &cues, 0); + ret = int(cues); + // Do nothing if we always paint underlines + Q_D(const QWindowsStyle); + if (!ret && widget && d) { +#if QT_CONFIG(menubar) + const QMenuBar *menuBar = qobject_cast(widget); + if (!menuBar && qobject_cast(widget)) { + QWidget *w = QApplication::activeWindow(); + if (w && w != widget) + menuBar = w->findChild(); + } + // If we paint a menu bar draw underlines if is in the keyboardState + if (menuBar) { + if (menuBar->d_func()->keyboardState || d->altDown()) + ret = 1; + // Otherwise draw underlines if the toplevel widget has seen an alt-press + } else +#endif // QT_CONFIG(menubar) + if (d->hasSeenAlt(widget)) { + ret = 1; + } + } +#if QT_CONFIG(accessibility) + if (!ret && opt && opt->type == QStyleOption::SO_MenuItem + && QStyleHelper::isInstanceOf(opt->styleObject, QAccessible::MenuItem) + && opt->styleObject->property("_q_showUnderlined").toBool()) + ret = 1; +#endif // QT_CONFIG(accessibility) + break; + } +#endif // Q_OS_WIN + case SH_Menu_SubMenuSloppyCloseTimeout: + case SH_Menu_SubMenuPopupDelay: { +#if defined(Q_OS_WIN) + DWORD delay; + if (SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &delay, 0)) + ret = delay; + else +#endif // Q_OS_WIN + ret = 400; + break; + } +#if QT_CONFIG(rubberband) + case SH_RubberBand_Mask: + if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast(opt)) { + ret = 0; + if (rbOpt->shape == QRubberBand::Rectangle) { + ret = true; + if (QStyleHintReturnMask *mask = qstyleoption_cast(returnData)) { + mask->region = opt->rect; + int size = 1; + if (widget && widget->isWindow()) + size = 4; + mask->region -= opt->rect.adjusted(size, size, -size, -size); + } + } + } + break; +#endif // QT_CONFIG(rubberband) +#if QT_CONFIG(wizard) + case SH_WizardStyle: + ret = QWizard::ModernStyle; + break; +#endif + case SH_ItemView_ArrowKeysNavigateIntoChildren: + ret = true; + break; + case SH_DialogButtonBox_ButtonsHaveIcons: + ret = 0; + break; + default: + ret = QCommonStyle::styleHint(hint, opt, widget, returnData); + break; + } + return ret; +} + +/*! \reimp */ +void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, + const QWidget *w) const +{ + // Used to restore across fallthrough cases. Currently only used in PE_IndicatorCheckBox + bool doRestore = false; + + switch (pe) { +#if QT_CONFIG(toolbar) + case PE_IndicatorToolBarSeparator: + { + QRect rect = opt->rect; + const int margin = 2; + QPen oldPen = p->pen(); + if (opt->state & State_Horizontal){ + const int offset = rect.width()/2; + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.bottomLeft().x() + offset, + rect.bottomLeft().y() - margin, + rect.topLeft().x() + offset, + rect.topLeft().y() + margin); + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.bottomLeft().x() + offset + 1, + rect.bottomLeft().y() - margin, + rect.topLeft().x() + offset + 1, + rect.topLeft().y() + margin); + } + else{ //Draw vertical separator + const int offset = rect.height()/2; + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.topLeft().x() + margin , + rect.topLeft().y() + offset, + rect.topRight().x() - margin, + rect.topRight().y() + offset); + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.topLeft().x() + margin , + rect.topLeft().y() + offset + 1, + rect.topRight().x() - margin, + rect.topRight().y() + offset + 1); + } + p->setPen(oldPen); + } + break; + case PE_IndicatorToolBarHandle: + p->save(); + p->translate(opt->rect.x(), opt->rect.y()); + if (opt->state & State_Horizontal) { + int x = opt->rect.width() / 2 - 4; + if (opt->direction == Qt::RightToLeft) + x -= 2; + if (opt->rect.height() > 4) { + qDrawShadePanel(p, x, 2, 3, opt->rect.height() - 4, + opt->palette, false, 1, nullptr); + qDrawShadePanel(p, x + 3, 2, 3, opt->rect.height() - 4, + opt->palette, false, 1, nullptr); + } + } else { + if (opt->rect.width() > 4) { + int y = opt->rect.height() / 2 - 4; + qDrawShadePanel(p, 2, y, opt->rect.width() - 4, 3, + opt->palette, false, 1, nullptr); + qDrawShadePanel(p, 2, y + 3, opt->rect.width() - 4, 3, + opt->palette, false, 1, nullptr); + } + } + p->restore(); + break; + +#endif // QT_CONFIG(toolbar) + case PE_FrameButtonTool: + case PE_PanelButtonTool: { + QPen oldPen = p->pen(); +#if QT_CONFIG(dockwidget) + if (w && w->inherits("QDockWidgetTitleButton")) { + if (const QWidget *dw = w->parentWidget()) + if (dw->isWindow()){ + qDrawWinButton(p, opt->rect.adjusted(1, 1, 0, 0), opt->palette, opt->state & (State_Sunken | State_On), + &opt->palette.button()); + + return; + } + } +#endif // QT_CONFIG(dockwidget) + QBrush fill; + bool stippled; + bool panel = (pe == PE_PanelButtonTool); + if ((!(opt->state & State_Sunken )) + && (!(opt->state & State_Enabled) + || !(opt->state & State_MouseOver && opt->state & State_AutoRaise)) + && (opt->state & State_On)) { + fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + stippled = true; + } else { + fill = opt->palette.brush(QPalette::Button); + stippled = false; + } + + if (opt->state & (State_Raised | State_Sunken | State_On)) { + if (opt->state & State_AutoRaise) { + if (opt->state & (State_Enabled | State_Sunken | State_On)){ + if (panel) + qDrawShadePanel(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1, &fill); + else + qDrawShadeRect(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1); + } + if (stippled) { + p->setPen(opt->palette.button().color()); + p->drawRect(opt->rect.adjusted(1,1,-2,-2)); + } + } else { + qDrawWinButton(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), panel ? &fill : nullptr); + } + } else { + p->fillRect(opt->rect, fill); + } + p->setPen(oldPen); + break; } + case PE_PanelButtonCommand: + if (const QStyleOptionButton *btn = qstyleoption_cast(opt)) { + QBrush fill; + State flags = opt->state; + QPalette pal = opt->palette; + QRect r = opt->rect; + if (! (flags & State_Sunken) && (flags & State_On)) + fill = QBrush(pal.light().color(), Qt::Dense4Pattern); + else + fill = pal.brush(QPalette::Button); + + if (btn->features & QStyleOptionButton::DefaultButton && flags & State_Sunken) { + p->setPen(pal.dark().color()); + p->setBrush(fill); + p->drawRect(r.adjusted(0, 0, -1, -1)); + } else if (flags & (State_Raised | State_On | State_Sunken)) { + qDrawWinButton(p, r, pal, flags & (State_Sunken | State_On), + &fill); + } else { + p->fillRect(r, fill); + } + } + break; + case PE_FrameDefaultButton: { + QPen oldPen = p->pen(); + p->setPen(QPen(opt->palette.shadow().color(), 0)); + QRectF rect = opt->rect; + const qreal dpi = QStyleHelper::dpi(opt); + const qreal topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi); + const qreal bottomRightAdjustment = QStyleHelper::dpiScaled(-1.5, dpi); + rect.adjust(topLevelAdjustment, topLevelAdjustment, + bottomRightAdjustment, bottomRightAdjustment); + p->drawRect(rect); + p->setPen(oldPen); + break; + } + case PE_IndicatorCheckBox: { + QBrush fill; + if (opt->state & State_NoChange) + fill = QBrush(opt->palette.base().color(), Qt::Dense4Pattern); + else if (opt->state & State_Sunken) + fill = opt->palette.button(); + else if (opt->state & State_Enabled) + fill = opt->palette.base(); + else + fill = opt->palette.window(); + p->save(); + doRestore = true; + qDrawWinPanel(p, opt->rect, opt->palette, true, &fill); + if (opt->state & State_NoChange) + p->setPen(opt->palette.dark().color()); + else + p->setPen(opt->palette.text().color()); + } + Q_FALLTHROUGH(); + case PE_IndicatorItemViewItemCheck: + if (!doRestore) { + p->save(); + doRestore = true; + } +#if QT_CONFIG(itemviews) + if (pe == PE_IndicatorItemViewItemCheck) { + const QStyleOptionViewItem *itemViewOpt = qstyleoption_cast(opt); + p->setPen(itemViewOpt + && itemViewOpt->showDecorationSelected + && opt->state & State_Selected + ? opt->palette.highlightedText().color() + : opt->palette.text().color()); + if (opt->state & State_NoChange) + p->setBrush(opt->palette.brush(QPalette::Button)); + p->drawRect(opt->rect.x() + 1, opt->rect.y() + 1, opt->rect.width() - 2, opt->rect.height() - 2); + } +#endif // QT_CONFIG(itemviews) + if (!(opt->state & State_Off)) { + std::array points; + qreal scaleh = opt->rect.width() / 12.0; + qreal scalev = opt->rect.height() / 12.0; + points[0] = { opt->rect.x() + qreal(3.5) * scaleh, opt->rect.y() + qreal(5.5) * scalev }; + points[1] = { points[0].x(), points[0].y() + 2 * scalev }; + points[2] = { points[1].x() + 2 * scaleh, points[1].y() + 2 * scalev }; + points[3] = { points[2].x() + 4 * scaleh, points[2].y() - 4 * scalev }; + points[4] = { points[3].x(), points[3].y() - 2 * scalev }; + points[5] = { points[4].x() - 4 * scaleh, points[4].y() + 4 * scalev }; + p->setPen(QPen(opt->palette.text().color(), 0)); + p->setBrush(opt->palette.text().color()); + p->drawPolygon(points.data(), static_cast(points.size())); + } + if (doRestore) + p->restore(); + break; + case PE_FrameFocusRect: + if (const QStyleOptionFocusRect *fropt = qstyleoption_cast(opt)) { + //### check for d->alt_down + if (!(fropt->state & State_KeyboardFocusChange) && !proxy()->styleHint(SH_UnderlineShortcut, opt)) + return; + QRect r = opt->rect; + p->save(); + p->setBackgroundMode(Qt::TransparentMode); + QColor bg_col = fropt->backgroundColor; + if (!bg_col.isValid()) + bg_col = p->background().color(); + // Create an "XOR" color. + QColor patternCol((bg_col.red() ^ 0xff) & 0xff, + (bg_col.green() ^ 0xff) & 0xff, + (bg_col.blue() ^ 0xff) & 0xff); + p->setBrush(QBrush(patternCol, Qt::Dense4Pattern)); + p->setBrushOrigin(r.topLeft()); + p->setPen(Qt::NoPen); + p->drawRect(r.left(), r.top(), r.width(), 1); // Top + p->drawRect(r.left(), r.bottom(), r.width(), 1); // Bottom + p->drawRect(r.left(), r.top(), 1, r.height()); // Left + p->drawRect(r.right(), r.top(), 1, r.height()); // Right + p->restore(); + } + break; + case PE_IndicatorRadioButton: + { + QRect r = opt->rect; + p->save(); + p->setRenderHint(QPainter::Antialiasing, true); + + QPointF circleCenter = r.center() + QPoint(1, 1); + qreal radius = (r.width() + (r.width() + 1) % 2) / 2.0 - 1; + + QPainterPath path1; + path1.addEllipse(circleCenter, radius, radius); + radius *= 0.85; + QPainterPath path2; + path2.addEllipse(circleCenter, radius, radius); + radius *= 0.85; + QPainterPath path3; + path3.addEllipse(circleCenter, radius, radius); + radius *= 0.5; + QPainterPath path4; + path4.addEllipse(circleCenter, radius, radius); + + QPolygon topLeftPol, bottomRightPol; + topLeftPol.setPoints(3, r.x(), r.y(), r.x(), r.y() + r.height(), r.x() + r.width(), r.y()); + bottomRightPol.setPoints(3, r.x(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height(), r.x() + r.width(), r.y()); + + p->setClipRegion(QRegion(topLeftPol)); + p->setPen(opt->palette.dark().color()); + p->setBrush(opt->palette.dark().color()); + p->drawPath(path1); + p->setPen(opt->palette.shadow().color()); + p->setBrush(opt->palette.shadow().color()); + p->drawPath(path2); + + p->setClipRegion(QRegion(bottomRightPol)); + p->setPen(opt->palette.light().color()); + p->setBrush(opt->palette.light().color()); + p->drawPath(path1); + p->setPen(opt->palette.midlight().color()); + p->setBrush(opt->palette.midlight().color()); + p->drawPath(path2); + + QColor fillColor = ((opt->state & State_Sunken) || !(opt->state & State_Enabled)) ? + opt->palette.button().color() : opt->palette.base().color(); + + p->setClipping(false); + p->setPen(fillColor); + p->setBrush(fillColor); + p->drawPath(path3); + + if (opt->state & State_On) { + p->setPen(opt->palette.text().color()); + p->setBrush(opt->palette.text()); + p->drawPath(path4); + } + p->restore(); + break; + } +#ifndef QT_NO_FRAME + case PE_Frame: + case PE_FrameMenu: + if (const QStyleOptionFrame *frame = qstyleoption_cast(opt)) { + if (frame->lineWidth == 2 || pe == PE_Frame) { + QPalette popupPal = frame->palette; + if (pe == PE_FrameMenu) { + popupPal.setColor(QPalette::Light, frame->palette.window().color()); + popupPal.setColor(QPalette::Midlight, frame->palette.light().color()); + } + if (pe == PE_Frame && (frame->state & State_Raised)) + qDrawWinButton(p, frame->rect, popupPal, frame->state & State_Sunken); + else if (pe == PE_Frame && (frame->state & State_Sunken)) + { + popupPal.setColor(QPalette::Midlight, frame->palette.window().color()); + qDrawWinPanel(p, frame->rect, popupPal, frame->state & State_Sunken); + } + else + qDrawWinPanel(p, frame->rect, popupPal, frame->state & State_Sunken); + } else { + QCommonStyle::drawPrimitive(pe, opt, p, w); + } + } else { + QPalette popupPal = opt->palette; + popupPal.setColor(QPalette::Light, opt->palette.window().color()); + popupPal.setColor(QPalette::Midlight, opt->palette.light().color()); + qDrawWinPanel(p, opt->rect, popupPal, opt->state & State_Sunken); + } + break; +#endif // QT_NO_FRAME + case PE_FrameButtonBevel: + case PE_PanelButtonBevel: { + QBrush fill; + bool panel = pe != PE_FrameButtonBevel; + p->setBrushOrigin(opt->rect.topLeft()); + if (!(opt->state & State_Sunken) && (opt->state & State_On)) + fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + else + fill = opt->palette.brush(QPalette::Button); + + if (opt->state & (State_Raised | State_On | State_Sunken)) { + qDrawWinButton(p, opt->rect, opt->palette, opt->state & (State_Sunken | State_On), + panel ? &fill : nullptr); + } else { + if (panel) + p->fillRect(opt->rect, fill); + else + p->drawRect(opt->rect); + } + break; } + case PE_FrameWindow: { + QPalette popupPal = opt->palette; + popupPal.setColor(QPalette::Light, opt->palette.window().color()); + popupPal.setColor(QPalette::Midlight, opt->palette.light().color()); + qDrawWinPanel(p, opt->rect, popupPal, opt->state & State_Sunken); + break; } +#if QT_CONFIG(dockwidget) + case PE_IndicatorDockWidgetResizeHandle: + break; + case PE_FrameDockWidget: + if (qstyleoption_cast(opt)) { + proxy()->drawPrimitive(QStyle::PE_FrameWindow, opt, p, w); + } + break; +#endif // QT_CONFIG(dockwidget) + + case PE_FrameStatusBarItem: + qDrawShadePanel(p, opt->rect, opt->palette, true, 1, nullptr); + break; + + case PE_IndicatorProgressChunk: + { + bool vertical = false, inverted = false; + if (const QStyleOptionProgressBar *pb = qstyleoption_cast(opt)) { + vertical = !(pb->state & QStyle::State_Horizontal); + inverted = pb->invertedAppearance; + } + + int space = 2; + int chunksize = proxy()->pixelMetric(PM_ProgressBarChunkWidth, opt, w) - space; + if (!vertical) { + if (opt->rect.width() <= chunksize) + space = 0; + + if (inverted) + p->fillRect(opt->rect.x() + space, opt->rect.y(), opt->rect.width() - space, opt->rect.height(), + opt->palette.brush(QPalette::Highlight)); + else + p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width() - space, opt->rect.height(), + opt->palette.brush(QPalette::Highlight)); + } else { + if (opt->rect.height() <= chunksize) + space = 0; + + if (inverted) + p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width(), opt->rect.height() - space, + opt->palette.brush(QPalette::Highlight)); + else + p->fillRect(opt->rect.x(), opt->rect.y() + space, opt->rect.width(), opt->rect.height() - space, + opt->palette.brush(QPalette::Highlight)); + } + } + break; + + case PE_FrameTabWidget: { + qDrawWinButton(p, opt->rect, opt->palette, false, nullptr); + break; + } + default: + QCommonStyle::drawPrimitive(pe, opt, p, w); + } +} + +/*! \reimp */ +void QWindowsStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p, + const QWidget *widget) const +{ + switch (ce) { +#if QT_CONFIG(rubberband) + case CE_RubberBand: + if (qstyleoption_cast(opt)) { + // ### workaround for slow general painter path + QPixmap tiledPixmap(16, 16); + QPainter pixmapPainter(&tiledPixmap); + pixmapPainter.setPen(Qt::NoPen); + pixmapPainter.setBrush(Qt::Dense4Pattern); + pixmapPainter.setBackground(Qt::white); + pixmapPainter.setBackgroundMode(Qt::OpaqueMode); + pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height()); + pixmapPainter.end(); + tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage()); + p->save(); + QRect r = opt->rect; + QStyleHintReturnMask mask; + if (proxy()->styleHint(QStyle::SH_RubberBand_Mask, opt, widget, &mask)) + p->setClipRegion(mask.region); + p->drawTiledPixmap(r.x(), r.y(), r.width(), r.height(), tiledPixmap); + p->restore(); + return; + } + break; +#endif // QT_CONFIG(rubberband) + +#if QT_CONFIG(menu) && QT_CONFIG(mainwindow) + case CE_MenuBarEmptyArea: + if (widget && qobject_cast(widget->parentWidget())) { + p->fillRect(opt->rect, opt->palette.button()); + QPen oldPen = p->pen(); + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight()); + p->setPen(oldPen); + } + break; +#endif +#if QT_CONFIG(menu) + case CE_MenuItem: + if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast(opt)) { + int x, y, w, h; + menuitem->rect.getRect(&x, &y, &w, &h); + int tab = menuitem->reservedShortcutWidth; + bool dis = !(menuitem->state & State_Enabled); + bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable + ? menuitem->checked : false; + bool act = menuitem->state & State_Selected; + + // windows always has a check column, regardless whether we have an icon or not + int checkcol = qMax(menuitem->maxIconWidth, QWindowsStylePrivate::windowsCheckMarkWidth); + + QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button); + p->fillRect(menuitem->rect.adjusted(0, 0, -1, 0), fill); + + if (menuitem->menuItemType == QStyleOptionMenuItem::Separator){ + int yoff = y-1 + h / 2; + p->setPen(menuitem->palette.dark().color()); + p->drawLine(x + 2, yoff, x + w - 4, yoff); + p->setPen(menuitem->palette.light().color()); + p->drawLine(x + 2, yoff + 1, x + w - 4, yoff + 1); + return; + } + + QRect vCheckRect = visualRect(opt->direction, menuitem->rect, QRect(menuitem->rect.x(), menuitem->rect.y(), checkcol, menuitem->rect.height())); + if (!menuitem->icon.isNull() && checked) { + if (act) { + qDrawShadePanel(p, vCheckRect, + menuitem->palette, true, 1, + &menuitem->palette.brush(QPalette::Button)); + } else { + QBrush fill(menuitem->palette.light().color(), Qt::Dense4Pattern); + qDrawShadePanel(p, vCheckRect, menuitem->palette, true, 1, &fill); + } + } else if (!act) { + p->fillRect(vCheckRect, menuitem->palette.brush(QPalette::Button)); + } + + // On Windows Style, if we have a checkable item and an icon we + // draw the icon recessed to indicate an item is checked. If we + // have no icon, we draw a checkmark instead. + if (!menuitem->icon.isNull()) { + QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; + if (act && !dis) + mode = QIcon::Active; + QPixmap pixmap; + if (checked) + pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, opt, widget), mode, QIcon::On); + else + pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, opt, widget), mode); + QRect pmr(QPoint(0, 0), pixmap.deviceIndependentSize().toSize()); + pmr.moveCenter(vCheckRect.center()); + p->setPen(menuitem->palette.text().color()); + p->drawPixmap(pmr.topLeft(), pixmap); + } else if (checked) { + QStyleOptionMenuItem newMi = *menuitem; + newMi.state = State_None; + if (!dis) + newMi.state |= State_Enabled; + if (act) + newMi.state |= State_On | State_Selected; + newMi.rect = visualRect(opt->direction, menuitem->rect, QRect(menuitem->rect.x() + QWindowsStylePrivate::windowsItemFrame, + menuitem->rect.y() + QWindowsStylePrivate::windowsItemFrame, + checkcol - 2 * QWindowsStylePrivate::windowsItemFrame, + menuitem->rect.height() - 2 * QWindowsStylePrivate::windowsItemFrame)); + proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget); + } + p->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color()); + + QColor discol; + if (dis) { + discol = menuitem->palette.text().color(); + p->setPen(discol); + } + + int xm = int(QWindowsStylePrivate::windowsItemFrame) + checkcol + int(QWindowsStylePrivate::windowsItemHMargin); + int xpos = menuitem->rect.x() + xm; + QRect textRect(xpos, y + QWindowsStylePrivate::windowsItemVMargin, + w - xm - QWindowsStylePrivate::windowsRightBorder - tab + 1, h - 2 * QWindowsStylePrivate::windowsItemVMargin); + QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect); + QStringView s(menuitem->text); + if (!s.isEmpty()) { // draw text + p->save(); + qsizetype t = s.indexOf(u'\t'); + int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) + text_flags |= Qt::TextHideMnemonic; + text_flags |= Qt::AlignLeft; + if (t >= 0) { + QRect vShortcutRect = visualRect(opt->direction, menuitem->rect, + QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom()))); + const QString textToDraw = s.mid(t + 1).toString(); + if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, opt, widget)) { + p->setPen(menuitem->palette.light().color()); + p->drawText(vShortcutRect.adjusted(1, 1, 1, 1), text_flags, textToDraw); + p->setPen(discol); + } + p->drawText(vShortcutRect, text_flags, textToDraw); + s = s.left(t); + } + QFont font = menuitem->font; + if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) + font.setBold(true); + p->setFont(font); + const QString textToDraw = s.left(t).toString(); + if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, opt, widget)) { + p->setPen(menuitem->palette.light().color()); + p->drawText(vTextRect.adjusted(1, 1, 1, 1), text_flags, textToDraw); + p->setPen(discol); + } + p->drawText(vTextRect, text_flags, textToDraw); + p->restore(); + } + if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow + int dim = (h - 2 * QWindowsStylePrivate::windowsItemFrame) / 2; + PrimitiveElement arrow; + arrow = (opt->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; + xpos = x + w - QWindowsStylePrivate::windowsArrowHMargin - QWindowsStylePrivate::windowsItemFrame - dim; + QRect vSubMenuRect = visualRect(opt->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim)); + QStyleOptionMenuItem newMI = *menuitem; + newMI.rect = vSubMenuRect; + newMI.state = dis ? State_None : State_Enabled; + if (act) + newMI.palette.setColor(QPalette::ButtonText, + newMI.palette.highlightedText().color()); + proxy()->drawPrimitive(arrow, &newMI, p, widget); + } + + } + break; +#endif // QT_CONFIG(menu) +#if QT_CONFIG(menubar) + case CE_MenuBarItem: + if (const QStyleOptionMenuItem *mbi = qstyleoption_cast(opt)) { + bool active = mbi->state & State_Selected; + bool hasFocus = mbi->state & State_HasFocus; + bool down = mbi->state & State_Sunken; + QStyleOptionMenuItem newMbi = *mbi; + p->fillRect(mbi->rect, mbi->palette.brush(QPalette::Button)); + if (active || hasFocus) { + QBrush b = mbi->palette.brush(QPalette::Button); + if (active && down) + p->setBrushOrigin(p->brushOrigin() + QPoint(1, 1)); + if (active && hasFocus) + qDrawShadeRect(p, mbi->rect.x(), mbi->rect.y(), mbi->rect.width(), + mbi->rect.height(), mbi->palette, active && down, 1, 0, &b); + if (active && down) { + newMbi.rect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, mbi, widget), + proxy()->pixelMetric(PM_ButtonShiftVertical, mbi, widget)); + p->setBrushOrigin(p->brushOrigin() - QPoint(1, 1)); + } + } + QCommonStyle::drawControl(ce, &newMbi, p, widget); + } + break; +#endif // QT_CONFIG(menubar) +#if QT_CONFIG(tabbar) + case CE_TabBarTabShape: + if (const QStyleOptionTab *tab = qstyleoption_cast(opt)) { + bool rtlHorTabs = (tab->direction == Qt::RightToLeft + && (tab->shape == QTabBar::RoundedNorth + || tab->shape == QTabBar::RoundedSouth)); + bool selected = tab->state & State_Selected; + bool lastTab = ((!rtlHorTabs && tab->position == QStyleOptionTab::End) + || (rtlHorTabs + && tab->position == QStyleOptionTab::Beginning)); + bool firstTab = ((!rtlHorTabs + && tab->position == QStyleOptionTab::Beginning) + || (rtlHorTabs + && tab->position == QStyleOptionTab::End)); + bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; + bool previousSelected = + ((!rtlHorTabs + && tab->selectedPosition == QStyleOptionTab::PreviousIsSelected) + || (rtlHorTabs + && tab->selectedPosition == QStyleOptionTab::NextIsSelected)); + bool nextSelected = + ((!rtlHorTabs + && tab->selectedPosition == QStyleOptionTab::NextIsSelected) + || (rtlHorTabs + && tab->selectedPosition + == QStyleOptionTab::PreviousIsSelected)); + int tabBarAlignment = proxy()->styleHint(SH_TabBar_Alignment, tab, widget); + bool leftAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignLeft) + || (rtlHorTabs + && tabBarAlignment == Qt::AlignRight); + + bool rightAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignRight) + || (rtlHorTabs + && tabBarAlignment == Qt::AlignLeft); + + QColor light = tab->palette.light().color(); + QColor dark = tab->palette.dark().color(); + QColor shadow = tab->palette.shadow().color(); + int borderThinkness = proxy()->pixelMetric(PM_TabBarBaseOverlap, tab, widget); + if (selected) + borderThinkness /= 2; + QRect r2(opt->rect); + int x1 = r2.left(); + int x2 = r2.right(); + int y1 = r2.top(); + int y2 = r2.bottom(); + switch (tab->shape) { + default: + QCommonStyle::drawControl(ce, tab, p, widget); + break; + case QTabBar::RoundedNorth: { + if (!selected) { + y1 += 2; + x1 += onlyOne || firstTab ? borderThinkness : 0; + x2 -= onlyOne || lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 2), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x1,y2-1,x2-x1,1), tab->palette.window()); + p->fillRect(QRect(x1,y2,x2-x1,1), tab->palette.window()); + } + // Left + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x1, y1 + 2, x1, y2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness)); + p->drawPoint(x1 + 1, y1 + 1); + } + // Top + { + int beg = x1 + (previousSelected ? 0 : 2); + int end = x2 - (nextSelected ? 0 : 2); + p->setPen(light); + p->drawLine(beg, y1, end, y1); + } + // Right + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x2, y1 + 2, x2, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + p->drawPoint(x2 - 1, y1 + 1); + p->setPen(dark); + p->drawLine(x2 - 1, y1 + 2, x2 - 1, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + } + break; } + case QTabBar::RoundedSouth: { + if (!selected) { + y2 -= 2; + x1 += firstTab ? borderThinkness : 0; + x2 -= lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 1, y1 + 2, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x1, y1 + 1, (x2 - 1)-x1, 1), tab->palette.window()); + p->fillRect(QRect(x1, y1, (x2 - 1)-x1, 1), tab->palette.window()); + } + // Left + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x1, y2 - 2, x1, y1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness)); + p->drawPoint(x1 + 1, y2 - 1); + } + // Bottom + { + int beg = x1 + (previousSelected ? 0 : 2); + int end = x2 - (nextSelected ? 0 : 2); + p->setPen(shadow); + p->drawLine(beg, y2, end, y2); + p->setPen(dark); + p->drawLine(beg, y2 - 1, end, y2 - 1); + } + // Right + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x2, y2 - 2, x2, y1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + p->drawPoint(x2 - 1, y2 - 1); + p->setPen(dark); + p->drawLine(x2 - 1, y2 - 2, x2 - 1, y1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + } + break; } + case QTabBar::RoundedWest: { + if (!selected) { + x1 += 2; + y1 += firstTab ? borderThinkness : 0; + y2 -= lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 2, (y2 - y1) - 1), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x2 - 1, y1, 1, y2-y1), tab->palette.window()); + p->fillRect(QRect(x2, y1, 1, y2-y1), tab->palette.window()); + } + // Top + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x1 + 2, y1, x2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1); + p->drawPoint(x1 + 1, y1 + 1); + } + // Left + { + int beg = y1 + (previousSelected ? 0 : 2); + int end = y2 - (nextSelected ? 0 : 2); + p->setPen(light); + p->drawLine(x1, beg, x1, end); + } + // Bottom + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x1 + 3, y2, x2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2); + p->drawPoint(x1 + 2, y2 - 1); + p->setPen(dark); + p->drawLine(x1 + 3, y2 - 1, x2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2 - 1); + p->drawPoint(x1 + 1, y2 - 1); + p->drawPoint(x1 + 2, y2); + } + break; } + case QTabBar::RoundedEast: { + if (!selected) { + x2 -= 2; + y1 += firstTab ? borderThinkness : 0; + y2 -= lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 2, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x1 + 1, y1, 1, (y2 - 1)-y1),tab->palette.window()); + p->fillRect(QRect(x1, y1, 1, (y2-1)-y1), tab->palette.window()); + } + // Top + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x2 - 2, y1, x1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1); + p->drawPoint(x2 - 1, y1 + 1); + } + // Right + { + int beg = y1 + (previousSelected ? 0 : 2); + int end = y2 - (nextSelected ? 0 : 2); + p->setPen(shadow); + p->drawLine(x2, beg, x2, end); + p->setPen(dark); + p->drawLine(x2 - 1, beg, x2 - 1, end); + } + // Bottom + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x2 - 2, y2, x1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2); + p->drawPoint(x2 - 1, y2 - 1); + p->setPen(dark); + p->drawLine(x2 - 2, y2 - 1, x1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2 - 1); + } + break; } + } + } + break; +#endif // QT_CONFIG(tabbar) + case CE_ToolBoxTabShape: + qDrawShadePanel(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1, + &opt->palette.brush(QPalette::Button)); + break; +#if QT_CONFIG(splitter) + case CE_Splitter: + p->eraseRect(opt->rect); + break; +#endif // QT_CONFIG(splitter) +#if QT_CONFIG(scrollbar) + case CE_ScrollBarSubLine: + case CE_ScrollBarAddLine: { + if ((opt->state & State_Sunken)) { + p->setPen(opt->palette.dark().color()); + p->setBrush(opt->palette.brush(QPalette::Button)); + p->drawRect(opt->rect.adjusted(0, 0, -1, -1)); + } else { + QStyleOption buttonOpt = *opt; + if (!(buttonOpt.state & State_Sunken)) + buttonOpt.state |= State_Raised; + QPalette pal(opt->palette); + pal.setColor(QPalette::Button, opt->palette.light().color()); + pal.setColor(QPalette::Light, opt->palette.button().color()); + qDrawWinButton(p, opt->rect, pal, opt->state & (State_Sunken | State_On), + &opt->palette.brush(QPalette::Button)); + } + PrimitiveElement arrow; + if (opt->state & State_Horizontal) { + if (ce == CE_ScrollBarAddLine) + arrow = opt->direction == Qt::LeftToRight ? PE_IndicatorArrowRight : PE_IndicatorArrowLeft; + else + arrow = opt->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; + } else { + if (ce == CE_ScrollBarAddLine) + arrow = PE_IndicatorArrowDown; + else + arrow = PE_IndicatorArrowUp; + } + QStyleOption arrowOpt = *opt; + arrowOpt.rect = opt->rect.adjusted(4, 4, -4, -4); + proxy()->drawPrimitive(arrow, &arrowOpt, p, widget); + break; } + case CE_ScrollBarAddPage: + case CE_ScrollBarSubPage: { + QBrush br; + QBrush bg = p->background(); + Qt::BGMode bg_mode = p->backgroundMode(); + p->setPen(Qt::NoPen); + p->setBackgroundMode(Qt::OpaqueMode); + + if (opt->state & State_Sunken) { + br = QBrush(opt->palette.shadow().color(), Qt::Dense4Pattern); + p->setBackground(opt->palette.dark().color()); + p->setBrush(br); + } else { + const QBrush paletteBrush = opt->palette.brush(QPalette::Light); + if (paletteBrush.style() == Qt::TexturePattern) { + if (qHasPixmapTexture(paletteBrush)) + br = QBrush(paletteBrush.texture()); + else + br = QBrush(paletteBrush.textureImage()); + } else + br = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + p->setBackground(opt->palette.window().color()); + p->setBrush(br); + } + p->drawRect(opt->rect); + p->setBackground(bg); + p->setBackgroundMode(bg_mode); + break; } + case CE_ScrollBarSlider: + if (!(opt->state & State_Enabled)) { + QBrush br; + const QBrush paletteBrush = opt->palette.brush(QPalette::Light); + if (paletteBrush.style() == Qt::TexturePattern) { + if (qHasPixmapTexture(paletteBrush)) + br = QBrush(paletteBrush.texture()); + else + br = QBrush(paletteBrush.textureImage()); + } else + br = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + p->setPen(Qt::NoPen); + p->setBrush(br); + p->setBackgroundMode(Qt::OpaqueMode); + p->drawRect(opt->rect); + } else { + QStyleOptionButton buttonOpt; + buttonOpt.QStyleOption::operator=(*opt); + buttonOpt.state = State_Enabled | State_Raised; + + QPalette pal(opt->palette); + pal.setColor(QPalette::Button, opt->palette.light().color()); + pal.setColor(QPalette::Light, opt->palette.button().color()); + qDrawWinButton(p, opt->rect, pal, false, &opt->palette.brush(QPalette::Button)); + } + break; +#endif // QT_CONFIG(scrollbar) + case CE_HeaderSection: { + QBrush fill; + if (opt->state & State_On) + fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + else + fill = opt->palette.brush(QPalette::Button); + + if (opt->state & (State_Raised | State_Sunken)) { + qDrawWinButton(p, opt->rect, opt->palette, opt->state & State_Sunken, &fill); + } else { + p->fillRect(opt->rect, fill); + } + break; } +#if QT_CONFIG(toolbar) + case CE_ToolBar: + if (const QStyleOptionToolBar *toolbar = qstyleoption_cast(opt)) { + // Reserve the beveled appearance only for mainwindow toolbars + if (!(widget && qobject_cast (widget->parentWidget()))) + break; + + QRect rect = opt->rect; + bool paintLeftBorder = true; + bool paintRightBorder = true; + bool paintBottomBorder = true; + + switch (toolbar->toolBarArea){ + case Qt::BottomToolBarArea : + switch(toolbar->positionOfLine){ + case QStyleOptionToolBar::Beginning: + case QStyleOptionToolBar::OnlyOne: + paintBottomBorder = false; + break; + default: + break; + } + Q_FALLTHROUGH(); // It continues in the end of the next case + case Qt::TopToolBarArea : + switch(toolbar->positionWithinLine){ + case QStyleOptionToolBar::Beginning: + paintLeftBorder = false; + break; + case QStyleOptionToolBar::End: + paintRightBorder = false; + break; + case QStyleOptionToolBar::OnlyOne: + paintRightBorder = false; + paintLeftBorder = false; + break; + default: + break; + } + if (opt->direction == Qt::RightToLeft) //reverse layout changes the order of Beginning/end + std::swap(paintLeftBorder, paintRightBorder); + break; + case Qt::RightToolBarArea : + switch (toolbar->positionOfLine){ + case QStyleOptionToolBar::Beginning: + case QStyleOptionToolBar::OnlyOne: + paintRightBorder = false; + break; + default: + break; + } + break; + case Qt::LeftToolBarArea : + switch (toolbar->positionOfLine){ + case QStyleOptionToolBar::Beginning: + case QStyleOptionToolBar::OnlyOne: + paintLeftBorder = false; + break; + default: + break; + } + break; + default: + break; + } + + + //draw top border + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.topLeft().x(), + rect.topLeft().y(), + rect.topRight().x(), + rect.topRight().y()); + + if (paintLeftBorder){ + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.topLeft().x(), + rect.topLeft().y(), + rect.bottomLeft().x(), + rect.bottomLeft().y()); + } + + if (paintRightBorder){ + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.topRight().x(), + rect.topRight().y(), + rect.bottomRight().x(), + rect.bottomRight().y()); + } + + if (paintBottomBorder){ + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.bottomLeft().x(), + rect.bottomLeft().y(), + rect.bottomRight().x(), + rect.bottomRight().y()); + } + } + break; + + +#endif // QT_CONFIG(toolbar) + + case CE_ProgressBarContents: + if (const QStyleOptionProgressBar *pb = qstyleoption_cast(opt)) { + QRect rect = pb->rect; + if (!rect.isValid()) + return; + + const bool vertical = !(pb->state & QStyle::State_Horizontal); + const bool inverted = pb->invertedAppearance; + + QTransform m; + if (vertical) { + rect = QRect(rect.y(), rect.x(), rect.height(), rect.width()); // flip width and height + m.rotate(90); + m.translate(0, -(rect.height() + rect.y()*2)); + } + QPalette pal2 = pb->palette; + // Correct the highlight color if it is the same as the background + if (pal2.highlight() == pal2.window()) + pal2.setColor(QPalette::Highlight, pb->palette.color(QPalette::Active, + QPalette::Highlight)); + bool reverse = ((!vertical && (pb->direction == Qt::RightToLeft)) || vertical); + if (inverted) + reverse = !reverse; + int w = rect.width(); + Q_D(const QWindowsStyle); + if (pb->minimum == 0 && pb->maximum == 0) { + const int unit_width = proxy()->pixelMetric(PM_ProgressBarChunkWidth, pb, widget); + QStyleOptionProgressBar pbBits = *pb; + Q_ASSERT(unit_width >0); + + pbBits.rect = rect; + pbBits.palette = pal2; + + int step = 0; + int chunkCount = w / unit_width + 1; +#if QT_CONFIG(animation) + if (QProgressStyleAnimation *animation = qobject_cast(d->animation(opt->styleObject))) + step = (animation->animationStep() / 3) % chunkCount; + else + d->startAnimation(new QProgressStyleAnimation(d->animationFps, opt->styleObject)); +#else + Q_UNUSED(d); +#endif + int chunksInRow = 5; + int myY = pbBits.rect.y(); + int myHeight = pbBits.rect.height(); + int chunksToDraw = chunksInRow; + + if (step > chunkCount - 5)chunksToDraw = (chunkCount - step); + p->save(); + p->setClipRect(m.mapRect(QRectF(rect)).toRect()); + + int x0 = reverse ? rect.left() + rect.width() - unit_width*(step) - unit_width : rect.left() + unit_width * step; + int x = 0; + + for (int i = 0; i < chunksToDraw ; ++i) { + pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight); + pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect(); + proxy()->drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p, widget); + x += reverse ? -unit_width : unit_width; + } + //Draw wrap-around chunks + if ( step > chunkCount-5){ + x0 = reverse ? rect.left() + rect.width() - unit_width : rect.left() ; + x = 0; + int chunksToDraw = step - (chunkCount - chunksInRow); + for (int i = 0; i < chunksToDraw ; ++i) { + pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight); + pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect(); + proxy()->drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p, widget); + x += reverse ? -unit_width : unit_width; + } + } + p->restore(); //restore state + } + else { +#if QT_CONFIG(animation) + d->stopAnimation(opt->styleObject); +#endif + QCommonStyle::drawControl(ce, opt, p, widget); + } + } + break; + +#if QT_CONFIG(dockwidget) + case CE_DockWidgetTitle: + + if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast(opt)) { + Q_D(const QWindowsStyle); + + const bool verticalTitleBar = dwOpt->verticalTitleBar; + + QRect rect = dwOpt->rect; + QRect r = rect; + + if (verticalTitleBar) { + r = r.transposed(); + + p->save(); + p->translate(r.left(), r.top() + r.width()); + p->rotate(-90); + p->translate(-r.left(), -r.top()); + } + + bool floating = false; + bool active = dwOpt->state & State_Active; + QColor inactiveCaptionTextColor = d->inactiveCaptionText; + if (dwOpt->movable) { + QColor left, right; + + //Titlebar gradient + if (opt->state & QStyle::State_Window) { + floating = true; + if (active) { + left = d->activeCaptionColor; + right = d->activeGradientCaptionColor; + } else { + left = d->inactiveCaptionColor; + right = d->inactiveGradientCaptionColor; + } + QBrush fillBrush(left); + if (left != right) { + QPoint p1(r.x(), r.top() + r.height()/2); + QPoint p2(rect.right(), r.top() + r.height()/2); + QLinearGradient lg(p1, p2); + lg.setColorAt(0, left); + lg.setColorAt(1, right); + fillBrush = lg; + } + p->fillRect(r.adjusted(0, 0, 0, -3), fillBrush); + } + } + if (!dwOpt->title.isEmpty()) { + QFont oldFont = p->font(); + if (floating) { + QFont font = oldFont; + font.setBold(true); + p->setFont(font); + } + QPalette palette = dwOpt->palette; + palette.setColor(QPalette::Window, inactiveCaptionTextColor); + QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, opt, widget); + if (verticalTitleBar) { + titleRect = QRect(r.left() + rect.bottom() + - titleRect.bottom(), + r.top() + titleRect.left() - rect.left(), + titleRect.height(), titleRect.width()); + } + proxy()->drawItemText(p, titleRect, + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextHideMnemonic, palette, + dwOpt->state & State_Enabled, dwOpt->title, + floating ? (active ? QPalette::BrightText : QPalette::Window) : QPalette::WindowText); + p->setFont(oldFont); + } + if (verticalTitleBar) + p->restore(); + } + return; +#endif // QT_CONFIG(dockwidget) +#if QT_CONFIG(combobox) + case CE_ComboBoxLabel: + if (const QStyleOptionComboBox *cb = qstyleoption_cast(opt)) { + if (cb->state & State_HasFocus) { + p->setPen(cb->palette.highlightedText().color()); + p->setBackground(cb->palette.highlight()); + } else { + p->setPen(cb->palette.text().color()); + p->setBackground(cb->palette.window()); + } + } + QCommonStyle::drawControl(ce, opt, p, widget); + break; +#endif // QT_CONFIG(combobox) + default: + QCommonStyle::drawControl(ce, opt, p, widget); + } +} + +/*! \reimp */ +QRect QWindowsStyle::subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *w) const +{ + QRect r; + switch (sr) { + case SE_SliderFocusRect: + case SE_ToolBoxTabContents: + r = visualRect(opt->direction, opt->rect, opt->rect); + break; + case SE_DockWidgetTitleBarText: { + r = QCommonStyle::subElementRect(sr, opt, w); + const QStyleOptionDockWidget *dwOpt + = qstyleoption_cast(opt); + const bool verticalTitleBar = dwOpt && dwOpt->verticalTitleBar; + int m = proxy()->pixelMetric(PM_DockWidgetTitleMargin, opt, w); + if (verticalTitleBar) { + r.adjust(0, 0, 0, -m); + } else { + if (opt->direction == Qt::LeftToRight) + r.adjust(m, 0, 0, 0); + else + r.adjust(0, 0, -m, 0); + } + break; + } + case SE_ProgressBarContents: + r = QCommonStyle::subElementRect(SE_ProgressBarGroove, opt, w); + r.adjust(3, 3, -3, -3); + break; + default: + r = QCommonStyle::subElementRect(sr, opt, w); + } + return r; +} + + +/*! \reimp */ +void QWindowsStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, + QPainter *p, const QWidget *widget) const +{ + switch (cc) { +#if QT_CONFIG(slider) + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast(opt)) { + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); + int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + int ticks = slider->tickPosition; + QRect groove = proxy()->subControlRect(CC_Slider, slider, SC_SliderGroove, widget); + QRect handle = proxy()->subControlRect(CC_Slider, slider, SC_SliderHandle, widget); + + if ((slider->subControls & SC_SliderGroove) && groove.isValid()) { + int mid = thickness / 2; + + if (ticks & QSlider::TicksAbove) + mid += len / 8; + if (ticks & QSlider::TicksBelow) + mid -= len / 8; + + p->setPen(slider->palette.shadow().color()); + if (slider->orientation == Qt::Horizontal) { + qDrawWinPanel(p, groove.x(), groove.y() + mid - 2, + groove.width(), 4, slider->palette, true); + p->drawLine(groove.x() + 1, groove.y() + mid - 1, + groove.x() + groove.width() - 3, groove.y() + mid - 1); + } else { + qDrawWinPanel(p, groove.x() + mid - 2, groove.y(), + 4, groove.height(), slider->palette, true); + p->drawLine(groove.x() + mid - 1, groove.y() + 1, + groove.x() + mid - 1, groove.y() + groove.height() - 3); + } + } + + if (slider->subControls & SC_SliderTickmarks) { + QStyleOptionSlider tmpSlider = *slider; + tmpSlider.subControls = SC_SliderTickmarks; + QCommonStyle::drawComplexControl(cc, &tmpSlider, p, widget); + } + + if (slider->subControls & SC_SliderHandle) { + // 4444440 + // 4333310 + // 4322210 + // 4322210 + // 4322210 + // 4322210 + // *43210* + // **410** + // ***0*** + const QColor c0 = slider->palette.shadow().color(); + const QColor c1 = slider->palette.dark().color(); + // const QColor c2 = g.button(); + const QColor c3 = slider->palette.midlight().color(); + const QColor c4 = slider->palette.light().color(); + QBrush handleBrush; + + if (slider->state & State_Enabled) { + handleBrush = slider->palette.color(QPalette::Button); + } else { + handleBrush = QBrush(slider->palette.color(QPalette::Button), + Qt::Dense4Pattern); + } + + + int x = handle.x(), y = handle.y(), + wi = handle.width(), he = handle.height(); + + int x1 = x; + int x2 = x+wi-1; + int y1 = y; + int y2 = y+he-1; + + Qt::Orientation orient = slider->orientation; + bool tickAbove = slider->tickPosition == QSlider::TicksAbove; + bool tickBelow = slider->tickPosition == QSlider::TicksBelow; + + if (slider->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*slider); + fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget); + } + + if ((tickAbove && tickBelow) || (!tickAbove && !tickBelow)) { + Qt::BGMode oldMode = p->backgroundMode(); + p->setBackgroundMode(Qt::OpaqueMode); + qDrawWinButton(p, QRect(x, y, wi, he), slider->palette, false, + &handleBrush); + p->setBackgroundMode(oldMode); + return; + } + + QSliderDirection dir; + + if (orient == Qt::Horizontal) + dir = tickAbove ? SlUp : SlDown; + else + dir = tickAbove ? SlLeft : SlRight; + + std::array points; + int d = 0; + switch (dir) { + case SlUp: + y1 = y1 + wi / 2; + d = (wi + 1) / 2 - 1; + points = {QPoint(x1, y1), QPoint(x1, y2), QPoint(x2, y2), + QPoint(x2, y1), QPoint(x1 + d, y1 - d)}; + break; + case SlDown: + y2 = y2 - wi / 2; + d = (wi + 1) / 2 - 1; + points = {QPoint(x1, y1), QPoint(x1, y2), QPoint(x1 + d, y2 + d), + QPoint(x2, y2), QPoint(x2, y1)}; + break; + case SlLeft: + d = (he + 1) / 2 - 1; + x1 = x1 + he / 2; + points = {QPoint(x1, y1), QPoint(x1 - d, y1 + d), QPoint(x1,y2), + QPoint(x2, y2), QPoint(x2, y1)}; + break; + case SlRight: + d = (he + 1) / 2 - 1; + x2 = x2 - he / 2; + points = {QPoint(x1, y1), QPoint(x1, y2), QPoint(x2, y2), + QPoint(x2 + d, y1 + d), QPoint(x2, y1)}; + break; + } + + QBrush oldBrush = p->brush(); + p->setPen(Qt::NoPen); + p->setBrush(handleBrush); + Qt::BGMode oldMode = p->backgroundMode(); + p->setBackgroundMode(Qt::OpaqueMode); + p->drawRect(x1, y1, x2-x1+1, y2-y1+1); + p->drawPolygon(points.data(), static_cast(points.size())); + p->setBrush(oldBrush); + p->setBackgroundMode(oldMode); + + if (dir != SlUp) { + p->setPen(c4); + p->drawLine(x1, y1, x2, y1); + p->setPen(c3); + p->drawLine(x1, y1+1, x2, y1+1); + } + if (dir != SlLeft) { + p->setPen(c3); + p->drawLine(x1+1, y1+1, x1+1, y2); + p->setPen(c4); + p->drawLine(x1, y1, x1, y2); + } + if (dir != SlRight) { + p->setPen(c0); + p->drawLine(x2, y1, x2, y2); + p->setPen(c1); + p->drawLine(x2-1, y1+1, x2-1, y2-1); + } + if (dir != SlDown) { + p->setPen(c0); + p->drawLine(x1, y2, x2, y2); + p->setPen(c1); + p->drawLine(x1+1, y2-1, x2-1, y2-1); + } + + switch (dir) { + case SlUp: + p->setPen(c4); + p->drawLine(x1, y1, x1+d, y1-d); + p->setPen(c0); + d = wi - d - 1; + p->drawLine(x2, y1, x2-d, y1-d); + d--; + p->setPen(c3); + p->drawLine(x1+1, y1, x1+1+d, y1-d); + p->setPen(c1); + p->drawLine(x2-1, y1, x2-1-d, y1-d); + break; + case SlDown: + p->setPen(c4); + p->drawLine(x1, y2, x1+d, y2+d); + p->setPen(c0); + d = wi - d - 1; + p->drawLine(x2, y2, x2-d, y2+d); + d--; + p->setPen(c3); + p->drawLine(x1+1, y2, x1+1+d, y2+d); + p->setPen(c1); + p->drawLine(x2-1, y2, x2-1-d, y2+d); + break; + case SlLeft: + p->setPen(c4); + p->drawLine(x1, y1, x1-d, y1+d); + p->setPen(c0); + d = he - d - 1; + p->drawLine(x1, y2, x1-d, y2-d); + d--; + p->setPen(c3); + p->drawLine(x1, y1+1, x1-d, y1+1+d); + p->setPen(c1); + p->drawLine(x1, y2-1, x1-d, y2-1-d); + break; + case SlRight: + p->setPen(c4); + p->drawLine(x2, y1, x2+d, y1+d); + p->setPen(c0); + d = he - d - 1; + p->drawLine(x2, y2, x2+d, y2-d); + d--; + p->setPen(c3); + p->drawLine(x2, y1+1, x2+d, y1+1+d); + p->setPen(c1); + p->drawLine(x2, y2-1, x2+d, y2-1-d); + break; + } + } + } + break; +#endif // QT_CONFIG(slider) +#if QT_CONFIG(scrollbar) + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast(opt)) { + QStyleOptionSlider newScrollbar = *scrollbar; + if (scrollbar->minimum == scrollbar->maximum) + newScrollbar.state &= ~State_Enabled; //do not draw the slider. + QCommonStyle::drawComplexControl(cc, &newScrollbar, p, widget); + } + break; +#endif // QT_CONFIG(scrollbar) +#if QT_CONFIG(combobox) + case CC_ComboBox: + if (const QStyleOptionComboBox *cmb = qstyleoption_cast(opt)) { + QBrush editBrush = cmb->palette.brush(QPalette::Button); + if ((cmb->subControls & SC_ComboBoxFrame)) { + if (cmb->frame) { + QPalette shadePal = opt->palette; + shadePal.setColor(QPalette::Midlight, shadePal.button().color()); + qDrawWinPanel(p, opt->rect, shadePal, true, &editBrush); + } + else { + p->fillRect(opt->rect, editBrush); + } + } + if (cmb->subControls & SC_ComboBoxArrow) { + State flags = State_None; + + QRect ar = proxy()->subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget); + bool sunkenArrow = cmb->activeSubControls == SC_ComboBoxArrow + && cmb->state & State_Sunken; + if (sunkenArrow) { + p->setPen(cmb->palette.dark().color()); + p->setBrush(cmb->palette.brush(QPalette::Button)); + p->drawRect(ar.adjusted(0,0,-1,-1)); + } else { + // Make qDrawWinButton use the right colors for drawing the shade of the button + QPalette pal(cmb->palette); + pal.setColor(QPalette::Button, cmb->palette.light().color()); + pal.setColor(QPalette::Light, cmb->palette.button().color()); + qDrawWinButton(p, ar, pal, false, + &cmb->palette.brush(QPalette::Button)); + } + + ar.adjust(2, 2, -2, -2); + if (opt->state & State_Enabled) + flags |= State_Enabled; + if (opt->state & State_HasFocus) + flags |= State_HasFocus; + + if (sunkenArrow) + flags |= State_Sunken; + QStyleOption arrowOpt = *cmb; + arrowOpt.rect = ar.adjusted(1, 1, -1, -1); + arrowOpt.state = flags; + proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget); + } + + if (cmb->subControls & SC_ComboBoxEditField) { + QRect re = proxy()->subControlRect(CC_ComboBox, cmb, SC_ComboBoxEditField, widget); + if (cmb->state & State_HasFocus && !cmb->editable) + p->fillRect(re.x(), re.y(), re.width(), re.height(), + cmb->palette.brush(QPalette::Highlight)); + + if (cmb->state & State_HasFocus) { + p->setPen(cmb->palette.highlightedText().color()); + p->setBackground(cmb->palette.highlight()); + + } else { + p->setPen(cmb->palette.text().color()); + p->setBackground(cmb->palette.window()); + } + + if (cmb->state & State_HasFocus && !cmb->editable) { + QStyleOptionFocusRect focus; + focus.QStyleOption::operator=(*cmb); + focus.rect = subElementRect(SE_ComboBoxFocusRect, cmb, widget); + focus.state |= State_FocusAtBorder; + focus.backgroundColor = cmb->palette.highlight().color(); + proxy()->drawPrimitive(PE_FrameFocusRect, &focus, p, widget); + } + } + } + break; +#endif // QT_CONFIG(combobox) +#if QT_CONFIG(spinbox) + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast(opt)) { + QStyleOptionSpinBox copy = *sb; + PrimitiveElement pe; + bool enabled = opt->state & State_Enabled; + if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) { + QBrush editBrush = sb->palette.brush(QPalette::Base); + QRect r = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxFrame, widget); + QPalette shadePal = sb->palette; + shadePal.setColor(QPalette::Midlight, shadePal.button().color()); + qDrawWinPanel(p, r, shadePal, true, &editBrush); + } + + QPalette shadePal(opt->palette); + shadePal.setColor(QPalette::Button, opt->palette.light().color()); + shadePal.setColor(QPalette::Light, opt->palette.button().color()); + + if (sb->subControls & SC_SpinBoxUp) { + copy.subControls = SC_SpinBoxUp; + QPalette pal2 = sb->palette; + if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled)) { + pal2.setCurrentColorGroup(QPalette::Disabled); + copy.state &= ~State_Enabled; + } + + copy.palette = pal2; + + if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) { + copy.state |= State_On; + copy.state |= State_Sunken; + } else { + copy.state |= State_Raised; + copy.state &= ~State_Sunken; + } + pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinPlus + : PE_IndicatorSpinUp); + + copy.rect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget); + qDrawWinButton(p, copy.rect, shadePal, copy.state & (State_Sunken | State_On), + ©.palette.brush(QPalette::Button)); + copy.rect.adjust(4, 1, -5, -1); + if ((!enabled || !(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled)) + && proxy()->styleHint(SH_EtchDisabledText, opt, widget) ) + { + QStyleOptionSpinBox lightCopy = copy; + lightCopy.rect.adjust(1, 1, 1, 1); + lightCopy.palette.setBrush(QPalette::ButtonText, copy.palette.light()); + proxy()->drawPrimitive(pe, &lightCopy, p, widget); + } + proxy()->drawPrimitive(pe, ©, p, widget); + } + + if (sb->subControls & SC_SpinBoxDown) { + copy.subControls = SC_SpinBoxDown; + copy.state = sb->state; + QPalette pal2 = sb->palette; + if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled)) { + pal2.setCurrentColorGroup(QPalette::Disabled); + copy.state &= ~State_Enabled; + } + copy.palette = pal2; + + if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) { + copy.state |= State_On; + copy.state |= State_Sunken; + } else { + copy.state |= State_Raised; + copy.state &= ~State_Sunken; + } + pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinMinus + : PE_IndicatorSpinDown); + + copy.rect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget); + qDrawWinButton(p, copy.rect, shadePal, copy.state & (State_Sunken | State_On), + ©.palette.brush(QPalette::Button)); + copy.rect.adjust(4, 0, -5, -1); + if ((!enabled || !(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled)) + && proxy()->styleHint(SH_EtchDisabledText, opt, widget) ) + { + QStyleOptionSpinBox lightCopy = copy; + lightCopy.rect.adjust(1, 1, 1, 1); + lightCopy.palette.setBrush(QPalette::ButtonText, copy.palette.light()); + proxy()->drawPrimitive(pe, &lightCopy, p, widget); + } + proxy()->drawPrimitive(pe, ©, p, widget); + } + } + break; +#endif // QT_CONFIG(spinbox) + + default: + QCommonStyle::drawComplexControl(cc, opt, p, widget); + } +} + +/*! \reimp */ +QSize QWindowsStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, + const QSize &csz, const QWidget *widget) const +{ + QSize sz(csz); + switch (ct) { + case CT_PushButton: + if (const QStyleOptionButton *btn = qstyleoption_cast(opt)) { + sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget); + int w = sz.width(), + h = sz.height(); + int defwidth = 0; + if (btn->features & QStyleOptionButton::AutoDefaultButton) + defwidth = 2 * proxy()->pixelMetric(PM_ButtonDefaultIndicator, btn, widget); + const qreal dpi = QStyleHelper::dpi(opt); + int minwidth = int(QStyleHelper::dpiScaled(75, dpi)); + int minheight = int(QStyleHelper::dpiScaled(23, dpi)); + + if (w < minwidth + defwidth && !btn->text.isEmpty()) + w = minwidth + defwidth; + if (h < minheight + defwidth) + h = minheight + defwidth; + + sz = QSize(w, h); + } + break; +#if QT_CONFIG(menu) + case CT_MenuItem: + if (const QStyleOptionMenuItem *mi = qstyleoption_cast(opt)) { + int w = sz.width(); + sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget); + + if (mi->menuItemType == QStyleOptionMenuItem::Separator) { + sz = QSize(10, QWindowsStylePrivate::windowsSepHeight); + } + else if (mi->icon.isNull()) { + sz.setHeight(sz.height() - 2); + w -= 6; + } + + if (mi->menuItemType != QStyleOptionMenuItem::Separator && !mi->icon.isNull()) { + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, opt, widget); + sz.setHeight(qMax(sz.height(), + mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + + 2 * QWindowsStylePrivate::windowsItemFrame)); + } + int maxpmw = mi->maxIconWidth; + int tabSpacing = 20; + if (mi->text.contains(u'\t')) + w += tabSpacing; + else if (mi->menuItemType == QStyleOptionMenuItem::SubMenu) + w += 2 * QWindowsStylePrivate::windowsArrowHMargin; + else if (mi->menuItemType == QStyleOptionMenuItem::DefaultItem) { + // adjust the font and add the difference in size. + // it would be better if the font could be adjusted in the initStyleOption qmenu func!! + QFontMetrics fm(mi->font); + QFont fontBold = mi->font; + fontBold.setBold(true); + QFontMetrics fmBold(fontBold); + w += fmBold.horizontalAdvance(mi->text) - fm.horizontalAdvance(mi->text); + } + + int checkcol = qMax(maxpmw, QWindowsStylePrivate::windowsCheckMarkWidth); // Windows always shows a check column + w += checkcol; + w += int(QWindowsStylePrivate::windowsRightBorder) + 10; + sz.setWidth(w); + } + break; +#endif // QT_CONFIG(menu) +#if QT_CONFIG(menubar) + case CT_MenuBarItem: + if (!sz.isEmpty()) + sz += QSize(QWindowsStylePrivate::windowsItemHMargin * 4, QWindowsStylePrivate::windowsItemVMargin * 2); + break; +#endif + case CT_ToolButton: + if (qstyleoption_cast(opt)) + return sz += QSize(7, 6); + Q_FALLTHROUGH(); + + default: + sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget); + } + return sz; +} + +/*! + \reimp +*/ +QIcon QWindowsStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, + const QWidget *widget) const +{ + return QCommonStyle::standardIcon(standardIcon, option, widget); +} + + + +QT_END_NAMESPACE + +#include "moc_qwindowsstyle_p.cpp" + +#endif // style_windows