diff --git a/src/qt5/pkg.sh b/src/qt5/pkg.sh index 624e674..db72daf 100644 --- a/src/qt5/pkg.sh +++ b/src/qt5/pkg.sh @@ -1,22 +1,26 @@ NAME="qt5" DESC="Cross-platform application framework that is widely used for developing application software with a graphical user interface" -VERSION="5.15.10" +VERSION="5.15.12" + FILES=( - "https://download.qt.io/archive/qt/${VERSION%.*}/$VERSION/single/qt-everywhere-opensource-src-$VERSION.tar.xz" - "https://www.linuxfromscratch.org/patches/blfs/12.0/qt-everywhere-opensource-src-$VERSION-kf5-1.patch" + "https://download.qt.io/archive/qt/${VERSION%.*}/${VERSION}/single/qt-everywhere-opensource-src-${VERSION}.tar.xz" + "qt-everywhere-opensource-src-5.15.12-security_fix-1.patch" + "qt-everywhere-opensource-src-5.15.12-kf5-1.patch" "assistant-qt5.desktop" "designer-qt5.desktop" "linguist-qt5.desktop" "qdbusviewer-qt5.desktop" ) HASHES=( - "fb41d86bea6bc4886030a5092c910b09" - "39848ffa81817b251db5f122b6942362174274e670ce51443878d4012c7ebf53" + "3fb1cd4f763f5d50d491508b7b99fb77" + "50db2eb15bada7e87ddbe43a9a6a8f2844513356dd9c9adc9f4fedfc12b0db6d" + "82257f13e8c3b85955bf1d0750049b945dedadc3f2f76960f3d922347372b1da" "162c1b77fa48db234f483148337c57f3850d9749cfdb8660054a8c81a52c1f4b" "945d209667d4bdb01ddb9d5ee4968dee55e4386f0927246995de8bda93b0c274" "27b1237daf07ac00b9035f607744c42f5824455f20f94e3f1ac9eed64508542c" "5ce3d8b7550bdd1423c786b6ef543564aacaec61a62fdee1601bea249ff794cb" ) + DEPENDS=( "xorg" "fontconfig" "libinput" "double-conversion" "libjpeg-turbo" "libxi" "libxkbcommon" @@ -26,15 +30,16 @@ DEPENDS=( ) PACKAGE() { - tar xf qt-everywhere-opensource-src-$VERSION.tar.xz - cd qt-everywhere-src-$VERSION + tar xf "qt-everywhere-opensource-src-${VERSION}.tar.xz" + cd "qt-everywhere-src-${VERSION}" - patch -Np1 -i ../qt-everywhere-opensource-src-$VERSION-kf5-1.patch + patch -Np1 -i "../qt-everywhere-opensource-src-${VERSION}-kf5-1.patch" mkdir -pv qtbase/.git sed -e "/pragma once/a#include " \ -i qtlocation/src/3rdparty/mapbox-gl-native/include/mbgl/util/geometry.hpp \ qtlocation/src/3rdparty/mapbox-gl-native/include/mbgl/util/string.hpp \ qtlocation/src/3rdparty/mapbox-gl-native/src/mbgl/gl/stencil_mode.hpp + patch -Np1 -i "../qt-everywhere-opensource-src-${VERSION}-security_fix-1.patch" ./configure -prefix /usr \ -sysconfdir /etc/xdg \ @@ -57,30 +62,31 @@ PACKAGE() { -docdir /usr/share/doc/qt5 \ -translationdir /usr/share/qt5/translations \ -examplesdir /usr/share/doc/qt5/examples - make && echo "Make completed, running the install" - make INSTALL_ROOT="$ROOTDIR" install - find "$ROOTDIR/usr/lib" -name \*.prl \ + make + make INSTALL_ROOT="${ROOTDIR}" install + + find "${ROOTDIR}/usr/lib" -name \*.prl \ -exec sed -i -e '/^QMAKE_PRL_BUILD_DIR/d' {} \; - install -v -dm755 "$ROOTDIR/usr/share/pixmaps/" + install -v -dm755 "${ROOTDIR}/usr/share/pixmaps/" install -v -Dm644 qttools/src/assistant/assistant/images/assistant-128.png \ - "$ROOTDIR/usr/share/pixmaps/assistant-qt5.png" + "${ROOTDIR}/usr/share/pixmaps/assistant-qt5.png" install -v -Dm644 qttools/src/designer/src/designer/images/designer.png \ - "$ROOTDIR/usr/share/pixmaps/designer-qt5.png" + "${ROOTDIR}/usr/share/pixmaps/designer-qt5.png" install -v -Dm644 qttools/src/linguist/linguist/images/icons/linguist-128-32.png \ - "$ROOTDIR/usr/share/pixmaps/linguist-qt5.png" + "${ROOTDIR}/usr/share/pixmaps/linguist-qt5.png" install -v -Dm644 qttools/src/qdbus/qdbusviewer/images/qdbusviewer-128.png \ - "$ROOTDIR/usr/share/pixmaps/qdbusviewer-qt5.png" + "${ROOTDIR}/usr/share/pixmaps/qdbusviewer-qt5.png" - install -dm755 "$ROOTDIR/usr/share/applications" - cp "$ROOTDIR/assistant-qt5.desktop" "$ROOTDIR/usr/share/applications/" - cp "$ROOTDIR/designer-qt5.desktop" "$ROOTDIR/usr/share/applications/" - cp "$ROOTDIR/linguist-qt5.desktop" "$ROOTDIR/usr/share/applications/" - cp "$ROOTDIR/qdbusviewer-qt5.desktop" "$ROOTDIR/usr/share/applications/" + install -Ddm755 "${ROOTDIR}/usr/share/applications" + cp "${ROOTDIR}/assistant-qt5.desktop" "${ROOTDIR}/usr/share/applications/" + cp "${ROOTDIR}/designer-qt5.desktop" "${ROOTDIR}/usr/share/applications/" + cp "${ROOTDIR}/linguist-qt5.desktop" "${ROOTDIR}/usr/share/applications/" + cp "${ROOTDIR}/qdbusviewer-qt5.desktop" "${ROOTDIR}/usr/share/applications/" for file in moc uic rcc qmake lconvert lrelease lupdate; do - ln -sfrvn "$ROOTDIR/usr/bin/$file" "$ROOTDIR/usr/bin/$file-qt5" + ln -sfrvn "${ROOTDIR}/usr/bin/$file" "${ROOTDIR}/usr/bin/${file}-qt5" done - cd .. && rm -r qt-everywhere-src-$VERSION + cd .. && rm -r "qt-everywhere-src-${VERSION}" } diff --git a/src/qt5/qt-everywhere-opensource-src-5.15.12-kf5-1.patch b/src/qt5/qt-everywhere-opensource-src-5.15.12-kf5-1.patch new file mode 100644 index 0000000..24abbe3 --- /dev/null +++ b/src/qt5/qt-everywhere-opensource-src-5.15.12-kf5-1.patch @@ -0,0 +1,13760 @@ +Submodule qtbase e4391422..8907dedc: +diff --git a/qtbase/mkspecs/common/android/qplatformdefs.h b/qtbase/mkspecs/common/android/qplatformdefs.h +index f75bc4093b..2bd59410d4 100644 +--- a/qtbase/mkspecs/common/android/qplatformdefs.h ++++ b/qtbase/mkspecs/common/android/qplatformdefs.h +@@ -144,11 +144,7 @@ + #define QT_SIGNAL_ARGS int + #define QT_SIGNAL_IGNORE SIG_IGN + +-#if defined(__GLIBC__) && (__GLIBC__ >= 2) + #define QT_SOCKLEN_T socklen_t +-#else +-#define QT_SOCKLEN_T int +-#endif + + #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) + #define QT_SNPRINTF ::snprintf +diff --git a/qtbase/mkspecs/linux-clang/qplatformdefs.h b/qtbase/mkspecs/linux-clang/qplatformdefs.h +index a818d973f0..c1ab72fbc6 100644 +--- a/qtbase/mkspecs/linux-clang/qplatformdefs.h ++++ b/qtbase/mkspecs/linux-clang/qplatformdefs.h +@@ -79,14 +79,6 @@ + #define QT_USE_XOPEN_LFS_EXTENSIONS + #include "../common/posix/qplatformdefs.h" + +-#undef QT_SOCKLEN_T +- +-#if defined(__GLIBC__) && (__GLIBC__ >= 2) +-#define QT_SOCKLEN_T socklen_t +-#else +-#define QT_SOCKLEN_T int +-#endif +- + #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) + #define QT_SNPRINTF ::snprintf + #define QT_VSNPRINTF ::vsnprintf +diff --git a/qtbase/mkspecs/linux-g++/qplatformdefs.h b/qtbase/mkspecs/linux-g++/qplatformdefs.h +index 13523f0702..4d2750d9ec 100644 +--- a/qtbase/mkspecs/linux-g++/qplatformdefs.h ++++ b/qtbase/mkspecs/linux-g++/qplatformdefs.h +@@ -79,14 +79,6 @@ + #define QT_USE_XOPEN_LFS_EXTENSIONS + #include "../common/posix/qplatformdefs.h" + +-#undef QT_SOCKLEN_T +- +-#if defined(__GLIBC__) && (__GLIBC__ < 2) +-#define QT_SOCKLEN_T int +-#else +-#define QT_SOCKLEN_T socklen_t +-#endif +- + #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) + #define QT_SNPRINTF ::snprintf + #define QT_VSNPRINTF ::vsnprintf +diff --git a/qtbase/mkspecs/linux-llvm/qplatformdefs.h b/qtbase/mkspecs/linux-llvm/qplatformdefs.h +index dc750ab1ef..d3cc39b47f 100644 +--- a/qtbase/mkspecs/linux-llvm/qplatformdefs.h ++++ b/qtbase/mkspecs/linux-llvm/qplatformdefs.h +@@ -80,14 +80,6 @@ + #define QT_USE_XOPEN_LFS_EXTENSIONS + #include "../common/posix/qplatformdefs.h" + +-#undef QT_SOCKLEN_T +- +-#if defined(__GLIBC__) && (__GLIBC__ >= 2) +-#define QT_SOCKLEN_T socklen_t +-#else +-#define QT_SOCKLEN_T int +-#endif +- + #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) + #define QT_SNPRINTF ::snprintf + #define QT_VSNPRINTF ::vsnprintf +diff --git a/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h b/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h +index 4c4e53da2a..83baffb3e3 100644 +--- a/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h ++++ b/qtbase/mkspecs/linux-lsb-g++/qplatformdefs.h +@@ -85,16 +85,9 @@ + #include "../common/posix/qplatformdefs.h" + + #undef QT_OPEN_LARGEFILE +-#undef QT_SOCKLEN_T + + #define QT_OPEN_LARGEFILE 0 + +-#if defined(__GLIBC__) && (__GLIBC__ >= 2) +-#define QT_SOCKLEN_T socklen_t +-#else +-#define QT_SOCKLEN_T int +-#endif +- + #ifndef SIOCGIFBRDADDR + # define SIOCGIFBRDADDR 0x8919 + #endif +diff --git a/qtbase/mkspecs/lynxos-g++/qplatformdefs.h b/qtbase/mkspecs/lynxos-g++/qplatformdefs.h +index 4339ea2b23..6007af0055 100644 +--- a/qtbase/mkspecs/lynxos-g++/qplatformdefs.h ++++ b/qtbase/mkspecs/lynxos-g++/qplatformdefs.h +@@ -72,14 +72,6 @@ + + #include "../common/posix/qplatformdefs.h" + +-#undef QT_SOCKLEN_T +- +-#if defined(__GLIBC__) && (__GLIBC__ >= 2) +-#define QT_SOCKLEN_T socklen_t +-#else +-#define QT_SOCKLEN_T int +-#endif +- + #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) + #define QT_SNPRINTF ::snprintf + #define QT_VSNPRINTF ::vsnprintf +diff --git a/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp b/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp +index c3c184258f..32af3f8f29 100644 +--- a/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp ++++ b/qtbase/src/3rdparty/angle/src/libANGLE/HandleAllocator.cpp +@@ -10,6 +10,7 @@ + #include "libANGLE/HandleAllocator.h" + + #include ++#include + + #include "common/debug.h" + +diff --git a/qtbase/src/3rdparty/forkfd/forkfd_linux.c b/qtbase/src/3rdparty/forkfd/forkfd_linux.c +index ffe0e9a5e2..b1f5408d11 100644 +--- a/qtbase/src/3rdparty/forkfd/forkfd_linux.c ++++ b/qtbase/src/3rdparty/forkfd/forkfd_linux.c +@@ -82,7 +82,8 @@ static int sys_clone(unsigned long cloneflags, int *ptid) + return syscall(__NR_clone, cloneflags, child_stack, stack_size, ptid, newtls, ctid); + #elif defined(__arc__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ + defined(__nds32__) || defined(__hppa__) || defined(__powerpc__) || defined(__i386__) || \ +- defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv) ++ defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv) || \ ++ defined(__loongarch__) + /* ctid and newtls are inverted on CONFIG_CLONE_BACKWARDS architectures, + * but since both values are 0, there's no harm. */ + return syscall(__NR_clone, cloneflags, child_stack, ptid, ctid, newtls); +diff --git a/qtbase/src/corelib/global/qglobal.cpp b/qtbase/src/corelib/global/qglobal.cpp +index 5ad82c259d..ecf7b1efaa 100644 +--- a/qtbase/src/corelib/global/qglobal.cpp ++++ b/qtbase/src/corelib/global/qglobal.cpp +@@ -97,6 +97,10 @@ + # include + #endif + ++#if defined(Q_OS_MACOS) ++#include ++#endif ++ + #ifdef Q_OS_UNIX + #include + #include +@@ -2133,6 +2137,15 @@ QT_WARNING_POP + static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) + { + #ifdef Q_OS_MACOS ++ if (version.majorVersion() == 13) ++ return "Ventura"; ++ if (version.majorVersion() == 12) ++ return "Monterey"; ++ // Compare against predefined constant to handle 10.16/11.0 ++ if (QVersionNumber(QOperatingSystemVersion::MacOSBigSur.majorVersion(), ++ QOperatingSystemVersion::MacOSBigSur.minorVersion(), QOperatingSystemVersion::MacOSBigSur.microVersion()).isPrefixOf( ++ QVersionNumber(version.majorVersion(), version.minorVersion(), version.microVersion()))) ++ return "Big Sur"; + if (version.majorVersion() == 10) { + switch (version.minorVersion()) { + case 9: +@@ -2147,13 +2160,15 @@ static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSyst + return "High Sierra"; + case 14: + return "Mojave"; ++ case 15: ++ return "Catalina"; + } + } + // unknown, future version + #else + Q_UNUSED(version); + #endif +- return 0; ++ return nullptr; + } + #endif + +@@ -2278,7 +2293,7 @@ static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSyst + } + #undef Q_WINVER + // unknown, future version +- return 0; ++ return nullptr; + } + + #endif +diff --git a/qtbase/src/corelib/global/qglobal.h b/qtbase/src/corelib/global/qglobal.h +index 450c1e586a..ff7167d9cc 100644 +--- a/qtbase/src/corelib/global/qglobal.h ++++ b/qtbase/src/corelib/global/qglobal.h +@@ -307,6 +307,8 @@ typedef double qreal; + # define QT_DEPRECATED_CONSTRUCTOR + # undef Q_DECL_ENUMERATOR_DEPRECATED + # define Q_DECL_ENUMERATOR_DEPRECATED ++# undef Q_DECL_ENUMERATOR_DEPRECATED_X ++# define Q_DECL_ENUMERATOR_DEPRECATED_X(ignored) + #endif + + #ifndef QT_DEPRECATED_WARNINGS_SINCE +diff --git a/qtbase/src/corelib/global/qlogging.cpp b/qtbase/src/corelib/global/qlogging.cpp +index 292116cc47..0f253d4a27 100644 +--- a/qtbase/src/corelib/global/qlogging.cpp ++++ b/qtbase/src/corelib/global/qlogging.cpp +@@ -189,6 +189,17 @@ static int checked_var_value(const char *varname) + return ok ? value : 1; + } + ++static bool is_fatal_count_down(QAtomicInt &n) ++{ ++ // it's fatal if the current value is exactly 1, ++ // otherwise decrement if it's non-zero ++ ++ int v = n.loadRelaxed(); ++ while (v != 0 && !n.testAndSetRelaxed(v, v - 1, v)) ++ ; ++ return v == 1; // we exited the loop, so either v == 0 or CAS succeeded to set n from v to v-1 ++} ++ + static bool isFatal(QtMsgType msgType) + { + if (msgType == QtFatalMsg) +@@ -196,18 +207,12 @@ static bool isFatal(QtMsgType msgType) + + if (msgType == QtCriticalMsg) { + static QAtomicInt fatalCriticals = checked_var_value("QT_FATAL_CRITICALS"); +- +- // it's fatal if the current value is exactly 1, +- // otherwise decrement if it's non-zero +- return fatalCriticals.loadRelaxed() && fatalCriticals.fetchAndAddRelaxed(-1) == 1; ++ return is_fatal_count_down(fatalCriticals); + } + + if (msgType == QtWarningMsg || msgType == QtCriticalMsg) { + static QAtomicInt fatalWarnings = checked_var_value("QT_FATAL_WARNINGS"); +- +- // it's fatal if the current value is exactly 1, +- // otherwise decrement if it's non-zero +- return fatalWarnings.loadRelaxed() && fatalWarnings.fetchAndAddRelaxed(-1) == 1; ++ return is_fatal_count_down(fatalWarnings); + } + + return false; +diff --git a/qtbase/src/corelib/global/qnamespace.h b/qtbase/src/corelib/global/qnamespace.h +index deab11f729..486b63fa3f 100644 +--- a/qtbase/src/corelib/global/qnamespace.h ++++ b/qtbase/src/corelib/global/qnamespace.h +@@ -1867,7 +1867,7 @@ public: + QT_Q_ENUM(TimerType) + QT_Q_ENUM(ScrollPhase) + QT_Q_ENUM(MouseEventSource) +- QT_Q_FLAG(MouseEventFlag) ++ QT_Q_FLAG(MouseEventFlags) + QT_Q_ENUM(ChecksumType) + QT_Q_ENUM(HighDpiScaleFactorRoundingPolicy) + QT_Q_ENUM(TabFocusBehavior) +diff --git a/qtbase/src/corelib/io/qfilesystemengine_win.cpp b/qtbase/src/corelib/io/qfilesystemengine_win.cpp +index 002f720926..e6f118a5c4 100644 +--- a/qtbase/src/corelib/io/qfilesystemengine_win.cpp ++++ b/qtbase/src/corelib/io/qfilesystemengine_win.cpp +@@ -664,14 +664,14 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) + return QFileSystemEntry(ret, QFileSystemEntry::FromInternalPath()); + } + +-#if defined(Q_CC_MINGW) && WINVER < 0x0602 // Windows 8 onwards ++#if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8 // Windows 8 onwards + + typedef struct _FILE_ID_INFO { + ULONGLONG VolumeSerialNumber; + FILE_ID_128 FileId; + } FILE_ID_INFO, *PFILE_ID_INFO; + +-#endif // if defined (Q_CC_MINGW) && WINVER < 0x0602 ++#endif // if defined(Q_CC_MINGW) && WINVER < 0x0602 && _WIN32_WINNT < _WIN32_WINNT_WIN8 + + // File ID for Windows up to version 7 and FAT32 drives + static inline QByteArray fileId(HANDLE handle) +diff --git a/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp b/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp +index 94d9d06bcb..27e0b13b0b 100644 +--- a/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp ++++ b/qtbase/src/corelib/io/qfilesystemwatcher_inotify.cpp +@@ -366,7 +366,9 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() + // qDebug("QInotifyFileSystemWatcherEngine::readFromInotify"); + + int buffSize = 0; +- ioctl(inotifyFd, FIONREAD, (char *) &buffSize); ++ if (ioctl(inotifyFd, FIONREAD, (char *) &buffSize) == -1 || buffSize == 0) ++ return; ++ + QVarLengthArray buffer(buffSize); + buffSize = read(inotifyFd, buffer.data(), buffSize); + char *at = buffer.data(); +diff --git a/qtbase/src/corelib/io/qfsfileengine.cpp b/qtbase/src/corelib/io/qfsfileengine.cpp +index 3042eac2f0..a862ce9166 100644 +--- a/qtbase/src/corelib/io/qfsfileengine.cpp ++++ b/qtbase/src/corelib/io/qfsfileengine.cpp +@@ -361,7 +361,7 @@ bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd) + + // Seek to the end when in Append mode. + if (openMode & QFile::Append) { +- int ret; ++ QT_OFF_T ret; + do { + ret = QT_LSEEK(fd, 0, SEEK_END); + } while (ret == -1 && errno == EINTR); +diff --git a/qtbase/src/corelib/io/qfsfileengine_unix.cpp b/qtbase/src/corelib/io/qfsfileengine_unix.cpp +index 4610e9306c..65e921c15a 100644 +--- a/qtbase/src/corelib/io/qfsfileengine_unix.cpp ++++ b/qtbase/src/corelib/io/qfsfileengine_unix.cpp +@@ -141,7 +141,7 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) + + // Seek to the end when in Append mode. + if (flags & QFile::Append) { +- int ret; ++ QT_OFF_T ret; + do { + ret = QT_LSEEK(fd, 0, SEEK_END); + } while (ret == -1 && errno == EINTR); +diff --git a/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp b/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp +index 997a634e76..1c1ca7394d 100644 +--- a/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp ++++ b/qtbase/src/corelib/itemmodels/qabstractitemmodel.cpp +@@ -52,6 +52,8 @@ + #include + #include + ++#include ++ + #include + + QT_BEGIN_NAMESPACE +@@ -213,7 +215,7 @@ bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other) const + if (d && other.d) + return d->index < other.d->index; + +- return d < other.d; ++ return std::less<>{}(d, other.d); + } + + /*! +diff --git a/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp +index 3d7fe43cd3..f2871a2da7 100644 +--- a/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp ++++ b/qtbase/src/corelib/itemmodels/qsortfilterproxymodel.cpp +@@ -939,8 +939,9 @@ void QSortFilterProxyModelPrivate::insert_source_items( + q->beginInsertColumns(proxy_parent, proxy_start, proxy_end); + } + +- for (int i = 0; i < source_items.size(); ++i) +- proxy_to_source.insert(proxy_start + i, source_items.at(i)); ++ // TODO: use the range QList::insert() overload once it is implemented (QTBUG-58633). ++ proxy_to_source.insert(proxy_start, source_items.size(), 0); ++ std::copy(source_items.cbegin(), source_items.cend(), proxy_to_source.begin() + proxy_start); + + build_source_to_proxy_mapping(proxy_to_source, source_to_proxy); + +@@ -3131,8 +3132,9 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & + + if (d->filter_data.isEmpty()) + return true; ++ ++ int column_count = d->model->columnCount(source_parent); + if (d->filter_column == -1) { +- int column_count = d->model->columnCount(source_parent); + for (int column = 0; column < column_count; ++column) { + QModelIndex source_index = d->model->index(source_row, column, source_parent); + QString key = d->model->data(source_index, d->filter_role).toString(); +@@ -3141,9 +3143,10 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & + } + return false; + } +- QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); +- if (!source_index.isValid()) // the column may not exist ++ ++ if (d->filter_column >= column_count) // the column may not exist + return true; ++ QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent); + QString key = d->model->data(source_index, d->filter_role).toString(); + return d->filter_data.hasMatch(key); + } +diff --git a/qtbase/src/corelib/kernel/qtranslator.cpp b/qtbase/src/corelib/kernel/qtranslator.cpp +index bdcd016630..e4375e7e40 100644 +--- a/qtbase/src/corelib/kernel/qtranslator.cpp ++++ b/qtbase/src/corelib/kernel/qtranslator.cpp +@@ -907,7 +907,7 @@ static QString getMessage(const uchar *m, const uchar *end, const char *context, + goto end; + case Tag_Translation: { + int len = read32(m); +- if (len % 1) ++ if (len & 1) + return QString(); + m += 4; + if (!numerus--) { +diff --git a/qtbase/src/corelib/mimetypes/qmimedatabase.cpp b/qtbase/src/corelib/mimetypes/qmimedatabase.cpp +index 9de22cef33..34591fc667 100644 +--- a/qtbase/src/corelib/mimetypes/qmimedatabase.cpp ++++ b/qtbase/src/corelib/mimetypes/qmimedatabase.cpp +@@ -46,6 +46,9 @@ + #include "qmimeprovider_p.h" + #include "qmimetype_p.h" + ++#include ++#include ++ + #include + #include + #include +@@ -389,20 +392,23 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa + // Disambiguate conflicting extensions (if magic matching found something) + if (candidateByData.isValid() && magicAccuracy > 0) { + const QString sniffedMime = candidateByData.name(); +- // If the sniffedMime matches a glob match, use it ++ // If the sniffedMime matches a highest-weight glob match, use it + if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime)) { + *accuracyPtr = 100; + return candidateByData; + } +- for (const QString &m : qAsConst(candidatesByName.m_matchingMimeTypes)) { ++ for (const QString &m : qAsConst(candidatesByName.m_allMatchingMimeTypes)) { + if (inherits(m, sniffedMime)) { + // We have magic + pattern pointing to this, so it's a pretty good match + *accuracyPtr = 100; + return mimeTypeForName(m); + } + } +- *accuracyPtr = magicAccuracy; +- return candidateByData; ++ if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) { ++ // No glob, use magic ++ *accuracyPtr = magicAccuracy; ++ return candidateByData; ++ } + } + } + +@@ -428,6 +434,7 @@ QList QMimeDatabasePrivate::allMimeTypes() + bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) + { + const QString resolvedParent = resolveAlias(parent); ++ QDuplicateTracker seen; + std::stack toCheck; + toCheck.push(mime); + while (!toCheck.empty()) { +@@ -436,8 +443,11 @@ bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) + const QString mimeName = toCheck.top(); + toCheck.pop(); + const auto parentList = parents(mimeName); +- for (const QString &par : parentList) +- toCheck.push(resolveAlias(par)); ++ for (const QString &par : parentList) { ++ const QString resolvedPar = resolveAlias(par); ++ if (!seen.hasSeen(resolvedPar)) ++ toCheck.push(resolvedPar); ++ } + } + return false; + } +diff --git a/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp b/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp +index b1de8907b2..fa8f4c545d 100644 +--- a/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp ++++ b/qtbase/src/corelib/mimetypes/qmimeglobpattern.cpp +@@ -83,7 +83,10 @@ void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const Q + } + if (!m_matchingMimeTypes.contains(mimeType)) { + m_matchingMimeTypes.append(mimeType); +- m_allMatchingMimeTypes.append(mimeType); ++ if (replace) ++ m_allMatchingMimeTypes.prepend(mimeType); // highest-weight first ++ else ++ m_allMatchingMimeTypes.append(mimeType); + m_knownSuffixLength = knownSuffixLength; + } + } +diff --git a/qtbase/src/corelib/mimetypes/qmimeprovider.cpp b/qtbase/src/corelib/mimetypes/qmimeprovider.cpp +index 258dddf8cb..5125704cf1 100644 +--- a/qtbase/src/corelib/mimetypes/qmimeprovider.cpp ++++ b/qtbase/src/corelib/mimetypes/qmimeprovider.cpp +@@ -242,21 +242,28 @@ void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobM + return; + Q_ASSERT(m_cacheFile); + const QString lowerFileName = fileName.toLower(); ++ int numMatches = 0; + // Check literals (e.g. "Makefile") +- matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), fileName); +- // Check complex globs (e.g. "callgrind.out[0-9]*") +- matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); ++ numMatches = matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), fileName); + // Check the very common *.txt cases with the suffix tree +- const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); +- const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); +- const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); +- matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); +- if (result.m_matchingMimeTypes.isEmpty()) +- matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); ++ if (numMatches == 0) { ++ const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); ++ const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); ++ const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); ++ if (matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false)) { ++ ++numMatches; ++ } else if (matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true)) { ++ ++numMatches; ++ } ++ } ++ // Check complex globs (e.g. "callgrind.out[0-9]*" or "README*") ++ if (numMatches == 0) ++ matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); + } + +-void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) ++int QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) + { ++ int numMatches = 0; + const int numGlobs = cacheFile->getUint32(off); + //qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset; + for (int i = 0; i < numGlobs; ++i) { +@@ -272,9 +279,12 @@ void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile + //qDebug() << pattern << mimeType << weight << caseSensitive; + QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive); + +- if (glob.matchFileName(fileName)) ++ if (glob.matchFileName(fileName)) { + result.addMatch(QLatin1String(mimeType), weight, pattern); ++ ++numMatches; ++ } + } ++ return numMatches; + } + + bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result, QMimeBinaryProvider::CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck) +diff --git a/qtbase/src/corelib/mimetypes/qmimeprovider_p.h b/qtbase/src/corelib/mimetypes/qmimeprovider_p.h +index f9c8ef384c..5b328a7d5e 100644 +--- a/qtbase/src/corelib/mimetypes/qmimeprovider_p.h ++++ b/qtbase/src/corelib/mimetypes/qmimeprovider_p.h +@@ -115,7 +115,7 @@ public: + private: + struct CacheFile; + +- void matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName); ++ int matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName); + bool matchSuffixTree(QMimeGlobMatchResult &result, CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck); + bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data); + QLatin1String iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime); +diff --git a/qtbase/src/corelib/mimetypes/qmimetype.cpp b/qtbase/src/corelib/mimetypes/qmimetype.cpp +index 0c0de63961..d6a351262b 100644 +--- a/qtbase/src/corelib/mimetypes/qmimetype.cpp ++++ b/qtbase/src/corelib/mimetypes/qmimetype.cpp +@@ -376,14 +376,17 @@ QStringList QMimeType::parentMimeTypes() const + static void collectParentMimeTypes(const QString &mime, QStringList &allParents) + { + const QStringList parents = QMimeDatabasePrivate::instance()->mimeParents(mime); ++ QStringList newParents; + for (const QString &parent : parents) { + // I would use QSet, but since order matters I better not +- if (!allParents.contains(parent)) ++ if (!allParents.contains(parent)) { + allParents.append(parent); ++ newParents.append(parent); ++ } + } + // We want a breadth-first search, so that the least-specific parent (octet-stream) is last + // This means iterating twice, unfortunately. +- for (const QString &parent : parents) ++ for (const QString &parent : newParents) + collectParentMimeTypes(parent, allParents); + } + +diff --git a/qtbase/src/corelib/plugin/qlibrary.cpp b/qtbase/src/corelib/plugin/qlibrary.cpp +index 5d2f024267..45b5a3fe27 100644 +--- a/qtbase/src/corelib/plugin/qlibrary.cpp ++++ b/qtbase/src/corelib/plugin/qlibrary.cpp +@@ -526,7 +526,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) + if (pHnd.loadRelaxed()) + return; + +- loadHintsInt.storeRelaxed(lh); ++ loadHintsInt.fetchAndOrRelaxed(lh); + } + + QFunctionPointer QLibraryPrivate::resolve(const char *symbol) +@@ -538,6 +538,13 @@ QFunctionPointer QLibraryPrivate::resolve(const char *symbol) + + void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh) + { ++ // Set the load hints directly for a dummy if this object is not associated ++ // with a file. Such object is not shared between multiple instances. ++ if (fileName.isEmpty()) { ++ loadHintsInt.storeRelaxed(lh); ++ return; ++ } ++ + // this locks a global mutex + QMutexLocker lock(&qt_library_mutex); + mergeLoadHints(lh); +@@ -1166,6 +1173,10 @@ QString QLibrary::errorString() const + lazy symbol resolution, and will not export external symbols for resolution + in other dynamically-loaded libraries. + ++ \note Hints can only be cleared when this object is not associated with a ++ file. Hints can only be added once the file name is set (\a hints will ++ be or'ed with the old hints). ++ + \note Setting this property after the library has been loaded has no effect + and loadHints() will not reflect those changes. + +diff --git a/qtbase/src/corelib/plugin/qpluginloader.cpp b/qtbase/src/corelib/plugin/qpluginloader.cpp +index 0a63b93762..02b8c588be 100644 +--- a/qtbase/src/corelib/plugin/qpluginloader.cpp ++++ b/qtbase/src/corelib/plugin/qpluginloader.cpp +@@ -105,6 +105,8 @@ QT_BEGIN_NAMESPACE + \sa QLibrary, {Plug & Paint Example} + */ + ++static constexpr QLibrary::LoadHints defaultLoadHints = QLibrary::PreventUnloadHint; ++ + /*! + \class QStaticPlugin + \inmodule QtCore +@@ -155,7 +157,7 @@ QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent) + : QObject(parent), d(nullptr), did_load(false) + { + setFileName(fileName); +- setLoadHints(QLibrary::PreventUnloadHint); ++ setLoadHints(defaultLoadHints); + } + + /*! +@@ -357,7 +359,7 @@ static QString locatePlugin(const QString& fileName) + void QPluginLoader::setFileName(const QString &fileName) + { + #if defined(QT_SHARED) +- QLibrary::LoadHints lh = QLibrary::PreventUnloadHint; ++ QLibrary::LoadHints lh = defaultLoadHints; + if (d) { + lh = d->loadHints(); + d->release(); +@@ -414,15 +416,21 @@ QString QPluginLoader::errorString() const + void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints) + { + if (!d) { +- d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr ++ d = QLibraryPrivate::findOrCreate({}, {}, loadHints); // ugly, but we need a d-ptr + d->errorString.clear(); ++ } else { ++ d->setLoadHints(loadHints); + } +- d->setLoadHints(loadHints); + } + + QLibrary::LoadHints QPluginLoader::loadHints() const + { +- return d ? d->loadHints() : QLibrary::LoadHints(); ++ // Not having a d-pointer means that the user hasn't called ++ // setLoadHints() / setFileName() yet. In setFileName() we will ++ // then force defaultLoadHints on loading, so we must return them ++ // from here as well. ++ ++ return d ? d->loadHints() : defaultLoadHints; + } + + #endif // QT_CONFIG(library) +diff --git a/qtbase/src/corelib/serialization/qcborvalue.cpp b/qtbase/src/corelib/serialization/qcborvalue.cpp +index 89a928d348..3a8c2cb9ec 100644 +--- a/qtbase/src/corelib/serialization/qcborvalue.cpp ++++ b/qtbase/src/corelib/serialization/qcborvalue.cpp +@@ -2123,7 +2123,8 @@ QCborArray QCborValue::toArray(const QCborArray &defaultValue) const + Q_ASSERT(n == -1 || container == nullptr); + if (n < 0) + dd = container; +- return dd ? QCborArray(*dd) : defaultValue; ++ // return QCborArray(*dd); but that's UB if dd is nullptr ++ return dd ? QCborArray(*dd) : QCborArray(); + } + + /*! +@@ -2165,7 +2166,8 @@ QCborMap QCborValue::toMap(const QCborMap &defaultValue) const + Q_ASSERT(n == -1 || container == nullptr); + if (n < 0) + dd = container; +- return dd ? QCborMap(*dd) : defaultValue; ++ // return QCborMap(*dd); but that's UB if dd is nullptr ++ return dd ? QCborMap(*dd) : QCborMap(); + } + + /*! +diff --git a/qtbase/src/corelib/serialization/qxmlstream.cpp b/qtbase/src/corelib/serialization/qxmlstream.cpp +index 0ac5548178..0d49cbac19 100644 +--- a/qtbase/src/corelib/serialization/qxmlstream.cpp ++++ b/qtbase/src/corelib/serialization/qxmlstream.cpp +@@ -160,7 +160,7 @@ enum { StreamEOF = ~0U }; + addData() or by waiting for it to arrive on the device(). + + \value UnexpectedElementError The parser encountered an element +- that was different to those it expected. ++ or token that was different to those it expected. + + */ + +@@ -295,13 +295,34 @@ QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const + + QXmlStreamReader is a well-formed XML 1.0 parser that does \e not + include external parsed entities. As long as no error occurs, the +- application code can thus be assured that the data provided by the +- stream reader satisfies the W3C's criteria for well-formed XML. For +- example, you can be certain that all tags are indeed nested and +- closed properly, that references to internal entities have been +- replaced with the correct replacement text, and that attributes have +- been normalized or added according to the internal subset of the +- DTD. ++ application code can thus be assured, that ++ \list ++ \li the data provided by the stream reader satisfies the W3C's ++ criteria for well-formed XML, ++ \li tokens are provided in a valid order. ++ \endlist ++ ++ Unless QXmlStreamReader raises an error, it guarantees the following: ++ \list ++ \li All tags are nested and closed properly. ++ \li References to internal entities have been replaced with the ++ correct replacement text. ++ \li Attributes have been normalized or added according to the ++ internal subset of the \l DTD. ++ \li Tokens of type \l StartDocument happen before all others, ++ aside from comments and processing instructions. ++ \li At most one DOCTYPE element (a token of type \l DTD) is present. ++ \li If present, the DOCTYPE appears before all other elements, ++ aside from StartDocument, comments and processing instructions. ++ \endlist ++ ++ In particular, once any token of type \l StartElement, \l EndElement, ++ \l Characters, \l EntityReference or \l EndDocument is seen, no ++ tokens of type StartDocument or DTD will be seen. If one is present in ++ the input stream, out of order, an error is raised. ++ ++ \note The token types \l Comment and \l ProcessingInstruction may appear ++ anywhere in the stream. + + If an error occurs while parsing, atEnd() and hasError() return + true, and error() returns the error that occurred. The functions +@@ -620,6 +641,7 @@ QXmlStreamReader::TokenType QXmlStreamReader::readNext() + d->token = -1; + return readNext(); + } ++ d->checkToken(); + return d->type; + } + +@@ -740,6 +762,14 @@ static const short QXmlStreamReader_tokenTypeString_indices[] = { + }; + + ++static const char QXmlStreamReader_XmlContextString[] = ++ "Prolog\0" ++ "Body\0"; ++ ++static const short QXmlStreamReader_XmlContextString_indices[] = { ++ 0, 7 ++}; ++ + /*! + \property QXmlStreamReader::namespaceProcessing + The namespace-processing flag of the stream reader +@@ -775,6 +805,16 @@ QString QXmlStreamReader::tokenString() const + QXmlStreamReader_tokenTypeString_indices[d->type]); + } + ++/*! ++ \internal ++ \return \param ctxt (Prolog/Body) as a string. ++ */ ++QString contextString(QXmlStreamReaderPrivate::XmlContext ctxt) ++{ ++ return QLatin1String(QXmlStreamReader_XmlContextString + ++ QXmlStreamReader_XmlContextString_indices[static_cast(ctxt)]); ++} ++ + #endif // QT_NO_XMLSTREAMREADER + + QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack() +@@ -866,6 +906,8 @@ void QXmlStreamReaderPrivate::init() + + type = QXmlStreamReader::NoToken; + error = QXmlStreamReader::NoError; ++ currentContext = XmlContext::Prolog; ++ foundDTD = false; + } + + /* +@@ -1302,15 +1344,18 @@ inline int QXmlStreamReaderPrivate::fastScanContentCharList() + return n; + } + +-inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) ++// Fast scan an XML attribute name (e.g. "xml:lang"). ++inline QXmlStreamReaderPrivate::FastScanNameResult ++QXmlStreamReaderPrivate::fastScanName(Value *val) + { + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + if (n >= 4096) { + // This is too long to be a sensible name, and +- // can exhaust memory +- return 0; ++ // can exhaust memory, or the range of decltype(*prefix) ++ raiseNamePrefixTooLongError(); ++ return {}; + } + switch (c) { + case '\n': +@@ -1339,23 +1384,23 @@ inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) + case '+': + case '*': + putChar(c); +- if (prefix && *prefix == n+1) { +- *prefix = 0; ++ if (val && val->prefix == n + 1) { ++ val->prefix = 0; + putChar(':'); + --n; + } +- return n; ++ return FastScanNameResult(n); + case ':': +- if (prefix) { +- if (*prefix == 0) { +- *prefix = n+2; ++ if (val) { ++ if (val->prefix == 0) { ++ val->prefix = n + 2; + } else { // only one colon allowed according to the namespace spec. + putChar(c); +- return n; ++ return FastScanNameResult(n); + } + } else { + putChar(c); +- return n; ++ return FastScanNameResult(n); + } + Q_FALLTHROUGH(); + default: +@@ -1364,12 +1409,12 @@ inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) + } + } + +- if (prefix) +- *prefix = 0; ++ if (val) ++ val->prefix = 0; + int pos = textBuffer.size() - n; + putString(textBuffer, pos); + textBuffer.resize(pos); +- return 0; ++ return FastScanNameResult(0); + } + + enum NameChar { NameBeginning, NameNotBeginning, NotName }; +@@ -1878,6 +1923,14 @@ void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message) + raiseError(QXmlStreamReader::NotWellFormedError, message); + } + ++void QXmlStreamReaderPrivate::raiseNamePrefixTooLongError() ++{ ++ // TODO: add a ImplementationLimitsExceededError and use it instead ++ raiseError(QXmlStreamReader::NotWellFormedError, ++ QXmlStream::tr("Length of XML attribute name exceeds implementation limits (4KiB " ++ "characters).")); ++} ++ + void QXmlStreamReaderPrivate::parseError() + { + +@@ -4050,6 +4103,92 @@ void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader) + } + } + ++static bool isTokenAllowedInContext(QXmlStreamReader::TokenType type, ++ QXmlStreamReaderPrivate::XmlContext loc) ++{ ++ switch (type) { ++ case QXmlStreamReader::StartDocument: ++ case QXmlStreamReader::DTD: ++ return loc == QXmlStreamReaderPrivate::XmlContext::Prolog; ++ ++ case QXmlStreamReader::StartElement: ++ case QXmlStreamReader::EndElement: ++ case QXmlStreamReader::Characters: ++ case QXmlStreamReader::EntityReference: ++ case QXmlStreamReader::EndDocument: ++ return loc == QXmlStreamReaderPrivate::XmlContext::Body; ++ ++ case QXmlStreamReader::Comment: ++ case QXmlStreamReader::ProcessingInstruction: ++ return true; ++ ++ case QXmlStreamReader::NoToken: ++ case QXmlStreamReader::Invalid: ++ return false; ++ default: ++ return false; ++ } ++} ++ ++/*! ++ \internal ++ \brief QXmlStreamReader::isValidToken ++ \return \c true if \param type is a valid token type. ++ \return \c false if \param type is an unexpected token, ++ which indicates a non-well-formed or invalid XML stream. ++ */ ++bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type) ++{ ++ // Don't change currentContext, if Invalid or NoToken occur in the prolog ++ if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken) ++ return false; ++ ++ // If a token type gets rejected in the body, there is no recovery ++ const bool result = isTokenAllowedInContext(type, currentContext); ++ if (result || currentContext == XmlContext::Body) ++ return result; ++ ++ // First non-Prolog token observed => switch context to body and check again. ++ currentContext = XmlContext::Body; ++ return isTokenAllowedInContext(type, currentContext); ++} ++ ++/*! ++ \internal ++ Checks token type and raises an error, if it is invalid ++ in the current context (prolog/body). ++ */ ++void QXmlStreamReaderPrivate::checkToken() ++{ ++ Q_Q(QXmlStreamReader); ++ ++ // The token type must be consumed, to keep track if the body has been reached. ++ const XmlContext context = currentContext; ++ const bool ok = isValidToken(type); ++ ++ // Do nothing if an error has been raised already (going along with an unexpected token) ++ if (error != QXmlStreamReader::Error::NoError) ++ return; ++ ++ if (!ok) { ++ raiseError(QXmlStreamReader::UnexpectedElementError, ++ QLatin1String("Unexpected token type %1 in %2.") ++ .arg(q->tokenString(), contextString(context))); ++ return; ++ } ++ ++ if (type != QXmlStreamReader::DTD) ++ return; ++ ++ // Raise error on multiple DTD tokens ++ if (foundDTD) { ++ raiseError(QXmlStreamReader::UnexpectedElementError, ++ QLatin1String("Found second DTD token in %1.").arg(contextString(context))); ++ } else { ++ foundDTD = true; ++ } ++} ++ + /*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &qualifiedName) const + \since 4.5 +diff --git a/qtbase/src/corelib/serialization/qxmlstream.g b/qtbase/src/corelib/serialization/qxmlstream.g +index 4321fed68a..8c6a1a5887 100644 +--- a/qtbase/src/corelib/serialization/qxmlstream.g ++++ b/qtbase/src/corelib/serialization/qxmlstream.g +@@ -516,7 +516,16 @@ public: + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); +- int fastScanName(int *prefix = nullptr); ++ ++ struct FastScanNameResult { ++ FastScanNameResult() : ok(false) {} ++ explicit FastScanNameResult(int len) : addToLen(len), ok(true) { } ++ operator bool() { return ok; } ++ int operator*() { Q_ASSERT(ok); return addToLen; } ++ int addToLen; ++ bool ok; ++ }; ++ FastScanNameResult fastScanName(Value *val = nullptr); + inline int fastScanNMTOKEN(); + + +@@ -525,6 +534,7 @@ public: + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); ++ void raiseNamePrefixTooLongError(); + + QXmlStreamEntityResolver *entityResolver; + +@@ -1811,7 +1821,12 @@ space_opt ::= space; + qname ::= LETTER; + /. + case $rule_number: { +- sym(1).len += fastScanName(&sym(1).prefix); ++ Value &val = sym(1); ++ if (auto res = fastScanName(&val)) ++ val.len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume($rule_number); + return false; +@@ -1822,7 +1837,11 @@ qname ::= LETTER; + name ::= LETTER; + /. + case $rule_number: +- sym(1).len += fastScanName(); ++ if (auto res = fastScanName()) ++ sym(1).len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume($rule_number); + return false; +diff --git a/qtbase/src/corelib/serialization/qxmlstream_p.h b/qtbase/src/corelib/serialization/qxmlstream_p.h +index e5bde7b98e..be7b1fe665 100644 +--- a/qtbase/src/corelib/serialization/qxmlstream_p.h ++++ b/qtbase/src/corelib/serialization/qxmlstream_p.h +@@ -804,6 +804,17 @@ public: + #endif + bool atEnd; + ++ enum class XmlContext ++ { ++ Prolog, ++ Body, ++ }; ++ ++ XmlContext currentContext = XmlContext::Prolog; ++ bool foundDTD = false; ++ bool isValidToken(QXmlStreamReader::TokenType type); ++ void checkToken(); ++ + /*! + \sa setType() + */ +@@ -1005,7 +1016,16 @@ public: + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); +- int fastScanName(int *prefix = nullptr); ++ ++ struct FastScanNameResult { ++ FastScanNameResult() : ok(false) {} ++ explicit FastScanNameResult(int len) : addToLen(len), ok(true) { } ++ operator bool() { return ok; } ++ int operator*() { Q_ASSERT(ok); return addToLen; } ++ int addToLen; ++ bool ok; ++ }; ++ FastScanNameResult fastScanName(Value *val = nullptr); + inline int fastScanNMTOKEN(); + + +@@ -1014,6 +1034,7 @@ public: + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); ++ void raiseNamePrefixTooLongError(); + + QXmlStreamEntityResolver *entityResolver; + +@@ -1939,7 +1960,12 @@ bool QXmlStreamReaderPrivate::parse() + break; + + case 262: { +- sym(1).len += fastScanName(&sym(1).prefix); ++ Value &val = sym(1); ++ if (auto res = fastScanName(&val)) ++ val.len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume(262); + return false; +@@ -1947,7 +1973,11 @@ bool QXmlStreamReaderPrivate::parse() + } break; + + case 263: +- sym(1).len += fastScanName(); ++ if (auto res = fastScanName()) ++ sym(1).len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume(263); + return false; +diff --git a/qtbase/src/corelib/text/qlocale_data_p.h b/qtbase/src/corelib/text/qlocale_data_p.h +index c5e6a0d461..c613e4e537 100644 +--- a/qtbase/src/corelib/text/qlocale_data_p.h ++++ b/qtbase/src/corelib/text/qlocale_data_p.h +@@ -1340,7 +1340,7 @@ static const QLocaleData locale_data[] = { + { 25, 6, 126, 46, 44, 59, 37, 48, 45, 43, 101, 12300, 12301, 12302, 12303, 166,5 , 166,5 , 176,5 , 176,5 , 415,8 , 402,13 , 209,6 , 226,13 , 2022,21 , 1980,28 , 2008,14 , 2022,21 , 1980,28 , 2008,14 , 58,2 , 55,2 , 283,3 , 5,17 , 22,23 , {77,79,80}, 137,4 , 3174,13 , 4,4 , 13,6 , 589,4 , 602,9 , 2, 1, 7, 6, 7 }, // Chinese/Traditional Han/Macau + { 25, 6, 208, 46, 44, 59, 37, 48, 45, 43, 101, 12300, 12301, 12302, 12303, 166,5 , 166,5 , 171,5 , 171,5 , 394,8 , 423,14 , 209,6 , 226,13 , 2022,21 , 1980,28 , 2008,14 , 2022,21 , 1980,28 , 2008,14 , 58,2 , 55,2 , 45,4 , 5,17 , 22,23 , {84,87,68}, 6,1 , 3187,13 , 4,4 , 13,6 , 589,4 , 611,2 , 2, 0, 7, 6, 7 }, // Chinese/Traditional Han/Taiwan + { 26, 7, 74, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {69,85,82}, 14,1 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 1, 6, 7 }, // Corsican/Latin/France +- { 27, 7, 54, 44, 46, 59, 37, 48, 8722, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 159,7 , 159,7 , 437,13 , 450,19 , 37,5 , 87,12 , 2043,28 , 2071,58 , 2129,14 , 2043,28 , 2071,58 , 2143,14 , 0,2 , 0,2 , 286,7 , 5,17 , 22,23 , {72,82,75}, 141,3 , 3200,60 , 19,5 , 4,0 , 613,8 , 621,8 , 2, 1, 1, 6, 7 }, // Croatian/Latin/Croatia ++ { 27, 7, 54, 44, 46, 59, 37, 48, 8722, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 159,7 , 159,7 , 437,13 , 450,19 , 37,5 , 87,12 , 2043,28 , 2071,58 , 2129,14 , 2043,28 , 2071,58 , 2143,14 , 0,2 , 0,2 , 286,7 , 5,17 , 22,23 , {69,85,82}, 14,1 , 3455,19 , 19,5 , 4,0 , 613,8 , 621,8 , 2, 1, 1, 6, 7 }, // Croatian/Latin/Croatia + { 27, 7, 27, 44, 46, 59, 37, 48, 8722, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 159,7 , 159,7 , 469,9 , 450,19 , 37,5 , 87,12 , 2043,28 , 2071,58 , 2143,14 , 2043,28 , 2071,58 , 2143,14 , 0,2 , 0,2 , 286,7 , 5,17 , 22,23 , {66,65,77}, 144,2 , 3260,85 , 19,5 , 4,0 , 613,8 , 629,19 , 2, 1, 1, 6, 7 }, // Croatian/Latin/Bosnia And Herzegowina + { 28, 7, 57, 44, 160, 59, 37, 48, 45, 43, 101, 8222, 8220, 8218, 8216, 0,6 , 0,6 , 181,7 , 181,7 , 156,8 , 478,17 , 55,4 , 59,9 , 2157,21 , 2178,49 , 2227,14 , 2157,21 , 2178,49 , 2227,14 , 60,4 , 57,4 , 293,5 , 5,17 , 22,23 , {67,90,75}, 146,2 , 3345,68 , 19,5 , 4,0 , 648,7 , 655,5 , 2, 0, 1, 6, 7 }, // Czech/Latin/Czech Republic + { 29, 7, 58, 44, 46, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 188,8 , 188,8 , 495,10 , 505,23 , 239,5 , 244,10 , 2241,28 , 2269,51 , 2320,14 , 2334,35 , 2269,51 , 2320,14 , 0,2 , 0,2 , 0,5 , 5,17 , 22,23 , {68,75,75}, 148,3 , 3413,42 , 19,5 , 4,0 , 660,5 , 665,7 , 2, 0, 1, 6, 7 }, // Danish/Latin/Denmark +diff --git a/qtbase/src/corelib/text/qstringiterator_p.h b/qtbase/src/corelib/text/qstringiterator_p.h +index b31c7673c2..731a241407 100644 +--- a/qtbase/src/corelib/text/qstringiterator_p.h ++++ b/qtbase/src/corelib/text/qstringiterator_p.h +@@ -123,16 +123,20 @@ public: + { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + +- if (Q_UNLIKELY((pos++)->isHighSurrogate())) ++ if (Q_UNLIKELY((pos++)->isHighSurrogate())) { ++ Q_ASSERT(pos < e && pos->isLowSurrogate()); + ++pos; ++ } + } + + inline uint peekNextUnchecked() const + { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + +- if (Q_UNLIKELY(pos->isHighSurrogate())) ++ if (Q_UNLIKELY(pos->isHighSurrogate())) { ++ Q_ASSERT(pos + 1 < e && pos[1].isLowSurrogate()); + return QChar::surrogateToUcs4(pos[0], pos[1]); ++ } + + return pos->unicode(); + } +@@ -158,8 +162,10 @@ public: + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + + const QChar cur = *pos++; +- if (Q_UNLIKELY(cur.isHighSurrogate())) ++ if (Q_UNLIKELY(cur.isHighSurrogate())) { ++ Q_ASSERT(pos < e && pos->isLowSurrogate()); + return QChar::surrogateToUcs4(cur, *pos++); ++ } + return cur.unicode(); + } + +@@ -199,16 +205,20 @@ public: + { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + +- if (Q_UNLIKELY((--pos)->isLowSurrogate())) ++ if (Q_UNLIKELY((--pos)->isLowSurrogate())) { ++ Q_ASSERT(pos > i && pos[-1].isHighSurrogate()); + --pos; ++ } + } + + inline uint peekPreviousUnchecked() const + { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + +- if (Q_UNLIKELY(pos[-1].isLowSurrogate())) ++ if (Q_UNLIKELY(pos[-1].isLowSurrogate())) { ++ Q_ASSERT(pos > i + 1 && pos[-2].isHighSurrogate()); + return QChar::surrogateToUcs4(pos[-2], pos[-1]); ++ } + return pos[-1].unicode(); + } + +@@ -233,8 +243,10 @@ public: + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + + const QChar cur = *--pos; +- if (Q_UNLIKELY(cur.isLowSurrogate())) ++ if (Q_UNLIKELY(cur.isLowSurrogate())) { ++ Q_ASSERT(pos > i && pos[-1].isHighSurrogate()); + return QChar::surrogateToUcs4(*--pos, cur); ++ } + return cur.unicode(); + } + +diff --git a/qtbase/src/corelib/thread/qfutex_p.h b/qtbase/src/corelib/thread/qfutex_p.h +index f287b752d7..e294537787 100644 +--- a/qtbase/src/corelib/thread/qfutex_p.h ++++ b/qtbase/src/corelib/thread/qfutex_p.h +@@ -52,6 +52,7 @@ + // + + #include ++#include + + QT_BEGIN_NAMESPACE + +@@ -106,16 +107,13 @@ namespace QtLinuxFutex { + inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0, + int *addr2 = nullptr, int val3 = 0) noexcept + { +- // A futex call ensures total ordering on the futex words +- // (in either success or failure of the call). Instruct TSAN accordingly, +- // as TSAN does not understand the futex(2) syscall. +- _q_tsan_release(addr, addr2); ++ QtTsan::futexRelease(addr, addr2); + + // we use __NR_futex because some libcs (like Android's bionic) don't + // provide SYS_futex etc. + int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3); + +- _q_tsan_acquire(addr, addr2); ++ QtTsan::futexAcquire(addr, addr2); + + return result; + } +diff --git a/qtbase/src/corelib/thread/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/qthreadpool.cpp b/qtbase/src/corelib/thread/qthreadpool.cpp +index cdeb077233..5b1e4f4c33 100644 +--- a/qtbase/src/corelib/thread/qthreadpool.cpp ++++ b/qtbase/src/corelib/thread/qthreadpool.cpp +@@ -602,8 +602,12 @@ bool QThreadPool::tryStart(std::function functionToRun) + return false; + + QRunnable *runnable = QRunnable::create(std::move(functionToRun)); ++ Q_ASSERT(runnable->ref == 0); ++ ++runnable->ref; + if (d->tryStart(runnable)) + return true; ++ --runnable->ref; ++ Q_ASSERT(runnable->ref == 0); + delete runnable; + return false; + } +diff --git a/qtbase/src/corelib/thread/qtsan_impl.h b/qtbase/src/corelib/thread/qtsan_impl.h +new file mode 100644 +index 0000000000..580a738b91 +--- /dev/null ++++ b/qtbase/src/corelib/thread/qtsan_impl.h +@@ -0,0 +1,115 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2017 Intel Corporation. ++** Copyright (C) 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the QtCore module of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU Lesser General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU Lesser ++** General Public License version 3 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL3 included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 3 requirements ++** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 2.0 or (at your option) the GNU General ++** Public license version 3 or any later version approved by the KDE Free ++** Qt Foundation. The licenses are as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-2.0.html and ++** https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef QTSAN_IMPL_H ++#define QTSAN_IMPL_H ++ ++#include ++ ++#if (__has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)) && __has_include() ++# define QT_BUILDING_UNDER_TSAN ++# include ++#endif ++ ++QT_BEGIN_NAMESPACE ++ ++namespace QtTsan { ++#ifdef QT_BUILDING_UNDER_TSAN ++inline void futexAcquire(void *addr, void *addr2 = nullptr) ++{ ++ // A futex call ensures total ordering on the futex words ++ // (in either success or failure of the call). Instruct TSAN accordingly, ++ // as TSAN does not understand the futex(2) syscall (or equivalent). ++ ::__tsan_acquire(addr); ++ if (addr2) ++ ::__tsan_acquire(addr2); ++} ++ ++inline void futexRelease(void *addr, void *addr2 = nullptr) ++{ ++ if (addr2) ++ ::__tsan_release(addr2); ++ ::__tsan_release(addr); ++} ++ ++inline void mutexPreLock(void *addr, unsigned flags) ++{ ++ ::__tsan_mutex_pre_lock(addr, flags); ++} ++ ++inline void mutexPostLock(void *addr, unsigned flags, int recursion) ++{ ++ ::__tsan_mutex_post_lock(addr, flags, recursion); ++} ++ ++inline void mutexPreUnlock(void *addr, unsigned flags) ++{ ++ ::__tsan_mutex_pre_unlock(addr, flags); ++} ++ ++inline void mutexPostUnlock(void *addr, unsigned flags) ++{ ++ ::__tsan_mutex_post_unlock(addr, flags); ++} ++ ++enum : unsigned { ++ MutexWriteReentrant = ::__tsan_mutex_write_reentrant, ++ TryLock = ::__tsan_mutex_try_lock, ++ TryLockFailed = ::__tsan_mutex_try_lock_failed, ++}; ++#else ++inline void futexAcquire(void *, void * = nullptr) {} ++inline void futexRelease(void *, void * = nullptr) {} ++ ++enum : unsigned { ++ MutexWriteReentrant, ++ TryLock, ++ TryLockFailed, ++}; ++inline void mutexPreLock(void *, unsigned) {} ++inline void mutexPostLock(void *, unsigned, int) {} ++inline void mutexPreUnlock(void *, unsigned) {} ++inline void mutexPostUnlock(void *, unsigned) {} ++#endif // QT_BUILDING_UNDER_TSAN ++} // namespace QtTsan ++ ++QT_END_NAMESPACE ++ ++#endif // QTSAN_IMPL_H +diff --git a/qtbase/src/corelib/thread/qwaitcondition_unix.cpp b/qtbase/src/corelib/thread/qwaitcondition_unix.cpp +index 88b058f410..0f1da4dc9b 100644 +--- a/qtbase/src/corelib/thread/qwaitcondition_unix.cpp ++++ b/qtbase/src/corelib/thread/qwaitcondition_unix.cpp +@@ -213,7 +213,7 @@ bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline) + { + if (! mutex) + return false; +- if (mutex->isRecursive()) { ++ if (static_cast(mutex)->isRecursive()) { + qWarning("QWaitCondition: cannot wait on recursive mutexes"); + return false; + } +diff --git a/qtbase/src/corelib/time/qtimezone.cpp b/qtbase/src/corelib/time/qtimezone.cpp +index 0309e43e52..3d451696a1 100644 +--- a/qtbase/src/corelib/time/qtimezone.cpp ++++ b/qtbase/src/corelib/time/qtimezone.cpp +@@ -1,6 +1,6 @@ + /**************************************************************************** + ** +-** Copyright (C) 2013 John Layt ++** Copyright (C) 2020 John Layt + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. +@@ -975,9 +975,15 @@ QList QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId, + } + + #ifndef QT_NO_DATASTREAM ++// Invalid, as an IANA ID: too long, starts with - and has other invalid characters in it ++static inline QString invalidId() { return QStringLiteral("-No Time Zone Specified!"); } ++ + QDataStream &operator<<(QDataStream &ds, const QTimeZone &tz) + { +- tz.d->serialize(ds); ++ if (tz.isValid()) ++ tz.d->serialize(ds); ++ else ++ ds << invalidId(); + return ds; + } + +@@ -985,7 +991,9 @@ QDataStream &operator>>(QDataStream &ds, QTimeZone &tz) + { + QString ianaId; + ds >> ianaId; +- if (ianaId == QLatin1String("OffsetFromUtc")) { ++ if (ianaId == invalidId()) { ++ tz = QTimeZone(); ++ } else if (ianaId == QLatin1String("OffsetFromUtc")) { + int utcOffset; + QString name; + QString abbreviation; +diff --git a/qtbase/src/dbus/qdbusintegrator.cpp b/qtbase/src/dbus/qdbusintegrator.cpp +index a4cbfecc98..9ccbbbb37d 100644 +--- a/qtbase/src/dbus/qdbusintegrator.cpp ++++ b/qtbase/src/dbus/qdbusintegrator.cpp +@@ -1135,7 +1135,13 @@ void QDBusConnectionPrivate::closeConnection() + } + } + +- qDeleteAll(pendingCalls); ++ for (auto it = pendingCalls.begin(); it != pendingCalls.end(); ++it) { ++ auto call = *it; ++ if (!call->ref.deref()) { ++ delete call; ++ } ++ } ++ pendingCalls.clear(); + + // Disconnect all signals from signal hooks and from the object tree to + // avoid QObject::destroyed being sent to dbus daemon thread which has +diff --git a/qtbase/src/gui/configure.json b/qtbase/src/gui/configure.json +index 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_neon.cpp b/qtbase/src/gui/image/qimage_neon.cpp +index 9dbcb11db5..c17f76f2b0 100644 +--- a/qtbase/src/gui/image/qimage_neon.cpp ++++ b/qtbase/src/gui/image/qimage_neon.cpp +@@ -54,7 +54,7 @@ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, cons + + // align dst on 128 bits + const int offsetToAlignOn16Bytes = (reinterpret_cast(dst) >> 2) & 0x3; +- for (int i = 0; i < offsetToAlignOn16Bytes; ++i) { ++ for (int i = 0; i < qMin(len, offsetToAlignOn16Bytes); ++i) { + *dst++ = qRgb(src[0], src[1], src[2]); + src += 3; + } +diff --git a/qtbase/src/gui/image/qimagereader.cpp b/qtbase/src/gui/image/qimagereader.cpp +index 5cb7e1328e..1274622d53 100644 +--- a/qtbase/src/gui/image/qimagereader.cpp ++++ b/qtbase/src/gui/image/qimagereader.cpp +@@ -515,9 +515,9 @@ QImageReaderPrivate::QImageReaderPrivate(QImageReader *qq) + */ + QImageReaderPrivate::~QImageReaderPrivate() + { ++ delete handler; + if (deleteDevice) + delete device; +- delete handler; + } + + /*! +@@ -774,12 +774,12 @@ bool QImageReader::decideFormatFromContent() const + */ + void QImageReader::setDevice(QIODevice *device) + { ++ delete d->handler; ++ d->handler = nullptr; + if (d->device && d->deleteDevice) + delete d->device; + d->device = device; + d->deleteDevice = false; +- delete d->handler; +- d->handler = nullptr; + d->text.clear(); + } + +diff --git a/qtbase/src/gui/image/qimagewriter.cpp b/qtbase/src/gui/image/qimagewriter.cpp +index 33f5e491c7..a679f25757 100644 +--- a/qtbase/src/gui/image/qimagewriter.cpp ++++ b/qtbase/src/gui/image/qimagewriter.cpp +@@ -349,9 +349,9 @@ QImageWriter::QImageWriter(const QString &fileName, const QByteArray &format) + */ + QImageWriter::~QImageWriter() + { ++ delete d->handler; + if (d->deleteDevice) + delete d->device; +- delete d->handler; + delete d; + } + +@@ -396,13 +396,13 @@ QByteArray QImageWriter::format() const + */ + void QImageWriter::setDevice(QIODevice *device) + { ++ delete d->handler; ++ d->handler = nullptr; + if (d->device && d->deleteDevice) + delete d->device; + + d->device = device; + d->deleteDevice = false; +- delete d->handler; +- d->handler = nullptr; + } + + /*! +diff --git a/qtbase/src/gui/image/qpixmapcache.cpp b/qtbase/src/gui/image/qpixmapcache.cpp +index b6e5e70f55..a4317a4eac 100644 +--- a/qtbase/src/gui/image/qpixmapcache.cpp ++++ b/qtbase/src/gui/image/qpixmapcache.cpp +@@ -466,6 +466,7 @@ void QPMCache::clear() + killTimer(theid); + theid = 0; + } ++ cacheKeys.clear(); + } + + QPixmapCache::KeyData* QPMCache::getKeyData(QPixmapCache::Key *key) +diff --git a/qtbase/src/gui/itemmodels/qstandarditemmodel.cpp b/qtbase/src/gui/itemmodels/qstandarditemmodel.cpp +index fc9424763e..a3e0853fa8 100644 +--- a/qtbase/src/gui/itemmodels/qstandarditemmodel.cpp ++++ b/qtbase/src/gui/itemmodels/qstandarditemmodel.cpp +@@ -3113,13 +3113,13 @@ QStringList QStandardItemModel::mimeTypes() const + */ + QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const + { +- QMimeData *data = QAbstractItemModel::mimeData(indexes); +- if(!data) ++ std::unique_ptr data(QAbstractItemModel::mimeData(indexes)); ++ if (!data) + return nullptr; + + const QString format = qStandardItemModelDataListMimeType(); + if (!mimeTypes().contains(format)) +- return data; ++ return data.release(); + QByteArray encoded; + QDataStream stream(&encoded, QIODevice::WriteOnly); + +@@ -3172,7 +3172,7 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const + } + + data->setData(format, encoded); +- return data; ++ return data.release(); + } + + +diff --git a/qtbase/src/gui/kernel/qhighdpiscaling.cpp b/qtbase/src/gui/kernel/qhighdpiscaling.cpp +index 85ff58c14c..a433e94c22 100644 +--- a/qtbase/src/gui/kernel/qhighdpiscaling.cpp ++++ b/qtbase/src/gui/kernel/qhighdpiscaling.cpp +@@ -580,9 +580,8 @@ void QHighDpiScaling::setScreenFactor(QScreen *screen, qreal factor) + else + qNamedScreenScaleFactors()->insert(name, factor); + +- // hack to force re-evaluation of screen geometry + if (screen->handle()) +- screen->d_func()->setPlatformScreen(screen->handle()); // updates geometries based on scale factor ++ screen->d_func()->updateLogicalDpi(); + } + + QPoint QHighDpiScaling::mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen) +diff --git a/qtbase/src/gui/kernel/qkeysequence.cpp b/qtbase/src/gui/kernel/qkeysequence.cpp +index 56cd2d02bc..d448429f6c 100644 +--- a/qtbase/src/gui/kernel/qkeysequence.cpp ++++ b/qtbase/src/gui/kernel/qkeysequence.cpp +@@ -701,6 +701,10 @@ static const struct { + { Qt::Key_TouchpadToggle, QT_TRANSLATE_NOOP("QShortcut", "Touchpad Toggle") }, + { Qt::Key_TouchpadOn, QT_TRANSLATE_NOOP("QShortcut", "Touchpad On") }, + { Qt::Key_TouchpadOff, QT_TRANSLATE_NOOP("QShortcut", "Touchpad Off") }, ++ { Qt::Key_Shift, QT_TRANSLATE_NOOP("QShortcut", "Shift") }, ++ { Qt::Key_Control, QT_TRANSLATE_NOOP("QShortcut", "Control") }, ++ { Qt::Key_Alt, QT_TRANSLATE_NOOP("QShortcut", "Alt") }, ++ { Qt::Key_Meta, QT_TRANSLATE_NOOP("QShortcut", "Meta") }, + + }; + static Q_CONSTEXPR int numKeyNames = sizeof keyname / sizeof *keyname; +diff --git a/qtbase/src/gui/kernel/qplatformservices.cpp b/qtbase/src/gui/kernel/qplatformservices.cpp +index fdc6a6c4aa..ac47f98c5d 100644 +--- a/qtbase/src/gui/kernel/qplatformservices.cpp ++++ b/qtbase/src/gui/kernel/qplatformservices.cpp +@@ -55,6 +55,19 @@ QT_BEGIN_NAMESPACE + \brief The QPlatformServices provides the backend for desktop-related functionality. + */ + ++/*! ++ \enum QPlatformServices::Capability ++ ++ Capabilities are used to determine a specific platform service's availability. ++ ++ \value ColorPickingFromScreen The platform natively supports color picking from screen. ++ This capability indicates that the platform supports "opaque" color picking, where the ++ platform implements a complete user experience for color picking and outputs a color. ++ This is in contrast to the application implementing the color picking user experience ++ (taking care of showing a cross hair, instructing the platform integration to obtain ++ the color at a given pixel, etc.). The related service function is pickColor(). ++ */ ++ + QPlatformServices::QPlatformServices() + { } + +@@ -85,5 +98,16 @@ QByteArray QPlatformServices::desktopEnvironment() const + return QByteArray("UNKNOWN"); + } + ++QPlatformServiceColorPicker *QPlatformServices::colorPicker(QWindow *parent) ++{ ++ Q_UNUSED(parent); ++ return nullptr; ++} ++ ++bool QPlatformServices::hasCapability(Capability capability) const ++{ ++ Q_UNUSED(capability) ++ return false; ++} + + QT_END_NAMESPACE +diff --git a/qtbase/src/gui/kernel/qplatformservices.h b/qtbase/src/gui/kernel/qplatformservices.h +index 5de96cfa7d..a8b2a4ce71 100644 +--- a/qtbase/src/gui/kernel/qplatformservices.h ++++ b/qtbase/src/gui/kernel/qplatformservices.h +@@ -50,16 +50,32 @@ + // + + #include ++#include + + QT_BEGIN_NAMESPACE + + class QUrl; ++class QWindow; ++ ++class Q_GUI_EXPORT QPlatformServiceColorPicker : public QObject ++{ ++ Q_OBJECT ++public: ++ using QObject::QObject; ++ virtual void pickColor() = 0; ++Q_SIGNALS: ++ void colorPicked(const QColor &color); ++}; + + class Q_GUI_EXPORT QPlatformServices + { + public: + Q_DISABLE_COPY_MOVE(QPlatformServices) + ++ enum Capability { ++ ColorPicking, ++ }; ++ + QPlatformServices(); + virtual ~QPlatformServices() { } + +@@ -67,6 +83,10 @@ public: + virtual bool openDocument(const QUrl &url); + + virtual QByteArray desktopEnvironment() const; ++ ++ virtual bool hasCapability(Capability capability) const; ++ ++ virtual QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr); + }; + + QT_END_NAMESPACE +diff --git a/qtbase/src/gui/kernel/qplatformtheme.cpp b/qtbase/src/gui/kernel/qplatformtheme.cpp +index 71521c0339..2325873245 100644 +--- a/qtbase/src/gui/kernel/qplatformtheme.cpp ++++ b/qtbase/src/gui/kernel/qplatformtheme.cpp +@@ -163,6 +163,8 @@ QT_BEGIN_NAMESPACE + + \value ShowShortcutsInContextMenus (bool) Whether to display shortcut key sequences in context menus. + ++ \value ButtonPressKeys (QList) A list of keys that can be used to press buttons via keyboard input. ++ + \sa themeHint(), QStyle::pixelMetric() + */ + +@@ -563,6 +565,8 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint) + } + case MouseQuickSelectionThreshold: + return QVariant(10); ++ case ButtonPressKeys: ++ return QVariant::fromValue(QList({ Qt::Key_Space, Qt::Key_Select })); + } + return QVariant(); + } +diff --git a/qtbase/src/gui/kernel/qplatformtheme.h b/qtbase/src/gui/kernel/qplatformtheme.h +index 3185fc4541..7e6c9d5740 100644 +--- a/qtbase/src/gui/kernel/qplatformtheme.h ++++ b/qtbase/src/gui/kernel/qplatformtheme.h +@@ -120,7 +120,8 @@ public: + TouchDoubleTapDistance, + ShowShortcutsInContextMenus, + IconFallbackSearchPaths, +- MouseQuickSelectionThreshold ++ MouseQuickSelectionThreshold, ++ ButtonPressKeys + }; + + enum DialogType { +diff --git a/qtbase/src/gui/kernel/qscreen.cpp b/qtbase/src/gui/kernel/qscreen.cpp +index 990272b0c2..d371dd60ab 100644 +--- a/qtbase/src/gui/kernel/qscreen.cpp ++++ b/qtbase/src/gui/kernel/qscreen.cpp +@@ -77,6 +77,12 @@ QScreen::QScreen(QPlatformScreen *screen) + d->setPlatformScreen(screen); + } + ++void QScreenPrivate::updateLogicalDpi() ++{ ++ logicalDpi = QPlatformScreen::overrideDpi(platformScreen->logicalDpi()); ++ updateGeometriesWithSignals(); // updates geometries based on scale factor ++} ++ + void QScreenPrivate::updateGeometriesWithSignals() + { + const QRect oldGeometry = geometry; +diff --git a/qtbase/src/gui/kernel/qscreen_p.h b/qtbase/src/gui/kernel/qscreen_p.h +index 7da542c25e..e50fc3190b 100644 +--- a/qtbase/src/gui/kernel/qscreen_p.h ++++ b/qtbase/src/gui/kernel/qscreen_p.h +@@ -70,6 +70,7 @@ public: + geometry = platformScreen->deviceIndependentGeometry(); + availableGeometry = QHighDpi::fromNative(platformScreen->availableGeometry(), QHighDpiScaling::factor(platformScreen), geometry.topLeft()); + } ++ void updateLogicalDpi(); + + void updatePrimaryOrientation(); + void updateGeometriesWithSignals(); +diff --git a/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp b/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp +index bb0d8e4ee7..b98fcc61e7 100644 +--- a/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp ++++ b/qtbase/src/gui/kernel/qshapedpixmapdndwindow.cpp +@@ -56,7 +56,7 @@ QShapedPixmapWindow::QShapedPixmapWindow(QScreen *screen) + QSurfaceFormat format; + format.setAlphaBufferSize(8); + setFormat(format); +- setFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint ++ setFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint + | Qt::WindowTransparentForInput | Qt::WindowDoesNotAcceptFocus); + } + +diff --git a/qtbase/src/gui/kernel/qwindow.cpp b/qtbase/src/gui/kernel/qwindow.cpp +index 639817257e..c2f7dc6776 100644 +--- a/qtbase/src/gui/kernel/qwindow.cpp ++++ b/qtbase/src/gui/kernel/qwindow.cpp +@@ -644,7 +644,7 @@ bool QWindow::isVisible() const + into an actual native surface. However, the window remains hidden until setVisible() is called. + + Note that it is not usually necessary to call this function directly, as it will be implicitly +- called by show(), setVisible(), and other functions that require access to the platform ++ called by show(), setVisible(), winId(), and other functions that require access to the platform + resources. + + Call destroy() to free the platform resources if necessary. +@@ -660,6 +660,9 @@ void QWindow::create() + /*! + Returns the window's platform id. + ++ \note This function will cause the platform window to be created if it is not already. ++ Returns 0, if the platform window creation failed. ++ + For platforms where this id might be useful, the value returned + will uniquely represent the window inside the corresponding screen. + +@@ -672,6 +675,9 @@ WId QWindow::winId() const + if(!d->platformWindow) + const_cast(this)->create(); + ++ if (!d->platformWindow) ++ return 0; ++ + return d->platformWindow->winId(); + } + +diff --git a/qtbase/src/gui/opengl/qopengltexture.cpp b/qtbase/src/gui/opengl/qopengltexture.cpp +index 5490ad8025..afd3d8dbe6 100644 +--- a/qtbase/src/gui/opengl/qopengltexture.cpp ++++ b/qtbase/src/gui/opengl/qopengltexture.cpp +@@ -3725,6 +3725,12 @@ void QOpenGLTexture::setData(const QImage& image, MipMapGeneration genMipMaps) + return; + } + ++ QImage glImage = image.convertToFormat(QImage::Format_RGBA8888); ++ if (glImage.isNull()) { ++ qWarning("QOpenGLTexture::setData() failed to convert image"); ++ return; ++ } ++ + if (context->isOpenGLES() && context->format().majorVersion() < 3) + setFormat(QOpenGLTexture::RGBAFormat); + else +@@ -3735,7 +3741,6 @@ void QOpenGLTexture::setData(const QImage& image, MipMapGeneration genMipMaps) + allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + + // Upload pixel data and generate mipmaps +- QImage glImage = image.convertToFormat(QImage::Format_RGBA8888); + QOpenGLPixelTransferOptions uploadOptions; + uploadOptions.setAlignment(1); + setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, glImage.constBits(), &uploadOptions); +diff --git a/qtbase/src/gui/painting/qcolortrclut_p.h b/qtbase/src/gui/painting/qcolortrclut_p.h +index 76a6a60803..24fd522e6c 100644 +--- a/qtbase/src/gui/painting/qcolortrclut_p.h ++++ b/qtbase/src/gui/painting/qcolortrclut_p.h +@@ -118,6 +118,7 @@ public: + return QRgba64::fromRgba64(r, g, b, qAlpha(rgb32) * 257); + #endif + } ++ QRgba64 toLinear64(QRgba64) const = delete; + + QRgb toLinear(QRgb rgb32) const + { +diff --git a/qtbase/src/gui/painting/qdrawhelper.cpp b/qtbase/src/gui/painting/qdrawhelper.cpp +index a61793508a..5ba2d277b7 100644 +--- a/qtbase/src/gui/painting/qdrawhelper.cpp ++++ b/qtbase/src/gui/painting/qdrawhelper.cpp +@@ -6091,7 +6091,7 @@ static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba + static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile) + { + // Do a gammacorrected RGB alphablend... +- const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(dst) : dst; ++ const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(dst) : dst; + + QRgba64 blend = rgbBlend(dlinear, slinear, coverage); + +diff --git a/qtbase/src/gui/painting/qfixed_p.h b/qtbase/src/gui/painting/qfixed_p.h +index 846592881c..57d750a4b3 100644 +--- a/qtbase/src/gui/painting/qfixed_p.h ++++ b/qtbase/src/gui/painting/qfixed_p.h +@@ -54,6 +54,7 @@ + #include + #include "QtCore/qdebug.h" + #include "QtCore/qpoint.h" ++#include + #include "QtCore/qsize.h" + + QT_BEGIN_NAMESPACE +@@ -182,6 +183,14 @@ Q_DECL_CONSTEXPR inline bool operator<(int i, const QFixed &f) { return i * 64 < + Q_DECL_CONSTEXPR inline bool operator>(const QFixed &f, int i) { return f.value() > i * 64; } + Q_DECL_CONSTEXPR inline bool operator>(int i, const QFixed &f) { return i * 64 > f.value(); } + ++inline bool qAddOverflow(QFixed v1, QFixed v2, QFixed *r) ++{ ++ int val; ++ bool result = add_overflow(v1.value(), v2.value(), &val); ++ r->setValue(val); ++ return result; ++} ++ + #ifndef QT_NO_DEBUG_STREAM + inline QDebug &operator<<(QDebug &dbg, const QFixed &f) + { return dbg << f.toReal(); } +diff --git a/qtbase/src/gui/painting/qpainterpath.cpp b/qtbase/src/gui/painting/qpainterpath.cpp +index f9544a3241..d80fafeaf1 100644 +--- a/qtbase/src/gui/painting/qpainterpath.cpp ++++ b/qtbase/src/gui/painting/qpainterpath.cpp +@@ -1253,7 +1253,7 @@ void QPainterPath::addText(const QPointF &point, const QFont &f, const QString & + + if (si.analysis.flags < QScriptAnalysis::TabOrObject) { + QGlyphLayout glyphs = eng->shapedGlyphs(&si); +- QFontEngine *fe = f.d->engineForScript(si.analysis.script); ++ QFontEngine *fe = eng->fontEngine(si); + Q_ASSERT(fe); + fe->addOutlineToPath(x, y, glyphs, this, + si.analysis.bidiLevel % 2 +diff --git a/qtbase/src/gui/painting/qpdf.cpp b/qtbase/src/gui/painting/qpdf.cpp +index 3066744f1b..2c8d3c3b53 100644 +--- a/qtbase/src/gui/painting/qpdf.cpp ++++ b/qtbase/src/gui/painting/qpdf.cpp +@@ -2760,6 +2760,8 @@ int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, + return gradientBrush(brush, matrix, gStateObject); + } + ++ matrix = brush.transform() * matrix; ++ + if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0) + *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity), + qRound(pen.color().alpha() * opacity)); +diff --git a/qtbase/src/gui/rhi/qshader_p_p.h b/qtbase/src/gui/rhi/qshader_p_p.h +index ec9d25971f..4a5a7a6d51 100644 +--- a/qtbase/src/gui/rhi/qshader_p_p.h ++++ b/qtbase/src/gui/rhi/qshader_p_p.h +@@ -68,13 +68,13 @@ struct Q_GUI_EXPORT QShaderPrivate + { + } + +- QShaderPrivate(const QShaderPrivate *other) ++ QShaderPrivate(const QShaderPrivate &other) + : ref(1), +- qsbVersion(other->qsbVersion), +- stage(other->stage), +- desc(other->desc), +- shaders(other->shaders), +- bindings(other->bindings) ++ qsbVersion(other.qsbVersion), ++ stage(other.stage), ++ desc(other.desc), ++ shaders(other.shaders), ++ bindings(other.bindings) + { + } + +diff --git a/qtbase/src/gui/rhi/qshaderdescription_p_p.h b/qtbase/src/gui/rhi/qshaderdescription_p_p.h +index ec2b0b6b4c..3da33a8a2b 100644 +--- a/qtbase/src/gui/rhi/qshaderdescription_p_p.h ++++ b/qtbase/src/gui/rhi/qshaderdescription_p_p.h +@@ -63,16 +63,16 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate + localSize[0] = localSize[1] = localSize[2] = 0; + } + +- QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other) ++ QShaderDescriptionPrivate(const QShaderDescriptionPrivate &other) + : ref(1), +- inVars(other->inVars), +- outVars(other->outVars), +- uniformBlocks(other->uniformBlocks), +- pushConstantBlocks(other->pushConstantBlocks), +- storageBlocks(other->storageBlocks), +- combinedImageSamplers(other->combinedImageSamplers), +- storageImages(other->storageImages), +- localSize(other->localSize) ++ inVars(other.inVars), ++ outVars(other.outVars), ++ uniformBlocks(other.uniformBlocks), ++ pushConstantBlocks(other.pushConstantBlocks), ++ storageBlocks(other.storageBlocks), ++ combinedImageSamplers(other.combinedImageSamplers), ++ storageImages(other.storageImages), ++ localSize(other.localSize) + { + } + +diff --git a/qtbase/src/gui/text/qfontdatabase.cpp b/qtbase/src/gui/text/qfontdatabase.cpp +index 2011f935a9..7aa6228948 100644 +--- a/qtbase/src/gui/text/qfontdatabase.cpp ++++ b/qtbase/src/gui/text/qfontdatabase.cpp +@@ -983,7 +983,7 @@ QFontEngine *loadSingleEngine(int script, + if (style->key.stretch != 0 && request.stretch != 0 + && (request.styleName.isEmpty() || request.styleName != style->styleName)) { + def.stretch = (request.stretch * 100 + style->key.stretch / 2) / style->key.stretch; +- } else { ++ } else if (request.stretch == QFont::AnyStretch) { + def.stretch = 100; + } + +diff --git a/qtbase/src/gui/text/qtextengine.cpp b/qtbase/src/gui/text/qtextengine.cpp +index 6336fadf74..a6c66e5d2d 100644 +--- a/qtbase/src/gui/text/qtextengine.cpp ++++ b/qtbase/src/gui/text/qtextengine.cpp +@@ -1,6 +1,6 @@ + /**************************************************************************** + ** +-** Copyright (C) 2016 The Qt Company Ltd. ++** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtGui module of the Qt Toolkit. +diff --git a/qtbase/src/gui/text/qtextlayout.cpp b/qtbase/src/gui/text/qtextlayout.cpp +index 30f07ba69b..f60597a076 100644 +--- a/qtbase/src/gui/text/qtextlayout.cpp ++++ b/qtbase/src/gui/text/qtextlayout.cpp +@@ -1336,13 +1336,16 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition + bool rightToLeft = d->isRightToLeft(); + if (itm >= 0) { + const QScriptItem &si = d->layoutData->items.at(itm); +- if (si.ascent >= 0) +- base = si.ascent; +- if (si.descent >= 0) +- descent = si.descent; ++ // objects need some special treatment as they can have special alignment or be floating ++ if (si.analysis.flags != QScriptAnalysis::Object) { ++ if (si.ascent > 0) ++ base = si.ascent; ++ if (si.descent > 0) ++ descent = si.descent; ++ } + rightToLeft = si.analysis.bidiLevel % 2; + } +- qreal y = position.y() + (sl.y + sl.base() + sl.descent - base - descent).toReal(); ++ qreal y = position.y() + (sl.y + sl.base() - base).toReal(); + bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing) + && (p->transform().type() > QTransform::TxTranslate); + if (toggleAntialiasing) +@@ -2163,11 +2166,14 @@ found: + eng->maxWidth = qMax(eng->maxWidth, line.textWidth); + } else { + eng->minWidth = qMax(eng->minWidth, lbh.minw); +- eng->maxWidth += line.textWidth; ++ if (qAddOverflow(eng->maxWidth, line.textWidth, &eng->maxWidth)) ++ eng->maxWidth = QFIXED_MAX; + } + +- if (line.textWidth > 0 && item < eng->layoutData->items.size()) +- eng->maxWidth += lbh.spaceData.textWidth; ++ if (line.textWidth > 0 && item < eng->layoutData->items.size()) { ++ if (qAddOverflow(eng->maxWidth, lbh.spaceData.textWidth, &eng->maxWidth)) ++ eng->maxWidth = QFIXED_MAX; ++ } + + line.textWidth += trailingSpace; + if (lbh.spaceData.length) { +diff --git a/qtbase/src/gui/util/qshadergenerator.cpp b/qtbase/src/gui/util/qshadergenerator.cpp +index 1ec25ccd7b..20ed6abc3a 100644 +--- a/qtbase/src/gui/util/qshadergenerator.cpp ++++ b/qtbase/src/gui/util/qshadergenerator.cpp +@@ -492,7 +492,7 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) + int end = begin + 1; + char endChar = line.at(end); + const int size = line.size(); +- while (end < size && (std::isalnum(endChar) || endChar == '_')) { ++ while (end < size && (std::isalnum(uchar(endChar)) || endChar == '_')) { + ++end; + endChar = line.at(end); + } +diff --git a/qtbase/src/gui/util/qshaderlanguage.cpp b/qtbase/src/gui/util/qshaderlanguage.cpp +index efd607ba60..9399d6efcc 100644 +--- a/qtbase/src/gui/util/qshaderlanguage.cpp ++++ b/qtbase/src/gui/util/qshaderlanguage.cpp +@@ -52,3 +52,5 @@ void qt_register_ShaderLanguage_enums() + } + + QT_END_NAMESPACE ++ ++#include "moc_qshaderlanguage_p.cpp" +diff --git a/qtbase/src/network/access/http2/hpacktable.cpp b/qtbase/src/network/access/http2/hpacktable.cpp +index fddb5feca5..315f3e2344 100644 +--- a/qtbase/src/network/access/http2/hpacktable.cpp ++++ b/qtbase/src/network/access/http2/hpacktable.cpp +@@ -40,6 +40,7 @@ + #include "hpacktable_p.h" + + #include ++#include + + #include + #include +@@ -62,8 +63,10 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value) + // for counting the number of references to the name and value would have + // 32 octets of overhead." + +- const unsigned sum = unsigned(name.size() + value.size()); +- if (std::numeric_limits::max() - 32 < sum) ++ size_t sum; ++ if (add_overflow(size_t(name.size()), size_t(value.size()), &sum)) ++ return HeaderSize(); ++ if (sum > (std::numeric_limits::max() - 32)) + return HeaderSize(); + return HeaderSize(true, quint32(sum + 32)); + } +diff --git a/qtbase/src/network/access/qhsts.cpp b/qtbase/src/network/access/qhsts.cpp +index 0cef0ad3dc..be7ef7ff58 100644 +--- a/qtbase/src/network/access/qhsts.cpp ++++ b/qtbase/src/network/access/qhsts.cpp +@@ -364,8 +364,8 @@ quoted-pair = "\" CHAR + bool QHstsHeaderParser::parse(const QList> &headers) + { + for (const auto &h : headers) { +- // We use '==' since header name was already 'trimmed' for us: +- if (h.first == "Strict-Transport-Security") { ++ // We compare directly because header name was already 'trimmed' for us: ++ if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) { + header = h.second; + // RFC6797, 8.1: + // +diff --git a/qtbase/src/network/access/qhttp2protocolhandler.cpp b/qtbase/src/network/access/qhttp2protocolhandler.cpp +index 39dd460881..ead88d781a 100644 +--- a/qtbase/src/network/access/qhttp2protocolhandler.cpp ++++ b/qtbase/src/network/access/qhttp2protocolhandler.cpp +@@ -46,10 +46,12 @@ + #include + + #include ++ + #include + #include + #include + #include ++#include + #include + + #include +@@ -124,8 +126,10 @@ std::vector assemble_hpack_block(const std::vector &frames) + std::vector hpackBlock; + + quint32 total = 0; +- for (const auto &frame : frames) +- total += frame.hpackBlockSize(); ++ for (const auto &frame : frames) { ++ if (add_overflow(total, frame.hpackBlockSize(), &total)) ++ return hpackBlock; ++ } + + if (!total) + return hpackBlock; +diff --git a/qtbase/src/network/access/qnetworkreplyfileimpl_p.h b/qtbase/src/network/access/qnetworkreplyfileimpl_p.h +index 48d82abd3f..4bfbc3f7d1 100644 +--- a/qtbase/src/network/access/qnetworkreplyfileimpl_p.h ++++ b/qtbase/src/network/access/qnetworkreplyfileimpl_p.h +@@ -37,8 +37,8 @@ + ** + ****************************************************************************/ + +-#ifndef QNETWORKREPLYFILEIMPL_H +-#define QNETWORKREPLYFILEIMPL_H ++#ifndef QNETWORKREPLYFILEIMPL_P_H ++#define QNETWORKREPLYFILEIMPL_P_H + + // + // W A R N I N G +@@ -99,4 +99,4 @@ QT_END_NAMESPACE + + Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders) + +-#endif // QNETWORKREPLYFILEIMPL_H ++#endif // QNETWORKREPLYFILEIMPL_P_H +diff --git a/qtbase/src/network/configure.json b/qtbase/src/network/configure.json +index 271ff164ac..ffba2d1eea 100644 +--- a/qtbase/src/network/configure.json ++++ b/qtbase/src/network/configure.json +@@ -53,7 +53,7 @@ + }, + "headers": "proxy.h", + "sources": [ +- "-lproxy" ++ { "type": "pkgConfig", "args": "libproxy-1.0" } + ] + }, + "openssl_headers": { +diff --git a/qtbase/src/network/kernel/qdnslookup_unix.cpp b/qtbase/src/network/kernel/qdnslookup_unix.cpp +index 12b40fc35d..99e999d436 100644 +--- a/qtbase/src/network/kernel/qdnslookup_unix.cpp ++++ b/qtbase/src/network/kernel/qdnslookup_unix.cpp +@@ -227,7 +227,6 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN + // responseLength in case of error, we still can extract the + // exact error code from the response. + HEADER *header = (HEADER*)response; +- const int answerCount = ntohs(header->ancount); + switch (header->rcode) { + case NOERROR: + break; +@@ -260,18 +259,31 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN + return; + } + +- // Skip the query host, type (2 bytes) and class (2 bytes). + char host[PACKETSZ], answer[PACKETSZ]; + unsigned char *p = response + sizeof(HEADER); +- int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); +- if (status < 0) { ++ int status; ++ ++ if (ntohs(header->qdcount) == 1) { ++ // Skip the query host, type (2 bytes) and class (2 bytes). ++ status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); ++ if (status < 0) { ++ reply->error = QDnsLookup::InvalidReplyError; ++ reply->errorString = tr("Could not expand domain name"); ++ return; ++ } ++ if ((p - response) + status + 4 >= responseLength) ++ header->qdcount = 0xffff; // invalid reply below ++ else ++ p += status + 4; ++ } ++ if (ntohs(header->qdcount) > 1) { + reply->error = QDnsLookup::InvalidReplyError; +- reply->errorString = tr("Could not expand domain name"); ++ reply->errorString = tr("Invalid reply received"); + return; + } +- p += status + 4; + + // Extract results. ++ const int answerCount = ntohs(header->ancount); + int answerIndex = 0; + while ((p < response + responseLength) && (answerIndex < answerCount)) { + status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); +@@ -283,6 +295,11 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN + const QString name = QUrl::fromAce(host); + + p += status; ++ ++ if ((p - response) + 10 > responseLength) { ++ // probably just a truncated reply, return what we have ++ return; ++ } + const quint16 type = (p[0] << 8) | p[1]; + p += 2; // RR type + p += 2; // RR class +@@ -290,6 +307,8 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN + p += 4; + const quint16 size = (p[0] << 8) | p[1]; + p += 2; ++ if ((p - response) + size > responseLength) ++ return; // truncated + + if (type == QDnsLookup::A) { + if (size != 4) { +diff --git a/qtbase/src/network/kernel/qhostinfo.cpp b/qtbase/src/network/kernel/qhostinfo.cpp +index 33fb629899..015963686c 100644 +--- a/qtbase/src/network/kernel/qhostinfo.cpp ++++ b/qtbase/src/network/kernel/qhostinfo.cpp +@@ -181,7 +181,6 @@ bool QHostInfoResult::event(QEvent *event) + // we didn't have a context object, or it's still alive + if (!withContextObject || receiver) + slotObj->call(const_cast(receiver.data()), args); +- slotObj->destroyIfLastRef(); + + deleteLater(); + return true; +@@ -801,6 +800,8 @@ int QHostInfoPrivate::lookupHostImpl(const QString &name, + + if (!QAbstractEventDispatcher::instance(QThread::currentThread())) { + qWarning("QHostInfo::lookupHost() called with no event dispatcher"); ++ if (slotObj) ++ slotObj->destroyIfLastRef(); + return -1; + } + +@@ -847,6 +848,8 @@ int QHostInfoPrivate::lookupHostImpl(const QString &name, + QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), + receiver, member, Qt::QueuedConnection); + manager->scheduleLookup(runnable); ++ } else if (slotObj) { ++ slotObj->destroyIfLastRef(); + } + return id; + } +diff --git a/qtbase/src/network/kernel/qhostinfo_p.h b/qtbase/src/network/kernel/qhostinfo_p.h +index d7875a0673..452c095b6a 100644 +--- a/qtbase/src/network/kernel/qhostinfo_p.h ++++ b/qtbase/src/network/kernel/qhostinfo_p.h +@@ -90,6 +90,12 @@ public: + moveToThread(receiver->thread()); + } + ++ ~QHostInfoResult() ++ { ++ if (slotObj) ++ slotObj->destroyIfLastRef(); ++ } ++ + void postResultsReady(const QHostInfo &info); + + Q_SIGNALS: +@@ -103,6 +109,8 @@ private: + : receiver(other->receiver), slotObj(other->slotObj), + withContextObject(other->withContextObject) + { ++ if (slotObj) ++ slotObj->ref(); + // cleanup if the application terminates before results are delivered + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + this, &QObject::deleteLater); +diff --git a/qtbase/src/network/ssl/qsslconfiguration.cpp b/qtbase/src/network/ssl/qsslconfiguration.cpp +index f5ce02807f..84a9187334 100644 +--- a/qtbase/src/network/ssl/qsslconfiguration.cpp ++++ b/qtbase/src/network/ssl/qsslconfiguration.cpp +@@ -929,7 +929,11 @@ void QSslConfiguration::setPreSharedKeyIdentityHint(const QByteArray &hint) + Retrieves the current set of Diffie-Hellman parameters. + + If no Diffie-Hellman parameters have been set, the QSslConfiguration object +- defaults to using the 1024-bit MODP group from RFC 2409. ++ defaults to using the 2048-bit MODP group from RFC 3526. ++ ++ \note The default parameters may change in future Qt versions. ++ Please check the documentation of the \e{exact Qt version} that you ++ are using in order to know what defaults that version uses. + */ + QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const + { +@@ -943,7 +947,11 @@ QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const + a server to \a dhparams. + + If no Diffie-Hellman parameters have been set, the QSslConfiguration object +- defaults to using the 1024-bit MODP group from RFC 2409. ++ defaults to using the 2048-bit MODP group from RFC 3526. ++ ++ \note The default parameters may change in future Qt versions. ++ Please check the documentation of the \e{exact Qt version} that you ++ are using in order to know what defaults that version uses. + */ + void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams) + { +diff --git a/qtbase/src/network/ssl/qsslcontext_openssl.cpp b/qtbase/src/network/ssl/qsslcontext_openssl.cpp +index c30192a4eb..e4bb61ecb5 100644 +--- a/qtbase/src/network/ssl/qsslcontext_openssl.cpp ++++ b/qtbase/src/network/ssl/qsslcontext_openssl.cpp +@@ -409,7 +409,7 @@ init_context: + break; + case QSsl::DtlsV1_0OrLater: + minVersion = DTLS1_VERSION; +- maxVersion = DTLS_MAX_VERSION; ++ maxVersion = 0; + break; + case QSsl::DtlsV1_2: + minVersion = DTLS1_2_VERSION; +@@ -417,7 +417,7 @@ init_context: + break; + case QSsl::DtlsV1_2OrLater: + minVersion = DTLS1_2_VERSION; +- maxVersion = DTLS_MAX_VERSION; ++ maxVersion = 0; + break; + case QSsl::TlsV1_3OrLater: + #ifdef TLS1_3_VERSION +diff --git a/qtbase/src/network/ssl/qssldiffiehellmanparameters.cpp b/qtbase/src/network/ssl/qssldiffiehellmanparameters.cpp +index 7807afaa30..7c2505a0be 100644 +--- a/qtbase/src/network/ssl/qssldiffiehellmanparameters.cpp ++++ b/qtbase/src/network/ssl/qssldiffiehellmanparameters.cpp +@@ -68,17 +68,18 @@ + + QT_BEGIN_NAMESPACE + +-// The 1024-bit MODP group from RFC 2459 (Second Oakley Group) ++// The 2048-bit MODP group from RFC 3526 + Q_AUTOTEST_EXPORT const char *qssl_dhparams_default_base64 = +- "MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR" +- "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL" +- "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC"; ++ "MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxObIlFKCHmO" ++ "NATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjftawv/XLb0Brft7jhr" ++ "+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXTmmkWP6j9JM9fg2VdI9yjrZYc" ++ "YvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhghfDKQXkYuNs474553LBgOhgObJ4Oi7Aei" ++ "j7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg=="; + + /*! + Returns the default QSslDiffieHellmanParameters used by QSslSocket. + +- This is currently the 1024-bit MODP group from RFC 2459, also +- known as the Second Oakley Group. ++ This is currently the 2048-bit MODP group from RFC 3526. + */ + QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters() + { +diff --git a/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp +index aaf8741130..93ad7fa728 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; +@@ -206,6 +155,7 @@ void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem) + if (isSafeDH(dh)) { + char *buf = nullptr; + int len = q_i2d_DHparams(dh, reinterpret_cast(&buf)); ++ auto freeBuf = qScopeGuard([&] { q_OPENSSL_free(buf); }); + if (len > 0) + derData = QByteArray(buf, len); + else +diff --git a/qtbase/src/network/ssl/qsslsocket.cpp b/qtbase/src/network/ssl/qsslsocket.cpp +index 5bb6e7ee4a..2a0b3a4f1d 100644 +--- a/qtbase/src/network/ssl/qsslsocket.cpp ++++ b/qtbase/src/network/ssl/qsslsocket.cpp +@@ -2221,6 +2221,10 @@ QSslSocketPrivate::QSslSocketPrivate() + , flushTriggered(false) + { + QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); ++ // If the global configuration doesn't allow root certificates to be loaded ++ // on demand then we have to disable it for this socket as well. ++ if (!configuration.allowRootCertOnDemandLoading) ++ allowRootCertOnDemandLoading = false; + } + + /*! +@@ -2470,6 +2474,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri + ptr->sessionProtocol = global->sessionProtocol; + ptr->ciphers = global->ciphers; + ptr->caCertificates = global->caCertificates; ++ ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading; + ptr->protocol = global->protocol; + ptr->peerVerifyMode = global->peerVerifyMode; + ptr->peerVerifyDepth = global->peerVerifyDepth; +diff --git a/qtbase/src/network/ssl/qsslsocket_mac.cpp b/qtbase/src/network/ssl/qsslsocket_mac.cpp +index 77e847e972..e38a5e75de 100644 +--- a/qtbase/src/network/ssl/qsslsocket_mac.cpp ++++ b/qtbase/src/network/ssl/qsslsocket_mac.cpp +@@ -468,6 +468,7 @@ void QSslSocketBackendPrivate::disconnectFromHost() + if (context) { + if (!shutdown) { + SSLClose(context); ++ context.reset(nullptr); + shutdown = true; + } + } +diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp +index 0ace951c77..6a9a3ef3b3 100644 +--- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp ++++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols.cpp +@@ -499,9 +499,7 @@ DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return) + DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG) + DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return) + DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return) +-#ifndef OPENSSL_NO_DEPRECATED_3_0 + DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return) +-#endif // OPENSSL_NO_DEPRECATED_3_0 + DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return) + + #ifndef OPENSSL_NO_EC +@@ -1220,9 +1218,7 @@ bool q_resolveOpenSslSymbols() + RESOLVEFUNC(DH_free) + RESOLVEFUNC(d2i_DHparams) + RESOLVEFUNC(i2d_DHparams) +-#ifndef OPENSSL_NO_DEPRECATED_3_0 + RESOLVEFUNC(DH_check) +-#endif // OPENSSL_NO_DEPRECATED_3_0 + RESOLVEFUNC(BN_bin2bn) + + #ifndef OPENSSL_NO_EC +diff --git a/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h b/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h +index 5e9faae291..bf165f67ad 100644 +--- a/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h ++++ b/qtbase/src/network/ssl/qsslsocket_openssl_symbols_p.h +@@ -598,10 +598,7 @@ DH *q_DH_new(); + void q_DH_free(DH *dh); + DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length); + int q_i2d_DHparams(DH *a, unsigned char **p); +- +-#ifndef OPENSSL_NO_DEPRECATED_3_0 + int q_DH_check(DH *dh, int *codes); +-#endif // OPENSSL_NO_DEPRECATED_3_0 + + BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); + #define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh) +diff --git a/qtbase/src/network/ssl/qsslsocket_schannel.cpp b/qtbase/src/network/ssl/qsslsocket_schannel.cpp +index c956ce3c2b..d1b23af29b 100644 +--- a/qtbase/src/network/ssl/qsslsocket_schannel.cpp ++++ b/qtbase/src/network/ssl/qsslsocket_schannel.cpp +@@ -1880,6 +1880,28 @@ bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext) + if (configuration.peerVerifyDepth > 0 && DWORD(configuration.peerVerifyDepth) < verifyDepth) + verifyDepth = DWORD(configuration.peerVerifyDepth); + ++ const auto &caCertificates = q->sslConfiguration().caCertificates(); ++ ++ if (!rootCertOnDemandLoadingAllowed() ++ && !(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) ++ && (q->peerVerifyMode() == QSslSocket::VerifyPeer ++ || (isClient && q->peerVerifyMode() == QSslSocket::AutoVerifyPeer))) { ++ // When verifying a peer Windows "helpfully" builds a chain that ++ // may include roots from the system store. But we don't want that if ++ // the user has set their own CA certificates. ++ // Since Windows claims this is not a partial chain the root is included ++ // and we have to check that it is one of our configured CAs. ++ CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1]; ++ QSslCertificate certificate = getCertificateFromChainElement(element); ++ if (!caCertificates.contains(certificate)) { ++ auto error = QSslError(QSslError::CertificateUntrusted, certificate); ++ sslErrors += error; ++ emit q->peerVerifyError(error); ++ if (q->state() != QAbstractSocket::ConnectedState) ++ return false; ++ } ++ } ++ + for (DWORD i = 0; i < verifyDepth; i++) { + CERT_CHAIN_ELEMENT *element = chain->rgpElement[i]; + QSslCertificate certificate = getCertificateFromChainElement(element); +diff --git a/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h b/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h +index bf37d07fd8..dbd42fb799 100644 +--- a/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h ++++ b/qtbase/src/platformsupport/eglconvenience/qt_egl_p.h +@@ -61,7 +61,11 @@ + # if !defined(Q_OS_INTEGRITY) + # define WIN_INTERFACE_CUSTOM // NV + # endif // Q_OS_INTEGRITY +-#endif // QT_EGL_NO_X11 ++#else // QT_EGL_NO_X11 ++// If one has an eglplatform.h with https://github.com/KhronosGroup/EGL-Registry/pull/130 ++// that needs USE_X11 to be defined. ++# define USE_X11 ++#endif + + #ifdef QT_EGL_WAYLAND + # define WAYLAND // NV +diff --git a/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +index 159b490ce0..00aa80cd58 100644 +--- a/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp ++++ b/qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +@@ -567,6 +567,8 @@ void QFontconfigDatabase::populateFontDatabase() + fonts = FcFontList(nullptr, pattern, os); + FcObjectSetDestroy(os); + FcPatternDestroy(pattern); ++ if (!fonts) ++ return; + } + + for (int i = 0; i < fonts->nfont; i++) +diff --git a/qtbase/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/qtbase/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp +index 09d2d916fe..0e6fe5eb84 100644 +--- a/qtbase/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp ++++ b/qtbase/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp +@@ -1471,36 +1471,70 @@ QT_WARNING_POP + return fontEngine; + } + +-static QList getTrueTypeFontOffsets(const uchar *fontData) ++static QList getTrueTypeFontOffsets(const uchar *fontData, const uchar *fileEndSentinel) + { + QList offsets; +- const quint32 headerTag = *reinterpret_cast(fontData); ++ if (fileEndSentinel - fontData < 12) { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ return offsets; ++ } ++ ++ const quint32 headerTag = qFromUnaligned(fontData); + if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) { + if (headerTag != MAKE_TAG(0, 1, 0, 0) + && headerTag != MAKE_TAG('O', 'T', 'T', 'O') + && headerTag != MAKE_TAG('t', 'r', 'u', 'e') +- && headerTag != MAKE_TAG('t', 'y', 'p', '1')) ++ && headerTag != MAKE_TAG('t', 'y', 'p', '1')) { + return offsets; ++ } + offsets << 0; + return offsets; + } ++ ++ const quint32 maximumNumFonts = 0xffff; + const quint32 numFonts = qFromBigEndian(fontData + 8); +- for (uint i = 0; i < numFonts; ++i) { +- offsets << qFromBigEndian(fontData + 12 + i * 4); ++ if (numFonts > maximumNumFonts) { ++ qCWarning(lcQpaFonts) << "Font collection of" << numFonts << "fonts is too large. Aborting."; ++ return offsets; + } ++ ++ if (quintptr(fileEndSentinel - fontData) > 12 + (numFonts - 1) * 4) { ++ for (quint32 i = 0; i < numFonts; ++i) ++ offsets << qFromBigEndian(fontData + 12 + i * 4); ++ } else { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ } ++ + return offsets; + } + +-static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length) ++static void getFontTable(const uchar *fileBegin, const uchar *fileEndSentinel, const uchar *data, quint32 tag, const uchar **table, quint32 *length) + { +- const quint16 numTables = qFromBigEndian(data + 4); +- for (uint i = 0; i < numTables; ++i) { +- const quint32 offset = 12 + 16 * i; +- if (*reinterpret_cast(data + offset) == tag) { +- *table = fileBegin + qFromBigEndian(data + offset + 8); +- *length = qFromBigEndian(data + offset + 12); +- return; ++ if (fileEndSentinel - data >= 6) { ++ const quint16 numTables = qFromBigEndian(data + 4); ++ if (fileEndSentinel - data >= 28 + 16 * (numTables - 1)) { ++ for (quint32 i = 0; i < numTables; ++i) { ++ const quint32 offset = 12 + 16 * i; ++ if (qFromUnaligned(data + offset) == tag) { ++ const quint32 tableOffset = qFromBigEndian(data + offset + 8); ++ if (quintptr(fileEndSentinel - fileBegin) <= tableOffset) { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ break; ++ } ++ *table = fileBegin + tableOffset; ++ *length = qFromBigEndian(data + offset + 12); ++ if (quintptr(fileEndSentinel - *table) < *length) { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ break; ++ } ++ return; ++ } ++ } ++ } else { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; + } ++ } else { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; + } + *table = 0; + *length = 0; +@@ -1513,8 +1547,9 @@ static void getFamiliesAndSignatures(const QByteArray &fontData, + QVector *values) + { + const uchar *data = reinterpret_cast(fontData.constData()); ++ const uchar *dataEndSentinel = data + fontData.size(); + +- QList offsets = getTrueTypeFontOffsets(data); ++ QList offsets = getTrueTypeFontOffsets(data, dataEndSentinel); + if (offsets.isEmpty()) + return; + +@@ -1522,7 +1557,7 @@ static void getFamiliesAndSignatures(const QByteArray &fontData, + const uchar *font = data + offsets.at(i); + const uchar *table; + quint32 length; +- getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); ++ getFontTable(data, dataEndSentinel, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); + if (!table) + continue; + QFontNames names = qt_getCanonicalFontNames(table, length); +@@ -1532,7 +1567,7 @@ static void getFamiliesAndSignatures(const QByteArray &fontData, + families->append(std::move(names)); + + if (values || signatures) +- getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); ++ getFontTable(data, dataEndSentinel, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); + + if (values) { + QFontValues fontValues; +diff --git a/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp b/qtbase/src/platformsupport/input/xkbcommon/qxkbcommon.cpp +index 7bfacf4abe..1c7c15d5d7 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 + +@@ -273,10 +274,14 @@ static constexpr const auto KeyTbl = qMakeArray( + Xkb2Qt, + Xkb2Qt, + Xkb2Qt, ++/* The following four XKB_KEY_dead keys got removed in libxkbcommon 1.6.0 ++ The define check is kind of version check here. */ ++#ifdef XKB_KEY_dead_lowline + Xkb2Qt, + Xkb2Qt, + Xkb2Qt, + Xkb2Qt, ++#endif + + // Special keys from X.org - This include multimedia keys, + // wireless/bluetooth/uwb keys, special launcher keys, etc. +@@ -754,6 +759,8 @@ xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keyco + { + xkb_layout_index_t layout; + xkb_keysym_t sym = XKB_KEY_NoSymbol; ++ if (!state) ++ return sym; + xkb_keymap *keymap = xkb_state_get_keymap(state); + const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, keycode); + const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, keycode); +diff --git a/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +index 9153fd20bb..26537ff892 100644 +--- a/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp ++++ b/qtbase/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +@@ -194,6 +194,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const + " \n" + " \n" + " \n" ++ " \n" ++ " \n" ++ " \n" + " \n" + ); + +@@ -913,8 +916,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) + } + case QAccessible::NameChanged: { + if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) { +- QString path = pathForInterface(event->accessibleInterface()); +- QVariantList args = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); ++ QAccessibleInterface *iface = event->accessibleInterface(); ++ if (!iface) { ++ qCDebug(lcAccessibilityAtspi, ++ "NameChanged event from invalid accessible."); ++ return; ++ } ++ ++ QString path = pathForInterface(iface); ++ QVariantList args = packDBusSignalArguments( ++ QLatin1String("accessible-name"), 0, 0, ++ QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("PropertyChange"), args); + } +@@ -922,8 +934,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) + } + case QAccessible::DescriptionChanged: { + if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) { +- QString path = pathForInterface(event->accessibleInterface()); +- QVariantList args = packDBusSignalArguments(QLatin1String("accessible-description"), 0, 0, variantForPath(path)); ++ QAccessibleInterface *iface = event->accessibleInterface(); ++ if (!iface) { ++ qCDebug(lcAccessibilityAtspi, ++ "DescriptionChanged event from invalid accessible."); ++ return; ++ } ++ ++ QString path = pathForInterface(iface); ++ QVariantList args = packDBusSignalArguments( ++ QLatin1String("accessible-description"), 0, 0, ++ QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Description)))); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("PropertyChange"), args); + } +@@ -1038,7 +1059,9 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) + // Combo Box with AT-SPI likes to be special + // It requires a name-change to update caches and then selection-changed + QString path = pathForInterface(iface); +- QVariantList args1 = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); ++ QVariantList args1 = packDBusSignalArguments( ++ QLatin1String("accessible-name"), 0, 0, ++ QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("PropertyChange"), args1); + QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0)))); +@@ -1358,6 +1381,26 @@ void AtSpiAdaptor::registerApplication() + delete registry; + } + ++namespace { ++QString accessibleIdForAccessible(QAccessibleInterface *accessible) ++{ ++ QString result; ++ while (accessible) { ++ if (!result.isEmpty()) ++ result.prepend(QLatin1Char('.')); ++ if (auto obj = accessible->object()) { ++ const QString name = obj->objectName(); ++ if (!name.isEmpty()) ++ result.prepend(name); ++ else ++ result.prepend(QString::fromUtf8(obj->metaObject()->className())); ++ } ++ accessible = accessible->parent(); ++ } ++ return result; ++} ++} // namespace ++ + // Accessible + bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) + { +@@ -1441,6 +1484,9 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS + children << ref; + } + connection.send(message.createReply(QVariant::fromValue(children))); ++ } else if (function == QLatin1String("GetAccessibleId")) { ++ sendReply(connection, message, ++ QVariant::fromValue(QDBusVariant(accessibleIdForAccessible(interface)))); + } else { + qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path(); + return false; +@@ -1560,11 +1606,12 @@ bool AtSpiAdaptor::inheritsQAction(QObject *object) + // Component + static QAccessibleInterface * getWindow(QAccessibleInterface * interface) + { +- if (interface->role() == QAccessible::Window) ++ if (interface->role() == QAccessible::Dialog || interface->role() == QAccessible::Window) + return interface; + + QAccessibleInterface * parent = interface->parent(); +- while (parent && parent->role() != QAccessible::Window) ++ while (parent && parent->role() != QAccessible::Dialog ++ && parent->role() != QAccessible::Window) + parent = parent->parent(); + + return parent; +@@ -1582,7 +1629,7 @@ static QRect getRelativeRect(QAccessibleInterface *interface) + wr = window->rect(); + + cr.setX(cr.x() - wr.x()); +- cr.setY(cr.x() - wr.y()); ++ cr.setY(cr.y() - wr.y()); + } + return cr; + } +@@ -1836,7 +1883,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString + uint coordType = message.arguments().at(2).toUInt(); + if (coordType == ATSPI_COORD_TYPE_WINDOW) { + QWindow *win = interface->window(); +- point -= QPoint(win->x(), win->y()); ++ point += QPoint(win->x(), win->y()); + } + int offset = interface->textInterface()->offsetAtPoint(point); + sendReply(connection, message, offset); +diff --git a/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp b/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp +index fdc8cd3198..b17e1749c8 100644 +--- a/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp ++++ b/qtbase/src/platformsupport/linuxaccessibility/bridge.cpp +@@ -229,7 +229,11 @@ static RoleMapping map[] = { + //: Role of an accessible object + { QAccessible::ButtonDropDown, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down") }, + //: Role of an accessible object ++#if ATSPI_ROLE_COUNT > 130 ++ { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON_MENU, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") }, ++#else + { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") }, ++#endif + //: Role of an accessible object - a button that expands a grid. + { QAccessible::ButtonDropGrid, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down grid") }, + //: Role of an accessible object - blank space between other objects. +diff --git a/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp b/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp +index 45ddc8e496..cc734abc63 100644 +--- a/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp ++++ b/qtbase/src/platformsupport/linuxaccessibility/dbusconnection.cpp +@@ -69,6 +69,21 @@ QT_BEGIN_NAMESPACE + DBusConnection::DBusConnection(QObject *parent) + : QObject(parent), m_a11yConnection(QString()), m_enabled(false) + { ++ // If the bus is explicitly set via env var it overrides everything else. ++ QByteArray addressEnv = qgetenv("AT_SPI_BUS_ADDRESS"); ++ if (!addressEnv.isEmpty()) { ++ // Only connect on next loop run, connections to our enabled signal are ++ // only established after the ctor returns. ++ QMetaObject::invokeMethod( ++ this, ++ [this, addressEnv] { ++ m_enabled = true; ++ connectA11yBus(QString::fromLocal8Bit(addressEnv)); ++ }, ++ Qt::QueuedConnection); ++ return; ++ } ++ + // Start monitoring if "org.a11y.Bus" is registered as DBus service. + QDBusConnection c = QDBusConnection::sessionBus(); + if (!c.isConnected()) { +diff --git a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp +index f0d1722c95..47ef7d2b5c 100644 +--- a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp ++++ b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp +@@ -51,6 +51,9 @@ + #include + #include + ++#include ++#include ++ + #if QT_CONFIG(dbus) + // These QtCore includes are needed for xdg-desktop-portal support + #include +@@ -58,6 +61,8 @@ + #include + #include + ++#include ++ + #include + #include + #include +@@ -205,8 +210,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) + // handle_token (s) - A string that will be used as the last element of the @handle. + // writable (b) - Whether to allow the chosen application to write to the file. + +-#ifdef O_PATH +- const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH); ++ const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY); + if (fd != -1) { + QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), +@@ -216,16 +220,13 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) + QDBusUnixFileDescriptor descriptor; + descriptor.giveFileDescriptor(fd); + +- const QVariantMap options = {{QLatin1String("writable"), true}}; ++ const QVariantMap options = {}; + + // FIXME parent_window_id + message << QString() << QVariant::fromValue(descriptor) << options; + + return QDBusConnection::sessionBus().call(message); + } +-#else +- Q_UNUSED(url) +-#endif + + return QDBusMessage::createError(QDBusError::InternalError, qt_error_string()); + } +@@ -298,8 +299,135 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) + + return QDBusConnection::sessionBus().call(message); + } ++ ++namespace { ++struct XDGDesktopColor ++{ ++ double r = 0; ++ double g = 0; ++ double b = 0; ++ ++ QColor toQColor() const ++ { ++ constexpr auto rgbMax = 255; ++ return { static_cast(r * rgbMax), static_cast(g * rgbMax), ++ static_cast(b * rgbMax) }; ++ } ++}; ++ ++const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) ++{ ++ argument.beginStructure(); ++ argument >> myStruct.r >> myStruct.g >> myStruct.b; ++ argument.endStructure(); ++ return argument; ++} ++ ++class XdgDesktopPortalColorPicker : public QPlatformServiceColorPicker ++{ ++ Q_OBJECT ++public: ++ XdgDesktopPortalColorPicker(const QString &parentWindowId, QWindow *parent) ++ : QPlatformServiceColorPicker(parent), m_parentWindowId(parentWindowId) ++ { ++ } ++ ++ void pickColor() override ++ { ++ // DBus signature: ++ // PickColor (IN s parent_window, ++ // IN a{sv} options ++ // OUT o handle) ++ // Options: ++ // handle_token (s) - A string that will be used as the last element of the @handle. ++ ++ QDBusMessage message = QDBusMessage::createMethodCall( ++ QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), ++ QStringLiteral("org.freedesktop.portal.Screenshot"), QStringLiteral("PickColor")); ++ message << m_parentWindowId << QVariantMap(); ++ ++ QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); ++ auto watcher = new QDBusPendingCallWatcher(pendingCall, this); ++ connect(watcher, &QDBusPendingCallWatcher::finished, this, ++ [this](QDBusPendingCallWatcher *watcher) { ++ watcher->deleteLater(); ++ QDBusPendingReply reply = *watcher; ++ if (reply.isError()) { ++ qWarning("DBus call to pick color failed: %s", ++ qPrintable(reply.error().message())); ++ Q_EMIT colorPicked({}); ++ } else { ++ QDBusConnection::sessionBus().connect( ++ QStringLiteral("org.freedesktop.portal.Desktop"), reply.value().path(), ++ QStringLiteral("org.freedesktop.portal.Request"), QStringLiteral("Response"), this, ++ // clang-format off ++ SLOT(gotColorResponse(uint,QVariantMap)) ++ // clang-format on ++ ); ++ } ++ }); ++ } ++ ++private Q_SLOTS: ++ void gotColorResponse(uint result, const QVariantMap &map) ++ { ++ if (result != 0) ++ return; ++ XDGDesktopColor color{}; ++ map.value(QStringLiteral("color")).value() >> color; ++ Q_EMIT colorPicked(color.toQColor()); ++ deleteLater(); ++ } ++ ++private: ++ const QString m_parentWindowId; ++}; ++} // namespace ++ + #endif // QT_CONFIG(dbus) + ++QGenericUnixServices::QGenericUnixServices() ++{ ++#if QT_CONFIG(dbus) ++ if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { ++ return; ++ } ++ QDBusMessage message = QDBusMessage::createMethodCall( ++ QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), ++ QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get")); ++ message << QStringLiteral("org.freedesktop.portal.Screenshot") ++ << QStringLiteral("version"); ++ ++ QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); ++ auto watcher = new QDBusPendingCallWatcher(pendingCall); ++ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, ++ [this](QDBusPendingCallWatcher *watcher) { ++ watcher->deleteLater(); ++ QDBusPendingReply reply = *watcher; ++ if (!reply.isError() && reply.value().toUInt() >= 2) ++ m_hasScreenshotPortalWithColorPicking = true; ++ }); ++ ++#endif ++} ++ ++QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) ++{ ++#if QT_CONFIG(dbus) ++ // Make double sure that we are in a wayland environment. In particular check ++ // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. ++ // Outside wayland we'll rather rely on other means than the XDG desktop portal. ++ if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") ++ || QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { ++ return new XdgDesktopPortalColorPicker(portalWindowIdentifier(parent), parent); ++ } ++ return nullptr; ++#else ++ Q_UNUSED(parent); ++ return nullptr; ++#endif ++} ++ + QByteArray QGenericUnixServices::desktopEnvironment() const + { + static const QByteArray result = detectDesktopEnvironment(); +@@ -354,6 +482,8 @@ bool QGenericUnixServices::openDocument(const QUrl &url) + } + + #else ++QGenericUnixServices::QGenericUnixServices() = default; ++ + QByteArray QGenericUnixServices::desktopEnvironment() const + { + return QByteArrayLiteral("UNKNOWN"); +@@ -373,6 +503,30 @@ bool QGenericUnixServices::openDocument(const QUrl &url) + return false; + } + ++QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) ++{ ++ Q_UNUSED(parent); ++ return nullptr; ++} ++ + #endif // QT_NO_MULTIPROCESS + ++QString QGenericUnixServices::portalWindowIdentifier(QWindow *window) ++{ ++ if (QGuiApplication::platformName() == QLatin1String("xcb")) ++ return QStringLiteral("x11:") + QString::number(window->winId(), 16); ++ return QString(); ++} ++ ++bool QGenericUnixServices::hasCapability(Capability capability) const ++{ ++ switch (capability) { ++ case Capability::ColorPicking: ++ return m_hasScreenshotPortalWithColorPicking; ++ } ++ return false; ++} ++ + QT_END_NAMESPACE ++ ++#include "qgenericunixservices.moc" +diff --git a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h +index 8ac3de6f03..30924e64bd 100644 +--- a/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h ++++ b/qtbase/src/platformsupport/services/genericunix/qgenericunixservices_p.h +@@ -59,16 +59,21 @@ QT_BEGIN_NAMESPACE + class QGenericUnixServices : public QPlatformServices + { + public: +- QGenericUnixServices() {} ++ QGenericUnixServices(); + + QByteArray desktopEnvironment() const override; + ++ bool hasCapability(Capability capability) const override; + bool openUrl(const QUrl &url) override; + bool openDocument(const QUrl &url) override; ++ QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr) override; ++ ++ virtual QString portalWindowIdentifier(QWindow *window); + + private: + QString m_webBrowser; + QString m_documentLauncher; ++ bool m_hasScreenshotPortalWithColorPicking = false; + }; + + QT_END_NAMESPACE +diff --git a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp +index 09470bccc6..cc7c7d4d8a 100644 +--- a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp ++++ b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection.cpp +@@ -69,6 +69,7 @@ const QString MenuBarPath = QLatin1String("/MenuBar"); + */ + QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &serviceName) + : QObject(parent) ++ , m_serviceName(serviceName) + , m_connection(serviceName.isNull() ? QDBusConnection::sessionBus() + : QDBusConnection::connectToBus(QDBusConnection::SessionBus, serviceName)) + , m_dbusWatcher(new QDBusServiceWatcher(StatusNotifierWatcherService, m_connection, QDBusServiceWatcher::WatchForRegistration, this)) +@@ -83,6 +84,12 @@ QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &service + #endif + } + ++QDBusMenuConnection::~QDBusMenuConnection() ++{ ++ if (!m_serviceName.isEmpty() && m_connection.isConnected()) ++ QDBusConnection::disconnectFromBus(m_serviceName); ++} ++ + void QDBusMenuConnection::dbusError(const QDBusError &error) + { + qWarning() << "QDBusTrayIcon encountered a D-Bus error:" << error; +@@ -105,13 +112,7 @@ void QDBusMenuConnection::unregisterTrayIconMenu(QDBusTrayIcon *item) + + bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) + { +- bool success = connection().registerService(item->instanceId()); +- if (!success) { +- qWarning() << "failed to register service" << item->instanceId(); +- return false; +- } +- +- success = connection().registerObject(StatusNotifierItemPath, item); ++ bool success = connection().registerObject(StatusNotifierItemPath, item); + if (!success) { + unregisterTrayIcon(item); + qWarning() << "failed to register" << item->instanceId() << StatusNotifierItemPath; +@@ -126,21 +127,18 @@ bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) + + bool QDBusMenuConnection::registerTrayIconWithWatcher(QDBusTrayIcon *item) + { ++ Q_UNUSED(item); + QDBusMessage registerMethod = QDBusMessage::createMethodCall( + StatusNotifierWatcherService, StatusNotifierWatcherPath, StatusNotifierWatcherService, + QLatin1String("RegisterStatusNotifierItem")); +- registerMethod.setArguments(QVariantList() << item->instanceId()); ++ registerMethod.setArguments(QVariantList() << m_connection.baseService()); + return m_connection.callWithCallback(registerMethod, this, SIGNAL(trayIconRegistered()), SLOT(dbusError(QDBusError))); + } + +-bool QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) ++void QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) + { + unregisterTrayIconMenu(item); + connection().unregisterObject(StatusNotifierItemPath); +- bool success = connection().unregisterService(item->instanceId()); +- if (!success) +- qWarning() << "failed to unregister service" << item->instanceId(); +- return success; + } + #endif // QT_NO_SYSTEMTRAYICON + +diff --git a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h +index f484795fbb..97bdfabb85 100644 +--- a/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h ++++ b/qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenuconnection_p.h +@@ -70,6 +70,7 @@ class QDBusMenuConnection : public QObject + + public: + QDBusMenuConnection(QObject *parent = nullptr, const QString &serviceName = QString()); ++ ~QDBusMenuConnection(); + QDBusConnection connection() const { return m_connection; } + QDBusServiceWatcher *dbusWatcher() const { return m_dbusWatcher; } + bool isStatusNotifierHostRegistered() const { return m_statusNotifierHostRegistered; } +@@ -78,7 +79,7 @@ public: + void unregisterTrayIconMenu(QDBusTrayIcon *item); + bool registerTrayIcon(QDBusTrayIcon *item); + bool registerTrayIconWithWatcher(QDBusTrayIcon *item); +- bool unregisterTrayIcon(QDBusTrayIcon *item); ++ void unregisterTrayIcon(QDBusTrayIcon *item); + #endif // QT_NO_SYSTEMTRAYICON + + Q_SIGNALS: +@@ -90,6 +91,7 @@ private Q_SLOTS: + void dbusError(const QDBusError &error); + + private: ++ QString m_serviceName; + QDBusConnection m_connection; + QDBusServiceWatcher *m_dbusWatcher; + bool m_statusNotifierHostRegistered; +diff --git a/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp b/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp +index cb1b39db64..6e01af052c 100644 +--- a/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp ++++ b/qtbase/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp +@@ -755,6 +755,9 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const + return QVariant(QChar(0x2022)); + case QPlatformTheme::UiEffects: + return QVariant(int(HoverEffect)); ++ case QPlatformTheme::ButtonPressKeys: ++ return QVariant::fromValue( ++ QList({ Qt::Key_Space, Qt::Key_Return, Qt::Key_Enter, Qt::Key_Select })); + default: + break; + } +diff --git a/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp b/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp +index 8038e1a3c3..79541fe636 100644 +--- a/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp ++++ b/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper.cpp +@@ -53,7 +53,6 @@ QWindowsUiaWrapper::QWindowsUiaWrapper() + m_pUiaHostProviderFromHwnd = reinterpret_cast(uiaLib.resolve("UiaHostProviderFromHwnd")); + m_pUiaRaiseAutomationPropertyChangedEvent = reinterpret_cast(uiaLib.resolve("UiaRaiseAutomationPropertyChangedEvent")); + m_pUiaRaiseAutomationEvent = reinterpret_cast(uiaLib.resolve("UiaRaiseAutomationEvent")); +- m_pUiaRaiseNotificationEvent = reinterpret_cast(uiaLib.resolve("UiaRaiseNotificationEvent")); + m_pUiaClientsAreListening = reinterpret_cast(uiaLib.resolve("UiaClientsAreListening")); + } + } +@@ -69,7 +68,7 @@ QWindowsUiaWrapper *QWindowsUiaWrapper::instance() + return &wrapper; + } + +-// True if most symbols resolved (UiaRaiseNotificationEvent is optional). ++// True if all symbols resolved. + BOOL QWindowsUiaWrapper::ready() + { + return m_pUiaReturnRawElementProvider +@@ -114,12 +113,5 @@ HRESULT QWindowsUiaWrapper::raiseAutomationEvent(IRawElementProviderSimple *pPro + return m_pUiaRaiseAutomationEvent(pProvider, id); + } + +-HRESULT QWindowsUiaWrapper::raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId) +-{ +- if (!m_pUiaRaiseNotificationEvent) +- return UIA_E_NOTSUPPORTED; +- return m_pUiaRaiseNotificationEvent(provider, notificationKind, notificationProcessing, displayString, activityId); +-} +- + QT_END_NAMESPACE + +diff --git a/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h b/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h +index 9208acbc31..3ebc3008d3 100644 +--- a/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h ++++ b/qtbase/src/platformsupport/windowsuiautomation/qwindowsuiawrapper_p.h +@@ -80,20 +80,17 @@ public: + HRESULT hostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider); + HRESULT raiseAutomationPropertyChangedEvent(IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue); + HRESULT raiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id); +- HRESULT raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId); + + private: + typedef LRESULT (WINAPI *PtrUiaReturnRawElementProvider)(HWND, WPARAM, LPARAM, IRawElementProviderSimple *); + typedef HRESULT (WINAPI *PtrUiaHostProviderFromHwnd)(HWND, IRawElementProviderSimple **); + typedef HRESULT (WINAPI *PtrUiaRaiseAutomationPropertyChangedEvent)(IRawElementProviderSimple *, PROPERTYID, VARIANT, VARIANT); + typedef HRESULT (WINAPI *PtrUiaRaiseAutomationEvent)(IRawElementProviderSimple *, EVENTID); +- typedef HRESULT (WINAPI *PtrUiaRaiseNotificationEvent)(IRawElementProviderSimple *, NotificationKind, NotificationProcessing, BSTR, BSTR); + typedef BOOL (WINAPI *PtrUiaClientsAreListening)(); + PtrUiaReturnRawElementProvider m_pUiaReturnRawElementProvider = nullptr; + PtrUiaHostProviderFromHwnd m_pUiaHostProviderFromHwnd = nullptr; + PtrUiaRaiseAutomationPropertyChangedEvent m_pUiaRaiseAutomationPropertyChangedEvent = nullptr; + PtrUiaRaiseAutomationEvent m_pUiaRaiseAutomationEvent = nullptr; +- PtrUiaRaiseNotificationEvent m_pUiaRaiseNotificationEvent = nullptr; + PtrUiaClientsAreListening m_pUiaClientsAreListening = nullptr; + }; + +diff --git a/qtbase/src/platformsupport/windowsuiautomation/uiatypes_p.h b/qtbase/src/platformsupport/windowsuiautomation/uiatypes_p.h +index 0d2e1161e4..afbc957094 100644 +--- a/qtbase/src/platformsupport/windowsuiautomation/uiatypes_p.h ++++ b/qtbase/src/platformsupport/windowsuiautomation/uiatypes_p.h +@@ -162,22 +162,6 @@ enum ExpandCollapseState { + ExpandCollapseState_LeafNode = 3 + }; + +-enum NotificationKind { +- NotificationKind_ItemAdded = 0, +- NotificationKind_ItemRemoved = 1, +- NotificationKind_ActionCompleted = 2, +- NotificationKind_ActionAborted = 3, +- NotificationKind_Other = 4 +-}; +- +-enum NotificationProcessing { +- NotificationProcessing_ImportantAll = 0, +- NotificationProcessing_ImportantMostRecent = 1, +- NotificationProcessing_All = 2, +- NotificationProcessing_MostRecent = 3, +- NotificationProcessing_CurrentThenMostRecent = 4 +-}; +- + struct UiaRect { + double left; + double top; +diff --git a/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml b/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml +index 9c67a38c57..30c326d06f 100644 +--- a/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml ++++ b/qtbase/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml +@@ -14,6 +14,12 @@ + + + ++ ++ ++ ++ ++ ++ + + + +diff --git a/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h b/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h +index 396a213aaa..31d5a71c41 100644 +--- a/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h ++++ b/qtbase/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h +@@ -112,6 +112,13 @@ public Q_SLOTS: // METHODS + return asyncCallWithArgumentList(QLatin1String("SetCursorLocation"), argumentList); + } + ++ inline QDBusPendingReply<> SetCursorLocationRelative(int x, int y, int w, int h) ++ { ++ QList argumentList; ++ argumentList << QVariant::fromValue(x) << QVariant::fromValue(y) << QVariant::fromValue(w) << QVariant::fromValue(h); ++ return asyncCallWithArgumentList(QLatin1String("SetCursorLocationRelative"), argumentList); ++ } ++ + inline QDBusPendingReply<> SetEngine(const QString &name) + { + QList argumentList; +diff --git a/qtbase/src/plugins/platforms/eglfs/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/uiautomation/qwindowsuiamainprovider.cpp b/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +index 754ded14f1..059df8d991 100644 +--- a/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp ++++ b/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +@@ -169,27 +169,11 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve + } + if (event->value().type() == QVariant::String) { + if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { +- +- // Tries to notify the change using UiaRaiseNotificationEvent(), which is only available on +- // Windows 10 version 1709 or newer. Otherwise uses UiaRaiseAutomationPropertyChangedEvent(). +- +- BSTR displayString = bStrFromQString(event->value().toString()); +- BSTR activityId = bStrFromQString(QString()); +- +- HRESULT hr = QWindowsUiaWrapper::instance()->raiseNotificationEvent(provider, NotificationKind_Other, +- NotificationProcessing_ImportantMostRecent, +- displayString, activityId); +- +- ::SysFreeString(displayString); +- ::SysFreeString(activityId); +- +- if (hr == static_cast(UIA_E_NOTSUPPORTED)) { +- VARIANT oldVal, newVal; +- clearVariant(&oldVal); +- setVariantString(event->value().toString(), &newVal); +- QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); +- ::SysFreeString(newVal.bstrVal); +- } ++ // Notifies changes in string values. ++ VARIANT oldVal, newVal; ++ clearVariant(&oldVal); ++ setVariantString(event->value().toString(), &newVal); ++ QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal); + } + } else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) { + if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp b/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp +index 780816605a..a769ddadbd 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp ++++ b/qtbase/src/plugins/platforms/xcb/qxcbatom.cpp +@@ -90,6 +90,8 @@ static const char *xcb_atomnames = { + + "_QT_CLOSE_CONNECTION\0" + ++ "_QT_GET_TIMESTAMP\0" ++ + "_MOTIF_WM_HINTS\0" + + "DTWM_IS_RUNNING\0" +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbatom.h b/qtbase/src/plugins/platforms/xcb/qxcbatom.h +index 9cf93ec314..1ce6cca573 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbatom.h ++++ b/qtbase/src/plugins/platforms/xcb/qxcbatom.h +@@ -91,6 +91,8 @@ public: + // Qt/XCB specific + _QT_CLOSE_CONNECTION, + ++ _QT_GET_TIMESTAMP, ++ + _MOTIF_WM_HINTS, + + DTWM_IS_RUNNING, +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp +index 730b2efbb9..631ade2ec7 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp ++++ b/qtbase/src/plugins/platforms/xcb/qxcbconnection.cpp +@@ -706,6 +706,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) + QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window); + if (virtualDesktop) + virtualDesktop->updateWorkArea(); ++ } else if (propertyNotify->atom == atom(QXcbAtom::_NET_SUPPORTED)) { ++ m_wmSupport->updateNetWMAtoms(); + } else { + HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); + } +@@ -802,8 +804,8 @@ xcb_timestamp_t QXcbConnection::getTimestamp() + { + // send a dummy event to myself to get the timestamp from X server. + xcb_window_t window = rootWindow(); +- xcb_atom_t dummyAtom = atom(QXcbAtom::CLIP_TEMPORARY); +- xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, window, dummyAtom, ++ xcb_atom_t dummyAtom = atom(QXcbAtom::_QT_GET_TIMESTAMP); ++ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, dummyAtom, + XCB_ATOM_INTEGER, 32, 0, nullptr); + + connection()->flush(); +@@ -835,8 +837,6 @@ xcb_timestamp_t QXcbConnection::getTimestamp() + xcb_timestamp_t timestamp = pn->time; + free(event); + +- xcb_delete_property(xcb_connection(), window, dummyAtom); +- + return timestamp; + } + +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +index 1ced02f31d..5c8298a49d 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp ++++ b/qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +@@ -1255,16 +1255,14 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD + if (Q_LIKELY(useValuators)) { + const qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.x(), physicalScreenArea.width()); + global.setX(value); +- // mapFromGlobal is ok for nested/embedded windows, but works only with whole-number QPoint; +- // so map it first, then add back the sub-pixel position +- local.setX(window->mapFromGlobal(QPoint(int(value), 0)).x() + (value - int(value))); ++ local.setX(xcbWindow->mapFromGlobal(QPoint(int(value), 0)).x() + (value - int(value))); + } + break; + case QXcbAtom::AbsY: + if (Q_LIKELY(useValuators)) { + qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.y(), physicalScreenArea.height()); + global.setY(value); +- local.setY(window->mapFromGlobal(QPoint(0, int(value))).y() + (value - int(value))); ++ local.setY(xcbWindow->mapFromGlobal(QPoint(0, int(value))).y() + (value - int(value))); + } + break; + case QXcbAtom::AbsPressure: +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp +index 4210bf428e..0670b6ebce 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp ++++ b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp +@@ -300,7 +300,7 @@ QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c) + #endif // !QT_NO_CURSOR + + QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) +- : QXcbObject(conn), m_screen(screen), m_gtkCursorThemeInitialized(false) ++ : QXcbObject(conn), m_screen(screen), m_gtkCursorThemeInitialized(false), m_callbackForPropertyRegistered(false) + { + #if QT_CONFIG(cursor) + // see NUM_BITMAPS in libXcursor/src/xcursorint.h +@@ -343,7 +343,7 @@ QXcbCursor::~QXcbCursor() + { + xcb_connection_t *conn = xcb_connection(); + +- if (m_gtkCursorThemeInitialized) { ++ if (m_callbackForPropertyRegistered) { + m_screen->xSettings()->removeCallbackForHandle(this); + } + +@@ -562,8 +562,10 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) + xcb_cursor_t cursor = XCB_NONE; + + #if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) +- if (m_screen->xSettings()->initialized()) ++ if (m_screen->xSettings()->initialized()) { + m_screen->xSettings()->registerCallbackForProperty("Gtk/CursorThemeName",cursorThemePropertyChanged,this); ++ m_callbackForPropertyRegistered = true; ++ } + + // Try Xcursor first + if (cshape >= 0 && cshape <= Qt::LastCursor) { +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.h b/qtbase/src/plugins/platforms/xcb/qxcbcursor.h +index 0b238823f0..82fb47e55d 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbcursor.h ++++ b/qtbase/src/plugins/platforms/xcb/qxcbcursor.h +@@ -122,6 +122,7 @@ private: + void *handle); + #endif + bool m_gtkCursorThemeInitialized; ++ bool m_callbackForPropertyRegistered; + }; + + QT_END_NAMESPACE +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp b/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp +index 76869ced60..02d2eebb56 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp ++++ b/qtbase/src/plugins/platforms/xcb/qxcbintegration.cpp +@@ -274,8 +274,7 @@ QPlatformWindow *QXcbIntegration::createForeignWindow(QWindow *window, WId nativ + #ifndef QT_NO_OPENGL + QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const + { +- QXcbScreen *screen = static_cast(context->screen()->handle()); +- QXcbGlIntegration *glIntegration = screen->connection()->glIntegration(); ++ QXcbGlIntegration *glIntegration = defaultConnection()->glIntegration(); + if (!glIntegration) { + qWarning("QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled"); + return nullptr; +diff --git a/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp b/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp +index bffad0fac5..c76c2d97c1 100644 +--- a/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp ++++ b/qtbase/src/plugins/platforms/xcb/qxcbwindow.cpp +@@ -299,11 +299,6 @@ void QXcbWindow::create() + return; + } + +- QPlatformWindow::setGeometry(rect); +- +- if (platformScreen != currentScreen) +- QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); +- + const QSize minimumSize = windowMinimumSize(); + if (rect.width() > 0 || rect.height() > 0) { + rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); +@@ -315,6 +310,11 @@ void QXcbWindow::create() + rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen())); + } + ++ QPlatformWindow::setGeometry(rect); ++ ++ if (platformScreen != currentScreen) ++ QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); ++ + xcb_window_t xcb_parent_id = platformScreen->root(); + if (parent()) { + xcb_parent_id = static_cast(parent())->xcb_window(); +@@ -1345,9 +1345,10 @@ void QXcbWindow::setWindowIcon(const QIcon &icon) + + if (!icon_data.isEmpty()) { + // Ignore icon exceeding maximum xcb request length +- if (size_t(icon_data.size()) > xcb_get_maximum_request_length(xcb_connection())) { +- qWarning("Ignoring window icon: Size %d exceeds maximum xcb request length %u.", +- icon_data.size(), xcb_get_maximum_request_length(xcb_connection())); ++ if (quint64(icon_data.size()) > quint64(xcb_get_maximum_request_length(xcb_connection()))) { ++ qWarning() << "Ignoring window icon" << icon_data.size() ++ << "exceeds maximum xcb request length" ++ << xcb_get_maximum_request_length(xcb_connection()); + return; + } + xcb_change_property(xcb_connection(), +@@ -1418,7 +1419,8 @@ void QXcbWindow::propagateSizeHints() + qMin(XCOORD_MAX, maximumSize.height())); + + if (sizeIncrement.width() > 0 || sizeIncrement.height() > 0) { +- xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height()); ++ if (!baseSize.isNull() && baseSize.isValid()) ++ xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height()); + xcb_icccm_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height()); + } + +diff --git a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp +index 2c72538387..8987e3efd0 100644 +--- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp ++++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp +@@ -102,15 +102,12 @@ const QDBusArgument &operator >>(const QDBusArgument &arg, QXdgDesktopPortalFile + class QXdgDesktopPortalFileDialogPrivate + { + public: +- QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog) ++ QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion) + : nativeFileDialog(nativeFileDialog) ++ , fileChooserPortalVersion(fileChooserPortalVersion) + { } + +- WId winId = 0; +- bool directoryMode = false; +- bool modal = false; +- bool multipleFiles = false; +- bool saveFile = false; ++ QEventLoop loop; + QString acceptLabel; + QString directory; + QString title; +@@ -122,11 +119,16 @@ public: + QString selectedNameFilter; + QStringList selectedFiles; + std::unique_ptr nativeFileDialog; ++ uint fileChooserPortalVersion = 0; ++ bool failedToOpen = false; ++ bool directoryMode = false; ++ bool multipleFiles = false; ++ bool saveFile = false; + }; + +-QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog) ++QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion) + : QPlatformFileDialogHelper() +- , d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog)) ++ , d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog, fileChooserPortalVersion)) + { + Q_D(QXdgDesktopPortalFileDialog); + +@@ -134,6 +136,9 @@ QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelp + connect(d->nativeFileDialog.get(), SIGNAL(accept()), this, SIGNAL(accept())); + connect(d->nativeFileDialog.get(), SIGNAL(reject()), this, SIGNAL(reject())); + } ++ ++ d->loop.connect(this, SIGNAL(accept()), SLOT(quit())); ++ d->loop.connect(this, SIGNAL(reject()), SLOT(quit())); + } + + QXdgDesktopPortalFileDialog::~QXdgDesktopPortalFileDialog() +@@ -177,7 +182,7 @@ void QXdgDesktopPortalFileDialog::initializeDialog() + setDirectory(options()->initialDirectory()); + } + +-void QXdgDesktopPortalFileDialog::openPortal() ++void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) + { + Q_D(QXdgDesktopPortalFileDialog); + +@@ -185,13 +190,13 @@ void QXdgDesktopPortalFileDialog::openPortal() + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.portal.FileChooser"), + d->saveFile ? QLatin1String("SaveFile") : QLatin1String("OpenFile")); +- QString parentWindowId = QLatin1String("x11:") + QString::number(d->winId, 16); ++ QString parentWindowId = QLatin1String("x11:") + QString::number(parent ? parent->winId() : 0, 16); + + QVariantMap options; + if (!d->acceptLabel.isEmpty()) + options.insert(QLatin1String("accept_label"), d->acceptLabel); + +- options.insert(QLatin1String("modal"), d->modal); ++ options.insert(QLatin1String("modal"), windowModality != Qt::NonModal); + options.insert(QLatin1String("multiple"), d->multipleFiles); + options.insert(QLatin1String("directory"), d->directoryMode); + +@@ -233,6 +238,9 @@ void QXdgDesktopPortalFileDialog::openPortal() + filter.name = mimeType.comment(); + filter.filterConditions = filterConditions; + ++ if (filter.name.isEmpty()) ++ filter.name = mimeTypefilter; ++ + filterList << filter; + + if (!d->selectedMimeTypeFilter.isEmpty() && d->selectedMimeTypeFilter == mimeTypefilter) +@@ -290,10 +298,18 @@ void QXdgDesktopPortalFileDialog::openPortal() + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); +- connect(watcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) { ++ connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher *watcher) { + QDBusPendingReply reply = *watcher; +- if (reply.isError()) { +- Q_EMIT reject(); ++ // Any error means the dialog is not shown and we need to fallback ++ d->failedToOpen = reply.isError(); ++ if (d->failedToOpen) { ++ if (d->nativeFileDialog) { ++ d->nativeFileDialog->show(windowFlags, windowModality, parent); ++ if (d->loop.isRunning()) ++ d->nativeFileDialog->exec(); ++ } else { ++ Q_EMIT reject(); ++ } + } else { + QDBusConnection::sessionBus().connect(nullptr, + reply.value().path(), +@@ -327,7 +343,7 @@ QUrl QXdgDesktopPortalFileDialog::directory() const + { + Q_D(const QXdgDesktopPortalFileDialog); + +- if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) ++ if (d->nativeFileDialog && useNativeFileDialog()) + return d->nativeFileDialog->directory(); + + return d->directory; +@@ -349,7 +365,7 @@ QList QXdgDesktopPortalFileDialog::selectedFiles() const + { + Q_D(const QXdgDesktopPortalFileDialog); + +- if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) ++ if (d->nativeFileDialog && useNativeFileDialog()) + return d->nativeFileDialog->selectedFiles(); + + QList files; +@@ -404,16 +420,13 @@ void QXdgDesktopPortalFileDialog::exec() + { + Q_D(QXdgDesktopPortalFileDialog); + +- if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) { ++ if (d->nativeFileDialog && useNativeFileDialog()) { + d->nativeFileDialog->exec(); + return; + } + + // HACK we have to avoid returning until we emit that the dialog was accepted or rejected +- QEventLoop loop; +- loop.connect(this, SIGNAL(accept()), SLOT(quit())); +- loop.connect(this, SIGNAL(reject()), SLOT(quit())); +- loop.exec(); ++ d->loop.exec(); + } + + void QXdgDesktopPortalFileDialog::hide() +@@ -430,13 +443,10 @@ bool QXdgDesktopPortalFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowMo + + initializeDialog(); + +- d->modal = windowModality != Qt::NonModal; +- d->winId = parent ? parent->winId() : 0; +- +- if (d->nativeFileDialog && (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly)) ++ if (d->nativeFileDialog && useNativeFileDialog(OpenFallback)) + return d->nativeFileDialog->show(windowFlags, windowModality, parent); + +- openPortal(); ++ openPortal(windowFlags, windowModality, parent); + + return true; + } +@@ -466,6 +476,23 @@ void QXdgDesktopPortalFileDialog::gotResponse(uint response, const QVariantMap & + } + } + ++bool QXdgDesktopPortalFileDialog::useNativeFileDialog(QXdgDesktopPortalFileDialog::FallbackType fallbackType) const ++{ ++ Q_D(const QXdgDesktopPortalFileDialog); ++ ++ if (d->failedToOpen && fallbackType != OpenFallback) ++ return true; ++ ++ if (d->fileChooserPortalVersion < 3) { ++ if (options()->fileMode() == QFileDialogOptions::Directory) ++ return true; ++ else if (options()->fileMode() == QFileDialogOptions::DirectoryOnly) ++ return true; ++ } ++ ++ return false; ++} ++ + QT_END_NAMESPACE + + #include "moc_qxdgdesktopportalfiledialog_p.cpp" +diff --git a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h +index 4f4de96ecf..65e22a5cf2 100644 +--- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h ++++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog_p.h +@@ -51,6 +51,11 @@ class QXdgDesktopPortalFileDialog : public QPlatformFileDialogHelper + Q_OBJECT + Q_DECLARE_PRIVATE(QXdgDesktopPortalFileDialog) + public: ++ enum FallbackType { ++ GenericFallback, ++ OpenFallback ++ }; ++ + enum ConditionType : uint { + GlobalPattern = 0, + MimeType = 1 +@@ -69,7 +74,7 @@ public: + }; + typedef QVector FilterList; + +- QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog = nullptr); ++ QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog = nullptr, uint fileChooserPortalVersion = 0); + ~QXdgDesktopPortalFileDialog(); + + bool defaultNameFilterDisables() const override; +@@ -92,7 +97,8 @@ private Q_SLOTS: + + private: + void initializeDialog(); +- void openPortal(); ++ void openPortal(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent); ++ bool useNativeFileDialog(FallbackType fallbackType = GenericFallback) const; + + QScopedPointer d_ptr; + }; +diff --git a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp +index 2fc3167fd5..b809503122 100644 +--- a/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp ++++ b/qtbase/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp +@@ -153,11 +153,12 @@ QPlatformDialogHelper* QXdgDesktopPortalTheme::createPlatformDialogHelper(Dialog + { + Q_D(const QXdgDesktopPortalTheme); + +- if (type == FileDialog) { ++ if (type == FileDialog && d->fileChooserPortalVersion) { + // Older versions of FileChooser portal don't support opening directories, therefore we fallback + // to native file dialog opened inside the sandbox to open a directory. +- if (d->fileChooserPortalVersion < 3 && d->baseTheme->usePlatformNativeDialog(type)) +- return new QXdgDesktopPortalFileDialog(static_cast(d->baseTheme->createPlatformDialogHelper(type))); ++ if (d->baseTheme->usePlatformNativeDialog(type)) ++ return new QXdgDesktopPortalFileDialog(static_cast(d->baseTheme->createPlatformDialogHelper(type)), ++ d->fileChooserPortalVersion); + + return new QXdgDesktopPortalFileDialog; + } +diff --git a/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp +index 6c522838df..353ea099d5 100644 +--- a/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp ++++ b/qtbase/src/plugins/sqldrivers/mysql/qsql_mysql.cpp +@@ -918,9 +918,9 @@ bool QMYSQLResult::prepare(const QString& query) + return false; + } + +- if (mysql_stmt_param_count(d->stmt) > 0) {// allocate memory for outvalues +- d->outBinds = new MYSQL_BIND[mysql_stmt_param_count(d->stmt)]; +- } ++ const auto paramCount = mysql_stmt_param_count(d->stmt); ++ if (paramCount > 0) // allocate memory for outvalues ++ d->outBinds = new MYSQL_BIND[paramCount](); + + setSelect(d->bindInValues()); + d->preparedQuery = true; +@@ -1365,20 +1365,20 @@ bool QMYSQLDriver::open(const QString& db, + } + + #if MYSQL_VERSION_ID >= 50007 +- if (mysql_get_client_version() >= 50503 && mysql_get_server_version(d->mysql) >= 50503) { +- // force the communication to be utf8mb4 (only utf8mb4 supports 4-byte characters) +- mysql_set_character_set(d->mysql, "utf8mb4"); ++ // force the communication to be utf8mb4 (only utf8mb4 supports 4-byte characters) ++ if (mysql_set_character_set(d->mysql, "utf8mb4")) { ++ // this failed, try forcing it to utf (BMP only) ++ if (mysql_set_character_set(d->mysql, "utf8")) ++ qWarning() << "MySQL: Unable to set the client character set to utf8."; + #if QT_CONFIG(textcodec) +- d->tc = QTextCodec::codecForName("UTF-8"); ++ else ++ d->tc = codec(d->mysql); + #endif +- } else +- { +- // force the communication to be utf8 +- mysql_set_character_set(d->mysql, "utf8"); ++ } + #if QT_CONFIG(textcodec) +- d->tc = codec(d->mysql); ++ else ++ d->tc = QTextCodec::codecForName("UTF-8"); + #endif +- } + #endif // MYSQL_VERSION_ID >= 50007 + + d->preparedQuerysEnabled = checkPreparedQueries(d->mysql); +diff --git a/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp +index 5f51de3843..6cac60d03d 100644 +--- a/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp ++++ b/qtbase/src/plugins/sqldrivers/odbc/qsql_odbc.cpp +@@ -92,23 +92,39 @@ inline static QString fromSQLTCHAR(const QVarLengthArray& input, int s + return result; + } + ++template ++void toSQLTCHARImpl(QVarLengthArray &result, const QString &input); // primary template undefined ++ ++template ++void do_append(QVarLengthArray &result, const Container &c) ++{ ++ result.append(reinterpret_cast(c.data()), c.size()); ++} ++ ++template <> ++void toSQLTCHARImpl<1>(QVarLengthArray &result, const QString &input) ++{ ++ const auto u8 = input.toUtf8(); ++ do_append(result, u8); ++} ++ ++template <> ++void toSQLTCHARImpl<2>(QVarLengthArray &result, const QString &input) ++{ ++ do_append(result, input); ++} ++ ++template <> ++void toSQLTCHARImpl<4>(QVarLengthArray &result, const QString &input) ++{ ++ const auto u32 = input.toUcs4(); ++ do_append(result, u32); ++} ++ + inline static QVarLengthArray toSQLTCHAR(const QString &input) + { + QVarLengthArray result; +- result.resize(input.size()); +- switch(sizeof(SQLTCHAR)) { +- case 1: +- memcpy(result.data(), input.toUtf8().data(), input.size()); +- break; +- case 2: +- memcpy(result.data(), input.unicode(), input.size() * 2); +- break; +- case 4: +- memcpy(result.data(), input.toUcs4().data(), input.size() * 4); +- break; +- default: +- qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR))); +- } ++ toSQLTCHARImpl(result, input); + result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't. + return result; + } +@@ -763,6 +779,14 @@ QChar QODBCDriverPrivate::quoteChar() + return quote; + } + ++static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr, const QString &val) ++{ ++ auto encoded = toSQLTCHAR(val); ++ return SQLSetConnectAttr(handle, attr, ++ encoded.data(), ++ SQLINTEGER(encoded.size() * sizeof(SQLTCHAR))); // size in bytes ++} ++ + + bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) + { +@@ -798,10 +822,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) + v = val.toUInt(); + r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) size_t(v), 0); + } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) { +- val.utf16(); // 0 terminate +- r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, +- toSQLTCHAR(val).data(), +- val.length()*sizeof(SQLTCHAR)); ++ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, val); + } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) { + if (val.toUpper() == QLatin1String("SQL_TRUE")) { + v = SQL_TRUE; +@@ -816,10 +837,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) + v = val.toUInt(); + r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) size_t(v), 0); + } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) { +- val.utf16(); // 0 terminate +- r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, +- toSQLTCHAR(val).data(), +- val.length()*sizeof(SQLTCHAR)); ++ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val); + } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) { + if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) { + v = SQL_OPT_TRACE_OFF; +@@ -1022,9 +1040,12 @@ bool QODBCResult::reset (const QString& query) + return false; + } + +- r = SQLExecDirect(d->hStmt, +- toSQLTCHAR(query).data(), +- (SQLINTEGER) query.length()); ++ { ++ auto encoded = toSQLTCHAR(query); ++ r = SQLExecDirect(d->hStmt, ++ encoded.data(), ++ SQLINTEGER(encoded.size())); ++ } + if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) { + setLastError(qMakeError(QCoreApplication::translate("QODBCResult", + "Unable to execute statement"), QSqlError::StatementError, d)); +@@ -1371,9 +1392,12 @@ bool QODBCResult::prepare(const QString& query) + return false; + } + +- r = SQLPrepare(d->hStmt, +- toSQLTCHAR(query).data(), +- (SQLINTEGER) query.length()); ++ { ++ auto encoded = toSQLTCHAR(query); ++ r = SQLPrepare(d->hStmt, ++ encoded.data(), ++ SQLINTEGER(encoded.size())); ++ } + + if (r != SQL_SUCCESS) { + setLastError(qMakeError(QCoreApplication::translate("QODBCResult", +@@ -1401,7 +1425,7 @@ bool QODBCResult::exec() + SQLCloseCursor(d->hStmt); + + QVector& values = boundValues(); +- QVector tmpStorage(values.count(), QByteArray()); // holds temporary buffers ++ QVector tmpStorage(values.count(), QByteArray()); // targets for SQLBindParameter() + QVarLengthArray indicators(values.count()); + memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN)); + +@@ -1580,35 +1604,36 @@ bool QODBCResult::exec() + case QVariant::String: + if (d->unicode) { + QByteArray &ba = tmpStorage[i]; +- QString str = val.toString(); ++ { ++ const auto encoded = toSQLTCHAR(val.toString()); ++ ba = QByteArray(reinterpret_cast(encoded.data()), ++ encoded.size() * sizeof(SQLTCHAR)); ++ } ++ + if (*ind != SQL_NULL_DATA) +- *ind = str.length() * sizeof(SQLTCHAR); +- int strSize = str.length() * sizeof(SQLTCHAR); ++ *ind = ba.size(); + + if (bindValueType(i) & QSql::Out) { +- const QVarLengthArray a(toSQLTCHAR(str)); +- ba = QByteArray((const char *)a.constData(), a.size() * sizeof(SQLTCHAR)); + r = SQLBindParameter(d->hStmt, + i + 1, + qParamType[bindValueType(i) & QSql::InOut], + SQL_C_TCHAR, +- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, ++ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, + 0, // god knows... don't change this! + 0, +- ba.data(), ++ const_cast(ba.constData()), // don't detach + ba.size(), + ind); + break; + } +- ba = QByteArray ((const char *)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR)); + r = SQLBindParameter(d->hStmt, + i + 1, + qParamType[bindValueType(i) & QSql::InOut], + SQL_C_TCHAR, +- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, +- strSize, ++ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR, ++ ba.size(), + 0, +- const_cast(ba.constData()), ++ const_cast(ba.constData()), // don't detach + ba.size(), + ind); + break; +@@ -1716,10 +1741,11 @@ bool QODBCResult::exec() + case QVariant::String: + if (d->unicode) { + if (bindValueType(i) & QSql::Out) { +- const QByteArray &first = tmpStorage.at(i); +- QVarLengthArray array; +- array.append((const SQLTCHAR *)first.constData(), first.size()); +- values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR)); ++ const QByteArray &bytes = tmpStorage.at(i); ++ const auto strSize = bytes.size() / int(sizeof(SQLTCHAR)); ++ QVarLengthArray string(strSize); ++ memcpy(string.data(), bytes.data(), strSize * sizeof(SQLTCHAR)); ++ values[i] = fromSQLTCHAR(string); + } + break; + } +@@ -1966,14 +1992,16 @@ bool QODBCDriver::open(const QString & db, + SQLSMALLINT cb; + QVarLengthArray connOut(1024); + memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR)); +- r = SQLDriverConnect(d->hDbc, +- NULL, +- toSQLTCHAR(connQStr).data(), +- (SQLSMALLINT)connQStr.length(), +- connOut.data(), +- 1024, +- &cb, +- /*SQL_DRIVER_NOPROMPT*/0); ++ { ++ auto encoded = toSQLTCHAR(connQStr); ++ r = SQLDriverConnect(d->hDbc, ++ nullptr, ++ encoded.data(), SQLSMALLINT(encoded.size()), ++ connOut.data(), ++ 1024, ++ &cb, ++ /*SQL_DRIVER_NOPROMPT*/0); ++ } + + if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) { + setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d)); +@@ -2352,17 +2380,15 @@ QStringList QODBCDriver::tables(QSql::TableType type) const + if (tableType.isEmpty()) + return tl; + +- QString joinedTableTypeString = tableType.join(QLatin1Char(',')); ++ { ++ auto joinedTableTypeString = toSQLTCHAR(tableType.join(u',')); + +- r = SQLTables(hStmt, +- NULL, +- 0, +- NULL, +- 0, +- NULL, +- 0, +- toSQLTCHAR(joinedTableTypeString).data(), +- joinedTableTypeString.length() /* characters, not bytes */); ++ r = SQLTables(hStmt, ++ nullptr, 0, ++ nullptr, 0, ++ nullptr, 0, ++ joinedTableTypeString.data(), joinedTableTypeString.size()); ++ } + + if (r != SQL_SUCCESS) + qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d); +@@ -2436,28 +2462,30 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, + SQL_IS_UINTEGER); +- r = SQLPrimaryKeys(hStmt, +- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), +- catalog.length(), +- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), +- schema.length(), +- toSQLTCHAR(table).data(), +- table.length() /* in characters, not in bytes */); ++ { ++ auto c = toSQLTCHAR(catalog); ++ auto s = toSQLTCHAR(schema); ++ auto t = toSQLTCHAR(table); ++ r = SQLPrimaryKeys(hStmt, ++ catalog.isEmpty() ? nullptr : c.data(), c.size(), ++ schema.isEmpty() ? nullptr : s.data(), s.size(), ++ t.data(), t.size()); ++ } + + // if the SQLPrimaryKeys() call does not succeed (e.g the driver + // does not support it) - try an alternative method to get hold of + // the primary index (e.g MS Access and FoxPro) + if (r != SQL_SUCCESS) { +- r = SQLSpecialColumns(hStmt, +- SQL_BEST_ROWID, +- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), +- catalog.length(), +- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), +- schema.length(), +- toSQLTCHAR(table).data(), +- table.length(), +- SQL_SCOPE_CURROW, +- SQL_NULLABLE); ++ auto c = toSQLTCHAR(catalog); ++ auto s = toSQLTCHAR(schema); ++ auto t = toSQLTCHAR(table); ++ r = SQLSpecialColumns(hStmt, ++ SQL_BEST_ROWID, ++ catalog.isEmpty() ? nullptr : c.data(), c.size(), ++ schema.isEmpty() ? nullptr : s.data(), s.size(), ++ t.data(), t.size(), ++ SQL_SCOPE_CURROW, ++ SQL_NULLABLE); + + if (r != SQL_SUCCESS) { + qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d); +@@ -2538,15 +2566,17 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const + SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, + SQL_IS_UINTEGER); +- r = SQLColumns(hStmt, +- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(), +- catalog.length(), +- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(), +- schema.length(), +- toSQLTCHAR(table).data(), +- table.length(), +- NULL, +- 0); ++ { ++ auto c = toSQLTCHAR(catalog); ++ auto s = toSQLTCHAR(schema); ++ auto t = toSQLTCHAR(table); ++ r = SQLColumns(hStmt, ++ catalog.isEmpty() ? nullptr : c.data(), c.size(), ++ schema.isEmpty() ? nullptr : s.data(), s.size(), ++ t.data(), t.size(), ++ nullptr, ++ 0); ++ } + if (r != SQL_SUCCESS) + qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d); + +diff --git a/qtbase/src/printsupport/dialogs/images/print-24.png b/qtbase/src/printsupport/dialogs/images/printer-24.png +similarity index 100% +rename from src/printsupport/dialogs/images/print-24.png +rename to src/printsupport/dialogs/images/printer-24.png +diff --git a/qtbase/src/printsupport/dialogs/images/print-32.png b/qtbase/src/printsupport/dialogs/images/printer-32.png +similarity index 100% +rename from src/printsupport/dialogs/images/print-32.png +rename to src/printsupport/dialogs/images/printer-32.png +diff --git a/qtbase/src/printsupport/dialogs/images/view-page-sided-24.png b/qtbase/src/printsupport/dialogs/images/view-pages-facing-24.png +similarity index 100% +rename from src/printsupport/dialogs/images/view-page-sided-24.png +rename to src/printsupport/dialogs/images/view-pages-facing-24.png +diff --git a/qtbase/src/printsupport/dialogs/images/view-page-sided-32.png b/qtbase/src/printsupport/dialogs/images/view-pages-facing-32.png +similarity index 100% +rename from src/printsupport/dialogs/images/view-page-sided-32.png +rename to src/printsupport/dialogs/images/view-pages-facing-32.png +diff --git a/qtbase/src/printsupport/dialogs/images/view-page-multi-24.png b/qtbase/src/printsupport/dialogs/images/view-pages-overview-24.png +similarity index 100% +rename from src/printsupport/dialogs/images/view-page-multi-24.png +rename to src/printsupport/dialogs/images/view-pages-overview-24.png +diff --git a/qtbase/src/printsupport/dialogs/images/view-page-multi-32.png b/qtbase/src/printsupport/dialogs/images/view-pages-overview-32.png +similarity index 100% +rename from src/printsupport/dialogs/images/view-page-multi-32.png +rename to src/printsupport/dialogs/images/view-pages-overview-32.png +diff --git a/qtbase/src/printsupport/dialogs/images/view-page-one-24.png b/qtbase/src/printsupport/dialogs/images/view-pages-single-24.png +similarity index 100% +rename from src/printsupport/dialogs/images/view-page-one-24.png +rename to src/printsupport/dialogs/images/view-pages-single-24.png +diff --git a/qtbase/src/printsupport/dialogs/images/view-page-one-32.png b/qtbase/src/printsupport/dialogs/images/view-pages-single-32.png +similarity index 100% +rename from src/printsupport/dialogs/images/view-page-one-32.png +rename to src/printsupport/dialogs/images/view-pages-single-32.png +diff --git a/qtbase/src/printsupport/dialogs/images/fit-page-24.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-page-24.png +similarity index 100% +rename from src/printsupport/dialogs/images/fit-page-24.png +rename to src/printsupport/dialogs/images/zoom-fit-page-24.png +diff --git a/qtbase/src/printsupport/dialogs/images/fit-page-32.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-page-32.png +similarity index 100% +rename from src/printsupport/dialogs/images/fit-page-32.png +rename to src/printsupport/dialogs/images/zoom-fit-page-32.png +diff --git a/qtbase/src/printsupport/dialogs/images/fit-width-24.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-width-24.png +similarity index 100% +rename from src/printsupport/dialogs/images/fit-width-24.png +rename to src/printsupport/dialogs/images/zoom-fit-width-24.png +diff --git a/qtbase/src/printsupport/dialogs/images/fit-width-32.png b/qtbase/src/printsupport/dialogs/images/zoom-fit-width-32.png +similarity index 100% +rename from src/printsupport/dialogs/images/fit-width-32.png +rename to src/printsupport/dialogs/images/zoom-fit-width-32.png +diff --git a/qtbase/src/printsupport/dialogs/qprintdialog.qrc b/qtbase/src/printsupport/dialogs/qprintdialog.qrc +index 5a579baa55..10b8e1d341 100644 +--- a/qtbase/src/printsupport/dialogs/qprintdialog.qrc ++++ b/qtbase/src/printsupport/dialogs/qprintdialog.qrc +@@ -1,9 +1,9 @@ + + +-images/fit-page-24.png +-images/fit-page-32.png +-images/fit-width-24.png +-images/fit-width-32.png ++images/zoom-fit-page-24.png ++images/zoom-fit-page-32.png ++images/zoom-fit-width-24.png ++images/zoom-fit-width-32.png + images/go-first-24.png + images/go-first-32.png + images/go-last-24.png +@@ -18,14 +18,14 @@ + images/layout-portrait-32.png + images/page-setup-24.png + images/page-setup-32.png +-images/print-24.png +-images/print-32.png +-images/view-page-multi-24.png +-images/view-page-multi-32.png +-images/view-page-one-24.png +-images/view-page-one-32.png +-images/view-page-sided-24.png +-images/view-page-sided-32.png ++images/printer-24.png ++images/printer-32.png ++images/view-pages-overview-24.png ++images/view-pages-overview-32.png ++images/view-pages-single-24.png ++images/view-pages-single-32.png ++images/view-pages-facing-24.png ++images/view-pages-facing-32.png + images/zoom-in-24.png + images/zoom-in-32.png + images/zoom-out-24.png +diff --git a/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp b/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp +index 39575d5f57..23b7e89538 100644 +--- a/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp ++++ b/qtbase/src/printsupport/dialogs/qprintpreviewdialog.cpp +@@ -352,7 +352,7 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) + static inline void qt_setupActionIcon(QAction *action, QLatin1String name) + { + QLatin1String imagePrefix(":/qt-project.org/dialogs/qprintpreviewdialog/images/"); +- QIcon icon; ++ QIcon icon = QIcon::fromTheme(name); + icon.addFile(imagePrefix + name + QLatin1String("-24.png"), QSize(24, 24)); + icon.addFile(imagePrefix + name + QLatin1String("-32.png"), QSize(32, 32)); + action->setIcon(icon); +@@ -383,8 +383,8 @@ void QPrintPreviewDialogPrivate::setupActions() + fitPageAction->setObjectName(QLatin1String("fitPageAction")); + fitWidthAction->setCheckable(true); + fitPageAction->setCheckable(true); +- qt_setupActionIcon(fitWidthAction, QLatin1String("fit-width")); +- qt_setupActionIcon(fitPageAction, QLatin1String("fit-page")); ++ qt_setupActionIcon(fitWidthAction, QLatin1String("zoom-fit-width")); ++ qt_setupActionIcon(fitPageAction, QLatin1String("zoom-fit-page")); + QObject::connect(fitGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_fit(QAction*))); + + // Zoom +@@ -410,9 +410,9 @@ void QPrintPreviewDialogPrivate::setupActions() + singleModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show single page")); + facingModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show facing pages")); + overviewModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show overview of all pages")); +- qt_setupActionIcon(singleModeAction, QLatin1String("view-page-one")); +- qt_setupActionIcon(facingModeAction, QLatin1String("view-page-sided")); +- qt_setupActionIcon(overviewModeAction, QLatin1String("view-page-multi")); ++ qt_setupActionIcon(singleModeAction, QLatin1String("view-pages-single")); ++ qt_setupActionIcon(facingModeAction, QLatin1String("view-pages-facing")); ++ qt_setupActionIcon(overviewModeAction, QLatin1String("view-pages-overview")); + singleModeAction->setObjectName(QLatin1String("singleModeAction")); + facingModeAction->setObjectName(QLatin1String("facingModeAction")); + overviewModeAction->setObjectName(QLatin1String("overviewModeAction")); +@@ -426,7 +426,7 @@ void QPrintPreviewDialogPrivate::setupActions() + printerGroup = new QActionGroup(q); + printAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Print")); + pageSetupAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Page setup")); +- qt_setupActionIcon(printAction, QLatin1String("print")); ++ qt_setupActionIcon(printAction, QLatin1String("printer")); + qt_setupActionIcon(pageSetupAction, QLatin1String("page-setup")); + QObject::connect(printAction, SIGNAL(triggered(bool)), q, SLOT(_q_print())); + QObject::connect(pageSetupAction, SIGNAL(triggered(bool)), q, SLOT(_q_pageSetup())); +diff --git a/qtbase/src/testlib/qabstractitemmodeltester.cpp b/qtbase/src/testlib/qabstractitemmodeltester.cpp +index 1cd18b98bb..41219a7e23 100644 +--- a/qtbase/src/testlib/qabstractitemmodeltester.cpp ++++ b/qtbase/src/testlib/qabstractitemmodeltester.cpp +@@ -454,7 +454,7 @@ void QAbstractItemModelTesterPrivate::parent() + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. +- if (model->rowCount(topIndex) > 0) { ++ if (model->rowCount(topIndex) > 0 && model->columnCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + MODELTESTER_VERIFY(childIndex.isValid()); + MODELTESTER_COMPARE(model->parent(childIndex), topIndex); +diff --git a/qtbase/src/testlib/qasciikey.cpp b/qtbase/src/testlib/qasciikey.cpp +index 9a308da2bc..93498b256f 100644 +--- a/qtbase/src/testlib/qasciikey.cpp ++++ b/qtbase/src/testlib/qasciikey.cpp +@@ -498,6 +498,11 @@ char QTest::keyToAscii(Qt::Key key) + case Qt::Key_LaunchE : return 0; // = 0x10b0, + case Qt::Key_LaunchF : return 0; // = 0x10b1, + ++ // Keypad navigation keys ++ case Qt::Key_Select : return 0; // = 0x01010000 ++ case Qt::Key_Yes : return 0; // = 0x01010001 ++ case Qt::Key_No : return 0; // = 0x01010002 ++ + default: QTEST_ASSERT(false); return 0; + } + } +diff --git a/qtbase/src/widgets/accessible/qaccessiblewidgets.cpp b/qtbase/src/widgets/accessible/qaccessiblewidgets.cpp +index 574be1f5ea..b499d5d620 100644 +--- a/qtbase/src/widgets/accessible/qaccessiblewidgets.cpp ++++ b/qtbase/src/widgets/accessible/qaccessiblewidgets.cpp +@@ -900,7 +900,7 @@ QString QAccessibleTextWidget::attributes(int offset, int *startOffset, int *end + underlineStyleValue = QStringLiteral("wave"); // this is not correct, but provides good approximation at least + break; + default: +- qWarning() << "Unknown QTextCharFormat::​UnderlineStyle value " << underlineStyle << " could not be translated to IAccessible2 value"; ++ qWarning() << "Unknown QTextCharFormat::UnderlineStyle value " << underlineStyle << " could not be translated to IAccessible2 value"; + break; + } + if (!underlineStyleValue.isNull()) { +diff --git a/qtbase/src/widgets/dialogs/qcolordialog.cpp b/qtbase/src/widgets/dialogs/qcolordialog.cpp +index 4247731275..30445fa069 100644 +--- a/qtbase/src/widgets/dialogs/qcolordialog.cpp ++++ b/qtbase/src/widgets/dialogs/qcolordialog.cpp +@@ -78,7 +78,10 @@ + #include "qwindow.h" + + #include "private/qdialog_p.h" ++#include "private/qguiapplication_p.h" + ++#include ++#include + #include + + QT_BEGIN_NAMESPACE +@@ -801,6 +804,10 @@ QColorLuminancePicker::~QColorLuminancePicker() + + void QColorLuminancePicker::mouseMoveEvent(QMouseEvent *m) + { ++ if (m->buttons() == Qt::NoButton) { ++ m->ignore(); ++ return; ++ } + setVal(y2val(m->y())); + } + void QColorLuminancePicker::mousePressEvent(QMouseEvent *m) +@@ -935,6 +942,10 @@ void QColorPicker::setCol(int h, int s) + void QColorPicker::mouseMoveEvent(QMouseEvent *m) + { + QPoint p = m->pos() - contentsRect().topLeft(); ++ if (m->buttons() == Qt::NoButton) { ++ m->ignore(); ++ return; ++ } + setCol(p); + emit newCol(hue, sat); + } +@@ -1611,6 +1622,20 @@ void QColorDialogPrivate::_q_newStandard(int r, int c) + void QColorDialogPrivate::_q_pickScreenColor() + { + Q_Q(QColorDialog); ++ ++ auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services(); ++ if (platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) { ++ if (auto *colorPicker = platformServices->colorPicker(q->windowHandle())) { ++ q->connect(colorPicker, &QPlatformServiceColorPicker::colorPicked, q, ++ [q, colorPicker](const QColor &color) { ++ colorPicker->deleteLater(); ++ q->setCurrentColor(color); ++ }); ++ colorPicker->pickColor(); ++ return; ++ } ++ } ++ + if (!colorPickingEventFilter) + colorPickingEventFilter = new QColorPickingEventFilter(this, q); + q->installEventFilter(colorPickingEventFilter); +diff --git a/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp b/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp +index e120817edc..8ea36b5427 100644 +--- a/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp ++++ b/qtbase/src/widgets/itemviews/qabstractitemdelegate.cpp +@@ -400,12 +400,7 @@ bool QAbstractItemDelegate::helpEvent(QHelpEvent *event, + const QString tooltip = index.isValid() ? + d->textForRole(Qt::ToolTipRole, index.data(Qt::ToolTipRole), option.locale, precision) : + QString(); +- QRect rect; +- if (index.isValid()) { +- const QRect r = view->visualRect(index); +- rect = QRect(view->mapToGlobal(r.topLeft()), r.size()); +- } +- QToolTip::showText(he->globalPos(), tooltip, view, rect); ++ QToolTip::showText(he->globalPos(), tooltip, view->viewport(), option.rect); + event->setAccepted(!tooltip.isEmpty()); + break; + } +diff --git a/qtbase/src/widgets/itemviews/qabstractitemview.cpp b/qtbase/src/widgets/itemviews/qabstractitemview.cpp +index deb49ca23d..93dc5ee80c 100644 +--- a/qtbase/src/widgets/itemviews/qabstractitemview.cpp ++++ b/qtbase/src/widgets/itemviews/qabstractitemview.cpp +@@ -2347,11 +2347,12 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) + + #if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT) + if (event == QKeySequence::Copy) { +- QVariant variant; +- if (d->model) +- variant = d->model->data(currentIndex(), Qt::DisplayRole); +- if (variant.canConvert()) +- QGuiApplication::clipboard()->setText(variant.toString()); ++ const QModelIndex index = currentIndex(); ++ if (index.isValid() && d->model) { ++ const QVariant variant = d->model->data(index, Qt::DisplayRole); ++ if (variant.canConvert()) ++ QGuiApplication::clipboard()->setText(variant.toString()); ++ } + event->accept(); + } + #endif +diff --git a/qtbase/src/widgets/itemviews/qlistview.cpp b/qtbase/src/widgets/itemviews/qlistview.cpp +index 5a88f1b1ef..63f760269c 100644 +--- a/qtbase/src/widgets/itemviews/qlistview.cpp ++++ b/qtbase/src/widgets/itemviews/qlistview.cpp +@@ -3388,6 +3388,7 @@ void QIconModeViewBase::updateContentsSize() + */ + void QListView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) + { ++ QAbstractItemView::currentChanged(current, previous); + #ifndef QT_NO_ACCESSIBILITY + if (QAccessible::isActive()) { + if (current.isValid()) { +@@ -3398,7 +3399,6 @@ void QListView::currentChanged(const QModelIndex ¤t, const QModelIndex &pr + } + } + #endif +- QAbstractItemView::currentChanged(current, previous); + } + + /*! +diff --git a/qtbase/src/widgets/itemviews/qtableview.cpp b/qtbase/src/widgets/itemviews/qtableview.cpp +index d120c41dc9..09d34005a7 100644 +--- a/qtbase/src/widgets/itemviews/qtableview.cpp ++++ b/qtbase/src/widgets/itemviews/qtableview.cpp +@@ -1013,6 +1013,7 @@ void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItem & + int QTableViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option) const + { + Q_Q(const QTableView); ++ const int oldHint = hint; + QWidget *editor = editorForIndex(index).widget.data(); + if (editor && persistent.contains(editor)) { + hint = qMax(hint, editor->sizeHint().width()); +@@ -1021,6 +1022,17 @@ int QTableViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, con + hint = qBound(min, hint, max); + } + hint = qMax(hint, q->itemDelegate(index)->sizeHint(option, index).width()); ++ ++ if (hasSpans()) { ++ auto span = spans.spanAt(index.column(), index.row()); ++ if (span && span->m_left == index.column() && span->m_top == index.row()) { ++ // spans are screwed up when sections are moved ++ const auto left = logicalColumn(span->m_left); ++ for (int i = 1; i <= span->width(); ++i) ++ hint -= q->columnWidth(visualColumn(left + i)); ++ } ++ hint = std::max(hint, oldHint); ++ } + return hint; + } + +@@ -1053,6 +1065,11 @@ int QTableViewPrivate::heightHintForIndex(const QModelIndex &index, int hint, QS + option.rect.setHeight(height); + option.rect.setX(q->columnViewportPosition(index.column())); + option.rect.setWidth(q->columnWidth(index.column())); ++ if (hasSpans()) { ++ auto span = spans.spanAt(index.column(), index.row()); ++ if (span && span->m_left == index.column() && span->m_top == index.row()) ++ option.rect.setWidth(std::max(option.rect.width(), visualSpanRect(*span).width())); ++ } + // 1px less space when grid is shown (see drawCell) + if (showGrid) + option.rect.setWidth(option.rect.width() - 1); +diff --git a/qtbase/src/widgets/kernel/qaction.h b/qtbase/src/widgets/kernel/qaction.h +index 258a1ea0a0..737c1e8285 100644 +--- a/qtbase/src/widgets/kernel/qaction.h ++++ b/qtbase/src/widgets/kernel/qaction.h +@@ -81,7 +81,7 @@ class Q_WIDGETS_EXPORT QAction : public QObject + Q_PROPERTY(MenuRole menuRole READ menuRole WRITE setMenuRole NOTIFY changed) + Q_PROPERTY(bool iconVisibleInMenu READ isIconVisibleInMenu WRITE setIconVisibleInMenu NOTIFY changed) + Q_PROPERTY(bool shortcutVisibleInContextMenu READ isShortcutVisibleInContextMenu WRITE setShortcutVisibleInContextMenu NOTIFY changed) +- Q_PROPERTY(Priority priority READ priority WRITE setPriority) ++ Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY changed) + + public: + // note this is copied into qplatformmenu.h, which must stay in sync +diff --git a/qtbase/src/widgets/kernel/qwidget.cpp b/qtbase/src/widgets/kernel/qwidget.cpp +index e94520021e..d661114fc7 100644 +--- a/qtbase/src/widgets/kernel/qwidget.cpp ++++ b/qtbase/src/widgets/kernel/qwidget.cpp +@@ -1272,7 +1272,6 @@ void QWidgetPrivate::create() + win->setProperty("_q_showWithoutActivating", QVariant(true)); + if (q->testAttribute(Qt::WA_MacAlwaysShowToolWindow)) + win->setProperty("_q_macAlwaysShowToolWindow", QVariant(true)); +- setNetWmWindowTypes(true); // do nothing if none of WA_X11NetWmWindowType* is set + win->setFlags(flags); + fixPosIncludesFrame(); + if (q->testAttribute(Qt::WA_Moved) +@@ -1345,6 +1344,7 @@ void QWidgetPrivate::create() + Q_ASSERT(id != WId(0)); + setWinId(id); + } ++ setNetWmWindowTypes(true); // do nothing if none of WA_X11NetWmWindowType* is set + + // Check children and create windows for them if necessary + q_createNativeChildrenAndSetParent(q); +diff --git a/qtbase/src/widgets/styles/qcommonstyle.cpp b/qtbase/src/widgets/styles/qcommonstyle.cpp +index 502a527901..a79c33005c 100644 +--- a/qtbase/src/widgets/styles/qcommonstyle.cpp ++++ b/qtbase/src/widgets/styles/qcommonstyle.cpp +@@ -1708,8 +1708,9 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, + alignment |= Qt::TextHideMnemonic; + rect.translate(shiftX, shiftY); + p->setFont(toolbutton->font); ++ const QString text = d->toolButtonElideText(toolbutton, rect, alignment); + proxy()->drawItemText(p, rect, alignment, toolbutton->palette, +- opt->state & State_Enabled, toolbutton->text, ++ opt->state & State_Enabled, text, + QPalette::ButtonText); + } else { + QPixmap pm; +diff --git a/qtbase/src/widgets/styles/qfusionstyle.cpp b/qtbase/src/widgets/styles/qfusionstyle.cpp +index a225d4b563..35e2769ac4 100644 +--- a/qtbase/src/widgets/styles/qfusionstyle.cpp ++++ b/qtbase/src/widgets/styles/qfusionstyle.cpp +@@ -1772,14 +1772,6 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio + proxy()->drawControl(CE_PushButtonLabel, &subopt, painter, widget); + } + break; +- case CE_PushButtonLabel: +- if (const QStyleOptionButton *button = qstyleoption_cast(option)) { +- QStyleOptionButton b(*button); +- // no PM_ButtonShiftHorizontal and PM_ButtonShiftVertical for fusion style +- b.state &= ~(State_On | State_Sunken); +- QCommonStyle::drawControl(element, &b, painter, widget); +- } +- break; + case CE_MenuBarEmptyArea: + painter->save(); + { +diff --git a/qtbase/src/widgets/util/qsystemtrayicon.cpp b/qtbase/src/widgets/util/qsystemtrayicon.cpp +index 203fcbc443..e9b2724903 100644 +--- a/qtbase/src/widgets/util/qsystemtrayicon.cpp ++++ b/qtbase/src/widgets/util/qsystemtrayicon.cpp +@@ -208,7 +208,7 @@ void QSystemTrayIcon::setContextMenu(QMenu *menu) + if (oldMenu != menu && d->qpa_sys) { + // Show the QMenu-based menu for QPA plugins that do not provide native menus + if (oldMenu && !oldMenu->platformMenu()) +- QObject::disconnect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, menu, nullptr); ++ QObject::disconnect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, oldMenu, nullptr); + if (menu && !menu->platformMenu()) { + QObject::connect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, + menu, +diff --git a/qtbase/src/widgets/widgets/qabstractbutton.cpp b/qtbase/src/widgets/widgets/qabstractbutton.cpp +index a128b23950..dc40bf62fb 100644 +--- a/qtbase/src/widgets/widgets/qabstractbutton.cpp ++++ b/qtbase/src/widgets/widgets/qabstractbutton.cpp +@@ -56,6 +56,7 @@ + #ifndef QT_NO_ACCESSIBILITY + #include "qaccessible.h" + #endif ++#include + + #include + +@@ -1076,19 +1077,19 @@ void QAbstractButton::keyPressEvent(QKeyEvent *e) + { + Q_D(QAbstractButton); + bool next = true; +- switch (e->key()) { +- case Qt::Key_Enter: +- case Qt::Key_Return: +- e->ignore(); +- break; +- case Qt::Key_Select: +- case Qt::Key_Space: +- if (!e->isAutoRepeat()) { +- setDown(true); +- repaint(); +- d->emitPressed(); +- } +- break; ++ ++ const auto key = static_cast(e->key()); ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(key) && !e->isAutoRepeat()) { ++ setDown(true); ++ repaint(); ++ d->emitPressed(); ++ return; ++ } ++ ++ switch (key) { + case Qt::Key_Up: + next = false; + Q_FALLTHROUGH(); +@@ -1153,15 +1154,15 @@ void QAbstractButton::keyReleaseEvent(QKeyEvent *e) + if (!e->isAutoRepeat()) + d->repeatTimer.stop(); + +- switch (e->key()) { +- case Qt::Key_Select: +- case Qt::Key_Space: +- if (!e->isAutoRepeat() && d->down) +- d->click(); +- break; +- default: +- e->ignore(); ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(static_cast(e->key())) && !e->isAutoRepeat() && d->down) { ++ d->click(); ++ return; + } ++ ++ e->ignore(); + } + + /*!\reimp +diff --git a/qtbase/src/widgets/widgets/qcombobox.cpp b/qtbase/src/widgets/widgets/qcombobox.cpp +index 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/qdial.cpp b/qtbase/src/widgets/widgets/qdial.cpp +index 8f774a3cc7..ec5cec0d82 100644 +--- a/qtbase/src/widgets/widgets/qdial.cpp ++++ b/qtbase/src/widgets/widgets/qdial.cpp +@@ -94,6 +94,8 @@ int QDialPrivate::bound(int val) const + if (wrapping) { + if ((val >= minimum) && (val <= maximum)) + return val; ++ if (minimum == maximum) ++ return minimum; + val = minimum + ((val - minimum) % (maximum - minimum)); + if (val < minimum) + val += maximum - minimum; +diff --git a/qtbase/src/widgets/widgets/qgroupbox.cpp b/qtbase/src/widgets/widgets/qgroupbox.cpp +index 02a0bed325..3f3eccc370 100644 +--- a/qtbase/src/widgets/widgets/qgroupbox.cpp ++++ b/qtbase/src/widgets/widgets/qgroupbox.cpp +@@ -54,6 +54,8 @@ + #include "qaccessible.h" + #endif + #include ++#include ++#include + + #include "qdebug.h" + +@@ -360,7 +362,10 @@ bool QGroupBox::event(QEvent *e) + return true; + case QEvent::KeyPress: { + QKeyEvent *k = static_cast(e); +- if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) { ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (!k->isAutoRepeat() && buttonPressKeys.contains(static_cast(k->key()))) { + d->pressedControl = QStyle::SC_GroupBoxCheckBox; + update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this)); + return true; +@@ -369,7 +374,10 @@ bool QGroupBox::event(QEvent *e) + } + case QEvent::KeyRelease: { + QKeyEvent *k = static_cast(e); +- if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) { ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (!k->isAutoRepeat() && buttonPressKeys.contains(static_cast(k->key()))) { + bool toggle = (d->pressedControl == QStyle::SC_GroupBoxLabel + || d->pressedControl == QStyle::SC_GroupBoxCheckBox); + d->pressedControl = QStyle::SC_None; +diff --git a/qtbase/src/widgets/widgets/qtabbar.cpp b/qtbase/src/widgets/widgets/qtabbar.cpp +index 9a382e96dd..7f80fcc250 100644 +--- a/qtbase/src/widgets/widgets/qtabbar.cpp ++++ b/qtbase/src/widgets/widgets/qtabbar.cpp +@@ -2190,7 +2190,8 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event) + } + } + // Buttons needs to follow the dragged tab +- d->layoutTab(d->pressedIndex); ++ if (d->pressedIndex != -1) ++ d->layoutTab(d->pressedIndex); + + update(); + } +diff --git a/qtbase/src/widgets/widgets/qtoolbutton.cpp b/qtbase/src/widgets/widgets/qtoolbutton.cpp +index e380cb647b..9953db73af 100644 +--- a/qtbase/src/widgets/widgets/qtoolbutton.cpp ++++ b/qtbase/src/widgets/widgets/qtoolbutton.cpp +@@ -982,7 +982,15 @@ QAction *QToolButton::defaultAction() const + return d->defaultAction; + } + +- ++/*! ++ \reimp ++ */ ++void QToolButton::checkStateSet() ++{ ++ Q_D(QToolButton); ++ if (d->defaultAction && d->defaultAction->isCheckable()) ++ d->defaultAction->setChecked(isChecked()); ++} + + /*! + \reimp +diff --git a/qtbase/src/widgets/widgets/qtoolbutton.h b/qtbase/src/widgets/widgets/qtoolbutton.h +index 52bd2d5f7a..82b5d7924f 100644 +--- a/qtbase/src/widgets/widgets/qtoolbutton.h ++++ b/qtbase/src/widgets/widgets/qtoolbutton.h +@@ -118,6 +118,7 @@ protected: + void changeEvent(QEvent *) override; + + bool hitButton(const QPoint &pos) const override; ++ void checkStateSet() override; + void nextCheckState() override; + void initStyleOption(QStyleOptionToolButton *option) const; + +diff --git a/qtbase/src/widgets/widgets/qwidgettextcontrol.cpp b/qtbase/src/widgets/widgets/qwidgettextcontrol.cpp +index ba9b6e0587..9493f090ec 100644 +--- a/qtbase/src/widgets/widgets/qwidgettextcontrol.cpp ++++ b/qtbase/src/widgets/widgets/qwidgettextcontrol.cpp +@@ -2057,6 +2057,11 @@ void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) + || e->preeditString() != cursor.block().layout()->preeditAreaText() + || e->replacementLength() > 0; + ++ if (!isGettingInput && e->attributes().isEmpty()) { ++ e->ignore(); ++ return; ++ } ++ + int oldCursorPos = cursor.position(); + + cursor.beginEditBlock(); +diff --git a/qtbase/src/xml/sax/qxml.cpp b/qtbase/src/xml/sax/qxml.cpp +index 7a444713c1..9076dcfd84 100644 +--- a/qtbase/src/xml/sax/qxml.cpp ++++ b/qtbase/src/xml/sax/qxml.cpp +@@ -7464,7 +7464,12 @@ bool QXmlSimpleReaderPrivate::parseReference() + case DoneD: + tmp = ref().toUInt(&ok, 10); + if (ok) { +- stringAddC(QChar(tmp)); ++ if (tmp > 0xffff) { ++ stringAddC(QChar::highSurrogate(tmp)); ++ stringAddC(QChar::lowSurrogate(tmp)); ++ } else { ++ stringAddC(QChar(tmp)); ++ } + } else { + reportParseError(QLatin1String(XMLERR_ERRORPARSINGREFERENCE)); + return false; +@@ -7475,7 +7480,12 @@ bool QXmlSimpleReaderPrivate::parseReference() + case DoneH: + tmp = ref().toUInt(&ok, 16); + if (ok) { +- stringAddC(QChar(tmp)); ++ if (tmp > 0xffff) { ++ stringAddC(QChar::highSurrogate(tmp)); ++ stringAddC(QChar::lowSurrogate(tmp)); ++ } else { ++ stringAddC(QChar(tmp)); ++ } + } else { + reportParseError(QLatin1String(XMLERR_ERRORPARSINGREFERENCE)); + return false; +diff --git a/qtbase/sync.profile b/qtbase/sync.profile +index 0292bf0dc2..7dd0177b90 100644 +--- a/qtbase/sync.profile ++++ b/qtbase/sync.profile +@@ -77,7 +77,7 @@ + "qsql.h" => "QtSql/qtsqlglobal.h" + }, + "QtDBus" => { +- "qdbusmacros.h" => "QtDbus/qtdbusglobal.h" ++ "qdbusmacros.h" => "QtDBus/qtdbusglobal.h" + }, + "QtTest" => { + "qtest_global.h" => "QtTest/qttestglobal.h" +diff --git a/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp b/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp +index e1ea7a4552..90972caa57 100644 +--- a/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp ++++ b/qtbase/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp +@@ -117,6 +117,7 @@ private Q_SLOTS: + void shouldPropagateDropAfterLastRow_data(); + void shouldPropagateDropAfterLastRow(); + void qtbug91788(); ++ void qtbug91878(); + + private: + QStandardItemModel mod; +@@ -843,6 +844,22 @@ void tst_QConcatenateTablesProxyModel::qtbug91788() + QCOMPARE(proxyConcat.columnCount(), 0); + } + ++void tst_QConcatenateTablesProxyModel::qtbug91878() ++{ ++ QStandardItemModel m; ++ m.setRowCount(4); ++ m.setColumnCount(4); ++ ++ QConcatenateTablesProxyModel pm; ++ QSortFilterProxyModel proxyFilter; ++ proxyFilter.setSourceModel(&pm); ++ proxyFilter.setFilterFixedString("something"); ++ pm.addSourceModel(&m); // This should not assert ++ ++ QCOMPARE(pm.columnCount(), 4); ++ QCOMPARE(pm.rowCount(), 4); ++} ++ + QTEST_GUILESS_MAIN(tst_QConcatenateTablesProxyModel) + + #include "tst_qconcatenatetablesproxymodel.moc" +diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/circular-inheritance.xml b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/circular-inheritance.xml +new file mode 100644 +index 0000000000..466f039803 +--- /dev/null ++++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/circular-inheritance.xml +@@ -0,0 +1,13 @@ ++ ++ ++ ++ It's more accurate to say that ECMAScript is a subset of JavaScript ++ ++ ++ ++ ++ than to say that JavaScript is a subset of ECMAScript ++ ++ ++ ++ +diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc +index 1002d0195d..49dbb0a8ba 100644 +--- a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc ++++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc +@@ -3,10 +3,12 @@ + yast2-metapackage-handler-mimetypes.xml + qml-again.xml + text-x-objcsrc.xml ++ text-plain-subclass.xml + test.qml + invalid-magic1.xml + invalid-magic2.xml + invalid-magic3.xml ++ circular-inheritance.xml + magic-and-hierarchy.xml + magic-and-hierarchy.foo + magic-and-hierarchy2.foo +diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/text-plain-subclass.xml b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/text-plain-subclass.xml +new file mode 100644 +index 0000000000..7b5cb7506d +--- /dev/null ++++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/text-plain-subclass.xml +@@ -0,0 +1,15 @@ ++ ++ ++ ++ MicroDVD subtitles ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +index 0ea422ecbc..d2dd8d340b 100644 +--- a/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp ++++ b/qtbase/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +@@ -49,10 +49,12 @@ static const char *const additionalMimeFiles[] = { + "yast2-metapackage-handler-mimetypes.xml", + "qml-again.xml", + "text-x-objcsrc.xml", ++ "text-plain-subclass.xml", + "invalid-magic1.xml", + "invalid-magic2.xml", + "invalid-magic3.xml", + "magic-and-hierarchy.xml", ++ "circular-inheritance.xml", + 0 + }; + +@@ -70,15 +72,15 @@ static inline QString testSuiteWarning() + + QString result; + QTextStream str(&result); +- str << "\nCannot find the shared-mime-info test suite\nstarting from: " ++ str << "\nCannot find the shared-mime-info test suite\nin the parent of: " + << QDir::toNativeSeparators(QDir::currentPath()) << "\n" + "cd " << QDir::toNativeSeparators(QStringLiteral("tests/auto/corelib/mimetypes/qmimedatabase")) << "\n" +- "wget http://cgit.freedesktop.org/xdg/shared-mime-info/snapshot/Release-1-10.zip\n" +- "unzip Release-1-10.zip\n"; ++ "wget https://gitlab.freedesktop.org/xdg/shared-mime-info/-/archive/2.1/shared-mime-info-2.1.zip\n" ++ "unzip shared-mime-info-2.1.zip\n"; + #ifdef Q_OS_WIN +- str << "mkdir testfiles\nxcopy /s Release-1-10 s-m-i\n"; ++ str << "mkdir testfiles\nxcopy /s shared-mime-info-2.1 s-m-i\n"; + #else +- str << "ln -s Release-1-10 s-m-i\n"; ++ str << "ln -s shared-mime-info-2.1 s-m-i\n"; + #endif + return result; + } +@@ -154,7 +156,7 @@ void tst_QMimeDatabase::initTestCase() + QVERIFY2(copyResourceFile(xmlFileName, xmlTargetFileName, &errorMessage), qPrintable(errorMessage)); + #endif + +- m_testSuite = QFINDTESTDATA("s-m-i/tests"); ++ m_testSuite = QFINDTESTDATA("s-m-i/tests/mime-detection"); + if (m_testSuite.isEmpty()) + qWarning("%s", qPrintable(testSuiteWarning())); + +@@ -390,6 +392,13 @@ void tst_QMimeDatabase::inheritance() + const QMimeType mswordTemplate = db.mimeTypeForName(QString::fromLatin1("application/msword-template")); + QVERIFY(mswordTemplate.isValid()); + QVERIFY(mswordTemplate.inherits(QLatin1String("application/msword"))); ++ ++ // Check that buggy type definitions that have circular inheritance don't cause an infinite ++ // loop, especially when resolving a conflict between the file's name and its contents ++ const QMimeType ecmascript = db.mimeTypeForName(QString::fromLatin1("application/ecmascript")); ++ QVERIFY(ecmascript.allAncestors().contains("text/plain")); ++ const QMimeType javascript = db.mimeTypeForFileNameAndData("xml.js", ""); ++ QVERIFY(javascript.inherits(QString::fromLatin1("text/javascript"))); + } + + void tst_QMimeDatabase::aliases() +@@ -611,7 +620,7 @@ void tst_QMimeDatabase::allMimeTypes() + QVERIFY(!lst.isEmpty()); + + // Hardcoding this is the only way to check both providers find the same number of mimetypes. +- QCOMPARE(lst.count(), 779); ++ QCOMPARE(lst.count(), 811); + + foreach (const QMimeType &mime, lst) { + const QString name = mime.name(); +@@ -631,10 +640,9 @@ void tst_QMimeDatabase::suffixes_data() + + QTest::newRow("mimetype with a single pattern") << "application/pdf" << "*.pdf" << "pdf"; + QTest::newRow("mimetype with multiple patterns") << "application/x-kpresenter" << "*.kpr;*.kpt" << "kpr"; +- QTest::newRow("jpeg") << "image/jpeg" << "*.jpe;*.jpg;*.jpeg" << "jpeg"; +- //if (KMimeType::sharedMimeInfoVersion() > KDE_MAKE_VERSION(0, 60, 0)) { +- QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << "wp"; +- //} ++ // The preferred suffix for image/jpeg is *.jpg, as per https://bugs.kde.org/show_bug.cgi?id=176737 ++ QTest::newRow("jpeg") << "image/jpeg" << "*.jpe;*.jpg;*.jpeg" << "jpg"; ++ QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << "wp"; + QTest::newRow("oasis text mimetype") << "application/vnd.oasis.opendocument.text" << "*.odt" << "odt"; + QTest::newRow("oasis presentation mimetype") << "application/vnd.oasis.opendocument.presentation" << "*.odp" << "odp"; + QTest::newRow("mimetype with multiple patterns") << "text/plain" << "*.asc;*.txt;*,v" << "txt"; +@@ -1070,6 +1078,8 @@ void tst_QMimeDatabase::installNewLocalMimeType() + QVERIFY(objcsrc.isValid()); + QCOMPARE(objcsrc.globPatterns(), QStringList()); + } ++ QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.txt"), QMimeDatabase::MatchExtension).name(), ++ QString::fromLatin1("text/plain")); + + // Test that a double-definition of a mimetype doesn't lead to sniffing ("conflicting globs"). + const QString qmlTestFile = QLatin1String(RESOURCE_PREFIX "test.qml"); +diff --git a/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h +index ca2ceed7a9..624316dfdb 100644 +--- a/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h ++++ b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.h +@@ -35,7 +35,7 @@ + class Plugin1 : public QObject, public PluginInterface1 + { + Q_OBJECT +- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.autotests.plugininterface1") ++ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.autotests.plugininterface1" FILE "plugin1.json") + Q_INTERFACES(PluginInterface1) + + public: +diff --git a/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.json b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.json +new file mode 100644 +index 0000000000..ce67846d48 +--- /dev/null ++++ b/qtbase/tests/auto/corelib/plugin/qfactoryloader/plugin1/plugin1.json +@@ -0,0 +1,5 @@ ++{ ++ "Keys": [ ++ "plugin1" ++ ] ++} +diff --git a/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp b/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp +index 9fa61804b3..88ada1b806 100644 +--- a/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp ++++ b/qtbase/tests/auto/corelib/plugin/qfactoryloader/tst_qfactoryloader.cpp +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include "plugin1/plugininterface1.h" + #include "plugin2/plugininterface2.h" + +@@ -52,6 +53,7 @@ public slots: + + private slots: + void usingTwoFactoriesFromSameDir(); ++ void multiplePaths(); + }; + + static const char binFolderC[] = "bin"; +@@ -92,5 +94,30 @@ void tst_QFactoryLoader::usingTwoFactoriesFromSameDir() + QCOMPARE(plugin2->pluginName(), QLatin1String("Plugin2 ok")); + } + ++void tst_QFactoryLoader::multiplePaths() ++{ ++#if !QT_CONFIG(library) || !(defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)) || defined(Q_OS_ANDROID) ++ QSKIP("Test not applicable in this configuration."); ++#else ++ const QString binFolder = QFINDTESTDATA(binFolderC); ++ ++ QTemporaryDir dir; ++ QVERIFY(dir.isValid()); ++ ++ QString pluginsPath = QFileInfo(binFolder, binFolderC).absolutePath(); ++ QString linkPath = dir.filePath(binFolderC); ++ QVERIFY(QFile::link(pluginsPath, linkPath)); ++ ++ QCoreApplication::setLibraryPaths({ QFileInfo(binFolder).absolutePath(), dir.path() }); ++ ++ const QString suffix = QLatin1Char('/') + QLatin1String(binFolderC); ++ QFactoryLoader loader1(PluginInterface1_iid, suffix); ++ ++ QLibraryPrivate *library1 = loader1.library("plugin1"); ++ QVERIFY(library1); ++ QCOMPARE(library1->loadHints(), QLibrary::PreventUnloadHint); ++#endif ++} ++ + QTEST_MAIN(tst_QFactoryLoader) + #include "tst_qfactoryloader.moc" +diff --git a/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +index ce8057372c..833f68b1de 100644 +--- a/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp ++++ b/qtbase/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +@@ -193,7 +193,9 @@ void tst_QPluginLoader::errorString() + QVERIFY(!unloaded); + } + +-#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_HPUX) ++// A bug in QNX causes the test to crash on exit after attempting to load ++// a shared library with undefined symbols (tracked as QTBUG-114682). ++#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_HPUX) && !defined(Q_OS_QNX) + { + QPluginLoader loader( sys_qualifiedLibraryName("almostplugin")); //a plugin with unresolved symbols + loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); +@@ -243,10 +245,37 @@ void tst_QPluginLoader::loadHints() + QSKIP("This test requires Qt to create shared libraries."); + #endif + QPluginLoader loader; +- QCOMPARE(loader.loadHints(), QLibrary::LoadHints{}); //Do not crash ++ QCOMPARE(loader.loadHints(), QLibrary::PreventUnloadHint); //Do not crash + loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); ++ QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); ++ // We can clear load hints when file name is not set. ++ loader.setLoadHints(QLibrary::LoadHints{}); ++ QCOMPARE(loader.loadHints(), QLibrary::LoadHints{}); ++ // Set the hints again ++ loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); ++ QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); + loader.setFileName( sys_qualifiedLibraryName("theplugin")); //a plugin + QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); ++ ++ QPluginLoader loader4; ++ QCOMPARE(loader4.loadHints(), QLibrary::PreventUnloadHint); ++ loader4.setLoadHints(QLibrary::LoadHints{}); ++ QCOMPARE(loader4.loadHints(), QLibrary::LoadHints{}); ++ loader4.setFileName(sys_qualifiedLibraryName("theplugin")); ++ // Hints are merged with hints from the previous loader. ++ QCOMPARE(loader4.loadHints(), QLibrary::ResolveAllSymbolsHint); ++ // We cannot clear load hints after associating the loader with a file. ++ loader.setLoadHints(QLibrary::LoadHints{}); ++ QCOMPARE(loader.loadHints(), QLibrary::ResolveAllSymbolsHint); ++ ++ QPluginLoader loader2; ++ QCOMPARE(loader2.loadHints(), QLibrary::PreventUnloadHint); ++ loader2.setFileName(sys_qualifiedLibraryName("theplugin")); ++ // Hints are merged with hints from previous loaders. ++ QCOMPARE(loader2.loadHints(), QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); ++ ++ QPluginLoader loader3(sys_qualifiedLibraryName("theplugin")); ++ QCOMPARE(loader3.loadHints(), QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); + } + + void tst_QPluginLoader::deleteinstanceOnUnload() +diff --git a/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +index 533fb1c8aa..63ce77d67f 100644 +--- a/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp ++++ b/qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +@@ -75,6 +75,7 @@ private slots: + void arrayStringElements(); + void arraySelfAssign_data() { basics_data(); } + void arraySelfAssign(); ++ void arrayNested(); + + void mapDefaultInitialization(); + void mapEmptyInitializerList(); +@@ -93,6 +94,7 @@ private slots: + void mapSelfAssign(); + void mapComplexKeys_data() { basics_data(); } + void mapComplexKeys(); ++ void mapNested(); + + void sorting(); + +@@ -1570,6 +1572,91 @@ void tst_QCborValue::mapComplexKeys() + QVERIFY(!m.contains(tagged)); + } + ++void tst_QCborValue::arrayNested() ++{ ++ const QCborArray wrongArray = { false, nullptr, QCborValue() }; ++ { ++ QCborArray a1 = { 42, 47 }; ++ QCborArray a2 = { QCborValue(a1) }; ++ QCOMPARE(a2.size(), 1); ++ const QCborValue &first = qAsConst(a2).first(); ++ QVERIFY(first.isArray()); ++ QCOMPARE(first.toArray(wrongArray).size(), 2); ++ QCOMPARE(first.toArray(wrongArray).first(), 42); ++ QCOMPARE(first.toArray(wrongArray).last(), 47); ++ } ++ { ++ QCborArray a1 = { 42, 47 }; ++ QCborArray a2 = { QCborValue(a1) }; ++ QCOMPARE(a2.size(), 1); ++ QCborValueRef first = a2.first(); ++ QVERIFY(first.isArray()); ++ QCOMPARE(first.toArray(wrongArray).size(), 2); ++ QCOMPARE(first.toArray(wrongArray).first(), 42); ++ QCOMPARE(first.toArray(wrongArray).last(), 47); ++ } ++ ++ { ++ QCborArray a1; ++ a1 = { QCborValue(a1) }; // insert it into itself ++ QCOMPARE(a1.size(), 1); ++ const QCborValue &first = qAsConst(a1).first(); ++ QVERIFY(first.isArray()); ++ QCOMPARE(first, QCborArray()); ++ QCOMPARE(first.toArray(wrongArray), QCborArray()); ++ } ++ { ++ QCborArray a1; ++ a1 = { QCborValue(a1) }; // insert it into itself ++ QCborValueRef first = a1.first(); ++ QVERIFY(first.isArray()); ++ QCOMPARE(first, QCborArray()); ++ QCOMPARE(first.toArray(wrongArray), QCborArray()); ++ } ++ { ++ QCborArray a1; ++ a1.append(a1); // insert into itself ++ QCOMPARE(a1.size(), 1); ++ const QCborValue &first = qAsConst(a1).first(); ++ QVERIFY(first.isArray()); ++ QCOMPARE(first, QCborArray()); ++ QCOMPARE(first.toArray(), QCborArray()); ++ } ++ { ++ QCborArray a1; ++ a1.append(a1); // insert into itself ++ QCborValueRef first = a1.first(); ++ QVERIFY(first.isArray()); ++ QCOMPARE(first, QCborArray()); ++ QCOMPARE(first.toArray(), QCborArray()); ++ } ++} ++ ++void tst_QCborValue::mapNested() ++{ ++ const QCborMap wrongMap = { { -1, false }, {-2, nullptr }, { -3, QCborValue() } }; ++ { ++ QCborMap m1 = { {1, 42}, {2, 47} }; ++ QCborMap m2 = { { QString(), m1 } }; ++ QCOMPARE(m2.size(), 1); ++ const QCborValue &first = m2.constBegin().value(); ++ QVERIFY(first.isMap()); ++ QCOMPARE(first.toMap(wrongMap).size(), 2); ++ QCOMPARE(first.toMap(wrongMap).begin().key(), 1); ++ QCOMPARE(first.toMap(wrongMap).begin().value(), 42); ++ } ++ ++ { ++ QCborMap m1; ++ m1 = { { QString(), QCborValue(m1) } }; // insert it into itself ++ QCOMPARE(m1.size(), 1); ++ const QCborValue &first = m1.constBegin().value(); ++ QVERIFY(first.isMap()); ++ QCOMPARE(first, QCborMap()); ++ QCOMPARE(first.toMap(wrongMap), QCborMap()); ++ } ++} ++ + void tst_QCborValue::sorting() + { + QCborValue vundef, vnull(nullptr); +diff --git a/qtbase/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp b/qtbase/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +index 533fcd96f5..513ba9baa9 100644 +--- a/qtbase/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp ++++ b/qtbase/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +@@ -42,6 +42,7 @@ + + #include "qc14n.h" + ++Q_DECLARE_METATYPE(QXmlStreamReader::Error) + Q_DECLARE_METATYPE(QXmlStreamReader::ReadElementTextBehaviour) + + static const char *const catalogFile = "XML-Test-Suite/xmlconf/finalCatalog.xml"; +@@ -587,6 +588,8 @@ private slots: + void readBack() const; + void roundTrip() const; + void roundTrip_data() const; ++ void test_fastScanName_data() const; ++ void test_fastScanName() const; + + void entityExpansionLimit() const; + +@@ -1842,5 +1845,42 @@ void tst_QXmlStream::roundTrip() const + QCOMPARE(out, in); + } + ++void tst_QXmlStream::test_fastScanName_data() const ++{ ++ QTest::addColumn("data"); ++ QTest::addColumn("errorType"); ++ ++ // 4096 is the limit in QXmlStreamReaderPrivate::fastScanName() ++ ++ QByteArray arr = "> 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/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/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp b/qtbase/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp +index 7ba3715e13..752aa122f6 100644 +--- a/qtbase/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp ++++ b/qtbase/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp +@@ -179,6 +179,7 @@ void tst_QAccessibilityLinux::initTestCase() + QVERIFY(!address.isEmpty()); + + m_window = new AccessibleTestWindow(); ++ m_window->setObjectName("mainWindow"_L1); + m_window->show(); + + QVERIFY(QTest::qWaitForWindowExposed(m_window)); +@@ -236,8 +237,11 @@ bool hasState(QDBusInterface *interface, AtspiStateType state) + void tst_QAccessibilityLinux::testLabel() + { + QLabel *l = new QLabel(m_window); ++ l->setObjectName("theObjectName"_L1); + l->setText("Hello A11y"); + m_window->addWidget(l); ++ auto a11yEmpty = new QLabel(m_window); ++ m_window->addWidget(l); + + // Application + QCOMPARE(getParent(mainWindow), QLatin1String(ATSPI_DBUS_PATH_ROOT)); +@@ -249,6 +253,8 @@ void tst_QAccessibilityLinux::testLabel() + QCOMPARE(getChildren(labelInterface).count(), 0); + QCOMPARE(labelInterface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("label")); + QCOMPARE(labelInterface->call(QDBus::Block, "GetRole").arguments().first().toUInt(), 29u); ++ QCOMPARE(labelInterface->call(QDBus::Block, "GetAccessibleId").arguments().first().toString(), ++ QLatin1String("mainWindow.theObjectName")); + QCOMPARE(getParent(labelInterface), mainWindow->path()); + QVERIFY(!hasState(labelInterface, ATSPI_STATE_EDITABLE)); + QVERIFY(hasState(labelInterface, ATSPI_STATE_READ_ONLY)); +@@ -256,7 +262,12 @@ void tst_QAccessibilityLinux::testLabel() + l->setText("New text"); + QCOMPARE(labelInterface->property("Name").toString(), l->text()); + ++ auto *a11yEmptyInterface = getInterface(children.at(1), "org.a11y.atspi.Accessible"); ++ QCOMPARE(a11yEmptyInterface->call(QDBus::Block, "GetAccessibleId").arguments().first().toString(), ++ QLatin1String("mainWindow.QLabel")); ++ + m_window->clearChildren(); ++ delete a11yEmptyInterface; + delete labelInterface; + } + +diff --git a/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp b/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp +index 4aa3f8d60b..d2050a61aa 100644 +--- a/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp ++++ b/qtbase/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp +@@ -115,6 +115,10 @@ void tst_QAbstractItemModelTester::standardItemModelZeroColumns() + // QTBUG-92886 + model.insertRows(0, 5); + model.removeRows(1, 2); ++ ++ const QModelIndex parentIndex = model.index(0, 0); ++ model.insertRows(0, 5, parentIndex); ++ model.removeRows(1, 2, parentIndex); + } + + void tst_QAbstractItemModelTester::testInsertThroughProxy() +diff --git a/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json b/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json +index 18282505e4..ce518e78fb 100644 +--- a/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json ++++ b/qtbase/tests/auto/tools/moc/allmocs_baseline_in.json +@@ -668,40 +668,6 @@ + "inputFile": "task192552.h", + "outputRevision": 67 + }, +- { +- "classes": [ +- { +- "className": "InlineSlotsWithThrowDeclaration", +- "object": true, +- "qualifiedClassName": "InlineSlotsWithThrowDeclaration", +- "slots": [ +- { +- "access": "public", +- "name": "a", +- "returnType": "void" +- }, +- { +- "access": "public", +- "name": "b", +- "returnType": "void" +- }, +- { +- "access": "public", +- "name": "c", +- "returnType": "void" +- } +- ], +- "superClasses": [ +- { +- "access": "public", +- "name": "QObject" +- } +- ] +- } +- ], +- "inputFile": "task189996.h", +- "outputRevision": 67 +- }, + { + "classes": [ + { +diff --git a/qtbase/tests/auto/tools/moc/moc.pro b/qtbase/tests/auto/tools/moc/moc.pro +index c324b3a8cd..4aceb78dc0 100644 +--- a/qtbase/tests/auto/tools/moc/moc.pro ++++ b/qtbase/tests/auto/tools/moc/moc.pro +@@ -15,7 +15,7 @@ cross_compile: DEFINES += MOC_CROSS_COMPILED + HEADERS += using-namespaces.h no-keywords.h task87883.h c-comments.h backslash-newlines.h oldstyle-casts.h \ + slots-with-void-template.h qinvokable.h namespaced-flags.h trigraphs.h \ + escapes-in-string-literals.h cstyle-enums.h qprivateslots.h gadgetwithnoenums.h \ +- dir-in-include-path.h single_function_keyword.h task192552.h task189996.h \ ++ dir-in-include-path.h single_function_keyword.h task192552.h \ + task234909.h task240368.h pure-virtual-signals.h cxx11-enums.h \ + cxx11-final-classes.h \ + cxx11-explicit-override-control.h \ +diff --git a/qtbase/tests/auto/tools/moc/task189996.h b/qtbase/tests/auto/tools/moc/task189996.h +deleted file mode 100644 +index ba9450c271..0000000000 +--- a/qtbase/tests/auto/tools/moc/task189996.h ++++ /dev/null +@@ -1,45 +0,0 @@ +-/**************************************************************************** +-** +-** Copyright (C) 2016 The Qt Company Ltd. +-** Contact: https://www.qt.io/licensing/ +-** +-** This file is part of the test suite of the Qt Toolkit. +-** +-** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +-** Commercial License Usage +-** Licensees holding valid commercial Qt licenses may use this file in +-** accordance with the commercial license agreement provided with the +-** Software or, alternatively, in accordance with the terms contained in +-** a written agreement between you and The Qt Company. For licensing terms +-** and conditions see https://www.qt.io/terms-conditions. For further +-** information use the contact form at https://www.qt.io/contact-us. +-** +-** GNU General Public License Usage +-** Alternatively, this file may be used under the terms of the GNU +-** General Public License version 3 as published by the Free Software +-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +-** included in the packaging of this file. Please review the following +-** information to ensure the GNU General Public License requirements will +-** be met: https://www.gnu.org/licenses/gpl-3.0.html. +-** +-** $QT_END_LICENSE$ +-** +-****************************************************************************/ +-// inline functions can have throw declarations +- +-#ifndef TASK189996_H +-#define TASK189996_H +- +-#include +- +-class InlineSlotsWithThrowDeclaration : public QObject +-{ +- Q_OBJECT +- +-public slots: +- void a() noexcept { } +- void b() const noexcept { } +- void c() noexcept; +-}; +- +-#endif +diff --git a/qtbase/tests/auto/tools/moc/tst_moc.cpp b/qtbase/tests/auto/tools/moc/tst_moc.cpp +index cc465a213a..a9ab6ec4f3 100644 +--- a/qtbase/tests/auto/tools/moc/tst_moc.cpp ++++ b/qtbase/tests/auto/tools/moc/tst_moc.cpp +@@ -671,7 +671,6 @@ private slots: + void templateGtGt(); + void qprivateslots(); + void qprivateproperties(); +- void inlineSlotsWithThrowDeclaration(); + void warnOnPropertyWithoutREAD(); + void constructors(); + void typenameWithUnsigned(); +@@ -816,7 +815,7 @@ void tst_Moc::oldStyleCasts() + + QStringList args; + args << "-c" << "-x" << "c++" << "-Wold-style-cast" << "-I" << "." +- << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++11" << "-"; ++ << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; + proc.start("gcc", args); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); +@@ -886,7 +885,7 @@ void tst_Moc::inputFileNameWithDotsButNoExtension() + + QStringList args; + args << "-c" << "-x" << "c++" << "-I" << ".." +- << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++11" << "-"; ++ << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; + proc.start("gcc", args); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); +@@ -1166,7 +1165,7 @@ void tst_Moc::ignoreOptionClashes() + QStringList gccArgs; + gccArgs << "-c" << "-x" << "c++" << "-I" << ".." + << "-I" << qtIncludePath << "-I" << includeDir << "-o" << "/dev/null" +- << "-fPIC" << "-std=c++11" << "-"; ++ << "-fPIC" << "-std=c++1z" << "-"; + proc.start("gcc", gccArgs); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); +@@ -1585,19 +1584,6 @@ void tst_Moc::qprivateproperties() + + } + +-#include "task189996.h" +- +-void InlineSlotsWithThrowDeclaration::c() noexcept {} +- +-void tst_Moc::inlineSlotsWithThrowDeclaration() +-{ +- InlineSlotsWithThrowDeclaration tst; +- const QMetaObject *mobj = tst.metaObject(); +- QVERIFY(mobj->indexOfSlot("a()") != -1); +- QVERIFY(mobj->indexOfSlot("b()") != -1); +- QVERIFY(mobj->indexOfSlot("c()") != -1); +-} +- + void tst_Moc::warnOnPropertyWithoutREAD() + { + #ifdef MOC_CROSS_COMPILED +@@ -1859,7 +1845,7 @@ void tst_Moc::notifyError() + + QStringList args; + args << "-c" << "-x" << "c++" << "-I" << "." +- << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++11" << "-"; ++ << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; + proc.start("gcc", args); + QVERIFY(proc.waitForStarted()); + proc.write(mocOut); +diff --git a/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp b/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp +index 761357b252..06bb706074 100644 +--- a/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp ++++ b/qtbase/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp +@@ -397,6 +397,7 @@ private slots: + void checkHeaderMinSize(); + + void resizeToContents(); ++ void resizeToContentsSpans(); + + void tabFocus(); + void bigModel(); +@@ -3721,6 +3722,70 @@ void tst_QTableView::resizeToContents() + + } + ++ ++class SpanModel : public QAbstractTableModel ++{ ++public: ++ SpanModel(bool sectionsMoved) ++ : _sectionsMoved(sectionsMoved) ++ {} ++ int columnCount(const QModelIndex & = {}) const override { return 2; } ++ int rowCount(const QModelIndex & = {}) const override { return 1; } ++ QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override ++ { ++ if (role != Qt::DisplayRole) ++ return QVariant(); ++ const int col = _sectionsMoved ? 1 - idx.column() : idx.column(); ++ if (col == 0) ++ return "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; ++ return QVariant(); ++ } ++private: ++ bool _sectionsMoved; ++}; ++ ++ ++void tst_QTableView::resizeToContentsSpans() ++{ ++ SpanModel model1(false); ++ SpanModel model2(true); ++ QTableView view1, view2, view3; ++ view1.setModel(&model1); ++ view2.setModel(&model2); ++ view2.horizontalHeader()->moveSection(0, 1); ++ view3.setModel(&model1); ++ ++ view1.setSpan(0, 0, 1, 2); ++ view2.setSpan(0, 1, 1, 2); ++ view1.show(); ++ view2.show(); ++ view3.show(); ++ QVERIFY(QTest::qWaitForWindowExposed(&view1)); ++ QVERIFY(QTest::qWaitForWindowExposed(&view2)); ++ QVERIFY(QTest::qWaitForWindowExposed(&view3)); ++ view1.setColumnWidth(0, 100); ++ view1.setColumnWidth(1, 100); ++ view2.setColumnWidth(0, 100); ++ view2.setColumnWidth(1, 100); ++ view3.setColumnWidth(0, 200); ++ ++ view1.resizeRowToContents(0); ++ view2.resizeRowToContents(0); ++ view3.resizeRowToContents(0); ++ QCOMPARE(view1.rowHeight(0), view3.rowHeight(0)); ++ QCOMPARE(view2.rowHeight(0), view3.rowHeight(0)); ++ ++ view3.resizeColumnToContents(0); ++ view3.resizeRowToContents(0); ++ // height should be only 1 text line for easy testing ++ view1.setRowHeight(0, view3.verticalHeader()->sectionSize(0)); ++ view2.setRowHeight(0, view3.verticalHeader()->sectionSize(0)); ++ view1.resizeColumnToContents(0); ++ view2.resizeColumnToContents(1); ++ QCOMPARE(view1.columnWidth(0), view3.columnWidth(0) - view1.columnWidth(1)); ++ QCOMPARE(view2.columnWidth(0), view3.columnWidth(0) - view2.columnWidth(1)); ++} ++ + QT_BEGIN_NAMESPACE + extern bool Q_WIDGETS_EXPORT qt_tab_all_widgets(); // qapplication.cpp + QT_END_NAMESPACE +diff --git a/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp b/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp +index eb108a40de..dca5528c1b 100644 +--- a/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp ++++ b/qtbase/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp +@@ -41,6 +41,7 @@ + + #include + #include ++#include + + class tst_QAbstractButton : public QObject + { +@@ -76,6 +77,8 @@ private slots: + void keyNavigation(); + #endif + ++ void buttonPressKeys(); ++ + protected slots: + void onClicked(); + void onToggled( bool on ); +@@ -269,7 +272,13 @@ void tst_QAbstractButton::setAutoRepeat() + QCOMPARE(press_count, click_count); + QVERIFY(click_count > 1); + break; +- case 4: ++ case 4: { ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(Qt::Key_Enter)) { ++ QSKIP("platform theme has Key_Enter in ButtonPressKeys"); ++ } + // check that pressing ENTER has no effect when autorepeat is false + testWidget->setDown( false ); + testWidget->setAutoRepeat( false ); +@@ -286,7 +295,14 @@ void tst_QAbstractButton::setAutoRepeat() + + QVERIFY( click_count == 0 ); + break; +- case 5: ++ } ++ case 5: { ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(Qt::Key_Enter)) { ++ QSKIP("platform theme has Key_Enter in ButtonPressKeys"); ++ } + // check that pressing ENTER has no effect when autorepeat is true + testWidget->setDown( false ); + testWidget->setAutoRepeat( true ); +@@ -304,6 +320,7 @@ void tst_QAbstractButton::setAutoRepeat() + + QVERIFY( click_count == 0 ); + break; ++ } + case 6: + // verify autorepeat is off by default. + MyButton tmp( 0); +@@ -651,5 +668,16 @@ void tst_QAbstractButton::keyNavigation() + } + #endif + ++void tst_QAbstractButton::buttonPressKeys() ++{ ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ for (int i = 0; i < buttonPressKeys.length(); ++i) { ++ QTest::keyClick(testWidget, buttonPressKeys[i]); ++ QCOMPARE(click_count, i + 1); ++ } ++} ++ + QTEST_MAIN(tst_QAbstractButton) + #include "tst_qabstractbutton.moc" +diff --git a/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +index 7af60ed757..46b5af6d63 100644 +--- a/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp ++++ b/qtbase/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +@@ -168,6 +168,7 @@ private slots: + void checkMenuItemPosWhenStyleSheetIsSet(); + void checkEmbeddedLineEditWhenStyleSheetIsSet(); + void propagateStyleChanges(); ++ void buttonPressKeys(); + + private: + PlatformInputContext m_platformInputContext; +@@ -3642,5 +3643,24 @@ void tst_QComboBox::propagateStyleChanges() + QVERIFY(frameStyle.inquired); + } + ++void tst_QComboBox::buttonPressKeys() ++{ ++ QComboBox comboBox; ++ comboBox.setEditable(false); ++ comboBox.addItem(QString::number(1)); ++ comboBox.addItem(QString::number(2)); ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ for (int i = 0; i < buttonPressKeys.length(); ++i) { ++ QTest::keyClick(&comboBox, buttonPressKeys[i]); ++ // On some platforms, a window will not be immediately visible, ++ // but take some event-loop iterations to complete. ++ // Using QTRY_VERIFY to deal with that. ++ QTRY_VERIFY(comboBox.view()->isVisible()); ++ comboBox.hidePopup(); ++ } ++} ++ + QTEST_MAIN(tst_QComboBox) + #include "tst_qcombobox.moc" +diff --git a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro +index be3cfcd104..c228fdfcca 100644 +--- a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro ++++ b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/qcommandlinkbutton.pro +@@ -1,6 +1,6 @@ + CONFIG += testcase + TARGET = tst_qcommandlinkbutton +-QT += widgets testlib ++QT += widgets testlib gui-private + SOURCES += tst_qcommandlinkbutton.cpp + + +diff --git a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp +index 0044d33c66..4cf06296e4 100644 +--- a/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp ++++ b/qtbase/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp +@@ -40,6 +40,9 @@ + #include + #include + ++#include ++#include ++ + class tst_QCommandLinkButton : public QObject + { + Q_OBJECT +@@ -223,6 +226,13 @@ void tst_QCommandLinkButton::setAutoRepeat() + // check that pressing ENTER has no effect + resetCounters(); + testWidget->setDown( false ); ++ // Skip after reset if ButtonPressKeys has Key_Enter ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(Qt::Key_Enter)) { ++ return; ++ } + testWidget->setAutoRepeat( false ); + QTest::keyPress( testWidget, Qt::Key_Enter ); + +@@ -255,6 +265,14 @@ void tst_QCommandLinkButton::pressed() + QCOMPARE( press_count, (uint)1 ); + QCOMPARE( release_count, (uint)1 ); + ++ // Skip if ButtonPressKeys has Key_Enter ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(Qt::Key_Enter)) { ++ return; ++ } ++ + QTest::keyPress( testWidget,Qt::Key_Enter ); + QCOMPARE( press_count, (uint)1 ); + QCOMPARE( release_count, (uint)1 ); +diff --git a/qtbase/tests/auto/widgets/widgets/qdial/tst_qdial.cpp b/qtbase/tests/auto/widgets/widgets/qdial/tst_qdial.cpp +index 356f773ae9..a014df3b15 100644 +--- a/qtbase/tests/auto/widgets/widgets/qdial/tst_qdial.cpp ++++ b/qtbase/tests/auto/widgets/widgets/qdial/tst_qdial.cpp +@@ -42,6 +42,7 @@ private slots: + void valueChanged(); + void sliderMoved(); + void wrappingCheck(); ++ void minEqualMaxValueOutsideRange(); + }; + + // Testing get/set functions +@@ -194,5 +195,14 @@ void tst_QDial::wrappingCheck() + } + } + ++// QTBUG-104641 ++void tst_QDial::minEqualMaxValueOutsideRange() ++{ ++ QDial dial; ++ dial.setRange(30, 30); ++ dial.setWrapping(true); ++ dial.setValue(45); ++} ++ + QTEST_MAIN(tst_QDial) + #include "tst_qdial.moc" +diff --git a/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro b/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro +index 4a5e76ff65..a235fa1fac 100644 +--- a/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro ++++ b/qtbase/tests/auto/widgets/widgets/qgroupbox/qgroupbox.pro +@@ -1,6 +1,6 @@ + CONFIG += testcase + TARGET = tst_qgroupbox +-QT += widgets testlib ++QT += widgets testlib gui-private + SOURCES += tst_qgroupbox.cpp + + +diff --git a/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp b/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp +index 4fb5d262ca..d8d7562b73 100644 +--- a/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp ++++ b/qtbase/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp +@@ -35,6 +35,9 @@ + #include + #include + ++#include ++#include ++ + #include "qgroupbox.h" + + class tst_QGroupBox : public QObject +@@ -69,6 +72,7 @@ private slots: + void propagateFocus(); + void task_QTBUG_19170_ignoreMouseReleaseEvent(); + void task_QTBUG_15519_propagateMouseEvents(); ++ void buttonPressKeys(); + + private: + bool checked; +@@ -610,6 +614,20 @@ void tst_QGroupBox::task_QTBUG_15519_propagateMouseEvents() + QCOMPARE(parent.mouseMoved, true); + } + ++void tst_QGroupBox::buttonPressKeys() ++{ ++ QGroupBox groupBox; ++ groupBox.setCheckable(true); ++ QSignalSpy clickedSpy(&groupBox, &QGroupBox::clicked); ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ for (int i = 0; i < buttonPressKeys.length(); ++i) { ++ QTest::keyClick(&groupBox, buttonPressKeys[i]); ++ QCOMPARE(clickedSpy.length(), i + 1); ++ } ++} ++ + void tst_QGroupBox::sendMouseMoveEvent(QWidget *widget, const QPoint &localPos) + { + // Send a MouseMove event without actually moving the pointer +diff --git a/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro b/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro +index 353ad06ca2..e55f6148f2 100644 +--- a/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro ++++ b/qtbase/tests/auto/widgets/widgets/qpushbutton/qpushbutton.pro +@@ -1,6 +1,6 @@ + CONFIG += testcase + TARGET = tst_qpushbutton +-QT += widgets testlib ++QT += widgets testlib gui-private + SOURCES += tst_qpushbutton.cpp + + +diff --git a/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp b/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +index e818514a79..4043e9326a 100644 +--- a/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp ++++ b/qtbase/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +@@ -41,6 +41,9 @@ + #include + #include + ++#include ++#include ++ + class tst_QPushButton : public QObject + { + Q_OBJECT +@@ -212,6 +215,13 @@ void tst_QPushButton::autoRepeat() + // check that pressing ENTER has no effect + resetCounters(); + testWidget->setDown( false ); ++ // Skip after reset if ButtonPressKeys has Key_Enter ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(Qt::Key_Enter)) { ++ return; ++ } + testWidget->setAutoRepeat( false ); + QTest::keyPress( testWidget, Qt::Key_Enter ); + +@@ -247,6 +257,14 @@ void tst_QPushButton::pressed() + QCOMPARE( press_count, (uint)1 ); + QCOMPARE( release_count, (uint)1 ); + ++ // Skip if ButtonPressKeys has Key_Enter ++ const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() ++ ->themeHint(QPlatformTheme::ButtonPressKeys) ++ .value>(); ++ if (buttonPressKeys.contains(Qt::Key_Enter)) { ++ return; ++ } ++ + QTest::keyPress( testWidget,Qt::Key_Enter ); + QCOMPARE( press_count, (uint)1 ); + QCOMPARE( release_count, (uint)1 ); +diff --git a/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/main.cpp b/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/main.cpp +index b5d9fea315..c5597c6f8e 100644 +--- a/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/main.cpp ++++ b/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/main.cpp +@@ -37,7 +37,6 @@ + + #include "parser.h" + +-static QTextStream qout(stdout, QIODevice::WriteOnly); + static QTextStream qerr(stderr, QIODevice::WriteOnly); + + static void usage() +@@ -79,19 +78,15 @@ int main(int argc, const char *argv[]) + if (out_file_name.isEmpty()) + out_file_name = file_name + ".ref"; + +- QFile _out_file; +- QTextStream _out_stream; +- QTextStream *out_stream; ++ QFile out_file; + if (out_file_name == "-") { +- out_stream = &qout; ++ out_file.open(stdout, QFile::WriteOnly); + } else { +- _out_file.setFileName(out_file_name); +- if (!_out_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { ++ out_file.setFileName(out_file_name); ++ if (!out_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qerr << "Could not open " << out_file_name << ": " << strerror(errno) << Qt::endl; + return 1; + } +- _out_stream.setDevice(&_out_file); +- out_stream = &_out_stream; + } + + Parser parser; +@@ -102,9 +97,7 @@ int main(int argc, const char *argv[]) + + parser.parseFile(&in_file); + +- out_stream->setCodec("utf8"); +- +- *out_stream << parser.result(); ++ out_file.write(parser.result().toUtf8()); + + return 0; + } +diff --git a/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/parser.cpp b/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/parser.cpp +index 44f8101955..24aa9376da 100644 +--- a/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/parser.cpp ++++ b/qtbase/tests/auto/xml/sax/qxmlsimplereader/parser/parser.cpp +@@ -142,11 +142,12 @@ bool ContentHandler::startElement(const QString &namespaceURI, + QString ContentHandler::escapeStr(const QString &s) + { + QString result = s; +- result.replace(QRegularExpression("\""), "\\\""); +- result.replace(QRegularExpression("\\"), "\\\\"); +- result.replace(QRegularExpression("\n"), "\\n"); +- result.replace(QRegularExpression("\r"), "\\r"); +- result.replace(QRegularExpression("\t"), "\\t"); ++ result.replace(QChar(0), "\\0"); ++ result.replace("\\", "\\\\"); ++ result.replace("\"", "\\\""); ++ result.replace("\n", "\\n"); ++ result.replace("\r", "\\r"); ++ result.replace("\t", "\\t"); + return result; + } + +diff --git a/qtbase/tests/auto/xml/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp b/qtbase/tests/auto/xml/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp +index cea4e3c8b8..df158cae0f 100644 +--- a/qtbase/tests/auto/xml/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp ++++ b/qtbase/tests/auto/xml/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp +@@ -311,14 +311,12 @@ void tst_QXmlSimpleReader::testGoodXmlFile() + QVERIFY(file.open(QIODevice::ReadOnly)); + Parser parser; + +- QEXPECT_FAIL(QFINDTESTDATA("xmldocs/valid/sa/089.xml").toLocal8Bit().constData(), "a form feed character is not accepted in XML", Continue); + QVERIFY(parser.parseFile(&file)); + + QFile ref_file(file_name + ".ref"); + QVERIFY(ref_file.open(QIODevice::ReadOnly | QIODevice::Text)); +- QTextStream ref_stream(&ref_file); +- ref_stream.setCodec("UTF-8"); +- QString ref_file_contents = ref_stream.readAll(); ++ QByteArray data = ref_file.readAll(); ++ QString ref_file_contents = QString::fromUtf8(data.constData(), data.size()); + + QCOMPARE(parser.result(), ref_file_contents); + } +@@ -393,9 +391,7 @@ void tst_QXmlSimpleReader::testBadXmlFile() + + QFile ref_file(file_name + ".ref"); + QVERIFY(ref_file.open(QIODevice::ReadOnly | QIODevice::Text)); +- QTextStream ref_stream(&ref_file); +- ref_stream.setCodec("UTF-8"); +- QString ref_file_contents = ref_stream.readAll(); ++ QString ref_file_contents = QString::fromUtf8(ref_file.readAll()); + + QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/145.xml").toLocal8Bit().constData(), "Surrogate code point 0xD800 should be rejected", Continue); + +@@ -469,9 +465,7 @@ void tst_QXmlSimpleReader::testIncrementalParsing() + + QFile ref_file(file_name + ".ref"); + QVERIFY(ref_file.open(QIODevice::ReadOnly | QIODevice::Text)); +- QTextStream ref_stream(&ref_file); +- ref_stream.setCodec("UTF-8"); +- QString ref_file_contents = ref_stream.readAll(); ++ QString ref_file_contents = QString::fromUtf8(ref_file.readAll()); + + QCOMPARE(parser.result(), ref_file_contents); + } +diff --git a/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/not-wf/sa/142.xml.ref b/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/not-wf/sa/142.xml.ref +index 7ce4da6a06..0684cfa943 100644 +Binary files a/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/not-wf/sa/142.xml.ref and b/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/not-wf/sa/142.xml.ref differ +diff --git a/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/064.xml.ref b/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/064.xml.ref +index 579aeb52f6..0b806c96a8 100644 +Binary files a/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/064.xml.ref and b/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/064.xml.ref differ +diff --git a/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/089.xml.ref b/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/089.xml.ref +index 7c68c32286..f09bc2bd09 100644 +Binary files a/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/089.xml.ref and b/qtbase/tests/auto/xml/sax/qxmlsimplereader/xmldocs/valid/sa/089.xml.ref differ +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 45604bed..70020cb6: +diff --git a/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp b/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp +index 9da67272..fcd0fbb2 100644 +--- a/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp ++++ b/qtconnectivity/src/bluetooth/qbluetoothsocket.cpp +@@ -797,7 +797,9 @@ void QBluetoothSocket::close() + Returns true on success + */ + +- ++// ### Qt 7 consider making this function private. The qbluetoothsocket_bluez backend is the ++// the only backend providing publicly accessible support for this. Other backends implement ++// similarly named, but private, overload + bool QBluetoothSocket::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, + SocketState socketState, OpenMode openMode) + { +diff --git a/qtconnectivity/src/bluetooth/qbluetoothuuid.cpp b/qtconnectivity/src/bluetooth/qbluetoothuuid.cpp +index bc00aa95..517465bd 100644 +--- a/qtconnectivity/src/bluetooth/qbluetoothuuid.cpp ++++ b/qtconnectivity/src/bluetooth/qbluetoothuuid.cpp +@@ -834,8 +834,6 @@ QString QBluetoothUuid::serviceClassToString(QBluetoothUuid::ServiceClassUuid uu + //: Connection management (Bluetooth) + case QBluetoothUuid::BondManagement: return QBluetoothServiceDiscoveryAgent::tr("Bond Management"); + case QBluetoothUuid::ContinuousGlucoseMonitoring: return QBluetoothServiceDiscoveryAgent::tr("Continuous Glucose Monitoring"); +- default: +- break; + } + + return QString(); +@@ -878,8 +876,6 @@ QString QBluetoothUuid::protocolToString(QBluetoothUuid::ProtocolUuid uuid) + case QBluetoothUuid::McapControlChannel: return QBluetoothServiceDiscoveryAgent::tr("Multi-Channel Adaptation Protocol - Control"); + case QBluetoothUuid::McapDataChannel: return QBluetoothServiceDiscoveryAgent::tr("Multi-Channel Adaptation Protocol - Data"); + case QBluetoothUuid::L2cap: return QBluetoothServiceDiscoveryAgent::tr("Layer 2 Control Protocol"); +- default: +- break; + } + + return QString(); +@@ -1081,13 +1077,13 @@ QString QBluetoothUuid::characteristicToString(CharacteristicType uuid) + case QBluetoothUuid::BodyCompositionFeature: return QBluetoothServiceDiscoveryAgent::tr("Body Composition Feature"); + case QBluetoothUuid::BodyCompositionMeasurement: return QBluetoothServiceDiscoveryAgent::tr("Body Composition Measurement"); + case QBluetoothUuid::WeightMeasurement: return QBluetoothServiceDiscoveryAgent::tr("Weight Measurement"); ++ case QBluetoothUuid::CharacteristicType::WeightScaleFeature: ++ return QBluetoothServiceDiscoveryAgent::tr("Weight Scale Feature"); + case QBluetoothUuid::UserControlPoint: return QBluetoothServiceDiscoveryAgent::tr("User Control Point"); + case QBluetoothUuid::MagneticFluxDensity2D: return QBluetoothServiceDiscoveryAgent::tr("Magnetic Flux Density 2D"); + case QBluetoothUuid::MagneticFluxDensity3D: return QBluetoothServiceDiscoveryAgent::tr("Magnetic Flux Density 3D"); + case QBluetoothUuid::Language: return QBluetoothServiceDiscoveryAgent::tr("Language"); + case QBluetoothUuid::BarometricPressureTrend: return QBluetoothServiceDiscoveryAgent::tr("Barometric Pressure Trend"); +- default: +- break; + } + + return QString(); +@@ -1104,6 +1100,8 @@ QString QBluetoothUuid::characteristicToString(CharacteristicType uuid) + QString QBluetoothUuid::descriptorToString(QBluetoothUuid::DescriptorType uuid) + { + switch (uuid) { ++ case QBluetoothUuid::UnknownDescriptorType: ++ break; // returns {} below + case QBluetoothUuid::CharacteristicExtendedProperties: + return QBluetoothServiceDiscoveryAgent::tr("Characteristic Extended Properties"); + case QBluetoothUuid::CharacteristicUserDescription: +@@ -1128,8 +1126,6 @@ QString QBluetoothUuid::descriptorToString(QBluetoothUuid::DescriptorType uuid) + return QBluetoothServiceDiscoveryAgent::tr("Environmental Sensing Measurement"); + case QBluetoothUuid::EnvironmentalSensingTriggerSetting: + return QBluetoothServiceDiscoveryAgent::tr("Environmental Sensing Trigger Setting"); +- default: +- break; + } + + return QString(); +diff --git a/qtconnectivity/src/tools/sdpscanner/main.cpp b/qtconnectivity/src/tools/sdpscanner/main.cpp +index 7e09ca6e..c39ff8f3 100644 +--- a/qtconnectivity/src/tools/sdpscanner/main.cpp ++++ b/qtconnectivity/src/tools/sdpscanner/main.cpp +@@ -39,6 +39,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -159,7 +160,9 @@ static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray & + break; + } else if (!isprint(text[i])) { + hasNonPrintableChar = true; +- text.resize(text.indexOf('\0')); // cut trailing content ++ const auto firstNullIdx = text.indexOf('\0'); ++ if (firstNullIdx > 0) ++ text.resize(firstNullIdx); // cut trailing content + break; + } + } +@@ -211,11 +214,17 @@ static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray & + case SDP_URL_STR8: + case SDP_URL_STR16: + case SDP_URL_STR32: +- strncpy(snBuffer, data->val.str, data->unitSize - 1); ++ { + xmlOutput.append("val.str, qstrnlen(data->val.str, data->unitSize)); ++ const QUrl url = QUrl::fromEncoded(urlData); ++ // Encoded url %-encodes all of the XML special characters except '&', ++ // so we need to do that manually ++ xmlOutput.append(url.toEncoded().replace('&', "&")); + xmlOutput.append("\"/>\n"); + break; ++ } + default: + fprintf(stderr, "Unknown dtd type\n"); + } +Submodule qtdeclarative 105e6105..792a55bb: +diff --git a/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp b/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp +index 88c0e9e60e..61a9dc36f8 100644 +--- a/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp ++++ b/qtdeclarative/src/qml/animations/qcontinuinganimationgroupjob.cpp +@@ -82,9 +82,9 @@ void QContinuingAnimationGroupJob::updateState(QAbstractAnimationJob::State newS + return; + } + for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { +- resetUncontrolledAnimationFinishTime(animation); ++ RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation)); + animation->setDirection(m_direction); +- animation->start(); ++ RETURN_IF_DELETED(animation->start()); + } + break; + } +diff --git a/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp b/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp +index 420a934ba2..a828d0e234 100644 +--- a/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp ++++ b/qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp +@@ -144,10 +144,10 @@ void QParallelAnimationGroupJob::updateState(QAbstractAnimationJob::State newSta + animation->stop(); + m_previousLoop = m_direction == Forward ? 0 : m_loopCount - 1; + } +- resetUncontrolledAnimationFinishTime(animation); ++ RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation)); + animation->setDirection(m_direction); + if (shouldAnimationStart(animation, oldState == Stopped)) +- animation->start(); ++ RETURN_IF_DELETED(animation->start()); + } + break; + } +diff --git a/qtdeclarative/src/qml/common/qqmljsmemorypool_p.h b/qtdeclarative/src/qml/common/qqmljsmemorypool_p.h +index 0cf7ea84e6..1b81a87a2c 100644 +--- a/qtdeclarative/src/qml/common/qqmljsmemorypool_p.h ++++ b/qtdeclarative/src/qml/common/qqmljsmemorypool_p.h +@@ -87,7 +87,7 @@ public: + inline void *allocate(size_t size) + { + size = (size + 7) & ~size_t(7); +- if (Q_LIKELY(_ptr && (_ptr + size < _end))) { ++ if (Q_LIKELY(_ptr && size < size_t(_end - _ptr))) { + void *addr = _ptr; + _ptr += size; + return addr; +diff --git a/qtdeclarative/src/qml/jit/qv4baselinejit.cpp b/qtdeclarative/src/qml/jit/qv4baselinejit.cpp +index 45150cfffd..5ad53faf95 100644 +--- a/qtdeclarative/src/qml/jit/qv4baselinejit.cpp ++++ b/qtdeclarative/src/qml/jit/qv4baselinejit.cpp +@@ -540,6 +540,8 @@ void BaselineJIT::generate_ThrowException() + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowException, CallResultDestination::Ignore); + as->gotoCatchException(); ++ ++ // LOAD_ACC(); <- not needed here since it would be unreachable. + } + + void BaselineJIT::generate_GetException() { as->getException(); } +@@ -547,9 +549,11 @@ void BaselineJIT::generate_SetException() { as->setException(); } + + void BaselineJIT::generate_CreateCallContext() + { ++ STORE_ACC(); + as->prepareCallWithArgCount(1); + as->passCppFrameAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(PushCallContext, CallResultDestination::Ignore); ++ LOAD_ACC(); + } + + void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); } +diff --git a/qtdeclarative/src/qml/jsruntime/qv4function.cpp b/qtdeclarative/src/qml/jsruntime/qv4function.cpp +index cf8a53cf9f..223e64271e 100644 +--- a/qtdeclarative/src/qml/jsruntime/qv4function.cpp ++++ b/qtdeclarative/src/qml/jsruntime/qv4function.cpp +@@ -136,7 +136,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList + T *QRecyclePool::New() + { + T *rv = d->allocate(); +- new (rv) T; +- return rv; ++ return new (rv) T; + } + + template +@@ -139,8 +138,7 @@ template + T *QRecyclePool::New(const T1 &a) + { + T *rv = d->allocate(); +- new (rv) T(a); +- return rv; ++ return new (rv) T(a); + } + + template +@@ -148,8 +146,7 @@ template + T *QRecyclePool::New(T1 &a) + { + T *rv = d->allocate(); +- new (rv) T(a); +- return rv; ++ return new (rv) T(a); + } + + template +diff --git a/qtdeclarative/src/qml/qml/qqmldata_p.h b/qtdeclarative/src/qml/qml/qqmldata_p.h +index ee31cb38d9..187339169b 100644 +--- a/qtdeclarative/src/qml/qml/qqmldata_p.h ++++ b/qtdeclarative/src/qml/qml/qqmldata_p.h +@@ -176,24 +176,24 @@ public: + }; + + struct NotifyList { +- quint64 connectionMask; +- +- quint16 maximumTodoIndex; +- quint16 notifiesSize; +- +- QQmlNotifierEndpoint *todo; +- QQmlNotifierEndpoint**notifies; ++ QAtomicInteger connectionMask; ++ QQmlNotifierEndpoint *todo = nullptr; ++ QQmlNotifierEndpoint**notifies = nullptr; ++ quint16 maximumTodoIndex = 0; ++ quint16 notifiesSize = 0; + void layout(); + private: + void layout(QQmlNotifierEndpoint*); + }; +- NotifyList *notifyList; ++ QAtomicPointer notifyList; + +- inline QQmlNotifierEndpoint *notify(int index); ++ inline QQmlNotifierEndpoint *notify(int index) const; + void addNotify(int index, QQmlNotifierEndpoint *); + int endpointCount(int index); + bool signalHasEndpoint(int index) const; +- void disconnectNotifiers(); ++ ++ enum class DeleteNotifyList { Yes, No }; ++ void disconnectNotifiers(DeleteNotifyList doDelete); + + // The context that created the C++ object + QQmlContextData *context = nullptr; +@@ -201,12 +201,12 @@ public: + QQmlContextData *outerContext = nullptr; + QQmlContextDataRef ownContext; + +- QQmlAbstractBinding *bindings; +- QQmlBoundSignal *signalHandlers; ++ QQmlAbstractBinding *bindings = nullptr; ++ QQmlBoundSignal *signalHandlers = nullptr; + + // Linked list for QQmlContext::contextObjects +- QQmlData *nextContextObject; +- QQmlData**prevContextObject; ++ QQmlData *nextContextObject = nullptr; ++ QQmlData**prevContextObject = nullptr; + + inline bool hasBindingBit(int) const; + inline void setBindingBit(QObject *obj, int); +@@ -216,10 +216,10 @@ public: + inline void setPendingBindingBit(QObject *obj, int); + inline void clearPendingBindingBit(int); + +- quint16 lineNumber; +- quint16 columnNumber; ++ quint16 lineNumber = 0; ++ quint16 columnNumber = 0; + +- quint32 jsEngineId; // id of the engine that created the jsWrapper ++ quint32 jsEngineId = 0; // id of the engine that created the jsWrapper + + struct DeferredData { + DeferredData(); +@@ -240,7 +240,7 @@ public: + + QQmlPropertyCache *propertyCache; + +- QQmlGuardImpl *guards; ++ QQmlGuardImpl *guards = nullptr; + + static QQmlData *get(const QObject *object, bool create = false) { + QObjectPrivate *priv = QObjectPrivate::get(const_cast(object)); +@@ -289,7 +289,7 @@ public: + + private: + // For attachedProperties +- mutable QQmlDataExtended *extendedData; ++ mutable QQmlDataExtended *extendedData = nullptr; + + Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv); + Q_NEVER_INLINE static QQmlPropertyCache *createPropertyCache(QJSEngine *engine, QObject *object); +@@ -342,23 +342,31 @@ bool QQmlData::wasDeleted(const QObject *object) + return ddata && ddata->isQueuedForDeletion; + } + +-QQmlNotifierEndpoint *QQmlData::notify(int index) ++inline bool isIndexInConnectionMask(quint64 connectionMask, int index) ++{ ++ return connectionMask & (1ULL << quint64(index % 64)); ++} ++ ++QQmlNotifierEndpoint *QQmlData::notify(int index) const + { ++ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. ++ + Q_ASSERT(index <= 0xFFFF); + +- if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) { ++ NotifyList *list = notifyList.loadRelaxed(); ++ if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index)) + return nullptr; +- } else if (index < notifyList->notifiesSize) { +- return notifyList->notifies[index]; +- } else if (index <= notifyList->maximumTodoIndex) { +- notifyList->layout(); +- } + +- if (index < notifyList->notifiesSize) { +- return notifyList->notifies[index]; +- } else { +- return nullptr; ++ if (index < list->notifiesSize) ++ return list->notifies[index]; ++ ++ if (index <= list->maximumTodoIndex) { ++ list->layout(); ++ if (index < list->notifiesSize) ++ return list->notifies[index]; + } ++ ++ return nullptr; + } + + /* +@@ -367,7 +375,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index) + */ + inline bool QQmlData::signalHasEndpoint(int index) const + { +- return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64))); ++ // This can be called from any thread. ++ // We still use relaxed semantics. If we're on a thread different from the "home" thread ++ // of the QQmlData, two interesting things might happen: ++ // ++ // 1. The list might go away while we hold it. In that case we are dealing with an object whose ++ // QObject dtor is being executed concurrently. This is UB already without the notify lists. ++ // Therefore, we don't need to consider it. ++ // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case ++ // we "misreport" the endpoint. Since ordering of events across threads is inherently ++ // nondeterministic, either result is correct in that case. We can accept it. ++ ++ NotifyList *list = notifyList.loadRelaxed(); ++ return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index); + } + + bool QQmlData::hasBindingBit(int coreIndex) const +diff --git a/qtdeclarative/src/qml/qml/qqmlengine.cpp b/qtdeclarative/src/qml/qml/qqmlengine.cpp +index 852a673ebd..5f3367e4d2 100644 +--- a/qtdeclarative/src/qml/qml/qqmlengine.cpp ++++ b/qtdeclarative/src/qml/qml/qqmlengine.cpp +@@ -718,18 +718,15 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) + // Disconnect the notifiers now - during object destruction this would be too late, since + // the disconnect call wouldn't be able to call disconnectNotify(), as it isn't possible to + // get the metaobject anymore. +- d->disconnectNotifiers(); ++ d->disconnectNotifiers(QQmlData::DeleteNotifyList::No); + } + } + + QQmlData::QQmlData() + : ownedByQml1(false), ownMemory(true), indestructible(true), explicitIndestructibleSet(false), + hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), +- hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), +- bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr), +- bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr), +- lineNumber(0), columnNumber(0), jsEngineId(0), +- propertyCache(nullptr), guards(nullptr), extendedData(nullptr) ++ hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), dummy(0), ++ bindingBitsArraySize(InlineBindingArraySize), propertyCache(nullptr) + { + memset(bindingBitsValue, 0, sizeof(bindingBitsValue)); + init(); +@@ -789,7 +786,10 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in + // QQmlEngine to emit signals from a different thread. These signals are then automatically + // marshalled back onto the QObject's thread and handled by QML from there. This is tested + // by the qqmlecmascript::threadSignal() autotest. +- if (!ddata->notifyList) ++ ++ // Relaxed semantics here. If we're on a different thread we might schedule a useless event, ++ // but that should be rare. ++ if (!ddata->notifyList.loadRelaxed()) + return; + + auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed(); +@@ -1588,17 +1588,22 @@ void qmlExecuteDeferred(QObject *object) + { + QQmlData *data = QQmlData::get(object); + +- if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) { +- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); ++ if (!data ++ || !data->context ++ || !data->context->engine ++ || data->deferredData.isEmpty() ++ || data->wasDeleted(object)) { ++ return; ++ } + +- QQmlComponentPrivate::DeferredState state; +- QQmlComponentPrivate::beginDeferred(ep, object, &state); ++ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); ++ QQmlComponentPrivate::DeferredState state; ++ QQmlComponentPrivate::beginDeferred(ep, object, &state); + +- // Release the reference for the deferral action (we still have one from construction) +- data->releaseDeferredData(); ++ // Release the reference for the deferral action (we still have one from construction) ++ data->releaseDeferredData(); + +- QQmlComponentPrivate::completeDeferred(ep, &state); +- } ++ QQmlComponentPrivate::completeDeferred(ep, &state); + } + + QQmlContext *qmlContext(const QObject *obj) +@@ -1835,49 +1840,73 @@ void QQmlData::releaseDeferredData() + + void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint) + { +- if (!notifyList) { +- notifyList = (NotifyList *)malloc(sizeof(NotifyList)); +- notifyList->connectionMask = 0; +- notifyList->maximumTodoIndex = 0; +- notifyList->notifiesSize = 0; +- notifyList->todo = nullptr; +- notifyList->notifies = nullptr; ++ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. ++ ++ NotifyList *list = notifyList.loadRelaxed(); ++ ++ if (!list) { ++ list = new NotifyList; ++ // We don't really care when this change takes effect on other threads. The notifyList can ++ // only become non-null once in the life time of a QQmlData. It becomes null again when the ++ // underlying QObject is deleted. At that point any interaction with the QQmlData is UB ++ // anyway. So, for all intents and purposese, the list becomes non-null once and then stays ++ // non-null "forever". We can apply relaxed semantics. ++ notifyList.storeRelaxed(list); + } + + Q_ASSERT(!endpoint->isConnected()); + + index = qMin(index, 0xFFFF - 1); +- notifyList->connectionMask |= (1ULL << quint64(index % 64)); + +- if (index < notifyList->notifiesSize) { ++ // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other ++ // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying ++ // the conenctionMask in the presence of concurrent modification, any result is correct. ++ list->connectionMask.storeRelaxed( ++ list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64))); + +- endpoint->next = notifyList->notifies[index]; ++ if (index < list->notifiesSize) { ++ endpoint->next = list->notifies[index]; + if (endpoint->next) endpoint->next->prev = &endpoint->next; +- endpoint->prev = ¬ifyList->notifies[index]; +- notifyList->notifies[index] = endpoint; +- ++ endpoint->prev = &list->notifies[index]; ++ list->notifies[index] = endpoint; + } else { +- notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index); ++ list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index); + +- endpoint->next = notifyList->todo; ++ endpoint->next = list->todo; + if (endpoint->next) endpoint->next->prev = &endpoint->next; +- endpoint->prev = ¬ifyList->todo; +- notifyList->todo = endpoint; ++ endpoint->prev = &list->todo; ++ list->todo = endpoint; + } + } + +-void QQmlData::disconnectNotifiers() ++void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete) + { +- if (notifyList) { +- while (notifyList->todo) +- notifyList->todo->disconnect(); +- for (int ii = 0; ii < notifyList->notifiesSize; ++ii) { +- while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii]) ++ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. ++ if (NotifyList *list = notifyList.loadRelaxed()) { ++ while (QQmlNotifierEndpoint *todo = list->todo) ++ todo->disconnect(); ++ for (int ii = 0; ii < list->notifiesSize; ++ii) { ++ while (QQmlNotifierEndpoint *ep = list->notifies[ii]) + ep->disconnect(); + } +- free(notifyList->notifies); +- free(notifyList); +- notifyList = nullptr; ++ free(list->notifies); ++ ++ if (doDelete == DeleteNotifyList::Yes) { ++ // We can only get here from QQmlData::destroyed(), and that can only come from the ++ // the QObject dtor. If you're still sending signals at that point you have UB already ++ // without any threads. Therefore, it's enough to apply relaxed semantics. ++ notifyList.storeRelaxed(nullptr); ++ delete list; ++ } else { ++ // We can use relaxed semantics here. The worst thing that can happen is that some ++ // signal is falsely reported as connected. Signal connectedness across threads ++ // is not quite deterministic anyway. ++ list->connectionMask.storeRelaxed(0); ++ list->maximumTodoIndex = 0; ++ list->notifiesSize = 0; ++ list->notifies = nullptr; ++ ++ } + } + } + +@@ -1961,7 +1990,7 @@ void QQmlData::destroyed(QObject *object) + guard->objectDestroyed(object); + } + +- disconnectNotifiers(); ++ disconnectNotifiers(DeleteNotifyList::Yes); + + if (extendedData) + delete extendedData; +diff --git a/qtdeclarative/src/qml/qml/qqmlimport.cpp b/qtdeclarative/src/qml/qml/qqmlimport.cpp +index e7263d1850..289f11d006 100644 +--- a/qtdeclarative/src/qml/qml/qqmlimport.cpp ++++ b/qtdeclarative/src/qml/qml/qqmlimport.cpp +@@ -2119,9 +2119,12 @@ void QQmlImportDatabase::addImportPath(const QString& path) + cPath.replace(Backslash, Slash); + } + +- if (!cPath.isEmpty() +- && !fileImportPath.contains(cPath)) +- fileImportPath.prepend(cPath); ++ if (!cPath.isEmpty()) { ++ if (fileImportPath.contains(cPath)) ++ fileImportPath.move(fileImportPath.indexOf(cPath), 0); ++ else ++ fileImportPath.prepend(cPath); ++ } + } + + /*! +diff --git a/qtdeclarative/src/qml/qml/qqmltypewrapper.cpp b/qtdeclarative/src/qml/qml/qqmltypewrapper.cpp +index 175de8b936..a6ba4b8cb3 100644 +--- a/qtdeclarative/src/qml/qml/qqmltypewrapper.cpp ++++ b/qtdeclarative/src/qml/qml/qqmltypewrapper.cpp +@@ -419,8 +419,10 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const + return Encode(false); + + QQmlRefPointer td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); +- ExecutableCompilationUnit *cu = td->compilationUnit(); +- myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); ++ if (ExecutableCompilationUnit *cu = td->compilationUnit()) ++ myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); ++ else ++ return Encode(false); // It seems myQmlType has some errors, so we could not compile it. + } else { + myQmlType = qenginepriv->metaObjectForType(myTypeId); + } +diff --git a/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp b/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp +index 1e0e4e419f..a0532d1794 100644 +--- a/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp ++++ b/qtdeclarative/src/qml/qml/qqmlvmemetaobject.cpp +@@ -251,7 +251,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() + if (!pd) + return; + +- if (pd->notifyIndex() != -1) ++ if (pd->notifyIndex() != -1 && ctxt->engine) + connect(target, pd->notifyIndex(), ctxt->engine); + } + +diff --git a/qtdeclarative/src/qml/types/qqmlconnections.cpp b/qtdeclarative/src/qml/types/qqmlconnections.cpp +index 4a4e6ce12c..a5889b7396 100644 +--- a/qtdeclarative/src/qml/types/qqmlconnections.cpp ++++ b/qtdeclarative/src/qml/types/qqmlconnections.cpp +@@ -341,7 +341,7 @@ void QQmlConnections::connectSignalsToMethods() + && propName.at(2).isUpper()) { + qmlWarning(this) << tr("Detected function \"%1\" in Connections element. " + "This is probably intended to be a signal handler but no " +- "signal of the target matches the name.").arg(propName); ++ "signal of the \"%2\" target matches the name.").arg(propName).arg(target->metaObject()->className()); + } + } + } +diff --git a/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp b/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp +index 4fcff70de6..5b7e767ae2 100644 +--- a/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp ++++ b/qtdeclarative/src/qmlmodels/qqmldelegatemodel.cpp +@@ -389,6 +389,12 @@ void QQmlDelegateModelPrivate::connectToAbstractItemModel() + q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); ++ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsInserted(QModelIndex,int,int)), ++ q, QQmlDelegateModel, SLOT(_q_columnsInserted(QModelIndex,int,int))); ++ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), ++ q, QQmlDelegateModel, SLOT(_q_columnsRemoved(QModelIndex,int,int))); ++ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), ++ q, QQmlDelegateModel, SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), + q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); + qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), +@@ -413,6 +419,12 @@ void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() + q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), + q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); ++ QObject::disconnect(aim, SIGNAL(columnsInserted(QModelIndex,int,int)), q, ++ SLOT(_q_columnsInserted(QModelIndex,int,int))); ++ QObject::disconnect(aim, SIGNAL(columnsRemoved(QModelIndex,int,int)), q, ++ SLOT(_q_columnsRemoved(QModelIndex,int,int))); ++ QObject::disconnect(aim, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), q, ++ SLOT(_q_columnsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), + q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); + QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), +@@ -1871,10 +1883,15 @@ void QQmlDelegateModelPrivate::emitChanges() + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); + +- auto cacheCopy = m_cache; // deliberate; emitChanges may alter m_cache +- for (QQmlDelegateModelItem *cacheItem : qAsConst(cacheCopy)) { +- if (cacheItem->attached) +- cacheItem->attached->emitChanges(); ++ // emitChanges may alter m_cache and delete items ++ QVarLengthArray> attachedObjects; ++ attachedObjects.reserve(m_cache.length()); ++ for (const QQmlDelegateModelItem *cacheItem : qAsConst(m_cache)) ++ attachedObjects.append(cacheItem->attached); ++ ++ for (const QPointer &attached : qAsConst(attachedObjects)) { ++ if (attached && attached->m_cacheItem) ++ attached->emitChanges(); + } + } + +@@ -1974,6 +1991,38 @@ void QQmlDelegateModel::_q_rowsMoved( + } + } + ++void QQmlDelegateModel::_q_columnsInserted(const QModelIndex &parent, int begin, int end) ++{ ++ Q_D(QQmlDelegateModel); ++ Q_UNUSED(end); ++ if (parent == d->m_adaptorModel.rootIndex && begin == 0) { ++ // mark all items as changed ++ _q_itemsChanged(0, d->m_count, QVector()); ++ } ++} ++ ++void QQmlDelegateModel::_q_columnsRemoved(const QModelIndex &parent, int begin, int end) ++{ ++ Q_D(QQmlDelegateModel); ++ Q_UNUSED(end); ++ if (parent == d->m_adaptorModel.rootIndex && begin == 0) { ++ // mark all items as changed ++ _q_itemsChanged(0, d->m_count, QVector()); ++ } ++} ++ ++void QQmlDelegateModel::_q_columnsMoved(const QModelIndex &parent, int start, int end, ++ const QModelIndex &destination, int column) ++{ ++ Q_D(QQmlDelegateModel); ++ Q_UNUSED(end); ++ if ((parent == d->m_adaptorModel.rootIndex && start == 0) ++ || (destination == d->m_adaptorModel.rootIndex && column == 0)) { ++ // mark all items as changed ++ _q_itemsChanged(0, d->m_count, QVector()); ++ } ++} ++ + void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) + { + Q_D(QQmlDelegateModel); +@@ -2663,20 +2712,24 @@ void QQmlDelegateModelAttached::emitChanges() + m_previousGroups = m_cacheItem->groups; + + int indexChanges = 0; +- for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { ++ const int groupCount = m_cacheItem->metaType->groupCount; ++ for (int i = 1; i < groupCount; ++i) { + if (m_previousIndex[i] != m_currentIndex[i]) { + m_previousIndex[i] = m_currentIndex[i]; + indexChanges |= (1 << i); + } + } + ++ // Don't access m_cacheItem anymore once we've started sending signals. ++ // We don't own it and someone might delete it. ++ + int notifierId = 0; + const QMetaObject *meta = metaObject(); +- for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { ++ for (int i = 1; i < groupCount; ++i, ++notifierId) { + if (groupChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, nullptr); + } +- for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { ++ for (int i = 1; i < groupCount; ++i, ++notifierId) { + if (indexChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, nullptr); + } +diff --git a/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h b/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h +index 8aab4badca..d140bfbaaf 100644 +--- a/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h ++++ b/qtdeclarative/src/qmlmodels/qqmldelegatemodel_p.h +@@ -152,6 +152,9 @@ private Q_SLOTS: + void _q_itemsMoved(int from, int to, int count); + void _q_modelReset(); + void _q_rowsInserted(const QModelIndex &,int,int); ++ void _q_columnsInserted(const QModelIndex &, int, int); ++ void _q_columnsRemoved(const QModelIndex &, int, int); ++ void _q_columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int); + void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); + void _q_rowsRemoved(const QModelIndex &,int,int); + void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); +diff --git a/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp b/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index ae1954ae8d..99e6eff7c3 100644 +--- a/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ b/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -46,6 +46,7 @@ + #include "QtQuick/private/qquicktextinput_p.h" + #include "QtQuick/private/qquickaccessibleattached_p.h" + #include "QtQuick/qquicktextdocument.h" ++#include "QtQuick/qquickrendercontrol.h" + QT_BEGIN_NAMESPACE + + #if QT_CONFIG(accessibility) +@@ -57,7 +58,19 @@ QAccessibleQuickItem::QAccessibleQuickItem(QQuickItem *item) + + QWindow *QAccessibleQuickItem::window() const + { +- return item()->window(); ++ QQuickWindow *window = item()->window(); ++ ++ // For QQuickWidget the above window will be the offscreen QQuickWindow, ++ // which is not a part of the accessibility tree. Detect this case and ++ // return the window for the QQuickWidget instead. ++ if (window && !window->handle()) { ++ if (QQuickRenderControl *renderControl = QQuickWindowPrivate::get(window)->renderControl) { ++ if (QWindow *renderWindow = renderControl->renderWindow(nullptr)) ++ return renderWindow; ++ } ++ } ++ ++ return window; + } + + int QAccessibleQuickItem::childCount() const +@@ -113,19 +126,15 @@ QAccessibleInterface *QAccessibleQuickItem::childAt(int x, int y) const + QAccessibleInterface *QAccessibleQuickItem::parent() const + { + QQuickItem *parent = item()->parentItem(); +- QQuickWindow *window = item()->window(); +- QQuickItem *ci = window ? window->contentItem() : nullptr; ++ QQuickWindow *itemWindow = item()->window(); ++ QQuickItem *ci = itemWindow ? itemWindow->contentItem() : nullptr; + while (parent && !QQuickItemPrivate::get(parent)->isAccessible && parent != ci) + parent = parent->parentItem(); + + if (parent) { + if (parent == ci) { +- // Jump out to the scene widget if the parent is the root item. +- // There are two root items, QQuickWindow::rootItem and +- // QQuickView::declarativeRoot. The former is the true root item, +- // but is not a part of the accessibility tree. Check if we hit +- // it here and return an interface for the scene instead. +- return QAccessible::queryAccessibleInterface(window); ++ // Jump out to the window if the parent is the root item ++ return QAccessible::queryAccessibleInterface(window()); + } else { + while (parent && !parent->d_func()->isAccessible) + parent = parent->parentItem(); +@@ -193,7 +202,7 @@ QAccessible::State QAccessibleQuickItem::state() const + QRect viewRect_ = viewRect(); + QRect itemRect = rect(); + +- if (viewRect_.isNull() || itemRect.isNull() || !item()->window() || !item()->window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) ++ if (viewRect_.isNull() || itemRect.isNull() || !window() || !window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) + state.invisible = true; + if (!viewRect_.intersects(itemRect)) + state.offscreen = true; +@@ -206,6 +215,10 @@ QAccessible::State QAccessibleQuickItem::state() const + if (role() == QAccessible::EditableText) + if (auto ti = qobject_cast(item())) + state.passwordEdit = ti->echoMode() != QQuickTextInput::Normal; ++ if (!item()->isEnabled()) { ++ state.focusable = false; ++ state.disabled = true; ++ } + return state; + } + +@@ -217,7 +230,7 @@ QAccessible::Role QAccessibleQuickItem::role() const + + QAccessible::Role role = QAccessible::NoRole; + if (item()) +- role = QQuickItemPrivate::get(item())->accessibleRole(); ++ role = QQuickItemPrivate::get(item())->effectiveAccessibleRole(); + if (role == QAccessible::NoRole) { + if (qobject_cast(const_cast(item()))) + role = QAccessible::StaticText; +diff --git a/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h b/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h +index 39ffcaf39c..8baa01330c 100644 +--- a/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h ++++ b/qtdeclarative/src/quick/accessible/qaccessiblequickview_p.h +@@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE + + #if QT_CONFIG(accessibility) + +-class QAccessibleQuickWindow : public QAccessibleObject ++class Q_QUICK_EXPORT QAccessibleQuickWindow : public QAccessibleObject + { + public: + QAccessibleQuickWindow(QQuickWindow *object); +diff --git a/qtdeclarative/src/quick/items/qquickdrag.cpp b/qtdeclarative/src/quick/items/qquickdrag.cpp +index 8321fcfeed..383078b3b9 100644 +--- a/qtdeclarative/src/quick/items/qquickdrag.cpp ++++ b/qtdeclarative/src/quick/items/qquickdrag.cpp +@@ -481,7 +481,9 @@ void QQuickDragAttached::setKeys(const QStringList &keys) + \qmlattachedproperty stringlist QtQuick::Drag::mimeData + \since 5.2 + +- This property holds a map of mimeData that is used during startDrag. ++ This property holds a map from mime type to data that is used during startDrag. ++ The mime data needs to be a \c string, or an \c ArrayBuffer with the data encoded ++ according to the mime type. + */ + + QVariantMap QQuickDragAttached::mimeData() const +@@ -766,8 +768,12 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct + QDrag *drag = new QDrag(source ? source : q); + QMimeData *mimeData = new QMimeData(); + +- for (auto it = externalMimeData.cbegin(), end = externalMimeData.cend(); it != end; ++it) +- mimeData->setData(it.key(), it.value().toString().toUtf8()); ++ for (auto it = externalMimeData.cbegin(), end = externalMimeData.cend(); it != end; ++it) { ++ if (static_cast(it.value().type()) == QMetaType::QByteArray) ++ mimeData->setData(it.key(), it.value().toByteArray()); ++ else ++ mimeData->setData(it.key(), it.value().toString().toUtf8()); ++ } + + drag->setMimeData(mimeData); + if (pixmapLoader.isReady()) { +diff --git a/qtdeclarative/src/quick/items/qquickflickable.cpp b/qtdeclarative/src/quick/items/qquickflickable.cpp +index ea357d819d..2634b68248 100644 +--- a/qtdeclarative/src/quick/items/qquickflickable.cpp ++++ b/qtdeclarative/src/quick/items/qquickflickable.cpp +@@ -2120,11 +2120,9 @@ void QQuickFlickable::setContentWidth(qreal w) + d->contentItem->setWidth(w); + d->hData.markExtentsDirty(); + // Make sure that we're entirely in view. +- if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->hData.dragging) { +- d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging; ++ if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupX(); +- d->hData.contentPositionChangedExternallyDuringDrag = false; + } else if (!d->pressed && d->hData.fixingUp) { + d->fixupMode = QQuickFlickablePrivate::ExtentChanged; + d->fixupX(); +@@ -2151,11 +2149,9 @@ void QQuickFlickable::setContentHeight(qreal h) + d->contentItem->setHeight(h); + d->vData.markExtentsDirty(); + // Make sure that we're entirely in view. +- if ((!d->pressed && !d->hData.moving && !d->vData.moving) || d->vData.dragging) { +- d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging; ++ if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupY(); +- d->vData.contentPositionChangedExternallyDuringDrag = false; + } else if (!d->pressed && d->vData.fixingUp) { + d->fixupMode = QQuickFlickablePrivate::ExtentChanged; + d->fixupY(); +diff --git a/qtdeclarative/src/quick/items/qquickflickable_p_p.h b/qtdeclarative/src/quick/items/qquickflickable_p_p.h +index d5d838eaea..aef15e150a 100644 +--- a/qtdeclarative/src/quick/items/qquickflickable_p_p.h ++++ b/qtdeclarative/src/quick/items/qquickflickable_p_p.h +@@ -120,6 +120,7 @@ public: + dragStartOffset = 0; + fixingUp = false; + inOvershoot = false; ++ contentPositionChangedExternallyDuringDrag = false; + } + + void markExtentsDirty() { +diff --git a/qtdeclarative/src/quick/items/qquickitem.cpp b/qtdeclarative/src/quick/items/qquickitem.cpp +index 33da9762d3..9e8b289376 100644 +--- a/qtdeclarative/src/quick/items/qquickitem.cpp ++++ b/qtdeclarative/src/quick/items/qquickitem.cpp +@@ -59,6 +59,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -2326,6 +2327,7 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent) + QQuickItem::~QQuickItem() + { + Q_D(QQuickItem); ++ d->inDestructor = true; + + if (d->windowRefCount > 1) + d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow(). +@@ -2398,7 +2400,7 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item) + return true; + + #if QT_CONFIG(accessibility) +- QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole(); ++ QAccessible::Role role = QQuickItemPrivate::get(item)->effectiveAccessibleRole(); + if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) { + return true; + } else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) { +@@ -2526,6 +2528,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo + QQuickItem *current = item; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem; ++ QDuplicateTracker cycleDetector; + do { + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from; +@@ -2592,7 +2595,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo + // traversed all of the chain (by compare the [current] item with [startItem]) + // Since the [startItem] might be promoted to its parent if it is invisible, + // we still have to check [current] item with original start item +- if ((current == startItem || current == originalStartItem) && from == firstFromItem) { ++ // We might also run into a cycle before we reach firstFromItem again ++ // but note that we have to ignore current if we are meant to skip it ++ if (((current == startItem || current == originalStartItem) && from == firstFromItem) || ++ (!skip && cycleDetector.hasSeen(current))) { + // wrapped around, avoid endless loops + if (item == contentItem) { + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem"; +@@ -2689,9 +2695,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) + + const bool wasVisible = isVisible(); + op->removeChild(this); +- if (wasVisible) { ++ if (wasVisible && !op->inDestructor) + emit oldParentItem->visibleChildrenChanged(); +- } + } else if (d->window) { + QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this); + } +@@ -2768,8 +2773,9 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) + + d->itemChange(ItemParentHasChanged, d->parentItem); + +- emit parentChanged(d->parentItem); +- if (isVisible() && d->parentItem) ++ if (!d->inDestructor) ++ emit parentChanged(d->parentItem); ++ if (isVisible() && d->parentItem && !QQuickItemPrivate::get(d->parentItem)->inDestructor) + emit d->parentItem->visibleChildrenChanged(); + } + +@@ -2965,7 +2971,8 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) + + itemChange(QQuickItem::ItemChildRemovedChange, child); + +- emit q->childrenChanged(); ++ if (!inDestructor) ++ emit q->childrenChanged(); + } + + void QQuickItemPrivate::refWindow(QQuickWindow *c) +@@ -3194,6 +3201,7 @@ QQuickItemPrivate::QQuickItemPrivate() + , touchEnabled(false) + #endif + , hasCursorHandler(false) ++ , inDestructor(false) + , dirtyAttributes(0) + , nextDirtyItem(nullptr) + , prevDirtyItem(nullptr) +@@ -5120,6 +5128,13 @@ void QQuickItem::componentComplete() + d->addToDirtyList(); + QQuickWindowPrivate::get(d->window)->dirtyItem(this); + } ++ ++#if QT_CONFIG(accessibility) ++ if (d->isAccessible && d->effectiveVisible) { ++ QAccessibleEvent ev(this, QAccessible::ObjectShow); ++ QAccessible::updateAccessibility(&ev); ++ } ++#endif + } + + QQuickStateGroup *QQuickItemPrivate::_states() +@@ -6106,9 +6121,11 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) + QAccessible::updateAccessibility(&ev); + } + #endif +- emit q->visibleChanged(); +- if (childVisibilityChanged) +- emit q->visibleChildrenChanged(); ++ if (!inDestructor) { ++ emit q->visibleChanged(); ++ if (childVisibilityChanged) ++ emit q->visibleChildrenChanged(); ++ } + + return true; // effective visibility DID change + } +@@ -6157,6 +6174,15 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec + } + + itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable); ++#if QT_CONFIG(accessibility) ++ if (isAccessible) { ++ QAccessible::State changedState; ++ changedState.disabled = true; ++ changedState.focusable = true; ++ QAccessibleStateChangeEvent ev(q, changedState); ++ QAccessible::updateAccessibility(&ev); ++ } ++#endif + emit q->enabledChanged(); + } + +@@ -8974,13 +9000,20 @@ QQuickItemPrivate::ExtraData::ExtraData() + + + #if QT_CONFIG(accessibility) +-QAccessible::Role QQuickItemPrivate::accessibleRole() const ++QAccessible::Role QQuickItemPrivate::effectiveAccessibleRole() const + { + Q_Q(const QQuickItem); +- QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, false)); +- if (accessibleAttached) +- return accessibleAttached->role(); ++ auto *attached = qmlAttachedPropertiesObject(q, false); ++ auto role = QAccessible::NoRole; ++ if (auto *accessibleAttached = qobject_cast(attached)) ++ role = accessibleAttached->role(); ++ if (role == QAccessible::NoRole) ++ role = accessibleRole(); ++ return role; ++} + ++QAccessible::Role QQuickItemPrivate::accessibleRole() const ++{ + return QAccessible::NoRole; + } + #endif +diff --git a/qtdeclarative/src/quick/items/qquickitem_p.h b/qtdeclarative/src/quick/items/qquickitem_p.h +index 841d91bb40..6f329bd119 100644 +--- a/qtdeclarative/src/quick/items/qquickitem_p.h ++++ b/qtdeclarative/src/quick/items/qquickitem_p.h +@@ -472,6 +472,7 @@ public: + bool replayingPressEvent:1; + bool touchEnabled:1; + bool hasCursorHandler:1; ++ quint32 inDestructor:1; // has entered ~QQuickItem + + enum DirtyType { + TransformOrigin = 0x00000001, +@@ -574,7 +575,10 @@ public: + virtual void implicitHeightChanged(); + + #if QT_CONFIG(accessibility) ++ QAccessible::Role effectiveAccessibleRole() const; ++private: + virtual QAccessible::Role accessibleRole() const; ++public: + #endif + + void setImplicitAntialiasing(bool antialiasing); +diff --git a/qtdeclarative/src/quick/items/qquickmousearea_p_p.h b/qtdeclarative/src/quick/items/qquickmousearea_p_p.h +index fba383e268..0d63618622 100644 +--- a/qtdeclarative/src/quick/items/qquickmousearea_p_p.h ++++ b/qtdeclarative/src/quick/items/qquickmousearea_p_p.h +@@ -61,7 +61,6 @@ QT_BEGIN_NAMESPACE + + class QQuickMouseEvent; + class QQuickMouseArea; +-class QQuickPointerMask; + class QQuickMouseAreaPrivate : public QQuickItemPrivate + { + Q_DECLARE_PUBLIC(QQuickMouseArea) +@@ -100,7 +99,6 @@ public: + #if QT_CONFIG(quick_draganddrop) + QQuickDrag *drag; + #endif +- QPointer mask; + QPointF startScene; + QPointF targetStartPos; + QPointF lastPos; +diff --git a/qtdeclarative/src/quick/util/qquickstategroup.cpp b/qtdeclarative/src/quick/util/qquickstategroup.cpp +index 7cb3138618..f732b1eb4a 100644 +--- a/qtdeclarative/src/quick/util/qquickstategroup.cpp ++++ b/qtdeclarative/src/quick/util/qquickstategroup.cpp +@@ -381,8 +381,14 @@ bool QQuickStateGroupPrivate::updateAutoState() + const auto potentialWhenBinding = QQmlPropertyPrivate::binding(whenProp); + // if there is a binding, the value in when might not be up-to-date at this point + // so we manually reevaluate the binding +- if (auto abstractBinding = dynamic_cast(potentialWhenBinding)) +- whenValue = abstractBinding->evaluate().toBool(); ++ if (auto abstractBinding = dynamic_cast(potentialWhenBinding)) { ++ QVariant evalResult = abstractBinding->evaluate(); ++ if (evalResult.userType() == qMetaTypeId()) ++ whenValue = evalResult.value().toBool(); ++ else ++ whenValue = evalResult.toBool(); ++ } ++ + if (whenValue) { + if (stateChangeDebug()) + qWarning() << "Setting auto state due to expression"; +diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp +new file mode 100644 +index 0000000000..8a1c901880 +--- /dev/null ++++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget.cpp +@@ -0,0 +1,110 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2021 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the QtQuick module of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU Lesser General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU Lesser ++** General Public License version 3 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL3 included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 3 requirements ++** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 2.0 or (at your option) the GNU General ++** Public license version 3 or any later version approved by the KDE Free ++** Qt Foundation. The licenses are as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-2.0.html and ++** https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "qaccessiblequickwidget_p.h" ++ ++#include "qquickwidget_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++QAccessibleQuickWidget::QAccessibleQuickWidget(QQuickWidget* widget) ++: QAccessibleWidget(widget) ++, m_accessibleWindow(QQuickWidgetPrivate::get(widget)->offscreenWindow) ++{ ++ // NOTE: m_accessibleWindow is a QAccessibleQuickWindow, and not a ++ // QAccessibleQuickWidgetOffscreenWindow (defined below). This means ++ // it will return the Quick item child interfaces, which is what's needed here ++ // (unlike QAccessibleQuickWidgetOffscreenWindow, which will report 0 children). ++} ++ ++QAccessibleInterface *QAccessibleQuickWidget::child(int index) const ++{ ++ return m_accessibleWindow.child(index); ++} ++ ++int QAccessibleQuickWidget::childCount() const ++{ ++ return m_accessibleWindow.childCount(); ++} ++ ++int QAccessibleQuickWidget::indexOfChild(const QAccessibleInterface *iface) const ++{ ++ return m_accessibleWindow.indexOfChild(iface); ++} ++ ++QAccessibleInterface *QAccessibleQuickWidget::childAt(int x, int y) const ++{ ++ return m_accessibleWindow.childAt(x, y); ++} ++ ++QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window) ++:QAccessibleQuickWindow(window) ++{ ++ ++} ++ ++QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::child(int index) const ++{ ++ Q_UNUSED(index); ++ return nullptr; ++} ++ ++int QAccessibleQuickWidgetOffscreenWindow::childCount() const ++{ ++ return 0; ++} ++ ++int QAccessibleQuickWidgetOffscreenWindow::indexOfChild(const QAccessibleInterface *iface) const ++{ ++ Q_UNUSED(iface); ++ return -1; ++} ++ ++QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow::childAt(int x, int y) const ++{ ++ Q_UNUSED(x); ++ Q_UNUSED(y); ++ return nullptr; ++} ++ ++#endif // accessibility ++ ++QT_END_NAMESPACE +diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h +new file mode 100644 +index 0000000000..7c2ab930e0 +--- /dev/null ++++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidget_p.h +@@ -0,0 +1,95 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2021 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the QtQuick module of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU Lesser General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU Lesser ++** General Public License version 3 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL3 included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 3 requirements ++** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 2.0 or (at your option) the GNU General ++** Public license version 3 or any later version approved by the KDE Free ++** Qt Foundation. The licenses are as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-2.0.html and ++** https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef QACCESSIBLEQUICKWIDGET_H ++#define QACCESSIBLEQUICKWIDGET_H ++ ++// ++// W A R N I N G ++// ------------- ++// ++// This file is not part of the Qt API. It exists purely as an ++// implementation detail. This header file may change from version to ++// version without notice, or even be removed. ++// ++// We mean it. ++// ++ ++#include "qquickwidget.h" ++#include ++ ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++// These classes implement the QQuickWiget accessibility switcharoo, ++// where the child items of the QQuickWidgetOffscreenWindow are reported ++// as child accessible interfaces of the QAccessibleQuickWidget. ++class QAccessibleQuickWidget: public QAccessibleWidget ++{ ++public: ++ QAccessibleQuickWidget(QQuickWidget* widget); ++ ++ QAccessibleInterface *child(int index) const override; ++ int childCount() const override; ++ int indexOfChild(const QAccessibleInterface *iface) const override; ++ QAccessibleInterface *childAt(int x, int y) const override; ++ ++private: ++ QAccessibleQuickWindow m_accessibleWindow; ++ Q_DISABLE_COPY(QAccessibleQuickWidget) ++}; ++ ++class QAccessibleQuickWidgetOffscreenWindow: public QAccessibleQuickWindow ++{ ++public: ++ QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window); ++ QAccessibleInterface *child(int index) const override; ++ int childCount() const override; ++ int indexOfChild(const QAccessibleInterface *iface) const override; ++ QAccessibleInterface *childAt(int x, int y) const override; ++}; ++ ++#endif // accessibility ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp +new file mode 100644 +index 0000000000..7ba88a1769 +--- /dev/null ++++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory.cpp +@@ -0,0 +1,60 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2021 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the QtQuick module of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU Lesser General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU Lesser ++** General Public License version 3 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL3 included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 3 requirements ++** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 2.0 or (at your option) the GNU General ++** Public license version 3 or any later version approved by the KDE Free ++** Qt Foundation. The licenses are as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-2.0.html and ++** https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "qaccessiblequickwidgetfactory_p.h" ++#include "qaccessiblequickwidget_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object) ++{ ++ if (classname == QLatin1String("QQuickWidget")) { ++ return new QAccessibleQuickWidget(qobject_cast(object)); ++ } else if (classname == QLatin1String("QQuickWidgetOffscreenWindow")) { ++ return new QAccessibleQuickWidgetOffscreenWindow(qobject_cast(object)); ++ } ++ return 0; ++} ++ ++#endif // accessibility ++ ++QT_END_NAMESPACE ++ +diff --git a/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h +new file mode 100644 +index 0000000000..8c63b09f81 +--- /dev/null ++++ b/qtdeclarative/src/quickwidgets/qaccessiblequickwidgetfactory_p.h +@@ -0,0 +1,66 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2021 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the QtQuick module of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** GNU Lesser General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU Lesser ++** General Public License version 3 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL3 included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 3 requirements ++** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 2.0 or (at your option) the GNU General ++** Public license version 3 or any later version approved by the KDE Free ++** Qt Foundation. The licenses are as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ++** included in the packaging of this file. Please review the following ++** information to ensure the GNU General Public License requirements will ++** be met: https://www.gnu.org/licenses/gpl-2.0.html and ++** https://www.gnu.org/licenses/gpl-3.0.html. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include ++ ++#ifndef QACCESSIBLEQUICKWIDGETFACTORY_H ++#define QACCESSIBLEQUICKWIDGETFACTORY_H ++ ++// ++// W A R N I N G ++// ------------- ++// ++// This file is not part of the Qt API. It exists purely as an ++// implementation detail. This header file may change from version to ++// version without notice, or even be removed. ++// ++// We mean it. ++// ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object); ++ ++#endif ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/qtdeclarative/src/quickwidgets/qquickwidget.cpp b/qtdeclarative/src/quickwidgets/qquickwidget.cpp +index cf021d9a7c..b0117683f7 100644 +--- a/qtdeclarative/src/quickwidgets/qquickwidget.cpp ++++ b/qtdeclarative/src/quickwidgets/qquickwidget.cpp +@@ -39,6 +39,7 @@ + + #include "qquickwidget.h" + #include "qquickwidget_p.h" ++#include "qaccessiblequickwidgetfactory_p.h" + + #include "private/qquickwindow_p.h" + #include "private/qquickitem_p.h" +@@ -75,9 +76,16 @@ + + QT_BEGIN_NAMESPACE + ++QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) ++:QQuickWindow(dd, control) ++{ ++ setTitle(QString::fromLatin1("Offscreen")); ++ setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); ++} ++ + // override setVisble to prevent accidental offscreen window being created + // by base class. +-class QQuickOffcreenWindowPrivate: public QQuickWindowPrivate { ++class QQuickWidgetOffscreenWindowPrivate: public QQuickWindowPrivate { + public: + void setVisible(bool visible) override { + Q_Q(QWindow); +@@ -105,9 +113,8 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) + Q_Q(QQuickWidget); + + renderControl = new QQuickWidgetRenderControl(q); +- offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl); +- offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); +- offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); ++ offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl); ++ offscreenWindow->setScreen(q->screen()); + // Do not call create() on offscreenWindow. + + // Check if the Software Adaptation is being used +@@ -138,6 +145,10 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) + QWidget::connect(offscreenWindow, &QQuickWindow::focusObjectChanged, q, &QQuickWidget::propagateFocusObjectChanged); + QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate())); + QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); ++ ++#if QT_CONFIG(accessibility) ++ QAccessible::installFactory(&qAccessibleQuickWidgetFactory); ++#endif + } + + void QQuickWidgetPrivate::ensureEngine() const +@@ -901,9 +912,7 @@ void QQuickWidgetPrivate::createContext() + + context = new QOpenGLContext; + context->setFormat(offscreenWindow->requestedFormat()); +- const QWindow *win = q->window()->windowHandle(); +- if (win && win->screen()) +- context->setScreen(win->screen()); ++ context->setScreen(q->screen()); + QOpenGLContext *shareContext = qt_gl_global_share_context(); + if (!shareContext) + shareContext = QWidgetPrivate::get(q->window())->shareContext(); +@@ -1527,19 +1536,16 @@ bool QQuickWidget::event(QEvent *e) + d->handleWindowChange(); + break; + +- case QEvent::ScreenChangeInternal: +- if (QWindow *window = this->window()->windowHandle()) { +- QScreen *newScreen = window->screen(); +- +- if (d->offscreenWindow) +- d->offscreenWindow->setScreen(newScreen); +- if (d->offscreenSurface) +- d->offscreenSurface->setScreen(newScreen); ++ case QEvent::ScreenChangeInternal: { ++ QScreen *newScreen = screen(); ++ if (d->offscreenWindow) ++ d->offscreenWindow->setScreen(newScreen); ++ if (d->offscreenSurface) ++ d->offscreenSurface->setScreen(newScreen); + #if QT_CONFIG(opengl) +- if (d->context) +- d->context->setScreen(newScreen); ++ if (d->context) ++ d->context->setScreen(newScreen); + #endif +- } + + if (d->useSoftwareRenderer + #if QT_CONFIG(opengl) +@@ -1552,7 +1558,7 @@ bool QQuickWidget::event(QEvent *e) + d->render(true); + } + break; +- ++ } + case QEvent::Show: + case QEvent::Move: + d->updatePosition(); +diff --git a/qtdeclarative/src/quickwidgets/qquickwidget_p.h b/qtdeclarative/src/quickwidgets/qquickwidget_p.h +index 881f7f9220..1a946bcc71 100644 +--- a/qtdeclarative/src/quickwidgets/qquickwidget_p.h ++++ b/qtdeclarative/src/quickwidgets/qquickwidget_p.h +@@ -148,6 +148,14 @@ public: + bool forceFullUpdate; + }; + ++class QQuickWidgetOffscreenWindow: public QQuickWindow ++{ ++ Q_OBJECT ++ ++public: ++ QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control); ++}; ++ + QT_END_NAMESPACE + + #endif // QQuickWidget_P_H +diff --git a/qtdeclarative/src/quickwidgets/quickwidgets.pro b/qtdeclarative/src/quickwidgets/quickwidgets.pro +index 2438e577ae..85d156b8a3 100644 +--- a/qtdeclarative/src/quickwidgets/quickwidgets.pro ++++ b/qtdeclarative/src/quickwidgets/quickwidgets.pro +@@ -7,9 +7,13 @@ DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FO + HEADERS += \ + qquickwidget.h \ + qquickwidget_p.h \ +- qtquickwidgetsglobal.h ++ qtquickwidgetsglobal.h \ ++ qaccessiblequickwidget_p.h \ ++ qaccessiblequickwidgetfactory_p.h + + SOURCES += \ +- qquickwidget.cpp ++ qquickwidget.cpp \ ++ qaccessiblequickwidget.cpp \ ++ qaccessiblequickwidgetfactory.cpp + + load(qt_module) +diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml +new file mode 100644 +index 0000000000..23874970e7 +--- /dev/null ++++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/deleteRace.qml +@@ -0,0 +1,50 @@ ++import QtQuick 2.15 ++import QtQml.Models 2.15 ++ ++Item { ++ DelegateModel { ++ id: delegateModel ++ model: ListModel { ++ id: sourceModel ++ ++ ListElement { title: "foo" } ++ ListElement { title: "bar" } ++ ++ function clear() { ++ if (count > 0) ++ remove(0, count); ++ } ++ } ++ ++ groups: [ ++ DelegateModelGroup { name: "selectedItems" } ++ ] ++ ++ delegate: Text { ++ height: DelegateModel.inSelectedItems ? implicitHeight * 2 : implicitHeight ++ Component.onCompleted: { ++ if (index === 0) ++ DelegateModel.inSelectedItems = true; ++ } ++ } ++ ++ Component.onCompleted: { ++ items.create(0) ++ items.create(1) ++ } ++ } ++ ++ ListView { ++ anchors.fill: parent ++ model: delegateModel ++ } ++ ++ Timer { ++ running: true ++ interval: 10 ++ onTriggered: sourceModel.clear() ++ } ++ ++ property int count: delegateModel.items.count ++} ++ +diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml +new file mode 100644 +index 0000000000..206133bb39 +--- /dev/null ++++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/data/redrawUponColumnChange.qml +@@ -0,0 +1,11 @@ ++import QtQuick 2.8 ++ ++ListView { ++ id: root ++ width: 200 ++ height: 200 ++ ++ delegate: Text { ++ text: display ++ } ++} +diff --git a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +index 35f1e2c94d..f473cff75f 100644 +--- a/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp ++++ b/qtdeclarative/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +@@ -27,6 +27,8 @@ + ****************************************************************************/ + + #include ++#include ++#include + #include + #include + #include +@@ -47,6 +49,8 @@ private slots: + void filterOnGroup_removeWhenCompleted(); + void qtbug_86017(); + void contextAccessedByHandler(); ++ void redrawUponColumnChange(); ++ void deleteRace(); + }; + + class AbstractItemModel : public QAbstractItemModel +@@ -186,6 +190,41 @@ void tst_QQmlDelegateModel::contextAccessedByHandler() + QVERIFY(root->property("works").toBool()); + } + ++void tst_QQmlDelegateModel::redrawUponColumnChange() ++{ ++ QStandardItemModel m1; ++ m1.appendRow({ ++ new QStandardItem("Banana"), ++ new QStandardItem("Coconut"), ++ }); ++ ++ QQuickView view(testFileUrl("redrawUponColumnChange.qml")); ++ QCOMPARE(view.status(), QQuickView::Ready); ++ view.show(); ++ QQuickItem *root = view.rootObject(); ++ root->setProperty("model", QVariant::fromValue(&m1)); ++ ++ QObject *item = root->property("currentItem").value(); ++ QVERIFY(item); ++ QCOMPARE(item->property("text").toString(), "Banana"); ++ ++ QVERIFY(root); ++ m1.removeColumn(0); ++ ++ QCOMPARE(item->property("text").toString(), "Coconut"); ++} ++ ++void tst_QQmlDelegateModel::deleteRace() ++{ ++ QQmlEngine engine; ++ QQmlComponent c(&engine, testFileUrl("deleteRace.qml")); ++ QVERIFY2(c.isReady(), qPrintable(c.errorString())); ++ QScopedPointer o(c.create()); ++ QVERIFY(!o.isNull()); ++ QTRY_COMPARE(o->property("count").toInt(), 2); ++ QTRY_COMPARE(o->property("count").toInt(), 0); ++} ++ + QTEST_MAIN(tst_QQmlDelegateModel) + + #include "tst_qqmldelegatemodel.moc" +diff --git a/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +index 9c865b3f73..1f788f7a7f 100644 +--- a/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp ++++ b/qtdeclarative/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +@@ -154,6 +154,11 @@ void tst_QQmlImport::importPathOrder() + engine.addImportPath(QT_QMLTEST_DATADIR); + expectedImportPaths.prepend(QT_QMLTEST_DATADIR); + QCOMPARE(expectedImportPaths, engine.importPathList()); ++ ++ // Add qml2Imports again to make it the first of the list ++ engine.addImportPath(qml2Imports); ++ expectedImportPaths.move(expectedImportPaths.indexOf(qml2Imports), 0); ++ QCOMPARE(expectedImportPaths, engine.importPathList()); + } + + Q_DECLARE_METATYPE(QQmlImports::ImportVersion) +diff --git a/qtdeclarative/tests/auto/qml/qqmllanguage/data/Broken.qml b/qtdeclarative/tests/auto/qml/qqmllanguage/data/Broken.qml +new file mode 100644 +index 0000000000..e24d9112a8 +--- /dev/null ++++ b/qtdeclarative/tests/auto/qml/qqmllanguage/data/Broken.qml +@@ -0,0 +1,5 @@ ++import QtQml 2.15 ++ ++QtObject { ++ notThere: 5 ++} +diff --git a/qtdeclarative/tests/auto/qml/qqmllanguage/data/asBroken.qml b/qtdeclarative/tests/auto/qml/qqmllanguage/data/asBroken.qml +new file mode 100644 +index 0000000000..bd88d14c76 +--- /dev/null ++++ b/qtdeclarative/tests/auto/qml/qqmllanguage/data/asBroken.qml +@@ -0,0 +1,6 @@ ++import QtQml 2.15 ++ ++QtObject { ++ id: self ++ property var selfAsBroken: self as Broken ++} +diff --git a/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +index ac6634290a..d16d117d65 100644 +--- a/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp ++++ b/qtdeclarative/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +@@ -337,6 +337,7 @@ private slots: + void bareInlineComponent(); + + void hangOnWarning(); ++ void objectAsBroken(); + + void ambiguousContainingType(); + void staticConstexprMembers(); +@@ -5951,6 +5952,21 @@ void tst_qqmllanguage::badGroupedProperty() + .arg(url.toString())); + } + ++void tst_qqmllanguage::objectAsBroken() ++{ ++ QQmlEngine engine; ++ QQmlComponent c(&engine, testFileUrl("asBroken.qml")); ++ QVERIFY2(c.isReady(), qPrintable(c.errorString())); ++ QScopedPointer o(c.create()); ++ QVERIFY(!o.isNull()); ++ QVariant selfAsBroken = o->property("selfAsBroken"); ++ QVERIFY(selfAsBroken.isValid()); ++ // QCOMPARE(selfAsBroken.metaType(), QMetaType::fromType()); ++ ++ QQmlComponent b(&engine, testFileUrl("Broken.qml")); ++ QVERIFY(b.isError()); ++} ++ + QTEST_MAIN(tst_qqmllanguage) + + #include "tst_qqmllanguage.moc" +diff --git a/qtdeclarative/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/qtdeclarative/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +index d092cd0170..62f7c67dd4 100644 +--- a/qtdeclarative/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp ++++ b/qtdeclarative/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +@@ -2642,7 +2642,12 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966 + } else if (newExtent >= 0) { + // ...or reduce the content size be be less than current (contentX, contentY) position + // This forces the content item to move. +- expectedContentPos = moveDelta; ++ // contentY: 150 ++ // 320 - 150 = 170 pixels down to bottom ++ // Now reduce contentHeight to 200 ++ // since we are at the bottom, and the flickable is 100 pixels tall, contentY must land ++ // at newExtent - 100. ++ + if (isHorizontal) { + flickable->setContentWidth(newExtent); + } else { +@@ -2652,6 +2657,7 @@ void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966 + // We therefore cannot scroll/flick it further down. Drag it up towards the top instead + // (by moving mouse down). + pos += moveDelta; ++ expectedContentPos = unitDelta * (newExtent - (isHorizontal ? flickable->width() : flickable->height())); + } + + QTest::mouseMove(window.data(), pos); +diff --git a/qtdeclarative/tests/auto/quick/qquickgridview/data/qtbug86255.qml b/qtdeclarative/tests/auto/quick/qquickgridview/data/qtbug86255.qml +new file mode 100644 +index 0000000000..20688b1967 +--- /dev/null ++++ b/qtdeclarative/tests/auto/quick/qquickgridview/data/qtbug86255.qml +@@ -0,0 +1,55 @@ ++import QtQuick 2.15 ++ ++Item { ++ width: 240 ++ height: 320 ++ ++ GridView { ++ id: grid ++ objectName: "view" ++ anchors.fill: parent ++ cellWidth: 64 ++ cellHeight: 64 ++ model: ListModel { ++ id: listModel ++ ++ Component.onCompleted: reload() ++ ++ function reload() { ++ clear(); ++ for (let i = 0; i < 1000; i++) { ++ let magic = Math.random(); ++ append( { magic } ); ++ } ++ } ++ } ++ clip: true ++ delegate: Item { ++ id: d ++ property string val: magic ++ Loader { ++ property alias value: d.val ++ asynchronous: true ++ sourceComponent: cmp ++ } ++ } ++ } ++ ++ Timer { ++ running: true ++ interval: 1000 ++ onTriggered: listModel.reload() ++ } ++ Timer { ++ running: true ++ interval: 500 ++ onTriggered: grid.flick(0, -4000) ++ } ++ ++ Component { ++ id: cmp ++ Text { ++ text: value ++ } ++ } ++} +diff --git a/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +index 94ec4f44d5..7d0d9fa7a7 100644 +--- a/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp ++++ b/qtdeclarative/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +@@ -213,6 +213,7 @@ private slots: + void QTBUG_45640(); + void QTBUG_49218(); + void QTBUG_48870_fastModelUpdates(); ++ void QTBUG_86255(); + + void keyNavigationEnabled(); + void resizeDynamicCellWidthRtL(); +@@ -6814,6 +6815,18 @@ void tst_QQuickGridView::resizeDynamicCellWidthRtL() + QTRY_COMPARE(gridview->contentX(), 0.f); + } + ++void tst_QQuickGridView::QTBUG_86255() ++{ ++ QScopedPointer window(createView()); ++ window->setSource(testFileUrl("qtbug86255.qml")); ++ window->show(); ++ QVERIFY(QTest::qWaitForWindowExposed(window.data())); ++ QQuickGridView *view = findItem(window->rootObject(), "view"); ++ QVERIFY(view != nullptr); ++ QTRY_COMPARE(view->isFlicking(), true); ++ QTRY_COMPARE(view->isFlicking(), false); ++} ++ + void tst_QQuickGridView::releaseItems() + { + QScopedPointer view(createView()); +diff --git a/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml b/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml +new file mode 100644 +index 0000000000..889e480f3b +--- /dev/null ++++ b/qtdeclarative/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml +@@ -0,0 +1,13 @@ ++import QtQuick 2.6 ++ ++Item { ++ visible: true ++ Item { ++ visible: false ++ Item { ++ objectName: "hiddenChild" ++ activeFocusOnTab: true ++ focus: true ++ } ++ } ++} +diff --git a/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +index c8f251dbe1..c8ef36ee68 100644 +--- a/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp ++++ b/qtdeclarative/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +@@ -67,6 +67,7 @@ private slots: + void activeFocusOnTab10(); + void activeFocusOnTab_infiniteLoop_data(); + void activeFocusOnTab_infiniteLoop(); ++ void activeFocusOnTab_infiniteLoopControls(); + + void nextItemInFocusChain(); + void nextItemInFocusChain2(); +@@ -1057,6 +1058,17 @@ void tst_QQuickItem::activeFocusOnTab_infiniteLoop() + QCOMPARE(item, window->rootObject()); + } + ++ ++void tst_QQuickItem::activeFocusOnTab_infiniteLoopControls() ++{ ++ auto source = testFileUrl("activeFocusOnTab_infiniteLoop3.qml"); ++ QScopedPointerwindow(new QQuickView()); ++ window->setSource(source); ++ window->show(); ++ QVERIFY(window->errors().isEmpty()); ++ QTest::keyClick(window.get(), Qt::Key_Tab); // should not hang ++} ++ + void tst_QQuickItem::nextItemInFocusChain() + { + if (!qt_tab_all_widgets()) +diff --git a/qtdeclarative/tests/auto/quick/qquickstates/data/jsValueWhen.qml b/qtdeclarative/tests/auto/quick/qquickstates/data/jsValueWhen.qml +new file mode 100644 +index 0000000000..6d5eb1600c +--- /dev/null ++++ b/qtdeclarative/tests/auto/quick/qquickstates/data/jsValueWhen.qml +@@ -0,0 +1,18 @@ ++import QtQuick 2.15 ++ ++Item { ++ id: root ++ property var prop: null ++ property bool works: false ++ states: [ ++ State { ++ name: "mystate" ++ when: root.prop ++ PropertyChanges { ++ target: root ++ works: "works" ++ } ++ } ++ ] ++ Component.onCompleted: root.prop = new Object ++} +diff --git a/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp +index aa55b42935..26e86672b0 100644 +--- a/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp ++++ b/qtdeclarative/tests/auto/quick/qquickstates/tst_qquickstates.cpp +@@ -188,6 +188,7 @@ private slots: + void revertListMemoryLeak(); + void duplicateStateName(); + void trivialWhen(); ++ void jsValueWhen(); + void noStateOsciallation(); + void parentChangeCorrectReversal(); + void revertNullObjectBinding(); +@@ -1734,6 +1735,16 @@ void tst_qquickstates::trivialWhen() + QVERIFY(c.create()); + } + ++void tst_qquickstates::jsValueWhen() ++{ ++ QQmlEngine engine; ++ ++ QQmlComponent c(&engine, testFileUrl("jsValueWhen.qml")); ++ QScopedPointer root(c.create()); ++ QVERIFY(root); ++ QVERIFY(root->property("works").toBool()); ++} ++ + void tst_qquickstates::noStateOsciallation() + { + QQmlEngine engine; +diff --git a/qtdeclarative/tests/manual/quickcontrols2/swipedelegate/CloseOnCompletedWorks.qml b/qtdeclarative/tests/manual/quickcontrols2/swipedelegate/CloseOnCompletedWorks.qml +new file mode 100644 +index 0000000000..38dfde41c3 +--- /dev/null ++++ b/qtdeclarative/tests/manual/quickcontrols2/swipedelegate/CloseOnCompletedWorks.qml +@@ -0,0 +1,74 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2022 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:BSD$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** BSD License Usage ++** Alternatively, you may use this file under the terms of the BSD license ++** as follows: ++** ++** "Redistribution and use in source and binary forms, with or without ++** modification, are permitted provided that the following conditions are ++** met: ++** * Redistributions of source code must retain the above copyright ++** notice, this list of conditions and the following disclaimer. ++** * Redistributions in binary form must reproduce the above copyright ++** notice, this list of conditions and the following disclaimer in ++** the documentation and/or other materials provided with the ++** distribution. ++** * Neither the name of The Qt Company Ltd nor the names of its ++** contributors may be used to endorse or promote products derived ++** from this software without specific prior written permission. ++** ++** ++** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++import QtQuick 2 ++import QtQuick.Controls 2 ++ApplicationWindow { ++ visible: true ++ width: 640 ++ height: 480 ++ ++ ListView { ++ anchors.fill: parent ++ model: 2 ++ ++ delegate: SwipeDelegate { ++ text: "Swipe me left (should not crash)" ++ ++ swipe.right: Label { ++ text: "Release (should not crash)" ++ } ++ ++ swipe.onCompleted: { ++ swipe.close() ++ } ++ } ++ } ++} +diff --git a/qtdeclarative/tools/qml/main.cpp b/qtdeclarative/tools/qml/main.cpp +index beeec88f07..2cb7653d65 100644 +--- a/qtdeclarative/tools/qml/main.cpp ++++ b/qtdeclarative/tools/qml/main.cpp +@@ -446,8 +446,8 @@ int main(int argc, char *argv[]) + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments); +- const QCommandLineOption helpOption = parser.addHelpOption(); +- const QCommandLineOption versionOption = parser.addVersionOption(); ++ parser.addHelpOption(); ++ parser.addVersionOption(); + #ifdef QT_GUI_LIB + QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"), + QCoreApplication::translate("main", "Select which application class to use. Default is gui."), +@@ -522,14 +522,7 @@ int main(int argc, char *argv[]) + parser.addPositionalArgument("args", + QCoreApplication::translate("main", "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), "[-- args...]"); + +- if (!parser.parse(QCoreApplication::arguments())) { +- qWarning() << parser.errorText(); +- exit(1); +- } +- if (parser.isSet(versionOption)) +- parser.showVersion(); +- if (parser.isSet(helpOption)) +- parser.showHelp(); ++ parser.process(*app); + if (parser.isSet(listConfOption)) + listConfFiles(); + if (applicationType == QmlApplicationTypeUnknown) { +Submodule qtdoc 39b27247...8a3dfe33: +Submodule qtimageformats 1019058c..142040e8: +diff --git a/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp b/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp +index 5d086c6..3961c16 100644 +--- a/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp ++++ b/qtimageformats/src/plugins/imageformats/tga/qtgafile.cpp +@@ -220,9 +220,18 @@ QImage QTgaFile::readImage() + + int offset = mHeader[IdLength]; // Mostly always zero + +- // Even in TrueColor files a color pallette may be present +- if (mHeader[ColorMapType] == 1) +- offset += littleEndianInt(&mHeader[CMapLength]) * littleEndianInt(&mHeader[CMapDepth]); ++ // Even in TrueColor files a color palette may be present so we have to check it here ++ // even we only support image type 2 (= uncompressed true-color image) ++ if (mHeader[ColorMapType] == 1) { ++ int cmapDepth = mHeader[CMapDepth]; ++ if (cmapDepth == 15) // 15 bit is stored as 16 bit + ignoring the highest bit (no alpha) ++ cmapDepth = 16; ++ if (cmapDepth != 16 && cmapDepth != 24 && cmapDepth != 32) { ++ mErrorMessage = tr("Invalid color map depth (%1)").arg(cmapDepth); ++ return {}; ++ } ++ offset += littleEndianInt(&mHeader[CMapLength]) * cmapDepth / 8; ++ } + + mDevice->seek(HeaderSize + offset); + +diff --git a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp +index ac8956c..79be154 100644 +--- a/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp ++++ b/qtimageformats/src/plugins/imageformats/tiff/qtiffhandler.cpp +@@ -38,11 +38,15 @@ + ****************************************************************************/ + + #include "qtiffhandler_p.h" +-#include + #include + #include + #include + #include ++#include ++#include ++#include ++#include ++ + extern "C" { + #include "tiffio.h" + } +@@ -90,13 +94,33 @@ toff_t qtiffSizeProc(thandle_t fd) + return static_cast(fd)->size(); + } + +-int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) ++int qtiffMapProc(thandle_t fd, void **base, toff_t *size) + { ++ QIODevice *device = static_cast(fd); ++ ++ QFileDevice *file = qobject_cast(device); ++ if (file) { ++ *base = file->map(0, file->size()); ++ if (*base != nullptr) { ++ *size = file->size(); ++ return 1; ++ } ++ } else { ++ QBuffer *buf = qobject_cast(device); ++ if (buf) { ++ *base = const_cast(buf->data().constData()); ++ *size = buf->size(); ++ return 1; ++ } ++ } + return 0; + } + +-void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/) ++void qtiffUnmapProc(thandle_t fd, void *base, toff_t /*size*/) + { ++ QFileDevice *file = qobject_cast(static_cast(fd)); ++ if (file && base) ++ file->unmap(static_cast(base)); + } + + +diff --git a/qtimageformats/src/plugins/imageformats/webp/CMakeLists.txt b/qtimageformats/src/plugins/imageformats/webp/CMakeLists.txt +index 25aa0c9..fbbcc1c 100644 +--- a/qtimageformats/src/plugins/imageformats/webp/CMakeLists.txt ++++ b/qtimageformats/src/plugins/imageformats/webp/CMakeLists.txt +@@ -30,6 +30,7 @@ qt_internal_extend_target(QWebpPlugin CONDITION QT_FEATURE_system_webp + qt_internal_extend_target(QWebpPlugin CONDITION NOT QT_FEATURE_system_webp + SOURCES + ../../../3rdparty/libwebp/sharpyuv/sharpyuv.c ++ ../../../3rdparty/libwebp/sharpyuv/sharpyuv_cpu.c + ../../../3rdparty/libwebp/sharpyuv/sharpyuv_csp.c + ../../../3rdparty/libwebp/sharpyuv/sharpyuv_dsp.c + ../../../3rdparty/libwebp/sharpyuv/sharpyuv_gamma.c +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 b6d96559..5b27b892: +Submodule src/3rdparty/mapbox-gl-native d3101bbc...4c88f2c0 (commits not present) +diff --git a/qtlocation/src/location/configure.json b/qtlocation/src/location/configure.json +index 6d01a9a3..d1e623a1 100644 +--- a/qtlocation/src/location/configure.json ++++ b/qtlocation/src/location/configure.json +@@ -9,7 +9,7 @@ + "label": "Qt.labs.location experimental QML plugin", + "purpose": "Provides experimental QtLocation QML types", + "section": "Location", +- "condition": "config.opengl", ++ "condition": "features.opengl", + "output": [ "privateFeature" ] + }, + "geoservices_osm": { +diff --git a/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp b/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp +index a978573d..11e1466f 100644 +--- a/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp ++++ b/qtlocation/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp +@@ -158,7 +158,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind + if (!root) + return; + +- if (m_mapObjectsRootNode && m_mapObjectsRootNode->parent()) ++ if (m_mapObjectsRootNode && !m_mapObjectsRootNode->parent()) + root->appendChildNode(m_mapObjectsRootNode.get()); + + if (!m_mapObjectsRootNode) { +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp +index 68b2429e..deef29f9 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp +@@ -66,11 +66,8 @@ QGeoCodingManagerEngineNokia::QGeoCodingManagerEngineNokia( + Q_ASSERT(networkManager); + m_networkManager->setParent(this); + +- if (parameters.contains(QStringLiteral("here.token"))) +- m_token = parameters.value(QStringLiteral("here.token")).toString(); +- +- if (parameters.contains(QStringLiteral("here.app_id"))) +- m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString(); ++ if (parameters.contains(QStringLiteral("here.apiKey"))) ++ m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); + + if (error) + *error = QGeoServiceProvider::NoError; +@@ -85,12 +82,9 @@ QString QGeoCodingManagerEngineNokia::getAuthenticationString() const + { + QString authenticationString; + +- if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { +- authenticationString += "?app_code="; +- authenticationString += m_token; +- +- authenticationString += "&app_id="; +- authenticationString += m_applicationId; ++ if (!m_apiKey.isEmpty()) { ++ authenticationString += "?apiKey="; ++ authenticationString += m_apiKey; + } + + return authenticationString; +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h +index 9e1564aa..a7cfd06a 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h +@@ -82,8 +82,7 @@ private: + QGeoNetworkAccessManager *m_networkManager; + QGeoUriProvider *m_uriProvider; + QGeoUriProvider *m_reverseGeocodingUriProvider; +- QString m_token; +- QString m_applicationId; ++ QString m_apiKey; + }; + + QT_END_NAMESPACE +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp +index 576ecd43..f49e0905 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeoerror_messages.cpp +@@ -39,7 +39,7 @@ + QT_BEGIN_NAMESPACE + + const char NOKIA_PLUGIN_CONTEXT_NAME[] = "QtLocationQML"; +-const char MISSED_CREDENTIALS[] = QT_TRANSLATE_NOOP("QtLocationQML", "Qt Location requires app_id and token parameters.\nPlease register at https://developer.here.com/ to get your personal application credentials."); ++const char MISSED_CREDENTIALS[] = QT_TRANSLATE_NOOP("QtLocationQML", "Qt Location requires apiKey parameter.\nPlease register at https://developer.here.com/ to get your personal application credentials."); + const char SAVING_PLACE_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Saving places is not supported."); + const char REMOVING_PLACE_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Removing places is not supported."); + const char SAVING_CATEGORY_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Saving categories is not supported."); +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp +index 73b998b1..a938096b 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp +@@ -60,8 +60,7 @@ QGeoRoutingManagerEngineNokia::QGeoRoutingManagerEngineNokia( + Q_ASSERT(networkManager); + m_networkManager->setParent(this); + +- m_appId = parameters.value(QStringLiteral("here.app_id")).toString(); +- m_token = parameters.value(QStringLiteral("here.token")).toString(); ++ m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); + + QGeoRouteRequest::FeatureTypes featureTypes; + featureTypes |= QGeoRouteRequest::TollFeature; +@@ -219,18 +218,16 @@ QStringList QGeoRoutingManagerEngineNokia::calculateRouteRequestString(const QGe + return QStringList(); + QStringList requests; + +- QString baseRequest = QStringLiteral("http://"); ++ QString baseRequest = QStringLiteral("https://"); + baseRequest += m_uriProvider->getCurrentHost(); + baseRequest += QStringLiteral("/routing/7.2/calculateroute.xml"); + + baseRequest += QStringLiteral("?alternatives="); + baseRequest += QString::number(request.numberAlternativeRoutes()); + +- if (!m_appId.isEmpty() && !m_token.isEmpty()) { +- baseRequest += QStringLiteral("&app_id="); +- baseRequest += m_appId; +- baseRequest += QStringLiteral("&token="); +- baseRequest += m_token; ++ if (!m_apiKey.isEmpty()) { ++ baseRequest += QStringLiteral("&apiKey="); ++ baseRequest += m_apiKey; + } + + const QList metadata = request.waypointsMetadata(); +@@ -281,7 +278,7 @@ QStringList QGeoRoutingManagerEngineNokia::updateRouteRequestString(const QGeoRo + return QStringList(); + QStringList requests; + +- QString baseRequest = "http://"; ++ QString baseRequest = "https://"; + baseRequest += m_uriProvider->getCurrentHost(); + baseRequest += "/routing/7.2/getroute.xml"; + +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h +index 9335bcac..67fb5825 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h +@@ -77,8 +77,7 @@ private: + + QGeoNetworkAccessManager *m_networkManager; + QGeoUriProvider *m_uriProvider; +- QString m_appId; +- QString m_token; ++ QString m_apiKey; + }; + + QT_END_NAMESPACE +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp +index e4ef86d6..2c53dd16 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp +@@ -75,20 +75,15 @@ namespace + + void checkUsageTerms(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) + { +- QString appId, token; ++ const QString apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); + +- appId = parameters.value(QStringLiteral("here.app_id")).toString(); +- token = parameters.value(QStringLiteral("here.token")).toString(); +- +- if (isValidParameter(appId) && isValidParameter(token)) ++ if (isValidParameter(apiKey)) + return; +- else if (!isValidParameter(appId)) +- qWarning() << "Invalid here.app_id"; + else +- qWarning() << "Invalid here.token"; ++ qWarning() << "Invalid here.apiKey"; + +- if (parameters.contains(QStringLiteral("app_id")) || parameters.contains(QStringLiteral("token"))) +- qWarning() << QStringLiteral("Please prefix 'app_id' and 'token' with prefix 'here' (e.g.: 'here.app_id')"); ++ if (parameters.contains(QStringLiteral("apiKey"))) ++ qWarning() << QStringLiteral("Please prefix 'apiKey' with prefix 'here' (e.g.: 'here.apiKey')"); + + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, MISSED_CREDENTIALS); +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp +index d07a93ba..4cfd62f8 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp +@@ -84,8 +84,7 @@ QGeoTileFetcherNokia::QGeoTileFetcherNokia(const QVariantMap ¶meters, + m_tileSize = qMax(tileSize.width(), tileSize.height()); + m_networkManager->setParent(this); + +- m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString(); +- m_token = parameters.value(QStringLiteral("here.token")).toString(); ++ m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); + } + + QGeoTileFetcherNokia::~QGeoTileFetcherNokia() +@@ -127,7 +126,7 @@ QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec, int ppi + if (!m_engineNokia) + return QString(); + +- static const QString http("http://"); ++ static const QString http("https://"); + static const QString path("/maptile/2.1/maptile/newest/"); + static const QChar slash('/'); + +@@ -152,12 +151,9 @@ QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec, int ppi + static const QString slashpng("/png8"); + requestString += slashpng; + +- if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { // TODO: remove the if +- requestString += "?token="; +- requestString += m_token; +- +- requestString += "&app_id="; +- requestString += m_applicationId; ++ if (!m_apiKey.isEmpty()) { // TODO: remove the if ++ requestString += "?apiKey="; ++ requestString += m_apiKey; + } + + requestString += "&ppi=" + QString::number(ppi); +@@ -235,14 +231,9 @@ QString QGeoTileFetcherNokia::getLanguageString() const + // No "lg" param means that we want English. + } + +-QString QGeoTileFetcherNokia::token() const +-{ +- return m_token; +-} +- +-QString QGeoTileFetcherNokia::applicationId() const ++QString QGeoTileFetcherNokia::apiKey() const + { +- return m_applicationId; ++ return m_apiKey; + } + + void QGeoTileFetcherNokia::copyrightsFetched() +@@ -271,19 +262,14 @@ void QGeoTileFetcherNokia::versionFetched() + + void QGeoTileFetcherNokia::fetchCopyrightsData() + { +- QString copyrightUrl = QStringLiteral("http://"); ++ QString copyrightUrl = QStringLiteral("https://"); + + copyrightUrl += m_baseUriProvider->getCurrentHost(); + copyrightUrl += QStringLiteral("/maptile/2.1/copyright/newest?output=json"); + +- if (!token().isEmpty()) { +- copyrightUrl += QStringLiteral("&token="); +- copyrightUrl += token(); +- } +- +- if (!applicationId().isEmpty()) { +- copyrightUrl += QStringLiteral("&app_id="); +- copyrightUrl += applicationId(); ++ if (!apiKey().isEmpty()) { ++ copyrightUrl += QStringLiteral("&apiKey="); ++ copyrightUrl += apiKey(); + } + + QNetworkRequest netRequest((QUrl(copyrightUrl))); +@@ -303,19 +289,14 @@ void QGeoTileFetcherNokia::fetchCopyrightsData() + + void QGeoTileFetcherNokia::fetchVersionData() + { +- QString versionUrl = QStringLiteral("http://"); ++ QString versionUrl = QStringLiteral("https://"); + + versionUrl += m_baseUriProvider->getCurrentHost(); + versionUrl += QStringLiteral("/maptile/2.1/version"); + +- if (!token().isEmpty()) { +- versionUrl += QStringLiteral("?token="); +- versionUrl += token(); +- } +- +- if (!applicationId().isEmpty()) { +- versionUrl += QStringLiteral("&app_id="); +- versionUrl += applicationId(); ++ if (!apiKey().isEmpty()) { ++ versionUrl += QStringLiteral("?apiKey="); ++ versionUrl += apiKey(); + } + + QNetworkRequest netRequest((QUrl(versionUrl))); +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h +index 06d1bba9..ee0cb0e9 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h +@@ -62,8 +62,7 @@ public: + + QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec); + +- QString token() const; +- QString applicationId() const; ++ QString apiKey() const; + + public Q_SLOTS: + void copyrightsFetched(); +@@ -82,11 +81,10 @@ private: + QGeoNetworkAccessManager *m_networkManager; + int m_tileSize; + int m_ppi; +- QString m_token; + QNetworkReply *m_copyrightsReply; + QNetworkReply *m_versionReply; + +- QString m_applicationId; ++ QString m_apiKey; + QGeoUriProvider *m_baseUriProvider; + QGeoUriProvider *m_aerialUriProvider; + }; +diff --git a/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp b/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp +index 7be90bb2..6a4f6986 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/qgeouriprovider.cpp +@@ -61,7 +61,7 @@ QGeoUriProvider::QGeoUriProvider( + QString QGeoUriProvider::getCurrentHost() const + { + if (m_maxSubdomains) { +- QString result(m_firstSubdomain.toLatin1() + QRandomGenerator::global()->bounded(m_maxSubdomains)); ++ QString result(static_cast(m_firstSubdomain.toLatin1() + QRandomGenerator::global()->bounded(m_maxSubdomains))); + result += '.' + m_currentHost; + return result; + } +diff --git a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp +index c5c05a2e..4c6e9774 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp +@@ -208,8 +208,7 @@ QPlaceManagerEngineNokiaV2::QPlaceManagerEngineNokiaV2( + + m_locales.append(QLocale()); + +- m_appId = parameters.value(QStringLiteral("here.app_id")).toString(); +- m_appCode = parameters.value(QStringLiteral("here.token")).toString(); ++ m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString(); + + m_theme = parameters.value(IconThemeKey, QString()).toString(); + +@@ -237,7 +236,7 @@ QPlaceManagerEngineNokiaV2::~QPlaceManagerEngineNokiaV2() {} + + QPlaceDetailsReply *QPlaceManagerEngineNokiaV2::getPlaceDetails(const QString &placeId) + { +- QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + ++ QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/places/") + placeId); + + QUrlQuery queryItems; +@@ -268,7 +267,7 @@ QPlaceContentReply *QPlaceManagerEngineNokiaV2::getPlaceContent(const QPlaceCont + + networkReply = sendRequest(u); + } else { +- QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + ++ QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/places/") + request.placeId() + + QStringLiteral("/media/")); + +@@ -410,7 +409,7 @@ QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest + networkReply = sendRequest(u); + } else if (!query.searchTerm().isEmpty()) { + // search term query +- QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + ++ QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/discover/search")); + + queryItems.addQueryItem(QStringLiteral("q"), query.searchTerm()); +@@ -432,7 +431,7 @@ QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest + + return reply; + } else if (!query.recommendationId().isEmpty()) { +- QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + ++ QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/places/") + query.recommendationId() + + QStringLiteral("/related/recommended")); + +@@ -443,7 +442,7 @@ QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest + networkReply = sendRequest(requestUrl); + } else { + // category search +- QUrl requestUrl(QStringLiteral("http://") + m_uriProvider->getCurrentHost() + ++ QUrl requestUrl(QStringLiteral("https://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/discover/explore")); + + QStringList ids; +@@ -498,7 +497,7 @@ QPlaceSearchSuggestionReply *QPlaceManagerEngineNokiaV2::searchSuggestions(const + return reply; + } + +- QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + ++ QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/suggest")); + + QUrlQuery queryItems; +@@ -633,7 +632,7 @@ QPlaceReply *QPlaceManagerEngineNokiaV2::initializeCategories() + for (auto it = m_tempTree.keyBegin(), end = m_tempTree.keyEnd(); it != end; ++it) { + if (*it == QString()) + continue; +- QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + ++ QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/categories/places/") + *it); + QNetworkReply *networkReply = sendRequest(requestUrl); + connect(networkReply, SIGNAL(finished()), this, SLOT(categoryReplyFinished())); +@@ -831,8 +830,7 @@ void QPlaceManagerEngineNokiaV2::categoryReplyError() + QNetworkReply *QPlaceManagerEngineNokiaV2::sendRequest(const QUrl &url) + { + QUrlQuery queryItems(url); +- queryItems.addQueryItem(QStringLiteral("app_id"), m_appId); +- queryItems.addQueryItem(QStringLiteral("app_code"), m_appCode); ++ queryItems.addQueryItem(QStringLiteral("apiKey"), m_apiKey); + + QUrl requestUrl = url; + requestUrl.setQuery(queryItems); +diff --git a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h +index cd632958..6784ce4f 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h ++++ b/qtlocation/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h +@@ -122,8 +122,7 @@ private: + QPointer m_categoryReply; + QHash m_categoryRequests; + +- QString m_appId; +- QString m_appCode; ++ QString m_apiKey; + + QString m_localDataPath; + QString m_theme; +diff --git a/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp b/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp +index 8db47beb..030006f5 100644 +--- a/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp ++++ b/qtlocation/src/plugins/geoservices/nokia/uri_constants.cpp +@@ -37,11 +37,11 @@ + + QT_BEGIN_NAMESPACE + +-const QString ROUTING_HOST = QLatin1String("route.api.here.com"); +-const QString GEOCODING_HOST = QLatin1String("geocoder.api.here.com"); +-const QString REVERSE_GEOCODING_HOST = QLatin1String("reverse.geocoder.api.here.com"); +-const QString PLACES_HOST = QLatin1String("places.api.here.com"); +-const QString MAP_TILES_HOST = QLatin1String("1-4.base.maps.api.here.com"); +-const QString MAP_TILES_HOST_AERIAL = QLatin1String("1-4.aerial.maps.api.here.com"); ++const QString ROUTING_HOST = QLatin1String("route.ls.hereapi.com"); ++const QString GEOCODING_HOST = QLatin1String("geocoder.ls.hereapi.com"); ++const QString REVERSE_GEOCODING_HOST = QLatin1String("reverse.geocoder.ls.hereapi.com"); ++const QString PLACES_HOST = QLatin1String("places.ls.hereapi.com"); ++const QString MAP_TILES_HOST = QLatin1String("1-4.base.maps.ls.hereapi.com"); ++const QString MAP_TILES_HOST_AERIAL = QLatin1String("1-4.aerial.maps.ls.hereapi.com"); + + QT_END_NAMESPACE +Submodule qtmultimedia 5197ff9e..36603a39: +diff --git a/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp b/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp +index 4000f2178..a446d93fe 100644 +--- a/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp ++++ b/qtmultimedia/src/gsttools/qgstvideorenderersink.cpp +@@ -368,7 +368,8 @@ static GstGLContext *gstGLDisplayContext(QAbstractVideoSurface *surface) + if (!nativeContext) + qWarning() << "Could not find resource for" << contextName; + +- GstGLContext *appContext = gst_gl_context_new_wrapped(display, (guintptr)nativeContext, glPlatform, GST_GL_API_ANY); ++ GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2; ++ GstGLContext *appContext = gst_gl_context_new_wrapped(display, (guintptr)nativeContext, glPlatform, glApi); + if (!appContext) + qWarning() << "Could not create wrappped context for platform:" << glPlatform; + +diff --git a/qtmultimedia/src/multimediawidgets/multimediawidgets.pro b/qtmultimedia/src/multimediawidgets/multimediawidgets.pro +index 1919e8107..4c30d8fbf 100644 +--- a/qtmultimedia/src/multimediawidgets/multimediawidgets.pro ++++ b/qtmultimedia/src/multimediawidgets/multimediawidgets.pro +@@ -2,8 +2,6 @@ + TARGET = QtMultimediaWidgets + QT = core gui multimedia widgets-private + QT_PRIVATE += multimedia-private +-qtHaveModule(opengl): \ +- QT_PRIVATE += opengl + + PRIVATE_HEADERS += \ + qvideowidget_p.h \ +Submodule qtquick3d d29b769a..d4f5966b: +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 7bd4fe4c..134ca5db: +diff --git a/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp b/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp +index e5fe734f7..e36922775 100644 +--- a/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp ++++ b/qtquickcontrols2/src/imports/platform/widgets/qwidgetplatformmenu.cpp +@@ -38,6 +38,7 @@ + #include "qwidgetplatformmenuitem_p.h" + + #include ++#include + #include + #include + +@@ -145,7 +146,7 @@ void QWidgetPlatformMenu::showPopup(const QWindow *window, const QRect &targetRe + + QPoint targetPos = targetRect.bottomLeft(); + if (window) +- targetPos = window->mapToGlobal(targetPos); ++ targetPos = window->mapToGlobal(QHighDpi::fromNativeLocalPosition(targetPos, window)); + + const QWidgetPlatformMenuItem *widgetItem = qobject_cast(item); + m_menu->popup(targetPos, widgetItem ? widgetItem->action() : nullptr); +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp +index 20cf59c1a..43af47a94 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton.cpp +@@ -1201,6 +1201,12 @@ QAccessible::Role QQuickAbstractButton::accessibleRole() const + } + return QAccessible::Button; + } ++ ++void QQuickAbstractButton::accessiblePressAction() ++{ ++ Q_D(QQuickAbstractButton); ++ d->trigger(); ++} + #endif + + QT_END_NAMESPACE +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h +index 0fa48980e..ab66220d0 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h ++++ b/qtquickcontrols2/src/quicktemplates2/qquickabstractbutton_p.h +@@ -209,6 +209,7 @@ protected: + #if QT_CONFIG(accessibility) + void accessibilityActiveChanged(bool active) override; + QAccessible::Role accessibleRole() const override; ++ Q_INVOKABLE void accessiblePressAction(); + #endif + + private: +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp b/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp +index f38c2b09c..6eed2a024 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquickcontainer.cpp +@@ -225,6 +225,7 @@ void QQuickContainerPrivate::cleanup() + QObject::disconnect(contentModel, &QQmlObjectModel::countChanged, q, &QQuickContainer::countChanged); + QObject::disconnect(contentModel, &QQmlObjectModel::childrenChanged, q, &QQuickContainer::contentChildrenChanged); + delete contentModel; ++ contentModel = nullptr; + } + + QQuickItem *QQuickContainerPrivate::itemAt(int index) const +@@ -436,7 +437,7 @@ void QQuickContainerPrivate::contentChildren_clear(QQmlListProperty + void QQuickContainerPrivate::updateContentWidth() + { + Q_Q(QQuickContainer); +- if (hasContentWidth || qFuzzyCompare(contentWidth, implicitContentWidth)) ++ if (hasContentWidth || qFuzzyCompare(contentWidth, implicitContentWidth) || !contentModel) + return; + + contentWidth = implicitContentWidth; +@@ -446,7 +447,7 @@ void QQuickContainerPrivate::updateContentWidth() + void QQuickContainerPrivate::updateContentHeight() + { + Q_Q(QQuickContainer); +- if (hasContentHeight || qFuzzyCompare(contentHeight, implicitContentHeight)) ++ if (hasContentHeight || qFuzzyCompare(contentHeight, implicitContentHeight) || !contentModel) + return; + + contentHeight = implicitContentHeight; +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp b/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp +index a719efd34..768691dac 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquickcontrol.cpp +@@ -2334,12 +2334,13 @@ QAccessible::Role QQuickControl::accessibleRole() const + + void QQuickControl::accessibilityActiveChanged(bool active) + { ++ Q_D(QQuickControl); + if (!active) + return; + + QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(this, true)); + Q_ASSERT(accessibleAttached); +- accessibleAttached->setRole(accessibleRole()); ++ accessibleAttached->setRole(d->effectiveAccessibleRole()); + } + #endif + +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp b/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp +index e6db14eb5..6197d1547 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquickdialogbuttonbox.cpp +@@ -237,7 +237,7 @@ static QRectF alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment + void QQuickDialogButtonBoxPrivate::resizeContent() + { + Q_Q(QQuickDialogButtonBox); +- if (!contentItem) ++ if (!contentItem || !contentModel) + return; + + QRectF geometry = q->boundingRect().adjusted(q->leftPadding(), q->topPadding(), -q->rightPadding(), -q->bottomPadding()); +@@ -322,6 +322,9 @@ void QQuickDialogButtonBoxPrivate::updateLayout() + qreal QQuickDialogButtonBoxPrivate::getContentWidth() const + { + Q_Q(const QQuickDialogButtonBox); ++ if (!contentModel) ++ return 0; ++ + const int count = contentModel->count(); + const qreal totalSpacing = qMax(0, count - 1) * spacing; + qreal totalWidth = totalSpacing; +@@ -341,6 +344,9 @@ qreal QQuickDialogButtonBoxPrivate::getContentWidth() const + qreal QQuickDialogButtonBoxPrivate::getContentHeight() const + { + Q_Q(const QQuickDialogButtonBox); ++ if (!contentModel) ++ return 0; ++ + const int count = contentModel->count(); + qreal maxHeight = 0; + for (int i = 0; i < count; ++i) { +diff --git a/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp b/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp +index 71b60a2bc..2bc621674 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquicklabel.cpp +@@ -263,7 +263,7 @@ void QQuickLabelPrivate::accessibilityActiveChanged(bool active) + Q_Q(QQuickLabel); + QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, true)); + Q_ASSERT(accessibleAttached); +- accessibleAttached->setRole(accessibleRole()); ++ accessibleAttached->setRole(effectiveAccessibleRole()); + maybeSetAccessibleName(text); + } + +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp b/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp +index 91bd59184..0ce518f84 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquickoverlay.cpp +@@ -399,8 +399,11 @@ void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data) + Q_D(QQuickOverlay); + QQuickItem::itemChange(change, data); + +- if (change == ItemChildAddedChange || change == ItemChildRemovedChange) ++ if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { + setVisible(!d->allDrawers.isEmpty() || !childItems().isEmpty()); ++ if (data.item->parent() == d->mouseGrabberPopup) ++ d->setMouseGrabberPopup(nullptr); ++ } + } + + void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp b/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp +index 7df80a047..bfaa84e30 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquickpopup.cpp +@@ -46,6 +46,7 @@ + + #include + #include ++#include + #include + #include + +@@ -2720,6 +2721,19 @@ QPalette QQuickPopup::defaultPalette() const + } + + #if QT_CONFIG(accessibility) ++QAccessible::Role QQuickPopup::effectiveAccessibleRole() const ++{ ++ auto *attached = qmlAttachedPropertiesObject(this, false); ++ ++ auto role = QAccessible::NoRole; ++ if (auto *accessibleAttached = qobject_cast(attached)) ++ role = accessibleAttached->role(); ++ if (role == QAccessible::NoRole) ++ role = accessibleRole(); ++ ++ return role; ++} ++ + QAccessible::Role QQuickPopup::accessibleRole() const + { + return QAccessible::Dialog; +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h b/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h +index dc3ebf6f8..a3773be3e 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h ++++ b/qtquickcontrols2/src/quicktemplates2/qquickpopup_p.h +@@ -454,7 +454,10 @@ protected: + virtual QPalette defaultPalette() const; + + #if QT_CONFIG(accessibility) ++ QAccessible::Role effectiveAccessibleRole() const; ++private: + virtual QAccessible::Role accessibleRole() const; ++protected: + virtual void accessibilityActiveChanged(bool active); + #endif + +diff --git a/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp b/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp +index 0069b9fc1..143c37fc3 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquickpopupitem.cpp +@@ -404,7 +404,7 @@ QPalette QQuickPopupItem::defaultPalette() const + QAccessible::Role QQuickPopupItem::accessibleRole() const + { + Q_D(const QQuickPopupItem); +- return d->popup->accessibleRole(); ++ return d->popup->effectiveAccessibleRole(); + } + + void QQuickPopupItem::accessibilityActiveChanged(bool active) +diff --git a/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp b/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp +index 64fc631dd..fba3f6b70 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquicktextarea.cpp +@@ -512,7 +512,7 @@ void QQuickTextAreaPrivate::accessibilityActiveChanged(bool active) + Q_Q(QQuickTextArea); + QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, true)); + Q_ASSERT(accessibleAttached); +- accessibleAttached->setRole(accessibleRole()); ++ accessibleAttached->setRole(effectiveAccessibleRole()); + accessibleAttached->set_readOnly(q->isReadOnly()); + accessibleAttached->setDescription(placeholder); + } +diff --git a/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp b/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp +index 8fa04bd3a..e83346cbd 100644 +--- a/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp ++++ b/qtquickcontrols2/src/quicktemplates2/qquicktextfield.cpp +@@ -359,7 +359,7 @@ void QQuickTextFieldPrivate::accessibilityActiveChanged(bool active) + Q_Q(QQuickTextField); + QQuickAccessibleAttached *accessibleAttached = qobject_cast(qmlAttachedPropertiesObject(q, true)); + Q_ASSERT(accessibleAttached); +- accessibleAttached->setRole(accessibleRole()); ++ accessibleAttached->setRole(effectiveAccessibleRole()); + accessibleAttached->set_readOnly(m_readOnly); + accessibleAttached->set_passwordEdit((m_echoMode == QQuickTextField::Password || m_echoMode == QQuickTextField::PasswordEchoOnEdit) ? true : false); + accessibleAttached->setDescription(placeholder); +diff --git a/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml b/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml +new file mode 100644 +index 000000000..9e4598b9f +--- /dev/null ++++ b/qtquickcontrols2/tests/auto/qquickpopup/data/releaseAfterExitTransition.qml +@@ -0,0 +1,78 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2021 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the test suite of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:BSD$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** BSD License Usage ++** Alternatively, you may use this file under the terms of the BSD license ++** as follows: ++** ++** "Redistribution and use in source and binary forms, with or without ++** modification, are permitted provided that the following conditions are ++** met: ++** * Redistributions of source code must retain the above copyright ++** notice, this list of conditions and the following disclaimer. ++** * Redistributions in binary form must reproduce the above copyright ++** notice, this list of conditions and the following disclaimer in ++** the documentation and/or other materials provided with the ++** distribution. ++** * Neither the name of The Qt Company Ltd nor the names of its ++** contributors may be used to endorse or promote products derived ++** from this software without specific prior written permission. ++** ++** ++** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++import QtQuick 2.15 ++import QtQuick.Controls 2.15 ++ ++ApplicationWindow { ++ id: window ++ width: 400 ++ height: 400 ++ title: "releaseAfterExitTransition" ++ ++ property alias popup: popup ++ property alias modalPopup: modalPopup ++ ++ Popup { ++ id: popup ++ y: parent.height - height ++ width: 50 ++ height: 50 ++ } ++ ++ Popup { ++ id: modalPopup ++ modal: true ++ y: parent.height - height ++ width: 50 ++ height: 50 ++ exit: Transition { PauseAnimation { duration: 100 } } ++ } ++} +diff --git a/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp b/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp +index 54952d128..3d50e2dd4 100644 +--- a/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp ++++ b/qtquickcontrols2/tests/auto/qquickpopup/tst_qquickpopup.cpp +@@ -100,6 +100,7 @@ private slots: + void invisibleToolTipOpen(); + void centerInOverlayWithinStackViewItem(); + void destroyDuringExitTransition(); ++ void releaseAfterExitTransition(); + }; + + void tst_QQuickPopup::initTestCase() +@@ -1575,6 +1576,34 @@ void tst_QQuickPopup::destroyDuringExitTransition() + QVERIFY(!button->isDown()); + } + ++void tst_QQuickPopup::releaseAfterExitTransition() ++{ ++ QQuickApplicationHelper helper(this, "releaseAfterExitTransition.qml"); ++ QVERIFY2(helper.ready, helper.failureMessage()); ++ ++ QQuickWindow *window = helper.window; ++ window->show(); ++ QVERIFY(QTest::qWaitForWindowActive(window)); ++ ++ QQuickOverlay *overlay = QQuickOverlay::overlay(window); ++ QQuickPopup *modalPopup = window->property("modalPopup").value(); ++ QQuickPopup *popup = window->property("popup").value(); ++ ++ modalPopup->open(); ++ QTRY_VERIFY(modalPopup->isOpened()); ++ ++ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); ++ // wait until the transition is finished and the overlay hides itself ++ QTRY_VERIFY(!overlay->isVisible()); ++ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); ++ ++ popup->open(); ++ QTRY_VERIFY(popup->isOpened()); ++ QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); ++ QTRY_VERIFY(!popup->isOpened()); ++} ++ ++ + QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup) + + #include "tst_qquickpopup.moc" +Submodule qtspeech d02bcabe..c41437ac: +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 7b88174e..5b1b4a99: +diff --git a/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp b/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp +index 561e77e..12e0574 100644 +--- a/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp ++++ b/qtsvg/src/plugins/imageformats/svg/qsvgiohandler.cpp +@@ -191,6 +191,8 @@ bool QSvgIOHandler::read(QImage *image) + } + } + if (!finalSize.isEmpty()) { ++ if (qMax(finalSize.width(), finalSize.height()) > 0xffff) ++ return false; // Assume corrupted file + image->fill(d->backColor.rgba()); + QPainter p(image); + d->r.render(&p, bounds); +diff --git a/qtsvg/src/svg/qsvgfont_p.h b/qtsvg/src/svg/qsvgfont_p.h +index fd0a3fa..fcffbe8 100644 +--- a/qtsvg/src/svg/qsvgfont_p.h ++++ b/qtsvg/src/svg/qsvgfont_p.h +@@ -74,6 +74,7 @@ public: + class Q_SVG_PRIVATE_EXPORT QSvgFont : public QSvgRefCounted + { + public: ++ static constexpr qreal DEFAULT_UNITS_PER_EM = 1000; + QSvgFont(qreal horizAdvX); + + void setFamilyName(const QString &name); +@@ -86,9 +87,7 @@ public: + void draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const; + public: + QString m_familyName; +- qreal m_unitsPerEm; +- qreal m_ascent; +- qreal m_descent; ++ qreal m_unitsPerEm = DEFAULT_UNITS_PER_EM; + qreal m_horizAdvX; + QHash m_glyphs; + }; +diff --git a/qtsvg/src/svg/qsvghandler.cpp b/qtsvg/src/svg/qsvghandler.cpp +index c229c3b..222b6d8 100644 +--- a/qtsvg/src/svg/qsvghandler.cpp ++++ b/qtsvg/src/svg/qsvghandler.cpp +@@ -1393,9 +1393,10 @@ static void parseFont(QSvgNode *node, + case FontSizeNone: + break; + case FontSizeValue: { +- QSvgHandler::LengthType dummy; // should always be pixel size +- fontStyle->setSize(qMin(parseLength(attributes.fontSize, dummy, handler), +- qreal(0xffff))); ++ QSvgHandler::LengthType type; ++ qreal fs = parseLength(attributes.fontSize, type, handler); ++ fs = convertToPixels(fs, true, type); ++ fontStyle->setSize(qMin(fs, qreal(0xffff))); + } + break; + default: +@@ -2578,6 +2579,8 @@ static QSvgNode *createCircleNode(QSvgNode *parent, + qreal ncx = toDouble(cx); + qreal ncy = toDouble(cy); + qreal nr = toDouble(r); ++ if (nr < 0.0) ++ return nullptr; + + QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2); + QSvgNode *circle = new QSvgCircle(parent, rect); +@@ -2668,7 +2671,7 @@ static bool parseFontFaceNode(QSvgStyleProperty *parent, + + qreal unitsPerEm = toDouble(unitsPerEmStr); + if (!unitsPerEm) +- unitsPerEm = 1000; ++ unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM; + + if (!name.isEmpty()) + font->setFamilyName(name); +@@ -3048,15 +3051,16 @@ static QSvgStyleProperty *createRadialGradientNode(QSvgNode *node, + + qreal ncx = 0.5; + qreal ncy = 0.5; +- qreal nr = 0.5; + if (!cx.isEmpty()) + ncx = toDouble(cx); + if (!cy.isEmpty()) + ncy = toDouble(cy); ++ ++ qreal nr = 0.0; + if (!r.isEmpty()) + nr = toDouble(r); +- if (nr < 0.5) +- nr = 0.5; ++ if (nr <= 0.0) ++ return nullptr; + + qreal nfx = ncx; + if (!fx.isEmpty()) +@@ -3352,7 +3356,9 @@ static QSvgNode *createTextNode(QSvgNode *parent, + //### editable and rotate not handled + QSvgHandler::LengthType type; + qreal nx = parseLength(x, type, handler); ++ nx = convertToPixels(nx, true, type); + qreal ny = parseLength(y, type, handler); ++ ny = convertToPixels(ny, true, type); + + QSvgNode *text = new QSvgText(parent, QPointF(nx, ny)); + return text; +diff --git a/qtsvg/src/svg/qsvgstructure.cpp b/qtsvg/src/svg/qsvgstructure.cpp +index 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]); + } +Submodule qttools e089fe78..bd0ceb7d: +diff --git a/qttools/src/assistant/help/help.pro b/qttools/src/assistant/help/help.pro +index 800c4a38d..7556f451b 100644 +--- a/qttools/src/assistant/help/help.pro ++++ b/qttools/src/assistant/help/help.pro +@@ -1,7 +1,6 @@ + TARGET = QtHelp + + QT = core-private gui widgets sql +-QT_PRIVATE = network + + DEFINES += QHELP_LIB + +diff --git a/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp b/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp +index feab1e2d5..cbfb82507 100644 +--- a/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp ++++ b/qttools/src/assistant/qhelpgenerator/helpgenerator.cpp +@@ -445,7 +445,9 @@ bool HelpGeneratorPrivate::insertFiles(const QStringList &files, const QString & + if (filterSetId < 0) + return false; + ++filterSetId; +- for (int attId : qAsConst(filterAtts)) { ++ QList attValues = filterAtts.values(); ++ std::sort(attValues.begin(), attValues.end()); ++ for (int attId : qAsConst(attValues)) { + m_query->prepare(QLatin1String("INSERT INTO FileAttributeSetTable " + "VALUES(?, ?)")); + m_query->bindValue(0, filterSetId); +diff --git a/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro b/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro +index bb22000c8..415347a00 100644 +--- a/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro ++++ b/qttools/src/assistant/qhelpgenerator/qhelpgenerator.pro +@@ -1,4 +1,4 @@ +-QT += network help-private ++QT += help-private + + QTPLUGIN.platforms = qminimal + QTPLUGIN.sqldrivers = qsqlite +diff --git a/qttools/src/linguist/Qt5LinguistToolsMacros.cmake b/qttools/src/linguist/Qt5LinguistToolsMacros.cmake +index 6a45e57be..0ccf59f3c 100644 +--- a/qttools/src/linguist/Qt5LinguistToolsMacros.cmake ++++ b/qttools/src/linguist/Qt5LinguistToolsMacros.cmake +@@ -68,6 +68,7 @@ function(QT5_CREATE_TRANSLATION _qm_files) + if(NOT EXISTS "${stamp_file_dir}") + file(MAKE_DIRECTORY "${stamp_file_dir}") + endif() ++ set(stamp_files "") + foreach(_ts_file ${_my_tsfiles}) + get_filename_component(_ts_name ${_ts_file} NAME) + if(_my_sources) +@@ -95,7 +96,14 @@ function(QT5_CREATE_TRANSLATION _qm_files) + + file(WRITE ${_ts_lst_file} "${_lst_file_srcs}") + endif() +- set(stamp_file "${stamp_file_dir}/${_ts_name}.stamp") ++ file(RELATIVE_PATH _ts_relative_path ${CMAKE_CURRENT_SOURCE_DIR} ${_ts_file}) ++ string(REPLACE "../" "__/" _ts_relative_path "${_ts_relative_path}") ++ set(stamp_file "${stamp_file_dir}/${_ts_relative_path}.stamp") ++ list(APPEND stamp_files ${stamp_file}) ++ get_filename_component(full_stamp_file_dir "${stamp_file}" DIRECTORY) ++ if(NOT EXISTS "${full_stamp_file_dir}") ++ file(MAKE_DIRECTORY "${full_stamp_file_dir}") ++ endif() + add_custom_command(OUTPUT ${stamp_file} + COMMAND ${Qt5_LUPDATE_EXECUTABLE} + ARGS ${_lupdate_options} "@${_ts_lst_file}" -ts ${_ts_file} +@@ -103,7 +111,7 @@ function(QT5_CREATE_TRANSLATION _qm_files) + DEPENDS ${_dependencies} + VERBATIM) + endforeach() +- qt5_add_translation(${_qm_files} __QT_INTERNAL_DEPEND_ON_TIMESTAMP_FILE ${_my_tsfiles}) ++ qt5_add_translation(${_qm_files} ${_my_tsfiles} __QT_INTERNAL_TIMESTAMP_FILES ${stamp_files}) + set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) + endfunction() + +@@ -120,17 +128,17 @@ endif() + + + function(QT5_ADD_TRANSLATION _qm_files) +- set(options __QT_INTERNAL_DEPEND_ON_TIMESTAMP_FILE) ++ set(options) + set(oneValueArgs) +- set(multiValueArgs OPTIONS) ++ set(multiValueArgs OPTIONS __QT_INTERNAL_TIMESTAMP_FILES) + + cmake_parse_arguments(_LRELEASE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(_lrelease_files ${_LRELEASE_UNPARSED_ARGUMENTS}) + ++ set(idx 0) + foreach(_current_FILE ${_lrelease_files}) + get_filename_component(_abs_FILE ${_current_FILE} ABSOLUTE) + get_filename_component(qm ${_abs_FILE} NAME) +- set(ts_stamp_file "${CMAKE_CURRENT_BINARY_DIR}/.lupdate/${qm}.stamp") + # everything before the last dot has to be considered the file name (including other dots) + string(REGEX REPLACE "\\.[^.]*$" "" FILE_NAME ${qm}) + get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION) +@@ -141,7 +149,9 @@ function(QT5_ADD_TRANSLATION _qm_files) + set(qm "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.qm") + endif() + +- if(_LRELEASE___QT_INTERNAL_DEPEND_ON_TIMESTAMP_FILE) ++ if(_LRELEASE___QT_INTERNAL_TIMESTAMP_FILES) ++ list(GET _LRELEASE___QT_INTERNAL_TIMESTAMP_FILES ${idx} ts_stamp_file) ++ math(EXPR idx "${idx} + 1") + set(qm_dep "${ts_stamp_file}") + else() + set(qm_dep "${_abs_FILE}") +diff --git a/qttools/src/qdoc/clangcodeparser.cpp b/qttools/src/qdoc/clangcodeparser.cpp +index 539a603da..a41b99cec 100644 +--- a/qttools/src/qdoc/clangcodeparser.cpp ++++ b/qttools/src/qdoc/clangcodeparser.cpp +@@ -1395,8 +1395,7 @@ void ClangCodeParser::buildPCH() + args_.push_back("-xc++"); + CXTranslationUnit tu; + QString tmpHeader = pchFileDir_->path() + "/" + module; +- QFile tmpHeaderFile(tmpHeader); +- if (tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) { ++ { QFile tmpHeaderFile(tmpHeader); if (tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) { + QTextStream out(&tmpHeaderFile); + if (header.isEmpty()) { + for (auto it = allHeaders_.constKeyValueBegin(); +@@ -1421,8 +1420,7 @@ void ClangCodeParser::buildPCH() + out << line << "\n"; + } + } +- tmpHeaderFile.close(); +- } ++ } } + if (printParsingErrors_ == 0) + qCWarning(lcQdoc) << "clang not printing errors; include paths were guessed"; + CXErrorCode err = +Submodule qtwayland ec9fb19e..c8b37a1b: +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 966096a8..36609c08 100644 +--- a/qtwayland/src/client/global/qwaylandclientextension.cpp ++++ b/qtwayland/src/client/global/qwaylandclientextension.cpp +@@ -74,7 +74,10 @@ void QWaylandClientExtensionPrivate::handleRegistryGlobal(void *data, ::wl_regis + void QWaylandClientExtension::addRegistryListener() + { + Q_D(QWaylandClientExtension); +- d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); ++ if (!d->registered) { ++ d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); ++ d->registered = true; ++ } + } + + QWaylandClientExtension::QWaylandClientExtension(const int ver) +@@ -88,6 +91,13 @@ QWaylandClientExtension::QWaylandClientExtension(const int ver) + QMetaObject::invokeMethod(this, "addRegistryListener", Qt::QueuedConnection); + } + ++QWaylandClientExtension::~QWaylandClientExtension() ++{ ++ Q_D(QWaylandClientExtension); ++ if (d->registered && !QCoreApplication::closingDown()) ++ d->waylandIntegration->display()->removeListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); ++} ++ + QtWaylandClient::QWaylandIntegration *QWaylandClientExtension::integration() const + { + Q_D(const QWaylandClientExtension); +diff --git a/qtwayland/src/client/global/qwaylandclientextension.h b/qtwayland/src/client/global/qwaylandclientextension.h +index 98272e57..5bd28398 100644 +--- a/qtwayland/src/client/global/qwaylandclientextension.h ++++ b/qtwayland/src/client/global/qwaylandclientextension.h +@@ -63,6 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtension : public QObject + Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) + public: + QWaylandClientExtension(const int version); ++ ~QWaylandClientExtension(); + + QtWaylandClient::QWaylandIntegration *integration() const; + int version() const; +diff --git a/qtwayland/src/client/global/qwaylandclientextension_p.h b/qtwayland/src/client/global/qwaylandclientextension_p.h +index 69cc46a0..9091efbe 100644 +--- a/qtwayland/src/client/global/qwaylandclientextension_p.h ++++ b/qtwayland/src/client/global/qwaylandclientextension_p.h +@@ -68,6 +68,7 @@ public: + QtWaylandClient::QWaylandIntegration *waylandIntegration = nullptr; + int version = -1; + bool active = false; ++ bool registered = false; + }; + + class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtensionTemplatePrivate : public QWaylandClientExtensionPrivate +diff --git a/qtwayland/src/client/qwaylandabstractdecoration.cpp b/qtwayland/src/client/qwaylandabstractdecoration.cpp +index b628930d..d15a7f9f 100644 +--- a/qtwayland/src/client/qwaylandabstractdecoration.cpp ++++ b/qtwayland/src/client/qwaylandabstractdecoration.cpp +@@ -122,7 +122,7 @@ const QImage &QWaylandAbstractDecoration::contentImage() + if (d->m_isDirty) { + // Update the decoration backingstore + +- const int bufferScale = waylandWindow()->scale(); ++ const qreal bufferScale = waylandWindow()->scale(); + const QSize imageSize = waylandWindow()->surfaceSize() * bufferScale; + d->m_decorationContentImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); + // Only scale by buffer scale, not QT_SCALE_FACTOR etc. +diff --git a/qtwayland/src/client/qwaylandclipboard.cpp b/qtwayland/src/client/qwaylandclipboard.cpp +index 81f48e05..14561c77 100644 +--- a/qtwayland/src/client/qwaylandclipboard.cpp ++++ b/qtwayland/src/client/qwaylandclipboard.cpp +@@ -54,10 +54,15 @@ namespace QtWaylandClient { + QWaylandClipboard::QWaylandClipboard(QWaylandDisplay *display) + : mDisplay(display) + { ++ m_clientClipboard[QClipboard::Clipboard] = nullptr; ++ m_clientClipboard[QClipboard::Selection] = nullptr; + } + + QWaylandClipboard::~QWaylandClipboard() + { ++ if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) ++ delete m_clientClipboard[QClipboard::Clipboard]; ++ delete m_clientClipboard[QClipboard::Selection]; + } + + QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) +@@ -69,8 +74,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) + switch (mode) { + case QClipboard::Clipboard: + if (auto *dataDevice = seat->dataDevice()) { +- if (auto *source = dataDevice->selectionSource()) +- return source->mimeData(); ++ if (dataDevice->selectionSource()) ++ return m_clientClipboard[QClipboard::Clipboard]; + if (auto *offer = dataDevice->selectionOffer()) + return offer->mimeData(); + } +@@ -78,8 +83,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) + case QClipboard::Selection: + #if QT_CONFIG(wayland_client_primary_selection) + if (auto *selectionDevice = seat->primarySelectionDevice()) { +- if (auto *source = selectionDevice->selectionSource()) +- return source->mimeData(); ++ if (selectionDevice->selectionSource()) ++ return m_clientClipboard[QClipboard::Selection]; + if (auto *offer = selectionDevice->selectionOffer()) + return offer->mimeData(); + } +@@ -104,17 +109,27 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) + if (data && data->hasFormat(plain) && !data->hasFormat(utf8)) + data->setData(utf8, data->data(plain)); + ++ if (m_clientClipboard[mode]) { ++ if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) ++ delete m_clientClipboard[mode]; ++ m_clientClipboard[mode] = nullptr; ++ } ++ ++ m_clientClipboard[mode] = data; ++ + switch (mode) { + case QClipboard::Clipboard: + if (auto *dataDevice = seat->dataDevice()) { +- dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr); ++ dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), ++ m_clientClipboard[QClipboard::Clipboard]) : nullptr); + emitChanged(mode); + } + break; + case QClipboard::Selection: + #if QT_CONFIG(wayland_client_primary_selection) + if (auto *selectionDevice = seat->primarySelectionDevice()) { +- selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), data) : nullptr); ++ selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), ++ m_clientClipboard[QClipboard::Selection]) : nullptr); + emitChanged(mode); + } + #endif +diff --git a/qtwayland/src/client/qwaylandclipboard_p.h b/qtwayland/src/client/qwaylandclipboard_p.h +index ce14e124..bb52683d 100644 +--- a/qtwayland/src/client/qwaylandclipboard_p.h ++++ b/qtwayland/src/client/qwaylandclipboard_p.h +@@ -80,6 +80,7 @@ public: + private: + QWaylandDisplay *mDisplay = nullptr; + QMimeData m_emptyData; ++ QMimeData *m_clientClipboard[2]; + }; + + } +diff --git a/qtwayland/src/client/qwaylandcursor.cpp b/qtwayland/src/client/qwaylandcursor.cpp +index e4eca9d4..ba76ba2d 100644 +--- a/qtwayland/src/client/qwaylandcursor.cpp ++++ b/qtwayland/src/client/qwaylandcursor.cpp +@@ -44,6 +44,7 @@ + #include "qwaylandshmbackingstore_p.h" + + #include ++#include + #include + + #include +@@ -250,7 +251,27 @@ QWaylandCursor::QWaylandCursor(QWaylandDisplay *display) + QSharedPointer QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor) + { + Q_ASSERT(cursor->shape() == Qt::BitmapCursor); +- const QImage &img = cursor->pixmap().toImage(); ++ ++ const QBitmap mask = cursor->mask(Qt::ReturnByValue); ++ QImage img; ++ if (cursor->pixmap().isNull()) ++ img = cursor->bitmap(Qt::ReturnByValue).toImage(); ++ else ++ img = cursor->pixmap().toImage(); ++ ++ // convert to supported format if necessary ++ if (!display->shm()->formatSupported(img.format())) { ++ if (mask.isNull()) { ++ img.convertTo(QImage::Format_RGB32); ++ } else { ++ // preserve mask ++ img.convertTo(QImage::Format_ARGB32); ++ QPixmap pixmap = QPixmap::fromImage(img); ++ pixmap.setMask(mask); ++ img = pixmap.toImage(); ++ } ++ } ++ + QSharedPointer buffer(new QWaylandShmBuffer(display, img.size(), img.format())); + memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes())); + return buffer; +diff --git a/qtwayland/src/client/qwaylanddatadevice.cpp b/qtwayland/src/client/qwaylanddatadevice.cpp +index 1e2db786..07b18ab0 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(); +@@ -122,8 +124,38 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) + return false; + } + ++ // dragging data without mimetypes is a legal operation in Qt terms ++ // but Wayland uses a mimetype to determine if a drag is accepted or not ++ // In this rare case, insert a placeholder ++ if (mimeData->formats().isEmpty()) ++ mimeData->setData(QString::fromLatin1("application/x-qt-avoid-empty-placeholder"), QByteArray("1")); ++ + m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); ++ ++ if (wl_data_device_get_version(object()) >= 3) ++ m_dragSource->set_actions(dropActionsToWl(supportedActions)); ++ + connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); ++ connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) { ++ auto drag = static_cast(QGuiApplicationPrivate::platformIntegration()->drag()); ++ if (!drag->currentDrag()) { ++ return; ++ } ++ // in old versions drop action is not set, so we guess ++ if (wl_data_source_get_version(m_dragSource->object()) < 3) { ++ drag->setResponse(accepted); ++ } else { ++ QPlatformDropQtResponse response(accepted, action); ++ drag->setResponse(response); ++ } ++ }); ++ connect(m_dragSource.data(), &QWaylandDataSource::dndDropped, this, [](bool accepted, Qt::DropAction action) { ++ QPlatformDropQtResponse response(accepted, action); ++ static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setDropResponse(response); ++ }); ++ connect(m_dragSource.data(), &QWaylandDataSource::finished, this, []() { ++ static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); ++ }); + + start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial()); + return true; +@@ -152,7 +184,7 @@ void QWaylandDataDevice::data_device_drop() + supportedActions = drag->supportedActions(); + } else if (m_dragOffer) { + dragData = m_dragOffer->mimeData(); +- supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; ++ supportedActions = m_dragOffer->supportedActions(); + } else { + return; + } +@@ -162,7 +194,11 @@ void QWaylandDataDevice::data_device_drop() + QGuiApplication::keyboardModifiers()); + + if (drag) { +- static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response); ++ auto drag = static_cast(QGuiApplicationPrivate::platformIntegration()->drag()); ++ drag->setDropResponse(response); ++ drag->finishDrag(); ++ } else if (m_dragOffer) { ++ m_dragOffer->finish(); + } + } + +@@ -186,7 +222,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, + supportedActions = drag->supportedActions(); + } else if (m_dragOffer) { + dragData = m_dragOffer->mimeData(); +- supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; ++ supportedActions = m_dragOffer->supportedActions(); + } + + const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, +@@ -197,11 +233,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, + static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); + } + +- if (response.isAccepted()) { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); +- } else { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); +- } ++ sendResponse(supportedActions, response); + } + + void QWaylandDataDevice::data_device_leave() +@@ -235,10 +267,10 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe + supportedActions = drag->supportedActions(); + } else { + dragData = m_dragOffer->mimeData(); +- supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; ++ supportedActions = m_dragOffer->supportedActions(); + } + +- QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, ++ const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, + QGuiApplication::mouseButtons(), + QGuiApplication::keyboardModifiers()); + +@@ -246,11 +278,7 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe + static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); + } + +- if (response.isAccepted()) { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); +- } else { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); +- } ++ sendResponse(supportedActions, response); + } + #endif // QT_CONFIG(draganddrop) + +@@ -277,14 +305,10 @@ void QWaylandDataDevice::selectionSourceCancelled() + #if QT_CONFIG(draganddrop) + void QWaylandDataDevice::dragSourceCancelled() + { ++ static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); + m_dragSource.reset(); + } + +-void QWaylandDataDevice::dragSourceTargetChanged(const QString &mimeType) +-{ +- static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->updateTarget(mimeType); +-} +- + QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) const + { + QPoint pnt(wl_fixed_to_int(x), wl_fixed_to_int(y)); +@@ -297,6 +321,33 @@ QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) con + } + return pnt; + } ++ ++void QWaylandDataDevice::sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response) ++{ ++ if (response.isAccepted()) { ++ if (wl_data_device_get_version(object()) >= 3) ++ m_dragOffer->set_actions(dropActionsToWl(supportedActions), dropActionsToWl(response.acceptedAction())); ++ ++ m_dragOffer->accept(m_enterSerial, m_dragOffer->firstFormat()); ++ } else { ++ m_dragOffer->accept(m_enterSerial, QString()); ++ } ++} ++ ++int QWaylandDataDevice::dropActionsToWl(Qt::DropActions actions) ++{ ++ ++ int wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; ++ if (actions & Qt::CopyAction) ++ wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; ++ if (actions & (Qt::MoveAction | Qt::TargetMoveAction)) ++ wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; ++ ++ // wayland does not support LinkAction at the time of writing ++ return wlActions; ++} ++ ++ + #endif // QT_CONFIG(draganddrop) + + } +diff --git a/qtwayland/src/client/qwaylanddatadevice_p.h b/qtwayland/src/client/qwaylanddatadevice_p.h +index 16c3ad28..801dcc2c 100644 +--- a/qtwayland/src/client/qwaylanddatadevice_p.h ++++ b/qtwayland/src/client/qwaylanddatadevice_p.h +@@ -64,6 +64,7 @@ QT_REQUIRE_CONFIG(wayland_datadevice); + QT_BEGIN_NAMESPACE + + class QMimeData; ++class QPlatformDragQtResponse; + class QWindow; + + namespace QtWaylandClient { +@@ -89,7 +90,7 @@ public: + + #if QT_CONFIG(draganddrop) + QWaylandDataOffer *dragOffer() const; +- bool startDrag(QMimeData *mimeData, QWaylandWindow *icon); ++ bool startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon); + void cancelDrag(); + #endif + +@@ -109,13 +110,16 @@ private Q_SLOTS: + + #if QT_CONFIG(draganddrop) + void dragSourceCancelled(); +- void dragSourceTargetChanged(const QString &mimeType); + #endif + + private: + #if QT_CONFIG(draganddrop) + QPoint calculateDragPosition(int x, int y, QWindow *wnd) const; + #endif ++ void sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response); ++ ++ static int dropActionsToWl(Qt::DropActions dropActions); ++ + + QWaylandDisplay *m_display = nullptr; + QWaylandInputDevice *m_inputDevice = nullptr; +diff --git a/qtwayland/src/client/qwaylanddatadevicemanager.cpp b/qtwayland/src/client/qwaylanddatadevicemanager.cpp +index 35d67307..6dc4f77f 100644 +--- a/qtwayland/src/client/qwaylanddatadevicemanager.cpp ++++ b/qtwayland/src/client/qwaylanddatadevicemanager.cpp +@@ -50,8 +50,8 @@ QT_BEGIN_NAMESPACE + + namespace QtWaylandClient { + +-QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id) +- : wl_data_device_manager(display->wl_registry(), id, 1) ++QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id) ++ : wl_data_device_manager(display->wl_registry(), id, qMin(version, 3)) + , m_display(display) + { + // Create transfer devices for all input devices. +diff --git a/qtwayland/src/client/qwaylanddatadevicemanager_p.h b/qtwayland/src/client/qwaylanddatadevicemanager_p.h +index bd05c0fb..510d9be4 100644 +--- a/qtwayland/src/client/qwaylanddatadevicemanager_p.h ++++ b/qtwayland/src/client/qwaylanddatadevicemanager_p.h +@@ -68,7 +68,7 @@ class QWaylandInputDevice; + class Q_WAYLAND_CLIENT_EXPORT QWaylandDataDeviceManager : public QtWayland::wl_data_device_manager + { + public: +- QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id); ++ QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id); + ~QWaylandDataDeviceManager() override; + + QWaylandDataDevice *getDataDevice(QWaylandInputDevice *inputDevice); +diff --git a/qtwayland/src/client/qwaylanddataoffer.cpp b/qtwayland/src/client/qwaylanddataoffer.cpp +index 2297e8a1..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 c2bc9dc4..321170a6 100644 +--- a/qtwayland/src/client/qwaylanddatasource.cpp ++++ b/qtwayland/src/client/qwaylanddatasource.cpp +@@ -72,11 +72,6 @@ QWaylandDataSource::~QWaylandDataSource() + destroy(); + } + +-QMimeData * QWaylandDataSource::mimeData() const +-{ +- return m_mime_data; +-} +- + void QWaylandDataSource::data_source_cancelled() + { + Q_EMIT cancelled(); +@@ -110,7 +105,32 @@ void QWaylandDataSource::data_source_send(const QString &mime_type, int32_t fd) + + void QWaylandDataSource::data_source_target(const QString &mime_type) + { +- Q_EMIT targetChanged(mime_type); ++ m_accepted = !mime_type.isEmpty(); ++ Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); ++} ++ ++void QWaylandDataSource::data_source_action(uint32_t action) ++{ ++ Qt::DropAction qtAction = Qt::IgnoreAction; ++ ++ if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) ++ qtAction = Qt::MoveAction; ++ else if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) ++ qtAction = Qt::CopyAction; ++ ++ m_dropAction = qtAction; ++ Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); ++} ++ ++void QWaylandDataSource::data_source_dnd_finished() ++{ ++ Q_EMIT finished(); ++} ++ ++void QWaylandDataSource::data_source_dnd_drop_performed() ++{ ++ ++ Q_EMIT dndDropped(m_accepted, m_dropAction); + } + + } +diff --git a/qtwayland/src/client/qwaylanddatasource_p.h b/qtwayland/src/client/qwaylanddatasource_p.h +index 3003da1b..089c5485 100644 +--- a/qtwayland/src/client/qwaylanddatasource_p.h ++++ b/qtwayland/src/client/qwaylanddatasource_p.h +@@ -74,19 +74,25 @@ public: + QWaylandDataSource(QWaylandDataDeviceManager *dataDeviceManager, QMimeData *mimeData); + ~QWaylandDataSource() override; + +- QMimeData *mimeData() const; +- + Q_SIGNALS: +- void targetChanged(const QString &mime_type); + void cancelled(); ++ void finished(); ++ ++ void dndResponseUpdated(bool accepted, Qt::DropAction action); ++ void dndDropped(bool accepted, Qt::DropAction action); + + protected: + void data_source_cancelled() override; + void data_source_send(const QString &mime_type, int32_t fd) override; + void data_source_target(const QString &mime_type) override; ++ void data_source_dnd_drop_performed() override; ++ void data_source_dnd_finished() override; ++ void data_source_action(uint32_t action) override; + + private: + QMimeData *m_mime_data = nullptr; ++ bool m_accepted = false; ++ Qt::DropAction m_dropAction = Qt::IgnoreAction; + }; + + } +diff --git a/qtwayland/src/client/qwaylanddisplay.cpp b/qtwayland/src/client/qwaylanddisplay.cpp +index 8a6d5db1..737b539d 100644 +--- a/qtwayland/src/client/qwaylanddisplay.cpp ++++ b/qtwayland/src/client/qwaylanddisplay.cpp +@@ -87,10 +87,203 @@ + + #include + ++#include // for std::tie ++ ++static void checkWaylandError(struct wl_display *display) ++{ ++ int ecode = wl_display_get_error(display); ++ if ((ecode == EPIPE || ecode == ECONNRESET)) { ++ // special case this to provide a nicer error ++ qWarning("The Wayland connection broke. Did the Wayland compositor die?"); ++ } else { ++ qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); ++ } ++ _exit(1); ++} ++ + QT_BEGIN_NAMESPACE + + namespace QtWaylandClient { + ++class EventThread : public QThread ++{ ++ Q_OBJECT ++public: ++ enum OperatingMode { ++ EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread. ++ SelfDispatch, // Dispatch the events inside this thread. ++ }; ++ ++ EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue, ++ OperatingMode mode) ++ : m_fd(wl_display_get_fd(wl)) ++ , m_pipefd{ -1, -1 } ++ , m_wldisplay(wl) ++ , m_wlevqueue(ev_queue) ++ , m_mode(mode) ++ , m_reading(true) ++ , m_quitting(false) ++ { ++ setObjectName(QStringLiteral("WaylandEventThread")); ++ } ++ ++ void readAndDispatchEvents() ++ { ++ /* ++ * Dispatch pending events and flush the requests at least once. If the event thread ++ * is not reading, try to call _prepare_read() to allow the event thread to poll(). ++ * If that fails, re-try dispatch & flush again until _prepare_read() is successful. ++ * ++ * This allow any call to readAndDispatchEvents() to start event thread's polling, ++ * not only the one issued from event thread's waitForReading(), which means functions ++ * called from dispatch_pending() can safely spin an event loop. ++ */ ++ for (;;) { ++ if (dispatchQueuePending() < 0) { ++ checkWaylandError(m_wldisplay); ++ return; ++ } ++ ++ wl_display_flush(m_wldisplay); ++ ++ // We have to check if event thread is reading every time we dispatch ++ // something, as that may recursively call this function. ++ if (m_reading.loadAcquire()) ++ break; ++ ++ if (prepareReadQueue() == 0) { ++ QMutexLocker l(&m_mutex); ++ m_reading.storeRelease(true); ++ m_cond.wakeOne(); ++ break; ++ } ++ } ++ } ++ ++ void stop() ++ { ++ // We have to both write to the pipe and set the flag, as the thread may be ++ // either in the poll() or waiting for _prepare_read(). ++ if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1) ++ qWarning("Failed to write to the pipe: %s.", strerror(errno)); ++ ++ { ++ QMutexLocker l(&m_mutex); ++ m_quitting = true; ++ m_cond.wakeOne(); ++ } ++ ++ wait(); ++ } ++ ++Q_SIGNALS: ++ void needReadAndDispatch(); ++ ++protected: ++ void run() override ++ { ++ // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets ++ // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore. ++ struct Pipe ++ { ++ Pipe(int *fds) ++ : fds(fds) ++ { ++ if (qt_safe_pipe(fds) != 0) ++ qWarning("Pipe creation failed. Quitting may hang."); ++ } ++ ~Pipe() ++ { ++ if (fds[0] != -1) { ++ close(fds[0]); ++ close(fds[1]); ++ } ++ } ++ ++ int *fds; ++ } pipe(m_pipefd); ++ ++ // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the ++ // outbound ones. Wait until it's done before proceeding, unless we're told to quit. ++ while (waitForReading()) { ++ pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } }; ++ poll(fds, 2, -1); ++ ++ if (fds[1].revents & POLLIN) { ++ // we don't really care to read the byte that was written here since we're closing down ++ wl_display_cancel_read(m_wldisplay); ++ break; ++ } ++ ++ if (fds[0].revents & POLLIN) ++ wl_display_read_events(m_wldisplay); ++ // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop ++ // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which ++ // case we don't care anymore about them. ++ else ++ wl_display_cancel_read(m_wldisplay); ++ } ++ } ++ ++private: ++ bool waitForReading() ++ { ++ Q_ASSERT(QThread::currentThread() == this); ++ ++ m_reading.storeRelease(false); ++ ++ if (m_mode == SelfDispatch) { ++ readAndDispatchEvents(); ++ } else { ++ Q_EMIT needReadAndDispatch(); ++ ++ QMutexLocker lock(&m_mutex); ++ // m_reading might be set from our emit or some other invocation of ++ // readAndDispatchEvents(). ++ while (!m_reading.loadRelaxed() && !m_quitting) ++ m_cond.wait(&m_mutex); ++ } ++ ++ return !m_quitting; ++ } ++ ++ int dispatchQueuePending() ++ { ++ if (m_wlevqueue) ++ return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue); ++ else ++ return wl_display_dispatch_pending(m_wldisplay); ++ } ++ ++ int prepareReadQueue() ++ { ++ if (m_wlevqueue) ++ return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue); ++ else ++ return wl_display_prepare_read(m_wldisplay); ++ } ++ ++ int m_fd; ++ int m_pipefd[2]; ++ wl_display *m_wldisplay; ++ wl_event_queue *m_wlevqueue; ++ OperatingMode m_mode; ++ ++ /* Concurrency note when operating in EmitToDispatch mode: ++ * m_reading is set to false inside event thread's waitForReading(), and is ++ * set to true inside main thread's readAndDispatchEvents(). ++ * The lock is not taken when setting m_reading to false, as the main thread ++ * is not actively waiting for it to turn false. However, the lock is taken ++ * inside readAndDispatchEvents() before setting m_reading to true, ++ * as the event thread is actively waiting for it under the wait condition. ++ */ ++ ++ QAtomicInteger m_reading; ++ bool m_quitting; ++ QMutex m_mutex; ++ QWaitCondition m_cond; ++}; ++ + Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging + + struct wl_surface *QWaylandDisplay::createSurface(void *handle) +@@ -160,17 +353,16 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) + if (!mXkbContext) + qCWarning(lcQpaWayland, "failed to create xkb context"); + #endif +- +- forceRoundTrip(); +- +- if (!mWaitingScreens.isEmpty()) { +- // Give wl_output.done and zxdg_output_v1.done events a chance to arrive +- forceRoundTrip(); +- } + } + + QWaylandDisplay::~QWaylandDisplay(void) + { ++ if (m_eventThread) ++ m_eventThread->stop(); ++ ++ if (m_frameEventQueueThread) ++ m_frameEventQueueThread->stop(); ++ + if (mSyncCallback) + wl_callback_destroy(mSyncCallback); + +@@ -187,10 +379,26 @@ QWaylandDisplay::~QWaylandDisplay(void) + #if QT_CONFIG(cursor) + qDeleteAll(mCursorThemes); + #endif ++ ++ if (m_frameEventQueue) ++ wl_event_queue_destroy(m_frameEventQueue); ++ + if (mDisplay) + wl_display_disconnect(mDisplay); + } + ++// Steps which is called just after constructor. This separates registry_global() out of the constructor ++// so that factory functions in integration can be overridden. ++void QWaylandDisplay::initialize() ++{ ++ forceRoundTrip(); ++ ++ if (!mWaitingScreens.isEmpty()) { ++ // Give wl_output.done and zxdg_output_v1.done events a chance to arrive ++ forceRoundTrip(); ++ } ++} ++ + void QWaylandDisplay::ensureScreen() + { + if (!mScreens.empty() || mPlaceholderScreen) +@@ -205,98 +413,37 @@ void QWaylandDisplay::ensureScreen() + + void QWaylandDisplay::checkError() const + { +- int ecode = wl_display_get_error(mDisplay); +- if ((ecode == EPIPE || ecode == ECONNRESET)) { +- // special case this to provide a nicer error +- qWarning("The Wayland connection broke. Did the Wayland compositor die?"); +- } else { +- qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); +- } +- _exit(1); ++ checkWaylandError(mDisplay); + } + ++// Called in main thread, either from queued signal or directly. + void QWaylandDisplay::flushRequests() + { +- if (wl_display_prepare_read(mDisplay) == 0) { +- wl_display_read_events(mDisplay); +- } +- +- if (wl_display_dispatch_pending(mDisplay) < 0) +- checkError(); +- +- { +- QReadLocker locker(&m_frameQueueLock); +- for (const FrameQueue &q : mExternalQueues) { +- QMutexLocker locker(q.mutex); +- while (wl_display_prepare_read_queue(mDisplay, q.queue) != 0) +- wl_display_dispatch_queue_pending(mDisplay, q.queue); +- wl_display_read_events(mDisplay); +- wl_display_dispatch_queue_pending(mDisplay, q.queue); +- } +- } +- +- wl_display_flush(mDisplay); +-} +- +-void QWaylandDisplay::blockingReadEvents() +-{ +- if (wl_display_dispatch(mDisplay) < 0) +- checkError(); +-} +- +-void QWaylandDisplay::destroyFrameQueue(const QWaylandDisplay::FrameQueue &q) +-{ +- QWriteLocker locker(&m_frameQueueLock); +- auto it = std::find_if(mExternalQueues.begin(), +- mExternalQueues.end(), +- [&q] (const QWaylandDisplay::FrameQueue &other){ return other.queue == q.queue; }); +- Q_ASSERT(it != mExternalQueues.end()); +- mExternalQueues.erase(it); +- if (q.queue != nullptr) +- wl_event_queue_destroy(q.queue); +- delete q.mutex; ++ m_eventThread->readAndDispatchEvents(); + } + +-QWaylandDisplay::FrameQueue QWaylandDisplay::createFrameQueue() ++// We have to wait until we have an eventDispatcher before creating the eventThread, ++// otherwise forceRoundTrip() may block inside _events_read() because eventThread is ++// polling. ++void QWaylandDisplay::initEventThread() + { +- QWriteLocker locker(&m_frameQueueLock); +- FrameQueue q{createEventQueue()}; +- mExternalQueues.append(q); +- return q; +-} ++ m_eventThread.reset( ++ new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch)); ++ connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this, ++ &QWaylandDisplay::flushRequests, Qt::QueuedConnection); ++ m_eventThread->start(); + +-wl_event_queue *QWaylandDisplay::createEventQueue() +-{ +- return wl_display_create_queue(mDisplay); ++ // wl_display_disconnect() free this. ++ m_frameEventQueue = wl_display_create_queue(mDisplay); ++ m_frameEventQueueThread.reset( ++ new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch)); ++ m_frameEventQueueThread->start(); + } + +-void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout) ++void QWaylandDisplay::blockingReadEvents() + { +- if (!condition()) +- return; +- +- QElapsedTimer timer; +- timer.start(); +- struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN); +- while (timeout == -1 || timer.elapsed() < timeout) { +- while (wl_display_prepare_read_queue(mDisplay, queue) != 0) +- wl_display_dispatch_queue_pending(mDisplay, queue); +- +- wl_display_flush(mDisplay); +- +- const int remaining = qMax(timeout - timer.elapsed(), 0ll); +- const int pollTimeout = timeout == -1 ? -1 : remaining; +- if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0) +- wl_display_read_events(mDisplay); +- else +- wl_display_cancel_read(mDisplay); +- +- if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) +- checkError(); +- +- if (!condition()) +- break; +- } ++ if (wl_display_dispatch(mDisplay) < 0) ++ checkWaylandError(mDisplay); + } + + QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const +@@ -347,7 +494,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin + if (interface == QStringLiteral("wl_output")) { + mWaitingScreens << new QWaylandScreen(this, version, id); + } else if (interface == QStringLiteral("wl_compositor")) { +- mCompositorVersion = qMin((int)version, 3); ++ mCompositorVersion = qMin((int)version, 4); + mCompositor.init(registry, id, mCompositorVersion); + } else if (interface == QStringLiteral("wl_shm")) { + mShm.reset(new QWaylandShm(this, version, id)); +@@ -356,7 +503,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin + mInputDevices.append(inputDevice); + #if QT_CONFIG(wayland_datadevice) + } else if (interface == QStringLiteral("wl_data_device_manager")) { +- mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, id)); ++ mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id)); + #endif + } else if (interface == QStringLiteral("qt_surface_extension")) { + mWindowExtension.reset(new QtWayland::qt_surface_extension(registry, id, 1)); +@@ -373,6 +520,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin + #if QT_CONFIG(wayland_client_primary_selection) + } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { + mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); ++ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) ++ inputDevice->setPrimarySelectionDevice(mPrimarySelectionManager->createDevice(inputDevice)); + #endif + } else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) { + mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1)); +@@ -431,6 +580,13 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) + inputDevice->setTextInput(nullptr); + mWaylandIntegration->reconfigureInputContext(); + } ++#if QT_CONFIG(wayland_client_primary_selection) ++ if (global.interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { ++ mPrimarySelectionManager.reset(); ++ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) ++ inputDevice->setPrimarySelectionDevice(nullptr); ++ } ++#endif + mGlobals.removeAt(i); + break; + } +@@ -456,9 +612,10 @@ void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data) + + void QWaylandDisplay::removeListener(RegistryListener listener, void *data) + { +- std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ ++ auto iter = std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ + return (l.listener == listener && l.data == data); + }); ++ mRegistryListeners.erase(iter, mRegistryListeners.end()); + } + + uint32_t QWaylandDisplay::currentTimeMillisec() +@@ -471,50 +628,9 @@ uint32_t QWaylandDisplay::currentTimeMillisec() + return 0; + } + +-static void +-sync_callback(void *data, struct wl_callback *callback, uint32_t serial) +-{ +- Q_UNUSED(serial) +- bool *done = static_cast(data); +- +- *done = true; +- +- // If the wl_callback done event is received after the condition check in the while loop in +- // forceRoundTrip(), but before the call to processEvents, the call to processEvents may block +- // forever if no more events are posted (eventhough the callback is handled in response to the +- // aboutToBlock signal). Hence, we wake up the event dispatcher so forceRoundTrip may return. +- // (QTBUG-64696) +- if (auto *dispatcher = QThread::currentThread()->eventDispatcher()) +- dispatcher->wakeUp(); +- +- wl_callback_destroy(callback); +-} +- +-static const struct wl_callback_listener sync_listener = { +- sync_callback +-}; +- + void QWaylandDisplay::forceRoundTrip() + { +- // wl_display_roundtrip() works on the main queue only, +- // but we use a separate one, so basically reimplement it here +- int ret = 0; +- bool done = false; +- wl_callback *callback = wl_display_sync(mDisplay); +- wl_callback_add_listener(callback, &sync_listener, &done); +- flushRequests(); +- if (QThread::currentThread()->eventDispatcher()) { +- while (!done && ret >= 0) { +- QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::WaitForMoreEvents); +- ret = wl_display_dispatch_pending(mDisplay); +- } +- } else { +- while (!done && ret >= 0) +- ret = wl_display_dispatch(mDisplay); +- } +- +- if (ret == -1 && !done) +- wl_callback_destroy(callback); ++ wl_display_roundtrip(mDisplay); + } + + bool QWaylandDisplay::supportsWindowDecoration() const +@@ -578,14 +694,10 @@ void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevic + if (mLastKeyboardFocus == keyboardFocus) + return; + +- if (mWaylandIntegration->mShellIntegration) { +- mWaylandIntegration->mShellIntegration->handleKeyboardFocusChanged(keyboardFocus, mLastKeyboardFocus); +- } else { +- if (keyboardFocus) +- handleWindowActivated(keyboardFocus); +- if (mLastKeyboardFocus) +- handleWindowDeactivated(mLastKeyboardFocus); +- } ++ if (keyboardFocus) ++ handleWindowActivated(keyboardFocus); ++ if (mLastKeyboardFocus) ++ handleWindowDeactivated(mLastKeyboardFocus); + + mLastKeyboardFocus = keyboardFocus; + } +@@ -604,6 +716,19 @@ void QWaylandDisplay::handleWaylandSync() + QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window(); + if (activeWindow != QGuiApplication::focusWindow()) + QWindowSystemInterface::handleWindowActivated(activeWindow); ++ ++ if (!activeWindow) { ++ if (lastInputDevice()) { ++#if QT_CONFIG(clipboard) ++ if (auto *dataDevice = lastInputDevice()->dataDevice()) ++ dataDevice->invalidateSelectionOffer(); ++#endif ++#if QT_CONFIG(wayland_client_primary_selection) ++ if (auto *device = lastInputDevice()->primarySelectionDevice()) ++ device->invalidateSelectionOffer(); ++#endif ++ } ++ } + } + + const wl_callback_listener QWaylandDisplay::syncCallbackListener = { +@@ -630,6 +755,13 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const + return mInputDevices.isEmpty() ? 0 : mInputDevices.first(); + } + ++bool QWaylandDisplay::isKeyboardAvailable() const ++{ ++ return std::any_of( ++ mInputDevices.constBegin(), mInputDevices.constEnd(), ++ [this](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; }); ++} ++ + #if QT_CONFIG(cursor) + + QWaylandCursor *QWaylandDisplay::waylandCursor() +@@ -656,6 +788,8 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p + + } // namespace QtWaylandClient + ++#include "qwaylanddisplay.moc" ++ + QT_END_NAMESPACE + + #include "moc_qwaylanddisplay_p.cpp" +diff --git a/qtwayland/src/client/qwaylanddisplay_p.h b/qtwayland/src/client/qwaylanddisplay_p.h +index 1bad8b67..cf91b924 100644 +--- a/qtwayland/src/client/qwaylanddisplay_p.h ++++ b/qtwayland/src/client/qwaylanddisplay_p.h +@@ -111,6 +111,7 @@ class QWaylandSurface; + class QWaylandShellIntegration; + class QWaylandCursor; + class QWaylandCursorTheme; ++class EventThread; + + typedef void (*RegistryListener)(void *data, + struct wl_registry *registry, +@@ -122,15 +123,11 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandDisplay : public QObject, public QtWayland + Q_OBJECT + + public: +- struct FrameQueue { +- FrameQueue(wl_event_queue *q = nullptr) : queue(q), mutex(new QMutex) {} +- wl_event_queue *queue; +- QMutex *mutex; +- }; +- + QWaylandDisplay(QWaylandIntegration *waylandIntegration); + ~QWaylandDisplay(void) override; + ++ void initialize(); ++ + #if QT_CONFIG(xkbcommon) + struct xkb_context *xkbContext() const { return mXkbContext.get(); } + #endif +@@ -214,11 +211,11 @@ public: + void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice); + void handleWindowDestroyed(QWaylandWindow *window); + +- wl_event_queue *createEventQueue(); +- FrameQueue createFrameQueue(); +- void destroyFrameQueue(const FrameQueue &q); +- void dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout = -1); ++ wl_event_queue *frameEventQueue() { return m_frameEventQueue; }; ++ ++ bool isKeyboardAvailable() const; + ++ void initEventThread(); + public slots: + void blockingReadEvents(); + void flushRequests(); +@@ -241,6 +238,9 @@ private: + }; + + struct wl_display *mDisplay = nullptr; ++ QScopedPointer m_eventThread; ++ wl_event_queue *m_frameEventQueue = nullptr; ++ QScopedPointer m_frameEventQueueThread; + QtWayland::wl_compositor mCompositor; + QScopedPointer mShm; + QList mWaitingScreens; +@@ -279,11 +279,9 @@ private: + QWaylandInputDevice *mLastInputDevice = nullptr; + QPointer mLastInputWindow; + QPointer mLastKeyboardFocus; +- QVector mActiveWindows; +- QVector mExternalQueues; ++ QList mActiveWindows; + struct wl_callback *mSyncCallback = nullptr; + static const wl_callback_listener syncCallbackListener; +- QReadWriteLock m_frameQueueLock; + + bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull(); + +diff --git a/qtwayland/src/client/qwaylanddnd.cpp b/qtwayland/src/client/qwaylanddnd.cpp +index 6535aa16..7c53f5fa 100644 +--- a/qtwayland/src/client/qwaylanddnd.cpp ++++ b/qtwayland/src/client/qwaylanddnd.cpp +@@ -66,7 +66,7 @@ void QWaylandDrag::startDrag() + { + QBasicDrag::startDrag(); + QWaylandWindow *icon = static_cast(shapedPixmapWindow()->handle()); +- if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) { ++ if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), drag()->supportedActions(), icon)) { + icon->addAttachOffset(-drag()->hotSpot()); + } else { + // Cancelling immediately does not work, since the event loop for QDrag::exec is started +@@ -80,6 +80,9 @@ void QWaylandDrag::cancel() + QBasicDrag::cancel(); + + m_display->currentInputDevice()->dataDevice()->cancelDrag(); ++ ++ if (drag()) ++ drag()->deleteLater(); + } + + void QWaylandDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) +@@ -103,33 +106,41 @@ void QWaylandDrag::endDrag() + m_display->currentInputDevice()->handleEndDrag(); + } + +-void QWaylandDrag::updateTarget(const QString &mimeType) ++void QWaylandDrag::setResponse(bool accepted) + { +- setCanDrop(!mimeType.isEmpty()); +- +- if (canDrop()) { +- updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); +- } else { +- updateCursor(Qt::IgnoreAction); +- } ++ // This method is used for old DataDevices where the drag action is not communicated ++ Qt::DropAction action = defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers()); ++ setResponse(QPlatformDropQtResponse(accepted, action)); + } + +-void QWaylandDrag::setResponse(const QPlatformDragQtResponse &response) ++void QWaylandDrag::setResponse(const QPlatformDropQtResponse &response) + { + setCanDrop(response.isAccepted()); + + if (canDrop()) { +- updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); ++ updateCursor(response.acceptedAction()); + } else { + updateCursor(Qt::IgnoreAction); + } + } + +-void QWaylandDrag::finishDrag(const QPlatformDropQtResponse &response) ++void QWaylandDrag::setDropResponse(const QPlatformDropQtResponse &response) + { + setExecutedDropAction(response.acceptedAction()); ++} ++ ++void QWaylandDrag::finishDrag() ++{ + QKeyEvent event(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); + eventFilter(shapedPixmapWindow(), &event); ++ ++ if (drag()) ++ drag()->deleteLater(); ++} ++ ++bool QWaylandDrag::ownsDragObject() const ++{ ++ return true; + } + + } +diff --git a/qtwayland/src/client/qwaylanddnd_p.h b/qtwayland/src/client/qwaylanddnd_p.h +index 474fe2ab..46f629ac 100644 +--- a/qtwayland/src/client/qwaylanddnd_p.h ++++ b/qtwayland/src/client/qwaylanddnd_p.h +@@ -71,9 +71,10 @@ public: + QWaylandDrag(QWaylandDisplay *display); + ~QWaylandDrag() override; + +- void updateTarget(const QString &mimeType); +- void setResponse(const QPlatformDragQtResponse &response); +- void finishDrag(const QPlatformDropQtResponse &response); ++ void setResponse(bool accepted); ++ void setResponse(const QPlatformDropQtResponse &response); ++ void setDropResponse(const QPlatformDropQtResponse &response); ++ void finishDrag(); + + protected: + void startDrag() override; +@@ -82,6 +83,7 @@ protected: + void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; + void endDrag() override; + ++ bool ownsDragObject() const override; + + private: + QWaylandDisplay *m_display = nullptr; +diff --git a/qtwayland/src/client/qwaylandinputcontext.cpp b/qtwayland/src/client/qwaylandinputcontext.cpp +index 47696a6a..9435e961 100644 +--- a/qtwayland/src/client/qwaylandinputcontext.cpp ++++ b/qtwayland/src/client/qwaylandinputcontext.cpp +@@ -93,9 +93,14 @@ void QWaylandTextInput::reset() + void QWaylandTextInput::commit() + { + if (QObject *o = QGuiApplication::focusObject()) { +- QInputMethodEvent event; +- event.setCommitString(m_preeditCommit); +- QCoreApplication::sendEvent(o, &event); ++ if (!m_preeditCommit.isEmpty()) { ++ ++ QInputMethodEvent event; ++ event.setCommitString(m_preeditCommit); ++ m_preeditCommit = QString(); ++ ++ QCoreApplication::sendEvent(o, &event); ++ } + } + + reset(); +diff --git a/qtwayland/src/client/qwaylandinputdevice.cpp b/qtwayland/src/client/qwaylandinputdevice.cpp +index b0e9692b..ab978d3f 100644 +--- a/qtwayland/src/client/qwaylandinputdevice.cpp ++++ b/qtwayland/src/client/qwaylandinputdevice.cpp +@@ -310,8 +310,7 @@ void QWaylandInputDevice::Pointer::updateCursor() + auto shape = seat()->mCursor.shape; + + if (shape == Qt::BlankCursor) { +- if (mCursor.surface) +- mCursor.surface->hide(); ++ getOrCreateCursorSurface()->hide(); + return; + } + +@@ -846,7 +845,7 @@ void QWaylandInputDevice::Pointer::releaseButtons() + mButtons = Qt::NoButton; + + if (auto *window = focusWindow()) { +- MotionEvent e(focusWindow(), mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers()); ++ ReleaseEvent e(focusWindow(), mParent->mTime, mSurfacePos, mGlobalPos, mButtons, Qt::NoButton, mParent->modifiers()); + window->handleMouse(mParent, e); + } + } +@@ -1304,14 +1303,6 @@ void QWaylandInputDevice::Keyboard::handleFocusDestroyed() + void QWaylandInputDevice::Keyboard::handleFocusLost() + { + mFocus = nullptr; +-#if QT_CONFIG(clipboard) +- if (auto *dataDevice = mParent->dataDevice()) +- dataDevice->invalidateSelectionOffer(); +-#endif +-#if QT_CONFIG(wayland_client_primary_selection) +- if (auto *device = mParent->primarySelectionDevice()) +- device->invalidateSelectionOffer(); +-#endif + mParent->mQDisplay->handleKeyboardFocusChanged(mParent); + mRepeatTimer.stop(); + } +@@ -1400,6 +1391,7 @@ void QWaylandInputDevice::Touch::touch_cancel() + if (touchExt) + touchExt->touchCanceled(); + ++ mFocus = nullptr; + QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice); + } + +diff --git a/qtwayland/src/client/qwaylandintegration.cpp b/qtwayland/src/client/qwaylandintegration.cpp +index d257e2e3..54861600 100644 +--- a/qtwayland/src/client/qwaylandintegration.cpp ++++ b/qtwayland/src/client/qwaylandintegration.cpp +@@ -125,6 +125,9 @@ QWaylandIntegration::QWaylandIntegration() + #endif + + reconfigureInputContext(); ++ ++ QWaylandWindow::fixedToplevelPositions = ++ !qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_FIXED_POSITIONS"); + } + + QWaylandIntegration::~QWaylandIntegration() +@@ -192,14 +195,18 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const + + void QWaylandIntegration::initialize() + { ++ mDisplay->initEventThread(); ++ ++ // Call after eventDispatcher is fully connected, for QWaylandDisplay::forceRoundTrip() ++ mDisplay->initialize(); ++ ++ // But the aboutToBlock() and awake() should be connected after initializePlatform(). ++ // Otherwise the connected flushRequests() may consumes up all events before processEvents starts to wait, ++ // so that processEvents(QEventLoop::WaitForMoreEvents) may be blocked in the forceRoundTrip(). + QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; + QObject::connect(dispatcher, SIGNAL(aboutToBlock()), mDisplay.data(), SLOT(flushRequests())); + QObject::connect(dispatcher, SIGNAL(awake()), mDisplay.data(), SLOT(flushRequests())); + +- int fd = wl_display_get_fd(mDisplay->wl_display()); +- QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data()); +- QObject::connect(sn, SIGNAL(activated(QSocketDescriptor)), mDisplay.data(), SLOT(flushRequests())); +- + // Qt does not support running with no screens + mDisplay->ensureScreen(); + } +@@ -262,6 +269,14 @@ QWaylandDisplay *QWaylandIntegration::display() const + return mDisplay.data(); + } + ++Qt::KeyboardModifiers QWaylandIntegration::queryKeyboardModifiers() const ++{ ++ if (auto *seat = mDisplay->currentInputDevice()) { ++ return seat->modifiers(); ++ } ++ return Qt::NoModifier; ++} ++ + QList QWaylandIntegration::possibleKeys(const QKeyEvent *event) const + { + if (auto *seat = mDisplay->currentInputDevice()) +diff --git a/qtwayland/src/client/qwaylandintegration_p.h b/qtwayland/src/client/qwaylandintegration_p.h +index ff70ae25..73b80658 100644 +--- a/qtwayland/src/client/qwaylandintegration_p.h ++++ b/qtwayland/src/client/qwaylandintegration_p.h +@@ -107,6 +107,8 @@ public: + + QWaylandDisplay *display() const; + ++ Qt::KeyboardModifiers queryKeyboardModifiers() const override; ++ + QList possibleKeys(const QKeyEvent *event) const override; + + QStringList themeNames() const override; +diff --git a/qtwayland/src/client/qwaylandnativeinterface.cpp b/qtwayland/src/client/qwaylandnativeinterface.cpp +index bf54a1a0..9763c312 100644 +--- a/qtwayland/src/client/qwaylandnativeinterface.cpp ++++ b/qtwayland/src/client/qwaylandnativeinterface.cpp +@@ -139,7 +139,7 @@ void *QWaylandNativeInterface::nativeResourceForScreen(const QByteArray &resourc + { + QByteArray lowerCaseResource = resourceString.toLower(); + +- if (lowerCaseResource == "output") ++ if (lowerCaseResource == "output" && !screen->handle()->isPlaceholder()) + return ((QWaylandScreen *) screen->handle())->output(); + + return nullptr; +diff --git a/qtwayland/src/client/qwaylandprimaryselectionv1.cpp b/qtwayland/src/client/qwaylandprimaryselectionv1.cpp +index 7805dd73..dac532b2 100644 +--- a/qtwayland/src/client/qwaylandprimaryselectionv1.cpp ++++ b/qtwayland/src/client/qwaylandprimaryselectionv1.cpp +@@ -54,11 +54,6 @@ QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1 + : zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1))) + , m_display(display) + { +- // Create devices for all seats. +- // This only works if we get the global before all devices +- const auto seats = m_display->inputDevices(); +- for (auto *seat : seats) +- seat->setPrimarySelectionDevice(createDevice(seat)); + } + + QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat) +diff --git a/qtwayland/src/client/qwaylandscreen.cpp b/qtwayland/src/client/qwaylandscreen.cpp +index 6cb337de..5537dafd 100644 +--- a/qtwayland/src/client/qwaylandscreen.cpp ++++ b/qtwayland/src/client/qwaylandscreen.cpp +@@ -60,7 +60,7 @@ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display, + } + + QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) +- : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2)) ++ : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3)) + , m_outputId(id) + , mWaylandDisplay(waylandDisplay) + , mOutputName(QStringLiteral("Screen%1").arg(id)) +@@ -72,7 +72,7 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin + qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor," + << "QScreen may not work correctly"; + mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc. +- mOutputDone = true; // Fake the done event ++ mProcessedEvents |= OutputDoneEvent; // Fake the done event + maybeInitialize(); + } + } +@@ -81,16 +81,29 @@ QWaylandScreen::~QWaylandScreen() + { + if (zxdg_output_v1::isInitialized()) + zxdg_output_v1::destroy(); ++ if (wl_output::isInitialized() && wl_output_get_version(wl_output::object()) >= WL_OUTPUT_RELEASE_SINCE_VERSION) ++ wl_output::release(); ++} ++ ++uint QWaylandScreen::requiredEvents() const ++{ ++ uint ret = OutputDoneEvent; ++ ++ if (mWaylandDisplay->xdgOutputManager()) { ++ ret |= XdgOutputNameEvent; ++ ++ if (mWaylandDisplay->xdgOutputManager()->version() < 3) ++ ret |= XdgOutputDoneEvent; ++ } ++ return ret; + } + + void QWaylandScreen::maybeInitialize() + { + Q_ASSERT(!mInitialized); + +- if (!mOutputDone) +- return; +- +- if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone) ++ const uint requiredEvents = this->requiredEvents(); ++ if ((mProcessedEvents & requiredEvents) != requiredEvents) + return; + + mInitialized = true; +@@ -276,9 +289,8 @@ void QWaylandScreen::output_scale(int32_t factor) + + void QWaylandScreen::output_done() + { +- mOutputDone = true; +- if (zxdg_output_v1::isInitialized() && mWaylandDisplay->xdgOutputManager()->version() >= 3) +- mXdgOutputDone = true; ++ mProcessedEvents |= OutputDoneEvent; ++ + if (mInitialized) { + updateOutputProperties(); + if (zxdg_output_v1::isInitialized()) +@@ -339,7 +351,7 @@ void QWaylandScreen::zxdg_output_v1_done() + if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3)) + qWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor"; + +- mXdgOutputDone = true; ++ mProcessedEvents |= XdgOutputDoneEvent; + if (mInitialized) + updateXdgOutputProperties(); + else +@@ -348,7 +360,11 @@ void QWaylandScreen::zxdg_output_v1_done() + + void QWaylandScreen::zxdg_output_v1_name(const QString &name) + { ++ if (Q_UNLIKELY(mInitialized)) ++ qWarning(lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor"; ++ + mOutputName = name; ++ mProcessedEvents |= XdgOutputNameEvent; + } + + void QWaylandScreen::updateXdgOutputProperties() +diff --git a/qtwayland/src/client/qwaylandscreen_p.h b/qtwayland/src/client/qwaylandscreen_p.h +index df1c94f2..050cfdc0 100644 +--- a/qtwayland/src/client/qwaylandscreen_p.h ++++ b/qtwayland/src/client/qwaylandscreen_p.h +@@ -116,6 +116,13 @@ public: + static QWaylandScreen *fromWlOutput(::wl_output *output); + + private: ++ enum Event : uint { ++ XdgOutputDoneEvent = 0x1, ++ OutputDoneEvent = 0x2, ++ XdgOutputNameEvent = 0x4, ++ }; ++ uint requiredEvents() const; ++ + void output_mode(uint32_t flags, int width, int height, int refresh) override; + void output_geometry(int32_t x, int32_t y, + int32_t width, int32_t height, +@@ -148,8 +155,7 @@ private: + QSize mPhysicalSize; + QString mOutputName; + Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; +- bool mOutputDone = false; +- bool mXdgOutputDone = false; ++ uint mProcessedEvents = 0; + bool mInitialized = false; + + #if QT_CONFIG(cursor) +diff --git a/qtwayland/src/client/qwaylandshmbackingstore.cpp b/qtwayland/src/client/qwaylandshmbackingstore.cpp +index dc7ff670..145f933b 100644 +--- a/qtwayland/src/client/qwaylandshmbackingstore.cpp ++++ b/qtwayland/src/client/qwaylandshmbackingstore.cpp +@@ -52,6 +52,7 @@ + + #include + ++#include + #include + #include + +@@ -61,6 +62,9 @@ + # ifndef MFD_CLOEXEC + # define MFD_CLOEXEC 0x0001U + # endif ++# ifndef MFD_ALLOW_SEALING ++# define MFD_ALLOW_SEALING 0x0002U ++# endif + #endif + + QT_BEGIN_NAMESPACE +@@ -68,14 +72,16 @@ QT_BEGIN_NAMESPACE + namespace QtWaylandClient { + + QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, +- const QSize &size, QImage::Format format, int scale) ++ const QSize &size, QImage::Format format, qreal scale) + { + int stride = size.width() * 4; + int alloc = stride * size.height(); + int fd = -1; + +-#ifdef SYS_memfd_create +- fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC); ++#if defined(SYS_memfd_create) && defined(F_SEAL_SEAL) ++ fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING); ++ if (fd >= 0) ++ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + #endif + + QScopedPointer filePointer; +@@ -108,7 +114,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, + QWaylandShm* shm = display->shm(); + wl_shm_format wl_format = shm->formatFrom(format); + mImage = QImage(data, size.width(), size.height(), stride, format); +- mImage.setDevicePixelRatio(qreal(scale)); ++ mImage.setDevicePixelRatio(scale); + + mShmPool = wl_shm_create_pool(shm->object(), fd, alloc); + init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(), +@@ -180,8 +186,6 @@ void QWaylandShmBackingStore::beginPaint(const QRegion ®ion) + mPainting = true; + ensureSize(); + +- waylandWindow()->setCanResize(false); +- + if (mBackBuffer->image()->hasAlphaChannel()) { + QPainter p(paintDevice()); + p.setCompositionMode(QPainter::CompositionMode_Source); +@@ -196,7 +200,6 @@ void QWaylandShmBackingStore::endPaint() + mPainting = false; + if (mPendingFlush) + flush(window(), mPendingRegion, QPoint()); +- waylandWindow()->setCanResize(true); + } + + void QWaylandShmBackingStore::ensureSize() +@@ -271,7 +274,7 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) + void QWaylandShmBackingStore::resize(const QSize &size) + { + QMargins margins = windowDecorationMargins(); +- int scale = waylandWindow()->scale(); ++ qreal scale = waylandWindow()->scale(); + QSize sizeWithMargins = (size + QSize(margins.left()+margins.right(),margins.top()+margins.bottom())) * scale; + + // We look for a free buffer to draw into. If the buffer is not the last buffer we used, +diff --git a/qtwayland/src/client/qwaylandshmbackingstore_p.h b/qtwayland/src/client/qwaylandshmbackingstore_p.h +index e01632da..f3fae438 100644 +--- a/qtwayland/src/client/qwaylandshmbackingstore_p.h ++++ b/qtwayland/src/client/qwaylandshmbackingstore_p.h +@@ -71,7 +71,7 @@ class QWaylandWindow; + class Q_WAYLAND_CLIENT_EXPORT QWaylandShmBuffer : public QWaylandBuffer { + public: + QWaylandShmBuffer(QWaylandDisplay *display, +- const QSize &size, QImage::Format format, int scale = 1); ++ const QSize &size, QImage::Format format, qreal scale = 1); + ~QWaylandShmBuffer() override; + QSize size() const override { return mImage.size(); } + int scale() const override { return int(mImage.devicePixelRatio()); } +diff --git a/qtwayland/src/client/qwaylandwindow.cpp b/qtwayland/src/client/qwaylandwindow.cpp +index d57094a7..7a9bccc1 100644 +--- a/qtwayland/src/client/qwaylandwindow.cpp ++++ b/qtwayland/src/client/qwaylandwindow.cpp +@@ -76,7 +76,6 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr; + QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) + : QPlatformWindow(window) + , mDisplay(display) +- , mFrameQueue(mDisplay->createFrameQueue()) + , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) + { + { +@@ -95,9 +94,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) + + QWaylandWindow::~QWaylandWindow() + { +- mDisplay->destroyFrameQueue(mFrameQueue); +- mDisplay->handleWindowDestroyed(this); +- + delete mWindowDecoration; + + if (mSurface) +@@ -189,7 +185,7 @@ void QWaylandWindow::initWindow() + // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale() + // to inform the compositor that high-resolution buffers will be provided. + if (mDisplay->compositorVersion() >= 3) +- mSurface->set_buffer_scale(scale()); ++ mSurface->set_buffer_scale(mScale); + + if (QScreen *s = window()->screen()) + setOrientationMask(s->orientationUpdateMask()); +@@ -204,6 +200,8 @@ void QWaylandWindow::initWindow() + mShellSurface->requestWindowStates(window()->windowStates()); + handleContentOrientationChange(window()->contentOrientation()); + mFlags = window()->flags(); ++ ++ mSurface->commit(); + } + + void QWaylandWindow::initializeWlSurface() +@@ -243,6 +241,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const + + void QWaylandWindow::reset() + { ++ closeChildPopups(); + delete mShellSurface; + mShellSurface = nullptr; + delete mSubSurfaceWindow; +@@ -255,17 +254,22 @@ void QWaylandWindow::reset() + mSurface.reset(); + } + +- if (mFrameCallback) { +- wl_callback_destroy(mFrameCallback); +- mFrameCallback = nullptr; +- } ++ { ++ QMutexLocker lock(&mFrameSyncMutex); ++ if (mFrameCallback) { ++ wl_callback_destroy(mFrameCallback); ++ mFrameCallback = nullptr; ++ } + +- mFrameCallbackElapsedTimer.invalidate(); +- mWaitingForFrameCallback = false; ++ mFrameCallbackElapsedTimer.invalidate(); ++ mWaitingForFrameCallback = false; ++ } + mFrameCallbackTimedOut = false; + + mMask = QRegion(); + mQueuedBuffer = nullptr; ++ ++ mDisplay->handleWindowDestroyed(this); + } + + QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface) +@@ -351,19 +355,25 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect) + } + } + +-void QWaylandWindow::setGeometry(const QRect &rect) ++void QWaylandWindow::setGeometry(const QRect &r) + { ++ auto rect = r; ++ if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup ++ && window()->type() != Qt::ToolTip) { ++ rect.moveTo(screen()->geometry().topLeft()); ++ } + setGeometry_helper(rect); + + if (window()->isVisible() && rect.isValid()) { + if (mWindowDecoration) + mWindowDecoration->update(); + +- if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) ++ if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) { ++ QMutexLocker lock(&mResizeLock); + mResizeDirty = true; +- else ++ } else { + QWindowSystemInterface::handleGeometryChange(window(), geometry()); +- ++ } + mSentInitialResize = true; + } + QRect exposeGeometry(QPoint(), geometry().size()); +@@ -374,7 +384,7 @@ void QWaylandWindow::setGeometry(const QRect &rect) + mShellSurface->setWindowGeometry(windowContentGeometry()); + + if (isOpaque() && mMask.isEmpty()) +- setOpaqueArea(rect); ++ setOpaqueArea(QRect(QPoint(0, 0), rect.size())); + } + + void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) +@@ -399,21 +409,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect) + mLastExposeGeometry = rect; + } + +- +-static QVector> activePopups; +- +-void QWaylandWindow::closePopups(QWaylandWindow *parent) +-{ +- while (!activePopups.isEmpty()) { +- auto popup = activePopups.takeLast(); +- if (popup.isNull()) +- continue; +- if (popup.data() == parent) +- return; +- popup->reset(); +- } +-} +- + QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const + { + QReadLocker lock(&mSurfaceLock); +@@ -433,10 +428,7 @@ void QWaylandWindow::setVisible(bool visible) + lastVisible = visible; + + if (visible) { +- if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip) +- activePopups << this; + initWindow(); +- mDisplay->flushRequests(); + + setGeometry(windowGeometry()); + // Don't flush the events here, or else the newly visible window may start drawing, but since +@@ -444,7 +436,6 @@ void QWaylandWindow::setVisible(bool visible) + // QWaylandShmBackingStore::beginPaint(). + } else { + sendExposeEvent(QRect()); +- closePopups(this); + reset(); + } + } +@@ -487,8 +478,6 @@ void QWaylandWindow::setMask(const QRegion &mask) + if (isOpaque()) + setOpaqueArea(mMask); + } +- +- mSurface->commit(); + } + + void QWaylandWindow::applyConfigureWhenPossible() +@@ -556,12 +545,12 @@ void QWaylandWindow::sendRecursiveExposeEvent() + + void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) + { +- Q_ASSERT(!buffer->committed()); + QReadLocker locker(&mSurfaceLock); + if (mSurface == nullptr) + return; + + if (buffer) { ++ Q_ASSERT(!buffer->committed()); + handleUpdate(); + buffer->setBusy(); + +@@ -583,7 +572,16 @@ void QWaylandWindow::damage(const QRect &rect) + if (mSurface == nullptr) + return; + +- mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); ++ const qreal s = scale(); ++ if (mDisplay->compositorVersion() >= 4) { ++ const QRect bufferRect = ++ QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()) ++ .toAlignedRect(); ++ mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(), ++ bufferRect.height()); ++ } else { ++ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); ++ } + } + + void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage) +@@ -619,8 +617,19 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) + return; + + attachOffset(buffer); +- for (const QRect &rect: damage) +- mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); ++ if (mDisplay->compositorVersion() >= 4) { ++ const qreal s = scale(); ++ for (const QRect &rect : damage) { ++ const QRect bufferRect = ++ QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()) ++ .toAlignedRect(); ++ mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(), ++ bufferRect.height()); ++ } ++ } else { ++ for (const QRect &rect: damage) ++ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); ++ } + Q_ASSERT(!buffer->committed()); + buffer->setCommitted(); + mSurface->commit(); +@@ -635,42 +644,53 @@ void QWaylandWindow::commit() + + const wl_callback_listener QWaylandWindow::callbackListener = { + [](void *data, wl_callback *callback, uint32_t time) { +- Q_UNUSED(callback); + Q_UNUSED(time); + auto *window = static_cast(data); +- window->handleFrameCallback(); ++ window->handleFrameCallback(callback); + } + }; + +-void QWaylandWindow::handleFrameCallback() ++void QWaylandWindow::handleFrameCallback(wl_callback* callback) + { ++ QMutexLocker locker(&mFrameSyncMutex); ++ if (!mFrameCallback) { ++ // This means the callback is already unset by QWaylandWindow::reset. ++ // The wl_callback object will be destroyed there too. ++ return; ++ } ++ Q_ASSERT(callback == mFrameCallback); ++ wl_callback_destroy(callback); ++ mFrameCallback = nullptr; ++ + mWaitingForFrameCallback = false; + mFrameCallbackElapsedTimer.invalidate(); + + // The rest can wait until we can run it on the correct thread +- if (!mWaitingForUpdateDelivery) { +- auto doHandleExpose = [this]() { +- bool wasExposed = isExposed(); +- mFrameCallbackTimedOut = false; +- if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? +- sendExposeEvent(QRect(QPoint(), geometry().size())); +- if (wasExposed && hasPendingUpdateRequest()) +- deliverUpdateRequest(); +- +- mWaitingForUpdateDelivery = false; +- }; ++ auto doHandleExpose = [this]() { ++ mWaitingForUpdateDelivery.storeRelease(false); ++ bool wasExposed = isExposed(); ++ mFrameCallbackTimedOut = false; ++ if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? ++ sendExposeEvent(QRect(QPoint(), geometry().size())); ++ if (wasExposed && hasPendingUpdateRequest()) ++ deliverUpdateRequest(); ++ }; + ++ if (mWaitingForUpdateDelivery.testAndSetAcquire(false, true)) { + // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() + // in the single-threaded case. +- mWaitingForUpdateDelivery = true; + QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); + } ++ ++ mFrameSyncWait.notify_all(); + } + + bool QWaylandWindow::waitForFrameSync(int timeout) + { +- QMutexLocker locker(mFrameQueue.mutex); +- mDisplay->dispatchQueueWhile(mFrameQueue.queue, [&]() { return mWaitingForFrameCallback; }, timeout); ++ QMutexLocker locker(&mFrameSyncMutex); ++ ++ QDeadlineTimer deadline(timeout); ++ while (mWaitingForFrameCallback && mFrameSyncWait.wait(&mFrameSyncMutex, deadline)) { } + + if (mWaitingForFrameCallback) { + qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; +@@ -772,8 +792,6 @@ void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orient + Q_UNREACHABLE(); + } + mSurface->set_buffer_transform(transform); +- // set_buffer_transform is double buffered, we need to commit. +- mSurface->commit(); + } + + void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask) +@@ -1032,8 +1050,17 @@ void QWaylandWindow::handleScreensChanged() + if (newScreen == mLastReportedScreen) + return; + ++ if (!newScreen->isPlaceholder() && !newScreen->QPlatformScreen::screen()) ++ mDisplay->forceRoundTrip(); + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); + mLastReportedScreen = newScreen; ++ if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup ++ && window()->type() != Qt::ToolTip ++ && geometry().topLeft() != newScreen->geometry().topLeft()) { ++ auto geometry = this->geometry(); ++ geometry.moveTo(newScreen->geometry().topLeft()); ++ setGeometry(geometry); ++ } + + int scale = newScreen->isPlaceholder() ? 1 : static_cast(newScreen)->scale(); + if (scale != mScale) { +@@ -1087,14 +1114,14 @@ bool QWaylandWindow::isActive() const + return mDisplay->isWindowActivated(this); + } + +-int QWaylandWindow::scale() const ++qreal QWaylandWindow::scale() const + { +- return mScale; ++ return devicePixelRatio(); + } + + qreal QWaylandWindow::devicePixelRatio() const + { +- return mScale; ++ return qreal(mScale); + } + + bool QWaylandWindow::setMouseGrabEnabled(bool grab) +@@ -1108,10 +1135,18 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab) + return true; + } + ++Qt::WindowStates QWaylandWindow::windowStates() const ++{ ++ return mLastReportedWindowStates; ++} ++ + void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) + { + createDecoration(); +- QWindowSystemInterface::handleWindowStateChanged(window(), states, mLastReportedWindowStates); ++ Qt::WindowStates statesWithoutActive = states & ~Qt::WindowActive; ++ Qt::WindowStates lastStatesWithoutActive = mLastReportedWindowStates & ~Qt::WindowActive; ++ QWindowSystemInterface::handleWindowStateChanged(window(), statesWithoutActive, ++ lastStatesWithoutActive); + mLastReportedWindowStates = states; + } + +@@ -1153,19 +1188,24 @@ void QWaylandWindow::timerEvent(QTimerEvent *event) + if (event->timerId() != mFrameCallbackCheckIntervalTimerId) + return; + +- bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); +- if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { +- killTimer(mFrameCallbackCheckIntervalTimerId); +- mFrameCallbackCheckIntervalTimerId = -1; +- } +- if (mFrameCallbackElapsedTimer.isValid() && callbackTimerExpired) { +- mFrameCallbackElapsedTimer.invalidate(); ++ { ++ QMutexLocker lock(&mFrameSyncMutex); + +- qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; +- mFrameCallbackTimedOut = true; +- mWaitingForUpdate = false; +- sendExposeEvent(QRect()); ++ bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); ++ if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { ++ killTimer(mFrameCallbackCheckIntervalTimerId); ++ mFrameCallbackCheckIntervalTimerId = -1; ++ } ++ if (!mFrameCallbackElapsedTimer.isValid() || !callbackTimerExpired) { ++ return; ++ } ++ mFrameCallbackElapsedTimer.invalidate(); + } ++ ++ qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; ++ mFrameCallbackTimedOut = true; ++ mWaitingForUpdate = false; ++ sendExposeEvent(QRect()); + } + + void QWaylandWindow::requestUpdate() +@@ -1174,8 +1214,11 @@ void QWaylandWindow::requestUpdate() + Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA + + // If we have a frame callback all is good and will be taken care of there +- if (mWaitingForFrameCallback) +- return; ++ { ++ QMutexLocker locker(&mFrameSyncMutex); ++ if (mWaitingForFrameCallback) ++ return; ++ } + + // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet + // This is a somewhat redundant behavior and might indicate a bug in the calling code, so log +@@ -1188,7 +1231,12 @@ void QWaylandWindow::requestUpdate() + // so use invokeMethod to delay the delivery a bit. + QMetaObject::invokeMethod(this, [this] { + // Things might have changed in the meantime +- if (hasPendingUpdateRequest() && !mWaitingForFrameCallback) ++ { ++ QMutexLocker locker(&mFrameSyncMutex); ++ if (mWaitingForFrameCallback) ++ return; ++ } ++ if (hasPendingUpdateRequest()) + deliverUpdateRequest(); + }, Qt::QueuedConnection); + } +@@ -1199,19 +1247,18 @@ void QWaylandWindow::requestUpdate() + void QWaylandWindow::handleUpdate() + { + qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread(); ++ + // TODO: Should sync subsurfaces avoid requesting frame callbacks? + QReadLocker lock(&mSurfaceLock); + if (!mSurface) + return; + +- if (mFrameCallback) { +- wl_callback_destroy(mFrameCallback); +- mFrameCallback = nullptr; +- } ++ QMutexLocker locker(&mFrameSyncMutex); ++ if (mWaitingForFrameCallback) ++ return; + +- QMutexLocker locker(mFrameQueue.mutex); + struct ::wl_surface *wrappedSurface = reinterpret_cast(wl_proxy_create_wrapper(mSurface->object())); +- wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mFrameQueue.queue); ++ wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mDisplay->frameEventQueue()); + mFrameCallback = wl_surface_frame(wrappedSurface); + wl_proxy_wrapper_destroy(wrappedSurface); + wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this); +@@ -1221,6 +1268,8 @@ void QWaylandWindow::handleUpdate() + // Start a timer for handling the case when the compositor stops sending frame callbacks. + if (mFrameCallbackTimeout > 0) { + QMetaObject::invokeMethod(this, [this] { ++ QMutexLocker locker(&mFrameSyncMutex); ++ + if (mWaitingForFrameCallback) { + if (mFrameCallbackCheckIntervalTimerId < 0) + mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout); +@@ -1281,6 +1330,20 @@ void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea) + wl_region_destroy(region); + } + ++void QWaylandWindow::addChildPopup(QWaylandWindow *surface) { ++ mChildPopups.append(surface); ++} ++ ++void QWaylandWindow::removeChildPopup(QWaylandWindow *surface) { ++ mChildPopups.removeAll(surface); ++} ++ ++void QWaylandWindow::closeChildPopups() { ++ while (!mChildPopups.isEmpty()) { ++ auto popup = mChildPopups.takeLast(); ++ popup->reset(); ++ } ++} + } + + QT_END_NAMESPACE +diff --git a/qtwayland/src/client/qwaylandwindow_p.h b/qtwayland/src/client/qwaylandwindow_p.h +index 01337cff..741f9e5c 100644 +--- a/qtwayland/src/client/qwaylandwindow_p.h ++++ b/qtwayland/src/client/qwaylandwindow_p.h +@@ -98,6 +98,9 @@ public: + QWaylandWindow(QWindow *window, QWaylandDisplay *display); + ~QWaylandWindow() override; + ++ // Keep Toplevels position on the top left corner of their screen ++ static inline bool fixedToplevelPositions = true; ++ + virtual WindowType windowType() const = 0; + virtual void ensureSize(); + WId winId() const override; +@@ -148,13 +151,14 @@ public: + void setWindowState(Qt::WindowStates states) override; + void setWindowFlags(Qt::WindowFlags flags) override; + void handleWindowStatesChanged(Qt::WindowStates states); ++ Qt::WindowStates windowStates() const; + + void raise() override; + void lower() override; + + void setMask(const QRegion ®ion) override; + +- int scale() const; ++ qreal scale() const; + qreal devicePixelRatio() const override; + + void requestActivateWindow() override; +@@ -206,6 +210,10 @@ public: + void handleUpdate(); + void deliverUpdateRequest() override; + ++ void addChildPopup(QWaylandWindow* child); ++ void removeChildPopup(QWaylandWindow* child); ++ void closeChildPopups(); ++ + public slots: + void applyConfigure(); + +@@ -215,7 +223,11 @@ signals: + + protected: + QWaylandDisplay *mDisplay = nullptr; ++ ++ // mSurface can be written by the main thread. Other threads should claim a read lock for access ++ mutable QReadWriteLock mSurfaceLock; + QScopedPointer mSurface; ++ + QWaylandShellSurface *mShellSurface = nullptr; + QWaylandSubSurface *mSubSurfaceWindow = nullptr; + QVector mChildren; +@@ -225,13 +237,14 @@ protected: + Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton; + + WId mWindowId; +- bool mWaitingForFrameCallback = false; + bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out +- bool mWaitingForUpdateDelivery = false; + int mFrameCallbackCheckIntervalTimerId = -1; +- QElapsedTimer mFrameCallbackElapsedTimer; +- struct ::wl_callback *mFrameCallback = nullptr; +- QWaylandDisplay::FrameQueue mFrameQueue; ++ QAtomicInt mWaitingForUpdateDelivery = false; ++ ++ bool mWaitingForFrameCallback = false; // Protected by mFrameSyncMutex ++ QElapsedTimer mFrameCallbackElapsedTimer; // Protected by mFrameSyncMutex ++ struct ::wl_callback *mFrameCallback = nullptr; // Protected by mFrameSyncMutex ++ QMutex mFrameSyncMutex; + QWaitCondition mFrameSyncWait; + + // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer +@@ -261,6 +274,8 @@ protected: + QWaylandBuffer *mQueuedBuffer = nullptr; + QRegion mQueuedBufferDamage; + ++ QList> mChildPopups; ++ + private: + void setGeometry_helper(const QRect &rect); + void initWindow(); +@@ -283,12 +298,10 @@ private: + QRect mLastExposeGeometry; + + static const wl_callback_listener callbackListener; +- void handleFrameCallback(); ++ void handleFrameCallback(struct ::wl_callback* callback); + + static QWaylandWindow *mMouseGrab; + +- mutable QReadWriteLock mSurfaceLock; +- + friend class QWaylandSubSurface; + }; + +diff --git a/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h b/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h +index ccad0048..4cc9b3b8 100644 +--- a/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h ++++ b/qtwayland/src/client/shellintegration/qwaylandshellintegration_p.h +@@ -73,11 +73,10 @@ public: + return true; + } + virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0; ++ // kept for binary compat with layer-shell-qt + virtual void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { +- if (newFocus) +- m_display->handleWindowActivated(newFocus); +- if (oldFocus) +- m_display->handleWindowDeactivated(oldFocus); ++ Q_UNUSED(newFocus); ++ Q_UNUSED(oldFocus); + } + virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) { + Q_UNUSED(resource); +diff --git a/qtwayland/src/compositor/configure.json b/qtwayland/src/compositor/configure.json +index 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 e00c28c3..dbe2845a 100644 +--- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp ++++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp +@@ -40,6 +40,7 @@ + #include "qwaylandeglwindow.h" + + #include ++#include + #include "qwaylandglcontext.h" + + #include +@@ -115,6 +116,7 @@ void QWaylandEglWindow::updateSurface(bool create) + } + mOffset = QPoint(); + } else { ++ QReadLocker locker(&mSurfaceLock); + if (m_waylandEglWindow) { + int current_width, current_height; + static bool disableResizeCheck = qgetenv("QT_WAYLAND_DISABLE_RESIZECHECK").toInt(); +@@ -122,14 +124,16 @@ void QWaylandEglWindow::updateSurface(bool create) + if (!disableResizeCheck) { + wl_egl_window_get_attached_size(m_waylandEglWindow, ¤t_width, ¤t_height); + } +- if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height())) { ++ if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height()) || m_requestedSize != sizeWithMargins) { + wl_egl_window_resize(m_waylandEglWindow, sizeWithMargins.width(), sizeWithMargins.height(), mOffset.x(), mOffset.y()); ++ m_requestedSize = sizeWithMargins; + mOffset = QPoint(); + + m_resize = true; + } +- } else if (create && wlSurface()) { +- m_waylandEglWindow = wl_egl_window_create(wlSurface(), sizeWithMargins.width(), sizeWithMargins.height()); ++ } else if (create && mSurface) { ++ m_waylandEglWindow = wl_egl_window_create(mSurface->object(), sizeWithMargins.width(), sizeWithMargins.height()); ++ m_requestedSize = sizeWithMargins; + } + + if (!m_eglSurface && m_waylandEglWindow && create) { +diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h +index 2fccbcea..ad1e5ee9 100644 +--- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h ++++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h +@@ -85,6 +85,7 @@ private: + mutable QOpenGLFramebufferObject *m_contentFBO = nullptr; + + QSurfaceFormat m_format; ++ QSize m_requestedSize; + }; + + } +diff --git a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +index c1f45fa6..bbc63444 100644 +--- a/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp ++++ b/qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +@@ -195,7 +195,7 @@ public: + QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context()); + + QSize surfaceSize = window->surfaceSize(); +- int scale = window->scale() ; ++ qreal scale = window->scale() ; + glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale); + + //Draw Decoration +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp +index 85d25e3c..60bdd491 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp +@@ -47,18 +47,21 @@ QT_BEGIN_NAMESPACE + + namespace QtWaylandClient { + +-QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window) ++QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window) + : QWaylandShellSurface(window) + , QtWayland::xdg_popup_v5(popup) ++ , m_parent(parent) + , m_window(window) + { + if (window->display()->windowExtension()) + m_extendedWindow = new QWaylandExtendedSurface(window); ++ m_parent->addChildPopup(m_window); + } + + QWaylandXdgPopupV5::~QWaylandXdgPopupV5() + { + xdg_popup_destroy(object()); ++ m_parent->removeChildPopup(m_window); + delete m_extendedWindow; + } + +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h +index 7494f6a6..d85f130b 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h +@@ -70,7 +70,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgPopupV5 : public QWaylandShellSurface + { + Q_OBJECT + public: +- QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window); ++ QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window); + ~QWaylandXdgPopupV5() override; + + protected: +@@ -78,6 +78,7 @@ protected: + + private: + QWaylandExtendedSurface *m_extendedWindow = nullptr; ++ QWaylandWindow *m_parent = nullptr; + QWaylandWindow *m_window = nullptr; + }; + +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp +index 7e242c4a..def8452a 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp +@@ -84,7 +84,7 @@ QWaylandXdgPopupV5 *QWaylandXdgShellV5::createXdgPopup(QWaylandWindow *window, Q + int x = position.x() + parentWindow->frameMargins().left(); + int y = position.y() + parentWindow->frameMargins().top(); + +- auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), window); ++ auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), parentWindow, window); + m_popups.append(window); + QObject::connect(popup, &QWaylandXdgPopupV5::destroyed, [this, window](){ + m_popups.removeOne(window); +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp +index 4e25949f..cfc60939 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp +@@ -85,13 +85,6 @@ QWaylandShellSurface *QWaylandXdgShellV5Integration::createShellSurface(QWayland + return m_xdgShell->createXdgSurface(window); + } + +-void QWaylandXdgShellV5Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { +- if (newFocus && qobject_cast(newFocus->shellSurface())) +- m_display->handleWindowActivated(newFocus); +- if (oldFocus && qobject_cast(oldFocus->shellSurface())) +- m_display->handleWindowDeactivated(oldFocus); +-} +- + } + + QT_END_NAMESPACE +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h +index ce6bdb9e..aed88670 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h +@@ -67,7 +67,6 @@ public: + QWaylandXdgShellV5Integration() {} + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; +- void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; + + private: + QScopedPointer m_xdgShell; +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp +index 8c371661..151c78e3 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp +@@ -174,6 +174,7 @@ QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdg + , m_xdgSurface(xdgSurface) + , m_parent(parent) + { ++ m_parent->window()->addChildPopup(m_xdgSurface->window()); + } + + QWaylandXdgSurfaceV6::Popup::~Popup() +@@ -181,6 +182,8 @@ QWaylandXdgSurfaceV6::Popup::~Popup() + if (isInitialized()) + destroy(); + ++ m_parent->window()->removeChildPopup(m_xdgSurface->window()); ++ + if (m_grabbing) { + auto *shell = m_xdgSurface->m_shell; + Q_ASSERT(shell->m_topmostGrabbingPopup == this); +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp +index 03164316..e8da8ba1 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp +@@ -68,20 +68,6 @@ QWaylandShellSurface *QWaylandXdgShellV6Integration::createShellSurface(QWayland + return m_xdgShell->getXdgSurface(window); + } + +-void QWaylandXdgShellV6Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) +-{ +- if (newFocus) { +- auto *xdgSurface = qobject_cast(newFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowActivated(newFocus); +- } +- if (oldFocus && qobject_cast(oldFocus->shellSurface())) { +- auto *xdgSurface = qobject_cast(oldFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowDeactivated(oldFocus); +- } +-} +- + } + + QT_END_NAMESPACE +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h +index 261f8cbb..c1bcd5c6 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h +@@ -65,7 +65,6 @@ public: + QWaylandXdgShellV6Integration() {} + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; +- void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; + + private: + QScopedPointer m_xdgShell; +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index 94ea573e..9c6cbb81 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -67,11 +68,6 @@ QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface) + + QWaylandXdgSurface::Toplevel::~Toplevel() + { +- if (m_applied.states & Qt::WindowActive) { +- QWaylandWindow *window = m_xdgSurface->window(); +- window->display()->handleWindowDeactivated(window); +- } +- + // The protocol spec requires that the decoration object is deleted before xdg_toplevel. + delete m_decoration; + m_decoration = nullptr; +@@ -85,16 +81,15 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() + if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) + m_normalSize = m_xdgSurface->m_window->windowFrameGeometry().size(); + +- if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive)) ++ if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive) ++ && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) + m_xdgSurface->m_window->display()->handleWindowActivated(m_xdgSurface->m_window); + +- if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive)) ++ if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive) ++ && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) + m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window); + +- // TODO: none of the other plugins send WindowActive either, but is it on purpose? +- Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive; +- +- m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive); ++ m_xdgSurface->m_window->handleWindowStatesChanged(m_pending.states); + + if (m_pending.size.isEmpty()) { + // An empty size in the configure means it's up to the client to choose the size +@@ -105,8 +100,6 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() + m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size); + } + +- m_xdgSurface->setSizeHints(); +- + m_applied = m_pending; + qCDebug(lcQpaWayland) << "Applied pending xdg_toplevel configure event:" << m_applied.size << m_applied.states; + } +@@ -203,12 +196,17 @@ QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResi + | ((edges & Qt::RightEdge) ? resize_edge_right : 0)); + } + +-QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, ++QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, + QtWayland::xdg_positioner *positioner) +- : xdg_popup(xdgSurface->get_popup(parent->object(), positioner->object())) +- , m_xdgSurface(xdgSurface) ++ : m_xdgSurface(xdgSurface) ++ , m_parentXdgSurface(qobject_cast(parent->shellSurface())) + , m_parent(parent) + { ++ ++ init(xdgSurface->get_popup(m_parentXdgSurface ? m_parentXdgSurface->object() : nullptr, positioner->object())); ++ if (m_parent) { ++ m_parent->addChildPopup(m_xdgSurface->window()); ++ } + } + + QWaylandXdgSurface::Popup::~Popup() +@@ -216,10 +214,14 @@ QWaylandXdgSurface::Popup::~Popup() + if (isInitialized()) + destroy(); + ++ if (m_parent) { ++ m_parent->removeChildPopup(m_xdgSurface->window()); ++ } ++ + if (m_grabbing) { + auto *shell = m_xdgSurface->m_shell; + Q_ASSERT(shell->m_topmostGrabbingPopup == this); +- shell->m_topmostGrabbingPopup = m_parent->m_popup; ++ shell->m_topmostGrabbingPopup = m_parentXdgSurface ? m_parentXdgSurface->m_popup : nullptr; + m_grabbing = false; + + // Synthesize Qt enter/leave events for popup +@@ -228,8 +230,10 @@ QWaylandXdgSurface::Popup::~Popup() + leave = m_xdgSurface->window()->window(); + QWindowSystemInterface::handleLeaveEvent(leave); + +- if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos())) +- QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos()); ++ if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos())) { ++ const auto pos = m_xdgSurface->window()->display()->waylandCursor()->pos(); ++ QWindowSystemInterface::handleEnterEvent(enter, enter->handle()->mapFromGlobal(pos), pos); ++ } + } + } + +@@ -267,6 +271,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s + m_toplevel->set_parent(parentXdgSurface->m_toplevel->object()); + } + } ++ setSizeHints(); + } + + QWaylandXdgSurface::~QWaylandXdgSurface() +@@ -365,9 +370,6 @@ bool QWaylandXdgSurface::wantsDecorations() const + void QWaylandXdgSurface::propagateSizeHints() + { + setSizeHints(); +- +- if (m_toplevel && m_window) +- m_window->commit(); + } + + void QWaylandXdgSurface::setWindowGeometry(const QRect &rect) +@@ -382,10 +384,10 @@ void QWaylandXdgSurface::setSizeHints() + const int minHeight = qMax(0, m_window->windowMinimumSize().height()); + m_toplevel->set_min_size(minWidth, minHeight); + +- int maxWidth = qMax(0, m_window->windowMaximumSize().width()); ++ int maxWidth = qMax(minWidth, m_window->windowMaximumSize().width()); + if (maxWidth == QWINDOWSIZE_MAX) + maxWidth = 0; +- int maxHeight = qMax(0, m_window->windowMaximumSize().height()); ++ int maxHeight = qMax(minHeight, m_window->windowMaximumSize().height()); + if (maxHeight == QWINDOWSIZE_MAX) + maxHeight = 0; + m_toplevel->set_max_size(maxWidth, maxHeight); +@@ -410,8 +412,6 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) + { + Q_ASSERT(!m_toplevel && !m_popup); + +- auto parentXdgSurface = static_cast(parent->shellSurface()); +- + auto positioner = new QtWayland::xdg_positioner(m_shell->create_positioner()); + // set_popup expects a position relative to the parent + QPoint transientPos = m_window->geometry().topLeft(); // this is absolute +@@ -425,11 +425,10 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) + positioner->set_gravity(QtWayland::xdg_positioner::gravity_bottom_right); + positioner->set_size(m_window->geometry().width(), m_window->geometry().height()); + positioner->set_constraint_adjustment(QtWayland::xdg_positioner::constraint_adjustment_slide_x +- | QtWayland::xdg_positioner::constraint_adjustment_slide_y +- | QtWayland::xdg_positioner::constraint_adjustment_flip_x +- | QtWayland::xdg_positioner::constraint_adjustment_flip_y); +- m_popup = new Popup(this, parentXdgSurface, positioner); ++ | QtWayland::xdg_positioner::constraint_adjustment_slide_y); ++ m_popup = new Popup(this, parent, positioner); + positioner->destroy(); ++ + delete positioner; + } + +@@ -466,8 +465,10 @@ void QWaylandXdgSurface::setGrabPopup(QWaylandWindow *parent, QWaylandInputDevic + if (m_popup && m_popup->m_xdgSurface && m_popup->m_xdgSurface->window()) + enter = m_popup->m_xdgSurface->window()->window(); + +- if (enter) +- QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos()); ++ if (enter) { ++ const auto pos = m_popup->m_xdgSurface->window()->display()->waylandCursor()->pos(); ++ QWindowSystemInterface::handleEnterEvent(enter, enter->handle()->mapFromGlobal(pos), pos); ++ } + } + + void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial) +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +index 96785205..4b518f0a 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +@@ -131,14 +131,15 @@ private: + + class Popup : public QtWayland::xdg_popup { + public: +- Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, QtWayland::xdg_positioner *positioner); ++ Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, QtWayland::xdg_positioner *positioner); + ~Popup() override; + + void grab(QWaylandInputDevice *seat, uint serial); + void xdg_popup_popup_done() override; + + QWaylandXdgSurface *m_xdgSurface = nullptr; +- QWaylandXdgSurface *m_parent = nullptr; ++ QWaylandXdgSurface *m_parentXdgSurface = nullptr; ++ QWaylandWindow *m_parent = nullptr; + bool m_grabbing = false; + }; + +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp +index 8769d971..da0dd6a7 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp +@@ -69,20 +69,6 @@ QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWi + return m_xdgShell->getXdgSurface(window); + } + +-void QWaylandXdgShellIntegration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) +-{ +- if (newFocus) { +- auto *xdgSurface = qobject_cast(newFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowActivated(newFocus); +- } +- if (oldFocus && qobject_cast(oldFocus->shellSurface())) { +- auto *xdgSurface = qobject_cast(oldFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowDeactivated(oldFocus); +- } +-} +- + } + + QT_END_NAMESPACE +diff --git a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h +index b6caa6c9..2f929f98 100644 +--- a/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h ++++ b/qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h +@@ -65,7 +65,6 @@ public: + QWaylandXdgShellIntegration() {} + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; +- void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; + + private: + QScopedPointer m_xdgShell; +diff --git a/qtwayland/src/shared/qwaylandmimehelper.cpp b/qtwayland/src/shared/qwaylandmimehelper.cpp +index c5266ab3..e2fe1928 100644 +--- a/qtwayland/src/shared/qwaylandmimehelper.cpp ++++ b/qtwayland/src/shared/qwaylandmimehelper.cpp +@@ -60,7 +60,7 @@ QByteArray QWaylandMimeHelper::getByteArray(QMimeData *mimeData, const QString & + buf.open(QIODevice::ReadWrite); + QByteArray fmt = "BMP"; + if (mimeType.startsWith(QLatin1String("image/"))) { +- QByteArray imgFmt = mimeType.mid(6).toUpper().toLatin1(); ++ QByteArray imgFmt = mimeType.mid(6).toLower().toLatin1(); + if (QImageWriter::supportedImageFormats().contains(imgFmt)) + fmt = imgFmt; + } +diff --git a/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp b/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp +index 1568b3b9..067410d0 100644 +--- a/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp ++++ b/qtwayland/tests/auto/client/datadevicev1/tst_datadevicev1.cpp +@@ -35,7 +35,7 @@ + + using namespace MockCompositor; + +-constexpr int dataDeviceVersion = 1; ++constexpr int dataDeviceVersion = 3; + + class DataDeviceCompositor : public DefaultCompositor { + public: +diff --git a/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp b/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp +index 9312c2e5..2ea382f1 100644 +--- a/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp ++++ b/qtwayland/tests/auto/client/seatv5/tst_seatv5.cpp +@@ -73,6 +73,7 @@ private slots: + void multiTouch(); + void multiTouchUpAndMotionFrame(); + void tapAndMoveInSameFrame(); ++ void cancelTouch(); + }; + + void tst_seatv5::bindsToSeat() +@@ -646,5 +647,34 @@ void tst_seatv5::tapAndMoveInSameFrame() + QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased); + } + ++void tst_seatv5::cancelTouch() ++{ ++ TouchWindow window; ++ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); ++ ++ exec([=] { ++ auto *t = touch(); ++ auto *c = client(); ++ t->sendDown(xdgToplevel()->surface(), {32, 32}, 1); ++ t->sendFrame(c); ++ t->sendCancel(c); ++ t->sendFrame(c); ++ }); ++ ++ QTRY_VERIFY(!window.m_events.empty()); ++ { ++ auto e = window.m_events.takeFirst(); ++ QCOMPARE(e.type, QEvent::TouchBegin); ++ QCOMPARE(e.touchPointStates, Qt::TouchPointPressed); ++ QCOMPARE(e.touchPoints.length(), 1); ++ QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); ++ } ++ { ++ auto e = window.m_events.takeFirst(); ++ QCOMPARE(e.type, QEvent::TouchCancel); ++ QCOMPARE(e.touchPoints.length(), 0); ++ } ++} ++ + QCOMPOSITOR_TEST_MAIN(tst_seatv5) + #include "tst_seatv5.moc" +diff --git a/qtwayland/tests/auto/client/shared/coreprotocol.cpp b/qtwayland/tests/auto/client/shared/coreprotocol.cpp +index 0d988521..53e12291 100644 +--- a/qtwayland/tests/auto/client/shared/coreprotocol.cpp ++++ b/qtwayland/tests/auto/client/shared/coreprotocol.cpp +@@ -185,6 +185,8 @@ void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource + + if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION) + wl_output::send_done(resource->handle); ++ ++ Q_EMIT outputBound(resource); + } + + // Seat stuff +@@ -451,6 +453,13 @@ void Touch::sendFrame(wl_client *client) + send_frame(r->handle); + } + ++void Touch::sendCancel(wl_client *client) ++{ ++ const auto touchResources = resourceMap().values(client); ++ for (auto *r : touchResources) ++ send_cancel(r->handle); ++} ++ + uint Keyboard::sendEnter(Surface *surface) + { + auto serial = m_seat->m_compositor->nextSerial(); +diff --git a/qtwayland/tests/auto/client/shared/coreprotocol.h b/qtwayland/tests/auto/client/shared/coreprotocol.h +index a1af137a..00c439e1 100644 +--- a/qtwayland/tests/auto/client/shared/coreprotocol.h ++++ b/qtwayland/tests/auto/client/shared/coreprotocol.h +@@ -158,7 +158,7 @@ class WlCompositor : public Global, public QtWaylandServer::wl_compositor + { + Q_OBJECT + public: +- explicit WlCompositor(CoreCompositor *compositor, int version = 3) ++ explicit WlCompositor(CoreCompositor *compositor, int version = 4) + : QtWaylandServer::wl_compositor(compositor->m_display, version) + , m_compositor(compositor) + {} +@@ -273,6 +273,9 @@ public: + OutputData m_data; + int m_version = 1; // TODO: remove on libwayland upgrade + ++Q_SIGNALS: ++ void outputBound(Resource *resource); ++ + protected: + void output_bind_resource(Resource *resource) override; + }; +@@ -364,6 +367,7 @@ public: + uint sendUp(wl_client *client, int id); + void sendMotion(wl_client *client, const QPointF &position, int id); + void sendFrame(wl_client *client); ++ void sendCancel(wl_client *client); + + Seat *m_seat = nullptr; + }; +diff --git a/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp b/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp +index a415cbf5..b1d3d07d 100644 +--- a/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp ++++ b/qtwayland/tests/auto/client/shared_old/mockcompositor.cpp +@@ -342,7 +342,7 @@ Compositor::Compositor(MockCompositor *mockCompositor) + exit(EXIT_FAILURE); + } + +- wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor); ++ wl_global_create(m_display, &wl_compositor_interface, 4, this, bindCompositor); + + m_data_device_manager.reset(new DataDeviceManager(this, m_display)); + +diff --git a/qtwayland/tests/auto/client/shared_old/mocksurface.cpp b/qtwayland/tests/auto/client/shared_old/mocksurface.cpp +index e9df5f90..c3246e4a 100644 +--- a/qtwayland/tests/auto/client/shared_old/mocksurface.cpp ++++ b/qtwayland/tests/auto/client/shared_old/mocksurface.cpp +@@ -125,6 +125,16 @@ void Surface::surface_damage(Resource *resource, + Q_UNUSED(height); + } + ++void Surface::surface_damage_buffer(Resource *resource, ++ int32_t x, int32_t y, int32_t width, int32_t height) ++{ ++ Q_UNUSED(resource); ++ Q_UNUSED(x); ++ Q_UNUSED(y); ++ Q_UNUSED(width); ++ Q_UNUSED(height); ++} ++ + void Surface::surface_frame(Resource *resource, + uint32_t callback) + { +diff --git a/qtwayland/tests/auto/client/shared_old/mocksurface.h b/qtwayland/tests/auto/client/shared_old/mocksurface.h +index 949dc23d..d176837e 100644 +--- a/qtwayland/tests/auto/client/shared_old/mocksurface.h ++++ b/qtwayland/tests/auto/client/shared_old/mocksurface.h +@@ -65,6 +65,8 @@ protected: + struct wl_resource *buffer, int x, int y) override; + void surface_damage(Resource *resource, + int32_t x, int32_t y, int32_t width, int32_t height) override; ++ void surface_damage_buffer(Resource *resource, ++ int32_t x, int32_t y, int32_t width, int32_t height) override; + void surface_frame(Resource *resource, + uint32_t callback) override; + void surface_commit(Resource *resource) override; +diff --git a/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp b/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp +index 80429608..68e8d77a 100644 +--- a/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp ++++ b/qtwayland/tests/auto/client/xdgoutput/tst_xdgoutput.cpp +@@ -55,6 +55,7 @@ private slots: + void primaryScreen(); + void overrideGeometry(); + void changeGeometry(); ++ void outputCreateEnterRace(); + }; + + void tst_xdgoutput::cleanup() +@@ -134,5 +135,39 @@ void tst_xdgoutput::changeGeometry() + exec([=] { remove(output(1)); }); + } + ++void tst_xdgoutput::outputCreateEnterRace() ++{ ++ m_config.autoConfigure = true; ++ m_config.autoEnter = false; ++ QRasterWindow window; ++ QSignalSpy screenChanged(&window, &QWindow::screenChanged); ++ window.resize(400, 320); ++ window.show(); ++ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); ++ exec([=] { xdgToplevel()->surface()->sendEnter(output(0));}); ++ ++ QTRY_COMPARE(QGuiApplication::screens().size(), 1); ++ QScreen *primaryScreen = QGuiApplication::screens().first(); ++ QCOMPARE(window.screen(), primaryScreen); ++ ++ auto *out = exec([=] { ++ return add(); ++ }); ++ ++ // In Compositor Thread ++ connect(out, &Output::outputBound, this, [this](QtWaylandServer::wl_output::Resource *resource){ ++ auto surface = xdgToplevel()->surface(); ++ surface->sendLeave(output(0)); ++ surface->QtWaylandServer::wl_surface::send_enter(surface->resource()->handle, resource->handle); ++ }, Qt::DirectConnection); ++ ++ QTRY_COMPARE(QGuiApplication::screens().size(), 2); ++ QTRY_COMPARE(window.screen(), QGuiApplication::screens()[1]); ++ ++ exec([=] { remove(out); }); ++ m_config.autoConfigure = false; ++ m_config.autoEnter = true; ++} ++ + QCOMPOSITOR_TEST_MAIN(tst_xdgoutput) + #include "tst_xdgoutput.moc" +diff --git a/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp b/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp +index 2277bbb8..afbeef53 100644 +--- a/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp ++++ b/qtwayland/tests/auto/client/xdgshell/tst_xdgshell.cpp +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + using namespace MockCompositor; + +@@ -45,6 +46,7 @@ private slots: + void configureStates(); + void popup(); + void tooltipOnPopup(); ++ void tooltipAndSiblingPopup(); + void switchPopups(); + void hidePopupParent(); + void pongs(); +@@ -138,6 +140,7 @@ void tst_xdgshell::configureSize() + + void tst_xdgshell::configureStates() + { ++ QVERIFY(qputenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", "0")); + QRasterWindow window; + window.resize(64, 48); + window.show(); +@@ -154,9 +157,12 @@ void tst_xdgshell::configureStates() + // Toplevel windows don't know their position on xdg-shell + // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled + +-// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue); +-// QVERIFY(window.isActive()); +- QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly ++ // window.windowstate() is driven by keyboard focus, however for decorations we want to follow ++ // XDGShell this is internal to QtWayland so it is queried directly ++ auto waylandWindow = static_cast(window.handle()); ++ Q_ASSERT(waylandWindow); ++ QTRY_VERIFY(waylandWindow->windowStates().testFlag( ++ Qt::WindowActive)); // Just make sure it eventually get's set correctly + + const QSize screenSize(640, 480); + const uint maximizedSerial = exec([=] { +@@ -186,6 +192,7 @@ void tst_xdgshell::configureStates() + QCOMPARE(window.windowStates(), Qt::WindowNoState); + QCOMPARE(window.frameGeometry().size(), windowedSize); + // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled ++ QVERIFY(qunsetenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT")); + } + + void tst_xdgshell::popup() +@@ -340,6 +347,92 @@ void tst_xdgshell::tooltipOnPopup() + QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); + } + ++void tst_xdgshell::tooltipAndSiblingPopup() ++{ ++ class ToolTip : public QRasterWindow { ++ public: ++ explicit ToolTip(QWindow *parent) { ++ setTransientParent(parent); ++ setFlags(Qt::ToolTip); ++ resize(100, 100); ++ show(); ++ } ++ void mousePressEvent(QMouseEvent *event) override { ++ QRasterWindow::mousePressEvent(event); ++ m_popup = new QRasterWindow; ++ m_popup->setTransientParent(transientParent()); ++ m_popup->setFlags(Qt::Popup); ++ m_popup->resize(100, 100); ++ m_popup->show(); ++ } ++ ++ QRasterWindow *m_popup = nullptr; ++ }; ++ ++ class Window : public QRasterWindow { ++ public: ++ void mousePressEvent(QMouseEvent *event) override { ++ QRasterWindow::mousePressEvent(event); ++ m_tooltip = new ToolTip(this); ++ } ++ ToolTip *m_tooltip = nullptr; ++ }; ++ ++ Window window; ++ window.resize(200, 200); ++ window.show(); ++ ++ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); ++ exec([=] { xdgToplevel()->sendCompleteConfigure(); }); ++ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); ++ ++ exec([=] { ++ auto *surface = xdgToplevel()->surface(); ++ auto *p = pointer(); ++ auto *c = client(); ++ p->sendEnter(surface, {100, 100}); ++ p->sendFrame(c); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); ++ p->sendFrame(c); ++ p->sendLeave(surface); ++ p->sendFrame(c); ++ }); ++ ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup()); ++ exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); ++ QCOMPOSITOR_TRY_VERIFY(!xdgPopup()->m_grabbed); ++ ++ exec([=] { ++ auto *surface = xdgPopup()->surface(); ++ auto *p = pointer(); ++ auto *c = client(); ++ p->sendEnter(surface, {100, 100}); ++ p->sendFrame(c); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); ++ p->sendFrame(c); ++ }); ++ ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); ++ exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_grabbed); ++ ++ // Close the middle tooltip (it should not close the sibling popup) ++ window.m_tooltip->close(); ++ ++ QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); ++ // Verify the remaining xdg surface is a grab popup.. ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)->m_grabbed); ++ ++ window.m_tooltip->m_popup->close(); ++ QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); ++ QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); ++} ++ + // QTBUG-65680 + void tst_xdgshell::switchPopups() + { +@@ -505,15 +598,17 @@ void tst_xdgshell::minMaxSize() + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + +- exec([=] { xdgToplevel()->sendCompleteConfigure(); }); ++ // we don't roundtrip with our configuration the initial commit should be correct + + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(100, 100)); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(1000, 1000)); + + window.setMaximumSize(QSize(500, 400)); ++ window.update(); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(500, 400)); + + window.setMinimumSize(QSize(50, 40)); ++ window.update(); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(50, 40)); + } + +Submodule qtwebchannel 47af6a4d..6d2f0c3a: +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 qtwebsockets 4a67d49f..9a7b9972: +diff --git a/qtwebsockets/src/websockets/qwebsocket_p.cpp b/qtwebsockets/src/websockets/qwebsocket_p.cpp +index cf3087f..0dd0fa6 100644 +--- a/qtwebsockets/src/websockets/qwebsocket_p.cpp ++++ b/qtwebsockets/src/websockets/qwebsocket_p.cpp +@@ -1100,6 +1100,8 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) + m_handshakeState = AllDoneState; + setErrorString(errorDescription); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); ++ if (m_pSocket->state() != QAbstractSocket::UnconnectedState) ++ m_pSocket->disconnectFromHost(); + } + } + +diff --git a/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp b/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp +index 2affdd5..95f1194 100644 +--- a/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp ++++ b/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp +@@ -273,6 +273,7 @@ void QWebSocketDataProcessor::clear() + if (!m_pConverterState) + m_pConverterState = new QTextCodec::ConverterState(QTextCodec::ConvertInvalidToNull | + QTextCodec::IgnoreHeader); ++ frame.clear(); + } + + /*! diff --git a/src/qt5/qt-everywhere-opensource-src-5.15.12-security_fix-1.patch b/src/qt5/qt-everywhere-opensource-src-5.15.12-security_fix-1.patch new file mode 100644 index 0000000..4a84c8f --- /dev/null +++ b/src/qt5/qt-everywhere-opensource-src-5.15.12-security_fix-1.patch @@ -0,0 +1,205 @@ +Submitted By: Douglas R. Reno +Date: 2024-02-17 +Initial Package Version: 5.15.12 +Origin: Upstream FTP Site (https://download.qt.io/official_releases/qt/5.15/CVE-2024-25580-qtbase-5.15.diff) +Upstream Status: Applied +Description: Patches CVE-2024-25580, a security vulnerability that + allows for a buffer overflow when reading crafted KTX + images. Qt6 is also impacted but it was fixed in an + official release (6.6.2). + +diff -Naurp qt-everywhere-src-5.15.12.orig/qtbase/src/gui/util/qktxhandler.cpp qt-everywhere-src-5.15.12/qtbase/src/gui/util/qktxhandler.cpp +--- qt-everywhere-src-5.15.12.orig/qtbase/src/gui/util/qktxhandler.cpp 2024-02-16 19:43:44.752246504 -0600 ++++ qt-everywhere-src-5.15.12/qtbase/src/gui/util/qktxhandler.cpp 2024-02-16 19:45:05.779751093 -0600 +@@ -73,7 +73,7 @@ struct KTXHeader { + quint32 bytesOfKeyValueData; + }; + +-static const quint32 headerSize = sizeof(KTXHeader); ++static constexpr quint32 qktxh_headerSize = sizeof(KTXHeader); + + // Currently unused, declared for future reference + struct KTXKeyValuePairItem { +@@ -103,11 +103,36 @@ struct KTXMipmapLevel { + */ + }; + +-bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) ++static bool qAddOverflow(quint32 v1, quint32 v2, quint32 *r) { ++ // unsigned additions are well-defined ++ *r = v1 + v2; ++ return v1 > quint32(v1 + v2); ++} ++ ++// Returns the nearest multiple of 4 greater than or equal to 'value' ++static bool nearestMultipleOf4(quint32 value, quint32 *result) + { +- Q_UNUSED(suffix) ++ constexpr quint32 rounding = 4; ++ *result = 0; ++ if (qAddOverflow(value, rounding - 1, result)) ++ return true; ++ *result &= ~(rounding - 1); ++ return false; ++} + +- return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0); ++// Returns a slice with prechecked bounds ++static QByteArray safeSlice(const QByteArray& array, quint32 start, quint32 length) ++{ ++ quint32 end = 0; ++ if (qAddOverflow(start, length, &end) || end > quint32(array.length())) ++ return {}; ++ return QByteArray(array.data() + start, length); ++} ++ ++bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) ++{ ++ Q_UNUSED(suffix); ++ return block.startsWith(QByteArray::fromRawData(ktxIdentifier, KTX_IDENTIFIER_LENGTH)); + } + + QTextureFileData QKtxHandler::read() +@@ -115,42 +140,97 @@ QTextureFileData QKtxHandler::read() + if (!device()) + return QTextureFileData(); + +- QByteArray buf = device()->readAll(); +- const quint32 dataSize = quint32(buf.size()); +- if (dataSize < headerSize || !canRead(QByteArray(), buf)) { +- qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); ++ const QByteArray buf = device()->readAll(); ++ if (size_t(buf.size()) > std::numeric_limits::max()) { ++ qWarning(lcQtGuiTextureIO, "Too big KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ if (!canRead(QByteArray(), buf)) { ++ qWarning(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ if (buf.size() < qsizetype(qktxh_headerSize)) { ++ qWarning(lcQtGuiTextureIO, "Invalid KTX header size in %s", logName().constData()); + return QTextureFileData(); + } + +- const KTXHeader *header = reinterpret_cast(buf.constData()); +- if (!checkHeader(*header)) { +- qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData()); ++ KTXHeader header; ++ memcpy(&header, buf.data(), qktxh_headerSize); ++ if (!checkHeader(header)) { ++ qWarning(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData()); + return QTextureFileData(); + } + + QTextureFileData texData; + texData.setData(buf); + +- texData.setSize(QSize(decode(header->pixelWidth), decode(header->pixelHeight))); +- texData.setGLFormat(decode(header->glFormat)); +- texData.setGLInternalFormat(decode(header->glInternalFormat)); +- texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat)); +- +- texData.setNumLevels(decode(header->numberOfMipmapLevels)); +- quint32 offset = headerSize + decode(header->bytesOfKeyValueData); +- const int maxLevels = qMin(texData.numLevels(), 32); // Cap iterations in case of corrupt file. +- for (int i = 0; i < maxLevels; i++) { +- if (offset + sizeof(KTXMipmapLevel) > dataSize) // Corrupt file; avoid oob read +- break; +- const KTXMipmapLevel *level = reinterpret_cast(buf.constData() + offset); +- quint32 levelLen = decode(level->imageSize); +- texData.setDataOffset(offset + sizeof(KTXMipmapLevel::imageSize), i); +- texData.setDataLength(levelLen, i); +- offset += sizeof(KTXMipmapLevel::imageSize) + levelLen + (3 - ((levelLen + 3) % 4)); ++ texData.setSize(QSize(decode(header.pixelWidth), decode(header.pixelHeight))); ++ texData.setGLFormat(decode(header.glFormat)); ++ texData.setGLInternalFormat(decode(header.glInternalFormat)); ++ texData.setGLBaseInternalFormat(decode(header.glBaseInternalFormat)); ++ ++ texData.setNumLevels(decode(header.numberOfMipmapLevels)); ++ ++ const quint32 bytesOfKeyValueData = decode(header.bytesOfKeyValueData); ++ quint32 headerKeyValueSize; ++ if (qAddOverflow(qktxh_headerSize, bytesOfKeyValueData, &headerKeyValueSize)) { ++ qWarning(lcQtGuiTextureIO, "Overflow in size of key value data in header of KTX file %s", ++ logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ if (headerKeyValueSize >= quint32(buf.size())) { ++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ // Technically, any number of levels is allowed but if the value is bigger than ++ // what is possible in KTX V2 (and what makes sense) we return an error. ++ // maxLevels = log2(max(width, height, depth)) ++ const int maxLevels = (sizeof(quint32) * 8) ++ - qCountLeadingZeroBits(std::max( ++ { header.pixelWidth, header.pixelHeight, header.pixelDepth })); ++ ++ if (texData.numLevels() > maxLevels) { ++ qWarning(lcQtGuiTextureIO, "Too many levels in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ quint32 offset = headerKeyValueSize; ++ for (int level = 0; level < texData.numLevels(); level++) { ++ const auto imageSizeSlice = safeSlice(buf, offset, sizeof(quint32)); ++ if (imageSizeSlice.isEmpty()) { ++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ const quint32 imageSize = decode(qFromUnaligned(imageSizeSlice.data())); ++ offset += sizeof(quint32); // overflow checked indirectly above ++ ++ texData.setDataOffset(offset, level); ++ texData.setDataLength(imageSize, level); ++ ++ // Add image data and padding to offset ++ quint32 padded = 0; ++ if (nearestMultipleOf4(imageSize, &padded)) { ++ qWarning(lcQtGuiTextureIO, "Overflow in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ quint32 offsetNext; ++ if (qAddOverflow(offset, padded, &offsetNext)) { ++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ offset = offsetNext; + } + + if (!texData.isValid()) { +- qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", logName().constData()); ++ qWarning(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", ++ logName().constData()); + return QTextureFileData(); + } + +@@ -191,7 +271,7 @@ bool QKtxHandler::checkHeader(const KTXH + (decode(header.numberOfFaces) == 1)); + } + +-quint32 QKtxHandler::decode(quint32 val) ++quint32 QKtxHandler::decode(quint32 val) const + { + return inverseEndian ? qbswap(val) : val; + } +diff -Naurp qt-everywhere-src-5.15.12.orig/qtbase/src/gui/util/qktxhandler_p.h qt-everywhere-src-5.15.12/qtbase/src/gui/util/qktxhandler_p.h +--- qt-everywhere-src-5.15.12.orig/qtbase/src/gui/util/qktxhandler_p.h 2024-02-16 19:43:44.752246504 -0600 ++++ qt-everywhere-src-5.15.12/qtbase/src/gui/util/qktxhandler_p.h 2024-02-16 19:45:05.779751093 -0600 +@@ -68,7 +68,7 @@ public: + + private: + bool checkHeader(const KTXHeader &header); +- quint32 decode(quint32 val); ++ quint32 decode(quint32 val) const; + + bool inverseEndian = false; + };