Submodule qtbase 8ad94db9..2529f7f0: 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/corelib/global/qendian.h b/qtbase/src/corelib/global/qendian.h index e9e51c4b93..85f72c494d 100644 --- a/qtbase/src/corelib/global/qendian.h +++ b/qtbase/src/corelib/global/qendian.h @@ -330,9 +330,9 @@ public: } static Q_DECL_CONSTEXPR QSpecialInteger max() - { return QSpecialInteger(std::numeric_limits::max()); } + { return QSpecialInteger((std::numeric_limits::max)()); } static Q_DECL_CONSTEXPR QSpecialInteger min() - { return QSpecialInteger(std::numeric_limits::min()); } + { return QSpecialInteger((std::numeric_limits::min)()); } }; template diff --git a/qtbase/src/corelib/global/qendian_p.h b/qtbase/src/corelib/global/qendian_p.h index 5421a452d9..1b310a65b5 100644 --- a/qtbase/src/corelib/global/qendian_p.h +++ b/qtbase/src/corelib/global/qendian_p.h @@ -171,16 +171,16 @@ public: operator Type() const noexcept { if (std::is_signed::value) { - UnsignedType i = S::fromSpecial(storage->val); + UnsignedType i = S::fromSpecial(m_storage->val); i <<= (sizeof(Type) * 8) - width - pos; Type t = Type(i); t >>= (sizeof(Type) * 8) - width; return t; } - return (S::fromSpecial(storage->val) & mask()) >> pos; + return (S::fromSpecial(m_storage->val) & mask()) >> pos; } - bool operator!() const noexcept { return !(storage->val & S::toSpecial(mask())); } + bool operator!() const noexcept { return !(m_storage->val & S::toSpecial(mask())); } static constexpr UnsignedType mask() noexcept { @@ -192,21 +192,21 @@ private: friend class QSpecialIntegerBitfieldUnion; friend class QSpecialIntegerAccessor; - explicit QSpecialIntegerConstAccessor(Storage *storage) : storage(storage) {} + explicit QSpecialIntegerConstAccessor(Storage *storage) : m_storage(storage) {} friend bool operator==(const QSpecialIntegerConstAccessor &i, const QSpecialIntegerConstAccessor &j) noexcept { - return ((i.storage->val ^ j.storage->val) & S::toSpecial(mask())) == 0; + return ((i.m_storage->val ^ j.m_storage->val) & S::toSpecial(mask())) == 0; } friend bool operator!=(const QSpecialIntegerConstAccessor &i, const QSpecialIntegerConstAccessor &j) noexcept { - return ((i.storage->val ^ j.storage->val) & S::toSpecial(mask())) != 0; + return ((i.m_storage->val ^ j.m_storage->val) & S::toSpecial(mask())) != 0; } - Storage *storage; + Storage *m_storage; }; template @@ -224,22 +224,22 @@ public: QSpecialIntegerAccessor &operator=(Type t) { - UnsignedType i = S::fromSpecial(storage->val); + UnsignedType i = S::fromSpecial(m_storage->val); i &= ~Const::mask(); i |= (UnsignedType(t) << pos) & Const::mask(); - storage->val = S::toSpecial(i); + m_storage->val = S::toSpecial(i); return *this; } - operator Const() { return Const(storage); } + operator Const() { return Const(m_storage); } private: template friend class QSpecialIntegerBitfieldUnion; - explicit QSpecialIntegerAccessor(Storage *storage) : storage(storage) {} + explicit QSpecialIntegerAccessor(Storage *storage) : m_storage(storage) {} - Storage *storage; + Storage *m_storage; }; template diff --git a/qtbase/src/corelib/global/qglobal.cpp b/qtbase/src/corelib/global/qglobal.cpp index 5ad82c259d..ecf7b1efaa 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 deab11f729..486b63fa3f 100644 --- a/qtbase/src/corelib/global/qnamespace.h +++ b/qtbase/src/corelib/global/qnamespace.h @@ -1867,7 +1867,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/global/qrandom.h b/qtbase/src/corelib/global/qrandom.h index 445b520c76..4c216eeb65 100644 --- a/qtbase/src/corelib/global/qrandom.h +++ b/qtbase/src/corelib/global/qrandom.h @@ -169,8 +169,8 @@ public: void seed(quint32 s = 1) { *this = { s }; } void seed(std::seed_seq &sseq) noexcept { *this = { sseq }; } Q_CORE_EXPORT void discard(unsigned long long z); - static Q_DECL_CONSTEXPR result_type min() { return std::numeric_limits::min(); } - static Q_DECL_CONSTEXPR result_type max() { return std::numeric_limits::max(); } + static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits::min)(); } + static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits::max)(); } static inline Q_DECL_CONST_FUNCTION QRandomGenerator *system(); static inline Q_DECL_CONST_FUNCTION QRandomGenerator *global(); @@ -245,8 +245,8 @@ public: QRandomGenerator::discard(z * 2); } - static Q_DECL_CONSTEXPR result_type min() { return std::numeric_limits::min(); } - static Q_DECL_CONSTEXPR result_type max() { return std::numeric_limits::max(); } + static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits::min)(); } + static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits::max)(); } static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 *system(); static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 *global(); static Q_CORE_EXPORT QRandomGenerator64 securelySeeded(); diff --git a/qtbase/src/corelib/io/qfilesystemengine_win.cpp b/qtbase/src/corelib/io/qfilesystemengine_win.cpp index 002f720926..e6f118a5c4 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/qsavefile_p.h b/qtbase/src/corelib/io/qsavefile_p.h index 3f81df9ae2..81a662133a 100644 --- a/qtbase/src/corelib/io/qsavefile_p.h +++ b/qtbase/src/corelib/io/qsavefile_p.h @@ -51,7 +51,7 @@ // We mean it. // -#include +#include #ifndef QT_NO_TEMPORARYFILE 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/qdeadlinetimer.h b/qtbase/src/corelib/kernel/qdeadlinetimer.h index 99e09eb31f..e0dcb8d3aa 100644 --- a/qtbase/src/corelib/kernel/qdeadlinetimer.h +++ b/qtbase/src/corelib/kernel/qdeadlinetimer.h @@ -66,7 +66,7 @@ public: Q_DECL_CONSTEXPR QDeadlineTimer(Qt::TimerType type_ = Qt::CoarseTimer) noexcept : t1(0), t2(0), type(type_) {} Q_DECL_CONSTEXPR QDeadlineTimer(ForeverConstant, Qt::TimerType type_ = Qt::CoarseTimer) noexcept - : t1(std::numeric_limits::max()), t2(0), type(type_) {} + : t1((std::numeric_limits::max)()), t2(0), type(type_) {} explicit QDeadlineTimer(qint64 msecs, Qt::TimerType type = Qt::CoarseTimer) noexcept; void swap(QDeadlineTimer &other) noexcept diff --git a/qtbase/src/corelib/kernel/qobject_p.h b/qtbase/src/corelib/kernel/qobject_p.h index 13ffb88999..325a974f30 100644 --- a/qtbase/src/corelib/kernel/qobject_p.h +++ b/qtbase/src/corelib/kernel/qobject_p.h @@ -220,7 +220,7 @@ public: s = s->previous; } } - Sender *previous; + Sender *previous = nullptr; QObject *receiver; QObject *sender; int signal; diff --git a/qtbase/src/corelib/kernel/qtranslator.cpp b/qtbase/src/corelib/kernel/qtranslator.cpp index bdcd016630..e4375e7e40 100644 --- a/qtbase/src/corelib/kernel/qtranslator.cpp +++ b/qtbase/src/corelib/kernel/qtranslator.cpp @@ -907,7 +907,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..34591fc667 100644 --- a/qtbase/src/corelib/mimetypes/qmimedatabase.cpp +++ b/qtbase/src/corelib/mimetypes/qmimedatabase.cpp @@ -46,6 +46,9 @@ #include "qmimeprovider_p.h" #include "qmimetype_p.h" +#include +#include + #include #include #include @@ -389,20 +392,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; + } } } @@ -428,6 +434,7 @@ QList QMimeDatabasePrivate::allMimeTypes() bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) { const QString resolvedParent = resolveAlias(parent); + QDuplicateTracker seen; std::stack toCheck; toCheck.push(mime); while (!toCheck.empty()) { @@ -436,8 +443,11 @@ bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) const QString mimeName = toCheck.top(); toCheck.pop(); const auto parentList = parents(mimeName); - for (const QString &par : parentList) - toCheck.push(resolveAlias(par)); + for (const QString &par : parentList) { + const QString resolvedPar = resolveAlias(par); + if (!seen.hasSeen(resolvedPar)) + toCheck.push(resolvedPar); + } } return false; } 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..5125704cf1 100644 --- a/qtbase/src/corelib/mimetypes/qmimeprovider.cpp +++ b/qtbase/src/corelib/mimetypes/qmimeprovider.cpp @@ -242,21 +242,28 @@ void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobM return; Q_ASSERT(m_cacheFile); const QString lowerFileName = fileName.toLower(); + int numMatches = 0; // 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); + numMatches = matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), 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()) - matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); + if (numMatches == 0) { + const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); + const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); + const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); + if (matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false)) { + ++numMatches; + } else if (matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true)) { + ++numMatches; + } + } + // Check complex globs (e.g. "callgrind.out[0-9]*" or "README*") + if (numMatches == 0) + matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); } -void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) +int QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) { + int numMatches = 0; const int numGlobs = cacheFile->getUint32(off); //qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset; for (int i = 0; i < numGlobs; ++i) { @@ -272,9 +279,12 @@ void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile //qDebug() << pattern << mimeType << weight << caseSensitive; QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive); - if (glob.matchFileName(fileName)) + if (glob.matchFileName(fileName)) { result.addMatch(QLatin1String(mimeType), weight, pattern); + ++numMatches; + } } + return numMatches; } bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result, QMimeBinaryProvider::CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck) diff --git a/qtbase/src/corelib/mimetypes/qmimeprovider_p.h b/qtbase/src/corelib/mimetypes/qmimeprovider_p.h index f9c8ef384c..5b328a7d5e 100644 --- a/qtbase/src/corelib/mimetypes/qmimeprovider_p.h +++ b/qtbase/src/corelib/mimetypes/qmimeprovider_p.h @@ -115,7 +115,7 @@ public: private: struct CacheFile; - void matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName); + int matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName); bool matchSuffixTree(QMimeGlobMatchResult &result, CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck); bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data); QLatin1String iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime); diff --git a/qtbase/src/corelib/mimetypes/qmimetype.cpp b/qtbase/src/corelib/mimetypes/qmimetype.cpp index 0c0de63961..d6a351262b 100644 --- a/qtbase/src/corelib/mimetypes/qmimetype.cpp +++ b/qtbase/src/corelib/mimetypes/qmimetype.cpp @@ -376,14 +376,17 @@ QStringList QMimeType::parentMimeTypes() const static void collectParentMimeTypes(const QString &mime, QStringList &allParents) { const QStringList parents = QMimeDatabasePrivate::instance()->mimeParents(mime); + QStringList newParents; for (const QString &parent : parents) { // I would use QSet, but since order matters I better not - if (!allParents.contains(parent)) + if (!allParents.contains(parent)) { allParents.append(parent); + newParents.append(parent); + } } // We want a breadth-first search, so that the least-specific parent (octet-stream) is last // This means iterating twice, unfortunately. - for (const QString &parent : parents) + for (const QString &parent : newParents) collectParentMimeTypes(parent, allParents); } diff --git a/qtbase/src/corelib/plugin/qlibrary.cpp b/qtbase/src/corelib/plugin/qlibrary.cpp index 5d2f024267..45b5a3fe27 100644 --- a/qtbase/src/corelib/plugin/qlibrary.cpp +++ b/qtbase/src/corelib/plugin/qlibrary.cpp @@ -526,7 +526,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) if (pHnd.loadRelaxed()) return; - loadHintsInt.storeRelaxed(lh); + loadHintsInt.fetchAndOrRelaxed(lh); } QFunctionPointer QLibraryPrivate::resolve(const char *symbol) @@ -538,6 +538,13 @@ QFunctionPointer QLibraryPrivate::resolve(const char *symbol) void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh) { + // Set the load hints directly for a dummy if this object is not associated + // with a file. Such object is not shared between multiple instances. + if (fileName.isEmpty()) { + loadHintsInt.storeRelaxed(lh); + return; + } + // this locks a global mutex QMutexLocker lock(&qt_library_mutex); mergeLoadHints(lh); @@ -1166,6 +1173,10 @@ QString QLibrary::errorString() const lazy symbol resolution, and will not export external symbols for resolution in other dynamically-loaded libraries. + \note Hints can only be cleared when this object is not associated with a + file. Hints can only be added once the file name is set (\a hints will + be or'ed with the old hints). + \note Setting this property after the library has been loaded has no effect and loadHints() will not reflect those changes. diff --git a/qtbase/src/corelib/plugin/qpluginloader.cpp b/qtbase/src/corelib/plugin/qpluginloader.cpp index 0a63b93762..02b8c588be 100644 --- a/qtbase/src/corelib/plugin/qpluginloader.cpp +++ b/qtbase/src/corelib/plugin/qpluginloader.cpp @@ -105,6 +105,8 @@ QT_BEGIN_NAMESPACE \sa QLibrary, {Plug & Paint Example} */ +static constexpr QLibrary::LoadHints defaultLoadHints = QLibrary::PreventUnloadHint; + /*! \class QStaticPlugin \inmodule QtCore @@ -155,7 +157,7 @@ QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent) : QObject(parent), d(nullptr), did_load(false) { setFileName(fileName); - setLoadHints(QLibrary::PreventUnloadHint); + setLoadHints(defaultLoadHints); } /*! @@ -357,7 +359,7 @@ static QString locatePlugin(const QString& fileName) void QPluginLoader::setFileName(const QString &fileName) { #if defined(QT_SHARED) - QLibrary::LoadHints lh = QLibrary::PreventUnloadHint; + QLibrary::LoadHints lh = defaultLoadHints; if (d) { lh = d->loadHints(); d->release(); @@ -414,15 +416,21 @@ QString QPluginLoader::errorString() const void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints) { if (!d) { - d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr + d = QLibraryPrivate::findOrCreate({}, {}, loadHints); // ugly, but we need a d-ptr d->errorString.clear(); + } else { + d->setLoadHints(loadHints); } - d->setLoadHints(loadHints); } QLibrary::LoadHints QPluginLoader::loadHints() const { - return d ? d->loadHints() : QLibrary::LoadHints(); + // Not having a d-pointer means that the user hasn't called + // setLoadHints() / setFileName() yet. In setFileName() we will + // then force defaultLoadHints on loading, so we must return them + // from here as well. + + return d ? d->loadHints() : defaultLoadHints; } #endif // QT_CONFIG(library) 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/serialization/qtextstream_p.h b/qtbase/src/corelib/serialization/qtextstream_p.h index 172824d27d..994e2da1a0 100644 --- a/qtbase/src/corelib/serialization/qtextstream_p.h +++ b/qtbase/src/corelib/serialization/qtextstream_p.h @@ -73,14 +73,14 @@ public: disconnect(); if (device) connect(device, SIGNAL(aboutToClose()), this, SLOT(flushStream())); - this->stream = stream; + m_stream = stream; } public Q_SLOTS: - inline void flushStream() { stream->flush(); } + void flushStream() { m_stream->flush(); } private: - QTextStream *stream; + QTextStream *m_stream; }; #endif diff --git a/qtbase/src/corelib/serialization/qxmlstream.g b/qtbase/src/corelib/serialization/qxmlstream.g index 8c6a1a5887..18fbf14c03 100644 --- a/qtbase/src/corelib/serialization/qxmlstream.g +++ b/qtbase/src/corelib/serialization/qxmlstream.g @@ -1512,8 +1512,8 @@ attribute_value_content ::= literal_content | char_ref | entity_ref_in_attribute attribute ::= qname space_opt EQ space_opt attribute_value; /. case $rule_number: { - QStringRef prefix = symPrefix(1); - if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + const QStringRef prfx = symPrefix(1); + if (prfx.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); namespaceDeclaration.prefix.clear(); @@ -1563,7 +1563,7 @@ attribute ::= qname space_opt EQ space_opt attribute_value; attribute.value.pos = pos; attribute.value.len = n; } - if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + if (prfx == QLatin1String("xmlns") && namespaceProcessing) { NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); QStringRef namespacePrefix = symString(attribute.key); QStringRef namespaceUri = symString(attribute.value); diff --git a/qtbase/src/corelib/serialization/qxmlstream_p.h b/qtbase/src/corelib/serialization/qxmlstream_p.h index be7b1fe665..fd8fcca4e1 100644 --- a/qtbase/src/corelib/serialization/qxmlstream_p.h +++ b/qtbase/src/corelib/serialization/qxmlstream_p.h @@ -1722,8 +1722,8 @@ bool QXmlStreamReaderPrivate::parse() break; case 229: { - QStringRef prefix = symPrefix(1); - if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + const QStringRef prfx = symPrefix(1); + if (prfx.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); namespaceDeclaration.prefix.clear(); @@ -1773,7 +1773,7 @@ bool QXmlStreamReaderPrivate::parse() attribute.value.pos = pos; attribute.value.len = n; } - if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + if (prfx == QLatin1String("xmlns") && namespaceProcessing) { NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); QStringRef namespacePrefix = symString(attribute.key); QStringRef namespaceUri = symString(attribute.value); diff --git a/qtbase/src/corelib/text/qbytearray.h b/qtbase/src/corelib/text/qbytearray.h index f4c335ca93..e1bcdc3e68 100644 --- a/qtbase/src/corelib/text/qbytearray.h +++ b/qtbase/src/corelib/text/qbytearray.h @@ -102,8 +102,10 @@ Q_CORE_EXPORT int qstrnicmp(const char *, const char *, uint len); Q_CORE_EXPORT int qstrnicmp(const char *, qsizetype, const char *, qsizetype = -1); // implemented in qvsnprintf.cpp -Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap); -Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt, ...); +Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap) + Q_ATTRIBUTE_FORMAT_PRINTF(3, 0); +Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt, ...) + Q_ATTRIBUTE_FORMAT_PRINTF(3, 4); // qChecksum: Internet checksum Q_CORE_EXPORT quint16 qChecksum(const char *s, uint len); // ### Qt 6: Remove diff --git a/qtbase/src/corelib/text/qlocale_data_p.h b/qtbase/src/corelib/text/qlocale_data_p.h index c5e6a0d461..c613e4e537 100644 --- a/qtbase/src/corelib/text/qlocale_data_p.h +++ b/qtbase/src/corelib/text/qlocale_data_p.h @@ -1340,7 +1340,7 @@ static const QLocaleData locale_data[] = { { 25, 6, 126, 46, 44, 59, 37, 48, 45, 43, 101, 12300, 12301, 12302, 12303, 166,5 , 166,5 , 176,5 , 176,5 , 415,8 , 402,13 , 209,6 , 226,13 , 2022,21 , 1980,28 , 2008,14 , 2022,21 , 1980,28 , 2008,14 , 58,2 , 55,2 , 283,3 , 5,17 , 22,23 , {77,79,80}, 137,4 , 3174,13 , 4,4 , 13,6 , 589,4 , 602,9 , 2, 1, 7, 6, 7 }, // Chinese/Traditional Han/Macau { 25, 6, 208, 46, 44, 59, 37, 48, 45, 43, 101, 12300, 12301, 12302, 12303, 166,5 , 166,5 , 171,5 , 171,5 , 394,8 , 423,14 , 209,6 , 226,13 , 2022,21 , 1980,28 , 2008,14 , 2022,21 , 1980,28 , 2008,14 , 58,2 , 55,2 , 45,4 , 5,17 , 22,23 , {84,87,68}, 6,1 , 3187,13 , 4,4 , 13,6 , 589,4 , 611,2 , 2, 0, 7, 6, 7 }, // Chinese/Traditional Han/Taiwan { 26, 7, 74, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {69,85,82}, 14,1 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 1, 6, 7 }, // Corsican/Latin/France - { 27, 7, 54, 44, 46, 59, 37, 48, 8722, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 159,7 , 159,7 , 437,13 , 450,19 , 37,5 , 87,12 , 2043,28 , 2071,58 , 2129,14 , 2043,28 , 2071,58 , 2143,14 , 0,2 , 0,2 , 286,7 , 5,17 , 22,23 , {72,82,75}, 141,3 , 3200,60 , 19,5 , 4,0 , 613,8 , 621,8 , 2, 1, 1, 6, 7 }, // Croatian/Latin/Croatia + { 27, 7, 54, 44, 46, 59, 37, 48, 8722, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 159,7 , 159,7 , 437,13 , 450,19 , 37,5 , 87,12 , 2043,28 , 2071,58 , 2129,14 , 2043,28 , 2071,58 , 2143,14 , 0,2 , 0,2 , 286,7 , 5,17 , 22,23 , {69,85,82}, 14,1 , 3455,19 , 19,5 , 4,0 , 613,8 , 621,8 , 2, 1, 1, 6, 7 }, // Croatian/Latin/Croatia { 27, 7, 27, 44, 46, 59, 37, 48, 8722, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 159,7 , 159,7 , 469,9 , 450,19 , 37,5 , 87,12 , 2043,28 , 2071,58 , 2143,14 , 2043,28 , 2071,58 , 2143,14 , 0,2 , 0,2 , 286,7 , 5,17 , 22,23 , {66,65,77}, 144,2 , 3260,85 , 19,5 , 4,0 , 613,8 , 629,19 , 2, 1, 1, 6, 7 }, // Croatian/Latin/Bosnia And Herzegowina { 28, 7, 57, 44, 160, 59, 37, 48, 45, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 181,7 , 181,7 , 156,8 , 478,17 , 55,4 , 59,9 , 2157,21 , 2178,49 , 2227,14 , 2157,21 , 2178,49 , 2227,14 , 60,4 , 57,4 , 293,5 , 5,17 , 22,23 , {67,90,75}, 146,2 , 3345,68 , 19,5 , 4,0 , 648,7 , 655,5 , 2, 0, 1, 6, 7 }, // Czech/Latin/Czech Republic { 29, 7, 58, 44, 46, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 188,8 , 188,8 , 495,10 , 505,23 , 239,5 , 244,10 , 2241,28 , 2269,51 , 2320,14 , 2334,35 , 2269,51 , 2320,14 , 0,2 , 0,2 , 0,5 , 5,17 , 22,23 , {68,75,75}, 148,3 , 3413,42 , 19,5 , 4,0 , 660,5 , 665,7 , 2, 0, 1, 6, 7 }, // Danish/Latin/Denmark diff --git a/qtbase/src/corelib/text/qlocale_p.h b/qtbase/src/corelib/text/qlocale_p.h index 322eca4362..bcf400527a 100644 --- a/qtbase/src/corelib/text/qlocale_p.h +++ b/qtbase/src/corelib/text/qlocale_p.h @@ -256,7 +256,7 @@ public: { if (qIsInf(d)) return float(d); - if (std::fabs(d) > std::numeric_limits::max()) { + if (std::fabs(d) > double{(std::numeric_limits::max)()}) { if (ok) *ok = false; const float huge = std::numeric_limits::infinity(); diff --git a/qtbase/src/corelib/text/qstringiterator_p.h b/qtbase/src/corelib/text/qstringiterator_p.h index b31c7673c2..731a241407 100644 --- a/qtbase/src/corelib/text/qstringiterator_p.h +++ b/qtbase/src/corelib/text/qstringiterator_p.h @@ -123,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(pos < e && 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(pos + 1 < e && pos[1].isLowSurrogate()); return QChar::surrogateToUcs4(pos[0], pos[1]); + } return pos->unicode(); } @@ -158,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(pos < e && pos->isLowSurrogate()); return QChar::surrogateToUcs4(cur, *pos++); + } return cur.unicode(); } @@ -199,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(pos > i && 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(pos > i + 1 && pos[-2].isHighSurrogate()); return QChar::surrogateToUcs4(pos[-2], pos[-1]); + } return pos[-1].unicode(); } @@ -233,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(pos > i && pos[-1].isHighSurrogate()); return QChar::surrogateToUcs4(*--pos, cur); + } return cur.unicode(); } 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/qfutureinterface.h b/qtbase/src/corelib/thread/qfutureinterface.h index e4cc46e929..5a39bb8a90 100644 --- a/qtbase/src/corelib/thread/qfutureinterface.h +++ b/qtbase/src/corelib/thread/qfutureinterface.h @@ -281,7 +281,7 @@ template <> class QFutureInterface : public QFutureInterfaceBase { public: - explicit QFutureInterface(State initialState = NoState) + explicit QFutureInterface(State initialState = NoState) : QFutureInterfaceBase(initialState) { } diff --git a/qtbase/src/corelib/thread/qfuturewatcher_p.h b/qtbase/src/corelib/thread/qfuturewatcher_p.h index ead247b040..2911e0fafe 100644 --- a/qtbase/src/corelib/thread/qfuturewatcher_p.h +++ b/qtbase/src/corelib/thread/qfuturewatcher_p.h @@ -51,6 +51,8 @@ // We mean it. // +#include + #include "qfutureinterface_p.h" #include @@ -60,7 +62,6 @@ QT_REQUIRE_CONFIG(future); QT_BEGIN_NAMESPACE -class QFutureWatcherBase; class QFutureWatcherBasePrivate : public QObjectPrivate, public QFutureCallOutInterface { 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/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/qtimezoneprivate_p.h b/qtbase/src/corelib/time/qtimezoneprivate_p.h index cf2a690f50..fb9fb1528b 100644 --- a/qtbase/src/corelib/time/qtimezoneprivate_p.h +++ b/qtbase/src/corelib/time/qtimezoneprivate_p.h @@ -137,10 +137,10 @@ public: virtual void serialize(QDataStream &ds) const; // Static Utility Methods - static inline qint64 maxMSecs() { return std::numeric_limits::max(); } - static inline qint64 minMSecs() { return std::numeric_limits::min() + 1; } - static inline qint64 invalidMSecs() { return std::numeric_limits::min(); } - static inline qint64 invalidSeconds() { return std::numeric_limits::min(); } + static inline qint64 maxMSecs() { return (std::numeric_limits::max)(); } + static inline qint64 minMSecs() { return (std::numeric_limits::min)() + 1; } + static inline qint64 invalidMSecs() { return (std::numeric_limits::min)(); } + static inline qint64 invalidSeconds() { return (std::numeric_limits::min)(); } static Data invalidData(); static QTimeZone::OffsetData invalidOffsetData(); static QTimeZone::OffsetData toOffsetData(const Data &data); diff --git a/qtbase/src/corelib/tools/qbitarray.h b/qtbase/src/corelib/tools/qbitarray.h index eaed17413d..7934b55164 100644 --- a/qtbase/src/corelib/tools/qbitarray.h +++ b/qtbase/src/corelib/tools/qbitarray.h @@ -84,9 +84,9 @@ public: bool toggleBit(int i); bool at(int i) const; - QBitRef operator[](int i); + inline QBitRef operator[](int i); bool operator[](int i) const; - QBitRef operator[](uint i); + inline QBitRef operator[](uint i); bool operator[](uint i) const; QBitArray& operator&=(const QBitArray &); @@ -156,9 +156,9 @@ public: QBitRef& operator=(bool val) { a.setBit(i, val); return *this; } }; -inline QBitRef QBitArray::operator[](int i) +QBitRef QBitArray::operator[](int i) { Q_ASSERT(i >= 0); return QBitRef(*this, i); } -inline QBitRef QBitArray::operator[](uint i) +QBitRef QBitArray::operator[](uint i) { return QBitRef(*this, i); } diff --git a/qtbase/src/corelib/tools/qduplicatetracker_p.h b/qtbase/src/corelib/tools/qduplicatetracker_p.h index 68284fb916..140fc31a34 100644 --- a/qtbase/src/corelib/tools/qduplicatetracker_p.h +++ b/qtbase/src/corelib/tools/qduplicatetracker_p.h @@ -52,7 +52,7 @@ #include -#if QT_HAS_INCLUDE() && __cplusplus > 201402L +#if defined(__cpp_lib_memory_resource) && __cplusplus > 201402L # include # include #else @@ -78,7 +78,7 @@ class QDuplicateTracker { QSet set = makeQSet(); int setSize = 0; #endif - Q_DISABLE_COPY_MOVE(QDuplicateTracker); + Q_DISABLE_COPY_MOVE(QDuplicateTracker) public: QDuplicateTracker() = default; void reserve(int n) { set.reserve(n); } diff --git a/qtbase/src/dbus/qdbusintegrator.cpp b/qtbase/src/dbus/qdbusintegrator.cpp index a4cbfecc98..9ccbbbb37d 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 diff --git a/qtbase/src/gui/configure.json b/qtbase/src/gui/configure.json index ed6e419f49..1f0e0534a5 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/itemmodels/qstandarditemmodel.cpp b/qtbase/src/gui/itemmodels/qstandarditemmodel.cpp index fc9424763e..a3e0853fa8 100644 --- a/qtbase/src/gui/itemmodels/qstandarditemmodel.cpp +++ b/qtbase/src/gui/itemmodels/qstandarditemmodel.cpp @@ -3113,13 +3113,13 @@ QStringList QStandardItemModel::mimeTypes() const */ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const { - QMimeData *data = QAbstractItemModel::mimeData(indexes); - if(!data) + std::unique_ptr data(QAbstractItemModel::mimeData(indexes)); + if (!data) return nullptr; const QString format = qStandardItemModelDataListMimeType(); if (!mimeTypes().contains(format)) - return data; + return data.release(); QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); @@ -3172,7 +3172,7 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const } data->setData(format, encoded); - return data; + return data.release(); } 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 56cd2d02bc..d448429f6c 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 a11388fdb6..aed480e875 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/kernel/qwindow.cpp b/qtbase/src/gui/kernel/qwindow.cpp index fa3a0243a6..c2f7dc6776 100644 --- a/qtbase/src/gui/kernel/qwindow.cpp +++ b/qtbase/src/gui/kernel/qwindow.cpp @@ -644,7 +644,7 @@ bool QWindow::isVisible() const into an actual native surface. However, the window remains hidden until setVisible() is called. Note that it is not usually necessary to call this function directly, as it will be implicitly - called by show(), setVisible(), and other functions that require access to the platform + called by show(), setVisible(), winId(), and other functions that require access to the platform resources. Call destroy() to free the platform resources if necessary. 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/qpaintengine_raster.cpp b/qtbase/src/gui/painting/qpaintengine_raster.cpp index 38bad9a6b0..a04160791b 100644 --- a/qtbase/src/gui/painting/qpaintengine_raster.cpp +++ b/qtbase/src/gui/painting/qpaintengine_raster.cpp @@ -3462,16 +3462,18 @@ void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSp // Boundaries int w = image.width(); int h = image.height(); - int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height()); - int ymin = qMax(qRound(pos.y()), 0); - int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width()); - int xmin = qMax(qRound(pos.x()), 0); + int px = qRound(pos.x()); + int py = qRound(pos.y()); + int ymax = qMin(py + h, d->rasterBuffer->height()); + int ymin = qMax(py, 0); + int xmax = qMin(px + w, d->rasterBuffer->width()); + int xmin = qMax(px, 0); - int x_offset = xmin - qRound(pos.x()); + int x_offset = xmin - px; QImage::Format format = image.format(); for (int y = ymin; y < ymax; ++y) { - const uchar *src = image.scanLine(y - qRound(pos.y())); + const uchar *src = image.scanLine(y - py); if (format == QImage::Format_MonoLSB) { for (int x = 0; x < xmax - xmin; ++x) { int src_x = x + x_offset; @@ -3860,7 +3862,7 @@ void QClipData::initialize() return; if (!m_clipLines) - m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight); + m_clipLines = (ClipLine *)calloc(clipSpanHeight, sizeof(ClipLine)); Q_CHECK_PTR(m_clipLines); QT_TRY { 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/qpathsimplifier.cpp b/qtbase/src/gui/painting/qpathsimplifier.cpp index 256a2fefe7..60018c1857 100644 --- a/qtbase/src/gui/painting/qpathsimplifier.cpp +++ b/qtbase/src/gui/painting/qpathsimplifier.cpp @@ -164,11 +164,15 @@ QPoint IntersectionPoint::round() const // Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the // line and zero if exactly on the line. -// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1', -// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order). -inline int pointDistanceFromLine(const QPoint &p, const QPoint &v1, const QPoint &v2) +// The returned value is the sign of the cross product between 'v2-v1' and 'p-v1'. +inline int pointSideOfLine(const QPoint &p, const QPoint &v1, const QPoint &v2) { - return cross(v2 - v1, p - v1); + qint64 ux = qint64(v2.x()) - v1.x(); + qint64 uy = qint64(v2.y()) - v1.y(); + qint64 vx = qint64(p.x()) - v1.x(); + qint64 vy = qint64(p.y()) - v1.y(); + qint64 c = (ux * vy) - (uy * vx); + return (c > 0) ? 1 : (c < 0) ? -1 : 0; } IntersectionPoint intersectionPoint(const QPoint &u1, const QPoint &u2, @@ -345,7 +349,7 @@ private: void initElements(const QVectorPath &path, const QTransform &matrix); void removeIntersections(); - void connectElements(); + bool connectElements(); void fillIndices(); BVHNode *buildTree(Element **elements, int elementCount); bool intersectNodes(QDataBuffer &elements, BVHNode *elementNode, BVHNode *treeNode); @@ -490,11 +494,17 @@ PathSimplifier::PathSimplifier(const QVectorPath &path, QDataBuffer &ver { m_points->reset(); m_indices->reset(); + bool ok = true; initElements(path, matrix); if (!m_elements.isEmpty()) { removeIntersections(); - connectElements(); - fillIndices(); + ok = connectElements(); + if (ok) + fillIndices(); + } + if (!ok) { + m_points->reset(); + m_indices->reset(); } } @@ -679,7 +689,7 @@ void PathSimplifier::removeIntersections() m_bvh.free(); // The bounding volume hierarchy is not needed anymore. } -void PathSimplifier::connectElements() +bool PathSimplifier::connectElements() { Q_ASSERT(!m_elements.isEmpty()); QDataBuffer events(m_elements.size() * 2); @@ -859,7 +869,8 @@ void PathSimplifier::connectElements() } if (!orderedElements.isEmpty()) { - Q_ASSERT((orderedElements.size() & 1) == 0); + if (orderedElements.size() & 1) // Unexpected path structure + return false; int i = 0; Element *firstElement = orderedElements.at(0); if (m_points->at(firstElement->indices[0]) != eventPoint) { @@ -885,6 +896,7 @@ void PathSimplifier::connectElements() Q_ASSERT((element->next == 0) == (element->previous == 0)); } #endif + return true; } void PathSimplifier::fillIndices() @@ -1420,19 +1432,19 @@ bool PathSimplifier::elementIsLeftOf(const Element *left, const Element *right) return true; if (leftU.x() > qMax(rightL.x(), rightU.x())) return false; - int d = pointDistanceFromLine(leftU, rightL, rightU); + int d = pointSideOfLine(leftU, rightL, rightU); // d < 0: left, d > 0: right, d == 0: on top if (d == 0) { - d = pointDistanceFromLine(leftL, rightL, rightU); + d = pointSideOfLine(leftL, rightL, rightU); if (d == 0) { if (right->degree > Element::Line) { - d = pointDistanceFromLine(leftL, rightL, m_points->at(right->indices[1])); + d = pointSideOfLine(leftL, rightL, m_points->at(right->indices[1])); if (d == 0) - d = pointDistanceFromLine(leftL, rightL, m_points->at(right->indices[2])); + d = pointSideOfLine(leftL, rightL, m_points->at(right->indices[2])); } else if (left->degree > Element::Line) { - d = pointDistanceFromLine(m_points->at(left->indices[1]), rightL, rightU); + d = pointSideOfLine(m_points->at(left->indices[1]), rightL, rightU); if (d == 0) - d = pointDistanceFromLine(m_points->at(left->indices[2]), rightL, rightU); + d = pointSideOfLine(m_points->at(left->indices[2]), rightL, rightU); } } } @@ -1452,13 +1464,13 @@ QPair PathSimplifier::outerB Q_ASSERT(point >= v2 && point <= v1); if (point == v1 || point == v2) break; - int d = pointDistanceFromLine(point, v1, v2); + int d = pointSideOfLine(point, v1, v2); if (d == 0) { if (element->degree == Element::Line) break; - d = pointDistanceFromLine(point, v1, m_points->at(element->indices[1])); + d = pointSideOfLine(point, v1, m_points->at(element->indices[1])); if (d == 0) - d = pointDistanceFromLine(point, v1, m_points->at(element->indices[2])); + d = pointSideOfLine(point, v1, m_points->at(element->indices[2])); Q_ASSERT(d != 0); } if (d < 0) { @@ -1484,7 +1496,7 @@ QPair PathSimplifier::outerB Q_ASSERT(point >= v2 && point <= v1); bool equal = (point == v1 || point == v2); if (!equal) { - int d = pointDistanceFromLine(point, v1, v2); + int d = pointSideOfLine(point, v1, v2); Q_ASSERT(d >= 0); equal = (d == 0 && element->degree == Element::Line); } @@ -1505,7 +1517,7 @@ QPair PathSimplifier::outerB Q_ASSERT(point >= v2 && point <= v1); bool equal = (point == v1 || point == v2); if (!equal) { - int d = pointDistanceFromLine(point, v1, v2); + int d = pointSideOfLine(point, v1, v2); Q_ASSERT(d <= 0); equal = (d == 0 && element->degree == Element::Line); } 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/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/qdistancefield.cpp b/qtbase/src/gui/text/qdistancefield.cpp index c843e3b706..71d9763210 100644 --- a/qtbase/src/gui/text/qdistancefield.cpp +++ b/qtbase/src/gui/text/qdistancefield.cpp @@ -508,6 +508,11 @@ static void makeDistanceField(QDistanceFieldData *data, const QPainterPath &path QDataBuffer pathIndices(0); QDataBuffer pathVertices(0); qSimplifyPath(path, pathVertices, pathIndices, transform); + if (pathVertices.isEmpty()) { + qCWarning(lcDistanceField) << "Unexpected glyph path structure, bailing out"; + memset(data->data, 0, data->nbytes); + return; + } const qint32 interiorColor = -0x7f80; // 8:8 signed format, -127.5 const qint32 exteriorColor = 0x7f80; // 8:8 signed format, 127.5 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 70f6ab285b..a0c847459c 100644 --- a/qtbase/src/gui/text/qtextlayout.cpp +++ b/qtbase/src/gui/text/qtextlayout.cpp @@ -1336,13 +1336,16 @@ 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) - base = si.ascent; - if (si.descent >= 0) - descent = si.descent; + // objects need some special treatment as they can have special alignment or be floating + if (si.analysis.flags != QScriptAnalysis::Object) { + if (si.ascent > 0) + base = si.ascent; + 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) diff --git a/qtbase/src/gui/util/qktxhandler.cpp b/qtbase/src/gui/util/qktxhandler.cpp index 7eda4c46fb..2853e46c3d 100644 --- a/qtbase/src/gui/util/qktxhandler.cpp +++ b/qtbase/src/gui/util/qktxhandler.cpp @@ -73,7 +73,7 @@ struct KTXHeader { quint32 bytesOfKeyValueData; }; -static const quint32 headerSize = sizeof(KTXHeader); +static constexpr quint32 qktxh_headerSize = sizeof(KTXHeader); // Currently unused, declared for future reference struct KTXKeyValuePairItem { @@ -103,11 +103,36 @@ struct KTXMipmapLevel { */ }; -bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) +static bool qAddOverflow(quint32 v1, quint32 v2, quint32 *r) { + // unsigned additions are well-defined + *r = v1 + v2; + return v1 > quint32(v1 + v2); +} + +// Returns the nearest multiple of 4 greater than or equal to 'value' +static bool nearestMultipleOf4(quint32 value, quint32 *result) +{ + constexpr quint32 rounding = 4; + *result = 0; + if (qAddOverflow(value, rounding - 1, result)) + return true; + *result &= ~(rounding - 1); + return false; +} + +// Returns a slice with prechecked bounds +static QByteArray safeSlice(const QByteArray& array, quint32 start, quint32 length) { - Q_UNUSED(suffix) + quint32 end = 0; + if (qAddOverflow(start, length, &end) || end > quint32(array.length())) + return {}; + return QByteArray(array.data() + start, length); +} - return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0); +bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) +{ + Q_UNUSED(suffix); + return block.startsWith(QByteArray::fromRawData(ktxIdentifier, KTX_IDENTIFIER_LENGTH)); } QTextureFileData QKtxHandler::read() @@ -115,42 +140,97 @@ QTextureFileData QKtxHandler::read() if (!device()) return QTextureFileData(); - QByteArray buf = device()->readAll(); - const quint32 dataSize = quint32(buf.size()); - if (dataSize < headerSize || !canRead(QByteArray(), buf)) { - qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); + const QByteArray buf = device()->readAll(); + if (size_t(buf.size()) > std::numeric_limits::max()) { + qWarning(lcQtGuiTextureIO, "Too big KTX file %s", logName().constData()); + return QTextureFileData(); + } + + if (!canRead(QByteArray(), buf)) { + qWarning(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); + return QTextureFileData(); + } + + if (buf.size() < qsizetype(qktxh_headerSize)) { + qWarning(lcQtGuiTextureIO, "Invalid KTX header size in %s", logName().constData()); return QTextureFileData(); } - const KTXHeader *header = reinterpret_cast(buf.constData()); - if (!checkHeader(*header)) { - qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData()); + KTXHeader header; + memcpy(&header, buf.data(), qktxh_headerSize); + if (!checkHeader(header)) { + qWarning(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData()); return QTextureFileData(); } QTextureFileData texData; texData.setData(buf); - texData.setSize(QSize(decode(header->pixelWidth), decode(header->pixelHeight))); - texData.setGLFormat(decode(header->glFormat)); - texData.setGLInternalFormat(decode(header->glInternalFormat)); - texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat)); - - texData.setNumLevels(decode(header->numberOfMipmapLevels)); - quint32 offset = headerSize + decode(header->bytesOfKeyValueData); - const int maxLevels = qMin(texData.numLevels(), 32); // Cap iterations in case of corrupt file. - for (int i = 0; i < maxLevels; i++) { - if (offset + sizeof(KTXMipmapLevel) > dataSize) // Corrupt file; avoid oob read - break; - const KTXMipmapLevel *level = reinterpret_cast(buf.constData() + offset); - quint32 levelLen = decode(level->imageSize); - texData.setDataOffset(offset + sizeof(KTXMipmapLevel::imageSize), i); - texData.setDataLength(levelLen, i); - offset += sizeof(KTXMipmapLevel::imageSize) + levelLen + (3 - ((levelLen + 3) % 4)); + texData.setSize(QSize(decode(header.pixelWidth), decode(header.pixelHeight))); + texData.setGLFormat(decode(header.glFormat)); + texData.setGLInternalFormat(decode(header.glInternalFormat)); + texData.setGLBaseInternalFormat(decode(header.glBaseInternalFormat)); + + texData.setNumLevels(decode(header.numberOfMipmapLevels)); + + const quint32 bytesOfKeyValueData = decode(header.bytesOfKeyValueData); + quint32 headerKeyValueSize; + if (qAddOverflow(qktxh_headerSize, bytesOfKeyValueData, &headerKeyValueSize)) { + qWarning(lcQtGuiTextureIO, "Overflow in size of key value data in header of KTX file %s", + logName().constData()); + return QTextureFileData(); + } + + if (headerKeyValueSize >= quint32(buf.size())) { + qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); + return QTextureFileData(); + } + + // Technically, any number of levels is allowed but if the value is bigger than + // what is possible in KTX V2 (and what makes sense) we return an error. + // maxLevels = log2(max(width, height, depth)) + const int maxLevels = (sizeof(quint32) * 8) + - qCountLeadingZeroBits(std::max( + { header.pixelWidth, header.pixelHeight, header.pixelDepth })); + + if (texData.numLevels() > maxLevels) { + qWarning(lcQtGuiTextureIO, "Too many levels in KTX file %s", logName().constData()); + return QTextureFileData(); + } + + quint32 offset = headerKeyValueSize; + for (int level = 0; level < texData.numLevels(); level++) { + const auto imageSizeSlice = safeSlice(buf, offset, sizeof(quint32)); + if (imageSizeSlice.isEmpty()) { + qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); + return QTextureFileData(); + } + + const quint32 imageSize = decode(qFromUnaligned(imageSizeSlice.data())); + offset += sizeof(quint32); // overflow checked indirectly above + + texData.setDataOffset(offset, level); + texData.setDataLength(imageSize, level); + + // Add image data and padding to offset + quint32 padded = 0; + if (nearestMultipleOf4(imageSize, &padded)) { + qWarning(lcQtGuiTextureIO, "Overflow in KTX file %s", logName().constData()); + return QTextureFileData(); + } + + quint32 offsetNext; + if (qAddOverflow(offset, padded, &offsetNext)) { + qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); + return QTextureFileData(); + } + + offset = offsetNext; } if (!texData.isValid()) { - qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", logName().constData()); + qWarning(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", + logName().constData()); return QTextureFileData(); } @@ -191,7 +271,7 @@ bool QKtxHandler::checkHeader(const KTXHeader &header) (decode(header.numberOfFaces) == 1)); } -quint32 QKtxHandler::decode(quint32 val) +quint32 QKtxHandler::decode(quint32 val) const { return inverseEndian ? qbswap(val) : val; } diff --git a/qtbase/src/gui/util/qktxhandler_p.h b/qtbase/src/gui/util/qktxhandler_p.h index 19f7b0e79a..8da990aaac 100644 --- a/qtbase/src/gui/util/qktxhandler_p.h +++ b/qtbase/src/gui/util/qktxhandler_p.h @@ -68,7 +68,7 @@ public: private: bool checkHeader(const KTXHeader &header); - quint32 decode(quint32 val); + quint32 decode(quint32 val) const; bool inverseEndian = false; }; diff --git a/qtbase/src/gui/util/qshadergenerator.cpp b/qtbase/src/gui/util/qshadergenerator.cpp index 1ec25ccd7b..20ed6abc3a 100644 --- a/qtbase/src/gui/util/qshadergenerator.cpp +++ b/qtbase/src/gui/util/qshadergenerator.cpp @@ -492,7 +492,7 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) int end = begin + 1; char endChar = line.at(end); const int size = line.size(); - while (end < size && (std::isalnum(endChar) || endChar == '_')) { + while (end < size && (std::isalnum(uchar(endChar)) || endChar == '_')) { ++end; endChar = line.at(end); } diff --git a/qtbase/src/gui/util/qshaderlanguage.cpp b/qtbase/src/gui/util/qshaderlanguage.cpp index efd607ba60..9399d6efcc 100644 --- a/qtbase/src/gui/util/qshaderlanguage.cpp +++ b/qtbase/src/gui/util/qshaderlanguage.cpp @@ -52,3 +52,5 @@ void qt_register_ShaderLanguage_enums() } QT_END_NAMESPACE + +#include "moc_qshaderlanguage_p.cpp" diff --git a/qtbase/src/network/access/http2/hpacktable.cpp b/qtbase/src/network/access/http2/hpacktable.cpp index fddb5feca5..315f3e2344 100644 --- a/qtbase/src/network/access/http2/hpacktable.cpp +++ b/qtbase/src/network/access/http2/hpacktable.cpp @@ -40,6 +40,7 @@ #include "hpacktable_p.h" #include +#include #include #include @@ -62,8 +63,10 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value) // for counting the number of references to the name and value would have // 32 octets of overhead." - const unsigned sum = unsigned(name.size() + value.size()); - if (std::numeric_limits::max() - 32 < sum) + size_t sum; + if (add_overflow(size_t(name.size()), size_t(value.size()), &sum)) + return HeaderSize(); + if (sum > (std::numeric_limits::max() - 32)) return HeaderSize(); return HeaderSize(true, quint32(sum + 32)); } diff --git a/qtbase/src/network/access/qhttp2protocolhandler.cpp b/qtbase/src/network/access/qhttp2protocolhandler.cpp index 39dd460881..926f3134a0 100644 --- a/qtbase/src/network/access/qhttp2protocolhandler.cpp +++ b/qtbase/src/network/access/qhttp2protocolhandler.cpp @@ -46,10 +46,12 @@ #include #include + #include #include #include #include +#include #include #include @@ -124,8 +126,10 @@ std::vector assemble_hpack_block(const std::vector &frames) std::vector hpackBlock; quint32 total = 0; - for (const auto &frame : frames) - total += frame.hpackBlockSize(); + for (const auto &frame : frames) { + if (add_overflow(total, frame.hpackBlockSize(), &total)) + return hpackBlock; + } if (!total) return hpackBlock; @@ -371,12 +375,12 @@ bool QHttp2ProtocolHandler::sendRequest() } } - if (!prefaceSent && !sendClientPreface()) - return false; - if (!requests.size()) return true; + if (!prefaceSent && !sendClientPreface()) + return false; + m_channel->state = QHttpNetworkConnectionChannel::WritingState; // Check what was promised/pushed, maybe we do not have to send a request // and have a response already? diff --git a/qtbase/src/network/access/qhttpmultipart_p.h b/qtbase/src/network/access/qhttpmultipart_p.h index ead1eadf3b..e45f7545a4 100644 --- a/qtbase/src/network/access/qhttpmultipart_p.h +++ b/qtbase/src/network/access/qhttpmultipart_p.h @@ -54,7 +54,9 @@ #include #include "QtCore/qshareddata.h" #include "qnetworkrequest_p.h" // for deriving QHttpPartPrivate from QNetworkHeadersPrivate + #include "private/qobject_p.h" +#include QT_REQUIRE_CONFIG(http); diff --git a/qtbase/src/network/access/qhttpnetworkconnectionchannel.cpp b/qtbase/src/network/access/qhttpnetworkconnectionchannel.cpp index 7620ca1647..13f9630c65 100644 --- a/qtbase/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/qtbase/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -255,6 +255,10 @@ void QHttpNetworkConnectionChannel::abort() bool QHttpNetworkConnectionChannel::sendRequest() { Q_ASSERT(!protocolHandler.isNull()); + if (waitingForPotentialAbort) { + needInvokeSendRequest = true; + return false; // this return value is unused + } return protocolHandler->sendRequest(); } @@ -267,21 +271,28 @@ bool QHttpNetworkConnectionChannel::sendRequest() void QHttpNetworkConnectionChannel::sendRequestDelayed() { QMetaObject::invokeMethod(this, [this] { - Q_ASSERT(!protocolHandler.isNull()); if (reply) - protocolHandler->sendRequest(); + sendRequest(); }, Qt::ConnectionType::QueuedConnection); } void QHttpNetworkConnectionChannel::_q_receiveReply() { Q_ASSERT(!protocolHandler.isNull()); + if (waitingForPotentialAbort) { + needInvokeReceiveReply = true; + return; + } protocolHandler->_q_receiveReply(); } void QHttpNetworkConnectionChannel::_q_readyRead() { Q_ASSERT(!protocolHandler.isNull()); + if (waitingForPotentialAbort) { + needInvokeReadyRead = true; + return; + } protocolHandler->_q_readyRead(); } @@ -1289,7 +1300,18 @@ void QHttpNetworkConnectionChannel::_q_encrypted() // Similar to HTTP/1.1 counterpart below: const auto &pairs = spdyRequestsToSend.values(); // (request, reply) const auto &pair = pairs.first(); + waitingForPotentialAbort = true; emit pair.second->encrypted(); + + // We don't send or handle any received data until any effects from + // emitting encrypted() have been processed. This is necessary + // because the user may have called abort(). We may also abort the + // whole connection if the request has been aborted and there is + // no more requests to send. + QMetaObject::invokeMethod(this, + &QHttpNetworkConnectionChannel::checkAndResumeCommunication, + Qt::QueuedConnection); + // In case our peer has sent us its settings (window size, max concurrent streams etc.) // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection). QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); @@ -1307,6 +1329,26 @@ void QHttpNetworkConnectionChannel::_q_encrypted() } } +void QHttpNetworkConnectionChannel::checkAndResumeCommunication() +{ + Q_ASSERT(connection->connectionType() > QHttpNetworkConnection::ConnectionTypeHTTP); + + // Because HTTP/2 requires that we send a SETTINGS frame as the first thing we do, and respond + // to a SETTINGS frame with an ACK, we need to delay any handling until we can ensure that any + // effects from emitting encrypted() have been processed. + // This function is called after encrypted() was emitted, so check for changes. + + if (!reply && spdyRequestsToSend.isEmpty()) + abort(); + waitingForPotentialAbort = false; + if (needInvokeReadyRead) + _q_readyRead(); + if (needInvokeReceiveReply) + _q_receiveReply(); + if (needInvokeSendRequest) + sendRequest(); +} + void QHttpNetworkConnectionChannel::requeueSpdyRequests() { QList spdyPairs = spdyRequestsToSend.values(); diff --git a/qtbase/src/network/access/qhttpnetworkconnectionchannel_p.h b/qtbase/src/network/access/qhttpnetworkconnectionchannel_p.h index d8ac3979d1..eac4446492 100644 --- a/qtbase/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/qtbase/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -107,6 +107,10 @@ public: QAbstractSocket *socket; bool ssl; bool isInitialized; + bool waitingForPotentialAbort = false; + bool needInvokeReceiveReply = false; + bool needInvokeReadyRead = false; + bool needInvokeSendRequest = false; ChannelState state; QHttpNetworkRequest request; // current request, only used for HTTP QHttpNetworkReply *reply; // current reply for this request, only used for HTTP @@ -187,6 +191,8 @@ public: void closeAndResendCurrentRequest(); void resendCurrentRequest(); + void checkAndResumeCommunication(); + bool isSocketBusy() const; bool isSocketWriting() const; bool isSocketWaiting() const; 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/ssl/qsslcontext_openssl.cpp b/qtbase/src/network/ssl/qsslcontext_openssl.cpp index c30192a4eb..e4bb61ecb5 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 diff --git a/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp index 333b09e9c1..0f7343c9d9 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_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_symbols.cpp b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp index 0ace951c77..6a9a3ef3b3 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -499,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 @@ -1220,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 5e9faae291..bf165f67ad 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -598,10 +598,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) 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 d3d793efc3..089fd39927 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 diff --git a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h index 8389bd8f5a..55c46de8cd 100644 --- a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h +++ b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h @@ -98,7 +98,46 @@ public: return sym <= 0xff; } static bool isKeypad(xkb_keysym_t sym) { - return sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9; + switch (sym) { + case XKB_KEY_KP_Space: + case XKB_KEY_KP_Tab: + case XKB_KEY_KP_Enter: + case XKB_KEY_KP_F1: + case XKB_KEY_KP_F2: + case XKB_KEY_KP_F3: + case XKB_KEY_KP_F4: + case XKB_KEY_KP_Home: + case XKB_KEY_KP_Left: + case XKB_KEY_KP_Up: + case XKB_KEY_KP_Right: + case XKB_KEY_KP_Down: + case XKB_KEY_KP_Prior: + case XKB_KEY_KP_Next: + case XKB_KEY_KP_End: + case XKB_KEY_KP_Begin: + case XKB_KEY_KP_Insert: + case XKB_KEY_KP_Delete: + case XKB_KEY_KP_Equal: + case XKB_KEY_KP_Multiply: + case XKB_KEY_KP_Add: + case XKB_KEY_KP_Separator: + case XKB_KEY_KP_Subtract: + case XKB_KEY_KP_Decimal: + case XKB_KEY_KP_Divide: + case XKB_KEY_KP_0: + case XKB_KEY_KP_1: + case XKB_KEY_KP_2: + case XKB_KEY_KP_3: + case XKB_KEY_KP_4: + case XKB_KEY_KP_5: + case XKB_KEY_KP_6: + case XKB_KEY_KP_7: + case XKB_KEY_KP_8: + case XKB_KEY_KP_9: + return true; + default: + return false; + } } static void setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context); diff --git a/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index 9153fd20bb..d30ed5b6dc 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; @@ -1560,11 +1606,12 @@ bool AtSpiAdaptor::inheritsQAction(QObject *object) // Component static QAccessibleInterface * getWindow(QAccessibleInterface * interface) { - if (interface->role() == QAccessible::Window) + if (interface->role() == QAccessible::Dialog || interface->role() == QAccessible::Window) return interface; QAccessibleInterface * parent = interface->parent(); - while (parent && parent->role() != QAccessible::Window) + while (parent && parent->role() != QAccessible::Dialog + && parent->role() != QAccessible::Window) parent = parent->parent(); return parent; @@ -1582,7 +1629,7 @@ static QRect getRelativeRect(QAccessibleInterface *interface) wr = window->rect(); cr.setX(cr.x() - wr.x()); - cr.setY(cr.x() - wr.y()); + cr.setY(cr.y() - wr.y()); } return cr; } @@ -1836,7 +1883,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString uint coordType = message.arguments().at(2).toUInt(); if (coordType == ATSPI_COORD_TYPE_WINDOW) { QWindow *win = interface->window(); - point -= QPoint(win->x(), win->y()); + point += QPoint(win->x(), win->y()); } int offset = interface->textInterface()->offsetAtPoint(point); sendReply(connection, message, offset); @@ -2393,13 +2440,14 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString if (cols > 0) { row = index / cols; col = index % cols; - QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface(); - if (cell) { - row = cell->rowIndex(); - col = cell->columnIndex(); - rowExtents = cell->rowExtent(); - colExtents = cell->columnExtent(); - isSelected = cell->isSelected(); + QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, col); + QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; + if (cellIface) { + row = cellIface->rowIndex(); + col = cellIface->columnIndex(); + rowExtents = cellIface->rowExtent(); + colExtents = cellIface->columnExtent(); + isSelected = cellIface->isSelected(); success = true; } } @@ -2410,12 +2458,22 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString } else if (function == QLatin1String("GetColumnExtentAt")) { int row = message.arguments().at(0).toInt(); int column = message.arguments().at(1).toInt(); - connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent())); + int columnExtent = 0; + QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); + QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; + if (cellIface) + columnExtent = cellIface->columnExtent(); + connection.send(message.createReply(columnExtent)); } else if (function == QLatin1String("GetRowExtentAt")) { int row = message.arguments().at(0).toInt(); int column = message.arguments().at(1).toInt(); - connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent())); + int rowExtent = 0; + QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); + QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; + if (cellIface) + rowExtent = cellIface->rowExtent(); + connection.send(message.createReply(rowExtent)); } else if (function == QLatin1String("GetColumnHeader")) { int column = message.arguments().at(0).toInt(); @@ -2455,8 +2513,12 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString } else if (function == QLatin1String("IsSelected")) { int row = message.arguments().at(0).toInt(); int column = message.arguments().at(1).toInt(); - QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface(); - connection.send(message.createReply(cell->isSelected())); + bool isSelected = false; + QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); + QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; + if (cellIface) + isSelected = cellIface->isSelected(); + connection.send(message.createReply(isSelected)); } else if (function == QLatin1String("AddColumnSelection")) { int column = message.arguments().at(0).toInt(); connection.send(message.createReply(interface->tableInterface()->selectColumn(column))); diff --git a/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp b/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp index fdc8cd3198..b17e1749c8 100644 --- a/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp +++ b/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp @@ -229,7 +229,11 @@ static RoleMapping map[] = { //: Role of an accessible object { QAccessible::ButtonDropDown, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down") }, //: Role of an accessible object +#if ATSPI_ROLE_COUNT > 130 + { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON_MENU, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") }, +#else { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") }, +#endif //: Role of an accessible object - a button that expands a grid. { QAccessible::ButtonDropGrid, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down grid") }, //: Role of an accessible object - blank space between other objects. 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 f0d1722c95..47ef7d2b5c 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 @@ -205,8 +210,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) // handle_token (s) - A string that will be used as the last element of the @handle. // writable (b) - Whether to allow the chosen application to write to the file. -#ifdef O_PATH - const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH); + const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY); if (fd != -1) { QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), QLatin1String("/org/freedesktop/portal/desktop"), @@ -216,16 +220,13 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) QDBusUnixFileDescriptor descriptor; descriptor.giveFileDescriptor(fd); - const QVariantMap options = {{QLatin1String("writable"), true}}; + const QVariantMap options = {}; // FIXME parent_window_id message << QString() << QVariant::fromValue(descriptor) << options; return QDBusConnection::sessionBus().call(message); } -#else - Q_UNUSED(url) -#endif return QDBusMessage::createError(QDBusError::InternalError, qt_error_string()); } @@ -298,8 +299,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(); @@ -354,6 +482,8 @@ bool QGenericUnixServices::openDocument(const QUrl &url) } #else +QGenericUnixServices::QGenericUnixServices() = default; + QByteArray QGenericUnixServices::desktopEnvironment() const { return QByteArrayLiteral("UNKNOWN"); @@ -373,6 +503,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/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/qxcbconnection.cpp b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp index 013ca7369f..631ade2ec7 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -706,6 +706,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); } diff --git a/qtbase/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/qtbase/src/plugins/platforms/xcb/qxcbconnection_screens.cpp index ec099101f5..9cee3bc5e1 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbconnection_screens.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbconnection_screens.cpp @@ -165,7 +165,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) // Screen has been disabled auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(), output.output, output.config_timestamp); - if (outputInfo->crtc == XCB_NONE) { + if (!outputInfo || outputInfo->crtc == XCB_NONE) { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled"; destroyScreen(screen); } else { diff --git a/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 1ced02f31d..5c8298a49d 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -1255,16 +1255,14 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD if (Q_LIKELY(useValuators)) { const qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.x(), physicalScreenArea.width()); global.setX(value); - // mapFromGlobal is ok for nested/embedded windows, but works only with whole-number QPoint; - // so map it first, then add back the sub-pixel position - local.setX(window->mapFromGlobal(QPoint(int(value), 0)).x() + (value - int(value))); + local.setX(xcbWindow->mapFromGlobal(QPoint(int(value), 0)).x() + (value - int(value))); } break; case QXcbAtom::AbsY: if (Q_LIKELY(useValuators)) { qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.y(), physicalScreenArea.height()); global.setY(value); - local.setY(window->mapFromGlobal(QPoint(0, int(value))).y() + (value - int(value))); + local.setY(xcbWindow->mapFromGlobal(QPoint(0, int(value))).y() + (value - int(value))); } break; case QXcbAtom::AbsPressure: diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp index 4210bf428e..4635c199d0 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_callbackForPropertyRegistered && 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 066874dc66..ed8c4ebd8f 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -539,6 +539,8 @@ void QXcbWindow::destroy() doFocusOut(); if (connection()->mouseGrabber() == this) connection()->setMouseGrabber(nullptr); + if (connection()->mousePressWindow() == this) + connection()->setMousePressWindow(nullptr); if (m_syncCounter && connection()->hasXSync()) xcb_sync_destroy_counter(xcb_connection(), m_syncCounter); @@ -1297,6 +1299,7 @@ void QXcbWindow::setParent(const QPlatformWindow *parent) m_embedded = false; } xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y()); + connection()->sync(); } void QXcbWindow::setWindowTitle(const QString &title) @@ -1345,9 +1348,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/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp index c6596c35de..8987e3efd0 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; @@ -122,11 +119,16 @@ public: QString selectedNameFilter; QStringList selectedFiles; 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); @@ -134,6 +136,9 @@ QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelp 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); @@ -293,10 +298,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(), @@ -330,7 +343,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; @@ -352,7 +365,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; @@ -407,16 +420,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() @@ -433,13 +443,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; } @@ -469,6 +476,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 e0c72fa240..309bbdad57 100644 --- a/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp +++ b/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp @@ -1370,20 +1370,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 8e2e883652..d147774055 100644 --- a/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp +++ b/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp @@ -745,10 +745,15 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMess f.setAutoValue(isAutoValue(hStmt, i)); QVarLengthArray tableName(TABLENAMESIZE); SQLSMALLINT tableNameLen; - r = SQLColAttribute(hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName.data(), - TABLENAMESIZE, &tableNameLen, 0); + r = SQLColAttribute(hStmt, + i + 1, + SQL_DESC_BASE_TABLE_NAME, + tableName.data(), + SQLSMALLINT(tableName.size() * sizeof(SQLTCHAR)), // SQLColAttribute needs/returns size in bytes + &tableNameLen, + 0); if (r == SQL_SUCCESS) - f.setTableName(fromSQLTCHAR(tableName, tableNameLen)); + f.setTableName(fromSQLTCHAR(tableName, tableNameLen / sizeof(SQLTCHAR))); return f; } 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/widgets/dialogs/qcolordialog.cpp b/qtbase/src/widgets/dialogs/qcolordialog.cpp index 4247731275..30445fa069 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 @@ -801,6 +804,10 @@ QColorLuminancePicker::~QColorLuminancePicker() void QColorLuminancePicker::mouseMoveEvent(QMouseEvent *m) { + if (m->buttons() == Qt::NoButton) { + m->ignore(); + return; + } setVal(y2val(m->y())); } void QColorLuminancePicker::mousePressEvent(QMouseEvent *m) @@ -935,6 +942,10 @@ void QColorPicker::setCol(int h, int s) void QColorPicker::mouseMoveEvent(QMouseEvent *m) { QPoint p = m->pos() - contentsRect().topLeft(); + if (m->buttons() == Qt::NoButton) { + m->ignore(); + return; + } setCol(p); emit newCol(hue, sat); } @@ -1611,6 +1622,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/qlistview.cpp b/qtbase/src/widgets/itemviews/qlistview.cpp index fab44923de..79254e052f 100644 --- a/qtbase/src/widgets/itemviews/qlistview.cpp +++ b/qtbase/src/widgets/itemviews/qlistview.cpp @@ -3389,6 +3389,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()) { @@ -3399,7 +3400,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/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/util/qsystemtrayicon.cpp b/qtbase/src/widgets/util/qsystemtrayicon.cpp index 203fcbc443..e9b2724903 100644 --- a/qtbase/src/widgets/util/qsystemtrayicon.cpp +++ b/qtbase/src/widgets/util/qsystemtrayicon.cpp @@ -208,7 +208,7 @@ void QSystemTrayIcon::setContextMenu(QMenu *menu) if (oldMenu != menu && d->qpa_sys) { // Show the QMenu-based menu for QPA plugins that do not provide native menus if (oldMenu && !oldMenu->platformMenu()) - QObject::disconnect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, menu, nullptr); + QObject::disconnect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, oldMenu, nullptr); if (menu && !menu->platformMenu()) { QObject::connect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, menu, 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 422082da6c..5692c6e82b 100644 --- a/qtbase/src/widgets/widgets/qcombobox.cpp +++ b/qtbase/src/widgets/widgets/qcombobox.cpp @@ -3354,7 +3354,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 @@ -3396,26 +3412,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/qcombobox_p.h b/qtbase/src/widgets/widgets/qcombobox_p.h index 45580ba943..2a97f56791 100644 --- a/qtbase/src/widgets/widgets/qcombobox_p.h +++ b/qtbase/src/widgets/widgets/qcombobox_p.h @@ -429,7 +429,7 @@ public: int minimumContentsLength = 0; int indexBeforeChange = -1; int maxVisibleItems = 10; - int maxCount = std::numeric_limits::max(); + int maxCount = (std::numeric_limits::max)(); int modelColumn = 0; int placeholderIndex = -1; bool shownOnce : 1; 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/qdial.cpp b/qtbase/src/widgets/widgets/qdial.cpp index 8f774a3cc7..ec5cec0d82 100644 --- a/qtbase/src/widgets/widgets/qdial.cpp +++ b/qtbase/src/widgets/widgets/qdial.cpp @@ -94,6 +94,8 @@ int QDialPrivate::bound(int val) const if (wrapping) { if ((val >= minimum) && (val <= maximum)) return val; + if (minimum == maximum) + return minimum; val = minimum + ((val - minimum) % (maximum - minimum)); if (val < minimum) val += maximum - minimum; 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/qlineedit_p.cpp b/qtbase/src/widgets/widgets/qlineedit_p.cpp index 80c9258da4..3fa0f29d28 100644 --- a/qtbase/src/widgets/widgets/qlineedit_p.cpp +++ b/qtbase/src/widgets/widgets/qlineedit_p.cpp @@ -434,7 +434,7 @@ void QLineEditIconButton::animateShow(bool visible) void QLineEditIconButton::startOpacityAnimation(qreal endValue) { - QPropertyAnimation *animation = new QPropertyAnimation(this, QByteArrayLiteral("opacity")); + QPropertyAnimation *animation = new QPropertyAnimation(this, QByteArrayLiteral("opacity"), this); connect(animation, &QPropertyAnimation::finished, this, &QLineEditIconButton::onAnimationFinished); animation->setDuration(160); 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/sync.profile b/qtbase/sync.profile index 0292bf0dc2..7dd0177b90 100644 --- a/qtbase/sync.profile +++ b/qtbase/sync.profile @@ -77,7 +77,7 @@ "qsql.h" => "QtSql/qtsqlglobal.h" }, "QtDBus" => { - "qdbusmacros.h" => "QtDbus/qtdbusglobal.h" + "qdbusmacros.h" => "QtDBus/qtdbusglobal.h" }, "QtTest" => { "qtest_global.h" => "QtTest/qttestglobal.h" 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/mimetypes/qmimedatabase/circular-inheritance.xml b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/circular-inheritance.xml new file mode 100644 index 0000000000..466f039803 --- /dev/null +++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/circular-inheritance.xml @@ -0,0 +1,13 @@ + + + + It's more accurate to say that ECMAScript is a subset of JavaScript + + + + + than to say that JavaScript is a subset of ECMAScript + + + + diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc index 1002d0195d..49dbb0a8ba 100644 --- a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc +++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc @@ -3,10 +3,12 @@ yast2-metapackage-handler-mimetypes.xml qml-again.xml text-x-objcsrc.xml + text-plain-subclass.xml test.qml invalid-magic1.xml invalid-magic2.xml invalid-magic3.xml + circular-inheritance.xml magic-and-hierarchy.xml magic-and-hierarchy.foo magic-and-hierarchy2.foo diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/text-plain-subclass.xml b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/text-plain-subclass.xml new file mode 100644 index 0000000000..7b5cb7506d --- /dev/null +++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/text-plain-subclass.xml @@ -0,0 +1,15 @@ + + + + MicroDVD subtitles + + + + + + + + + + + diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 0ea422ecbc..d2dd8d340b 100644 --- a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -49,10 +49,12 @@ static const char *const additionalMimeFiles[] = { "yast2-metapackage-handler-mimetypes.xml", "qml-again.xml", "text-x-objcsrc.xml", + "text-plain-subclass.xml", "invalid-magic1.xml", "invalid-magic2.xml", "invalid-magic3.xml", "magic-and-hierarchy.xml", + "circular-inheritance.xml", 0 }; @@ -70,15 +72,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 +156,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())); @@ -390,6 +392,13 @@ void tst_QMimeDatabase::inheritance() const QMimeType mswordTemplate = db.mimeTypeForName(QString::fromLatin1("application/msword-template")); QVERIFY(mswordTemplate.isValid()); QVERIFY(mswordTemplate.inherits(QLatin1String("application/msword"))); + + // Check that buggy type definitions that have circular inheritance don't cause an infinite + // loop, especially when resolving a conflict between the file's name and its contents + const QMimeType ecmascript = db.mimeTypeForName(QString::fromLatin1("application/ecmascript")); + QVERIFY(ecmascript.allAncestors().contains("text/plain")); + const QMimeType javascript = db.mimeTypeForFileNameAndData("xml.js", ""); + QVERIFY(javascript.inherits(QString::fromLatin1("text/javascript"))); } void tst_QMimeDatabase::aliases() @@ -611,7 +620,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 +640,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"; @@ -1070,6 +1078,8 @@ void tst_QMimeDatabase::installNewLocalMimeType() QVERIFY(objcsrc.isValid()); QCOMPARE(objcsrc.globPatterns(), QStringList()); } + QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.txt"), QMimeDatabase::MatchExtension).name(), + QString::fromLatin1("text/plain")); // Test that a double-definition of a mimetype doesn't lead to sniffing ("conflicting globs"). const QString qmlTestFile = QLatin1String(RESOURCE_PREFIX "test.qml"); diff --git a/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h index ca2ceed7a9..624316dfdb 100644 --- a/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h +++ b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h @@ -35,7 +35,7 @@ class Plugin1 : public QObject, public PluginInterface1 { Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.autotests.plugininterface1") + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.autotests.plugininterface1" FILE "plugin1.json") Q_INTERFACES(PluginInterface1) public: diff --git a/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.json b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.json new file mode 100644 index 0000000000..ce67846d48 --- /dev/null +++ b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.json @@ -0,0 +1,5 @@ +{ + "Keys": [ + "plugin1" + ] +} diff --git a/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp b/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp index 9fa61804b3..88ada1b806 100644 --- a/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp +++ b/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "plugin1/plugininterface1.h" #include "plugin2/plugininterface2.h" @@ -52,6 +53,7 @@ public slots: private slots: void usingTwoFactoriesFromSameDir(); + void multiplePaths(); }; static const char binFolderC[] = "bin"; @@ -92,5 +94,30 @@ void tst_QFactoryLoader::usingTwoFactoriesFromSameDir() QCOMPARE(plugin2->pluginName(), QLatin1String("Plugin2 ok")); } +void tst_QFactoryLoader::multiplePaths() +{ +#if !QT_CONFIG(library) || !(defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)) || defined(Q_OS_ANDROID) + QSKIP("Test not applicable in this configuration."); +#else + const QString binFolder = QFINDTESTDATA(binFolderC); + + QTemporaryDir dir; + QVERIFY(dir.isValid()); + + QString pluginsPath = QFileInfo(binFolder, binFolderC).absolutePath(); + QString linkPath = dir.filePath(binFolderC); + QVERIFY(QFile::link(pluginsPath, linkPath)); + + QCoreApplication::setLibraryPaths({ QFileInfo(binFolder).absolutePath(), dir.path() }); + + const QString suffix = QLatin1Char('/') + QLatin1String(binFolderC); + QFactoryLoader loader1(PluginInterface1_iid, suffix); + + QLibraryPrivate *library1 = loader1.library("plugin1"); + QVERIFY(library1); + QCOMPARE(library1->loadHints(), QLibrary::PreventUnloadHint); +#endif +} + QTEST_MAIN(tst_QFactoryLoader) #include "tst_qfactoryloader.moc" diff --git a/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index ce8057372c..833f68b1de 100644 --- a/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -193,7 +193,9 @@ void tst_QPluginLoader::errorString() QVERIFY(!unloaded); } -#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_HPUX) +// A bug in QNX causes the test to crash on exit after attempting to load +// a shared library with undefined symbols (tracked as QTBUG-114682). +#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_HPUX) && !defined(Q_OS_QNX) { QPluginLoader loader( sys_qualifiedLibraryName("almostplugin")); //a plugin with unresolved symbols loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); @@ -243,10 +245,37 @@ void tst_QPluginLoader::loadHints() QSKIP("This test requires Qt to create shared libraries."); #endif QPluginLoader loader; - QCOMPARE(loader.loadHints(), QLibrary::LoadHints{}); //Do not crash + QCOMPARE(loader.loadHints(), QLibrary::PreventUnloadHint); //Do not crash loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); + QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); + // We can clear load hints when file name is not set. + loader.setLoadHints(QLibrary::LoadHints{}); + QCOMPARE(loader.loadHints(), QLibrary::LoadHints{}); + // Set the hints again + loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); + QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); loader.setFileName( sys_qualifiedLibraryName("theplugin")); //a plugin QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); + + QPluginLoader loader4; + QCOMPARE(loader4.loadHints(), QLibrary::PreventUnloadHint); + loader4.setLoadHints(QLibrary::LoadHints{}); + QCOMPARE(loader4.loadHints(), QLibrary::LoadHints{}); + loader4.setFileName(sys_qualifiedLibraryName("theplugin")); + // Hints are merged with hints from the previous loader. + QCOMPARE(loader4.loadHints(), QLibrary::ResolveAllSymbolsHint); + // We cannot clear load hints after associating the loader with a file. + loader.setLoadHints(QLibrary::LoadHints{}); + QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); + + QPluginLoader loader2; + QCOMPARE(loader2.loadHints(), QLibrary::PreventUnloadHint); + loader2.setFileName(sys_qualifiedLibraryName("theplugin")); + // Hints are merged with hints from previous loaders. + QCOMPARE(loader2.loadHints(), QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); + + QPluginLoader loader3(sys_qualifiedLibraryName("theplugin")); + QCOMPARE(loader3.loadHints(), QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); } void tst_QPluginLoader::deleteinstanceOnUnload() 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/text/qbytearray/tst_qbytearray.cpp b/qtbase/tests/auto/corelib/text/qbytearray/tst_qbytearray.cpp index d8aa17d9ef..d539941378 100644 --- a/qtbase/tests/auto/corelib/text/qbytearray/tst_qbytearray.cpp +++ b/qtbase/tests/auto/corelib/text/qbytearray/tst_qbytearray.cpp @@ -869,7 +869,11 @@ void tst_QByteArray::qvsnprintf() #ifndef Q_OS_WIN memset(buf, 42, sizeof(buf)); + QT_WARNING_PUSH + QT_WARNING_DISABLE_GCC("-Wformat-zero-length") + QT_WARNING_DISABLE_CLANG("-Wformat-zero-length") QCOMPARE(::qsnprintf(buf, 10, ""), 0); + QT_WARNING_POP #endif } 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/painting/qpainter/tst_qpainter.cpp b/qtbase/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index d7c3f95f1d..bfc77b0831 100644 --- a/qtbase/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/qtbase/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -84,6 +84,7 @@ private slots: #endif void drawPixmapFragments(); void drawPixmapNegativeScale(); + void drawPixmapRounding(); void drawLine_data(); void drawLine(); @@ -799,6 +800,16 @@ void tst_QPainter::drawPixmapNegativeScale() QVERIFY(resultImage.pixel(12, 8) == qRgba(0, 0, 0, 255)); // and right strip is now black } +void tst_QPainter::drawPixmapRounding() +{ + // Just test that we don't assert + QBitmap bm(8, 8); + QImage out(64, 64, QImage::Format_RGB32); + QPainter p(&out); + qreal y = 26.499999999999996; + p.drawPixmap(QPointF(0, y), bm); +} + void tst_QPainter::drawLine_data() { QTest::addColumn("line"); 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/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 f6ad97a96b..61452dceae 100644 --- a/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp +++ b/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp @@ -116,6 +116,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/qdial/tst_qdial.cpp b/qtbase/tests/auto/widgets/widgets/qdial/tst_qdial.cpp index 356f773ae9..a014df3b15 100644 --- a/qtbase/tests/auto/widgets/widgets/qdial/tst_qdial.cpp +++ b/qtbase/tests/auto/widgets/widgets/qdial/tst_qdial.cpp @@ -42,6 +42,7 @@ private slots: void valueChanged(); void sliderMoved(); void wrappingCheck(); + void minEqualMaxValueOutsideRange(); }; // Testing get/set functions @@ -194,5 +195,14 @@ void tst_QDial::wrappingCheck() } } +// QTBUG-104641 +void tst_QDial::minEqualMaxValueOutsideRange() +{ + QDial dial; + dial.setRange(30, 30); + dial.setWrapping(true); + dial.setValue(45); +} + QTEST_MAIN(tst_QDial) #include "tst_qdial.moc" 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 qtcanvas3d 1319e096...00000000 (submodule deleted) Submodule qtconnectivity b242dc4f..c8a0f0b1: diff --git a/qtconnectivity/src/tools/sdpscanner/main.cpp b/qtconnectivity/src/tools/sdpscanner/main.cpp index c39ff8f3..e005a70f 100644 --- a/qtconnectivity/src/tools/sdpscanner/main.cpp +++ b/qtconnectivity/src/tools/sdpscanner/main.cpp @@ -96,7 +96,8 @@ static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray & xmlOutput.append(snBuffer); break; case SDP_UINT64: - qsnprintf(snBuffer, BUFFER_SIZE, "\n", data->val.uint64); + qsnprintf(snBuffer, BUFFER_SIZE, "\n", + qulonglong(data->val.uint64)); xmlOutput.append(snBuffer); break; case SDP_UINT128: @@ -119,7 +120,8 @@ static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray & xmlOutput.append(snBuffer); break; case SDP_INT64: - qsnprintf(snBuffer, BUFFER_SIZE, "/n", data->val.int64); + qsnprintf(snBuffer, BUFFER_SIZE, "/n", + qlonglong(data->val.int64)); xmlOutput.append(snBuffer); break; case SDP_INT128: Submodule qtdeclarative abe4729e..e2b38659: diff --git a/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp index b5c5f6a2b0..1a3d3cdf97 100644 --- a/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp +++ b/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -112,10 +112,7 @@ void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, if (result == MAP_FAILED) CRASH(); - while (madvise(result, bytes, MADV_DONTNEED)) { - if (errno != EAGAIN) - CRASH(); - } + while (madvise(result, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } if (fd != -1) close(fd); @@ -248,8 +245,10 @@ void OSAllocator::decommit(void* address, size_t bytes) mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); #elif OS(LINUX) while (madvise(address, bytes, MADV_DONTNEED)) { - if (errno != EAGAIN) - CRASH(); + if (errno != EAGAIN) { + memset(address, 0, bytes); // We rely on madvise to zero-out the memory + break; + } } if (mprotect(address, bytes, PROT_NONE)) CRASH(); diff --git a/qtdeclarative/src/qml/jsruntime/qv4function.cpp b/qtdeclarative/src/qml/jsruntime/qv4function.cpp index cf8a53cf9f..223e64271e 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 notifyList; + QAtomicPointer notifyList = nullptr; inline QQmlNotifierEndpoint *notify(int index) const; void addNotify(int index, QQmlNotifierEndpoint *); @@ -201,12 +201,12 @@ public: QQmlContextData *outerContext = nullptr; QQmlContextDataRef ownContext; - QQmlAbstractBinding *bindings; - QQmlBoundSignal *signalHandlers; + QQmlAbstractBinding *bindings = nullptr; + QQmlBoundSignal *signalHandlers = nullptr; // Linked list for QQmlContext::contextObjects - QQmlData *nextContextObject; - QQmlData**prevContextObject; + QQmlData *nextContextObject = nullptr; + QQmlData**prevContextObject = nullptr; inline bool hasBindingBit(int) const; inline void setBindingBit(QObject *obj, int); @@ -216,10 +216,10 @@ public: inline void setPendingBindingBit(QObject *obj, int); inline void clearPendingBindingBit(int); - quint16 lineNumber; - quint16 columnNumber; + quint16 lineNumber = 0; + quint16 columnNumber = 0; - quint32 jsEngineId; // id of the engine that created the jsWrapper + quint32 jsEngineId = 0; // id of the engine that created the jsWrapper struct DeferredData { DeferredData(); @@ -240,7 +240,7 @@ public: QQmlPropertyCache *propertyCache; - QQmlGuardImpl *guards; + QQmlGuardImpl *guards = 0; static QQmlData *get(const QObject *object, bool create = false) { QObjectPrivate *priv = QObjectPrivate::get(const_cast(object)); @@ -289,7 +289,7 @@ public: private: // For attachedProperties - mutable QQmlDataExtended *extendedData; + mutable QQmlDataExtended *extendedData = nullptr; Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv); Q_NEVER_INLINE static QQmlPropertyCache *createPropertyCache(QJSEngine *engine, QObject *object); diff --git a/qtdeclarative/src/qml/qml/qqmlengine.cpp b/qtdeclarative/src/qml/qml/qqmlengine.cpp index 2325c4c1e0..069e369319 100644 --- a/qtdeclarative/src/qml/qml/qqmlengine.cpp +++ b/qtdeclarative/src/qml/qml/qqmlengine.cpp @@ -725,11 +725,8 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) QQmlData::QQmlData() : ownedByQml1(false), ownMemory(true), indestructible(true), explicitIndestructibleSet(false), hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), - hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), - bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr), - bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr), - lineNumber(0), columnNumber(0), jsEngineId(0), - propertyCache(nullptr), guards(nullptr), extendedData(nullptr) + hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), dummy(0), + bindingBitsArraySize(InlineBindingArraySize), propertyCache(nullptr) { memset(bindingBitsValue, 0, sizeof(bindingBitsValue)); init(); @@ -1591,17 +1588,22 @@ void qmlExecuteDeferred(QObject *object) { QQmlData *data = QQmlData::get(object); - if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); + if (!data + || !data->context + || !data->context->engine + || data->deferredData.isEmpty() + || data->wasDeleted(object)) { + return; + } - QQmlComponentPrivate::DeferredState state; - QQmlComponentPrivate::beginDeferred(ep, object, &state); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); + QQmlComponentPrivate::DeferredState state; + QQmlComponentPrivate::beginDeferred(ep, object, &state); - // Release the reference for the deferral action (we still have one from construction) - data->releaseDeferredData(); + // Release the reference for the deferral action (we still have one from construction) + data->releaseDeferredData(); - QQmlComponentPrivate::completeDeferred(ep, &state); - } + QQmlComponentPrivate::completeDeferred(ep, &state); } QQmlContext *qmlContext(const QObject *obj) diff --git a/qtdeclarative/src/qml/qml/qqmlimport.cpp b/qtdeclarative/src/qml/qml/qqmlimport.cpp index e7263d1850..289f11d006 100644 --- a/qtdeclarative/src/qml/qml/qqmlimport.cpp +++ b/qtdeclarative/src/qml/qml/qqmlimport.cpp @@ -2119,9 +2119,12 @@ void QQmlImportDatabase::addImportPath(const QString& path) cPath.replace(Backslash, Slash); } - if (!cPath.isEmpty() - && !fileImportPath.contains(cPath)) - fileImportPath.prepend(cPath); + if (!cPath.isEmpty()) { + if (fileImportPath.contains(cPath)) + fileImportPath.move(fileImportPath.indexOf(cPath), 0); + else + fileImportPath.prepend(cPath); + } } /*! diff --git a/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp b/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp index 4fd2092fd3..0d59d197dc 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/qmlmodels/qqmldelegatemodel.cpp b/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp index 3b57edfc5d..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)), @@ -1979,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); 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 36b65f906c..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; } 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 c655b4c327..8b139cb539 100644 --- a/qtdeclarative/src/quick/items/qquickitem.cpp +++ b/qtdeclarative/src/quick/items/qquickitem.cpp @@ -2328,6 +2328,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(). @@ -2695,9 +2696,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); } @@ -2774,8 +2774,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(); } @@ -2971,7 +2972,8 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) itemChange(QQuickItem::ItemChildRemovedChange, child); - emit q->childrenChanged(); + if (!inDestructor) + emit q->childrenChanged(); } void QQuickItemPrivate::refWindow(QQuickWindow *c) @@ -3200,6 +3202,7 @@ QQuickItemPrivate::QQuickItemPrivate() , touchEnabled(false) #endif , hasCursorHandler(false) + , inDestructor(false) , dirtyAttributes(0) , nextDirtyItem(nullptr) , prevDirtyItem(nullptr) @@ -5126,6 +5129,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() @@ -6112,9 +6122,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 } @@ -6163,6 +6175,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(); } diff --git a/qtdeclarative/src/quick/items/qquickitem_p.h b/qtdeclarative/src/quick/items/qquickitem_p.h index d48b551064..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, 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/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 cf021d9a7c..b0117683f7 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(); @@ -1527,19 +1536,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) @@ -1552,7 +1558,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/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 f0afdb16ca..e5daf2d28b 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 @@ -48,6 +50,8 @@ private slots: void qtbug_86017(); void contextAccessedByHandler(); void deleteRace(); + void redrawUponColumnChange(); + void deleteRace(); }; class AbstractItemModel : public QAbstractItemModel @@ -187,6 +191,41 @@ void tst_QQmlDelegateModel::deleteRace() QTRY_COMPARE(o->property("count").toInt(), 0); } +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/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() + } + } + } +} Submodule qtdoc 2653ffbb...47e2864f: Submodule qtdocgallery e36e6f0a...00000000 (submodule deleted) Submodule qtfeedback 9ac8d8c2...00000000 (submodule deleted) Submodule qtimageformats a9636f96..7b25a043: diff --git a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp index f0dfe7f..5cb0522 100644 --- a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp +++ b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp @@ -38,13 +38,14 @@ ****************************************************************************/ #include "qtiffhandler_p.h" -#include #include #include #include #include #include #include +#include +#include extern "C" { #include "tiffio.h" 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 2bcec6f6..6e89db9f: Submodule src/3rdparty/mapbox-gl-native d3101bbc...35d56672 (commits not present) diff --git a/qtlocation/src/location/configure.json b/qtlocation/src/location/configure.json index 6d01a9a3..d1e623a1 100644 --- a/qtlocation/src/location/configure.json +++ b/qtlocation/src/location/configure.json @@ -9,7 +9,7 @@ "label": "Qt.labs.location experimental QML plugin", "purpose": "Provides experimental QtLocation QML types", "section": "Location", - "condition": "config.opengl", + "condition": "features.opengl", "output": [ "privateFeature" ] }, "geoservices_osm": { 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/nokia/qgeocodingmanagerengine_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp index 68b2429e..deef29f9 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp @@ -66,11 +66,8 @@ QGeoCodingManagerEngineNokia::QGeoCodingManagerEngineNokia( Q_ASSERT(networkManager); m_networkManager->setParent(this); - if (parameters.contains(QStringLiteral("here.token"))) - m_token = parameters.value(QStringLiteral("here.token")).toString(); - - if (parameters.contains(QStringLiteral("here.app_id"))) - m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString(); + if (parameters.contains(QStringLiteral("here.apiKey"))) + m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); if (error) *error = QGeoServiceProvider::NoError; @@ -85,12 +82,9 @@ QString QGeoCodingManagerEngineNokia::getAuthenticationString() const { QString authenticationString; - if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { - authenticationString += "?app_code="; - authenticationString += m_token; - - authenticationString += "&app_id="; - authenticationString += m_applicationId; + if (!m_apiKey.isEmpty()) { + authenticationString += "?apiKey="; + authenticationString += m_apiKey; } return authenticationString; diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h index 9e1564aa..a7cfd06a 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h +++ b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h @@ -82,8 +82,7 @@ private: QGeoNetworkAccessManager *m_networkManager; QGeoUriProvider *m_uriProvider; QGeoUriProvider *m_reverseGeocodingUriProvider; - QString m_token; - QString m_applicationId; + QString m_apiKey; }; QT_END_NAMESPACE diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp index 576ecd43..f49e0905 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp @@ -39,7 +39,7 @@ QT_BEGIN_NAMESPACE const char NOKIA_PLUGIN_CONTEXT_NAME[] = "QtLocationQML"; -const char MISSED_CREDENTIALS[] = QT_TRANSLATE_NOOP("QtLocationQML", "Qt Location requires app_id and token parameters.\nPlease register at https://developer.here.com/ to get your personal application credentials."); +const char MISSED_CREDENTIALS[] = QT_TRANSLATE_NOOP("QtLocationQML", "Qt Location requires apiKey parameter.\nPlease register at https://developer.here.com/ to get your personal application credentials."); const char SAVING_PLACE_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Saving places is not supported."); const char REMOVING_PLACE_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Removing places is not supported."); const char SAVING_CATEGORY_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Saving categories is not supported."); diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp index 73b998b1..a938096b 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp @@ -60,8 +60,7 @@ QGeoRoutingManagerEngineNokia::QGeoRoutingManagerEngineNokia( Q_ASSERT(networkManager); m_networkManager->setParent(this); - m_appId = parameters.value(QStringLiteral("here.app_id")).toString(); - m_token = parameters.value(QStringLiteral("here.token")).toString(); + m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); QGeoRouteRequest::FeatureTypes featureTypes; featureTypes |= QGeoRouteRequest::TollFeature; @@ -219,18 +218,16 @@ QStringList QGeoRoutingManagerEngineNokia::calculateRouteRequestString(const QGe return QStringList(); QStringList requests; - QString baseRequest = QStringLiteral("http://"); + QString baseRequest = QStringLiteral("https://"); baseRequest += m_uriProvider->getCurrentHost(); baseRequest += QStringLiteral("/routing/7.2/calculateroute.xml"); baseRequest += QStringLiteral("?alternatives="); baseRequest += QString::number(request.numberAlternativeRoutes()); - if (!m_appId.isEmpty() && !m_token.isEmpty()) { - baseRequest += QStringLiteral("&app_id="); - baseRequest += m_appId; - baseRequest += QStringLiteral("&token="); - baseRequest += m_token; + if (!m_apiKey.isEmpty()) { + baseRequest += QStringLiteral("&apiKey="); + baseRequest += m_apiKey; } const QList metadata = request.waypointsMetadata(); @@ -281,7 +278,7 @@ QStringList QGeoRoutingManagerEngineNokia::updateRouteRequestString(const QGeoRo return QStringList(); QStringList requests; - QString baseRequest = "http://"; + QString baseRequest = "https://"; baseRequest += m_uriProvider->getCurrentHost(); baseRequest += "/routing/7.2/getroute.xml"; diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h index 9335bcac..67fb5825 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h +++ b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h @@ -77,8 +77,7 @@ private: QGeoNetworkAccessManager *m_networkManager; QGeoUriProvider *m_uriProvider; - QString m_appId; - QString m_token; + QString m_apiKey; }; QT_END_NAMESPACE diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp index e4ef86d6..2c53dd16 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp @@ -75,20 +75,15 @@ namespace void checkUsageTerms(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) { - QString appId, token; + const QString apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); - appId = parameters.value(QStringLiteral("here.app_id")).toString(); - token = parameters.value(QStringLiteral("here.token")).toString(); - - if (isValidParameter(appId) && isValidParameter(token)) + if (isValidParameter(apiKey)) return; - else if (!isValidParameter(appId)) - qWarning() << "Invalid here.app_id"; else - qWarning() << "Invalid here.token"; + qWarning() << "Invalid here.apiKey"; - if (parameters.contains(QStringLiteral("app_id")) || parameters.contains(QStringLiteral("token"))) - qWarning() << QStringLiteral("Please prefix 'app_id' and 'token' with prefix 'here' (e.g.: 'here.app_id')"); + if (parameters.contains(QStringLiteral("apiKey"))) + qWarning() << QStringLiteral("Please prefix 'apiKey' with prefix 'here' (e.g.: 'here.apiKey')"); *error = QGeoServiceProvider::MissingRequiredParameterError; *errorString = QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, MISSED_CREDENTIALS); diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp index d07a93ba..4cfd62f8 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp @@ -84,8 +84,7 @@ QGeoTileFetcherNokia::QGeoTileFetcherNokia(const QVariantMap ¶meters, m_tileSize = qMax(tileSize.width(), tileSize.height()); m_networkManager->setParent(this); - m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString(); - m_token = parameters.value(QStringLiteral("here.token")).toString(); + m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); } QGeoTileFetcherNokia::~QGeoTileFetcherNokia() @@ -127,7 +126,7 @@ QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec, int ppi if (!m_engineNokia) return QString(); - static const QString http("http://"); + static const QString http("https://"); static const QString path("/maptile/2.1/maptile/newest/"); static const QChar slash('/'); @@ -152,12 +151,9 @@ QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec, int ppi static const QString slashpng("/png8"); requestString += slashpng; - if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { // TODO: remove the if - requestString += "?token="; - requestString += m_token; - - requestString += "&app_id="; - requestString += m_applicationId; + if (!m_apiKey.isEmpty()) { // TODO: remove the if + requestString += "?apiKey="; + requestString += m_apiKey; } requestString += "&ppi=" + QString::number(ppi); @@ -235,14 +231,9 @@ QString QGeoTileFetcherNokia::getLanguageString() const // No "lg" param means that we want English. } -QString QGeoTileFetcherNokia::token() const -{ - return m_token; -} - -QString QGeoTileFetcherNokia::applicationId() const +QString QGeoTileFetcherNokia::apiKey() const { - return m_applicationId; + return m_apiKey; } void QGeoTileFetcherNokia::copyrightsFetched() @@ -271,19 +262,14 @@ void QGeoTileFetcherNokia::versionFetched() void QGeoTileFetcherNokia::fetchCopyrightsData() { - QString copyrightUrl = QStringLiteral("http://"); + QString copyrightUrl = QStringLiteral("https://"); copyrightUrl += m_baseUriProvider->getCurrentHost(); copyrightUrl += QStringLiteral("/maptile/2.1/copyright/newest?output=json"); - if (!token().isEmpty()) { - copyrightUrl += QStringLiteral("&token="); - copyrightUrl += token(); - } - - if (!applicationId().isEmpty()) { - copyrightUrl += QStringLiteral("&app_id="); - copyrightUrl += applicationId(); + if (!apiKey().isEmpty()) { + copyrightUrl += QStringLiteral("&apiKey="); + copyrightUrl += apiKey(); } QNetworkRequest netRequest((QUrl(copyrightUrl))); @@ -303,19 +289,14 @@ void QGeoTileFetcherNokia::fetchCopyrightsData() void QGeoTileFetcherNokia::fetchVersionData() { - QString versionUrl = QStringLiteral("http://"); + QString versionUrl = QStringLiteral("https://"); versionUrl += m_baseUriProvider->getCurrentHost(); versionUrl += QStringLiteral("/maptile/2.1/version"); - if (!token().isEmpty()) { - versionUrl += QStringLiteral("?token="); - versionUrl += token(); - } - - if (!applicationId().isEmpty()) { - versionUrl += QStringLiteral("&app_id="); - versionUrl += applicationId(); + if (!apiKey().isEmpty()) { + versionUrl += QStringLiteral("?apiKey="); + versionUrl += apiKey(); } QNetworkRequest netRequest((QUrl(versionUrl))); diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h index 06d1bba9..ee0cb0e9 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h +++ b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h @@ -62,8 +62,7 @@ public: QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec); - QString token() const; - QString applicationId() const; + QString apiKey() const; public Q_SLOTS: void copyrightsFetched(); @@ -82,11 +81,10 @@ private: QGeoNetworkAccessManager *m_networkManager; int m_tileSize; int m_ppi; - QString m_token; QNetworkReply *m_copyrightsReply; QNetworkReply *m_versionReply; - QString m_applicationId; + QString m_apiKey; QGeoUriProvider *m_baseUriProvider; QGeoUriProvider *m_aerialUriProvider; }; diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp index 7be90bb2..6a4f6986 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp @@ -61,7 +61,7 @@ QGeoUriProvider::QGeoUriProvider( QString QGeoUriProvider::getCurrentHost() const { if (m_maxSubdomains) { - QString result(m_firstSubdomain.toLatin1() + QRandomGenerator::global()->bounded(m_maxSubdomains)); + QString result(static_cast(m_firstSubdomain.toLatin1() + QRandomGenerator::global()->bounded(m_maxSubdomains))); result += '.' + m_currentHost; return result; } diff --git a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp index c5c05a2e..4c6e9774 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp @@ -208,8 +208,7 @@ QPlaceManagerEngineNokiaV2::QPlaceManagerEngineNokiaV2( m_locales.append(QLocale()); - m_appId = parameters.value(QStringLiteral("here.app_id")).toString(); - m_appCode = parameters.value(QStringLiteral("here.token")).toString(); + m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); m_theme = parameters.value(IconThemeKey, QString()).toString(); @@ -237,7 +236,7 @@ QPlaceManagerEngineNokiaV2::~QPlaceManagerEngineNokiaV2() {} QPlaceDetailsReply *QPlaceManagerEngineNokiaV2::getPlaceDetails(const QString &placeId) { - QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + QStringLiteral("/places/v1/places/") + placeId); QUrlQuery queryItems; @@ -268,7 +267,7 @@ QPlaceContentReply *QPlaceManagerEngineNokiaV2::getPlaceContent(const QPlaceCont networkReply = sendRequest(u); } else { - QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + QStringLiteral("/places/v1/places/") + request.placeId() + QStringLiteral("/media/")); @@ -410,7 +409,7 @@ QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest networkReply = sendRequest(u); } else if (!query.searchTerm().isEmpty()) { // search term query - QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + QStringLiteral("/places/v1/discover/search")); queryItems.addQueryItem(QStringLiteral("q"), query.searchTerm()); @@ -432,7 +431,7 @@ QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest return reply; } else if (!query.recommendationId().isEmpty()) { - QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + QStringLiteral("/places/v1/places/") + query.recommendationId() + QStringLiteral("/related/recommended")); @@ -443,7 +442,7 @@ QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest networkReply = sendRequest(requestUrl); } else { // category search - QUrl requestUrl(QStringLiteral("http://") + m_uriProvider->getCurrentHost() + + QUrl requestUrl(QStringLiteral("https://") + m_uriProvider->getCurrentHost() + QStringLiteral("/places/v1/discover/explore")); QStringList ids; @@ -498,7 +497,7 @@ QPlaceSearchSuggestionReply *QPlaceManagerEngineNokiaV2::searchSuggestions(const return reply; } - QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + QStringLiteral("/places/v1/suggest")); QUrlQuery queryItems; @@ -633,7 +632,7 @@ QPlaceReply *QPlaceManagerEngineNokiaV2::initializeCategories() for (auto it = m_tempTree.keyBegin(), end = m_tempTree.keyEnd(); it != end; ++it) { if (*it == QString()) continue; - QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + QStringLiteral("/places/v1/categories/places/") + *it); QNetworkReply *networkReply = sendRequest(requestUrl); connect(networkReply, SIGNAL(finished()), this, SLOT(categoryReplyFinished())); @@ -831,8 +830,7 @@ void QPlaceManagerEngineNokiaV2::categoryReplyError() QNetworkReply *QPlaceManagerEngineNokiaV2::sendRequest(const QUrl &url) { QUrlQuery queryItems(url); - queryItems.addQueryItem(QStringLiteral("app_id"), m_appId); - queryItems.addQueryItem(QStringLiteral("app_code"), m_appCode); + queryItems.addQueryItem(QStringLiteral("apiKey"), m_apiKey); QUrl requestUrl = url; requestUrl.setQuery(queryItems); diff --git a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h index cd632958..6784ce4f 100644 --- a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h +++ b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h @@ -122,8 +122,7 @@ private: QPointer m_categoryReply; QHash m_categoryRequests; - QString m_appId; - QString m_appCode; + QString m_apiKey; QString m_localDataPath; QString m_theme; diff --git a/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp b/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp index 8db47beb..030006f5 100644 --- a/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp +++ b/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp @@ -37,11 +37,11 @@ QT_BEGIN_NAMESPACE -const QString ROUTING_HOST = QLatin1String("route.api.here.com"); -const QString GEOCODING_HOST = QLatin1String("geocoder.api.here.com"); -const QString REVERSE_GEOCODING_HOST = QLatin1String("reverse.geocoder.api.here.com"); -const QString PLACES_HOST = QLatin1String("places.api.here.com"); -const QString MAP_TILES_HOST = QLatin1String("1-4.base.maps.api.here.com"); -const QString MAP_TILES_HOST_AERIAL = QLatin1String("1-4.aerial.maps.api.here.com"); +const QString ROUTING_HOST = QLatin1String("route.ls.hereapi.com"); +const QString GEOCODING_HOST = QLatin1String("geocoder.ls.hereapi.com"); +const QString REVERSE_GEOCODING_HOST = QLatin1String("reverse.geocoder.ls.hereapi.com"); +const QString PLACES_HOST = QLatin1String("places.ls.hereapi.com"); +const QString MAP_TILES_HOST = QLatin1String("1-4.base.maps.ls.hereapi.com"); +const QString MAP_TILES_HOST_AERIAL = QLatin1String("1-4.aerial.maps.ls.hereapi.com"); QT_END_NAMESPACE Submodule qtmultimedia 5dc0ed6f..b7c7ff4a: 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 \ Submodule qtnetworkauth b7cb0184..0ca0165f: diff --git a/qtnetworkauth/src/oauth/qabstractoauth.cpp b/qtnetworkauth/src/oauth/qabstractoauth.cpp index 46985d6..09939ea 100644 --- a/qtnetworkauth/src/oauth/qabstractoauth.cpp +++ b/qtnetworkauth/src/oauth/qabstractoauth.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -46,6 +45,9 @@ #include #include +#include +#include + #include Q_DECLARE_METATYPE(QAbstractOAuth::Error) @@ -290,15 +292,19 @@ void QAbstractOAuthPrivate::setStatus(QAbstractOAuth::Status newStatus) } } +static QBasicMutex prngMutex; +Q_GLOBAL_STATIC_WITH_ARGS(std::mt19937, prng, (*QRandomGenerator::system())) + QByteArray QAbstractOAuthPrivate::generateRandomString(quint8 length) { - const char characters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - static std::mt19937 randomEngine(QDateTime::currentDateTime().toMSecsSinceEpoch()); + constexpr char characters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; std::uniform_int_distribution distribution(0, sizeof(characters) - 2); QByteArray data; data.reserve(length); + auto lock = qt_unique_lock(prngMutex); for (quint8 i = 0; i < length; ++i) - data.append(characters[distribution(randomEngine)]); + data.append(characters[distribution(*prng)]); + lock.unlock(); return data; } @@ -614,6 +620,7 @@ void QAbstractOAuth::resourceOwnerAuthorization(const QUrl &url, const QVariantM } /*! + \threadsafe Generates a random string which could be used as state or nonce. The parameter \a length determines the size of the generated string. Submodule qtpim 02efef5e...00000000 (submodule deleted) Submodule qtquick3d 336052ef..4db879b7: diff --git a/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro b/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro index ca5c499ed..174a075bd 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 3e790bf2..8f244d09: 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 a719efd34..768691dac 100644 --- a/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp +++ b/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp @@ -2334,12 +2334,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/qquickdialogbuttonbox.cpp b/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp index e33c5c934..9afabd18a 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/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/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 qtrepotools ee34618d...00000000 (submodule deleted) Submodule qtspeech 5e253d1e..fe7fc4f6: 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 ba3671e4..9c3d4062: diff --git a/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp b/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp index 561e77e9..12e05748 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/qsvghandler.cpp b/qtsvg/src/svg/qsvghandler.cpp index dd9b7164..222b6d89 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: @@ -2578,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); @@ -3048,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()) @@ -3352,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 b89608b5..89c9e4ec 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]); } Submodule qtsystems 434af789...00000000 (submodule deleted) Submodule qttools 62998610..15deb8f2: 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 diff --git a/qttools/src/qdoc/clangcodeparser.cpp b/qttools/src/qdoc/clangcodeparser.cpp index 539a603da..a41b99cec 100644 --- a/qttools/src/qdoc/clangcodeparser.cpp +++ b/qttools/src/qdoc/clangcodeparser.cpp @@ -1395,8 +1395,7 @@ void ClangCodeParser::buildPCH() args_.push_back("-xc++"); CXTranslationUnit tu; QString tmpHeader = pchFileDir_->path() + "/" + module; - QFile tmpHeaderFile(tmpHeader); - if (tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) { + { QFile tmpHeaderFile(tmpHeader); if (tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) { QTextStream out(&tmpHeaderFile); if (header.isEmpty()) { for (auto it = allHeaders_.constKeyValueBegin(); @@ -1421,8 +1420,7 @@ void ClangCodeParser::buildPCH() out << line << "\n"; } } - tmpHeaderFile.close(); - } + } } if (printParsingErrors_ == 0) qCWarning(lcQdoc) << "clang not printing errors; include paths were guessed"; CXErrorCode err = Submodule qtwayland 7558876d..9340737a: diff --git a/qtwayland/src/client/configure.json b/qtwayland/src/client/configure.json index 73f23362..6247f85e 100644 --- a/qtwayland/src/client/configure.json +++ b/qtwayland/src/client/configure.json @@ -167,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", @@ -182,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 966096a8..36609c08 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/qwaylandabstractdecoration.cpp b/qtwayland/src/client/qwaylandabstractdecoration.cpp index b628930d..d15a7f9f 100644 --- a/qtwayland/src/client/qwaylandabstractdecoration.cpp +++ b/qtwayland/src/client/qwaylandabstractdecoration.cpp @@ -122,7 +122,7 @@ const QImage &QWaylandAbstractDecoration::contentImage() if (d->m_isDirty) { // Update the decoration backingstore - const int bufferScale = waylandWindow()->scale(); + const qreal bufferScale = waylandWindow()->scale(); const QSize imageSize = waylandWindow()->surfaceSize() * bufferScale; d->m_decorationContentImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); // Only scale by buffer scale, not QT_SCALE_FACTOR etc. diff --git a/qtwayland/src/client/qwaylandcursor.cpp b/qtwayland/src/client/qwaylandcursor.cpp index e4eca9d4..ba76ba2d 100644 --- a/qtwayland/src/client/qwaylandcursor.cpp +++ b/qtwayland/src/client/qwaylandcursor.cpp @@ -44,6 +44,7 @@ #include "qwaylandshmbackingstore_p.h" #include +#include #include #include @@ -250,7 +251,27 @@ QWaylandCursor::QWaylandCursor(QWaylandDisplay *display) QSharedPointer QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor) { Q_ASSERT(cursor->shape() == Qt::BitmapCursor); - const QImage &img = cursor->pixmap().toImage(); + + const QBitmap mask = cursor->mask(Qt::ReturnByValue); + QImage img; + if (cursor->pixmap().isNull()) + img = cursor->bitmap(Qt::ReturnByValue).toImage(); + else + img = cursor->pixmap().toImage(); + + // convert to supported format if necessary + if (!display->shm()->formatSupported(img.format())) { + if (mask.isNull()) { + img.convertTo(QImage::Format_RGB32); + } else { + // preserve mask + img.convertTo(QImage::Format_ARGB32); + QPixmap pixmap = QPixmap::fromImage(img); + pixmap.setMask(mask); + img = pixmap.toImage(); + } + } + QSharedPointer buffer(new QWaylandShmBuffer(display, img.size(), img.format())); memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes())); return buffer; diff --git a/qtwayland/src/client/qwaylanddatadevice.cpp b/qtwayland/src/client/qwaylanddatadevice.cpp index 4d2459d1..c57f1a49 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(); @@ -129,7 +131,31 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) mimeData->setData("application/x-qt-avoid-empty-placeholder", QByteArray("1")); 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()); + if (!drag->currentDrag()) { + return; + } + // 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; @@ -158,7 +184,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; } @@ -168,7 +194,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(); } } @@ -192,7 +222,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, @@ -203,11 +233,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() @@ -241,10 +267,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()); @@ -252,11 +278,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) @@ -283,14 +305,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)); @@ -303,6 +321,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..0241a1df 100644 --- a/qtwayland/src/client/qwaylanddataoffer.cpp +++ b/qtwayland/src/client/qwaylanddataoffer.cpp @@ -56,6 +56,11 @@ static QString utf8Text() return QStringLiteral("text/plain;charset=utf-8"); } +static QString portalFileTransfer() +{ + return QStringLiteral("application/vnd.portal.filetransfer"); +} + QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer) : QtWayland::wl_data_offer(offer) , m_display(display) @@ -82,6 +87,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 +107,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) { @@ -157,23 +187,26 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T } close(pipefd[0]); - m_data.insert(mimeType, content); + if (mimeType != portalFileTransfer()) + m_data.insert(mimeType, content); + return content; } 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 c86c1416..321170a6 100644 --- a/qtwayland/src/client/qwaylanddatasource.cpp +++ b/qtwayland/src/client/qwaylanddatasource.cpp @@ -105,7 +105,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 520b3165..089c5485 100644 --- a/qtwayland/src/client/qwaylanddatasource_p.h +++ b/qtwayland/src/client/qwaylanddatasource_p.h @@ -75,16 +75,24 @@ public: ~QWaylandDataSource() override; 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: 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 8a6d5db1..737b539d 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); @@ -187,10 +379,26 @@ QWaylandDisplay::~QWaylandDisplay(void) #if QT_CONFIG(cursor) qDeleteAll(mCursorThemes); #endif + + if (m_frameEventQueue) + wl_event_queue_destroy(m_frameEventQueue); + if (mDisplay) wl_display_disconnect(mDisplay); } +// 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() { if (!mScreens.empty() || mPlaceholderScreen) @@ -205,98 +413,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 +494,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 +503,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 +520,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 +580,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 +612,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 +628,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 +694,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 +716,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 +755,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,6 +788,8 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p } // namespace QtWaylandClient +#include "qwaylanddisplay.moc" + QT_END_NAMESPACE #include "moc_qwaylanddisplay_p.cpp" 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 346c438c..278e31f9 100644 --- a/qtwayland/src/client/qwaylandinputdevice.cpp +++ b/qtwayland/src/client/qwaylandinputdevice.cpp @@ -92,11 +92,12 @@ QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p) return; } mRepeatTimer.setInterval(1000 / mRepeatRate); - handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, mRepeatKey.modifiers, - mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers, + Qt::KeyboardModifiers modifiers = this->modifiers(); + handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, modifiers, + mRepeatKey.code, mRepeatKey.nativeVirtualKey, this->mNativeModifiers, mRepeatKey.text, true); - handleKey(mRepeatKey.time, QEvent::KeyPress, mRepeatKey.key, mRepeatKey.modifiers, - mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers, + handleKey(mRepeatKey.time, QEvent::KeyPress, mRepeatKey.key, modifiers, + mRepeatKey.code, mRepeatKey.nativeVirtualKey, this->mNativeModifiers, mRepeatKey.text, true); }); } @@ -310,8 +311,7 @@ void QWaylandInputDevice::Pointer::updateCursor() auto shape = seat()->mCursor.shape; if (shape == Qt::BlankCursor) { - if (mCursor.surface) - mCursor.surface->hide(); + getOrCreateCursorSurface()->hide(); return; } @@ -1271,8 +1271,6 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, mRepeatKey.code = code; mRepeatKey.time = time; mRepeatKey.text = text; - mRepeatKey.modifiers = modifiers; - mRepeatKey.nativeModifiers = mNativeModifiers; mRepeatKey.nativeVirtualKey = sym; mRepeatTimer.setInterval(mRepeatDelay); mRepeatTimer.start(); @@ -1304,14 +1302,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 +1390,7 @@ void QWaylandInputDevice::Touch::touch_cancel() if (touchExt) touchExt->touchCanceled(); + mFocus = nullptr; QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice); } diff --git a/qtwayland/src/client/qwaylandinputdevice_p.h b/qtwayland/src/client/qwaylandinputdevice_p.h index 5795f138..3b92567c 100644 --- a/qtwayland/src/client/qwaylandinputdevice_p.h +++ b/qtwayland/src/client/qwaylandinputdevice_p.h @@ -250,9 +250,7 @@ public: uint32_t code; uint32_t time; QString text; - Qt::KeyboardModifiers modifiers; uint32_t nativeVirtualKey; - uint32_t nativeModifiers; } mRepeatKey; QTimer mRepeatTimer; 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/qwaylandprimaryselectionv1.cpp b/qtwayland/src/client/qwaylandprimaryselectionv1.cpp index 7805dd73..dac532b2 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..145f933b 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 @@ -68,14 +72,16 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, - const QSize &size, QImage::Format format, int scale) + const QSize &size, QImage::Format format, qreal scale) { int stride = size.width() * 4; 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; @@ -108,7 +114,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, QWaylandShm* shm = display->shm(); wl_shm_format wl_format = shm->formatFrom(format); mImage = QImage(data, size.width(), size.height(), stride, format); - mImage.setDevicePixelRatio(qreal(scale)); + mImage.setDevicePixelRatio(scale); mShmPool = wl_shm_create_pool(shm->object(), fd, alloc); init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(), @@ -180,8 +186,6 @@ void QWaylandShmBackingStore::beginPaint(const QRegion ®ion) mPainting = true; ensureSize(); - waylandWindow()->setCanResize(false); - if (mBackBuffer->image()->hasAlphaChannel()) { QPainter p(paintDevice()); p.setCompositionMode(QPainter::CompositionMode_Source); @@ -196,7 +200,6 @@ void QWaylandShmBackingStore::endPaint() mPainting = false; if (mPendingFlush) flush(window(), mPendingRegion, QPoint()); - waylandWindow()->setCanResize(true); } void QWaylandShmBackingStore::ensureSize() @@ -271,7 +274,7 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) void QWaylandShmBackingStore::resize(const QSize &size) { QMargins margins = windowDecorationMargins(); - int scale = waylandWindow()->scale(); + qreal scale = waylandWindow()->scale(); QSize sizeWithMargins = (size + QSize(margins.left()+margins.right(),margins.top()+margins.bottom())) * scale; // We look for a free buffer to draw into. If the buffer is not the last buffer we used, diff --git a/qtwayland/src/client/qwaylandshmbackingstore_p.h b/qtwayland/src/client/qwaylandshmbackingstore_p.h index e01632da..f3fae438 100644 --- a/qtwayland/src/client/qwaylandshmbackingstore_p.h +++ b/qtwayland/src/client/qwaylandshmbackingstore_p.h @@ -71,7 +71,7 @@ class QWaylandWindow; class Q_WAYLAND_CLIENT_EXPORT QWaylandShmBuffer : public QWaylandBuffer { public: QWaylandShmBuffer(QWaylandDisplay *display, - const QSize &size, QImage::Format format, int scale = 1); + const QSize &size, QImage::Format format, qreal scale = 1); ~QWaylandShmBuffer() override; QSize size() const override { return mImage.size(); } int scale() const override { return int(mImage.devicePixelRatio()); } diff --git a/qtwayland/src/client/qwaylandwindow.cpp b/qtwayland/src/client/qwaylandwindow.cpp index d57094a7..7a9bccc1 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) @@ -189,7 +185,7 @@ void QWaylandWindow::initWindow() // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale() // to inform the compositor that high-resolution buffers will be provided. if (mDisplay->compositorVersion() >= 3) - mSurface->set_buffer_scale(scale()); + mSurface->set_buffer_scale(mScale); if (QScreen *s = window()->screen()) setOrientationMask(s->orientationUpdateMask()); @@ -204,6 +200,8 @@ void QWaylandWindow::initWindow() mShellSurface->requestWindowStates(window()->windowStates()); handleContentOrientationChange(window()->contentOrientation()); mFlags = window()->flags(); + + mSurface->commit(); } void QWaylandWindow::initializeWlSurface() @@ -243,6 +241,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const void QWaylandWindow::reset() { + closeChildPopups(); delete mShellSurface; mShellSurface = nullptr; delete mSubSurfaceWindow; @@ -255,17 +254,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 +355,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 +384,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 +409,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 +428,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 +436,6 @@ void QWaylandWindow::setVisible(bool visible) // QWaylandShmBackingStore::beginPaint(). } else { sendExposeEvent(QRect()); - closePopups(this); reset(); } } @@ -487,8 +478,6 @@ void QWaylandWindow::setMask(const QRegion &mask) if (isOpaque()) setOpaqueArea(mMask); } - - mSurface->commit(); } void QWaylandWindow::applyConfigureWhenPossible() @@ -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,16 @@ void QWaylandWindow::damage(const QRect &rect) if (mSurface == nullptr) return; - mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + const qreal s = scale(); + if (mDisplay->compositorVersion() >= 4) { + const QRect bufferRect = + QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()) + .toAlignedRect(); + mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(), + bufferRect.height()); + } else { + mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + } } void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage) @@ -619,8 +617,19 @@ 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 qreal s = scale(); + for (const QRect &rect : damage) { + const QRect bufferRect = + QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()) + .toAlignedRect(); + mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(), + bufferRect.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 +644,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"; @@ -772,8 +792,6 @@ void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orient Q_UNREACHABLE(); } mSurface->set_buffer_transform(transform); - // set_buffer_transform is double buffered, we need to commit. - mSurface->commit(); } void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask) @@ -1032,8 +1050,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) { @@ -1087,14 +1114,14 @@ bool QWaylandWindow::isActive() const return mDisplay->isWindowActivated(this); } -int QWaylandWindow::scale() const +qreal QWaylandWindow::scale() const { - return mScale; + return devicePixelRatio(); } qreal QWaylandWindow::devicePixelRatio() const { - return mScale; + return qreal(mScale); } bool QWaylandWindow::setMouseGrabEnabled(bool grab) @@ -1108,10 +1135,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; } @@ -1153,19 +1188,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() @@ -1174,8 +1214,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 @@ -1188,7 +1231,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); } @@ -1199,19 +1247,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); @@ -1221,6 +1268,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); @@ -1281,6 +1330,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..741f9e5c 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,13 +151,14 @@ 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; void setMask(const QRegion ®ion) override; - int scale() const; + qreal scale() const; qreal devicePixelRatio() const override; void requestActivateWindow() 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 c5b0f03e..031e4cc3 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", @@ -193,7 +218,8 @@ "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;", "return 0;" ] - } + }, + "use": "wayland-client" } }, diff --git a/qtwayland/src/compositor/wayland_wrapper/qwldatadevice.cpp b/qtwayland/src/compositor/wayland_wrapper/qwldatadevice.cpp index a3a795f9..f301678e 100644 --- a/qtwayland/src/compositor/wayland_wrapper/qwldatadevice.cpp +++ b/qtwayland/src/compositor/wayland_wrapper/qwldatadevice.cpp @@ -76,6 +76,9 @@ void DataDevice::sourceDestroyed(DataSource *source) { if (m_selectionSource == source) m_selectionSource = nullptr; + + if (m_dragDataSource == source) + m_dragDataSource = nullptr; } #if QT_CONFIG(draganddrop) @@ -105,9 +108,11 @@ void DataDevice::setDragFocus(QWaylandSurface *focus, const QPointF &localPositi if (m_dragDataSource && !offer) return; - send_enter(resource->handle, serial, focus->resource(), - wl_fixed_from_double(localPosition.x()), wl_fixed_from_double(localPosition.y()), - offer->resource()->handle); + if (offer) { + send_enter(resource->handle, serial, focus->resource(), + wl_fixed_from_double(localPosition.x()), wl_fixed_from_double(localPosition.y()), + offer->resource()->handle); + } m_dragFocus = focus; m_dragFocusResource = resource; @@ -139,7 +144,7 @@ void DataDevice::drop() if (m_dragFocusResource) { send_drop(m_dragFocusResource->handle); setDragFocus(nullptr, QPoint()); - } else { + } else if (m_dragDataSource) { m_dragDataSource->cancel(); } m_dragOrigin = nullptr; @@ -155,6 +160,8 @@ void DataDevice::data_device_start_drag(Resource *resource, struct ::wl_resource { m_dragClient = resource->client(); m_dragDataSource = source ? DataSource::fromResource(source) : nullptr; + if (m_dragDataSource) + m_dragDataSource->setDevice(this); m_dragOrigin = QWaylandSurface::fromResource(origin); QWaylandDrag *drag = m_seat->drag(); setDragIcon(icon ? QWaylandSurface::fromResource(icon) : nullptr); diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp index e00c28c3..95e8c666 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,21 +116,25 @@ void QWaylandEglWindow::updateSurface(bool create) } mOffset = QPoint(); } else { + QReadLocker locker(&mSurfaceLock); if (m_waylandEglWindow) { - int current_width, current_height; + int current_width = 0; + int current_height = 0; static bool disableResizeCheck = qgetenv("QT_WAYLAND_DISABLE_RESIZECHECK").toInt(); 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 2fccbcea..ad1e5ee9 100644 --- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h +++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h @@ -85,6 +85,7 @@ private: mutable QOpenGLFramebufferObject *m_contentFBO = nullptr; QSurfaceFormat m_format; + QSize m_requestedSize; }; } diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp index c1f45fa6..bbc63444 100644 --- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp @@ -195,7 +195,7 @@ public: QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context()); QSize surfaceSize = window->surfaceSize(); - int scale = window->scale() ; + qreal scale = window->scale() ; glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale); //Draw Decoration 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 49e9d953..9c6cbb81 100644 --- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -67,11 +68,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 +81,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 +100,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 +196,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 +214,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 @@ -228,8 +230,10 @@ QWaylandXdgSurface::Popup::~Popup() leave = m_xdgSurface->window()->window(); QWindowSystemInterface::handleLeaveEvent(leave); - if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos())) - QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos()); + if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos())) { + const auto pos = m_xdgSurface->window()->display()->waylandCursor()->pos(); + QWindowSystemInterface::handleEnterEvent(enter, enter->handle()->mapFromGlobal(pos), pos); + } } } @@ -267,6 +271,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s m_toplevel->set_parent(parentXdgSurface->m_toplevel->object()); } } + setSizeHints(); } QWaylandXdgSurface::~QWaylandXdgSurface() @@ -365,9 +370,6 @@ bool QWaylandXdgSurface::wantsDecorations() const void QWaylandXdgSurface::propagateSizeHints() { setSizeHints(); - - if (m_toplevel && m_window) - m_window->commit(); } void QWaylandXdgSurface::setWindowGeometry(const QRect &rect) @@ -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 @@ -426,8 +426,9 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) 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); - m_popup = new Popup(this, parentXdgSurface, positioner); + m_popup = new Popup(this, parent, positioner); positioner->destroy(); + delete positioner; } @@ -464,8 +465,10 @@ void QWaylandXdgSurface::setGrabPopup(QWaylandWindow *parent, QWaylandInputDevic if (m_popup && m_popup->m_xdgSurface && m_popup->m_xdgSurface->window()) enter = m_popup->m_xdgSurface->window()->window(); - if (enter) - QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos()); + if (enter) { + const auto pos = m_popup->m_xdgSurface->window()->display()->waylandCursor()->pos(); + QWindowSystemInterface::handleEnterEvent(enter, enter->handle()->mapFromGlobal(pos), pos); + } } void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial) 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 c5266ab3..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; } 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 20f762e0..2a0cad1d 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 1c23728b..f2181fd6 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,15 +598,17 @@ 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)); window.setMaximumSize(QSize(500, 400)); + window.update(); QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(500, 400)); window.setMinimumSize(QSize(50, 40)); + window.update(); QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(50, 40)); } Submodule qtwebchannel b292c653...00000000 (submodule deleted) Submodule qtwebengine f328054d...00000000 (submodule deleted) Submodule qtwebsockets e1f9a1a1..a0c1c335: 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(); } /*!