From 22c3fd050843d2a56fb927492501c3798d253257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 25 Oct 2019 19:20:06 +0200 Subject: [PATCH 01/13] macOS: Remove assert that primary display always matches CGMainDisplayID Fixes: QTBUG-78707 Change-Id: Ia517f543728c76dcf19558e9e68ed97db7cfaaa4 Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 392099d0837..0e55838e051 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -552,10 +552,10 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) */ QCocoaScreen *QCocoaScreen::primaryScreen() { - auto screen = static_cast(QGuiApplication::primaryScreen()->handle()); - Q_ASSERT_X(screen == get(CGMainDisplayID()), "QCocoaScreen", - "The application's primary screen should always be in sync with the main display"); - return screen; + // Note: The primary screen that Qt knows about may not match the current CGMainDisplayID() + // if macOS has not yet been able to inform us that the main display has changed, but we + // will update the primary screen accordingly once the reconfiguration callback comes in. + return static_cast(QGuiApplication::primaryScreen()->handle()); } QList QCocoaScreen::virtualSiblings() const From 7ac3bc9f830a8ea1ebdf4738e883bdc01522c31f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 25 Oct 2019 09:33:17 +0200 Subject: [PATCH 02/13] Network proxy (macOS) - fix a memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit there are several conditional statements where we return without releasing a dictionary (which is returned by a function having 'Copy' in its name, thus giving us the ownership == CFRelease is needed). QCFType fixes this issue. Fixes: QTBUG-79524 Change-Id: Id8a8616ad5b6ec21b5e8103bf52b1d9df9ca5c2f Reviewed-by: Mårten Nordheim --- src/network/kernel/qnetworkproxy_mac.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp index 92f91956b90..67fda24ea6e 100644 --- a/src/network/kernel/qnetworkproxy_mac.cpp +++ b/src/network/kernel/qnetworkproxy_mac.cpp @@ -210,16 +210,14 @@ QList macQueryInternal(const QNetworkProxyQuery &query) QList result; // obtain a dictionary to the proxy settings: - CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + const QCFType dict = SCDynamicStoreCopyProxies(NULL); if (!dict) { qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL"); return result; // failed } - if (isHostExcluded(dict, query.peerHostName())) { - CFRelease(dict); + if (isHostExcluded(dict, query.peerHostName())) return result; // no proxy for this host - } // is there a PAC enabled? If so, use it first. CFNumberRef pacEnabled; @@ -329,7 +327,6 @@ QList macQueryInternal(const QNetworkProxyQuery &query) result << https; } - CFRelease(dict); return result; } From a9b34f5726923babed448e2b4af42f117ae53ca4 Mon Sep 17 00:00:00 2001 From: Francisco Boni Date: Tue, 15 Oct 2019 17:56:26 -0300 Subject: [PATCH 03/13] QRandom: add support for RDSEED on INTEL_ICL & MSVC We set the macro for RDSEED because neither MSVC nor the Intel compiler on Windows defines the macro. The implication is that when qRandomCpu() calls qCpuHasFeature() in simd.cpp, qDetectCpuFeatures() correctly receives the expected CPU features enabled in the build from qCompilerCpuFeatures, namely CpuFeatureRDSEED (qsimd_x86_p.h) Change-Id: I5741d4f956a93f21c358af8a4ee393c1741b85ee Reviewed-by: Thiago Macieira --- mkspecs/win32-icc/qmake.conf | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mkspecs/win32-icc/qmake.conf b/mkspecs/win32-icc/qmake.conf index 3cb0d58824f..af26c5bc157 100644 --- a/mkspecs/win32-icc/qmake.conf +++ b/mkspecs/win32-icc/qmake.conf @@ -22,7 +22,7 @@ QMAKE_CFLAGS_WARN_OFF = -W0 QMAKE_CFLAGS_DEBUG = $$QMAKE_CFLAGS_OPTIMIZE_DEBUG -Zi -MDd QMAKE_CFLAGS_UTF8_SOURCE = -Qoption,cpp,--unicode_source_kind,UTF-8 QMAKE_CFLAGS_LTCG = -Qipo -QMAKE_CFLAGS_DISABLE_LTCG = -Qno-ipo +QMAKE_CFLAGS_DISABLE_LTCG = -Qipo- QMAKE_CFLAGS_SSE2 = -QxSSE2 QMAKE_CFLAGS_SSE3 = -QxSSE3 @@ -39,6 +39,11 @@ QMAKE_CFLAGS_AVX512DQ += -QxCORE-AVX512 QMAKE_CFLAGS_AVX512BW += -QxCORE-AVX512 QMAKE_CFLAGS_AVX512VL += -QxCORE-AVX512 QMAKE_CFLAGS_F16C = $$QMAKE_CFLAGS_AVX2 +QMAKE_CFLAGS_RDRND = $$QMAKE_CFLAGS_AVX2 +# ICC on Windows lacks the mrdseed compiler option that sets the RDSEED macro +QMAKE_CFLAGS_RDSEED = -D__RDSEED__=1 +QMAKE_CFLAGS_ARCH_HASWELL = $$QMAKE_CFLAGS_AVX2 + QMAKE_CFLAGS_AESNI = -QxSSE2 QMAKE_CFLAGS_SHANI = -QxSSE4.2 From 360a2e79c8abd90fb274b6dcbede222e1e5e37db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:23:10 +0100 Subject: [PATCH 04/13] macOS: Don't leak CFUUIDRefs when resolving NSScreen for platform screen Change-Id: I5609071346ef44dc9f16359db451ea9b29dd2b0d Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 0e55838e051..886198d9d5b 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -597,7 +597,7 @@ NSScreen *QCocoaScreen::nativeScreen() const QCFType uuid = CGDisplayCreateUUIDFromDisplayID(m_displayId); for (NSScreen *screen in [NSScreen screens]) { - if (CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId) == uuid) + if (QCFType(CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId)) == uuid) return screen; } From 5982451ac60057948f19af9adcf2b9ebb611cdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:28:03 +0100 Subject: [PATCH 05/13] macOS: Class initialize QCocoaScreen members Change-Id: I163858400da28668b8a85241e9e6b1d989227a3e Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.h | 8 ++++---- src/plugins/platforms/cocoa/qcocoascreen.mm | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h index 60243b79bee..7ec9a2b5af7 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.h +++ b/src/plugins/platforms/cocoa/qcocoascreen.h @@ -99,18 +99,18 @@ private: static void add(CGDirectDisplayID displayId); void remove(); - CGDirectDisplayID m_displayId = 0; + CGDirectDisplayID m_displayId = kCGNullDirectDisplay; QRect m_geometry; QRect m_availableGeometry; QDpi m_logicalDpi; - qreal m_refreshRate; - int m_depth; + qreal m_refreshRate = 0; + int m_depth = 0; QString m_name; QImage::Format m_format; QSizeF m_physicalSize; QCocoaCursor *m_cursor; - qreal m_devicePixelRatio; + qreal m_devicePixelRatio = 0; CVDisplayLinkRef m_displayLink = nullptr; dispatch_source_t m_displayLinkSource = nullptr; diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 886198d9d5b..7a54f616d0b 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -127,7 +127,7 @@ void QCocoaScreen::cleanupScreens() void QCocoaScreen::remove() { - m_displayId = 0; // Prevent stale references during removal + m_displayId = kCGNullDirectDisplay; // Prevent stale references during removal // This may result in the application responding to QGuiApplication::screenRemoved // by moving the window to another screen, either by setGeometry, or by setScreen. From 3ee634d520b28143d4bbbf46c011449bade917de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:29:03 +0100 Subject: [PATCH 06/13] macOS: Extend QCocoaScreen logging Change-Id: I91f89ff336b3f48aea91e50860264bd8359805cb Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 34 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 7a54f616d0b..aaaf9d0dcca 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -54,6 +54,23 @@ QT_BEGIN_NAMESPACE +namespace CoreGraphics { + Q_NAMESPACE + enum DisplayChange { + Moved = kCGDisplayMovedFlag, + SetMain = kCGDisplaySetMainFlag, + SetMode = kCGDisplaySetModeFlag, + Added = kCGDisplayAddFlag, + Removed = kCGDisplayRemoveFlag, + Enabled = kCGDisplayEnabledFlag, + Disabled = kCGDisplayDisabledFlag, + Mirrored = kCGDisplayMirrorFlag, + UnMirrored = kCGDisplayUnMirrorFlag, + DesktopShapeChanged = kCGDisplayDesktopShapeChangedFlag + }; + Q_ENUM_NS(DisplayChange) +} + void QCocoaScreen::initializeScreens() { uint32_t displayCount = 0; @@ -73,6 +90,10 @@ void QCocoaScreen::initializeScreens() Q_UNUSED(userInfo); + qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display reconfiguration" + << " (" << QFlags(flags) << ")" + << " for displayId=" << displayId; + QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId); if ((flags & kCGDisplayAddFlag) || !cocoaScreen) { @@ -99,16 +120,19 @@ void QCocoaScreen::initializeScreens() return; // Already reconfigured cocoaScreen->updateProperties(); - qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen; + qCInfo(lcQpaScreen).nospace() << "Reconfigured " << + (primaryScreen() == cocoaScreen ? "primary " : "") + << cocoaScreen; } }, nullptr); } void QCocoaScreen::add(CGDirectDisplayID displayId) { + const bool isPrimary = CGDisplayIsMain(displayId); QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId); - qCInfo(lcQpaScreen) << "Adding" << cocoaScreen; - QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId)); + qCInfo(lcQpaScreen).nospace() << "Adding " << (isPrimary ? "new primary " : "") << cocoaScreen; + QWindowSystemInterface::handleScreenAdded(cocoaScreen, isPrimary); } QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId) @@ -140,6 +164,7 @@ void QCocoaScreen::remove() // QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have // already changed its screen, but that's only true if comparing the Qt screens, // not when comparing the NSScreens. + qCInfo(lcQpaScreen).nospace() << "Removing " << (primaryScreen() == this ? "current primary " : "") << this; QWindowSystemInterface::handleScreenRemoved(this); } @@ -639,6 +664,7 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) debug << ", geometry=" << screen->geometry(); debug << ", dpr=" << screen->devicePixelRatio(); debug << ", name=" << screen->name(); + debug << ", displayId=" << screen->m_displayId; debug << ", native=" << screen->nativeScreen(); } debug << ')'; @@ -646,6 +672,8 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) } #endif // !QT_NO_DEBUG_STREAM +#include "qcocoascreen.moc" + QT_END_NAMESPACE @implementation NSScreen (QtExtras) From dcbe25bbbb603bc335d7cf0982a80293289b0d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 27 Oct 2019 18:57:56 +0100 Subject: [PATCH 07/13] macOS: Only skip screen reconfigure if primary screen changed Change-Id: Ia5d208ace5086e8e92f95f859383773894a18768 Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoascreen.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index aaaf9d0dcca..40273bac842 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -114,11 +114,10 @@ void QCocoaScreen::initializeScreens() mainDisplay->updateProperties(); qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay; QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay); + if (cocoaScreen == mainDisplay) + return; // Already reconfigured } - if (cocoaScreen == mainDisplay) - return; // Already reconfigured - cocoaScreen->updateProperties(); qCInfo(lcQpaScreen).nospace() << "Reconfigured " << (primaryScreen() == cocoaScreen ? "primary " : "") From 5d4b5dab7fec0f2a511145209daea9a85e741cc7 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sat, 26 Oct 2019 21:07:09 +0200 Subject: [PATCH 08/13] Widget examples: replace QItemDelegate with QStyledItemDelegate Replace QItemDelegate with QStyledItemDelegate in the examples since QItemDelegate is deprecated. Also fix up some unused documentation snippet references. Change-Id: I42b8780ad0c317b9a253cc722d0b471695ed253f Reviewed-by: Friedemann Kleint --- .../spreadsheet/spreadsheetdelegate.cpp | 2 +- .../spreadsheet/spreadsheetdelegate.h | 4 +-- .../widgets/widgets/icons/imagedelegate.cpp | 5 ++-- .../widgets/widgets/icons/imagedelegate.h | 6 ++-- examples/widgets/widgets/icons/mainwindow.cpp | 28 ++++++------------- 5 files changed, 17 insertions(+), 28 deletions(-) diff --git a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp index eadd5fadb82..ad80a238aa4 100644 --- a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp +++ b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.cpp @@ -53,7 +53,7 @@ #include SpreadSheetDelegate::SpreadSheetDelegate(QObject *parent) - : QItemDelegate(parent) + : QStyledItemDelegate(parent) {} QWidget *SpreadSheetDelegate::createEditor(QWidget *parent, diff --git a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h index c89459cadf8..36c70d2391e 100644 --- a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h +++ b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.h @@ -51,9 +51,9 @@ #ifndef SPREADSHEETDELEGATE_H #define SPREADSHEETDELEGATE_H -#include +#include -class SpreadSheetDelegate : public QItemDelegate +class SpreadSheetDelegate : public QStyledItemDelegate { Q_OBJECT diff --git a/examples/widgets/widgets/icons/imagedelegate.cpp b/examples/widgets/widgets/icons/imagedelegate.cpp index 39c2e431349..4fd251aa1bf 100644 --- a/examples/widgets/widgets/icons/imagedelegate.cpp +++ b/examples/widgets/widgets/icons/imagedelegate.cpp @@ -55,9 +55,8 @@ //! [0] ImageDelegate::ImageDelegate(QObject *parent) - : QItemDelegate(parent) -{ -} + : QStyledItemDelegate(parent) +{} //! [0] //! [1] diff --git a/examples/widgets/widgets/icons/imagedelegate.h b/examples/widgets/widgets/icons/imagedelegate.h index 3b76b78339e..9d65304e2ca 100644 --- a/examples/widgets/widgets/icons/imagedelegate.h +++ b/examples/widgets/widgets/icons/imagedelegate.h @@ -51,10 +51,10 @@ #ifndef IMAGEDELEGATE_H #define IMAGEDELEGATE_H -#include +#include //! [0] -class ImageDelegate : public QItemDelegate +class ImageDelegate : public QStyledItemDelegate { Q_OBJECT @@ -72,7 +72,7 @@ public: //! [1] //! [2] private slots: void emitCommitData(); -}; //! [2] +}; #endif diff --git a/examples/widgets/widgets/icons/mainwindow.cpp b/examples/widgets/widgets/icons/mainwindow.cpp index f342c18c4ce..8e612600414 100644 --- a/examples/widgets/widgets/icons/mainwindow.cpp +++ b/examples/widgets/widgets/icons/mainwindow.cpp @@ -216,16 +216,13 @@ void MainWindow::changeIcon() QImage image(fileName); if (!image.isNull()) icon.addPixmap(QPixmap::fromImage(image), mode, state); -//! [8] //! [9] +//! [8] } -//! [9] //! [10] } -//! [10] - //! [11] previewArea->setIcon(icon); -} //! [11] +} void MainWindow::addSampleImages() { @@ -280,17 +277,15 @@ void MainWindow::loadImages(const QStringList &fileNames) .arg(QDir::toNativeSeparators(fileInfo.absolutePath()), fileInfo.fileName()) .arg(fileInfo2x.exists() ? fileInfo2x.fileName() : tr("")) .arg(image.width()).arg(image.height()); -//! [13] //! [14] QTableWidgetItem *fileItem = new QTableWidgetItem(imageName); fileItem->setData(Qt::UserRole, fileName); fileItem->setIcon(QPixmap::fromImage(image)); fileItem->setFlags((fileItem->flags() | Qt::ItemIsUserCheckable) & ~Qt::ItemIsEditable); fileItem->setToolTip(toolTip); -//! [14] +//! [13] //! [15] QIcon::Mode mode = QIcon::Normal; -//! [15] //! [16] QIcon::State state = QIcon::Off; if (guessModeStateAct->isChecked()) { if (imageName.contains(QLatin1String("_act"), Qt::CaseInsensitive)) @@ -302,13 +297,11 @@ void MainWindow::loadImages(const QStringList &fileNames) if (imageName.contains(QLatin1String("_on"), Qt::CaseInsensitive)) state = QIcon::On; -//! [16] //! [17] +//! [15] } -//! [17] //! [18] imagesTable->setItem(row, 0, fileItem); -//! [18] //! [19] QTableWidgetItem *modeItem = new QTableWidgetItem(IconPreviewArea::iconModeNames().at(IconPreviewArea::iconModes().indexOf(mode))); modeItem->setToolTip(toolTip); @@ -321,9 +314,9 @@ void MainWindow::loadImages(const QStringList &fileNames) imagesTable->openPersistentEditor(stateItem); fileItem->setCheckState(Qt::Checked); +//! [18] } } -//! [19] void MainWindow::useHighDpiPixmapsChanged(int checkState) { @@ -350,9 +343,7 @@ QWidget *MainWindow::createImagesGroupBox() //! [21] //! [22] - QStringList labels; -//! [22] //! [23] - labels << tr("Image") << tr("Mode") << tr("State"); + const QStringList labels({tr("Image"), tr("Mode"), tr("State")}); imagesTable->horizontalHeader()->setDefaultSectionSize(90); imagesTable->setColumnCount(3); @@ -361,18 +352,17 @@ QWidget *MainWindow::createImagesGroupBox() imagesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed); imagesTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed); imagesTable->verticalHeader()->hide(); -//! [23] +//! [22] //! [24] connect(imagesTable, &QTableWidget::itemChanged, -//! [24] //! [25] this, &MainWindow::changeIcon); QVBoxLayout *layout = new QVBoxLayout(imagesGroupBox); layout->addWidget(imagesTable); return imagesGroupBox; -} //! [25] +} //! [26] QWidget *MainWindow::createIconSizeGroupBox() @@ -428,8 +418,8 @@ QWidget *MainWindow::createIconSizeGroupBox() layout->addLayout(otherSizeLayout, 3, 0, 1, 2); layout->setRowStretch(4, 1); return iconSizeGroupBox; -} //! [27] +} void MainWindow::screenChanged() { From c33d8bfc996cb52d3b9d5b821e51860347c74fa2 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Thu, 17 Oct 2019 17:38:53 +0300 Subject: [PATCH 09/13] Add a timeout for transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a transfer timeout is set for QNetworkRequest, downloads and uploads are aborted if the timeout expires and bytes haven't been transmitted in either direction. Task-number: QTBUG-3443 Change-Id: I702d223d673f0c6612343dc9d053815acfcb61b8 Reviewed-by: Mårten Nordheim --- src/network/access/qnetworkreplyhttpimpl.cpp | 36 ++++++++++- src/network/access/qnetworkreplyhttpimpl_p.h | 7 +++ src/network/access/qnetworkrequest.cpp | 50 ++++++++++++++++ src/network/access/qnetworkrequest.h | 6 ++ .../qnetworkreply/tst_qnetworkreply.cpp | 60 ++++++++++++++++++- 5 files changed, 154 insertions(+), 5 deletions(-) diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 44c1d3e4221..a13c2b144c3 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -461,6 +461,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate() , preMigrationDownloaded(-1) , bytesDownloaded(0) , bytesBuffered(0) + , transferTimeout(nullptr) , downloadBufferReadPosition(0) , downloadBufferCurrentSize(0) , downloadZerocopyBuffer(nullptr) @@ -1067,6 +1068,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d) if (!isHttpRedirectResponse()) { buffer.append(d); bytesDownloaded += d.size(); + setupTransferTimeout(); } bytesBuffered += d.size(); @@ -1401,6 +1403,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceive return; bytesDownloaded = bytesReceived; + setupTransferTimeout(); downloadBufferCurrentSize = bytesReceived; @@ -1857,7 +1860,6 @@ bool QNetworkReplyHttpImplPrivate::startWaitForSession(QSharedPointersetFinished(true); @@ -2033,6 +2036,31 @@ void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingData() } } +void QNetworkReplyHttpImplPrivate::_q_transferTimedOut() +{ + Q_Q(QNetworkReplyHttpImpl); + q->abort(); +} + +void QNetworkReplyHttpImplPrivate::setupTransferTimeout() +{ + Q_Q(QNetworkReplyHttpImpl); + if (!transferTimeout) { + transferTimeout = new QTimer(q); + QObject::connect(transferTimeout, SIGNAL(timeout()), + q, SLOT(_q_transferTimedOut()), + Qt::QueuedConnection); + } + transferTimeout->stop(); + if (request.transferTimeout()) { + transferTimeout->setSingleShot(true); + transferTimeout->setInterval(request.transferTimeout()); + QMetaObject::invokeMethod(transferTimeout, "start", + Qt::QueuedConnection); + + } +} + #ifndef QT_NO_BEARERMANAGEMENT void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected() { @@ -2115,6 +2143,8 @@ void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qin if (isFinished) return; + setupTransferTimeout(); + if (!emitAllUploadProgressSignals) { //choke signal emissions, except the first and last signals which are unconditional if (uploadProgressSignalChoke.isValid()) { @@ -2126,7 +2156,6 @@ void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qin uploadProgressSignalChoke.start(); } } - emit q->uploadProgress(bytesSent, bytesTotal); } @@ -2159,7 +2188,8 @@ void QNetworkReplyHttpImplPrivate::_q_finished() void QNetworkReplyHttpImplPrivate::finished() { Q_Q(QNetworkReplyHttpImpl); - + if (transferTimeout) + transferTimeout->stop(); if (state == Finished || state == Aborted || state == WaitingForSession) return; diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h index ef69ce06534..dec0c4c5899 100644 --- a/src/network/access/qnetworkreplyhttpimpl_p.h +++ b/src/network/access/qnetworkreplyhttpimpl_p.h @@ -59,6 +59,7 @@ #include "QtCore/qdatetime.h" #include "QtCore/qsharedpointer.h" #include "QtCore/qscopedpointer.h" +#include "QtCore/qtimer.h" #include "qatomic.h" #include @@ -100,6 +101,7 @@ public: Q_PRIVATE_SLOT(d_func(), void _q_cacheLoadReadyRead()) Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData()) Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished()) + Q_PRIVATE_SLOT(d_func(), void _q_transferTimedOut()) #ifndef QT_NO_BEARERMANAGEMENT Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected()) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed()) @@ -181,6 +183,9 @@ public: void _q_cacheSaveDeviceAboutToClose(); + void _q_transferTimedOut(); + void setupTransferTimeout(); + #ifndef QT_NO_BEARERMANAGEMENT void _q_networkSessionConnected(); void _q_networkSessionFailed(); @@ -250,6 +255,8 @@ public: qint64 bytesDownloaded; qint64 bytesBuffered; + QTimer *transferTimeout; + // Only used when the "zero copy" style is used. // Please note that the whole "zero copy" download buffer API is private right now. Do not use it. qint64 downloadBufferReadPosition; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 7899bce32b2..e03d844af91 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -425,6 +425,18 @@ QT_BEGIN_NAMESPACE based on some app-specific configuration. */ +/*! + \enum QNetworkRequest::TransferTimeoutConstant + \since 5.15 + + A constant that can be used for enabling transfer + timeouts with a preset value. + + \value TransferTimeoutPreset The transfer timeout in milliseconds. + Used if setTimeout() is called + without an argument. + */ + class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate { public: @@ -435,6 +447,7 @@ public: , sslConfiguration(0) #endif , maxRedirectsAllowed(maxRedirectCount) + , transferTimeout(0) { qRegisterMetaType(); } ~QNetworkRequestPrivate() { @@ -459,6 +472,7 @@ public: #if QT_CONFIG(http) h2Configuration = other.h2Configuration; #endif + transferTimeout = other.transferTimeout; } inline bool operator==(const QNetworkRequestPrivate &other) const @@ -472,6 +486,7 @@ public: #if QT_CONFIG(http) && h2Configuration == other.h2Configuration #endif + && transferTimeout == other.transferTimeout ; // don't compare cookedHeaders } @@ -486,6 +501,7 @@ public: #if QT_CONFIG(http) QHttp2Configuration h2Configuration; #endif + int transferTimeout; }; /*! @@ -909,6 +925,40 @@ void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configura { d->h2Configuration = configuration; } + +/*! + \since 5.15 + + Returns the timeout used for transfers, in milliseconds. + + This timeout is zero if setTransferTimeout hasn't been + called, which means that the timeout is not used. + + \sa setTransferTimeout +*/ +int QNetworkRequest::transferTimeout() +{ + return d->transferTimeout; +} + +/*! + \since 5.15 + + Sets \a timeout as the transfer timeout in milliseconds. + + Transfers are aborted if no bytes are transferred before + the timeout expires. Zero means no timer is set. If no + argument is provided, the timeout is + QNetworkRequest::TransferTimeoutPreset. If this function + is not called, the timeout is disabled and has the + value zero. + + \sa transferTimeout +*/ +void QNetworkRequest::setTransferTimeout(int timeout) +{ + d->transferTimeout = timeout; +} #endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) static QByteArray headerName(QNetworkRequest::KnownHeaders header) diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 72a6555d91a..5d9969bd9b6 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -134,6 +134,9 @@ public: UserVerifiedRedirectPolicy }; + enum TransferTimeoutConstant { + TransferTimeoutPreset = 30000 + }; QNetworkRequest(); explicit QNetworkRequest(const QUrl &url); @@ -185,6 +188,9 @@ public: #if QT_CONFIG(http) || defined(Q_CLANG_QDOC) QHttp2Configuration http2Configuration() const; void setHttp2Configuration(const QHttp2Configuration &configuration); + + int transferTimeout(); + void setTransferTimeout(int timeout = TransferTimeoutPreset); #endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) private: QSharedDataPointer d; diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 418e1caf687..3bc6717d58d 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -511,6 +511,8 @@ private Q_SLOTS: void autoDeleteReplies_data(); void autoDeleteReplies(); + void getWithTimeout(); + void postWithTimeout(); // NOTE: This test must be last! void parentingRepliesToTheApp(); private: @@ -589,6 +591,7 @@ public: bool multiple; int totalConnections; + bool stopTransfer = false; bool hasContent = false; int contentRead = 0; int contentLength = 0; @@ -655,7 +658,7 @@ protected: // we need to emulate the bytesWrittenSlot call if the data is empty. if (dataToTransmit.size() == 0) { emit client->bytesWritten(0); - } else { + } else if (!stopTransfer) { client->write(dataToTransmit); // FIXME: For SSL connections, if we don't flush the socket, the // client never receives the data and since we're doing a disconnect @@ -711,7 +714,8 @@ public slots: Q_ASSERT(currentClient); if (currentClient != client) client = currentClient; - + if (stopTransfer) + return; receivedData += client->readAll(); const int doubleEndlPos = receivedData.indexOf("\r\n\r\n"); @@ -9342,6 +9346,58 @@ void tst_QNetworkReply::autoDeleteReplies() } } +void tst_QNetworkReply::getWithTimeout() +{ + MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply), int(Success)); + + QCOMPARE(spy.count(), 0); + QVERIFY(reply->error() == QNetworkReply::NoError); + + request.setTransferTimeout(1000); + server.stopTransfer = true; + + QNetworkReplyPtr reply2(manager.get(request)); + QSignalSpy spy2(reply2.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply2), int(Failure)); + + QCOMPARE(spy2.count(), 1); + QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); +} + +void tst_QNetworkReply::postWithTimeout() +{ + MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + request.setRawHeader("Content-Type", "application/octet-stream"); + QByteArray postData("Just some nonsense"); + QNetworkReplyPtr reply(manager.post(request, postData)); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply), int(Success)); + + QCOMPARE(spy.count(), 0); + QVERIFY(reply->error() == QNetworkReply::NoError); + + request.setTransferTimeout(1000); + server.stopTransfer = true; + + QNetworkReplyPtr reply2(manager.post(request, postData)); + QSignalSpy spy2(reply2.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply2), int(Failure)); + + QCOMPARE(spy2.count(), 1); + QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); +} + // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp() { From 538d5bdf662f4198a2d6c38a6002abb81433fb29 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Fri, 18 Oct 2019 13:26:02 +0200 Subject: [PATCH 10/13] Add a getter for QObjectPrivate::threadData Strictly a temporary measure to deal with cross-module merges. Change-Id: I344bb3f20f68f04367041834e608669122ff70b1 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qobject_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index feafcaf3237..45fc27917d3 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -374,6 +374,7 @@ public: } public: ExtraData *extraData; // extra data set by the user + QThreadData *getThreadData() const { return threadData; } QThreadData *threadData; // id of the thread that owns the object using ConnectionDataPointer = QExplicitlySharedDataPointer; From c87e2c37de439996895fc2e2e4c79376b6ec1f09 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sun, 8 Sep 2019 20:21:41 +0200 Subject: [PATCH 11/13] tst_QDataWidgetMapper/QFileIconProvider/ItemEditorFactory: cleanup Cleanup QDataWidgetMapper/QFileIconProvider/ItemEditorFactory autotests: - use range-based for loops - use nullptr - use member initialization - use new signal/slot syntax - use static invocations - use override Change-Id: I1a36ea2da7de1cfa5d5d4e305ef508fda3a6c460 Reviewed-by: Friedemann Kleint --- .../tst_qdatawidgetmapper.cpp | 28 +++++++++--------- .../tst_qfileiconprovider.cpp | 29 ++++++------------- .../tst_qitemeditorfactory.cpp | 8 +++-- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp index 5717afab51c..856672b957a 100644 --- a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp +++ b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp @@ -25,15 +25,16 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include -#include -#include + #include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include class tst_QDataWidgetMapper: public QObject { @@ -56,7 +57,7 @@ private slots: Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint) -static QStandardItemModel *testModel(QObject *parent = 0) +static QStandardItemModel *testModel(QObject *parent) { QStandardItemModel *model = new QStandardItemModel(10, 10, parent); @@ -84,7 +85,7 @@ void tst_QDataWidgetMapper::setModel() { // let the model go out of scope firstma QStandardItemModel model; mapper.setModel(&model); - QCOMPARE(mapper.model(), static_cast(&model)); + QCOMPARE(mapper.model(), &model); } QCOMPARE(mapper.model(), nullptr); @@ -273,7 +274,7 @@ void tst_QDataWidgetMapper::currentIndexChanged() QAbstractItemModel *model = testModel(&mapper); mapper.setModel(model); - QSignalSpy spy(&mapper, SIGNAL(currentIndexChanged(int))); + QSignalSpy spy(&mapper, &QDataWidgetMapper::currentIndexChanged); mapper.toFirst(); QCOMPARE(spy.count(), 1); @@ -405,13 +406,13 @@ void tst_QDataWidgetMapper::mappedWidgetAt() mapper.addMapping(&lineEdit1, 1); mapper.addMapping(&lineEdit2, 2); - QCOMPARE(mapper.mappedWidgetAt(1), static_cast(&lineEdit1)); - QCOMPARE(mapper.mappedWidgetAt(2), static_cast(&lineEdit2)); + QCOMPARE(mapper.mappedWidgetAt(1), &lineEdit1); + QCOMPARE(mapper.mappedWidgetAt(2), &lineEdit2); mapper.addMapping(&lineEdit2, 4242); QCOMPARE(mapper.mappedWidgetAt(2), nullptr); - QCOMPARE(mapper.mappedWidgetAt(4242), static_cast(&lineEdit2)); + QCOMPARE(mapper.mappedWidgetAt(4242), &lineEdit2); } void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() @@ -423,7 +424,8 @@ void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() QAbstractItemModel *model = testModel(&mapper); mapper.setModel(model); - QSignalSpy closeEditorSpy(mapper.itemDelegate(), SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); + QSignalSpy closeEditorSpy(mapper.itemDelegate(), + &QAbstractItemDelegate::closeEditor); QVERIFY(closeEditorSpy.isValid()); QWidget container; diff --git a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp index 6db1f783120..4824973576d 100644 --- a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp +++ b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp @@ -27,16 +27,15 @@ ****************************************************************************/ -#include -#include -#include +#include +#include +#include class tst_QFileIconProvider : public QObject { Q_OBJECT private slots: - void qfileiconprovider_data(); void qfileiconprovider(); void iconType_data(); @@ -51,21 +50,10 @@ private slots: void taskQTBUG_46755_QFileIconEngine_crash(); }; -// Subclass that exposes the protected functions. -class SubQFileIconProvider : public QFileIconProvider -{ -public: - -}; - -void tst_QFileIconProvider::qfileiconprovider_data() -{ -} - void tst_QFileIconProvider::qfileiconprovider() { // don't crash - SubQFileIconProvider provider; + QFileIconProvider provider; } Q_DECLARE_METATYPE(QFileIconProvider::IconType) @@ -86,7 +74,7 @@ void tst_QFileIconProvider::iconType_data() void tst_QFileIconProvider::iconType() { QFETCH(QFileIconProvider::IconType, type); - SubQFileIconProvider provider; + QFileIconProvider provider; QVERIFY(!provider.icon(type).isNull()); } @@ -109,7 +97,7 @@ void tst_QFileIconProvider::iconInfo() if (setPath) QVERIFY(info.exists()); - SubQFileIconProvider provider; + QFileIconProvider provider; // we should always get an icon QVERIFY(!provider.icon(info).isNull()); } @@ -131,7 +119,7 @@ void tst_QFileIconProvider::type_data() void tst_QFileIconProvider::type() { QFETCH(QFileInfo, info); - SubQFileIconProvider provider; + QFileIconProvider provider; QVERIFY(!provider.type(info).isEmpty()); } @@ -144,7 +132,8 @@ static QIcon getIcon() void tst_QFileIconProvider::taskQTBUG_46755_QFileIconEngine_crash() { const QIcon &icon = getIcon(); - foreach (const QSize &size, icon.availableSizes()) + const auto sizes = icon.availableSizes(); + for (const QSize &size : sizes) icon.pixmap(size); // No crash, all good. diff --git a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp index ae587279b2d..ed4c543d0a3 100644 --- a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp +++ b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp @@ -25,9 +25,11 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include -#include -#include + + +#include +#include +#include class tst_QItemEditorFactory: public QObject { From 58a67e4e0a88e52d71eef5d08df1465f7ac610ae Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 28 Oct 2019 16:37:57 +0100 Subject: [PATCH 12/13] rhi: Move to CBOR in QShader and expand the autotest Binary JSON is said to become deprecated. Therefore, add support for CBOR. Binary JSON is still supported for deserialization, so all existing .qsb files will continue to work, as long as the binaryjson feature is enabled in the Qt build. Also makes QShaderDescription comparable. This is important for tests in particular. A nice side effect of using CBOR is that .qsb files become smaller. For a typical Qt Quick material shader this can mean a reduction of 300 bytes or more. Task-number: QTBUG-79576 Change-Id: I5547c0266e3e8128c9653e954e47487352267f71 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qshader.cpp | 23 ++- src/gui/rhi/qshader_p_p.h | 6 + src/gui/rhi/qshaderdescription.cpp | 152 ++++++++++++++- src/gui/rhi/qshaderdescription_p.h | 40 ++++ tests/auto/gui/rhi/qshader/data/README | 15 ++ .../{color.vert.qsb => color_all_v1.vert.qsb} | Bin ...imple.vert.qsb => color_spirv_v1.vert.qsb} | Bin tests/auto/gui/rhi/qshader/data/texture.frag | 16 ++ .../rhi/qshader/data/texture_all_v2.frag.qsb | Bin 0 -> 1691 bytes .../rhi/qshader/data/texture_all_v3.frag.qsb | Bin 0 -> 1432 bytes tests/auto/gui/rhi/qshader/tst_qshader.cpp | 174 +++++++++++++++++- 11 files changed, 410 insertions(+), 16 deletions(-) create mode 100644 tests/auto/gui/rhi/qshader/data/README rename tests/auto/gui/rhi/qshader/data/{color.vert.qsb => color_all_v1.vert.qsb} (100%) rename tests/auto/gui/rhi/qshader/data/{color_simple.vert.qsb => color_spirv_v1.vert.qsb} (100%) create mode 100644 tests/auto/gui/rhi/qshader/data/texture.frag create mode 100644 tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb create mode 100644 tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp index c22b029dc8f..14d780b4e42 100644 --- a/src/gui/rhi/qshader.cpp +++ b/src/gui/rhi/qshader.cpp @@ -214,9 +214,6 @@ QT_BEGIN_NAMESPACE QShader, it indicates no shader code was found for the requested key. */ -static const int QSB_VERSION = 2; -static const int QSB_VERSION_WITHOUT_BINDINGS = 1; - /*! Constructs a new, empty (and thus invalid) QShader instance. */ @@ -368,9 +365,9 @@ QByteArray QShader::serialized() const if (!buf.open(QIODevice::WriteOnly)) return QByteArray(); - ds << QSB_VERSION; + ds << QShaderPrivate::QSB_VERSION; ds << d->stage; - ds << d->desc.toBinaryJson(); + ds << d->desc.toCbor(); ds << d->shaders.count(); for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) { const QShaderKey &k(it.key()); @@ -429,9 +426,12 @@ QShader QShader::fromSerialized(const QByteArray &data) Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached int intVal; ds >> intVal; - const int qsbVersion = intVal; - if (qsbVersion != QSB_VERSION && qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) { - qWarning("Attempted to deserialize QShader with unknown version %d.", qsbVersion); + d->qsbVersion = intVal; + if (d->qsbVersion != QShaderPrivate::QSB_VERSION + && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON + && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) + { + qWarning("Attempted to deserialize QShader with unknown version %d.", d->qsbVersion); return QShader(); } @@ -439,7 +439,10 @@ QShader QShader::fromSerialized(const QByteArray &data) d->stage = Stage(intVal); QByteArray descBin; ds >> descBin; - d->desc = QShaderDescription::fromBinaryJson(descBin); + if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) + d->desc = QShaderDescription::fromCbor(descBin); + else + d->desc = QShaderDescription::fromBinaryJson(descBin); int count; ds >> count; for (int i = 0; i < count; ++i) { @@ -454,7 +457,7 @@ QShader QShader::fromSerialized(const QByteArray &data) d->shaders[k] = shader; } - if (qsbVersion != QSB_VERSION_WITHOUT_BINDINGS) { + if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) { ds >> count; for (int i = 0; i < count; ++i) { QShaderKey k; diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h index 4535e01491a..8c89f2b45f0 100644 --- a/src/gui/rhi/qshader_p_p.h +++ b/src/gui/rhi/qshader_p_p.h @@ -57,6 +57,10 @@ QT_BEGIN_NAMESPACE struct Q_GUI_EXPORT QShaderPrivate { + static const int QSB_VERSION = 3; + static const int QSB_VERSION_WITH_BINARY_JSON = 2; + static const int QSB_VERSION_WITHOUT_BINDINGS = 1; + QShaderPrivate() : ref(1) { @@ -64,6 +68,7 @@ struct Q_GUI_EXPORT QShaderPrivate QShaderPrivate(const QShaderPrivate *other) : ref(1), + qsbVersion(other->qsbVersion), stage(other->stage), desc(other->desc), shaders(other->shaders), @@ -75,6 +80,7 @@ struct Q_GUI_EXPORT QShaderPrivate static const QShaderPrivate *get(const QShader *s) { return s->d; } QAtomicInt ref; + int qsbVersion = QSB_VERSION; QShader::Stage stage = QShader::VertexStage; QShaderDescription desc; QHash shaders; diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp index 179d5f3a072..d0f73f6aa77 100644 --- a/src/gui/rhi/qshaderdescription.cpp +++ b/src/gui/rhi/qshaderdescription.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include QT_BEGIN_NAMESPACE @@ -335,11 +338,27 @@ bool QShaderDescription::isValid() const /*! \return a serialized binary version of the data. - \sa toJson() + \sa toJson(), toCbor() */ QByteArray QShaderDescription::toBinaryJson() const { +#if QT_CONFIG(binaryjson) return d->makeDoc().toBinaryData(); +#else + qWarning("Cannot generate binary JSON from QShaderDescription due to disabled binaryjson feature"); + return QByteArray(); +#endif +} + +/*! + \return a serialized binary version of the data in CBOR (Concise Binary + Object Representation) format. + + \sa QCborValue, toBinaryJson(), toJson() + */ +QByteArray QShaderDescription::toCbor() const +{ + return QCborValue::fromJsonValue(d->makeDoc().object()).toCbor(); } /*! @@ -347,7 +366,7 @@ QByteArray QShaderDescription::toBinaryJson() const \note There is no deserialization method provided for JSON text. - \sa toBinaryJson() + \sa toBinaryJson(), toCbor() */ QByteArray QShaderDescription::toJson() const { @@ -357,11 +376,38 @@ QByteArray QShaderDescription::toJson() const /*! Deserializes the given binary JSON \a data and returns a new QShaderDescription. + + \sa fromCbor() */ QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data) { QShaderDescription desc; +#if QT_CONFIG(binaryjson) QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data)); +#else + Q_UNUSED(data); + qWarning("Cannot load QShaderDescription from binary JSON due to disabled binaryjson feature"); +#endif + return desc; +} + +/*! + Deserializes the given CBOR \a data and returns a new QShaderDescription. + + \sa fromBinaryJson() + */ +QShaderDescription QShaderDescription::fromCbor(const QByteArray &data) +{ + QShaderDescription desc; + const QCborValue cbor = QCborValue::fromCbor(data); + if (cbor.isMap()) { + const QJsonDocument doc(cbor.toMap().toJsonObject()); + QShaderDescriptionPrivate::get(&desc)->loadDoc(doc); + } + if (cbor.isArray()) { + const QJsonDocument doc(cbor.toArray().toJsonArray()); + QShaderDescriptionPrivate::get(&desc)->loadDoc(doc); + } return desc; } @@ -1119,4 +1165,106 @@ void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc) } } +/*! + Returns \c true if the two QShaderDescription objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription + */ +bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW +{ + if (lhs.d == rhs.d) + return true; + + return lhs.d->inVars == rhs.d->inVars + && lhs.d->outVars == rhs.d->outVars + && lhs.d->uniformBlocks == rhs.d->uniformBlocks + && lhs.d->pushConstantBlocks == rhs.d->pushConstantBlocks + && lhs.d->storageBlocks == rhs.d->storageBlocks + && lhs.d->combinedImageSamplers == rhs.d->combinedImageSamplers + && lhs.d->storageImages == rhs.d->storageImages + && lhs.d->localSize == rhs.d->localSize; +} + +/*! + Returns \c true if the two InOutVariable objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::InOutVariable + */ +bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.type == rhs.type + && lhs.location == rhs.location + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.imageFormat == rhs.imageFormat + && lhs.imageFlags == rhs.imageFlags; +} + +/*! + Returns \c true if the two BlockVariable objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::BlockVariable + */ +bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.type == rhs.type + && lhs.offset == rhs.offset + && lhs.size == rhs.size + && lhs.arrayDims == rhs.arrayDims + && lhs.arrayStride == rhs.arrayStride + && lhs.matrixStride == rhs.matrixStride + && lhs.matrixIsRowMajor == rhs.matrixIsRowMajor + && lhs.structMembers == rhs.structMembers; +} + +/*! + Returns \c true if the two UniformBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::UniformBlock + */ +bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.blockName == rhs.blockName + && lhs.structName == rhs.structName + && lhs.size == rhs.size + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.members == rhs.members; +} + +/*! + Returns \c true if the two PushConstantBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::PushConstantBlock + */ +bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.name == rhs.name + && lhs.size == rhs.size + && lhs.members == rhs.members; +} + +/*! + Returns \c true if the two StorageBlock objects \a lhs and \a rhs are + equal. + + \relates QShaderDescription::StorageBlock + */ +bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW +{ + return lhs.blockName == rhs.blockName + && lhs.instanceName == rhs.instanceName + && lhs.knownSize == rhs.knownSize + && lhs.binding == rhs.binding + && lhs.descriptorSet == rhs.descriptorSet + && lhs.members == rhs.members; +} + QT_END_NAMESPACE diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h index 5a63b998cda..e02a53dcb58 100644 --- a/src/gui/rhi/qshaderdescription_p.h +++ b/src/gui/rhi/qshaderdescription_p.h @@ -69,9 +69,11 @@ public: bool isValid() const; QByteArray toBinaryJson() const; + QByteArray toCbor() const; QByteArray toJson() const; static QShaderDescription fromBinaryJson(const QByteArray &data); + static QShaderDescription fromCbor(const QByteArray &data); enum VariableType { Unknown = 0, @@ -263,6 +265,7 @@ private: #ifndef QT_NO_DEBUG_STREAM friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &); #endif + friend Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags) @@ -276,6 +279,43 @@ Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlo Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &); #endif +Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW; +Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW; + +inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + +inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW +{ + return !(lhs == rhs); +} + QT_END_NAMESPACE #endif diff --git a/tests/auto/gui/rhi/qshader/data/README b/tests/auto/gui/rhi/qshader/data/README new file mode 100644 index 00000000000..3d89f2a0c52 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/README @@ -0,0 +1,15 @@ +Warning: Do NOT regenerate the .qsb files without proper planning and understanding +the following. + +Among other things, we are also testing backwards compatibility for QShader +deserialization. + +.qsb files with _v1 in the name were produced with an older qtshadertools +and have a QSB_VERSION of 1. + +Files with _v2 are generated with a newer qsb, those have QSB_VERSION 2. +The difference is the support for nativeResourceBindingMap() which is only +present in v2. + +Files with _v3 come from an even newer qsb, and have QSB_VERSION 3. The +difference to 2 is the use of CBOR instead of binary JSON for QShaderDescription. diff --git a/tests/auto/gui/rhi/qshader/data/color.vert.qsb b/tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb similarity index 100% rename from tests/auto/gui/rhi/qshader/data/color.vert.qsb rename to tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb diff --git a/tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb b/tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb similarity index 100% rename from tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb rename to tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb diff --git a/tests/auto/gui/rhi/qshader/data/texture.frag b/tests/auto/gui/rhi/qshader/data/texture.frag new file mode 100644 index 00000000000..bd22f817e03 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/texture.frag @@ -0,0 +1,16 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float opacity; +} ubuf; + +layout(binding = 1) uniform sampler2D qt_Texture; + +void main() +{ + fragColor = texture(qt_Texture, qt_TexCoord) * ubuf.opacity; +} diff --git a/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb b/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..79f54869458093e5b26fa0e5553ad27bf9cc5f0b GIT binary patch literal 1691 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 mv(Y%lmO(OVd;ITjgqsbVersion, 1); QCOMPARE(s.availableShaders().count(), 1); const QShaderCode shader = s.shader(QShaderKey(QShader::SpirvShader, @@ -125,10 +129,11 @@ void tst_QShader::simpleCompileCheckResults() void tst_QShader::genVariants() { - QShader s = getShader(QLatin1String(":/data/color.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_all_v1.vert.qsb")); // spirv, glsl 100, glsl 330, glsl 120, hlsl 50, msl 12 // + batchable variants QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 2 * 6); int batchableVariantCount = 0; @@ -149,8 +154,9 @@ void tst_QShader::genVariants() void tst_QShader::shaderDescImplicitSharing() { - QShader s = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 1); QVERIFY(s.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); @@ -168,6 +174,7 @@ void tst_QShader::shaderDescImplicitSharing() QCOMPARE(d1.inputVariables().count(), 2); QCOMPARE(d1.outputVariables().count(), 1); QCOMPARE(d1.uniformBlocks().count(), 1); + QCOMPARE(d0, d1); d1.detach(); QVERIFY(QShaderDescriptionPrivate::get(&d0) != QShaderDescriptionPrivate::get(&d1)); @@ -177,12 +184,17 @@ void tst_QShader::shaderDescImplicitSharing() QCOMPARE(d1.inputVariables().count(), 2); QCOMPARE(d1.outputVariables().count(), 1); QCOMPARE(d1.uniformBlocks().count(), 1); + QCOMPARE(d0, d1); + + d1 = QShaderDescription(); + QVERIFY(d0 != d1); } void tst_QShader::bakedShaderImplicitSharing() { - QShader s0 = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s0 = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s0.isValid()); + QCOMPARE(QShaderPrivate::get(&s0)->qsbVersion, 1); QCOMPARE(s0.availableShaders().count(), 1); QVERIFY(s0.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); @@ -229,5 +241,159 @@ void tst_QShader::bakedShaderImplicitSharing() } } +void tst_QShader::mslResourceMapping() +{ + QShader s = getShader(QLatin1String(":/data/texture_all_v2.frag.qsb")); + QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 2); + + const QVector availableShaders = s.availableShaders(); + QCOMPARE(availableShaders.count(), 7); + QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330)))); + + const QShader::NativeResourceBindingMap *resMap = + s.nativeResourceBindingMap(QShaderKey(QShader::GlslShader, QShaderVersion(330))); + QVERIFY(!resMap); + + // The Metal shader must come with a mapping table for binding points 0 + // (uniform buffer) and 1 (combined image sampler mapped to a texture and + // sampler in the shader). + resMap = s.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))); + QVERIFY(resMap); + + QCOMPARE(resMap->count(), 2); + QCOMPARE(resMap->value(0).first, 0); // mapped to native buffer index 0 + QCOMPARE(resMap->value(1), qMakePair(0, 0)); // mapped to native texture index 0 and sampler index 0 +} + +void tst_QShader::loadV3() +{ + // qsb version 3: QShaderDescription is serialized as CBOR. Ensure the deserialized data is as expected. + QShader s = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb")); + QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 3); + + const QVector availableShaders = s.availableShaders(); + QCOMPARE(availableShaders.count(), 7); + QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330)))); + + const QShaderDescription desc = s.description(); + QVERIFY(desc.isValid()); + QCOMPARE(desc.inputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.inputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("qt_TexCoord")); + QCOMPARE(v.type, QShaderDescription::Vec2); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.outputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.outputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("fragColor")); + QCOMPARE(v.type, QShaderDescription::Vec4); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.uniformBlocks().count(), 1); + const QShaderDescription::UniformBlock blk = desc.uniformBlocks().first(); + QCOMPARE(blk.blockName, QLatin1String("buf")); + QCOMPARE(blk.structName, QLatin1String("ubuf")); + QCOMPARE(blk.size, 68); + QCOMPARE(blk.binding, 0); + QCOMPARE(blk.descriptorSet, 0); + QCOMPARE(blk.members.count(), 2); + for (int i = 0; i < blk.members.count(); ++i) { + const QShaderDescription::BlockVariable v = blk.members[i]; + switch (i) { + case 0: + QCOMPARE(v.offset, 0); + QCOMPARE(v.size, 64); + QCOMPARE(v.name, QLatin1String("qt_Matrix")); + QCOMPARE(v.type, QShaderDescription::Mat4); + QCOMPARE(v.matrixStride, 16); + break; + case 1: + QCOMPARE(v.offset, 64); + QCOMPARE(v.size, 4); + QCOMPARE(v.name, QLatin1String("opacity")); + QCOMPARE(v.type, QShaderDescription::Float); + break; + default: + QVERIFY(false); + break; + } + } +} + +void tst_QShader::serializeShaderDesc() +{ + // default constructed QShaderDescription + { + QShaderDescription desc; + QVERIFY(!desc.isValid()); + + const QByteArray data = desc.toCbor(); + QVERIFY(!data.isEmpty()); + + QShaderDescription desc2 = QShaderDescription::fromCbor(data); + QVERIFY(!desc2.isValid()); + } + + // a QShaderDescription with inputs, outputs, uniform block and combined image sampler + { + QShader s = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb")); + QVERIFY(s.isValid()); + const QShaderDescription desc = s.description(); + QVERIFY(desc.isValid()); + + const QByteArray data = desc.toCbor(); + QVERIFY(!data.isEmpty()); + + QShaderDescription desc2; + QVERIFY(!desc2.isValid()); + QVERIFY(!(desc == desc2)); + QVERIFY(desc != desc2); + + desc2 = QShaderDescription::fromCbor(data); + QVERIFY(desc2.isValid()); + QCOMPARE(desc, desc2); + } + + // exercise QShader and QShaderDescription comparisons + { + QShader s1 = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb")); + QVERIFY(s1.isValid()); + QShader s2 = getShader(QLatin1String(":/data/color_all_v1.vert.qsb")); + QVERIFY(s2.isValid()); + + QVERIFY(s1.description().isValid()); + QVERIFY(s2.description().isValid()); + + QVERIFY(s1 != s2); + QVERIFY(s1.description() != s2.description()); + } +} + #include QTEST_MAIN(tst_QShader) From b56e856d218976bf39d981468267337d8d6223f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Mon, 18 Mar 2019 12:25:14 +0100 Subject: [PATCH 13/13] Cocoa: rename IsMouseOrKeyEvent -> isUserInputEvent This matches the intended use of this function. Reformat to modern Qt style. Change-Id: I076d2bdb3ac14b346f0dc6934f7a47765badc6b0 Reviewed-by: Timur Pocheptsov --- .../platforms/cocoa/qcocoaeventdispatcher.mm | 68 +++++++++---------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index e87fc39c421..b3ce9e45dc6 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -293,46 +293,42 @@ bool QCocoaEventDispatcher::hasPendingEvents() return qGlobalPostedEventsCount() || (qt_is_gui_used && !CFRunLoopIsWaiting(CFRunLoopGetMain())); } -static bool IsMouseOrKeyEvent( NSEvent* event ) +static bool isUserInputEvent(NSEvent* event) { - bool result = false; - - switch( [event type] ) - { - case NSEventTypeLeftMouseDown: - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseDown: - case NSEventTypeRightMouseUp: - case NSEventTypeMouseMoved: // ?? - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeMouseEntered: - case NSEventTypeMouseExited: - case NSEventTypeKeyDown: - case NSEventTypeKeyUp: - case NSEventTypeFlagsChanged: // key modifiers changed? - case NSEventTypeCursorUpdate: // ?? - case NSEventTypeScrollWheel: - case NSEventTypeTabletPoint: - case NSEventTypeTabletProximity: - case NSEventTypeOtherMouseDown: - case NSEventTypeOtherMouseUp: - case NSEventTypeOtherMouseDragged: + switch ([event type]) { + case NSEventTypeLeftMouseDown: + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseDown: + case NSEventTypeRightMouseUp: + case NSEventTypeMouseMoved: // ?? + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeMouseEntered: + case NSEventTypeMouseExited: + case NSEventTypeKeyDown: + case NSEventTypeKeyUp: + case NSEventTypeFlagsChanged: // key modifiers changed? + case NSEventTypeCursorUpdate: // ?? + case NSEventTypeScrollWheel: + case NSEventTypeTabletPoint: + case NSEventTypeTabletProximity: + case NSEventTypeOtherMouseDown: + case NSEventTypeOtherMouseUp: + case NSEventTypeOtherMouseDragged: #ifndef QT_NO_GESTURES - case NSEventTypeGesture: // touch events - case NSEventTypeMagnify: - case NSEventTypeSwipe: - case NSEventTypeRotate: - case NSEventTypeBeginGesture: - case NSEventTypeEndGesture: + case NSEventTypeGesture: // touch events + case NSEventTypeMagnify: + case NSEventTypeSwipe: + case NSEventTypeRotate: + case NSEventTypeBeginGesture: + case NSEventTypeEndGesture: #endif // QT_NO_GESTURES - result = true; + return true; break; - - default: + default: break; } - return result; + return false; } static inline void qt_mac_waitForMoreEvents(NSString *runLoopMode = NSDefaultRunLoopMode) @@ -465,7 +461,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) dequeue: YES]; if (event) { - if (IsMouseOrKeyEvent(event)) { + if (isUserInputEvent(event)) { [event retain]; d->queuedUserInputEvents.append(event); continue; @@ -485,7 +481,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) if (event) { if (flags & QEventLoop::ExcludeUserInputEvents) { - if (IsMouseOrKeyEvent(event)) { + if (isUserInputEvent(event)) { [event retain]; d->queuedUserInputEvents.append(event); continue;