Submitted By: Pierre Labastie Date: 2023-01-05 Initial Package Version: 5.15.8 Upstream Status: Applied (according to KDE) Origin: KDE patch set Description: Patch set maintained by the KDE folks see https://dot.kde.org/2021/04/06/announcing-kdes-qt-5-patch-collection Patch generated using the procedure in https://wiki.linuxfromscratch.org/blfs/ticket/17476#comment:3 Submodule qtbase 4ee4fc18..9cf586d6: 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/features/mac/sdk.mk b/qtbase/mkspecs/features/mac/sdk.mk index f30b5830b4..a32ceacb6c 100644 --- a/qtbase/mkspecs/features/mac/sdk.mk +++ b/qtbase/mkspecs/features/mac/sdk.mk @@ -1,6 +1,6 @@ ifeq ($(QT_MAC_SDK_NO_VERSION_CHECK),) - CHECK_SDK_COMMAND = /usr/bin/xcrun --sdk $(EXPORT_QMAKE_MAC_SDK) -show-sdk-version 2>&1 + CHECK_SDK_COMMAND = /usr/bin/xcrun --sdk $(EXPORT_QMAKE_MAC_SDK) -show-sdk-version 2>/dev/null CURRENT_MAC_SDK_VERSION := $(shell DEVELOPER_DIR=$(EXPORT_QMAKE_XCODE_DEVELOPER_PATH) $(CHECK_SDK_COMMAND)) ifneq ($(CURRENT_MAC_SDK_VERSION),$(EXPORT_QMAKE_MAC_SDK_VERSION)) # We don't want to complain about out of date SDK unless the target needs to be remade. diff --git a/qtbase/mkspecs/linux-clang/qplatformdefs.h b/qtbase/mkspecs/linux-clang/qplatformdefs.h index a818d973f0..c1ab72fbc6 100644 --- a/qtbase/mkspecs/linux-clang/qplatformdefs.h +++ b/qtbase/mkspecs/linux-clang/qplatformdefs.h @@ -79,14 +79,6 @@ #define QT_USE_XOPEN_LFS_EXTENSIONS #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/mkspecs/linux-g++/qplatformdefs.h b/qtbase/mkspecs/linux-g++/qplatformdefs.h index 13523f0702..4d2750d9ec 100644 --- a/qtbase/mkspecs/linux-g++/qplatformdefs.h +++ b/qtbase/mkspecs/linux-g++/qplatformdefs.h @@ -79,14 +79,6 @@ #define QT_USE_XOPEN_LFS_EXTENSIONS #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ < 2) -#define QT_SOCKLEN_T int -#else -#define QT_SOCKLEN_T socklen_t -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/mkspecs/linux-llvm/qplatformdefs.h b/qtbase/mkspecs/linux-llvm/qplatformdefs.h index dc750ab1ef..d3cc39b47f 100644 --- a/qtbase/mkspecs/linux-llvm/qplatformdefs.h +++ b/qtbase/mkspecs/linux-llvm/qplatformdefs.h @@ -80,14 +80,6 @@ #define QT_USE_XOPEN_LFS_EXTENSIONS #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h b/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h index 4c4e53da2a..83baffb3e3 100644 --- a/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h +++ b/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h @@ -85,16 +85,9 @@ #include "../common/posix/qplatformdefs.h" #undef QT_OPEN_LARGEFILE -#undef QT_SOCKLEN_T #define QT_OPEN_LARGEFILE 0 -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #ifndef SIOCGIFBRDADDR # define SIOCGIFBRDADDR 0x8919 #endif diff --git a/qtbase/mkspecs/lynxos-g++/qplatformdefs.h b/qtbase/mkspecs/lynxos-g++/qplatformdefs.h index 4339ea2b23..6007af0055 100644 --- a/qtbase/mkspecs/lynxos-g++/qplatformdefs.h +++ b/qtbase/mkspecs/lynxos-g++/qplatformdefs.h @@ -72,14 +72,6 @@ #include "../common/posix/qplatformdefs.h" -#undef QT_SOCKLEN_T - -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) #define QT_SNPRINTF ::snprintf #define QT_VSNPRINTF ::vsnprintf diff --git a/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp b/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp index c3c184258f..32af3f8f29 100644 --- a/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp +++ b/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp @@ -10,6 +10,7 @@ #include "libANGLE/HandleAllocator.h" #include +#include #include "common/debug.h" diff --git a/qtbase/src/3rdparty/forkfd/forkfd_linux.c b/qtbase/src/3rdparty/forkfd/forkfd_linux.c index ffe0e9a5e2..b1f5408d11 100644 --- a/qtbase/src/3rdparty/forkfd/forkfd_linux.c +++ b/qtbase/src/3rdparty/forkfd/forkfd_linux.c @@ -82,7 +82,8 @@ static int sys_clone(unsigned long cloneflags, int *ptid) return syscall(__NR_clone, cloneflags, child_stack, stack_size, ptid, newtls, ctid); #elif defined(__arc__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ defined(__nds32__) || defined(__hppa__) || defined(__powerpc__) || defined(__i386__) || \ - defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv) + defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv) || \ + defined(__loongarch__) /* ctid and newtls are inverted on CONFIG_CLONE_BACKWARDS architectures, * but since both values are 0, there's no harm. */ return syscall(__NR_clone, cloneflags, child_stack, ptid, ctid, newtls); diff --git a/qtbase/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/qtbase/src/android/jar/src/org/qtproject/qt5/android/QtNative.java index 5792b5b2f5..2f74750fcf 100644 --- a/qtbase/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/qtbase/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -116,6 +116,7 @@ public class QtNative public static QtThread m_qtThread = new QtThread(); private static HashMap m_cachedUris = new HashMap(); private static ArrayList m_knownDirs = new ArrayList(); + private static final String NoPermissionErrorMessage = "No permissions to open Uri"; private static final Runnable runPendingCppRunnablesRunnable = new Runnable() { @Override @@ -193,11 +194,10 @@ public class QtNative return iterUri; } - // Android 6 and earlier could still manage to open the file so we can return the - // parsed uri here - if (Build.VERSION.SDK_INT < 24) - return parsedUri; - return null; + // if we only have transient permissions on uri all the above will fail, + // but we will be able to read the file anyway, so continue with uri here anyway + // and check for SecurityExceptions later + return parsedUri; } catch (SecurityException e) { e.printStackTrace(); return null; @@ -244,7 +244,7 @@ public class QtNative int error = -1; if (uri == null) { - Log.e(QtTAG, "openFdForContentUrl(): No permissions to open Uri"); + Log.e(QtTAG, "openFdForContentUrl(): " + NoPermissionErrorMessage); return error; } @@ -254,12 +254,13 @@ public class QtNative return fdDesc.detachFd(); } catch (FileNotFoundException e) { e.printStackTrace(); - return error; } catch (IllegalArgumentException e) { Log.e(QtTAG, "openFdForContentUrl(): Invalid Uri"); e.printStackTrace(); - return error; + } catch (SecurityException e) { + Log.e(QtTAG, NoPermissionErrorMessage); } + return error; } public static long getSize(Context context, String contentUrl) @@ -270,7 +271,7 @@ public class QtNative uri = getUriWithValidPermission(context, contentUrl, "r"); if (uri == null) { - Log.e(QtTAG, "getSize(): No permissions to open Uri"); + Log.e(QtTAG, NoPermissionErrorMessage); return size; } else if (!m_cachedUris.containsKey(contentUrl)) { m_cachedUris.put(contentUrl, uri); @@ -288,12 +289,13 @@ public class QtNative } catch (IllegalArgumentException e) { Log.e(QtTAG, "getSize(): Invalid Uri"); e.printStackTrace(); - return size; } catch (UnsupportedOperationException e) { Log.e(QtTAG, "getSize(): Unsupported operation for given Uri"); e.printStackTrace(); - return size; + } catch (SecurityException e) { + Log.e(QtTAG, NoPermissionErrorMessage); } + return size; } public static boolean checkFileExists(Context context, String contentUrl) @@ -303,7 +305,7 @@ public class QtNative if (uri == null) uri = getUriWithValidPermission(context, contentUrl, "r"); if (uri == null) { - Log.e(QtTAG, "checkFileExists(): No permissions to open Uri"); + Log.e(QtTAG, NoPermissionErrorMessage); return exists; } else { if (!m_cachedUris.containsKey(contentUrl)) @@ -321,12 +323,13 @@ public class QtNative } catch (IllegalArgumentException e) { Log.e(QtTAG, "checkFileExists(): Invalid Uri"); e.printStackTrace(); - return exists; } catch (UnsupportedOperationException e) { Log.e(QtTAG, "checkFileExists(): Unsupported operation for given Uri"); e.printStackTrace(); - return false; + } catch (SecurityException e) { + Log.e(QtTAG, NoPermissionErrorMessage); } + return exists; } public static boolean checkIfWritable(Context context, String contentUrl) @@ -344,7 +347,7 @@ public class QtNative uri = getUriWithValidPermission(context, contentUrl, "r"); } if (uri == null) { - Log.e(QtTAG, "isDir(): No permissions to open Uri"); + Log.e(QtTAG, NoPermissionErrorMessage); return isDir; } else { if (!m_cachedUris.containsKey(contentUrl)) @@ -374,12 +377,13 @@ public class QtNative } catch (IllegalArgumentException e) { Log.e(QtTAG, "checkIfDir(): Invalid Uri"); e.printStackTrace(); - return false; } catch (UnsupportedOperationException e) { Log.e(QtTAG, "checkIfDir(): Unsupported operation for given Uri"); e.printStackTrace(); - return false; + } catch (SecurityException e) { + Log.e(QtTAG, NoPermissionErrorMessage); } + return false; } public static String[] listContentsFromTreeUri(Context context, String contentUrl) { diff --git a/qtbase/src/concurrent/qtconcurrentreducekernel.h b/qtbase/src/concurrent/qtconcurrentreducekernel.h index 8f9a938952..a98dedef2e 100644 --- a/qtbase/src/concurrent/qtconcurrentreducekernel.h +++ b/qtbase/src/concurrent/qtconcurrentreducekernel.h @@ -212,11 +212,13 @@ public: inline bool shouldThrottle() { + std::lock_guard locker(mutex); return (resultsMapSize > (ReduceQueueThrottleLimit * threadCount)); } inline bool shouldStartThread() { + std::lock_guard locker(mutex); return (resultsMapSize <= (ReduceQueueStartLimit * threadCount)); } }; diff --git a/qtbase/src/concurrent/qtconcurrentthreadengine.cpp b/qtbase/src/concurrent/qtconcurrentthreadengine.cpp index ea6ce3ac42..7f91a2ba68 100644 --- a/qtbase/src/concurrent/qtconcurrentthreadengine.cpp +++ b/qtbase/src/concurrent/qtconcurrentthreadengine.cpp @@ -176,6 +176,39 @@ void ThreadEngineBase::startSingleThreaded() finish(); } +void ThreadEngineBase::startBlocking() +{ + start(); + barrier.acquire(); + startThreads(); + + bool throttled = false; +#ifndef QT_NO_EXCEPTIONS + try { +#endif + while (threadFunction() == ThrottleThread) { + if (threadThrottleExit()) { + throttled = true; + break; + } + } +#ifndef QT_NO_EXCEPTIONS + } catch (QException &e) { + handleException(e); + } catch (...) { + handleException(QUnhandledException()); + } +#endif + + if (throttled == false) { + barrier.release(); + } + + barrier.wait(); + finish(); + exceptionStore.throwPossibleException(); +} + void ThreadEngineBase::startThread() { startThreadInternal(); diff --git a/qtbase/src/concurrent/qtconcurrentthreadengine.h b/qtbase/src/concurrent/qtconcurrentthreadengine.h index 7c30cebdbc..a4c8548cc4 100644 --- a/qtbase/src/concurrent/qtconcurrentthreadengine.h +++ b/qtbase/src/concurrent/qtconcurrentthreadengine.h @@ -91,6 +91,7 @@ public: ThreadEngineBase(); virtual ~ThreadEngineBase(); void startSingleThreaded(); + void startBlocking(); void startThread(); bool isCanceled(); void waitForResume(); @@ -143,6 +144,15 @@ public: return result(); } + // Runs the user algorithm using multiple threads. + // This function blocks until the algorithm is finished, + // and then returns the result. + T *startBlocking() + { + ThreadEngineBase::startBlocking(); + return result(); + } + // Runs the user algorithm using multiple threads. // Does not block, returns a future. QFuture startAsynchronously() @@ -223,6 +233,13 @@ class ThreadEngineStarter : public ThreadEngineStarterBase public: ThreadEngineStarter(TypedThreadEngine *eng) : Base(eng) { } + + T startBlocking() + { + T t = *this->threadEngine->startBlocking(); + delete this->threadEngine; + return t; + } }; // Full template specialization where T is void. @@ -232,6 +249,12 @@ class ThreadEngineStarter : public ThreadEngineStarterBase public: ThreadEngineStarter(ThreadEngine *_threadEngine) : ThreadEngineStarterBase(_threadEngine) {} + + void startBlocking() + { + this->threadEngine->startBlocking(); + delete this->threadEngine; + } }; //! [qtconcurrentthreadengine-1] diff --git a/qtbase/src/corelib/animation/qvariantanimation.cpp b/qtbase/src/corelib/animation/qvariantanimation.cpp index 98b02f0202..eac1524107 100644 --- a/qtbase/src/corelib/animation/qvariantanimation.cpp +++ b/qtbase/src/corelib/animation/qvariantanimation.cpp @@ -276,7 +276,9 @@ void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress) const qreal startProgress = currentInterval.start.first; const qreal endProgress = currentInterval.end.first; - const qreal localProgress = (progress - startProgress) / (endProgress - startProgress); + const qreal localProgress = + qIsNull(progress - startProgress) ? 0.0 // avoid 0/0 below + /* else */ : (progress - startProgress) / (endProgress - startProgress); QVariant ret = q->interpolated(currentInterval.start.second, currentInterval.end.second, diff --git a/qtbase/src/corelib/global/qnamespace.h b/qtbase/src/corelib/global/qnamespace.h index ad4150b317..bf19b1627b 100644 --- a/qtbase/src/corelib/global/qnamespace.h +++ b/qtbase/src/corelib/global/qnamespace.h @@ -1864,7 +1864,7 @@ public: QT_Q_ENUM(TimerType) QT_Q_ENUM(ScrollPhase) QT_Q_ENUM(MouseEventSource) - QT_Q_FLAG(MouseEventFlag) + QT_Q_FLAG(MouseEventFlags) QT_Q_ENUM(ChecksumType) QT_Q_ENUM(HighDpiScaleFactorRoundingPolicy) QT_Q_ENUM(TabFocusBehavior) diff --git a/qtbase/src/corelib/global/qrandom.cpp b/qtbase/src/corelib/global/qrandom.cpp index 10672c1f92..25f87c7e6a 100644 --- a/qtbase/src/corelib/global/qrandom.cpp +++ b/qtbase/src/corelib/global/qrandom.cpp @@ -383,7 +383,6 @@ struct QRandomGenerator::SystemAndGlobalGenerators constexpr SystemAndGlobalGenerators g = {}; Q_UNUSED(g); - Q_STATIC_ASSERT(std::is_literal_type::value); #endif } diff --git a/qtbase/src/corelib/io/qbuffer.cpp b/qtbase/src/corelib/io/qbuffer.cpp index 595fcd2724..032c0ddd4b 100644 --- a/qtbase/src/corelib/io/qbuffer.cpp +++ b/qtbase/src/corelib/io/qbuffer.cpp @@ -41,6 +41,8 @@ #include #include "private/qiodevice_p.h" +#include + QT_BEGIN_NAMESPACE /** QBufferPrivate **/ @@ -366,7 +368,9 @@ qint64 QBuffer::size() const bool QBuffer::seek(qint64 pos) { Q_D(QBuffer); - if (pos > d->buf->size() && isWritable()) { + const auto oldBufSize = d->buf->size(); + constexpr qint64 MaxSeekPos = (std::numeric_limits::max)(); + if (pos <= MaxSeekPos && pos > oldBufSize && isWritable()) { if (seek(d->buf->size())) { const qint64 gapSize = pos - d->buf->size(); if (write(QByteArray(gapSize, 0)) != gapSize) { @@ -377,7 +381,7 @@ bool QBuffer::seek(qint64 pos) return false; } } else if (pos > d->buf->size() || pos < 0) { - qWarning("QBuffer::seek: Invalid pos: %d", int(pos)); + qWarning("QBuffer::seek: Invalid pos: %lld", pos); return false; } return QIODevice::seek(pos); diff --git a/qtbase/src/corelib/io/qfilesystemengine_win.cpp b/qtbase/src/corelib/io/qfilesystemengine_win.cpp index 81d3a71986..c86ed1f9b0 100644 --- a/qtbase/src/corelib/io/qfilesystemengine_win.cpp +++ b/qtbase/src/corelib/io/qfilesystemengine_win.cpp @@ -664,14 +664,14 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath()); } -#if defined(Q_CC_MINGW) && WINVER < 0x0602 // Windows 8 onwards +#if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8 // Windows 8 onwards typedef struct _FILE_ID_INFO { ULONGLONG VolumeSerialNumber; FILE_ID_128 FileId; } FILE_ID_INFO, *PFILE_ID_INFO; -#endif // if defined (Q_CC_MINGW) && WINVER < 0x0602 +#endif // if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8 // File ID for Windows up to version 7 and FAT32 drives static inline QByteArray fileId(HANDLE handle) diff --git a/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp b/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp index 94d9d06bcb..27e0b13b0b 100644 --- a/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp +++ b/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp @@ -366,7 +366,9 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() // qDebug("QInotifyFileSystemWatcherEngine::readFromInotify"); int buffSize = 0; - ioctl(inotifyFd, FIONREAD, (char *) &buffSize); + if (ioctl(inotifyFd, FIONREAD, (char *) &buffSize) == -1 || buffSize == 0) + return; + QVarLengthArray buffer(buffSize); buffSize = read(inotifyFd, buffer.data(), buffSize); char *at = buffer.data(); diff --git a/qtbase/src/corelib/io/qlockfile_win.cpp b/qtbase/src/corelib/io/qlockfile_win.cpp index 277f8d4230..38ecef5550 100644 --- a/qtbase/src/corelib/io/qlockfile_win.cpp +++ b/qtbase/src/corelib/io/qlockfile_win.cpp @@ -48,6 +48,8 @@ #include "QtCore/qdebug.h" #include "QtCore/qthread.h" +#include "private/qsystemlibrary_p.h" + QT_BEGIN_NAMESPACE static inline bool fileExists(const wchar_t *fileName) @@ -150,7 +152,7 @@ QString QLockFilePrivate::processNameByPid(qint64 pid) #if !defined(Q_OS_WINRT) typedef DWORD (WINAPI *GetModuleFileNameExFunc)(HANDLE, HMODULE, LPTSTR, DWORD); - HMODULE hPsapi = LoadLibraryA("psapi"); + HMODULE hPsapi = QSystemLibrary::load(L"psapi"); if (!hPsapi) return QString(); GetModuleFileNameExFunc qGetModuleFileNameEx = reinterpret_cast( diff --git a/qtbase/src/corelib/io/qprocess_unix.cpp b/qtbase/src/corelib/io/qprocess_unix.cpp index 50390e57f5..15c8f30745 100644 --- a/qtbase/src/corelib/io/qprocess_unix.cpp +++ b/qtbase/src/corelib/io/qprocess_unix.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2022 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -422,14 +422,15 @@ void QProcessPrivate::startProcess() // Add the program name to the argument list. argv[0] = nullptr; if (!program.contains(QLatin1Char('/'))) { + // findExecutable() returns its argument if it's an absolute path, + // otherwise it searches $PATH; returns empty if not found (we handle + // that case much later) const QString &exeFilePath = QStandardPaths::findExecutable(program); - if (!exeFilePath.isEmpty()) { - const QByteArray &tmp = QFile::encodeName(exeFilePath); - argv[0] = ::strdup(tmp.constData()); - } - } - if (!argv[0]) + const QByteArray &tmp = QFile::encodeName(exeFilePath); + argv[0] = ::strdup(tmp.constData()); + } else { argv[0] = ::strdup(encodedProgramName.constData()); + } // Add every argument to the list for (int i = 0; i < arguments.count(); ++i) @@ -983,15 +984,16 @@ bool QProcessPrivate::startDetached(qint64 *pid) envp = _q_dupEnvironment(environment.d.constData()->vars, &envc); } - QByteArray tmp; if (!program.contains(QLatin1Char('/'))) { + // findExecutable() returns its argument if it's an absolute path, + // otherwise it searches $PATH; returns empty if not found (we handle + // that case much later) const QString &exeFilePath = QStandardPaths::findExecutable(program); - if (!exeFilePath.isEmpty()) - tmp = QFile::encodeName(exeFilePath); + const QByteArray &tmp = QFile::encodeName(exeFilePath); + argv[0] = ::strdup(tmp.constData()); + } else { + argv[0] = ::strdup(QFile::encodeName(program)); } - if (tmp.isEmpty()) - tmp = QFile::encodeName(program); - argv[0] = tmp.data(); if (envp) qt_safe_execve(argv[0], argv, envp); diff --git a/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp b/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp index 3a79c502af..997a634e76 100644 --- a/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp +++ b/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp @@ -3196,9 +3196,8 @@ bool QAbstractItemModel::beginMoveColumns(const QModelIndex &sourceParent, int s destinationChange.needsAdjust = destinationParent.isValid() && destinationParent.row() >= sourceLast && destinationParent.parent() == sourceParent; d->changes.push(destinationChange); - d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal); - emit columnsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, QPrivateSignal()); + d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal); return true; } @@ -3231,7 +3230,6 @@ void QAbstractItemModel::endMoveColumns() adjustedSource = createIndex(adjustedSource.row(), adjustedSource.column() + numMoved, adjustedSource.internalPointer()); d->itemsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first, Qt::Horizontal); - emit columnsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first, QPrivateSignal()); } diff --git a/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp index dc6379d9fb..c5e287ee84 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); @@ -3123,8 +3124,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(); @@ -3133,9 +3135,10 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & } return false; } - QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); - if (!source_index.isValid()) // the column may not exist + + if (d->filter_column >= column_count) // the column may not exist return true; + QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); QString key = d->model->data(source_index, d->filter_role).toString(); return d->filter_data.hasMatch(key); } diff --git a/qtbase/src/corelib/kernel/qcoreapplication.cpp b/qtbase/src/corelib/kernel/qcoreapplication.cpp index ca2864df83..132b2eae52 100644 --- a/qtbase/src/corelib/kernel/qcoreapplication.cpp +++ b/qtbase/src/corelib/kernel/qcoreapplication.cpp @@ -1157,7 +1157,7 @@ static bool doNotify(QObject *receiver, QEvent *event) bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event) { // We can't access the application event filters outside of the main thread (race conditions) - Q_ASSERT(receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()); + Q_ASSERT(receiver->d_func()->threadData.loadAcquire()->thread.loadRelaxed() == mainThread()); if (extraData) { // application event filters are only called for objects in the GUI thread diff --git a/qtbase/src/corelib/kernel/qobject.cpp b/qtbase/src/corelib/kernel/qobject.cpp index 1eb79138d9..36cd780ee1 100644 --- a/qtbase/src/corelib/kernel/qobject.cpp +++ b/qtbase/src/corelib/kernel/qobject.cpp @@ -1010,7 +1010,7 @@ QObject::~QObject() emit destroyed(this); } - if (d->declarativeData) { + if (!d->isDeletingChildren && d->declarativeData) { if (static_cast(d->declarativeData)->ownedByQml1) { if (QAbstractDeclarativeData::destroyed_qml1) QAbstractDeclarativeData::destroyed_qml1(d->declarativeData, this); @@ -1580,7 +1580,7 @@ void QObject::moveToThread(QThread *targetThread) QThreadData *currentData = QThreadData::current(); QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr; - QThreadData *thisThreadData = d->threadData.loadRelaxed(); + QThreadData *thisThreadData = d->threadData.loadAcquire(); if (!thisThreadData->thread.loadAcquire() && currentData == targetData) { // one exception to the rule: we allow moving objects with no thread affinity to the current thread currentData = d->threadData; @@ -2620,7 +2620,7 @@ int QObject::receivers(const char *signal) const if (!d->isSignalConnected(signal_index)) return receivers; - if (d->declarativeData && QAbstractDeclarativeData::receivers) { + if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::receivers) { receivers += QAbstractDeclarativeData::receivers(d->declarativeData, this, signal_index); } diff --git a/qtbase/src/corelib/kernel/qobject_p.h b/qtbase/src/corelib/kernel/qobject_p.h index 0b827a52ca..13ffb88999 100644 --- a/qtbase/src/corelib/kernel/qobject_p.h +++ b/qtbase/src/corelib/kernel/qobject_p.h @@ -444,7 +444,7 @@ inline void QObjectPrivate::checkForIncompatibleLibraryVersion(int version) cons inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const { - return declarativeData && QAbstractDeclarativeData::isSignalConnected + return !isDeletingChildren && declarativeData && QAbstractDeclarativeData::isSignalConnected && QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index); } diff --git a/qtbase/src/corelib/kernel/qtranslator.cpp b/qtbase/src/corelib/kernel/qtranslator.cpp index 2812ffb7ea..bc0177c5ec 100644 --- a/qtbase/src/corelib/kernel/qtranslator.cpp +++ b/qtbase/src/corelib/kernel/qtranslator.cpp @@ -902,7 +902,7 @@ static QString getMessage(const uchar *m, const uchar *end, const char *context, goto end; case Tag_Translation: { int len = read32(m); - if (len % 1) + if (len & 1) return QString(); m += 4; if (!numerus--) { diff --git a/qtbase/src/corelib/mimetypes/qmimedatabase.cpp b/qtbase/src/corelib/mimetypes/qmimedatabase.cpp index 9de22cef33..ff868a3268 100644 --- a/qtbase/src/corelib/mimetypes/qmimedatabase.cpp +++ b/qtbase/src/corelib/mimetypes/qmimedatabase.cpp @@ -389,20 +389,23 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa // Disambiguate conflicting extensions (if magic matching found something) if (candidateByData.isValid() && magicAccuracy > 0) { const QString sniffedMime = candidateByData.name(); - // If the sniffedMime matches a glob match, use it + // If the sniffedMime matches a highest-weight glob match, use it if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime)) { *accuracyPtr = 100; return candidateByData; } - for (const QString &m : qAsConst(candidatesByName.m_matchingMimeTypes)) { + for (const QString &m : qAsConst(candidatesByName.m_allMatchingMimeTypes)) { if (inherits(m, sniffedMime)) { // We have magic + pattern pointing to this, so it's a pretty good match *accuracyPtr = 100; return mimeTypeForName(m); } } - *accuracyPtr = magicAccuracy; - return candidateByData; + if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) { + // No glob, use magic + *accuracyPtr = magicAccuracy; + return candidateByData; + } } } diff --git a/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp b/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp index a81112d227..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; } } @@ -94,7 +97,7 @@ QMimeGlobPattern::PatternType QMimeGlobPattern::detectPatternType(const QString if (!patternLength) return OtherPattern; - const bool starCount = pattern.count(QLatin1Char('*')) == 1; + const int starCount = pattern.count(QLatin1Char('*')); const bool hasSquareBracket = pattern.indexOf(QLatin1Char('[')) != -1; const bool hasQuestionMark = pattern.indexOf(QLatin1Char('?')) != -1; @@ -106,10 +109,10 @@ QMimeGlobPattern::PatternType QMimeGlobPattern::detectPatternType(const QString // Patterns like "README*" (well this is currently the only one like that...) if (pattern.at(patternLength - 1) == QLatin1Char('*')) return PrefixPattern; - } - // Names without any wildcards like "README" - if (starCount == 0) + } else if (starCount == 0) { + // Names without any wildcards like "README" return LiteralPattern; + } } if (pattern == QLatin1String("[0-9][0-9][0-9].vdr")) diff --git a/qtbase/src/corelib/mimetypes/qmimeprovider.cpp b/qtbase/src/corelib/mimetypes/qmimeprovider.cpp index 258dddf8cb..4642d0f2d0 100644 --- a/qtbase/src/corelib/mimetypes/qmimeprovider.cpp +++ b/qtbase/src/corelib/mimetypes/qmimeprovider.cpp @@ -244,15 +244,18 @@ void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobM const QString lowerFileName = fileName.toLower(); // Check literals (e.g. "Makefile") matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), fileName); - // Check complex globs (e.g. "callgrind.out[0-9]*") - matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); // Check the very common *.txt cases with the suffix tree - const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); - const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); - const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); - matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); + if (result.m_matchingMimeTypes.isEmpty()) { + const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); + const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); + const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); + matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); + if (result.m_matchingMimeTypes.isEmpty()) + matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); + } + // Check complex globs (e.g. "callgrind.out[0-9]*" or "README*") if (result.m_matchingMimeTypes.isEmpty()) - matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); + matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); } void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) diff --git a/qtbase/src/corelib/serialization/qjsonparser.cpp b/qtbase/src/corelib/serialization/qjsonparser.cpp index 17e6f111ab..2e40b6c8fb 100644 --- a/qtbase/src/corelib/serialization/qjsonparser.cpp +++ b/qtbase/src/corelib/serialization/qjsonparser.cpp @@ -379,10 +379,30 @@ error: return QCborValue(); } +// We need to retain the _last_ value for any duplicate keys and we need to deref containers. +// Therefore the manual implementation of std::unique(). +template +static Iterator customAssigningUniqueLast(Iterator first, Iterator last, + Compare compare, Assign assign) +{ + first = std::adjacent_find(first, last, compare); + if (first == last) + return last; + + Iterator result = first; + while (++first != last) { + if (!compare(*result, *first)) + ++result; + if (result != first) + assign(*result, *first); + } + + return ++result; +} + static void sortContainer(QCborContainerPrivate *container) { using Forward = QJsonPrivate::KeyIterator; - using Reverse = std::reverse_iterator; using Value = Forward::value_type; auto compare = [container](const Value &a, const Value &b) @@ -420,17 +440,31 @@ static void sortContainer(QCborContainerPrivate *container) } }; - std::sort(Forward(container->elements.begin()), Forward(container->elements.end()), - [&compare](const Value &a, const Value &b) { return compare(a, b) < 0; }); + // The elements' containers are owned by the outer container, not by the elements themselves. + auto move = [](Forward::reference target, Forward::reference source) + { + QtCbor::Element &targetValue = target.value(); + + // If the target has a container, deref it before overwriting, so that we don't leak. + if (targetValue.flags & QtCbor::Element::IsContainer) + targetValue.container->deref(); + + // Do not move, so that we can clear the value afterwards. + target = source; + + // Clear the source value, so that we don't store the same container twice. + source.value() = QtCbor::Element(); + }; + + std::stable_sort( + Forward(container->elements.begin()), Forward(container->elements.end()), + [&compare](const Value &a, const Value &b) { return compare(a, b) < 0; }); - // We need to retain the _last_ value for any duplicate keys. Therefore the reverse dance here. - auto it = std::unique(Reverse(container->elements.end()), Reverse(container->elements.begin()), - [&compare](const Value &a, const Value &b) { - return compare(a, b) == 0; - }).base().elementsIterator(); + Forward result = customAssigningUniqueLast( + Forward(container->elements.begin()), Forward(container->elements.end()), + [&compare](const Value &a, const Value &b) { return compare(a, b) == 0; }, move); - // The erase from beginning is expensive but hopefully rare. - container->elements.erase(container->elements.begin(), it); + container->elements.erase(result.elementsIterator(), container->elements.end()); } diff --git a/qtbase/src/corelib/serialization/qxmlstream.cpp b/qtbase/src/corelib/serialization/qxmlstream.cpp index b2f846544d..7cd457ba3a 100644 --- a/qtbase/src/corelib/serialization/qxmlstream.cpp +++ b/qtbase/src/corelib/serialization/qxmlstream.cpp @@ -980,7 +980,7 @@ inline uint QXmlStreamReaderPrivate::peekChar() bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject) { int pos = textBuffer.size(); - int oldLineNumber = lineNumber; + const auto oldLineNumber = lineNumber; uint c; while ((c = getChar()) != StreamEOF) { diff --git a/qtbase/src/corelib/text/qstring.cpp b/qtbase/src/corelib/text/qstring.cpp index 3e88ab4859..8deeae47bb 100644 --- a/qtbase/src/corelib/text/qstring.cpp +++ b/qtbase/src/corelib/text/qstring.cpp @@ -12312,7 +12312,7 @@ static qsizetype qLastIndexOf(Haystack haystack0, qsizetype from, const auto needle = needle0.data(); const auto *end = haystack; haystack += from; - const std::size_t sl_minus_1 = sl - 1; + const std::size_t sl_minus_1 = sl ? sl - 1 : 0; const auto *n = needle + sl_minus_1; const auto *h = haystack + sl_minus_1; std::size_t hashNeedle = 0, hashHaystack = 0; diff --git a/qtbase/src/corelib/text/qstringiterator_p.h b/qtbase/src/corelib/text/qstringiterator_p.h index 219589b6e4..1d0c66cc78 100644 --- a/qtbase/src/corelib/text/qstringiterator_p.h +++ b/qtbase/src/corelib/text/qstringiterator_p.h @@ -61,6 +61,8 @@ class QStringIterator { QString::const_iterator i, pos, e; Q_STATIC_ASSERT((std::is_same::value)); + static bool less(const QChar *lhs, const QChar *rhs) noexcept + { return std::less{}(lhs, rhs); } public: explicit QStringIterator(QStringView string, qsizetype idx = 0) : i(string.begin()), @@ -95,7 +97,8 @@ public: inline void setPosition(QString::const_iterator position) { - Q_ASSERT_X(i <= position && position <= e, Q_FUNC_INFO, "position out of bounds"); + Q_ASSERT_X(!less(position, i) && !less(e, position), + Q_FUNC_INFO, "position out of bounds"); pos = position; } @@ -103,7 +106,7 @@ public: inline bool hasNext() const { - return pos < e; + return less(pos, e); } inline void advance() @@ -120,16 +123,20 @@ public: { Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); - if (Q_UNLIKELY((pos++)->isHighSurrogate())) + if (Q_UNLIKELY((pos++)->isHighSurrogate())) { + Q_ASSERT(hasNext() && pos->isLowSurrogate()); ++pos; + } } inline uint peekNextUnchecked() const { Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); - if (Q_UNLIKELY(pos->isHighSurrogate())) + if (Q_UNLIKELY(pos->isHighSurrogate())) { + Q_ASSERT(less(pos + 1, e) && pos[1].isLowSurrogate()); return QChar::surrogateToUcs4(pos[0], pos[1]); + } return pos->unicode(); } @@ -155,8 +162,10 @@ public: Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); const QChar cur = *pos++; - if (Q_UNLIKELY(cur.isHighSurrogate())) + if (Q_UNLIKELY(cur.isHighSurrogate())) { + Q_ASSERT(hasNext() && pos->isLowSurrogate()); return QChar::surrogateToUcs4(cur, *pos++); + } return cur.unicode(); } @@ -166,7 +175,7 @@ public: const QChar uc = *pos++; if (Q_UNLIKELY(uc.isSurrogate())) { - if (Q_LIKELY(uc.isHighSurrogate() && pos < e && pos->isLowSurrogate())) + if (Q_LIKELY(uc.isHighSurrogate() && hasNext() && pos->isLowSurrogate())) return QChar::surrogateToUcs4(uc, *pos++); return invalidAs; } @@ -178,7 +187,7 @@ public: inline bool hasPrevious() const { - return pos > i; + return less(i, pos); } inline void recede() @@ -196,16 +205,20 @@ public: { Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); - if (Q_UNLIKELY((--pos)->isLowSurrogate())) + if (Q_UNLIKELY((--pos)->isLowSurrogate())) { + Q_ASSERT(hasPrevious() && pos[-1].isHighSurrogate()); --pos; + } } inline uint peekPreviousUnchecked() const { Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); - if (Q_UNLIKELY(pos[-1].isLowSurrogate())) + if (Q_UNLIKELY(pos[-1].isLowSurrogate())) { + Q_ASSERT(less(i + 1, pos) && pos[-2].isHighSurrogate()); return QChar::surrogateToUcs4(pos[-2], pos[-1]); + } return pos[-1].unicode(); } @@ -230,8 +243,10 @@ public: Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); const QChar cur = *--pos; - if (Q_UNLIKELY(cur.isLowSurrogate())) + if (Q_UNLIKELY(cur.isLowSurrogate())) { + Q_ASSERT(hasPrevious() && pos[-1].isHighSurrogate()); return QChar::surrogateToUcs4(*--pos, cur); + } return cur.unicode(); } @@ -241,7 +256,7 @@ public: const QChar uc = *--pos; if (Q_UNLIKELY(uc.isSurrogate())) { - if (Q_LIKELY(uc.isLowSurrogate() && pos > i && pos[-1].isHighSurrogate())) + if (Q_LIKELY(uc.isLowSurrogate() && hasPrevious() && pos[-1].isHighSurrogate())) return QChar::surrogateToUcs4(*--pos, uc); return invalidAs; } diff --git a/qtbase/src/corelib/thread/qfutex_p.h b/qtbase/src/corelib/thread/qfutex_p.h index f287b752d7..e294537787 100644 --- a/qtbase/src/corelib/thread/qfutex_p.h +++ b/qtbase/src/corelib/thread/qfutex_p.h @@ -52,6 +52,7 @@ // #include +#include QT_BEGIN_NAMESPACE @@ -106,16 +107,13 @@ namespace QtLinuxFutex { inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0, int *addr2 = nullptr, int val3 = 0) noexcept { - // A futex call ensures total ordering on the futex words - // (in either success or failure of the call). Instruct TSAN accordingly, - // as TSAN does not understand the futex(2) syscall. - _q_tsan_release(addr, addr2); + QtTsan::futexRelease(addr, addr2); // we use __NR_futex because some libcs (like Android's bionic) don't // provide SYS_futex etc. int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3); - _q_tsan_acquire(addr, addr2); + QtTsan::futexAcquire(addr, addr2); return result; } diff --git a/qtbase/src/corelib/thread/qmutex.cpp b/qtbase/src/corelib/thread/qmutex.cpp index 310d1cb14f..7097122d8e 100644 --- a/qtbase/src/corelib/thread/qmutex.cpp +++ b/qtbase/src/corelib/thread/qmutex.cpp @@ -152,6 +152,7 @@ public: /*! \enum QMutex::RecursionMode + \obsolete Use QRecursiveMutex to create a recursive mutex. \value Recursive In this mode, a thread can lock the same mutex multiple times and the mutex won't be unlocked @@ -173,6 +174,7 @@ public: /*! Constructs a new mutex. The mutex is created in an unlocked state. + \obsolete Use QRecursiveMutex to create a recursive mutex. If \a mode is QMutex::Recursive, a thread can lock the same mutex multiple times and the mutex won't be unlocked until a @@ -197,7 +199,7 @@ QMutex::QMutex(RecursionMode mode) QMutex::~QMutex() { QMutexData *d = d_ptr.loadRelaxed(); - if (isRecursive()) { + if (QBasicMutex::isRecursive()) { delete static_cast(d); } else if (d) { #ifndef QT_LINUX_FUTEX diff --git a/qtbase/src/corelib/thread/qmutex.h b/qtbase/src/corelib/thread/qmutex.h index 73c9e00663..1bae573a03 100644 --- a/qtbase/src/corelib/thread/qmutex.h +++ b/qtbase/src/corelib/thread/qmutex.h @@ -42,6 +42,7 @@ #include #include +#include #include #if __has_include() @@ -77,19 +78,37 @@ public: // BasicLockable concept inline void lock() QT_MUTEX_LOCK_NOEXCEPT { + QtTsan::mutexPreLock(this, 0u); + if (!fastTryLock()) lockInternal(); + + QtTsan::mutexPostLock(this, 0u, 0); } // BasicLockable concept inline void unlock() noexcept { Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked + + QtTsan::mutexPreUnlock(this, 0u); + if (!fastTryUnlock()) unlockInternal(); + + QtTsan::mutexPostUnlock(this, 0u); } bool tryLock() noexcept { - return fastTryLock(); + unsigned tsanFlags = QtTsan::TryLock; + QtTsan::mutexPreLock(this, tsanFlags); + + const bool success = fastTryLock(); + + if (!success) + tsanFlags |= QtTsan::TryLockFailed; + QtTsan::mutexPostLock(this, tsanFlags, 0); + + return success; } // Lockable concept @@ -134,8 +153,16 @@ public: #else QMutex() { d_ptr.storeRelaxed(nullptr); } #endif +#if QT_DEPRECATED_SINCE(5,15) enum RecursionMode { NonRecursive, Recursive }; + QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex") explicit QMutex(RecursionMode mode); + + QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex") + bool isRecursive() const noexcept + { return QBasicMutex::isRecursive(); } +#endif + ~QMutex(); // BasicLockable concept @@ -166,9 +193,6 @@ public: } #endif - bool isRecursive() const noexcept - { return QBasicMutex::isRecursive(); } - private: Q_DISABLE_COPY(QMutex) friend class QMutexLocker; diff --git a/qtbase/src/corelib/thread/qreadwritelock.cpp b/qtbase/src/corelib/thread/qreadwritelock.cpp index 8c28507d5a..9dd8503116 100644 --- a/qtbase/src/corelib/thread/qreadwritelock.cpp +++ b/qtbase/src/corelib/thread/qreadwritelock.cpp @@ -155,7 +155,7 @@ QReadWriteLock::QReadWriteLock(RecursionMode recursionMode) */ QReadWriteLock::~QReadWriteLock() { - auto d = d_ptr.loadRelaxed(); + auto d = d_ptr.loadAcquire(); if (isUncontendedLocked(d)) { qWarning("QReadWriteLock: destroying locked QReadWriteLock"); return; @@ -445,7 +445,7 @@ void QReadWriteLock::unlock() /*! \internal Helper for QWaitCondition::wait */ QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() const { - QReadWriteLockPrivate *d = d_ptr.loadRelaxed(); + QReadWriteLockPrivate *d = d_ptr.loadAcquire(); switch (quintptr(d) & StateMask) { case StateLockedForRead: return LockedForRead; case StateLockedForWrite: return LockedForWrite; @@ -453,6 +453,7 @@ QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() co if (!d) return Unlocked; + const auto lock = qt_scoped_lock(d->mutex); if (d->writerCount > 1) return RecursivelyLocked; else if (d->writerCount == 1) diff --git a/qtbase/src/corelib/thread/qsemaphore.cpp b/qtbase/src/corelib/thread/qsemaphore.cpp index 1d01fc1b28..3ecdee3747 100644 --- a/qtbase/src/corelib/thread/qsemaphore.cpp +++ b/qtbase/src/corelib/thread/qsemaphore.cpp @@ -354,7 +354,12 @@ void QSemaphore::release(int n) quintptr nn = unsigned(n); if (futexHasWaiterCount) nn |= quint64(nn) << 32; // token count replicated in high word - quintptr prevValue = u.fetchAndAddRelease(nn); + quintptr prevValue = u.loadRelaxed(); + quintptr newValue; + do { // loop just to ensure the operations are done atomically + newValue = prevValue + nn; + newValue &= (futexNeedsWakeAllBit - 1); + } while (!u.testAndSetRelease(prevValue, newValue, prevValue)); if (futexNeedsWake(prevValue)) { #ifdef FUTEX_OP if (futexHasWaiterCount) { @@ -376,7 +381,6 @@ void QSemaphore::release(int n) quint32 oparg = 0; quint32 cmp = FUTEX_OP_CMP_NE; quint32 cmparg = 0; - u.fetchAndAndRelease(futexNeedsWakeAllBit - 1); futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg)); return; } @@ -388,7 +392,6 @@ void QSemaphore::release(int n) // its acquisition anyway, so it has to wait; // 2) it did not see the new counter value, in which case its // futexWait will fail. - u.fetchAndAndRelease(futexNeedsWakeAllBit - 1); if (futexHasWaiterCount) { futexWakeAll(*futexLow32(&u)); futexWakeAll(*futexHigh32(&u)); diff --git a/qtbase/src/corelib/thread/qthread_p.h b/qtbase/src/corelib/thread/qthread_p.h index b2d1628e6e..f32f6f0460 100644 --- a/qtbase/src/corelib/thread/qthread_p.h +++ b/qtbase/src/corelib/thread/qthread_p.h @@ -174,7 +174,7 @@ public: int returnCode; uint stackSize; - QThread::Priority priority; + std::underlying_type::type priority; static QThread *threadForId(int id); @@ -213,6 +213,13 @@ public: QCoreApplication::instance()->postEvent(q_ptr, new QEvent(QEvent::Quit)); } } + +#ifndef Q_OS_INTEGRITY +private: + // Used in QThread(Private)::start to avoid racy access to QObject::objectName, + // unset afterwards. On INTEGRITY we set the thread name before starting it. + QString objectName; +#endif }; #else // QT_CONFIG(thread) diff --git a/qtbase/src/corelib/thread/qthread_unix.cpp b/qtbase/src/corelib/thread/qthread_unix.cpp index 56a8e0a038..610117a640 100644 --- a/qtbase/src/corelib/thread/qthread_unix.cpp +++ b/qtbase/src/corelib/thread/qthread_unix.cpp @@ -169,8 +169,7 @@ static void set_thread_data(QThreadData *data) static void clear_thread_data() { - currentThreadData = nullptr; - pthread_setspecific(current_thread_data_key, nullptr); + set_thread_data(nullptr); } template @@ -296,7 +295,7 @@ void *QThreadPrivate::start(void *arg) QMutexLocker locker(&thr->d_func()->mutex); // do we need to reset the thread priority? - if (int(thr->d_func()->priority) & ThreadPriorityResetFlag) { + if (thr->d_func()->priority & ThreadPriorityResetFlag) { thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag)); } @@ -316,10 +315,10 @@ void *QThreadPrivate::start(void *arg) // Sets the name of the current thread. We can only do this // when the thread is starting, as we don't have a cross // platform way of setting the name of an arbitrary thread. - if (Q_LIKELY(thr->objectName().isEmpty())) + if (Q_LIKELY(thr->d_func()->objectName.isEmpty())) setCurrentThreadName(thr->metaObject()->className()); else - setCurrentThreadName(thr->objectName().toLocal8Bit()); + setCurrentThreadName(std::exchange(thr->d_func()->objectName, {}).toLocal8Bit()); } #endif @@ -671,7 +670,7 @@ void QThread::start(Priority priority) // could not set scheduling hints, fallback to inheriting them // we'll try again from inside the thread pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); - d->priority = Priority(priority | ThreadPriorityResetFlag); + d->priority = static_cast::type>(priority) | ThreadPriorityResetFlag; } break; } @@ -702,7 +701,10 @@ void QThread::start(Priority priority) pthread_attr_setthreadname(&attr, metaObject()->className()); else pthread_attr_setthreadname(&attr, objectName().toLocal8Bit()); +#else + d->objectName = objectName(); #endif + pthread_t threadId; int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this); if (code == EPERM) { diff --git a/qtbase/src/corelib/thread/qthread_win.cpp b/qtbase/src/corelib/thread/qthread_win.cpp index bc70e3178a..4e76972498 100644 --- a/qtbase/src/corelib/thread/qthread_win.cpp +++ b/qtbase/src/corelib/thread/qthread_win.cpp @@ -394,10 +394,9 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi #if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINRT) // sets the name of the current thread. - QByteArray objectName = thr->objectName().toLocal8Bit(); - qt_set_thread_name(HANDLE(-1), - objectName.isEmpty() ? - thr->metaObject()->className() : objectName.constData()); + qt_set_thread_name(HANDLE(-1), thr->d_func()->objectName.isEmpty() + ? thr->metaObject()->className() + : std::exchange(thr->d_func()->objectName, {}).toLocal8Bit().constData()); #endif emit thr->started(QThread::QPrivateSignal()); @@ -503,6 +502,7 @@ void QThread::start(Priority priority) if (d->running) return; + d->objectName = objectName(); d->running = true; d->finished = false; d->exited = false; @@ -548,7 +548,7 @@ void QThread::start(Priority priority) int prio; d->priority = priority; - switch (d->priority) { + switch (priority) { case IdlePriority: prio = THREAD_PRIORITY_IDLE; break; @@ -686,7 +686,7 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority) int prio; priority = threadPriority; - switch (priority) { + switch (threadPriority) { case QThread::IdlePriority: prio = THREAD_PRIORITY_IDLE; break; 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/qdatetime.cpp b/qtbase/src/corelib/time/qdatetime.cpp index ec12569e3f..2c0b098079 100644 --- a/qtbase/src/corelib/time/qdatetime.cpp +++ b/qtbase/src/corelib/time/qdatetime.cpp @@ -43,6 +43,7 @@ #if QT_CONFIG(datetimeparser) #include "private/qdatetimeparser_p.h" #endif +#include #include "qdatastream.h" #include "qset.h" @@ -1429,9 +1430,11 @@ QDate QDate::addDays(qint64 ndays) const if (isNull()) return QDate(); - // Due to limits on minJd() and maxJd() we know that any overflow - // will be invalid and caught by fromJulianDay(). - return fromJulianDay(jd + ndays); + qint64 r; + if (Q_UNLIKELY(add_overflow(jd, ndays, &r))) + return QDate(); + else + return fromJulianDay(r); } /*! diff --git a/qtbase/src/corelib/time/qtimezone.cpp b/qtbase/src/corelib/time/qtimezone.cpp index 0309e43e52..3d451696a1 100644 --- a/qtbase/src/corelib/time/qtimezone.cpp +++ b/qtbase/src/corelib/time/qtimezone.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 John Layt +** Copyright (C) 2020 John Layt ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -975,9 +975,15 @@ QList QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId, } #ifndef QT_NO_DATASTREAM +// Invalid, as an IANA ID: too long, starts with - and has other invalid characters in it +static inline QString invalidId() { return QStringLiteral("-No Time Zone Specified!"); } + QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz) { - tz.d->serialize(ds); + if (tz.isValid()) + tz.d->serialize(ds); + else + ds << invalidId(); return ds; } @@ -985,7 +991,9 @@ QDataStream &operator>>(QDataStream &ds, QTimeZone &tz) { QString ianaId; ds >> ianaId; - if (ianaId == QLatin1String("OffsetFromUtc")) { + if (ianaId == invalidId()) { + tz = QTimeZone(); + } else if (ianaId == QLatin1String("OffsetFromUtc")) { int utcOffset; QString name; QString abbreviation; diff --git a/qtbase/src/corelib/time/qtimezoneprivate_tz.cpp b/qtbase/src/corelib/time/qtimezoneprivate_tz.cpp index b1611d7ddb..a7a6a72ccf 100644 --- a/qtbase/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/qtbase/src/corelib/time/qtimezoneprivate_tz.cpp @@ -42,6 +42,7 @@ #include "qtimezone.h" #include "qtimezoneprivate_p.h" #include "private/qlocale_tools_p.h" +#include "private/qlocking_p.h" #include #include @@ -62,6 +63,10 @@ QT_BEGIN_NAMESPACE +#if QT_CONFIG(icu) +static QBasicMutex s_icu_mutex; +#endif + /* Private @@ -670,6 +675,9 @@ QTzTimeZonePrivate::~QTzTimeZonePrivate() QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const { +#if QT_CONFIG(icu) + const auto lock = qt_scoped_lock(s_icu_mutex); +#endif return new QTzTimeZonePrivate(*this); } @@ -913,12 +921,14 @@ QString QTzTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch, const QLocale &locale) const { #if QT_CONFIG(icu) + auto lock = qt_unique_lock(s_icu_mutex); if (!m_icu) m_icu = new QIcuTimeZonePrivate(m_id); // TODO small risk may not match if tran times differ due to outdated files // TODO Some valid TZ names are not valid ICU names, use translation table? if (m_icu->isValid()) return m_icu->displayName(atMSecsSinceEpoch, nameType, locale); + lock.unlock(); #else Q_UNUSED(nameType) Q_UNUSED(locale) @@ -932,12 +942,14 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType, const QLocale &locale) const { #if QT_CONFIG(icu) + auto lock = qt_unique_lock(s_icu_mutex); if (!m_icu) m_icu = new QIcuTimeZonePrivate(m_id); // TODO small risk may not match if tran times differ due to outdated files // TODO Some valid TZ names are not valid ICU names, use translation table? if (m_icu->isValid()) return m_icu->displayName(timeType, nameType, locale); + lock.unlock(); #else Q_UNUSED(timeType) Q_UNUSED(nameType) diff --git a/qtbase/src/corelib/tools/qarraydata.h b/qtbase/src/corelib/tools/qarraydata.h index dcd95924c1..d01739e7e6 100644 --- a/qtbase/src/corelib/tools/qarraydata.h +++ b/qtbase/src/corelib/tools/qarraydata.h @@ -42,6 +42,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -58,14 +59,14 @@ struct Q_CORE_EXPORT QArrayData { Q_ASSERT(size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)); - return reinterpret_cast(this) + offset; + return reinterpret_cast (reinterpret_cast(this) + offset); } const void *data() const { Q_ASSERT(size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)); - return reinterpret_cast(this) + offset; + return reinterpret_cast (reinterpret_cast(this) + offset); } // This refers to array data mutability, not "header data" represented by diff --git a/qtbase/src/corelib/tools/qfreelist_p.h b/qtbase/src/corelib/tools/qfreelist_p.h index 5ba23b344b..9974102136 100644 --- a/qtbase/src/corelib/tools/qfreelist_p.h +++ b/qtbase/src/corelib/tools/qfreelist_p.h @@ -161,7 +161,7 @@ class QFreeList return i; x -= size; } - Q_ASSERT(false); + Q_UNREACHABLE(); return -1; } diff --git a/qtbase/src/corelib/tools/qversionnumber.h b/qtbase/src/corelib/tools/qversionnumber.h index d43b86ba51..2843f2b043 100644 --- a/qtbase/src/corelib/tools/qversionnumber.h +++ b/qtbase/src/corelib/tools/qversionnumber.h @@ -273,10 +273,10 @@ public: Q_REQUIRED_RESULT Q_CORE_EXPORT QString toString() const; #if QT_STRINGVIEW_LEVEL < 2 - Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr); + Q_REQUIRED_RESULT Q_CORE_EXPORT static QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr); #endif - Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QLatin1String string, int *suffixIndex = nullptr); - Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QStringView string, int *suffixIndex = nullptr); + Q_REQUIRED_RESULT Q_CORE_EXPORT static QVersionNumber fromString(QLatin1String string, int *suffixIndex = nullptr); + Q_REQUIRED_RESULT Q_CORE_EXPORT static QVersionNumber fromString(QStringView string, int *suffixIndex = nullptr); private: #ifndef QT_NO_DATASTREAM diff --git a/qtbase/src/dbus/qdbusintegrator.cpp b/qtbase/src/dbus/qdbusintegrator.cpp index 5b66ce971b..28a3b0ffea 100644 --- a/qtbase/src/dbus/qdbusintegrator.cpp +++ b/qtbase/src/dbus/qdbusintegrator.cpp @@ -1135,7 +1135,13 @@ void QDBusConnectionPrivate::closeConnection() } } - qDeleteAll(pendingCalls); + for (auto it = pendingCalls.begin(); it != pendingCalls.end(); ++it) { + auto call = *it; + if (!call->ref.deref()) { + delete call; + } + } + pendingCalls.clear(); // Disconnect all signals from signal hooks and from the object tree to // avoid QObject::destroyed being sent to dbus daemon thread which has @@ -2584,6 +2590,11 @@ QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &pa if (mo) return mo; } + if (path.isEmpty()) { + error = QDBusError(QDBusError::InvalidObjectPath, QLatin1String("Object path cannot be empty")); + lastError = error; + return nullptr; + } // introspect the target object QDBusMessage msg = QDBusMessage::createMethodCall(service, path, diff --git a/qtbase/src/dbus/qdbusmetaobject.cpp b/qtbase/src/dbus/qdbusmetaobject.cpp index 5265568f42..7e9f0f1acc 100644 --- a/qtbase/src/dbus/qdbusmetaobject.cpp +++ b/qtbase/src/dbus/qdbusmetaobject.cpp @@ -210,6 +210,9 @@ QDBusMetaObjectGenerator::findType(const QByteArray &signature, } else if (signature == "a{ss}") { result.name = "QMap"; type = qMetaTypeId >(); + } else if (signature == "aay") { + result.name = "QByteArrayList"; + type = qMetaTypeId(); } else { result.name = "{D-Bus type \"" + signature + "\"}"; type = registerComplexDBusType(result.name); diff --git a/qtbase/src/gui/accessible/qaccessible.cpp b/qtbase/src/gui/accessible/qaccessible.cpp index d705bfccb5..0caf3c808c 100644 --- a/qtbase/src/gui/accessible/qaccessible.cpp +++ b/qtbase/src/gui/accessible/qaccessible.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -681,6 +682,25 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) // Create a QAccessibleInterface for the object class. Start by the most // derived class and walk up the class hierarchy. const QMetaObject *mo = object->metaObject(); + const auto *objectPriv = QObjectPrivate::get(object); + /* + We do not want to cache each and every QML metaobject (Button_QMLTYPE_124, + Button_QMLTYPE_125, etc.). Those dynamic metaobjects shouldn't have an + accessible interface in any case. Instead, we start the whole checking + with the first non-dynamic meta-object. To avoid potential regressions + in other areas of Qt that also use dynamic metaobjects, we only do this + for objects that are QML-related (approximated by checking whether they + have ddata set). + */ + const bool qmlRelated = !objectPriv->isDeletingChildren && + objectPriv->declarativeData; + while (qmlRelated && mo) { + auto mop = QMetaObjectPrivate::get(mo); + if (!mop || !(mop->flags & DynamicMetaObject)) + break; + + mo = mo->superClass(); + }; while (mo) { const QString cn = QLatin1String(mo->className()); @@ -696,14 +716,15 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) // Find a QAccessiblePlugin (factory) for the class name. If there's // no entry in the cache try to create it using the plugin loader. if (!qAccessiblePlugins()->contains(cn)) { + QAccessiblePlugin *factory = nullptr; // 0 means "no plugin found". This is cached as well. const int index = loader()->indexOf(cn); - if (index != -1) { - QAccessiblePlugin *factory = qobject_cast(loader()->instance(index)); - qAccessiblePlugins()->insert(cn, factory); - } + if (index != -1) + factory = qobject_cast(loader()->instance(index)); + qAccessiblePlugins()->insert(cn, factory); } // At this point the cache should contain a valid factory pointer or 0: + Q_ASSERT(qAccessiblePlugins()->contains(cn)); QAccessiblePlugin *factory = qAccessiblePlugins()->value(cn); if (factory) { QAccessibleInterface *result = factory->create(cn, object); diff --git a/qtbase/src/gui/configure.json b/qtbase/src/gui/configure.json index 1f08795c57..12c95742d2 100644 --- a/qtbase/src/gui/configure.json +++ b/qtbase/src/gui/configure.json @@ -834,7 +834,8 @@ "// embedded devices, are not intended to be used together with X. EGL support", "// has to be disabled in plugins like xcb in this case since the native display,", "// window and pixmap types will be different than what an X-based platform", - "// plugin would expect." + "// plugin would expect.", + "#define USE_X11" ], "include": [ "EGL/egl.h", "X11/Xlib.h" ], "main": [ diff --git a/qtbase/src/gui/image/qbmphandler.cpp b/qtbase/src/gui/image/qbmphandler.cpp index 96f1e8cb1d..0e73bbbdb0 100644 --- a/qtbase/src/gui/image/qbmphandler.cpp +++ b/qtbase/src/gui/image/qbmphandler.cpp @@ -150,16 +150,42 @@ static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) return s; } -static int calc_shift(uint mask) +static uint calc_shift(uint mask) { - int result = 0; - while (mask && !(mask & 1)) { + uint result = 0; + while ((mask >= 0x100) || (!(mask & 1) && mask)) { result++; mask >>= 1; } return result; } +static uint calc_scale(uint low_mask) +{ + uint result = 8; + while (low_mask && result) { + result--; + low_mask >>= 1; + } + return result; +} + +static inline uint apply_scale(uint value, uint scale) +{ + if (!(scale & 0x07)) // return immediately if scale == 8 or 0 + return value; + + uint filled = 8 - scale; + uint result = value << scale; + + do { + result |= result >> filled; + filled <<= 1; + } while (filled < 8); + + return result; +} + static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf) { // read BMP file header @@ -222,14 +248,14 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, uint green_mask = 0; uint blue_mask = 0; uint alpha_mask = 0; - int red_shift = 0; - int green_shift = 0; - int blue_shift = 0; - int alpha_shift = 0; - int red_scale = 0; - int green_scale = 0; - int blue_scale = 0; - int alpha_scale = 0; + uint red_shift = 0; + uint green_shift = 0; + uint blue_shift = 0; + uint alpha_shift = 0; + uint red_scale = 0; + uint green_scale = 0; + uint blue_scale = 0; + uint alpha_scale = 0; if (!d->isSequential()) d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap or masks @@ -308,19 +334,19 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, red_shift = calc_shift(red_mask); if (((red_mask >> red_shift) + 1) == 0) return false; - red_scale = 256 / ((red_mask >> red_shift) + 1); + red_scale = calc_scale(red_mask >> red_shift); green_shift = calc_shift(green_mask); if (((green_mask >> green_shift) + 1) == 0) return false; - green_scale = 256 / ((green_mask >> green_shift) + 1); + green_scale = calc_scale(green_mask >> green_shift); blue_shift = calc_shift(blue_mask); if (((blue_mask >> blue_shift) + 1) == 0) return false; - blue_scale = 256 / ((blue_mask >> blue_shift) + 1); + blue_scale = calc_scale(blue_mask >> blue_shift); alpha_shift = calc_shift(alpha_mask); if (((alpha_mask >> alpha_shift) + 1) == 0) return false; - alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1); + alpha_scale = calc_scale(alpha_mask >> alpha_shift); } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) { blue_mask = 0x000000ff; green_mask = 0x0000ff00; @@ -328,17 +354,15 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, blue_shift = 0; green_shift = 8; red_shift = 16; - blue_scale = green_scale = red_scale = 1; + blue_scale = green_scale = red_scale = 0; } else if (comp == BMP_RGB && nbits == 16) { blue_mask = 0x001f; green_mask = 0x03e0; red_mask = 0x7c00; blue_shift = 0; - green_shift = 2; - red_shift = 7; - red_scale = 1; - green_scale = 1; - blue_scale = 8; + green_shift = 5; + red_shift = 10; + blue_scale = green_scale = red_scale = 3; } #if 0 @@ -544,10 +568,10 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, qint64 offset, c |= *(uchar*)(b+2)<<16; if (nbits > 24) c |= *(uchar*)(b+3)<<24; - *p++ = qRgba(((c & red_mask) >> red_shift) * red_scale, - ((c & green_mask) >> green_shift) * green_scale, - ((c & blue_mask) >> blue_shift) * blue_scale, - transp ? ((c & alpha_mask) >> alpha_shift) * alpha_scale : 0xff); + *p++ = qRgba(apply_scale((c & red_mask) >> red_shift, red_scale), + apply_scale((c & green_mask) >> green_shift, green_scale), + apply_scale((c & blue_mask) >> blue_shift, blue_scale), + transp ? apply_scale((c & alpha_mask) >> alpha_shift, alpha_scale) : 0xff); b += nbits/8; } } diff --git a/qtbase/src/gui/image/qimage.cpp b/qtbase/src/gui/image/qimage.cpp index 7152ac1607..0df772564c 100644 --- a/qtbase/src/gui/image/qimage.cpp +++ b/qtbase/src/gui/image/qimage.cpp @@ -4694,6 +4694,8 @@ QImage QImage::smoothScaled(int w, int h) const { static QImage rotated90(const QImage &image) { QImage out(image.height(), image.width(), image.format()); + if (out.isNull()) + return out; copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); @@ -4722,6 +4724,8 @@ static QImage rotated180(const QImage &image) return image.mirrored(true, true); QImage out(image.width(), image.height(), image.format()); + if (out.isNull()) + return out; copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); @@ -4734,6 +4738,8 @@ static QImage rotated180(const QImage &image) static QImage rotated270(const QImage &image) { QImage out(image.height(), image.width(), image.format()); + if (out.isNull()) + return out; copyMetadata(&out, image); if (image.colorCount() > 0) out.setColorTable(image.colorTable()); diff --git a/qtbase/src/gui/kernel/qcursor.cpp b/qtbase/src/gui/kernel/qcursor.cpp index d6ccaff8ed..455088241c 100644 --- a/qtbase/src/gui/kernel/qcursor.cpp +++ b/qtbase/src/gui/kernel/qcursor.cpp @@ -251,7 +251,8 @@ void QCursor::setPos(QScreen *screen, int x, int y) { if (screen) { if (QPlatformCursor *cursor = screen->handle()->cursor()) { - const QPoint devicePos = QHighDpi::toNativePixels(QPoint(x, y), screen); + const QPoint pos(x, y); + const QPoint devicePos = QHighDpi::toNativePixels(pos, screen->virtualSiblingAt(pos)); // Need to check, since some X servers generate null mouse move // events, causing looping in applications which call setPos() on // every mouse move event. diff --git a/qtbase/src/gui/kernel/qhighdpiscaling.cpp b/qtbase/src/gui/kernel/qhighdpiscaling.cpp index 9bbf2773a9..3ac4a4d8b6 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 a75b8ef920..4f2d294739 100644 --- a/qtbase/src/gui/kernel/qkeysequence.cpp +++ b/qtbase/src/gui/kernel/qkeysequence.cpp @@ -701,6 +701,10 @@ static const struct { { Qt::Key_TouchpadToggle, QT_TRANSLATE_NOOP("QShortcut", "Touchpad Toggle") }, { Qt::Key_TouchpadOn, QT_TRANSLATE_NOOP("QShortcut", "Touchpad On") }, { Qt::Key_TouchpadOff, QT_TRANSLATE_NOOP("QShortcut", "Touchpad Off") }, + { Qt::Key_Shift, QT_TRANSLATE_NOOP("QShortcut", "Shift") }, + { Qt::Key_Control, QT_TRANSLATE_NOOP("QShortcut", "Control") }, + { Qt::Key_Alt, QT_TRANSLATE_NOOP("QShortcut", "Alt") }, + { Qt::Key_Meta, QT_TRANSLATE_NOOP("QShortcut", "Meta") }, }; static Q_CONSTEXPR int numKeyNames = sizeof keyname / sizeof *keyname; diff --git a/qtbase/src/gui/kernel/qplatformservices.cpp b/qtbase/src/gui/kernel/qplatformservices.cpp index fdc6a6c4aa..ac47f98c5d 100644 --- a/qtbase/src/gui/kernel/qplatformservices.cpp +++ b/qtbase/src/gui/kernel/qplatformservices.cpp @@ -55,6 +55,19 @@ QT_BEGIN_NAMESPACE \brief The QPlatformServices provides the backend for desktop-related functionality. */ +/*! + \enum QPlatformServices::Capability + + Capabilities are used to determine a specific platform service's availability. + + \value ColorPickingFromScreen The platform natively supports color picking from screen. + This capability indicates that the platform supports "opaque" color picking, where the + platform implements a complete user experience for color picking and outputs a color. + This is in contrast to the application implementing the color picking user experience + (taking care of showing a cross hair, instructing the platform integration to obtain + the color at a given pixel, etc.). The related service function is pickColor(). + */ + QPlatformServices::QPlatformServices() { } @@ -85,5 +98,16 @@ QByteArray QPlatformServices::desktopEnvironment() const return QByteArray("UNKNOWN"); } +QPlatformServiceColorPicker *QPlatformServices::colorPicker(QWindow *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + +bool QPlatformServices::hasCapability(Capability capability) const +{ + Q_UNUSED(capability) + return false; +} QT_END_NAMESPACE diff --git a/qtbase/src/gui/kernel/qplatformservices.h b/qtbase/src/gui/kernel/qplatformservices.h index 5de96cfa7d..a8b2a4ce71 100644 --- a/qtbase/src/gui/kernel/qplatformservices.h +++ b/qtbase/src/gui/kernel/qplatformservices.h @@ -50,16 +50,32 @@ // #include +#include QT_BEGIN_NAMESPACE class QUrl; +class QWindow; + +class Q_GUI_EXPORT QPlatformServiceColorPicker : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; + virtual void pickColor() = 0; +Q_SIGNALS: + void colorPicked(const QColor &color); +}; class Q_GUI_EXPORT QPlatformServices { public: Q_DISABLE_COPY_MOVE(QPlatformServices) + enum Capability { + ColorPicking, + }; + QPlatformServices(); virtual ~QPlatformServices() { } @@ -67,6 +83,10 @@ public: virtual bool openDocument(const QUrl &url); virtual QByteArray desktopEnvironment() const; + + virtual bool hasCapability(Capability capability) const; + + virtual QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr); }; QT_END_NAMESPACE diff --git a/qtbase/src/gui/kernel/qplatformtheme.cpp b/qtbase/src/gui/kernel/qplatformtheme.cpp index 71521c0339..2325873245 100644 --- a/qtbase/src/gui/kernel/qplatformtheme.cpp +++ b/qtbase/src/gui/kernel/qplatformtheme.cpp @@ -163,6 +163,8 @@ QT_BEGIN_NAMESPACE \value ShowShortcutsInContextMenus (bool) Whether to display shortcut key sequences in context menus. + \value ButtonPressKeys (QList) A list of keys that can be used to press buttons via keyboard input. + \sa themeHint(), QStyle::pixelMetric() */ @@ -563,6 +565,8 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint) } case MouseQuickSelectionThreshold: return QVariant(10); + case ButtonPressKeys: + return QVariant::fromValue(QList({ Qt::Key_Space, Qt::Key_Select })); } return QVariant(); } diff --git a/qtbase/src/gui/kernel/qplatformtheme.h b/qtbase/src/gui/kernel/qplatformtheme.h index 3185fc4541..7e6c9d5740 100644 --- a/qtbase/src/gui/kernel/qplatformtheme.h +++ b/qtbase/src/gui/kernel/qplatformtheme.h @@ -120,7 +120,8 @@ public: TouchDoubleTapDistance, ShowShortcutsInContextMenus, IconFallbackSearchPaths, - MouseQuickSelectionThreshold + MouseQuickSelectionThreshold, + ButtonPressKeys }; enum DialogType { diff --git a/qtbase/src/gui/kernel/qscreen.cpp b/qtbase/src/gui/kernel/qscreen.cpp index 8e0eb35116..5d172a58dd 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/painting/qblendfunctions_p.h b/qtbase/src/gui/painting/qblendfunctions_p.h index 080da98ec4..6997d62b3c 100644 --- a/qtbase/src/gui/painting/qblendfunctions_p.h +++ b/qtbase/src/gui/painting/qblendfunctions_p.h @@ -246,25 +246,32 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl, int dudx, int dvdx, int dudy, int dvdy, int u0, int v0, Blender blender) { - int fromY = qMax(qRound(topY), clip.top()); - int toY = qMin(qRound(bottomY), clip.top() + clip.height()); + qint64 fromY = qMax(qRound(topY), clip.top()); + qint64 toY = qMin(qRound(bottomY), clip.top() + clip.height()); if (fromY >= toY) return; qreal leftSlope = (bottomLeft.x - topLeft.x) / (bottomLeft.y - topLeft.y); qreal rightSlope = (bottomRight.x - topRight.x) / (bottomRight.y - topRight.y); - int dx_l = int(leftSlope * 0x10000); - int dx_r = int(rightSlope * 0x10000); - int x_l = int((topLeft.x + (qreal(0.5) + fromY - topLeft.y) * leftSlope + qreal(0.5)) * 0x10000); - int x_r = int((topRight.x + (qreal(0.5) + fromY - topRight.y) * rightSlope + qreal(0.5)) * 0x10000); - - int fromX, toX, x1, x2, u, v, i, ii; + qint64 dx_l = qint64(leftSlope * 0x10000); + qint64 dx_r = qint64(rightSlope * 0x10000); + qint64 x_l = qint64((topLeft.x + (qreal(0.5) + fromY - topLeft.y) * leftSlope + qreal(0.5)) * 0x10000); + qint64 x_r = qint64((topRight.x + (qreal(0.5) + fromY - topRight.y) * rightSlope + qreal(0.5)) * 0x10000); + + qint64 sourceRectTop = qint64(sourceRect.top()); + qint64 sourceRectLeft = qint64(sourceRect.left()); + qint64 sourceRectWidth = qint64(sourceRect.width()); + qint64 sourceRectHeight = qint64(sourceRect.height()); + qint64 clipLeft = qint64(clip.left()); + qint64 clipWidth = qint64(clip.width()); + + qint64 fromX, toX, x1, x2, u, v, i, ii; DestT *line; - for (int y = fromY; y < toY; ++y) { + for (qint64 y = fromY; y < toY; ++y) { line = reinterpret_cast(reinterpret_cast(destPixels) + y * dbpl); - fromX = qMax(x_l >> 16, clip.left()); - toX = qMin(x_r >> 16, clip.left() + clip.width()); + fromX = qMax(x_l >> 16, clipLeft); + toX = qMin(x_r >> 16, clipLeft + clipWidth); if (fromX < toX) { // Because of rounding, we can get source coordinates outside the source image. // Clamp these coordinates to the source rect to avoid segmentation fault and @@ -275,10 +282,10 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl, u = x1 * dudx + y * dudy + u0; v = x1 * dvdx + y * dvdy + v0; for (; x1 < toX; ++x1) { - int uu = u >> 16; - int vv = v >> 16; - if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width() - && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) { + qint64 uu = u >> 16; + qint64 vv = v >> 16; + if (uu >= sourceRectLeft && uu < sourceRectLeft + sourceRectWidth + && vv >= sourceRectTop && vv < sourceRectTop + sourceRectHeight) { break; } u += dudx; @@ -290,10 +297,10 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl, u = (x2 - 1) * dudx + y * dudy + u0; v = (x2 - 1) * dvdx + y * dvdy + v0; for (; x2 > x1; --x2) { - int uu = u >> 16; - int vv = v >> 16; - if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width() - && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) { + qint64 uu = u >> 16; + qint64 vv = v >> 16; + if (uu >= sourceRectLeft && uu < sourceRectLeft + sourceRectWidth + && vv >= sourceRectTop && vv < sourceRectTop + sourceRectHeight) { break; } u -= dudx; @@ -308,8 +315,8 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl, // Beginning of the scan line, with per-pixel checks. i = x1 - fromX; while (i) { - int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1); - int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1); + qint64 uu = qBound(sourceRectLeft, u >> 16, sourceRectLeft + sourceRectWidth - 1); + qint64 vv = qBound(sourceRectTop, v >> 16, sourceRectTop + sourceRectHeight - 1); blender.write(line, reinterpret_cast(reinterpret_cast(srcPixels) + vv * sbpl)[uu]); u += dudx; v += dvdx; @@ -348,8 +355,8 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl, // End of the scan line, with per-pixel checks. i = toX - x2; while (i) { - int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1); - int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1); + qint64 uu = qBound(sourceRectLeft, u >> 16, sourceRectLeft + sourceRectWidth - 1); + qint64 vv = qBound(sourceRectTop, v >> 16, sourceRectTop + sourceRectHeight - 1); blender.write(line, reinterpret_cast(reinterpret_cast(srcPixels) + vv * sbpl)[uu]); u += dudx; v += dvdx; diff --git a/qtbase/src/gui/painting/qcolortrclut_p.h b/qtbase/src/gui/painting/qcolortrclut_p.h index 76a6a60803..24fd522e6c 100644 --- a/qtbase/src/gui/painting/qcolortrclut_p.h +++ b/qtbase/src/gui/painting/qcolortrclut_p.h @@ -118,6 +118,7 @@ public: return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257); #endif } + QRgba64 toLinear64(QRgba64) const = delete; QRgb toLinear(QRgb rgb32) const { diff --git a/qtbase/src/gui/painting/qdrawhelper.cpp b/qtbase/src/gui/painting/qdrawhelper.cpp index a61793508a..5ba2d277b7 100644 --- a/qtbase/src/gui/painting/qdrawhelper.cpp +++ b/qtbase/src/gui/painting/qdrawhelper.cpp @@ -6091,7 +6091,7 @@ static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile) { // Do a gammacorrected RGB alphablend... - const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(dst) : dst; + const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(dst) : dst; QRgba64 blend = rgbBlend(dlinear, slinear, coverage); diff --git a/qtbase/src/gui/painting/qpaintengine_raster.cpp b/qtbase/src/gui/painting/qpaintengine_raster.cpp index 283923de52..f08eb9cbee 100644 --- a/qtbase/src/gui/painting/qpaintengine_raster.cpp +++ b/qtbase/src/gui/painting/qpaintengine_raster.cpp @@ -560,31 +560,6 @@ void QRasterPaintEngine::updateMatrix(const QTransform &matrix) QRasterPaintEngineState *s = state(); // FALCON: get rid of this line, see drawImage call below. s->matrix = matrix; - QTransform::TransformationType txop = s->matrix.type(); - - switch (txop) { - - case QTransform::TxNone: - s->flags.int_xform = true; - break; - - case QTransform::TxTranslate: - s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx() - && qreal(int(s->matrix.dy())) == s->matrix.dy(); - break; - - case QTransform::TxScale: - s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx() - && qreal(int(s->matrix.dy())) == s->matrix.dy() - && qreal(int(s->matrix.m11())) == s->matrix.m11() - && qreal(int(s->matrix.m22())) == s->matrix.m22(); - break; - - default: // shear / perspective... - s->flags.int_xform = false; - break; - } - s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale); ensureOutlineMapper(); @@ -617,7 +592,6 @@ QRasterPaintEngineState::QRasterPaintEngineState() flags.bilinear = false; flags.legacy_rounding = false; flags.fast_text = true; - flags.int_xform = true; flags.tx_noshear = true; flags.fast_images = true; @@ -1793,7 +1767,7 @@ void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush) QRectF cpRect = path.controlPointRect(); const QRectF pathDeviceRect = s->matrix.mapRect(cpRect); // Skip paths that by conservative estimates are completely outside the paint device. - if (!pathDeviceRect.intersects(QRectF(d->deviceRect))) + if (!pathDeviceRect.intersects(QRectF(d->deviceRect)) || !pathDeviceRect.isValid()) return; ProcessSpans blend = d->getBrushFunc(pathDeviceRect, &s->brushData); diff --git a/qtbase/src/gui/painting/qpaintengine_raster_p.h b/qtbase/src/gui/painting/qpaintengine_raster_p.h index 089aadc3f7..bf7363d5aa 100644 --- a/qtbase/src/gui/painting/qpaintengine_raster_p.h +++ b/qtbase/src/gui/painting/qpaintengine_raster_p.h @@ -111,7 +111,6 @@ public: uint bilinear : 1; uint legacy_rounding : 1; uint fast_text : 1; - uint int_xform : 1; uint tx_noshear : 1; uint fast_images : 1; }; 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/qpathclipper_p.h b/qtbase/src/gui/painting/qpathclipper_p.h index 9444a87b71..18f64c5e8c 100644 --- a/qtbase/src/gui/painting/qpathclipper_p.h +++ b/qtbase/src/gui/painting/qpathclipper_p.h @@ -156,7 +156,7 @@ public: int vertex(Direction direction) const; private: - int m_next[2][2]; + int m_next[2][2] = { { -1, -1 }, { -1, -1 } }; }; class QPathSegments @@ -296,10 +296,6 @@ inline QPathEdge::QPathEdge(int a, int b) , angle(0) , invAngle(0) { - m_next[0][0] = -1; - m_next[1][0] = -1; - m_next[0][0] = -1; - m_next[1][0] = -1; } inline int QPathEdge::next(Traversal traversal, Direction direction) const diff --git a/qtbase/src/gui/painting/qpdf.cpp b/qtbase/src/gui/painting/qpdf.cpp index 3066744f1b..2c8d3c3b53 100644 --- a/qtbase/src/gui/painting/qpdf.cpp +++ b/qtbase/src/gui/painting/qpdf.cpp @@ -2760,6 +2760,8 @@ int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, return gradientBrush(brush, matrix, gStateObject); } + matrix = brush.transform() * matrix; + if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0) *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity), qRound(pen.color().alpha() * opacity)); diff --git a/qtbase/src/gui/painting/qstroker.cpp b/qtbase/src/gui/painting/qstroker.cpp index cb5d395e80..3b6357a893 100644 --- a/qtbase/src/gui/painting/qstroker.cpp +++ b/qtbase/src/gui/painting/qstroker.cpp @@ -1182,6 +1182,7 @@ void QDashStroker::processCurrentSubpath() // Check if the entire line should be clipped away or simplified bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br); bool skipDashing = elen * invSumLength > repetitionLimit(); + int maxDashes = dashCount; if (skipDashing || clipIt) { // Cut away full dash sequences. elen -= std::floor(elen * invSumLength) * sumLength; @@ -1196,7 +1197,7 @@ void QDashStroker::processCurrentSubpath() pos = estop; // move pos to next path element done = true; } else { // Dash is on this line - pos = dpos + estart; + pos = --maxDashes > 0 ? dpos + estart : estop; done = pos >= estop; if (++idash >= dashCount) idash = 0; diff --git a/qtbase/src/gui/painting/qt_attribution.json b/qtbase/src/gui/painting/qt_attribution.json index 7b16e8c211..0e87d30aa2 100644 --- a/qtbase/src/gui/painting/qt_attribution.json +++ b/qtbase/src/gui/painting/qt_attribution.json @@ -10,7 +10,7 @@ "Homepage": "http://www.freetype.org", "License": "Freetype Project License or GNU General Public License v2.0 only", "LicenseId": "FTL or GPL-2.0", - "LicenseFile": "../../3rdparty/freetype/docs/LICENSE.TXT", + "LicenseFile": "../../3rdparty/freetype/LICENSE.TXT", "Copyright": "Copyright 2000-2016 by David Turner, Robert Wilhelm, and Werner Lemberg." }, { diff --git a/qtbase/src/gui/rhi/qshader_p_p.h b/qtbase/src/gui/rhi/qshader_p_p.h index ec9d25971f..4a5a7a6d51 100644 --- a/qtbase/src/gui/rhi/qshader_p_p.h +++ b/qtbase/src/gui/rhi/qshader_p_p.h @@ -68,13 +68,13 @@ struct Q_GUI_EXPORT QShaderPrivate { } - QShaderPrivate(const QShaderPrivate *other) + QShaderPrivate(const QShaderPrivate &other) : ref(1), - qsbVersion(other->qsbVersion), - stage(other->stage), - desc(other->desc), - shaders(other->shaders), - bindings(other->bindings) + qsbVersion(other.qsbVersion), + stage(other.stage), + desc(other.desc), + shaders(other.shaders), + bindings(other.bindings) { } diff --git a/qtbase/src/gui/rhi/qshaderdescription_p_p.h b/qtbase/src/gui/rhi/qshaderdescription_p_p.h index ec2b0b6b4c..3da33a8a2b 100644 --- a/qtbase/src/gui/rhi/qshaderdescription_p_p.h +++ b/qtbase/src/gui/rhi/qshaderdescription_p_p.h @@ -63,16 +63,16 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate localSize[0] = localSize[1] = localSize[2] = 0; } - QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other) + QShaderDescriptionPrivate(const QShaderDescriptionPrivate &other) : ref(1), - inVars(other->inVars), - outVars(other->outVars), - uniformBlocks(other->uniformBlocks), - pushConstantBlocks(other->pushConstantBlocks), - storageBlocks(other->storageBlocks), - combinedImageSamplers(other->combinedImageSamplers), - storageImages(other->storageImages), - localSize(other->localSize) + inVars(other.inVars), + outVars(other.outVars), + uniformBlocks(other.uniformBlocks), + pushConstantBlocks(other.pushConstantBlocks), + storageBlocks(other.storageBlocks), + combinedImageSamplers(other.combinedImageSamplers), + storageImages(other.storageImages), + localSize(other.localSize) { } diff --git a/qtbase/src/gui/text/qfontdatabase.cpp b/qtbase/src/gui/text/qfontdatabase.cpp index a657a8c516..4fc0100958 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/qtextdocumentlayout.cpp b/qtbase/src/gui/text/qtextdocumentlayout.cpp index f3e6ea2100..72267e9380 100644 --- a/qtbase/src/gui/text/qtextdocumentlayout.cpp +++ b/qtbase/src/gui/text/qtextdocumentlayout.cpp @@ -2123,7 +2123,7 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p { Q_Q(const QTextDocumentLayout); const QTextBlockFormat blockFormat = bl.blockFormat(); - const QTextCharFormat charFormat = QTextCursor(bl).charFormat(); + const QTextCharFormat charFormat = bl.charFormat(); QFont font(charFormat.font()); if (q->paintDevice()) font = QFont(font, q->paintDevice()); diff --git a/qtbase/src/gui/text/qtextengine.cpp b/qtbase/src/gui/text/qtextengine.cpp index ce4abac472..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. @@ -1565,6 +1565,12 @@ void QTextEngine::shapeText(int item) const // Overwrite with 0 token to indicate failure QGlyphLayout g = availableGlyphs(&si); g.glyphs[0] = 0; + g.attributes[0].clusterStart = true; + + ushort *log_clusters = logClusters(&si); + for (int i = 0; i < itemLength; ++i) + log_clusters[i] = 0; + return; } diff --git a/qtbase/src/gui/util/qdesktopservices.cpp b/qtbase/src/gui/util/qdesktopservices.cpp index fbb63a9408..4d5fafaaa7 100644 --- a/qtbase/src/gui/util/qdesktopservices.cpp +++ b/qtbase/src/gui/util/qdesktopservices.cpp @@ -54,6 +54,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QOpenUrlHandlerRegistry : public QObject @@ -81,6 +83,7 @@ Q_GLOBAL_STATIC(QOpenUrlHandlerRegistry, handlerRegistry) void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler) { + const auto lock = qt_scoped_lock(mutex); HandlerHash::Iterator it = handlers.begin(); while (it != handlers.end()) { if (it->receiver == handler) { @@ -294,7 +297,8 @@ void QDesktopServices::setUrlHandler(const QString &scheme, QObject *receiver, c h.name = method; registry->handlers.insert(scheme.toLower(), h); QObject::connect(receiver, SIGNAL(destroyed(QObject*)), - registry, SLOT(handlerDestroyed(QObject*))); + registry, SLOT(handlerDestroyed(QObject*)), + Qt::DirectConnection); } /*! diff --git a/qtbase/src/gui/util/qpkmhandler.cpp b/qtbase/src/gui/util/qpkmhandler.cpp index e0c3b75efe..3414f9d8ba 100644 --- a/qtbase/src/gui/util/qpkmhandler.cpp +++ b/qtbase/src/gui/util/qpkmhandler.cpp @@ -57,7 +57,7 @@ struct PkmType quint32 bytesPerBlock; }; -static PkmType typeMap[5] = { +static constexpr PkmType typeMap[5] = { { 0x8D64, 8 }, // GL_ETC1_RGB8_OES { 0x9274, 8 }, // GL_COMPRESSED_RGB8_ETC2 { 0, 0 }, // unused (obsolete) diff --git a/qtbase/src/network/access/qnetworkreplyfileimpl.cpp b/qtbase/src/network/access/qnetworkreplyfileimpl.cpp index b6be93147a..6e69b4c4d3 100644 --- a/qtbase/src/network/access/qnetworkreplyfileimpl.cpp +++ b/qtbase/src/network/access/qnetworkreplyfileimpl.cpp @@ -89,9 +89,10 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con // we handle only local files QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()); setError(QNetworkReply::ProtocolInvalidOperationError, msg); + setFinished(true); // We're finished, will emit finished() after ctor is done. QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError)); - fileOpenFinished(false); + QMetaObject::invokeMethod(this, [this](){ fileOpenFinished(false); }, Qt::QueuedConnection); return; } #endif diff --git a/qtbase/src/network/access/qnetworkreplyimpl.cpp b/qtbase/src/network/access/qnetworkreplyimpl.cpp index bcdf6ee2f1..3b6deb82bd 100644 --- a/qtbase/src/network/access/qnetworkreplyimpl.cpp +++ b/qtbase/src/network/access/qnetworkreplyimpl.cpp @@ -680,7 +680,7 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data) // read until EOF from data if (Q_UNLIKELY(copyDevice)) { qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- " - "backend probly needs to be fixed"); + "backend probably needs to be fixed"); return; } diff --git a/qtbase/src/network/kernel/qauthenticator.cpp b/qtbase/src/network/kernel/qauthenticator.cpp index d9fe7bc47b..ccd04b294a 100644 --- a/qtbase/src/network/kernel/qauthenticator.cpp +++ b/qtbase/src/network/kernel/qauthenticator.cpp @@ -50,6 +50,7 @@ #include #include #include +#include "private/qsystemlibrary_p.h" #ifdef Q_OS_WIN #include @@ -1563,7 +1564,7 @@ static bool q_SSPI_library_load() // Initialize security interface if (pSecurityFunctionTable == nullptr) { - securityDLLHandle = LoadLibrary(L"secur32.dll"); + securityDLLHandle = QSystemLibrary::load(L"secur32"); if (securityDLLHandle != nullptr) { INIT_SECURITY_INTERFACE pInitSecurityInterface = reinterpret_cast( diff --git a/qtbase/src/network/kernel/qhostinfo_unix.cpp b/qtbase/src/network/kernel/qhostinfo_unix.cpp index 9b0a2ee669..73679c9ef1 100644 --- a/qtbase/src/network/kernel/qhostinfo_unix.cpp +++ b/qtbase/src/network/kernel/qhostinfo_unix.cpp @@ -122,7 +122,6 @@ static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym) LibResolv::LibResolv() { - QLibrary lib; #ifdef LIBRESOLV_SO lib.setFileName(QStringLiteral(LIBRESOLV_SO)); if (!lib.load()) diff --git a/qtbase/src/network/socket/qsocks5socketengine.cpp b/qtbase/src/network/socket/qsocks5socketengine.cpp index 3a046fd116..97fadca0b3 100644 --- a/qtbase/src/network/socket/qsocks5socketengine.cpp +++ b/qtbase/src/network/socket/qsocks5socketengine.cpp @@ -1296,7 +1296,7 @@ void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification() int pos = 0; const char *buf = inBuf.constData(); if (inBuf.size() < 4) { - QSOCKS5_D_DEBUG << "bugus udp data, discarding"; + QSOCKS5_D_DEBUG << "bogus udp data, discarding"; return; } QSocks5RevivedDatagram datagram; diff --git a/qtbase/src/network/ssl/qsslcontext_openssl.cpp b/qtbase/src/network/ssl/qsslcontext_openssl.cpp index c9f202f573..c992da9d8e 100644 --- a/qtbase/src/network/ssl/qsslcontext_openssl.cpp +++ b/qtbase/src/network/ssl/qsslcontext_openssl.cpp @@ -409,7 +409,7 @@ init_context: break; case QSsl::DtlsV1_0OrLater: minVersion = DTLS1_VERSION; - maxVersion = DTLS_MAX_VERSION; + maxVersion = 0; break; case QSsl::DtlsV1_2: minVersion = DTLS1_2_VERSION; @@ -417,7 +417,7 @@ init_context: break; case QSsl::DtlsV1_2OrLater: minVersion = DTLS1_2_VERSION; - maxVersion = DTLS_MAX_VERSION; + maxVersion = 0; break; case QSsl::TlsV1_3OrLater: #ifdef TLS1_3_VERSION @@ -455,7 +455,7 @@ init_context: } // Enable bug workarounds. - long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); + qssloptions options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); q_SSL_CTX_set_options(sslContext->ctx, options); // Tell OpenSSL to release memory early diff --git a/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp index aaf8741130..b58935372c 100644 --- a/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp +++ b/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp @@ -59,57 +59,6 @@ QT_BEGIN_NAMESPACE -#ifdef OPENSSL_NO_DEPRECATED_3_0 - -static int q_DH_check(DH *dh, int *status) -{ - // DH_check was first deprecated in OpenSSL 3.0.0, as low-level - // API; the EVP_PKEY family of functions was advised as an alternative. - // As of now EVP_PKEY_params_check ends up calling ... DH_check, - // which is good enough. - - Q_ASSERT(dh); - Q_ASSERT(status); - - EVP_PKEY *key = q_EVP_PKEY_new(); - if (!key) { - qCWarning(lcSsl, "EVP_PKEY_new failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - const auto keyDeleter = qScopeGuard([key](){ - q_EVP_PKEY_free(key); - }); - if (!q_EVP_PKEY_set1_DH(key, dh)) { - qCWarning(lcSsl, "EVP_PKEY_set1_DH failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - - EVP_PKEY_CTX *keyCtx = q_EVP_PKEY_CTX_new(key, nullptr); - if (!keyCtx) { - qCWarning(lcSsl, "EVP_PKEY_CTX_new failed"); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - return 0; - } - const auto ctxDeleter = qScopeGuard([keyCtx]{ - q_EVP_PKEY_CTX_free(keyCtx); - }); - - const int result = q_EVP_PKEY_param_check(keyCtx); - QSslSocketBackendPrivate::logAndClearErrorQueue(); - // Note: unlike DH_check, we cannot obtain the 'status', - // if the 'result' is 0 (actually the result is 1 only - // if this 'status' was 0). We could probably check the - // errors from the error queue, but it's not needed anyway - // - see the 'isSafeDH' below, how it returns immediately - // on 0. - Q_UNUSED(status) - - return result; -} -#endif // OPENSSL_NO_DEPRECATED_3_0 - static bool isSafeDH(DH *dh) { int status = 0; diff --git a/qtbase/src/network/ssl/qsslsocket_openssl.cpp b/qtbase/src/network/ssl/qsslsocket_openssl.cpp index 37fad2a68f..8f6858c867 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl.cpp +++ b/qtbase/src/network/ssl/qsslsocket_openssl.cpp @@ -550,9 +550,9 @@ static void q_loadCiphersForConnection(SSL *connection, QList &ciphe // Defined in qsslsocket.cpp void q_setDefaultDtlsCiphers(const QList &ciphers); -long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) +qssloptions QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) { - long options; + qssloptions options; if (protocol == QSsl::TlsV1SslV3) options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3; else if (protocol == QSsl::SecureProtocols) diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_p.h b/qtbase/src/network/ssl/qsslsocket_openssl_p.h index 4103de23e8..5547589256 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_p.h +++ b/qtbase/src/network/ssl/qsslsocket_openssl_p.h @@ -107,6 +107,12 @@ QT_BEGIN_NAMESPACE +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +typedef uint64_t qssloptions; +#else +typedef unsigned long qssloptions; +#endif + struct QSslErrorEntry { int code; int depth; @@ -171,7 +177,7 @@ public: QVector ocspErrors; QByteArray ocspResponseDer; - Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); + Q_AUTOTEST_EXPORT static qssloptions setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); static QList STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); static QList verify(const QList &certificateChain, const QString &hostName); diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp index e53fb279f0..459ccd0b19 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -148,7 +148,6 @@ DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, return) DEFINEFUNC2(EVP_PKEY_CTX *, EVP_PKEY_CTX_new, EVP_PKEY *pkey, pkey, ENGINE *e, e, return nullptr, return) DEFINEFUNC(int, EVP_PKEY_param_check, EVP_PKEY_CTX *ctx, ctx, return 0, return) DEFINEFUNC(void, EVP_PKEY_CTX_free, EVP_PKEY_CTX *ctx, ctx, return, return) -DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return) DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return) DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return) DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return) @@ -158,7 +157,7 @@ DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMM DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG) DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return) DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return) -DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return) +DEFINEFUNC2(qssloptions, SSL_CTX_set_options, SSL_CTX *ctx, ctx, qssloptions op, op, return 0, return) DEFINEFUNC(int, SSL_CTX_get_security_level, const SSL_CTX *ctx, ctx, return -1, return) DEFINEFUNC2(void, SSL_CTX_set_security_level, SSL_CTX *ctx, ctx, int level, level, return, return) #ifdef TLS1_3_VERSION @@ -371,7 +370,15 @@ DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return) DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return) DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return) + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +DEFINEFUNC(X509 *, SSL_get1_peer_certificate, SSL *a, a, return nullptr, return) +DEFINEFUNC(int, EVP_PKEY_get_base_id, const EVP_PKEY *pkey, pkey, return -1, return) +#else DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return) +DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return) +#endif // OPENSSL_VERSION_MAJOR >= 3 + DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return) DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return) @@ -492,9 +499,7 @@ DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG) DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return) DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return) -#ifndef OPENSSL_NO_DEPRECATED_3_0 DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return) -#endif // OPENSSL_NO_DEPRECATED_3_0 DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return) #ifndef OPENSSL_NO_EC @@ -868,7 +873,6 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EVP_PKEY_CTX_new) RESOLVEFUNC(EVP_PKEY_param_check) RESOLVEFUNC(EVP_PKEY_CTX_free) - RESOLVEFUNC(EVP_PKEY_base_id) RESOLVEFUNC(RSA_bits) RESOLVEFUNC(OPENSSL_sk_new_null) RESOLVEFUNC(OPENSSL_sk_push) @@ -1107,7 +1111,15 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_version) RESOLVEFUNC(SSL_get_error) RESOLVEFUNC(SSL_get_peer_cert_chain) + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + RESOLVEFUNC(SSL_get1_peer_certificate) + RESOLVEFUNC(EVP_PKEY_get_base_id) +#else RESOLVEFUNC(SSL_get_peer_certificate) + RESOLVEFUNC(EVP_PKEY_base_id) +#endif // OPENSSL_VERSION_MAJOR >= 3 + RESOLVEFUNC(SSL_get_verify_result) RESOLVEFUNC(SSL_new) RESOLVEFUNC(SSL_get_SSL_CTX) @@ -1206,9 +1218,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(DH_free) RESOLVEFUNC(d2i_DHparams) RESOLVEFUNC(i2d_DHparams) -#ifndef OPENSSL_NO_DEPRECATED_3_0 RESOLVEFUNC(DH_check) -#endif // OPENSSL_NO_DEPRECATED_3_0 RESOLVEFUNC(BN_bin2bn) #ifndef OPENSSL_NO_EC diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h b/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h index 95e8897a3b..27aeffa18a 100644 --- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -237,7 +237,6 @@ Q_AUTOTEST_EXPORT int q_EVP_PKEY_up_ref(EVP_PKEY *a); EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx); int q_EVP_PKEY_param_check(EVP_PKEY_CTX *ctx); -int q_EVP_PKEY_base_id(EVP_PKEY *a); int q_RSA_bits(RSA *a); Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a); Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *)); @@ -246,7 +245,7 @@ Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data); Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a); Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b); int q_SSL_session_reused(SSL *a); -unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op); +qssloptions q_SSL_CTX_set_options(SSL_CTX *ctx, qssloptions op); int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen); size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen); @@ -510,7 +509,6 @@ const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a); int q_SSL_version(const SSL *a); int q_SSL_get_error(SSL *a, int b); STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); -X509 *q_SSL_get_peer_certificate(SSL *a); long q_SSL_get_verify_result(const SSL *a); SSL *q_SSL_new(SSL_CTX *a); SSL_CTX *q_SSL_get_SSL_CTX(SSL *a); @@ -582,10 +580,7 @@ DH *q_DH_new(); void q_DH_free(DH *dh); DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length); int q_i2d_DHparams(DH *a, unsigned char **p); - -#ifndef OPENSSL_NO_DEPRECATED_3_0 int q_DH_check(DH *dh, int *codes); -#endif // OPENSSL_NO_DEPRECATED_3_0 BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); #define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh) @@ -754,6 +749,17 @@ void q_CRYPTO_free(void *str, const char *file, int line); int q_SSL_CTX_get_security_level(const SSL_CTX *ctx); void q_SSL_CTX_set_security_level(SSL_CTX *ctx, int level); +// Here we have the ones that make difference between OpenSSL pre/post v3: +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +X509 *q_SSL_get1_peer_certificate(SSL *a); +#define q_SSL_get_peer_certificate q_SSL_get1_peer_certificate +int q_EVP_PKEY_get_base_id(const EVP_PKEY *pkey); +#define q_EVP_PKEY_base_id q_EVP_PKEY_get_base_id +#else +X509 *q_SSL_get_peer_certificate(SSL *a); +int q_EVP_PKEY_base_id(EVP_PKEY *a); +#endif // OPENSSL_VERSION_MAJOR >= 3 + QT_END_NAMESPACE #endif diff --git a/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h b/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h index bf37d07fd8..dbd42fb799 100644 --- a/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h +++ b/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h @@ -61,7 +61,11 @@ # if !defined(Q_OS_INTEGRITY) # define WIN_INTERFACE_CUSTOM // NV # endif // Q_OS_INTEGRITY -#endif // QT_EGL_NO_X11 +#else // QT_EGL_NO_X11 +// If one has an eglplatform.h with https://github.com/KhronosGroup/EGL-Registry/pull/130 +// that needs USE_X11 to be defined. +# define USE_X11 +#endif #ifdef QT_EGL_WAYLAND # define WAYLAND // NV diff --git a/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 159b490ce0..00aa80cd58 100644 --- a/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -567,6 +567,8 @@ void QFontconfigDatabase::populateFontDatabase() fonts = FcFontList(nullptr, pattern, os); FcObjectSetDestroy(os); FcPatternDestroy(pattern); + if (!fonts) + return; } for (int i = 0; i < fonts->nfont; i++) diff --git a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp index b713c19447..7cb9a8c79a 100644 --- a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp +++ b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp @@ -95,6 +95,7 @@ static constexpr const auto KeyTbl = qMakeArray( Xkb2Qt, Xkb2Qt, Xkb2Qt, + Xkb2Qt, Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq @@ -471,7 +472,7 @@ QVector QXkbCommon::toKeysym(QKeyEvent *event) } else if (event->modifiers() & Qt::KeypadModifier) { if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9) keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0)); - } else if (isLatin(qtKey) && event->text().isUpper()) { + } else if (isLatin1(qtKey) && event->text().isUpper()) { keysyms.append(qtKey); } @@ -523,7 +524,7 @@ int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifie // With standard shortcuts we should prefer a latin character, this is // for checks like "some qkeyevent == QKeySequence::Copy" to work even // when using for example 'russian' keyboard layout. - if (!QXkbCommon::isLatin(keysym)) { + if (!QXkbCommon::isLatin1(keysym)) { xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, code); if (latinKeysym != XKB_KEY_NoSymbol) keysym = latinKeysym; @@ -546,7 +547,7 @@ static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers mod } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { // numeric keypad keys qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0); - } else if (QXkbCommon::isLatin(keysym)) { + } else if (QXkbCommon::isLatin1(keysym)) { qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym); } else { // check if we have a direct mapping @@ -678,7 +679,7 @@ QList QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event, Qt::KeyboardModifiers neededMods = ModsTbl[i]; if ((modifiers & neededMods) == neededMods) { if (i == 8) { - if (isLatin(baseQtKey)) + if (isLatin1(baseQtKey)) continue; // add a latin key as a fall back key sym = lookupLatinKeysym(state, keycode); @@ -733,7 +734,7 @@ void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap) for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) { for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &keysyms); - if (keysyms && isLatin(keysyms[0])) + if (keysyms && isLatin1(keysyms[0])) nrLatinKeys++; if (nrLatinKeys > 10) // arbitrarily chosen threshold return; @@ -766,7 +767,7 @@ xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keyco xkb_level_index_t level = xkb_state_key_get_level(state, keycode, layout); if (xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms) != 1) continue; - if (isLatin(syms[0])) { + if (isLatin1(syms[0])) { sym = syms[0]; break; } diff --git a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h index 561eae03db..8389bd8f5a 100644 --- a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h +++ b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon_p.h @@ -94,8 +94,8 @@ public: static void verifyHasLatinLayout(xkb_keymap *keymap); static xkb_keysym_t lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode); - static bool isLatin(xkb_keysym_t sym) { - return ((sym >= 'a' && sym <= 'z') || (sym >= 'A' && sym <= 'Z')); + static bool isLatin1(xkb_keysym_t sym) { + return sym <= 0xff; } static bool isKeypad(xkb_keysym_t sym) { return sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9; diff --git a/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp index 8cd7f9b368..406f84663f 100644 --- a/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp +++ b/qtbase/src/platformsupport/kmsconvenience/qkmsdevice.cpp @@ -318,7 +318,7 @@ QPlatformScreen *QKmsDevice::createScreenForConnector(drmModeResPtr resources, if (current < 0 && crtc_mode.clock != 0) { modes << crtc_mode; - current = mode.size() - 1; + current = modes.size() - 1; } if (configuration == OutputConfigCurrent) diff --git a/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index 9153fd20bb..255ea5e33e 100644 --- a/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ b/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp @@ -194,6 +194,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const " \n" " \n" " \n" + " \n" + " \n" + " \n" " \n" ); @@ -913,8 +916,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) } case QAccessible::NameChanged: { if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); + QAccessibleInterface *iface = event->accessibleInterface(); + if (!iface) { + qCDebug(lcAccessibilityAtspi, + "NameChanged event from invalid accessible."); + return; + } + + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments( + QLatin1String("accessible-name"), 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("PropertyChange"), args); } @@ -922,8 +934,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) } case QAccessible::DescriptionChanged: { if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments(QLatin1String("accessible-description"), 0, 0, variantForPath(path)); + QAccessibleInterface *iface = event->accessibleInterface(); + if (!iface) { + qCDebug(lcAccessibilityAtspi, + "DescriptionChanged event from invalid accessible."); + return; + } + + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments( + QLatin1String("accessible-description"), 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Description)))); sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("PropertyChange"), args); } @@ -1038,7 +1059,9 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) // Combo Box with AT-SPI likes to be special // It requires a name-change to update caches and then selection-changed QString path = pathForInterface(iface); - QVariantList args1 = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); + QVariantList args1 = packDBusSignalArguments( + QLatin1String("accessible-name"), 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("PropertyChange"), args1); QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0)))); @@ -1358,6 +1381,26 @@ void AtSpiAdaptor::registerApplication() delete registry; } +namespace { +QString accessibleIdForAccessible(QAccessibleInterface *accessible) +{ + QString result; + while (accessible) { + if (!result.isEmpty()) + result.prepend(QLatin1Char('.')); + if (auto obj = accessible->object()) { + const QString name = obj->objectName(); + if (!name.isEmpty()) + result.prepend(name); + else + result.prepend(QString::fromUtf8(obj->metaObject()->className())); + } + accessible = accessible->parent(); + } + return result; +} +} // namespace + // Accessible bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) { @@ -1441,6 +1484,9 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS children << ref; } connection.send(message.createReply(QVariant::fromValue(children))); + } else if (function == QLatin1String("GetAccessibleId")) { + sendReply(connection, message, + QVariant::fromValue(QDBusVariant(accessibleIdForAccessible(interface)))); } else { qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path(); return false; diff --git a/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp b/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp index 45ddc8e496..cc734abc63 100644 --- a/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp +++ b/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp @@ -69,6 +69,21 @@ QT_BEGIN_NAMESPACE DBusConnection::DBusConnection(QObject *parent) : QObject(parent), m_a11yConnection(QString()), m_enabled(false) { + // If the bus is explicitly set via env var it overrides everything else. + QByteArray addressEnv = qgetenv("AT_SPI_BUS_ADDRESS"); + if (!addressEnv.isEmpty()) { + // Only connect on next loop run, connections to our enabled signal are + // only established after the ctor returns. + QMetaObject::invokeMethod( + this, + [this, addressEnv] { + m_enabled = true; + connectA11yBus(QString::fromLocal8Bit(addressEnv)); + }, + Qt::QueuedConnection); + return; + } + // Start monitoring if "org.a11y.Bus" is registered as DBus service. QDBusConnection c = QDBusConnection::sessionBus(); if (!c.isConnected()) { diff --git a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp index 1a3cab275d..2abe039126 100644 --- a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp +++ b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp @@ -51,6 +51,9 @@ #include #include +#include +#include + #if QT_CONFIG(dbus) // These QtCore includes are needed for xdg-desktop-portal support #include @@ -58,6 +61,8 @@ #include #include +#include + #include #include #include @@ -142,6 +147,8 @@ static inline bool detectWebBrowser(const QByteArray &desktop, } if (desktop == QByteArray("KDE")) { + if (checkExecutable(QStringLiteral("kde-open5"), browser)) + return true; // Konqueror launcher if (checkExecutable(QStringLiteral("kfmclient"), browser)) { browser->append(QLatin1String(" exec")); @@ -296,8 +303,135 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) return QDBusConnection::sessionBus().call(message); } + +namespace { +struct XDGDesktopColor +{ + double r = 0; + double g = 0; + double b = 0; + + QColor toQColor() const + { + constexpr auto rgbMax = 255; + return { static_cast(r * rgbMax), static_cast(g * rgbMax), + static_cast(b * rgbMax) }; + } +}; + +const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) +{ + argument.beginStructure(); + argument >> myStruct.r >> myStruct.g >> myStruct.b; + argument.endStructure(); + return argument; +} + +class XdgDesktopPortalColorPicker : public QPlatformServiceColorPicker +{ + Q_OBJECT +public: + XdgDesktopPortalColorPicker(const QString &parentWindowId, QWindow *parent) + : QPlatformServiceColorPicker(parent), m_parentWindowId(parentWindowId) + { + } + + void pickColor() override + { + // DBus signature: + // PickColor (IN s parent_window, + // IN a{sv} options + // OUT o handle) + // Options: + // handle_token (s) - A string that will be used as the last element of the @handle. + + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.portal.Screenshot"), QStringLiteral("PickColor")); + message << m_parentWindowId << QVariantMap(); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + qWarning("DBus call to pick color failed: %s", + qPrintable(reply.error().message())); + Q_EMIT colorPicked({}); + } else { + QDBusConnection::sessionBus().connect( + QStringLiteral("org.freedesktop.portal.Desktop"), reply.value().path(), + QStringLiteral("org.freedesktop.portal.Request"), QStringLiteral("Response"), this, + // clang-format off + SLOT(gotColorResponse(uint,QVariantMap)) + // clang-format on + ); + } + }); + } + +private Q_SLOTS: + void gotColorResponse(uint result, const QVariantMap &map) + { + if (result != 0) + return; + XDGDesktopColor color{}; + map.value(QStringLiteral("color")).value() >> color; + Q_EMIT colorPicked(color.toQColor()); + deleteLater(); + } + +private: + const QString m_parentWindowId; +}; +} // namespace + #endif // QT_CONFIG(dbus) +QGenericUnixServices::QGenericUnixServices() +{ +#if QT_CONFIG(dbus) + if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall( + QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get")); + message << QStringLiteral("org.freedesktop.portal.Screenshot") + << QStringLiteral("version"); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (!reply.isError() && reply.value().toUInt() >= 2) + m_hasScreenshotPortalWithColorPicking = true; + }); + +#endif +} + +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ +#if QT_CONFIG(dbus) + // Make double sure that we are in a wayland environment. In particular check + // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. + // Outside wayland we'll rather rely on other means than the XDG desktop portal. + if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") + || QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { + return new XdgDesktopPortalColorPicker(portalWindowIdentifier(parent), parent); + } + return nullptr; +#else + Q_UNUSED(parent); + return nullptr; +#endif +} + QByteArray QGenericUnixServices::desktopEnvironment() const { static const QByteArray result = detectDesktopEnvironment(); @@ -352,6 +486,8 @@ bool QGenericUnixServices::openDocument(const QUrl &url) } #else +QGenericUnixServices::QGenericUnixServices() = default; + QByteArray QGenericUnixServices::desktopEnvironment() const { return QByteArrayLiteral("UNKNOWN"); @@ -371,6 +507,30 @@ bool QGenericUnixServices::openDocument(const QUrl &url) return false; } +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + #endif // QT_NO_MULTIPROCESS +QString QGenericUnixServices::portalWindowIdentifier(QWindow *window) +{ + if (QGuiApplication::platformName() == QLatin1String("xcb")) + return QStringLiteral("x11:") + QString::number(window->winId(), 16); + return QString(); +} + +bool QGenericUnixServices::hasCapability(Capability capability) const +{ + switch (capability) { + case Capability::ColorPicking: + return m_hasScreenshotPortalWithColorPicking; + } + return false; +} + QT_END_NAMESPACE + +#include "qgenericunixservices.moc" diff --git a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h index 8ac3de6f03..30924e64bd 100644 --- a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h +++ b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h @@ -59,16 +59,21 @@ QT_BEGIN_NAMESPACE class QGenericUnixServices : public QPlatformServices { public: - QGenericUnixServices() {} + QGenericUnixServices(); QByteArray desktopEnvironment() const override; + bool hasCapability(Capability capability) const override; bool openUrl(const QUrl &url) override; bool openDocument(const QUrl &url) override; + QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr) override; + + virtual QString portalWindowIdentifier(QWindow *window); private: QString m_webBrowser; QString m_documentLauncher; + bool m_hasScreenshotPortalWithColorPicking = false; }; QT_END_NAMESPACE diff --git a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp index 09470bccc6..cc7c7d4d8a 100644 --- a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp +++ b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp @@ -69,6 +69,7 @@ const QString MenuBarPath = QLatin1String("/MenuBar"); */ QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &serviceName) : QObject(parent) + , m_serviceName(serviceName) , m_connection(serviceName.isNull() ? QDBusConnection::sessionBus() : QDBusConnection::connectToBus(QDBusConnection::SessionBus, serviceName)) , m_dbusWatcher(new QDBusServiceWatcher(StatusNotifierWatcherService, m_connection, QDBusServiceWatcher::WatchForRegistration, this)) @@ -83,6 +84,12 @@ QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &service #endif } +QDBusMenuConnection::~QDBusMenuConnection() +{ + if (!m_serviceName.isEmpty() && m_connection.isConnected()) + QDBusConnection::disconnectFromBus(m_serviceName); +} + void QDBusMenuConnection::dbusError(const QDBusError &error) { qWarning() << "QDBusTrayIcon encountered a D-Bus error:" << error; @@ -105,13 +112,7 @@ void QDBusMenuConnection::unregisterTrayIconMenu(QDBusTrayIcon *item) bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) { - bool success = connection().registerService(item->instanceId()); - if (!success) { - qWarning() << "failed to register service" << item->instanceId(); - return false; - } - - success = connection().registerObject(StatusNotifierItemPath, item); + bool success = connection().registerObject(StatusNotifierItemPath, item); if (!success) { unregisterTrayIcon(item); qWarning() << "failed to register" << item->instanceId() << StatusNotifierItemPath; @@ -126,21 +127,18 @@ bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) bool QDBusMenuConnection::registerTrayIconWithWatcher(QDBusTrayIcon *item) { + Q_UNUSED(item); QDBusMessage registerMethod = QDBusMessage::createMethodCall( StatusNotifierWatcherService, StatusNotifierWatcherPath, StatusNotifierWatcherService, QLatin1String("RegisterStatusNotifierItem")); - registerMethod.setArguments(QVariantList() << item->instanceId()); + registerMethod.setArguments(QVariantList() << m_connection.baseService()); return m_connection.callWithCallback(registerMethod, this, SIGNAL(trayIconRegistered()), SLOT(dbusError(QDBusError))); } -bool QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) +void QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) { unregisterTrayIconMenu(item); connection().unregisterObject(StatusNotifierItemPath); - bool success = connection().unregisterService(item->instanceId()); - if (!success) - qWarning() << "failed to unregister service" << item->instanceId(); - return success; } #endif // QT_NO_SYSTEMTRAYICON diff --git a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h index f484795fbb..97bdfabb85 100644 --- a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h +++ b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h @@ -70,6 +70,7 @@ class QDBusMenuConnection : public QObject public: QDBusMenuConnection(QObject *parent = nullptr, const QString &serviceName = QString()); + ~QDBusMenuConnection(); QDBusConnection connection() const { return m_connection; } QDBusServiceWatcher *dbusWatcher() const { return m_dbusWatcher; } bool isStatusNotifierHostRegistered() const { return m_statusNotifierHostRegistered; } @@ -78,7 +79,7 @@ public: void unregisterTrayIconMenu(QDBusTrayIcon *item); bool registerTrayIcon(QDBusTrayIcon *item); bool registerTrayIconWithWatcher(QDBusTrayIcon *item); - bool unregisterTrayIcon(QDBusTrayIcon *item); + void unregisterTrayIcon(QDBusTrayIcon *item); #endif // QT_NO_SYSTEMTRAYICON Q_SIGNALS: @@ -90,6 +91,7 @@ private Q_SLOTS: void dbusError(const QDBusError &error); private: + QString m_serviceName; QDBusConnection m_connection; QDBusServiceWatcher *m_dbusWatcher; bool m_statusNotifierHostRegistered; diff --git a/qtbase/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon.cpp b/qtbase/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon.cpp index bede70975a..76bfec5e5a 100644 --- a/qtbase/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon.cpp +++ b/qtbase/src/platformsupport/themes/genericunix/dbustray/qdbustrayicon.cpp @@ -71,8 +71,12 @@ Q_LOGGING_CATEGORY(qLcTray, "qt.qpa.tray") static QString iconTempPath() { QString tempPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); - if (!tempPath.isEmpty()) + if (!tempPath.isEmpty()) { + QString flatpakId = qEnvironmentVariable("FLATPAK_ID"); + if (!flatpakId.isEmpty() && QFileInfo::exists(QLatin1String("/.flatpak-info"))) + tempPath += QLatin1String("/app/") + flatpakId; return tempPath; + } tempPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); 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/api/qeglfscursor.cpp b/qtbase/src/plugins/platforms/eglfs/api/qeglfscursor.cpp index 98e05195ee..1e92f43dec 100644 --- a/qtbase/src/plugins/platforms/eglfs/api/qeglfscursor.cpp +++ b/qtbase/src/plugins/platforms/eglfs/api/qeglfscursor.cpp @@ -343,8 +343,7 @@ void QEglFSCursor::paintOnScreen() // screens are siblings of each other. When not enabled, the sibling list // only contains m_screen itself. for (QPlatformScreen *screen : m_screen->virtualSiblings()) { - if (screen->geometry().contains(cr.topLeft().toPoint() + m_cursor.hotSpot) - && QOpenGLContext::currentContext()->screen() == screen->screen()) + if (screen->geometry().contains(cr.topLeft().toPoint() + m_cursor.hotSpot)) { cr.translate(-screen->geometry().topLeft()); const QSize screenSize = screen->geometry().size(); @@ -468,11 +467,12 @@ void QEglFSCursor::draw(const QRectF &r) { StateSaver stateSaver; - QEglFSCursorData &gfx = static_cast(QOpenGLContext::currentContext()->handle())->cursorData; - if (!gfx.program) { - // one time initialization + // one time initialization + if (!QOpenGLFunctions::d_ptr) initializeOpenGLFunctions(); + QEglFSCursorData &gfx = static_cast(QOpenGLContext::currentContext()->handle())->cursorData; + if (!gfx.program) { createShaderPrograms(); if (!gfx.atlasTexture) { diff --git a/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp index 645a0ae2e9..3e0e406f1a 100644 --- a/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp +++ b/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp @@ -179,7 +179,7 @@ void QEglFSKmsEventReader::create(QEglFSKmsDevice *device) m_device = device; - qCDebug(qLcEglfsKmsDebug, "Initalizing event reader for device %p fd %d", + qCDebug(qLcEglfsKmsDebug, "Initializing event reader for device %p fd %d", m_device, m_device->fd()); m_thread = new QEglFSKmsEventReaderThread(m_device->fd()); diff --git a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp index 141fb68a23..d4294d425a 100644 --- a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp +++ b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -122,11 +122,13 @@ QOffscreenIntegration::QOffscreenIntegration() #endif m_services.reset(new QPlatformServices); - QWindowSystemInterface::handleScreenAdded(new QOffscreenScreen); + m_screen = new QOffscreenScreen; + QWindowSystemInterface::handleScreenAdded(m_screen); } QOffscreenIntegration::~QOffscreenIntegration() { + QWindowSystemInterface::handleScreenRemoved(m_screen); } void QOffscreenIntegration::initialize() diff --git a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h index 0ea90f6c2f..fe00fde07c 100644 --- a/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h +++ b/qtbase/src/plugins/platforms/offscreen/qoffscreenintegration.h @@ -84,6 +84,7 @@ protected: #endif QScopedPointer m_inputContext; QScopedPointer m_services; + QPlatformScreen *m_screen; mutable QScopedPointer m_nativeInterface; }; diff --git a/qtbase/src/plugins/platforms/windows/qwindowsglcontext.cpp b/qtbase/src/plugins/platforms/windows/qwindowsglcontext.cpp index 6fa5a8a2b3..1f49f0aefd 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -162,19 +163,25 @@ QFunctionPointer QWindowsOpengl32DLL::resolve(const char *name) bool QWindowsOpengl32DLL::init(bool softwareRendering) { - const QByteArray opengl32 = QByteArrayLiteral("opengl32.dll"); - const QByteArray swopengl = QByteArrayLiteral("opengl32sw.dll"); + const QByteArray opengl32 = QByteArrayLiteral("opengl32"); + const QByteArray swopengl = QByteArrayLiteral("opengl32sw"); + bool useSystemLib = false; QByteArray openglDll = qgetenv("QT_OPENGL_DLL"); - if (openglDll.isEmpty()) + if (openglDll.isEmpty()) { openglDll = softwareRendering ? swopengl : opengl32; + useSystemLib = !softwareRendering; + } openglDll = openglDll.toLower(); m_nonOpengl32 = openglDll != opengl32; qCDebug(lcQpaGl) << "Qt: Using WGL and OpenGL from" << openglDll; - m_lib = ::LoadLibraryA(openglDll.constData()); + if (useSystemLib) + m_lib = QSystemLibrary::load((wchar_t*)(QString::fromLatin1(openglDll).utf16())); + else + m_lib = LoadLibraryA(openglDll.constData()); if (!m_lib) { qErrnoWarning(::GetLastError(), "Failed to load %s", openglDll.constData()); return false; @@ -184,7 +191,7 @@ bool QWindowsOpengl32DLL::init(bool softwareRendering) // Load opengl32.dll always. GDI functions like ChoosePixelFormat do // GetModuleHandle for opengl32.dll and behave differently (and call back into // opengl32) when the module is present. This is fine for dummy contexts and windows. - ::LoadLibraryA("opengl32.dll"); + QSystemLibrary::load(L"opengl32"); } wglCreateContext = reinterpret_cast(resolve("wglCreateContext")); diff --git a/qtbase/src/plugins/platforms/windows/qwindowsopengltester.cpp b/qtbase/src/plugins/platforms/windows/qwindowsopengltester.cpp index d7d186e804..9eb4011bf2 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowsopengltester.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowsopengltester.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #ifndef QT_NO_OPENGL #include @@ -396,7 +397,7 @@ bool QWindowsOpenGLTester::testDesktopGL() // Test #1: Load opengl32.dll and try to resolve an OpenGL 2 function. // This will typically fail on systems that do not have a real OpenGL driver. - lib = LoadLibraryA("opengl32.dll"); + lib = QSystemLibrary::load(L"opengl32"); if (lib) { CreateContext = reinterpret_cast( reinterpret_cast(::GetProcAddress(lib, "wglCreateContext"))); diff --git a/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp b/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp index 1afa00cfc9..118af6ce73 100644 --- a/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp +++ b/qtbase/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include diff --git a/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp index 9e2a37a6a1..e6e175a118 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -488,12 +488,12 @@ void QXcbConnection::printXcbError(const char *message, xcb_generic_error_t *err uint clamped_error_code = qMin(error->error_code, (sizeof(xcb_errors) / sizeof(xcb_errors[0])) - 1); uint clamped_major_code = qMin(error->major_code, (sizeof(xcb_protocol_request_codes) / sizeof(xcb_protocol_request_codes[0])) - 1); - qCWarning(lcQpaXcb, "%s: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d", - message, - int(error->error_code), xcb_errors[clamped_error_code], - int(error->sequence), int(error->resource_id), - int(error->major_code), xcb_protocol_request_codes[clamped_major_code], - int(error->minor_code)); + qCDebug(lcQpaXcb, "%s: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d", + message, + int(error->error_code), xcb_errors[clamped_error_code], + int(error->sequence), int(error->resource_id), + int(error->major_code), xcb_protocol_request_codes[clamped_major_code], + int(error->minor_code)); } static Qt::MouseButtons translateMouseButtons(int s) @@ -702,6 +702,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window); if (virtualDesktop) virtualDesktop->updateWorkArea(); + } else if (propertyNotify->atom == atom(QXcbAtom::_NET_SUPPORTED)) { + m_wmSupport->updateNetWMAtoms(); } else { HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); } @@ -750,7 +752,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_XKB_NEW_KEYBOARD_NOTIFY: { xcb_xkb_new_keyboard_notify_event_t *ev = &xkb_event->new_keyboard_notify; if (ev->changed & XCB_XKB_NKN_DETAIL_KEYCODES) - m_keyboard->updateKeymap(ev); + m_keyboard->updateKeymap(); break; } default: 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/qxcbkeyboard.cpp b/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp index efecd7e2d1..e8286381a2 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -365,17 +365,6 @@ void QXcbKeyboard::updateKeymap(xcb_mapping_notify_event_t *event) updateKeymap(); } -void QXcbKeyboard::updateKeymap(xcb_xkb_new_keyboard_notify_event_t *event) -{ - if (!event) - return; - - if (event->deviceID != event->oldDeviceID) - m_config = false; - - updateKeymap(); -} - void QXcbKeyboard::updateKeymap() { KeysymModifierMap keysymMods; @@ -383,6 +372,8 @@ void QXcbKeyboard::updateKeymap() keysymMods = keysymsToModifiers(); updateModifiers(keysymMods); + m_config = true; + if (!m_xkbContext) { m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES)); if (!m_xkbContext) { @@ -398,13 +389,8 @@ void QXcbKeyboard::updateKeymap() if (connection()->hasXKB()) { m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), xcb_connection(), core_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS)); - if (m_xkbKeymap) { - if (m_config) - m_xkbState.reset(xkb_state_new(m_xkbKeymap.get())); - else - m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), xcb_connection(), core_device_id)); - - } + if (m_xkbKeymap) + m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), xcb_connection(), core_device_id)); } else { m_xkbKeymap.reset(keymapFromCore(keysymMods)); if (m_xkbKeymap) @@ -425,8 +411,6 @@ void QXcbKeyboard::updateKeymap() updateXKBMods(); QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get()); - - m_config = true; } QList QXcbKeyboard::possibleKeys(const QKeyEvent *event) const diff --git a/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.h b/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.h index b652c85fe3..0ee08aeff2 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -69,7 +69,6 @@ public: Qt::KeyboardModifiers translateModifiers(int s) const; void updateKeymap(xcb_mapping_notify_event_t *event); - void updateKeymap(xcb_xkb_new_keyboard_notify_event_t *event); void updateKeymap(); QList possibleKeys(const QKeyEvent *event) const; diff --git a/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp b/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp index ffda7b6a57..863e961f4c 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -564,11 +564,6 @@ void QXcbWindow::setGeometry(const QRect &rect) { QPlatformWindow::setGeometry(rect); - if (shouldDeferTask(Task::SetGeometry)) { - m_deferredGeometry = rect; - return; - } - propagateSizeHints(); QXcbScreen *currentScreen = xcbScreen(); @@ -693,9 +688,6 @@ void QXcbWindow::setVisible(bool visible) void QXcbWindow::show() { - if (shouldDeferTask(Task::Map)) - return; - if (window()->isTopLevel()) { // update WM_NORMAL_HINTS @@ -706,7 +698,7 @@ void QXcbWindow::show() if (isTransient(window())) { const QWindow *tp = window()->transientParent(); if (tp && tp->handle()) - transientXcbParent = static_cast(tp->handle())->winId(); + transientXcbParent = tp->handle()->winId(); // Default to client leader if there is no transient parent, else modal dialogs can // be hidden by their parents. if (!transientXcbParent) @@ -746,10 +738,6 @@ void QXcbWindow::show() void QXcbWindow::hide() { - if (shouldDeferTask(Task::Unmap)) - return; - - m_wmStateValid = false; xcb_unmap_window(xcb_connection(), m_window); // send synthetic UnmapNotify event according to icccm 4.1.4 @@ -909,9 +897,6 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates() void QXcbWindow::setWindowFlags(Qt::WindowFlags flags) { - if (shouldDeferTask(Task::SetWindowFlags)) - return; - Qt::WindowType type = static_cast(int(flags & Qt::WindowType_Mask)); if (type == Qt::ToolTip) @@ -941,8 +926,6 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags) setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput); updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus); - - m_isWmManagedWindow = !(flags & Qt::X11BypassWindowManagerHint); } void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags) @@ -1142,9 +1125,6 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) if (state == m_windowState) return; - if (shouldDeferTask(Task::SetWindowState)) - return; - // unset old state if (m_windowState & Qt::WindowMinimized) xcb_map_window(xcb_connection(), m_window); @@ -1350,6 +1330,12 @@ void QXcbWindow::setWindowIcon(const QIcon &icon) } if (!icon_data.isEmpty()) { + // Ignore icon exceeding maximum xcb request length + if (icon_data.size() > xcb_get_maximum_request_length(xcb_connection())) { + qWarning("Ignoring window icon: Size %llu exceeds maximum xcb request length %u.", + icon_data.size(), xcb_get_maximum_request_length(xcb_connection())); + return; + } xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, @@ -1639,7 +1625,7 @@ void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::W break; } - if ((flags & Qt::FramelessWindowHint) && !(type & QXcbWindowFunctions::KdeOverride)) { + if ((flags & Qt::FramelessWindowHint) && !(types & QXcbWindowFunctions::KdeOverride)) { // override netwm type - quick and easy for KDE noborder atoms.append(atom(QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)); } @@ -1894,10 +1880,6 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event) if (event->window == m_window) { m_mapped = false; QWindowSystemInterface::handleExposeEvent(window(), QRegion()); - if (!m_isWmManagedWindow || parent()) { - m_wmStateValid = true; - handleDeferredTasks(); - } } } @@ -2212,98 +2194,30 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time); } -bool QXcbWindow::shouldDeferTask(Task task) -{ - if (m_wmStateValid) - return false; - - m_deferredTasks.append(task); - return true; -} - -void QXcbWindow::handleDeferredTasks() -{ - Q_ASSERT(m_wmStateValid == true); - if (m_deferredTasks.isEmpty()) - return; - - bool map = false; - bool unmap = false; - - QVector tasks; - for (auto taskIt = m_deferredTasks.rbegin(); taskIt != m_deferredTasks.rend(); ++taskIt) { - if (!tasks.contains(*taskIt)) - tasks.prepend(*taskIt); - } - - for (Task task : tasks) { - switch (task) { - case Task::Map: - map = true; - unmap = false; - break; - case Task::Unmap: - unmap = true; - map = false; - break; - case Task::SetGeometry: - setGeometry(m_deferredGeometry); - break; - case Task::SetWindowFlags: - setWindowFlags(window()->flags()); - break; - case Task::SetWindowState: - setWindowState(window()->windowState()); - break; - } - } - m_deferredTasks.clear(); - - if (map) { - Q_ASSERT(unmap == false); - show(); - } - if (unmap) { - Q_ASSERT(map == false); - hide(); - } -} - void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) { connection()->setTime(event->time); - const bool wmStateChanged = event->atom == atom(QXcbAtom::WM_STATE); - const bool netWmStateChanged = event->atom == atom(QXcbAtom::_NET_WM_STATE); - if (netWmStateChanged || wmStateChanged) { - if (wmStateChanged && !m_wmStateValid && m_isWmManagedWindow) { - // ICCCM 4.1.4 - // Clients that want to re-use a client window (e.g. by mapping it again) - // after withdrawing it must wait for the withdrawal to be complete before - // proceeding. The preferred method for doing this is for clients to wait for - // a window manager to update or remove the WM_STATE property. - m_wmStateValid = true; - handleDeferredTasks(); - } - if (event->state == XCB_PROPERTY_DELETE) + const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE; + + if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) { + if (propertyDeleted) return; - if (wmStateChanged) { + Qt::WindowStates newState = Qt::WindowNoState; + + if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'. auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), 0, m_window, atom(QXcbAtom::WM_STATE), XCB_ATOM_ANY, 0, 1024); if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) { - auto data = static_cast(xcb_get_property_value(reply.get())); - if (reply->length != 0) { - const bool changedToWithdrawn = data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN; - const bool changedToIconic = data[0] == XCB_ICCCM_WM_STATE_ICONIC; - m_minimized = changedToIconic || (changedToWithdrawn && m_minimized); - } + const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get()); + if (reply->length != 0) + m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC + || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized)); } } - // _NET_WM_STATE handling - Qt::WindowStates newState = Qt::WindowNoState; const NetWmStates states = netWmStates(); // _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would // not be visible on the screen if its desktop/viewport were active and its coordinates were @@ -2325,6 +2239,7 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this) connection()->setMouseGrabber(nullptr); } + return; } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins = true; } diff --git a/qtbase/src/plugins/platforms/xcb/qxcbwindow.h b/qtbase/src/plugins/platforms/xcb/qxcbwindow.h index 55af9279b1..6f5c1f5ed9 100644 --- a/qtbase/src/plugins/platforms/xcb/qxcbwindow.h +++ b/qtbase/src/plugins/platforms/xcb/qxcbwindow.h @@ -74,14 +74,6 @@ public: Q_DECLARE_FLAGS(NetWmStates, NetWmState) - enum Task { - Map, - Unmap, - SetGeometry, - SetWindowFlags, - SetWindowState - }; - QXcbWindow(QWindow *window); ~QXcbWindow(); @@ -151,9 +143,6 @@ public: QXcbWindow *toWindow() override; - bool shouldDeferTask(Task task); - void handleDeferredTasks(); - void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global, Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source); @@ -292,11 +281,6 @@ protected: int m_swapInterval = -1; qreal m_sizeHintsScaleFactor = 1.0; - - bool m_wmStateValid = true; - QVector m_deferredTasks; - bool m_isWmManagedWindow = true; - QRect m_deferredGeometry; }; class QXcbForeignWindow : public QXcbWindow diff --git a/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp b/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp index d9d117faeb..640b49daa0 100644 --- a/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp +++ b/qtbase/src/plugins/platformthemes/gtk3/qgtk3menu.cpp @@ -85,6 +85,7 @@ QGtk3MenuItem::QGtk3MenuItem() m_checkable(false), m_checked(false), m_enabled(true), + m_exclusive(false), m_underline(false), m_invalid(true), m_menu(nullptr), diff --git a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp index 0048bbc1e6..396e7f6d1c 100644 --- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp +++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp @@ -102,15 +102,12 @@ const QDBusArgument &operator >>(const QDBusArgument &arg, QXdgDesktopPortalFile class QXdgDesktopPortalFileDialogPrivate { public: - QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog) + QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion) : nativeFileDialog(nativeFileDialog) + , fileChooserPortalVersion(fileChooserPortalVersion) { } - WId winId = 0; - bool directoryMode = false; - bool modal = false; - bool multipleFiles = false; - bool saveFile = false; + QEventLoop loop; QString acceptLabel; QString directory; QString title; @@ -121,19 +118,27 @@ public: QString selectedMimeTypeFilter; QString selectedNameFilter; QStringList selectedFiles; - QPlatformFileDialogHelper *nativeFileDialog = nullptr; + std::unique_ptr nativeFileDialog; + uint fileChooserPortalVersion = 0; + bool failedToOpen = false; + bool directoryMode = false; + bool multipleFiles = false; + bool saveFile = false; }; -QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog) +QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion) : QPlatformFileDialogHelper() - , d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog)) + , d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog, fileChooserPortalVersion)) { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { - connect(d->nativeFileDialog, SIGNAL(accept()), this, SIGNAL(accept())); - connect(d->nativeFileDialog, SIGNAL(reject()), this, SIGNAL(reject())); + connect(d->nativeFileDialog.get(), SIGNAL(accept()), this, SIGNAL(accept())); + connect(d->nativeFileDialog.get(), SIGNAL(reject()), this, SIGNAL(reject())); } + + d->loop.connect(this, SIGNAL(accept()), SLOT(quit())); + d->loop.connect(this, SIGNAL(reject()), SLOT(quit())); } QXdgDesktopPortalFileDialog::~QXdgDesktopPortalFileDialog() @@ -177,7 +182,7 @@ void QXdgDesktopPortalFileDialog::initializeDialog() setDirectory(options()->initialDirectory()); } -void QXdgDesktopPortalFileDialog::openPortal() +void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) { Q_D(QXdgDesktopPortalFileDialog); @@ -185,13 +190,13 @@ void QXdgDesktopPortalFileDialog::openPortal() QLatin1String("/org/freedesktop/portal/desktop"), QLatin1String("org.freedesktop.portal.FileChooser"), d->saveFile ? QLatin1String("SaveFile") : QLatin1String("OpenFile")); - QString parentWindowId = QLatin1String("x11:") + QString::number(d->winId, 16); + QString parentWindowId = QLatin1String("x11:") + QString::number(parent ? parent->winId() : 0, 16); QVariantMap options; if (!d->acceptLabel.isEmpty()) options.insert(QLatin1String("accept_label"), d->acceptLabel); - options.insert(QLatin1String("modal"), d->modal); + options.insert(QLatin1String("modal"), windowModality != Qt::NonModal); options.insert(QLatin1String("multiple"), d->multipleFiles); options.insert(QLatin1String("directory"), d->directoryMode); @@ -290,10 +295,18 @@ void QXdgDesktopPortalFileDialog::openPortal() QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); - connect(watcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) { + connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; - if (reply.isError()) { - Q_EMIT reject(); + // Any error means the dialog is not shown and we need to fallback + d->failedToOpen = reply.isError(); + if (d->failedToOpen) { + if (d->nativeFileDialog) { + d->nativeFileDialog->show(windowFlags, windowModality, parent); + if (d->loop.isRunning()) + d->nativeFileDialog->exec(); + } else { + Q_EMIT reject(); + } } else { QDBusConnection::sessionBus().connect(nullptr, reply.value().path(), @@ -302,6 +315,7 @@ void QXdgDesktopPortalFileDialog::openPortal() this, SLOT(gotResponse(uint,QVariantMap))); } + watcher->deleteLater(); }); } @@ -326,7 +340,7 @@ QUrl QXdgDesktopPortalFileDialog::directory() const { Q_D(const QXdgDesktopPortalFileDialog); - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) + if (d->nativeFileDialog && useNativeFileDialog()) return d->nativeFileDialog->directory(); return d->directory; @@ -348,7 +362,7 @@ QList QXdgDesktopPortalFileDialog::selectedFiles() const { Q_D(const QXdgDesktopPortalFileDialog); - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) + if (d->nativeFileDialog && useNativeFileDialog()) return d->nativeFileDialog->selectedFiles(); QList files; @@ -403,16 +417,13 @@ void QXdgDesktopPortalFileDialog::exec() { Q_D(QXdgDesktopPortalFileDialog); - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) { + if (d->nativeFileDialog && useNativeFileDialog()) { d->nativeFileDialog->exec(); return; } // HACK we have to avoid returning until we emit that the dialog was accepted or rejected - QEventLoop loop; - loop.connect(this, SIGNAL(accept()), SLOT(quit())); - loop.connect(this, SIGNAL(reject()), SLOT(quit())); - loop.exec(); + d->loop.exec(); } void QXdgDesktopPortalFileDialog::hide() @@ -429,13 +440,10 @@ bool QXdgDesktopPortalFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowMo initializeDialog(); - d->modal = windowModality != Qt::NonModal; - d->winId = parent ? parent->winId() : 0; - - if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) + if (d->nativeFileDialog && useNativeFileDialog(OpenFallback)) return d->nativeFileDialog->show(windowFlags, windowModality, parent); - openPortal(); + openPortal(windowFlags, windowModality, parent); return true; } @@ -465,4 +473,21 @@ 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 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 fb324afbd8..b809503122 100644 --- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp +++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp @@ -111,6 +111,7 @@ QXdgDesktopPortalTheme::QXdgDesktopPortalTheme() if (reply.isValid()) { d->fileChooserPortalVersion = reply.value().toUInt(); } + watcher->deleteLater(); }); } @@ -152,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/printsupport/cups/qcupsprintengine.cpp b/qtbase/src/plugins/printsupport/cups/qcupsprintengine.cpp index 43d5e119ad..1f9cbc4565 100644 --- a/qtbase/src/plugins/printsupport/cups/qcupsprintengine.cpp +++ b/qtbase/src/plugins/printsupport/cups/qcupsprintengine.cpp @@ -89,8 +89,10 @@ void QCupsPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &v break; case PPK_Duplex: { QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt()); - if (mode != d->duplex && d->m_printDevice.supportedDuplexModes().contains(mode)) + if (d->m_printDevice.supportedDuplexModes().contains(mode)) { d->duplex = mode; + d->duplexRequestedExplicitly = true; + } break; } case PPK_PrinterName: @@ -277,9 +279,12 @@ void QCupsPrintEnginePrivate::changePrinter(const QString &newPrinter) m_printDevice.swap(printDevice); printerName = m_printDevice.id(); - // Check if new printer supports current settings, otherwise us defaults - if (duplex != QPrint::DuplexAuto && !m_printDevice.supportedDuplexModes().contains(duplex)) + // in case a duplex value was explicitly set, check if new printer supports current value, + // otherwise use device default + if (!duplexRequestedExplicitly || !m_printDevice.supportedDuplexModes().contains(duplex)) { duplex = m_printDevice.defaultDuplexMode(); + duplexRequestedExplicitly = false; + } QPrint::ColorMode colorMode = grayscale ? QPrint::GrayScale : QPrint::Color; if (!m_printDevice.supportedColorModes().contains(colorMode)) grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale; diff --git a/qtbase/src/plugins/printsupport/cups/qcupsprintengine_p.h b/qtbase/src/plugins/printsupport/cups/qcupsprintengine_p.h index c021b0c643..8f81fe478d 100644 --- a/qtbase/src/plugins/printsupport/cups/qcupsprintengine_p.h +++ b/qtbase/src/plugins/printsupport/cups/qcupsprintengine_p.h @@ -100,6 +100,7 @@ private: QStringList cupsOptions; QString cupsTempFile; QPrint::DuplexMode duplex; + bool duplexRequestedExplicitly = false; }; QT_END_NAMESPACE diff --git a/qtbase/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/qtbase/src/plugins/sqldrivers/ibase/qsql_ibase.cpp index 67e7154fa6..40876152e5 100644 --- a/qtbase/src/plugins/sqldrivers/ibase/qsql_ibase.cpp +++ b/qtbase/src/plugins/sqldrivers/ibase/qsql_ibase.cpp @@ -817,7 +817,7 @@ bool QIBaseResultPrivate::writeArray(int column, const QList &list) ba.resize(int(bufLen)); if (list.size() > arraySize) { - error = QLatin1String("Array size missmatch: size of %1 is %2, size of provided list is %3"); + error = QLatin1String("Array size mismatch: size of %1 is %2, size of provided list is %3"); error = error.arg(QLatin1String(sqlname)).arg(arraySize).arg(list.size()); q->setLastError(QSqlError(error, QLatin1String(""), QSqlError::StatementError)); return false; diff --git a/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp index 7ca055eea2..6bc9d46755 100644 --- a/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp +++ b/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp @@ -223,7 +223,7 @@ public: struct QMyField { char *outField = nullptr; - MYSQL_FIELD *myField = nullptr; + const MYSQL_FIELD *myField = nullptr; QMetaType::Type type = QMetaType::UnknownType; my_bool nullIndicator = false; ulong bufLength = 0ul; @@ -361,7 +361,7 @@ static bool qIsInteger(int t) void QMYSQLResultPrivate::bindBlobs() { int i; - MYSQL_FIELD *fieldInfo; + const MYSQL_FIELD *fieldInfo; MYSQL_BIND *bind; for(i = 0; i < fields.count(); ++i) { @@ -379,7 +379,6 @@ void QMYSQLResultPrivate::bindBlobs() bool QMYSQLResultPrivate::bindInValues() { MYSQL_BIND *bind; - char *field; int i = 0; if (!meta) @@ -392,35 +391,34 @@ bool QMYSQLResultPrivate::bindInValues() inBinds = new MYSQL_BIND[fields.size()]; memset(inBinds, 0, fields.size() * sizeof(MYSQL_BIND)); - MYSQL_FIELD *fieldInfo; + const MYSQL_FIELD *fieldInfo; while((fieldInfo = mysql_fetch_field(meta))) { + bind = &inBinds[i]; + QMyField &f = fields[i]; f.myField = fieldInfo; - + bind->buffer_length = f.bufLength = fieldInfo->length + 1; + bind->buffer_type = fieldInfo->type; f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags); if (qIsBlob(fieldInfo->type)) { // the size of a blob-field is available as soon as we call // mysql_stmt_store_result() // after mysql_stmt_exec() in QMYSQLResult::exec() - fieldInfo->length = 0; + bind->buffer_length = f.bufLength = 0; hasBlobs = true; } else if (qIsInteger(f.type)) { - fieldInfo->length = 8; + bind->buffer_length = f.bufLength = 8; } else { - fieldInfo->type = MYSQL_TYPE_STRING; + bind->buffer_type = MYSQL_TYPE_STRING; } - bind = &inBinds[i]; - field = new char[fieldInfo->length + 1]; - memset(field, 0, fieldInfo->length + 1); - bind->buffer_type = fieldInfo->type; - bind->buffer = field; - bind->buffer_length = f.bufLength = fieldInfo->length + 1; bind->is_null = &f.nullIndicator; bind->length = &f.bufLength; bind->is_unsigned = fieldInfo->flags & UNSIGNED_FLAG ? 1 : 0; - f.outField=field; + + char *field = new char[bind->buffer_length + 1]{}; + bind->buffer = f.outField = field; ++i; } @@ -1369,20 +1367,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/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/qpagesetupdialog_unix.cpp b/qtbase/src/printsupport/dialogs/qpagesetupdialog_unix.cpp index 78e5b8d1ef..2a28516719 100644 --- a/qtbase/src/printsupport/dialogs/qpagesetupdialog_unix.cpp +++ b/qtbase/src/printsupport/dialogs/qpagesetupdialog_unix.cpp @@ -532,6 +532,7 @@ void QPageSetupWidget::updateWidget() void QPageSetupWidget::setupPrinter() const { m_printer->setPageLayout(m_pageLayout); + m_printer->setPageOrientation(m_pageLayout.orientation()); #if QT_CONFIG(cups) QCUPSSupport::PagesPerSheet pagesPerSheet = qvariant_cast(m_ui.pagesPerSheetCombo->currentData() ); 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/qprintdialog_unix.cpp b/qtbase/src/printsupport/dialogs/qprintdialog_unix.cpp index c5b845f4ec..5a5ba1d0a8 100644 --- a/qtbase/src/printsupport/dialogs/qprintdialog_unix.cpp +++ b/qtbase/src/printsupport/dialogs/qprintdialog_unix.cpp @@ -702,13 +702,14 @@ void QPrintDialogPrivate::selectPrinter(const QPrinter::OutputFormat outputForma else options.grayscale->setChecked(true); - // keep duplex value explicitly set by user, if any, and selected printer supports it; - // use device default otherwise + // duplex priorities to be as follows: + // 1) a user-selected duplex value in the dialog has highest prority + // 2) duplex value set in the QPrinter QPrint::DuplexMode duplex; if (explicitDuplexMode != QPrint::DuplexAuto && supportedDuplexMode.contains(explicitDuplexMode)) duplex = explicitDuplexMode; else - duplex = top->d->m_currentPrintDevice.defaultDuplexMode(); + duplex = static_cast(p->duplex()); switch (duplex) { case QPrint::DuplexNone: options.noDuplex->setChecked(true); break; @@ -1242,10 +1243,10 @@ void QUnixPrintWidgetPrivate::_q_printerChanged(int index) QString filename = widget.filename->text(); widget.filename->setText(filename); widget.lOutput->setEnabled(true); - if (optionsPane) - optionsPane->selectPrinter(QPrinter::PdfFormat); printer->setOutputFormat(QPrinter::PdfFormat); m_currentPrintDevice = QPrintDevice(); + if (optionsPane) + optionsPane->selectPrinter(QPrinter::PdfFormat); return; } } 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/sql/kernel/qsqlquery.cpp b/qtbase/src/sql/kernel/qsqlquery.cpp index 32c6166c79..60afef10c3 100644 --- a/qtbase/src/sql/kernel/qsqlquery.cpp +++ b/qtbase/src/sql/kernel/qsqlquery.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtSql module of the Qt Toolkit. @@ -377,6 +377,10 @@ bool QSqlQuery::exec(const QString& query) QElapsedTimer t; t.start(); #endif + if (!driver()) { + qWarning("QSqlQuery::exec: called before driver has been set up"); + return false; + } if (d->ref.loadRelaxed() != 1) { bool fo = isForwardOnly(); *this = QSqlQuery(driver()->createResult()); diff --git a/qtbase/src/testlib/qabstractitemmodeltester.cpp b/qtbase/src/testlib/qabstractitemmodeltester.cpp index 8efb9c53d4..ce062f66d7 100644 --- a/qtbase/src/testlib/qabstractitemmodeltester.cpp +++ b/qtbase/src/testlib/qabstractitemmodeltester.cpp @@ -454,7 +454,7 @@ void QAbstractItemModelTesterPrivate::parent() // Common error test #2, make sure that a second level index has a parent // that is the first level index. - if (model->rowCount(topIndex) > 0) { + if (model->rowCount(topIndex) > 0 && model->columnCount(topIndex) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); MODELTESTER_VERIFY(childIndex.isValid()); MODELTESTER_COMPARE(model->parent(childIndex), topIndex); diff --git a/qtbase/src/testlib/qasciikey.cpp b/qtbase/src/testlib/qasciikey.cpp index 9a308da2bc..93498b256f 100644 --- a/qtbase/src/testlib/qasciikey.cpp +++ b/qtbase/src/testlib/qasciikey.cpp @@ -498,6 +498,11 @@ char QTest::keyToAscii(Qt::Key key) case Qt::Key_LaunchE : return 0; // = 0x10b0, case Qt::Key_LaunchF : return 0; // = 0x10b1, + // Keypad navigation keys + case Qt::Key_Select : return 0; // = 0x01010000 + case Qt::Key_Yes : return 0; // = 0x01010001 + case Qt::Key_No : return 0; // = 0x01010002 + default: QTEST_ASSERT(false); return 0; } } diff --git a/qtbase/src/testlib/qtestresult.cpp b/qtbase/src/testlib/qtestresult.cpp index 88028aac6e..2e89930776 100644 --- a/qtbase/src/testlib/qtestresult.cpp +++ b/qtbase/src/testlib/qtestresult.cpp @@ -251,7 +251,8 @@ bool QTestResult::verify(bool statement, const char *statementStr, { QTEST_ASSERT(statementStr); - char msg[1024] = {'\0'}; + char msg[1024]; + msg[0] = '\0'; if (QTestLog::verboseLevel() >= 2) { qsnprintf(msg, 1024, "QVERIFY(%s)", statementStr); @@ -309,7 +310,8 @@ static bool compareHelper(bool success, const char *failureMsg, bool hasValues = true) { const size_t maxMsgLen = 1024; - char msg[maxMsgLen] = {'\0'}; + char msg[maxMsgLen]; + msg[0] = '\0'; QTEST_ASSERT(expected); QTEST_ASSERT(actual); diff --git a/qtbase/src/widgets/dialogs/qcolordialog.cpp b/qtbase/src/widgets/dialogs/qcolordialog.cpp index 4247731275..cb325be85c 100644 --- a/qtbase/src/widgets/dialogs/qcolordialog.cpp +++ b/qtbase/src/widgets/dialogs/qcolordialog.cpp @@ -78,7 +78,10 @@ #include "qwindow.h" #include "private/qdialog_p.h" +#include "private/qguiapplication_p.h" +#include +#include #include QT_BEGIN_NAMESPACE @@ -1611,6 +1614,20 @@ void QColorDialogPrivate::_q_newStandard(int r, int c) void QColorDialogPrivate::_q_pickScreenColor() { Q_Q(QColorDialog); + + auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services(); + if (platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) { + if (auto *colorPicker = platformServices->colorPicker(q->windowHandle())) { + q->connect(colorPicker, &QPlatformServiceColorPicker::colorPicked, q, + [q, colorPicker](const QColor &color) { + colorPicker->deleteLater(); + q->setCurrentColor(color); + }); + colorPicker->pickColor(); + return; + } + } + if (!colorPickingEventFilter) colorPickingEventFilter = new QColorPickingEventFilter(this, q); q->installEventFilter(colorPickingEventFilter); diff --git a/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp b/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp index e120817edc..8ea36b5427 100644 --- a/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp +++ b/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp @@ -400,12 +400,7 @@ bool QAbstractItemDelegate::helpEvent(QHelpEvent *event, const QString tooltip = index.isValid() ? d->textForRole(Qt::ToolTipRole, index.data(Qt::ToolTipRole), option.locale, precision) : QString(); - QRect rect; - if (index.isValid()) { - const QRect r = view->visualRect(index); - rect = QRect(view->mapToGlobal(r.topLeft()), r.size()); - } - QToolTip::showText(he->globalPos(), tooltip, view, rect); + QToolTip::showText(he->globalPos(), tooltip, view->viewport(), option.rect); event->setAccepted(!tooltip.isEmpty()); break; } diff --git a/qtbase/src/widgets/itemviews/qlistview.cpp b/qtbase/src/widgets/itemviews/qlistview.cpp index 2b34476642..eebfaa030e 100644 --- a/qtbase/src/widgets/itemviews/qlistview.cpp +++ b/qtbase/src/widgets/itemviews/qlistview.cpp @@ -3386,6 +3386,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()) { @@ -3396,7 +3397,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 934ac581b2..f79bb24249 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/kernel/qwidget.cpp b/qtbase/src/widgets/kernel/qwidget.cpp index 9eba1e001e..26f6d27e18 100644 --- a/qtbase/src/widgets/kernel/qwidget.cpp +++ b/qtbase/src/widgets/kernel/qwidget.cpp @@ -5296,7 +5296,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP QWidgetEffectSourcePrivate *sourced = static_cast (source->d_func()); if (!sourced->context) { - const QRegion effectRgn(rgn.boundingRect()); + const QRegion effectRgn((flags & UseEffectRegionBounds) ? rgn.boundingRect() : rgn); QWidgetPaintContext context(pdev, effectRgn, offset, flags, sharedPainter, repaintManager); sourced->context = &context; if (!sharedPainter) { @@ -5328,6 +5328,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } } #endif // QT_CONFIG(graphicseffect) + flags = flags & ~UseEffectRegionBounds; const bool alsoOnScreen = flags & DrawPaintOnScreen; const bool recursive = flags & DrawRecursive; diff --git a/qtbase/src/widgets/kernel/qwidget_p.h b/qtbase/src/widgets/kernel/qwidget_p.h index eef6b8fa2c..c432f4b5d5 100644 --- a/qtbase/src/widgets/kernel/qwidget_p.h +++ b/qtbase/src/widgets/kernel/qwidget_p.h @@ -229,7 +229,8 @@ public: DontSubtractOpaqueChildren = 0x10, DontDrawOpaqueChildren = 0x20, DontDrawNativeChildren = 0x40, - DontSetCompositionMode = 0x80 + DontSetCompositionMode = 0x80, + UseEffectRegionBounds = 0x100 }; Q_DECLARE_FLAGS(DrawWidgetFlags, DrawWidgetFlag) Q_FLAG(DrawWidgetFlags) diff --git a/qtbase/src/widgets/kernel/qwidgetrepaintmanager.cpp b/qtbase/src/widgets/kernel/qwidgetrepaintmanager.cpp index 02880b34da..0d44e1326d 100644 --- a/qtbase/src/widgets/kernel/qwidgetrepaintmanager.cpp +++ b/qtbase/src/widgets/kernel/qwidgetrepaintmanager.cpp @@ -1018,7 +1018,8 @@ void QWidgetRepaintManager::paintAndFlush() // Paint the rest with composition. if (repaintAllWidgets || !dirtyCopy.isEmpty()) { - QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive; + QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive + | QWidgetPrivate::UseEffectRegionBounds; tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, nullptr, this); } diff --git a/qtbase/src/widgets/styles/qfusionstyle.cpp b/qtbase/src/widgets/styles/qfusionstyle.cpp index f4345d97c4..962912c838 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/qcompleter.cpp b/qtbase/src/widgets/util/qcompleter.cpp index 18b24063d8..ef7207a8a3 100644 --- a/qtbase/src/widgets/util/qcompleter.cpp +++ b/qtbase/src/widgets/util/qcompleter.cpp @@ -1120,6 +1120,8 @@ void QCompleter::setModel(QAbstractItemModel *model) { Q_D(QCompleter); QAbstractItemModel *oldModel = d->proxy->sourceModel(); + if (oldModel == model) + return; #if QT_CONFIG(filesystemmodel) if (qobject_cast(oldModel)) setCompletionRole(Qt::EditRole); // QTBUG-54642, clear FileNameRole set by QFileSystemModel diff --git a/qtbase/src/widgets/widgets/qabstractbutton.cpp b/qtbase/src/widgets/widgets/qabstractbutton.cpp index a128b23950..dc40bf62fb 100644 --- a/qtbase/src/widgets/widgets/qabstractbutton.cpp +++ b/qtbase/src/widgets/widgets/qabstractbutton.cpp @@ -56,6 +56,7 @@ #ifndef QT_NO_ACCESSIBILITY #include "qaccessible.h" #endif +#include #include @@ -1076,19 +1077,19 @@ void QAbstractButton::keyPressEvent(QKeyEvent *e) { Q_D(QAbstractButton); bool next = true; - switch (e->key()) { - case Qt::Key_Enter: - case Qt::Key_Return: - e->ignore(); - break; - case Qt::Key_Select: - case Qt::Key_Space: - if (!e->isAutoRepeat()) { - setDown(true); - repaint(); - d->emitPressed(); - } - break; + + const auto key = static_cast(e->key()); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(key) && !e->isAutoRepeat()) { + setDown(true); + repaint(); + d->emitPressed(); + return; + } + + switch (key) { case Qt::Key_Up: next = false; Q_FALLTHROUGH(); @@ -1153,15 +1154,15 @@ void QAbstractButton::keyReleaseEvent(QKeyEvent *e) if (!e->isAutoRepeat()) d->repeatTimer.stop(); - switch (e->key()) { - case Qt::Key_Select: - case Qt::Key_Space: - if (!e->isAutoRepeat() && d->down) - d->click(); - break; - default: - e->ignore(); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(static_cast(e->key())) && !e->isAutoRepeat() && d->down) { + d->click(); + return; } + + e->ignore(); } /*!\reimp diff --git a/qtbase/src/widgets/widgets/qcombobox.cpp b/qtbase/src/widgets/widgets/qcombobox.cpp index 7a496c27e0..0a3d96647b 100644 --- a/qtbase/src/widgets/widgets/qcombobox.cpp +++ b/qtbase/src/widgets/widgets/qcombobox.cpp @@ -3352,7 +3352,23 @@ void QComboBox::keyPressEvent(QKeyEvent *e) Move move = NoMove; int newIndex = currentIndex(); - switch (e->key()) { + + bool pressLikeButton = !d->lineEdit; +#ifdef QT_KEYPAD_NAVIGATION + pressLikeButton |= QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus(); +#endif + auto key = static_cast(e->key()); + if (pressLikeButton) { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(key)) { + showPopup(); + return; + } + } + + switch (key) { case Qt::Key_Up: if (e->modifiers() & Qt::ControlModifier) break; // pass to line edit for auto completion @@ -3394,26 +3410,11 @@ void QComboBox::keyPressEvent(QKeyEvent *e) return; } break; - case Qt::Key_Space: - if (!d->lineEdit) { - showPopup(); - return; - } - break; - case Qt::Key_Enter: - case Qt::Key_Return: case Qt::Key_Escape: if (!d->lineEdit) e->ignore(); break; #ifdef QT_KEYPAD_NAVIGATION - case Qt::Key_Select: - if (QApplicationPrivate::keypadNavigationEnabled() - && (!hasEditFocus() || !d->lineEdit)) { - showPopup(); - return; - } - break; case Qt::Key_Left: case Qt::Key_Right: if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) diff --git a/qtbase/src/widgets/widgets/qdatetimeedit_p.h b/qtbase/src/widgets/widgets/qdatetimeedit_p.h index d36b6f8f9a..e0df5b5158 100644 --- a/qtbase/src/widgets/widgets/qdatetimeedit_p.h +++ b/qtbase/src/widgets/widgets/qdatetimeedit_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWidgets module of the Qt Toolkit. diff --git a/qtbase/src/widgets/widgets/qgroupbox.cpp b/qtbase/src/widgets/widgets/qgroupbox.cpp index 02a0bed325..3f3eccc370 100644 --- a/qtbase/src/widgets/widgets/qgroupbox.cpp +++ b/qtbase/src/widgets/widgets/qgroupbox.cpp @@ -54,6 +54,8 @@ #include "qaccessible.h" #endif #include +#include +#include #include "qdebug.h" @@ -360,7 +362,10 @@ bool QGroupBox::event(QEvent *e) return true; case QEvent::KeyPress: { QKeyEvent *k = static_cast(e); - if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (!k->isAutoRepeat() && buttonPressKeys.contains(static_cast(k->key()))) { d->pressedControl = QStyle::SC_GroupBoxCheckBox; update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)); return true; @@ -369,7 +374,10 @@ bool QGroupBox::event(QEvent *e) } case QEvent::KeyRelease: { QKeyEvent *k = static_cast(e); - if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (!k->isAutoRepeat() && buttonPressKeys.contains(static_cast(k->key()))) { bool toggle = (d->pressedControl == QStyle::SC_GroupBoxLabel || d->pressedControl == QStyle::SC_GroupBoxCheckBox); d->pressedControl = QStyle::SC_None; diff --git a/qtbase/src/widgets/widgets/qlabel.cpp b/qtbase/src/widgets/widgets/qlabel.cpp index 30ff80cbb0..cf2605c17c 100644 --- a/qtbase/src/widgets/widgets/qlabel.cpp +++ b/qtbase/src/widgets/widgets/qlabel.cpp @@ -421,9 +421,6 @@ void QLabel::setPixmap(const QPixmap &pixmap) d->pixmap = new QPixmap(pixmap); } - if (d->pixmap->depth() == 1 && !d->pixmap->mask()) - d->pixmap->setMask(*((QBitmap *)d->pixmap)); - d->updateLabel(); } diff --git a/qtbase/src/widgets/widgets/qtoolbararealayout.cpp b/qtbase/src/widgets/widgets/qtoolbararealayout.cpp index 493c094cc1..d5f4b59017 100644 --- a/qtbase/src/widgets/widgets/qtoolbararealayout.cpp +++ b/qtbase/src/widgets/widgets/qtoolbararealayout.cpp @@ -1367,7 +1367,7 @@ bool QToolBarAreaLayout::restoreState(QDataStream &stream, const QListsetOrientation(floating ? ((shown & 2) ? Qt::Vertical : Qt::Horizontal) : dock.o); toolBar->setVisible(shown & 1); - toolBar->d_func()->setWindowState(floating, true, rect); + toolBar->d_func()->setWindowState(floating, false, rect); item.preferredSize = item.size; line.toolBarItems.append(item); diff --git a/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp b/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp index 212c0a2e13..3c77b1ba0b 100644 --- a/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp +++ b/qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp @@ -126,8 +126,7 @@ public: void tst_QtConcurrentIterateKernel::instantiate() { - auto future = startThreadEngine(new PrintFor(0, 40)).startAsynchronously(); - future.waitForFinished(); + startThreadEngine(new PrintFor(0, 40)).startBlocking(); QCOMPARE(iterations.loadRelaxed(), 40); } @@ -166,10 +165,8 @@ void tst_QtConcurrentIterateKernel::stresstest() const int times = 50; for (int i = 0; i < times; ++i) { counter.storeRelaxed(0); - // ThreadEngine will delete f when it finishes - auto f = new CountFor(0, iterations); - auto future = f->startAsynchronously(); - future.waitForFinished(); + CountFor f(0, iterations); + f.startBlocking(); QCOMPARE(counter.loadRelaxed(), iterations); } } @@ -177,11 +174,8 @@ void tst_QtConcurrentIterateKernel::stresstest() void tst_QtConcurrentIterateKernel::noIterations() { const int times = 20000; - for (int i = 0; i < times; ++i) { - auto future = startThreadEngine(new IterateKernel(0, 0)) - .startAsynchronously(); - future.waitForFinished(); - } + for (int i = 0; i < times; ++i) + startThreadEngine(new IterateKernel(0, 0)).startBlocking(); } QMutex threadsMutex; @@ -236,10 +230,8 @@ void tst_QtConcurrentIterateKernel::throttling() threads.clear(); - // ThreadEngine will delete f when it finishes - auto f = new ThrottleFor(0, totalIterations); - auto future = f->startAsynchronously(); - future.waitForFinished(); + ThrottleFor f(0, totalIterations); + f.startBlocking(); QCOMPARE(iterations.loadRelaxed(), totalIterations); diff --git a/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp b/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp index ac7be7acdd..81bc64c8d9 100644 --- a/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp +++ b/qtbase/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp @@ -65,9 +65,16 @@ public: void tst_QtConcurrentThreadEngine::runDirectly() { - PrintUser *engine = new PrintUser(); - QFuture f = engine->startAsynchronously(); - f.waitForFinished(); + { + PrintUser engine; + engine.startSingleThreaded(); + engine.startBlocking(); + } + { + PrintUser *engine = new PrintUser(); + QFuture f = engine->startAsynchronously(); + f.waitForFinished(); + } } class StringResultUser : public ThreadEngine @@ -99,10 +106,8 @@ public: void tst_QtConcurrentThreadEngine::result() { - // ThreadEngine will delete 'engine' when it finishes - auto engine = new StringResultUser(); - auto future = engine->startAsynchronously(); - QCOMPARE(future.result(), QString("Foo")); + StringResultUser engine; + QCOMPARE(*engine.startBlocking(), QString("Foo")); } class VoidResultUser : public ThreadEngine @@ -128,9 +133,17 @@ public: void tst_QtConcurrentThreadEngine::runThroughStarter() { - ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); - QFuture f = starter.startAsynchronously(); - QCOMPARE(f.result(), QString("Foo")); + { + ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); + QFuture f = starter.startAsynchronously(); + QCOMPARE(f.result(), QString("Foo")); + } + + { + ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); + QString str = starter.startBlocking(); + QCOMPARE(str, QString("Foo")); + } } class CancelUser : public ThreadEngine @@ -213,6 +226,12 @@ void tst_QtConcurrentThreadEngine::throttle() f.waitForFinished(); QCOMPARE(count.loadRelaxed(), 0); } + + for (int i = 0; i < repeats; ++i) { + ThrottleAlwaysUser t; + t.startBlocking(); + QCOMPARE(count.loadRelaxed(), 0); + } } QSet threads; @@ -249,17 +268,35 @@ void tst_QtConcurrentThreadEngine::threadCount() { const int repeats = 10; for (int i = 0; i < repeats; ++i) { + ThreadCountUser t; + t.startBlocking(); + int count = threads.count(); + int count_expected = QThreadPool::globalInstance()->maxThreadCount() + 1; // +1 for the main thread. + if (count != count_expected) + QEXPECT_FAIL("", "QTBUG-23333", Abort); + QCOMPARE(count, count_expected); + (new ThreadCountUser())->startAsynchronously().waitForFinished(); - const auto count = threads.count(); - const auto maxThreadCount = QThreadPool::globalInstance()->maxThreadCount(); - QVERIFY(count <= maxThreadCount); - QVERIFY(!threads.contains(QThread::currentThread())); + count = threads.count(); + count_expected = QThreadPool::globalInstance()->maxThreadCount(); + if (count != count_expected) + QEXPECT_FAIL("", "QTBUG-23333", Abort); + QCOMPARE(count, count_expected); } // Set the finish flag immediately, this should give us one thread only. for (int i = 0; i < repeats; ++i) { + ThreadCountUser t(true /*finishImmediately*/); + t.startBlocking(); + int count = threads.count(); + if (count != 1) + QEXPECT_FAIL("", "QTBUG-23333", Abort); + QCOMPARE(count, 1); + (new ThreadCountUser(true /*finishImmediately*/))->startAsynchronously().waitForFinished(); - const auto count = threads.count(); + count = threads.count(); + if (count != 1) + QEXPECT_FAIL("", "QTBUG-23333", Abort); QCOMPARE(count, 1); QVERIFY(!threads.contains(QThread::currentThread())); } @@ -400,6 +437,7 @@ public: void tst_QtConcurrentThreadEngine::exceptions() { + // Asynchronous mode: { bool caught = false; try { @@ -412,6 +450,32 @@ void tst_QtConcurrentThreadEngine::exceptions() QVERIFY2(caught, "did not get exception"); } + // Blocking mode: + // test throwing the exception from a worker thread. + { + bool caught = false; + try { + QtConcurrentExceptionThrower e(QThread::currentThread()); + e.startBlocking(); + } catch (const QException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } + + // test throwing the exception from the main thread (different code path) + { + bool caught = false; + try { + QtConcurrentExceptionThrower e(0); + e.startBlocking(); + } catch (const QException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } + + // Asynchronous mode: { bool caught = false; try { @@ -423,6 +487,31 @@ void tst_QtConcurrentThreadEngine::exceptions() } QVERIFY2(caught, "did not get exception"); } + + // Blocking mode: + // test throwing the exception from a worker thread. + { + bool caught = false; + try { + UnrelatedExceptionThrower e(QThread::currentThread()); + e.startBlocking(); + } catch (const QUnhandledException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } + + // test throwing the exception from the main thread (different code path) + { + bool caught = false; + try { + UnrelatedExceptionThrower e(0); + e.startBlocking(); + } catch (const QUnhandledException &) { + caught = true; + } + QVERIFY2(caught, "did not get exception"); + } } #endif diff --git a/qtbase/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp b/qtbase/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp index 4968742110..f4c3fb711d 100644 --- a/qtbase/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp +++ b/qtbase/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp @@ -42,6 +42,7 @@ private slots: void writeBlock_data(); void writeBlock(); void seek(); + void invalidSeeks(); void seekTest_data(); void seekTest(); void read_rawdata(); @@ -286,6 +287,29 @@ void tst_QBuffer::seek() QCOMPARE(buffer.size(), pos); } +void tst_QBuffer::invalidSeeks() +{ + if constexpr (sizeof(qsizetype) == sizeof(qint64)) { + // sizeof(qsizetype) == sizeof(qint64), so +1 would overflow + QSKIP("This is a 32-bit-only test."); + } else { + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QCOMPARE(buffer.buffer().size(), qsizetype(0)); + QCOMPARE(buffer.pos(), qint64(0)); + constexpr qint64 MaxQByteArrayCapacity = (std::numeric_limits::max)(); + // this should fail fast, not after trying to allocate nearly 2 GiB of data, + // potentially crashing in the process: + QVERIFY(!buffer.seek(2 * MaxQByteArrayCapacity - 1)); + QCOMPARE(buffer.buffer().size(), qsizetype(0)); + QCOMPARE(buffer.pos(), qint64(0)); + // ditto: + QVERIFY(!buffer.seek(MaxQByteArrayCapacity + 1)); + QCOMPARE(buffer.buffer().size(), qsizetype(0)); + QCOMPARE(buffer.pos(), qint64(0)); + } +} + void tst_QBuffer::seekTest_data() { writeBlock_data(); diff --git a/qtbase/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp b/qtbase/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp index da5327594c..4a04e0f7c6 100644 --- a/qtbase/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp +++ b/qtbase/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp @@ -561,7 +561,8 @@ protected: qint64 readData(char *data, qint64 maxSize) override { maxSize = qMin(maxSize, qint64(buf->size() - offset)); - memcpy(data, buf->constData() + offset, maxSize); + if (maxSize > 0) + memcpy(data, buf->constData() + offset, maxSize); offset += maxSize; return maxSize; } @@ -604,13 +605,15 @@ protected: qint64 readData(char *data, qint64 maxSize) override { maxSize = qMin(maxSize, qint64(buf.size() - pos())); - memcpy(data, buf.constData() + pos(), maxSize); + if (maxSize > 0) + memcpy(data, buf.constData() + pos(), maxSize); return maxSize; } qint64 writeData(const char *data, qint64 maxSize) override { maxSize = qMin(maxSize, qint64(buf.size() - pos())); - memcpy(buf.data() + pos(), data, maxSize); + if (maxSize > 0) + memcpy(buf.data() + pos(), data, maxSize); return maxSize; } diff --git a/qtbase/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/qtbase/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index db60bead64..f345a44414 100644 --- a/qtbase/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/qtbase/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -1,7 +1,8 @@ /**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2020 Intel Corporation. +** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2022 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -150,6 +151,8 @@ private slots: void startStopStartStopBuffers(); void processEventsInAReadyReadSlot_data(); void processEventsInAReadyReadSlot(); + void startFromCurrentWorkingDir_data(); + void startFromCurrentWorkingDir(); // keep these at the end, since they use lots of processes and sometimes // caused obscure failures to occur in tests that followed them (esp. on the Mac) @@ -2731,5 +2734,94 @@ void tst_QProcess::finishProcessBeforeReadingDone_deprecated() #endif +enum class ChdirMode { + None = 0, + InParent, + InChild +}; +Q_DECLARE_METATYPE(ChdirMode) + +void tst_QProcess::startFromCurrentWorkingDir_data() +{ + qRegisterMetaType(); + QTest::addColumn("programPrefix"); + QTest::addColumn("chdirMode"); + QTest::addColumn("success"); + + constexpr bool IsWindows = true +#ifdef Q_OS_UNIX + && false +#endif + ; + + // baseline: trying to execute the directory, this can't possibly succeed! + QTest::newRow("plain-same-cwd") << QString() << ChdirMode::None << false; + + // cross-platform behavior: neither OS searches the setWorkingDirectory() + // dir without "./" + QTest::newRow("plain-child-chdir") << QString() << ChdirMode::InChild << false; + + // cross-platform behavior: both OSes search the parent's CWD with "./" + QTest::newRow("prefixed-parent-chdir") << "./" << ChdirMode::InParent << true; + + // opposite behaviors: Windows searches the parent's CWD and Unix searches + // the child's with "./" + QTest::newRow("prefixed-child-chdir") << "./" << ChdirMode::InChild << !IsWindows; + + // Windows searches the parent's CWD without "./" + QTest::newRow("plain-parent-chdir") << QString() << ChdirMode::InParent << IsWindows; +} + +void tst_QProcess::startFromCurrentWorkingDir() +{ + QFETCH(QString, programPrefix); + QFETCH(ChdirMode, chdirMode); + QFETCH(bool, success); + + QProcess process; + qRegisterMetaType(); + QSignalSpy errorSpy(&process, &QProcess::errorOccurred); + QVERIFY(errorSpy.isValid()); + + // both the dir name and the executable name + const QString target = QStringLiteral("testProcessNormal"); + process.setProgram(programPrefix + target); + +#ifdef Q_OS_UNIX + // Reset PATH, to be sure it doesn't contain . or the empty path. + // We can't do this on Windows because DLLs are searched in PATH + // and Windows always searches "." anyway. + auto restoreEnv = qScopeGuard([old = qgetenv("PATH")] { + qputenv("PATH", old); + }); + qputenv("PATH", "/"); +#endif + + switch (chdirMode) { + case ChdirMode::InParent: { + auto restoreCwd = qScopeGuard([old = QDir::currentPath()] { + QDir::setCurrent(old); + }); + QVERIFY(QDir::setCurrent(target)); + process.start(); + break; + } + case ChdirMode::InChild: + process.setWorkingDirectory(target); + Q_FALLTHROUGH(); + case ChdirMode::None: + process.start(); + break; + } + + QCOMPARE(process.waitForStarted(), success); + QCOMPARE(errorSpy.count(), int(!success)); + if (success) { + QVERIFY(process.waitForFinished()); + } else { + QCOMPARE(process.error(), QProcess::FailedToStart); + } +} + QTEST_MAIN(tst_QProcess) #include "tst_qprocess.moc" diff --git a/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp b/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp index e1ea7a4552..90972caa57 100644 --- a/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp +++ b/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp @@ -117,6 +117,7 @@ private Q_SLOTS: void shouldPropagateDropAfterLastRow_data(); void shouldPropagateDropAfterLastRow(); void qtbug91788(); + void qtbug91878(); private: QStandardItemModel mod; @@ -843,6 +844,22 @@ void tst_QConcatenateTablesProxyModel::qtbug91788() QCOMPARE(proxyConcat.columnCount(), 0); } +void tst_QConcatenateTablesProxyModel::qtbug91878() +{ + QStandardItemModel m; + m.setRowCount(4); + m.setColumnCount(4); + + QConcatenateTablesProxyModel pm; + QSortFilterProxyModel proxyFilter; + proxyFilter.setSourceModel(&pm); + proxyFilter.setFilterFixedString("something"); + pm.addSourceModel(&m); // This should not assert + + QCOMPARE(pm.columnCount(), 4); + QCOMPARE(pm.rowCount(), 4); +} + QTEST_GUILESS_MAIN(tst_QConcatenateTablesProxyModel) #include "tst_qconcatenatetablesproxymodel.moc" diff --git a/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 9bd66c0835..ed4a0bae5d 100644 --- a/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/qtbase/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -158,6 +158,7 @@ private slots: void nullReceiver(); void functorReferencesConnection(); void disconnectDisconnects(); + void declarativeData(); }; struct QObjectCreatedOnShutdown @@ -7679,5 +7680,81 @@ void tst_QObject::disconnectDisconnects() Q_STATIC_ASSERT(QtPrivate::HasQ_OBJECT_Macro::Value); Q_STATIC_ASSERT(!QtPrivate::HasQ_OBJECT_Macro::Value); +#ifdef QT_BUILD_INTERNAL +/* + Since QObjectPrivate stores the declarativeData pointer in a union with the pointer + to the currently destroyed child, calls to the QtDeclarative handlers need to be + correctly guarded. QTBUG-105286 +*/ +namespace QtDeclarative { +static QAbstractDeclarativeData *theData; + +static void destroyed(QAbstractDeclarativeData *data, QObject *) +{ + QCOMPARE(data, theData); +} +static void signalEmitted(QAbstractDeclarativeData *data, QObject *, int, void **) +{ + QCOMPARE(data, theData); +} +// we can't use QCOMPARE in the next two functions, as they don't return void +static int receivers(QAbstractDeclarativeData *data, const QObject *, int) +{ + QTest::qCompare(data, theData, "data", "theData", __FILE__, __LINE__); + return 0; +} +static bool isSignalConnected(QAbstractDeclarativeData *data, const QObject *, int) +{ + QTest::qCompare(data, theData, "data", "theData", __FILE__, __LINE__); + return true; +} + +class Object : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; + ~Object() + { + if (Object *p = static_cast(parent())) + p->emitSignal(); + } + + void emitSignal() + { + emit theSignal(); + } + +signals: + void theSignal(); +}; + +} +#endif + +void tst_QObject::declarativeData() +{ +#ifdef QT_BUILD_INTERNAL + QScopedValueRollback destroyed(QAbstractDeclarativeData::destroyed, + QtDeclarative::destroyed); + QScopedValueRollback signalEmitted(QAbstractDeclarativeData::signalEmitted, + QtDeclarative::signalEmitted); + QScopedValueRollback receivers(QAbstractDeclarativeData::receivers, + QtDeclarative::receivers); + QScopedValueRollback isSignalConnected(QAbstractDeclarativeData::isSignalConnected, + QtDeclarative::isSignalConnected); + + QtDeclarative::Object p; + QObjectPrivate *priv = QObjectPrivate::get(&p); + priv->declarativeData = QtDeclarative::theData = new QAbstractDeclarativeData; + + connect(&p, &QtDeclarative::Object::theSignal, &p, []{ + }); + + QtDeclarative::Object *child = new QtDeclarative::Object; + child->setParent(&p); +#endif +} + QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 0ea422ecbc..1a3256534b 100644 --- a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -70,15 +70,15 @@ static inline QString testSuiteWarning() QString result; QTextStream str(&result); - str << "\nCannot find the shared-mime-info test suite\nstarting from: " + str << "\nCannot find the shared-mime-info test suite\nin the parent of: " << QDir::toNativeSeparators(QDir::currentPath()) << "\n" "cd " << QDir::toNativeSeparators(QStringLiteral("tests/auto/corelib/mimetypes/qmimedatabase")) << "\n" - "wget http://cgit.freedesktop.org/xdg/shared-mime-info/snapshot/Release-1-10.zip\n" - "unzip Release-1-10.zip\n"; + "wget https://gitlab.freedesktop.org/xdg/shared-mime-info/-/archive/2.1/shared-mime-info-2.1.zip\n" + "unzip shared-mime-info-2.1.zip\n"; #ifdef Q_OS_WIN - str << "mkdir testfiles\nxcopy /s Release-1-10 s-m-i\n"; + str << "mkdir testfiles\nxcopy /s shared-mime-info-2.1 s-m-i\n"; #else - str << "ln -s Release-1-10 s-m-i\n"; + str << "ln -s shared-mime-info-2.1 s-m-i\n"; #endif return result; } @@ -154,7 +154,7 @@ void tst_QMimeDatabase::initTestCase() QVERIFY2(copyResourceFile(xmlFileName, xmlTargetFileName, &errorMessage), qPrintable(errorMessage)); #endif - m_testSuite = QFINDTESTDATA("s-m-i/tests"); + m_testSuite = QFINDTESTDATA("s-m-i/tests/mime-detection"); if (m_testSuite.isEmpty()) qWarning("%s", qPrintable(testSuiteWarning())); @@ -611,7 +611,7 @@ void tst_QMimeDatabase::allMimeTypes() QVERIFY(!lst.isEmpty()); // Hardcoding this is the only way to check both providers find the same number of mimetypes. - QCOMPARE(lst.count(), 779); + QCOMPARE(lst.count(), 811); foreach (const QMimeType &mime, lst) { const QString name = mime.name(); @@ -631,10 +631,9 @@ void tst_QMimeDatabase::suffixes_data() QTest::newRow("mimetype with a single pattern") << "application/pdf" << "*.pdf" << "pdf"; QTest::newRow("mimetype with multiple patterns") << "application/x-kpresenter" << "*.kpr;*.kpt" << "kpr"; - QTest::newRow("jpeg") << "image/jpeg" << "*.jpe;*.jpg;*.jpeg" << "jpeg"; - //if (KMimeType::sharedMimeInfoVersion() > KDE_MAKE_VERSION(0, 60, 0)) { - QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << "wp"; - //} + // The preferred suffix for image/jpeg is *.jpg, as per https://bugs.kde.org/show_bug.cgi?id=176737 + QTest::newRow("jpeg") << "image/jpeg" << "*.jpe;*.jpg;*.jpeg" << "jpg"; + QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << "wp"; QTest::newRow("oasis text mimetype") << "application/vnd.oasis.opendocument.text" << "*.odt" << "odt"; QTest::newRow("oasis presentation mimetype") << "application/vnd.oasis.opendocument.presentation" << "*.odp" << "odp"; QTest::newRow("mimetype with multiple patterns") << "text/plain" << "*.asc;*.txt;*,v" << "txt"; diff --git a/qtbase/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/qtbase/tests/auto/corelib/serialization/json/tst_qtjson.cpp index 3e58dd03cc..c8f82ef5d5 100644 --- a/qtbase/tests/auto/corelib/serialization/json/tst_qtjson.cpp +++ b/qtbase/tests/auto/corelib/serialization/json/tst_qtjson.cpp @@ -176,6 +176,8 @@ private Q_SLOTS: void fromToVariantConversions_data(); void fromToVariantConversions(); + void noLeakOnNameClash(); + private: QString testDataDir; }; @@ -3685,5 +3687,23 @@ void tst_QtJson::fromToVariantConversions() } } +void tst_QtJson::noLeakOnNameClash() +{ + QJsonDocument doc = QJsonDocument::fromJson("{\"\":{\"\":0},\"\":0}"); + QVERIFY(!doc.isNull()); + const QJsonObject obj = doc.object(); + + // Removed the duplicate key. + QCOMPARE(obj.length(), 1); + + // Retained the last of the duplicates. + const QJsonValue val = obj.begin().value(); + QVERIFY(val.isDouble()); + QCOMPARE(val.toDouble(), 0.0); + + // It should not leak. + // In particular it should not forget to deref the container for the inner object. +} + QTEST_MAIN(tst_QtJson) #include "tst_qtjson.moc" diff --git a/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index a59b58d57f..241c3e84ac 100644 --- a/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/qtbase/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -63,6 +63,7 @@ private slots: void isValidId_data(); void isValidId(); void malformed(); + void serialize(); // Backend tests void utcTest(); void icuTest(); @@ -947,6 +948,33 @@ void tst_QTimeZone::malformed() barf.offsetFromUtc(now); } +void tst_QTimeZone::serialize() +{ + int parts = 0; +#ifndef QT_NO_DEBUG_STREAM + qDebug() << QTimeZone(); // to verify no crash + parts++; +#endif +#ifndef QT_NO_DATASTREAM + QByteArray blob; + { + QDataStream stream(&blob, QIODevice::WriteOnly); + stream << QTimeZone("Europe/Oslo") << QTimeZone(420) << QTimeZone() << qint64(-1); + } + QDataStream stream(&blob, QIODevice::ReadOnly); + QTimeZone invalid, offset, oslo; + qint64 minusone; + stream >> oslo >> offset >> invalid >> minusone; + QCOMPARE(oslo, QTimeZone("Europe/Oslo")); + QCOMPARE(offset, QTimeZone(420)); + QVERIFY(!invalid.isValid()); + QCOMPARE(minusone, qint64(-1)); + parts++; +#endif + if (!parts) + QSKIP("No serialization enabled"); +} + void tst_QTimeZone::utcTest() { #ifdef QT_BUILD_INTERNAL diff --git a/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp b/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp index 05480c6dd2..70f4c75756 100644 --- a/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp +++ b/qtbase/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp @@ -324,6 +324,13 @@ void tst_QDBusInterface::notValid() QVERIFY(!interface.isValid()); QVERIFY(!QMetaObject::invokeMethod(&interface, "ListNames", Qt::DirectConnection)); + + // With a connection, but empty/null service and path specified + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + QDBusInterface iface({}, {}, {}, con); + QVERIFY(!iface.isValid()); + QVERIFY(!QMetaObject::invokeMethod(&interface, "ListNames", Qt::DirectConnection)); } void tst_QDBusInterface::notValidDerived() diff --git a/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp b/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp index 874468c954..04ceb4ab65 100644 --- a/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp +++ b/qtbase/tests/auto/gui/kernel/qkeysequence/tst_qkeysequence.cpp @@ -507,6 +507,10 @@ void tst_QKeySequence::toStringFromKeycode_data() QTest::newRow("Ctrl+Alt+Num+Del") << QKeySequence(Qt::ControlModifier | Qt::AltModifier | Qt::KeypadModifier | Qt::Key_Delete) << "Ctrl+Alt+Num+Del"; QTest::newRow("Ctrl+Ins") << QKeySequence(Qt::ControlModifier | Qt::Key_Insert) << "Ctrl+Ins"; QTest::newRow("Ctrl+Num+Ins(1)") << QKeySequence(Qt::Key_Insert | Qt::KeypadModifier | Qt::ControlModifier) << "Ctrl+Num+Ins"; + QTest::newRow("Ctrl") << QKeySequence(Qt::Key_Control) << "Control"; + QTest::newRow("Alt") << QKeySequence(Qt::Key_Alt) << "Alt"; + QTest::newRow("Shift") << QKeySequence(Qt::Key_Shift) << "Shift"; + QTest::newRow("Meta") << QKeySequence(Qt::Key_Meta) << "Meta"; } void tst_QKeySequence::toStringFromKeycode() diff --git a/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp index 15e0ecadaa..b4eca74283 100644 --- a/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp +++ b/qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp @@ -81,6 +81,8 @@ private slots: void registerOpenTypePreferredNamesSystem(); void registerOpenTypePreferredNamesApplication(); + void stretchRespected(); + private: QString m_ledFont; QString m_testFont; @@ -355,6 +357,28 @@ static QString testString() return QStringLiteral("foo bar"); } +void tst_QFontDatabase::stretchRespected() +{ + int italicId = QFontDatabase::addApplicationFont(m_testFontItalic); + QVERIFY(italicId != -1); + + QVERIFY(!QFontDatabase::applicationFontFamilies(italicId).isEmpty()); + + QString italicFontName = QFontDatabase::applicationFontFamilies(italicId).first(); + + QFont italicFont = QFontDatabase().font(italicFontName, + QString::fromLatin1("Italic"), 14); + QVERIFY(italicFont.italic()); + + QFont italicStretchedFont = italicFont; + italicStretchedFont.setStretch( 400 ); + + QVERIFY(QFontMetricsF(italicFont).horizontalAdvance(QStringLiteral("foobar")) < + QFontMetricsF(italicStretchedFont).horizontalAdvance(QStringLiteral("foobar"))); + + QFontDatabase::removeApplicationFont(italicId); +} + void tst_QFontDatabase::condensedFontWidthNoFontMerging() { int regularFontId = QFontDatabase::addApplicationFont(m_testFont); diff --git a/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 3d4a094c43..b423e11a99 100644 --- a/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -281,6 +281,7 @@ private Q_SLOTS: void ioGetFromFileSpecial(); void ioGetFromFile_data(); void ioGetFromFile(); + void ioGetFromFileUrl(); void ioGetFromFtp_data(); void ioGetFromFtp(); void ioGetFromFtpWithReuse(); @@ -3300,6 +3301,18 @@ void tst_QNetworkReply::ioGetFromFile() QCOMPARE(reader.data, data); } +void tst_QNetworkReply::ioGetFromFileUrl() +{ + // This immediately fails on non-windows platforms: + QNetworkRequest request(QUrl("file://unc-server/some/path")); + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy finishedSpy(reply.get(), &QNetworkReply::finished); + // QTBUG-105618: This would, on non-Windows platforms, never happen because the signal + // was emitted before the constructor finished, leaving no chance at all to connect to the + // signal + QVERIFY(finishedSpy.wait()); +} + void tst_QNetworkReply::ioGetFromFtp_data() { QTest::addColumn("fileName"); diff --git a/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.cert b/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.cert index 069fa6b341..4cc4d9a5ea 100644 --- a/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.cert +++ b/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.cert @@ -1,75 +1,34 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 0 (0x0) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=NO, ST=Oslo, L=Nydalen, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com - Validity - Not Before: Dec 4 01:10:32 2007 GMT - Not After : Apr 21 01:10:32 2035 GMT - Subject: C=NO, ST=Oslo, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:a7:c8:a0:4a:c4:19:05:1b:66:ba:32:e2:d2:f1: - 1c:6f:17:82:e4:39:2e:01:51:90:db:04:34:32:11: - 21:c2:0d:6f:59:d8:53:90:54:3f:83:8f:a9:d3:b3: - d5:ee:1a:9b:80:ae:c3:25:c9:5e:a5:af:4b:60:05: - aa:a0:d1:91:01:1f:ca:04:83:e3:58:1c:99:32:45: - 84:70:72:58:03:98:4a:63:8b:41:f5:08:49:d2:91: - 02:60:6b:e4:64:fe:dd:a0:aa:74:08:e9:34:4c:91: - 5f:12:3d:37:4d:54:2c:ad:7f:5b:98:60:36:02:8c: - 3b:f6:45:f3:27:6a:9b:94:9d - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - Netscape Comment: - OpenSSL Generated Certificate - X509v3 Subject Key Identifier: - 21:85:04:3D:23:01:66:E5:F7:9F:1A:84:24:8A:AF:0A:79:F4:E5:AC - X509v3 Authority Key Identifier: - DirName:/C=NO/ST=Oslo/L=Nydalen/O=Nokia Corporation and/or its subsidiary(-ies)/OU=Development/CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com - serial:8E:A8:B4:E8:91:B7:54:2E - - Signature Algorithm: sha1WithRSAEncryption - 6d:57:5f:d1:05:43:f0:62:05:ec:2a:71:a5:dc:19:08:f2:c4: - a6:bd:bb:25:d9:ca:89:01:0e:e4:cf:1f:c1:8c:c8:24:18:35: - 53:59:7b:c0:43:b4:32:e6:98:b2:a6:ef:15:05:0b:48:5f:e1: - a0:0c:97:a9:a1:77:d8:35:18:30:bc:a9:8f:d3:b7:54:c7:f1: - a9:9e:5d:e6:19:bf:f6:3c:5b:2b:d8:e4:3e:62:18:88:8b:d3: - 24:e1:40:9b:0c:e6:29:16:62:ab:ea:05:24:70:36:aa:55:93: - ef:02:81:1b:23:10:a2:04:eb:56:95:75:fc:f8:94:b1:5d:42: - c5:3f:36:44:85:5d:3a:2e:90:46:8a:a2:b9:6f:87:ae:0c:15: - 40:19:31:90:fc:3b:25:bb:ae:f1:66:13:0d:85:90:d9:49:34: - 8f:f2:5d:f9:7a:db:4d:5d:27:f6:76:9d:35:8c:06:a6:4c:a3: - b1:b2:b6:6f:1d:d7:a3:00:fd:72:eb:9e:ea:44:a1:af:21:34: - 7d:c7:42:e2:49:91:19:8b:c0:ad:ba:82:80:a8:71:70:f4:35: - 31:91:63:84:20:95:e9:60:af:64:8b:cc:ff:3d:8a:76:74:3d: - c8:55:6d:e4:8e:c3:2b:1c:e8:42:18:ae:9f:e6:6b:9c:34:06: - ec:6a:f2:c3 -----BEGIN CERTIFICATE----- -MIIEEzCCAvugAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCTk8x -DTALBgNVBAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xs -dGVjaCBBU0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50 -cm9sbC5ubzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbTAe -Fw0wNzEyMDQwMTEwMzJaFw0zNTA0MjEwMTEwMzJaMGMxCzAJBgNVBAYTAk5PMQ0w -CwYDVQQIEwRPc2xvMRYwFAYDVQQKEw1Ucm9sbHRlY2ggQVNBMRQwEgYDVQQLEwtE -ZXZlbG9wbWVudDEXMBUGA1UEAxMOZmx1a2UudHJvbGwubm8wgZ8wDQYJKoZIhvcN -AQEBBQADgY0AMIGJAoGBAKfIoErEGQUbZroy4tLxHG8XguQ5LgFRkNsENDIRIcIN -b1nYU5BUP4OPqdOz1e4am4CuwyXJXqWvS2AFqqDRkQEfygSD41gcmTJFhHByWAOY -SmOLQfUISdKRAmBr5GT+3aCqdAjpNEyRXxI9N01ULK1/W5hgNgKMO/ZF8ydqm5Sd -AgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM -IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIYUEPSMBZuX3nxqEJIqv -Cnn05awwgbsGA1UdIwSBszCBsKGBoqSBnzCBnDELMAkGA1UEBhMCTk8xDTALBgNV -BAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xsdGVjaCBB -U0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50cm9sbC5u -bzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbYIJAI6otOiR -t1QuMA0GCSqGSIb3DQEBBQUAA4IBAQBtV1/RBUPwYgXsKnGl3BkI8sSmvbsl2cqJ -AQ7kzx/BjMgkGDVTWXvAQ7Qy5piypu8VBQtIX+GgDJepoXfYNRgwvKmP07dUx/Gp -nl3mGb/2PFsr2OQ+YhiIi9Mk4UCbDOYpFmKr6gUkcDaqVZPvAoEbIxCiBOtWlXX8 -+JSxXULFPzZEhV06LpBGiqK5b4euDBVAGTGQ/Dslu67xZhMNhZDZSTSP8l35ettN -XSf2dp01jAamTKOxsrZvHdejAP1y657qRKGvITR9x0LiSZEZi8CtuoKAqHFw9DUx -kWOEIJXpYK9ki8z/PYp2dD3IVW3kjsMrHOhCGK6f5mucNAbsavLD +MIIF6zCCA9OgAwIBAgIUfo9amJtJGWqWE6f+SkAO85zkGr4wDQYJKoZIhvcNAQEL +BQAwgYMxCzAJBgNVBAYTAk5PMQ0wCwYDVQQIDARPc2xvMQ0wCwYDVQQHDARPc2xv +MRcwFQYDVQQKDA5UaGUgUXQgQ29tcGFueTEMMAoGA1UECwwDUiZEMRIwEAYDVQQD +DAlIMiBUZXN0ZXIxGzAZBgkqhkiG9w0BCQEWDG1pbmltaUBxdC5pbzAgFw0yMDEw +MjYxMjAxMzFaGA8yMTIwMTAwMjEyMDEzMVowgYMxCzAJBgNVBAYTAk5PMQ0wCwYD +VQQIDARPc2xvMQ0wCwYDVQQHDARPc2xvMRcwFQYDVQQKDA5UaGUgUXQgQ29tcGFu +eTEMMAoGA1UECwwDUiZEMRIwEAYDVQQDDAlIMiBUZXN0ZXIxGzAZBgkqhkiG9w0B +CQEWDG1pbmltaUBxdC5pbzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AOiUp5+E4blouKH7q+rVNR8NoYX2XkBW+q+rpy1zu5ssRSzbqxAjDx9dkht7Qlnf +VlDT00JvpOWdeuPon5915edQRsY4Unl6mKH29ra3OtUa1/yCJXsGVJTKCj7k4Bxb +5mZzb/fTlZntMLdTIBMfUbw62FKir1WjKIcJ9fCoG8JaGeKVO4Rh5p0ezd4UUUId +r1BXl5Nqdqy2vTMsEDnjOsD3egkv8I2SKN4O6n/C3wWYpMOWYZkGoZiKz7rJs/i/ +ez7bsV7JlwdzTlhpJzkcOSVFBP6JlEOxTNNxZ1wtKy7PtZGmsSSATq2e6+bw38Ae +Op0XnzzqcGjtDDofBmT7OFzZWjS9VZS6+DOOe2QHWle1nCHcHyH4ku6IRlsr9xkR +NAIlOfnvHHxqJUenoeaZ4oQDjCBKS1KXygJO/tL7BLTQVn/xK1EmPvKNnjzWk4tR +PnibUhhs5635qpOU/YPqFBh1JjVruZbsWcDAhRcew0uxONXOa9E+4lttQ9ySYa1A +LvWqJuAX7gu2BsBMLyqfm811YnA7CIFMyO+HlqmkLFfv5L/xIRAXR7l26YGO0VwX +CGjMfz4NVPMMke4nB7qa9NkpXQBQKMms3Qzd5JW0Hy9Ruj5O8GPcFZmV0twjd1uJ +PD/cAjkWLaXjdNsJ16QWc2nghQRS6HYqKRX6j+CXOxupAgMBAAGjUzBRMB0GA1Ud +DgQWBBRSCOU58j9NJZkMamt623qyCrhN3TAfBgNVHSMEGDAWgBRSCOU58j9NJZkM +amt623qyCrhN3TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCq +q4jxsWeNDv5Nq14hJtF9HB+ZL64zcZtRjJP1YgNs0QppKICmjPOL2nIMGmI/jKrs +0eGAL/9XXNVHPxm1OPOncvimMMmU6emZfpMdEtTfKP43+Pg9HgKRjLoQp406vGeQ +8ki/mbBhrItVPgEm3tu2AFA02XTYi+YxCI9kRZLGkM3FbgtOuTLPl0Z9y+kiPc9F +uCSC03anBEqv+vDSI8+wODymQ/IJ3Jyz1lxIRDfp4qAekmy0jU2c91VOHHEmOmqq +kqygGFRdwbe99m9yP63r6q0b5K3X2UnJ6bns0hmTwThYwpVPXLU8jdaTddbMukN2 +/Ef96Tsw8nWOEOPMySHOTIPgwyZRp26b0kA9EmhLwOP401SxXVQCmSRmtwNagmtg +jJKmZoYBN+//D45ibK8z6Q0oOm9P+Whf/uUXehcRxBxyV3xz7k0wKGQbHj/ddwcy +IUoIN4lrAlib+lK170kTKN352PDmrpo2gmIzPEsfurKAIMSelDl6H+kih16BtZ8y +Nz6fh9Soqrg3OSAware8pxV7k51crBMoPLN78KoRV8MFCK4K7Fddq4rRISq6hiXq +r1nsjoEPuKM9huprmZVZe9t5YcDa2I+wb3IiE3uwpZbAdaLDyQ5n6F/qpsiIkZXn +gtcF7oqpG5oYrwCcZ53y/ezUgUg7PlSz2XwAGvQtgg== -----END CERTIFICATE----- diff --git a/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.key b/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.key index 9d1664d609..337ce541a6 100644 --- a/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.key +++ b/qtbase/tests/auto/network/ssl/qsslsocket/certs/fluke.key @@ -1,15 +1,52 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQCnyKBKxBkFG2a6MuLS8RxvF4LkOS4BUZDbBDQyESHCDW9Z2FOQ -VD+Dj6nTs9XuGpuArsMlyV6lr0tgBaqg0ZEBH8oEg+NYHJkyRYRwclgDmEpji0H1 -CEnSkQJga+Rk/t2gqnQI6TRMkV8SPTdNVCytf1uYYDYCjDv2RfMnapuUnQIDAQAB -AoGANFzLkanTeSGNFM0uttBipFT9F4a00dqHz6JnO7zXAT26I5r8sU1pqQBb6uLz -/+Qz5Zwk8RUAQcsMRgJetuPQUb0JZjF6Duv24hNazqXBCu7AZzUenjafwmKC/8ri -KpX3fTwqzfzi//FKGgbXQ80yykSSliDL3kn/drATxsLCgQECQQDXhEFWLJ0vVZ1s -1Ekf+3NITE+DR16X+LQ4W6vyEHAjTbaNWtcTKdAWLA2l6N4WAAPYSi6awm+zMxx4 -VomVTsjdAkEAx0z+e7natLeFcrrq8pbU+wa6SAP1VfhQWKitxL1e7u/QO90NCpxE -oQYKzMkmmpOOFjQwEMAy1dvFMbm4LHlewQJAC/ksDBaUcQHHqjktCtrUb8rVjAyW -A8lscckeB2fEYyG5J6dJVaY4ClNOOs5yMDS2Afk1F6H/xKvtQ/5CzInA/QJATDub -K+BPU8jO9q+gpuIi3VIZdupssVGmCgObVCHLakG4uO04y9IyPhV9lA9tALtoIf4c -VIvv5fWGXBrZ48kZAQJBAJmVCdzQxd9LZI5vxijUCj5EI4e+x5DRqVUvyP8KCZrC -AiNyoDP85T+hBZaSXK3aYGpVwelyj3bvo1GrTNwNWLw= ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDolKefhOG5aLih ++6vq1TUfDaGF9l5AVvqvq6ctc7ubLEUs26sQIw8fXZIbe0JZ31ZQ09NCb6TlnXrj +6J+fdeXnUEbGOFJ5epih9va2tzrVGtf8giV7BlSUygo+5OAcW+Zmc2/305WZ7TC3 +UyATH1G8OthSoq9VoyiHCfXwqBvCWhnilTuEYeadHs3eFFFCHa9QV5eTanastr0z +LBA54zrA93oJL/CNkijeDup/wt8FmKTDlmGZBqGYis+6ybP4v3s+27FeyZcHc05Y +aSc5HDklRQT+iZRDsUzTcWdcLSsuz7WRprEkgE6tnuvm8N/AHjqdF5886nBo7Qw6 +HwZk+zhc2Vo0vVWUuvgzjntkB1pXtZwh3B8h+JLuiEZbK/cZETQCJTn57xx8aiVH +p6HmmeKEA4wgSktSl8oCTv7S+wS00FZ/8StRJj7yjZ481pOLUT54m1IYbOet+aqT +lP2D6hQYdSY1a7mW7FnAwIUXHsNLsTjVzmvRPuJbbUPckmGtQC71qibgF+4LtgbA +TC8qn5vNdWJwOwiBTMjvh5appCxX7+S/8SEQF0e5dumBjtFcFwhozH8+DVTzDJHu +Jwe6mvTZKV0AUCjJrN0M3eSVtB8vUbo+TvBj3BWZldLcI3dbiTw/3AI5Fi2l43Tb +CdekFnNp4IUEUuh2KikV+o/glzsbqQIDAQABAoICAFw1q6tr5I48vY7DF+rXsuLn +5ZUWE1IQ6fzB4lr72nJv/9EEGnMgYzt9PpMUsD6vdCpBgS2C0+6RHArFzJtNA+RM +iHLIG7K7702veyr/xBx/MwiSlMeMv/XpkFxVI6E6skMGG2s3AMXxKvJTy5CpRx+I +eQFyLG+Ya1X2lgJes/q+/CpAHkOjCOpcLySQC5NZ74q734V7nSdmn+Zs3tYEh+O/ +eiuwTP/j5b38Te5vVTqDxTciJPmljmXLCwa0N100lWlbcpvw8qbqiTI2Jm3XCbUE +AzHjW9vmrF3cRS1fXxKFGShw3SRqlkbxjfeWoi8qDPUBS4m8LOr8qG9Wo5Nfon0z +zLP4bci3zHDvVcaaZrrsUBs/yZbg+Dgka1DmX7ekmeccr2yTdKDFgPupYUyxVbTl +a9ZLJysjFD7rgBv1ZclHonLp6Vbm+ZoTqvteo4ikAy6L9RtBWJ23XEK34PkP/+c5 +2vWZaOrnjSeBHbFce8cdJSxqWpP+eSCI5I9XbDrYFIsQ/gqKgtzDKy2ihJ2Y8STL +yO4hyFPFjxc+Gg4/P2PpmT5CY2ty44M0BWs+JGW96CJPrrplf2lmQUQJj5LZY66X +Z/4C9L7ZYtKZ+bs5SvU46yWugAvQZX22Xm9xLXWyVXRdx3bj+3M3fDnF9di/zdbh +CgLx7oWPNrXc7FCajnn9AoIBAQD5FMYwRpw9NWT9WDxQwx+cSI4Icbd88ByTW63S +LzeRwZA0J9/SfwO+aBRupzc9GkGXCiZcGMw3AGsCtig8yFlw8E5KnzN7KlftDMnM +9NUxxzlR8VwKyLnZfG7sDTl057ZlUujnqhmt/F8F7dIy7FVO1dE/8nngA+FYTCOG +UZdGjwyBDlDM0JJdUWGY3xslutcpCDN5mzSTKjy9drMvImAshRawxRF6WBpn7vr2 +nC6vciqfx1Mzx1vyk0Jm0ilaydDdLMADjt/iL4Nkr0BEs4k+UzQiKDwp8gu7abQ1 +eBfxd9Iar4htQa2I1Ewl6P01G/q+ZYwgHhJ9RVn4AxQXefILAoIBAQDvCouORdQX +C8wsyp7MwXlF/3NQeNN5/+B2mhbxrBOf7PmMCXLnkRWcjwJtzypWFqJ0sqai/2+0 +bqbMcjX5maT8stT2shl3zXe/Ejt2e3TBYpc1tyuses8Kb5BMU8hu6tTd3G2CMXpD +dT6DVemJZCTtwj9aBNIxSizvlgMolJnCpzhPnlfHSI6E+g3m/LTTo3HwbjMSw/Uq +irgjOpI2wSBB6LZPSgjvfcYPRyWUk16L4A5uSX0cADnovDFLa5/h0wJvN/OoCSQg +rLCXG5E18EyL5Wc58BCY1ZvxmjG3lQtgPxYu2Jwc36R/y/JKlxW5suER5ZNpbbD4 +uOyTt2VxMQ2bAoIBAQC5+MzRFqdo/AjfL5Y5JrbfVTzXCTDa09xCGd16ZU60QTWN ++4ed/r+o1sUKqUcRFB2MzEM/2DQBjQpZB/CbEWvWa1XJWXxypXbowveZU+QqOnmN +uQvj8WLyA3o+PNF9e9QvauwCrHpn8VpxbtPWuaYoKnUFreFZZQxHhPGxRBIS2JOZ +eDrT8ZaWnkCkh1AZp5smQ71LOprSlmKrg4jd1GjCVMxQR5N5KXbtyv0OTCZ/UFqK +2aRBsMPyJgkaBChkZPLRcKwc+/wlQRx1fHQb14DNTApMxoXFO7eOwqmOkpAt9iyl +SBIwoS0UUI5ab88+bBmXNvKcuFdNuQ4nowTJUn9pAoIBADMNkILBXSvS5DeIyuO2 +Sp1tkoZUV+5NfPY3sMDK3KIibaW/+t+EOBZo4L7tKQCb8vRzl21mmsfxfgRaPDbj +3r3tv9g0b4YLxxBy52pFscj/soXRai17SS7UZwA2QK+XzgDYbDcLNC6mIsTQG4Gx +dsWk3/zs3KuUSQaehmwrWK+fIUK38c1pLK8v7LoxrLkqxlHwZ04RthHw8KTthH7X +Pnl1J0LF8CSeOyfWLSuPUfkT0GEzptnNHpEbaHfQM6R6eaGhVJPF6AZme4y6YYgg +m2ihhSt1n0XVEWpHYWjxFy3mK2mz75unFC4LM+NEY2p2zuUQoCw7NjnY3QYrfCnx +rRMCggEAXeXsMSLFjjyuoL7iKbAxo52HD/P0fBoy58LyRcwfNVr0lvYan4pYEx+o +KijIh9K16PqXZXKMA9v003B+ulmF8bJ7SddCZ5NGvnFhUTDe4DdTKgp2RuwQ3Bsc +3skPIDbhVETyOLCtys34USHrq8U/0DlGY3eLRfxw9GnbKxSBGa/KEu/qQLPNUo50 +7xHZDg7GKeC3kqNJeqKM9rkp0VzIGkEnaD9127LeNDmERDfftxJzFoC/THvUBLfU +6Sus2ZYwRE8VFvKC30Q45t/c54X3IuhYvAuiCuTmyfE4ruyzyOwKzhUkeeLq1APX +g0veFbyfzlJ0q8qzD/iffqqIa2ZSmQ== +-----END PRIVATE KEY----- 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/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/qtbase/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp index 76df61c892..6d6d65b791 100644 --- a/qtbase/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +++ b/qtbase/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -68,6 +68,8 @@ private slots: void query_exec(); void execErrorRecovery_data() { generic_data(); } void execErrorRecovery(); + void prematureExec_data() { generic_data(); } + void prematureExec(); void first_data() { generic_data(); } void first(); void next_data() { generic_data(); } @@ -2770,6 +2772,35 @@ void tst_QSqlQuery::execErrorRecovery() QVERIFY_SQL( q, exec() ); } +void tst_QSqlQuery::prematureExec() +{ + QFETCH(QString, dbName); + // We only want the engine name, for addDatabase(): + int cut = dbName.indexOf(QChar('@')); + if (cut < 0) + QSKIP("Failed to parse database type out of name"); + dbName.truncate(cut); + cut = dbName.indexOf(QChar('_')); + if (cut >= 0) + dbName = dbName.mid(cut + 1); + + auto db = QSqlDatabase::addDatabase(dbName); + QSqlQuery q(db); + + QTest::ignoreMessage(QtWarningMsg, + "QSqlDatabasePrivate::removeDatabase: connection " + "'qt_sql_default_connection' is still in use, all " + "queries will cease to work."); + QTest::ignoreMessage(QtWarningMsg, + "QSqlDatabasePrivate::addDatabase: duplicate connection name " + "'qt_sql_default_connection', old connection removed."); + auto otherDb = QSqlDatabase::addDatabase(dbName); + + QTest::ignoreMessage(QtWarningMsg, "QSqlQuery::exec: called before driver has been set up"); + // QTBUG-100037: shouldn't crash ! + QVERIFY(!q.exec("select stuff from TheVoid")); +} + void tst_QSqlQuery::lastInsertId() { QFETCH( QString, dbName ); diff --git a/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp b/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp index 4aa3f8d60b..d2050a61aa 100644 --- a/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp +++ b/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp @@ -115,6 +115,10 @@ void tst_QAbstractItemModelTester::standardItemModelZeroColumns() // QTBUG-92886 model.insertRows(0, 5); model.removeRows(1, 2); + + const QModelIndex parentIndex = model.index(0, 0); + model.insertRows(0, 5, parentIndex); + model.removeRows(1, 2, parentIndex); } void tst_QAbstractItemModelTester::testInsertThroughProxy() diff --git a/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json b/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json index 12a4a22a19..ce518e78fb 100644 --- a/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json +++ b/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json @@ -668,50 +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" - }, - { - "access": "public", - "name": "d", - "returnType": "void" - }, - { - "access": "public", - "name": "e", - "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 f94a051b3a..0000000000 --- a/qtbase/tests/auto/tools/moc/task189996.h +++ /dev/null @@ -1,47 +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() throw() { } - void b() const throw() { } - void c() throw(); - void d() throw(int) { } - void e() const throw(int,double) { } -}; - -#endif diff --git a/qtbase/tests/auto/tools/moc/tst_moc.cpp b/qtbase/tests/auto/tools/moc/tst_moc.cpp index c716aead21..15dd9e41e5 100644 --- a/qtbase/tests/auto/tools/moc/tst_moc.cpp +++ b/qtbase/tests/auto/tools/moc/tst_moc.cpp @@ -631,7 +631,6 @@ public: private slots: void initTestCase(); - void slotWithException() throw(MyStruct); void dontStripNamespaces(); void oldStyleCasts(); void warnOnExtraSignalSlotQualifiaction(); @@ -673,7 +672,6 @@ private slots: void templateGtGt(); void qprivateslots(); void qprivateproperties(); - void inlineSlotsWithThrowDeclaration(); void warnOnPropertyWithoutREAD(); void constructors(); void typenameWithUnsigned(); @@ -784,12 +782,6 @@ void tst_Moc::initTestCase() #endif } -void tst_Moc::slotWithException() throw(MyStruct) -{ - // be happy - QVERIFY(true); -} - void tst_Moc::dontStripNamespaces() { Sender sender; @@ -824,7 +816,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); @@ -894,7 +886,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); @@ -1174,7 +1166,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); @@ -1593,21 +1585,6 @@ void tst_Moc::qprivateproperties() } -#include "task189996.h" - -void InlineSlotsWithThrowDeclaration::c() throw() {} - -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); - QVERIFY(mobj->indexOfSlot("d()") != -1); - QVERIFY(mobj->indexOfSlot("e()") != -1); -} - void tst_Moc::warnOnPropertyWithoutREAD() { #ifdef MOC_CROSS_COMPILED @@ -1869,7 +1846,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/qheaderview/tst_qheaderview.cpp b/qtbase/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp index c355ee9665..88c09de8e0 100644 --- a/qtbase/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp +++ b/qtbase/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp @@ -251,6 +251,7 @@ private slots: void testResetCachedSizeHint(); void statusTips(); void testRemovingColumnsViaLayoutChanged(); + void testModelMovingColumns(); protected: void setupTestData(bool use_reset_model = false); @@ -360,6 +361,12 @@ public: endRemoveColumns(); } + void moveColumn(int from, int to) + { + beginMoveColumns(QModelIndex(), from, from, QModelIndex(), to); + endMoveColumns(); + } + void cleanup() { emit layoutAboutToBeChanged(); @@ -3627,5 +3634,18 @@ void tst_QHeaderView::testRemovingColumnsViaLayoutChanged() // The main point of this test is that the section-size restoring code didn't go out of bounds. } +void tst_QHeaderView::testModelMovingColumns() +{ + QtTestModel model(10, 10); + QHeaderView hv(Qt::Horizontal); + hv.setModel(&model); + hv.resizeSections(QHeaderView::ResizeToContents); + hv.show(); + + QPersistentModelIndex index3 = model.index(0, 3); + model.moveColumn(3, 1); + QCOMPARE(index3.column(), 1); +} + QTEST_MAIN(tst_QHeaderView) #include "tst_qheaderview.moc" 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/kernel/qapplication/tst_qapplication.cpp b/qtbase/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index a3b8ef78d8..4955cebd5d 100644 --- a/qtbase/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/qtbase/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -1498,7 +1498,7 @@ void tst_QApplication::desktopSettingsAware() environment += QLatin1String("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM=1"); testProcess.setEnvironment(environment); #endif - testProcess.start("desktopsettingsaware_helper"); + testProcess.start("./desktopsettingsaware_helper"); QVERIFY2(testProcess.waitForStarted(), qPrintable(QString::fromLatin1("Cannot start 'desktopsettingsaware_helper': %1").arg(testProcess.errorString()))); QVERIFY(testProcess.waitForFinished(10000)); @@ -2452,7 +2452,7 @@ void tst_QApplication::qtbug_12673() #if QT_CONFIG(process) QProcess testProcess; QStringList arguments; - testProcess.start("modal_helper", arguments); + testProcess.start("./modal_helper", arguments); QVERIFY2(testProcess.waitForStarted(), qPrintable(QString::fromLatin1("Cannot start 'modal_helper': %1").arg(testProcess.errorString()))); QVERIFY(testProcess.waitForFinished(20000)); 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 32437050f5..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; @@ -1682,6 +1683,16 @@ void tst_QComboBox::setModel() QCOMPARE(box.rootModelIndex(), rootModelIndex); box.setModel(box.model()); QCOMPARE(box.rootModelIndex(), rootModelIndex); + + // check that setting the same model as the completer's doesn't crash + QCompleter *completer = new QCompleter(&box); + box.setEditable(true); + box.setCompleter(completer); + auto *listModel = new QStringListModel({ "one", "two" }, completer); + completer->setModel(listModel); + QCOMPARE(listModel->rowCount(), 2); // make sure it wasn't deleted + box.setModel(listModel); + QCOMPARE(listModel->rowCount(), 2); // make sure it wasn't deleted } void tst_QComboBox::setCustomModelAndView() @@ -3632,5 +3643,24 @@ void tst_QComboBox::propagateStyleChanges() QVERIFY(frameStyle.inquired); } +void tst_QComboBox::buttonPressKeys() +{ + QComboBox comboBox; + comboBox.setEditable(false); + comboBox.addItem(QString::number(1)); + comboBox.addItem(QString::number(2)); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + for (int i = 0; i < buttonPressKeys.length(); ++i) { + QTest::keyClick(&comboBox, buttonPressKeys[i]); + // On some platforms, a window will not be immediately visible, + // but take some event-loop iterations to complete. + // Using QTRY_VERIFY to deal with that. + QTRY_VERIFY(comboBox.view()->isVisible()); + comboBox.hidePopup(); + } +} + QTEST_MAIN(tst_QComboBox) #include "tst_qcombobox.moc" diff --git a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro index be3cfcd104..c228fdfcca 100644 --- a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro +++ b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qcommandlinkbutton -QT += widgets testlib +QT += widgets testlib gui-private SOURCES += tst_qcommandlinkbutton.cpp diff --git a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp index 0044d33c66..4cf06296e4 100644 --- a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp +++ b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp @@ -40,6 +40,9 @@ #include #include +#include +#include + class tst_QCommandLinkButton : public QObject { Q_OBJECT @@ -223,6 +226,13 @@ void tst_QCommandLinkButton::setAutoRepeat() // check that pressing ENTER has no effect resetCounters(); testWidget->setDown( false ); + // Skip after reset if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } testWidget->setAutoRepeat( false ); QTest::keyPress( testWidget, Qt::Key_Enter ); @@ -255,6 +265,14 @@ void tst_QCommandLinkButton::pressed() QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); + // Skip if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } + QTest::keyPress( testWidget,Qt::Key_Enter ); QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); diff --git a/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro b/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro index 4a5e76ff65..a235fa1fac 100644 --- a/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro +++ b/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qgroupbox -QT += widgets testlib +QT += widgets testlib gui-private SOURCES += tst_qgroupbox.cpp diff --git a/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp b/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp index 4fb5d262ca..d8d7562b73 100644 --- a/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp +++ b/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp @@ -35,6 +35,9 @@ #include #include +#include +#include + #include "qgroupbox.h" class tst_QGroupBox : public QObject @@ -69,6 +72,7 @@ private slots: void propagateFocus(); void task_QTBUG_19170_ignoreMouseReleaseEvent(); void task_QTBUG_15519_propagateMouseEvents(); + void buttonPressKeys(); private: bool checked; @@ -610,6 +614,20 @@ void tst_QGroupBox::task_QTBUG_15519_propagateMouseEvents() QCOMPARE(parent.mouseMoved, true); } +void tst_QGroupBox::buttonPressKeys() +{ + QGroupBox groupBox; + groupBox.setCheckable(true); + QSignalSpy clickedSpy(&groupBox, &QGroupBox::clicked); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + for (int i = 0; i < buttonPressKeys.length(); ++i) { + QTest::keyClick(&groupBox, buttonPressKeys[i]); + QCOMPARE(clickedSpy.length(), i + 1); + } +} + void tst_QGroupBox::sendMouseMoveEvent(QWidget *widget, const QPoint &localPos) { // Send a MouseMove event without actually moving the pointer diff --git a/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro b/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro index 353ad06ca2..e55f6148f2 100644 --- a/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro +++ b/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qpushbutton -QT += widgets testlib +QT += widgets testlib gui-private SOURCES += tst_qpushbutton.cpp diff --git a/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp b/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp index e818514a79..4043e9326a 100644 --- a/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +++ b/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp @@ -41,6 +41,9 @@ #include #include +#include +#include + class tst_QPushButton : public QObject { Q_OBJECT @@ -212,6 +215,13 @@ void tst_QPushButton::autoRepeat() // check that pressing ENTER has no effect resetCounters(); testWidget->setDown( false ); + // Skip after reset if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } testWidget->setAutoRepeat( false ); QTest::keyPress( testWidget, Qt::Key_Enter ); @@ -247,6 +257,14 @@ void tst_QPushButton::pressed() QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); + // Skip if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } + QTest::keyPress( testWidget,Qt::Key_Enter ); QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); diff --git a/qtbase/tests/manual/rhi/cubemap_render/buildshader.bat b/qtbase/tests/manual/rhi/cubemap_render/buildshader.bat old mode 100755 new mode 100644 Submodule qtconnectivity e957d481..056294c0: diff --git a/qtconnectivity/src/bluetooth/bluez/hcimanager.cpp b/qtconnectivity/src/bluetooth/bluez/hcimanager.cpp index e2635fae..a8b8e3b9 100644 --- a/qtconnectivity/src/bluetooth/bluez/hcimanager.cpp +++ b/qtconnectivity/src/bluetooth/bluez/hcimanager.cpp @@ -563,9 +563,11 @@ void HciManager::handleHciAclPacket(const quint8 *data, int size) void HciManager::handleLeMetaEvent(const quint8 *data) { - // Spec v4.2, Vol 2, part E, 7.7.65ff + // Spec v5.3, Vol 4, part E, 7.7.65.* switch (*data) { - case 0x1: { + case 0x1: // HCI_LE_Connection_Complete + case 0xA: // HCI_LE_Enhanced_Connection_Complete + { const quint16 handle = bt_get_le16(data + 2); emit connectionComplete(handle); break; diff --git a/qtconnectivity/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/qtconnectivity/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp index 6a93143b..4a18cfc7 100644 --- a/qtconnectivity/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp +++ b/qtconnectivity/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp @@ -320,7 +320,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_finishSdpScan(QBluetoothServiceD for (const QBluetoothUuid &id : serviceClassUuids) { if (id.minimumSize() == 16) { serviceInfo.setServiceUuid(id); - serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Custom Service")); + if (serviceInfo.serviceName().isEmpty()) { + serviceInfo.setServiceName( + QBluetoothServiceDiscoveryAgent::tr("Custom Service")); + } QBluetoothServiceInfo::Sequence modSeq = serviceInfo.attribute(QBluetoothServiceInfo::ServiceClassIds).value(); modSeq.removeOne(QVariant::fromValue(id)); @@ -334,8 +337,10 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_finishSdpScan(QBluetoothServiceD qCDebug(QT_BT_BLUEZ) << "Discovered services" << discoveredDevices.at(0).address().toString() << serviceInfo.serviceName() << serviceInfo.serviceUuid() << ">>>" << serviceInfo.serviceClassUuids(); - - emit q->serviceDiscovered(serviceInfo); + // Use queued connection to allow us finish the service looping; the application + // might call stop() when it has detected the service-of-interest. + QMetaObject::invokeMethod(q, "serviceDiscovered", Qt::QueuedConnection, + Q_ARG(QBluetoothServiceInfo, serviceInfo)); } } } diff --git a/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp b/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp index e4d85447..aadd4755 100644 --- a/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp +++ b/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp @@ -593,7 +593,7 @@ void QBluetoothSocket::setSocketState(QBluetoothSocket::SocketState state) bool QBluetoothSocket::canReadLine() const { Q_D(const QBluetoothSocketBase); - return d->canReadLine(); + return d->canReadLine() || QIODevice::canReadLine(); } /*! diff --git a/qtconnectivity/src/bluetooth/qbluetoothsocket_bluezdbus.cpp b/qtconnectivity/src/bluetooth/qbluetoothsocket_bluezdbus.cpp index 084aa958..d6aa17a7 100644 --- a/qtconnectivity/src/bluetooth/qbluetoothsocket_bluezdbus.cpp +++ b/qtconnectivity/src/bluetooth/qbluetoothsocket_bluezdbus.cpp @@ -284,6 +284,10 @@ void QBluetoothSocketPrivateBluezDBus::connectToService( return; } + if (service.socketProtocol() != QBluetoothServiceInfo::Protocol::UnknownProtocol) + socketType = service.socketProtocol(); + qCDebug(QT_BT_BLUEZ) << "Socket protocol used:" << socketType; + connectToService(service.device().address(), targetService, openMode); } diff --git a/qtconnectivity/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp b/qtconnectivity/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp index 05bc1a0f..a7b5ef1f 100644 --- a/qtconnectivity/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp +++ b/qtconnectivity/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp @@ -142,7 +142,7 @@ void tst_QBluetoothSocket::initTestCase() qDebug() << "Starting discovery"; sda->setUuidFilter(QBluetoothUuid(QString(TEST_SERVICE_UUID))); - sda->start(QBluetoothServiceDiscoveryAgent::MinimalDiscovery); + sda->start(QBluetoothServiceDiscoveryAgent::FullDiscovery); for (int connectTime = MaxConnectTime; !done_discovery && connectTime > 0; connectTime -= 1000) QTest::qWait(1000); Submodule qtdeclarative f5701f0d..8defe7bf: diff --git a/qtdeclarative/src/qml/common/qqmljsfixedpoolarray_p.h b/qtdeclarative/src/qml/common/qqmljsfixedpoolarray_p.h index b65b994d6c..15a8cd6878 100644 --- a/qtdeclarative/src/qml/common/qqmljsfixedpoolarray_p.h +++ b/qtdeclarative/src/qml/common/qqmljsfixedpoolarray_p.h @@ -86,7 +86,7 @@ public: if (QTypeInfo::isComplex) { for (int i = 0; i < count; ++i) new (data + i) T(vector.at(i)); - } else { + } else if (count) { memcpy(data, static_cast(vector.constData()), count * sizeof(T)); } } diff --git a/qtdeclarative/src/qml/jsruntime/qv4qobjectwrapper.cpp b/qtdeclarative/src/qml/jsruntime/qv4qobjectwrapper.cpp index e57cdd8278..94613598af 100644 --- a/qtdeclarative/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/qtdeclarative/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1145,8 +1145,7 @@ void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markSta void QObjectWrapper::destroyObject(bool lastCall) { Heap::QObjectWrapper *h = d(); - if (!h->internalClass) - return; // destroyObject already got called + Q_ASSERT(h->internalClass); if (h->object()) { QQmlData *ddata = QQmlData::get(h->object(), false); @@ -1176,7 +1175,7 @@ void QObjectWrapper::destroyObject(bool lastCall) } } - h->~Data(); + h->destroy(); } diff --git a/qtdeclarative/src/qml/memory/qv4mm.cpp b/qtdeclarative/src/qml/memory/qv4mm.cpp index 06caf04e5a..da149a67c4 100644 --- a/qtdeclarative/src/qml/memory/qv4mm.cpp +++ b/qtdeclarative/src/qml/memory/qv4mm.cpp @@ -981,7 +981,7 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = engine->m_multiplyWrappedQObjects) { for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) { - if (!it.value().isNullOrUndefined()) + if (it.value().isNullOrUndefined()) it = multiplyWrappedQObjects->erase(it); else ++it; diff --git a/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp b/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp index 2079a8ed04..a577cb2351 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)), @@ -1973,6 +1985,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 85719fdc80..78e2ab302c 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(); @@ -188,7 +197,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; @@ -201,6 +210,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 75f1457816..dec0ae19ae 100644 --- a/qtdeclarative/src/quick/items/qquickitem.cpp +++ b/qtdeclarative/src/quick/items/qquickitem.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -2326,6 +2327,7 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent) QQuickItem::~QQuickItem() { Q_D(QQuickItem); + d->inDestructor = true; if (d->windowRefCount > 1) d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow(). @@ -2526,6 +2528,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo QQuickItem *current = item; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem; + QDuplicateTracker cycleDetector; do { qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current; qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from; @@ -2592,7 +2595,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo // traversed all of the chain (by compare the [current] item with [startItem]) // Since the [startItem] might be promoted to its parent if it is invisible, // we still have to check [current] item with original start item - if ((current == startItem || current == originalStartItem) && from == firstFromItem) { + // We might also run into a cycle before we reach firstFromItem again + // but note that we have to ignore current if we are meant to skip it + if (((current == startItem || current == originalStartItem) && from == firstFromItem) || + (!skip && cycleDetector.hasSeen(current))) { // wrapped around, avoid endless loops if (item == contentItem) { qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem"; @@ -2689,9 +2695,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) const bool wasVisible = isVisible(); op->removeChild(this); - if (wasVisible) { + if (wasVisible && !op->inDestructor) emit oldParentItem->visibleChildrenChanged(); - } } else if (d->window) { QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this); } @@ -2768,8 +2773,9 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) d->itemChange(ItemParentHasChanged, d->parentItem); - emit parentChanged(d->parentItem); - if (isVisible() && d->parentItem) + if (!d->inDestructor) + emit parentChanged(d->parentItem); + if (isVisible() && d->parentItem && !QQuickItemPrivate::get(d->parentItem)->inDestructor) emit d->parentItem->visibleChildrenChanged(); } @@ -2965,7 +2971,8 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) itemChange(QQuickItem::ItemChildRemovedChange, child); - emit q->childrenChanged(); + if (!inDestructor) + emit q->childrenChanged(); } void QQuickItemPrivate::refWindow(QQuickWindow *c) @@ -3194,6 +3201,7 @@ QQuickItemPrivate::QQuickItemPrivate() , touchEnabled(false) #endif , hasCursorHandler(false) + , inDestructor(false) , dirtyAttributes(0) , nextDirtyItem(nullptr) , prevDirtyItem(nullptr) @@ -5120,6 +5128,13 @@ void QQuickItem::componentComplete() d->addToDirtyList(); QQuickWindowPrivate::get(d->window)->dirtyItem(this); } + +#if QT_CONFIG(accessibility) + if (d->isAccessible && d->effectiveVisible) { + QAccessibleEvent ev(this, QAccessible::ObjectShow); + QAccessible::updateAccessibility(&ev); + } +#endif } QQuickStateGroup *QQuickItemPrivate::_states() @@ -6106,9 +6121,11 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) QAccessible::updateAccessibility(&ev); } #endif - emit q->visibleChanged(); - if (childVisibilityChanged) - emit q->visibleChildrenChanged(); + if (!inDestructor) { + emit q->visibleChanged(); + if (childVisibilityChanged) + emit q->visibleChildrenChanged(); + } return true; // effective visibility DID change } @@ -6157,6 +6174,15 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec } itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable); +#if QT_CONFIG(accessibility) + if (isAccessible) { + QAccessible::State changedState; + changedState.disabled = true; + changedState.focusable = true; + QAccessibleStateChangeEvent ev(q, changedState); + QAccessible::updateAccessibility(&ev); + } +#endif emit q->enabledChanged(); } diff --git a/qtdeclarative/src/quick/items/qquickitem_p.h b/qtdeclarative/src/quick/items/qquickitem_p.h index 841d91bb40..ade8fb61f2 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/qquickitemview.cpp b/qtdeclarative/src/quick/items/qquickitemview.cpp index 010a0152e1..f8ad168a17 100644 --- a/qtdeclarative/src/quick/items/qquickitemview.cpp +++ b/qtdeclarative/src/quick/items/qquickitemview.cpp @@ -1785,7 +1785,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to) do { bufferPause.stop(); - if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges()) { + if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges() || currentChanges.active) { currentChanges.reset(); bufferedChanges.reset(); releaseVisibleItems(reusableFlag); diff --git a/qtdeclarative/src/quick/items/qquickmousearea_p_p.h b/qtdeclarative/src/quick/items/qquickmousearea_p_p.h index fba383e268..0d63618622 100644 --- a/qtdeclarative/src/quick/items/qquickmousearea_p_p.h +++ b/qtdeclarative/src/quick/items/qquickmousearea_p_p.h @@ -61,7 +61,6 @@ QT_BEGIN_NAMESPACE class QQuickMouseEvent; class QQuickMouseArea; -class QQuickPointerMask; class QQuickMouseAreaPrivate : public QQuickItemPrivate { Q_DECLARE_PUBLIC(QQuickMouseArea) @@ -100,7 +99,6 @@ public: #if QT_CONFIG(quick_draganddrop) QQuickDrag *drag; #endif - QPointer mask; QPointF startScene; QPointF targetStartPos; QPointF lastPos; diff --git a/qtdeclarative/src/quick/items/qquicktext.cpp b/qtdeclarative/src/quick/items/qquicktext.cpp index 6230186933..e823ca1095 100644 --- a/qtdeclarative/src/quick/items/qquicktext.cpp +++ b/qtdeclarative/src/quick/items/qquicktext.cpp @@ -2168,7 +2168,7 @@ void QQuickText::resetMaximumLineCount() - inline images
    ,
      and
    • - ordered and unordered lists
       - preformatted
      -    > < &
      +    > < & "   '
           \endcode
       
           \c Text.StyledText parser is strict, requiring tags to be correctly nested.
      diff --git a/qtdeclarative/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp b/qtdeclarative/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
      index eb4db0f85e..2325a2665b 100644
      --- a/qtdeclarative/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
      +++ b/qtdeclarative/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
      @@ -446,7 +446,7 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
       
               const char *textureRecord = allocatorData;
               for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
      -            if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
      +            if (qtdfTableEnd - textureRecord < Qtdf::TextureRecordSize) {
                       qWarning("qtdf table too small in font '%s'.",
                                qPrintable(font.familyName()));
                       return false;
      @@ -462,7 +462,7 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
       
               const char *glyphRecord = textureRecord;
               for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
      -            if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
      +            if (qtdfTableEnd - glyphRecord < Qtdf:: GlyphRecordSize) {
                       qWarning("qtdf table too small in font '%s'.",
                                qPrintable(font.familyName()));
                       return false;
      @@ -512,8 +512,8 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
       
                   int width = texInfo->allocatedArea.width();
                   int height = texInfo->allocatedArea.height();
      -            qint64 size = width * height;
      -            if (reinterpret_cast(textureData + size) > qtdfTableEnd) {
      +            qint64 size = qint64(width) * height;
      +            if (qtdfTableEnd - reinterpret_cast(textureData) < size) {
                       qWarning("qtdf table too small in font '%s'.",
                                qPrintable(font.familyName()));
                       return false;
      diff --git a/qtdeclarative/src/quick/util/qquickstyledtext.cpp b/qtdeclarative/src/quick/util/qquickstyledtext.cpp
      index d531fc9205..a25af90414 100644
      --- a/qtdeclarative/src/quick/util/qquickstyledtext.cpp
      +++ b/qtdeclarative/src/quick/util/qquickstyledtext.cpp
      @@ -564,6 +564,8 @@ void QQuickStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textI
                       textOut += QChar(60);
                   else if (entity == QLatin1String("amp"))
                       textOut += QChar(38);
      +            else if (entity == QLatin1String("apos"))
      +                textOut += QChar(39);
                   else if (entity == QLatin1String("quot"))
                       textOut += QChar(34);
                   else if (entity == QLatin1String("nbsp"))
      diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp
      new file mode 100644
      index 0000000000..8a1c901880
      --- /dev/null
      +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp
      @@ -0,0 +1,110 @@
      +/****************************************************************************
      +**
      +** Copyright (C) 2021 The Qt Company Ltd.
      +** Contact: https://www.qt.io/licensing/
      +**
      +** This file is part of the QtQuick module of the Qt Toolkit.
      +**
      +** $QT_BEGIN_LICENSE:LGPL$
      +** Commercial License Usage
      +** Licensees holding valid commercial Qt licenses may use this file in
      +** accordance with the commercial license agreement provided with the
      +** Software or, alternatively, in accordance with the terms contained in
      +** a written agreement between you and The Qt Company. For licensing terms
      +** and conditions see https://www.qt.io/terms-conditions. For further
      +** information use the contact form at https://www.qt.io/contact-us.
      +**
      +** GNU Lesser General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU Lesser
      +** General Public License version 3 as published by the Free Software
      +** Foundation and appearing in the file LICENSE.LGPL3 included in the
      +** packaging of this file. Please review the following information to
      +** ensure the GNU Lesser General Public License version 3 requirements
      +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
      +**
      +** GNU General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU
      +** General Public License version 2.0 or (at your option) the GNU General
      +** Public license version 3 or any later version approved by the KDE Free
      +** Qt Foundation. The licenses are as published by the Free Software
      +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
      +** included in the packaging of this file. Please review the following
      +** information to ensure the GNU General Public License requirements will
      +** be met: https://www.gnu.org/licenses/gpl-2.0.html and
      +** https://www.gnu.org/licenses/gpl-3.0.html.
      +**
      +** $QT_END_LICENSE$
      +**
      +****************************************************************************/
      +
      +#include "qaccessiblequickwidget_p.h"
      +
      +#include "qquickwidget_p.h"
      +
      +QT_BEGIN_NAMESPACE
      +
      +#if QT_CONFIG(accessibility)
      +
      +QAccessibleQuickWidget::QAccessibleQuickWidget(QQuickWidget* widget)
      +: QAccessibleWidget(widget)
      +, m_accessibleWindow(QQuickWidgetPrivate::get(widget)->offscreenWindow)
      +{
      +    // NOTE: m_accessibleWindow is a QAccessibleQuickWindow, and not a
      +    // QAccessibleQuickWidgetOffscreenWindow (defined below). This means
      +    // it will return the Quick item child interfaces, which is what's needed here
      +    // (unlike QAccessibleQuickWidgetOffscreenWindow, which will report 0 children).
      +}
      +
      +QAccessibleInterface *QAccessibleQuickWidget::child(int index) const
      +{
      +    return m_accessibleWindow.child(index);
      +}
      +
      +int QAccessibleQuickWidget::childCount() const
      +{
      +    return m_accessibleWindow.childCount();
      +}
      +
      +int QAccessibleQuickWidget::indexOfChild(const QAccessibleInterface *iface) const
      +{
      +    return m_accessibleWindow.indexOfChild(iface);
      +}
      +
      +QAccessibleInterface *QAccessibleQuickWidget::childAt(int x, int y) const
      +{
      +    return m_accessibleWindow.childAt(x, y);
      +}
      +
      +QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window)
      +:QAccessibleQuickWindow(window)
      +{
      +
      +}
      +
      +QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::child(int index) const
      +{
      +    Q_UNUSED(index);
      +    return nullptr;
      +}
      +
      +int QAccessibleQuickWidgetOffscreenWindow::childCount() const
      +{
      +    return 0;
      +}
      +
      +int QAccessibleQuickWidgetOffscreenWindow::indexOfChild(const QAccessibleInterface *iface) const
      +{
      +    Q_UNUSED(iface);
      +    return -1;
      +}
      +
      +QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow::childAt(int x, int y) const
      +{
      +    Q_UNUSED(x);
      +    Q_UNUSED(y);
      +    return nullptr;
      +}
      +
      +#endif // accessibility
      +
      +QT_END_NAMESPACE
      diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h
      new file mode 100644
      index 0000000000..7c2ab930e0
      --- /dev/null
      +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h
      @@ -0,0 +1,95 @@
      +/****************************************************************************
      +**
      +** Copyright (C) 2021 The Qt Company Ltd.
      +** Contact: https://www.qt.io/licensing/
      +**
      +** This file is part of the QtQuick module of the Qt Toolkit.
      +**
      +** $QT_BEGIN_LICENSE:LGPL$
      +** Commercial License Usage
      +** Licensees holding valid commercial Qt licenses may use this file in
      +** accordance with the commercial license agreement provided with the
      +** Software or, alternatively, in accordance with the terms contained in
      +** a written agreement between you and The Qt Company. For licensing terms
      +** and conditions see https://www.qt.io/terms-conditions. For further
      +** information use the contact form at https://www.qt.io/contact-us.
      +**
      +** GNU Lesser General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU Lesser
      +** General Public License version 3 as published by the Free Software
      +** Foundation and appearing in the file LICENSE.LGPL3 included in the
      +** packaging of this file. Please review the following information to
      +** ensure the GNU Lesser General Public License version 3 requirements
      +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
      +**
      +** GNU General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU
      +** General Public License version 2.0 or (at your option) the GNU General
      +** Public license version 3 or any later version approved by the KDE Free
      +** Qt Foundation. The licenses are as published by the Free Software
      +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
      +** included in the packaging of this file. Please review the following
      +** information to ensure the GNU General Public License requirements will
      +** be met: https://www.gnu.org/licenses/gpl-2.0.html and
      +** https://www.gnu.org/licenses/gpl-3.0.html.
      +**
      +** $QT_END_LICENSE$
      +**
      +****************************************************************************/
      +
      +#ifndef QACCESSIBLEQUICKWIDGET_H
      +#define QACCESSIBLEQUICKWIDGET_H
      +
      +//
      +//  W A R N I N G
      +//  -------------
      +//
      +// This file is not part of the Qt API.  It exists purely as an
      +// implementation detail.  This header file may change from version to
      +// version without notice, or even be removed.
      +//
      +// We mean it.
      +//
      +
      +#include "qquickwidget.h"
      +#include 
      +
      +#include 
      +
      +QT_BEGIN_NAMESPACE
      +
      +#if QT_CONFIG(accessibility)
      +
      +// These classes implement the QQuickWiget accessibility switcharoo,
      +// where the child items of the QQuickWidgetOffscreenWindow are reported
      +// as child accessible interfaces of the QAccessibleQuickWidget.
      +class QAccessibleQuickWidget: public QAccessibleWidget
      +{
      +public:
      +    QAccessibleQuickWidget(QQuickWidget* widget);
      +
      +    QAccessibleInterface *child(int index) const override;
      +    int childCount() const override;
      +    int indexOfChild(const QAccessibleInterface *iface) const override;
      +    QAccessibleInterface *childAt(int x, int y) const override;
      +
      +private:
      +    QAccessibleQuickWindow m_accessibleWindow;
      +    Q_DISABLE_COPY(QAccessibleQuickWidget)
      +};
      +
      +class QAccessibleQuickWidgetOffscreenWindow: public QAccessibleQuickWindow
      +{
      +public:
      +    QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window);
      +    QAccessibleInterface *child(int index) const override;
      +    int childCount() const override;
      +    int indexOfChild(const QAccessibleInterface *iface) const override;
      +    QAccessibleInterface *childAt(int x, int y) const override;
      +};
      +
      +#endif // accessibility
      +
      +QT_END_NAMESPACE
      +
      +#endif
      diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp
      new file mode 100644
      index 0000000000..7ba88a1769
      --- /dev/null
      +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp
      @@ -0,0 +1,60 @@
      +/****************************************************************************
      +**
      +** Copyright (C) 2021 The Qt Company Ltd.
      +** Contact: https://www.qt.io/licensing/
      +**
      +** This file is part of the QtQuick module of the Qt Toolkit.
      +**
      +** $QT_BEGIN_LICENSE:LGPL$
      +** Commercial License Usage
      +** Licensees holding valid commercial Qt licenses may use this file in
      +** accordance with the commercial license agreement provided with the
      +** Software or, alternatively, in accordance with the terms contained in
      +** a written agreement between you and The Qt Company. For licensing terms
      +** and conditions see https://www.qt.io/terms-conditions. For further
      +** information use the contact form at https://www.qt.io/contact-us.
      +**
      +** GNU Lesser General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU Lesser
      +** General Public License version 3 as published by the Free Software
      +** Foundation and appearing in the file LICENSE.LGPL3 included in the
      +** packaging of this file. Please review the following information to
      +** ensure the GNU Lesser General Public License version 3 requirements
      +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
      +**
      +** GNU General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU
      +** General Public License version 2.0 or (at your option) the GNU General
      +** Public license version 3 or any later version approved by the KDE Free
      +** Qt Foundation. The licenses are as published by the Free Software
      +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
      +** included in the packaging of this file. Please review the following
      +** information to ensure the GNU General Public License requirements will
      +** be met: https://www.gnu.org/licenses/gpl-2.0.html and
      +** https://www.gnu.org/licenses/gpl-3.0.html.
      +**
      +** $QT_END_LICENSE$
      +**
      +****************************************************************************/
      +
      +#include "qaccessiblequickwidgetfactory_p.h"
      +#include "qaccessiblequickwidget_p.h"
      +
      +QT_BEGIN_NAMESPACE
      +
      +#if QT_CONFIG(accessibility)
      +
      +QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object)
      +{
      +    if (classname == QLatin1String("QQuickWidget")) {
      +        return new QAccessibleQuickWidget(qobject_cast(object));
      +    } else if (classname == QLatin1String("QQuickWidgetOffscreenWindow")) {
      +        return new QAccessibleQuickWidgetOffscreenWindow(qobject_cast(object));
      +    }
      +    return 0;
      +}
      +
      +#endif // accessibility
      +
      +QT_END_NAMESPACE
      +
      diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h
      new file mode 100644
      index 0000000000..8c63b09f81
      --- /dev/null
      +++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h
      @@ -0,0 +1,66 @@
      +/****************************************************************************
      +**
      +** Copyright (C) 2021 The Qt Company Ltd.
      +** Contact: https://www.qt.io/licensing/
      +**
      +** This file is part of the QtQuick module of the Qt Toolkit.
      +**
      +** $QT_BEGIN_LICENSE:LGPL$
      +** Commercial License Usage
      +** Licensees holding valid commercial Qt licenses may use this file in
      +** accordance with the commercial license agreement provided with the
      +** Software or, alternatively, in accordance with the terms contained in
      +** a written agreement between you and The Qt Company. For licensing terms
      +** and conditions see https://www.qt.io/terms-conditions. For further
      +** information use the contact form at https://www.qt.io/contact-us.
      +**
      +** GNU Lesser General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU Lesser
      +** General Public License version 3 as published by the Free Software
      +** Foundation and appearing in the file LICENSE.LGPL3 included in the
      +** packaging of this file. Please review the following information to
      +** ensure the GNU Lesser General Public License version 3 requirements
      +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
      +**
      +** GNU General Public License Usage
      +** Alternatively, this file may be used under the terms of the GNU
      +** General Public License version 2.0 or (at your option) the GNU General
      +** Public license version 3 or any later version approved by the KDE Free
      +** Qt Foundation. The licenses are as published by the Free Software
      +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
      +** included in the packaging of this file. Please review the following
      +** information to ensure the GNU General Public License requirements will
      +** be met: https://www.gnu.org/licenses/gpl-2.0.html and
      +** https://www.gnu.org/licenses/gpl-3.0.html.
      +**
      +** $QT_END_LICENSE$
      +**
      +****************************************************************************/
      +
      +#include 
      +
      +#ifndef QACCESSIBLEQUICKWIDGETFACTORY_H
      +#define QACCESSIBLEQUICKWIDGETFACTORY_H
      +
      +//
      +//  W A R N I N G
      +//  -------------
      +//
      +// This file is not part of the Qt API.  It exists purely as an
      +// implementation detail.  This header file may change from version to
      +// version without notice, or even be removed.
      +//
      +// We mean it.
      +//
      +
      +QT_BEGIN_NAMESPACE
      +
      +#if QT_CONFIG(accessibility)
      +
      +QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object);
      +
      +#endif
      +
      +QT_END_NAMESPACE
      +
      +#endif
      diff --git a/qtdeclarative/src/quickwidgets/qquickwidget.cpp b/qtdeclarative/src/quickwidgets/qquickwidget.cpp
      index 39780f8de3..9c97b43518 100644
      --- a/qtdeclarative/src/quickwidgets/qquickwidget.cpp
      +++ b/qtdeclarative/src/quickwidgets/qquickwidget.cpp
      @@ -39,6 +39,7 @@
       
       #include "qquickwidget.h"
       #include "qquickwidget_p.h"
      +#include "qaccessiblequickwidgetfactory_p.h"
       
       #include "private/qquickwindow_p.h"
       #include "private/qquickitem_p.h"
      @@ -75,9 +76,16 @@
       
       QT_BEGIN_NAMESPACE
       
      +QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control)
      +:QQuickWindow(dd, control)
      +{
      +    setTitle(QString::fromLatin1("Offscreen"));
      +    setObjectName(QString::fromLatin1("QQuickOffScreenWindow"));
      +}
      +
       // override setVisble to prevent accidental offscreen window being created
       // by base class.
      -class QQuickOffcreenWindowPrivate: public QQuickWindowPrivate {
      +class QQuickWidgetOffscreenWindowPrivate: public QQuickWindowPrivate {
       public:
           void setVisible(bool visible) override {
               Q_Q(QWindow);
      @@ -105,9 +113,8 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
           Q_Q(QQuickWidget);
       
           renderControl = new QQuickWidgetRenderControl(q);
      -    offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl);
      -    offscreenWindow->setTitle(QString::fromLatin1("Offscreen"));
      -    offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow"));
      +    offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl);
      +    offscreenWindow->setScreen(q->screen());
           // Do not call create() on offscreenWindow.
       
           // Check if the Software Adaptation is being used
      @@ -138,6 +145,10 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
           QWidget::connect(offscreenWindow, &QQuickWindow::focusObjectChanged, q, &QQuickWidget::propagateFocusObjectChanged);
           QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate()));
           QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate()));
      +
      +#if QT_CONFIG(accessibility)
      +    QAccessible::installFactory(&qAccessibleQuickWidgetFactory);
      +#endif
       }
       
       void QQuickWidgetPrivate::ensureEngine() const
      @@ -901,9 +912,7 @@ void QQuickWidgetPrivate::createContext()
       
               context = new QOpenGLContext;
               context->setFormat(offscreenWindow->requestedFormat());
      -        const QWindow *win = q->window()->windowHandle();
      -        if (win && win->screen())
      -            context->setScreen(win->screen());
      +        context->setScreen(q->screen());
               QOpenGLContext *shareContext = qt_gl_global_share_context();
               if (!shareContext)
                   shareContext = QWidgetPrivate::get(q->window())->shareContext();
      @@ -1520,19 +1529,16 @@ bool QQuickWidget::event(QEvent *e)
               d->handleWindowChange();
               break;
       
      -    case QEvent::ScreenChangeInternal:
      -        if (QWindow *window = this->window()->windowHandle()) {
      -            QScreen *newScreen = window->screen();
      -
      -            if (d->offscreenWindow)
      -                d->offscreenWindow->setScreen(newScreen);
      -            if (d->offscreenSurface)
      -                d->offscreenSurface->setScreen(newScreen);
      +    case QEvent::ScreenChangeInternal: {
      +        QScreen *newScreen = screen();
      +        if (d->offscreenWindow)
      +            d->offscreenWindow->setScreen(newScreen);
      +        if (d->offscreenSurface)
      +            d->offscreenSurface->setScreen(newScreen);
       #if QT_CONFIG(opengl)
      -            if (d->context)
      -                d->context->setScreen(newScreen);
      +        if (d->context)
      +            d->context->setScreen(newScreen);
       #endif
      -        }
       
               if (d->useSoftwareRenderer
       #if QT_CONFIG(opengl)
      @@ -1545,7 +1551,7 @@ bool QQuickWidget::event(QEvent *e)
                   d->render(true);
               }
               break;
      -
      +    }
           case QEvent::Show:
           case QEvent::Move:
               d->updatePosition();
      diff --git a/qtdeclarative/src/quickwidgets/qquickwidget_p.h b/qtdeclarative/src/quickwidgets/qquickwidget_p.h
      index 881f7f9220..1a946bcc71 100644
      --- a/qtdeclarative/src/quickwidgets/qquickwidget_p.h
      +++ b/qtdeclarative/src/quickwidgets/qquickwidget_p.h
      @@ -148,6 +148,14 @@ public:
           bool forceFullUpdate;
       };
       
      +class QQuickWidgetOffscreenWindow: public QQuickWindow
      +{
      +    Q_OBJECT
      +
      +public:
      +    QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control);
      +};
      +
       QT_END_NAMESPACE
       
       #endif // QQuickWidget_P_H
      diff --git a/qtdeclarative/src/quickwidgets/quickwidgets.pro b/qtdeclarative/src/quickwidgets/quickwidgets.pro
      index 2438e577ae..85d156b8a3 100644
      --- a/qtdeclarative/src/quickwidgets/quickwidgets.pro
      +++ b/qtdeclarative/src/quickwidgets/quickwidgets.pro
      @@ -7,9 +7,13 @@ DEFINES   += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FO
       HEADERS += \
           qquickwidget.h \
           qquickwidget_p.h \
      -    qtquickwidgetsglobal.h
      +    qtquickwidgetsglobal.h \
      +    qaccessiblequickwidget_p.h \
      +    qaccessiblequickwidgetfactory_p.h
       
       SOURCES += \
      -    qquickwidget.cpp
      +    qquickwidget.cpp \
      +    qaccessiblequickwidget.cpp \
      +    qaccessiblequickwidgetfactory.cpp
       
       load(qt_module)
      diff --git a/qtdeclarative/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/qtdeclarative/tests/auto/qml/qjsengine/tst_qjsengine.cpp
      index 3b7d74df63..b75bf820d5 100644
      --- a/qtdeclarative/tests/auto/qml/qjsengine/tst_qjsengine.cpp
      +++ b/qtdeclarative/tests/auto/qml/qjsengine/tst_qjsengine.cpp
      @@ -102,6 +102,7 @@ private slots:
           void valueConversion_RegularExpression();
           void castWithMultipleInheritance();
           void collectGarbage();
      +    void collectGarbageNestedWrappersTwoEngines();
           void gcWithNestedDataStructure();
           void stacktrace();
           void numberParsing_data();
      @@ -1809,6 +1810,44 @@ void tst_QJSEngine::collectGarbage()
           QVERIFY(ptr.isNull());
       }
       
      +class TestObjectContainer : public QObject
      +{
      +    Q_OBJECT
      +    Q_PROPERTY(QObject *dummy MEMBER m_dummy CONSTANT)
      +
      +public:
      +    TestObjectContainer() : m_dummy(new QObject(this)) {}
      +
      +private:
      +    QObject *m_dummy;
      +};
      +
      +void tst_QJSEngine::collectGarbageNestedWrappersTwoEngines()
      +{
      +    QJSEngine engine1;
      +    QJSEngine engine2;
      +
      +    TestObjectContainer container;
      +    QQmlEngine::setObjectOwnership(&container, QQmlEngine::CppOwnership);
      +
      +    engine1.globalObject().setProperty("foobar", engine1.newQObject(&container));
      +    engine2.globalObject().setProperty("foobar", engine2.newQObject(&container));
      +
      +    engine1.evaluate("foobar.dummy.baz = 42");
      +    engine2.evaluate("foobar.dummy.baz = 43");
      +
      +    QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42);
      +    QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43);
      +
      +    engine1.collectGarbage();
      +    engine2.collectGarbage();
      +
      +    // The GC should not collect dummy object wrappers neither in engine1 nor engine2, we
      +    // verify that by checking whether the baz property still has its previous value.
      +    QCOMPARE(engine1.evaluate("foobar.dummy.baz").toInt(), 42);
      +    QCOMPARE(engine2.evaluate("foobar.dummy.baz").toInt(), 43);
      +}
      +
       void tst_QJSEngine::gcWithNestedDataStructure()
       {
           // The GC must be able to traverse deeply nested objects, otherwise this
      diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml
      new file mode 100644
      index 0000000000..206133bb39
      --- /dev/null
      +++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml
      @@ -0,0 +1,11 @@
      +import QtQuick 2.8
      +
      +ListView {
      +    id: root
      +    width: 200
      +    height: 200
      +
      +    delegate: Text {
      +        text: display
      +    }
      +}
      diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
      index 35f1e2c94d..1722447830 100644
      --- a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
      +++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
      @@ -27,6 +27,8 @@
       ****************************************************************************/
       
       #include 
      +#include 
      +#include 
       #include 
       #include 
       #include 
      @@ -47,6 +49,7 @@ private slots:
           void filterOnGroup_removeWhenCompleted();
           void qtbug_86017();
           void contextAccessedByHandler();
      +    void redrawUponColumnChange();
       };
       
       class AbstractItemModel : public QAbstractItemModel
      @@ -186,6 +189,30 @@ void tst_QQmlDelegateModel::contextAccessedByHandler()
           QVERIFY(root->property("works").toBool());
       }
       
      +void tst_QQmlDelegateModel::redrawUponColumnChange()
      +{
      +    QStandardItemModel m1;
      +    m1.appendRow({
      +            new QStandardItem("Banana"),
      +            new QStandardItem("Coconut"),
      +    });
      +
      +    QQuickView view(testFileUrl("redrawUponColumnChange.qml"));
      +    QCOMPARE(view.status(), QQuickView::Ready);
      +    view.show();
      +    QQuickItem *root = view.rootObject();
      +    root->setProperty("model", QVariant::fromValue(&m1));
      +
      +    QObject *item = root->property("currentItem").value();
      +    QVERIFY(item);
      +    QCOMPARE(item->property("text").toString(), "Banana");
      +
      +    QVERIFY(root);
      +    m1.removeColumn(0);
      +
      +    QCOMPARE(item->property("text").toString(), "Coconut");
      +}
      +
       QTEST_MAIN(tst_QQmlDelegateModel)
       
       #include "tst_qqmldelegatemodel.moc"
      diff --git a/qtdeclarative/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/qtdeclarative/tests/auto/qml/qv4mm/tst_qv4mm.cpp
      index 5d635aa63b..824fd89e5b 100644
      --- a/qtdeclarative/tests/auto/qml/qv4mm/tst_qv4mm.cpp
      +++ b/qtdeclarative/tests/auto/qml/qv4mm/tst_qv4mm.cpp
      @@ -76,10 +76,10 @@ void tst_qv4mm::multiWrappedQObjects()
               QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1);
               QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0);
       
      -        // Moves the additional WeakValue from m_multiplyWrappedQObjects to
      -        // m_pendingFreedObjectWrapperValue. It's still alive after all.
      +        // The additional WeakValue from m_multiplyWrappedQObjects hasn't been moved
      +        // to m_pendingFreedObjectWrapperValue yet. It's still alive after all.
               engine1.memoryManager->runGC();
      -        QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 2);
      +        QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1);
       
               // engine2 doesn't own the object as engine1 was the first to wrap it above.
               // Therefore, no effect here.
      diff --git a/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml b/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml
      new file mode 100644
      index 0000000000..889e480f3b
      --- /dev/null
      +++ b/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml
      @@ -0,0 +1,13 @@
      +import QtQuick 2.6
      +
      +Item {
      +    visible: true
      +    Item {
      +        visible: false
      +        Item {
      +            objectName: "hiddenChild"
      +            activeFocusOnTab: true
      +            focus: true
      +        }
      +    }
      +}
      diff --git a/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
      index c8f251dbe1..c8ef36ee68 100644
      --- a/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
      +++ b/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
      @@ -67,6 +67,7 @@ private slots:
           void activeFocusOnTab10();
           void activeFocusOnTab_infiniteLoop_data();
           void activeFocusOnTab_infiniteLoop();
      +    void activeFocusOnTab_infiniteLoopControls();
       
           void nextItemInFocusChain();
           void nextItemInFocusChain2();
      @@ -1057,6 +1058,17 @@ void tst_QQuickItem::activeFocusOnTab_infiniteLoop()
           QCOMPARE(item, window->rootObject());
       }
       
      +
      +void tst_QQuickItem::activeFocusOnTab_infiniteLoopControls()
      +{
      +    auto source = testFileUrl("activeFocusOnTab_infiniteLoop3.qml");
      +    QScopedPointerwindow(new QQuickView());
      +    window->setSource(source);
      +    window->show();
      +    QVERIFY(window->errors().isEmpty());
      +    QTest::keyClick(window.get(), Qt::Key_Tab); // should not hang
      +}
      +
       void tst_QQuickItem::nextItemInFocusChain()
       {
           if (!qt_tab_all_widgets())
      Submodule qtdoc c0c7bf07...79d6ef69:
      Submodule qtimageformats 2dcf2899..abe44c0f:
      diff --git a/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp b/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp
      index dde783c..1bf9074 100644
      --- a/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp
      +++ b/qtimageformats/src/plugins/imageformats/icns/qicnshandler.cpp
      @@ -462,8 +462,12 @@ static bool parseIconEntryInfo(ICNSEntry &icon)
           if (isIconCompressed(icon))
               return true;
           // Icon depth:
      -    if (!depth.isEmpty())
      -        icon.depth = ICNSEntry::Depth(depth.toUInt());
      +    if (!depth.isEmpty()) {
      +        const uint depthUInt = depth.toUInt();
      +        if (depthUInt > 32)
      +            return false;
      +        icon.depth = ICNSEntry::Depth(depthUInt);
      +    }
           // Try mono if depth not found
           if (icon.depth == ICNSEntry::DepthUnknown)
               icon.depth = ICNSEntry::DepthMono;
      @@ -515,6 +519,9 @@ static bool parseIconEntryInfo(ICNSEntry &icon)
               }
               icon.height = icon.width;
           }
      +    // Sanity check
      +    if (icon.width == 0 || icon.width > 4096)
      +        return false;
           return true;
       }
       
      @@ -685,7 +692,7 @@ bool QICNSHandler::canRead() const
       bool QICNSHandler::read(QImage *outImage)
       {
           QImage img;
      -    if (!ensureScanned()) {
      +    if (!ensureScanned() || m_currentIconIndex >= m_icons.size()) {
               qWarning("QICNSHandler::read(): The device wasn't parsed properly!");
               return false;
           }
      @@ -892,7 +899,7 @@ bool QICNSHandler::scanDevice()
                   return false;
       
               const qint64 blockDataOffset = device()->pos();
      -        if (!isBlockHeaderValid(blockHeader)) {
      +        if (!isBlockHeaderValid(blockHeader, ICNSBlockHeaderSize + filelength - blockDataOffset)) {
                   qWarning("QICNSHandler::scanDevice(): Failed, bad header at pos %s. OSType \"%s\", length %u",
                            QByteArray::number(blockDataOffset).constData(),
                            nameFromOSType(blockHeader.ostype).constData(), blockHeader.length);
      @@ -927,11 +934,14 @@ bool QICNSHandler::scanDevice()
               case ICNSBlockHeader::TypeOdrp:
                   // Icns container seems to have an embedded icon variant container
                   // Let's start a scan for entries
      -            while (device()->pos() < nextBlockOffset) {
      +            while (!stream.atEnd() && device()->pos() < nextBlockOffset) {
                       ICNSBlockHeader icon;
                       stream >> icon;
      +                if (stream.status() != QDataStream::Ok)
      +                    return false;
                       // Check for incorrect variant entry header and stop scan
      -                if (!isBlockHeaderValid(icon, blockDataLength))
      +                quint64 remaining = blockDataLength - (device()->pos() - blockDataOffset);
      +                if (!isBlockHeaderValid(icon, ICNSBlockHeaderSize + remaining))
                           break;
                       if (!addEntry(icon, device()->pos(), blockHeader.ostype))
                           return false;
      @@ -1003,7 +1013,7 @@ bool QICNSHandler::scanDevice()
                   break;
               }
           }
      -    return true;
      +    return (m_icons.size() > 0);
       }
       
       const ICNSEntry &QICNSHandler::getIconMask(const ICNSEntry &icon) const
      diff --git a/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp b/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp
      index 5082023..cb34374 100644
      --- a/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp
      +++ b/qtimageformats/src/plugins/imageformats/jp2/qjp2handler.cpp
      @@ -43,6 +43,7 @@
       #include "qimage.h"
       #include "qvariant.h"
       #include "qcolor.h"
      +#include "qimagereader.h"
       
       #include 
       #include  // for pow
      @@ -333,16 +334,46 @@ private:
       Jpeg2000JasperReader::Jpeg2000JasperReader(QIODevice *iod, SubFormat format)
           : jasperOk(true), ioDevice(iod), format(format), hasAlpha(false)
       {
      +#if JAS_VERSION_MAJOR < 3
           if (jas_init()) {
               jasperOk = false;
               qDebug("Jasper Library initialization failed");
           }
      +#else
      +    jas_conf_clear();
      +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
      +    jas_conf_set_max_mem_usage(QImageReader::allocationLimit() * 1024 * 1024);
      +#else
      +    // 128MB seems to be enough.
      +    jas_conf_set_max_mem_usage(128 * 1024 * 1024);
      +#endif
      +    if (jas_init_library()) {
      +        jasperOk = false;
      +        qDebug("Jasper library initialization failed");
      +    }
      +    if (jas_init_thread()) {
      +        jas_cleanup_library();
      +        jasperOk = false;
      +        qDebug("Jasper thread initialization failed");
      +    }
      +#endif
       }
       
       Jpeg2000JasperReader::~Jpeg2000JasperReader()
       {
      +#if JAS_VERSION_MAJOR < 3
           if (jasperOk)
               jas_cleanup();
      +#else
      +    if (jasperOk) {
      +        if (jas_cleanup_thread()) {
      +            qDebug("Jasper thread cleanup failed");
      +        }
      +        if (jas_cleanup_library()) {
      +            qDebug("Jasper library cleanup failed");
      +        }
      +    }
      +#endif
       }
       
       /*! \internal
      @@ -857,7 +888,7 @@ bool Jpeg2000JasperReader::write(const QImage &image, int quality)
           }
       
           // Open an empty jasper stream that grows automatically
      -    jas_stream_t * memory_stream = jas_stream_memopen(0, -1);
      +    jas_stream_t * memory_stream = jas_stream_memopen(0, 0);
       
           // Jasper wants a non-const string.
           char *str = qstrdup(jasperFormatString.toLatin1().constData());
      diff --git a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp
      index 1386750..ac8956c 100644
      --- a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp
      +++ b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp
      @@ -361,6 +361,8 @@ bool QTiffHandler::read(QImage *image)
           }
       
           TIFF *const tiff = d->tiff;
      +    if (TIFFIsTiled(tiff) && TIFFTileSize64(tiff) > uint64_t(image->sizeInBytes())) // Corrupt image
      +        return false;
           const quint32 width = d->size.width();
           const quint32 height = d->size.height();
       
      diff --git a/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp b/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp
      index 82d38cb..d02eb05 100644
      --- a/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp
      +++ b/qtimageformats/src/plugins/imageformats/webp/qwebphandler.cpp
      @@ -45,6 +45,7 @@
       #include 
       #include 
       #include 
      +#include 
       
       static const int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_constants.h
       
      @@ -102,21 +103,23 @@ bool QWebpHandler::ensureScanned() const
       
           m_scanState = ScanError;
       
      -    if (device()->isSequential()) {
      -        qWarning() << "Sequential devices are not supported";
      +    QWebpHandler *that = const_cast(this);
      +    const int headerBytesNeeded = sizeof(WebPBitstreamFeatures);
      +    QByteArray header = device()->peek(headerBytesNeeded);
      +    if (header.size() < headerBytesNeeded)
               return false;
      -    }
       
      -    qint64 oldPos = device()->pos();
      -    device()->seek(0);
      -
      -    QWebpHandler *that = const_cast(this);
      -    QByteArray header = device()->peek(sizeof(WebPBitstreamFeatures));
      +    // We do no random access during decoding, just a readAll() of the whole image file. So if
      +    // if it is all available already, we can accept a sequential device. The riff header contains
      +    // the file size minus 8 bytes header
      +    qint64 byteSize = qFromLittleEndian(header.constData() + 4);
      +    if (device()->isSequential() && device()->bytesAvailable() < byteSize + 8) {
      +        qWarning() << "QWebpHandler: Insufficient data available in sequential device";
      +        return false;
      +    }
           if (WebPGetFeatures((const uint8_t*)header.constData(), header.size(), &(that->m_features)) == VP8_STATUS_OK) {
               if (m_features.has_animation) {
                   // For animation, we have to read and scan whole file to determine loop count and images count
      -            device()->seek(oldPos);
      -
                   if (that->ensureDemuxer()) {
                       that->m_loop = WebPDemuxGetI(m_demuxer, WEBP_FF_LOOP_COUNT);
                       that->m_frameCount = WebPDemuxGetI(m_demuxer, WEBP_FF_FRAME_COUNT);
      @@ -126,17 +129,13 @@ bool QWebpHandler::ensureScanned() const
                       if (that->m_features.has_alpha)
                           that->m_composited->fill(Qt::transparent);
       
      -                // We do not reset device position since we have read in all data
                       m_scanState = ScanSuccess;
      -                return true;
                   }
               } else {
                   m_scanState = ScanSuccess;
               }
           }
       
      -    device()->seek(oldPos);
      -
           return m_scanState == ScanSuccess;
       }
       
      @@ -159,7 +158,7 @@ bool QWebpHandler::ensureDemuxer()
       
       bool QWebpHandler::read(QImage *image)
       {
      -    if (!ensureScanned() || device()->isSequential() || !ensureDemuxer())
      +    if (!ensureScanned() || !ensureDemuxer())
               return false;
       
           QRect prevFrameRect;
      Submodule qtlocation 98a5c511..f991e28c:
      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/esri/geocodingmanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp
      index 5a40467e..d123c6a8 100644
      --- a/qtlocation/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp
      +++ b/qtlocation/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp
      @@ -59,8 +59,10 @@ QT_BEGIN_NAMESPACE
       static const QString kPrefixEsri(QStringLiteral("esri."));
       static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent"));
       
      -static const QString kUrlGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates"));
      -static const QString kUrlReverseGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode"));
      +static const QString kUrlGeocode(QStringLiteral("https://geocode.arcgis.com/arcgis/rest/services/"
      +                                                "World/GeocodeServer/findAddressCandidates"));
      +static const QString kUrlReverseGeocode(QStringLiteral(
      +        "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode"));
       
       static QString addressToQuery(const QGeoAddress &address)
       {
      diff --git a/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp b/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp
      index 17492d94..96c12b1b 100644
      --- a/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp
      +++ b/qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp
      @@ -46,7 +46,8 @@
       
       QT_BEGIN_NAMESPACE
       
      -// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/
      +// JSON reference:
      +// https://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/
       
       static const QString kErrorMessage(QStringLiteral("Error %1: %2."));
       static const QString kErrorJson(QStringLiteral("Error: invalide JSON document."));
      diff --git a/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp b/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp
      index 8cadfb29..92b6bb13 100644
      --- a/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp
      +++ b/qtlocation/src/plugins/geoservices/esri/georoutereply_esri.cpp
      @@ -44,7 +44,8 @@
       
       QT_BEGIN_NAMESPACE
       
      -// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/
      +// JSON reference:
      +// https://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/
       
       GeoRouteReplyEsri::GeoRouteReplyEsri(QNetworkReply *reply, const QGeoRouteRequest &request,
                                            QObject *parent) :
      diff --git a/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp
      index 0e6bc2c7..ed613f4b 100644
      --- a/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp
      +++ b/qtlocation/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp
      @@ -48,7 +48,8 @@ static const QString kPrefixEsri(QStringLiteral("esri."));
       static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent"));
       static const QString kParamToken(kPrefixEsri + QStringLiteral("token"));
       
      -static const QString kUrlRouting(QStringLiteral("http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World/solve"));
      +static const QString kUrlRouting(QStringLiteral(
      +        "https://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World/solve"));
       
       GeoRoutingManagerEngineEsri::GeoRoutingManagerEngineEsri(const QVariantMap ¶meters,
                                                                QGeoServiceProvider::Error *error,
      @@ -70,7 +71,8 @@ GeoRoutingManagerEngineEsri::~GeoRoutingManagerEngineEsri()
       {
       }
       
      -// REST reference: http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000
      +// REST reference:
      +// https://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000
       
       QGeoRouteReply *GeoRoutingManagerEngineEsri::calculateRoute(const QGeoRouteRequest &request)
       {
      @@ -125,7 +127,7 @@ void GeoRoutingManagerEngineEsri::replyError(QGeoRouteReply::Error errorCode, co
       QString GeoRoutingManagerEngineEsri::preferedDirectionLangage()
       {
           // list of supported langages is defined in:
      -    // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000
      +    // https://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000
           const QStringList supportedLanguages = {
               "ar", // Generate directions in Arabic
               "cs", // Generate directions in Czech
      diff --git a/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp
      index 5d15835d..24148f95 100644
      --- a/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp
      +++ b/qtlocation/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp
      @@ -230,7 +230,8 @@ QGeoMap *GeoTiledMappingManagerEngineEsri::createMap()
       // ${y} = Y
       // ${token} = Token
       
      -// template = 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{{z}}/{{y}}/{{x}}.png'
      +// template =
      +// 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{{z}}/{{y}}/{{x}}.png'
       
       bool GeoTiledMappingManagerEngineEsri::initializeMapSources(QGeoServiceProvider::Error *error,
                                                                   QString *errorString,
      diff --git a/qtlocation/src/plugins/geoservices/esri/maps.json b/qtlocation/src/plugins/geoservices/esri/maps.json
      index 8167ae7d..862e087d 100644
      --- a/qtlocation/src/plugins/geoservices/esri/maps.json
      +++ b/qtlocation/src/plugins/geoservices/esri/maps.json
      @@ -6,8 +6,8 @@
                   "description": "ArcGIS Online World Street Map",
                   "mobile": true,
                   "night": false,
      -            "url": "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -16,8 +16,8 @@
                   "": "ArcGIS Online World Imagery",
                   "mobile": true,
                   "night": false,
      -            "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -26,8 +26,8 @@
                   "description": "ArcGIS Online World Terrain Base",
                   "mobile": false,
                   "night": false,
      -            "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -36,8 +36,8 @@
                   "description": "ArcGIS Online World Topography",
                   "mobile": true,
                   "night": false,
      -            "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -46,8 +46,8 @@
                   "description": "This map presents land cover and detailed topographic maps for the United States.",
                   "mobile": true,
                   "night": false,
      -            "url": "http://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -56,8 +56,8 @@
                   "description": "National Geographic World Map",
                   "mobile": false,
                   "night": false,
      -            "url": "http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -66,8 +66,8 @@
                   "description": "Thematic content providing a neutral background with minimal colors",
                   "mobile": true,
                   "night": false,
      -            "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -76,8 +76,8 @@
                   "description": "Natural Earth physical map for the world",
                   "mobile": false,
                   "night": false,
      -            "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -86,8 +86,8 @@
                   "description": "Portrays surface elevation as shaded relief",
                   "mobile": false,
                   "night": false,
      -            "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -96,8 +96,8 @@
                   "description": "This map is designed to be used as a basemap by marine GIS professionals and as a reference map by anyone interested in ocean data",
                   "mobile": false,
                   "night": false,
      -            "url": "http://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -106,8 +106,8 @@
                   "description": "Thematic content providing a neutral background with minimal colors",
                   "mobile": false,
                   "night": true,
      -            "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer",
      +            "copyrightText": "© Esri contributors"
               },
       
               {
      @@ -116,8 +116,8 @@
                   "description": "DeLorme’s topographic basemap is a seamless global data set that portrays transportation, hydrography, jurisdiction boundaries, and major geographic features",
                   "mobile": false,
                   "night": false,
      -            "url": "http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer",
      -            "copyrightText": "© Esri contributors"
      +            "url": "https://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer",
      +            "copyrightText": "© Esri contributors"
               }
           ]
       }
      diff --git a/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp b/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp
      index 3858ddf5..af66f28e 100644
      --- a/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp
      +++ b/qtlocation/src/plugins/geoservices/esri/placemanagerengine_esri.cpp
      @@ -63,8 +63,10 @@ static const QString kCountriesKey(QStringLiteral("detailedCountries"));
       static const QString kLocalizedNamesKey(QStringLiteral("localizedNames"));
       static const QString kMaxLocationsKey(QStringLiteral("maxLocations"));
       
      -static const QUrl kUrlGeocodeServer("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer?f=pjson");
      -static const QUrl kUrlFindAddressCandidates("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates");
      +static const QUrl kUrlGeocodeServer(
      +        "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer?f=pjson");
      +static const QUrl kUrlFindAddressCandidates("https://geocode.arcgis.com/arcgis/rest/services/World/"
      +                                            "GeocodeServer/findAddressCandidates");
       
       PlaceManagerEngineEsri::PlaceManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error,
                                                      QString *errorString) :
      Submodule qtmultimedia f0344079..0d7cc33a:
      diff --git a/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp
      index 2b5325132..b68b4af1b 100644
      --- a/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp
      +++ b/qtmultimedia/src/plugins/pulseaudio/qaudioinput_pulse.cpp
      @@ -402,6 +402,8 @@ int QPulseAudioInput::bytesReady() const
       
       qint64 QPulseAudioInput::read(char *data, qint64 len)
       {
      +    Q_ASSERT(data != nullptr || len == 0);
      +
           m_bytesAvailable = checkBytesReady();
       
           setError(QAudio::NoError);
      @@ -411,7 +413,8 @@ qint64 QPulseAudioInput::read(char *data, qint64 len)
       
           if (!m_pullMode && !m_tempBuffer.isEmpty()) {
               readBytes = qMin(static_cast(len), m_tempBuffer.size());
      -        memcpy(data, m_tempBuffer.constData(), readBytes);
      +        if (readBytes)
      +            memcpy(data, m_tempBuffer.constData(), readBytes);
               m_totalTimeValue += readBytes;
       
               if (readBytes < m_tempBuffer.size()) {
      @@ -502,9 +505,10 @@ qint64 QPulseAudioInput::read(char *data, qint64 len)
       
       void QPulseAudioInput::applyVolume(const void *src, void *dest, int len)
       {
      +    Q_ASSERT((src && dest) || len == 0);
           if (m_volume < 1.f)
               QAudioHelperInternal::qMultiplySamples(m_volume, m_format, src, dest, len);
      -    else
      +    else if (len)
               memcpy(dest, src, len);
       }
       
      Submodule qtpim 02efef5e...f9a8f0fc (commits not present)
      Submodule qtquick3d b85cf622..47defc8b:
      diff --git a/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro b/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro
      index ca5c499e..174a075b 100644
      --- a/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro
      +++ b/qtquick3d/src/plugins/assetimporters/assimp/assimp.pro
      @@ -10,7 +10,7 @@ QT_FOR_CONFIG += assetimporters-private
       include($$OUT_PWD/../qtassetimporters-config.pri)
       
       qtConfig(system-assimp):!if(cross_compile:host_build) {
      -    QMAKE_USE_PRIVATE += assimp
      +    QMAKE_USE_PRIVATE += quick3d-assimp
       } else {
           include(../../../3rdparty/assimp/assimp.pri)
       }
      Submodule qtquickcontrols2 3a657dc0..56ce8233:
      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 950e4e099..b81fbea0b 100644
      --- a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp
      +++ b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp
      @@ -387,6 +387,17 @@ void QQuickAbstractButtonPrivate::itemImplicitHeightChanged(QQuickItem *item)
               emit q->implicitIndicatorHeightChanged();
       }
       
      +void QQuickAbstractButtonPrivate::itemDestroyed(QQuickItem *item)
      +{
      +    Q_Q(QQuickAbstractButton);
      +    QQuickControlPrivate::itemDestroyed(item);
      +    if (item == indicator) {
      +        indicator = nullptr;
      +        emit q->implicitIndicatorWidthChanged();
      +        emit q->implicitIndicatorHeightChanged();
      +    }
      +}
      +
       QQuickAbstractButton *QQuickAbstractButtonPrivate::findCheckedButton() const
       {
           Q_Q(const QQuickAbstractButton);
      @@ -1176,6 +1187,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/qquickabstractbutton_p_p.h b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p_p.h
      index 9291c1a87..b729720f2 100644
      --- a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p_p.h
      +++ b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p_p.h
      @@ -109,6 +109,7 @@ public:
       
           void itemImplicitWidthChanged(QQuickItem *item) override;
           void itemImplicitHeightChanged(QQuickItem *item) override;
      +    void itemDestroyed(QQuickItem *item) override;
       
           // copied from qabstractbutton.cpp
           static const int AUTO_REPEAT_DELAY = 300;
      diff --git a/qtquickcontrols2/src/quicktemplates2/qquickaction.cpp b/qtquickcontrols2/src/quicktemplates2/qquickaction.cpp
      index 2041e7741..8610cdfae 100644
      --- a/qtquickcontrols2/src/quicktemplates2/qquickaction.cpp
      +++ b/qtquickcontrols2/src/quicktemplates2/qquickaction.cpp
      @@ -145,7 +145,7 @@ int QQuickActionPrivate::ShortcutEntry::shortcutId() const
       
       void QQuickActionPrivate::ShortcutEntry::grab(const QKeySequence &shortcut, bool enabled)
       {
      -    if (shortcut.isEmpty())
      +    if (shortcut.isEmpty() || m_shortcutId)
               return;
       
           Qt::ShortcutContext context = Qt::WindowShortcut; // TODO
      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 bbbd0e622..1f4b47343 100644
      --- a/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp
      +++ b/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp
      @@ -845,6 +845,13 @@ void QQuickControlPrivate::executeBackground(bool complete)
               quickCompleteDeferred(q, backgroundName(), background);
       }
       
      +/*
      +    \internal
      +
      +    Hides an item that was replaced by a newer one, rather than
      +    deleting it, as the item is typically created in QML and hence
      +    we don't own it.
      +*/
       void QQuickControlPrivate::hideOldItem(QQuickItem *item)
       {
           if (!item)
      @@ -863,6 +870,29 @@ void QQuickControlPrivate::hideOldItem(QQuickItem *item)
       #endif
       }
       
      +/*
      +    \internal
      +
      +    Named "unhide" because it's used for cases where an item
      +    that was previously hidden by \l hideOldItem() wants to be
      +    shown by a control again, such as a ScrollBar in ScrollView.
      +*/
      +void QQuickControlPrivate::unhideOldItem(QQuickControl *control, QQuickItem *item)
      +{
      +    Q_ASSERT(item);
      +    qCDebug(lcItemManagement) << "unhiding old item" << item;
      +
      +    item->setVisible(true);
      +    item->setParentItem(control);
      +
      +#if QT_CONFIG(accessibility)
      +    // Add the item back in to the accessibility tree.
      +    QQuickAccessibleAttached *accessible = accessibleAttached(item);
      +    if (accessible)
      +        accessible->setIgnored(false);
      +#endif
      +}
      +
       void QQuickControlPrivate::updateBaselineOffset()
       {
           Q_Q(QQuickControl);
      diff --git a/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h b/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h
      index 8e979079e..a6e624c91 100644
      --- a/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h
      +++ b/qtquickcontrols2/src/quicktemplates2/qquickcontrol_p_p.h
      @@ -173,6 +173,7 @@ public:
           virtual void executeBackground(bool complete = false);
       
           static void hideOldItem(QQuickItem *item);
      +    static void unhideOldItem(QQuickControl *control, QQuickItem *item);
       
           void updateBaselineOffset();
       
      diff --git a/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp b/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp
      index e6db14eb5..6197d1547 100644
      --- a/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp
      +++ b/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp
      @@ -237,7 +237,7 @@ static QRectF alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment
       void QQuickDialogButtonBoxPrivate::resizeContent()
       {
           Q_Q(QQuickDialogButtonBox);
      -    if (!contentItem)
      +    if (!contentItem || !contentModel)
               return;
       
           QRectF geometry = q->boundingRect().adjusted(q->leftPadding(), q->topPadding(), -q->rightPadding(), -q->bottomPadding());
      @@ -322,6 +322,9 @@ void QQuickDialogButtonBoxPrivate::updateLayout()
       qreal QQuickDialogButtonBoxPrivate::getContentWidth() const
       {
           Q_Q(const QQuickDialogButtonBox);
      +    if (!contentModel)
      +        return 0;
      +
           const int count = contentModel->count();
           const qreal totalSpacing = qMax(0, count - 1) * spacing;
           qreal totalWidth = totalSpacing;
      @@ -341,6 +344,9 @@ qreal QQuickDialogButtonBoxPrivate::getContentWidth() const
       qreal QQuickDialogButtonBoxPrivate::getContentHeight() const
       {
           Q_Q(const QQuickDialogButtonBox);
      +    if (!contentModel)
      +        return 0;
      +
           const int count = contentModel->count();
           qreal maxHeight = 0;
           for (int i = 0; i < count; ++i) {
      diff --git a/qtquickcontrols2/src/quicktemplates2/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/qquickscrollbar.cpp b/qtquickcontrols2/src/quicktemplates2/qquickscrollbar.cpp
      index 4e2f509db..1c4b308cd 100644
      --- a/qtquickcontrols2/src/quicktemplates2/qquickscrollbar.cpp
      +++ b/qtquickcontrols2/src/quicktemplates2/qquickscrollbar.cpp
      @@ -797,6 +797,14 @@ void QQuickScrollBarAttachedPrivate::initHorizontal()
           if (parent && parent == flickable->parentItem())
               horizontal->stackAfter(flickable);
       
      +    // If a scroll bar was previously hidden (due to e.g. setting a new contentItem
      +    // on a ScrollView), we need to make sure that we un-hide it.
      +    // We don't bother checking if the item is actually the old one, because
      +    // if it's not, all of the things the function does (setting parent, visibility, etc.)
      +    // should be no-ops anyway.
      +    if (auto control = qobject_cast(q_ptr->parent()))
      +        QQuickControlPrivate::unhideOldItem(control, horizontal);
      +
           layoutHorizontal();
           horizontal->setSize(area->property("widthRatio").toReal());
           horizontal->setPosition(area->property("xPosition").toReal());
      @@ -818,6 +826,9 @@ void QQuickScrollBarAttachedPrivate::initVertical()
           if (parent && parent == flickable->parentItem())
               vertical->stackAfter(flickable);
       
      +    if (auto control = qobject_cast(q_ptr->parent()))
      +        QQuickControlPrivate::unhideOldItem(control, vertical);
      +
           layoutVertical();
           vertical->setSize(area->property("heightRatio").toReal());
           vertical->setPosition(area->property("yPosition").toReal());
      diff --git a/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml b/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml
      index 0e8b08352..cd4931184 100644
      --- a/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml
      +++ b/qtquickcontrols2/tests/auto/controls/data/tst_scrollview.qml
      @@ -576,4 +576,51 @@ TestCase {
               verify(newHorizontalScrollBar.visible)
               verify(!oldHorizontalScrollBar.visible)
           }
      +
      +    Component {
      +        id: bindingToContentItemAndStandaloneFlickable
      +
      +        Item {
      +            width: 200
      +            height: 200
      +
      +            property alias scrollView: scrollView
      +
      +            ScrollView {
      +                id: scrollView
      +                anchors.fill: parent
      +                contentItem: listView
      +
      +                property Item someBinding: contentItem
      +            }
      +            ListView {
      +                id: listView
      +                model: 10
      +                delegate: ItemDelegate {
      +                    text: modelData
      +                    width: listView.width
      +                }
      +            }
      +        }
      +    }
      +
      +    // Tests that scroll bars show up for a ScrollView where
      +    // - its contentItem is declared as a standalone, separate item
      +    // - there is a binding to contentItem (which causes a default Flickable to be created)
      +    function test_bindingToContentItemAndStandaloneFlickable() {
      +        let root = createTemporaryObject(bindingToContentItemAndStandaloneFlickable, testCase)
      +        verify(root)
      +
      +        let control = root.scrollView
      +        let verticalScrollBar = control.ScrollBar.vertical
      +        let horizontalScrollBar = control.ScrollBar.horizontal
      +        compare(verticalScrollBar.parent, control)
      +        compare(horizontalScrollBar.parent, control)
      +        verify(verticalScrollBar.visible)
      +        verify(horizontalScrollBar.visible)
      +
      +        mouseDrag(verticalScrollBar, verticalScrollBar.width / 2, verticalScrollBar.height / 2, 0, 50)
      +        verify(verticalScrollBar.active)
      +        verify(horizontalScrollBar.active)
      +    }
       }
      diff --git a/qtquickcontrols2/tests/auto/controls/data/tst_switch.qml b/qtquickcontrols2/tests/auto/controls/data/tst_switch.qml
      index b3fab41ca..10b6baa02 100644
      --- a/qtquickcontrols2/tests/auto/controls/data/tst_switch.qml
      +++ b/qtquickcontrols2/tests/auto/controls/data/tst_switch.qml
      @@ -608,4 +608,27 @@ TestCase {
               mouseClick(control.indicator)
               verify(control.activeFocus)
           }
      +
      +    Component {
      +        id: deletionOrder1
      +        Item {
      +            Image { id: innerImage }
      +            Switch { indicator: innerImage }
      +        }
      +    }
      +
      +    Component {
      +        id: deletionOrder2
      +        Item {
      +            Switch { indicator: innerImage }
      +            Image { id: innerImage }
      +        }
      +    }
      +
      +    function test_deletionOrder() {
      +        var control1 = createTemporaryObject(deletionOrder1, testCase)
      +        verify(control1)
      +        var control2 = createTemporaryObject(deletionOrder2, testCase)
      +        verify(control2)
      +    }
       }
      diff --git a/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml b/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml
      new file mode 100644
      index 000000000..9e4598b9f
      --- /dev/null
      +++ b/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml
      @@ -0,0 +1,78 @@
      +/****************************************************************************
      +**
      +** Copyright (C) 2021 The Qt Company Ltd.
      +** Contact: https://www.qt.io/licensing/
      +**
      +** This file is part of the test suite of the Qt Toolkit.
      +**
      +** $QT_BEGIN_LICENSE:BSD$
      +** Commercial License Usage
      +** Licensees holding valid commercial Qt licenses may use this file in
      +** accordance with the commercial license agreement provided with the
      +** Software or, alternatively, in accordance with the terms contained in
      +** a written agreement between you and The Qt Company. For licensing terms
      +** and conditions see https://www.qt.io/terms-conditions. For further
      +** information use the contact form at https://www.qt.io/contact-us.
      +**
      +** BSD License Usage
      +** Alternatively, you may use this file under the terms of the BSD license
      +** as follows:
      +**
      +** "Redistribution and use in source and binary forms, with or without
      +** modification, are permitted provided that the following conditions are
      +** met:
      +**   * Redistributions of source code must retain the above copyright
      +**     notice, this list of conditions and the following disclaimer.
      +**   * Redistributions in binary form must reproduce the above copyright
      +**     notice, this list of conditions and the following disclaimer in
      +**     the documentation and/or other materials provided with the
      +**     distribution.
      +**   * Neither the name of The Qt Company Ltd nor the names of its
      +**     contributors may be used to endorse or promote products derived
      +**     from this software without specific prior written permission.
      +**
      +**
      +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      +**
      +** $QT_END_LICENSE$
      +**
      +****************************************************************************/
      +
      +import QtQuick 2.15
      +import QtQuick.Controls 2.15
      +
      +ApplicationWindow {
      +    id: window
      +    width: 400
      +    height: 400
      +    title: "releaseAfterExitTransition"
      +
      +    property alias popup: popup
      +    property alias modalPopup: modalPopup
      +
      +    Popup {
      +        id: popup
      +        y: parent.height - height
      +        width: 50
      +        height: 50
      +    }
      +
      +    Popup {
      +        id: modalPopup
      +        modal: true
      +        y: parent.height - height
      +        width: 50
      +        height: 50
      +        exit:  Transition { PauseAnimation { duration: 100 } }
      +    }
      +}
      diff --git a/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp b/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp
      index 54952d128..3d50e2dd4 100644
      --- a/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp
      +++ b/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp
      @@ -100,6 +100,7 @@ private slots:
           void invisibleToolTipOpen();
           void centerInOverlayWithinStackViewItem();
           void destroyDuringExitTransition();
      +    void releaseAfterExitTransition();
       };
       
       void tst_QQuickPopup::initTestCase()
      @@ -1575,6 +1576,34 @@ void tst_QQuickPopup::destroyDuringExitTransition()
           QVERIFY(!button->isDown());
       }
       
      +void tst_QQuickPopup::releaseAfterExitTransition()
      +{
      +    QQuickApplicationHelper helper(this, "releaseAfterExitTransition.qml");
      +    QVERIFY2(helper.ready, helper.failureMessage());
      +
      +    QQuickWindow *window = helper.window;
      +    window->show();
      +    QVERIFY(QTest::qWaitForWindowActive(window));
      +
      +    QQuickOverlay *overlay = QQuickOverlay::overlay(window);
      +    QQuickPopup *modalPopup = window->property("modalPopup").value();
      +    QQuickPopup *popup = window->property("popup").value();
      +
      +    modalPopup->open();
      +    QTRY_VERIFY(modalPopup->isOpened());
      +
      +    QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
      +    // wait until the transition is finished and the overlay hides itself
      +    QTRY_VERIFY(!overlay->isVisible());
      +    QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
      +
      +    popup->open();
      +    QTRY_VERIFY(popup->isOpened());
      +    QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
      +    QTRY_VERIFY(!popup->isOpened());
      +}
      +
      +
       QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup)
       
       #include "tst_qquickpopup.moc"
      Submodule qtscript 5be95f96...4d8e4bd2 (commits not present)
      Submodule qtspeech 17fc9fde..255845e2:
      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 0c05780e..a7a0f249:
      diff --git a/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp b/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp
      index 561e77e..12e0574 100644
      --- a/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp
      +++ b/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp
      @@ -191,6 +191,8 @@ bool QSvgIOHandler::read(QImage *image)
                   }
               }
               if (!finalSize.isEmpty()) {
      +            if (qMax(finalSize.width(), finalSize.height()) > 0xffff)
      +                return false; // Assume corrupted file
                   image->fill(d->backColor.rgba());
                   QPainter p(image);
                   d->r.render(&p, bounds);
      diff --git a/qtsvg/src/svg/qsvghandler.cpp b/qtsvg/src/svg/qsvghandler.cpp
      index a5f877f..8dda563 100644
      --- a/qtsvg/src/svg/qsvghandler.cpp
      +++ b/qtsvg/src/svg/qsvghandler.cpp
      @@ -1393,9 +1393,10 @@ static void parseFont(QSvgNode *node,
               case FontSizeNone:
                   break;
               case FontSizeValue: {
      -            QSvgHandler::LengthType dummy; // should always be pixel size
      -            fontStyle->setSize(qMin(parseLength(attributes.fontSize, dummy, handler),
      -                                    qreal(0xffff)));
      +            QSvgHandler::LengthType type;
      +            qreal fs = parseLength(attributes.fontSize, type, handler);
      +            fs = convertToPixels(fs, true, type);
      +            fontStyle->setSize(qMin(fs, qreal(0xffff)));
               }
                   break;
               default:
      @@ -2513,6 +2514,8 @@ static bool parseAnimateTransformNode(QSvgNode *parent,
                   ++s;
               }
           }
      +    if (vals.count() % 3 != 0)
      +        return false;
       
           bool ok = true;
           int begin = parseClockValue(beginStr, &ok);
      @@ -2576,6 +2579,8 @@ static QSvgNode *createCircleNode(QSvgNode *parent,
           qreal ncx = toDouble(cx);
           qreal ncy = toDouble(cy);
           qreal nr  = toDouble(r);
      +    if (nr < 0.0)
      +        return nullptr;
       
           QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
           QSvgNode *circle = new QSvgCircle(parent, rect);
      @@ -3046,15 +3051,16 @@ static QSvgStyleProperty *createRadialGradientNode(QSvgNode *node,
       
           qreal ncx = 0.5;
           qreal ncy = 0.5;
      -    qreal nr  = 0.5;
           if (!cx.isEmpty())
               ncx = toDouble(cx);
           if (!cy.isEmpty())
               ncy = toDouble(cy);
      +
      +    qreal nr = 0.0;
           if (!r.isEmpty())
               nr = toDouble(r);
      -    if (nr < 0.5)
      -        nr = 0.5;
      +    if (nr <= 0.0)
      +        return nullptr;
       
           qreal nfx = ncx;
           if (!fx.isEmpty())
      @@ -3350,7 +3356,9 @@ static QSvgNode *createTextNode(QSvgNode *parent,
           //### editable and rotate not handled
           QSvgHandler::LengthType type;
           qreal nx = parseLength(x, type, handler);
      +    nx = convertToPixels(nx, true, type);
           qreal ny = parseLength(y, type, handler);
      +    ny = convertToPixels(ny, true, type);
       
           QSvgNode *text = new QSvgText(parent, QPointF(nx, ny));
           return text;
      @@ -3691,9 +3699,7 @@ void QSvgHandler::parse()
               case QXmlStreamReader::EndElement:
                   endElement(xml->name());
                   ++remainingUnfinishedElements;
      -            // if we are using somebody else's qxmlstreamreader
      -            // we should not read until the end of the stream
      -            done = !m_ownsReader && (xml->name() == QLatin1String("svg"));
      +            done = (xml->name() == QLatin1String("svg"));
                   break;
               case QXmlStreamReader::Characters:
                   characters(xml->text());
      diff --git a/qtsvg/src/svg/qsvgstructure.cpp b/qtsvg/src/svg/qsvgstructure.cpp
      index b89608b..89c9e4e 100644
      --- a/qtsvg/src/svg/qsvgstructure.cpp
      +++ b/qtsvg/src/svg/qsvgstructure.cpp
      @@ -255,9 +255,13 @@ inline static bool isSupportedSvgFeature(const QString &str)
           };
       
           if (str.length() <= MAX_WORD_LENGTH && str.length() >= MIN_WORD_LENGTH) {
      +        const char16_t unicode44 = str.at(44).unicode();
      +        const char16_t unicode45 = str.at(45).unicode();
      +        if (unicode44 >= sizeof(asso_values) || unicode45 >= sizeof(asso_values))
      +            return false;
               const int key = str.length()
      -                        + asso_values[str.at(45).unicode()]
      -                        + asso_values[str.at(44).unicode()];
      +                        + asso_values[unicode45]
      +                        + asso_values[unicode44];
               if (key <= MAX_HASH_VALUE && key >= 0)
                   return str == QLatin1String(wordlist[key]);
           }
      diff --git a/qtsvg/src/svg/qsvgtinydocument.cpp b/qtsvg/src/svg/qsvgtinydocument.cpp
      index 63d0797..19e7154 100644
      --- a/qtsvg/src/svg/qsvgtinydocument.cpp
      +++ b/qtsvg/src/svg/qsvgtinydocument.cpp
      @@ -433,8 +433,16 @@ void QSvgTinyDocument::draw(QPainter *p, QSvgExtraStates &)
           draw(p);
       }
       
      +static bool isValidMatrix(const QTransform &transform)
      +{
      +    qreal determinant = transform.determinant();
      +    return qIsFinite(determinant);
      +}
      +
       void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect)
       {
      +    QTransform oldTransform = p->worldTransform();
      +
           QRectF target = targetRect;
           if (target.isEmpty()) {
               QPaintDevice *dev = p->device();
      @@ -487,6 +495,9 @@ void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect,
               }
       #endif
           }
      +
      +    if (!isValidMatrix(p->worldTransform()))
      +        p->setWorldTransform(oldTransform);
       }
       
       QRectF QSvgTinyDocument::boundsOnElement(const QString &id) const
      diff --git a/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
      index 36c76ec..db71e02 100644
      --- a/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
      +++ b/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
      @@ -86,6 +86,8 @@ private slots:
           void oss_fuzz_23731();
           void oss_fuzz_24131();
           void oss_fuzz_24738();
      +    void illegalAnimateTransform_data();
      +    void illegalAnimateTransform();
       
       #ifndef QT_NO_COMPRESS
           void testGzLoading();
      @@ -1646,5 +1648,22 @@ void tst_QSvgRenderer::oss_fuzz_24738()
           QSvgRenderer().load(QByteArray(""));
       }
       
      +void tst_QSvgRenderer::illegalAnimateTransform_data()
      +{
      +    QTest::addColumn("svg");
      +
      +    QTest::newRow("case1") << QByteArray("");
      +    QTest::newRow("case2") << QByteArray("");
      +    QTest::newRow("case3") << QByteArray("");
      +    QTest::newRow("case4") << QByteArray("");
      +}
      +
      +void tst_QSvgRenderer::illegalAnimateTransform()
      +{
      +    QFETCH(QByteArray, svg);
      +    QSvgRenderer renderer;
      +    QVERIFY(!renderer.load(svg)); // also shouldn't assert
      +}
      +
       QTEST_MAIN(tst_QSvgRenderer)
       #include "tst_qsvgrenderer.moc"
      Submodule qttools 6914f7c8..090e526e:
      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);
      Submodule qtwayland dfb0129c..2904e1b3:
      diff --git a/qtwayland/src/client/configure.json b/qtwayland/src/client/configure.json
      index 2f424580..29222357 100644
      --- a/qtwayland/src/client/configure.json
      +++ b/qtwayland/src/client/configure.json
      @@ -149,8 +149,7 @@
                           "#endif"
                       ]
                   },
      -            "libs": "-ldrm",
      -            "use": "egl"
      +            "use": "drm egl"
               },
               "vulkan-server-buffer": {
                   "label": "Vulkan Buffer Sharing",
      @@ -168,7 +167,8 @@
                           "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;",
                           "return 0;"
                       ]
      -            }
      +            },
      +            "use": "wayland-client"
               },
               "egl_1_5-wayland": {
                   "label": "EGL 1.5 with Wayland Platform",
      @@ -183,7 +183,7 @@
                           "eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_EXT, (struct wl_display *)(nullptr), nullptr);"
                       ]
                   },
      -            "use": "egl"
      +            "use": "egl wayland-client"
               }
           },
       
      diff --git a/qtwayland/src/client/global/qwaylandclientextension.cpp b/qtwayland/src/client/global/qwaylandclientextension.cpp
      index 125b1e19..edccfe63 100644
      --- a/qtwayland/src/client/global/qwaylandclientextension.cpp
      +++ b/qtwayland/src/client/global/qwaylandclientextension.cpp
      @@ -74,7 +74,10 @@ void QWaylandClientExtensionPrivate::handleRegistryGlobal(void *data, ::wl_regis
       void QWaylandClientExtension::addRegistryListener()
       {
           Q_D(QWaylandClientExtension);
      -    d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this);
      +    if (!d->registered) {
      +        d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this);
      +        d->registered = true;
      +    }
       }
       
       QWaylandClientExtension::QWaylandClientExtension(const int ver)
      @@ -88,6 +91,13 @@ QWaylandClientExtension::QWaylandClientExtension(const int ver)
           QMetaObject::invokeMethod(this, "addRegistryListener", Qt::QueuedConnection);
       }
       
      +QWaylandClientExtension::~QWaylandClientExtension()
      +{
      +    Q_D(QWaylandClientExtension);
      +    if (d->registered && !QCoreApplication::closingDown())
      +        d->waylandIntegration->display()->removeListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this);
      +}
      +
       QtWaylandClient::QWaylandIntegration *QWaylandClientExtension::integration() const
       {
           Q_D(const QWaylandClientExtension);
      diff --git a/qtwayland/src/client/global/qwaylandclientextension.h b/qtwayland/src/client/global/qwaylandclientextension.h
      index 98272e57..5bd28398 100644
      --- a/qtwayland/src/client/global/qwaylandclientextension.h
      +++ b/qtwayland/src/client/global/qwaylandclientextension.h
      @@ -63,6 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtension : public QObject
           Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
       public:
           QWaylandClientExtension(const int version);
      +    ~QWaylandClientExtension();
       
           QtWaylandClient::QWaylandIntegration *integration() const;
           int version() const;
      diff --git a/qtwayland/src/client/global/qwaylandclientextension_p.h b/qtwayland/src/client/global/qwaylandclientextension_p.h
      index 69cc46a0..9091efbe 100644
      --- a/qtwayland/src/client/global/qwaylandclientextension_p.h
      +++ b/qtwayland/src/client/global/qwaylandclientextension_p.h
      @@ -68,6 +68,7 @@ public:
           QtWaylandClient::QWaylandIntegration *waylandIntegration = nullptr;
           int version = -1;
           bool active = false;
      +    bool registered = false;
       };
       
       class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtensionTemplatePrivate : public QWaylandClientExtensionPrivate
      diff --git a/qtwayland/src/client/qwaylanddatadevice.cpp b/qtwayland/src/client/qwaylanddatadevice.cpp
      index 7e2e3308..e3e60ed5 100644
      --- a/qtwayland/src/client/qwaylanddatadevice.cpp
      +++ b/qtwayland/src/client/qwaylanddatadevice.cpp
      @@ -72,6 +72,8 @@ QWaylandDataDevice::QWaylandDataDevice(QWaylandDataDeviceManager *manager, QWayl
       
       QWaylandDataDevice::~QWaylandDataDevice()
       {
      +    if (wl_data_device_get_version(object()) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION)
      +        release();
       }
       
       QWaylandDataOffer *QWaylandDataDevice::selectionOffer() const
      @@ -110,7 +112,7 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
           return m_dragOffer.data();
       }
       
      -bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
      +bool QWaylandDataDevice::startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon)
       {
           auto *seat = m_display->currentInputDevice();
           auto *origin = seat->pointerFocus();
      @@ -123,7 +125,28 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
           }
       
           m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
      +
      +    if (wl_data_device_get_version(object()) >= 3)
      +        m_dragSource->set_actions(dropActionsToWl(supportedActions));
      +
           connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
      +    connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) {
      +            auto drag = static_cast(QGuiApplicationPrivate::platformIntegration()->drag());
      +            // in old versions drop action is not set, so we guess
      +            if (wl_data_source_get_version(m_dragSource->object()) < 3) {
      +                drag->setResponse(accepted);
      +            } else {
      +                QPlatformDropQtResponse response(accepted, action);
      +                drag->setResponse(response);
      +            }
      +    });
      +    connect(m_dragSource.data(), &QWaylandDataSource::dndDropped, this, [](bool accepted, Qt::DropAction action) {
      +        QPlatformDropQtResponse response(accepted, action);
      +        static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setDropResponse(response);
      +    });
      +    connect(m_dragSource.data(), &QWaylandDataSource::finished, this, []() {
      +        static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag();
      +    });
       
           start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
           return true;
      @@ -152,7 +175,7 @@ void QWaylandDataDevice::data_device_drop()
               supportedActions = drag->supportedActions();
           } else if (m_dragOffer) {
               dragData = m_dragOffer->mimeData();
      -        supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
      +        supportedActions = m_dragOffer->supportedActions();
           } else {
               return;
           }
      @@ -162,7 +185,11 @@ void QWaylandDataDevice::data_device_drop()
                                                                                 QGuiApplication::keyboardModifiers());
       
           if (drag) {
      -        static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response);
      +        auto drag =  static_cast(QGuiApplicationPrivate::platformIntegration()->drag());
      +        drag->setDropResponse(response);
      +        drag->finishDrag();
      +    } else if (m_dragOffer) {
      +        m_dragOffer->finish();
           }
       }
       
      @@ -186,7 +213,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface,
               supportedActions = drag->supportedActions();
           } else if (m_dragOffer) {
               dragData = m_dragOffer->mimeData();
      -        supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
      +        supportedActions = m_dragOffer->supportedActions();
           }
       
           const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions,
      @@ -197,11 +224,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface,
               static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);
           }
       
      -    if (response.isAccepted()) {
      -        wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData());
      -    } else {
      -        wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr);
      -    }
      +    sendResponse(supportedActions, response);
       }
       
       void QWaylandDataDevice::data_device_leave()
      @@ -235,10 +258,10 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe
               supportedActions = drag->supportedActions();
           } else {
               dragData = m_dragOffer->mimeData();
      -        supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
      +        supportedActions = m_dragOffer->supportedActions();
           }
       
      -    QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions,
      +    const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions,
                                                                                 QGuiApplication::mouseButtons(),
                                                                                 QGuiApplication::keyboardModifiers());
       
      @@ -246,11 +269,7 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe
               static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response);
           }
       
      -    if (response.isAccepted()) {
      -        wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData());
      -    } else {
      -        wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr);
      -    }
      +    sendResponse(supportedActions, response);
       }
       #endif // QT_CONFIG(draganddrop)
       
      @@ -277,14 +296,10 @@ void QWaylandDataDevice::selectionSourceCancelled()
       #if QT_CONFIG(draganddrop)
       void QWaylandDataDevice::dragSourceCancelled()
       {
      +    static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag();
           m_dragSource.reset();
       }
       
      -void QWaylandDataDevice::dragSourceTargetChanged(const QString &mimeType)
      -{
      -    static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->updateTarget(mimeType);
      -}
      -
       QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) const
       {
           QPoint pnt(wl_fixed_to_int(x), wl_fixed_to_int(y));
      @@ -297,6 +312,33 @@ QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) con
           }
           return pnt;
       }
      +
      +void QWaylandDataDevice::sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response)
      +{
      +    if (response.isAccepted()) {
      +        if (wl_data_device_get_version(object()) >= 3)
      +            m_dragOffer->set_actions(dropActionsToWl(supportedActions), dropActionsToWl(response.acceptedAction()));
      +
      +        m_dragOffer->accept(m_enterSerial, m_dragOffer->firstFormat());
      +    } else {
      +        m_dragOffer->accept(m_enterSerial, QString());
      +    }
      +}
      +
      +int QWaylandDataDevice::dropActionsToWl(Qt::DropActions actions)
      +{
      +
      +    int wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
      +    if (actions & Qt::CopyAction)
      +        wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
      +    if (actions & (Qt::MoveAction | Qt::TargetMoveAction))
      +        wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
      +
      +    // wayland does not support LinkAction at the time of writing
      +    return wlActions;
      +}
      +
      +
       #endif // QT_CONFIG(draganddrop)
       
       }
      diff --git a/qtwayland/src/client/qwaylanddatadevice_p.h b/qtwayland/src/client/qwaylanddatadevice_p.h
      index 16c3ad28..801dcc2c 100644
      --- a/qtwayland/src/client/qwaylanddatadevice_p.h
      +++ b/qtwayland/src/client/qwaylanddatadevice_p.h
      @@ -64,6 +64,7 @@ QT_REQUIRE_CONFIG(wayland_datadevice);
       QT_BEGIN_NAMESPACE
       
       class QMimeData;
      +class QPlatformDragQtResponse;
       class QWindow;
       
       namespace QtWaylandClient {
      @@ -89,7 +90,7 @@ public:
       
       #if QT_CONFIG(draganddrop)
           QWaylandDataOffer *dragOffer() const;
      -    bool startDrag(QMimeData *mimeData, QWaylandWindow *icon);
      +    bool startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon);
           void cancelDrag();
       #endif
       
      @@ -109,13 +110,16 @@ private Q_SLOTS:
       
       #if QT_CONFIG(draganddrop)
           void dragSourceCancelled();
      -    void dragSourceTargetChanged(const QString &mimeType);
       #endif
       
       private:
       #if QT_CONFIG(draganddrop)
           QPoint calculateDragPosition(int x, int y, QWindow *wnd) const;
       #endif
      +    void sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response);
      +
      +    static int dropActionsToWl(Qt::DropActions dropActions);
      +
       
           QWaylandDisplay *m_display = nullptr;
           QWaylandInputDevice *m_inputDevice = nullptr;
      diff --git a/qtwayland/src/client/qwaylanddatadevicemanager.cpp b/qtwayland/src/client/qwaylanddatadevicemanager.cpp
      index 35d67307..6dc4f77f 100644
      --- a/qtwayland/src/client/qwaylanddatadevicemanager.cpp
      +++ b/qtwayland/src/client/qwaylanddatadevicemanager.cpp
      @@ -50,8 +50,8 @@ QT_BEGIN_NAMESPACE
       
       namespace QtWaylandClient {
       
      -QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id)
      -    : wl_data_device_manager(display->wl_registry(), id, 1)
      +QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id)
      +    : wl_data_device_manager(display->wl_registry(), id, qMin(version, 3))
           , m_display(display)
       {
           // Create transfer devices for all input devices.
      diff --git a/qtwayland/src/client/qwaylanddatadevicemanager_p.h b/qtwayland/src/client/qwaylanddatadevicemanager_p.h
      index bd05c0fb..510d9be4 100644
      --- a/qtwayland/src/client/qwaylanddatadevicemanager_p.h
      +++ b/qtwayland/src/client/qwaylanddatadevicemanager_p.h
      @@ -68,7 +68,7 @@ class QWaylandInputDevice;
       class Q_WAYLAND_CLIENT_EXPORT QWaylandDataDeviceManager : public QtWayland::wl_data_device_manager
       {
       public:
      -    QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id);
      +    QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id);
           ~QWaylandDataDeviceManager() override;
       
           QWaylandDataDevice *getDataDevice(QWaylandInputDevice *inputDevice);
      diff --git a/qtwayland/src/client/qwaylanddataoffer.cpp b/qtwayland/src/client/qwaylanddataoffer.cpp
      index 2297e8a1..fe0ea8c9 100644
      --- a/qtwayland/src/client/qwaylanddataoffer.cpp
      +++ b/qtwayland/src/client/qwaylanddataoffer.cpp
      @@ -82,6 +82,15 @@ QMimeData *QWaylandDataOffer::mimeData()
           return m_mimeData.data();
       }
       
      +Qt::DropActions QWaylandDataOffer::supportedActions() const
      +{
      +    if (wl_data_offer_get_version(const_cast<::wl_data_offer*>(object())) < 3) {
      +        return Qt::MoveAction | Qt::CopyAction;
      +    }
      +
      +    return m_supportedActions;
      +}
      +
       void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd)
       {
           receive(mimeType, fd);
      @@ -93,6 +102,22 @@ void QWaylandDataOffer::data_offer_offer(const QString &mime_type)
           m_mimeData->appendFormat(mime_type);
       }
       
      +void QWaylandDataOffer::data_offer_action(uint32_t dnd_action)
      +{
      +    Q_UNUSED(dnd_action);
      +    // This is the compositor telling the drag target what action it should perform
      +    // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action?
      +}
      +
      +void QWaylandDataOffer::data_offer_source_actions(uint32_t source_actions)
      +{
      +    m_supportedActions = Qt::DropActions();
      +    if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
      +        m_supportedActions |= Qt::MoveAction;
      +    if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
      +        m_supportedActions |= Qt::CopyAction;
      +}
      +
       QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer)
           : m_dataOffer(dataOffer)
       {
      @@ -163,17 +188,18 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T
       
       int QWaylandMimeData::readData(int fd, QByteArray &data) const
       {
      -    fd_set readset;
      -    FD_ZERO(&readset);
      -    FD_SET(fd, &readset);
      -    struct timeval timeout;
      +    struct pollfd readset;
      +    readset.fd = fd;
      +    readset.events = POLLIN;
      +    struct timespec timeout;
           timeout.tv_sec = 1;
      -    timeout.tv_usec = 0;
      +    timeout.tv_nsec = 0;
      +
       
           Q_FOREVER {
      -        int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout);
      +        int ready = qt_safe_poll(&readset, 1, &timeout);
               if (ready < 0) {
      -            qWarning() << "QWaylandDataOffer: select() failed";
      +            qWarning() << "QWaylandDataOffer: qt_safe_poll() failed";
                   return -1;
               } else if (ready == 0) {
                   qWarning("QWaylandDataOffer: timeout reading from pipe");
      diff --git a/qtwayland/src/client/qwaylanddataoffer_p.h b/qtwayland/src/client/qwaylanddataoffer_p.h
      index 9cf1483c..6f667398 100644
      --- a/qtwayland/src/client/qwaylanddataoffer_p.h
      +++ b/qtwayland/src/client/qwaylanddataoffer_p.h
      @@ -82,6 +82,7 @@ public:
           explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer);
           ~QWaylandDataOffer() override;
           QMimeData *mimeData() override;
      +    Qt::DropActions supportedActions() const;
       
           QString firstFormat() const;
       
      @@ -89,10 +90,13 @@ public:
       
       protected:
           void data_offer_offer(const QString &mime_type) override;
      +    void data_offer_source_actions(uint32_t source_actions) override;
      +    void data_offer_action(uint32_t dnd_action) override;
       
       private:
           QWaylandDisplay *m_display = nullptr;
           QScopedPointer m_mimeData;
      +    Qt::DropActions m_supportedActions;
       };
       
       
      diff --git a/qtwayland/src/client/qwaylanddatasource.cpp b/qtwayland/src/client/qwaylanddatasource.cpp
      index f45122fb..5599cbd4 100644
      --- a/qtwayland/src/client/qwaylanddatasource.cpp
      +++ b/qtwayland/src/client/qwaylanddatasource.cpp
      @@ -101,7 +101,32 @@ void QWaylandDataSource::data_source_send(const QString &mime_type, int32_t fd)
       
       void QWaylandDataSource::data_source_target(const QString &mime_type)
       {
      -    Q_EMIT targetChanged(mime_type);
      +    m_accepted = !mime_type.isEmpty();
      +    Q_EMIT dndResponseUpdated(m_accepted, m_dropAction);
      +}
      +
      +void QWaylandDataSource::data_source_action(uint32_t action)
      +{
      +    Qt::DropAction qtAction = Qt::IgnoreAction;
      +
      +    if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
      +        qtAction = Qt::MoveAction;
      +    else if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
      +        qtAction = Qt::CopyAction;
      +
      +    m_dropAction = qtAction;
      +    Q_EMIT dndResponseUpdated(m_accepted, m_dropAction);
      +}
      +
      +void QWaylandDataSource::data_source_dnd_finished()
      +{
      +    Q_EMIT finished();
      +}
      +
      +void QWaylandDataSource::data_source_dnd_drop_performed()
      +{
      +
      +    Q_EMIT dndDropped(m_accepted, m_dropAction);
       }
       
       }
      diff --git a/qtwayland/src/client/qwaylanddatasource_p.h b/qtwayland/src/client/qwaylanddatasource_p.h
      index 25afff79..96f07bc3 100644
      --- a/qtwayland/src/client/qwaylanddatasource_p.h
      +++ b/qtwayland/src/client/qwaylanddatasource_p.h
      @@ -77,17 +77,25 @@ public:
           QMimeData *mimeData() const;
       
       Q_SIGNALS:
      -    void targetChanged(const QString &mime_type);
           void cancelled();
      +    void finished();
      +
      +    void dndResponseUpdated(bool accepted, Qt::DropAction action);
      +    void dndDropped(bool accepted, Qt::DropAction action);
       
       protected:
           void data_source_cancelled() override;
           void data_source_send(const QString &mime_type, int32_t fd) override;
           void data_source_target(const QString &mime_type) override;
      +    void data_source_dnd_drop_performed() override;
      +    void data_source_dnd_finished() override;
      +    void data_source_action(uint32_t action) override;
       
       private:
           QWaylandDisplay *m_display = nullptr;
           QMimeData *m_mime_data = nullptr;
      +    bool m_accepted = false;
      +    Qt::DropAction m_dropAction = Qt::IgnoreAction;
       };
       
       }
      diff --git a/qtwayland/src/client/qwaylanddisplay.cpp b/qtwayland/src/client/qwaylanddisplay.cpp
      index f10c1f79..c01e238b 100644
      --- a/qtwayland/src/client/qwaylanddisplay.cpp
      +++ b/qtwayland/src/client/qwaylanddisplay.cpp
      @@ -85,10 +85,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)
      @@ -158,17 +351,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,6 +379,21 @@ QWaylandDisplay::~QWaylandDisplay(void)
       #endif
           if (mDisplay)
               wl_display_disconnect(mDisplay);
      +
      +    if (m_frameEventQueue)
      +        wl_event_queue_destroy(m_frameEventQueue);
      +}
      +
      +// Steps which is called just after constructor. This separates registry_global() out of the constructor
      +// so that factory functions in integration can be overridden.
      +void QWaylandDisplay::initialize()
      +{
      +    forceRoundTrip();
      +
      +    if (!mWaitingScreens.isEmpty()) {
      +        // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
      +        forceRoundTrip();
      +    }
       }
       
       void QWaylandDisplay::ensureScreen()
      @@ -203,98 +410,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
      @@ -345,7 +491,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));
      @@ -354,7 +500,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));
      @@ -369,6 +515,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));
      @@ -427,6 +575,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;
               }
      @@ -452,9 +607,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()
      @@ -467,50 +623,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
      @@ -574,14 +689,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;
       }
      @@ -600,6 +711,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 = {
      @@ -626,6 +750,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()
      @@ -652,4 +783,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p
       
       } // namespace QtWaylandClient
       
      +#include "qwaylanddisplay.moc"
      +
       QT_END_NAMESPACE
      diff --git a/qtwayland/src/client/qwaylanddisplay_p.h b/qtwayland/src/client/qwaylanddisplay_p.h
      index 3b092bc8..42bc661d 100644
      --- a/qtwayland/src/client/qwaylanddisplay_p.h
      +++ b/qtwayland/src/client/qwaylanddisplay_p.h
      @@ -109,6 +109,7 @@ class QWaylandSurface;
       class QWaylandShellIntegration;
       class QWaylandCursor;
       class QWaylandCursorTheme;
      +class EventThread;
       
       typedef void (*RegistryListener)(void *data,
                                        struct wl_registry *registry,
      @@ -120,15 +121,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
      @@ -210,11 +207,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();
      @@ -237,6 +234,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;
      @@ -273,11 +273,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 613fe862..4b90de84 100644
      --- a/qtwayland/src/client/qwaylandinputdevice.cpp
      +++ b/qtwayland/src/client/qwaylandinputdevice.cpp
      @@ -685,6 +685,11 @@ public:
       
       void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surface *surface)
       {
      +    invalidateFocus();
      +    mButtons = Qt::NoButton;
      +
      +    mParent->mTime = time;
      +
           // The event may arrive after destroying the window, indicated by
           // a null surface.
           if (!surface)
      @@ -696,11 +701,6 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
       
           if (!QWaylandWindow::mouseGrab())
               setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos));
      -
      -    invalidateFocus();
      -    mButtons = Qt::NoButton;
      -
      -    mParent->mTime = time;
       }
       
       class MotionEvent : public QWaylandPointerEvent
      @@ -1300,14 +1300,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();
       }
      @@ -1396,6 +1388,7 @@ void QWaylandInputDevice::Touch::touch_cancel()
           if (touchExt)
               touchExt->touchCanceled();
       
      +    mFocus = nullptr;
           QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice);
       }
       
      diff --git a/qtwayland/src/client/qwaylandintegration.cpp b/qtwayland/src/client/qwaylandintegration.cpp
      index c53ccb78..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())
      @@ -479,7 +494,7 @@ void QWaylandIntegration::reconfigureInputContext()
           }
       #endif
       
      -    qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className();
      +    qCDebug(lcQpaWayland) << "using input method:" << (inputContext() ? inputContext()->metaObject()->className() : "");
       }
       
       QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName)
      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 832f9678..ea508771 100644
      --- a/qtwayland/src/client/qwaylandprimaryselectionv1.cpp
      +++ b/qtwayland/src/client/qwaylandprimaryselectionv1.cpp
      @@ -54,11 +54,6 @@ QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1
           : zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1)))
           , m_display(display)
       {
      -    // Create devices for all seats.
      -    // This only works if we get the global before all devices
      -    const auto seats = m_display->inputDevices();
      -    for (auto *seat : seats)
      -        seat->setPrimarySelectionDevice(createDevice(seat));
       }
       
       QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat)
      diff --git a/qtwayland/src/client/qwaylandscreen.cpp b/qtwayland/src/client/qwaylandscreen.cpp
      index 6cb337de..5537dafd 100644
      --- a/qtwayland/src/client/qwaylandscreen.cpp
      +++ b/qtwayland/src/client/qwaylandscreen.cpp
      @@ -60,7 +60,7 @@ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display,
       }
       
       QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id)
      -    : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2))
      +    : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3))
           , m_outputId(id)
           , mWaylandDisplay(waylandDisplay)
           , mOutputName(QStringLiteral("Screen%1").arg(id))
      @@ -72,7 +72,7 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin
               qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor,"
                                       << "QScreen may not work correctly";
               mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc.
      -        mOutputDone = true; // Fake the done event
      +        mProcessedEvents |= OutputDoneEvent; // Fake the done event
               maybeInitialize();
           }
       }
      @@ -81,16 +81,29 @@ QWaylandScreen::~QWaylandScreen()
       {
           if (zxdg_output_v1::isInitialized())
               zxdg_output_v1::destroy();
      +    if (wl_output::isInitialized() && wl_output_get_version(wl_output::object()) >= WL_OUTPUT_RELEASE_SINCE_VERSION)
      +        wl_output::release();
      +}
      +
      +uint QWaylandScreen::requiredEvents() const
      +{
      +    uint ret = OutputDoneEvent;
      +
      +    if (mWaylandDisplay->xdgOutputManager()) {
      +        ret |= XdgOutputNameEvent;
      +
      +        if (mWaylandDisplay->xdgOutputManager()->version() < 3)
      +            ret |= XdgOutputDoneEvent;
      +    }
      +    return ret;
       }
       
       void QWaylandScreen::maybeInitialize()
       {
           Q_ASSERT(!mInitialized);
       
      -    if (!mOutputDone)
      -        return;
      -
      -    if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone)
      +    const uint requiredEvents = this->requiredEvents();
      +    if ((mProcessedEvents & requiredEvents) != requiredEvents)
               return;
       
           mInitialized = true;
      @@ -276,9 +289,8 @@ void QWaylandScreen::output_scale(int32_t factor)
       
       void QWaylandScreen::output_done()
       {
      -    mOutputDone = true;
      -    if (zxdg_output_v1::isInitialized() && mWaylandDisplay->xdgOutputManager()->version() >= 3)
      -        mXdgOutputDone = true;
      +    mProcessedEvents |= OutputDoneEvent;
      +
           if (mInitialized) {
               updateOutputProperties();
               if (zxdg_output_v1::isInitialized())
      @@ -339,7 +351,7 @@ void QWaylandScreen::zxdg_output_v1_done()
           if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3))
               qWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor";
       
      -    mXdgOutputDone = true;
      +    mProcessedEvents |= XdgOutputDoneEvent;
           if (mInitialized)
               updateXdgOutputProperties();
           else
      @@ -348,7 +360,11 @@ void QWaylandScreen::zxdg_output_v1_done()
       
       void QWaylandScreen::zxdg_output_v1_name(const QString &name)
       {
      +    if (Q_UNLIKELY(mInitialized))
      +        qWarning(lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor";
      +
           mOutputName = name;
      +    mProcessedEvents |= XdgOutputNameEvent;
       }
       
       void QWaylandScreen::updateXdgOutputProperties()
      diff --git a/qtwayland/src/client/qwaylandscreen_p.h b/qtwayland/src/client/qwaylandscreen_p.h
      index df1c94f2..050cfdc0 100644
      --- a/qtwayland/src/client/qwaylandscreen_p.h
      +++ b/qtwayland/src/client/qwaylandscreen_p.h
      @@ -116,6 +116,13 @@ public:
           static QWaylandScreen *fromWlOutput(::wl_output *output);
       
       private:
      +    enum Event : uint {
      +        XdgOutputDoneEvent = 0x1,
      +        OutputDoneEvent = 0x2,
      +        XdgOutputNameEvent = 0x4,
      +    };
      +    uint requiredEvents() const;
      +
           void output_mode(uint32_t flags, int width, int height, int refresh) override;
           void output_geometry(int32_t x, int32_t y,
                                int32_t width, int32_t height,
      @@ -148,8 +155,7 @@ private:
           QSize mPhysicalSize;
           QString mOutputName;
           Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
      -    bool mOutputDone = false;
      -    bool mXdgOutputDone = false;
      +    uint mProcessedEvents = 0;
           bool mInitialized = false;
       
       #if QT_CONFIG(cursor)
      diff --git a/qtwayland/src/client/qwaylandshmbackingstore.cpp b/qtwayland/src/client/qwaylandshmbackingstore.cpp
      index dc7ff670..41cffdf7 100644
      --- a/qtwayland/src/client/qwaylandshmbackingstore.cpp
      +++ b/qtwayland/src/client/qwaylandshmbackingstore.cpp
      @@ -52,6 +52,7 @@
       
       #include 
       
      +#include 
       #include 
       #include 
       
      @@ -61,6 +62,9 @@
       #  ifndef MFD_CLOEXEC
       #    define MFD_CLOEXEC     0x0001U
       #  endif
      +#  ifndef MFD_ALLOW_SEALING
      +#    define MFD_ALLOW_SEALING 0x0002U
      +#  endif
       #endif
       
       QT_BEGIN_NAMESPACE
      @@ -74,8 +78,10 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display,
           int alloc = stride * size.height();
           int fd = -1;
       
      -#ifdef SYS_memfd_create
      -    fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC);
      +#if defined(SYS_memfd_create) && defined(F_SEAL_SEAL)
      +    fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
      +    if (fd >= 0)
      +        fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
       #endif
       
           QScopedPointer filePointer;
      diff --git a/qtwayland/src/client/qwaylandwindow.cpp b/qtwayland/src/client/qwaylandwindow.cpp
      index cb82857a..fb2c59dc 100644
      --- a/qtwayland/src/client/qwaylandwindow.cpp
      +++ b/qtwayland/src/client/qwaylandwindow.cpp
      @@ -76,7 +76,6 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
       QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
           : QPlatformWindow(window)
           , mDisplay(display)
      -    , mFrameQueue(mDisplay->createFrameQueue())
           , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
       {
           {
      @@ -95,9 +94,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
       
       QWaylandWindow::~QWaylandWindow()
       {
      -    mDisplay->destroyFrameQueue(mFrameQueue);
      -    mDisplay->handleWindowDestroyed(this);
      -
           delete mWindowDecoration;
       
           if (mSurface)
      @@ -243,6 +239,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const
       
       void QWaylandWindow::reset()
       {
      +    closeChildPopups();
           delete mShellSurface;
           mShellSurface = nullptr;
           delete mSubSurfaceWindow;
      @@ -255,17 +252,22 @@ void QWaylandWindow::reset()
               mSurface.reset();
           }
       
      -    if (mFrameCallback) {
      -        wl_callback_destroy(mFrameCallback);
      -        mFrameCallback = nullptr;
      -    }
      +    {
      +        QMutexLocker lock(&mFrameSyncMutex);
      +        if (mFrameCallback) {
      +            wl_callback_destroy(mFrameCallback);
      +            mFrameCallback = nullptr;
      +        }
       
      -    mFrameCallbackElapsedTimer.invalidate();
      -    mWaitingForFrameCallback = false;
      +        mFrameCallbackElapsedTimer.invalidate();
      +        mWaitingForFrameCallback = false;
      +    }
           mFrameCallbackTimedOut = false;
       
           mMask = QRegion();
           mQueuedBuffer = nullptr;
      +
      +    mDisplay->handleWindowDestroyed(this);
       }
       
       QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface)
      @@ -351,19 +353,25 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect)
           }
       }
       
      -void QWaylandWindow::setGeometry(const QRect &rect)
      +void QWaylandWindow::setGeometry(const QRect &r)
       {
      +    auto rect = r;
      +    if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup
      +        && window()->type() != Qt::ToolTip) {
      +        rect.moveTo(screen()->geometry().topLeft());
      +    }
           setGeometry_helper(rect);
       
           if (window()->isVisible() && rect.isValid()) {
               if (mWindowDecoration)
                   mWindowDecoration->update();
       
      -        if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize)
      +        if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) {
      +            QMutexLocker lock(&mResizeLock);
                   mResizeDirty = true;
      -        else
      +        } else {
                   QWindowSystemInterface::handleGeometryChange(window(), geometry());
      -
      +        }
               mSentInitialResize = true;
           }
           QRect exposeGeometry(QPoint(), geometry().size());
      @@ -374,7 +382,7 @@ void QWaylandWindow::setGeometry(const QRect &rect)
               mShellSurface->setWindowGeometry(windowContentGeometry());
       
           if (isOpaque() && mMask.isEmpty())
      -        setOpaqueArea(rect);
      +        setOpaqueArea(QRect(QPoint(0, 0), rect.size()));
       }
       
       void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
      @@ -399,21 +407,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
           mLastExposeGeometry = rect;
       }
       
      -
      -static QVector> activePopups;
      -
      -void QWaylandWindow::closePopups(QWaylandWindow *parent)
      -{
      -    while (!activePopups.isEmpty()) {
      -        auto popup = activePopups.takeLast();
      -        if (popup.isNull())
      -            continue;
      -        if (popup.data() == parent)
      -            return;
      -        popup->reset();
      -    }
      -}
      -
       QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
       {
           QReadLocker lock(&mSurfaceLock);
      @@ -433,10 +426,7 @@ void QWaylandWindow::setVisible(bool visible)
           lastVisible = visible;
       
           if (visible) {
      -        if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip)
      -            activePopups << this;
               initWindow();
      -        mDisplay->flushRequests();
       
               setGeometry(windowGeometry());
               // Don't flush the events here, or else the newly visible window may start drawing, but since
      @@ -444,7 +434,6 @@ void QWaylandWindow::setVisible(bool visible)
               // QWaylandShmBackingStore::beginPaint().
           } else {
               sendExposeEvent(QRect());
      -        closePopups(this);
               reset();
           }
       }
      @@ -556,12 +545,12 @@ void QWaylandWindow::sendRecursiveExposeEvent()
       
       void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
       {
      -    Q_ASSERT(!buffer->committed());
           QReadLocker locker(&mSurfaceLock);
           if (mSurface == nullptr)
               return;
       
           if (buffer) {
      +        Q_ASSERT(!buffer->committed());
               handleUpdate();
               buffer->setBusy();
       
      @@ -583,7 +572,11 @@ void QWaylandWindow::damage(const QRect &rect)
           if (mSurface == nullptr)
               return;
       
      -    mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
      +    const int s = scale();
      +    if (mDisplay->compositorVersion() >= 4)
      +        mSurface->damage_buffer(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height());
      +    else
      +        mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
       }
       
       void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
      @@ -619,8 +612,14 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
               return;
       
           attachOffset(buffer);
      -    for (const QRect &rect: damage)
      -        mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
      +    if (mDisplay->compositorVersion() >= 4) {
      +        const int s = scale();
      +        for (const QRect &rect: damage)
      +            mSurface->damage_buffer(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height());
      +    } else {
      +        for (const QRect &rect: damage)
      +            mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
      +    }
           Q_ASSERT(!buffer->committed());
           buffer->setCommitted();
           mSurface->commit();
      @@ -635,42 +634,53 @@ void QWaylandWindow::commit()
       
       const wl_callback_listener QWaylandWindow::callbackListener = {
           [](void *data, wl_callback *callback, uint32_t time) {
      -        Q_UNUSED(callback);
               Q_UNUSED(time);
               auto *window = static_cast(data);
      -        window->handleFrameCallback();
      +        window->handleFrameCallback(callback);
           }
       };
       
      -void QWaylandWindow::handleFrameCallback()
      +void QWaylandWindow::handleFrameCallback(wl_callback* callback)
       {
      +    QMutexLocker locker(&mFrameSyncMutex);
      +    if (!mFrameCallback) {
      +        // This means the callback is already unset by QWaylandWindow::reset.
      +        // The wl_callback object will be destroyed there too.
      +        return;
      +    }
      +    Q_ASSERT(callback == mFrameCallback);
      +    wl_callback_destroy(callback);
      +    mFrameCallback = nullptr;
      +
           mWaitingForFrameCallback = false;
           mFrameCallbackElapsedTimer.invalidate();
       
           // The rest can wait until we can run it on the correct thread
      -    if (!mWaitingForUpdateDelivery) {
      -        auto doHandleExpose = [this]() {
      -            bool wasExposed = isExposed();
      -            mFrameCallbackTimedOut = false;
      -            if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed?
      -                sendExposeEvent(QRect(QPoint(), geometry().size()));
      -            if (wasExposed && hasPendingUpdateRequest())
      -                deliverUpdateRequest();
      -
      -            mWaitingForUpdateDelivery = false;
      -        };
      +    auto doHandleExpose = [this]() {
      +        mWaitingForUpdateDelivery.storeRelease(false);
      +        bool wasExposed = isExposed();
      +        mFrameCallbackTimedOut = false;
      +        if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed?
      +            sendExposeEvent(QRect(QPoint(), geometry().size()));
      +        if (wasExposed && hasPendingUpdateRequest())
      +            deliverUpdateRequest();
      +    };
       
      +    if (mWaitingForUpdateDelivery.testAndSetAcquire(false, true)) {
               // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
               // in the single-threaded case.
      -        mWaitingForUpdateDelivery = true;
               QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection);
           }
      +
      +    mFrameSyncWait.notify_all();
       }
       
       bool QWaylandWindow::waitForFrameSync(int timeout)
       {
      -    QMutexLocker locker(mFrameQueue.mutex);
      -    mDisplay->dispatchQueueWhile(mFrameQueue.queue, [&]() { return mWaitingForFrameCallback; }, timeout);
      +    QMutexLocker locker(&mFrameSyncMutex);
      +
      +    QDeadlineTimer deadline(timeout);
      +    while (mWaitingForFrameCallback && mFrameSyncWait.wait(&mFrameSyncMutex, deadline)) { }
       
           if (mWaitingForFrameCallback) {
               qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
      @@ -868,6 +878,17 @@ bool QWaylandWindow::createDecoration()
                   subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
               }
               sendExposeEvent(QRect(QPoint(), geometry().size()));
      +
      +        // This is a special case where the buffer is recreated, but since
      +        // the content rect remains the same, the widgets remain the same
      +        // size and are not redrawn, leaving the new buffer empty. As a simple
      +        // work-around, we trigger a full extra update whenever the client-side
      +        // window decorations are toggled while the window is showing.
      +        // Note: createDecoration() is sometimes called from the render thread
      +        // of Qt Quick. This is essentially wrong and could potentially cause problems,
      +        // but until the underlying issue has been fixed, we have to use invokeMethod()
      +        // here to avoid asserts.
      +        QMetaObject::invokeMethod(window(), &QWindow::requestUpdate);
           }
       
           return mWindowDecoration;
      @@ -1023,6 +1044,13 @@ void QWaylandWindow::handleScreensChanged()
       
           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) {
      @@ -1094,10 +1122,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;
       }
       
      @@ -1139,19 +1175,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()
      @@ -1160,8 +1201,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
      @@ -1174,7 +1218,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);
       }
      @@ -1185,19 +1234,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);
      @@ -1207,6 +1255,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);
      @@ -1267,6 +1317,20 @@ void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea)
           wl_region_destroy(region);
       }
       
      +void QWaylandWindow::addChildPopup(QWaylandWindow *surface) {
      +    mChildPopups.append(surface);
      +}
      +
      +void QWaylandWindow::removeChildPopup(QWaylandWindow *surface) {
      +    mChildPopups.removeAll(surface);
      +}
      +
      +void QWaylandWindow::closeChildPopups() {
      +    while (!mChildPopups.isEmpty()) {
      +        auto popup = mChildPopups.takeLast();
      +        popup->reset();
      +    }
      +}
       }
       
       QT_END_NAMESPACE
      diff --git a/qtwayland/src/client/qwaylandwindow_p.h b/qtwayland/src/client/qwaylandwindow_p.h
      index 01337cff..2f219d8c 100644
      --- a/qtwayland/src/client/qwaylandwindow_p.h
      +++ b/qtwayland/src/client/qwaylandwindow_p.h
      @@ -98,6 +98,9 @@ public:
           QWaylandWindow(QWindow *window, QWaylandDisplay *display);
           ~QWaylandWindow() override;
       
      +    // Keep Toplevels position on the top left corner of their screen
      +    static inline bool fixedToplevelPositions = true;
      +
           virtual WindowType windowType() const = 0;
           virtual void ensureSize();
           WId winId() const override;
      @@ -148,6 +151,7 @@ public:
           void setWindowState(Qt::WindowStates states) override;
           void setWindowFlags(Qt::WindowFlags flags) override;
           void handleWindowStatesChanged(Qt::WindowStates states);
      +    Qt::WindowStates windowStates() const;
       
           void raise() override;
           void lower() override;
      @@ -206,6 +210,10 @@ public:
           void handleUpdate();
           void deliverUpdateRequest() override;
       
      +    void addChildPopup(QWaylandWindow* child);
      +    void removeChildPopup(QWaylandWindow* child);
      +    void closeChildPopups();
      +
       public slots:
           void applyConfigure();
       
      @@ -215,7 +223,11 @@ signals:
       
       protected:
           QWaylandDisplay *mDisplay = nullptr;
      +
      +    // mSurface can be written by the main thread. Other threads should claim a read lock for access
      +    mutable QReadWriteLock mSurfaceLock;
           QScopedPointer mSurface;
      +
           QWaylandShellSurface *mShellSurface = nullptr;
           QWaylandSubSurface *mSubSurfaceWindow = nullptr;
           QVector mChildren;
      @@ -225,13 +237,14 @@ protected:
           Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton;
       
           WId mWindowId;
      -    bool mWaitingForFrameCallback = false;
           bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out
      -    bool mWaitingForUpdateDelivery = false;
           int mFrameCallbackCheckIntervalTimerId = -1;
      -    QElapsedTimer mFrameCallbackElapsedTimer;
      -    struct ::wl_callback *mFrameCallback = nullptr;
      -    QWaylandDisplay::FrameQueue mFrameQueue;
      +    QAtomicInt mWaitingForUpdateDelivery = false;
      +
      +    bool mWaitingForFrameCallback = false; // Protected by mFrameSyncMutex
      +    QElapsedTimer mFrameCallbackElapsedTimer; // Protected by mFrameSyncMutex
      +    struct ::wl_callback *mFrameCallback = nullptr; // Protected by mFrameSyncMutex
      +    QMutex mFrameSyncMutex;
           QWaitCondition mFrameSyncWait;
       
           // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
      @@ -261,6 +274,8 @@ protected:
           QWaylandBuffer *mQueuedBuffer = nullptr;
           QRegion mQueuedBufferDamage;
       
      +    QList> mChildPopups;
      +
       private:
           void setGeometry_helper(const QRect &rect);
           void initWindow();
      @@ -283,12 +298,10 @@ private:
           QRect mLastExposeGeometry;
       
           static const wl_callback_listener callbackListener;
      -    void handleFrameCallback();
      +    void handleFrameCallback(struct ::wl_callback* callback);
       
           static QWaylandWindow *mMouseGrab;
       
      -    mutable QReadWriteLock mSurfaceLock;
      -
           friend class QWaylandSubSurface;
       };
       
      diff --git a/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h b/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h
      index ccad0048..4cc9b3b8 100644
      --- a/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h
      +++ b/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h
      @@ -73,11 +73,10 @@ public:
               return true;
           }
           virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0;
      +    // kept for binary compat with layer-shell-qt
           virtual void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) {
      -        if (newFocus)
      -            m_display->handleWindowActivated(newFocus);
      -        if (oldFocus)
      -            m_display->handleWindowDeactivated(oldFocus);
      +        Q_UNUSED(newFocus);
      +        Q_UNUSED(oldFocus);
           }
           virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) {
               Q_UNUSED(resource);
      diff --git a/qtwayland/src/compositor/configure.json b/qtwayland/src/compositor/configure.json
      index bcfd5215..da95d07b 100644
      --- a/qtwayland/src/compositor/configure.json
      +++ b/qtwayland/src/compositor/configure.json
      @@ -7,6 +7,31 @@
           "testDir": "../../config.tests",
       
           "libraries": {
      +        "wayland-client": {
      +            "label": "Wayland client library",
      +            "headers": "wayland-version.h",
      +            "test": {
      +                "main": [
      +                    "#if WAYLAND_VERSION_MAJOR < 1",
      +                    "# error Wayland 1.8.0 or higher required",
      +                    "#endif",
      +                    "#if WAYLAND_VERSION_MAJOR == 1",
      +                    "# if WAYLAND_VERSION_MINOR < 8",
      +                    "#  error Wayland 1.8.0 or higher required",
      +                    "# endif",
      +                    "# if WAYLAND_VERSION_MINOR == 8",
      +                    "#  if WAYLAND_VERSION_MICRO < 0",
      +                    "#   error Wayland 1.8.0 or higher required",
      +                    "#  endif",
      +                    "# endif",
      +                    "#endif"
      +                 ]
      +            },
      +            "sources": [
      +                { "type": "pkgConfig", "args": "wayland-client" },
      +                "-lwayland-client"
      +            ]
      +        },
               "wayland-server": {
                   "label": "wayland-server",
                   "headers": "wayland-version.h",
      @@ -151,8 +176,7 @@
                           "#endif"
                       ]
                   },
      -            "libs": "-ldrm",
      -            "use": "egl"
      +            "use": "drm egl"
               },
               "dmabuf-client-buffer": {
                   "label": "Linux Client dma-buf Buffer Sharing",
      @@ -176,8 +200,7 @@
                           "return 0;"
                       ]
                   },
      -            "libs": "-ldrm",
      -            "use": "egl"
      +            "use": "drm egl"
               },
               "vulkan-server-buffer": {
                   "label": "Vulkan Buffer Sharing",
      @@ -195,7 +218,8 @@
                           "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;",
                           "return 0;"
                       ]
      -            }
      +            },
      +            "use": "wayland-client"
               }
           },
       
      diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
      index 7889f575..64140672 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 
      @@ -124,6 +125,7 @@ void QWaylandEglWindow::updateSurface(bool create)
               }
               mOffset = QPoint();
           } else {
      +        QReadLocker locker(&mSurfaceLock);
               if (m_waylandEglWindow) {
                   int current_width, current_height;
                   static bool disableResizeCheck = qgetenv("QT_WAYLAND_DISABLE_RESIZECHECK").toInt();
      @@ -131,14 +133,16 @@ void QWaylandEglWindow::updateSurface(bool create)
                   if (!disableResizeCheck) {
                       wl_egl_window_get_attached_size(m_waylandEglWindow, ¤t_width, ¤t_height);
                   }
      -            if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height())) {
      +            if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height()) || m_requestedSize != sizeWithMargins) {
                       wl_egl_window_resize(m_waylandEglWindow, sizeWithMargins.width(), sizeWithMargins.height(), mOffset.x(), mOffset.y());
      +                m_requestedSize = sizeWithMargins;
                       mOffset = QPoint();
       
                       m_resize = true;
                   }
      -        } else if (create && wlSurface()) {
      -            m_waylandEglWindow = wl_egl_window_create(wlSurface(), sizeWithMargins.width(), sizeWithMargins.height());
      +        } else if (create && mSurface) {
      +            m_waylandEglWindow = wl_egl_window_create(mSurface->object(), sizeWithMargins.width(), sizeWithMargins.height());
      +            m_requestedSize = sizeWithMargins;
               }
       
               if (!m_eglSurface && m_waylandEglWindow && create) {
      diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
      index 5b1f4d56..0079dfef 100644
      --- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
      +++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
      @@ -88,6 +88,7 @@ private:
           mutable QOpenGLFramebufferObject *m_contentFBO = nullptr;
       
           QSurfaceFormat m_format;
      +    QSize m_requestedSize;
       };
       
       }
      diff --git a/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h b/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
      index 56a710c3..c6a8b6c6 100644
      --- a/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
      +++ b/qtwayland/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h
      @@ -41,6 +41,8 @@
       #include 
       #include 
       
      +#include 
      +
       #include 
       #include 
       
      diff --git a/qtwayland/src/plugins/decorations/bradient/main.cpp b/qtwayland/src/plugins/decorations/bradient/main.cpp
      index e75fda3c..fa885143 100644
      --- a/qtwayland/src/plugins/decorations/bradient/main.cpp
      +++ b/qtwayland/src/plugins/decorations/bradient/main.cpp
      @@ -164,13 +164,10 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device)
           // Window icon
           QIcon icon = waylandWindow()->windowIcon();
           if (!icon.isNull()) {
      -        QPixmap pixmap = icon.pixmap(QSize(128, 128));
      -        QPixmap scaled = pixmap.scaled(22, 22, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
      -
               QRectF iconRect(0, 0, 22, 22);
      -        p.drawPixmap(iconRect.adjusted(margins().left() + BUTTON_SPACING, 4,
      -                                       margins().left() + BUTTON_SPACING, 4),
      -                     scaled, iconRect);
      +        iconRect.adjust(margins().left() + BUTTON_SPACING, 4,
      +                        margins().left() + BUTTON_SPACING, 4),
      +        icon.paint(&p, iconRect.toRect());
           }
       
           // Window title
      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 f3e3c330..fa6d5808 100644
      --- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
      +++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
      @@ -67,11 +67,6 @@ QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface)
       
       QWaylandXdgSurface::Toplevel::~Toplevel()
       {
      -    if (m_applied.states & Qt::WindowActive) {
      -        QWaylandWindow *window = m_xdgSurface->window();
      -        window->display()->handleWindowDeactivated(window);
      -    }
      -
           // The protocol spec requires that the decoration object is deleted before xdg_toplevel.
           delete m_decoration;
           m_decoration = nullptr;
      @@ -85,16 +80,15 @@ void QWaylandXdgSurface::Toplevel::applyConfigure()
           if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen)))
               m_normalSize = m_xdgSurface->m_window->windowFrameGeometry().size();
       
      -    if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive))
      +    if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive)
      +        && !m_xdgSurface->m_window->display()->isKeyboardAvailable())
               m_xdgSurface->m_window->display()->handleWindowActivated(m_xdgSurface->m_window);
       
      -    if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive))
      +    if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive)
      +        && !m_xdgSurface->m_window->display()->isKeyboardAvailable())
               m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window);
       
      -    // TODO: none of the other plugins send WindowActive either, but is it on purpose?
      -    Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive;
      -
      -    m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive);
      +    m_xdgSurface->m_window->handleWindowStatesChanged(m_pending.states);
       
           if (m_pending.size.isEmpty()) {
               // An empty size in the configure means it's up to the client to choose the size
      @@ -105,8 +99,6 @@ void QWaylandXdgSurface::Toplevel::applyConfigure()
               m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size);
           }
       
      -    m_xdgSurface->setSizeHints();
      -
           m_applied = m_pending;
           qCDebug(lcQpaWayland) << "Applied pending xdg_toplevel configure event:" << m_applied.size << m_applied.states;
       }
      @@ -203,12 +195,17 @@ QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResi
                       | ((edges & Qt::RightEdge) ? resize_edge_right : 0));
       }
       
      -QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent,
      +QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent,
                                        QtWayland::xdg_positioner *positioner)
      -    : xdg_popup(xdgSurface->get_popup(parent->object(), positioner->object()))
      -    , m_xdgSurface(xdgSurface)
      +    : m_xdgSurface(xdgSurface)
      +    , m_parentXdgSurface(qobject_cast(parent->shellSurface()))
           , m_parent(parent)
       {
      +
      +    init(xdgSurface->get_popup(m_parentXdgSurface ? m_parentXdgSurface->object() : nullptr, positioner->object()));
      +    if (m_parent) {
      +        m_parent->addChildPopup(m_xdgSurface->window());
      +    }
       }
       
       QWaylandXdgSurface::Popup::~Popup()
      @@ -216,10 +213,24 @@ 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
      +        QWindow *leave = nullptr;
      +        if (m_xdgSurface && m_xdgSurface->window())
      +            leave = m_xdgSurface->window()->window();
      +        QWindowSystemInterface::handleLeaveEvent(leave);
      +
      +        if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos()))
      +            QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos());
           }
       }
       
      @@ -257,6 +268,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s
                       m_toplevel->set_parent(parentXdgSurface->m_toplevel->object());
               }
           }
      +    setSizeHints();
       }
       
       QWaylandXdgSurface::~QWaylandXdgSurface()
      @@ -400,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
      @@ -414,8 +424,9 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent)
           positioner->set_anchor(QtWayland::xdg_positioner::anchor_top_left);
           positioner->set_gravity(QtWayland::xdg_positioner::gravity_bottom_right);
           positioner->set_size(m_window->geometry().width(), m_window->geometry().height());
      -    m_popup = new Popup(this, parentXdgSurface, positioner);
      +    m_popup = new Popup(this, parent, positioner);
           positioner->destroy();
      +
           delete positioner;
       }
       
      @@ -437,6 +448,23 @@ void QWaylandXdgSurface::setGrabPopup(QWaylandWindow *parent, QWaylandInputDevic
           }
           setPopup(parent);
           m_popup->grab(device, serial);
      +
      +    // Synthesize Qt enter/leave events for popup
      +    if (!parent)
      +        return;
      +    QWindow *current = QGuiApplication::topLevelAt(QCursor::pos());
      +    QWindow *leave = parent->window();
      +    if (current != leave)
      +        return;
      +
      +    QWindowSystemInterface::handleLeaveEvent(leave);
      +
      +    QWindow *enter = nullptr;
      +    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());
       }
       
       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/qwaylandinputmethodeventbuilder.cpp b/qtwayland/src/shared/qwaylandinputmethodeventbuilder.cpp
      index 526d0ef4..f50ccf30 100644
      --- a/qtwayland/src/shared/qwaylandinputmethodeventbuilder.cpp
      +++ b/qtwayland/src/shared/qwaylandinputmethodeventbuilder.cpp
      @@ -39,7 +39,10 @@
       
       #include "qwaylandinputmethodeventbuilder_p.h"
       
      +#include 
      +#include 
       #include 
      +#include 
       #include 
       
       #ifdef QT_BUILD_WAYLANDCOMPOSITOR_LIB
      @@ -81,32 +84,38 @@ void QWaylandInputMethodEventBuilder::addPreeditStyling(uint32_t index, uint32_t
           QTextCharFormat format;
       
           switch (style) {
      -    case 0:
      -    case 1:
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_NONE:
      +        break;
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_DEFAULT:
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_UNDERLINE:
               format.setFontUnderline(true);
               format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
               m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format));
               break;
      -    case 2:
      -    case 3:
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_ACTIVE:
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_INACTIVE:
               format.setFontWeight(QFont::Bold);
               format.setFontUnderline(true);
               format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
               m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format));
               break;
      -    case 4:
      -        format.setFontUnderline(true);
      -        format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
      -        m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format));
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_HIGHLIGHT:
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_SELECTION:
      +        {
      +            format.setFontUnderline(true);
      +            format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
      +            QPalette palette = qApp->palette();
      +            format.setBackground(QBrush(palette.color(QPalette::Active, QPalette::Highlight)));
      +            format.setForeground(QBrush(palette.color(QPalette::Active, QPalette::HighlightedText)));
      +            m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format));
      +        }
               break;
      -    case 5:
      +    case ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_INCORRECT:
               format.setFontUnderline(true);
               format.setUnderlineStyle(QTextCharFormat::WaveUnderline);
               format.setUnderlineColor(QColor(Qt::red));
               m_preeditStyles.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, index, length, format));
               break;
      -//    case QtWayland::wl_text_input::preedit_style_selection:
      -//    case QtWayland::wl_text_input::preedit_style_none:
           default:
               break;
           }
      @@ -153,7 +162,7 @@ QInputMethodEvent QWaylandInputMethodEventBuilder::buildPreedit(const QString &t
       
           if (m_preeditCursor < 0) {
               attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
      -    } else if (m_preeditCursor > 0) {
      +    } else {
               attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, indexFromWayland(text, m_preeditCursor), 1, QVariant()));
           }
       
      diff --git a/qtwayland/src/shared/qwaylandmimehelper.cpp b/qtwayland/src/shared/qwaylandmimehelper.cpp
      index a5fdd34d..e2fe1928 100644
      --- a/qtwayland/src/shared/qwaylandmimehelper.cpp
      +++ b/qtwayland/src/shared/qwaylandmimehelper.cpp
      @@ -60,7 +60,7 @@ QByteArray QWaylandMimeHelper::getByteArray(QMimeData *mimeData, const QString &
                   buf.open(QIODevice::ReadWrite);
                   QByteArray fmt = "BMP";
                   if (mimeType.startsWith(QLatin1String("image/"))) {
      -                QByteArray imgFmt = mimeType.mid(6).toUpper().toLatin1();
      +                QByteArray imgFmt = mimeType.mid(6).toLower().toLatin1();
                       if (QImageWriter::supportedImageFormats().contains(imgFmt))
                           fmt = imgFmt;
                   }
      @@ -74,7 +74,7 @@ QByteArray QWaylandMimeHelper::getByteArray(QMimeData *mimeData, const QString &
               QList urls = mimeData->urls();
               for (int i = 0; i < urls.count(); ++i) {
                   content.append(urls.at(i).toEncoded());
      -            content.append('\n');
      +            content.append("\r\n");
               }
           } else {
               content = mimeData->data(mimeType);
      diff --git a/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp b/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
      index 1568b3b9..067410d0 100644
      --- a/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
      +++ b/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
      @@ -35,7 +35,7 @@
       
       using namespace MockCompositor;
       
      -constexpr int dataDeviceVersion = 1;
      +constexpr int dataDeviceVersion = 3;
       
       class DataDeviceCompositor : public DefaultCompositor {
       public:
      diff --git a/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp b/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp
      index 9312c2e5..2ea382f1 100644
      --- a/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp
      +++ b/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp
      @@ -73,6 +73,7 @@ private slots:
           void multiTouch();
           void multiTouchUpAndMotionFrame();
           void tapAndMoveInSameFrame();
      +    void cancelTouch();
       };
       
       void tst_seatv5::bindsToSeat()
      @@ -646,5 +647,34 @@ void tst_seatv5::tapAndMoveInSameFrame()
           QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased);
       }
       
      +void tst_seatv5::cancelTouch()
      +{
      +    TouchWindow window;
      +    QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
      +
      +    exec([=] {
      +        auto *t = touch();
      +        auto *c = client();
      +        t->sendDown(xdgToplevel()->surface(), {32, 32}, 1);
      +        t->sendFrame(c);
      +        t->sendCancel(c);
      +        t->sendFrame(c);
      +    });
      +
      +    QTRY_VERIFY(!window.m_events.empty());
      +    {
      +        auto e = window.m_events.takeFirst();
      +        QCOMPARE(e.type, QEvent::TouchBegin);
      +        QCOMPARE(e.touchPointStates, Qt::TouchPointPressed);
      +        QCOMPARE(e.touchPoints.length(), 1);
      +        QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top()));
      +    }
      +    {
      +        auto e = window.m_events.takeFirst();
      +        QCOMPARE(e.type, QEvent::TouchCancel);
      +        QCOMPARE(e.touchPoints.length(), 0);
      +    }
      +}
      +
       QCOMPOSITOR_TEST_MAIN(tst_seatv5)
       #include "tst_seatv5.moc"
      diff --git a/qtwayland/tests/auto/client/shared/corecompositor.cpp b/qtwayland/tests/auto/client/shared/corecompositor.cpp
      index 5c6c83ba..fa9b7662 100644
      --- a/qtwayland/tests/auto/client/shared/corecompositor.cpp
      +++ b/qtwayland/tests/auto/client/shared/corecompositor.cpp
      @@ -27,6 +27,7 @@
       ****************************************************************************/
       
       #include "corecompositor.h"
      +#include 
       
       namespace MockCompositor {
       
      diff --git a/qtwayland/tests/auto/client/shared/coreprotocol.cpp b/qtwayland/tests/auto/client/shared/coreprotocol.cpp
      index 0d988521..d1a2e7cb 100644
      --- a/qtwayland/tests/auto/client/shared/coreprotocol.cpp
      +++ b/qtwayland/tests/auto/client/shared/coreprotocol.cpp
      @@ -451,6 +451,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..210d8ddb 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)
           {}
      @@ -364,6 +364,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/surface/tst_surface.cpp b/qtwayland/tests/auto/client/surface/tst_surface.cpp
      index 95e4e609..60c672ce 100644
      --- a/qtwayland/tests/auto/client/surface/tst_surface.cpp
      +++ b/qtwayland/tests/auto/client/surface/tst_surface.cpp
      @@ -129,6 +129,10 @@ void tst_surface::waitForFrameCallbackGl()
           // Make sure we follow frame callbacks for some frames
           for (int i = 0; i < 5; ++i) {
               xdgPingAndWaitForPong(); // Make sure things have happened on the client
      +        if (!qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_WINDOWDECORATION") && i == 0) {
      +            QCOMPARE(bufferSpy.count(), 1);
      +            bufferSpy.removeFirst();
      +        }
               exec([&] {
                   QVERIFY(bufferSpy.empty()); // Make sure no extra buffers have arrived
                   QVERIFY(!xdgToplevel()->surface()->m_waitingFrameCallbacks.empty());
      diff --git a/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp b/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp
      index 2277bbb8..747875b4 100644
      --- a/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp
      +++ b/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp
      @@ -31,6 +31,7 @@
       #include 
       #include 
       #include 
      +#include 
       
       using namespace MockCompositor;
       
      @@ -45,6 +46,7 @@ private slots:
           void configureStates();
           void popup();
           void tooltipOnPopup();
      +    void tooltipAndSiblingPopup();
           void switchPopups();
           void hidePopupParent();
           void pongs();
      @@ -138,6 +140,7 @@ void tst_xdgshell::configureSize()
       
       void tst_xdgshell::configureStates()
       {
      +    QVERIFY(qputenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", "0"));
           QRasterWindow window;
           window.resize(64, 48);
           window.show();
      @@ -154,9 +157,12 @@ void tst_xdgshell::configureStates()
           // Toplevel windows don't know their position on xdg-shell
       //    QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
       
      -//    QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue);
      -//    QVERIFY(window.isActive());
      -    QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly
      +    // window.windowstate() is driven by keyboard focus, however for decorations we want to follow
      +    // XDGShell this is internal to QtWayland so it is queried directly
      +    auto waylandWindow = static_cast(window.handle());
      +    Q_ASSERT(waylandWindow);
      +    QTRY_VERIFY(waylandWindow->windowStates().testFlag(
      +            Qt::WindowActive)); // Just make sure it eventually get's set correctly
       
           const QSize screenSize(640, 480);
           const uint maximizedSerial = exec([=] {
      @@ -186,6 +192,7 @@ void tst_xdgshell::configureStates()
           QCOMPARE(window.windowStates(), Qt::WindowNoState);
           QCOMPARE(window.frameGeometry().size(), windowedSize);
       //    QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled
      +    QVERIFY(qunsetenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT"));
       }
       
       void tst_xdgshell::popup()
      @@ -340,6 +347,92 @@ void tst_xdgshell::tooltipOnPopup()
           QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
       }
       
      +void tst_xdgshell::tooltipAndSiblingPopup()
      +{
      +    class ToolTip : public QRasterWindow {
      +    public:
      +        explicit ToolTip(QWindow *parent) {
      +            setTransientParent(parent);
      +            setFlags(Qt::ToolTip);
      +            resize(100, 100);
      +            show();
      +        }
      +        void mousePressEvent(QMouseEvent *event) override {
      +            QRasterWindow::mousePressEvent(event);
      +            m_popup = new QRasterWindow;
      +            m_popup->setTransientParent(transientParent());
      +            m_popup->setFlags(Qt::Popup);
      +            m_popup->resize(100, 100);
      +            m_popup->show();
      +        }
      +
      +        QRasterWindow *m_popup = nullptr;
      +    };
      +
      +    class Window : public QRasterWindow {
      +    public:
      +        void mousePressEvent(QMouseEvent *event) override {
      +            QRasterWindow::mousePressEvent(event);
      +            m_tooltip = new ToolTip(this);
      +        }
      +        ToolTip *m_tooltip = nullptr;
      +    };
      +
      +    Window window;
      +    window.resize(200, 200);
      +    window.show();
      +
      +    QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
      +    exec([=] { xdgToplevel()->sendCompleteConfigure(); });
      +    QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
      +
      +    exec([=] {
      +        auto *surface = xdgToplevel()->surface();
      +        auto *p = pointer();
      +        auto *c = client();
      +        p->sendEnter(surface, {100, 100});
      +        p->sendFrame(c);
      +        p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
      +        p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
      +        p->sendFrame(c);
      +        p->sendLeave(surface);
      +        p->sendFrame(c);
      +    });
      +
      +    QCOMPOSITOR_TRY_VERIFY(xdgPopup());
      +    exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
      +    QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
      +    QCOMPOSITOR_TRY_VERIFY(!xdgPopup()->m_grabbed);
      +
      +    exec([=] {
      +        auto *surface = xdgPopup()->surface();
      +        auto *p = pointer();
      +        auto *c = client();
      +        p->sendEnter(surface, {100, 100});
      +        p->sendFrame(c);
      +        p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
      +        p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
      +        p->sendFrame(c);
      +    });
      +
      +    QCOMPOSITOR_TRY_VERIFY(xdgPopup(1));
      +    exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
      +    QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial);
      +    QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_grabbed);
      +
      +    // Close the middle tooltip (it should not close the sibling popup)
      +    window.m_tooltip->close();
      +
      +    QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr);
      +    // Verify the remaining xdg surface is a grab popup..
      +    QCOMPOSITOR_TRY_VERIFY(xdgPopup(0));
      +    QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)->m_grabbed);
      +
      +    window.m_tooltip->m_popup->close();
      +    QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr);
      +    QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
      +}
      +
       // QTBUG-65680
       void tst_xdgshell::switchPopups()
       {
      @@ -505,7 +598,7 @@ void tst_xdgshell::minMaxSize()
           window.show();
           QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
       
      -    exec([=] { xdgToplevel()->sendCompleteConfigure(); });
      +    // we don't roundtrip with our configuration the initial commit should be correct
       
           QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(100, 100));
           QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(1000, 1000));
      Submodule qtwebchannel 618422ee..f8949655:
      diff --git a/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp b/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp
      index 536eb5c..898d769 100644
      --- a/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp
      +++ b/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp
      @@ -186,8 +186,6 @@ Q_DECLARE_TYPEINFO(OverloadResolutionCandidate, Q_MOVABLE_TYPE);
       QMetaObjectPublisher::QMetaObjectPublisher(QWebChannel *webChannel)
           : QObject(webChannel)
           , webChannel(webChannel)
      -    , signalHandler(this)
      -    , clientIsIdle(false)
           , blockUpdates(false)
           , propertyUpdatesInitialized(false)
       {
      @@ -301,17 +299,17 @@ QJsonObject QMetaObjectPublisher::classInfoForObject(const QObject *object, QWeb
           return data;
       }
       
      -void QMetaObjectPublisher::setClientIsIdle(bool isIdle)
      +void QMetaObjectPublisher::setClientIsIdle(bool isIdle, QWebChannelAbstractTransport *transport)
       {
      -    if (clientIsIdle == isIdle) {
      -        return;
      -    }
      -    clientIsIdle = isIdle;
      -    if (!isIdle && timer.isActive()) {
      -        timer.stop();
      -    } else if (isIdle && !timer.isActive()) {
      -        timer.start(PROPERTY_UPDATE_INTERVAL, this);
      -    }
      +    transportState[transport].clientIsIdle = isIdle;
      +    if (isIdle)
      +        sendEnqueuedPropertyUpdates(transport);
      +}
      +
      +bool QMetaObjectPublisher::isClientIdle(QWebChannelAbstractTransport *transport)
      +{
      +    auto found = transportState.find(transport);
      +    return found != transportState.end() && found.value().clientIsIdle;
       }
       
       QJsonObject QMetaObjectPublisher::initializeClient(QWebChannelAbstractTransport *transport)
      @@ -333,6 +331,7 @@ QJsonObject QMetaObjectPublisher::initializeClient(QWebChannelAbstractTransport
       
       void QMetaObjectPublisher::initializePropertyUpdates(const QObject *const object, const QJsonObject &objectInfo)
       {
      +    auto *signalHandler = signalHandlerFor(object);
           foreach (const QJsonValue &propertyInfoVar, objectInfo[KEY_PROPERTIES].toArray()) {
               const QJsonArray &propertyInfo = propertyInfoVar.toArray();
               if (propertyInfo.size() < 2) {
      @@ -353,19 +352,19 @@ void QMetaObjectPublisher::initializePropertyUpdates(const QObject *const object
       
               // Only connect for a property update once
               if (connectedProperties.isEmpty()) {
      -            signalHandler.connectTo(object, signalIndex);
      +            signalHandler->connectTo(object, signalIndex);
               }
       
               connectedProperties.insert(propertyIndex);
           }
       
           // also always connect to destroyed signal
      -    signalHandler.connectTo(object, s_destroyedSignalIndex);
      +    signalHandler->connectTo(object, s_destroyedSignalIndex);
       }
       
       void QMetaObjectPublisher::sendPendingPropertyUpdates()
       {
      -    if (blockUpdates || !clientIsIdle || pendingPropertyUpdates.isEmpty()) {
      +    if (blockUpdates) {
               return;
           }
       
      @@ -415,18 +414,19 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates()
       
           // data does not contain specific updates
           if (!data.isEmpty()) {
      -        setClientIsIdle(false);
      -
               message[KEY_DATA] = data;
      -        broadcastMessage(message);
      +        enqueueBroadcastMessage(message);
           }
       
           // send every property update which is not supposed to be broadcasted
           const QHash::const_iterator suend = specificUpdates.constEnd();
           for (QHash::const_iterator it = specificUpdates.constBegin(); it != suend; ++it) {
               message[KEY_DATA] = it.value();
      -        it.key()->sendMessage(message);
      +        enqueueMessage(message, it.key());
           }
      +
      +    for (auto state = transportState.begin(); state != transportState.end(); ++state)
      +        sendEnqueuedPropertyUpdates(state.key());
       }
       
       QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMethod &method,
      @@ -572,7 +572,7 @@ void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signal
               }
           } else {
               pendingPropertyUpdates[object][signalIndex] = arguments;
      -        if (clientIsIdle && !blockUpdates && !timer.isActive()) {
      +        if (!blockUpdates && !timer.isActive()) {
                   timer.start(PROPERTY_UPDATE_INTERVAL, this);
               }
           }
      @@ -590,7 +590,7 @@ void QMetaObjectPublisher::objectDestroyed(const QObject *object)
           // only remove from handler when we initialized the property updates
           // cf: https://bugreports.qt.io/browse/QTBUG-60250
           if (propertyUpdatesInitialized) {
      -        signalHandler.remove(object);
      +        signalHandlerFor(object)->remove(object);
               signalToPropertyMap.remove(object);
           }
           pendingPropertyUpdates.remove(object);
      @@ -852,6 +852,52 @@ void QMetaObjectPublisher::broadcastMessage(const QJsonObject &message) const
           }
       }
       
      +void QMetaObjectPublisher::enqueueBroadcastMessage(const QJsonObject &message)
      +{
      +    if (webChannel->d_func()->transports.isEmpty()) {
      +        qWarning("QWebChannel is not connected to any transports, cannot send message: %s",
      +                 QJsonDocument(message).toJson().constData());
      +        return;
      +    }
      +
      +    for (auto *transport : webChannel->d_func()->transports) {
      +        auto &state = transportState[transport];
      +        state.queuedMessages.append(message);
      +    }
      +}
      +
      +void QMetaObjectPublisher::enqueueMessage(const QJsonObject &message,
      +                                          QWebChannelAbstractTransport *transport)
      +{
      +    auto &state = transportState[transport];
      +    state.queuedMessages.append(message);
      +}
      +
      +void QMetaObjectPublisher::sendEnqueuedPropertyUpdates(QWebChannelAbstractTransport *transport)
      +{
      +    auto found = transportState.find(transport);
      +    if (found != transportState.end() && found.value().clientIsIdle
      +        && !found.value().queuedMessages.isEmpty()) {
      +
      +        // If the client is connected with an in-process transport, it can
      +        // happen that a message triggers a subsequent property change. In
      +        // that case, we need to ensure that the queued messages have already
      +        // been cleared; otherwise the recursive call will send everythig again.
      +        // Case in point: The qmlwebchannel tests fail if we don't clear the
      +        // queued messages before sending them out.
      +        // For that same reason set the client to "busy" (aka non-idle) just
      +        // right before sending out the messages; otherwise a potential
      +        // "Idle" type message will not correctly restore the Idle state.
      +        const auto messages = std::move(found.value().queuedMessages);
      +        Q_ASSERT(found.value().queuedMessages.isEmpty());
      +        found.value().clientIsIdle = false;
      +
      +        for (const auto &message : messages) {
      +            transport->sendMessage(message);
      +        }
      +    }
      +}
      +
       void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannelAbstractTransport *transport)
       {
           if (!webChannel->d_func()->transports.contains(transport)) {
      @@ -866,7 +912,7 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel
       
           const MessageType type = toType(message.value(KEY_TYPE));
           if (type == TypeIdle) {
      -        setClientIsIdle(true);
      +        setClientIsIdle(true, transport);
           } else if (type == TypeInit) {
               if (!message.contains(KEY_ID)) {
                   qWarning("JSON message object is missing the id property: %s",
      @@ -913,9 +959,9 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel
                       return;
                   transport->sendMessage(createResponse(message.value(KEY_ID), wrapResult(result, transport)));
               } else if (type == TypeConnectToSignal) {
      -            signalHandler.connectTo(object, message.value(KEY_SIGNAL).toInt(-1));
      +            signalHandlerFor(object)->connectTo(object, message.value(KEY_SIGNAL).toInt(-1));
               } else if (type == TypeDisconnectFromSignal) {
      -            signalHandler.disconnectFrom(object, message.value(KEY_SIGNAL).toInt(-1));
      +            signalHandlerFor(object)->disconnectFrom(object, message.value(KEY_SIGNAL).toInt(-1));
               } else if (type == TypeSetProperty) {
                   setProperty(object, message.value(KEY_PROPERTY).toInt(-1),
                               message.value(KEY_VALUE));
      @@ -931,6 +977,7 @@ void QMetaObjectPublisher::setBlockUpdates(bool block)
           blockUpdates = block;
       
           if (!blockUpdates) {
      +        timer.start(PROPERTY_UPDATE_INTERVAL, this);
               sendPendingPropertyUpdates();
           } else if (timer.isActive()) {
               timer.stop();
      @@ -948,4 +995,15 @@ void QMetaObjectPublisher::timerEvent(QTimerEvent *event)
           }
       }
       
      +SignalHandler *QMetaObjectPublisher::signalHandlerFor(const QObject *object)
      +{
      +    auto thread = object->thread();
      +    auto it = signalHandlers.find(thread);
      +    if (it == signalHandlers.end()) {
      +        it = signalHandlers.emplace(thread, this).first;
      +        it->second.moveToThread(thread);
      +    }
      +    return &it->second;
      +}
      +
       QT_END_NAMESPACE
      diff --git a/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h b/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h
      index bbd9875..4713ef1 100644
      --- a/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h
      +++ b/qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h
      @@ -59,6 +59,9 @@
       #include 
       #include 
       #include 
      +#include 
      +
      +#include 
       
       #include "qwebchannelglobal.h"
       
      @@ -109,17 +112,36 @@ public:
            */
           void broadcastMessage(const QJsonObject &message) const;
       
      +    /**
      +     * Enqueue the given @p message to all known transports.
      +     */
      +    void enqueueBroadcastMessage(const QJsonObject &message);
      +
      +    /**
      +     * Enqueue the given @p message to @p transport.
      +     */
      +    void enqueueMessage(const QJsonObject &message, QWebChannelAbstractTransport *transport);
      +
      +    /**
      +     * If client for given @p transport is idle, send queued messaged to @p transport and then mark
      +     * the client as not idle.
      +     */
      +    void sendEnqueuedPropertyUpdates(QWebChannelAbstractTransport *transport);
      +
           /**
            * Serialize the QMetaObject of @p object and return it in JSON form.
            */
           QJsonObject classInfoForObject(const QObject *object, QWebChannelAbstractTransport *transport);
       
           /**
      -     * Set the client to idle or busy, based on the value of @p isIdle.
      -     *
      -     * When the value changed, start/stop the property update timer accordingly.
      +     * Set the client to idle or busy for a single @p transport, based on the value of @p isIdle.
      +     */
      +    void setClientIsIdle(bool isIdle, QWebChannelAbstractTransport *transport);
      +
      +    /**
      +     * Check that client is idle for @p transport.
            */
      -    void setClientIsIdle(bool isIdle);
      +    bool isClientIdle(QWebChannelAbstractTransport *transport);
       
           /**
            * Initialize clients by sending them the class information of the registered objects.
      @@ -272,10 +294,18 @@ private:
           friend class TestWebChannel;
       
           QWebChannel *webChannel;
      -    SignalHandler signalHandler;
      +    std::unordered_map> signalHandlers;
      +    SignalHandler *signalHandlerFor(const QObject *object);
       
      -    // true when the client is idle, false otherwise
      -    bool clientIsIdle;
      +    struct TransportState
      +    {
      +        TransportState() : clientIsIdle(false) { }
      +        // true when the client is idle, false otherwise
      +        bool clientIsIdle;
      +        // messages to send
      +        QQueue queuedMessages;
      +    };
      +    QHash transportState;
       
           // true when no property updates should be sent, false otherwise
           bool blockUpdates;
      diff --git a/qtwebchannel/src/webchannel/signalhandler_p.h b/qtwebchannel/src/webchannel/signalhandler_p.h
      index 27afadb..d77373c 100644
      --- a/qtwebchannel/src/webchannel/signalhandler_p.h
      +++ b/qtwebchannel/src/webchannel/signalhandler_p.h
      @@ -56,6 +56,7 @@
       #include 
       #include 
       #include 
      +#include 
       
       QT_BEGIN_NAMESPACE
       
      @@ -71,6 +72,7 @@ static const int s_destroyedSignalIndex = QObject::staticMetaObject.indexOfMetho
       template
       class SignalHandler : public QObject
       {
      +    Q_DISABLE_COPY(SignalHandler)
       public:
           SignalHandler(Receiver *receiver, QObject *parent = 0);
       
      @@ -268,6 +270,7 @@ int SignalHandler::qt_metacall(QMetaObject::Call call, int methodId, v
           if (call == QMetaObject::InvokeMetaMethod) {
               const QObject *object = sender();
               Q_ASSERT(object);
      +        Q_ASSERT(QThread::currentThread() == object->thread());
               Q_ASSERT(senderSignalIndex() == methodId);
               Q_ASSERT(m_connectionsCounter.contains(object));
               Q_ASSERT(m_connectionsCounter.value(object).contains(methodId));
      diff --git a/qtwebchannel/tests/auto/qml/testwebchannel.cpp b/qtwebchannel/tests/auto/qml/testwebchannel.cpp
      index 9891687..3ca81c2 100644
      --- a/qtwebchannel/tests/auto/qml/testwebchannel.cpp
      +++ b/qtwebchannel/tests/auto/qml/testwebchannel.cpp
      @@ -46,7 +46,11 @@ TestWebChannel::~TestWebChannel()
       
       bool TestWebChannel::clientIsIdle() const
       {
      -    return QWebChannel::d_func()->publisher->clientIsIdle;
      +    for (auto *transport : QWebChannel::d_func()->transports) {
      +        if (QWebChannel::d_func()->publisher->isClientIdle(transport))
      +            return true;
      +    }
      +    return false;
       }
       
       QT_END_NAMESPACE
      diff --git a/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp b/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp
      index 181da9e..37f989a 100644
      --- a/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp
      +++ b/qtwebchannel/tests/auto/webchannel/tst_webchannel.cpp
      @@ -785,7 +785,7 @@ void TestWebChannel::testTransportWrapObjectProperties()
           DummyTransport *dummyTransport = new DummyTransport(this);
           channel.connectTo(dummyTransport);
           channel.d_func()->publisher->initializeClient(dummyTransport);
      -    channel.d_func()->publisher->setClientIsIdle(true);
      +    channel.d_func()->publisher->setClientIsIdle(true, dummyTransport);
       
           QCOMPARE(channel.d_func()->publisher->transportedWrappedObjects.size(), 0);
       
      @@ -943,8 +943,6 @@ void TestWebChannel::testInfiniteRecursion()
       
       void TestWebChannel::testAsyncObject()
       {
      -    QSKIP("This test is broken. See QTBUG-80729");
      -
           QWebChannel channel;
           channel.connectTo(m_dummyTransport);
       
      @@ -990,6 +988,50 @@ void TestWebChannel::testAsyncObject()
           thread.wait();
       }
       
      +void TestWebChannel::testPropertyMultipleTransports()
      +{
      +    DummyTransport transport1;
      +    DummyTransport transport2;
      +
      +    QWebChannel channel;
      +    QMetaObjectPublisher *publisher = channel.d_func()->publisher;
      +
      +    TestObject testObj;
      +    testObj.setObjectName("testObject");
      +    channel.registerObject(testObj.objectName(), &testObj);
      +    channel.connectTo(&transport1);
      +    channel.connectTo(&transport2);
      +
      +    testObj.setProp("Hello");
      +
      +    publisher->initializeClient(&transport1);
      +    publisher->initializeClient(&transport2);
      +    publisher->setClientIsIdle(true, &transport1);
      +    QCOMPARE(publisher->isClientIdle(&transport1), true);
      +    QCOMPARE(publisher->isClientIdle(&transport2), false);
      +    QVERIFY(transport1.messagesSent().isEmpty());
      +    QVERIFY(transport2.messagesSent().isEmpty());
      +
      +    testObj.setProp("World");
      +    QTRY_COMPARE_WITH_TIMEOUT(transport1.messagesSent().size(), 1u, 2000);
      +    QCOMPARE(transport2.messagesSent().size(), 0u);
      +    publisher->setClientIsIdle(true, &transport2);
      +    QTRY_COMPARE_WITH_TIMEOUT(transport2.messagesSent().size(), 1u, 2000);
      +    QCOMPARE(publisher->isClientIdle(&transport1), false);
      +    QCOMPARE(publisher->isClientIdle(&transport2), false);
      +
      +    testObj.setProp("!!!");
      +    publisher->setClientIsIdle(true, &transport2);
      +    QCOMPARE(publisher->isClientIdle(&transport2), true);
      +    QCOMPARE(publisher->isClientIdle(&transport1), false);
      +    QTRY_COMPARE_WITH_TIMEOUT(transport2.messagesSent().size(), 2u, 2000);
      +    QCOMPARE(transport1.messagesSent().size(), 1u);
      +    publisher->setClientIsIdle(true, &transport1);
      +    QTRY_COMPARE_WITH_TIMEOUT(transport1.messagesSent().size(), 2u, 2000);
      +    QCOMPARE(publisher->isClientIdle(&transport1), false);
      +    QCOMPARE(publisher->isClientIdle(&transport2), false);
      +}
      +
       class FunctionWrapper : public QObject
       {
           Q_OBJECT
      @@ -1082,7 +1124,7 @@ void TestWebChannel::benchInitializeClients()
       
               publisher->propertyUpdatesInitialized = false;
               publisher->signalToPropertyMap.clear();
      -        publisher->signalHandler.clear();
      +        publisher->signalHandlers.clear();
           }
       }
       
      @@ -1107,7 +1149,7 @@ void TestWebChannel::benchPropertyUpdates()
                   obj->change();
               }
       
      -        channel.d_func()->publisher->clientIsIdle = true;
      +        channel.d_func()->publisher->setClientIsIdle(true, m_dummyTransport);
               channel.d_func()->publisher->sendPendingPropertyUpdates();
           }
       }
      diff --git a/qtwebchannel/tests/auto/webchannel/tst_webchannel.h b/qtwebchannel/tests/auto/webchannel/tst_webchannel.h
      index eae21f4..dd4e690 100644
      --- a/qtwebchannel/tests/auto/webchannel/tst_webchannel.h
      +++ b/qtwebchannel/tests/auto/webchannel/tst_webchannel.h
      @@ -348,6 +348,7 @@ private slots:
           void testJsonToVariant();
           void testInfiniteRecursion();
           void testAsyncObject();
      +    void testPropertyMultipleTransports();
           void testDeletionDuringMethodInvocation_data();
           void testDeletionDuringMethodInvocation();
       
      Submodule qtwebengine f328054d...38e0df6c (commits not present)
      Submodule qtwebsockets 65850954..63fb8da1:
      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();
       }
       
       /*!
      diff --git a/qtwebsockets/src/websockets/qwebsocketprotocol.cpp b/qtwebsockets/src/websockets/qwebsocketprotocol.cpp
      index df87a93..d0465f1 100644
      --- a/qtwebsockets/src/websockets/qwebsocketprotocol.cpp
      +++ b/qtwebsockets/src/websockets/qwebsocketprotocol.cpp
      @@ -210,7 +210,7 @@ void QWebSocketProtocol::mask(char *payload, quint64 size, quint32 maskingKey)
                                   quint8((maskingKey & 0x0000FF00u) >> 8),
                                   quint8((maskingKey & 0x000000FFu))
                                 };
      -    int i = 0;
      +    quint64 i = 0;
           while (size-- > 0)
               *payload++ ^= mask[i++ % 4];
       }