Submitted By: Pierre Labastie Date: 2023-06-12 Initial Package Version: 5.15.10 Upstream Status: Applied (according to KDE) Origin: KDE patch set Description: Patch set maintained by the KDE folks see https://dot.kde.org/2021/04/06/announcing-kdes-qt-5-patch-collection Patch generated using the procedure in https://wiki.linuxfromscratch.org/blfs/ticket/17476#comment:3 Submodule qtbase 29400a68..e8d9e68d: diff --git a/qtbase/doc/global/html-footer.qdocconf b/qtbase/doc/global/html-footer.qdocconf index 673e500291..f799534c58 100644 --- a/qtbase/doc/global/html-footer.qdocconf +++ b/qtbase/doc/global/html-footer.qdocconf @@ -14,7 +14,7 @@ HTML.footer = \ " The documentation provided herein is licensed under the terms of the" \ " GNU Free Documentation" \ " License version 1.3 as published by the Free Software Foundation.
" \ - " Qt and respective logos are trademarks of The Qt Company Ltd. " \ - " in Finland and/or other countries worldwide. All other trademarks are property\n" \ - " of their respective owners.

\n" \ + " Qt and respective logos are " \ + " trademarks of The Qt Company Ltd. in Finland and/or other countries\n" \ + " worldwide. All other trademarks are property of their respective owners.

\n" \ "\n" \ diff --git a/qtbase/doc/global/qt-module-defaults-online.qdocconf b/qtbase/doc/global/qt-module-defaults-online.qdocconf index 635caf0b94..b35790e2d8 100644 --- a/qtbase/doc/global/qt-module-defaults-online.qdocconf +++ b/qtbase/doc/global/qt-module-defaults-online.qdocconf @@ -11,9 +11,9 @@ HTML.footer = \ " The documentation provided herein is licensed under the terms of the" \ " GNU Free Documentation" \ " License version 1.3 as published by the Free Software Foundation. " \ - " Qt and respective logos are trademarks of The Qt Company Ltd. " \ - " in Finland and/or other countries worldwide. All other trademarks are property\n" \ - " of their respective owners.

\n" + " Qt and respective logos are " \ + " trademarks of The Qt Company Ltd. in Finland and/or other countries\n" \ + " worldwide. All other trademarks are property of their respective owners.

\n" #include standard set of macros and C++ defines and ignores include(macros.qdocconf) diff --git a/qtbase/mkspecs/common/android/qplatformdefs.h b/qtbase/mkspecs/common/android/qplatformdefs.h index f75bc4093b..2bd59410d4 100644 --- a/qtbase/mkspecs/common/android/qplatformdefs.h +++ b/qtbase/mkspecs/common/android/qplatformdefs.h @@ -144,11 +144,7 @@ #define QT_SIGNAL_ARGS int #define QT_SIGNAL_IGNORE SIG_IGN -#if defined(__GLIBC__) && (__GLIBC__ >= 2) #define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf diff --git a/qtbase/mkspecs/linux-clang/qplatformdefs.h b/qtbase/mkspecs/linux-clang/qplatformdefs.h index a818d973f0..c1ab72fbc6 100644 --- a/qtbase/mkspecs/linux-clang/qplatformdefs.h +++ b/qtbase/mkspecs/linux-clang/qplatformdefs.h @@ -79,14 +79,6 @@ #define QT_USE_XOPEN_LFS_EXTENSIONS #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/mkspecs/linux-g++/qplatformdefs.h b/qtbase/mkspecs/linux-g++/qplatformdefs.h index 13523f0702..4d2750d9ec 100644 --- a/qtbase/mkspecs/linux-g++/qplatformdefs.h +++ b/qtbase/mkspecs/linux-g++/qplatformdefs.h @@ -79,14 +79,6 @@ #define QT_USE_XOPEN_LFS_EXTENSIONS #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ < 2) -#define QT_SOCKLEN_T int -#else -#define QT_SOCKLEN_T socklen_t -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/mkspecs/linux-llvm/qplatformdefs.h b/qtbase/mkspecs/linux-llvm/qplatformdefs.h index dc750ab1ef..d3cc39b47f 100644 --- a/qtbase/mkspecs/linux-llvm/qplatformdefs.h +++ b/qtbase/mkspecs/linux-llvm/qplatformdefs.h @@ -80,14 +80,6 @@ #define QT_USE_XOPEN_LFS_EXTENSIONS #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h b/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h index 4c4e53da2a..83baffb3e3 100644 --- a/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h +++ b/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h @@ -85,16 +85,9 @@ #include "../common/posix/qplatformdefs.h" #undef QT_OPEN_LARGEFILE -#undef QT_SOCKLEN_T #define QT_OPEN_LARGEFILE 0 -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #ifndef SIOCGIFBRDADDR # define SIOCGIFBRDADDR 0x8919 #endif diff --git a/qtbase/mkspecs/lynxos-g++/qplatformdefs.h b/qtbase/mkspecs/lynxos-g++/qplatformdefs.h index 4339ea2b23..6007af0055 100644 --- a/qtbase/mkspecs/lynxos-g++/qplatformdefs.h +++ b/qtbase/mkspecs/lynxos-g++/qplatformdefs.h @@ -72,14 +72,6 @@ #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp b/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp index c3c184258f..32af3f8f29 100644 --- a/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp +++ b/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp @@ -10,6 +10,7 @@ #include "libANGLE/HandleAllocator.h" #include +#include #include "common/debug.h" diff --git a/qtbase/src/3rdparty/forkfd/forkfd_linux.c b/qtbase/src/3rdparty/forkfd/forkfd_linux.c index ffe0e9a5e2..b1f5408d11 100644 --- a/qtbase/src/3rdparty/forkfd/forkfd_linux.c +++ b/qtbase/src/3rdparty/forkfd/forkfd_linux.c @@ -82,7 +82,8 @@ static int sys_clone(unsigned long cloneflags, int *ptid) return syscall(__NR_clone, cloneflags, child_stack, stack_size, ptid, newtls, ctid); #elif defined(__arc__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ defined(__nds32__) || defined(__hppa__) || defined(__powerpc__) || defined(__i386__) || \ - defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv) + defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv) || \ + defined(__loongarch__) /* ctid and newtls are inverted on CONFIG_CLONE_BACKWARDS architectures, * but since both values are 0, there's no harm. */ return syscall(__NR_clone, cloneflags, child_stack, ptid, ctid, newtls); diff --git a/qtbase/src/concurrent/qtconcurrentreducekernel.h b/qtbase/src/concurrent/qtconcurrentreducekernel.h index 8f9a938952..a98dedef2e 100644 --- a/qtbase/src/concurrent/qtconcurrentreducekernel.h +++ b/qtbase/src/concurrent/qtconcurrentreducekernel.h @@ -212,11 +212,13 @@ public: inline bool shouldThrottle() { + std::lock_guard locker(mutex); return (resultsMapSize > (ReduceQueueThrottleLimit * threadCount)); } inline bool shouldStartThread() { + std::lock_guard locker(mutex); return (resultsMapSize <= (ReduceQueueStartLimit * threadCount)); } }; diff --git a/qtbase/src/concurrent/qtconcurrentthreadengine.cpp b/qtbase/src/concurrent/qtconcurrentthreadengine.cpp index ea6ce3ac42..7f91a2ba68 100644 --- a/qtbase/src/concurrent/qtconcurrentthreadengine.cpp +++ b/qtbase/src/concurrent/qtconcurrentthreadengine.cpp @@ -176,6 +176,39 @@ void ThreadEngineBase::startSingleThreaded() finish(); } +void ThreadEngineBase::startBlocking() +{ + start(); + barrier.acquire(); + startThreads(); + + bool throttled = false; +#ifndef QT_NO_EXCEPTIONS + try { +#endif + while (threadFunction() == ThrottleThread) { + if (threadThrottleExit()) { + throttled = true; + break; + } + } +#ifndef QT_NO_EXCEPTIONS + } catch (QException &e) { + handleException(e); + } catch (...) { + handleException(QUnhandledException()); + } +#endif + + if (throttled == false) { + barrier.release(); + } + + barrier.wait(); + finish(); + exceptionStore.throwPossibleException(); +} + void ThreadEngineBase::startThread() { startThreadInternal(); diff --git a/qtbase/src/concurrent/qtconcurrentthreadengine.h b/qtbase/src/concurrent/qtconcurrentthreadengine.h index 7c30cebdbc..a4c8548cc4 100644 --- a/qtbase/src/concurrent/qtconcurrentthreadengine.h +++ b/qtbase/src/concurrent/qtconcurrentthreadengine.h @@ -91,6 +91,7 @@ public: ThreadEngineBase(); virtual ~ThreadEngineBase(); void startSingleThreaded(); + void startBlocking(); void startThread(); bool isCanceled(); void waitForResume(); @@ -143,6 +144,15 @@ public: return result(); } + // Runs the user algorithm using multiple threads. + // This function blocks until the algorithm is finished, + // and then returns the result. + T *startBlocking() + { + ThreadEngineBase::startBlocking(); + return result(); + } + // Runs the user algorithm using multiple threads. // Does not block, returns a future. QFuture startAsynchronously() @@ -223,6 +233,13 @@ class ThreadEngineStarter : public ThreadEngineStarterBase public: ThreadEngineStarter(TypedThreadEngine *eng) : Base(eng) { } + + T startBlocking() + { + T t = *this->threadEngine->startBlocking(); + delete this->threadEngine; + return t; + } }; // Full template specialization where T is void. @@ -232,6 +249,12 @@ class ThreadEngineStarter : public ThreadEngineStarterBase public: ThreadEngineStarter(ThreadEngine *_threadEngine) : ThreadEngineStarterBase(_threadEngine) {} + + void startBlocking() + { + this->threadEngine->startBlocking(); + delete this->threadEngine; + } }; //! [qtconcurrentthreadengine-1] diff --git a/qtbase/src/corelib/global/qglobal.cpp b/qtbase/src/corelib/global/qglobal.cpp index ac2e85c51e..f46a6f68ad 100644 --- a/qtbase/src/corelib/global/qglobal.cpp +++ b/qtbase/src/corelib/global/qglobal.cpp @@ -97,6 +97,10 @@ # include #endif +#if defined(Q_OS_MACOS) +#include +#endif + #ifdef Q_OS_UNIX #include #include @@ -2133,6 +2137,15 @@ QT_WARNING_POP static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) { #ifdef Q_OS_MACOS + if (version.majorVersion() == 13) + return "Ventura"; + if (version.majorVersion() == 12) + return "Monterey"; + // Compare against predefined constant to handle 10.16/11.0 + if (QVersionNumber(QOperatingSystemVersion::MacOSBigSur.majorVersion(), + QOperatingSystemVersion::MacOSBigSur.minorVersion(), QOperatingSystemVersion::MacOSBigSur.microVersion()).isPrefixOf( + QVersionNumber(version.majorVersion(), version.minorVersion(), version.microVersion()))) + return "Big Sur"; if (version.majorVersion() == 10) { switch (version.minorVersion()) { case 9: @@ -2147,13 +2160,15 @@ static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSyst return "High Sierra"; case 14: return "Mojave"; + case 15: + return "Catalina"; } } // unknown, future version #else Q_UNUSED(version); #endif - return 0; + return nullptr; } #endif @@ -2278,7 +2293,7 @@ static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSyst } #undef Q_WINVER // unknown, future version - return 0; + return nullptr; } #endif diff --git a/qtbase/src/corelib/global/qnamespace.h b/qtbase/src/corelib/global/qnamespace.h index ad4150b317..bf19b1627b 100644 --- a/qtbase/src/corelib/global/qnamespace.h +++ b/qtbase/src/corelib/global/qnamespace.h @@ -1864,7 +1864,7 @@ public: QT_Q_ENUM(TimerType) QT_Q_ENUM(ScrollPhase) QT_Q_ENUM(MouseEventSource) - QT_Q_FLAG(MouseEventFlag) + QT_Q_FLAG(MouseEventFlags) QT_Q_ENUM(ChecksumType) QT_Q_ENUM(HighDpiScaleFactorRoundingPolicy) QT_Q_ENUM(TabFocusBehavior) diff --git a/qtbase/src/corelib/io/qfilesystemengine_win.cpp b/qtbase/src/corelib/io/qfilesystemengine_win.cpp index 81d3a71986..c86ed1f9b0 100644 --- a/qtbase/src/corelib/io/qfilesystemengine_win.cpp +++ b/qtbase/src/corelib/io/qfilesystemengine_win.cpp @@ -664,14 +664,14 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath()); } -#if defined(Q_CC_MINGW) && WINVER < 0x0602 // Windows 8 onwards +#if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8 // Windows 8 onwards typedef struct _FILE_ID_INFO { ULONGLONG VolumeSerialNumber; FILE_ID_128 FileId; } FILE_ID_INFO, *PFILE_ID_INFO; -#endif // if defined (Q_CC_MINGW) && WINVER < 0x0602 +#endif // if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8 // File ID for Windows up to version 7 and FAT32 drives static inline QByteArray fileId(HANDLE handle) diff --git a/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp b/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp index 94d9d06bcb..27e0b13b0b 100644 --- a/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp +++ b/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp @@ -366,7 +366,9 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() // qDebug("QInotifyFileSystemWatcherEngine::readFromInotify"); int buffSize = 0; - ioctl(inotifyFd, FIONREAD, (char *) &buffSize); + if (ioctl(inotifyFd, FIONREAD, (char *) &buffSize) == -1 || buffSize == 0) + return; + QVarLengthArray buffer(buffSize); buffSize = read(inotifyFd, buffer.data(), buffSize); char *at = buffer.data(); diff --git a/qtbase/src/corelib/io/qfsfileengine.cpp b/qtbase/src/corelib/io/qfsfileengine.cpp index 3042eac2f0..a862ce9166 100644 --- a/qtbase/src/corelib/io/qfsfileengine.cpp +++ b/qtbase/src/corelib/io/qfsfileengine.cpp @@ -361,7 +361,7 @@ bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd) // Seek to the end when in Append mode. if (openMode & QFile::Append) { - int ret; + QT_OFF_T ret; do { ret = QT_LSEEK(fd, 0, SEEK_END); } while (ret == -1 && errno == EINTR); diff --git a/qtbase/src/corelib/io/qfsfileengine_unix.cpp b/qtbase/src/corelib/io/qfsfileengine_unix.cpp index 4610e9306c..65e921c15a 100644 --- a/qtbase/src/corelib/io/qfsfileengine_unix.cpp +++ b/qtbase/src/corelib/io/qfsfileengine_unix.cpp @@ -141,7 +141,7 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) // Seek to the end when in Append mode. if (flags & QFile::Append) { - int ret; + QT_OFF_T ret; do { ret = QT_LSEEK(fd, 0, SEEK_END); } while (ret == -1 && errno == EINTR); diff --git a/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp index 3d7fe43cd3..f2871a2da7 100644 --- a/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp +++ b/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp @@ -939,8 +939,9 @@ void QSortFilterProxyModelPrivate::insert_source_items( q->beginInsertColumns(proxy_parent, proxy_start, proxy_end); } - for (int i = 0; i < source_items.size(); ++i) - proxy_to_source.insert(proxy_start + i, source_items.at(i)); + // TODO: use the range QList::insert() overload once it is implemented (QTBUG-58633). + proxy_to_source.insert(proxy_start, source_items.size(), 0); + std::copy(source_items.cbegin(), source_items.cend(), proxy_to_source.begin() + proxy_start); build_source_to_proxy_mapping(proxy_to_source, source_to_proxy); @@ -3131,8 +3132,9 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & if (d->filter_data.isEmpty()) return true; + + int column_count = d->model->columnCount(source_parent); if (d->filter_column == -1) { - int column_count = d->model->columnCount(source_parent); for (int column = 0; column < column_count; ++column) { QModelIndex source_index = d->model->index(source_row, column, source_parent); QString key = d->model->data(source_index, d->filter_role).toString(); @@ -3141,9 +3143,10 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & } return false; } - QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); - if (!source_index.isValid()) // the column may not exist + + if (d->filter_column >= column_count) // the column may not exist return true; + QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); QString key = d->model->data(source_index, d->filter_role).toString(); return d->filter_data.hasMatch(key); } diff --git a/qtbase/src/corelib/kernel/qcoreapplication.cpp b/qtbase/src/corelib/kernel/qcoreapplication.cpp index 2a2dcc72ca..d671f70bd3 100644 --- a/qtbase/src/corelib/kernel/qcoreapplication.cpp +++ b/qtbase/src/corelib/kernel/qcoreapplication.cpp @@ -1157,7 +1157,7 @@ static bool doNotify(QObject *receiver, QEvent *event) bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event) { // We can't access the application event filters outside of the main thread (race conditions) - Q_ASSERT(receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()); + Q_ASSERT(receiver->d_func()->threadData.loadAcquire()->thread.loadRelaxed() == mainThread()); if (extraData) { // application event filters are only called for objects in the GUI thread diff --git a/qtbase/src/corelib/kernel/qobject.cpp b/qtbase/src/corelib/kernel/qobject.cpp index d57bcc31cc..4fccf8dd56 100644 --- a/qtbase/src/corelib/kernel/qobject.cpp +++ b/qtbase/src/corelib/kernel/qobject.cpp @@ -1010,7 +1010,7 @@ QObject::~QObject() emit destroyed(this); } - if (d->declarativeData) { + if (!d->isDeletingChildren && d->declarativeData) { if (static_cast(d->declarativeData)->ownedByQml1) { if (QAbstractDeclarativeData::destroyed_qml1) QAbstractDeclarativeData::destroyed_qml1(d->declarativeData, this); @@ -1580,7 +1580,7 @@ void QObject::moveToThread(QThread *targetThread) QThreadData *currentData = QThreadData::current(); QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr; - QThreadData *thisThreadData = d->threadData.loadRelaxed(); + QThreadData *thisThreadData = d->threadData.loadAcquire(); if (!thisThreadData->thread.loadAcquire() && currentData == targetData) { // one exception to the rule: we allow moving objects with no thread affinity to the current thread currentData = d->threadData; @@ -2622,7 +2622,7 @@ int QObject::receivers(const char *signal) const if (!d->isSignalConnected(signal_index)) return receivers; - if (d->declarativeData && QAbstractDeclarativeData::receivers) { + if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::receivers) { receivers += QAbstractDeclarativeData::receivers(d->declarativeData, this, signal_index); } diff --git a/qtbase/src/corelib/kernel/qobject_p.h b/qtbase/src/corelib/kernel/qobject_p.h index 0b827a52ca..13ffb88999 100644 --- a/qtbase/src/corelib/kernel/qobject_p.h +++ b/qtbase/src/corelib/kernel/qobject_p.h @@ -444,7 +444,7 @@ inline void QObjectPrivate::checkForIncompatibleLibraryVersion(int version) cons inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const { - return declarativeData && QAbstractDeclarativeData::isSignalConnected + return !isDeletingChildren && declarativeData && QAbstractDeclarativeData::isSignalConnected && QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index); } diff --git a/qtbase/src/corelib/kernel/qtranslator.cpp b/qtbase/src/corelib/kernel/qtranslator.cpp index 2812ffb7ea..bc0177c5ec 100644 --- a/qtbase/src/corelib/kernel/qtranslator.cpp +++ b/qtbase/src/corelib/kernel/qtranslator.cpp @@ -902,7 +902,7 @@ static QString getMessage(const uchar *m, const uchar *end, const char *context, goto end; case Tag_Translation: { int len = read32(m); - if (len % 1) + if (len & 1) return QString(); m += 4; if (!numerus--) { diff --git a/qtbase/src/corelib/mimetypes/qmimedatabase.cpp b/qtbase/src/corelib/mimetypes/qmimedatabase.cpp index 9de22cef33..ff868a3268 100644 --- a/qtbase/src/corelib/mimetypes/qmimedatabase.cpp +++ b/qtbase/src/corelib/mimetypes/qmimedatabase.cpp @@ -389,20 +389,23 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa // Disambiguate conflicting extensions (if magic matching found something) if (candidateByData.isValid() && magicAccuracy > 0) { const QString sniffedMime = candidateByData.name(); - // If the sniffedMime matches a glob match, use it + // If the sniffedMime matches a highest-weight glob match, use it if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime)) { *accuracyPtr = 100; return candidateByData; } - for (const QString &m : qAsConst(candidatesByName.m_matchingMimeTypes)) { + for (const QString &m : qAsConst(candidatesByName.m_allMatchingMimeTypes)) { if (inherits(m, sniffedMime)) { // We have magic + pattern pointing to this, so it's a pretty good match *accuracyPtr = 100; return mimeTypeForName(m); } } - *accuracyPtr = magicAccuracy; - return candidateByData; + if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) { + // No glob, use magic + *accuracyPtr = magicAccuracy; + return candidateByData; + } } } diff --git a/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp b/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp index b1de8907b2..fa8f4c545d 100644 --- a/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp +++ b/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp @@ -83,7 +83,10 @@ void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const Q } if (!m_matchingMimeTypes.contains(mimeType)) { m_matchingMimeTypes.append(mimeType); - m_allMatchingMimeTypes.append(mimeType); + if (replace) + m_allMatchingMimeTypes.prepend(mimeType); // highest-weight first + else + m_allMatchingMimeTypes.append(mimeType); m_knownSuffixLength = knownSuffixLength; } } diff --git a/qtbase/src/corelib/mimetypes/qmimeprovider.cpp b/qtbase/src/corelib/mimetypes/qmimeprovider.cpp index 258dddf8cb..4642d0f2d0 100644 --- a/qtbase/src/corelib/mimetypes/qmimeprovider.cpp +++ b/qtbase/src/corelib/mimetypes/qmimeprovider.cpp @@ -244,15 +244,18 @@ void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobM const QString lowerFileName = fileName.toLower(); // Check literals (e.g. "Makefile") matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), fileName); - // Check complex globs (e.g. "callgrind.out[0-9]*") - matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); // Check the very common *.txt cases with the suffix tree - const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); - const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); - const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); - matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); + if (result.m_matchingMimeTypes.isEmpty()) { + const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); + const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); + const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); + matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); + if (result.m_matchingMimeTypes.isEmpty()) + matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); + } + // Check complex globs (e.g. "callgrind.out[0-9]*" or "README*") if (result.m_matchingMimeTypes.isEmpty()) - matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); + matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); } void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) diff --git a/qtbase/src/corelib/serialization/qcborvalue.cpp b/qtbase/src/corelib/serialization/qcborvalue.cpp index 89a928d348..3a8c2cb9ec 100644 --- a/qtbase/src/corelib/serialization/qcborvalue.cpp +++ b/qtbase/src/corelib/serialization/qcborvalue.cpp @@ -2123,7 +2123,8 @@ QCborArray QCborValue::toArray(const QCborArray &defaultValue) const Q_ASSERT(n == -1 || container == nullptr); if (n < 0) dd = container; - return dd ? QCborArray(*dd) : defaultValue; + // return QCborArray(*dd); but that's UB if dd is nullptr + return dd ? QCborArray(*dd) : QCborArray(); } /*! @@ -2165,7 +2166,8 @@ QCborMap QCborValue::toMap(const QCborMap &defaultValue) const Q_ASSERT(n == -1 || container == nullptr); if (n < 0) dd = container; - return dd ? QCborMap(*dd) : defaultValue; + // return QCborMap(*dd); but that's UB if dd is nullptr + return dd ? QCborMap(*dd) : QCborMap(); } /*! diff --git a/qtbase/src/corelib/text/qstringiterator_p.h b/qtbase/src/corelib/text/qstringiterator_p.h index 219589b6e4..1d0c66cc78 100644 --- a/qtbase/src/corelib/text/qstringiterator_p.h +++ b/qtbase/src/corelib/text/qstringiterator_p.h @@ -61,6 +61,8 @@ class QStringIterator { QString::const_iterator i, pos, e; Q_STATIC_ASSERT((std::is_same::value)); + static bool less(const QChar *lhs, const QChar *rhs) noexcept + { return std::less{}(lhs, rhs); } public: explicit QStringIterator(QStringView string, qsizetype idx = 0) : i(string.begin()), @@ -95,7 +97,8 @@ public: inline void setPosition(QString::const_iterator position) { - Q_ASSERT_X(i <= position && position <= e, Q_FUNC_INFO, "position out of bounds"); + Q_ASSERT_X(!less(position, i) && !less(e, position), + Q_FUNC_INFO, "position out of bounds"); pos = position; } @@ -103,7 +106,7 @@ public: inline bool hasNext() const { - return pos < e; + return less(pos, e); } inline void advance() @@ -120,16 +123,20 @@ public: { Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); - if (Q_UNLIKELY((pos++)->isHighSurrogate())) + if (Q_UNLIKELY((pos++)->isHighSurrogate())) { + Q_ASSERT(hasNext() && pos->isLowSurrogate()); ++pos; + } } inline uint peekNextUnchecked() const { Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); - if (Q_UNLIKELY(pos->isHighSurrogate())) + if (Q_UNLIKELY(pos->isHighSurrogate())) { + Q_ASSERT(less(pos + 1, e) && pos[1].isLowSurrogate()); return QChar::surrogateToUcs4(pos[0], pos[1]); + } return pos->unicode(); } @@ -155,8 +162,10 @@ public: Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); const QChar cur = *pos++; - if (Q_UNLIKELY(cur.isHighSurrogate())) + if (Q_UNLIKELY(cur.isHighSurrogate())) { + Q_ASSERT(hasNext() && pos->isLowSurrogate()); return QChar::surrogateToUcs4(cur, *pos++); + } return cur.unicode(); } @@ -166,7 +175,7 @@ public: const QChar uc = *pos++; if (Q_UNLIKELY(uc.isSurrogate())) { - if (Q_LIKELY(uc.isHighSurrogate() && pos < e && pos->isLowSurrogate())) + if (Q_LIKELY(uc.isHighSurrogate() && hasNext() && pos->isLowSurrogate())) return QChar::surrogateToUcs4(uc, *pos++); return invalidAs; } @@ -178,7 +187,7 @@ public: inline bool hasPrevious() const { - return pos > i; + return less(i, pos); } inline void recede() @@ -196,16 +205,20 @@ public: { Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); - if (Q_UNLIKELY((--pos)->isLowSurrogate())) + if (Q_UNLIKELY((--pos)->isLowSurrogate())) { + Q_ASSERT(hasPrevious() && pos[-1].isHighSurrogate()); --pos; + } } inline uint peekPreviousUnchecked() const { Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); - if (Q_UNLIKELY(pos[-1].isLowSurrogate())) + if (Q_UNLIKELY(pos[-1].isLowSurrogate())) { + Q_ASSERT(less(i + 1, pos) && pos[-2].isHighSurrogate()); return QChar::surrogateToUcs4(pos[-2], pos[-1]); + } return pos[-1].unicode(); } @@ -230,8 +243,10 @@ public: Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); const QChar cur = *--pos; - if (Q_UNLIKELY(cur.isLowSurrogate())) + if (Q_UNLIKELY(cur.isLowSurrogate())) { + Q_ASSERT(hasPrevious() && pos[-1].isHighSurrogate()); return QChar::surrogateToUcs4(*--pos, cur); + } return cur.unicode(); } @@ -241,7 +256,7 @@ public: const QChar uc = *--pos; if (Q_UNLIKELY(uc.isSurrogate())) { - if (Q_LIKELY(uc.isLowSurrogate() && pos > i && pos[-1].isHighSurrogate())) + if (Q_LIKELY(uc.isLowSurrogate() && hasPrevious() && pos[-1].isHighSurrogate())) return QChar::surrogateToUcs4(*--pos, uc); return invalidAs; } diff --git a/qtbase/src/corelib/thread/qfutex_p.h b/qtbase/src/corelib/thread/qfutex_p.h index f287b752d7..e294537787 100644 --- a/qtbase/src/corelib/thread/qfutex_p.h +++ b/qtbase/src/corelib/thread/qfutex_p.h @@ -52,6 +52,7 @@ // #include +#include QT_BEGIN_NAMESPACE @@ -106,16 +107,13 @@ namespace QtLinuxFutex { inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0, int *addr2 = nullptr, int val3 = 0) noexcept { - // A futex call ensures total ordering on the futex words - // (in either success or failure of the call). Instruct TSAN accordingly, - // as TSAN does not understand the futex(2) syscall. - _q_tsan_release(addr, addr2); + 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); - _q_tsan_acquire(addr, addr2); + QtTsan::futexAcquire(addr, addr2); return result; } diff --git a/qtbase/src/corelib/thread/qmutex.cpp b/qtbase/src/corelib/thread/qmutex.cpp index 310d1cb14f..7097122d8e 100644 --- a/qtbase/src/corelib/thread/qmutex.cpp +++ b/qtbase/src/corelib/thread/qmutex.cpp @@ -152,6 +152,7 @@ public: /*! \enum QMutex::RecursionMode + \obsolete Use QRecursiveMutex to create a recursive mutex. \value Recursive In this mode, a thread can lock the same mutex multiple times and the mutex won't be unlocked @@ -173,6 +174,7 @@ public: /*! Constructs a new mutex. The mutex is created in an unlocked state. + \obsolete Use QRecursiveMutex to create a recursive mutex. If \a mode is QMutex::Recursive, a thread can lock the same mutex multiple times and the mutex won't be unlocked until a @@ -197,7 +199,7 @@ QMutex::QMutex(RecursionMode mode) QMutex::~QMutex() { QMutexData *d = d_ptr.loadRelaxed(); - if (isRecursive()) { + if (QBasicMutex::isRecursive()) { delete static_cast(d); } else if (d) { #ifndef QT_LINUX_FUTEX diff --git a/qtbase/src/corelib/thread/qmutex.h b/qtbase/src/corelib/thread/qmutex.h index 73c9e00663..1bae573a03 100644 --- a/qtbase/src/corelib/thread/qmutex.h +++ b/qtbase/src/corelib/thread/qmutex.h @@ -42,6 +42,7 @@ #include #include +#include #include #if __has_include() @@ -77,19 +78,37 @@ public: // BasicLockable concept inline void lock() QT_MUTEX_LOCK_NOEXCEPT { + QtTsan::mutexPreLock(this, 0u); + if (!fastTryLock()) lockInternal(); + + QtTsan::mutexPostLock(this, 0u, 0); } // BasicLockable concept inline void unlock() noexcept { Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked + + QtTsan::mutexPreUnlock(this, 0u); + if (!fastTryUnlock()) unlockInternal(); + + QtTsan::mutexPostUnlock(this, 0u); } bool tryLock() noexcept { - return fastTryLock(); + unsigned tsanFlags = QtTsan::TryLock; + QtTsan::mutexPreLock(this, tsanFlags); + + const bool success = fastTryLock(); + + if (!success) + tsanFlags |= QtTsan::TryLockFailed; + QtTsan::mutexPostLock(this, tsanFlags, 0); + + return success; } // Lockable concept @@ -134,8 +153,16 @@ public: #else QMutex() { d_ptr.storeRelaxed(nullptr); } #endif +#if QT_DEPRECATED_SINCE(5,15) enum RecursionMode { NonRecursive, Recursive }; + QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex") explicit QMutex(RecursionMode mode); + + QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex") + bool isRecursive() const noexcept + { return QBasicMutex::isRecursive(); } +#endif + ~QMutex(); // BasicLockable concept @@ -166,9 +193,6 @@ public: } #endif - bool isRecursive() const noexcept - { return QBasicMutex::isRecursive(); } - private: Q_DISABLE_COPY(QMutex) friend class QMutexLocker; diff --git a/qtbase/src/corelib/thread/qreadwritelock.cpp b/qtbase/src/corelib/thread/qreadwritelock.cpp index 63637ae05b..9dd8503116 100644 --- a/qtbase/src/corelib/thread/qreadwritelock.cpp +++ b/qtbase/src/corelib/thread/qreadwritelock.cpp @@ -453,6 +453,7 @@ QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() co if (!d) return Unlocked; + const auto lock = qt_scoped_lock(d->mutex); if (d->writerCount > 1) return RecursivelyLocked; else if (d->writerCount == 1) diff --git a/qtbase/src/corelib/thread/qthread_unix.cpp b/qtbase/src/corelib/thread/qthread_unix.cpp index f6499b38d0..a27782d37c 100644 --- a/qtbase/src/corelib/thread/qthread_unix.cpp +++ b/qtbase/src/corelib/thread/qthread_unix.cpp @@ -169,8 +169,7 @@ static void set_thread_data(QThreadData *data) static void clear_thread_data() { - currentThreadData = nullptr; - pthread_setspecific(current_thread_data_key, nullptr); + set_thread_data(nullptr); } template diff --git a/qtbase/src/corelib/thread/qthreadpool.cpp b/qtbase/src/corelib/thread/qthreadpool.cpp index 40cc646519..8aa06a4c8f 100644 --- a/qtbase/src/corelib/thread/qthreadpool.cpp +++ b/qtbase/src/corelib/thread/qthreadpool.cpp @@ -602,8 +602,12 @@ bool QThreadPool::tryStart(std::function functionToRun) return false; QRunnable *runnable = QRunnable::create(std::move(functionToRun)); + Q_ASSERT(runnable->ref == 0); + ++runnable->ref; if (d->tryStart(runnable)) return true; + --runnable->ref; + Q_ASSERT(runnable->ref == 0); delete runnable; return false; } diff --git a/qtbase/src/corelib/thread/qtsan_impl.h b/qtbase/src/corelib/thread/qtsan_impl.h new file mode 100644 index 0000000000..580a738b91 --- /dev/null +++ b/qtbase/src/corelib/thread/qtsan_impl.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation. +** Copyright (C) 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTSAN_IMPL_H +#define QTSAN_IMPL_H + +#include + +#if (__has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)) && __has_include() +# define QT_BUILDING_UNDER_TSAN +# include +#endif + +QT_BEGIN_NAMESPACE + +namespace QtTsan { +#ifdef QT_BUILDING_UNDER_TSAN +inline void futexAcquire(void *addr, void *addr2 = nullptr) +{ + // A futex call ensures total ordering on the futex words + // (in either success or failure of the call). Instruct TSAN accordingly, + // as TSAN does not understand the futex(2) syscall (or equivalent). + ::__tsan_acquire(addr); + if (addr2) + ::__tsan_acquire(addr2); +} + +inline void futexRelease(void *addr, void *addr2 = nullptr) +{ + if (addr2) + ::__tsan_release(addr2); + ::__tsan_release(addr); +} + +inline void mutexPreLock(void *addr, unsigned flags) +{ + ::__tsan_mutex_pre_lock(addr, flags); +} + +inline void mutexPostLock(void *addr, unsigned flags, int recursion) +{ + ::__tsan_mutex_post_lock(addr, flags, recursion); +} + +inline void mutexPreUnlock(void *addr, unsigned flags) +{ + ::__tsan_mutex_pre_unlock(addr, flags); +} + +inline void mutexPostUnlock(void *addr, unsigned flags) +{ + ::__tsan_mutex_post_unlock(addr, flags); +} + +enum : unsigned { + MutexWriteReentrant = ::__tsan_mutex_write_reentrant, + TryLock = ::__tsan_mutex_try_lock, + TryLockFailed = ::__tsan_mutex_try_lock_failed, +}; +#else +inline void futexAcquire(void *, void * = nullptr) {} +inline void futexRelease(void *, void * = nullptr) {} + +enum : unsigned { + MutexWriteReentrant, + TryLock, + TryLockFailed, +}; +inline void mutexPreLock(void *, unsigned) {} +inline void mutexPostLock(void *, unsigned, int) {} +inline void mutexPreUnlock(void *, unsigned) {} +inline void mutexPostUnlock(void *, unsigned) {} +#endif // QT_BUILDING_UNDER_TSAN +} // namespace QtTsan + +QT_END_NAMESPACE + +#endif // QTSAN_IMPL_H diff --git a/qtbase/src/corelib/thread/qwaitcondition_unix.cpp b/qtbase/src/corelib/thread/qwaitcondition_unix.cpp index 88b058f410..0f1da4dc9b 100644 --- a/qtbase/src/corelib/thread/qwaitcondition_unix.cpp +++ b/qtbase/src/corelib/thread/qwaitcondition_unix.cpp @@ -213,7 +213,7 @@ bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline) { if (! mutex) return false; - if (mutex->isRecursive()) { + if (static_cast(mutex)->isRecursive()) { qWarning("QWaitCondition: cannot wait on recursive mutexes"); return false; } diff --git a/qtbase/src/corelib/time/qtimezone.cpp b/qtbase/src/corelib/time/qtimezone.cpp index 0309e43e52..3d451696a1 100644 --- a/qtbase/src/corelib/time/qtimezone.cpp +++ b/qtbase/src/corelib/time/qtimezone.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 John Layt +** Copyright (C) 2020 John Layt ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -975,9 +975,15 @@ QList QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId, } #ifndef QT_NO_DATASTREAM +// Invalid, as an IANA ID: too long, starts with - and has other invalid characters in it +static inline QString invalidId() { return QStringLiteral("-No Time Zone Specified!"); } + QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz) { - tz.d->serialize(ds); + if (tz.isValid()) + tz.d->serialize(ds); + else + ds << invalidId(); return ds; } @@ -985,7 +991,9 @@ QDataStream &operator>>(QDataStream &ds, QTimeZone &tz) { QString ianaId; ds >> ianaId; - if (ianaId == QLatin1String("OffsetFromUtc")) { + if (ianaId == invalidId()) { + tz = QTimeZone(); + } else if (ianaId == QLatin1String("OffsetFromUtc")) { int utcOffset; QString name; QString abbreviation; diff --git a/qtbase/src/corelib/tools/qarraydata.h b/qtbase/src/corelib/tools/qarraydata.h index dcd95924c1..d01739e7e6 100644 --- a/qtbase/src/corelib/tools/qarraydata.h +++ b/qtbase/src/corelib/tools/qarraydata.h @@ -42,6 +42,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -58,14 +59,14 @@ struct Q_CORE_EXPORT QArrayData { Q_ASSERT(size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)); - return reinterpret_cast(this) + offset; + return reinterpret_cast (reinterpret_cast(this) + offset); } const void *data() const { Q_ASSERT(size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)); - return reinterpret_cast(this) + offset; + return reinterpret_cast (reinterpret_cast(this) + offset); } // This refers to array data mutability, not "header data" represented by diff --git a/qtbase/src/dbus/qdbusintegrator.cpp b/qtbase/src/dbus/qdbusintegrator.cpp index 5b66ce971b..28a3b0ffea 100644 --- a/qtbase/src/dbus/qdbusintegrator.cpp +++ b/qtbase/src/dbus/qdbusintegrator.cpp @@ -1135,7 +1135,13 @@ void QDBusConnectionPrivate::closeConnection() } } - qDeleteAll(pendingCalls); + for (auto it = pendingCalls.begin(); it != pendingCalls.end(); ++it) { + auto call = *it; + if (!call->ref.deref()) { + delete call; + } + } + pendingCalls.clear(); // Disconnect all signals from signal hooks and from the object tree to // avoid QObject::destroyed being sent to dbus daemon thread which has @@ -2584,6 +2590,11 @@ QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &pa if (mo) return mo; } + if (path.isEmpty()) { + error = QDBusError(QDBusError::InvalidObjectPath, QLatin1String("Object path cannot be empty")); + lastError = error; + return nullptr; + } // introspect the target object QDBusMessage msg = QDBusMessage::createMethodCall(service, path, diff --git a/qtbase/src/gui/configure.json b/qtbase/src/gui/configure.json index 1f08795c57..12c95742d2 100644 --- a/qtbase/src/gui/configure.json +++ b/qtbase/src/gui/configure.json @@ -834,7 +834,8 @@ "// embedded devices, are not intended to be used together with X. EGL support", "// has to be disabled in plugins like xcb in this case since the native display,", "// window and pixmap types will be different than what an X-based platform", - "// plugin would expect." + "// plugin would expect.", + "#define USE_X11" ], "include": [ "EGL/egl.h", "X11/Xlib.h" ], "main": [ diff --git a/qtbase/src/gui/image/qbmphandler.cpp b/qtbase/src/gui/image/qbmphandler.cpp index 96f1e8cb1d..0e73bbbdb0 100644 --- a/qtbase/src/gui/image/qbmphandler.cpp +++ b/qtbase/src/gui/image/qbmphandler.cpp @@ -150,16 +150,42 @@ static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) return s; } -static int calc_shift(uint mask) +static uint calc_shift(uint mask) { - int result = 0; - while (mask && !(mask & 1)) { + uint result = 0; + while ((mask >= 0x100) || (!(mask & 1) && mask)) { result++; mask >>= 1; } return result; } +static uint calc_scale(uint low_mask) +{ + uint result = 8; + while (low_mask && result) { + result--; + low_mask >>= 1; + } + return result; +} + +static inline uint apply_scale(uint value, uint scale) +{ + if (!(scale & 0x07)) // return immediately if scale == 8 or 0 + return value; + + uint filled = 8 - scale; + uint result = value << scale; + + do { + result |= result >> filled; + filled <<= 1; + } while (filled < 8); + + return result; +} + static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf) { // read BMP file header @@ -222,14 +248,14 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, uint green_mask = 0; uint blue_mask = 0; uint alpha_mask = 0; - int red_shift = 0; - int green_shift = 0; - int blue_shift = 0; - int alpha_shift = 0; - int red_scale = 0; - int green_scale = 0; - int blue_scale = 0; - int alpha_scale = 0; + uint red_shift = 0; + uint green_shift = 0; + uint blue_shift = 0; + uint alpha_shift = 0; + uint red_scale = 0; + uint green_scale = 0; + uint blue_scale = 0; + uint alpha_scale = 0; if (!d->isSequential()) d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap or masks @@ -308,19 +334,19 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, red_shift = calc_shift(red_mask); if (((red_mask >> red_shift) + 1) == 0) return false; - red_scale = 256 / ((red_mask >> red_shift) + 1); + red_scale = calc_scale(red_mask >> red_shift); green_shift = calc_shift(green_mask); if (((green_mask >> green_shift) + 1) == 0) return false; - green_scale = 256 / ((green_mask >> green_shift) + 1); + green_scale = calc_scale(green_mask >> green_shift); blue_shift = calc_shift(blue_mask); if (((blue_mask >> blue_shift) + 1) == 0) return false; - blue_scale = 256 / ((blue_mask >> blue_shift) + 1); + blue_scale = calc_scale(blue_mask >> blue_shift); alpha_shift = calc_shift(alpha_mask); if (((alpha_mask >> alpha_shift) + 1) == 0) return false; - alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1); + alpha_scale = calc_scale(alpha_mask >> alpha_shift); } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) { blue_mask = 0x000000ff; green_mask = 0x0000ff00; @@ -328,17 +354,15 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, blue_shift = 0; green_shift = 8; red_shift = 16; - blue_scale = green_scale = red_scale = 1; + blue_scale = green_scale = red_scale = 0; } else if (comp == BMP_RGB && nbits == 16) { blue_mask = 0x001f; green_mask = 0x03e0; red_mask = 0x7c00; blue_shift = 0; - green_shift = 2; - red_shift = 7; - red_scale = 1; - green_scale = 1; - blue_scale = 8; + green_shift = 5; + red_shift = 10; + blue_scale = green_scale = red_scale = 3; } #if 0 @@ -544,10 +568,10 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, c |= *(uchar*)(b+2)<<16; if (nbits > 24) c |= *(uchar*)(b+3)<<24; - *p++ = qRgba(((c & red_mask) >> red_shift) * red_scale, - ((c & green_mask) >> green_shift) * green_scale, - ((c & blue_mask) >> blue_shift) * blue_scale, - transp ? ((c & alpha_mask) >> alpha_shift) * alpha_scale : 0xff); + *p++ = qRgba(apply_scale((c & red_mask) >> red_shift, red_scale), + apply_scale((c & green_mask) >> green_shift, green_scale), + apply_scale((c & blue_mask) >> blue_shift, blue_scale), + transp ? apply_scale((c & alpha_mask) >> alpha_shift, alpha_scale) : 0xff); b += nbits/8; } } diff --git a/qtbase/src/gui/image/qimage.cpp b/qtbase/src/gui/image/qimage.cpp index 53bb3beb10..04e1a00429 100644 --- a/qtbase/src/gui/image/qimage.cpp +++ b/qtbase/src/gui/image/qimage.cpp @@ -4694,6 +4694,8 @@ QImage QImage::smoothScaled(int w, int h) const { static QImage rotated90(const QImage &image) { QImage out(image.height(), image.width(), image.format()); + if (out.isNull()) + return out; copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); @@ -4722,6 +4724,8 @@ static QImage rotated180(const QImage &image) return image.mirrored(true, true); QImage out(image.width(), image.height(), image.format()); + if (out.isNull()) + return out; copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); @@ -4734,6 +4738,8 @@ static QImage rotated180(const QImage &image) static QImage rotated270(const QImage &image) { QImage out(image.height(), image.width(), image.format()); + if (out.isNull()) + return out; copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); diff --git a/qtbase/src/gui/image/qimage_neon.cpp b/qtbase/src/gui/image/qimage_neon.cpp index 9dbcb11db5..c17f76f2b0 100644 --- a/qtbase/src/gui/image/qimage_neon.cpp +++ b/qtbase/src/gui/image/qimage_neon.cpp @@ -54,7 +54,7 @@ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, cons // align dst on 128 bits const int offsetToAlignOn16Bytes = (reinterpret_cast(dst) >> 2) & 0x3; - for (int i = 0; i < offsetToAlignOn16Bytes; ++i) { + for (int i = 0; i < qMin(len, offsetToAlignOn16Bytes); ++i) { *dst++ = qRgb(src[0], src[1], src[2]); src += 3; } diff --git a/qtbase/src/gui/image/qimagereader.cpp b/qtbase/src/gui/image/qimagereader.cpp index 5cb7e1328e..1274622d53 100644 --- a/qtbase/src/gui/image/qimagereader.cpp +++ b/qtbase/src/gui/image/qimagereader.cpp @@ -515,9 +515,9 @@ QImageReaderPrivate::QImageReaderPrivate(QImageReader *qq) */ QImageReaderPrivate::~QImageReaderPrivate() { + delete handler; if (deleteDevice) delete device; - delete handler; } /*! @@ -774,12 +774,12 @@ bool QImageReader::decideFormatFromContent() const */ void QImageReader::setDevice(QIODevice *device) { + delete d->handler; + d->handler = nullptr; if (d->device && d->deleteDevice) delete d->device; d->device = device; d->deleteDevice = false; - delete d->handler; - d->handler = nullptr; d->text.clear(); } diff --git a/qtbase/src/gui/image/qimagewriter.cpp b/qtbase/src/gui/image/qimagewriter.cpp index 33f5e491c7..a679f25757 100644 --- a/qtbase/src/gui/image/qimagewriter.cpp +++ b/qtbase/src/gui/image/qimagewriter.cpp @@ -349,9 +349,9 @@ QImageWriter::QImageWriter(const QString &fileName, const QByteArray &format) */ QImageWriter::~QImageWriter() { + delete d->handler; if (d->deleteDevice) delete d->device; - delete d->handler; delete d; } @@ -396,13 +396,13 @@ QByteArray QImageWriter::format() const */ void QImageWriter::setDevice(QIODevice *device) { + delete d->handler; + d->handler = nullptr; if (d->device && d->deleteDevice) delete d->device; d->device = device; d->deleteDevice = false; - delete d->handler; - d->handler = nullptr; } /*! diff --git a/qtbase/src/gui/kernel/qhighdpiscaling.cpp b/qtbase/src/gui/kernel/qhighdpiscaling.cpp index 85ff58c14c..a433e94c22 100644 --- a/qtbase/src/gui/kernel/qhighdpiscaling.cpp +++ b/qtbase/src/gui/kernel/qhighdpiscaling.cpp @@ -580,9 +580,8 @@ void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor) else qNamedScreenScaleFactors()->insert(name, factor); - // hack to force re-evaluation of screen geometry if (screen->handle()) - screen->d_func()->setPlatformScreen(screen->handle()); // updates geometries based on scale factor + screen->d_func()->updateLogicalDpi(); } QPoint QHighDpiScaling::mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen) diff --git a/qtbase/src/gui/kernel/qkeysequence.cpp b/qtbase/src/gui/kernel/qkeysequence.cpp index 23be5e1ac0..268e0e3ab1 100644 --- a/qtbase/src/gui/kernel/qkeysequence.cpp +++ b/qtbase/src/gui/kernel/qkeysequence.cpp @@ -701,6 +701,10 @@ static const struct { { Qt::Key_TouchpadToggle, QT_TRANSLATE_NOOP("QShortcut", "Touchpad Toggle") }, { Qt::Key_TouchpadOn, QT_TRANSLATE_NOOP("QShortcut", "Touchpad On") }, { Qt::Key_TouchpadOff, QT_TRANSLATE_NOOP("QShortcut", "Touchpad Off") }, + { Qt::Key_Shift, QT_TRANSLATE_NOOP("QShortcut", "Shift") }, + { Qt::Key_Control, QT_TRANSLATE_NOOP("QShortcut", "Control") }, + { Qt::Key_Alt, QT_TRANSLATE_NOOP("QShortcut", "Alt") }, + { Qt::Key_Meta, QT_TRANSLATE_NOOP("QShortcut", "Meta") }, }; static Q_CONSTEXPR int numKeyNames = sizeof keyname / sizeof *keyname; diff --git a/qtbase/src/gui/kernel/qplatformservices.cpp b/qtbase/src/gui/kernel/qplatformservices.cpp index fdc6a6c4aa..ac47f98c5d 100644 --- a/qtbase/src/gui/kernel/qplatformservices.cpp +++ b/qtbase/src/gui/kernel/qplatformservices.cpp @@ -55,6 +55,19 @@ QT_BEGIN_NAMESPACE \brief The QPlatformServices provides the backend for desktop-related functionality. */ +/*! + \enum QPlatformServices::Capability + + Capabilities are used to determine a specific platform service's availability. + + \value ColorPickingFromScreen The platform natively supports color picking from screen. + This capability indicates that the platform supports "opaque" color picking, where the + platform implements a complete user experience for color picking and outputs a color. + This is in contrast to the application implementing the color picking user experience + (taking care of showing a cross hair, instructing the platform integration to obtain + the color at a given pixel, etc.). The related service function is pickColor(). + */ + QPlatformServices::QPlatformServices() { } @@ -85,5 +98,16 @@ QByteArray QPlatformServices::desktopEnvironment() const return QByteArray("UNKNOWN"); } +QPlatformServiceColorPicker *QPlatformServices::colorPicker(QWindow *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + +bool QPlatformServices::hasCapability(Capability capability) const +{ + Q_UNUSED(capability) + return false; +} QT_END_NAMESPACE diff --git a/qtbase/src/gui/kernel/qplatformservices.h b/qtbase/src/gui/kernel/qplatformservices.h index 5de96cfa7d..a8b2a4ce71 100644 --- a/qtbase/src/gui/kernel/qplatformservices.h +++ b/qtbase/src/gui/kernel/qplatformservices.h @@ -50,16 +50,32 @@ // #include +#include QT_BEGIN_NAMESPACE class QUrl; +class QWindow; + +class Q_GUI_EXPORT QPlatformServiceColorPicker : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; + virtual void pickColor() = 0; +Q_SIGNALS: + void colorPicked(const QColor &color); +}; class Q_GUI_EXPORT QPlatformServices { public: Q_DISABLE_COPY_MOVE(QPlatformServices) + enum Capability { + ColorPicking, + }; + QPlatformServices(); virtual ~QPlatformServices() { } @@ -67,6 +83,10 @@ public: virtual bool openDocument(const QUrl &url); virtual QByteArray desktopEnvironment() const; + + virtual bool hasCapability(Capability capability) const; + + virtual QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr); }; QT_END_NAMESPACE diff --git a/qtbase/src/gui/kernel/qplatformtheme.cpp b/qtbase/src/gui/kernel/qplatformtheme.cpp index 71521c0339..2325873245 100644 --- a/qtbase/src/gui/kernel/qplatformtheme.cpp +++ b/qtbase/src/gui/kernel/qplatformtheme.cpp @@ -163,6 +163,8 @@ QT_BEGIN_NAMESPACE \value ShowShortcutsInContextMenus (bool) Whether to display shortcut key sequences in context menus. + \value ButtonPressKeys (QList) A list of keys that can be used to press buttons via keyboard input. + \sa themeHint(), QStyle::pixelMetric() */ @@ -563,6 +565,8 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint) } case MouseQuickSelectionThreshold: return QVariant(10); + case ButtonPressKeys: + return QVariant::fromValue(QList({ Qt::Key_Space, Qt::Key_Select })); } return QVariant(); } diff --git a/qtbase/src/gui/kernel/qplatformtheme.h b/qtbase/src/gui/kernel/qplatformtheme.h index 3185fc4541..7e6c9d5740 100644 --- a/qtbase/src/gui/kernel/qplatformtheme.h +++ b/qtbase/src/gui/kernel/qplatformtheme.h @@ -120,7 +120,8 @@ public: TouchDoubleTapDistance, ShowShortcutsInContextMenus, IconFallbackSearchPaths, - MouseQuickSelectionThreshold + MouseQuickSelectionThreshold, + ButtonPressKeys }; enum DialogType { diff --git a/qtbase/src/gui/kernel/qscreen.cpp b/qtbase/src/gui/kernel/qscreen.cpp index 990272b0c2..d371dd60ab 100644 --- a/qtbase/src/gui/kernel/qscreen.cpp +++ b/qtbase/src/gui/kernel/qscreen.cpp @@ -77,6 +77,12 @@ QScreen::QScreen(QPlatformScreen *screen) d->setPlatformScreen(screen); } +void QScreenPrivate::updateLogicalDpi() +{ + logicalDpi = QPlatformScreen::overrideDpi(platformScreen->logicalDpi()); + updateGeometriesWithSignals(); // updates geometries based on scale factor +} + void QScreenPrivate::updateGeometriesWithSignals() { const QRect oldGeometry = geometry; diff --git a/qtbase/src/gui/kernel/qscreen_p.h b/qtbase/src/gui/kernel/qscreen_p.h index 7da542c25e..e50fc3190b 100644 --- a/qtbase/src/gui/kernel/qscreen_p.h +++ b/qtbase/src/gui/kernel/qscreen_p.h @@ -70,6 +70,7 @@ public: geometry = platformScreen->deviceIndependentGeometry(); availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(), QHighDpiScaling::factor(platformScreen), geometry.topLeft()); } + void updateLogicalDpi(); void updatePrimaryOrientation(); void updateGeometriesWithSignals(); diff --git a/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp b/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp index bb0d8e4ee7..b98fcc61e7 100644 --- a/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp +++ b/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp @@ -56,7 +56,7 @@ QShapedPixmapWindow::QShapedPixmapWindow(QScreen *screen) QSurfaceFormat format; format.setAlphaBufferSize(8); setFormat(format); - setFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint + setFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::WindowTransparentForInput | Qt::WindowDoesNotAcceptFocus); } diff --git a/qtbase/src/gui/opengl/qopengltexture.cpp b/qtbase/src/gui/opengl/qopengltexture.cpp index 5490ad8025..afd3d8dbe6 100644 --- a/qtbase/src/gui/opengl/qopengltexture.cpp +++ b/qtbase/src/gui/opengl/qopengltexture.cpp @@ -3725,6 +3725,12 @@ void QOpenGLTexture::setData(const QImage& image, MipMapGeneration genMipMaps) return; } + QImage glImage = image.convertToFormat(QImage::Format_RGBA8888); + if (glImage.isNull()) { + qWarning("QOpenGLTexture::setData() failed to convert image"); + return; + } + if (context->isOpenGLES() && context->format().majorVersion() < 3) setFormat(QOpenGLTexture::RGBAFormat); else @@ -3735,7 +3741,6 @@ void QOpenGLTexture::setData(const QImage& image, MipMapGeneration genMipMaps) allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); // Upload pixel data and generate mipmaps - QImage glImage = image.convertToFormat(QImage::Format_RGBA8888); QOpenGLPixelTransferOptions uploadOptions; uploadOptions.setAlignment(1); setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, glImage.constBits(), &uploadOptions); diff --git a/qtbase/src/gui/painting/qcolortrclut_p.h b/qtbase/src/gui/painting/qcolortrclut_p.h index 76a6a60803..24fd522e6c 100644 --- a/qtbase/src/gui/painting/qcolortrclut_p.h +++ b/qtbase/src/gui/painting/qcolortrclut_p.h @@ -118,6 +118,7 @@ public: return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257); #endif } + QRgba64 toLinear64(QRgba64) const = delete; QRgb toLinear(QRgb rgb32) const { diff --git a/qtbase/src/gui/painting/qdrawhelper.cpp b/qtbase/src/gui/painting/qdrawhelper.cpp index a61793508a..5ba2d277b7 100644 --- a/qtbase/src/gui/painting/qdrawhelper.cpp +++ b/qtbase/src/gui/painting/qdrawhelper.cpp @@ -6091,7 +6091,7 @@ static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile) { // Do a gammacorrected RGB alphablend... - const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(dst) : dst; + const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(dst) : dst; QRgba64 blend = rgbBlend(dlinear, slinear, coverage); diff --git a/qtbase/src/gui/painting/qfixed_p.h b/qtbase/src/gui/painting/qfixed_p.h index 846592881c..57d750a4b3 100644 --- a/qtbase/src/gui/painting/qfixed_p.h +++ b/qtbase/src/gui/painting/qfixed_p.h @@ -54,6 +54,7 @@ #include #include "QtCore/qdebug.h" #include "QtCore/qpoint.h" +#include #include "QtCore/qsize.h" QT_BEGIN_NAMESPACE @@ -182,6 +183,14 @@ Q_DECL_CONSTEXPR inline bool operator<(int i, const QFixed &f) { return i * 64 < Q_DECL_CONSTEXPR inline bool operator>(const QFixed &f, int i) { return f.value() > i * 64; } Q_DECL_CONSTEXPR inline bool operator>(int i, const QFixed &f) { return i * 64 > f.value(); } +inline bool qAddOverflow(QFixed v1, QFixed v2, QFixed *r) +{ + int val; + bool result = add_overflow(v1.value(), v2.value(), &val); + r->setValue(val); + return result; +} + #ifndef QT_NO_DEBUG_STREAM inline QDebug &operator<<(QDebug &dbg, const QFixed &f) { return dbg << f.toReal(); } diff --git a/qtbase/src/gui/painting/qpainterpath.cpp b/qtbase/src/gui/painting/qpainterpath.cpp index f9544a3241..d80fafeaf1 100644 --- a/qtbase/src/gui/painting/qpainterpath.cpp +++ b/qtbase/src/gui/painting/qpainterpath.cpp @@ -1253,7 +1253,7 @@ void QPainterPath::addText(const QPointF &point, const QFont &f, const QString & if (si.analysis.flags < QScriptAnalysis::TabOrObject) { QGlyphLayout glyphs = eng->shapedGlyphs(&si); - QFontEngine *fe = f.d->engineForScript(si.analysis.script); + QFontEngine *fe = eng->fontEngine(si); Q_ASSERT(fe); fe->addOutlineToPath(x, y, glyphs, this, si.analysis.bidiLevel % 2 diff --git a/qtbase/src/gui/painting/qpdf.cpp b/qtbase/src/gui/painting/qpdf.cpp index 3066744f1b..2c8d3c3b53 100644 --- a/qtbase/src/gui/painting/qpdf.cpp +++ b/qtbase/src/gui/painting/qpdf.cpp @@ -2760,6 +2760,8 @@ int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, return gradientBrush(brush, matrix, gStateObject); } + matrix = brush.transform() * matrix; + if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0) *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity), qRound(pen.color().alpha() * opacity)); diff --git a/qtbase/src/gui/painting/qt_attribution.json b/qtbase/src/gui/painting/qt_attribution.json index 7b16e8c211..0e87d30aa2 100644 --- a/qtbase/src/gui/painting/qt_attribution.json +++ b/qtbase/src/gui/painting/qt_attribution.json @@ -10,7 +10,7 @@ "Homepage": "http://www.freetype.org", "License": "Freetype Project License or GNU General Public License v2.0 only", "LicenseId": "FTL or GPL-2.0", - "LicenseFile": "../../3rdparty/freetype/docs/LICENSE.TXT", + "LicenseFile": "../../3rdparty/freetype/LICENSE.TXT", "Copyright": "Copyright 2000-2016 by David Turner, Robert Wilhelm, and Werner Lemberg." }, { diff --git a/qtbase/src/gui/rhi/qshader_p_p.h b/qtbase/src/gui/rhi/qshader_p_p.h index ec9d25971f..4a5a7a6d51 100644 --- a/qtbase/src/gui/rhi/qshader_p_p.h +++ b/qtbase/src/gui/rhi/qshader_p_p.h @@ -68,13 +68,13 @@ struct Q_GUI_EXPORT QShaderPrivate { } - QShaderPrivate(const QShaderPrivate *other) + QShaderPrivate(const QShaderPrivate &other) : ref(1), - qsbVersion(other->qsbVersion), - stage(other->stage), - desc(other->desc), - shaders(other->shaders), - bindings(other->bindings) + qsbVersion(other.qsbVersion), + stage(other.stage), + desc(other.desc), + shaders(other.shaders), + bindings(other.bindings) { } diff --git a/qtbase/src/gui/rhi/qshaderdescription_p_p.h b/qtbase/src/gui/rhi/qshaderdescription_p_p.h index ec2b0b6b4c..3da33a8a2b 100644 --- a/qtbase/src/gui/rhi/qshaderdescription_p_p.h +++ b/qtbase/src/gui/rhi/qshaderdescription_p_p.h @@ -63,16 +63,16 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate localSize[0] = localSize[1] = localSize[2] = 0; } - QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other) + QShaderDescriptionPrivate(const QShaderDescriptionPrivate &other) : ref(1), - inVars(other->inVars), - outVars(other->outVars), - uniformBlocks(other->uniformBlocks), - pushConstantBlocks(other->pushConstantBlocks), - storageBlocks(other->storageBlocks), - combinedImageSamplers(other->combinedImageSamplers), - storageImages(other->storageImages), - localSize(other->localSize) + inVars(other.inVars), + outVars(other.outVars), + uniformBlocks(other.uniformBlocks), + pushConstantBlocks(other.pushConstantBlocks), + storageBlocks(other.storageBlocks), + combinedImageSamplers(other.combinedImageSamplers), + storageImages(other.storageImages), + localSize(other.localSize) { } diff --git a/qtbase/src/gui/text/qfontdatabase.cpp b/qtbase/src/gui/text/qfontdatabase.cpp index 2011f935a9..7aa6228948 100644 --- a/qtbase/src/gui/text/qfontdatabase.cpp +++ b/qtbase/src/gui/text/qfontdatabase.cpp @@ -983,7 +983,7 @@ QFontEngine *loadSingleEngine(int script, if (style->key.stretch != 0 && request.stretch != 0 && (request.styleName.isEmpty() || request.styleName != style->styleName)) { def.stretch = (request.stretch * 100 + style->key.stretch / 2) / style->key.stretch; - } else { + } else if (request.stretch == QFont::AnyStretch) { def.stretch = 100; } diff --git a/qtbase/src/gui/text/qtextengine.cpp b/qtbase/src/gui/text/qtextengine.cpp index 6336fadf74..a6c66e5d2d 100644 --- a/qtbase/src/gui/text/qtextengine.cpp +++ b/qtbase/src/gui/text/qtextengine.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. diff --git a/qtbase/src/gui/text/qtextlayout.cpp b/qtbase/src/gui/text/qtextlayout.cpp index d4f0a5efdc..f6c69ff4a2 100644 --- a/qtbase/src/gui/text/qtextlayout.cpp +++ b/qtbase/src/gui/text/qtextlayout.cpp @@ -1336,13 +1336,13 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition bool rightToLeft = d->isRightToLeft(); if (itm >= 0) { const QScriptItem &si = d->layoutData->items.at(itm); - if (si.ascent >= 0) + if (si.ascent > 0) base = si.ascent; - if (si.descent >= 0) + if (si.descent > 0) descent = si.descent; rightToLeft = si.analysis.bidiLevel % 2; } - qreal y = position.y() + (sl.y + sl.base() + sl.descent - base - descent).toReal(); + qreal y = position.y() + (sl.y + sl.base() - base).toReal(); bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing) && (p->transform().type() > QTransform::TxTranslate); if (toggleAntialiasing) @@ -2150,11 +2150,14 @@ found: eng->maxWidth = qMax(eng->maxWidth, line.textWidth); } else { eng->minWidth = qMax(eng->minWidth, lbh.minw); - eng->maxWidth += line.textWidth; + if (qAddOverflow(eng->maxWidth, line.textWidth, &eng->maxWidth)) + eng->maxWidth = QFIXED_MAX; } - if (line.textWidth > 0 && item < eng->layoutData->items.size()) - eng->maxWidth += lbh.spaceData.textWidth; + if (line.textWidth > 0 && item < eng->layoutData->items.size()) { + if (qAddOverflow(eng->maxWidth, lbh.spaceData.textWidth, &eng->maxWidth)) + eng->maxWidth = QFIXED_MAX; + } line.textWidth += trailingSpace; if (lbh.spaceData.length) { diff --git a/qtbase/src/network/access/qhsts.cpp b/qtbase/src/network/access/qhsts.cpp index 0cef0ad3dc..be7ef7ff58 100644 --- a/qtbase/src/network/access/qhsts.cpp +++ b/qtbase/src/network/access/qhsts.cpp @@ -364,8 +364,8 @@ quoted-pair = "\" CHAR bool QHstsHeaderParser::parse(const QList> &headers) { for (const auto &h : headers) { - // We use '==' since header name was already 'trimmed' for us: - if (h.first == "Strict-Transport-Security") { + // We compare directly because header name was already 'trimmed' for us: + if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) { header = h.second; // RFC6797, 8.1: // diff --git a/qtbase/src/network/access/qnetworkreply.cpp b/qtbase/src/network/access/qnetworkreply.cpp index 30e4e290ea..dadea58080 100644 --- a/qtbase/src/network/access/qnetworkreply.cpp +++ b/qtbase/src/network/access/qnetworkreply.cpp @@ -76,7 +76,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() itself. QNetworkReply is a sequential-access QIODevice, which means that - once data is read from the object, it no longer kept by the + once data is read from the object, it is no longer kept by the device. It is therefore the application's responsibility to keep this data if it needs to. Whenever more data is received from the network and processed, the readyRead() signal is emitted. @@ -344,7 +344,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() processing. After this signal is emitted, there will be no more updates to the reply's data or metadata. - Unless close() or abort() have been called, the reply will be still be opened + Unless close() or abort() have been called, the reply will still be opened for reading, so the data can be retrieved by calls to read() or readAll(). In particular, if no calls to read() were made as a result of readyRead(), a call to readAll() will retrieve the full diff --git a/qtbase/src/network/access/qnetworkreplyfileimpl.cpp b/qtbase/src/network/access/qnetworkreplyfileimpl.cpp index b6be93147a..6e69b4c4d3 100644 --- a/qtbase/src/network/access/qnetworkreplyfileimpl.cpp +++ b/qtbase/src/network/access/qnetworkreplyfileimpl.cpp @@ -89,9 +89,10 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con // we handle only local files QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()); setError(QNetworkReply::ProtocolInvalidOperationError, msg); + setFinished(true); // We're finished, will emit finished() after ctor is done. QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError)); - fileOpenFinished(false); + QMetaObject::invokeMethod(this, [this](){ fileOpenFinished(false); }, Qt::QueuedConnection); return; } #endif diff --git a/qtbase/src/network/access/qnetworkreplyfileimpl_p.h b/qtbase/src/network/access/qnetworkreplyfileimpl_p.h index 48d82abd3f..4bfbc3f7d1 100644 --- a/qtbase/src/network/access/qnetworkreplyfileimpl_p.h +++ b/qtbase/src/network/access/qnetworkreplyfileimpl_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QNETWORKREPLYFILEIMPL_H -#define QNETWORKREPLYFILEIMPL_H +#ifndef QNETWORKREPLYFILEIMPL_P_H +#define QNETWORKREPLYFILEIMPL_P_H // // W A R N I N G @@ -99,4 +99,4 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders) -#endif // QNETWORKREPLYFILEIMPL_H +#endif // QNETWORKREPLYFILEIMPL_P_H diff --git a/qtbase/src/network/configure.json b/qtbase/src/network/configure.json index 271ff164ac..ffba2d1eea 100644 --- a/qtbase/src/network/configure.json +++ b/qtbase/src/network/configure.json @@ -53,7 +53,7 @@ }, "headers": "proxy.h", "sources": [ - "-lproxy" + { "type": "pkgConfig", "args": "libproxy-1.0" } ] }, "openssl_headers": { diff --git a/qtbase/src/network/kernel/qdnslookup_unix.cpp b/qtbase/src/network/kernel/qdnslookup_unix.cpp index 12b40fc35d..99e999d436 100644 --- a/qtbase/src/network/kernel/qdnslookup_unix.cpp +++ b/qtbase/src/network/kernel/qdnslookup_unix.cpp @@ -227,7 +227,6 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN // responseLength in case of error, we still can extract the // exact error code from the response. HEADER *header = (HEADER*)response; - const int answerCount = ntohs(header->ancount); switch (header->rcode) { case NOERROR: break; @@ -260,18 +259,31 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN return; } - // Skip the query host, type (2 bytes) and class (2 bytes). char host[PACKETSZ], answer[PACKETSZ]; unsigned char *p = response + sizeof(HEADER); - int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); - if (status < 0) { + int status; + + if (ntohs(header->qdcount) == 1) { + // Skip the query host, type (2 bytes) and class (2 bytes). + status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); + if (status < 0) { + reply->error = QDnsLookup::InvalidReplyError; + reply->errorString = tr("Could not expand domain name"); + return; + } + if ((p - response) + status + 4 >= responseLength) + header->qdcount = 0xffff; // invalid reply below + else + p += status + 4; + } + if (ntohs(header->qdcount) > 1) { reply->error = QDnsLookup::InvalidReplyError; - reply->errorString = tr("Could not expand domain name"); + reply->errorString = tr("Invalid reply received"); return; } - p += status + 4; // Extract results. + const int answerCount = ntohs(header->ancount); int answerIndex = 0; while ((p < response + responseLength) && (answerIndex < answerCount)) { status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); @@ -283,6 +295,11 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN const QString name = QUrl::fromAce(host); p += status; + + if ((p - response) + 10 > responseLength) { + // probably just a truncated reply, return what we have + return; + } const quint16 type = (p[0] << 8) | p[1]; p += 2; // RR type p += 2; // RR class @@ -290,6 +307,8 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN p += 4; const quint16 size = (p[0] << 8) | p[1]; p += 2; + if ((p - response) + size > responseLength) + return; // truncated if (type == QDnsLookup::A) { if (size != 4) { diff --git a/qtbase/src/network/ssl/qsslcontext_openssl.cpp b/qtbase/src/network/ssl/qsslcontext_openssl.cpp index c9f202f573..c992da9d8e 100644 --- a/qtbase/src/network/ssl/qsslcontext_openssl.cpp +++ b/qtbase/src/network/ssl/qsslcontext_openssl.cpp @@ -409,7 +409,7 @@ init_context: break; case QSsl::DtlsV1_0OrLater: minVersion = DTLS1_VERSION; - maxVersion = DTLS_MAX_VERSION; + maxVersion = 0; break; case QSsl::DtlsV1_2: minVersion = DTLS1_2_VERSION; @@ -417,7 +417,7 @@ init_context: break; case QSsl::DtlsV1_2OrLater: minVersion = DTLS1_2_VERSION; - maxVersion = DTLS_MAX_VERSION; + maxVersion = 0; break; case QSsl::TlsV1_3OrLater: #ifdef TLS1_3_VERSION @@ -455,7 +455,7 @@ init_context: } // Enable bug workarounds. - long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); + qssloptions options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); q_SSL_CTX_set_options(sslContext->ctx, options); // Tell OpenSSL to release memory early diff --git a/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp index aaf8741130..b58935372c 100644 --- a/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp +++ b/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp @@ -59,57 +59,6 @@ QT_BEGIN_NAMESPACE -#ifdef OPENSSL_NO_DEPRECATED_3_0 - -static int q_DH_check(DH *dh, int *status) -{ - // DH_check was first deprecated in OpenSSL 3.0.0, as low-level - // API; the EVP_PKEY family of functions was advised as an alternative. - // As of now EVP_PKEY_params_check ends up calling ... DH_check, - // which is good enough. - - Q_ASSERT(dh); - Q_ASSERT(status); - - EVP_PKEY *key = q_EVP_PKEY_new(); - if (!key) { - qCWarning(lcSsl, "EVP_PKEY_new failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - const auto keyDeleter = qScopeGuard([key](){ - q_EVP_PKEY_free(key); - }); - if (!q_EVP_PKEY_set1_DH(key, dh)) { - qCWarning(lcSsl, "EVP_PKEY_set1_DH failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - - EVP_PKEY_CTX *keyCtx = q_EVP_PKEY_CTX_new(key, nullptr); - if (!keyCtx) { - qCWarning(lcSsl, "EVP_PKEY_CTX_new failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - const auto ctxDeleter = qScopeGuard([keyCtx]{ - q_EVP_PKEY_CTX_free(keyCtx); - }); - - const int result = q_EVP_PKEY_param_check(keyCtx); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - // Note: unlike DH_check, we cannot obtain the 'status', - // if the 'result' is 0 (actually the result is 1 only - // if this 'status' was 0). We could probably check the - // errors from the error queue, but it's not needed anyway - // - see the 'isSafeDH' below, how it returns immediately - // on 0. - Q_UNUSED(status) - - return result; -} -#endif // OPENSSL_NO_DEPRECATED_3_0 - static bool isSafeDH(DH *dh) { int status = 0; diff --git a/qtbase/src/network/ssl/qsslsocket.cpp b/qtbase/src/network/ssl/qsslsocket.cpp index 5bb6e7ee4a..2a0b3a4f1d 100644 --- a/qtbase/src/network/ssl/qsslsocket.cpp +++ b/qtbase/src/network/ssl/qsslsocket.cpp @@ -2221,6 +2221,10 @@ QSslSocketPrivate::QSslSocketPrivate() , flushTriggered(false) { QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); + // If the global configuration doesn't allow root certificates to be loaded + // on demand then we have to disable it for this socket as well. + if (!configuration.allowRootCertOnDemandLoading) + allowRootCertOnDemandLoading = false; } /*! @@ -2470,6 +2474,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri ptr->sessionProtocol = global->sessionProtocol; ptr->ciphers = global->ciphers; ptr->caCertificates = global->caCertificates; + ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading; ptr->protocol = global->protocol; ptr->peerVerifyMode = global->peerVerifyMode; ptr->peerVerifyDepth = global->peerVerifyDepth; diff --git a/qtbase/src/network/ssl/qsslsocket_mac.cpp b/qtbase/src/network/ssl/qsslsocket_mac.cpp index 77e847e972..e38a5e75de 100644 --- a/qtbase/src/network/ssl/qsslsocket_mac.cpp +++ b/qtbase/src/network/ssl/qsslsocket_mac.cpp @@ -468,6 +468,7 @@ void QSslSocketBackendPrivate::disconnectFromHost() if (context) { if (!shutdown) { SSLClose(context); + context.reset(nullptr); shutdown = true; } } diff --git a/qtbase/src/network/ssl/qsslsocket_openssl.cpp b/qtbase/src/network/ssl/qsslsocket_openssl.cpp index 37fad2a68f..8f6858c867 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl.cpp +++ b/qtbase/src/network/ssl/qsslsocket_openssl.cpp @@ -550,9 +550,9 @@ static void q_loadCiphersForConnection(SSL *connection, QList &ciphe // Defined in qsslsocket.cpp void q_setDefaultDtlsCiphers(const QList &ciphers); -long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) +qssloptions QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) { - long options; + qssloptions options; if (protocol == QSsl::TlsV1SslV3) options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3; else if (protocol == QSsl::SecureProtocols) diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_p.h b/qtbase/src/network/ssl/qsslsocket_openssl_p.h index 4103de23e8..5547589256 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_p.h +++ b/qtbase/src/network/ssl/qsslsocket_openssl_p.h @@ -107,6 +107,12 @@ QT_BEGIN_NAMESPACE +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +typedef uint64_t qssloptions; +#else +typedef unsigned long qssloptions; +#endif + struct QSslErrorEntry { int code; int depth; @@ -171,7 +177,7 @@ public: QVector ocspErrors; QByteArray ocspResponseDer; - Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); + Q_AUTOTEST_EXPORT static qssloptions setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); static QList STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); static QList verify(const QList &certificateChain, const QString &hostName); diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp index e53fb279f0..459ccd0b19 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -148,7 +148,6 @@ DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, return) DEFINEFUNC2(EVP_PKEY_CTX *, EVP_PKEY_CTX_new, EVP_PKEY *pkey, pkey, ENGINE *e, e, return nullptr, return) DEFINEFUNC(int, EVP_PKEY_param_check, EVP_PKEY_CTX *ctx, ctx, return 0, return) DEFINEFUNC(void, EVP_PKEY_CTX_free, EVP_PKEY_CTX *ctx, ctx, return, return) -DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return) DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return) DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return) DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return) @@ -158,7 +157,7 @@ DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMM DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG) DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return) DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return) -DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return) +DEFINEFUNC2(qssloptions, SSL_CTX_set_options, SSL_CTX *ctx, ctx, qssloptions op, op, return 0, return) DEFINEFUNC(int, SSL_CTX_get_security_level, const SSL_CTX *ctx, ctx, return -1, return) DEFINEFUNC2(void, SSL_CTX_set_security_level, SSL_CTX *ctx, ctx, int level, level, return, return) #ifdef TLS1_3_VERSION @@ -371,7 +370,15 @@ DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return) DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return) DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return) + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +DEFINEFUNC(X509 *, SSL_get1_peer_certificate, SSL *a, a, return nullptr, return) +DEFINEFUNC(int, EVP_PKEY_get_base_id, const EVP_PKEY *pkey, pkey, return -1, return) +#else DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return) +DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return) +#endif // OPENSSL_VERSION_MAJOR >= 3 + DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return) DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return) @@ -492,9 +499,7 @@ DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG) DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return) DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return) -#ifndef OPENSSL_NO_DEPRECATED_3_0 DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return) -#endif // OPENSSL_NO_DEPRECATED_3_0 DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return) #ifndef OPENSSL_NO_EC @@ -868,7 +873,6 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EVP_PKEY_CTX_new) RESOLVEFUNC(EVP_PKEY_param_check) RESOLVEFUNC(EVP_PKEY_CTX_free) - RESOLVEFUNC(EVP_PKEY_base_id) RESOLVEFUNC(RSA_bits) RESOLVEFUNC(OPENSSL_sk_new_null) RESOLVEFUNC(OPENSSL_sk_push) @@ -1107,7 +1111,15 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_version) RESOLVEFUNC(SSL_get_error) RESOLVEFUNC(SSL_get_peer_cert_chain) + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + RESOLVEFUNC(SSL_get1_peer_certificate) + RESOLVEFUNC(EVP_PKEY_get_base_id) +#else RESOLVEFUNC(SSL_get_peer_certificate) + RESOLVEFUNC(EVP_PKEY_base_id) +#endif // OPENSSL_VERSION_MAJOR >= 3 + RESOLVEFUNC(SSL_get_verify_result) RESOLVEFUNC(SSL_new) RESOLVEFUNC(SSL_get_SSL_CTX) @@ -1206,9 +1218,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(DH_free) RESOLVEFUNC(d2i_DHparams) RESOLVEFUNC(i2d_DHparams) -#ifndef OPENSSL_NO_DEPRECATED_3_0 RESOLVEFUNC(DH_check) -#endif // OPENSSL_NO_DEPRECATED_3_0 RESOLVEFUNC(BN_bin2bn) #ifndef OPENSSL_NO_EC diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h b/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h index 95e8897a3b..27aeffa18a 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -237,7 +237,6 @@ Q_AUTOTEST_EXPORT int q_EVP_PKEY_up_ref(EVP_PKEY *a); EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx); int q_EVP_PKEY_param_check(EVP_PKEY_CTX *ctx); -int q_EVP_PKEY_base_id(EVP_PKEY *a); int q_RSA_bits(RSA *a); Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a); Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *)); @@ -246,7 +245,7 @@ Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data); Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a); Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b); int q_SSL_session_reused(SSL *a); -unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op); +qssloptions q_SSL_CTX_set_options(SSL_CTX *ctx, qssloptions op); int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen); size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen); @@ -510,7 +509,6 @@ const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a); int q_SSL_version(const SSL *a); int q_SSL_get_error(SSL *a, int b); STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); -X509 *q_SSL_get_peer_certificate(SSL *a); long q_SSL_get_verify_result(const SSL *a); SSL *q_SSL_new(SSL_CTX *a); SSL_CTX *q_SSL_get_SSL_CTX(SSL *a); @@ -582,10 +580,7 @@ DH *q_DH_new(); void q_DH_free(DH *dh); DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length); int q_i2d_DHparams(DH *a, unsigned char **p); - -#ifndef OPENSSL_NO_DEPRECATED_3_0 int q_DH_check(DH *dh, int *codes); -#endif // OPENSSL_NO_DEPRECATED_3_0 BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); #define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh) @@ -754,6 +749,17 @@ void q_CRYPTO_free(void *str, const char *file, int line); int q_SSL_CTX_get_security_level(const SSL_CTX *ctx); void q_SSL_CTX_set_security_level(SSL_CTX *ctx, int level); +// Here we have the ones that make difference between OpenSSL pre/post v3: +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +X509 *q_SSL_get1_peer_certificate(SSL *a); +#define q_SSL_get_peer_certificate q_SSL_get1_peer_certificate +int q_EVP_PKEY_get_base_id(const EVP_PKEY *pkey); +#define q_EVP_PKEY_base_id q_EVP_PKEY_get_base_id +#else +X509 *q_SSL_get_peer_certificate(SSL *a); +int q_EVP_PKEY_base_id(EVP_PKEY *a); +#endif // OPENSSL_VERSION_MAJOR >= 3 + QT_END_NAMESPACE #endif diff --git a/qtbase/src/network/ssl/qsslsocket_schannel.cpp b/qtbase/src/network/ssl/qsslsocket_schannel.cpp index c956ce3c2b..d1b23af29b 100644 --- a/qtbase/src/network/ssl/qsslsocket_schannel.cpp +++ b/qtbase/src/network/ssl/qsslsocket_schannel.cpp @@ -1880,6 +1880,28 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext) if (configuration.peerVerifyDepth > 0 && DWORD(configuration.peerVerifyDepth) < verifyDepth) verifyDepth = DWORD(configuration.peerVerifyDepth); + const auto &caCertificates = q->sslConfiguration().caCertificates(); + + if (!rootCertOnDemandLoadingAllowed() + && !(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) + && (q->peerVerifyMode() == QSslSocket::VerifyPeer + || (isClient && q->peerVerifyMode() == QSslSocket::AutoVerifyPeer))) { + // When verifying a peer Windows "helpfully" builds a chain that + // may include roots from the system store. But we don't want that if + // the user has set their own CA certificates. + // Since Windows claims this is not a partial chain the root is included + // and we have to check that it is one of our configured CAs. + CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1]; + QSslCertificate certificate = getCertificateFromChainElement(element); + if (!caCertificates.contains(certificate)) { + auto error = QSslError(QSslError::CertificateUntrusted, certificate); + sslErrors += error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + for (DWORD i = 0; i < verifyDepth; i++) { CERT_CHAIN_ELEMENT *element = chain->rgpElement[i]; QSslCertificate certificate = getCertificateFromChainElement(element); diff --git a/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h b/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h index bf37d07fd8..dbd42fb799 100644 --- a/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h +++ b/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h @@ -61,7 +61,11 @@ # if !defined(Q_OS_INTEGRITY) # define WIN_INTERFACE_CUSTOM // NV # endif // Q_OS_INTEGRITY -#endif // QT_EGL_NO_X11 +#else // QT_EGL_NO_X11 +// If one has an eglplatform.h with https://github.com/KhronosGroup/EGL-Registry/pull/130 +// that needs USE_X11 to be defined. +# define USE_X11 +#endif #ifdef QT_EGL_WAYLAND # define WAYLAND // NV diff --git a/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 159b490ce0..00aa80cd58 100644 --- a/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -567,6 +567,8 @@ void QFontconfigDatabase::populateFontDatabase() fonts = FcFontList(nullptr, pattern, os); FcObjectSetDestroy(os); FcPatternDestroy(pattern); + if (!fonts) + return; } for (int i = 0; i < fonts->nfont; i++) diff --git a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp index b713c19447..7cb9a8c79a 100644 --- a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp +++ b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp @@ -95,6 +95,7 @@ static constexpr const auto KeyTbl = qMakeArray( Xkb2Qt, Xkb2Qt, Xkb2Qt, + Xkb2Qt, Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq @@ -471,7 +472,7 @@ QVector QXkbCommon::toKeysym(QKeyEvent *event) } else if (event->modifiers() & Qt::KeypadModifier) { if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9) keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0)); - } else if (isLatin(qtKey) && event->text().isUpper()) { + } else if (isLatin1(qtKey) && event->text().isUpper()) { keysyms.append(qtKey); } @@ -523,7 +524,7 @@ int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifie // With standard shortcuts we should prefer a latin character, this is // for checks like "some qkeyevent == QKeySequence::Copy" to work even // when using for example 'russian' keyboard layout. - if (!QXkbCommon::isLatin(keysym)) { + if (!QXkbCommon::isLatin1(keysym)) { xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, code); if (latinKeysym != XKB_KEY_NoSymbol) keysym = latinKeysym; @@ -546,7 +547,7 @@ static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers mod } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { // numeric keypad keys qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0); - } else if (QXkbCommon::isLatin(keysym)) { + } else if (QXkbCommon::isLatin1(keysym)) { qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym); } else { // check if we have a direct mapping @@ -678,7 +679,7 @@ QList QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event, Qt::KeyboardModifiers neededMods = ModsTbl[i]; if ((modifiers & neededMods) == neededMods) { if (i == 8) { - if (isLatin(baseQtKey)) + if (isLatin1(baseQtKey)) continue; // add a latin key as a fall back key sym = lookupLatinKeysym(state, keycode); @@ -733,7 +734,7 @@ void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap) for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) { for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &keysyms); - if (keysyms && isLatin(keysyms[0])) + if (keysyms && isLatin1(keysyms[0])) nrLatinKeys++; if (nrLatinKeys > 10) // arbitrarily chosen threshold return; @@ -766,7 +767,7 @@ xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keyco xkb_level_index_t level = xkb_state_key_get_level(state, keycode, layout); if (xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms) != 1) continue; - if (isLatin(syms[0])) { + if (isLatin1(syms[0])) { sym = syms[0]; break; } diff --git a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h index 561eae03db..8389bd8f5a 100644 --- a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h +++ b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h @@ -94,8 +94,8 @@ public: static void verifyHasLatinLayout(xkb_keymap *keymap); static xkb_keysym_t lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode); - static bool isLatin(xkb_keysym_t sym) { - return ((sym >= 'a' && sym <= 'z') || (sym >= 'A' && sym <= 'Z')); + static bool isLatin1(xkb_keysym_t sym) { + return sym <= 0xff; } static bool isKeypad(xkb_keysym_t sym) { return sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9; diff --git a/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp index 8cd7f9b368..406f84663f 100644 --- a/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp +++ b/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp @@ -318,7 +318,7 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, if (current < 0 && crtc_mode.clock != 0) { modes << crtc_mode; - current = mode.size() - 1; + current = modes.size() - 1; } if (configuration == OutputConfigCurrent) diff --git a/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index 9153fd20bb..255ea5e33e 100644 --- a/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ b/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp @@ -194,6 +194,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const " \n" " \n" " \n" + " \n" + " \n" + " \n" " \n" ); @@ -913,8 +916,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) } case QAccessible::NameChanged: { if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); + QAccessibleInterface *iface = event->accessibleInterface(); + if (!iface) { + qCDebug(lcAccessibilityAtspi, + "NameChanged event from invalid accessible."); + return; + } + + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments( + QLatin1String("accessible-name"), 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("PropertyChange"), args); } @@ -922,8 +934,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) } case QAccessible::DescriptionChanged: { if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments(QLatin1String("accessible-description"), 0, 0, variantForPath(path)); + QAccessibleInterface *iface = event->accessibleInterface(); + if (!iface) { + qCDebug(lcAccessibilityAtspi, + "DescriptionChanged event from invalid accessible."); + return; + } + + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments( + QLatin1String("accessible-description"), 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Description)))); sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("PropertyChange"), args); } @@ -1038,7 +1059,9 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) // Combo Box with AT-SPI likes to be special // It requires a name-change to update caches and then selection-changed QString path = pathForInterface(iface); - QVariantList args1 = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); + QVariantList args1 = packDBusSignalArguments( + QLatin1String("accessible-name"), 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("PropertyChange"), args1); QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0)))); @@ -1358,6 +1381,26 @@ void AtSpiAdaptor::registerApplication() delete registry; } +namespace { +QString accessibleIdForAccessible(QAccessibleInterface *accessible) +{ + QString result; + while (accessible) { + if (!result.isEmpty()) + result.prepend(QLatin1Char('.')); + if (auto obj = accessible->object()) { + const QString name = obj->objectName(); + if (!name.isEmpty()) + result.prepend(name); + else + result.prepend(QString::fromUtf8(obj->metaObject()->className())); + } + accessible = accessible->parent(); + } + return result; +} +} // namespace + // Accessible bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) { @@ -1441,6 +1484,9 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS children << ref; } connection.send(message.createReply(QVariant::fromValue(children))); + } else if (function == QLatin1String("GetAccessibleId")) { + sendReply(connection, message, + QVariant::fromValue(QDBusVariant(accessibleIdForAccessible(interface)))); } else { qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path(); return false; diff --git a/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp b/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp index 45ddc8e496..cc734abc63 100644 --- a/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp +++ b/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp @@ -69,6 +69,21 @@ QT_BEGIN_NAMESPACE DBusConnection::DBusConnection(QObject *parent) : QObject(parent), m_a11yConnection(QString()), m_enabled(false) { + // If the bus is explicitly set via env var it overrides everything else. + QByteArray addressEnv = qgetenv("AT_SPI_BUS_ADDRESS"); + if (!addressEnv.isEmpty()) { + // Only connect on next loop run, connections to our enabled signal are + // only established after the ctor returns. + QMetaObject::invokeMethod( + this, + [this, addressEnv] { + m_enabled = true; + connectA11yBus(QString::fromLocal8Bit(addressEnv)); + }, + Qt::QueuedConnection); + return; + } + // Start monitoring if "org.a11y.Bus" is registered as DBus service. QDBusConnection c = QDBusConnection::sessionBus(); if (!c.isConnected()) { diff --git a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp index 1a3cab275d..2abe039126 100644 --- a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp +++ b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp @@ -51,6 +51,9 @@ #include #include +#include +#include + #if QT_CONFIG(dbus) // These QtCore includes are needed for xdg-desktop-portal support #include @@ -58,6 +61,8 @@ #include #include +#include + #include #include #include @@ -142,6 +147,8 @@ static inline bool detectWebBrowser(const QByteArray &desktop, } if (desktop == QByteArray("KDE")) { + if (checkExecutable(QStringLiteral("kde-open5"), browser)) + return true; // Konqueror launcher if (checkExecutable(QStringLiteral("kfmclient"), browser)) { browser->append(QLatin1String(" exec")); @@ -296,8 +303,135 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) return QDBusConnection::sessionBus().call(message); } + +namespace { +struct XDGDesktopColor +{ + double r = 0; + double g = 0; + double b = 0; + + QColor toQColor() const + { + constexpr auto rgbMax = 255; + return { static_cast(r * rgbMax), static_cast(g * rgbMax), + static_cast(b * rgbMax) }; + } +}; + +const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) +{ + argument.beginStructure(); + argument >> myStruct.r >> myStruct.g >> myStruct.b; + argument.endStructure(); + return argument; +} + +class XdgDesktopPortalColorPicker : public QPlatformServiceColorPicker +{ + Q_OBJECT +public: + XdgDesktopPortalColorPicker(const QString &parentWindowId, QWindow *parent) + : QPlatformServiceColorPicker(parent), m_parentWindowId(parentWindowId) + { + } + + void pickColor() override + { + // DBus signature: + // PickColor (IN s parent_window, + // IN a{sv} options + // OUT o handle) + // Options: + // handle_token (s) - A string that will be used as the last element of the @handle. + + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.portal.Screenshot"), QStringLiteral("PickColor")); + message << m_parentWindowId << QVariantMap(); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + qWarning("DBus call to pick color failed: %s", + qPrintable(reply.error().message())); + Q_EMIT colorPicked({}); + } else { + QDBusConnection::sessionBus().connect( + QStringLiteral("org.freedesktop.portal.Desktop"), reply.value().path(), + QStringLiteral("org.freedesktop.portal.Request"), QStringLiteral("Response"), this, + // clang-format off + SLOT(gotColorResponse(uint,QVariantMap)) + // clang-format on + ); + } + }); + } + +private Q_SLOTS: + void gotColorResponse(uint result, const QVariantMap &map) + { + if (result != 0) + return; + XDGDesktopColor color{}; + map.value(QStringLiteral("color")).value() >> color; + Q_EMIT colorPicked(color.toQColor()); + deleteLater(); + } + +private: + const QString m_parentWindowId; +}; +} // namespace + #endif // QT_CONFIG(dbus) +QGenericUnixServices::QGenericUnixServices() +{ +#if QT_CONFIG(dbus) + if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get")); + message << QStringLiteral("org.freedesktop.portal.Screenshot") + << QStringLiteral("version"); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (!reply.isError() && reply.value().toUInt() >= 2) + m_hasScreenshotPortalWithColorPicking = true; + }); + +#endif +} + +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ +#if QT_CONFIG(dbus) + // Make double sure that we are in a wayland environment. In particular check + // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. + // Outside wayland we'll rather rely on other means than the XDG desktop portal. + if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") + || QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { + return new XdgDesktopPortalColorPicker(portalWindowIdentifier(parent), parent); + } + return nullptr; +#else + Q_UNUSED(parent); + return nullptr; +#endif +} + QByteArray QGenericUnixServices::desktopEnvironment() const { static const QByteArray result = detectDesktopEnvironment(); @@ -352,6 +486,8 @@ bool QGenericUnixServices::openDocument(const QUrl &url) } #else +QGenericUnixServices::QGenericUnixServices() = default; + QByteArray QGenericUnixServices::desktopEnvironment() const { return QByteArrayLiteral("UNKNOWN"); @@ -371,6 +507,30 @@ bool QGenericUnixServices::openDocument(const QUrl &url) return false; } +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + #endif // QT_NO_MULTIPROCESS +QString QGenericUnixServices::portalWindowIdentifier(QWindow *window) +{ + if (QGuiApplication::platformName() == QLatin1String("xcb")) + return QStringLiteral("x11:") + QString::number(window->winId(), 16); + return QString(); +} + +bool QGenericUnixServices::hasCapability(Capability capability) const +{ + switch (capability) { + case Capability::ColorPicking: + return m_hasScreenshotPortalWithColorPicking; + } + return false; +} + QT_END_NAMESPACE + +#include "qgenericunixservices.moc" diff --git a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h index 8ac3de6f03..30924e64bd 100644 --- a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h +++ b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h @@ -59,16 +59,21 @@ QT_BEGIN_NAMESPACE class QGenericUnixServices : public QPlatformServices { public: - QGenericUnixServices() {} + QGenericUnixServices(); QByteArray desktopEnvironment() const override; + bool hasCapability(Capability capability) const override; bool openUrl(const QUrl &url) override; bool openDocument(const QUrl &url) override; + QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr) override; + + virtual QString portalWindowIdentifier(QWindow *window); private: QString m_webBrowser; QString m_documentLauncher; + bool m_hasScreenshotPortalWithColorPicking = false; }; QT_END_NAMESPACE diff --git a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp index 09470bccc6..cc7c7d4d8a 100644 --- a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp +++ b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp @@ -69,6 +69,7 @@ const QString MenuBarPath = QLatin1String("/MenuBar"); */ QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &serviceName) : QObject(parent) + , m_serviceName(serviceName) , m_connection(serviceName.isNull() ? QDBusConnection::sessionBus() : QDBusConnection::connectToBus(QDBusConnection::SessionBus, serviceName)) , m_dbusWatcher(new QDBusServiceWatcher(StatusNotifierWatcherService, m_connection, QDBusServiceWatcher::WatchForRegistration, this)) @@ -83,6 +84,12 @@ QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &service #endif } +QDBusMenuConnection::~QDBusMenuConnection() +{ + if (!m_serviceName.isEmpty() && m_connection.isConnected()) + QDBusConnection::disconnectFromBus(m_serviceName); +} + void QDBusMenuConnection::dbusError(const QDBusError &error) { qWarning() << "QDBusTrayIcon encountered a D-Bus error:" << error; @@ -105,13 +112,7 @@ void QDBusMenuConnection::unregisterTrayIconMenu(QDBusTrayIcon *item) bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) { - bool success = connection().registerService(item->instanceId()); - if (!success) { - qWarning() << "failed to register service" << item->instanceId(); - return false; - } - - success = connection().registerObject(StatusNotifierItemPath, item); + bool success = connection().registerObject(StatusNotifierItemPath, item); if (!success) { unregisterTrayIcon(item); qWarning() << "failed to register" << item->instanceId() << StatusNotifierItemPath; @@ -126,21 +127,18 @@ bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) bool QDBusMenuConnection::registerTrayIconWithWatcher(QDBusTrayIcon *item) { + Q_UNUSED(item); QDBusMessage registerMethod = QDBusMessage::createMethodCall( StatusNotifierWatcherService, StatusNotifierWatcherPath, StatusNotifierWatcherService, QLatin1String("RegisterStatusNotifierItem")); - registerMethod.setArguments(QVariantList() << item->instanceId()); + registerMethod.setArguments(QVariantList() << m_connection.baseService()); return m_connection.callWithCallback(registerMethod, this, SIGNAL(trayIconRegistered()), SLOT(dbusError(QDBusError))); } -bool QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) +void QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) { unregisterTrayIconMenu(item); connection().unregisterObject(StatusNotifierItemPath); - bool success = connection().unregisterService(item->instanceId()); - if (!success) - qWarning() << "failed to unregister service" << item->instanceId(); - return success; } #endif // QT_NO_SYSTEMTRAYICON diff --git a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h index f484795fbb..97bdfabb85 100644 --- a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h +++ b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h @@ -70,6 +70,7 @@ class QDBusMenuConnection : public QObject public: QDBusMenuConnection(QObject *parent = nullptr, const QString &serviceName = QString()); + ~QDBusMenuConnection(); QDBusConnection connection() const { return m_connection; } QDBusServiceWatcher *dbusWatcher() const { return m_dbusWatcher; } bool isStatusNotifierHostRegistered() const { return m_statusNotifierHostRegistered; } @@ -78,7 +79,7 @@ public: void unregisterTrayIconMenu(QDBusTrayIcon *item); bool registerTrayIcon(QDBusTrayIcon *item); bool registerTrayIconWithWatcher(QDBusTrayIcon *item); - bool unregisterTrayIcon(QDBusTrayIcon *item); + void unregisterTrayIcon(QDBusTrayIcon *item); #endif // QT_NO_SYSTEMTRAYICON Q_SIGNALS: @@ -90,6 +91,7 @@ private Q_SLOTS: void dbusError(const QDBusError &error); private: + QString m_serviceName; QDBusConnection m_connection; QDBusServiceWatcher *m_dbusWatcher; bool m_statusNotifierHostRegistered; diff --git a/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp b/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp index cb1b39db64..6e01af052c 100644 --- a/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp +++ b/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp @@ -755,6 +755,9 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant(QChar(0x2022)); case QPlatformTheme::UiEffects: return QVariant(int(HoverEffect)); + case QPlatformTheme::ButtonPressKeys: + return QVariant::fromValue( + QList({ Qt::Key_Space, Qt::Key_Return, Qt::Key_Enter, Qt::Key_Select })); default: break; } diff --git a/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml b/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml index 9c67a38c57..30c326d06f 100644 --- a/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml +++ b/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml @@ -14,6 +14,12 @@ + + + + + + diff --git a/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h b/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h index 396a213aaa..31d5a71c41 100644 --- a/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h +++ b/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h @@ -112,6 +112,13 @@ public Q_SLOTS: // METHODS return asyncCallWithArgumentList(QLatin1String("SetCursorLocation"), argumentList); } + inline QDBusPendingReply<> SetCursorLocationRelative(int x, int y, int w, int h) + { + QList argumentList; + argumentList << QVariant::fromValue(x) << QVariant::fromValue(y) << QVariant::fromValue(w) << QVariant::fromValue(h); + return asyncCallWithArgumentList(QLatin1String("SetCursorLocationRelative"), argumentList); + } + inline QDBusPendingReply<> SetEngine(const QString &name) { QList argumentList; diff --git a/qtbase/src/plugins/platforms/eglfs/api/qeglfscursor.cpp b/qtbase/src/plugins/platforms/eglfs/api/qeglfscursor.cpp index b35935bb5b..a3d6df7d11 100644 --- a/qtbase/src/plugins/platforms/eglfs/api/qeglfscursor.cpp +++ b/qtbase/src/plugins/platforms/eglfs/api/qeglfscursor.cpp @@ -343,8 +343,7 @@ void QEglFSCursor::paintOnScreen() // screens are siblings of each other. When not enabled, the sibling list // only contains m_screen itself. for (QPlatformScreen *screen : m_screen->virtualSiblings()) { - if (screen->geometry().contains(cr.topLeft().toPoint() + m_cursor.hotSpot) - && QOpenGLContext::currentContext()->screen() == screen->screen()) + if (screen->geometry().contains(cr.topLeft().toPoint() + m_cursor.hotSpot)) { cr.translate(-screen->geometry().topLeft()); const QSize screenSize = screen->geometry().size(); @@ -468,11 +467,12 @@ void QEglFSCursor::draw(const QRectF &r) { StateSaver stateSaver; - QEglFSCursorData &gfx = static_cast(QOpenGLContext::currentContext()->handle())->cursorData; - if (!gfx.program) { - // one time initialization + // one time initialization + if (!QOpenGLFunctions::d_ptr) initializeOpenGLFunctions(); + QEglFSCursorData &gfx = static_cast(QOpenGLContext::currentContext()->handle())->cursorData; + if (!gfx.program) { createShaderPrograms(); if (!gfx.atlasTexture) { diff --git a/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp index 645a0ae2e9..3e0e406f1a 100644 --- a/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp +++ b/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp @@ -179,7 +179,7 @@ void QEglFSKmsEventReader::create(QEglFSKmsDevice *device) m_device = device; - qCDebug(qLcEglfsKmsDebug, "Initalizing event reader for device %p fd %d", + qCDebug(qLcEglfsKmsDebug, "Initializing event reader for device %p fd %d", m_device, m_device->fd()); m_thread = new QEglFSKmsEventReaderThread(m_device->fd()); diff --git a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp index 141fb68a23..d4294d425a 100644 --- a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp +++ b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -122,11 +122,13 @@ QOffscreenIntegration::QOffscreenIntegration() #endif m_services.reset(new QPlatformServices); - QWindowSystemInterface::handleScreenAdded(new QOffscreenScreen); + m_screen = new QOffscreenScreen; + QWindowSystemInterface::handleScreenAdded(m_screen); } QOffscreenIntegration::~QOffscreenIntegration() { + QWindowSystemInterface::handleScreenRemoved(m_screen); } void QOffscreenIntegration::initialize() diff --git a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h index 0ea90f6c2f..fe00fde07c 100644 --- a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h +++ b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h @@ -84,6 +84,7 @@ protected: #endif QScopedPointer m_inputContext; QScopedPointer m_services; + QPlatformScreen *m_screen; mutable QScopedPointer m_nativeInterface; }; diff --git a/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp b/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp index 1afa00cfc9..118af6ce73 100644 --- a/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp +++ b/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include diff --git a/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp b/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp index 780816605a..a769ddadbd 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp @@ -90,6 +90,8 @@ static const char *xcb_atomnames = { "_QT_CLOSE_CONNECTION\0" + "_QT_GET_TIMESTAMP\0" + "_MOTIF_WM_HINTS\0" "DTWM_IS_RUNNING\0" diff --git a/qtbase/src/plugins/platforms/xcb/qxcbatom.h b/qtbase/src/plugins/platforms/xcb/qxcbatom.h index 9cf93ec314..1ce6cca573 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbatom.h +++ b/qtbase/src/plugins/platforms/xcb/qxcbatom.h @@ -91,6 +91,8 @@ public: // Qt/XCB specific _QT_CLOSE_CONNECTION, + _QT_GET_TIMESTAMP, + _MOTIF_WM_HINTS, DTWM_IS_RUNNING, diff --git a/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp index 0eafb9b368..152ab2c52d 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -488,12 +488,12 @@ void QXcbConnection::printXcbError(const char *message, xcb_generic_error_t *err uint clamped_error_code = qMin(error->error_code, (sizeof(xcb_errors) / sizeof(xcb_errors[0])) - 1); uint clamped_major_code = qMin(error->major_code, (sizeof(xcb_protocol_request_codes) / sizeof(xcb_protocol_request_codes[0])) - 1); - qCWarning(lcQpaXcb, "%s: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d", - message, - int(error->error_code), xcb_errors[clamped_error_code], - int(error->sequence), int(error->resource_id), - int(error->major_code), xcb_protocol_request_codes[clamped_major_code], - int(error->minor_code)); + qCDebug(lcQpaXcb, "%s: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d", + message, + int(error->error_code), xcb_errors[clamped_error_code], + int(error->sequence), int(error->resource_id), + int(error->major_code), xcb_protocol_request_codes[clamped_major_code], + int(error->minor_code)); } static Qt::MouseButtons translateMouseButtons(int s) @@ -702,6 +702,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window); if (virtualDesktop) virtualDesktop->updateWorkArea(); + } else if (propertyNotify->atom == atom(QXcbAtom::_NET_SUPPORTED)) { + m_wmSupport->updateNetWMAtoms(); } else { HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); } @@ -798,8 +800,8 @@ xcb_timestamp_t QXcbConnection::getTimestamp() { // send a dummy event to myself to get the timestamp from X server. xcb_window_t window = rootWindow(); - xcb_atom_t dummyAtom = atom(QXcbAtom::CLIP_TEMPORARY); - xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, window, dummyAtom, + xcb_atom_t dummyAtom = atom(QXcbAtom::_QT_GET_TIMESTAMP); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, dummyAtom, XCB_ATOM_INTEGER, 32, 0, nullptr); connection()->flush(); @@ -831,8 +833,6 @@ xcb_timestamp_t QXcbConnection::getTimestamp() xcb_timestamp_t timestamp = pn->time; free(event); - xcb_delete_property(xcb_connection(), window, dummyAtom); - return timestamp; } diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp index 4210bf428e..0670b6ebce 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -300,7 +300,7 @@ QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c) #endif // !QT_NO_CURSOR QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) - : QXcbObject(conn), m_screen(screen), m_gtkCursorThemeInitialized(false) + : QXcbObject(conn), m_screen(screen), m_gtkCursorThemeInitialized(false), m_callbackForPropertyRegistered(false) { #if QT_CONFIG(cursor) // see NUM_BITMAPS in libXcursor/src/xcursorint.h @@ -343,7 +343,7 @@ QXcbCursor::~QXcbCursor() { xcb_connection_t *conn = xcb_connection(); - if (m_gtkCursorThemeInitialized) { + if (m_callbackForPropertyRegistered) { m_screen->xSettings()->removeCallbackForHandle(this); } @@ -562,8 +562,10 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) xcb_cursor_t cursor = XCB_NONE; #if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) - if (m_screen->xSettings()->initialized()) + if (m_screen->xSettings()->initialized()) { m_screen->xSettings()->registerCallbackForProperty("Gtk/CursorThemeName",cursorThemePropertyChanged,this); + m_callbackForPropertyRegistered = true; + } // Try Xcursor first if (cshape >= 0 && cshape <= Qt::LastCursor) { diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.h b/qtbase/src/plugins/platforms/xcb/qxcbcursor.h index 0b238823f0..82fb47e55d 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbcursor.h +++ b/qtbase/src/plugins/platforms/xcb/qxcbcursor.h @@ -122,6 +122,7 @@ private: void *handle); #endif bool m_gtkCursorThemeInitialized; + bool m_callbackForPropertyRegistered; }; QT_END_NAMESPACE diff --git a/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp b/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp index 76869ced60..02d2eebb56 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -274,8 +274,7 @@ QPlatformWindow *QXcbIntegration::createForeignWindow(QWindow *window, WId nativ #ifndef QT_NO_OPENGL QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - QXcbScreen *screen = static_cast(context->screen()->handle()); - QXcbGlIntegration *glIntegration = screen->connection()->glIntegration(); + QXcbGlIntegration *glIntegration = defaultConnection()->glIntegration(); if (!glIntegration) { qWarning("QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled"); return nullptr; diff --git a/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp b/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp index bffad0fac5..c67ffa9dd7 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -299,11 +299,6 @@ void QXcbWindow::create() return; } - QPlatformWindow::setGeometry(rect); - - if (platformScreen != currentScreen) - QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); - const QSize minimumSize = windowMinimumSize(); if (rect.width() > 0 || rect.height() > 0) { rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); @@ -315,6 +310,11 @@ void QXcbWindow::create() rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen())); } + QPlatformWindow::setGeometry(rect); + + if (platformScreen != currentScreen) + QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); + xcb_window_t xcb_parent_id = platformScreen->root(); if (parent()) { xcb_parent_id = static_cast(parent())->xcb_window(); @@ -1345,9 +1345,10 @@ void QXcbWindow::setWindowIcon(const QIcon &icon) if (!icon_data.isEmpty()) { // Ignore icon exceeding maximum xcb request length - if (size_t(icon_data.size()) > xcb_get_maximum_request_length(xcb_connection())) { - qWarning("Ignoring window icon: Size %d exceeds maximum xcb request length %u.", - icon_data.size(), xcb_get_maximum_request_length(xcb_connection())); + if (quint64(icon_data.size()) > quint64(xcb_get_maximum_request_length(xcb_connection()))) { + qWarning() << "Ignoring window icon" << icon_data.size() + << "exceeds maximum xcb request length" + << xcb_get_maximum_request_length(xcb_connection()); return; } xcb_change_property(xcb_connection(), diff --git a/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp b/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp index d264fabf0c..f464037989 100644 --- a/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp +++ b/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp @@ -85,6 +85,7 @@ QGtk3MenuItem::QGtk3MenuItem() m_checkable(false), m_checked(false), m_enabled(true), + m_exclusive(false), m_underline(false), m_invalid(true), m_menu(nullptr), diff --git a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp index 32b2e84fa0..b2301c8c15 100644 --- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp +++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp @@ -102,15 +102,12 @@ const QDBusArgument &operator >>(const QDBusArgument &arg, QXdgDesktopPortalFile class QXdgDesktopPortalFileDialogPrivate { public: - QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog) + QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion) : nativeFileDialog(nativeFileDialog) + , fileChooserPortalVersion(fileChooserPortalVersion) { } - WId winId = 0; - bool directoryMode = false; - bool modal = false; - bool multipleFiles = false; - bool saveFile = false; + QEventLoop loop; QString acceptLabel; QString directory; QString title; @@ -121,19 +118,27 @@ public: QString selectedMimeTypeFilter; QString selectedNameFilter; QStringList selectedFiles; - QPlatformFileDialogHelper *nativeFileDialog = nullptr; + std::unique_ptr nativeFileDialog; + uint fileChooserPortalVersion = 0; + bool failedToOpen = false; + bool directoryMode = false; + bool multipleFiles = false; + bool saveFile = false; }; -QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog) +QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion) : QPlatformFileDialogHelper() - , d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog)) + , d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog, fileChooserPortalVersion)) { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { - connect(d->nativeFileDialog, SIGNAL(accept()), this, SIGNAL(accept())); - connect(d->nativeFileDialog, SIGNAL(reject()), this, SIGNAL(reject())); + connect(d->nativeFileDialog.get(), SIGNAL(accept()), this, SIGNAL(accept())); + connect(d->nativeFileDialog.get(), SIGNAL(reject()), this, SIGNAL(reject())); } + + d->loop.connect(this, SIGNAL(accept()), SLOT(quit())); + d->loop.connect(this, SIGNAL(reject()), SLOT(quit())); } QXdgDesktopPortalFileDialog::~QXdgDesktopPortalFileDialog() @@ -177,7 +182,7 @@ void QXdgDesktopPortalFileDialog::initializeDialog() setDirectory(options()->initialDirectory()); } -void QXdgDesktopPortalFileDialog::openPortal() +void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) { Q_D(QXdgDesktopPortalFileDialog); @@ -185,13 +190,13 @@ void QXdgDesktopPortalFileDialog::openPortal() QLatin1String("/org/freedesktop/portal/desktop"), QLatin1String("org.freedesktop.portal.FileChooser"), d->saveFile ? QLatin1String("SaveFile") : QLatin1String("OpenFile")); - QString parentWindowId = QLatin1String("x11:") + QString::number(d->winId, 16); + QString parentWindowId = QLatin1String("x11:") + QString::number(parent ? parent->winId() : 0, 16); QVariantMap options; if (!d->acceptLabel.isEmpty()) options.insert(QLatin1String("accept_label"), d->acceptLabel); - options.insert(QLatin1String("modal"), d->modal); + options.insert(QLatin1String("modal"), windowModality != Qt::NonModal); options.insert(QLatin1String("multiple"), d->multipleFiles); options.insert(QLatin1String("directory"), d->directoryMode); @@ -290,10 +295,18 @@ void QXdgDesktopPortalFileDialog::openPortal() QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); - connect(watcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) { + connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; - if (reply.isError()) { - Q_EMIT reject(); + // Any error means the dialog is not shown and we need to fallback + d->failedToOpen = reply.isError(); + if (d->failedToOpen) { + if (d->nativeFileDialog) { + d->nativeFileDialog->show(windowFlags, windowModality, parent); + if (d->loop.isRunning()) + d->nativeFileDialog->exec(); + } else { + Q_EMIT reject(); + } } else { QDBusConnection::sessionBus().connect(nullptr, reply.value().path(), @@ -327,7 +340,7 @@ QUrl QXdgDesktopPortalFileDialog::directory() const { Q_D(const QXdgDesktopPortalFileDialog); - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) + if (d->nativeFileDialog && useNativeFileDialog()) return d->nativeFileDialog->directory(); return d->directory; @@ -349,7 +362,7 @@ QList QXdgDesktopPortalFileDialog::selectedFiles() const { Q_D(const QXdgDesktopPortalFileDialog); - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) + if (d->nativeFileDialog && useNativeFileDialog()) return d->nativeFileDialog->selectedFiles(); QList files; @@ -404,16 +417,13 @@ void QXdgDesktopPortalFileDialog::exec() { Q_D(QXdgDesktopPortalFileDialog); - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) { + if (d->nativeFileDialog && useNativeFileDialog()) { d->nativeFileDialog->exec(); return; } // HACK we have to avoid returning until we emit that the dialog was accepted or rejected - QEventLoop loop; - loop.connect(this, SIGNAL(accept()), SLOT(quit())); - loop.connect(this, SIGNAL(reject()), SLOT(quit())); - loop.exec(); + d->loop.exec(); } void QXdgDesktopPortalFileDialog::hide() @@ -430,13 +440,10 @@ bool QXdgDesktopPortalFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowMo initializeDialog(); - d->modal = windowModality != Qt::NonModal; - d->winId = parent ? parent->winId() : 0; - - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) + if (d->nativeFileDialog && useNativeFileDialog(OpenFallback)) return d->nativeFileDialog->show(windowFlags, windowModality, parent); - openPortal(); + openPortal(windowFlags, windowModality, parent); return true; } @@ -466,6 +473,23 @@ void QXdgDesktopPortalFileDialog::gotResponse(uint response, const QVariantMap & } } +bool QXdgDesktopPortalFileDialog::useNativeFileDialog(QXdgDesktopPortalFileDialog::FallbackType fallbackType) const +{ + Q_D(const QXdgDesktopPortalFileDialog); + + if (d->failedToOpen && fallbackType != OpenFallback) + return true; + + if (d->fileChooserPortalVersion < 3) { + if (options()->fileMode() == QFileDialogOptions::Directory) + return true; + else if (options()->fileMode() == QFileDialogOptions::DirectoryOnly) + return true; + } + + return false; +} + QT_END_NAMESPACE #include "moc_qxdgdesktopportalfiledialog_p.cpp" diff --git a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h index 4f4de96ecf..65e22a5cf2 100644 --- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h +++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h @@ -51,6 +51,11 @@ class QXdgDesktopPortalFileDialog : public QPlatformFileDialogHelper Q_OBJECT Q_DECLARE_PRIVATE(QXdgDesktopPortalFileDialog) public: + enum FallbackType { + GenericFallback, + OpenFallback + }; + enum ConditionType : uint { GlobalPattern = 0, MimeType = 1 @@ -69,7 +74,7 @@ public: }; typedef QVector FilterList; - QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog = nullptr); + QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog = nullptr, uint fileChooserPortalVersion = 0); ~QXdgDesktopPortalFileDialog(); bool defaultNameFilterDisables() const override; @@ -92,7 +97,8 @@ private Q_SLOTS: private: void initializeDialog(); - void openPortal(); + void openPortal(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent); + bool useNativeFileDialog(FallbackType fallbackType = GenericFallback) const; QScopedPointer d_ptr; }; diff --git a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp index 2fc3167fd5..b809503122 100644 --- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp +++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp @@ -153,11 +153,12 @@ QPlatformDialogHelper* QXdgDesktopPortalTheme::createPlatformDialogHelper(Dialog { Q_D(const QXdgDesktopPortalTheme); - if (type == FileDialog) { + if (type == FileDialog && d->fileChooserPortalVersion) { // Older versions of FileChooser portal don't support opening directories, therefore we fallback // to native file dialog opened inside the sandbox to open a directory. - if (d->fileChooserPortalVersion < 3 && d->baseTheme->usePlatformNativeDialog(type)) - return new QXdgDesktopPortalFileDialog(static_cast(d->baseTheme->createPlatformDialogHelper(type))); + if (d->baseTheme->usePlatformNativeDialog(type)) + return new QXdgDesktopPortalFileDialog(static_cast(d->baseTheme->createPlatformDialogHelper(type)), + d->fileChooserPortalVersion); return new QXdgDesktopPortalFileDialog; } diff --git a/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp index 4427efb770..781dad8d0b 100644 --- a/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp +++ b/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp @@ -1363,20 +1363,20 @@ bool QMYSQLDriver::open(const QString& db, } #if MYSQL_VERSION_ID >= 50007 - if (mysql_get_client_version() >= 50503 && mysql_get_server_version(d->mysql) >= 50503) { - // force the communication to be utf8mb4 (only utf8mb4 supports 4-byte characters) - mysql_set_character_set(d->mysql, "utf8mb4"); + // force the communication to be utf8mb4 (only utf8mb4 supports 4-byte characters) + if (mysql_set_character_set(d->mysql, "utf8mb4")) { + // this failed, try forcing it to utf (BMP only) + if (mysql_set_character_set(d->mysql, "utf8")) + qWarning() << "MySQL: Unable to set the client character set to utf8."; #if QT_CONFIG(textcodec) - d->tc = QTextCodec::codecForName("UTF-8"); + else + d->tc = codec(d->mysql); #endif - } else - { - // force the communication to be utf8 - mysql_set_character_set(d->mysql, "utf8"); + } #if QT_CONFIG(textcodec) - d->tc = codec(d->mysql); + else + d->tc = QTextCodec::codecForName("UTF-8"); #endif - } #endif // MYSQL_VERSION_ID >= 50007 d->preparedQuerysEnabled = checkPreparedQueries(d->mysql); diff --git a/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp index 5f51de3843..6cac60d03d 100644 --- a/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp +++ b/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp @@ -92,23 +92,39 @@ inline static QString fromSQLTCHAR(const QVarLengthArray& input, int s return result; } +template +void toSQLTCHARImpl(QVarLengthArray &result, const QString &input); // primary template undefined + +template +void do_append(QVarLengthArray &result, const Container &c) +{ + result.append(reinterpret_cast(c.data()), c.size()); +} + +template <> +void toSQLTCHARImpl<1>(QVarLengthArray &result, const QString &input) +{ + const auto u8 = input.toUtf8(); + do_append(result, u8); +} + +template <> +void toSQLTCHARImpl<2>(QVarLengthArray &result, const QString &input) +{ + do_append(result, input); +} + +template <> +void toSQLTCHARImpl<4>(QVarLengthArray &result, const QString &input) +{ + const auto u32 = input.toUcs4(); + do_append(result, u32); +} + inline static QVarLengthArray toSQLTCHAR(const QString &input) { QVarLengthArray result; - result.resize(input.size()); - switch(sizeof(SQLTCHAR)) { - case 1: - memcpy(result.data(), input.toUtf8().data(), input.size()); - break; - case 2: - memcpy(result.data(), input.unicode(), input.size() * 2); - break; - case 4: - memcpy(result.data(), input.toUcs4().data(), input.size() * 4); - break; - default: - qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR))); - } + toSQLTCHARImpl(result, input); result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't. return result; } @@ -763,6 +779,14 @@ QChar QODBCDriverPrivate::quoteChar() return quote; } +static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr, const QString &val) +{ + auto encoded = toSQLTCHAR(val); + return SQLSetConnectAttr(handle, attr, + encoded.data(), + SQLINTEGER(encoded.size() * sizeof(SQLTCHAR))); // size in bytes +} + bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) { @@ -798,10 +822,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) v = val.toUInt(); r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) size_t(v), 0); } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) { - val.utf16(); // 0 terminate - r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, - toSQLTCHAR(val).data(), - val.length()*sizeof(SQLTCHAR)); + r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, val); } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) { if (val.toUpper() == QLatin1String("SQL_TRUE")) { v = SQL_TRUE; @@ -816,10 +837,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) v = val.toUInt(); r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) size_t(v), 0); } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) { - val.utf16(); // 0 terminate - r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, - toSQLTCHAR(val).data(), - val.length()*sizeof(SQLTCHAR)); + r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val); } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) { if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) { v = SQL_OPT_TRACE_OFF; @@ -1022,9 +1040,12 @@ bool QODBCResult::reset (const QString& query) return false; } - r = SQLExecDirect(d->hStmt, - toSQLTCHAR(query).data(), - (SQLINTEGER) query.length()); + { + auto encoded = toSQLTCHAR(query); + r = SQLExecDirect(d->hStmt, + encoded.data(), + SQLINTEGER(encoded.size())); + } if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) { setLastError(qMakeError(QCoreApplication::translate("QODBCResult", "Unable to execute statement"), QSqlError::StatementError, d)); @@ -1371,9 +1392,12 @@ bool QODBCResult::prepare(const QString& query) return false; } - r = SQLPrepare(d->hStmt, - toSQLTCHAR(query).data(), - (SQLINTEGER) query.length()); + { + auto encoded = toSQLTCHAR(query); + r = SQLPrepare(d->hStmt, + encoded.data(), + SQLINTEGER(encoded.size())); + } if (r != SQL_SUCCESS) { setLastError(qMakeError(QCoreApplication::translate("QODBCResult", @@ -1401,7 +1425,7 @@ bool QODBCResult::exec() SQLCloseCursor(d->hStmt); QVector& values = boundValues(); - QVector tmpStorage(values.count(), QByteArray()); // holds temporary buffers + QVector tmpStorage(values.count(), QByteArray()); // targets for SQLBindParameter() QVarLengthArray indicators(values.count()); memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN)); @@ -1580,35 +1604,36 @@ bool QODBCResult::exec() case QVariant::String: if (d->unicode) { QByteArray &ba = tmpStorage[i]; - QString str = val.toString(); + { + const auto encoded = toSQLTCHAR(val.toString()); + ba = QByteArray(reinterpret_cast(encoded.data()), + encoded.size() * sizeof(SQLTCHAR)); + } + if (*ind != SQL_NULL_DATA) - *ind = str.length() * sizeof(SQLTCHAR); - int strSize = str.length() * sizeof(SQLTCHAR); + *ind = ba.size(); if (bindValueType(i) & QSql::Out) { - const QVarLengthArray a(toSQLTCHAR(str)); - ba = QByteArray((const char *)a.constData(), a.size() * sizeof(SQLTCHAR)); r = SQLBindParameter(d->hStmt, i + 1, qParamType[bindValueType(i) & QSql::InOut], SQL_C_TCHAR, - strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, + ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, 0, // god knows... don't change this! 0, - ba.data(), + const_cast(ba.constData()), // don't detach ba.size(), ind); break; } - ba = QByteArray ((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR)); r = SQLBindParameter(d->hStmt, i + 1, qParamType[bindValueType(i) & QSql::InOut], SQL_C_TCHAR, - strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, - strSize, + ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, + ba.size(), 0, - const_cast(ba.constData()), + const_cast(ba.constData()), // don't detach ba.size(), ind); break; @@ -1716,10 +1741,11 @@ bool QODBCResult::exec() case QVariant::String: if (d->unicode) { if (bindValueType(i) & QSql::Out) { - const QByteArray &first = tmpStorage.at(i); - QVarLengthArray array; - array.append((const SQLTCHAR *)first.constData(), first.size()); - values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR)); + const QByteArray &bytes = tmpStorage.at(i); + const auto strSize = bytes.size() / int(sizeof(SQLTCHAR)); + QVarLengthArray string(strSize); + memcpy(string.data(), bytes.data(), strSize * sizeof(SQLTCHAR)); + values[i] = fromSQLTCHAR(string); } break; } @@ -1966,14 +1992,16 @@ bool QODBCDriver::open(const QString & db, SQLSMALLINT cb; QVarLengthArray connOut(1024); memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR)); - r = SQLDriverConnect(d->hDbc, - NULL, - toSQLTCHAR(connQStr).data(), - (SQLSMALLINT)connQStr.length(), - connOut.data(), - 1024, - &cb, - /*SQL_DRIVER_NOPROMPT*/0); + { + auto encoded = toSQLTCHAR(connQStr); + r = SQLDriverConnect(d->hDbc, + nullptr, + encoded.data(), SQLSMALLINT(encoded.size()), + connOut.data(), + 1024, + &cb, + /*SQL_DRIVER_NOPROMPT*/0); + } if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d)); @@ -2352,17 +2380,15 @@ QStringList QODBCDriver::tables(QSql::TableType type) const if (tableType.isEmpty()) return tl; - QString joinedTableTypeString = tableType.join(QLatin1Char(',')); + { + auto joinedTableTypeString = toSQLTCHAR(tableType.join(u',')); - r = SQLTables(hStmt, - NULL, - 0, - NULL, - 0, - NULL, - 0, - toSQLTCHAR(joinedTableTypeString).data(), - joinedTableTypeString.length() /* characters, not bytes */); + r = SQLTables(hStmt, + nullptr, 0, + nullptr, 0, + nullptr, 0, + joinedTableTypeString.data(), joinedTableTypeString.size()); + } if (r != SQL_SUCCESS) qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d); @@ -2436,28 +2462,30 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, SQL_IS_UINTEGER); - r = SQLPrimaryKeys(hStmt, - catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), - catalog.length(), - schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), - schema.length(), - toSQLTCHAR(table).data(), - table.length() /* in characters, not in bytes */); + { + auto c = toSQLTCHAR(catalog); + auto s = toSQLTCHAR(schema); + auto t = toSQLTCHAR(table); + r = SQLPrimaryKeys(hStmt, + catalog.isEmpty() ? nullptr : c.data(), c.size(), + schema.isEmpty() ? nullptr : s.data(), s.size(), + t.data(), t.size()); + } // if the SQLPrimaryKeys() call does not succeed (e.g the driver // does not support it) - try an alternative method to get hold of // the primary index (e.g MS Access and FoxPro) if (r != SQL_SUCCESS) { - r = SQLSpecialColumns(hStmt, - SQL_BEST_ROWID, - catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), - catalog.length(), - schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), - schema.length(), - toSQLTCHAR(table).data(), - table.length(), - SQL_SCOPE_CURROW, - SQL_NULLABLE); + auto c = toSQLTCHAR(catalog); + auto s = toSQLTCHAR(schema); + auto t = toSQLTCHAR(table); + r = SQLSpecialColumns(hStmt, + SQL_BEST_ROWID, + catalog.isEmpty() ? nullptr : c.data(), c.size(), + schema.isEmpty() ? nullptr : s.data(), s.size(), + t.data(), t.size(), + SQL_SCOPE_CURROW, + SQL_NULLABLE); if (r != SQL_SUCCESS) { qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d); @@ -2538,15 +2566,17 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, SQL_IS_UINTEGER); - r = SQLColumns(hStmt, - catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), - catalog.length(), - schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), - schema.length(), - toSQLTCHAR(table).data(), - table.length(), - NULL, - 0); + { + auto c = toSQLTCHAR(catalog); + auto s = toSQLTCHAR(schema); + auto t = toSQLTCHAR(table); + r = SQLColumns(hStmt, + catalog.isEmpty() ? nullptr : c.data(), c.size(), + schema.isEmpty() ? nullptr : s.data(), s.size(), + t.data(), t.size(), + nullptr, + 0); + } if (r != SQL_SUCCESS) qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d); diff --git a/qtbase/src/printsupport/dialogs/images/print-24.png b/qtbase/src/printsupport/dialogs/images/printer-24.png similarity index 100% rename from src/printsupport/dialogs/images/print-24.png rename to src/printsupport/dialogs/images/printer-24.png diff --git a/qtbase/src/printsupport/dialogs/images/print-32.png b/qtbase/src/printsupport/dialogs/images/printer-32.png similarity index 100% rename from src/printsupport/dialogs/images/print-32.png rename to src/printsupport/dialogs/images/printer-32.png diff --git a/qtbase/src/printsupport/dialogs/images/view-page-sided-24.png b/qtbase/src/printsupport/dialogs/images/view-pages-facing-24.png similarity index 100% rename from src/printsupport/dialogs/images/view-page-sided-24.png rename to src/printsupport/dialogs/images/view-pages-facing-24.png diff --git a/qtbase/src/printsupport/dialogs/images/view-page-sided-32.png b/qtbase/src/printsupport/dialogs/images/view-pages-facing-32.png similarity index 100% rename from src/printsupport/dialogs/images/view-page-sided-32.png rename to src/printsupport/dialogs/images/view-pages-facing-32.png diff --git a/qtbase/src/printsupport/dialogs/images/view-page-multi-24.png b/qtbase/src/printsupport/dialogs/images/view-pages-overview-24.png similarity index 100% rename from src/printsupport/dialogs/images/view-page-multi-24.png rename to src/printsupport/dialogs/images/view-pages-overview-24.png diff --git a/qtbase/src/printsupport/dialogs/images/view-page-multi-32.png b/qtbase/src/printsupport/dialogs/images/view-pages-overview-32.png similarity index 100% rename from src/printsupport/dialogs/images/view-page-multi-32.png rename to src/printsupport/dialogs/images/view-pages-overview-32.png diff --git a/qtbase/src/printsupport/dialogs/images/view-page-one-24.png b/qtbase/src/printsupport/dialogs/images/view-pages-single-24.png similarity index 100% rename from src/printsupport/dialogs/images/view-page-one-24.png rename to src/printsupport/dialogs/images/view-pages-single-24.png diff --git a/qtbase/src/printsupport/dialogs/images/view-page-one-32.png b/qtbase/src/printsupport/dialogs/images/view-pages-single-32.png similarity index 100% rename from src/printsupport/dialogs/images/view-page-one-32.png rename to src/printsupport/dialogs/images/view-pages-single-32.png diff --git a/qtbase/src/printsupport/dialogs/images/fit-page-24.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-page-24.png similarity index 100% rename from src/printsupport/dialogs/images/fit-page-24.png rename to src/printsupport/dialogs/images/zoom-fit-page-24.png diff --git a/qtbase/src/printsupport/dialogs/images/fit-page-32.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-page-32.png similarity index 100% rename from src/printsupport/dialogs/images/fit-page-32.png rename to src/printsupport/dialogs/images/zoom-fit-page-32.png diff --git a/qtbase/src/printsupport/dialogs/images/fit-width-24.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-width-24.png similarity index 100% rename from src/printsupport/dialogs/images/fit-width-24.png rename to src/printsupport/dialogs/images/zoom-fit-width-24.png diff --git a/qtbase/src/printsupport/dialogs/images/fit-width-32.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-width-32.png similarity index 100% rename from src/printsupport/dialogs/images/fit-width-32.png rename to src/printsupport/dialogs/images/zoom-fit-width-32.png diff --git a/qtbase/src/printsupport/dialogs/qprintdialog.qrc b/qtbase/src/printsupport/dialogs/qprintdialog.qrc index 5a579baa55..10b8e1d341 100644 --- a/qtbase/src/printsupport/dialogs/qprintdialog.qrc +++ b/qtbase/src/printsupport/dialogs/qprintdialog.qrc @@ -1,9 +1,9 @@ -images/fit-page-24.png -images/fit-page-32.png -images/fit-width-24.png -images/fit-width-32.png +images/zoom-fit-page-24.png +images/zoom-fit-page-32.png +images/zoom-fit-width-24.png +images/zoom-fit-width-32.png images/go-first-24.png images/go-first-32.png images/go-last-24.png @@ -18,14 +18,14 @@ images/layout-portrait-32.png images/page-setup-24.png images/page-setup-32.png -images/print-24.png -images/print-32.png -images/view-page-multi-24.png -images/view-page-multi-32.png -images/view-page-one-24.png -images/view-page-one-32.png -images/view-page-sided-24.png -images/view-page-sided-32.png +images/printer-24.png +images/printer-32.png +images/view-pages-overview-24.png +images/view-pages-overview-32.png +images/view-pages-single-24.png +images/view-pages-single-32.png +images/view-pages-facing-24.png +images/view-pages-facing-32.png images/zoom-in-24.png images/zoom-in-32.png images/zoom-out-24.png diff --git a/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp b/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp index 39575d5f57..23b7e89538 100644 --- a/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp +++ b/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp @@ -352,7 +352,7 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) static inline void qt_setupActionIcon(QAction *action, QLatin1String name) { QLatin1String imagePrefix(":/qt-project.org/dialogs/qprintpreviewdialog/images/"); - QIcon icon; + QIcon icon = QIcon::fromTheme(name); icon.addFile(imagePrefix + name + QLatin1String("-24.png"), QSize(24, 24)); icon.addFile(imagePrefix + name + QLatin1String("-32.png"), QSize(32, 32)); action->setIcon(icon); @@ -383,8 +383,8 @@ void QPrintPreviewDialogPrivate::setupActions() fitPageAction->setObjectName(QLatin1String("fitPageAction")); fitWidthAction->setCheckable(true); fitPageAction->setCheckable(true); - qt_setupActionIcon(fitWidthAction, QLatin1String("fit-width")); - qt_setupActionIcon(fitPageAction, QLatin1String("fit-page")); + qt_setupActionIcon(fitWidthAction, QLatin1String("zoom-fit-width")); + qt_setupActionIcon(fitPageAction, QLatin1String("zoom-fit-page")); QObject::connect(fitGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_fit(QAction*))); // Zoom @@ -410,9 +410,9 @@ void QPrintPreviewDialogPrivate::setupActions() singleModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show single page")); facingModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show facing pages")); overviewModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show overview of all pages")); - qt_setupActionIcon(singleModeAction, QLatin1String("view-page-one")); - qt_setupActionIcon(facingModeAction, QLatin1String("view-page-sided")); - qt_setupActionIcon(overviewModeAction, QLatin1String("view-page-multi")); + qt_setupActionIcon(singleModeAction, QLatin1String("view-pages-single")); + qt_setupActionIcon(facingModeAction, QLatin1String("view-pages-facing")); + qt_setupActionIcon(overviewModeAction, QLatin1String("view-pages-overview")); singleModeAction->setObjectName(QLatin1String("singleModeAction")); facingModeAction->setObjectName(QLatin1String("facingModeAction")); overviewModeAction->setObjectName(QLatin1String("overviewModeAction")); @@ -426,7 +426,7 @@ void QPrintPreviewDialogPrivate::setupActions() printerGroup = new QActionGroup(q); printAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Print")); pageSetupAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Page setup")); - qt_setupActionIcon(printAction, QLatin1String("print")); + qt_setupActionIcon(printAction, QLatin1String("printer")); qt_setupActionIcon(pageSetupAction, QLatin1String("page-setup")); QObject::connect(printAction, SIGNAL(triggered(bool)), q, SLOT(_q_print())); QObject::connect(pageSetupAction, SIGNAL(triggered(bool)), q, SLOT(_q_pageSetup())); diff --git a/qtbase/src/testlib/qabstractitemmodeltester.cpp b/qtbase/src/testlib/qabstractitemmodeltester.cpp index 1cd18b98bb..41219a7e23 100644 --- a/qtbase/src/testlib/qabstractitemmodeltester.cpp +++ b/qtbase/src/testlib/qabstractitemmodeltester.cpp @@ -454,7 +454,7 @@ void QAbstractItemModelTesterPrivate::parent() // Common error test #2, make sure that a second level index has a parent // that is the first level index. - if (model->rowCount(topIndex) > 0) { + if (model->rowCount(topIndex) > 0 && model->columnCount(topIndex) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); MODELTESTER_VERIFY(childIndex.isValid()); MODELTESTER_COMPARE(model->parent(childIndex), topIndex); diff --git a/qtbase/src/testlib/qasciikey.cpp b/qtbase/src/testlib/qasciikey.cpp index 9a308da2bc..93498b256f 100644 --- a/qtbase/src/testlib/qasciikey.cpp +++ b/qtbase/src/testlib/qasciikey.cpp @@ -498,6 +498,11 @@ char QTest::keyToAscii(Qt::Key key) case Qt::Key_LaunchE : return 0; // = 0x10b0, case Qt::Key_LaunchF : return 0; // = 0x10b1, + // Keypad navigation keys + case Qt::Key_Select : return 0; // = 0x01010000 + case Qt::Key_Yes : return 0; // = 0x01010001 + case Qt::Key_No : return 0; // = 0x01010002 + default: QTEST_ASSERT(false); return 0; } } diff --git a/qtbase/src/testlib/qtestresult.cpp b/qtbase/src/testlib/qtestresult.cpp index 88028aac6e..2e89930776 100644 --- a/qtbase/src/testlib/qtestresult.cpp +++ b/qtbase/src/testlib/qtestresult.cpp @@ -251,7 +251,8 @@ bool QTestResult::verify(bool statement, const char *statementStr, { QTEST_ASSERT(statementStr); - char msg[1024] = {'\0'}; + char msg[1024]; + msg[0] = '\0'; if (QTestLog::verboseLevel() >= 2) { qsnprintf(msg, 1024, "QVERIFY(%s)", statementStr); @@ -309,7 +310,8 @@ static bool compareHelper(bool success, const char *failureMsg, bool hasValues = true) { const size_t maxMsgLen = 1024; - char msg[maxMsgLen] = {'\0'}; + char msg[maxMsgLen]; + msg[0] = '\0'; QTEST_ASSERT(expected); QTEST_ASSERT(actual); diff --git a/qtbase/src/widgets/dialogs/qcolordialog.cpp b/qtbase/src/widgets/dialogs/qcolordialog.cpp index 4247731275..cb325be85c 100644 --- a/qtbase/src/widgets/dialogs/qcolordialog.cpp +++ b/qtbase/src/widgets/dialogs/qcolordialog.cpp @@ -78,7 +78,10 @@ #include "qwindow.h" #include "private/qdialog_p.h" +#include "private/qguiapplication_p.h" +#include +#include #include QT_BEGIN_NAMESPACE @@ -1611,6 +1614,20 @@ void QColorDialogPrivate::_q_newStandard(int r, int c) void QColorDialogPrivate::_q_pickScreenColor() { Q_Q(QColorDialog); + + auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services(); + if (platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) { + if (auto *colorPicker = platformServices->colorPicker(q->windowHandle())) { + q->connect(colorPicker, &QPlatformServiceColorPicker::colorPicked, q, + [q, colorPicker](const QColor &color) { + colorPicker->deleteLater(); + q->setCurrentColor(color); + }); + colorPicker->pickColor(); + return; + } + } + if (!colorPickingEventFilter) colorPickingEventFilter = new QColorPickingEventFilter(this, q); q->installEventFilter(colorPickingEventFilter); diff --git a/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp b/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp index e120817edc..8ea36b5427 100644 --- a/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp +++ b/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp @@ -400,12 +400,7 @@ bool QAbstractItemDelegate::helpEvent(QHelpEvent *event, const QString tooltip = index.isValid() ? d->textForRole(Qt::ToolTipRole, index.data(Qt::ToolTipRole), option.locale, precision) : QString(); - QRect rect; - if (index.isValid()) { - const QRect r = view->visualRect(index); - rect = QRect(view->mapToGlobal(r.topLeft()), r.size()); - } - QToolTip::showText(he->globalPos(), tooltip, view, rect); + QToolTip::showText(he->globalPos(), tooltip, view->viewport(), option.rect); event->setAccepted(!tooltip.isEmpty()); break; } diff --git a/qtbase/src/widgets/itemviews/qabstractitemview.cpp b/qtbase/src/widgets/itemviews/qabstractitemview.cpp index deb49ca23d..93dc5ee80c 100644 --- a/qtbase/src/widgets/itemviews/qabstractitemview.cpp +++ b/qtbase/src/widgets/itemviews/qabstractitemview.cpp @@ -2347,11 +2347,12 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) #if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT) if (event == QKeySequence::Copy) { - QVariant variant; - if (d->model) - variant = d->model->data(currentIndex(), Qt::DisplayRole); - if (variant.canConvert()) - QGuiApplication::clipboard()->setText(variant.toString()); + const QModelIndex index = currentIndex(); + if (index.isValid() && d->model) { + const QVariant variant = d->model->data(index, Qt::DisplayRole); + if (variant.canConvert()) + QGuiApplication::clipboard()->setText(variant.toString()); + } event->accept(); } #endif diff --git a/qtbase/src/widgets/itemviews/qlistview.cpp b/qtbase/src/widgets/itemviews/qlistview.cpp index 5a88f1b1ef..63f760269c 100644 --- a/qtbase/src/widgets/itemviews/qlistview.cpp +++ b/qtbase/src/widgets/itemviews/qlistview.cpp @@ -3388,6 +3388,7 @@ void QIconModeViewBase::updateContentsSize() */ void QListView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + QAbstractItemView::currentChanged(current, previous); #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { if (current.isValid()) { @@ -3398,7 +3399,6 @@ void QListView::currentChanged(const QModelIndex ¤t, const QModelIndex &pr } } #endif - QAbstractItemView::currentChanged(current, previous); } /*! diff --git a/qtbase/src/widgets/itemviews/qtableview.cpp b/qtbase/src/widgets/itemviews/qtableview.cpp index d120c41dc9..09d34005a7 100644 --- a/qtbase/src/widgets/itemviews/qtableview.cpp +++ b/qtbase/src/widgets/itemviews/qtableview.cpp @@ -1013,6 +1013,7 @@ void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItem & int QTableViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option) const { Q_Q(const QTableView); + const int oldHint = hint; QWidget *editor = editorForIndex(index).widget.data(); if (editor && persistent.contains(editor)) { hint = qMax(hint, editor->sizeHint().width()); @@ -1021,6 +1022,17 @@ int QTableViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, con hint = qBound(min, hint, max); } hint = qMax(hint, q->itemDelegate(index)->sizeHint(option, index).width()); + + if (hasSpans()) { + auto span = spans.spanAt(index.column(), index.row()); + if (span && span->m_left == index.column() && span->m_top == index.row()) { + // spans are screwed up when sections are moved + const auto left = logicalColumn(span->m_left); + for (int i = 1; i <= span->width(); ++i) + hint -= q->columnWidth(visualColumn(left + i)); + } + hint = std::max(hint, oldHint); + } return hint; } @@ -1053,6 +1065,11 @@ int QTableViewPrivate::heightHintForIndex(const QModelIndex &index, int hint, QS option.rect.setHeight(height); option.rect.setX(q->columnViewportPosition(index.column())); option.rect.setWidth(q->columnWidth(index.column())); + if (hasSpans()) { + auto span = spans.spanAt(index.column(), index.row()); + if (span && span->m_left == index.column() && span->m_top == index.row()) + option.rect.setWidth(std::max(option.rect.width(), visualSpanRect(*span).width())); + } // 1px less space when grid is shown (see drawCell) if (showGrid) option.rect.setWidth(option.rect.width() - 1); diff --git a/qtbase/src/widgets/kernel/qaction.h b/qtbase/src/widgets/kernel/qaction.h index 258a1ea0a0..737c1e8285 100644 --- a/qtbase/src/widgets/kernel/qaction.h +++ b/qtbase/src/widgets/kernel/qaction.h @@ -81,7 +81,7 @@ class Q_WIDGETS_EXPORT QAction : public QObject Q_PROPERTY(MenuRole menuRole READ menuRole WRITE setMenuRole NOTIFY changed) Q_PROPERTY(bool iconVisibleInMenu READ isIconVisibleInMenu WRITE setIconVisibleInMenu NOTIFY changed) Q_PROPERTY(bool shortcutVisibleInContextMenu READ isShortcutVisibleInContextMenu WRITE setShortcutVisibleInContextMenu NOTIFY changed) - Q_PROPERTY(Priority priority READ priority WRITE setPriority) + Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY changed) public: // note this is copied into qplatformmenu.h, which must stay in sync diff --git a/qtbase/src/widgets/styles/qcommonstyle.cpp b/qtbase/src/widgets/styles/qcommonstyle.cpp index 502a527901..a79c33005c 100644 --- a/qtbase/src/widgets/styles/qcommonstyle.cpp +++ b/qtbase/src/widgets/styles/qcommonstyle.cpp @@ -1708,8 +1708,9 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, alignment |= Qt::TextHideMnemonic; rect.translate(shiftX, shiftY); p->setFont(toolbutton->font); + const QString text = d->toolButtonElideText(toolbutton, rect, alignment); proxy()->drawItemText(p, rect, alignment, toolbutton->palette, - opt->state & State_Enabled, toolbutton->text, + opt->state & State_Enabled, text, QPalette::ButtonText); } else { QPixmap pm; diff --git a/qtbase/src/widgets/styles/qfusionstyle.cpp b/qtbase/src/widgets/styles/qfusionstyle.cpp index a225d4b563..35e2769ac4 100644 --- a/qtbase/src/widgets/styles/qfusionstyle.cpp +++ b/qtbase/src/widgets/styles/qfusionstyle.cpp @@ -1772,14 +1772,6 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio proxy()->drawControl(CE_PushButtonLabel, &subopt, painter, widget); } break; - case CE_PushButtonLabel: - if (const QStyleOptionButton *button = qstyleoption_cast(option)) { - QStyleOptionButton b(*button); - // no PM_ButtonShiftHorizontal and PM_ButtonShiftVertical for fusion style - b.state &= ~(State_On | State_Sunken); - QCommonStyle::drawControl(element, &b, painter, widget); - } - break; case CE_MenuBarEmptyArea: painter->save(); { diff --git a/qtbase/src/widgets/styles/qstylesheetstyle.cpp b/qtbase/src/widgets/styles/qstylesheetstyle.cpp index 55d95c7fb2..8e9d2abbaa 100644 --- a/qtbase/src/widgets/styles/qstylesheetstyle.cpp +++ b/qtbase/src/widgets/styles/qstylesheetstyle.cpp @@ -4453,12 +4453,15 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op case PE_PanelLineEdit: if (const QStyleOptionFrame *frm = qstyleoption_cast(opt)) { - QWidget *container = containerWidget(w); - if (container != w) { - QRenderRule containerRule = renderRule(container, opt); - if (!containerRule.hasNativeBorder() || !containerRule.baseStyleCanDraw()) - return; - rule = containerRule; + // Fall back to container widget's render rule + if (w) { + QWidget *container = containerWidget(w); + if (container != w) { + QRenderRule containerRule = renderRule(container, opt); + if (!containerRule.hasNativeBorder() || !containerRule.baseStyleCanDraw()) + return; + rule = containerRule; + } } if (rule.hasNativeBorder()) { diff --git a/qtbase/src/widgets/widgets/qabstractbutton.cpp b/qtbase/src/widgets/widgets/qabstractbutton.cpp index a128b23950..dc40bf62fb 100644 --- a/qtbase/src/widgets/widgets/qabstractbutton.cpp +++ b/qtbase/src/widgets/widgets/qabstractbutton.cpp @@ -56,6 +56,7 @@ #ifndef QT_NO_ACCESSIBILITY #include "qaccessible.h" #endif +#include #include @@ -1076,19 +1077,19 @@ void QAbstractButton::keyPressEvent(QKeyEvent *e) { Q_D(QAbstractButton); bool next = true; - switch (e->key()) { - case Qt::Key_Enter: - case Qt::Key_Return: - e->ignore(); - break; - case Qt::Key_Select: - case Qt::Key_Space: - if (!e->isAutoRepeat()) { - setDown(true); - repaint(); - d->emitPressed(); - } - break; + + const auto key = static_cast(e->key()); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(key) && !e->isAutoRepeat()) { + setDown(true); + repaint(); + d->emitPressed(); + return; + } + + switch (key) { case Qt::Key_Up: next = false; Q_FALLTHROUGH(); @@ -1153,15 +1154,15 @@ void QAbstractButton::keyReleaseEvent(QKeyEvent *e) if (!e->isAutoRepeat()) d->repeatTimer.stop(); - switch (e->key()) { - case Qt::Key_Select: - case Qt::Key_Space: - if (!e->isAutoRepeat() && d->down) - d->click(); - break; - default: - e->ignore(); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(static_cast(e->key())) && !e->isAutoRepeat() && d->down) { + d->click(); + return; } + + e->ignore(); } /*!\reimp diff --git a/qtbase/src/widgets/widgets/qcombobox.cpp b/qtbase/src/widgets/widgets/qcombobox.cpp index 7a496c27e0..0a3d96647b 100644 --- a/qtbase/src/widgets/widgets/qcombobox.cpp +++ b/qtbase/src/widgets/widgets/qcombobox.cpp @@ -3352,7 +3352,23 @@ void QComboBox::keyPressEvent(QKeyEvent *e) Move move = NoMove; int newIndex = currentIndex(); - switch (e->key()) { + + bool pressLikeButton = !d->lineEdit; +#ifdef QT_KEYPAD_NAVIGATION + pressLikeButton |= QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus(); +#endif + auto key = static_cast(e->key()); + if (pressLikeButton) { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(key)) { + showPopup(); + return; + } + } + + switch (key) { case Qt::Key_Up: if (e->modifiers() & Qt::ControlModifier) break; // pass to line edit for auto completion @@ -3394,26 +3410,11 @@ void QComboBox::keyPressEvent(QKeyEvent *e) return; } break; - case Qt::Key_Space: - if (!d->lineEdit) { - showPopup(); - return; - } - break; - case Qt::Key_Enter: - case Qt::Key_Return: case Qt::Key_Escape: if (!d->lineEdit) e->ignore(); break; #ifdef QT_KEYPAD_NAVIGATION - case Qt::Key_Select: - if (QApplicationPrivate::keypadNavigationEnabled() - && (!hasEditFocus() || !d->lineEdit)) { - showPopup(); - return; - } - break; case Qt::Key_Left: case Qt::Key_Right: if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) diff --git a/qtbase/src/widgets/widgets/qdatetimeedit_p.h b/qtbase/src/widgets/widgets/qdatetimeedit_p.h index d36b6f8f9a..e0df5b5158 100644 --- a/qtbase/src/widgets/widgets/qdatetimeedit_p.h +++ b/qtbase/src/widgets/widgets/qdatetimeedit_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWidgets module of the Qt Toolkit. diff --git a/qtbase/src/widgets/widgets/qgroupbox.cpp b/qtbase/src/widgets/widgets/qgroupbox.cpp index 02a0bed325..3f3eccc370 100644 --- a/qtbase/src/widgets/widgets/qgroupbox.cpp +++ b/qtbase/src/widgets/widgets/qgroupbox.cpp @@ -54,6 +54,8 @@ #include "qaccessible.h" #endif #include +#include +#include #include "qdebug.h" @@ -360,7 +362,10 @@ bool QGroupBox::event(QEvent *e) return true; case QEvent::KeyPress: { QKeyEvent *k = static_cast(e); - if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (!k->isAutoRepeat() && buttonPressKeys.contains(static_cast(k->key()))) { d->pressedControl = QStyle::SC_GroupBoxCheckBox; update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)); return true; @@ -369,7 +374,10 @@ bool QGroupBox::event(QEvent *e) } case QEvent::KeyRelease: { QKeyEvent *k = static_cast(e); - if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (!k->isAutoRepeat() && buttonPressKeys.contains(static_cast(k->key()))) { bool toggle = (d->pressedControl == QStyle::SC_GroupBoxLabel || d->pressedControl == QStyle::SC_GroupBoxCheckBox); d->pressedControl = QStyle::SC_None; diff --git a/qtbase/src/widgets/widgets/qtoolbararealayout.cpp b/qtbase/src/widgets/widgets/qtoolbararealayout.cpp index 493c094cc1..d5f4b59017 100644 --- a/qtbase/src/widgets/widgets/qtoolbararealayout.cpp +++ b/qtbase/src/widgets/widgets/qtoolbararealayout.cpp @@ -1367,7 +1367,7 @@ bool QToolBarAreaLayout::restoreState(QDataStream &stream, const QListsetOrientation(floating ? ((shown & 2) ? Qt::Vertical : Qt::Horizontal) : dock.o); toolBar->setVisible(shown & 1); - toolBar->d_func()->setWindowState(floating, true, rect); + toolBar->d_func()->setWindowState(floating, false, rect); item.preferredSize = item.size; line.toolBarItems.append(item); diff --git a/qtbase/src/widgets/widgets/qtoolbutton.cpp b/qtbase/src/widgets/widgets/qtoolbutton.cpp index e380cb647b..9953db73af 100644 --- a/qtbase/src/widgets/widgets/qtoolbutton.cpp +++ b/qtbase/src/widgets/widgets/qtoolbutton.cpp @@ -982,7 +982,15 @@ QAction *QToolButton::defaultAction() const return d->defaultAction; } - +/*! + \reimp + */ +void QToolButton::checkStateSet() +{ + Q_D(QToolButton); + if (d->defaultAction && d->defaultAction->isCheckable()) + d->defaultAction->setChecked(isChecked()); +} /*! \reimp diff --git a/qtbase/src/widgets/widgets/qtoolbutton.h b/qtbase/src/widgets/widgets/qtoolbutton.h index 52bd2d5f7a..82b5d7924f 100644 --- a/qtbase/src/widgets/widgets/qtoolbutton.h +++ b/qtbase/src/widgets/widgets/qtoolbutton.h @@ -118,6 +118,7 @@ protected: void changeEvent(QEvent *) override; bool hitButton(const QPoint &pos) const override; + void checkStateSet() override; void nextCheckState() override; void initStyleOption(QStyleOptionToolButton *option) const; diff --git a/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp b/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp index 212c0a2e13..3c77b1ba0b 100644 --- a/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp +++ b/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp @@ -126,8 +126,7 @@ public: void tst_QtConcurrentIterateKernel::instantiate() { - auto future = startThreadEngine(new PrintFor(0, 40)).startAsynchronously(); - future.waitForFinished(); + startThreadEngine(new PrintFor(0, 40)).startBlocking(); QCOMPARE(iterations.loadRelaxed(), 40); } @@ -166,10 +165,8 @@ void tst_QtConcurrentIterateKernel::stresstest() const int times = 50; for (int i = 0; i < times; ++i) { counter.storeRelaxed(0); - // ThreadEngine will delete f when it finishes - auto f = new CountFor(0, iterations); - auto future = f->startAsynchronously(); - future.waitForFinished(); + CountFor f(0, iterations); + f.startBlocking(); QCOMPARE(counter.loadRelaxed(), iterations); } } @@ -177,11 +174,8 @@ void tst_QtConcurrentIterateKernel::stresstest() void tst_QtConcurrentIterateKernel::noIterations() { const int times = 20000; - for (int i = 0; i < times; ++i) { - auto future = startThreadEngine(new IterateKernel(0, 0)) - .startAsynchronously(); - future.waitForFinished(); - } + for (int i = 0; i < times; ++i) + startThreadEngine(new IterateKernel(0, 0)).startBlocking(); } QMutex threadsMutex; @@ -236,10 +230,8 @@ void tst_QtConcurrentIterateKernel::throttling() threads.clear(); - // ThreadEngine will delete f when it finishes - auto f = new ThrottleFor(0, totalIterations); - auto future = f->startAsynchronously(); - future.waitForFinished(); + ThrottleFor f(0, totalIterations); + f.startBlocking(); QCOMPARE(iterations.loadRelaxed(), totalIterations); diff --git a/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp b/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp index ac7be7acdd..81bc64c8d9 100644 --- a/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp +++ b/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp @@ -65,9 +65,16 @@ public: void tst_QtConcurrentThreadEngine::runDirectly() { - PrintUser *engine = new PrintUser(); - QFuture f = engine->startAsynchronously(); - f.waitForFinished(); + { + PrintUser engine; + engine.startSingleThreaded(); + engine.startBlocking(); + } + { + PrintUser *engine = new PrintUser(); + QFuture f = engine->startAsynchronously(); + f.waitForFinished(); + } } class StringResultUser : public ThreadEngine @@ -99,10 +106,8 @@ public: void tst_QtConcurrentThreadEngine::result() { - // ThreadEngine will delete 'engine' when it finishes - auto engine = new StringResultUser(); - auto future = engine->startAsynchronously(); - QCOMPARE(future.result(), QString("Foo")); + StringResultUser engine; + QCOMPARE(*engine.startBlocking(), QString("Foo")); } class VoidResultUser : public ThreadEngine @@ -128,9 +133,17 @@ public: void tst_QtConcurrentThreadEngine::runThroughStarter() { - ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); - QFuture f = starter.startAsynchronously(); - QCOMPARE(f.result(), QString("Foo")); + { + ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); + QFuture f = starter.startAsynchronously(); + QCOMPARE(f.result(), QString("Foo")); + } + + { + ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); + QString str = starter.startBlocking(); + QCOMPARE(str, QString("Foo")); + } } class CancelUser : public ThreadEngine @@ -213,6 +226,12 @@ void tst_QtConcurrentThreadEngine::throttle() f.waitForFinished(); QCOMPARE(count.loadRelaxed(), 0); } + + for (int i = 0; i < repeats; ++i) { + ThrottleAlwaysUser t; + t.startBlocking(); + QCOMPARE(count.loadRelaxed(), 0); + } } QSet threads; @@ -249,17 +268,35 @@ void tst_QtConcurrentThreadEngine::threadCount() { const int repeats = 10; for (int i = 0; i < repeats; ++i) { + ThreadCountUser t; + t.startBlocking(); + int count = threads.count(); + int count_expected = QThreadPool::globalInstance()->maxThreadCount() + 1; // +1 for the main thread. + if (count != count_expected) + QEXPECT_FAIL("", "QTBUG-23333", Abort); + QCOMPARE(count, count_expected); + (new ThreadCountUser())->startAsynchronously().waitForFinished(); - const auto count = threads.count(); - const auto maxThreadCount = QThreadPool::globalInstance()->maxThreadCount(); - QVERIFY(count <= maxThreadCount); - QVERIFY(!threads.contains(QThread::currentThread())); + count = threads.count(); + count_expected = QThreadPool::globalInstance()->maxThreadCount(); + if (count != count_expected) + QEXPECT_FAIL("", "QTBUG-23333", Abort); + QCOMPARE(count, count_expected); } // Set the finish flag immediately, this should give us one thread only. for (int i = 0; i < repeats; ++i) { + ThreadCountUser t(true /*finishImmediately*/); + t.startBlocking(); + int count = threads.count(); + if (count != 1) + QEXPECT_FAIL("", "QTBUG-23333", Abort); + QCOMPARE(count, 1); + (new ThreadCountUser(true /*finishImmediately*/))->startAsynchronously().waitForFinished(); - const auto count = threads.count(); + count = threads.count(); + if (count != 1) + QEXPECT_FAIL("", "QTBUG-23333", Abort); QCOMPARE(count, 1); QVERIFY(!threads.contains(QThread::currentThread())); } @@ -400,6 +437,7 @@ public: void tst_QtConcurrentThreadEngine::exceptions() { + // Asynchronous mode: { bool caught = false; try { @@ -412,6 +450,32 @@ void tst_QtConcurrentThreadEngine::exceptions() QVERIFY2(caught, "did not get exception"); } + // Blocking mode: + // test throwing the exception from a worker thread. + { + bool caught = false; + try { + QtConcurrentExceptionThrower e(QThread::currentThread()); + e.startBlocking(); + } catch (const QException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } + + // test throwing the exception from the main thread (different code path) + { + bool caught = false; + try { + QtConcurrentExceptionThrower e(0); + e.startBlocking(); + } catch (const QException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } + + // Asynchronous mode: { bool caught = false; try { @@ -423,6 +487,31 @@ void tst_QtConcurrentThreadEngine::exceptions() } QVERIFY2(caught, "did not get exception"); } + + // Blocking mode: + // test throwing the exception from a worker thread. + { + bool caught = false; + try { + UnrelatedExceptionThrower e(QThread::currentThread()); + e.startBlocking(); + } catch (const QUnhandledException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } + + // test throwing the exception from the main thread (different code path) + { + bool caught = false; + try { + UnrelatedExceptionThrower e(0); + e.startBlocking(); + } catch (const QUnhandledException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } } #endif diff --git a/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp b/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp index e1ea7a4552..90972caa57 100644 --- a/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp +++ b/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp @@ -117,6 +117,7 @@ private Q_SLOTS: void shouldPropagateDropAfterLastRow_data(); void shouldPropagateDropAfterLastRow(); void qtbug91788(); + void qtbug91878(); private: QStandardItemModel mod; @@ -843,6 +844,22 @@ void tst_QConcatenateTablesProxyModel::qtbug91788() QCOMPARE(proxyConcat.columnCount(), 0); } +void tst_QConcatenateTablesProxyModel::qtbug91878() +{ + QStandardItemModel m; + m.setRowCount(4); + m.setColumnCount(4); + + QConcatenateTablesProxyModel pm; + QSortFilterProxyModel proxyFilter; + proxyFilter.setSourceModel(&pm); + proxyFilter.setFilterFixedString("something"); + pm.addSourceModel(&m); // This should not assert + + QCOMPARE(pm.columnCount(), 4); + QCOMPARE(pm.rowCount(), 4); +} + QTEST_GUILESS_MAIN(tst_QConcatenateTablesProxyModel) #include "tst_qconcatenatetablesproxymodel.moc" diff --git a/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 25da013292..5ca7d3e3bb 100644 --- a/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -158,6 +158,7 @@ private slots: void nullReceiver(); void functorReferencesConnection(); void disconnectDisconnects(); + void declarativeData(); }; struct QObjectCreatedOnShutdown @@ -7689,5 +7690,81 @@ void tst_QObject::disconnectDisconnects() Q_STATIC_ASSERT(QtPrivate::HasQ_OBJECT_Macro::Value); Q_STATIC_ASSERT(!QtPrivate::HasQ_OBJECT_Macro::Value); +#ifdef QT_BUILD_INTERNAL +/* + Since QObjectPrivate stores the declarativeData pointer in a union with the pointer + to the currently destroyed child, calls to the QtDeclarative handlers need to be + correctly guarded. QTBUG-105286 +*/ +namespace QtDeclarative { +static QAbstractDeclarativeData *theData; + +static void destroyed(QAbstractDeclarativeData *data, QObject *) +{ + QCOMPARE(data, theData); +} +static void signalEmitted(QAbstractDeclarativeData *data, QObject *, int, void **) +{ + QCOMPARE(data, theData); +} +// we can't use QCOMPARE in the next two functions, as they don't return void +static int receivers(QAbstractDeclarativeData *data, const QObject *, int) +{ + QTest::qCompare(data, theData, "data", "theData", __FILE__, __LINE__); + return 0; +} +static bool isSignalConnected(QAbstractDeclarativeData *data, const QObject *, int) +{ + QTest::qCompare(data, theData, "data", "theData", __FILE__, __LINE__); + return true; +} + +class Object : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; + ~Object() + { + if (Object *p = static_cast(parent())) + p->emitSignal(); + } + + void emitSignal() + { + emit theSignal(); + } + +signals: + void theSignal(); +}; + +} +#endif + +void tst_QObject::declarativeData() +{ +#ifdef QT_BUILD_INTERNAL + QScopedValueRollback destroyed(QAbstractDeclarativeData::destroyed, + QtDeclarative::destroyed); + QScopedValueRollback signalEmitted(QAbstractDeclarativeData::signalEmitted, + QtDeclarative::signalEmitted); + QScopedValueRollback receivers(QAbstractDeclarativeData::receivers, + QtDeclarative::receivers); + QScopedValueRollback isSignalConnected(QAbstractDeclarativeData::isSignalConnected, + QtDeclarative::isSignalConnected); + + QtDeclarative::Object p; + QObjectPrivate *priv = QObjectPrivate::get(&p); + priv->declarativeData = QtDeclarative::theData = new QAbstractDeclarativeData; + + connect(&p, &QtDeclarative::Object::theSignal, &p, []{ + }); + + QtDeclarative::Object *child = new QtDeclarative::Object; + child->setParent(&p); +#endif +} + QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 0ea422ecbc..1a3256534b 100644 --- a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -70,15 +70,15 @@ static inline QString testSuiteWarning() QString result; QTextStream str(&result); - str << "\nCannot find the shared-mime-info test suite\nstarting from: " + str << "\nCannot find the shared-mime-info test suite\nin the parent of: " << QDir::toNativeSeparators(QDir::currentPath()) << "\n" "cd " << QDir::toNativeSeparators(QStringLiteral("tests/auto/corelib/mimetypes/qmimedatabase")) << "\n" - "wget http://cgit.freedesktop.org/xdg/shared-mime-info/snapshot/Release-1-10.zip\n" - "unzip Release-1-10.zip\n"; + "wget https://gitlab.freedesktop.org/xdg/shared-mime-info/-/archive/2.1/shared-mime-info-2.1.zip\n" + "unzip shared-mime-info-2.1.zip\n"; #ifdef Q_OS_WIN - str << "mkdir testfiles\nxcopy /s Release-1-10 s-m-i\n"; + str << "mkdir testfiles\nxcopy /s shared-mime-info-2.1 s-m-i\n"; #else - str << "ln -s Release-1-10 s-m-i\n"; + str << "ln -s shared-mime-info-2.1 s-m-i\n"; #endif return result; } @@ -154,7 +154,7 @@ void tst_QMimeDatabase::initTestCase() QVERIFY2(copyResourceFile(xmlFileName, xmlTargetFileName, &errorMessage), qPrintable(errorMessage)); #endif - m_testSuite = QFINDTESTDATA("s-m-i/tests"); + m_testSuite = QFINDTESTDATA("s-m-i/tests/mime-detection"); if (m_testSuite.isEmpty()) qWarning("%s", qPrintable(testSuiteWarning())); @@ -611,7 +611,7 @@ void tst_QMimeDatabase::allMimeTypes() QVERIFY(!lst.isEmpty()); // Hardcoding this is the only way to check both providers find the same number of mimetypes. - QCOMPARE(lst.count(), 779); + QCOMPARE(lst.count(), 811); foreach (const QMimeType &mime, lst) { const QString name = mime.name(); @@ -631,10 +631,9 @@ void tst_QMimeDatabase::suffixes_data() QTest::newRow("mimetype with a single pattern") << "application/pdf" << "*.pdf" << "pdf"; QTest::newRow("mimetype with multiple patterns") << "application/x-kpresenter" << "*.kpr;*.kpt" << "kpr"; - QTest::newRow("jpeg") << "image/jpeg" << "*.jpe;*.jpg;*.jpeg" << "jpeg"; - //if (KMimeType::sharedMimeInfoVersion() > KDE_MAKE_VERSION(0, 60, 0)) { - QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << "wp"; - //} + // The preferred suffix for image/jpeg is *.jpg, as per https://bugs.kde.org/show_bug.cgi?id=176737 + QTest::newRow("jpeg") << "image/jpeg" << "*.jpe;*.jpg;*.jpeg" << "jpg"; + QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << "wp"; QTest::newRow("oasis text mimetype") << "application/vnd.oasis.opendocument.text" << "*.odt" << "odt"; QTest::newRow("oasis presentation mimetype") << "application/vnd.oasis.opendocument.presentation" << "*.odp" << "odp"; QTest::newRow("mimetype with multiple patterns") << "text/plain" << "*.asc;*.txt;*,v" << "txt"; diff --git a/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp index 533fb1c8aa..63ce77d67f 100644 --- a/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +++ b/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -75,6 +75,7 @@ private slots: void arrayStringElements(); void arraySelfAssign_data() { basics_data(); } void arraySelfAssign(); + void arrayNested(); void mapDefaultInitialization(); void mapEmptyInitializerList(); @@ -93,6 +94,7 @@ private slots: void mapSelfAssign(); void mapComplexKeys_data() { basics_data(); } void mapComplexKeys(); + void mapNested(); void sorting(); @@ -1570,6 +1572,91 @@ void tst_QCborValue::mapComplexKeys() QVERIFY(!m.contains(tagged)); } +void tst_QCborValue::arrayNested() +{ + const QCborArray wrongArray = { false, nullptr, QCborValue() }; + { + QCborArray a1 = { 42, 47 }; + QCborArray a2 = { QCborValue(a1) }; + QCOMPARE(a2.size(), 1); + const QCborValue &first = qAsConst(a2).first(); + QVERIFY(first.isArray()); + QCOMPARE(first.toArray(wrongArray).size(), 2); + QCOMPARE(first.toArray(wrongArray).first(), 42); + QCOMPARE(first.toArray(wrongArray).last(), 47); + } + { + QCborArray a1 = { 42, 47 }; + QCborArray a2 = { QCborValue(a1) }; + QCOMPARE(a2.size(), 1); + QCborValueRef first = a2.first(); + QVERIFY(first.isArray()); + QCOMPARE(first.toArray(wrongArray).size(), 2); + QCOMPARE(first.toArray(wrongArray).first(), 42); + QCOMPARE(first.toArray(wrongArray).last(), 47); + } + + { + QCborArray a1; + a1 = { QCborValue(a1) }; // insert it into itself + QCOMPARE(a1.size(), 1); + const QCborValue &first = qAsConst(a1).first(); + QVERIFY(first.isArray()); + QCOMPARE(first, QCborArray()); + QCOMPARE(first.toArray(wrongArray), QCborArray()); + } + { + QCborArray a1; + a1 = { QCborValue(a1) }; // insert it into itself + QCborValueRef first = a1.first(); + QVERIFY(first.isArray()); + QCOMPARE(first, QCborArray()); + QCOMPARE(first.toArray(wrongArray), QCborArray()); + } + { + QCborArray a1; + a1.append(a1); // insert into itself + QCOMPARE(a1.size(), 1); + const QCborValue &first = qAsConst(a1).first(); + QVERIFY(first.isArray()); + QCOMPARE(first, QCborArray()); + QCOMPARE(first.toArray(), QCborArray()); + } + { + QCborArray a1; + a1.append(a1); // insert into itself + QCborValueRef first = a1.first(); + QVERIFY(first.isArray()); + QCOMPARE(first, QCborArray()); + QCOMPARE(first.toArray(), QCborArray()); + } +} + +void tst_QCborValue::mapNested() +{ + const QCborMap wrongMap = { { -1, false }, {-2, nullptr }, { -3, QCborValue() } }; + { + QCborMap m1 = { {1, 42}, {2, 47} }; + QCborMap m2 = { { QString(), m1 } }; + QCOMPARE(m2.size(), 1); + const QCborValue &first = m2.constBegin().value(); + QVERIFY(first.isMap()); + QCOMPARE(first.toMap(wrongMap).size(), 2); + QCOMPARE(first.toMap(wrongMap).begin().key(), 1); + QCOMPARE(first.toMap(wrongMap).begin().value(), 42); + } + + { + QCborMap m1; + m1 = { { QString(), QCborValue(m1) } }; // insert it into itself + QCOMPARE(m1.size(), 1); + const QCborValue &first = m1.constBegin().value(); + QVERIFY(first.isMap()); + QCOMPARE(first, QCborMap()); + QCOMPARE(first.toMap(wrongMap), QCborMap()); + } +} + void tst_QCborValue::sorting() { QCborValue vundef, vnull(nullptr); diff --git a/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index 412f092377..59b94179f4 100644 --- a/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -65,6 +65,7 @@ private slots: void isValidId_data(); void isValidId(); void malformed(); + void serialize(); // Backend tests void utcTest(); void icuTest(); @@ -951,6 +952,33 @@ void tst_QTimeZone::malformed() barf.offsetFromUtc(now); } +void tst_QTimeZone::serialize() +{ + int parts = 0; +#ifndef QT_NO_DEBUG_STREAM + qDebug() << QTimeZone(); // to verify no crash + parts++; +#endif +#ifndef QT_NO_DATASTREAM + QByteArray blob; + { + QDataStream stream(&blob, QIODevice::WriteOnly); + stream << QTimeZone("Europe/Oslo") << QTimeZone(420) << QTimeZone() << qint64(-1); + } + QDataStream stream(&blob, QIODevice::ReadOnly); + QTimeZone invalid, offset, oslo; + qint64 minusone; + stream >> oslo >> offset >> invalid >> minusone; + QCOMPARE(oslo, QTimeZone("Europe/Oslo")); + QCOMPARE(offset, QTimeZone(420)); + QVERIFY(!invalid.isValid()); + QCOMPARE(minusone, qint64(-1)); + parts++; +#endif + if (!parts) + QSKIP("No serialization enabled"); +} + void tst_QTimeZone::utcTest() { #ifdef QT_BUILD_INTERNAL diff --git a/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp b/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp index 05480c6dd2..70f4c75756 100644 --- a/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp +++ b/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp @@ -324,6 +324,13 @@ void tst_QDBusInterface::notValid() QVERIFY(!interface.isValid()); QVERIFY(!QMetaObject::invokeMethod(&interface, "ListNames", Qt::DirectConnection)); + + // With a connection, but empty/null service and path specified + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + QDBusInterface iface({}, {}, {}, con); + QVERIFY(!iface.isValid()); + QVERIFY(!QMetaObject::invokeMethod(&interface, "ListNames", Qt::DirectConnection)); } void tst_QDBusInterface::notValidDerived() diff --git a/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp b/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp index 874468c954..04ceb4ab65 100644 --- a/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp +++ b/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp @@ -507,6 +507,10 @@ void tst_QKeySequence::toStringFromKeycode_data() QTest::newRow("Ctrl+Alt+Num+Del") << QKeySequence(Qt::ControlModifier | Qt::AltModifier | Qt::KeypadModifier | Qt::Key_Delete) << "Ctrl+Alt+Num+Del"; QTest::newRow("Ctrl+Ins") << QKeySequence(Qt::ControlModifier | Qt::Key_Insert) << "Ctrl+Ins"; QTest::newRow("Ctrl+Num+Ins(1)") << QKeySequence(Qt::Key_Insert | Qt::KeypadModifier | Qt::ControlModifier) << "Ctrl+Num+Ins"; + QTest::newRow("Ctrl") << QKeySequence(Qt::Key_Control) << "Control"; + QTest::newRow("Alt") << QKeySequence(Qt::Key_Alt) << "Alt"; + QTest::newRow("Shift") << QKeySequence(Qt::Key_Shift) << "Shift"; + QTest::newRow("Meta") << QKeySequence(Qt::Key_Meta) << "Meta"; } void tst_QKeySequence::toStringFromKeycode() diff --git a/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp index 15e0ecadaa..b4eca74283 100644 --- a/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp +++ b/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp @@ -81,6 +81,8 @@ private slots: void registerOpenTypePreferredNamesSystem(); void registerOpenTypePreferredNamesApplication(); + void stretchRespected(); + private: QString m_ledFont; QString m_testFont; @@ -355,6 +357,28 @@ static QString testString() return QStringLiteral("foo bar"); } +void tst_QFontDatabase::stretchRespected() +{ + int italicId = QFontDatabase::addApplicationFont(m_testFontItalic); + QVERIFY(italicId != -1); + + QVERIFY(!QFontDatabase::applicationFontFamilies(italicId).isEmpty()); + + QString italicFontName = QFontDatabase::applicationFontFamilies(italicId).first(); + + QFont italicFont = QFontDatabase().font(italicFontName, + QString::fromLatin1("Italic"), 14); + QVERIFY(italicFont.italic()); + + QFont italicStretchedFont = italicFont; + italicStretchedFont.setStretch( 400 ); + + QVERIFY(QFontMetricsF(italicFont).horizontalAdvance(QStringLiteral("foobar")) < + QFontMetricsF(italicStretchedFont).horizontalAdvance(QStringLiteral("foobar"))); + + QFontDatabase::removeApplicationFont(italicId); +} + void tst_QFontDatabase::condensedFontWidthNoFontMerging() { int regularFontId = QFontDatabase::addApplicationFont(m_testFont); diff --git a/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 278139f2bd..7eb23b3344 100644 --- a/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -281,6 +281,7 @@ private Q_SLOTS: void ioGetFromFileSpecial(); void ioGetFromFile_data(); void ioGetFromFile(); + void ioGetFromFileUrl(); void ioGetFromFtp_data(); void ioGetFromFtp(); void ioGetFromFtpWithReuse(); @@ -3300,6 +3301,18 @@ void tst_QNetworkReply::ioGetFromFile() QCOMPARE(reader.data, data); } +void tst_QNetworkReply::ioGetFromFileUrl() +{ + // This immediately fails on non-windows platforms: + QNetworkRequest request(QUrl("file://unc-server/some/path")); + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy finishedSpy(reply.get(), &QNetworkReply::finished); + // QTBUG-105618: This would, on non-Windows platforms, never happen because the signal + // was emitted before the constructor finished, leaving no chance at all to connect to the + // signal + QVERIFY(finishedSpy.wait()); +} + void tst_QNetworkReply::ioGetFromFtp_data() { QTest::addColumn("fileName"); diff --git a/qtbase/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp b/qtbase/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp index 7ba3715e13..752aa122f6 100644 --- a/qtbase/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp +++ b/qtbase/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp @@ -179,6 +179,7 @@ void tst_QAccessibilityLinux::initTestCase() QVERIFY(!address.isEmpty()); m_window = new AccessibleTestWindow(); + m_window->setObjectName("mainWindow"_L1); m_window->show(); QVERIFY(QTest::qWaitForWindowExposed(m_window)); @@ -236,8 +237,11 @@ bool hasState(QDBusInterface *interface, AtspiStateType state) void tst_QAccessibilityLinux::testLabel() { QLabel *l = new QLabel(m_window); + l->setObjectName("theObjectName"_L1); l->setText("Hello A11y"); m_window->addWidget(l); + auto a11yEmpty = new QLabel(m_window); + m_window->addWidget(l); // Application QCOMPARE(getParent(mainWindow), QLatin1String(ATSPI_DBUS_PATH_ROOT)); @@ -249,6 +253,8 @@ void tst_QAccessibilityLinux::testLabel() QCOMPARE(getChildren(labelInterface).count(), 0); QCOMPARE(labelInterface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("label")); QCOMPARE(labelInterface->call(QDBus::Block, "GetRole").arguments().first().toUInt(), 29u); + QCOMPARE(labelInterface->call(QDBus::Block, "GetAccessibleId").arguments().first().toString(), + QLatin1String("mainWindow.theObjectName")); QCOMPARE(getParent(labelInterface), mainWindow->path()); QVERIFY(!hasState(labelInterface, ATSPI_STATE_EDITABLE)); QVERIFY(hasState(labelInterface, ATSPI_STATE_READ_ONLY)); @@ -256,7 +262,12 @@ void tst_QAccessibilityLinux::testLabel() l->setText("New text"); QCOMPARE(labelInterface->property("Name").toString(), l->text()); + auto *a11yEmptyInterface = getInterface(children.at(1), "org.a11y.atspi.Accessible"); + QCOMPARE(a11yEmptyInterface->call(QDBus::Block, "GetAccessibleId").arguments().first().toString(), + QLatin1String("mainWindow.QLabel")); + m_window->clearChildren(); + delete a11yEmptyInterface; delete labelInterface; } diff --git a/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp b/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp index 4aa3f8d60b..d2050a61aa 100644 --- a/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp +++ b/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp @@ -115,6 +115,10 @@ void tst_QAbstractItemModelTester::standardItemModelZeroColumns() // QTBUG-92886 model.insertRows(0, 5); model.removeRows(1, 2); + + const QModelIndex parentIndex = model.index(0, 0); + model.insertRows(0, 5, parentIndex); + model.removeRows(1, 2, parentIndex); } void tst_QAbstractItemModelTester::testInsertThroughProxy() diff --git a/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json b/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json index 18282505e4..ce518e78fb 100644 --- a/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json +++ b/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json @@ -668,40 +668,6 @@ "inputFile": "task192552.h", "outputRevision": 67 }, - { - "classes": [ - { - "className": "InlineSlotsWithThrowDeclaration", - "object": true, - "qualifiedClassName": "InlineSlotsWithThrowDeclaration", - "slots": [ - { - "access": "public", - "name": "a", - "returnType": "void" - }, - { - "access": "public", - "name": "b", - "returnType": "void" - }, - { - "access": "public", - "name": "c", - "returnType": "void" - } - ], - "superClasses": [ - { - "access": "public", - "name": "QObject" - } - ] - } - ], - "inputFile": "task189996.h", - "outputRevision": 67 - }, { "classes": [ { diff --git a/qtbase/tests/auto/tools/moc/moc.pro b/qtbase/tests/auto/tools/moc/moc.pro index c324b3a8cd..4aceb78dc0 100644 --- a/qtbase/tests/auto/tools/moc/moc.pro +++ b/qtbase/tests/auto/tools/moc/moc.pro @@ -15,7 +15,7 @@ cross_compile: DEFINES += MOC_CROSS_COMPILED HEADERS += using-namespaces.h no-keywords.h task87883.h c-comments.h backslash-newlines.h oldstyle-casts.h \ slots-with-void-template.h qinvokable.h namespaced-flags.h trigraphs.h \ escapes-in-string-literals.h cstyle-enums.h qprivateslots.h gadgetwithnoenums.h \ - dir-in-include-path.h single_function_keyword.h task192552.h task189996.h \ + dir-in-include-path.h single_function_keyword.h task192552.h \ task234909.h task240368.h pure-virtual-signals.h cxx11-enums.h \ cxx11-final-classes.h \ cxx11-explicit-override-control.h \ diff --git a/qtbase/tests/auto/tools/moc/task189996.h b/qtbase/tests/auto/tools/moc/task189996.h deleted file mode 100644 index ba9450c271..0000000000 --- a/qtbase/tests/auto/tools/moc/task189996.h +++ /dev/null @@ -1,45 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -// inline functions can have throw declarations - -#ifndef TASK189996_H -#define TASK189996_H - -#include - -class InlineSlotsWithThrowDeclaration : public QObject -{ - Q_OBJECT - -public slots: - void a() noexcept { } - void b() const noexcept { } - void c() noexcept; -}; - -#endif diff --git a/qtbase/tests/auto/tools/moc/tst_moc.cpp b/qtbase/tests/auto/tools/moc/tst_moc.cpp index cc465a213a..a9ab6ec4f3 100644 --- a/qtbase/tests/auto/tools/moc/tst_moc.cpp +++ b/qtbase/tests/auto/tools/moc/tst_moc.cpp @@ -671,7 +671,6 @@ private slots: void templateGtGt(); void qprivateslots(); void qprivateproperties(); - void inlineSlotsWithThrowDeclaration(); void warnOnPropertyWithoutREAD(); void constructors(); void typenameWithUnsigned(); @@ -816,7 +815,7 @@ void tst_Moc::oldStyleCasts() QStringList args; args << "-c" << "-x" << "c++" << "-Wold-style-cast" << "-I" << "." - << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++11" << "-"; + << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", args); QVERIFY(proc.waitForStarted()); proc.write(mocOut); @@ -886,7 +885,7 @@ void tst_Moc::inputFileNameWithDotsButNoExtension() QStringList args; args << "-c" << "-x" << "c++" << "-I" << ".." - << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++11" << "-"; + << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", args); QVERIFY(proc.waitForStarted()); proc.write(mocOut); @@ -1166,7 +1165,7 @@ void tst_Moc::ignoreOptionClashes() QStringList gccArgs; gccArgs << "-c" << "-x" << "c++" << "-I" << ".." << "-I" << qtIncludePath << "-I" << includeDir << "-o" << "/dev/null" - << "-fPIC" << "-std=c++11" << "-"; + << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", gccArgs); QVERIFY(proc.waitForStarted()); proc.write(mocOut); @@ -1585,19 +1584,6 @@ void tst_Moc::qprivateproperties() } -#include "task189996.h" - -void InlineSlotsWithThrowDeclaration::c() noexcept {} - -void tst_Moc::inlineSlotsWithThrowDeclaration() -{ - InlineSlotsWithThrowDeclaration tst; - const QMetaObject *mobj = tst.metaObject(); - QVERIFY(mobj->indexOfSlot("a()") != -1); - QVERIFY(mobj->indexOfSlot("b()") != -1); - QVERIFY(mobj->indexOfSlot("c()") != -1); -} - void tst_Moc::warnOnPropertyWithoutREAD() { #ifdef MOC_CROSS_COMPILED @@ -1859,7 +1845,7 @@ void tst_Moc::notifyError() QStringList args; args << "-c" << "-x" << "c++" << "-I" << "." - << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++11" << "-"; + << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", args); QVERIFY(proc.waitForStarted()); proc.write(mocOut); diff --git a/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp b/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp index 761357b252..06bb706074 100644 --- a/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp +++ b/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp @@ -397,6 +397,7 @@ private slots: void checkHeaderMinSize(); void resizeToContents(); + void resizeToContentsSpans(); void tabFocus(); void bigModel(); @@ -3721,6 +3722,70 @@ void tst_QTableView::resizeToContents() } + +class SpanModel : public QAbstractTableModel +{ +public: + SpanModel(bool sectionsMoved) + : _sectionsMoved(sectionsMoved) + {} + int columnCount(const QModelIndex & = {}) const override { return 2; } + int rowCount(const QModelIndex & = {}) const override { return 1; } + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override + { + if (role != Qt::DisplayRole) + return QVariant(); + const int col = _sectionsMoved ? 1 - idx.column() : idx.column(); + if (col == 0) + return "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + return QVariant(); + } +private: + bool _sectionsMoved; +}; + + +void tst_QTableView::resizeToContentsSpans() +{ + SpanModel model1(false); + SpanModel model2(true); + QTableView view1, view2, view3; + view1.setModel(&model1); + view2.setModel(&model2); + view2.horizontalHeader()->moveSection(0, 1); + view3.setModel(&model1); + + view1.setSpan(0, 0, 1, 2); + view2.setSpan(0, 1, 1, 2); + view1.show(); + view2.show(); + view3.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view1)); + QVERIFY(QTest::qWaitForWindowExposed(&view2)); + QVERIFY(QTest::qWaitForWindowExposed(&view3)); + view1.setColumnWidth(0, 100); + view1.setColumnWidth(1, 100); + view2.setColumnWidth(0, 100); + view2.setColumnWidth(1, 100); + view3.setColumnWidth(0, 200); + + view1.resizeRowToContents(0); + view2.resizeRowToContents(0); + view3.resizeRowToContents(0); + QCOMPARE(view1.rowHeight(0), view3.rowHeight(0)); + QCOMPARE(view2.rowHeight(0), view3.rowHeight(0)); + + view3.resizeColumnToContents(0); + view3.resizeRowToContents(0); + // height should be only 1 text line for easy testing + view1.setRowHeight(0, view3.verticalHeader()->sectionSize(0)); + view2.setRowHeight(0, view3.verticalHeader()->sectionSize(0)); + view1.resizeColumnToContents(0); + view2.resizeColumnToContents(1); + QCOMPARE(view1.columnWidth(0), view3.columnWidth(0) - view1.columnWidth(1)); + QCOMPARE(view2.columnWidth(0), view3.columnWidth(0) - view2.columnWidth(1)); +} + QT_BEGIN_NAMESPACE extern bool Q_WIDGETS_EXPORT qt_tab_all_widgets(); // qapplication.cpp QT_END_NAMESPACE diff --git a/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp b/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp index eb108a40de..dca5528c1b 100644 --- a/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp +++ b/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp @@ -41,6 +41,7 @@ #include #include +#include class tst_QAbstractButton : public QObject { @@ -76,6 +77,8 @@ private slots: void keyNavigation(); #endif + void buttonPressKeys(); + protected slots: void onClicked(); void onToggled( bool on ); @@ -269,7 +272,13 @@ void tst_QAbstractButton::setAutoRepeat() QCOMPARE(press_count, click_count); QVERIFY(click_count > 1); break; - case 4: + case 4: { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + QSKIP("platform theme has Key_Enter in ButtonPressKeys"); + } // check that pressing ENTER has no effect when autorepeat is false testWidget->setDown( false ); testWidget->setAutoRepeat( false ); @@ -286,7 +295,14 @@ void tst_QAbstractButton::setAutoRepeat() QVERIFY( click_count == 0 ); break; - case 5: + } + case 5: { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + QSKIP("platform theme has Key_Enter in ButtonPressKeys"); + } // check that pressing ENTER has no effect when autorepeat is true testWidget->setDown( false ); testWidget->setAutoRepeat( true ); @@ -304,6 +320,7 @@ void tst_QAbstractButton::setAutoRepeat() QVERIFY( click_count == 0 ); break; + } case 6: // verify autorepeat is off by default. MyButton tmp( 0); @@ -651,5 +668,16 @@ void tst_QAbstractButton::keyNavigation() } #endif +void tst_QAbstractButton::buttonPressKeys() +{ + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + for (int i = 0; i < buttonPressKeys.length(); ++i) { + QTest::keyClick(testWidget, buttonPressKeys[i]); + QCOMPARE(click_count, i + 1); + } +} + QTEST_MAIN(tst_QAbstractButton) #include "tst_qabstractbutton.moc" diff --git a/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index 7af60ed757..46b5af6d63 100644 --- a/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -168,6 +168,7 @@ private slots: void checkMenuItemPosWhenStyleSheetIsSet(); void checkEmbeddedLineEditWhenStyleSheetIsSet(); void propagateStyleChanges(); + void buttonPressKeys(); private: PlatformInputContext m_platformInputContext; @@ -3642,5 +3643,24 @@ void tst_QComboBox::propagateStyleChanges() QVERIFY(frameStyle.inquired); } +void tst_QComboBox::buttonPressKeys() +{ + QComboBox comboBox; + comboBox.setEditable(false); + comboBox.addItem(QString::number(1)); + comboBox.addItem(QString::number(2)); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + for (int i = 0; i < buttonPressKeys.length(); ++i) { + QTest::keyClick(&comboBox, buttonPressKeys[i]); + // On some platforms, a window will not be immediately visible, + // but take some event-loop iterations to complete. + // Using QTRY_VERIFY to deal with that. + QTRY_VERIFY(comboBox.view()->isVisible()); + comboBox.hidePopup(); + } +} + QTEST_MAIN(tst_QComboBox) #include "tst_qcombobox.moc" diff --git a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro index be3cfcd104..c228fdfcca 100644 --- a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro +++ b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qcommandlinkbutton -QT += widgets testlib +QT += widgets testlib gui-private SOURCES += tst_qcommandlinkbutton.cpp diff --git a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp index 0044d33c66..4cf06296e4 100644 --- a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp +++ b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp @@ -40,6 +40,9 @@ #include #include +#include +#include + class tst_QCommandLinkButton : public QObject { Q_OBJECT @@ -223,6 +226,13 @@ void tst_QCommandLinkButton::setAutoRepeat() // check that pressing ENTER has no effect resetCounters(); testWidget->setDown( false ); + // Skip after reset if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } testWidget->setAutoRepeat( false ); QTest::keyPress( testWidget, Qt::Key_Enter ); @@ -255,6 +265,14 @@ void tst_QCommandLinkButton::pressed() QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); + // Skip if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } + QTest::keyPress( testWidget,Qt::Key_Enter ); QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); diff --git a/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro b/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro index 4a5e76ff65..a235fa1fac 100644 --- a/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro +++ b/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qgroupbox -QT += widgets testlib +QT += widgets testlib gui-private SOURCES += tst_qgroupbox.cpp diff --git a/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp b/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp index 4fb5d262ca..d8d7562b73 100644 --- a/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp +++ b/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp @@ -35,6 +35,9 @@ #include #include +#include +#include + #include "qgroupbox.h" class tst_QGroupBox : public QObject @@ -69,6 +72,7 @@ private slots: void propagateFocus(); void task_QTBUG_19170_ignoreMouseReleaseEvent(); void task_QTBUG_15519_propagateMouseEvents(); + void buttonPressKeys(); private: bool checked; @@ -610,6 +614,20 @@ void tst_QGroupBox::task_QTBUG_15519_propagateMouseEvents() QCOMPARE(parent.mouseMoved, true); } +void tst_QGroupBox::buttonPressKeys() +{ + QGroupBox groupBox; + groupBox.setCheckable(true); + QSignalSpy clickedSpy(&groupBox, &QGroupBox::clicked); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + for (int i = 0; i < buttonPressKeys.length(); ++i) { + QTest::keyClick(&groupBox, buttonPressKeys[i]); + QCOMPARE(clickedSpy.length(), i + 1); + } +} + void tst_QGroupBox::sendMouseMoveEvent(QWidget *widget, const QPoint &localPos) { // Send a MouseMove event without actually moving the pointer diff --git a/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro b/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro index 353ad06ca2..e55f6148f2 100644 --- a/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro +++ b/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qpushbutton -QT += widgets testlib +QT += widgets testlib gui-private SOURCES += tst_qpushbutton.cpp diff --git a/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp b/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp index e818514a79..4043e9326a 100644 --- a/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +++ b/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp @@ -41,6 +41,9 @@ #include #include +#include +#include + class tst_QPushButton : public QObject { Q_OBJECT @@ -212,6 +215,13 @@ void tst_QPushButton::autoRepeat() // check that pressing ENTER has no effect resetCounters(); testWidget->setDown( false ); + // Skip after reset if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } testWidget->setAutoRepeat( false ); QTest::keyPress( testWidget, Qt::Key_Enter ); @@ -247,6 +257,14 @@ void tst_QPushButton::pressed() QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); + // Skip if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } + QTest::keyPress( testWidget,Qt::Key_Enter ); QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); diff --git a/qtbase/tests/manual/rhi/cubemap_render/buildshader.bat b/qtbase/tests/manual/rhi/cubemap_render/buildshader.bat old mode 100755 new mode 100644 Submodule qtconnectivity e2dd3230..eeaf42bc: diff --git a/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp b/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp index 60a64375..fcd0fbb2 100644 --- a/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp +++ b/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp @@ -791,10 +791,15 @@ void QBluetoothSocket::close() Set the socket to use \a socketDescriptor with a type of \a socketType, which is in state, \a socketState, and mode, \a openMode. + The set socket descriptor is considered owned by the QBluetoothSocket + and may be e.g. closed once finished. + Returns true on success */ - +// ### Qt 7 consider making this function private. The qbluetoothsocket_bluez backend is the +// the only backend providing publicly accessible support for this. Other backends implement +// similarly named, but private, overload bool QBluetoothSocket::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, SocketState socketState, OpenMode openMode) { diff --git a/qtconnectivity/src/bluetooth/qbluetoothsocket_bluez.cpp b/qtconnectivity/src/bluetooth/qbluetoothsocket_bluez.cpp index da92082d..6d27c7a2 100644 --- a/qtconnectivity/src/bluetooth/qbluetoothsocket_bluez.cpp +++ b/qtconnectivity/src/bluetooth/qbluetoothsocket_bluez.cpp @@ -75,6 +75,10 @@ QBluetoothSocketPrivateBluez::~QBluetoothSocketPrivateBluez() readNotifier = nullptr; delete connectWriteNotifier; connectWriteNotifier = nullptr; + + // If the socket wasn't closed/aborted make sure we free the socket file descriptor + if (socket != -1) + QT_CLOSE(socket); } bool QBluetoothSocketPrivateBluez::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) @@ -658,6 +662,8 @@ qint64 QBluetoothSocketPrivateBluez::readData(char *data, qint64 maxSize) void QBluetoothSocketPrivateBluez::close() { + // If we have pending data on the write buffer, wait until it has been written, + // after which this close() will be called again if (txBuffer.size() > 0) connectWriteNotifier->setEnabled(true); else diff --git a/qtconnectivity/src/tools/sdpscanner/main.cpp b/qtconnectivity/src/tools/sdpscanner/main.cpp index 7e09ca6e..c39ff8f3 100644 --- a/qtconnectivity/src/tools/sdpscanner/main.cpp +++ b/qtconnectivity/src/tools/sdpscanner/main.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -159,7 +160,9 @@ static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray & break; } else if (!isprint(text[i])) { hasNonPrintableChar = true; - text.resize(text.indexOf('\0')); // cut trailing content + const auto firstNullIdx = text.indexOf('\0'); + if (firstNullIdx > 0) + text.resize(firstNullIdx); // cut trailing content break; } } @@ -211,11 +214,17 @@ static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray & case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: - strncpy(snBuffer, data->val.str, data->unitSize - 1); + { xmlOutput.append("val.str, qstrnlen(data->val.str, data->unitSize)); + const QUrl url = QUrl::fromEncoded(urlData); + // Encoded url %-encodes all of the XML special characters except '&', + // so we need to do that manually + xmlOutput.append(url.toEncoded().replace('&', "&")); xmlOutput.append("\"/>\n"); break; + } default: fprintf(stderr, "Unknown dtd type\n"); } Submodule qtdeclarative 540c4e4a..5352f113: diff --git a/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp b/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp index 88c0e9e60e..61a9dc36f8 100644 --- a/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp +++ b/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp @@ -82,9 +82,9 @@ void QContinuingAnimationGroupJob::updateState(QAbstractAnimationJob::State newS return; } for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { - resetUncontrolledAnimationFinishTime(animation); + RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation)); animation->setDirection(m_direction); - animation->start(); + RETURN_IF_DELETED(animation->start()); } break; } diff --git a/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp b/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp index 420a934ba2..a828d0e234 100644 --- a/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp +++ b/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp @@ -144,10 +144,10 @@ void QParallelAnimationGroupJob::updateState(QAbstractAnimationJob::State newSta animation->stop(); m_previousLoop = m_direction == Forward ? 0 : m_loopCount - 1; } - resetUncontrolledAnimationFinishTime(animation); + RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation)); animation->setDirection(m_direction); if (shouldAnimationStart(animation, oldState == Stopped)) - animation->start(); + RETURN_IF_DELETED(animation->start()); } break; } diff --git a/qtdeclarative/src/qml/jsruntime/qv4function.cpp b/qtdeclarative/src/qml/jsruntime/qv4function.cpp index aeb4835c40..9082628a1a 100644 --- a/qtdeclarative/src/qml/jsruntime/qv4function.cpp +++ b/qtdeclarative/src/qml/jsruntime/qv4function.cpp @@ -136,7 +136,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); - ExecutableCompilationUnit *cu = td->compilationUnit(); - myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + if (ExecutableCompilationUnit *cu = td->compilationUnit()) + myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + else + return Encode(false); // It seems myQmlType has some errors, so we could not compile it. } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); } diff --git a/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp b/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp index aa9f4bc1bd..09503a7e99 100644 --- a/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp +++ b/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp @@ -254,7 +254,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() if (!pd) return; - if (pd->notifyIndex() != -1) + if (pd->notifyIndex() != -1 && ctxt->engine) connect(target, pd->notifyIndex(), ctxt->engine); } diff --git a/qtdeclarative/src/qml/types/qqmlconnections.cpp b/qtdeclarative/src/qml/types/qqmlconnections.cpp index 29ed62cd39..aba930dfe1 100644 --- a/qtdeclarative/src/qml/types/qqmlconnections.cpp +++ b/qtdeclarative/src/qml/types/qqmlconnections.cpp @@ -338,7 +338,7 @@ void QQmlConnections::connectSignalsToMethods() && propName.at(2).isUpper()) { qmlWarning(this) << tr("Detected function \"%1\" in Connections element. " "This is probably intended to be a signal handler but no " - "signal of the target matches the name.").arg(propName); + "signal of the \"%2\" target matches the name.").arg(propName).arg(target->metaObject()->className()); } } } diff --git a/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp b/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp index 4fcff70de6..5b7e767ae2 100644 --- a/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp +++ b/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp @@ -389,6 +389,12 @@ void QQmlDelegateModelPrivate::connectToAbstractItemModel() q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsInserted(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_columnsInserted(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_columnsRemoved(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), + q, QQmlDelegateModel, SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int))); qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), @@ -413,6 +419,12 @@ void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(columnsInserted(QModelIndex,int,int)), q, + SLOT(_q_columnsInserted(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(columnsRemoved(QModelIndex,int,int)), q, + SLOT(_q_columnsRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), q, + SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int))); QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), @@ -1871,10 +1883,15 @@ void QQmlDelegateModelPrivate::emitChanges() for (int i = 1; i < m_groupCount; ++i) QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - auto cacheCopy = m_cache; // deliberate; emitChanges may alter m_cache - for (QQmlDelegateModelItem *cacheItem : qAsConst(cacheCopy)) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); + // emitChanges may alter m_cache and delete items + QVarLengthArray> attachedObjects; + attachedObjects.reserve(m_cache.length()); + for (const QQmlDelegateModelItem *cacheItem : qAsConst(m_cache)) + attachedObjects.append(cacheItem->attached); + + for (const QPointer &attached : qAsConst(attachedObjects)) { + if (attached && attached->m_cacheItem) + attached->emitChanges(); } } @@ -1974,6 +1991,38 @@ void QQmlDelegateModel::_q_rowsMoved( } } +void QQmlDelegateModel::_q_columnsInserted(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + Q_UNUSED(end); + if (parent == d->m_adaptorModel.rootIndex && begin == 0) { + // mark all items as changed + _q_itemsChanged(0, d->m_count, QVector()); + } +} + +void QQmlDelegateModel::_q_columnsRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + Q_UNUSED(end); + if (parent == d->m_adaptorModel.rootIndex && begin == 0) { + // mark all items as changed + _q_itemsChanged(0, d->m_count, QVector()); + } +} + +void QQmlDelegateModel::_q_columnsMoved(const QModelIndex &parent, int start, int end, + const QModelIndex &destination, int column) +{ + Q_D(QQmlDelegateModel); + Q_UNUSED(end); + if ((parent == d->m_adaptorModel.rootIndex && start == 0) + || (destination == d->m_adaptorModel.rootIndex && column == 0)) { + // mark all items as changed + _q_itemsChanged(0, d->m_count, QVector()); + } +} + void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) { Q_D(QQmlDelegateModel); @@ -2663,20 +2712,24 @@ void QQmlDelegateModelAttached::emitChanges() m_previousGroups = m_cacheItem->groups; int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + const int groupCount = m_cacheItem->metaType->groupCount; + for (int i = 1; i < groupCount; ++i) { if (m_previousIndex[i] != m_currentIndex[i]) { m_previousIndex[i] = m_currentIndex[i]; indexChanges |= (1 << i); } } + // Don't access m_cacheItem anymore once we've started sending signals. + // We don't own it and someone might delete it. + int notifierId = 0; const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + for (int i = 1; i < groupCount; ++i, ++notifierId) { if (groupChanges & (1 << i)) QMetaObject::activate(this, meta, notifierId, nullptr); } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + for (int i = 1; i < groupCount; ++i, ++notifierId) { if (indexChanges & (1 << i)) QMetaObject::activate(this, meta, notifierId, nullptr); } diff --git a/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h b/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h index 8aab4badca..d140bfbaaf 100644 --- a/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h +++ b/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h @@ -152,6 +152,9 @@ private Q_SLOTS: void _q_itemsMoved(int from, int to, int count); void _q_modelReset(); void _q_rowsInserted(const QModelIndex &,int,int); + void _q_columnsInserted(const QModelIndex &, int, int); + void _q_columnsRemoved(const QModelIndex &, int, int); + void _q_columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int); void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); void _q_rowsRemoved(const QModelIndex &,int,int); void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); diff --git a/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp b/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp index ae1954ae8d..99e6eff7c3 100644 --- a/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +++ b/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp @@ -46,6 +46,7 @@ #include "QtQuick/private/qquicktextinput_p.h" #include "QtQuick/private/qquickaccessibleattached_p.h" #include "QtQuick/qquicktextdocument.h" +#include "QtQuick/qquickrendercontrol.h" QT_BEGIN_NAMESPACE #if QT_CONFIG(accessibility) @@ -57,7 +58,19 @@ QAccessibleQuickItem::QAccessibleQuickItem(QQuickItem *item) QWindow *QAccessibleQuickItem::window() const { - return item()->window(); + QQuickWindow *window = item()->window(); + + // For QQuickWidget the above window will be the offscreen QQuickWindow, + // which is not a part of the accessibility tree. Detect this case and + // return the window for the QQuickWidget instead. + if (window && !window->handle()) { + if (QQuickRenderControl *renderControl = QQuickWindowPrivate::get(window)->renderControl) { + if (QWindow *renderWindow = renderControl->renderWindow(nullptr)) + return renderWindow; + } + } + + return window; } int QAccessibleQuickItem::childCount() const @@ -113,19 +126,15 @@ QAccessibleInterface *QAccessibleQuickItem::childAt(int x, int y) const QAccessibleInterface *QAccessibleQuickItem::parent() const { QQuickItem *parent = item()->parentItem(); - QQuickWindow *window = item()->window(); - QQuickItem *ci = window ? window->contentItem() : nullptr; + QQuickWindow *itemWindow = item()->window(); + QQuickItem *ci = itemWindow ? itemWindow->contentItem() : nullptr; while (parent && !QQuickItemPrivate::get(parent)->isAccessible && parent != ci) parent = parent->parentItem(); if (parent) { if (parent == ci) { - // Jump out to the scene widget if the parent is the root item. - // There are two root items, QQuickWindow::rootItem and - // QQuickView::declarativeRoot. The former is the true root item, - // but is not a part of the accessibility tree. Check if we hit - // it here and return an interface for the scene instead. - return QAccessible::queryAccessibleInterface(window); + // Jump out to the window if the parent is the root item + return QAccessible::queryAccessibleInterface(window()); } else { while (parent && !parent->d_func()->isAccessible) parent = parent->parentItem(); @@ -193,7 +202,7 @@ QAccessible::State QAccessibleQuickItem::state() const QRect viewRect_ = viewRect(); QRect itemRect = rect(); - if (viewRect_.isNull() || itemRect.isNull() || !item()->window() || !item()->window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) + if (viewRect_.isNull() || itemRect.isNull() || !window() || !window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) state.invisible = true; if (!viewRect_.intersects(itemRect)) state.offscreen = true; @@ -206,6 +215,10 @@ QAccessible::State QAccessibleQuickItem::state() const if (role() == QAccessible::EditableText) if (auto ti = qobject_cast(item())) state.passwordEdit = ti->echoMode() != QQuickTextInput::Normal; + if (!item()->isEnabled()) { + state.focusable = false; + state.disabled = true; + } return state; } @@ -217,7 +230,7 @@ QAccessible::Role QAccessibleQuickItem::role() const QAccessible::Role role = QAccessible::NoRole; if (item()) - role = QQuickItemPrivate::get(item())->accessibleRole(); + role = QQuickItemPrivate::get(item())->effectiveAccessibleRole(); if (role == QAccessible::NoRole) { if (qobject_cast(const_cast(item()))) role = QAccessible::StaticText; diff --git a/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h b/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h index 39ffcaf39c..8baa01330c 100644 --- a/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h +++ b/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE #if QT_CONFIG(accessibility) -class QAccessibleQuickWindow : public QAccessibleObject +class Q_QUICK_EXPORT QAccessibleQuickWindow : public QAccessibleObject { public: QAccessibleQuickWindow(QQuickWindow *object); diff --git a/qtdeclarative/src/quick/items/qquickdrag.cpp b/qtdeclarative/src/quick/items/qquickdrag.cpp index 8321fcfeed..383078b3b9 100644 --- a/qtdeclarative/src/quick/items/qquickdrag.cpp +++ b/qtdeclarative/src/quick/items/qquickdrag.cpp @@ -481,7 +481,9 @@ void QQuickDragAttached::setKeys(const QStringList &keys) \qmlattachedproperty stringlist QtQuick::Drag::mimeData \since 5.2 - This property holds a map of mimeData that is used during startDrag. + This property holds a map from mime type to data that is used during startDrag. + The mime data needs to be a \c string, or an \c ArrayBuffer with the data encoded + according to the mime type. */ QVariantMap QQuickDragAttached::mimeData() const @@ -766,8 +768,12 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct QDrag *drag = new QDrag(source ? source : q); QMimeData *mimeData = new QMimeData(); - for (auto it = externalMimeData.cbegin(), end = externalMimeData.cend(); it != end; ++it) - mimeData->setData(it.key(), it.value().toString().toUtf8()); + for (auto it = externalMimeData.cbegin(), end = externalMimeData.cend(); it != end; ++it) { + if (static_cast(it.value().type()) == QMetaType::QByteArray) + mimeData->setData(it.key(), it.value().toByteArray()); + else + mimeData->setData(it.key(), it.value().toString().toUtf8()); + } drag->setMimeData(mimeData); if (pixmapLoader.isReady()) { diff --git a/qtdeclarative/src/quick/items/qquickitem.cpp b/qtdeclarative/src/quick/items/qquickitem.cpp index 33da9762d3..9e8b289376 100644 --- a/qtdeclarative/src/quick/items/qquickitem.cpp +++ b/qtdeclarative/src/quick/items/qquickitem.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -2326,6 +2327,7 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent) QQuickItem::~QQuickItem() { Q_D(QQuickItem); + d->inDestructor = true; if (d->windowRefCount > 1) d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow(). @@ -2398,7 +2400,7 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item) return true; #if QT_CONFIG(accessibility) - QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole(); + QAccessible::Role role = QQuickItemPrivate::get(item)->effectiveAccessibleRole(); if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) { return true; } else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) { @@ -2526,6 +2528,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo QQuickItem *current = item; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem; + QDuplicateTracker cycleDetector; do { qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from; @@ -2592,7 +2595,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo // traversed all of the chain (by compare the [current] item with [startItem]) // Since the [startItem] might be promoted to its parent if it is invisible, // we still have to check [current] item with original start item - if ((current == startItem || current == originalStartItem) && from == firstFromItem) { + // We might also run into a cycle before we reach firstFromItem again + // but note that we have to ignore current if we are meant to skip it + if (((current == startItem || current == originalStartItem) && from == firstFromItem) || + (!skip && cycleDetector.hasSeen(current))) { // wrapped around, avoid endless loops if (item == contentItem) { qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem"; @@ -2689,9 +2695,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) const bool wasVisible = isVisible(); op->removeChild(this); - if (wasVisible) { + if (wasVisible && !op->inDestructor) emit oldParentItem->visibleChildrenChanged(); - } } else if (d->window) { QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this); } @@ -2768,8 +2773,9 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) d->itemChange(ItemParentHasChanged, d->parentItem); - emit parentChanged(d->parentItem); - if (isVisible() && d->parentItem) + if (!d->inDestructor) + emit parentChanged(d->parentItem); + if (isVisible() && d->parentItem && !QQuickItemPrivate::get(d->parentItem)->inDestructor) emit d->parentItem->visibleChildrenChanged(); } @@ -2965,7 +2971,8 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) itemChange(QQuickItem::ItemChildRemovedChange, child); - emit q->childrenChanged(); + if (!inDestructor) + emit q->childrenChanged(); } void QQuickItemPrivate::refWindow(QQuickWindow *c) @@ -3194,6 +3201,7 @@ QQuickItemPrivate::QQuickItemPrivate() , touchEnabled(false) #endif , hasCursorHandler(false) + , inDestructor(false) , dirtyAttributes(0) , nextDirtyItem(nullptr) , prevDirtyItem(nullptr) @@ -5120,6 +5128,13 @@ void QQuickItem::componentComplete() d->addToDirtyList(); QQuickWindowPrivate::get(d->window)->dirtyItem(this); } + +#if QT_CONFIG(accessibility) + if (d->isAccessible && d->effectiveVisible) { + QAccessibleEvent ev(this, QAccessible::ObjectShow); + QAccessible::updateAccessibility(&ev); + } +#endif } QQuickStateGroup *QQuickItemPrivate::_states() @@ -6106,9 +6121,11 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) QAccessible::updateAccessibility(&ev); } #endif - emit q->visibleChanged(); - if (childVisibilityChanged) - emit q->visibleChildrenChanged(); + if (!inDestructor) { + emit q->visibleChanged(); + if (childVisibilityChanged) + emit q->visibleChildrenChanged(); + } return true; // effective visibility DID change } @@ -6157,6 +6174,15 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec } itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable); +#if QT_CONFIG(accessibility) + if (isAccessible) { + QAccessible::State changedState; + changedState.disabled = true; + changedState.focusable = true; + QAccessibleStateChangeEvent ev(q, changedState); + QAccessible::updateAccessibility(&ev); + } +#endif emit q->enabledChanged(); } @@ -8974,13 +9000,20 @@ QQuickItemPrivate::ExtraData::ExtraData() #if QT_CONFIG(accessibility) -QAccessible::Role QQuickItemPrivate::accessibleRole() const +QAccessible::Role QQuickItemPrivate::effectiveAccessibleRole() const { Q_Q(const QQuickItem); - QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, false)); - if (accessibleAttached) - return accessibleAttached->role(); + auto *attached = qmlAttachedPropertiesObject(q, false); + auto role = QAccessible::NoRole; + if (auto *accessibleAttached = qobject_cast(attached)) + role = accessibleAttached->role(); + if (role == QAccessible::NoRole) + role = accessibleRole(); + return role; +} +QAccessible::Role QQuickItemPrivate::accessibleRole() const +{ return QAccessible::NoRole; } #endif diff --git a/qtdeclarative/src/quick/items/qquickitem_p.h b/qtdeclarative/src/quick/items/qquickitem_p.h index 841d91bb40..6f329bd119 100644 --- a/qtdeclarative/src/quick/items/qquickitem_p.h +++ b/qtdeclarative/src/quick/items/qquickitem_p.h @@ -472,6 +472,7 @@ public: bool replayingPressEvent:1; bool touchEnabled:1; bool hasCursorHandler:1; + quint32 inDestructor:1; // has entered ~QQuickItem enum DirtyType { TransformOrigin = 0x00000001, @@ -574,7 +575,10 @@ public: virtual void implicitHeightChanged(); #if QT_CONFIG(accessibility) + QAccessible::Role effectiveAccessibleRole() const; +private: virtual QAccessible::Role accessibleRole() const; +public: #endif void setImplicitAntialiasing(bool antialiasing); diff --git a/qtdeclarative/src/quick/items/qquickmousearea_p_p.h b/qtdeclarative/src/quick/items/qquickmousearea_p_p.h index fba383e268..0d63618622 100644 --- a/qtdeclarative/src/quick/items/qquickmousearea_p_p.h +++ b/qtdeclarative/src/quick/items/qquickmousearea_p_p.h @@ -61,7 +61,6 @@ QT_BEGIN_NAMESPACE class QQuickMouseEvent; class QQuickMouseArea; -class QQuickPointerMask; class QQuickMouseAreaPrivate : public QQuickItemPrivate { Q_DECLARE_PUBLIC(QQuickMouseArea) @@ -100,7 +99,6 @@ public: #if QT_CONFIG(quick_draganddrop) QQuickDrag *drag; #endif - QPointer mask; QPointF startScene; QPointF targetStartPos; QPointF lastPos; diff --git a/qtdeclarative/src/quick/util/qquickstategroup.cpp b/qtdeclarative/src/quick/util/qquickstategroup.cpp index 7cb3138618..f732b1eb4a 100644 --- a/qtdeclarative/src/quick/util/qquickstategroup.cpp +++ b/qtdeclarative/src/quick/util/qquickstategroup.cpp @@ -381,8 +381,14 @@ bool QQuickStateGroupPrivate::updateAutoState() const auto potentialWhenBinding = QQmlPropertyPrivate::binding(whenProp); // if there is a binding, the value in when might not be up-to-date at this point // so we manually reevaluate the binding - if (auto abstractBinding = dynamic_cast(potentialWhenBinding)) - whenValue = abstractBinding->evaluate().toBool(); + if (auto abstractBinding = dynamic_cast(potentialWhenBinding)) { + QVariant evalResult = abstractBinding->evaluate(); + if (evalResult.userType() == qMetaTypeId()) + whenValue = evalResult.value().toBool(); + else + whenValue = evalResult.toBool(); + } + if (whenValue) { if (stateChangeDebug()) qWarning() << "Setting auto state due to expression"; diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp new file mode 100644 index 0000000000..8a1c901880 --- /dev/null +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblequickwidget_p.h" + +#include "qquickwidget_p.h" + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +QAccessibleQuickWidget::QAccessibleQuickWidget(QQuickWidget* widget) +: QAccessibleWidget(widget) +, m_accessibleWindow(QQuickWidgetPrivate::get(widget)->offscreenWindow) +{ + // NOTE: m_accessibleWindow is a QAccessibleQuickWindow, and not a + // QAccessibleQuickWidgetOffscreenWindow (defined below). This means + // it will return the Quick item child interfaces, which is what's needed here + // (unlike QAccessibleQuickWidgetOffscreenWindow, which will report 0 children). +} + +QAccessibleInterface *QAccessibleQuickWidget::child(int index) const +{ + return m_accessibleWindow.child(index); +} + +int QAccessibleQuickWidget::childCount() const +{ + return m_accessibleWindow.childCount(); +} + +int QAccessibleQuickWidget::indexOfChild(const QAccessibleInterface *iface) const +{ + return m_accessibleWindow.indexOfChild(iface); +} + +QAccessibleInterface *QAccessibleQuickWidget::childAt(int x, int y) const +{ + return m_accessibleWindow.childAt(x, y); +} + +QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window) +:QAccessibleQuickWindow(window) +{ + +} + +QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::child(int index) const +{ + Q_UNUSED(index); + return nullptr; +} + +int QAccessibleQuickWidgetOffscreenWindow::childCount() const +{ + return 0; +} + +int QAccessibleQuickWidgetOffscreenWindow::indexOfChild(const QAccessibleInterface *iface) const +{ + Q_UNUSED(iface); + return -1; +} + +QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow::childAt(int x, int y) const +{ + Q_UNUSED(x); + Q_UNUSED(y); + return nullptr; +} + +#endif // accessibility + +QT_END_NAMESPACE diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h new file mode 100644 index 0000000000..7c2ab930e0 --- /dev/null +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLEQUICKWIDGET_H +#define QACCESSIBLEQUICKWIDGET_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickwidget.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +// These classes implement the QQuickWiget accessibility switcharoo, +// where the child items of the QQuickWidgetOffscreenWindow are reported +// as child accessible interfaces of the QAccessibleQuickWidget. +class QAccessibleQuickWidget: public QAccessibleWidget +{ +public: + QAccessibleQuickWidget(QQuickWidget* widget); + + QAccessibleInterface *child(int index) const override; + int childCount() const override; + int indexOfChild(const QAccessibleInterface *iface) const override; + QAccessibleInterface *childAt(int x, int y) const override; + +private: + QAccessibleQuickWindow m_accessibleWindow; + Q_DISABLE_COPY(QAccessibleQuickWidget) +}; + +class QAccessibleQuickWidgetOffscreenWindow: public QAccessibleQuickWindow +{ +public: + QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window); + QAccessibleInterface *child(int index) const override; + int childCount() const override; + int indexOfChild(const QAccessibleInterface *iface) const override; + QAccessibleInterface *childAt(int x, int y) const override; +}; + +#endif // accessibility + +QT_END_NAMESPACE + +#endif diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp new file mode 100644 index 0000000000..7ba88a1769 --- /dev/null +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblequickwidgetfactory_p.h" +#include "qaccessiblequickwidget_p.h" + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object) +{ + if (classname == QLatin1String("QQuickWidget")) { + return new QAccessibleQuickWidget(qobject_cast(object)); + } else if (classname == QLatin1String("QQuickWidgetOffscreenWindow")) { + return new QAccessibleQuickWidgetOffscreenWindow(qobject_cast(object)); + } + return 0; +} + +#endif // accessibility + +QT_END_NAMESPACE + diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h new file mode 100644 index 0000000000..8c63b09f81 --- /dev/null +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifndef QACCESSIBLEQUICKWIDGETFACTORY_H +#define QACCESSIBLEQUICKWIDGETFACTORY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object); + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/qtdeclarative/src/quickwidgets/qquickwidget.cpp b/qtdeclarative/src/quickwidgets/qquickwidget.cpp index 39780f8de3..9c97b43518 100644 --- a/qtdeclarative/src/quickwidgets/qquickwidget.cpp +++ b/qtdeclarative/src/quickwidgets/qquickwidget.cpp @@ -39,6 +39,7 @@ #include "qquickwidget.h" #include "qquickwidget_p.h" +#include "qaccessiblequickwidgetfactory_p.h" #include "private/qquickwindow_p.h" #include "private/qquickitem_p.h" @@ -75,9 +76,16 @@ QT_BEGIN_NAMESPACE +QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) +:QQuickWindow(dd, control) +{ + setTitle(QString::fromLatin1("Offscreen")); + setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); +} + // override setVisble to prevent accidental offscreen window being created // by base class. -class QQuickOffcreenWindowPrivate: public QQuickWindowPrivate { +class QQuickWidgetOffscreenWindowPrivate: public QQuickWindowPrivate { public: void setVisible(bool visible) override { Q_Q(QWindow); @@ -105,9 +113,8 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) Q_Q(QQuickWidget); renderControl = new QQuickWidgetRenderControl(q); - offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl); - offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); - offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); + offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl); + offscreenWindow->setScreen(q->screen()); // Do not call create() on offscreenWindow. // Check if the Software Adaptation is being used @@ -138,6 +145,10 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) QWidget::connect(offscreenWindow, &QQuickWindow::focusObjectChanged, q, &QQuickWidget::propagateFocusObjectChanged); QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate())); QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); + +#if QT_CONFIG(accessibility) + QAccessible::installFactory(&qAccessibleQuickWidgetFactory); +#endif } void QQuickWidgetPrivate::ensureEngine() const @@ -901,9 +912,7 @@ void QQuickWidgetPrivate::createContext() context = new QOpenGLContext; context->setFormat(offscreenWindow->requestedFormat()); - const QWindow *win = q->window()->windowHandle(); - if (win && win->screen()) - context->setScreen(win->screen()); + context->setScreen(q->screen()); QOpenGLContext *shareContext = qt_gl_global_share_context(); if (!shareContext) shareContext = QWidgetPrivate::get(q->window())->shareContext(); @@ -1520,19 +1529,16 @@ bool QQuickWidget::event(QEvent *e) d->handleWindowChange(); break; - case QEvent::ScreenChangeInternal: - if (QWindow *window = this->window()->windowHandle()) { - QScreen *newScreen = window->screen(); - - if (d->offscreenWindow) - d->offscreenWindow->setScreen(newScreen); - if (d->offscreenSurface) - d->offscreenSurface->setScreen(newScreen); + case QEvent::ScreenChangeInternal: { + QScreen *newScreen = screen(); + if (d->offscreenWindow) + d->offscreenWindow->setScreen(newScreen); + if (d->offscreenSurface) + d->offscreenSurface->setScreen(newScreen); #if QT_CONFIG(opengl) - if (d->context) - d->context->setScreen(newScreen); + if (d->context) + d->context->setScreen(newScreen); #endif - } if (d->useSoftwareRenderer #if QT_CONFIG(opengl) @@ -1545,7 +1551,7 @@ bool QQuickWidget::event(QEvent *e) d->render(true); } break; - + } case QEvent::Show: case QEvent::Move: d->updatePosition(); diff --git a/qtdeclarative/src/quickwidgets/qquickwidget_p.h b/qtdeclarative/src/quickwidgets/qquickwidget_p.h index 881f7f9220..1a946bcc71 100644 --- a/qtdeclarative/src/quickwidgets/qquickwidget_p.h +++ b/qtdeclarative/src/quickwidgets/qquickwidget_p.h @@ -148,6 +148,14 @@ public: bool forceFullUpdate; }; +class QQuickWidgetOffscreenWindow: public QQuickWindow +{ + Q_OBJECT + +public: + QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control); +}; + QT_END_NAMESPACE #endif // QQuickWidget_P_H diff --git a/qtdeclarative/src/quickwidgets/quickwidgets.pro b/qtdeclarative/src/quickwidgets/quickwidgets.pro index 2438e577ae..85d156b8a3 100644 --- a/qtdeclarative/src/quickwidgets/quickwidgets.pro +++ b/qtdeclarative/src/quickwidgets/quickwidgets.pro @@ -7,9 +7,13 @@ DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FO HEADERS += \ qquickwidget.h \ qquickwidget_p.h \ - qtquickwidgetsglobal.h + qtquickwidgetsglobal.h \ + qaccessiblequickwidget_p.h \ + qaccessiblequickwidgetfactory_p.h SOURCES += \ - qquickwidget.cpp + qquickwidget.cpp \ + qaccessiblequickwidget.cpp \ + qaccessiblequickwidgetfactory.cpp load(qt_module) diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml new file mode 100644 index 0000000000..23874970e7 --- /dev/null +++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml @@ -0,0 +1,50 @@ +import QtQuick 2.15 +import QtQml.Models 2.15 + +Item { + DelegateModel { + id: delegateModel + model: ListModel { + id: sourceModel + + ListElement { title: "foo" } + ListElement { title: "bar" } + + function clear() { + if (count > 0) + remove(0, count); + } + } + + groups: [ + DelegateModelGroup { name: "selectedItems" } + ] + + delegate: Text { + height: DelegateModel.inSelectedItems ? implicitHeight * 2 : implicitHeight + Component.onCompleted: { + if (index === 0) + DelegateModel.inSelectedItems = true; + } + } + + Component.onCompleted: { + items.create(0) + items.create(1) + } + } + + ListView { + anchors.fill: parent + model: delegateModel + } + + Timer { + running: true + interval: 10 + onTriggered: sourceModel.clear() + } + + property int count: delegateModel.items.count +} + diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml new file mode 100644 index 0000000000..206133bb39 --- /dev/null +++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml @@ -0,0 +1,11 @@ +import QtQuick 2.8 + +ListView { + id: root + width: 200 + height: 200 + + delegate: Text { + text: display + } +} diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp index 35f1e2c94d..f473cff75f 100644 --- a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp @@ -27,6 +27,8 @@ ****************************************************************************/ #include +#include +#include #include #include #include @@ -47,6 +49,8 @@ private slots: void filterOnGroup_removeWhenCompleted(); void qtbug_86017(); void contextAccessedByHandler(); + void redrawUponColumnChange(); + void deleteRace(); }; class AbstractItemModel : public QAbstractItemModel @@ -186,6 +190,41 @@ void tst_QQmlDelegateModel::contextAccessedByHandler() QVERIFY(root->property("works").toBool()); } +void tst_QQmlDelegateModel::redrawUponColumnChange() +{ + QStandardItemModel m1; + m1.appendRow({ + new QStandardItem("Banana"), + new QStandardItem("Coconut"), + }); + + QQuickView view(testFileUrl("redrawUponColumnChange.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QQuickItem *root = view.rootObject(); + root->setProperty("model", QVariant::fromValue(&m1)); + + QObject *item = root->property("currentItem").value(); + QVERIFY(item); + QCOMPARE(item->property("text").toString(), "Banana"); + + QVERIFY(root); + m1.removeColumn(0); + + QCOMPARE(item->property("text").toString(), "Coconut"); +} + +void tst_QQmlDelegateModel::deleteRace() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("deleteRace.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + QTRY_COMPARE(o->property("count").toInt(), 2); + QTRY_COMPARE(o->property("count").toInt(), 0); +} + QTEST_MAIN(tst_QQmlDelegateModel) #include "tst_qqmldelegatemodel.moc" diff --git a/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index 9c865b3f73..1f788f7a7f 100644 --- a/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -154,6 +154,11 @@ void tst_QQmlImport::importPathOrder() engine.addImportPath(QT_QMLTEST_DATADIR); expectedImportPaths.prepend(QT_QMLTEST_DATADIR); QCOMPARE(expectedImportPaths, engine.importPathList()); + + // Add qml2Imports again to make it the first of the list + engine.addImportPath(qml2Imports); + expectedImportPaths.move(expectedImportPaths.indexOf(qml2Imports), 0); + QCOMPARE(expectedImportPaths, engine.importPathList()); } Q_DECLARE_METATYPE(QQmlImports::ImportVersion) diff --git a/qtdeclarative/tests/auto/qml/qqmllanguage/data/Broken.qml b/qtdeclarative/tests/auto/qml/qqmllanguage/data/Broken.qml new file mode 100644 index 0000000000..e24d9112a8 --- /dev/null +++ b/qtdeclarative/tests/auto/qml/qqmllanguage/data/Broken.qml @@ -0,0 +1,5 @@ +import QtQml 2.15 + +QtObject { + notThere: 5 +} diff --git a/qtdeclarative/tests/auto/qml/qqmllanguage/data/asBroken.qml b/qtdeclarative/tests/auto/qml/qqmllanguage/data/asBroken.qml new file mode 100644 index 0000000000..bd88d14c76 --- /dev/null +++ b/qtdeclarative/tests/auto/qml/qqmllanguage/data/asBroken.qml @@ -0,0 +1,6 @@ +import QtQml 2.15 + +QtObject { + id: self + property var selfAsBroken: self as Broken +} diff --git a/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index bffb62c59e..97cc64991f 100644 --- a/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -336,6 +336,7 @@ private slots: void bareInlineComponent(); void hangOnWarning(); + void objectAsBroken(); void ambiguousContainingType(); @@ -5876,6 +5877,21 @@ void tst_qqmllanguage::ambiguousContainingType() } } +void tst_qqmllanguage::objectAsBroken() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("asBroken.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + QVariant selfAsBroken = o->property("selfAsBroken"); + QVERIFY(selfAsBroken.isValid()); + // QCOMPARE(selfAsBroken.metaType(), QMetaType::fromType()); + + QQmlComponent b(&engine, testFileUrl("Broken.qml")); + QVERIFY(b.isError()); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/qtdeclarative/tests/auto/quick/qquickgridview/data/qtbug86255.qml b/qtdeclarative/tests/auto/quick/qquickgridview/data/qtbug86255.qml new file mode 100644 index 0000000000..20688b1967 --- /dev/null +++ b/qtdeclarative/tests/auto/quick/qquickgridview/data/qtbug86255.qml @@ -0,0 +1,55 @@ +import QtQuick 2.15 + +Item { + width: 240 + height: 320 + + GridView { + id: grid + objectName: "view" + anchors.fill: parent + cellWidth: 64 + cellHeight: 64 + model: ListModel { + id: listModel + + Component.onCompleted: reload() + + function reload() { + clear(); + for (let i = 0; i < 1000; i++) { + let magic = Math.random(); + append( { magic } ); + } + } + } + clip: true + delegate: Item { + id: d + property string val: magic + Loader { + property alias value: d.val + asynchronous: true + sourceComponent: cmp + } + } + } + + Timer { + running: true + interval: 1000 + onTriggered: listModel.reload() + } + Timer { + running: true + interval: 500 + onTriggered: grid.flick(0, -4000) + } + + Component { + id: cmp + Text { + text: value + } + } +} diff --git a/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 94ec4f44d5..7d0d9fa7a7 100644 --- a/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -213,6 +213,7 @@ private slots: void QTBUG_45640(); void QTBUG_49218(); void QTBUG_48870_fastModelUpdates(); + void QTBUG_86255(); void keyNavigationEnabled(); void resizeDynamicCellWidthRtL(); @@ -6814,6 +6815,18 @@ void tst_QQuickGridView::resizeDynamicCellWidthRtL() QTRY_COMPARE(gridview->contentX(), 0.f); } +void tst_QQuickGridView::QTBUG_86255() +{ + QScopedPointer window(createView()); + window->setSource(testFileUrl("qtbug86255.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + QQuickGridView *view = findItem(window->rootObject(), "view"); + QVERIFY(view != nullptr); + QTRY_COMPARE(view->isFlicking(), true); + QTRY_COMPARE(view->isFlicking(), false); +} + void tst_QQuickGridView::releaseItems() { QScopedPointer view(createView()); diff --git a/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml b/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml new file mode 100644 index 0000000000..889e480f3b --- /dev/null +++ b/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml @@ -0,0 +1,13 @@ +import QtQuick 2.6 + +Item { + visible: true + Item { + visible: false + Item { + objectName: "hiddenChild" + activeFocusOnTab: true + focus: true + } + } +} diff --git a/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index c8f251dbe1..c8ef36ee68 100644 --- a/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -67,6 +67,7 @@ private slots: void activeFocusOnTab10(); void activeFocusOnTab_infiniteLoop_data(); void activeFocusOnTab_infiniteLoop(); + void activeFocusOnTab_infiniteLoopControls(); void nextItemInFocusChain(); void nextItemInFocusChain2(); @@ -1057,6 +1058,17 @@ void tst_QQuickItem::activeFocusOnTab_infiniteLoop() QCOMPARE(item, window->rootObject()); } + +void tst_QQuickItem::activeFocusOnTab_infiniteLoopControls() +{ + auto source = testFileUrl("activeFocusOnTab_infiniteLoop3.qml"); + QScopedPointerwindow(new QQuickView()); + window->setSource(source); + window->show(); + QVERIFY(window->errors().isEmpty()); + QTest::keyClick(window.get(), Qt::Key_Tab); // should not hang +} + void tst_QQuickItem::nextItemInFocusChain() { if (!qt_tab_all_widgets()) diff --git a/qtdeclarative/tests/auto/quick/qquickstates/data/jsValueWhen.qml b/qtdeclarative/tests/auto/quick/qquickstates/data/jsValueWhen.qml new file mode 100644 index 0000000000..6d5eb1600c --- /dev/null +++ b/qtdeclarative/tests/auto/quick/qquickstates/data/jsValueWhen.qml @@ -0,0 +1,18 @@ +import QtQuick 2.15 + +Item { + id: root + property var prop: null + property bool works: false + states: [ + State { + name: "mystate" + when: root.prop + PropertyChanges { + target: root + works: "works" + } + } + ] + Component.onCompleted: root.prop = new Object +} diff --git a/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp index aa55b42935..26e86672b0 100644 --- a/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp @@ -188,6 +188,7 @@ private slots: void revertListMemoryLeak(); void duplicateStateName(); void trivialWhen(); + void jsValueWhen(); void noStateOsciallation(); void parentChangeCorrectReversal(); void revertNullObjectBinding(); @@ -1734,6 +1735,16 @@ void tst_qquickstates::trivialWhen() QVERIFY(c.create()); } +void tst_qquickstates::jsValueWhen() +{ + QQmlEngine engine; + + QQmlComponent c(&engine, testFileUrl("jsValueWhen.qml")); + QScopedPointer root(c.create()); + QVERIFY(root); + QVERIFY(root->property("works").toBool()); +} + void tst_qquickstates::noStateOsciallation() { QQmlEngine engine; diff --git a/qtdeclarative/tests/manual/quickcontrols2/swipedelegate/CloseOnCompletedWorks.qml b/qtdeclarative/tests/manual/quickcontrols2/swipedelegate/CloseOnCompletedWorks.qml new file mode 100644 index 0000000000..38dfde41c3 --- /dev/null +++ b/qtdeclarative/tests/manual/quickcontrols2/swipedelegate/CloseOnCompletedWorks.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2 +import QtQuick.Controls 2 +ApplicationWindow { + visible: true + width: 640 + height: 480 + + ListView { + anchors.fill: parent + model: 2 + + delegate: SwipeDelegate { + text: "Swipe me left (should not crash)" + + swipe.right: Label { + text: "Release (should not crash)" + } + + swipe.onCompleted: { + swipe.close() + } + } + } +} diff --git a/qtdeclarative/tools/qml/main.cpp b/qtdeclarative/tools/qml/main.cpp index beeec88f07..2cb7653d65 100644 --- a/qtdeclarative/tools/qml/main.cpp +++ b/qtdeclarative/tools/qml/main.cpp @@ -446,8 +446,8 @@ int main(int argc, char *argv[]) QCommandLineParser parser; parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments); - const QCommandLineOption helpOption = parser.addHelpOption(); - const QCommandLineOption versionOption = parser.addVersionOption(); + parser.addHelpOption(); + parser.addVersionOption(); #ifdef QT_GUI_LIB QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"), QCoreApplication::translate("main", "Select which application class to use. Default is gui."), @@ -522,14 +522,7 @@ int main(int argc, char *argv[]) parser.addPositionalArgument("args", QCoreApplication::translate("main", "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), "[-- args...]"); - if (!parser.parse(QCoreApplication::arguments())) { - qWarning() << parser.errorText(); - exit(1); - } - if (parser.isSet(versionOption)) - parser.showVersion(); - if (parser.isSet(helpOption)) - parser.showHelp(); + parser.process(*app); if (parser.isSet(listConfOption)) listConfFiles(); if (applicationType == QmlApplicationTypeUnknown) { Submodule qtdoc 52946b84...9dfbbfb9: Submodule qtimageformats e57b0df6..5aa33ec8: diff --git a/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp b/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp index dde783c..1bf9074 100644 --- a/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp +++ b/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp @@ -462,8 +462,12 @@ static bool parseIconEntryInfo(ICNSEntry &icon) if (isIconCompressed(icon)) return true; // Icon depth: - if (!depth.isEmpty()) - icon.depth = ICNSEntry::Depth(depth.toUInt()); + if (!depth.isEmpty()) { + const uint depthUInt = depth.toUInt(); + if (depthUInt > 32) + return false; + icon.depth = ICNSEntry::Depth(depthUInt); + } // Try mono if depth not found if (icon.depth == ICNSEntry::DepthUnknown) icon.depth = ICNSEntry::DepthMono; @@ -515,6 +519,9 @@ static bool parseIconEntryInfo(ICNSEntry &icon) } icon.height = icon.width; } + // Sanity check + if (icon.width == 0 || icon.width > 4096) + return false; return true; } @@ -685,7 +692,7 @@ bool QICNSHandler::canRead() const bool QICNSHandler::read(QImage *outImage) { QImage img; - if (!ensureScanned()) { + if (!ensureScanned() || m_currentIconIndex >= m_icons.size()) { qWarning("QICNSHandler::read(): The device wasn't parsed properly!"); return false; } @@ -892,7 +899,7 @@ bool QICNSHandler::scanDevice() return false; const qint64 blockDataOffset = device()->pos(); - if (!isBlockHeaderValid(blockHeader)) { + if (!isBlockHeaderValid(blockHeader, ICNSBlockHeaderSize + filelength - blockDataOffset)) { qWarning("QICNSHandler::scanDevice(): Failed, bad header at pos %s. OSType \"%s\", length %u", QByteArray::number(blockDataOffset).constData(), nameFromOSType(blockHeader.ostype).constData(), blockHeader.length); @@ -927,11 +934,14 @@ bool QICNSHandler::scanDevice() case ICNSBlockHeader::TypeOdrp: // Icns container seems to have an embedded icon variant container // Let's start a scan for entries - while (device()->pos() < nextBlockOffset) { + while (!stream.atEnd() && device()->pos() < nextBlockOffset) { ICNSBlockHeader icon; stream >> icon; + if (stream.status() != QDataStream::Ok) + return false; // Check for incorrect variant entry header and stop scan - if (!isBlockHeaderValid(icon, blockDataLength)) + quint64 remaining = blockDataLength - (device()->pos() - blockDataOffset); + if (!isBlockHeaderValid(icon, ICNSBlockHeaderSize + remaining)) break; if (!addEntry(icon, device()->pos(), blockHeader.ostype)) return false; @@ -1003,7 +1013,7 @@ bool QICNSHandler::scanDevice() break; } } - return true; + return (m_icons.size() > 0); } const ICNSEntry &QICNSHandler::getIconMask(const ICNSEntry &icon) const diff --git a/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp b/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp index 5082023..cb34374 100644 --- a/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp +++ b/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp @@ -43,6 +43,7 @@ #include "qimage.h" #include "qvariant.h" #include "qcolor.h" +#include "qimagereader.h" #include #include // for pow @@ -333,16 +334,46 @@ private: Jpeg2000JasperReader::Jpeg2000JasperReader(QIODevice *iod, SubFormat format) : jasperOk(true), ioDevice(iod), format(format), hasAlpha(false) { +#if JAS_VERSION_MAJOR < 3 if (jas_init()) { jasperOk = false; qDebug("Jasper Library initialization failed"); } +#else + jas_conf_clear(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + jas_conf_set_max_mem_usage(QImageReader::allocationLimit() * 1024 * 1024); +#else + // 128MB seems to be enough. + jas_conf_set_max_mem_usage(128 * 1024 * 1024); +#endif + if (jas_init_library()) { + jasperOk = false; + qDebug("Jasper library initialization failed"); + } + if (jas_init_thread()) { + jas_cleanup_library(); + jasperOk = false; + qDebug("Jasper thread initialization failed"); + } +#endif } Jpeg2000JasperReader::~Jpeg2000JasperReader() { +#if JAS_VERSION_MAJOR < 3 if (jasperOk) jas_cleanup(); +#else + if (jasperOk) { + if (jas_cleanup_thread()) { + qDebug("Jasper thread cleanup failed"); + } + if (jas_cleanup_library()) { + qDebug("Jasper library cleanup failed"); + } + } +#endif } /*! \internal @@ -857,7 +888,7 @@ bool Jpeg2000JasperReader::write(const QImage &image, int quality) } // Open an empty jasper stream that grows automatically - jas_stream_t * memory_stream = jas_stream_memopen(0, -1); + jas_stream_t * memory_stream = jas_stream_memopen(0, 0); // Jasper wants a non-const string. char *str = qstrdup(jasperFormatString.toLatin1().constData()); diff --git a/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp b/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp index 5d086c6..3961c16 100644 --- a/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp +++ b/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp @@ -220,9 +220,18 @@ QImage QTgaFile::readImage() int offset = mHeader[IdLength]; // Mostly always zero - // Even in TrueColor files a color pallette may be present - if (mHeader[ColorMapType] == 1) - offset += littleEndianInt(&mHeader[CMapLength]) * littleEndianInt(&mHeader[CMapDepth]); + // Even in TrueColor files a color palette may be present so we have to check it here + // even we only support image type 2 (= uncompressed true-color image) + if (mHeader[ColorMapType] == 1) { + int cmapDepth = mHeader[CMapDepth]; + if (cmapDepth == 15) // 15 bit is stored as 16 bit + ignoring the highest bit (no alpha) + cmapDepth = 16; + if (cmapDepth != 16 && cmapDepth != 24 && cmapDepth != 32) { + mErrorMessage = tr("Invalid color map depth (%1)").arg(cmapDepth); + return {}; + } + offset += littleEndianInt(&mHeader[CMapLength]) * cmapDepth / 8; + } mDevice->seek(HeaderSize + offset); diff --git a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp index 1386750..79be154 100644 --- a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp +++ b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp @@ -38,11 +38,15 @@ ****************************************************************************/ #include "qtiffhandler_p.h" -#include #include #include #include #include +#include +#include +#include +#include + extern "C" { #include "tiffio.h" } @@ -90,13 +94,33 @@ toff_t qtiffSizeProc(thandle_t fd) return static_cast(fd)->size(); } -int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) +int qtiffMapProc(thandle_t fd, void **base, toff_t *size) { + QIODevice *device = static_cast(fd); + + QFileDevice *file = qobject_cast(device); + if (file) { + *base = file->map(0, file->size()); + if (*base != nullptr) { + *size = file->size(); + return 1; + } + } else { + QBuffer *buf = qobject_cast(device); + if (buf) { + *base = const_cast(buf->data().constData()); + *size = buf->size(); + return 1; + } + } return 0; } -void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/) +void qtiffUnmapProc(thandle_t fd, void *base, toff_t /*size*/) { + QFileDevice *file = qobject_cast(static_cast(fd)); + if (file && base) + file->unmap(static_cast(base)); } @@ -361,6 +385,8 @@ bool QTiffHandler::read(QImage *image) } TIFF *const tiff = d->tiff; + if (TIFFIsTiled(tiff) && TIFFTileSize64(tiff) > uint64_t(image->sizeInBytes())) // Corrupt image + return false; const quint32 width = d->size.width(); const quint32 height = d->size.height(); diff --git a/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp b/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp index 82d38cb..d02eb05 100644 --- a/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp +++ b/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp @@ -45,6 +45,7 @@ #include #include #include +#include static const int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_constants.h @@ -102,21 +103,23 @@ bool QWebpHandler::ensureScanned() const m_scanState = ScanError; - if (device()->isSequential()) { - qWarning() << "Sequential devices are not supported"; + QWebpHandler *that = const_cast(this); + const int headerBytesNeeded = sizeof(WebPBitstreamFeatures); + QByteArray header = device()->peek(headerBytesNeeded); + if (header.size() < headerBytesNeeded) return false; - } - qint64 oldPos = device()->pos(); - device()->seek(0); - - QWebpHandler *that = const_cast(this); - QByteArray header = device()->peek(sizeof(WebPBitstreamFeatures)); + // We do no random access during decoding, just a readAll() of the whole image file. So if + // if it is all available already, we can accept a sequential device. The riff header contains + // the file size minus 8 bytes header + qint64 byteSize = qFromLittleEndian(header.constData() + 4); + if (device()->isSequential() && device()->bytesAvailable() < byteSize + 8) { + qWarning() << "QWebpHandler: Insufficient data available in sequential device"; + return false; + } if (WebPGetFeatures((const uint8_t*)header.constData(), header.size(), &(that->m_features)) == VP8_STATUS_OK) { if (m_features.has_animation) { // For animation, we have to read and scan whole file to determine loop count and images count - device()->seek(oldPos); - if (that->ensureDemuxer()) { that->m_loop = WebPDemuxGetI(m_demuxer, WEBP_FF_LOOP_COUNT); that->m_frameCount = WebPDemuxGetI(m_demuxer, WEBP_FF_FRAME_COUNT); @@ -126,17 +129,13 @@ bool QWebpHandler::ensureScanned() const if (that->m_features.has_alpha) that->m_composited->fill(Qt::transparent); - // We do not reset device position since we have read in all data m_scanState = ScanSuccess; - return true; } } else { m_scanState = ScanSuccess; } } - device()->seek(oldPos); - return m_scanState == ScanSuccess; } @@ -159,7 +158,7 @@ bool QWebpHandler::ensureDemuxer() bool QWebpHandler::read(QImage *image) { - if (!ensureScanned() || device()->isSequential() || !ensureDemuxer()) + if (!ensureScanned() || !ensureDemuxer()) return false; QRect prevFrameRect; Submodule qtlocation a87a9d1a..664701dc: Submodule src/3rdparty/mapbox-gl-native d3101bbc...5a07e196 (commits not present) diff --git a/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp b/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp index a978573d..11e1466f 100644 --- a/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp +++ b/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp @@ -158,7 +158,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind if (!root) return; - if (m_mapObjectsRootNode && m_mapObjectsRootNode->parent()) + if (m_mapObjectsRootNode && !m_mapObjectsRootNode->parent()) root->appendChildNode(m_mapObjectsRootNode.get()); if (!m_mapObjectsRootNode) { diff --git a/qtlocation/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp index 5a40467e..d123c6a8 100644 --- a/qtlocation/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp +++ b/qtlocation/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp @@ -59,8 +59,10 @@ QT_BEGIN_NAMESPACE static const QString kPrefixEsri(QStringLiteral("esri.")); static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); -static const QString kUrlGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates")); -static const QString kUrlReverseGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode")); +static const QString kUrlGeocode(QStringLiteral("https://geocode.arcgis.com/arcgis/rest/services/" + "World/GeocodeServer/findAddressCandidates")); +static const QString kUrlReverseGeocode(QStringLiteral( + "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode")); static QString addressToQuery(const QGeoAddress &address) { diff --git a/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp b/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp index 17492d94..96c12b1b 100644 --- a/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp +++ b/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp @@ -46,7 +46,8 @@ QT_BEGIN_NAMESPACE -// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ +// JSON reference: +// https://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ static const QString kErrorMessage(QStringLiteral("Error %1: %2.")); static const QString kErrorJson(QStringLiteral("Error: invalide JSON document.")); diff --git a/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp b/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp index 8cadfb29..92b6bb13 100644 --- a/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp +++ b/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp @@ -44,7 +44,8 @@ QT_BEGIN_NAMESPACE -// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ +// JSON reference: +// https://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ GeoRouteReplyEsri::GeoRouteReplyEsri(QNetworkReply *reply, const QGeoRouteRequest &request, QObject *parent) : diff --git a/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp index 0e6bc2c7..ed613f4b 100644 --- a/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp +++ b/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp @@ -48,7 +48,8 @@ static const QString kPrefixEsri(QStringLiteral("esri.")); static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); static const QString kParamToken(kPrefixEsri + QStringLiteral("token")); -static const QString kUrlRouting(QStringLiteral("http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World/solve")); +static const QString kUrlRouting(QStringLiteral( + "https://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World/solve")); GeoRoutingManagerEngineEsri::GeoRoutingManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, @@ -70,7 +71,8 @@ GeoRoutingManagerEngineEsri::~GeoRoutingManagerEngineEsri() { } -// REST reference: http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 +// REST reference: +// https://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 QGeoRouteReply *GeoRoutingManagerEngineEsri::calculateRoute(const QGeoRouteRequest &request) { @@ -125,7 +127,7 @@ void GeoRoutingManagerEngineEsri::replyError(QGeoRouteReply::Error errorCode, co QString GeoRoutingManagerEngineEsri::preferedDirectionLangage() { // list of supported langages is defined in: - // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 + // https://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 const QStringList supportedLanguages = { "ar", // Generate directions in Arabic "cs", // Generate directions in Czech diff --git a/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp index 5d15835d..24148f95 100644 --- a/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp +++ b/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp @@ -230,7 +230,8 @@ QGeoMap *GeoTiledMappingManagerEngineEsri::createMap() // ${y} = Y // ${token} = Token -// template = 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{{z}}/{{y}}/{{x}}.png' +// template = +// 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{{z}}/{{y}}/{{x}}.png' bool GeoTiledMappingManagerEngineEsri::initializeMapSources(QGeoServiceProvider::Error *error, QString *errorString, diff --git a/qtlocation/src/plugins/geoservices/esri/maps.json b/qtlocation/src/plugins/geoservices/esri/maps.json index 8167ae7d..862e087d 100644 --- a/qtlocation/src/plugins/geoservices/esri/maps.json +++ b/qtlocation/src/plugins/geoservices/esri/maps.json @@ -6,8 +6,8 @@ "description": "ArcGIS Online World Street Map", "mobile": true, "night": false, - "url": "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -16,8 +16,8 @@ "": "ArcGIS Online World Imagery", "mobile": true, "night": false, - "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -26,8 +26,8 @@ "description": "ArcGIS Online World Terrain Base", "mobile": false, "night": false, - "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -36,8 +36,8 @@ "description": "ArcGIS Online World Topography", "mobile": true, "night": false, - "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -46,8 +46,8 @@ "description": "This map presents land cover and detailed topographic maps for the United States.", "mobile": true, "night": false, - "url": "http://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -56,8 +56,8 @@ "description": "National Geographic World Map", "mobile": false, "night": false, - "url": "http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -66,8 +66,8 @@ "description": "Thematic content providing a neutral background with minimal colors", "mobile": true, "night": false, - "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -76,8 +76,8 @@ "description": "Natural Earth physical map for the world", "mobile": false, "night": false, - "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -86,8 +86,8 @@ "description": "Portrays surface elevation as shaded relief", "mobile": false, "night": false, - "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -96,8 +96,8 @@ "description": "This map is designed to be used as a basemap by marine GIS professionals and as a reference map by anyone interested in ocean data", "mobile": false, "night": false, - "url": "http://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -106,8 +106,8 @@ "description": "Thematic content providing a neutral background with minimal colors", "mobile": false, "night": true, - "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer", + "copyrightText": "© Esri contributors" }, { @@ -116,8 +116,8 @@ "description": "DeLorme’s topographic basemap is a seamless global data set that portrays transportation, hydrography, jurisdiction boundaries, and major geographic features", "mobile": false, "night": false, - "url": "http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer", - "copyrightText": "© Esri contributors" + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer", + "copyrightText": "© Esri contributors" } ] } diff --git a/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp index 3858ddf5..af66f28e 100644 --- a/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp +++ b/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp @@ -63,8 +63,10 @@ static const QString kCountriesKey(QStringLiteral("detailedCountries")); static const QString kLocalizedNamesKey(QStringLiteral("localizedNames")); static const QString kMaxLocationsKey(QStringLiteral("maxLocations")); -static const QUrl kUrlGeocodeServer("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer?f=pjson"); -static const QUrl kUrlFindAddressCandidates("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates"); +static const QUrl kUrlGeocodeServer( + "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer?f=pjson"); +static const QUrl kUrlFindAddressCandidates("https://geocode.arcgis.com/arcgis/rest/services/World/" + "GeocodeServer/findAddressCandidates"); PlaceManagerEngineEsri::PlaceManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) : Submodule qtmultimedia baeb08ba..78d05cfc: diff --git a/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp b/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp index 4000f2178..a446d93fe 100644 --- a/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp +++ b/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp @@ -368,7 +368,8 @@ static GstGLContext *gstGLDisplayContext(QAbstractVideoSurface *surface) if (!nativeContext) qWarning() << "Could not find resource for" << contextName; - GstGLContext *appContext = gst_gl_context_new_wrapped(display, (guintptr)nativeContext, glPlatform, GST_GL_API_ANY); + GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2; + GstGLContext *appContext = gst_gl_context_new_wrapped(display, (guintptr)nativeContext, glPlatform, glApi); if (!appContext) qWarning() << "Could not create wrappped context for platform:" << glPlatform; diff --git a/qtmultimedia/src/multimediawidgets/multimediawidgets.pro b/qtmultimedia/src/multimediawidgets/multimediawidgets.pro index 1919e8107..4c30d8fbf 100644 --- a/qtmultimedia/src/multimediawidgets/multimediawidgets.pro +++ b/qtmultimedia/src/multimediawidgets/multimediawidgets.pro @@ -2,8 +2,6 @@ TARGET = QtMultimediaWidgets QT = core gui multimedia widgets-private QT_PRIVATE += multimedia-private -qtHaveModule(opengl): \ - QT_PRIVATE += opengl PRIVATE_HEADERS += \ qvideowidget_p.h \ diff --git a/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp index 2b5325132..b68b4af1b 100644 --- a/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp +++ b/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp @@ -402,6 +402,8 @@ int QPulseAudioInput::bytesReady() const qint64 QPulseAudioInput::read(char *data, qint64 len) { + Q_ASSERT(data != nullptr || len == 0); + m_bytesAvailable = checkBytesReady(); setError(QAudio::NoError); @@ -411,7 +413,8 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) if (!m_pullMode && !m_tempBuffer.isEmpty()) { readBytes = qMin(static_cast(len), m_tempBuffer.size()); - memcpy(data, m_tempBuffer.constData(), readBytes); + if (readBytes) + memcpy(data, m_tempBuffer.constData(), readBytes); m_totalTimeValue += readBytes; if (readBytes < m_tempBuffer.size()) { @@ -502,9 +505,10 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) void QPulseAudioInput::applyVolume(const void *src, void *dest, int len) { + Q_ASSERT((src && dest) || len == 0); if (m_volume < 1.f) QAudioHelperInternal::qMultiplySamples(m_volume, m_format, src, dest, len); - else + else if (len) memcpy(dest, src, len); } Submodule qtpim 02efef5e...f9a8f0fc (commits not present) Submodule qtquick3d b485a86d..353f50a9: diff --git a/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro b/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro index ca5c499e..174a075b 100644 --- a/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro +++ b/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro @@ -10,7 +10,7 @@ QT_FOR_CONFIG += assetimporters-private include($$OUT_PWD/../qtassetimporters-config.pri) qtConfig(system-assimp):!if(cross_compile:host_build) { - QMAKE_USE_PRIVATE += assimp + QMAKE_USE_PRIVATE += quick3d-assimp } else { include(../../../3rdparty/assimp/assimp.pri) } Submodule qtquickcontrols2 c433697a..0472a07a: diff --git a/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp b/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp index e5fe734f7..e36922775 100644 --- a/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp +++ b/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp @@ -38,6 +38,7 @@ #include "qwidgetplatformmenuitem_p.h" #include +#include #include #include @@ -145,7 +146,7 @@ void QWidgetPlatformMenu::showPopup(const QWindow *window, const QRect &targetRe QPoint targetPos = targetRect.bottomLeft(); if (window) - targetPos = window->mapToGlobal(targetPos); + targetPos = window->mapToGlobal(QHighDpi::fromNativeLocalPosition(targetPos, window)); const QWidgetPlatformMenuItem *widgetItem = qobject_cast(item); m_menu->popup(targetPos, widgetItem ? widgetItem->action() : nullptr); diff --git a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp index 20cf59c1a..43af47a94 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp @@ -1201,6 +1201,12 @@ QAccessible::Role QQuickAbstractButton::accessibleRole() const } return QAccessible::Button; } + +void QQuickAbstractButton::accessiblePressAction() +{ + Q_D(QQuickAbstractButton); + d->trigger(); +} #endif QT_END_NAMESPACE diff --git a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h index 0fa48980e..ab66220d0 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h +++ b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h @@ -209,6 +209,7 @@ protected: #if QT_CONFIG(accessibility) void accessibilityActiveChanged(bool active) override; QAccessible::Role accessibleRole() const override; + Q_INVOKABLE void accessiblePressAction(); #endif private: diff --git a/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp b/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp index f38c2b09c..6eed2a024 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp @@ -225,6 +225,7 @@ void QQuickContainerPrivate::cleanup() QObject::disconnect(contentModel, &QQmlObjectModel::countChanged, q, &QQuickContainer::countChanged); QObject::disconnect(contentModel, &QQmlObjectModel::childrenChanged, q, &QQuickContainer::contentChildrenChanged); delete contentModel; + contentModel = nullptr; } QQuickItem *QQuickContainerPrivate::itemAt(int index) const @@ -436,7 +437,7 @@ void QQuickContainerPrivate::contentChildren_clear(QQmlListProperty void QQuickContainerPrivate::updateContentWidth() { Q_Q(QQuickContainer); - if (hasContentWidth || qFuzzyCompare(contentWidth, implicitContentWidth)) + if (hasContentWidth || qFuzzyCompare(contentWidth, implicitContentWidth) || !contentModel) return; contentWidth = implicitContentWidth; @@ -446,7 +447,7 @@ void QQuickContainerPrivate::updateContentWidth() void QQuickContainerPrivate::updateContentHeight() { Q_Q(QQuickContainer); - if (hasContentHeight || qFuzzyCompare(contentHeight, implicitContentHeight)) + if (hasContentHeight || qFuzzyCompare(contentHeight, implicitContentHeight) || !contentModel) return; contentHeight = implicitContentHeight; diff --git a/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp b/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp index 409fb582c..c4b05c13c 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp @@ -846,6 +846,13 @@ void QQuickControlPrivate::executeBackground(bool complete) quickCompleteDeferred(q, backgroundName(), background); } +/* + \internal + + Hides an item that was replaced by a newer one, rather than + deleting it, as the item is typically created in QML and hence + we don't own it. +*/ void QQuickControlPrivate::hideOldItem(QQuickItem *item) { if (!item) @@ -864,6 +871,29 @@ void QQuickControlPrivate::hideOldItem(QQuickItem *item) #endif } +/* + \internal + + Named "unhide" because it's used for cases where an item + that was previously hidden by \l hideOldItem() wants to be + shown by a control again, such as a ScrollBar in ScrollView. +*/ +void QQuickControlPrivate::unhideOldItem(QQuickControl *control, QQuickItem *item) +{ + Q_ASSERT(item); + qCDebug(lcItemManagement) << "unhiding old item" << item; + + item->setVisible(true); + item->setParentItem(control); + +#if QT_CONFIG(accessibility) + // Add the item back in to the accessibility tree. + QQuickAccessibleAttached *accessible = accessibleAttached(item); + if (accessible) + accessible->setIgnored(false); +#endif +} + void QQuickControlPrivate::updateBaselineOffset() { Q_Q(QQuickControl); @@ -2297,12 +2327,13 @@ QAccessible::Role QQuickControl::accessibleRole() const void QQuickControl::accessibilityActiveChanged(bool active) { + Q_D(QQuickControl); if (!active) return; QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(this, true)); Q_ASSERT(accessibleAttached); - accessibleAttached->setRole(accessibleRole()); + accessibleAttached->setRole(d->effectiveAccessibleRole()); } #endif diff --git a/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h b/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h index 8e979079e..a6e624c91 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h +++ b/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h @@ -173,6 +173,7 @@ public: virtual void executeBackground(bool complete = false); static void hideOldItem(QQuickItem *item); + static void unhideOldItem(QQuickControl *control, QQuickItem *item); void updateBaselineOffset(); diff --git a/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp b/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp index e6db14eb5..6197d1547 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp @@ -237,7 +237,7 @@ static QRectF alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment void QQuickDialogButtonBoxPrivate::resizeContent() { Q_Q(QQuickDialogButtonBox); - if (!contentItem) + if (!contentItem || !contentModel) return; QRectF geometry = q->boundingRect().adjusted(q->leftPadding(), q->topPadding(), -q->rightPadding(), -q->bottomPadding()); @@ -322,6 +322,9 @@ void QQuickDialogButtonBoxPrivate::updateLayout() qreal QQuickDialogButtonBoxPrivate::getContentWidth() const { Q_Q(const QQuickDialogButtonBox); + if (!contentModel) + return 0; + const int count = contentModel->count(); const qreal totalSpacing = qMax(0, count - 1) * spacing; qreal totalWidth = totalSpacing; @@ -341,6 +344,9 @@ qreal QQuickDialogButtonBoxPrivate::getContentWidth() const qreal QQuickDialogButtonBoxPrivate::getContentHeight() const { Q_Q(const QQuickDialogButtonBox); + if (!contentModel) + return 0; + const int count = contentModel->count(); qreal maxHeight = 0; for (int i = 0; i < count; ++i) { diff --git a/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp b/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp index 71b60a2bc..2bc621674 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp @@ -263,7 +263,7 @@ void QQuickLabelPrivate::accessibilityActiveChanged(bool active) Q_Q(QQuickLabel); QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, true)); Q_ASSERT(accessibleAttached); - accessibleAttached->setRole(accessibleRole()); + accessibleAttached->setRole(effectiveAccessibleRole()); maybeSetAccessibleName(text); } diff --git a/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp b/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp index 91bd59184..0ce518f84 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp @@ -399,8 +399,11 @@ void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data) Q_D(QQuickOverlay); QQuickItem::itemChange(change, data); - if (change == ItemChildAddedChange || change == ItemChildRemovedChange) + if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { setVisible(!d->allDrawers.isEmpty() || !childItems().isEmpty()); + if (data.item->parent() == d->mouseGrabberPopup) + d->setMouseGrabberPopup(nullptr); + } } void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) diff --git a/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp b/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp index 7df80a047..bfaa84e30 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -2720,6 +2721,19 @@ QPalette QQuickPopup::defaultPalette() const } #if QT_CONFIG(accessibility) +QAccessible::Role QQuickPopup::effectiveAccessibleRole() const +{ + auto *attached = qmlAttachedPropertiesObject(this, false); + + auto role = QAccessible::NoRole; + if (auto *accessibleAttached = qobject_cast(attached)) + role = accessibleAttached->role(); + if (role == QAccessible::NoRole) + role = accessibleRole(); + + return role; +} + QAccessible::Role QQuickPopup::accessibleRole() const { return QAccessible::Dialog; diff --git a/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h b/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h index dc3ebf6f8..a3773be3e 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h +++ b/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h @@ -454,7 +454,10 @@ protected: virtual QPalette defaultPalette() const; #if QT_CONFIG(accessibility) + QAccessible::Role effectiveAccessibleRole() const; +private: virtual QAccessible::Role accessibleRole() const; +protected: virtual void accessibilityActiveChanged(bool active); #endif diff --git a/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp b/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp index 0069b9fc1..143c37fc3 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp @@ -404,7 +404,7 @@ QPalette QQuickPopupItem::defaultPalette() const QAccessible::Role QQuickPopupItem::accessibleRole() const { Q_D(const QQuickPopupItem); - return d->popup->accessibleRole(); + return d->popup->effectiveAccessibleRole(); } void QQuickPopupItem::accessibilityActiveChanged(bool active) diff --git a/qtquickcontrols2/src/quicktemplates2/qquickscrollbar.cpp b/qtquickcontrols2/src/quicktemplates2/qquickscrollbar.cpp index 4e2f509db..1c4b308cd 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickscrollbar.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickscrollbar.cpp @@ -797,6 +797,14 @@ void QQuickScrollBarAttachedPrivate::initHorizontal() if (parent && parent == flickable->parentItem()) horizontal->stackAfter(flickable); + // If a scroll bar was previously hidden (due to e.g. setting a new contentItem + // on a ScrollView), we need to make sure that we un-hide it. + // We don't bother checking if the item is actually the old one, because + // if it's not, all of the things the function does (setting parent, visibility, etc.) + // should be no-ops anyway. + if (auto control = qobject_cast(q_ptr->parent())) + QQuickControlPrivate::unhideOldItem(control, horizontal); + layoutHorizontal(); horizontal->setSize(area->property("widthRatio").toReal()); horizontal->setPosition(area->property("xPosition").toReal()); @@ -818,6 +826,9 @@ void QQuickScrollBarAttachedPrivate::initVertical() if (parent && parent == flickable->parentItem()) vertical->stackAfter(flickable); + if (auto control = qobject_cast(q_ptr->parent())) + QQuickControlPrivate::unhideOldItem(control, vertical); + layoutVertical(); vertical->setSize(area->property("heightRatio").toReal()); vertical->setPosition(area->property("yPosition").toReal()); diff --git a/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp b/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp index 64fc631dd..fba3f6b70 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp @@ -512,7 +512,7 @@ void QQuickTextAreaPrivate::accessibilityActiveChanged(bool active) Q_Q(QQuickTextArea); QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, true)); Q_ASSERT(accessibleAttached); - accessibleAttached->setRole(accessibleRole()); + accessibleAttached->setRole(effectiveAccessibleRole()); accessibleAttached->set_readOnly(q->isReadOnly()); accessibleAttached->setDescription(placeholder); } diff --git a/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp b/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp index 8fa04bd3a..e83346cbd 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp @@ -359,7 +359,7 @@ void QQuickTextFieldPrivate::accessibilityActiveChanged(bool active) Q_Q(QQuickTextField); QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, true)); Q_ASSERT(accessibleAttached); - accessibleAttached->setRole(accessibleRole()); + accessibleAttached->setRole(effectiveAccessibleRole()); accessibleAttached->set_readOnly(m_readOnly); accessibleAttached->set_passwordEdit((m_echoMode == QQuickTextField::Password || m_echoMode == QQuickTextField::PasswordEchoOnEdit) ? true : false); accessibleAttached->setDescription(placeholder); diff --git a/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml b/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml index 0e8b08352..cd4931184 100644 --- a/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml +++ b/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml @@ -576,4 +576,51 @@ TestCase { verify(newHorizontalScrollBar.visible) verify(!oldHorizontalScrollBar.visible) } + + Component { + id: bindingToContentItemAndStandaloneFlickable + + Item { + width: 200 + height: 200 + + property alias scrollView: scrollView + + ScrollView { + id: scrollView + anchors.fill: parent + contentItem: listView + + property Item someBinding: contentItem + } + ListView { + id: listView + model: 10 + delegate: ItemDelegate { + text: modelData + width: listView.width + } + } + } + } + + // Tests that scroll bars show up for a ScrollView where + // - its contentItem is declared as a standalone, separate item + // - there is a binding to contentItem (which causes a default Flickable to be created) + function test_bindingToContentItemAndStandaloneFlickable() { + let root = createTemporaryObject(bindingToContentItemAndStandaloneFlickable, testCase) + verify(root) + + let control = root.scrollView + let verticalScrollBar = control.ScrollBar.vertical + let horizontalScrollBar = control.ScrollBar.horizontal + compare(verticalScrollBar.parent, control) + compare(horizontalScrollBar.parent, control) + verify(verticalScrollBar.visible) + verify(horizontalScrollBar.visible) + + mouseDrag(verticalScrollBar, verticalScrollBar.width / 2, verticalScrollBar.height / 2, 0, 50) + verify(verticalScrollBar.active) + verify(horizontalScrollBar.active) + } } diff --git a/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml b/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml new file mode 100644 index 000000000..9e4598b9f --- /dev/null +++ b/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +ApplicationWindow { + id: window + width: 400 + height: 400 + title: "releaseAfterExitTransition" + + property alias popup: popup + property alias modalPopup: modalPopup + + Popup { + id: popup + y: parent.height - height + width: 50 + height: 50 + } + + Popup { + id: modalPopup + modal: true + y: parent.height - height + width: 50 + height: 50 + exit: Transition { PauseAnimation { duration: 100 } } + } +} diff --git a/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp b/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp index 54952d128..3d50e2dd4 100644 --- a/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp +++ b/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp @@ -100,6 +100,7 @@ private slots: void invisibleToolTipOpen(); void centerInOverlayWithinStackViewItem(); void destroyDuringExitTransition(); + void releaseAfterExitTransition(); }; void tst_QQuickPopup::initTestCase() @@ -1575,6 +1576,34 @@ void tst_QQuickPopup::destroyDuringExitTransition() QVERIFY(!button->isDown()); } +void tst_QQuickPopup::releaseAfterExitTransition() +{ + QQuickApplicationHelper helper(this, "releaseAfterExitTransition.qml"); + QVERIFY2(helper.ready, helper.failureMessage()); + + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window)); + + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + QQuickPopup *modalPopup = window->property("modalPopup").value(); + QQuickPopup *popup = window->property("popup").value(); + + modalPopup->open(); + QTRY_VERIFY(modalPopup->isOpened()); + + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); + // wait until the transition is finished and the overlay hides itself + QTRY_VERIFY(!overlay->isVisible()); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); + + popup->open(); + QTRY_VERIFY(popup->isOpened()); + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); + QTRY_VERIFY(!popup->isOpened()); +} + + QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup) #include "tst_qquickpopup.moc" Submodule qtscript 5be95f96...be545189 (commits not present) Submodule qtspeech 87bd324a..75142c77: diff --git a/qtspeech/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp b/qtspeech/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp index 6eb74b8..bcc7dd1 100644 --- a/qtspeech/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp +++ b/qtspeech/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp @@ -357,7 +357,9 @@ QVector QTextToSpeechEngineSpeechd::availableLocales() const QVector QTextToSpeechEngineSpeechd::availableVoices() const { - return m_voices.values(m_currentLocale.name()).toVector(); + QList resultList = m_voices.values(m_currentLocale.name()); + std::reverse(resultList.begin(), resultList.end()); + return resultList.toVector(); } // We have no way of knowing our own client_id since speech-dispatcher seems to be incomplete Submodule qtsvg 7e6a3b38..37b2c764: diff --git a/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp b/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp index 561e77e..12e0574 100644 --- a/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp +++ b/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp @@ -191,6 +191,8 @@ bool QSvgIOHandler::read(QImage *image) } } if (!finalSize.isEmpty()) { + if (qMax(finalSize.width(), finalSize.height()) > 0xffff) + return false; // Assume corrupted file image->fill(d->backColor.rgba()); QPainter p(image); d->r.render(&p, bounds); diff --git a/qtsvg/src/svg/qsvgfont_p.h b/qtsvg/src/svg/qsvgfont_p.h index fd0a3fa..fcffbe8 100644 --- a/qtsvg/src/svg/qsvgfont_p.h +++ b/qtsvg/src/svg/qsvgfont_p.h @@ -74,6 +74,7 @@ public: class Q_SVG_PRIVATE_EXPORT QSvgFont : public QSvgRefCounted { public: + static constexpr qreal DEFAULT_UNITS_PER_EM = 1000; QSvgFont(qreal horizAdvX); void setFamilyName(const QString &name); @@ -86,9 +87,7 @@ public: void draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const; public: QString m_familyName; - qreal m_unitsPerEm; - qreal m_ascent; - qreal m_descent; + qreal m_unitsPerEm = DEFAULT_UNITS_PER_EM; qreal m_horizAdvX; QHash m_glyphs; }; diff --git a/qtsvg/src/svg/qsvghandler.cpp b/qtsvg/src/svg/qsvghandler.cpp index b2227b6..222b6d8 100644 --- a/qtsvg/src/svg/qsvghandler.cpp +++ b/qtsvg/src/svg/qsvghandler.cpp @@ -1393,9 +1393,10 @@ static void parseFont(QSvgNode *node, case FontSizeNone: break; case FontSizeValue: { - QSvgHandler::LengthType dummy; // should always be pixel size - fontStyle->setSize(qMin(parseLength(attributes.fontSize, dummy, handler), - qreal(0xffff))); + QSvgHandler::LengthType type; + qreal fs = parseLength(attributes.fontSize, type, handler); + fs = convertToPixels(fs, true, type); + fontStyle->setSize(qMin(fs, qreal(0xffff))); } break; default: @@ -2513,6 +2514,8 @@ static bool parseAnimateTransformNode(QSvgNode *parent, ++s; } } + if (vals.count() % 3 != 0) + return false; bool ok = true; int begin = parseClockValue(beginStr, &ok); @@ -2576,6 +2579,8 @@ static QSvgNode *createCircleNode(QSvgNode *parent, qreal ncx = toDouble(cx); qreal ncy = toDouble(cy); qreal nr = toDouble(r); + if (nr < 0.0) + return nullptr; QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2); QSvgNode *circle = new QSvgCircle(parent, rect); @@ -2666,7 +2671,7 @@ static bool parseFontFaceNode(QSvgStyleProperty *parent, qreal unitsPerEm = toDouble(unitsPerEmStr); if (!unitsPerEm) - unitsPerEm = 1000; + unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM; if (!name.isEmpty()) font->setFamilyName(name); @@ -3046,15 +3051,16 @@ static QSvgStyleProperty *createRadialGradientNode(QSvgNode *node, qreal ncx = 0.5; qreal ncy = 0.5; - qreal nr = 0.5; if (!cx.isEmpty()) ncx = toDouble(cx); if (!cy.isEmpty()) ncy = toDouble(cy); + + qreal nr = 0.0; if (!r.isEmpty()) nr = toDouble(r); - if (nr < 0.5) - nr = 0.5; + if (nr <= 0.0) + return nullptr; qreal nfx = ncx; if (!fx.isEmpty()) @@ -3350,7 +3356,9 @@ static QSvgNode *createTextNode(QSvgNode *parent, //### editable and rotate not handled QSvgHandler::LengthType type; qreal nx = parseLength(x, type, handler); + nx = convertToPixels(nx, true, type); qreal ny = parseLength(y, type, handler); + ny = convertToPixels(ny, true, type); QSvgNode *text = new QSvgText(parent, QPointF(nx, ny)); return text; diff --git a/qtsvg/src/svg/qsvgstructure.cpp b/qtsvg/src/svg/qsvgstructure.cpp index b89608b..89c9e4e 100644 --- a/qtsvg/src/svg/qsvgstructure.cpp +++ b/qtsvg/src/svg/qsvgstructure.cpp @@ -255,9 +255,13 @@ inline static bool isSupportedSvgFeature(const QString &str) }; if (str.length() <= MAX_WORD_LENGTH && str.length() >= MIN_WORD_LENGTH) { + const char16_t unicode44 = str.at(44).unicode(); + const char16_t unicode45 = str.at(45).unicode(); + if (unicode44 >= sizeof(asso_values) || unicode45 >= sizeof(asso_values)) + return false; const int key = str.length() - + asso_values[str.at(45).unicode()] - + asso_values[str.at(44).unicode()]; + + asso_values[unicode45] + + asso_values[unicode44]; if (key <= MAX_HASH_VALUE && key >= 0) return str == QLatin1String(wordlist[key]); } diff --git a/qtsvg/src/svg/qsvgtinydocument.cpp b/qtsvg/src/svg/qsvgtinydocument.cpp index 63d0797..19e7154 100644 --- a/qtsvg/src/svg/qsvgtinydocument.cpp +++ b/qtsvg/src/svg/qsvgtinydocument.cpp @@ -433,8 +433,16 @@ void QSvgTinyDocument::draw(QPainter *p, QSvgExtraStates &) draw(p); } +static bool isValidMatrix(const QTransform &transform) +{ + qreal determinant = transform.determinant(); + return qIsFinite(determinant); +} + void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect) { + QTransform oldTransform = p->worldTransform(); + QRectF target = targetRect; if (target.isEmpty()) { QPaintDevice *dev = p->device(); @@ -487,6 +495,9 @@ void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, } #endif } + + if (!isValidMatrix(p->worldTransform())) + p->setWorldTransform(oldTransform); } QRectF QSvgTinyDocument::boundsOnElement(const QString &id) const diff --git a/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index 36c76ec..db71e02 100644 --- a/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -86,6 +86,8 @@ private slots: void oss_fuzz_23731(); void oss_fuzz_24131(); void oss_fuzz_24738(); + void illegalAnimateTransform_data(); + void illegalAnimateTransform(); #ifndef QT_NO_COMPRESS void testGzLoading(); @@ -1646,5 +1648,22 @@ void tst_QSvgRenderer::oss_fuzz_24738() QSvgRenderer().load(QByteArray("")); } +void tst_QSvgRenderer::illegalAnimateTransform_data() +{ + QTest::addColumn("svg"); + + QTest::newRow("case1") << QByteArray(""); + QTest::newRow("case2") << QByteArray(""); + QTest::newRow("case3") << QByteArray(""); + QTest::newRow("case4") << QByteArray(""); +} + +void tst_QSvgRenderer::illegalAnimateTransform() +{ + QFETCH(QByteArray, svg); + QSvgRenderer renderer; + QVERIFY(!renderer.load(svg)); // also shouldn't assert +} + QTEST_MAIN(tst_QSvgRenderer) #include "tst_qsvgrenderer.moc" Submodule qttools 7918981b..c14dc7cf: diff --git a/qttools/src/assistant/help/help.pro b/qttools/src/assistant/help/help.pro index 800c4a38d..7556f451b 100644 --- a/qttools/src/assistant/help/help.pro +++ b/qttools/src/assistant/help/help.pro @@ -1,7 +1,6 @@ TARGET = QtHelp QT = core-private gui widgets sql -QT_PRIVATE = network DEFINES += QHELP_LIB diff --git a/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp b/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp index feab1e2d5..cbfb82507 100644 --- a/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp +++ b/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp @@ -445,7 +445,9 @@ bool HelpGeneratorPrivate::insertFiles(const QStringList &files, const QString & if (filterSetId < 0) return false; ++filterSetId; - for (int attId : qAsConst(filterAtts)) { + QList attValues = filterAtts.values(); + std::sort(attValues.begin(), attValues.end()); + for (int attId : qAsConst(attValues)) { m_query->prepare(QLatin1String("INSERT INTO FileAttributeSetTable " "VALUES(?, ?)")); m_query->bindValue(0, filterSetId); diff --git a/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro b/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro index bb22000c8..415347a00 100644 --- a/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro +++ b/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro @@ -1,4 +1,4 @@ -QT += network help-private +QT += help-private QTPLUGIN.platforms = qminimal QTPLUGIN.sqldrivers = qsqlite Submodule qtwayland 9e0ec09d..aabaa4ef: diff --git a/qtwayland/src/client/configure.json b/qtwayland/src/client/configure.json index 2f424580..29222357 100644 --- a/qtwayland/src/client/configure.json +++ b/qtwayland/src/client/configure.json @@ -149,8 +149,7 @@ "#endif" ] }, - "libs": "-ldrm", - "use": "egl" + "use": "drm egl" }, "vulkan-server-buffer": { "label": "Vulkan Buffer Sharing", @@ -168,7 +167,8 @@ "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;", "return 0;" ] - } + }, + "use": "wayland-client" }, "egl_1_5-wayland": { "label": "EGL 1.5 with Wayland Platform", @@ -183,7 +183,7 @@ "eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_EXT, (struct wl_display *)(nullptr), nullptr);" ] }, - "use": "egl" + "use": "egl wayland-client" } }, diff --git a/qtwayland/src/client/global/qwaylandclientextension.cpp b/qtwayland/src/client/global/qwaylandclientextension.cpp index 125b1e19..edccfe63 100644 --- a/qtwayland/src/client/global/qwaylandclientextension.cpp +++ b/qtwayland/src/client/global/qwaylandclientextension.cpp @@ -74,7 +74,10 @@ void QWaylandClientExtensionPrivate::handleRegistryGlobal(void *data, ::wl_regis void QWaylandClientExtension::addRegistryListener() { Q_D(QWaylandClientExtension); - d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); + if (!d->registered) { + d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); + d->registered = true; + } } QWaylandClientExtension::QWaylandClientExtension(const int ver) @@ -88,6 +91,13 @@ QWaylandClientExtension::QWaylandClientExtension(const int ver) QMetaObject::invokeMethod(this, "addRegistryListener", Qt::QueuedConnection); } +QWaylandClientExtension::~QWaylandClientExtension() +{ + Q_D(QWaylandClientExtension); + if (d->registered && !QCoreApplication::closingDown()) + d->waylandIntegration->display()->removeListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); +} + QtWaylandClient::QWaylandIntegration *QWaylandClientExtension::integration() const { Q_D(const QWaylandClientExtension); diff --git a/qtwayland/src/client/global/qwaylandclientextension.h b/qtwayland/src/client/global/qwaylandclientextension.h index 98272e57..5bd28398 100644 --- a/qtwayland/src/client/global/qwaylandclientextension.h +++ b/qtwayland/src/client/global/qwaylandclientextension.h @@ -63,6 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtension : public QObject Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) public: QWaylandClientExtension(const int version); + ~QWaylandClientExtension(); QtWaylandClient::QWaylandIntegration *integration() const; int version() const; diff --git a/qtwayland/src/client/global/qwaylandclientextension_p.h b/qtwayland/src/client/global/qwaylandclientextension_p.h index 69cc46a0..9091efbe 100644 --- a/qtwayland/src/client/global/qwaylandclientextension_p.h +++ b/qtwayland/src/client/global/qwaylandclientextension_p.h @@ -68,6 +68,7 @@ public: QtWaylandClient::QWaylandIntegration *waylandIntegration = nullptr; int version = -1; bool active = false; + bool registered = false; }; class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtensionTemplatePrivate : public QWaylandClientExtensionPrivate diff --git a/qtwayland/src/client/qwaylandclipboard.cpp b/qtwayland/src/client/qwaylandclipboard.cpp index 81f48e05..14561c77 100644 --- a/qtwayland/src/client/qwaylandclipboard.cpp +++ b/qtwayland/src/client/qwaylandclipboard.cpp @@ -54,10 +54,15 @@ namespace QtWaylandClient { QWaylandClipboard::QWaylandClipboard(QWaylandDisplay *display) : mDisplay(display) { + m_clientClipboard[QClipboard::Clipboard] = nullptr; + m_clientClipboard[QClipboard::Selection] = nullptr; } QWaylandClipboard::~QWaylandClipboard() { + if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) + delete m_clientClipboard[QClipboard::Clipboard]; + delete m_clientClipboard[QClipboard::Selection]; } QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) @@ -69,8 +74,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) switch (mode) { case QClipboard::Clipboard: if (auto *dataDevice = seat->dataDevice()) { - if (auto *source = dataDevice->selectionSource()) - return source->mimeData(); + if (dataDevice->selectionSource()) + return m_clientClipboard[QClipboard::Clipboard]; if (auto *offer = dataDevice->selectionOffer()) return offer->mimeData(); } @@ -78,8 +83,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) case QClipboard::Selection: #if QT_CONFIG(wayland_client_primary_selection) if (auto *selectionDevice = seat->primarySelectionDevice()) { - if (auto *source = selectionDevice->selectionSource()) - return source->mimeData(); + if (selectionDevice->selectionSource()) + return m_clientClipboard[QClipboard::Selection]; if (auto *offer = selectionDevice->selectionOffer()) return offer->mimeData(); } @@ -104,17 +109,27 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) if (data && data->hasFormat(plain) && !data->hasFormat(utf8)) data->setData(utf8, data->data(plain)); + if (m_clientClipboard[mode]) { + if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) + delete m_clientClipboard[mode]; + m_clientClipboard[mode] = nullptr; + } + + m_clientClipboard[mode] = data; + switch (mode) { case QClipboard::Clipboard: if (auto *dataDevice = seat->dataDevice()) { - dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr); + dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), + m_clientClipboard[QClipboard::Clipboard]) : nullptr); emitChanged(mode); } break; case QClipboard::Selection: #if QT_CONFIG(wayland_client_primary_selection) if (auto *selectionDevice = seat->primarySelectionDevice()) { - selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), data) : nullptr); + selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), + m_clientClipboard[QClipboard::Selection]) : nullptr); emitChanged(mode); } #endif diff --git a/qtwayland/src/client/qwaylandclipboard_p.h b/qtwayland/src/client/qwaylandclipboard_p.h index ce14e124..bb52683d 100644 --- a/qtwayland/src/client/qwaylandclipboard_p.h +++ b/qtwayland/src/client/qwaylandclipboard_p.h @@ -80,6 +80,7 @@ public: private: QWaylandDisplay *mDisplay = nullptr; QMimeData m_emptyData; + QMimeData *m_clientClipboard[2]; }; } diff --git a/qtwayland/src/client/qwaylanddatadevice.cpp b/qtwayland/src/client/qwaylanddatadevice.cpp index 7e2e3308..e3e60ed5 100644 --- a/qtwayland/src/client/qwaylanddatadevice.cpp +++ b/qtwayland/src/client/qwaylanddatadevice.cpp @@ -72,6 +72,8 @@ QWaylandDataDevice::QWaylandDataDevice(QWaylandDataDeviceManager *manager, QWayl QWaylandDataDevice::~QWaylandDataDevice() { + if (wl_data_device_get_version(object()) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) + release(); } QWaylandDataOffer *QWaylandDataDevice::selectionOffer() const @@ -110,7 +112,7 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const return m_dragOffer.data(); } -bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) +bool QWaylandDataDevice::startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon) { auto *seat = m_display->currentInputDevice(); auto *origin = seat->pointerFocus(); @@ -123,7 +125,28 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) } m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); + + if (wl_data_device_get_version(object()) >= 3) + m_dragSource->set_actions(dropActionsToWl(supportedActions)); + connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); + connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) { + auto drag = static_cast(QGuiApplicationPrivate::platformIntegration()->drag()); + // in old versions drop action is not set, so we guess + if (wl_data_source_get_version(m_dragSource->object()) < 3) { + drag->setResponse(accepted); + } else { + QPlatformDropQtResponse response(accepted, action); + drag->setResponse(response); + } + }); + connect(m_dragSource.data(), &QWaylandDataSource::dndDropped, this, [](bool accepted, Qt::DropAction action) { + QPlatformDropQtResponse response(accepted, action); + static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setDropResponse(response); + }); + connect(m_dragSource.data(), &QWaylandDataSource::finished, this, []() { + static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); + }); start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial()); return true; @@ -152,7 +175,7 @@ void QWaylandDataDevice::data_device_drop() supportedActions = drag->supportedActions(); } else if (m_dragOffer) { dragData = m_dragOffer->mimeData(); - supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; + supportedActions = m_dragOffer->supportedActions(); } else { return; } @@ -162,7 +185,11 @@ void QWaylandDataDevice::data_device_drop() QGuiApplication::keyboardModifiers()); if (drag) { - static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response); + auto drag = static_cast(QGuiApplicationPrivate::platformIntegration()->drag()); + drag->setDropResponse(response); + drag->finishDrag(); + } else if (m_dragOffer) { + m_dragOffer->finish(); } } @@ -186,7 +213,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, supportedActions = drag->supportedActions(); } else if (m_dragOffer) { dragData = m_dragOffer->mimeData(); - supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; + supportedActions = m_dragOffer->supportedActions(); } const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, @@ -197,11 +224,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); } - if (response.isAccepted()) { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); - } else { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); - } + sendResponse(supportedActions, response); } void QWaylandDataDevice::data_device_leave() @@ -235,10 +258,10 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe supportedActions = drag->supportedActions(); } else { dragData = m_dragOffer->mimeData(); - supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; + supportedActions = m_dragOffer->supportedActions(); } - QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, + const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); @@ -246,11 +269,7 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); } - if (response.isAccepted()) { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); - } else { - wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); - } + sendResponse(supportedActions, response); } #endif // QT_CONFIG(draganddrop) @@ -277,14 +296,10 @@ void QWaylandDataDevice::selectionSourceCancelled() #if QT_CONFIG(draganddrop) void QWaylandDataDevice::dragSourceCancelled() { + static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); m_dragSource.reset(); } -void QWaylandDataDevice::dragSourceTargetChanged(const QString &mimeType) -{ - static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->updateTarget(mimeType); -} - QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) const { QPoint pnt(wl_fixed_to_int(x), wl_fixed_to_int(y)); @@ -297,6 +312,33 @@ QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) con } return pnt; } + +void QWaylandDataDevice::sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response) +{ + if (response.isAccepted()) { + if (wl_data_device_get_version(object()) >= 3) + m_dragOffer->set_actions(dropActionsToWl(supportedActions), dropActionsToWl(response.acceptedAction())); + + m_dragOffer->accept(m_enterSerial, m_dragOffer->firstFormat()); + } else { + m_dragOffer->accept(m_enterSerial, QString()); + } +} + +int QWaylandDataDevice::dropActionsToWl(Qt::DropActions actions) +{ + + int wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (actions & Qt::CopyAction) + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (actions & (Qt::MoveAction | Qt::TargetMoveAction)) + wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + + // wayland does not support LinkAction at the time of writing + return wlActions; +} + + #endif // QT_CONFIG(draganddrop) } diff --git a/qtwayland/src/client/qwaylanddatadevice_p.h b/qtwayland/src/client/qwaylanddatadevice_p.h index 16c3ad28..801dcc2c 100644 --- a/qtwayland/src/client/qwaylanddatadevice_p.h +++ b/qtwayland/src/client/qwaylanddatadevice_p.h @@ -64,6 +64,7 @@ QT_REQUIRE_CONFIG(wayland_datadevice); QT_BEGIN_NAMESPACE class QMimeData; +class QPlatformDragQtResponse; class QWindow; namespace QtWaylandClient { @@ -89,7 +90,7 @@ public: #if QT_CONFIG(draganddrop) QWaylandDataOffer *dragOffer() const; - bool startDrag(QMimeData *mimeData, QWaylandWindow *icon); + bool startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon); void cancelDrag(); #endif @@ -109,13 +110,16 @@ private Q_SLOTS: #if QT_CONFIG(draganddrop) void dragSourceCancelled(); - void dragSourceTargetChanged(const QString &mimeType); #endif private: #if QT_CONFIG(draganddrop) QPoint calculateDragPosition(int x, int y, QWindow *wnd) const; #endif + void sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response); + + static int dropActionsToWl(Qt::DropActions dropActions); + QWaylandDisplay *m_display = nullptr; QWaylandInputDevice *m_inputDevice = nullptr; diff --git a/qtwayland/src/client/qwaylanddatadevicemanager.cpp b/qtwayland/src/client/qwaylanddatadevicemanager.cpp index 35d67307..6dc4f77f 100644 --- a/qtwayland/src/client/qwaylanddatadevicemanager.cpp +++ b/qtwayland/src/client/qwaylanddatadevicemanager.cpp @@ -50,8 +50,8 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id) - : wl_data_device_manager(display->wl_registry(), id, 1) +QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id) + : wl_data_device_manager(display->wl_registry(), id, qMin(version, 3)) , m_display(display) { // Create transfer devices for all input devices. diff --git a/qtwayland/src/client/qwaylanddatadevicemanager_p.h b/qtwayland/src/client/qwaylanddatadevicemanager_p.h index bd05c0fb..510d9be4 100644 --- a/qtwayland/src/client/qwaylanddatadevicemanager_p.h +++ b/qtwayland/src/client/qwaylanddatadevicemanager_p.h @@ -68,7 +68,7 @@ class QWaylandInputDevice; class Q_WAYLAND_CLIENT_EXPORT QWaylandDataDeviceManager : public QtWayland::wl_data_device_manager { public: - QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id); + QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id); ~QWaylandDataDeviceManager() override; QWaylandDataDevice *getDataDevice(QWaylandInputDevice *inputDevice); diff --git a/qtwayland/src/client/qwaylanddataoffer.cpp b/qtwayland/src/client/qwaylanddataoffer.cpp index 2297e8a1..fe0ea8c9 100644 --- a/qtwayland/src/client/qwaylanddataoffer.cpp +++ b/qtwayland/src/client/qwaylanddataoffer.cpp @@ -82,6 +82,15 @@ QMimeData *QWaylandDataOffer::mimeData() return m_mimeData.data(); } +Qt::DropActions QWaylandDataOffer::supportedActions() const +{ + if (wl_data_offer_get_version(const_cast<::wl_data_offer*>(object())) < 3) { + return Qt::MoveAction | Qt::CopyAction; + } + + return m_supportedActions; +} + void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd) { receive(mimeType, fd); @@ -93,6 +102,22 @@ void QWaylandDataOffer::data_offer_offer(const QString &mime_type) m_mimeData->appendFormat(mime_type); } +void QWaylandDataOffer::data_offer_action(uint32_t dnd_action) +{ + Q_UNUSED(dnd_action); + // This is the compositor telling the drag target what action it should perform + // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action? +} + +void QWaylandDataOffer::data_offer_source_actions(uint32_t source_actions) +{ + m_supportedActions = Qt::DropActions(); + if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + m_supportedActions |= Qt::MoveAction; + if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + m_supportedActions |= Qt::CopyAction; +} + QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer) : m_dataOffer(dataOffer) { @@ -163,17 +188,18 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T int QWaylandMimeData::readData(int fd, QByteArray &data) const { - fd_set readset; - FD_ZERO(&readset); - FD_SET(fd, &readset); - struct timeval timeout; + struct pollfd readset; + readset.fd = fd; + readset.events = POLLIN; + struct timespec timeout; timeout.tv_sec = 1; - timeout.tv_usec = 0; + timeout.tv_nsec = 0; + Q_FOREVER { - int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout); + int ready = qt_safe_poll(&readset, 1, &timeout); if (ready < 0) { - qWarning() << "QWaylandDataOffer: select() failed"; + qWarning() << "QWaylandDataOffer: qt_safe_poll() failed"; return -1; } else if (ready == 0) { qWarning("QWaylandDataOffer: timeout reading from pipe"); diff --git a/qtwayland/src/client/qwaylanddataoffer_p.h b/qtwayland/src/client/qwaylanddataoffer_p.h index 9cf1483c..6f667398 100644 --- a/qtwayland/src/client/qwaylanddataoffer_p.h +++ b/qtwayland/src/client/qwaylanddataoffer_p.h @@ -82,6 +82,7 @@ public: explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer); ~QWaylandDataOffer() override; QMimeData *mimeData() override; + Qt::DropActions supportedActions() const; QString firstFormat() const; @@ -89,10 +90,13 @@ public: protected: void data_offer_offer(const QString &mime_type) override; + void data_offer_source_actions(uint32_t source_actions) override; + void data_offer_action(uint32_t dnd_action) override; private: QWaylandDisplay *m_display = nullptr; QScopedPointer m_mimeData; + Qt::DropActions m_supportedActions; }; diff --git a/qtwayland/src/client/qwaylanddatasource.cpp b/qtwayland/src/client/qwaylanddatasource.cpp index f45122fb..e085152c 100644 --- a/qtwayland/src/client/qwaylanddatasource.cpp +++ b/qtwayland/src/client/qwaylanddatasource.cpp @@ -71,11 +71,6 @@ QWaylandDataSource::~QWaylandDataSource() destroy(); } -QMimeData * QWaylandDataSource::mimeData() const -{ - return m_mime_data; -} - void QWaylandDataSource::data_source_cancelled() { Q_EMIT cancelled(); @@ -101,7 +96,32 @@ void QWaylandDataSource::data_source_send(const QString &mime_type, int32_t fd) void QWaylandDataSource::data_source_target(const QString &mime_type) { - Q_EMIT targetChanged(mime_type); + m_accepted = !mime_type.isEmpty(); + Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); +} + +void QWaylandDataSource::data_source_action(uint32_t action) +{ + Qt::DropAction qtAction = Qt::IgnoreAction; + + if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + qtAction = Qt::MoveAction; + else if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + qtAction = Qt::CopyAction; + + m_dropAction = qtAction; + Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); +} + +void QWaylandDataSource::data_source_dnd_finished() +{ + Q_EMIT finished(); +} + +void QWaylandDataSource::data_source_dnd_drop_performed() +{ + + Q_EMIT dndDropped(m_accepted, m_dropAction); } } diff --git a/qtwayland/src/client/qwaylanddatasource_p.h b/qtwayland/src/client/qwaylanddatasource_p.h index 25afff79..14d1542d 100644 --- a/qtwayland/src/client/qwaylanddatasource_p.h +++ b/qtwayland/src/client/qwaylanddatasource_p.h @@ -74,20 +74,26 @@ public: QWaylandDataSource(QWaylandDataDeviceManager *dataDeviceManager, QMimeData *mimeData); ~QWaylandDataSource() override; - QMimeData *mimeData() const; - Q_SIGNALS: - void targetChanged(const QString &mime_type); void cancelled(); + void finished(); + + void dndResponseUpdated(bool accepted, Qt::DropAction action); + void dndDropped(bool accepted, Qt::DropAction action); protected: void data_source_cancelled() override; void data_source_send(const QString &mime_type, int32_t fd) override; void data_source_target(const QString &mime_type) override; + void data_source_dnd_drop_performed() override; + void data_source_dnd_finished() override; + void data_source_action(uint32_t action) override; private: QWaylandDisplay *m_display = nullptr; QMimeData *m_mime_data = nullptr; + bool m_accepted = false; + Qt::DropAction m_dropAction = Qt::IgnoreAction; }; } diff --git a/qtwayland/src/client/qwaylanddisplay.cpp b/qtwayland/src/client/qwaylanddisplay.cpp index 78e387bc..27f55965 100644 --- a/qtwayland/src/client/qwaylanddisplay.cpp +++ b/qtwayland/src/client/qwaylanddisplay.cpp @@ -87,10 +87,203 @@ #include +#include // for std::tie + +static void checkWaylandError(struct wl_display *display) +{ + int ecode = wl_display_get_error(display); + if ((ecode == EPIPE || ecode == ECONNRESET)) { + // special case this to provide a nicer error + qWarning("The Wayland connection broke. Did the Wayland compositor die?"); + } else { + qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); + } + _exit(1); +} + QT_BEGIN_NAMESPACE namespace QtWaylandClient { +class EventThread : public QThread +{ + Q_OBJECT +public: + enum OperatingMode { + EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread. + SelfDispatch, // Dispatch the events inside this thread. + }; + + EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue, + OperatingMode mode) + : m_fd(wl_display_get_fd(wl)) + , m_pipefd{ -1, -1 } + , m_wldisplay(wl) + , m_wlevqueue(ev_queue) + , m_mode(mode) + , m_reading(true) + , m_quitting(false) + { + setObjectName(QStringLiteral("WaylandEventThread")); + } + + void readAndDispatchEvents() + { + /* + * Dispatch pending events and flush the requests at least once. If the event thread + * is not reading, try to call _prepare_read() to allow the event thread to poll(). + * If that fails, re-try dispatch & flush again until _prepare_read() is successful. + * + * This allow any call to readAndDispatchEvents() to start event thread's polling, + * not only the one issued from event thread's waitForReading(), which means functions + * called from dispatch_pending() can safely spin an event loop. + */ + for (;;) { + if (dispatchQueuePending() < 0) { + checkWaylandError(m_wldisplay); + return; + } + + wl_display_flush(m_wldisplay); + + // We have to check if event thread is reading every time we dispatch + // something, as that may recursively call this function. + if (m_reading.loadAcquire()) + break; + + if (prepareReadQueue() == 0) { + QMutexLocker l(&m_mutex); + m_reading.storeRelease(true); + m_cond.wakeOne(); + break; + } + } + } + + void stop() + { + // We have to both write to the pipe and set the flag, as the thread may be + // either in the poll() or waiting for _prepare_read(). + if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1) + qWarning("Failed to write to the pipe: %s.", strerror(errno)); + + { + QMutexLocker l(&m_mutex); + m_quitting = true; + m_cond.wakeOne(); + } + + wait(); + } + +Q_SIGNALS: + void needReadAndDispatch(); + +protected: + void run() override + { + // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets + // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore. + struct Pipe + { + Pipe(int *fds) + : fds(fds) + { + if (qt_safe_pipe(fds) != 0) + qWarning("Pipe creation failed. Quitting may hang."); + } + ~Pipe() + { + if (fds[0] != -1) { + close(fds[0]); + close(fds[1]); + } + } + + int *fds; + } pipe(m_pipefd); + + // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the + // outbound ones. Wait until it's done before proceeding, unless we're told to quit. + while (waitForReading()) { + pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } }; + poll(fds, 2, -1); + + if (fds[1].revents & POLLIN) { + // we don't really care to read the byte that was written here since we're closing down + wl_display_cancel_read(m_wldisplay); + break; + } + + if (fds[0].revents & POLLIN) + wl_display_read_events(m_wldisplay); + // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop + // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which + // case we don't care anymore about them. + else + wl_display_cancel_read(m_wldisplay); + } + } + +private: + bool waitForReading() + { + Q_ASSERT(QThread::currentThread() == this); + + m_reading.storeRelease(false); + + if (m_mode == SelfDispatch) { + readAndDispatchEvents(); + } else { + Q_EMIT needReadAndDispatch(); + + QMutexLocker lock(&m_mutex); + // m_reading might be set from our emit or some other invocation of + // readAndDispatchEvents(). + while (!m_reading.loadRelaxed() && !m_quitting) + m_cond.wait(&m_mutex); + } + + return !m_quitting; + } + + int dispatchQueuePending() + { + if (m_wlevqueue) + return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue); + else + return wl_display_dispatch_pending(m_wldisplay); + } + + int prepareReadQueue() + { + if (m_wlevqueue) + return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue); + else + return wl_display_prepare_read(m_wldisplay); + } + + int m_fd; + int m_pipefd[2]; + wl_display *m_wldisplay; + wl_event_queue *m_wlevqueue; + OperatingMode m_mode; + + /* Concurrency note when operating in EmitToDispatch mode: + * m_reading is set to false inside event thread's waitForReading(), and is + * set to true inside main thread's readAndDispatchEvents(). + * The lock is not taken when setting m_reading to false, as the main thread + * is not actively waiting for it to turn false. However, the lock is taken + * inside readAndDispatchEvents() before setting m_reading to true, + * as the event thread is actively waiting for it under the wait condition. + */ + + QAtomicInteger m_reading; + bool m_quitting; + QMutex m_mutex; + QWaitCondition m_cond; +}; + Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging struct wl_surface *QWaylandDisplay::createSurface(void *handle) @@ -160,17 +353,16 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) if (!mXkbContext) qCWarning(lcQpaWayland, "failed to create xkb context"); #endif - - forceRoundTrip(); - - if (!mWaitingScreens.isEmpty()) { - // Give wl_output.done and zxdg_output_v1.done events a chance to arrive - forceRoundTrip(); - } } QWaylandDisplay::~QWaylandDisplay(void) { + if (m_eventThread) + m_eventThread->stop(); + + if (m_frameEventQueueThread) + m_frameEventQueueThread->stop(); + if (mSyncCallback) wl_callback_destroy(mSyncCallback); @@ -189,6 +381,21 @@ QWaylandDisplay::~QWaylandDisplay(void) #endif if (mDisplay) wl_display_disconnect(mDisplay); + + if (m_frameEventQueue) + wl_event_queue_destroy(m_frameEventQueue); +} + +// Steps which is called just after constructor. This separates registry_global() out of the constructor +// so that factory functions in integration can be overridden. +void QWaylandDisplay::initialize() +{ + forceRoundTrip(); + + if (!mWaitingScreens.isEmpty()) { + // Give wl_output.done and zxdg_output_v1.done events a chance to arrive + forceRoundTrip(); + } } void QWaylandDisplay::ensureScreen() @@ -205,98 +412,37 @@ void QWaylandDisplay::ensureScreen() void QWaylandDisplay::checkError() const { - int ecode = wl_display_get_error(mDisplay); - if ((ecode == EPIPE || ecode == ECONNRESET)) { - // special case this to provide a nicer error - qWarning("The Wayland connection broke. Did the Wayland compositor die?"); - } else { - qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); - } - _exit(1); + checkWaylandError(mDisplay); } +// Called in main thread, either from queued signal or directly. void QWaylandDisplay::flushRequests() { - if (wl_display_prepare_read(mDisplay) == 0) { - wl_display_read_events(mDisplay); - } - - if (wl_display_dispatch_pending(mDisplay) < 0) - checkError(); - - { - QReadLocker locker(&m_frameQueueLock); - for (const FrameQueue &q : mExternalQueues) { - QMutexLocker locker(q.mutex); - while (wl_display_prepare_read_queue(mDisplay, q.queue) != 0) - wl_display_dispatch_queue_pending(mDisplay, q.queue); - wl_display_read_events(mDisplay); - wl_display_dispatch_queue_pending(mDisplay, q.queue); - } - } - - wl_display_flush(mDisplay); -} - -void QWaylandDisplay::blockingReadEvents() -{ - if (wl_display_dispatch(mDisplay) < 0) - checkError(); -} - -void QWaylandDisplay::destroyFrameQueue(const QWaylandDisplay::FrameQueue &q) -{ - QWriteLocker locker(&m_frameQueueLock); - auto it = std::find_if(mExternalQueues.begin(), - mExternalQueues.end(), - [&q] (const QWaylandDisplay::FrameQueue &other){ return other.queue == q.queue; }); - Q_ASSERT(it != mExternalQueues.end()); - mExternalQueues.erase(it); - if (q.queue != nullptr) - wl_event_queue_destroy(q.queue); - delete q.mutex; + m_eventThread->readAndDispatchEvents(); } -QWaylandDisplay::FrameQueue QWaylandDisplay::createFrameQueue() +// We have to wait until we have an eventDispatcher before creating the eventThread, +// otherwise forceRoundTrip() may block inside _events_read() because eventThread is +// polling. +void QWaylandDisplay::initEventThread() { - QWriteLocker locker(&m_frameQueueLock); - FrameQueue q{createEventQueue()}; - mExternalQueues.append(q); - return q; -} + m_eventThread.reset( + new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch)); + connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this, + &QWaylandDisplay::flushRequests, Qt::QueuedConnection); + m_eventThread->start(); -wl_event_queue *QWaylandDisplay::createEventQueue() -{ - return wl_display_create_queue(mDisplay); + // wl_display_disconnect() free this. + m_frameEventQueue = wl_display_create_queue(mDisplay); + m_frameEventQueueThread.reset( + new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch)); + m_frameEventQueueThread->start(); } -void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout) +void QWaylandDisplay::blockingReadEvents() { - if (!condition()) - return; - - QElapsedTimer timer; - timer.start(); - struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN); - while (timeout == -1 || timer.elapsed() < timeout) { - while (wl_display_prepare_read_queue(mDisplay, queue) != 0) - wl_display_dispatch_queue_pending(mDisplay, queue); - - wl_display_flush(mDisplay); - - const int remaining = qMax(timeout - timer.elapsed(), 0ll); - const int pollTimeout = timeout == -1 ? -1 : remaining; - if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0) - wl_display_read_events(mDisplay); - else - wl_display_cancel_read(mDisplay); - - if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) - checkError(); - - if (!condition()) - break; - } + if (wl_display_dispatch(mDisplay) < 0) + checkWaylandError(mDisplay); } QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const @@ -347,7 +493,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin if (interface == QStringLiteral("wl_output")) { mWaitingScreens << new QWaylandScreen(this, version, id); } else if (interface == QStringLiteral("wl_compositor")) { - mCompositorVersion = qMin((int)version, 3); + mCompositorVersion = qMin((int)version, 4); mCompositor.init(registry, id, mCompositorVersion); } else if (interface == QStringLiteral("wl_shm")) { mShm.reset(new QWaylandShm(this, version, id)); @@ -356,7 +502,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mInputDevices.append(inputDevice); #if QT_CONFIG(wayland_datadevice) } else if (interface == QStringLiteral("wl_data_device_manager")) { - mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, id)); + mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id)); #endif } else if (interface == QStringLiteral("qt_surface_extension")) { mWindowExtension.reset(new QtWayland::qt_surface_extension(registry, id, 1)); @@ -373,6 +519,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin #if QT_CONFIG(wayland_client_primary_selection) } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); + for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) + inputDevice->setPrimarySelectionDevice(mPrimarySelectionManager->createDevice(inputDevice)); #endif } else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) { mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1)); @@ -431,6 +579,13 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) inputDevice->setTextInput(nullptr); mWaylandIntegration->reconfigureInputContext(); } +#if QT_CONFIG(wayland_client_primary_selection) + if (global.interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { + mPrimarySelectionManager.reset(); + for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) + inputDevice->setPrimarySelectionDevice(nullptr); + } +#endif mGlobals.removeAt(i); break; } @@ -456,9 +611,10 @@ void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data) void QWaylandDisplay::removeListener(RegistryListener listener, void *data) { - std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ + auto iter = std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ return (l.listener == listener && l.data == data); }); + mRegistryListeners.erase(iter, mRegistryListeners.end()); } uint32_t QWaylandDisplay::currentTimeMillisec() @@ -471,50 +627,9 @@ uint32_t QWaylandDisplay::currentTimeMillisec() return 0; } -static void -sync_callback(void *data, struct wl_callback *callback, uint32_t serial) -{ - Q_UNUSED(serial) - bool *done = static_cast(data); - - *done = true; - - // If the wl_callback done event is received after the condition check in the while loop in - // forceRoundTrip(), but before the call to processEvents, the call to processEvents may block - // forever if no more events are posted (eventhough the callback is handled in response to the - // aboutToBlock signal). Hence, we wake up the event dispatcher so forceRoundTrip may return. - // (QTBUG-64696) - if (auto *dispatcher = QThread::currentThread()->eventDispatcher()) - dispatcher->wakeUp(); - - wl_callback_destroy(callback); -} - -static const struct wl_callback_listener sync_listener = { - sync_callback -}; - void QWaylandDisplay::forceRoundTrip() { - // wl_display_roundtrip() works on the main queue only, - // but we use a separate one, so basically reimplement it here - int ret = 0; - bool done = false; - wl_callback *callback = wl_display_sync(mDisplay); - wl_callback_add_listener(callback, &sync_listener, &done); - flushRequests(); - if (QThread::currentThread()->eventDispatcher()) { - while (!done && ret >= 0) { - QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::WaitForMoreEvents); - ret = wl_display_dispatch_pending(mDisplay); - } - } else { - while (!done && ret >= 0) - ret = wl_display_dispatch(mDisplay); - } - - if (ret == -1 && !done) - wl_callback_destroy(callback); + wl_display_roundtrip(mDisplay); } bool QWaylandDisplay::supportsWindowDecoration() const @@ -578,14 +693,10 @@ void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevic if (mLastKeyboardFocus == keyboardFocus) return; - if (mWaylandIntegration->mShellIntegration) { - mWaylandIntegration->mShellIntegration->handleKeyboardFocusChanged(keyboardFocus, mLastKeyboardFocus); - } else { - if (keyboardFocus) - handleWindowActivated(keyboardFocus); - if (mLastKeyboardFocus) - handleWindowDeactivated(mLastKeyboardFocus); - } + if (keyboardFocus) + handleWindowActivated(keyboardFocus); + if (mLastKeyboardFocus) + handleWindowDeactivated(mLastKeyboardFocus); mLastKeyboardFocus = keyboardFocus; } @@ -604,6 +715,19 @@ void QWaylandDisplay::handleWaylandSync() QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window(); if (activeWindow != QGuiApplication::focusWindow()) QWindowSystemInterface::handleWindowActivated(activeWindow); + + if (!activeWindow) { + if (lastInputDevice()) { +#if QT_CONFIG(clipboard) + if (auto *dataDevice = lastInputDevice()->dataDevice()) + dataDevice->invalidateSelectionOffer(); +#endif +#if QT_CONFIG(wayland_client_primary_selection) + if (auto *device = lastInputDevice()->primarySelectionDevice()) + device->invalidateSelectionOffer(); +#endif + } + } } const wl_callback_listener QWaylandDisplay::syncCallbackListener = { @@ -630,6 +754,13 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const return mInputDevices.isEmpty() ? 0 : mInputDevices.first(); } +bool QWaylandDisplay::isKeyboardAvailable() const +{ + return std::any_of( + mInputDevices.constBegin(), mInputDevices.constEnd(), + [this](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; }); +} + #if QT_CONFIG(cursor) QWaylandCursor *QWaylandDisplay::waylandCursor() @@ -656,4 +787,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p } // namespace QtWaylandClient +#include "qwaylanddisplay.moc" + QT_END_NAMESPACE diff --git a/qtwayland/src/client/qwaylanddisplay_p.h b/qtwayland/src/client/qwaylanddisplay_p.h index 1bad8b67..cf91b924 100644 --- a/qtwayland/src/client/qwaylanddisplay_p.h +++ b/qtwayland/src/client/qwaylanddisplay_p.h @@ -111,6 +111,7 @@ class QWaylandSurface; class QWaylandShellIntegration; class QWaylandCursor; class QWaylandCursorTheme; +class EventThread; typedef void (*RegistryListener)(void *data, struct wl_registry *registry, @@ -122,15 +123,11 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandDisplay : public QObject, public QtWayland Q_OBJECT public: - struct FrameQueue { - FrameQueue(wl_event_queue *q = nullptr) : queue(q), mutex(new QMutex) {} - wl_event_queue *queue; - QMutex *mutex; - }; - QWaylandDisplay(QWaylandIntegration *waylandIntegration); ~QWaylandDisplay(void) override; + void initialize(); + #if QT_CONFIG(xkbcommon) struct xkb_context *xkbContext() const { return mXkbContext.get(); } #endif @@ -214,11 +211,11 @@ public: void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice); void handleWindowDestroyed(QWaylandWindow *window); - wl_event_queue *createEventQueue(); - FrameQueue createFrameQueue(); - void destroyFrameQueue(const FrameQueue &q); - void dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout = -1); + wl_event_queue *frameEventQueue() { return m_frameEventQueue; }; + + bool isKeyboardAvailable() const; + void initEventThread(); public slots: void blockingReadEvents(); void flushRequests(); @@ -241,6 +238,9 @@ private: }; struct wl_display *mDisplay = nullptr; + QScopedPointer m_eventThread; + wl_event_queue *m_frameEventQueue = nullptr; + QScopedPointer m_frameEventQueueThread; QtWayland::wl_compositor mCompositor; QScopedPointer mShm; QList mWaitingScreens; @@ -279,11 +279,9 @@ private: QWaylandInputDevice *mLastInputDevice = nullptr; QPointer mLastInputWindow; QPointer mLastKeyboardFocus; - QVector mActiveWindows; - QVector mExternalQueues; + QList mActiveWindows; struct wl_callback *mSyncCallback = nullptr; static const wl_callback_listener syncCallbackListener; - QReadWriteLock m_frameQueueLock; bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull(); diff --git a/qtwayland/src/client/qwaylanddnd.cpp b/qtwayland/src/client/qwaylanddnd.cpp index 6535aa16..7c53f5fa 100644 --- a/qtwayland/src/client/qwaylanddnd.cpp +++ b/qtwayland/src/client/qwaylanddnd.cpp @@ -66,7 +66,7 @@ void QWaylandDrag::startDrag() { QBasicDrag::startDrag(); QWaylandWindow *icon = static_cast(shapedPixmapWindow()->handle()); - if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) { + if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), drag()->supportedActions(), icon)) { icon->addAttachOffset(-drag()->hotSpot()); } else { // Cancelling immediately does not work, since the event loop for QDrag::exec is started @@ -80,6 +80,9 @@ void QWaylandDrag::cancel() QBasicDrag::cancel(); m_display->currentInputDevice()->dataDevice()->cancelDrag(); + + if (drag()) + drag()->deleteLater(); } void QWaylandDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) @@ -103,33 +106,41 @@ void QWaylandDrag::endDrag() m_display->currentInputDevice()->handleEndDrag(); } -void QWaylandDrag::updateTarget(const QString &mimeType) +void QWaylandDrag::setResponse(bool accepted) { - setCanDrop(!mimeType.isEmpty()); - - if (canDrop()) { - updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); - } else { - updateCursor(Qt::IgnoreAction); - } + // This method is used for old DataDevices where the drag action is not communicated + Qt::DropAction action = defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers()); + setResponse(QPlatformDropQtResponse(accepted, action)); } -void QWaylandDrag::setResponse(const QPlatformDragQtResponse &response) +void QWaylandDrag::setResponse(const QPlatformDropQtResponse &response) { setCanDrop(response.isAccepted()); if (canDrop()) { - updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); + updateCursor(response.acceptedAction()); } else { updateCursor(Qt::IgnoreAction); } } -void QWaylandDrag::finishDrag(const QPlatformDropQtResponse &response) +void QWaylandDrag::setDropResponse(const QPlatformDropQtResponse &response) { setExecutedDropAction(response.acceptedAction()); +} + +void QWaylandDrag::finishDrag() +{ QKeyEvent event(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); eventFilter(shapedPixmapWindow(), &event); + + if (drag()) + drag()->deleteLater(); +} + +bool QWaylandDrag::ownsDragObject() const +{ + return true; } } diff --git a/qtwayland/src/client/qwaylanddnd_p.h b/qtwayland/src/client/qwaylanddnd_p.h index 474fe2ab..46f629ac 100644 --- a/qtwayland/src/client/qwaylanddnd_p.h +++ b/qtwayland/src/client/qwaylanddnd_p.h @@ -71,9 +71,10 @@ public: QWaylandDrag(QWaylandDisplay *display); ~QWaylandDrag() override; - void updateTarget(const QString &mimeType); - void setResponse(const QPlatformDragQtResponse &response); - void finishDrag(const QPlatformDropQtResponse &response); + void setResponse(bool accepted); + void setResponse(const QPlatformDropQtResponse &response); + void setDropResponse(const QPlatformDropQtResponse &response); + void finishDrag(); protected: void startDrag() override; @@ -82,6 +83,7 @@ protected: void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; void endDrag() override; + bool ownsDragObject() const override; private: QWaylandDisplay *m_display = nullptr; diff --git a/qtwayland/src/client/qwaylandinputdevice.cpp b/qtwayland/src/client/qwaylandinputdevice.cpp index e931d1f5..9a0fe49d 100644 --- a/qtwayland/src/client/qwaylandinputdevice.cpp +++ b/qtwayland/src/client/qwaylandinputdevice.cpp @@ -310,8 +310,7 @@ void QWaylandInputDevice::Pointer::updateCursor() auto shape = seat()->mCursor.shape; if (shape == Qt::BlankCursor) { - if (mCursor.surface) - mCursor.surface->hide(); + getOrCreateCursorSurface()->hide(); return; } @@ -1304,14 +1303,6 @@ void QWaylandInputDevice::Keyboard::handleFocusDestroyed() void QWaylandInputDevice::Keyboard::handleFocusLost() { mFocus = nullptr; -#if QT_CONFIG(clipboard) - if (auto *dataDevice = mParent->dataDevice()) - dataDevice->invalidateSelectionOffer(); -#endif -#if QT_CONFIG(wayland_client_primary_selection) - if (auto *device = mParent->primarySelectionDevice()) - device->invalidateSelectionOffer(); -#endif mParent->mQDisplay->handleKeyboardFocusChanged(mParent); mRepeatTimer.stop(); } @@ -1400,6 +1391,7 @@ void QWaylandInputDevice::Touch::touch_cancel() if (touchExt) touchExt->touchCanceled(); + mFocus = nullptr; QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice); } diff --git a/qtwayland/src/client/qwaylandintegration.cpp b/qtwayland/src/client/qwaylandintegration.cpp index d257e2e3..54861600 100644 --- a/qtwayland/src/client/qwaylandintegration.cpp +++ b/qtwayland/src/client/qwaylandintegration.cpp @@ -125,6 +125,9 @@ QWaylandIntegration::QWaylandIntegration() #endif reconfigureInputContext(); + + QWaylandWindow::fixedToplevelPositions = + !qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_FIXED_POSITIONS"); } QWaylandIntegration::~QWaylandIntegration() @@ -192,14 +195,18 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const void QWaylandIntegration::initialize() { + mDisplay->initEventThread(); + + // Call after eventDispatcher is fully connected, for QWaylandDisplay::forceRoundTrip() + mDisplay->initialize(); + + // But the aboutToBlock() and awake() should be connected after initializePlatform(). + // Otherwise the connected flushRequests() may consumes up all events before processEvents starts to wait, + // so that processEvents(QEventLoop::WaitForMoreEvents) may be blocked in the forceRoundTrip(). QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; QObject::connect(dispatcher, SIGNAL(aboutToBlock()), mDisplay.data(), SLOT(flushRequests())); QObject::connect(dispatcher, SIGNAL(awake()), mDisplay.data(), SLOT(flushRequests())); - int fd = wl_display_get_fd(mDisplay->wl_display()); - QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data()); - QObject::connect(sn, SIGNAL(activated(QSocketDescriptor)), mDisplay.data(), SLOT(flushRequests())); - // Qt does not support running with no screens mDisplay->ensureScreen(); } @@ -262,6 +269,14 @@ QWaylandDisplay *QWaylandIntegration::display() const return mDisplay.data(); } +Qt::KeyboardModifiers QWaylandIntegration::queryKeyboardModifiers() const +{ + if (auto *seat = mDisplay->currentInputDevice()) { + return seat->modifiers(); + } + return Qt::NoModifier; +} + QList QWaylandIntegration::possibleKeys(const QKeyEvent *event) const { if (auto *seat = mDisplay->currentInputDevice()) diff --git a/qtwayland/src/client/qwaylandintegration_p.h b/qtwayland/src/client/qwaylandintegration_p.h index ff70ae25..73b80658 100644 --- a/qtwayland/src/client/qwaylandintegration_p.h +++ b/qtwayland/src/client/qwaylandintegration_p.h @@ -107,6 +107,8 @@ public: QWaylandDisplay *display() const; + Qt::KeyboardModifiers queryKeyboardModifiers() const override; + QList possibleKeys(const QKeyEvent *event) const override; QStringList themeNames() const override; diff --git a/qtwayland/src/client/qwaylandnativeinterface.cpp b/qtwayland/src/client/qwaylandnativeinterface.cpp index bf54a1a0..9763c312 100644 --- a/qtwayland/src/client/qwaylandnativeinterface.cpp +++ b/qtwayland/src/client/qwaylandnativeinterface.cpp @@ -139,7 +139,7 @@ void *QWaylandNativeInterface::nativeResourceForScreen(const QByteArray &resourc { QByteArray lowerCaseResource = resourceString.toLower(); - if (lowerCaseResource == "output") + if (lowerCaseResource == "output" && !screen->handle()->isPlaceholder()) return ((QWaylandScreen *) screen->handle())->output(); return nullptr; diff --git a/qtwayland/src/client/qwaylandprimaryselectionv1.cpp b/qtwayland/src/client/qwaylandprimaryselectionv1.cpp index 832f9678..ea508771 100644 --- a/qtwayland/src/client/qwaylandprimaryselectionv1.cpp +++ b/qtwayland/src/client/qwaylandprimaryselectionv1.cpp @@ -54,11 +54,6 @@ QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1 : zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1))) , m_display(display) { - // Create devices for all seats. - // This only works if we get the global before all devices - const auto seats = m_display->inputDevices(); - for (auto *seat : seats) - seat->setPrimarySelectionDevice(createDevice(seat)); } QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat) diff --git a/qtwayland/src/client/qwaylandscreen.cpp b/qtwayland/src/client/qwaylandscreen.cpp index 6cb337de..5537dafd 100644 --- a/qtwayland/src/client/qwaylandscreen.cpp +++ b/qtwayland/src/client/qwaylandscreen.cpp @@ -60,7 +60,7 @@ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display, } QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) - : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2)) + : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3)) , m_outputId(id) , mWaylandDisplay(waylandDisplay) , mOutputName(QStringLiteral("Screen%1").arg(id)) @@ -72,7 +72,7 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor," << "QScreen may not work correctly"; mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc. - mOutputDone = true; // Fake the done event + mProcessedEvents |= OutputDoneEvent; // Fake the done event maybeInitialize(); } } @@ -81,16 +81,29 @@ QWaylandScreen::~QWaylandScreen() { if (zxdg_output_v1::isInitialized()) zxdg_output_v1::destroy(); + if (wl_output::isInitialized() && wl_output_get_version(wl_output::object()) >= WL_OUTPUT_RELEASE_SINCE_VERSION) + wl_output::release(); +} + +uint QWaylandScreen::requiredEvents() const +{ + uint ret = OutputDoneEvent; + + if (mWaylandDisplay->xdgOutputManager()) { + ret |= XdgOutputNameEvent; + + if (mWaylandDisplay->xdgOutputManager()->version() < 3) + ret |= XdgOutputDoneEvent; + } + return ret; } void QWaylandScreen::maybeInitialize() { Q_ASSERT(!mInitialized); - if (!mOutputDone) - return; - - if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone) + const uint requiredEvents = this->requiredEvents(); + if ((mProcessedEvents & requiredEvents) != requiredEvents) return; mInitialized = true; @@ -276,9 +289,8 @@ void QWaylandScreen::output_scale(int32_t factor) void QWaylandScreen::output_done() { - mOutputDone = true; - if (zxdg_output_v1::isInitialized() && mWaylandDisplay->xdgOutputManager()->version() >= 3) - mXdgOutputDone = true; + mProcessedEvents |= OutputDoneEvent; + if (mInitialized) { updateOutputProperties(); if (zxdg_output_v1::isInitialized()) @@ -339,7 +351,7 @@ void QWaylandScreen::zxdg_output_v1_done() if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3)) qWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor"; - mXdgOutputDone = true; + mProcessedEvents |= XdgOutputDoneEvent; if (mInitialized) updateXdgOutputProperties(); else @@ -348,7 +360,11 @@ void QWaylandScreen::zxdg_output_v1_done() void QWaylandScreen::zxdg_output_v1_name(const QString &name) { + if (Q_UNLIKELY(mInitialized)) + qWarning(lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor"; + mOutputName = name; + mProcessedEvents |= XdgOutputNameEvent; } void QWaylandScreen::updateXdgOutputProperties() diff --git a/qtwayland/src/client/qwaylandscreen_p.h b/qtwayland/src/client/qwaylandscreen_p.h index df1c94f2..050cfdc0 100644 --- a/qtwayland/src/client/qwaylandscreen_p.h +++ b/qtwayland/src/client/qwaylandscreen_p.h @@ -116,6 +116,13 @@ public: static QWaylandScreen *fromWlOutput(::wl_output *output); private: + enum Event : uint { + XdgOutputDoneEvent = 0x1, + OutputDoneEvent = 0x2, + XdgOutputNameEvent = 0x4, + }; + uint requiredEvents() const; + void output_mode(uint32_t flags, int width, int height, int refresh) override; void output_geometry(int32_t x, int32_t y, int32_t width, int32_t height, @@ -148,8 +155,7 @@ private: QSize mPhysicalSize; QString mOutputName; Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; - bool mOutputDone = false; - bool mXdgOutputDone = false; + uint mProcessedEvents = 0; bool mInitialized = false; #if QT_CONFIG(cursor) diff --git a/qtwayland/src/client/qwaylandshmbackingstore.cpp b/qtwayland/src/client/qwaylandshmbackingstore.cpp index dc7ff670..41cffdf7 100644 --- a/qtwayland/src/client/qwaylandshmbackingstore.cpp +++ b/qtwayland/src/client/qwaylandshmbackingstore.cpp @@ -52,6 +52,7 @@ #include +#include #include #include @@ -61,6 +62,9 @@ # ifndef MFD_CLOEXEC # define MFD_CLOEXEC 0x0001U # endif +# ifndef MFD_ALLOW_SEALING +# define MFD_ALLOW_SEALING 0x0002U +# endif #endif QT_BEGIN_NAMESPACE @@ -74,8 +78,10 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, int alloc = stride * size.height(); int fd = -1; -#ifdef SYS_memfd_create - fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC); +#if defined(SYS_memfd_create) && defined(F_SEAL_SEAL) + fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); #endif QScopedPointer filePointer; diff --git a/qtwayland/src/client/qwaylandwindow.cpp b/qtwayland/src/client/qwaylandwindow.cpp index df2dcdaa..3b700002 100644 --- a/qtwayland/src/client/qwaylandwindow.cpp +++ b/qtwayland/src/client/qwaylandwindow.cpp @@ -76,7 +76,6 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr; QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) : QPlatformWindow(window) , mDisplay(display) - , mFrameQueue(mDisplay->createFrameQueue()) , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) { { @@ -95,9 +94,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) QWaylandWindow::~QWaylandWindow() { - mDisplay->destroyFrameQueue(mFrameQueue); - mDisplay->handleWindowDestroyed(this); - delete mWindowDecoration; if (mSurface) @@ -243,6 +239,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const void QWaylandWindow::reset() { + closeChildPopups(); delete mShellSurface; mShellSurface = nullptr; delete mSubSurfaceWindow; @@ -255,17 +252,22 @@ void QWaylandWindow::reset() mSurface.reset(); } - if (mFrameCallback) { - wl_callback_destroy(mFrameCallback); - mFrameCallback = nullptr; - } + { + QMutexLocker lock(&mFrameSyncMutex); + if (mFrameCallback) { + wl_callback_destroy(mFrameCallback); + mFrameCallback = nullptr; + } - mFrameCallbackElapsedTimer.invalidate(); - mWaitingForFrameCallback = false; + mFrameCallbackElapsedTimer.invalidate(); + mWaitingForFrameCallback = false; + } mFrameCallbackTimedOut = false; mMask = QRegion(); mQueuedBuffer = nullptr; + + mDisplay->handleWindowDestroyed(this); } QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface) @@ -351,19 +353,25 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect) } } -void QWaylandWindow::setGeometry(const QRect &rect) +void QWaylandWindow::setGeometry(const QRect &r) { + auto rect = r; + if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup + && window()->type() != Qt::ToolTip) { + rect.moveTo(screen()->geometry().topLeft()); + } setGeometry_helper(rect); if (window()->isVisible() && rect.isValid()) { if (mWindowDecoration) mWindowDecoration->update(); - if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) + if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) { + QMutexLocker lock(&mResizeLock); mResizeDirty = true; - else + } else { QWindowSystemInterface::handleGeometryChange(window(), geometry()); - + } mSentInitialResize = true; } QRect exposeGeometry(QPoint(), geometry().size()); @@ -374,7 +382,7 @@ void QWaylandWindow::setGeometry(const QRect &rect) mShellSurface->setWindowGeometry(windowContentGeometry()); if (isOpaque() && mMask.isEmpty()) - setOpaqueArea(rect); + setOpaqueArea(QRect(QPoint(0, 0), rect.size())); } void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) @@ -399,21 +407,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect) mLastExposeGeometry = rect; } - -static QVector> activePopups; - -void QWaylandWindow::closePopups(QWaylandWindow *parent) -{ - while (!activePopups.isEmpty()) { - auto popup = activePopups.takeLast(); - if (popup.isNull()) - continue; - if (popup.data() == parent) - return; - popup->reset(); - } -} - QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const { QReadLocker lock(&mSurfaceLock); @@ -433,10 +426,7 @@ void QWaylandWindow::setVisible(bool visible) lastVisible = visible; if (visible) { - if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip) - activePopups << this; initWindow(); - mDisplay->flushRequests(); setGeometry(windowGeometry()); // Don't flush the events here, or else the newly visible window may start drawing, but since @@ -444,7 +434,6 @@ void QWaylandWindow::setVisible(bool visible) // QWaylandShmBackingStore::beginPaint(). } else { sendExposeEvent(QRect()); - closePopups(this); reset(); } } @@ -556,12 +545,12 @@ void QWaylandWindow::sendRecursiveExposeEvent() void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) { - Q_ASSERT(!buffer->committed()); QReadLocker locker(&mSurfaceLock); if (mSurface == nullptr) return; if (buffer) { + Q_ASSERT(!buffer->committed()); handleUpdate(); buffer->setBusy(); @@ -583,7 +572,11 @@ void QWaylandWindow::damage(const QRect &rect) if (mSurface == nullptr) return; - mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + const int s = scale(); + if (mDisplay->compositorVersion() >= 4) + mSurface->damage_buffer(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()); + else + mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); } void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage) @@ -619,8 +612,14 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) return; attachOffset(buffer); - for (const QRect &rect: damage) - mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + if (mDisplay->compositorVersion() >= 4) { + const int s = scale(); + for (const QRect &rect: damage) + mSurface->damage_buffer(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()); + } else { + for (const QRect &rect: damage) + mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + } Q_ASSERT(!buffer->committed()); buffer->setCommitted(); mSurface->commit(); @@ -635,42 +634,53 @@ void QWaylandWindow::commit() const wl_callback_listener QWaylandWindow::callbackListener = { [](void *data, wl_callback *callback, uint32_t time) { - Q_UNUSED(callback); Q_UNUSED(time); auto *window = static_cast(data); - window->handleFrameCallback(); + window->handleFrameCallback(callback); } }; -void QWaylandWindow::handleFrameCallback() +void QWaylandWindow::handleFrameCallback(wl_callback* callback) { + QMutexLocker locker(&mFrameSyncMutex); + if (!mFrameCallback) { + // This means the callback is already unset by QWaylandWindow::reset. + // The wl_callback object will be destroyed there too. + return; + } + Q_ASSERT(callback == mFrameCallback); + wl_callback_destroy(callback); + mFrameCallback = nullptr; + mWaitingForFrameCallback = false; mFrameCallbackElapsedTimer.invalidate(); // The rest can wait until we can run it on the correct thread - if (!mWaitingForUpdateDelivery) { - auto doHandleExpose = [this]() { - bool wasExposed = isExposed(); - mFrameCallbackTimedOut = false; - if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? - sendExposeEvent(QRect(QPoint(), geometry().size())); - if (wasExposed && hasPendingUpdateRequest()) - deliverUpdateRequest(); - - mWaitingForUpdateDelivery = false; - }; + auto doHandleExpose = [this]() { + mWaitingForUpdateDelivery.storeRelease(false); + bool wasExposed = isExposed(); + mFrameCallbackTimedOut = false; + if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? + sendExposeEvent(QRect(QPoint(), geometry().size())); + if (wasExposed && hasPendingUpdateRequest()) + deliverUpdateRequest(); + }; + if (mWaitingForUpdateDelivery.testAndSetAcquire(false, true)) { // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() // in the single-threaded case. - mWaitingForUpdateDelivery = true; QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); } + + mFrameSyncWait.notify_all(); } bool QWaylandWindow::waitForFrameSync(int timeout) { - QMutexLocker locker(mFrameQueue.mutex); - mDisplay->dispatchQueueWhile(mFrameQueue.queue, [&]() { return mWaitingForFrameCallback; }, timeout); + QMutexLocker locker(&mFrameSyncMutex); + + QDeadlineTimer deadline(timeout); + while (mWaitingForFrameCallback && mFrameSyncWait.wait(&mFrameSyncMutex, deadline)) { } if (mWaitingForFrameCallback) { qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; @@ -874,7 +884,11 @@ bool QWaylandWindow::createDecoration() // size and are not redrawn, leaving the new buffer empty. As a simple // work-around, we trigger a full extra update whenever the client-side // window decorations are toggled while the window is showing. - window()->requestUpdate(); + // Note: createDecoration() is sometimes called from the render thread + // of Qt Quick. This is essentially wrong and could potentially cause problems, + // but until the underlying issue has been fixed, we have to use invokeMethod() + // here to avoid asserts. + QMetaObject::invokeMethod(window(), &QWindow::requestUpdate); } return mWindowDecoration; @@ -1028,8 +1042,17 @@ void QWaylandWindow::handleScreensChanged() if (newScreen == mLastReportedScreen) return; + if (!newScreen->isPlaceholder() && !newScreen->QPlatformScreen::screen()) + mDisplay->forceRoundTrip(); QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); mLastReportedScreen = newScreen; + if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup + && window()->type() != Qt::ToolTip + && geometry().topLeft() != newScreen->geometry().topLeft()) { + auto geometry = this->geometry(); + geometry.moveTo(newScreen->geometry().topLeft()); + setGeometry(geometry); + } int scale = newScreen->isPlaceholder() ? 1 : static_cast(newScreen)->scale(); if (scale != mScale) { @@ -1101,10 +1124,18 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab) return true; } +Qt::WindowStates QWaylandWindow::windowStates() const +{ + return mLastReportedWindowStates; +} + void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) { createDecoration(); - QWindowSystemInterface::handleWindowStateChanged(window(), states, mLastReportedWindowStates); + Qt::WindowStates statesWithoutActive = states & ~Qt::WindowActive; + Qt::WindowStates lastStatesWithoutActive = mLastReportedWindowStates & ~Qt::WindowActive; + QWindowSystemInterface::handleWindowStateChanged(window(), statesWithoutActive, + lastStatesWithoutActive); mLastReportedWindowStates = states; } @@ -1146,19 +1177,24 @@ void QWaylandWindow::timerEvent(QTimerEvent *event) if (event->timerId() != mFrameCallbackCheckIntervalTimerId) return; - bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); - if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { - killTimer(mFrameCallbackCheckIntervalTimerId); - mFrameCallbackCheckIntervalTimerId = -1; - } - if (mFrameCallbackElapsedTimer.isValid() && callbackTimerExpired) { - mFrameCallbackElapsedTimer.invalidate(); + { + QMutexLocker lock(&mFrameSyncMutex); - qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; - mFrameCallbackTimedOut = true; - mWaitingForUpdate = false; - sendExposeEvent(QRect()); + bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); + if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { + killTimer(mFrameCallbackCheckIntervalTimerId); + mFrameCallbackCheckIntervalTimerId = -1; + } + if (!mFrameCallbackElapsedTimer.isValid() || !callbackTimerExpired) { + return; + } + mFrameCallbackElapsedTimer.invalidate(); } + + qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; + mFrameCallbackTimedOut = true; + mWaitingForUpdate = false; + sendExposeEvent(QRect()); } void QWaylandWindow::requestUpdate() @@ -1167,8 +1203,11 @@ void QWaylandWindow::requestUpdate() Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA // If we have a frame callback all is good and will be taken care of there - if (mWaitingForFrameCallback) - return; + { + QMutexLocker locker(&mFrameSyncMutex); + if (mWaitingForFrameCallback) + return; + } // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet // This is a somewhat redundant behavior and might indicate a bug in the calling code, so log @@ -1181,7 +1220,12 @@ void QWaylandWindow::requestUpdate() // so use invokeMethod to delay the delivery a bit. QMetaObject::invokeMethod(this, [this] { // Things might have changed in the meantime - if (hasPendingUpdateRequest() && !mWaitingForFrameCallback) + { + QMutexLocker locker(&mFrameSyncMutex); + if (mWaitingForFrameCallback) + return; + } + if (hasPendingUpdateRequest()) deliverUpdateRequest(); }, Qt::QueuedConnection); } @@ -1192,19 +1236,18 @@ void QWaylandWindow::requestUpdate() void QWaylandWindow::handleUpdate() { qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread(); + // TODO: Should sync subsurfaces avoid requesting frame callbacks? QReadLocker lock(&mSurfaceLock); if (!mSurface) return; - if (mFrameCallback) { - wl_callback_destroy(mFrameCallback); - mFrameCallback = nullptr; - } + QMutexLocker locker(&mFrameSyncMutex); + if (mWaitingForFrameCallback) + return; - QMutexLocker locker(mFrameQueue.mutex); struct ::wl_surface *wrappedSurface = reinterpret_cast(wl_proxy_create_wrapper(mSurface->object())); - wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mFrameQueue.queue); + wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mDisplay->frameEventQueue()); mFrameCallback = wl_surface_frame(wrappedSurface); wl_proxy_wrapper_destroy(wrappedSurface); wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this); @@ -1214,6 +1257,8 @@ void QWaylandWindow::handleUpdate() // Start a timer for handling the case when the compositor stops sending frame callbacks. if (mFrameCallbackTimeout > 0) { QMetaObject::invokeMethod(this, [this] { + QMutexLocker locker(&mFrameSyncMutex); + if (mWaitingForFrameCallback) { if (mFrameCallbackCheckIntervalTimerId < 0) mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout); @@ -1274,6 +1319,20 @@ void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea) wl_region_destroy(region); } +void QWaylandWindow::addChildPopup(QWaylandWindow *surface) { + mChildPopups.append(surface); +} + +void QWaylandWindow::removeChildPopup(QWaylandWindow *surface) { + mChildPopups.removeAll(surface); +} + +void QWaylandWindow::closeChildPopups() { + while (!mChildPopups.isEmpty()) { + auto popup = mChildPopups.takeLast(); + popup->reset(); + } +} } QT_END_NAMESPACE diff --git a/qtwayland/src/client/qwaylandwindow_p.h b/qtwayland/src/client/qwaylandwindow_p.h index 01337cff..2f219d8c 100644 --- a/qtwayland/src/client/qwaylandwindow_p.h +++ b/qtwayland/src/client/qwaylandwindow_p.h @@ -98,6 +98,9 @@ public: QWaylandWindow(QWindow *window, QWaylandDisplay *display); ~QWaylandWindow() override; + // Keep Toplevels position on the top left corner of their screen + static inline bool fixedToplevelPositions = true; + virtual WindowType windowType() const = 0; virtual void ensureSize(); WId winId() const override; @@ -148,6 +151,7 @@ public: void setWindowState(Qt::WindowStates states) override; void setWindowFlags(Qt::WindowFlags flags) override; void handleWindowStatesChanged(Qt::WindowStates states); + Qt::WindowStates windowStates() const; void raise() override; void lower() override; @@ -206,6 +210,10 @@ public: void handleUpdate(); void deliverUpdateRequest() override; + void addChildPopup(QWaylandWindow* child); + void removeChildPopup(QWaylandWindow* child); + void closeChildPopups(); + public slots: void applyConfigure(); @@ -215,7 +223,11 @@ signals: protected: QWaylandDisplay *mDisplay = nullptr; + + // mSurface can be written by the main thread. Other threads should claim a read lock for access + mutable QReadWriteLock mSurfaceLock; QScopedPointer mSurface; + QWaylandShellSurface *mShellSurface = nullptr; QWaylandSubSurface *mSubSurfaceWindow = nullptr; QVector mChildren; @@ -225,13 +237,14 @@ protected: Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton; WId mWindowId; - bool mWaitingForFrameCallback = false; bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out - bool mWaitingForUpdateDelivery = false; int mFrameCallbackCheckIntervalTimerId = -1; - QElapsedTimer mFrameCallbackElapsedTimer; - struct ::wl_callback *mFrameCallback = nullptr; - QWaylandDisplay::FrameQueue mFrameQueue; + QAtomicInt mWaitingForUpdateDelivery = false; + + bool mWaitingForFrameCallback = false; // Protected by mFrameSyncMutex + QElapsedTimer mFrameCallbackElapsedTimer; // Protected by mFrameSyncMutex + struct ::wl_callback *mFrameCallback = nullptr; // Protected by mFrameSyncMutex + QMutex mFrameSyncMutex; QWaitCondition mFrameSyncWait; // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer @@ -261,6 +274,8 @@ protected: QWaylandBuffer *mQueuedBuffer = nullptr; QRegion mQueuedBufferDamage; + QList> mChildPopups; + private: void setGeometry_helper(const QRect &rect); void initWindow(); @@ -283,12 +298,10 @@ private: QRect mLastExposeGeometry; static const wl_callback_listener callbackListener; - void handleFrameCallback(); + void handleFrameCallback(struct ::wl_callback* callback); static QWaylandWindow *mMouseGrab; - mutable QReadWriteLock mSurfaceLock; - friend class QWaylandSubSurface; }; diff --git a/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h b/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h index ccad0048..4cc9b3b8 100644 --- a/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h +++ b/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h @@ -73,11 +73,10 @@ public: return true; } virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0; + // kept for binary compat with layer-shell-qt virtual void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { - if (newFocus) - m_display->handleWindowActivated(newFocus); - if (oldFocus) - m_display->handleWindowDeactivated(oldFocus); + Q_UNUSED(newFocus); + Q_UNUSED(oldFocus); } virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) { Q_UNUSED(resource); diff --git a/qtwayland/src/compositor/configure.json b/qtwayland/src/compositor/configure.json index bcfd5215..da95d07b 100644 --- a/qtwayland/src/compositor/configure.json +++ b/qtwayland/src/compositor/configure.json @@ -7,6 +7,31 @@ "testDir": "../../config.tests", "libraries": { + "wayland-client": { + "label": "Wayland client library", + "headers": "wayland-version.h", + "test": { + "main": [ + "#if WAYLAND_VERSION_MAJOR < 1", + "# error Wayland 1.8.0 or higher required", + "#endif", + "#if WAYLAND_VERSION_MAJOR == 1", + "# if WAYLAND_VERSION_MINOR < 8", + "# error Wayland 1.8.0 or higher required", + "# endif", + "# if WAYLAND_VERSION_MINOR == 8", + "# if WAYLAND_VERSION_MICRO < 0", + "# error Wayland 1.8.0 or higher required", + "# endif", + "# endif", + "#endif" + ] + }, + "sources": [ + { "type": "pkgConfig", "args": "wayland-client" }, + "-lwayland-client" + ] + }, "wayland-server": { "label": "wayland-server", "headers": "wayland-version.h", @@ -151,8 +176,7 @@ "#endif" ] }, - "libs": "-ldrm", - "use": "egl" + "use": "drm egl" }, "dmabuf-client-buffer": { "label": "Linux Client dma-buf Buffer Sharing", @@ -176,8 +200,7 @@ "return 0;" ] }, - "libs": "-ldrm", - "use": "egl" + "use": "drm egl" }, "vulkan-server-buffer": { "label": "Vulkan Buffer Sharing", @@ -195,7 +218,8 @@ "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;", "return 0;" ] - } + }, + "use": "wayland-client" } }, diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp index 57d4eb6b..872a6237 100644 --- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp +++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp @@ -40,6 +40,7 @@ #include "qwaylandeglwindow.h" #include +#include #include "qwaylandglcontext.h" #include @@ -115,6 +116,7 @@ void QWaylandEglWindow::updateSurface(bool create) } mOffset = QPoint(); } else { + QReadLocker locker(&mSurfaceLock); if (m_waylandEglWindow) { int current_width, current_height; static bool disableResizeCheck = qgetenv("QT_WAYLAND_DISABLE_RESIZECHECK").toInt(); @@ -122,14 +124,16 @@ void QWaylandEglWindow::updateSurface(bool create) if (!disableResizeCheck) { wl_egl_window_get_attached_size(m_waylandEglWindow, ¤t_width, ¤t_height); } - if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height())) { + if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height()) || m_requestedSize != sizeWithMargins) { wl_egl_window_resize(m_waylandEglWindow, sizeWithMargins.width(), sizeWithMargins.height(), mOffset.x(), mOffset.y()); + m_requestedSize = sizeWithMargins; mOffset = QPoint(); m_resize = true; } - } else if (create && wlSurface()) { - m_waylandEglWindow = wl_egl_window_create(wlSurface(), sizeWithMargins.width(), sizeWithMargins.height()); + } else if (create && mSurface) { + m_waylandEglWindow = wl_egl_window_create(mSurface->object(), sizeWithMargins.width(), sizeWithMargins.height()); + m_requestedSize = sizeWithMargins; } if (!m_eglSurface && m_waylandEglWindow && create) { diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h index 6c8f04ec..94c56325 100644 --- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h +++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h @@ -87,6 +87,7 @@ private: mutable QOpenGLFramebufferObject *m_contentFBO = nullptr; QSurfaceFormat m_format; + QSize m_requestedSize; }; } diff --git a/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h b/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h index 56a710c3..c6a8b6c6 100644 --- a/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h +++ b/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h @@ -41,6 +41,8 @@ #include #include +#include + #include #include diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp index 85d25e3c..60bdd491 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp @@ -47,18 +47,21 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window) +QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window) : QWaylandShellSurface(window) , QtWayland::xdg_popup_v5(popup) + , m_parent(parent) , m_window(window) { if (window->display()->windowExtension()) m_extendedWindow = new QWaylandExtendedSurface(window); + m_parent->addChildPopup(m_window); } QWaylandXdgPopupV5::~QWaylandXdgPopupV5() { xdg_popup_destroy(object()); + m_parent->removeChildPopup(m_window); delete m_extendedWindow; } diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h index 7494f6a6..d85f130b 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h @@ -70,7 +70,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgPopupV5 : public QWaylandShellSurface { Q_OBJECT public: - QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window); + QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window); ~QWaylandXdgPopupV5() override; protected: @@ -78,6 +78,7 @@ protected: private: QWaylandExtendedSurface *m_extendedWindow = nullptr; + QWaylandWindow *m_parent = nullptr; QWaylandWindow *m_window = nullptr; }; diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp index 7e242c4a..def8452a 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp @@ -84,7 +84,7 @@ QWaylandXdgPopupV5 *QWaylandXdgShellV5::createXdgPopup(QWaylandWindow *window, Q int x = position.x() + parentWindow->frameMargins().left(); int y = position.y() + parentWindow->frameMargins().top(); - auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), window); + auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), parentWindow, window); m_popups.append(window); QObject::connect(popup, &QWaylandXdgPopupV5::destroyed, [this, window](){ m_popups.removeOne(window); diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp index 4e25949f..cfc60939 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp @@ -85,13 +85,6 @@ QWaylandShellSurface *QWaylandXdgShellV5Integration::createShellSurface(QWayland return m_xdgShell->createXdgSurface(window); } -void QWaylandXdgShellV5Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { - if (newFocus && qobject_cast(newFocus->shellSurface())) - m_display->handleWindowActivated(newFocus); - if (oldFocus && qobject_cast(oldFocus->shellSurface())) - m_display->handleWindowDeactivated(oldFocus); -} - } QT_END_NAMESPACE diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h index ce6bdb9e..aed88670 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h @@ -67,7 +67,6 @@ public: QWaylandXdgShellV5Integration() {} bool initialize(QWaylandDisplay *display) override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; - void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; private: QScopedPointer m_xdgShell; diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp index 8c371661..151c78e3 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp @@ -174,6 +174,7 @@ QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdg , m_xdgSurface(xdgSurface) , m_parent(parent) { + m_parent->window()->addChildPopup(m_xdgSurface->window()); } QWaylandXdgSurfaceV6::Popup::~Popup() @@ -181,6 +182,8 @@ QWaylandXdgSurfaceV6::Popup::~Popup() if (isInitialized()) destroy(); + m_parent->window()->removeChildPopup(m_xdgSurface->window()); + if (m_grabbing) { auto *shell = m_xdgSurface->m_shell; Q_ASSERT(shell->m_topmostGrabbingPopup == this); diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp index 03164316..e8da8ba1 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp @@ -68,20 +68,6 @@ QWaylandShellSurface *QWaylandXdgShellV6Integration::createShellSurface(QWayland return m_xdgShell->getXdgSurface(window); } -void QWaylandXdgShellV6Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) -{ - if (newFocus) { - auto *xdgSurface = qobject_cast(newFocus->shellSurface()); - if (xdgSurface && !xdgSurface->handlesActiveState()) - m_display->handleWindowActivated(newFocus); - } - if (oldFocus && qobject_cast(oldFocus->shellSurface())) { - auto *xdgSurface = qobject_cast(oldFocus->shellSurface()); - if (xdgSurface && !xdgSurface->handlesActiveState()) - m_display->handleWindowDeactivated(oldFocus); - } -} - } QT_END_NAMESPACE diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h index 261f8cbb..c1bcd5c6 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h +++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h @@ -65,7 +65,6 @@ public: QWaylandXdgShellV6Integration() {} bool initialize(QWaylandDisplay *display) override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; - void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; private: QScopedPointer m_xdgShell; diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index fa9c01aa..822b385c 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -67,11 +67,6 @@ QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface) QWaylandXdgSurface::Toplevel::~Toplevel() { - if (m_applied.states & Qt::WindowActive) { - QWaylandWindow *window = m_xdgSurface->window(); - window->display()->handleWindowDeactivated(window); - } - // The protocol spec requires that the decoration object is deleted before xdg_toplevel. delete m_decoration; m_decoration = nullptr; @@ -85,16 +80,15 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) m_normalSize = m_xdgSurface->m_window->windowFrameGeometry().size(); - if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive)) + if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive) + && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) m_xdgSurface->m_window->display()->handleWindowActivated(m_xdgSurface->m_window); - if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive)) + if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive) + && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window); - // TODO: none of the other plugins send WindowActive either, but is it on purpose? - Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive; - - m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive); + m_xdgSurface->m_window->handleWindowStatesChanged(m_pending.states); if (m_pending.size.isEmpty()) { // An empty size in the configure means it's up to the client to choose the size @@ -105,8 +99,6 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size); } - m_xdgSurface->setSizeHints(); - m_applied = m_pending; qCDebug(lcQpaWayland) << "Applied pending xdg_toplevel configure event:" << m_applied.size << m_applied.states; } @@ -203,12 +195,17 @@ QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResi | ((edges & Qt::RightEdge) ? resize_edge_right : 0)); } -QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, +QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, QtWayland::xdg_positioner *positioner) - : xdg_popup(xdgSurface->get_popup(parent->object(), positioner->object())) - , m_xdgSurface(xdgSurface) + : m_xdgSurface(xdgSurface) + , m_parentXdgSurface(qobject_cast(parent->shellSurface())) , m_parent(parent) { + + init(xdgSurface->get_popup(m_parentXdgSurface ? m_parentXdgSurface->object() : nullptr, positioner->object())); + if (m_parent) { + m_parent->addChildPopup(m_xdgSurface->window()); + } } QWaylandXdgSurface::Popup::~Popup() @@ -216,10 +213,14 @@ QWaylandXdgSurface::Popup::~Popup() if (isInitialized()) destroy(); + if (m_parent) { + m_parent->removeChildPopup(m_xdgSurface->window()); + } + if (m_grabbing) { auto *shell = m_xdgSurface->m_shell; Q_ASSERT(shell->m_topmostGrabbingPopup == this); - shell->m_topmostGrabbingPopup = m_parent->m_popup; + shell->m_topmostGrabbingPopup = m_parentXdgSurface ? m_parentXdgSurface->m_popup : nullptr; m_grabbing = false; // Synthesize Qt enter/leave events for popup @@ -267,6 +268,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s m_toplevel->set_parent(parentXdgSurface->m_toplevel->object()); } } + setSizeHints(); } QWaylandXdgSurface::~QWaylandXdgSurface() @@ -382,10 +384,10 @@ void QWaylandXdgSurface::setSizeHints() const int minHeight = qMax(0, m_window->windowMinimumSize().height()); m_toplevel->set_min_size(minWidth, minHeight); - int maxWidth = qMax(0, m_window->windowMaximumSize().width()); + int maxWidth = qMax(minWidth, m_window->windowMaximumSize().width()); if (maxWidth == QWINDOWSIZE_MAX) maxWidth = 0; - int maxHeight = qMax(0, m_window->windowMaximumSize().height()); + int maxHeight = qMax(minHeight, m_window->windowMaximumSize().height()); if (maxHeight == QWINDOWSIZE_MAX) maxHeight = 0; m_toplevel->set_max_size(maxWidth, maxHeight); @@ -410,8 +412,6 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) { Q_ASSERT(!m_toplevel && !m_popup); - auto parentXdgSurface = static_cast(parent->shellSurface()); - auto positioner = new QtWayland::xdg_positioner(m_shell->create_positioner()); // set_popup expects a position relative to the parent QPoint transientPos = m_window->geometry().topLeft(); // this is absolute @@ -425,11 +425,10 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) positioner->set_gravity(QtWayland::xdg_positioner::gravity_bottom_right); positioner->set_size(m_window->geometry().width(), m_window->geometry().height()); positioner->set_constraint_adjustment(QtWayland::xdg_positioner::constraint_adjustment_slide_x - | QtWayland::xdg_positioner::constraint_adjustment_slide_y - | QtWayland::xdg_positioner::constraint_adjustment_flip_x - | QtWayland::xdg_positioner::constraint_adjustment_flip_y); - m_popup = new Popup(this, parentXdgSurface, positioner); + | QtWayland::xdg_positioner::constraint_adjustment_slide_y); + m_popup = new Popup(this, parent, positioner); positioner->destroy(); + delete positioner; } diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h index 96785205..4b518f0a 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h @@ -131,14 +131,15 @@ private: class Popup : public QtWayland::xdg_popup { public: - Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, QtWayland::xdg_positioner *positioner); + Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, QtWayland::xdg_positioner *positioner); ~Popup() override; void grab(QWaylandInputDevice *seat, uint serial); void xdg_popup_popup_done() override; QWaylandXdgSurface *m_xdgSurface = nullptr; - QWaylandXdgSurface *m_parent = nullptr; + QWaylandXdgSurface *m_parentXdgSurface = nullptr; + QWaylandWindow *m_parent = nullptr; bool m_grabbing = false; }; diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp index 8769d971..da0dd6a7 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp @@ -69,20 +69,6 @@ QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWi return m_xdgShell->getXdgSurface(window); } -void QWaylandXdgShellIntegration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) -{ - if (newFocus) { - auto *xdgSurface = qobject_cast(newFocus->shellSurface()); - if (xdgSurface && !xdgSurface->handlesActiveState()) - m_display->handleWindowActivated(newFocus); - } - if (oldFocus && qobject_cast(oldFocus->shellSurface())) { - auto *xdgSurface = qobject_cast(oldFocus->shellSurface()); - if (xdgSurface && !xdgSurface->handlesActiveState()) - m_display->handleWindowDeactivated(oldFocus); - } -} - } QT_END_NAMESPACE diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h index b6caa6c9..2f929f98 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h +++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h @@ -65,7 +65,6 @@ public: QWaylandXdgShellIntegration() {} bool initialize(QWaylandDisplay *display) override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; - void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; private: QScopedPointer m_xdgShell; diff --git a/qtwayland/src/shared/qwaylandmimehelper.cpp b/qtwayland/src/shared/qwaylandmimehelper.cpp index a5fdd34d..e2fe1928 100644 --- a/qtwayland/src/shared/qwaylandmimehelper.cpp +++ b/qtwayland/src/shared/qwaylandmimehelper.cpp @@ -60,7 +60,7 @@ QByteArray QWaylandMimeHelper::getByteArray(QMimeData *mimeData, const QString & buf.open(QIODevice::ReadWrite); QByteArray fmt = "BMP"; if (mimeType.startsWith(QLatin1String("image/"))) { - QByteArray imgFmt = mimeType.mid(6).toUpper().toLatin1(); + QByteArray imgFmt = mimeType.mid(6).toLower().toLatin1(); if (QImageWriter::supportedImageFormats().contains(imgFmt)) fmt = imgFmt; } @@ -74,7 +74,7 @@ QByteArray QWaylandMimeHelper::getByteArray(QMimeData *mimeData, const QString & QList urls = mimeData->urls(); for (int i = 0; i < urls.count(); ++i) { content.append(urls.at(i).toEncoded()); - content.append('\n'); + content.append("\r\n"); } } else { content = mimeData->data(mimeType); diff --git a/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp b/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp index 1568b3b9..067410d0 100644 --- a/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp +++ b/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp @@ -35,7 +35,7 @@ using namespace MockCompositor; -constexpr int dataDeviceVersion = 1; +constexpr int dataDeviceVersion = 3; class DataDeviceCompositor : public DefaultCompositor { public: diff --git a/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp b/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp index 9312c2e5..2ea382f1 100644 --- a/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp +++ b/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp @@ -73,6 +73,7 @@ private slots: void multiTouch(); void multiTouchUpAndMotionFrame(); void tapAndMoveInSameFrame(); + void cancelTouch(); }; void tst_seatv5::bindsToSeat() @@ -646,5 +647,34 @@ void tst_seatv5::tapAndMoveInSameFrame() QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased); } +void tst_seatv5::cancelTouch() +{ + TouchWindow window; + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { + auto *t = touch(); + auto *c = client(); + t->sendDown(xdgToplevel()->surface(), {32, 32}, 1); + t->sendFrame(c); + t->sendCancel(c); + t->sendFrame(c); + }); + + QTRY_VERIFY(!window.m_events.empty()); + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.type, QEvent::TouchBegin); + QCOMPARE(e.touchPointStates, Qt::TouchPointPressed); + QCOMPARE(e.touchPoints.length(), 1); + QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); + } + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.type, QEvent::TouchCancel); + QCOMPARE(e.touchPoints.length(), 0); + } +} + QCOMPOSITOR_TEST_MAIN(tst_seatv5) #include "tst_seatv5.moc" diff --git a/qtwayland/tests/auto/client/shared/coreprotocol.cpp b/qtwayland/tests/auto/client/shared/coreprotocol.cpp index 0d988521..53e12291 100644 --- a/qtwayland/tests/auto/client/shared/coreprotocol.cpp +++ b/qtwayland/tests/auto/client/shared/coreprotocol.cpp @@ -185,6 +185,8 @@ void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION) wl_output::send_done(resource->handle); + + Q_EMIT outputBound(resource); } // Seat stuff @@ -451,6 +453,13 @@ void Touch::sendFrame(wl_client *client) send_frame(r->handle); } +void Touch::sendCancel(wl_client *client) +{ + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + send_cancel(r->handle); +} + uint Keyboard::sendEnter(Surface *surface) { auto serial = m_seat->m_compositor->nextSerial(); diff --git a/qtwayland/tests/auto/client/shared/coreprotocol.h b/qtwayland/tests/auto/client/shared/coreprotocol.h index a1af137a..00c439e1 100644 --- a/qtwayland/tests/auto/client/shared/coreprotocol.h +++ b/qtwayland/tests/auto/client/shared/coreprotocol.h @@ -158,7 +158,7 @@ class WlCompositor : public Global, public QtWaylandServer::wl_compositor { Q_OBJECT public: - explicit WlCompositor(CoreCompositor *compositor, int version = 3) + explicit WlCompositor(CoreCompositor *compositor, int version = 4) : QtWaylandServer::wl_compositor(compositor->m_display, version) , m_compositor(compositor) {} @@ -273,6 +273,9 @@ public: OutputData m_data; int m_version = 1; // TODO: remove on libwayland upgrade +Q_SIGNALS: + void outputBound(Resource *resource); + protected: void output_bind_resource(Resource *resource) override; }; @@ -364,6 +367,7 @@ public: uint sendUp(wl_client *client, int id); void sendMotion(wl_client *client, const QPointF &position, int id); void sendFrame(wl_client *client); + void sendCancel(wl_client *client); Seat *m_seat = nullptr; }; diff --git a/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp b/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp index a415cbf5..b1d3d07d 100644 --- a/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp +++ b/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp @@ -342,7 +342,7 @@ Compositor::Compositor(MockCompositor *mockCompositor) exit(EXIT_FAILURE); } - wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor); + wl_global_create(m_display, &wl_compositor_interface, 4, this, bindCompositor); m_data_device_manager.reset(new DataDeviceManager(this, m_display)); diff --git a/qtwayland/tests/auto/client/shared_old/mocksurface.cpp b/qtwayland/tests/auto/client/shared_old/mocksurface.cpp index e9df5f90..c3246e4a 100644 --- a/qtwayland/tests/auto/client/shared_old/mocksurface.cpp +++ b/qtwayland/tests/auto/client/shared_old/mocksurface.cpp @@ -125,6 +125,16 @@ void Surface::surface_damage(Resource *resource, Q_UNUSED(height); } +void Surface::surface_damage_buffer(Resource *resource, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(width); + Q_UNUSED(height); +} + void Surface::surface_frame(Resource *resource, uint32_t callback) { diff --git a/qtwayland/tests/auto/client/shared_old/mocksurface.h b/qtwayland/tests/auto/client/shared_old/mocksurface.h index 949dc23d..d176837e 100644 --- a/qtwayland/tests/auto/client/shared_old/mocksurface.h +++ b/qtwayland/tests/auto/client/shared_old/mocksurface.h @@ -65,6 +65,8 @@ protected: struct wl_resource *buffer, int x, int y) override; void surface_damage(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; + void surface_damage_buffer(Resource *resource, + int32_t x, int32_t y, int32_t width, int32_t height) override; void surface_frame(Resource *resource, uint32_t callback) override; void surface_commit(Resource *resource) override; diff --git a/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp b/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp index 80429608..68e8d77a 100644 --- a/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp +++ b/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp @@ -55,6 +55,7 @@ private slots: void primaryScreen(); void overrideGeometry(); void changeGeometry(); + void outputCreateEnterRace(); }; void tst_xdgoutput::cleanup() @@ -134,5 +135,39 @@ void tst_xdgoutput::changeGeometry() exec([=] { remove(output(1)); }); } +void tst_xdgoutput::outputCreateEnterRace() +{ + m_config.autoConfigure = true; + m_config.autoEnter = false; + QRasterWindow window; + QSignalSpy screenChanged(&window, &QWindow::screenChanged); + window.resize(400, 320); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + exec([=] { xdgToplevel()->surface()->sendEnter(output(0));}); + + QTRY_COMPARE(QGuiApplication::screens().size(), 1); + QScreen *primaryScreen = QGuiApplication::screens().first(); + QCOMPARE(window.screen(), primaryScreen); + + auto *out = exec([=] { + return add(); + }); + + // In Compositor Thread + connect(out, &Output::outputBound, this, [this](QtWaylandServer::wl_output::Resource *resource){ + auto surface = xdgToplevel()->surface(); + surface->sendLeave(output(0)); + surface->QtWaylandServer::wl_surface::send_enter(surface->resource()->handle, resource->handle); + }, Qt::DirectConnection); + + QTRY_COMPARE(QGuiApplication::screens().size(), 2); + QTRY_COMPARE(window.screen(), QGuiApplication::screens()[1]); + + exec([=] { remove(out); }); + m_config.autoConfigure = false; + m_config.autoEnter = true; +} + QCOMPOSITOR_TEST_MAIN(tst_xdgoutput) #include "tst_xdgoutput.moc" diff --git a/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp b/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp index 2277bbb8..747875b4 100644 --- a/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp +++ b/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp @@ -31,6 +31,7 @@ #include #include #include +#include using namespace MockCompositor; @@ -45,6 +46,7 @@ private slots: void configureStates(); void popup(); void tooltipOnPopup(); + void tooltipAndSiblingPopup(); void switchPopups(); void hidePopupParent(); void pongs(); @@ -138,6 +140,7 @@ void tst_xdgshell::configureSize() void tst_xdgshell::configureStates() { + QVERIFY(qputenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", "0")); QRasterWindow window; window.resize(64, 48); window.show(); @@ -154,9 +157,12 @@ void tst_xdgshell::configureStates() // Toplevel windows don't know their position on xdg-shell // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled -// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue); -// QVERIFY(window.isActive()); - QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly + // window.windowstate() is driven by keyboard focus, however for decorations we want to follow + // XDGShell this is internal to QtWayland so it is queried directly + auto waylandWindow = static_cast(window.handle()); + Q_ASSERT(waylandWindow); + QTRY_VERIFY(waylandWindow->windowStates().testFlag( + Qt::WindowActive)); // Just make sure it eventually get's set correctly const QSize screenSize(640, 480); const uint maximizedSerial = exec([=] { @@ -186,6 +192,7 @@ void tst_xdgshell::configureStates() QCOMPARE(window.windowStates(), Qt::WindowNoState); QCOMPARE(window.frameGeometry().size(), windowedSize); // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled + QVERIFY(qunsetenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT")); } void tst_xdgshell::popup() @@ -340,6 +347,92 @@ void tst_xdgshell::tooltipOnPopup() QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); } +void tst_xdgshell::tooltipAndSiblingPopup() +{ + class ToolTip : public QRasterWindow { + public: + explicit ToolTip(QWindow *parent) { + setTransientParent(parent); + setFlags(Qt::ToolTip); + resize(100, 100); + show(); + } + void mousePressEvent(QMouseEvent *event) override { + QRasterWindow::mousePressEvent(event); + m_popup = new QRasterWindow; + m_popup->setTransientParent(transientParent()); + m_popup->setFlags(Qt::Popup); + m_popup->resize(100, 100); + m_popup->show(); + } + + QRasterWindow *m_popup = nullptr; + }; + + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *event) override { + QRasterWindow::mousePressEvent(event); + m_tooltip = new ToolTip(this); + } + ToolTip *m_tooltip = nullptr; + }; + + Window window; + window.resize(200, 200); + window.show(); + + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); + + exec([=] { + auto *surface = xdgToplevel()->surface(); + auto *p = pointer(); + auto *c = client(); + p->sendEnter(surface, {100, 100}); + p->sendFrame(c); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); + p->sendLeave(surface); + p->sendFrame(c); + }); + + QCOMPOSITOR_TRY_VERIFY(xdgPopup()); + exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); + QCOMPOSITOR_TRY_VERIFY(!xdgPopup()->m_grabbed); + + exec([=] { + auto *surface = xdgPopup()->surface(); + auto *p = pointer(); + auto *c = client(); + p->sendEnter(surface, {100, 100}); + p->sendFrame(c); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); + }); + + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); + exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial); + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_grabbed); + + // Close the middle tooltip (it should not close the sibling popup) + window.m_tooltip->close(); + + QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); + // Verify the remaining xdg surface is a grab popup.. + QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)); + QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)->m_grabbed); + + window.m_tooltip->m_popup->close(); + QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); + QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); +} + // QTBUG-65680 void tst_xdgshell::switchPopups() { @@ -505,7 +598,7 @@ void tst_xdgshell::minMaxSize() window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + // we don't roundtrip with our configuration the initial commit should be correct QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(100, 100)); QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(1000, 1000)); Submodule qtwebchannel fc02db2d..74c06253: diff --git a/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp b/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp index 536eb5c..898d769 100644 --- a/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp +++ b/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp @@ -186,8 +186,6 @@ Q_DECLARE_TYPEINFO(OverloadResolutionCandidate, Q_MOVABLE_TYPE); QMetaObjectPublisher::QMetaObjectPublisher(QWebChannel *webChannel) : QObject(webChannel) , webChannel(webChannel) - , signalHandler(this) - , clientIsIdle(false) , blockUpdates(false) , propertyUpdatesInitialized(false) { @@ -301,17 +299,17 @@ QJsonObject QMetaObjectPublisher::classInfoForObject(const QObject *object, QWeb return data; } -void QMetaObjectPublisher::setClientIsIdle(bool isIdle) +void QMetaObjectPublisher::setClientIsIdle(bool isIdle, QWebChannelAbstractTransport *transport) { - if (clientIsIdle == isIdle) { - return; - } - clientIsIdle = isIdle; - if (!isIdle && timer.isActive()) { - timer.stop(); - } else if (isIdle && !timer.isActive()) { - timer.start(PROPERTY_UPDATE_INTERVAL, this); - } + transportState[transport].clientIsIdle = isIdle; + if (isIdle) + sendEnqueuedPropertyUpdates(transport); +} + +bool QMetaObjectPublisher::isClientIdle(QWebChannelAbstractTransport *transport) +{ + auto found = transportState.find(transport); + return found != transportState.end() && found.value().clientIsIdle; } QJsonObject QMetaObjectPublisher::initializeClient(QWebChannelAbstractTransport *transport) @@ -333,6 +331,7 @@ QJsonObject QMetaObjectPublisher::initializeClient(QWebChannelAbstractTransport void QMetaObjectPublisher::initializePropertyUpdates(const QObject *const object, const QJsonObject &objectInfo) { + auto *signalHandler = signalHandlerFor(object); foreach (const QJsonValue &propertyInfoVar, objectInfo[KEY_PROPERTIES].toArray()) { const QJsonArray &propertyInfo = propertyInfoVar.toArray(); if (propertyInfo.size() < 2) { @@ -353,19 +352,19 @@ void QMetaObjectPublisher::initializePropertyUpdates(const QObject *const object // Only connect for a property update once if (connectedProperties.isEmpty()) { - signalHandler.connectTo(object, signalIndex); + signalHandler->connectTo(object, signalIndex); } connectedProperties.insert(propertyIndex); } // also always connect to destroyed signal - signalHandler.connectTo(object, s_destroyedSignalIndex); + signalHandler->connectTo(object, s_destroyedSignalIndex); } void QMetaObjectPublisher::sendPendingPropertyUpdates() { - if (blockUpdates || !clientIsIdle || pendingPropertyUpdates.isEmpty()) { + if (blockUpdates) { return; } @@ -415,18 +414,19 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates() // data does not contain specific updates if (!data.isEmpty()) { - setClientIsIdle(false); - message[KEY_DATA] = data; - broadcastMessage(message); + enqueueBroadcastMessage(message); } // send every property update which is not supposed to be broadcasted const QHash::const_iterator suend = specificUpdates.constEnd(); for (QHash::const_iterator it = specificUpdates.constBegin(); it != suend; ++it) { message[KEY_DATA] = it.value(); - it.key()->sendMessage(message); + enqueueMessage(message, it.key()); } + + for (auto state = transportState.begin(); state != transportState.end(); ++state) + sendEnqueuedPropertyUpdates(state.key()); } QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMethod &method, @@ -572,7 +572,7 @@ void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signal } } else { pendingPropertyUpdates[object][signalIndex] = arguments; - if (clientIsIdle && !blockUpdates && !timer.isActive()) { + if (!blockUpdates && !timer.isActive()) { timer.start(PROPERTY_UPDATE_INTERVAL, this); } } @@ -590,7 +590,7 @@ void QMetaObjectPublisher::objectDestroyed(const QObject *object) // only remove from handler when we initialized the property updates // cf: https://bugreports.qt.io/browse/QTBUG-60250 if (propertyUpdatesInitialized) { - signalHandler.remove(object); + signalHandlerFor(object)->remove(object); signalToPropertyMap.remove(object); } pendingPropertyUpdates.remove(object); @@ -852,6 +852,52 @@ void QMetaObjectPublisher::broadcastMessage(const QJsonObject &message) const } } +void QMetaObjectPublisher::enqueueBroadcastMessage(const QJsonObject &message) +{ + if (webChannel->d_func()->transports.isEmpty()) { + qWarning("QWebChannel is not connected to any transports, cannot send message: %s", + QJsonDocument(message).toJson().constData()); + return; + } + + for (auto *transport : webChannel->d_func()->transports) { + auto &state = transportState[transport]; + state.queuedMessages.append(message); + } +} + +void QMetaObjectPublisher::enqueueMessage(const QJsonObject &message, + QWebChannelAbstractTransport *transport) +{ + auto &state = transportState[transport]; + state.queuedMessages.append(message); +} + +void QMetaObjectPublisher::sendEnqueuedPropertyUpdates(QWebChannelAbstractTransport *transport) +{ + auto found = transportState.find(transport); + if (found != transportState.end() && found.value().clientIsIdle + && !found.value().queuedMessages.isEmpty()) { + + // If the client is connected with an in-process transport, it can + // happen that a message triggers a subsequent property change. In + // that case, we need to ensure that the queued messages have already + // been cleared; otherwise the recursive call will send everythig again. + // Case in point: The qmlwebchannel tests fail if we don't clear the + // queued messages before sending them out. + // For that same reason set the client to "busy" (aka non-idle) just + // right before sending out the messages; otherwise a potential + // "Idle" type message will not correctly restore the Idle state. + const auto messages = std::move(found.value().queuedMessages); + Q_ASSERT(found.value().queuedMessages.isEmpty()); + found.value().clientIsIdle = false; + + for (const auto &message : messages) { + transport->sendMessage(message); + } + } +} + void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannelAbstractTransport *transport) { if (!webChannel->d_func()->transports.contains(transport)) { @@ -866,7 +912,7 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel const MessageType type = toType(message.value(KEY_TYPE)); if (type == TypeIdle) { - setClientIsIdle(true); + setClientIsIdle(true, transport); } else if (type == TypeInit) { if (!message.contains(KEY_ID)) { qWarning("JSON message object is missing the id property: %s", @@ -913,9 +959,9 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel return; transport->sendMessage(createResponse(message.value(KEY_ID), wrapResult(result, transport))); } else if (type == TypeConnectToSignal) { - signalHandler.connectTo(object, message.value(KEY_SIGNAL).toInt(-1)); + signalHandlerFor(object)->connectTo(object, message.value(KEY_SIGNAL).toInt(-1)); } else if (type == TypeDisconnectFromSignal) { - signalHandler.disconnectFrom(object, message.value(KEY_SIGNAL).toInt(-1)); + signalHandlerFor(object)->disconnectFrom(object, message.value(KEY_SIGNAL).toInt(-1)); } else if (type == TypeSetProperty) { setProperty(object, message.value(KEY_PROPERTY).toInt(-1), message.value(KEY_VALUE)); @@ -931,6 +977,7 @@ void QMetaObjectPublisher::setBlockUpdates(bool block) blockUpdates = block; if (!blockUpdates) { + timer.start(PROPERTY_UPDATE_INTERVAL, this); sendPendingPropertyUpdates(); } else if (timer.isActive()) { timer.stop(); @@ -948,4 +995,15 @@ void QMetaObjectPublisher::timerEvent(QTimerEvent *event) } } +SignalHandler *QMetaObjectPublisher::signalHandlerFor(const QObject *object) +{ + auto thread = object->thread(); + auto it = signalHandlers.find(thread); + if (it == signalHandlers.end()) { + it = signalHandlers.emplace(thread, this).first; + it->second.moveToThread(thread); + } + return &it->second; +} + QT_END_NAMESPACE diff --git a/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h b/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h index bbd9875..4713ef1 100644 --- a/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h +++ b/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h @@ -59,6 +59,9 @@ #include #include #include +#include + +#include #include "qwebchannelglobal.h" @@ -109,17 +112,36 @@ public: */ void broadcastMessage(const QJsonObject &message) const; + /** + * Enqueue the given @p message to all known transports. + */ + void enqueueBroadcastMessage(const QJsonObject &message); + + /** + * Enqueue the given @p message to @p transport. + */ + void enqueueMessage(const QJsonObject &message, QWebChannelAbstractTransport *transport); + + /** + * If client for given @p transport is idle, send queued messaged to @p transport and then mark + * the client as not idle. + */ + void sendEnqueuedPropertyUpdates(QWebChannelAbstractTransport *transport); + /** * Serialize the QMetaObject of @p object and return it in JSON form. */ QJsonObject classInfoForObject(const QObject *object, QWebChannelAbstractTransport *transport); /** - * Set the client to idle or busy, based on the value of @p isIdle. - * - * When the value changed, start/stop the property update timer accordingly. + * Set the client to idle or busy for a single @p transport, based on the value of @p isIdle. + */ + void setClientIsIdle(bool isIdle, QWebChannelAbstractTransport *transport); + + /** + * Check that client is idle for @p transport. */ - void setClientIsIdle(bool isIdle); + bool isClientIdle(QWebChannelAbstractTransport *transport); /** * Initialize clients by sending them the class information of the registered objects. @@ -272,10 +294,18 @@ private: friend class TestWebChannel; QWebChannel *webChannel; - SignalHandler signalHandler; + std::unordered_map> signalHandlers; + SignalHandler *signalHandlerFor(const QObject *object); - // true when the client is idle, false otherwise - bool clientIsIdle; + struct TransportState + { + TransportState() : clientIsIdle(false) { } + // true when the client is idle, false otherwise + bool clientIsIdle; + // messages to send + QQueue queuedMessages; + }; + QHash transportState; // true when no property updates should be sent, false otherwise bool blockUpdates; diff --git a/qtwebchannel/src/webchannel/signalhandler_p.h b/qtwebchannel/src/webchannel/signalhandler_p.h index 27afadb..d77373c 100644 --- a/qtwebchannel/src/webchannel/signalhandler_p.h +++ b/qtwebchannel/src/webchannel/signalhandler_p.h @@ -56,6 +56,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -71,6 +72,7 @@ static const int s_destroyedSignalIndex = QObject::staticMetaObject.indexOfMetho template class SignalHandler : public QObject { + Q_DISABLE_COPY(SignalHandler) public: SignalHandler(Receiver *receiver, QObject *parent = 0); @@ -268,6 +270,7 @@ int SignalHandler::qt_metacall(QMetaObject::Call call, int methodId, v if (call == QMetaObject::InvokeMetaMethod) { const QObject *object = sender(); Q_ASSERT(object); + Q_ASSERT(QThread::currentThread() == object->thread()); Q_ASSERT(senderSignalIndex() == methodId); Q_ASSERT(m_connectionsCounter.contains(object)); Q_ASSERT(m_connectionsCounter.value(object).contains(methodId)); diff --git a/qtwebchannel/tests/auto/qml/testwebchannel.cpp b/qtwebchannel/tests/auto/qml/testwebchannel.cpp index 9891687..3ca81c2 100644 --- a/qtwebchannel/tests/auto/qml/testwebchannel.cpp +++ b/qtwebchannel/tests/auto/qml/testwebchannel.cpp @@ -46,7 +46,11 @@ TestWebChannel::~TestWebChannel() bool TestWebChannel::clientIsIdle() const { - return QWebChannel::d_func()->publisher->clientIsIdle; + for (auto *transport : QWebChannel::d_func()->transports) { + if (QWebChannel::d_func()->publisher->isClientIdle(transport)) + return true; + } + return false; } QT_END_NAMESPACE diff --git a/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp b/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp index 181da9e..37f989a 100644 --- a/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp +++ b/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp @@ -785,7 +785,7 @@ void TestWebChannel::testTransportWrapObjectProperties() DummyTransport *dummyTransport = new DummyTransport(this); channel.connectTo(dummyTransport); channel.d_func()->publisher->initializeClient(dummyTransport); - channel.d_func()->publisher->setClientIsIdle(true); + channel.d_func()->publisher->setClientIsIdle(true, dummyTransport); QCOMPARE(channel.d_func()->publisher->transportedWrappedObjects.size(), 0); @@ -943,8 +943,6 @@ void TestWebChannel::testInfiniteRecursion() void TestWebChannel::testAsyncObject() { - QSKIP("This test is broken. See QTBUG-80729"); - QWebChannel channel; channel.connectTo(m_dummyTransport); @@ -990,6 +988,50 @@ void TestWebChannel::testAsyncObject() thread.wait(); } +void TestWebChannel::testPropertyMultipleTransports() +{ + DummyTransport transport1; + DummyTransport transport2; + + QWebChannel channel; + QMetaObjectPublisher *publisher = channel.d_func()->publisher; + + TestObject testObj; + testObj.setObjectName("testObject"); + channel.registerObject(testObj.objectName(), &testObj); + channel.connectTo(&transport1); + channel.connectTo(&transport2); + + testObj.setProp("Hello"); + + publisher->initializeClient(&transport1); + publisher->initializeClient(&transport2); + publisher->setClientIsIdle(true, &transport1); + QCOMPARE(publisher->isClientIdle(&transport1), true); + QCOMPARE(publisher->isClientIdle(&transport2), false); + QVERIFY(transport1.messagesSent().isEmpty()); + QVERIFY(transport2.messagesSent().isEmpty()); + + testObj.setProp("World"); + QTRY_COMPARE_WITH_TIMEOUT(transport1.messagesSent().size(), 1u, 2000); + QCOMPARE(transport2.messagesSent().size(), 0u); + publisher->setClientIsIdle(true, &transport2); + QTRY_COMPARE_WITH_TIMEOUT(transport2.messagesSent().size(), 1u, 2000); + QCOMPARE(publisher->isClientIdle(&transport1), false); + QCOMPARE(publisher->isClientIdle(&transport2), false); + + testObj.setProp("!!!"); + publisher->setClientIsIdle(true, &transport2); + QCOMPARE(publisher->isClientIdle(&transport2), true); + QCOMPARE(publisher->isClientIdle(&transport1), false); + QTRY_COMPARE_WITH_TIMEOUT(transport2.messagesSent().size(), 2u, 2000); + QCOMPARE(transport1.messagesSent().size(), 1u); + publisher->setClientIsIdle(true, &transport1); + QTRY_COMPARE_WITH_TIMEOUT(transport1.messagesSent().size(), 2u, 2000); + QCOMPARE(publisher->isClientIdle(&transport1), false); + QCOMPARE(publisher->isClientIdle(&transport2), false); +} + class FunctionWrapper : public QObject { Q_OBJECT @@ -1082,7 +1124,7 @@ void TestWebChannel::benchInitializeClients() publisher->propertyUpdatesInitialized = false; publisher->signalToPropertyMap.clear(); - publisher->signalHandler.clear(); + publisher->signalHandlers.clear(); } } @@ -1107,7 +1149,7 @@ void TestWebChannel::benchPropertyUpdates() obj->change(); } - channel.d_func()->publisher->clientIsIdle = true; + channel.d_func()->publisher->setClientIsIdle(true, m_dummyTransport); channel.d_func()->publisher->sendPendingPropertyUpdates(); } } diff --git a/qtwebchannel/tests/auto/webchannel/tst_webchannel.h b/qtwebchannel/tests/auto/webchannel/tst_webchannel.h index eae21f4..dd4e690 100644 --- a/qtwebchannel/tests/auto/webchannel/tst_webchannel.h +++ b/qtwebchannel/tests/auto/webchannel/tst_webchannel.h @@ -348,6 +348,7 @@ private slots: void testJsonToVariant(); void testInfiniteRecursion(); void testAsyncObject(); + void testPropertyMultipleTransports(); void testDeletionDuringMethodInvocation_data(); void testDeletionDuringMethodInvocation(); Submodule qtwebengine f328054d...872003d9 (commits not present) Submodule qtwebsockets 33f36d90..89fbe461: diff --git a/qtwebsockets/src/websockets/qwebsocket_p.cpp b/qtwebsockets/src/websockets/qwebsocket_p.cpp index cf3087f..0dd0fa6 100644 --- a/qtwebsockets/src/websockets/qwebsocket_p.cpp +++ b/qtwebsockets/src/websockets/qwebsocket_p.cpp @@ -1100,6 +1100,8 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) m_handshakeState = AllDoneState; setErrorString(errorDescription); Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); + if (m_pSocket->state() != QAbstractSocket::UnconnectedState) + m_pSocket->disconnectFromHost(); } } diff --git a/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp b/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp index 2affdd5..95f1194 100644 --- a/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp +++ b/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp @@ -273,6 +273,7 @@ void QWebSocketDataProcessor::clear() if (!m_pConverterState) m_pConverterState = new QTextCodec::ConverterState(QTextCodec::ConvertInvalidToNull | QTextCodec::IgnoreHeader); + frame.clear(); } /*!