diff options
59 files changed, 1669 insertions, 735 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 337703bda3a..1147205b79f 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -577,10 +577,15 @@ if(QT_FEATURE_async_io) io/qrandomaccessasyncfile.cpp io/qrandomaccessasyncfile_p.h io/qrandomaccessasyncfile_p_p.h ) - # TODO: This should become the last (fallback) condition later. - # We migth also want to rewrite it so that it does not depend on - # QT_FEATURE_future. - if(QT_FEATURE_thread AND QT_FEATURE_future) + if(APPLE) + qt_internal_extend_target(Core + SOURCES + io/qrandomaccessasyncfile_darwin.mm + ) + elseif(QT_FEATURE_thread AND QT_FEATURE_future) + # TODO: This should become the last (fallback) condition later. + # We migth also want to rewrite it so that it does not depend on + # QT_FEATURE_future. qt_internal_extend_target(Core SOURCES io/qrandomaccessasyncfile_threadpool.cpp diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 535e3742cd2..d951b85c147 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -1233,7 +1233,7 @@ qt_feature("openssl-hash" PRIVATE qt_feature("async-io" PRIVATE LABEL "Async File I/O" PURPOSE "Provides support for asynchronous file I/O." - CONDITION QT_FEATURE_thread AND QT_FEATURE_future + CONDITION (QT_FEATURE_thread AND QT_FEATURE_future) OR APPLE ) qt_configure_add_summary_section(NAME "Qt Core") diff --git a/src/corelib/io/qrandomaccessasyncfile_darwin.mm b/src/corelib/io/qrandomaccessasyncfile_darwin.mm new file mode 100644 index 00000000000..2d7d3b196b2 --- /dev/null +++ b/src/corelib/io/qrandomaccessasyncfile_darwin.mm @@ -0,0 +1,728 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default + +#include "qrandomaccessasyncfile_p_p.h" + +#include "qiooperation_p.h" +#include "qiooperation_p_p.h" +#include "qplatformdefs.h" + +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/private/qfilesystemengine_p.h> + +QT_BEGIN_NAMESPACE + +namespace { + +static bool isBarrierOperation(QIOOperation::Type type) +{ + return type == QIOOperation::Type::Flush || type == QIOOperation::Type::Open; +} + +} // anonymous namespace + +// Fine to provide the definition here, because all the usages are in this file +// only! +template <typename Operation, typename ...Args> +Operation * +QRandomAccessAsyncFilePrivate::addOperation(QIOOperation::Type type, qint64 offset, Args &&...args) +{ + auto dataStorage = new QtPrivate::QIOOperationDataStorage(std::forward<Args>(args)...); + auto *priv = new QIOOperationPrivate(dataStorage); + priv->offset = offset; + priv->type = type; + + Operation *op = new Operation(*priv, q_ptr); + auto opId = getNextId(); + m_operations.push_back(OperationInfo(opId, op)); + startOperationsUntilBarrier(); + + return op; +} + +QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() + : QObjectPrivate() +{ +} + +QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() + = default; + +void QRandomAccessAsyncFilePrivate::init() +{ +} + +void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) +{ + auto it = std::find_if(m_operations.cbegin(), m_operations.cend(), + [op](const auto &opInfo) { + return opInfo.operation.get() == op; + }); + // not found + if (it == m_operations.cend()) + return; + + const auto opInfo = m_operations.takeAt(std::distance(m_operations.cbegin(), it)); + + if (opInfo.state == OpState::Running) { + // cancel this operation + m_mutex.lock(); + if (m_runningOps.contains(opInfo.opId)) { + m_opToCancel = opInfo.opId; + closeIoChannel(opInfo.channel); + m_cancellationCondition.wait(&m_mutex); + m_opToCancel = kInvalidOperationId; // reset + } + m_mutex.unlock(); + } // otherwise it was not started yet + + // clean up the operation + releaseIoChannel(opInfo.channel); + auto *priv = QIOOperationPrivate::get(opInfo.operation); + priv->setError(QIOOperation::Error::Aborted); + + // we could cancel a barrier operation, so try to execute next operations + startOperationsUntilBarrier(); +} + +void QRandomAccessAsyncFilePrivate::close() +{ + if (m_fileState == FileState::Closed) + return; + + // cancel all operations + m_mutex.lock(); + m_opToCancel = kAllOperationIds; + for (const auto &op : m_operations) + closeIoChannel(op.channel); + closeIoChannel(m_ioChannel); + // we're not interested in any results anymore + if (!m_runningOps.isEmpty() || m_ioChannel) + m_cancellationCondition.wait(&m_mutex); + m_opToCancel = kInvalidOperationId; // reset + m_mutex.unlock(); + + // clean up all operations + for (auto &opInfo : m_operations) { + releaseIoChannel(opInfo.channel); + auto *priv = QIOOperationPrivate::get(opInfo.operation); + priv->setError(QIOOperation::Error::Aborted); + } + m_operations.clear(); + + releaseIoChannel(m_ioChannel); + + if (m_fd >= 0) { + ::close(m_fd); + m_fd = -1; + } + + m_fileState = FileState::Closed; +} + +qint64 QRandomAccessAsyncFilePrivate::size() const +{ + if (m_fileState != FileState::Opened) + return -1; + + QFileSystemMetaData metaData; + if (QFileSystemEngine::fillMetaData(m_fd, metaData)) + return metaData.size(); + + return -1; +} + +QIOOperation * +QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode) +{ + if (m_fileState == FileState::Closed) { + m_filePath = path; + m_openMode = mode; + // Open is a barrier, so we won't have two open() operations running + // in parallel + m_fileState = FileState::OpenPending; + } + + return addOperation<QIOOperation>(QIOOperation::Type::Open, 0); +} + +QIOOperation *QRandomAccessAsyncFilePrivate::flush() +{ + return addOperation<QIOOperation>(QIOOperation::Type::Flush, 0); +} + +QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize) +{ + QByteArray array(maxSize, Qt::Uninitialized); + return addOperation<QIOReadOperation>(QIOOperation::Type::Read, offset, std::move(array)); +} + +QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) +{ + QByteArray copy = data; + return write(offset, std::move(copy)); +} + +QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) +{ + return addOperation<QIOWriteOperation>(QIOOperation::Type::Write, offset, std::move(data)); +} + +QIOVectoredReadOperation * +QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<std::byte> buffer) +{ + return addOperation<QIOVectoredReadOperation>(QIOOperation::Type::Read, offset, + QSpan<const QSpan<std::byte>>{buffer}); +} + +QIOVectoredWriteOperation * +QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const std::byte> buffer) +{ + return addOperation<QIOVectoredWriteOperation>(QIOOperation::Type::Write, offset, + QSpan<const QSpan<const std::byte>>{buffer}); +} + +QIOVectoredReadOperation * +QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) +{ + // GCD implementation does not have vectored read. Spawning several read + // operations (each with an updated offset), is not ideal, because some + // of them could fail, and it wouldn't be clear what would be the return + // value in such case. + // So, we'll just execute several reads one-after-another, and complete the + // whole operation only when they all finish (or when an operation fails + // at some point). + + return addOperation<QIOVectoredReadOperation>(QIOOperation::Type::Read, offset, buffers); +} + +QIOVectoredWriteOperation * +QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) +{ + return addOperation<QIOVectoredWriteOperation>(QIOOperation::Type::Write, offset, buffers); +} + +dispatch_io_t QRandomAccessAsyncFilePrivate::createMainChannel(int fd) +{ + auto sharedThis = this; + return dispatch_io_create(DISPATCH_IO_RANDOM, fd, + dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), + ^(int /*error*/) { + // Notify that the file descriptor can be closed + QMutexLocker locker(&sharedThis->m_mutex); + sharedThis->m_cancellationCondition.wakeOne(); + }); +} + +dispatch_io_t QRandomAccessAsyncFilePrivate::duplicateIoChannel(OperationId opId) +{ + if (!m_ioChannel) + return nullptr; + // We need to create a new channel for each operation, because the only way + // to cancel an operation is to call dispatch_io_close() with + // DISPATCH_IO_STOP flag. + // We do not care about the callback in this case, because we have the + // callback from the "main" io channel to do all the proper cleanup + auto channel = + dispatch_io_create_with_io(DISPATCH_IO_RANDOM, m_ioChannel, + dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), + ^(int){ /* empty callback */ }); + + if (channel) { + QMutexLocker locker(&m_mutex); + m_runningOps.insert(opId); + } + return channel; +} + +void QRandomAccessAsyncFilePrivate::closeIoChannel(dispatch_io_t channel) +{ + if (channel) + dispatch_io_close(channel, DISPATCH_IO_STOP); +} + +void QRandomAccessAsyncFilePrivate::releaseIoChannel(dispatch_io_t channel) +{ + if (channel) { + dispatch_release(channel); + channel = nullptr; + } +} + +void QRandomAccessAsyncFilePrivate::handleOperationComplete(const OperationResult &opResult) +{ + // try to start next operations on return + auto onReturn = qScopeGuard([this] { + startOperationsUntilBarrier(); + }); + + auto it = std::find_if(m_operations.cbegin(), m_operations.cend(), + [opId = opResult.opId](const auto &opInfo) { + return opInfo.opId == opId; + }); + if (it == m_operations.cend()) + return; + qsizetype idx = std::distance(m_operations.cbegin(), it); + + const OperationInfo info = m_operations.takeAt(idx); + closeIoChannel(info.channel); + releaseIoChannel(info.channel); + + if (!info.operation) + return; + + auto convertError = [](int error, QIOOperation::Type type) { + if (error == 0) { + return QIOOperation::Error::None; + } else if (error == ECANCELED) { + return QIOOperation::Error::Aborted; + } else if (error == EBADF) { + return QIOOperation::Error::FileNotOpen; + } else if (error == EINVAL) { + switch (type) { + case QIOOperation::Type::Read: + case QIOOperation::Type::Write: + return QIOOperation::Error::IncorrectOffset; + case QIOOperation::Type::Flush: + return QIOOperation::Error::Flush; + case QIOOperation::Type::Open: + return QIOOperation::Error::Open; + case QIOOperation::Type::Unknown: + Q_UNREACHABLE_RETURN(QIOOperation::Error::FileNotOpen); + } + } else { + switch (type) { + case QIOOperation::Type::Read: + return QIOOperation::Error::Read; + case QIOOperation::Type::Write: + return QIOOperation::Error::Write; + case QIOOperation::Type::Flush: + return QIOOperation::Error::Flush; + case QIOOperation::Type::Open: + return QIOOperation::Error::Open; + case QIOOperation::Type::Unknown: + Q_UNREACHABLE_RETURN(QIOOperation::Error::FileNotOpen); + } + } + }; + + auto *priv = QIOOperationPrivate::get(info.operation); + switch (priv->type) { + case QIOOperation::Type::Read: + case QIOOperation::Type::Write: + priv->appendBytesProcessed(opResult.result); + // make sure that read buffers are truncated to the actual amount of + // bytes read + if (priv->type == QIOOperation::Type::Read) { + auto dataStorage = priv->dataStorage.get(); + auto processed = priv->processed; + if (dataStorage->containsByteArray()) { + QByteArray &array = dataStorage->getByteArray(); + array.truncate(processed); + } else if (dataStorage->containsReadSpans()) { + qint64 left = processed; + auto &readBuffers = dataStorage->getReadSpans(); + for (auto &s : readBuffers) { + const qint64 spanSize = qint64(s.size_bytes()); + const qint64 newSize = (std::min)(left, spanSize); + if (newSize < spanSize) + s.chop(spanSize - newSize); + left -= newSize; + } + } + } + priv->operationComplete(convertError(opResult.error, priv->type)); + break; + case QIOOperation::Type::Flush: { + const QIOOperation::Error error = convertError(opResult.error, priv->type); + priv->operationComplete(error); + break; + } + case QIOOperation::Type::Open: { + const QIOOperation::Error error = convertError(opResult.error, priv->type); + if (opResult.result >= 0 && error == QIOOperation::Error::None) { + m_fd = (int)opResult.result; + m_ioChannel = createMainChannel(m_fd); + m_fileState = FileState::Opened; + } else { + m_fileState = FileState::Closed; + } + priv->operationComplete(error); + break; + } + case QIOOperation::Type::Unknown: + Q_UNREACHABLE(); + break; + } +} + +void QRandomAccessAsyncFilePrivate::queueCompletion(OperationId opId, int error) +{ + const OperationResult res = { opId, 0LL, error }; + QMetaObject::invokeMethod(q_ptr, [this, res] { + handleOperationComplete(res); + }, Qt::QueuedConnection); +} + +void QRandomAccessAsyncFilePrivate::startOperationsUntilBarrier() +{ + // starts all operations until barrier, or a barrier operation if it's the + // first one + bool first = true; + for (auto &opInfo : m_operations) { + const bool isBarrier = isBarrierOperation(opInfo.operation->type()); + const bool shouldExecute = (opInfo.state == OpState::Pending) && (!isBarrier || first); + first = false; + if (shouldExecute) { + opInfo.state = OpState::Running; + switch (opInfo.operation->type()) { + case QIOOperation::Type::Read: + executeRead(opInfo); + break; + case QIOOperation::Type::Write: + executeWrite(opInfo); + break; + case QIOOperation::Type::Flush: + executeFlush(opInfo); + break; + case QIOOperation::Type::Open: + executeOpen(opInfo); + break; + case QIOOperation::Type::Unknown: + Q_UNREACHABLE(); + break; + } + } + if (isBarrier) + break; + } +} + +void QRandomAccessAsyncFilePrivate::executeRead(OperationInfo &opInfo) +{ + opInfo.channel = duplicateIoChannel(opInfo.opId); + if (!opInfo.channel) { + queueCompletion(opInfo.opId, EBADF); + return; + } + auto priv = QIOOperationPrivate::get(opInfo.operation); + auto dataStorage = priv->dataStorage.get(); + if (dataStorage->containsByteArray()) { + auto &array = dataStorage->getByteArray(); + char *bytesPtr = array.data(); + qint64 maxSize = array.size(); + readOneBufferHelper(opInfo.opId, opInfo.channel, priv->offset, + bytesPtr, maxSize, + 0, 1, 0); + } else { + Q_ASSERT(dataStorage->containsReadSpans()); + auto &readBuffers = dataStorage->getReadSpans(); + const auto totalBuffers = readBuffers.size(); + if (totalBuffers == 0) { + queueCompletion(opInfo.opId, 0); + return; + } + auto buf = readBuffers[0]; + readOneBufferHelper(opInfo.opId, opInfo.channel, priv->offset, + buf.data(), buf.size(), + 0, totalBuffers, 0); + } +} + +void QRandomAccessAsyncFilePrivate::executeWrite(OperationInfo &opInfo) +{ + opInfo.channel = duplicateIoChannel(opInfo.opId); + if (!opInfo.channel) { + queueCompletion(opInfo.opId, EBADF); + return; + } + auto priv = QIOOperationPrivate::get(opInfo.operation); + auto dataStorage = priv->dataStorage.get(); + if (dataStorage->containsByteArray()) { + const auto &array = dataStorage->getByteArray(); + const char *dataPtr = array.constData(); + const qint64 dataSize = array.size(); + + dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0); + // We handle the bytes on our own, so we need to specify an empty block as + // a destructor. + // dataToWrite is retained, so should be properly cleaned up. We always do + // it in the callback. + dispatch_data_t dataToWrite = dispatch_data_create(dataPtr, dataSize, queue, ^{}); + + writeHelper(opInfo.opId, opInfo.channel, priv->offset, dataToWrite, dataSize); + } else { + Q_ASSERT(dataStorage->containsWriteSpans()); + + const auto &writeBuffers = dataStorage->getWriteSpans(); + const auto totalBuffers = writeBuffers.size(); + if (totalBuffers == 0) { + queueCompletion(opInfo.opId, 0); + return; + } + + dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0); + qsizetype idx = 0; + dispatch_data_t dataToWrite = dispatch_data_empty; + qint64 totalSize = 0; + do { + const std::byte *dataPtr = writeBuffers[idx].data(); + const qint64 dataSize = writeBuffers[idx].size(); + dispatch_data_t data = dispatch_data_create(dataPtr, dataSize, queue, ^{}); + dataToWrite = dispatch_data_create_concat(dataToWrite, data); + [data release]; + totalSize += dataSize; + } while (++idx < totalBuffers); + + writeHelper(opInfo.opId, opInfo.channel, priv->offset, dataToWrite, totalSize); + } +} + +void QRandomAccessAsyncFilePrivate::executeFlush(OperationInfo &opInfo) +{ + opInfo.channel = duplicateIoChannel(opInfo.opId); + if (!opInfo.channel) { + queueCompletion(opInfo.opId, EBADF); + return; + } + + // flush() is a barrier operation, but dispatch_io_barrier does not work + // as documented with multiple channels :( + auto sharedThis = this; + const int fd = m_fd; + const OperationId opId = opInfo.opId; + dispatch_io_barrier(opInfo.channel, ^{ + const int err = fsync(fd); + + QMutexLocker locker(&sharedThis->m_mutex); + sharedThis->m_runningOps.remove(opId); + const auto cancelId = sharedThis->m_opToCancel; + if (cancelId == kAllOperationIds || cancelId == opId) { + if (cancelId == opId) + sharedThis->m_cancellationCondition.wakeOne(); + } else { + auto context = sharedThis->q_ptr; + const OperationResult res = { opId, 0LL, err }; + QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { + sharedThis->handleOperationComplete(r); + }, Qt::QueuedConnection, res); + } + }); +} + +// stolen from qfsfileengine_unix.cpp +static inline int openModeToOpenFlags(QIODevice::OpenMode mode) +{ + int oflags = QT_OPEN_RDONLY; +#ifdef QT_LARGEFILE_SUPPORT + oflags |= QT_OPEN_LARGEFILE; +#endif + if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) + oflags = QT_OPEN_RDWR; + else if (mode & QIODevice::WriteOnly) + oflags = QT_OPEN_WRONLY; + if ((mode & QIODevice::WriteOnly) + && !(mode & QIODevice::ExistingOnly)) // QFSFileEnginePrivate::openModeCanCreate(mode)) + oflags |= QT_OPEN_CREAT; + if (mode & QIODevice::Truncate) + oflags |= QT_OPEN_TRUNC; + if (mode & QIODevice::Append) + oflags |= QT_OPEN_APPEND; + if (mode & QIODevice::NewOnly) + oflags |= QT_OPEN_EXCL; + return oflags; +} + +void QRandomAccessAsyncFilePrivate::executeOpen(OperationInfo &opInfo) +{ + if (m_fileState != FileState::OpenPending) { + queueCompletion(opInfo.opId, EINVAL); + return; + } + + const QByteArray nativeName = QFile::encodeName(QDir::toNativeSeparators(m_filePath)); + + int openFlags = openModeToOpenFlags(m_openMode); + openFlags |= O_NONBLOCK; + + auto sharedThis = this; + const OperationId opId = opInfo.opId; + + // We don'd call duplicateIOChannel(), so need to update the running ops + // explicitly. + m_mutex.lock(); + m_runningOps.insert(opId); + m_mutex.unlock(); + + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), + ^{ + int err = 0; + const int fd = ::open(nativeName.data(), openFlags); + if (fd < 0) + err = errno; + + QMutexLocker locker(&sharedThis->m_mutex); + sharedThis->m_runningOps.remove(opId); + const auto cancelId = sharedThis->m_opToCancel; + if (cancelId == kAllOperationIds || cancelId == opId) { + // open() is a barrier operation, so it's always the + // only executing operation. + // Also, the main IO channel is not created yet. + // So we need to notify the condition variable in + // any both cases. + Q_ASSERT(sharedThis->m_runningOps.isEmpty()); + sharedThis->m_cancellationCondition.wakeOne(); + } else { + auto context = sharedThis->q_ptr; + const OperationResult res = { opId, qint64(fd), err }; + QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { + sharedThis->handleOperationComplete(r); + }, Qt::QueuedConnection, res); + } + }); +} + +void QRandomAccessAsyncFilePrivate::readOneBuffer(OperationId opId, qsizetype bufferIdx, + qint64 alreadyRead) +{ + // we need to lookup the operation again, because it could have beed removed + // by the user... + + auto it = std::find_if(m_operations.cbegin(), m_operations.cend(), + [opId](const auto &opInfo) { + return opId == opInfo.opId; + }); + if (it == m_operations.cend()) + return; + + auto op = it->operation; // QPointer could be null + if (!op) { + closeIoChannel(it->channel); + return; + } + + auto *priv = QIOOperationPrivate::get(op); + Q_ASSERT(priv->type == QIOOperation::Type::Read); + Q_ASSERT(priv->dataStorage->containsReadSpans()); + + auto &readBuffers = priv->dataStorage->getReadSpans(); + Q_ASSERT(readBuffers.size() > bufferIdx); + + qint64 newOffset = priv->offset; + for (qsizetype idx = 0; idx < bufferIdx; ++idx) + newOffset += readBuffers[idx].size(); + + std::byte *bytesPtr = readBuffers[bufferIdx].data(); + qint64 maxSize = readBuffers[bufferIdx].size(); + + readOneBufferHelper(opId, it->channel, newOffset, bytesPtr, maxSize, bufferIdx, + readBuffers.size(), alreadyRead); +} + +void QRandomAccessAsyncFilePrivate::readOneBufferHelper(OperationId opId, dispatch_io_t channel, + qint64 offset, void *bytesPtr, + qint64 maxSize, qsizetype currentBufferIdx, + qsizetype totalBuffers, qint64 alreadyRead) +{ + auto sharedThis = this; + __block size_t readFromBuffer = 0; + dispatch_io_read(channel, offset, maxSize, + dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), + ^(bool done, dispatch_data_t data, int error) { + // Handle data. If there's an error, handle as much as + // we can. + if (data) { + dispatch_data_apply(data, ^(dispatch_data_t /*region*/, size_t offset, + const void *buffer, size_t size) { + const char *startPtr = + reinterpret_cast<const char *>(buffer) + offset; + // NOTE: This is a copy, but looks like we + // cannot do better :( + std::memcpy((std::byte *)bytesPtr + readFromBuffer, + startPtr, size); + readFromBuffer += size; + return true; // Keep processing if there is more data. + }); + } + + QMutexLocker locker(&sharedThis->m_mutex); + const auto cancelId = sharedThis->m_opToCancel; + if (cancelId == kAllOperationIds || cancelId == opId) { + sharedThis->m_runningOps.remove(opId); + if (cancelId == opId) + sharedThis->m_cancellationCondition.wakeOne(); + } else if (done) { + sharedThis->m_runningOps.remove(opId); + auto context = sharedThis->q_ptr; + // if error, or last buffer, or read less than expected, + // report operation completion + qint64 totalRead = qint64(readFromBuffer) + alreadyRead; + qsizetype nextBufferIdx = currentBufferIdx + 1; + if (error || nextBufferIdx == totalBuffers + || qint64(readFromBuffer) != maxSize) { + const OperationResult res = { opId, totalRead, error }; + QMetaObject::invokeMethod(context, + [sharedThis](const OperationResult &r) { + sharedThis->handleOperationComplete(r); + }, Qt::QueuedConnection, res); + } else { + // else execute read for the next buffer + QMetaObject::invokeMethod(context, + [sharedThis, opId, nextBufferIdx, totalRead] { + sharedThis->readOneBuffer(opId, nextBufferIdx, totalRead); + }, Qt::QueuedConnection); + } + } + }); +} + +void QRandomAccessAsyncFilePrivate::writeHelper(OperationId opId, dispatch_io_t channel, + qint64 offset, dispatch_data_t dataToWrite, + qint64 dataSize) +{ + auto sharedThis = this; + dispatch_io_write(channel, offset, dataToWrite, + dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), + ^(bool done, dispatch_data_t data, int error) { + // Either an error or complete write. + // If there's an error, return the amount that we have + // written so far + QMutexLocker locker(&sharedThis->m_mutex); + const auto cancelId = sharedThis->m_opToCancel; + if (cancelId == kAllOperationIds || cancelId == opId) { + // Operation is canceled - do nothing + sharedThis->m_runningOps.remove(opId); + if (cancelId == opId) + sharedThis->m_cancellationCondition.wakeOne(); + } else if (done) { + sharedThis->m_runningOps.remove(opId); + // if no error, an attempt to access the data will + // crash, because it seems to have no buffer + // allocated (as everything was written) + const size_t toBeWritten = + (error == 0) ? 0 : dispatch_data_get_size(data); + const size_t written = dataSize - toBeWritten; + [dataToWrite release]; + + auto context = sharedThis->q_ptr; + const OperationResult res = { opId, qint64(written), error }; + QMetaObject::invokeMethod(context, + [sharedThis](const OperationResult &r) { + sharedThis->handleOperationComplete(r); + }, Qt::QueuedConnection, res); + } + }); +} + +QRandomAccessAsyncFilePrivate::OperationId QRandomAccessAsyncFilePrivate::getNextId() +{ + // never return reserved values + static OperationId opId = kInvalidOperationId; + if (++opId == kAllOperationIds) + opId = kInvalidOperationId + 1; + return opId; +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qrandomaccessasyncfile_p_p.h b/src/corelib/io/qrandomaccessasyncfile_p_p.h index ef996c37f07..73d7eebdf72 100644 --- a/src/corelib/io/qrandomaccessasyncfile_p_p.h +++ b/src/corelib/io/qrandomaccessasyncfile_p_p.h @@ -32,6 +32,17 @@ #endif // QT_RANDOMACCESSASYNCFILE_THREAD +#ifdef Q_OS_DARWIN + +#include <QtCore/qlist.h> +#include <QtCore/qmutex.h> +#include <QtCore/qset.h> +#include <QtCore/qwaitcondition.h> + +#include <dispatch/dispatch.h> + +#endif // Q_OS_DARWIN + QT_BEGIN_NAMESPACE class QRandomAccessAsyncFilePrivate : public QObjectPrivate @@ -69,6 +80,18 @@ public: writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers); private: + // common for all backends + enum class FileState : quint8 + { + Closed, + OpenPending, // already got an open request + Opened, + }; + + QString m_filePath; + QIODeviceBase::OpenMode m_openMode; + FileState m_fileState = FileState::Closed; + #ifdef QT_RANDOMACCESSASYNCFILE_THREAD public: struct OperationResult @@ -78,13 +101,6 @@ public: }; private: - enum class FileState : quint8 - { - Closed, - OpenPending, // already got an open request - Opened, - }; - mutable QBasicMutex m_engineMutex; std::unique_ptr<QFSFileEngine> m_engine; QFutureWatcher<OperationResult> m_watcher; @@ -93,16 +109,90 @@ private: QPointer<QIOOperation> m_currentOperation; qsizetype numProcessedBuffers = 0; - QString m_filePath; - QIODeviceBase::OpenMode m_openMode; - FileState m_fileState = FileState::Closed; - void executeNextOperation(); void processBufferAt(qsizetype idx); void processFlush(); void processOpen(); void operationComplete(); #endif +#ifdef Q_OS_DARWIN + using OperationId = quint64; + static constexpr OperationId kInvalidOperationId = 0; + static constexpr OperationId kAllOperationIds = std::numeric_limits<OperationId>::max(); + + struct OperationResult + { + OperationId opId; + qint64 result; // num bytes processed or file descriptor + int error; + }; + + enum class OpState : quint8 + { + Pending, + Running, + }; + + struct OperationInfo + { + OperationId opId; + dispatch_io_t channel; + QPointer<QIOOperation> operation; + OpState state; + + OperationInfo(OperationId _id, QIOOperation *_op) + : opId(_id), + channel(nullptr), + operation(_op), + state(OpState::Pending) + {} + }; + + // We need to maintain an actual queue of the operations, because + // certain operations (i.e. flush) should act like barriers. The docs + // for dispatch_io_barrier mention that it can synchronize between multiple + // channels handling the same file descriptor, but that DOES NOT work in + // practice. It works correctly only when there's a signle IO channel. But + // with a signle IO channel we're not able to cancel individual operations. + // As a result, we need to make sure that all previous operations are + // completed before starting a barrier operation. Similarly, we cannot start + // any other operation until a barrier operation is finished. + QList<OperationInfo> m_operations; + dispatch_io_t m_ioChannel = nullptr; + int m_fd = -1; + + QMutex m_mutex; + // the members below should only be accessed with the mutex + OperationId m_opToCancel = kInvalidOperationId; + QSet<OperationId> m_runningOps; + QWaitCondition m_cancellationCondition; + + static OperationId getNextId(); + + template <typename Operation, typename ...Args> + Operation *addOperation(QIOOperation::Type type, qint64 offset, Args &&...args); + + dispatch_io_t createMainChannel(int fd); + dispatch_io_t duplicateIoChannel(OperationId opId); + void closeIoChannel(dispatch_io_t channel); + void releaseIoChannel(dispatch_io_t channel); + void handleOperationComplete(const OperationResult &opResult); + + void queueCompletion(OperationId opId, int error); + + void startOperationsUntilBarrier(); + void executeRead(OperationInfo &opInfo); + void executeWrite(OperationInfo &opInfo); + void executeFlush(OperationInfo &opInfo); + void executeOpen(OperationInfo &opInfo); + + void readOneBuffer(OperationId opId, qsizetype bufferIdx, qint64 alreadyRead); + void readOneBufferHelper(OperationId opId, dispatch_io_t channel, qint64 offset, + void *bytesPtr, qint64 maxSize, qsizetype currentBufferIdx, + qsizetype totalBuffers, qint64 alreadyRead); + void writeHelper(OperationId opId, dispatch_io_t channel, qint64 offset, + dispatch_data_t dataToWrite, qint64 dataSize); +#endif }; QT_END_NAMESPACE diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 560a8c7d789..02c9f00f301 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2715,6 +2715,20 @@ static void err_info_about_objects(const char *func, const QObject *sender, cons qCWarning(lcConnect, "QObject::%s: (receiver name: '%s')", func, b.toLocal8Bit().data()); } +Q_DECL_COLD_FUNCTION +static void connectWarning(const QObject *sender, + const QMetaObject *senderMetaObject, + const QObject *receiver, + const char *message) +{ + const char *senderString = sender ? sender->metaObject()->className() + : senderMetaObject ? senderMetaObject->className() + : "Unknown"; + const char *receiverString = receiver ? receiver->metaObject()->className() + : "Unknown"; + qCWarning(lcConnect, "QObject::connect(%s, %s): %s", senderString, receiverString, message); +} + /*! Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns \nullptr. The pointer @@ -4105,8 +4119,9 @@ QMetaObject::Connection QMetaObject::connectImpl(const QObject *sender, const QM { QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw); + const QMetaObject *senderMetaObject = sender->metaObject(); if (!signal.isValid() || signal.methodType() != QMetaMethod::Signal) { - qCWarning(lcConnect, "QObject::connect: invalid signal parameter"); + connectWarning(sender, senderMetaObject, receiver, "invalid signal parameter"); return QMetaObject::Connection(); } @@ -4116,7 +4131,6 @@ QMetaObject::Connection QMetaObject::connectImpl(const QObject *sender, const QM QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); } - const QMetaObject *senderMetaObject = sender->metaObject(); if (signal_index == -1) { qCWarning(lcConnect, "QObject::connect: Can't find signal %s on instance of class %s", signal.methodSignature().constData(), senderMetaObject->className()); @@ -5433,7 +5447,7 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa { QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw); if (!signal) { - qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter"); + connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter"); return QMetaObject::Connection(); } @@ -5445,26 +5459,13 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa break; } if (!senderMetaObject) { - qCWarning(lcConnect, "QObject::connect: signal not found in %s", sender->metaObject()->className()); + connectWarning(sender, senderMetaObject, receiver, "signal not found"); return QMetaObject::Connection(nullptr); } signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject); return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj.release(), type, types, senderMetaObject); } -static void connectWarning(const QObject *sender, - const QMetaObject *senderMetaObject, - const QObject *receiver, - const char *message) -{ - const char *senderString = sender ? sender->metaObject()->className() - : senderMetaObject ? senderMetaObject->className() - : "Unknown"; - const char *receiverString = receiver ? receiver->metaObject()->className() - : "Unknown"; - qCWarning(lcConnect, "QObject::connect(%s, %s): %s", senderString, receiverString, message); -} - /*! \internal @@ -5495,7 +5496,7 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s QOrderedMutexLocker locker(signalSlotLock(sender), signalSlotLock(receiver)); - if (type & Qt::UniqueConnection && slot) { + if (type & Qt::UniqueConnection) { QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed(); if (connections && connections->signalVectorCount() > signal_index) { const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); @@ -5683,7 +5684,7 @@ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signa { QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw); if (!sender) { - qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter"); + connectWarning(sender, nullptr, receiver, "invalid nullptr parameter"); return QMetaObject::Connection(); } const QMetaObject *senderMetaObject = sender->metaObject(); diff --git a/src/corelib/platform/android/qandroidnativeinterface.cpp b/src/corelib/platform/android/qandroidnativeinterface.cpp index 680725dc6a1..5771d28f3a9 100644 --- a/src/corelib/platform/android/qandroidnativeinterface.cpp +++ b/src/corelib/platform/android/qandroidnativeinterface.cpp @@ -48,8 +48,9 @@ QT_DEFINE_NATIVE_INTERFACE(QAndroidApplication); /*! \fn QJniObject QNativeInterface::QAndroidApplication::context() - Returns the Android context as a \c QJniObject. The context is an \c Activity - if the most recently started activity object is valid. Otherwise, the context is a \c Service. + Returns the Android context as a \c QtJniTypes::Context. The context + is an \c Activity if the most recently started activity object is valid. + Otherwise, the context is a \c Service. \since 6.2 */ diff --git a/src/corelib/time/qtimezoneprivate.cpp b/src/corelib/time/qtimezoneprivate.cpp index 5b61940cbbe..d8434f4fe1b 100644 --- a/src/corelib/time/qtimezoneprivate.cpp +++ b/src/corelib/time/qtimezoneprivate.cpp @@ -1081,12 +1081,6 @@ QList<QByteArray> QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windows return list; } -// Define template for derived classes to reimplement so QSharedDataPointer clone() works correctly -template<> QTimeZonePrivate *QSharedDataPointer<QTimeZonePrivate>::clone() -{ - return d->clone(); -} - static bool isEntryInIanaList(QByteArrayView id, QByteArrayView ianaIds) { qsizetype cut; diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h index 2714c67b093..b1217402ce7 100644 --- a/src/corelib/time/qtimezoneprivate_p.h +++ b/src/corelib/time/qtimezoneprivate_p.h @@ -209,8 +209,6 @@ protected: }; Q_DECLARE_TYPEINFO(QTimeZonePrivate::Data, Q_RELOCATABLE_TYPE); -template<> QTimeZonePrivate *QSharedDataPointer<QTimeZonePrivate>::clone(); - class Q_AUTOTEST_EXPORT QUtcTimeZonePrivate final : public QTimeZonePrivate { bool operator=(const QUtcTimeZonePrivate &) const = delete; diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index d1e76b250bb..de749ee112e 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -413,14 +413,14 @@ qHashMultiCommutative(size_t seed, const T &... args) template <typename InputIterator> inline size_t qHashRange(InputIterator first, InputIterator last, size_t seed = 0) - noexcept(noexcept(qHash(*first))) // assume iterator operations don't throw + noexcept(noexcept(qHash(*first, 0))) // assume iterator operations don't throw { return std::accumulate(first, last, seed, QtPrivate::QHashCombine(seed)); } template <typename InputIterator> inline size_t qHashRangeCommutative(InputIterator first, InputIterator last, size_t seed = 0) - noexcept(noexcept(qHash(*first))) // assume iterator operations don't throw + noexcept(noexcept(qHash(*first, 0))) // assume iterator operations don't throw { return std::accumulate(first, last, seed, QtPrivate::QHashCombineCommutative(seed)); } diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 93f7ddb9465..a11f7913dc7 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -579,7 +579,12 @@ public: { d->assign(first, last); return *this; } QList &assign(std::initializer_list<T> l) - { return assign(l.begin(), l.end()); } + { + if (l.size()) + return assign(l.begin(), l.end()); + clear(); + return *this; + } template <typename ...Args> iterator emplace(const_iterator before, Args&&... args) diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index e44d9b1468b..518843ffcbd 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -2626,7 +2626,6 @@ void QGuiApplicationPrivate::processEnterEvent(QWindowSystemInterfacePrivate::En } currentMouseWindow = e->enter; - lastCursorPosition = e->globalPos; // TODO later: EnterEvent must report _which_ mouse entered the window; for now we assume primaryPointingDevice() QEnterEvent event(e->localPos, e->localPos, e->globalPos); @@ -3408,6 +3407,15 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E return; QWindowPrivate *p = qt_window_private(window); + if (e->isExposed) { + // If the window has been automatically positioned or resized by the + // window manager, we now assume those have taken effect, even for + // asynchronous window managers. From this point on we want the window + // to keep its geometry, even when recreated. + p->positionAutomatic = false; + p->resizeAutomatic = false; + } + if (!p->receivedExpose) { if (p->resizeEventPending) { // as a convenience for plugins, send a resize event before the first expose event if they haven't done so diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index c23be8eb3ad..bad5932c457 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -582,12 +582,6 @@ void QWindowPrivate::create(bool recursive) platformWindow->initialize(); - // Now that the window is created and initialized the platform has had - // a chance to position and size it automatically. From this point on - // we want the window to keep its geometry, even when recreated. - positionAutomatic = false; - resizeAutomatic = false; - QObjectList childObjects = q->children(); for (int i = 0; i < childObjects.size(); i ++) { QObject *object = childObjects.at(i); diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 5763696266a..38c4a9ea87f 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -2937,6 +2937,8 @@ qreal QPainterPath::percentAtLength(qreal len) const if (len > totalLength) return 1; + Q_ASSERT(totalLength != 0); + if (d->cacheEnabled) { const int ei = qMax(d->elementAtT(len / totalLength), 1); // Skip initial MoveTo qreal res = 0; @@ -3117,6 +3119,7 @@ QPointF QPainterPath::pointAtPercent(qreal t) const qreal curLen = 0; qreal bezierLen = 0; QBezier b = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen); + Q_ASSERT(bezierLen != 0); qreal realT = (totalLength * t - curLen) / bezierLen; return b.pointAt(qBound(qreal(0), realT, qreal(1))); @@ -3141,10 +3144,14 @@ qreal QPainterPath::angleAtPercent(qreal t) const return 0; } + if (isEmpty()) + return 0; + qreal totalLength = length(); qreal curLen = 0; qreal bezierLen = 0; QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen); + Q_ASSERT(bezierLen != 0); qreal realT = (totalLength * t - curLen) / bezierLen; qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4); @@ -3170,10 +3177,14 @@ qreal QPainterPath::slopeAtPercent(qreal t) const return 0; } + if (isEmpty()) + return 0; + qreal totalLength = length(); qreal curLen = 0; qreal bezierLen = 0; QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen); + Q_ASSERT(bezierLen != 0); qreal realT = (totalLength * t - curLen) / bezierLen; qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4); diff --git a/src/gui/platform/darwin/qappleiconengine.mm b/src/gui/platform/darwin/qappleiconengine.mm index 20d50b0d8a8..3228b97fdb0 100644 --- a/src/gui/platform/darwin/qappleiconengine.mm +++ b/src/gui/platform/darwin/qappleiconengine.mm @@ -30,27 +30,28 @@ auto *loadImage(const QString &iconName) {"call-start"_L1, @"phone.arrow.up.right"}, {"call-stop"_L1, @"phone.down"}, {"contact-new"_L1, @"person.crop.circle.badge.plus"}, - {"document-new"_L1, @"doc.badge.plus"}, - {"document-open"_L1, @"folder"}, - {"document-open-recent"_L1, @"doc.badge.clock"}, - {"document-page-setup"_L1, @"doc.badge.gearshape"}, + {"document-new"_L1, @"plus"}, + {"document-open"_L1, @"arrow.up.forward"}, + {"document-open-recent"_L1, @"clock"}, + {"document-page-setup"_L1, @"text.page"}, {"document-print"_L1, @"printer"}, - //{"document-print-preview"_L1, @""}, - {"document-properties"_L1, @"doc.badge.ellipsis"}, - //{"document-revert"_L1, @""}, + {"document-print-preview"_L1, @"richtext.page"}, + {"document-properties"_L1, @"info.circle.text.page"}, + {"document-revert"_L1, @"arrow.counterclockwise"}, {"document-save"_L1, @"square.and.arrow.down"}, - //{"document-save-as"_L1, @""}, + {"document-save-as"_L1, @"square.and.arrow.down.on.square"}, {"document-send"_L1, @"paperplane"}, + {"document-export"_L1, @"square.and.arrow.up"}, {"edit-clear"_L1, @"xmark.circle"}, {"edit-copy"_L1, @"doc.on.doc"}, {"edit-cut"_L1, @"scissors"}, - {"edit-delete"_L1, @"delete.left"}, - {"edit-find"_L1, @"magnifyingglass"}, + {"edit-delete"_L1, @"trash"}, + {"edit-find"_L1, @"text.page.badge.magnifyingglass"}, //{"edit-find-replace"_L1, @"arrow.up.left.and.down.right.magnifyingglass"}, - {"edit-paste"_L1, @"clipboard"}, - {"edit-redo"_L1, @"arrowshape.turn.up.right"}, - //{"edit-select-all"_L1, @""}, - {"edit-undo"_L1, @"arrowshape.turn.up.left"}, + {"edit-paste"_L1, @"document.on.clipboard"}, + {"edit-redo"_L1, @"arrow.uturn.forward"}, + {"edit-select-all"_L1, @"character.textbox"}, + {"edit-undo"_L1, @"arrow.uturn.backward"}, {"folder-new"_L1, @"folder.badge.plus"}, {"format-indent-less"_L1, @"decrease.indent"}, {"format-indent-more"_L1, @"increase.indent"}, @@ -65,35 +66,36 @@ auto *loadImage(const QString &iconName) {"format-text-underline"_L1, @"underline"}, {"format-text-strikethrough"_L1, @"strikethrough"}, //{"go-bottom"_L1, @""}, - {"go-down"_L1, @"arrowshape.down"}, - {"go-first"_L1, @"increase.indent"}, + {"go-down"_L1, @"chevron.down"}, + //{"go-first"_L1, @""}, {"go-home"_L1, @"house"}, //{"go-jump"_L1, @""}, //{"go-last"_L1, @""}, - {"go-next"_L1, @"arrowshape.right"}, - {"go-previous"_L1, @"arrowshape.left"}, + {"go-next"_L1, @"chevron.right"}, + {"go-previous"_L1, @"chevron.left"}, //{"go-top"_L1, @""}, - {"go-up"_L1, @"arrowshape.up"}, + {"go-up"_L1, @"chevron.up"}, {"help-about"_L1, @"info.circle"}, //{"help-contents"_L1, @""}, {"help-faq"_L1, @"questionmark.app"}, {"insert-image"_L1, @"photo.badge.plus"}, {"insert-link"_L1, @"link.badge.plus"}, - //{"insert-object"_L1, @""}, - {"insert-text"_L1, @"textformat"}, + {"insert-object"_L1, @"widget.small.badge.plus"}, + {"insert-text"_L1, @"text.badge.plus"}, {"list-add"_L1, @"plus.circle"}, {"list-remove"_L1, @"minus.circle"}, {"mail-forward"_L1, @"arrowshape.turn.up.right"}, {"mail-mark-important"_L1, @"star"}, {"mail-mark-junk"_L1, @"xmark.bin"}, - {"mail-mark-notjunk"_L1, @"trash.slash"}, + {"mail-mark-notjunk"_L1, @"arrow.up.bin"}, {"mail-mark-read"_L1, @"envelope.open"}, - {"mail-mark-unread"_L1, @"envelope.fill"}, + {"mail-mark-unread"_L1, @"envelope.badge"}, {"mail-message-new"_L1, @"square.and.pencil"}, {"mail-reply-all"_L1, @"arrowshape.turn.up.left.2"}, {"mail-reply-sender"_L1, @"arrowshape.turn.up.left"}, - {"mail-send"_L1, @"paperplane"}, - {"mail-send-receive"_L1, @"envelope.arrow.triangle.branch"}, + {"mail-send"_L1, @"arrow.up.circle.fill"}, + {"mail-send-receive"_L1, @"arrow.trianglehead.2.clockwise.rotate.90"}, + {"mail-replied"_L1, @"envelope.and.arrow.trianglehead.branch"}, {"media-eject"_L1, @"eject"}, {"media-playback-pause"_L1, @"pause"}, {"media-playback-start"_L1, @"play"}, @@ -108,61 +110,61 @@ auto *loadImage(const QString &iconName) {"object-rotate-left"_L1, @"rotate.left"}, {"object-rotate-right"_L1, @"rotate.right"}, {"process-stop"_L1, @"stop.circle"}, - {"system-lock-screen"_L1, @"lock.display"}, - {"system-log-out"_L1, @"door.left.hand.open"}, + {"system-lock-screen"_L1, @"lock"}, + {"system-log-out"_L1, @"person.crop.circle"}, //{"system-run"_L1, @""}, {"system-search"_L1, @"magnifyingglass"}, - //{"system-reboot"_L1, @""}, + {"system-reboot"_L1, @"restart"}, {"system-shutdown"_L1, @"power"}, - //{"tools-check-spelling"_L1, @""}, + {"tools-check-spelling"_L1, @"textformat.characters.dottedunderline"}, {"view-fullscreen"_L1, @"arrow.up.left.and.arrow.down.right"}, {"view-refresh"_L1, @"arrow.clockwise"}, {"view-restore"_L1, @"arrow.down.right.and.arrow.up.left"}, - //{"view-sort-ascending"_L1, @""}, - //{"view-sort-descending"_L1, @""}, + {"view-sort-ascending"_L1, @"arrow.up"}, + {"view-sort-descending"_L1, @"arrow.down"}, {"window-close"_L1, @"xmark.circle"}, {"window-new"_L1, @"macwindow.badge.plus"}, {"zoom-fit-best"_L1, @"square.arrowtriangle.4.outward"}, {"zoom-in"_L1, @"plus.magnifyingglass"}, - //{"zoom-original"_L1, @""}, + {"zoom-original"_L1, @"1.magnifyingglass"}, {"zoom-out"_L1, @"minus.magnifyingglass"}, {"process-working"_L1, @"circle.dotted"}, - //{"accessories-calculator"_L1, @""}, - //{"accessories-character-map"_L1, @""}, + {"accessories-calculator"_L1, @"plus.forwardslash.minus"}, + {"accessories-character-map"_L1, @"keyboard.macwindow"}, {"accessories-dictionary"_L1, @"character.book.closed"}, {"accessories-text-editor"_L1, @"textformat"}, {"help-browser"_L1, @"folder.badge.questionmark"}, {"multimedia-volume-control"_L1, @"speaker.wave.3"}, {"preferences-desktop-accessibility"_L1, @"accessibility"}, - //{"preferences-desktop-font"_L1, @""}, - {"preferences-desktop-keyboard"_L1, @"keyboard.badge.ellipsis"}, - //{"preferences-desktop-locale"_L1, @""}, - //{"preferences-desktop-multimedia"_L1, @""}, - //{"preferences-desktop-screensaver"_L1, @""}, - //{"preferences-desktop-theme"_L1, @""}, - //{"preferences-desktop-wallpaper"_L1, @""}, + {"preferences-desktop-font"_L1, @"textformat.characters"}, + {"preferences-desktop-keyboard"_L1, @"keyboard"}, + {"preferences-desktop-locale"_L1, @"mappin.and.ellipse"}, + {"preferences-desktop-multimedia"_L1, @"music.note.tv"}, + {"preferences-desktop-screensaver"_L1, @"display"}, + {"preferences-desktop-theme"_L1, @"paintpalette"}, + {"preferences-desktop-wallpaper"_L1, @"photo.tv"}, {"system-file-manager"_L1, @"folder.badge.gearshape"}, - //{"system-software-install"_L1, @""}, - //{"system-software-update"_L1, @""}, d - //{"utilities-system-monitor"_L1, @""}, + {"system-software-install"_L1, @"laptopcomputer.and.arrow.down"}, + {"system-software-update"_L1, @"laptopcomputer.badge.checkmark"}, + {"utilities-system-monitor"_L1, @"chart.bar.xaxis"}, {"utilities-terminal"_L1, @"apple.terminal"}, - //{"applications-accessories"_L1, @""}, - //{"applications-development"_L1, @""}, - //{"applications-engineering"_L1, @""}, - {"applications-games"_L1, @"gamecontroller"}, - //{"applications-graphics"_L1, @""}, + {"applications-accessories"_L1, @"scanner"}, + {"applications-development"_L1, @"hammer"}, + {"applications-engineering"_L1, @"wrench.and.screwdriver"}, + {"applications-games"_L1, @"gamecontroller.circle"}, + {"applications-graphics"_L1, @"theatermask.and.paintbrush"}, {"applications-internet"_L1, @"network"}, {"applications-multimedia"_L1, @"tv.and.mediabox"}, - //{"applications-office"_L1, @""}, - //{"applications-other"_L1, @""}, + {"applications-office"_L1, @"square.and.pencil.circle"}, + {"applications-other"_L1, @"app.grid"}, {"applications-science"_L1, @"atom"}, - //{"applications-system"_L1, @""}, - //{"applications-utilities"_L1, @""}, + {"applications-system"_L1, @"gear"}, + {"applications-utilities"_L1, @"wrench.and.screwdriver"}, {"preferences-desktop"_L1, @"menubar.dock.rectangle"}, - //{"preferences-desktop-peripherals"_L1, @""}, - //{"preferences-desktop-personal"_L1, @""}, - //{"preferences-other"_L1, @""}, - //{"preferences-system"_L1, @""}, + {"preferences-desktop-peripherals"_L1, @"iphone.and.ipod"}, + {"preferences-desktop-personal"_L1, @"person.badge.key"}, + {"preferences-other"_L1, @"gear"}, + {"preferences-system"_L1, @"gear"}, {"preferences-system-network"_L1, @"network"}, {"system-help"_L1, @"questionmark.diamond"}, {"audio-card"_L1, @"waveform.circle"}, @@ -175,40 +177,40 @@ auto *loadImage(const QString &iconName) {"drive-harddisk"_L1, @"internaldrive"}, {"drive-optical"_L1, @"opticaldiscdrive"}, {"drive-removable-media"_L1, @"externaldrive"}, - {"input-gaming"_L1, @"gamecontroller"}, // "games" also using this one + {"input-gaming"_L1, @"gamecontroller"}, {"input-keyboard"_L1, @"keyboard"}, {"input-mouse"_L1, @"computermouse"}, {"input-tablet"_L1, @"ipad"}, {"media-flash"_L1, @"mediastick"}, //{"media-floppy"_L1, @""}, - //{"media-optical"_L1, @""}, + {"media-optical"_L1, @"opticaldisc"}, {"media-tape"_L1, @"recordingtape"}, - //{"modem"_L1, @""}, - {"multimedia-player"_L1, @"play.rectangle"}, + {"modem"_L1, @"phone.connection"}, + {"multimedia-player"_L1, @"ipod"}, {"network-wired"_L1, @"app.connected.to.app.below.fill"}, {"network-wireless"_L1, @"wifi"}, {"network-workgroup"_L1, @"network"}, - //{"pda"_L1, @""}, + {"pda"_L1, @"flipphone"}, {"phone"_L1, @"iphone"}, {"printer"_L1, @"printer"}, {"scanner"_L1, @"scanner"}, {"video-display"_L1, @"play.display"}, - //{"emblem-default"_L1, @""}, + {"emblem-default"_L1, @"circle"}, {"emblem-documents"_L1, @"doc.circle"}, {"emblem-downloads"_L1, @"arrow.down.circle"}, - {"emblem-favorite"_L1, @"star"}, - {"emblem-important"_L1, @"exclamationmark.bubble.circle"}, - {"emblem-mail"_L1, @"envelope"}, - {"emblem-photos"_L1, @"photo.stack"}, - //{"emblem-readonly"_L1, @""}, - {"emblem-shared"_L1, @"folder.badge.person.crop"}, + {"emblem-favorite"_L1, @"star.circle"}, + {"emblem-important"_L1, @"exclamationmark.circle"}, + {"emblem-mail"_L1, @"envelope.circle"}, + {"emblem-photos"_L1, @"photo.circle"}, + {"emblem-readonly"_L1, @"eye.circle"}, + {"emblem-shared"_L1, @"person.2.circle"}, {"emblem-symbolic-link"_L1, @"link.circle"}, {"emblem-synchronized"_L1, @"arrow.triangle.2.circlepath.circle"}, - {"emblem-system"_L1, @"gear"}, - //{"emblem-unreadable"_L1, @""}, + {"emblem-system"_L1, @"gear.circle"}, + {"emblem-unreadable"_L1, @"eye.slash.circle"}, {"text-x-generic"_L1, @"doc"}, // until iOS 18/macOS 15; @"document" after that {"folder"_L1, @"folder"}, - //{"folder-remote"_L1, @""}, + {"folder-remote"_L1, @"icloud"}, {"network-server"_L1, @"server.rack"}, //{"start-here"_L1, @""}, {"user-bookmarks"_L1, @"bookmark.circle"}, @@ -229,28 +231,28 @@ auto *loadImage(const QString &iconName) {"dialog-question"_L1, @"questionmark.circle"}, {"dialog-warning"_L1, @"exclamationmark.octagon"}, {"folder-drag-accept"_L1, @"plus.rectangle.on.folder"}, - //{"folder-open"_L1, @""}, + {"folder-open"_L1, @"arrow.forward.folder"}, {"folder-visiting"_L1, @"folder.circle"}, - {"image-loading"_L1, @"photo.circle"}, - {"image-missing"_L1, @"photo"}, + {"image-loading"_L1, @"photo.badge.arrow.down"}, + {"image-missing"_L1, @"photo.badge.exclamationmark"}, {"mail-attachment"_L1, @"paperclip"}, {"mail-unread"_L1, @"envelope.badge"}, {"mail-read"_L1, @"envelope.open"}, - {"mail-replied"_L1, @"arrowshape.turn.up.left"}, + //{"mail-replied"_L1, @""}, //{"mail-signed"_L1, @""}, - //{"mail-signed-verified"_L1, @""}, - {"media-playlist-repeat"_L1, @"repet"}, + {"mail-signed-verified"_L1, @"envelope.badge.shield.half.filled"}, + {"media-playlist-repeat"_L1, @"repeat"}, {"media-playlist-shuffle"_L1, @"shuffle"}, - //{"network-error"_L1, @""}, - //{"network-idle"_L1, @""}, - {"network-offline"_L1, @"network.slash"}, - //{"network-receive"_L1, @""}, - //{"network-transmit"_L1, @""}, - //{"network-transmit-receive"_L1, @""}, + {"network-error"_L1, @"xmark.icloud"}, + {"network-idle"_L1, @"icloud"}, + {"network-offline"_L1, @"icloud.slash"}, + {"network-receive"_L1, @"icloud.and.arrow.down"}, + {"network-transmit"_L1, @"icloud.and.arrow.up"}, + {"network-transmit-receive"_L1, @"arrow.trianglehead.2.clockwise.rotate.90.icloud"}, //{"printer-error"_L1, @""}, - {"printer-printing"_L1, @"printer.dotmatrix.filled.and.paper"}, // not sure - {"security-high"_L1, @"lock.shield"}, - //{"security-medium"_L1, @""}, + {"printer-printing"_L1, @"printer.dotmatrix"}, + {"security-high"_L1, @"lock.badge.checkmark"}, + {"security-medium"_L1, @"lock"}, {"security-low"_L1, @"lock.trianglebadge.exclamationmark"}, {"software-update-available"_L1, @"arrowshape.up.circle"}, {"software-update-urgent"_L1, @"exclamationmark.transmission"}, @@ -258,9 +260,9 @@ auto *loadImage(const QString &iconName) {"sync-synchronizing"_L1, @"arrow.triangle.2.circlepath"}, {"task-due"_L1, @"clock.badge.exclamationmark"}, {"task-past-due"_L1, @"clock.badge.xmark"}, - {"user-available"_L1, @"person.crop.circle.badge.checkmark"}, - {"user-away"_L1, @"person.crop.circle.badge.clock"}, - //{"user-idle"_L1, @""}, + {"user-available"_L1, @"person.crop.circle.badge"}, + {"user-away"_L1, @"person.crop.circle.badge.moon"}, + {"user-idle"_L1, @"person.crop.circle.badge.clock"}, {"user-offline"_L1, @"person.crop.circle.badge.xmark"}, //{"user-trash-full"_L1, @""}, {"weather-clear"_L1, @"sun.max"}, @@ -269,9 +271,9 @@ auto *loadImage(const QString &iconName) {"weather-few-clouds-night"_L1, @"cloud.moon"}, {"weather-fog"_L1, @"cloud.fog"}, {"weather-overcast"_L1, @"cloud"}, - //{"weather-severe-alert"_L1, @""}, - {"weather-showers"_L1, @"cloud.rain"}, - //{"weather-showers-scattered"_L1, @""}, + {"weather-severe-alert"_L1, @"cloud.bolt.rain"}, + {"weather-showers"_L1, @"cloud.heavyrain"}, + {"weather-showers-scattered"_L1, @"cloud.drizzle"}, {"weather-snow"_L1, @"cloud.snow"}, {"weather-storm"_L1, @"tropicalstorm"}, }; diff --git a/src/gui/platform/darwin/qapplekeymapper.mm b/src/gui/platform/darwin/qapplekeymapper.mm index 214865864ff..49f7a8ff0cb 100644 --- a/src/gui/platform/darwin/qapplekeymapper.mm +++ b/src/gui/platform/darwin/qapplekeymapper.mm @@ -94,60 +94,10 @@ static CarbonModifiers toCarbonModifiers(Qt::KeyboardModifiers qtModifiers) return carbonModifiers; } -// Keyboard keys (non-modifiers) -static QHash<char16_t, Qt::Key> standardKeys = { - { kHomeCharCode, Qt::Key_Home }, - { kEnterCharCode, Qt::Key_Enter }, - { kEndCharCode, Qt::Key_End }, - { kBackspaceCharCode, Qt::Key_Backspace }, - { kTabCharCode, Qt::Key_Tab }, - { kPageUpCharCode, Qt::Key_PageUp }, - { kPageDownCharCode, Qt::Key_PageDown }, - { kReturnCharCode, Qt::Key_Return }, - { kEscapeCharCode, Qt::Key_Escape }, - { kLeftArrowCharCode, Qt::Key_Left }, - { kRightArrowCharCode, Qt::Key_Right }, - { kUpArrowCharCode, Qt::Key_Up }, - { kDownArrowCharCode, Qt::Key_Down }, - { kHelpCharCode, Qt::Key_Help }, - { kDeleteCharCode, Qt::Key_Delete }, - // ASCII maps, for debugging - { ':', Qt::Key_Colon }, - { ';', Qt::Key_Semicolon }, - { '<', Qt::Key_Less }, - { '=', Qt::Key_Equal }, - { '>', Qt::Key_Greater }, - { '?', Qt::Key_Question }, - { '@', Qt::Key_At }, - { ' ', Qt::Key_Space }, - { '!', Qt::Key_Exclam }, - { '"', Qt::Key_QuoteDbl }, - { '#', Qt::Key_NumberSign }, - { '$', Qt::Key_Dollar }, - { '%', Qt::Key_Percent }, - { '&', Qt::Key_Ampersand }, - { '\'', Qt::Key_Apostrophe }, - { '(', Qt::Key_ParenLeft }, - { ')', Qt::Key_ParenRight }, - { '*', Qt::Key_Asterisk }, - { '+', Qt::Key_Plus }, - { ',', Qt::Key_Comma }, - { '-', Qt::Key_Minus }, - { '.', Qt::Key_Period }, - { '/', Qt::Key_Slash }, - { '[', Qt::Key_BracketLeft }, - { ']', Qt::Key_BracketRight }, - { '\\', Qt::Key_Backslash }, - { '_', Qt::Key_Underscore }, - { '`', Qt::Key_QuoteLeft }, - { '{', Qt::Key_BraceLeft }, - { '}', Qt::Key_BraceRight }, - { '|', Qt::Key_Bar }, - { '~', Qt::Key_AsciiTilde }, - { '^', Qt::Key_AsciiCircum } -}; - -static QHash<char16_t, Qt::Key> virtualKeys = { +// NSEvent.keyCode codes for keys that are independent of keyboard layout. +// Some of these are technically possible to add custom key maps for, but +// doing so would be unexpected. +static QHash<char16_t, Qt::Key> layoutIndependentKeyCodes = { { kVK_F1, Qt::Key_F1 }, { kVK_F2, Qt::Key_F2 }, { kVK_F3, Qt::Key_F3 }, @@ -164,16 +114,57 @@ static QHash<char16_t, Qt::Key> virtualKeys = { { kVK_F14, Qt::Key_F14 }, { kVK_F15, Qt::Key_F15 }, { kVK_F16, Qt::Key_F16 }, + { kVK_F17, Qt::Key_F17 }, + { kVK_F18, Qt::Key_F18 }, + { kVK_F19, Qt::Key_F19 }, + { kVK_F20, Qt::Key_F20 }, + { kVK_Return, Qt::Key_Return }, { kVK_Tab, Qt::Key_Tab }, + { kVK_Space, Qt::Key_Space }, { kVK_Escape, Qt::Key_Escape }, - { kVK_Help, Qt::Key_Help }, + { kVK_Delete, Qt::Key_Backspace }, + { kVK_ForwardDelete, Qt::Key_Delete }, + + { kVK_Home, Qt::Key_Home }, + { kVK_End, Qt::Key_End }, + { kVK_PageUp, Qt::Key_PageUp }, + { kVK_PageDown, Qt::Key_PageDown }, + { kVK_UpArrow, Qt::Key_Up }, { kVK_DownArrow, Qt::Key_Down }, { kVK_LeftArrow, Qt::Key_Left }, { kVK_RightArrow, Qt::Key_Right }, - { kVK_PageUp, Qt::Key_PageUp }, - { kVK_PageDown, Qt::Key_PageDown } + + { kVK_CapsLock, Qt::Key_CapsLock }, + { kVK_Shift, Qt::Key_Shift }, + { kVK_RightShift, Qt::Key_Shift }, + +#if 0 + // FIXME: Map these here instead of relying on + // custom logic in [QNSView flagsChanged:] + + { kVK_Command, Qt::Key_unknown }, + { kVK_RightCommand, Qt::Key_unknown }, + { kVK_Option, Qt::Key_unknown }, + { kVK_RightOption, Qt::Key_unknown }, + { kVK_Control, Qt::Key_unknown }, + { kVK_RightControl, Qt::Key_unknown }, + { kVK_Function, Qt::Key_unknown }, +#endif + + { kVK_VolumeUp, Qt::Key_VolumeUp }, + { kVK_VolumeDown, Qt::Key_VolumeDown }, + { kVK_Mute, Qt::Key_VolumeMute }, + +#if 0 + // FIXME: Figure out which Qt::Key this maps to + { kVK_ContextualMenu, Qt::Key_unknown }, +#endif + { kVK_Help, Qt::Key_Help }, + + { kVK_ANSI_KeypadClear, Qt::Key_Clear }, + { kVK_ANSI_KeypadEnter, Qt::Key_Enter }, }; static QHash<char16_t, Qt::Key> functionKeys = { @@ -211,41 +202,19 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers) qCDebug(lcQpaKeyMapperKeys, "Mapping key: %d (0x%04x) / vk %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey); - if (key == char16_t(kClearCharCode) && virtualKey == 0x47) - return Qt::Key_Clear; - - if (key.isDigit()) { - qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue()); - return key.digitValue() + Qt::Key_0; - } - - if (key.isLetter()) { - qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A')); - return (key.toUpper().unicode() - 'A') + Qt::Key_A; - } - if (key.isSymbol()) { - qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode())); - return key.unicode(); - } - - if (auto qtKey = standardKeys.value(key.unicode())) { + // Check first if we have a virtual key that should be treated as layout + // independent. If so, we want to return early without inspecting the key. + if (auto qtKey = layoutIndependentKeyCodes.value(virtualKey)) { + qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey << "based on layout independent virtual key"; // To work like Qt for X11 we issue Backtab when Shift + Tab are pressed if (qtKey == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { - qCDebug(lcQpaKeyMapperKeys, "Got key: Qt::Key_Backtab"); + qCDebug(lcQpaKeyMapperKeys, "Transformed into Qt::Key_Backtab"); return Qt::Key_Backtab; } - - qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; - return qtKey; - } - - // Last ditch try to match the scan code - if (auto qtKey = virtualKeys.value(virtualKey)) { - qCDebug(lcQpaKeyMapperKeys) << "Got scancode" << qtKey; return qtKey; } - // Check if they belong to key codes in private unicode range + // Then check if the key is one of the functions keys in the private Unicode range if (key >= char16_t(NSUpArrowFunctionKey) && key <= char16_t(NSModeSwitchFunctionKey)) { if (auto qtKey = functionKeys.value(key.unicode())) { qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; @@ -257,6 +226,20 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers) } } + if (key.isDigit()) { + qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue()); + return key.digitValue() + Qt::Key_0; + } + + if (key.isLetter()) { + qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A')); + return (key.toUpper().unicode() - 'A') + Qt::Key_A; + } + if (key.isSymbol()) { + qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode())); + return key.unicode(); + } + qCDebug(lcQpaKeyMapperKeys, "Unknown case.. %d[%d] %d", key.unicode(), key.toLatin1(), virtualKey); return Qt::Key_unknown; } diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index ec67ee80a1e..82632110d32 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -140,13 +140,6 @@ bool QHostAddressPrivate::parse(const QString &ipString) return false; } -void QHostAddressPrivate::clear() -{ - a = 0; - protocol = QHostAddress::UnknownNetworkLayerProtocol; - memset(&a6, 0, sizeof(a6)); -} - AddressClassification QHostAddressPrivate::classify() const { if (a) { diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h index 6cc28cd5a9b..608080e9ede 100644 --- a/src/network/kernel/qhostaddress_p.h +++ b/src/network/kernel/qhostaddress_p.h @@ -74,7 +74,13 @@ public: void setAddress(const Q_IPV6ADDR &a_); bool parse(const QString &ipString); - void clear(); + void clear() + { + a6 = {}; + a = 0; + protocol = QHostAddress::UnknownNetworkLayerProtocol; + scopeId.clear(); + } QString scopeId; diff --git a/src/network/kernel/qnetworkinformation.cpp b/src/network/kernel/qnetworkinformation.cpp index 80551b64633..5c4e65839c5 100644 --- a/src/network/kernel/qnetworkinformation.cpp +++ b/src/network/kernel/qnetworkinformation.cpp @@ -16,7 +16,6 @@ #include <algorithm> #include <memory> -#include <mutex> QT_BEGIN_NAMESPACE diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 120d2ecbc78..430197ccc6e 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -33,7 +33,7 @@ #include <sys/socket.h> #include <netinet/sctp.h> #endif -#ifdef Q_OS_BSD4 +#ifdef AF_LINK # include <net/if_dl.h> #endif @@ -142,10 +142,6 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, level = IPPROTO_IP; #ifdef IP_PKTINFO n = IP_PKTINFO; -#elif defined(IP_RECVDSTADDR) - // variant found in QNX and FreeBSD; it will get us only the - // destination address, not the interface; we need IP_RECVIF for that. - n = IP_RECVDSTADDR; #endif } break; @@ -360,6 +356,18 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::BindExclusively: return true; + case QNativeSocketEngine::ReceivePacketInformation: + if (socketProtocol == QAbstractSocket::IPv4Protocol) { +#if !defined(IP_PKTINFO) && defined(IP_RECVDSTADDR) && defined(IP_RECVIF) + // Seen on FreeBSD and QNX. We need both to get the information we want. + int r = 0; + r += ::setsockopt(socketDescriptor, IPPROTO_IP, IP_RECVDSTADDR, &v, sizeof(v)); + r += ::setsockopt(socketDescriptor, IPPROTO_IP, IP_RECVIF, &v, sizeof(v)); + return r == 0; +#endif + } + break; + case QNativeSocketEngine::MaxStreamsSocketOption: { #ifndef QT_NO_SCTP sctp_initmsg sctpInitMsg; @@ -915,7 +923,7 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS { // we use quintptr to force the alignment quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) -#if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(Q_OS_BSD4) +#if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(AF_LINK) + CMSG_SPACE(sizeof(sockaddr_dl)) #endif #ifndef QT_NO_SCTP @@ -1007,7 +1015,7 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS header->destinationAddress.setAddress(ntohl(addr->s_addr)); } # endif -# if defined(IP_RECVIF) && defined(Q_OS_BSD4) +# if defined(IP_RECVIF) && defined(AF_LINK) if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVIF && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sockaddr_dl))) { sockaddr_dl *sdl = reinterpret_cast<sockaddr_dl *>(CMSG_DATA(cmsgptr)); diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index d4c5d0f0425..e0ef6cec794 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -161,7 +161,8 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; bool selectable = (m_options->acceptMode() == QFileDialogOptions::AcceptSave) || [self panel:m_panel shouldEnableURL:url]; - m_panel.nameFieldStringValue = selectable ? info.fileName().toNSString() : @""; + if (!openpanel_cast(m_panel)) + m_panel.nameFieldStringValue = selectable ? info.fileName().toNSString() : @""; [self updateProperties]; diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm index 4e019b69cc4..6ca6554f673 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm @@ -7,8 +7,10 @@ #include "qiosdocumentpickercontroller.h" +#include <QtCore/qpointer.h> + @implementation QIOSDocumentPickerController { - QIOSFileDialog *m_fileDialog; + QPointer<QIOSFileDialog> m_fileDialog; } - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog @@ -61,6 +63,9 @@ { Q_UNUSED(controller); + if (!m_fileDialog) + return; + QList<QUrl> files; for (NSURL* url in urls) files.append(QUrl::fromNSURL(url)); @@ -71,12 +76,18 @@ - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { + if (!m_fileDialog) + return; + Q_UNUSED(controller); emit m_fileDialog->reject(); } - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController { + if (!m_fileDialog) + return; + Q_UNUSED(presentationController); // "Called on the delegate when the user has taken action to dismiss the diff --git a/src/plugins/platforms/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp index 614d5bd25a3..18a457198f1 100644 --- a/src/plugins/platforms/wasm/qwasminputcontext.cpp +++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp @@ -225,8 +225,8 @@ void QWasmInputContext::updateGeometry() qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "propagating inputItemRectangle:" << inputItemRectangle; m_inputElement["style"].set("left", std::to_string(inputItemRectangle.x()) + "px"); m_inputElement["style"].set("top", std::to_string(inputItemRectangle.y()) + "px"); - m_inputElement["style"].set("width", std::to_string(inputItemRectangle.width()) + "px"); - m_inputElement["style"].set("height", std::to_string(inputItemRectangle.height()) + "px"); + m_inputElement["style"].set("width", "1px"); + m_inputElement["style"].set("height", "1px"); } } diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp index 25142612c4f..3b1b7a7eacb 100644 --- a/src/plugins/styles/modernwindows/qwindows11style.cpp +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -923,15 +923,13 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption case PE_IndicatorRadioButton: { const bool isRtl = option->direction == Qt::RightToLeft; const bool isOn = option->state & State_On; - qreal innerRadius = 7.0f; + qreal innerRadius = radioButtonInnerRadius(state); if (d->transitionsEnabled() && option->styleObject) { if (option->styleObject->property("_q_end_radius").isNull()) option->styleObject->setProperty("_q_end_radius", innerRadius); QNumberStyleAnimation *animation = qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject)); innerRadius = animation ? animation->currentValue() : option->styleObject->property("_q_end_radius").toFloat(); option->styleObject->setProperty("_q_inner_radius", innerRadius); - } else { - innerRadius = radioButtonInnerRadius(state); } QRectF rect = isRtl ? option->rect.adjusted(0, 0, -2, 0) : option->rect.adjusted(2, 0, 0, 0); @@ -2452,9 +2450,6 @@ void QWindows11Style::polish(QPalette& result) SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color()); - if (highContrastTheme) - result.setColor(QPalette::Active, QPalette::HighlightedText, result.windowText().color()); - auto *d = const_cast<QWindows11StylePrivate *>(d_func()); d->m_titleBarMinIcon = QIcon(); d->m_titleBarMaxIcon = QIcon(); diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index ebb32238d67..ad3874bd4c9 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -430,7 +430,7 @@ namespace QTest Q_TESTLIB_EXPORT int qExec(QObject *testObject, const QStringList &arguments); #if QT_CONFIG(batch_test_support) || defined(Q_QDOC) - using TestEntryFunction = int (*)(int, char **); + using TestEntryFunction = std::function<int(int, char **)>; Q_TESTLIB_EXPORT void qRegisterTestCase(const QString &name, TestEntryFunction entryFunction); #endif // QT_CONFIG(batch_test_support) diff --git a/src/testlib/qtestregistry_p.h b/src/testlib/qtestregistry_p.h index 85e236cd046..fcfa6c60701 100644 --- a/src/testlib/qtestregistry_p.h +++ b/src/testlib/qtestregistry_p.h @@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE namespace QTest { class TestRegistry { public: - using TestEntryFunction = int(*)(int argv, char** argc); + using TestEntryFunction = std::function<int(int, char **)>; static TestRegistry* instance(); diff --git a/src/tools/macdeployqt/macdeployqt/main.cpp b/src/tools/macdeployqt/macdeployqt/main.cpp index f5e6ab8f31a..ecbccdef2b3 100644 --- a/src/tools/macdeployqt/macdeployqt/main.cpp +++ b/src/tools/macdeployqt/macdeployqt/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char **argv) QStringList qmlDirs; QStringList qmlImportPaths; extern bool runCodesign; - extern QString codesignIdentiy; + QString codesignIdentity = QStringLiteral("-"); extern bool hardenedRuntime; bool noCodesignExplicit = false; extern bool appstoreCompliant; @@ -166,7 +166,7 @@ int main(int argc, char **argv) return 1; } else { runCodesign = true; - codesignIdentiy = argument.mid(index+1); + codesignIdentity = argument.mid(index + 1); } } else if (argument.startsWith(QByteArray("-sign-for-notarization"))) { LogDebug() << "Argument found:" << argument; @@ -182,7 +182,7 @@ int main(int argc, char **argv) runCodesign = true; hardenedRuntime = true; secureTimestamp = true; - codesignIdentiy = argument.mid(index+1); + codesignIdentity = argument.mid(index + 1); } } else if (argument.startsWith(QByteArray("-hardened-runtime"))) { LogDebug() << "Argument found:" << argument; @@ -273,7 +273,7 @@ int main(int argc, char **argv) stripAppBinary(appBundlePath); if (runCodesign) - codesign(codesignIdentiy, appBundlePath); + codesign(codesignIdentity, appBundlePath); if (dmg) { LogNormal(); diff --git a/src/tools/macdeployqt/shared/shared.cpp b/src/tools/macdeployqt/shared/shared.cpp index 4e81229ebf5..bd7f4fba854 100644 --- a/src/tools/macdeployqt/shared/shared.cpp +++ b/src/tools/macdeployqt/shared/shared.cpp @@ -30,7 +30,6 @@ bool runStripEnabled = true; bool alwaysOwerwriteEnabled = false; bool runCodesign = true; QStringList librarySearchPath; -QString codesignIdentiy = "-"; QString extraEntitlements; bool hardenedRuntime = false; bool secureTimestamp = false; diff --git a/src/widgets/doc/images/designer-stylesheet-options.png b/src/widgets/doc/images/designer-stylesheet-options.png Binary files differdeleted file mode 100644 index a6893e770bc..00000000000 --- a/src/widgets/doc/images/designer-stylesheet-options.png +++ /dev/null diff --git a/src/widgets/doc/images/designer-stylesheet-options.webp b/src/widgets/doc/images/designer-stylesheet-options.webp Binary files differnew file mode 100644 index 00000000000..14d1ad368fc --- /dev/null +++ b/src/widgets/doc/images/designer-stylesheet-options.webp diff --git a/src/widgets/doc/images/designer-stylesheet-usage.png b/src/widgets/doc/images/designer-stylesheet-usage.png Binary files differdeleted file mode 100644 index f6875900def..00000000000 --- a/src/widgets/doc/images/designer-stylesheet-usage.png +++ /dev/null diff --git a/src/widgets/doc/images/designer-stylesheet-usage.webp b/src/widgets/doc/images/designer-stylesheet-usage.webp Binary files differnew file mode 100644 index 00000000000..79dd6803853 --- /dev/null +++ b/src/widgets/doc/images/designer-stylesheet-usage.webp diff --git a/src/widgets/doc/images/designer-validator-highlighter.png b/src/widgets/doc/images/designer-validator-highlighter.png Binary files differdeleted file mode 100644 index a6661d5c955..00000000000 --- a/src/widgets/doc/images/designer-validator-highlighter.png +++ /dev/null diff --git a/src/widgets/doc/images/designer-validator-highlighter.webp b/src/widgets/doc/images/designer-validator-highlighter.webp Binary files differnew file mode 100644 index 00000000000..7ca6cdf6eb3 --- /dev/null +++ b/src/widgets/doc/images/designer-validator-highlighter.webp diff --git a/src/widgets/doc/src/modelview.qdoc b/src/widgets/doc/src/modelview.qdoc index b2448a2c705..121cc30ed09 100644 --- a/src/widgets/doc/src/modelview.qdoc +++ b/src/widgets/doc/src/modelview.qdoc @@ -573,281 +573,10 @@ out of range when using ModelTest. - \section1 4. Good Sources of Additional Information - - \section2 4.1 Books - - Model/View programming is covered quite extensively in the documentation of - Qt but also in several good books. - - \list 1 - \li \b{C++ GUI Programming with Qt 4} / Jasmin Blanchette, Mark Summerfield, - \e{Prentice Hall, 2nd edition}, ISBN 0-13-235416-0. Also available in - German: \b{C++ GUI Programmierung mit Qt 4: Die offizielle Einführung}, - \e{Addison-Wesley}, ISBN 3-827327-29-6 - \li \b{The Book of Qt4, The Art of Building Qt Applications} / Daniel Molkentin, - \e{Open Source Press}, ISBN 1-59327-147-6. - Translated from \b{Qt 4, Einführung in die Applikationsentwicklung}, - \e{Open Source Press}, ISBN 3-937514-12-0. - \li \b{Foundations of Qt Development} / Johan Thelin, \e{Apress}, ISBN 1-59059-831-8. - \li \b{Advanced Qt Programming} / Mark Summerfield, \e{Prentice Hall}, ISBN 0-321-63590-6. - This book covers Model/View programming on more than 150 pages. - \endlist - - The following list provides an overview of example programs contained in the first three - books listed above. Some of them make very good templates for developing similar - applications. - - \table - \header - \li Example name - \li View class used - \li Model used - \li Aspects covered - \li - \row - \li Team Leaders - \li QListview - \li QStringListModel - \li - \li Book 1, Chapter 10, Figure 10.6 - \row - \li Color Names - \li QListView - \li QSortFilterProxyModel - applied to QStringListModel - \li - \li Book 1, Chapter 10, Figure 10.8 - \row - \li Currencies - \li QTableView - \li custom model based on - QAbstractTableModel - \li Read only - \li Book 1, Chapter 10, Figure 10.10 - \row - \li Cities - \li QTableView - \li Custom model based on - QAbstractTableModel - \li Read / write - \li Book 1, Chapter 10, Figure 10.12 - \row - \li Boolean Parser - \li QTreeView - \li Custom model based on - QAbstractItemModel - \li Read only - \li Book 1, Chapter 10, Figure 10.14 - \row - \li Track Editor - \li {2, 1} QTableWidget - \li Custom delegate providing a custom editor - \li Book 1, Chapter 10, Figure 10.15 - - \row - \li Address Book - \li QListView - QTableView - QTreeView - \li Custom model based on - QAbstractTableModel - \li Read / write - \li Book2, Chapter 8.4 - \row - \li Address Book with sorting - \li - \li QSortfilterProxyModel - \li Introducing sort and filter capabilities - \li Book2, Chapter 8.5 - \row - \li Address Book - with checkboxes - \li - \li - \li Introducing checkboxes in model/view - \li Book2, Chapter 8.6 - \row - \li Address Book with transposed grid - \li - \li Custom proxy Model based on QAbstractProxyModel - \li Introducing a custom model - \li Book2, Chapter 8.7 - \row - \li Address Book with drag and drop - \li - \li - \li Introducing drag and drop support - \li Book2, Chapter 8.8 - \row - \li Address Book with custom editor - \li - \li - \li Introducing custom delegates - \li Book2, Chapter 8.9 - \row - \li Views - \li QListView - QTableView - QTreeView - \li QStandardItemModel - \li Read only - \li Book 3, Chapter 5, figure 5-3 - \row - \li Bardelegate - \li QTableView - \li - \li Custom delegate for presentation based on QAbstractItemDelegate - \li Book 3, Chapter 5, figure 5-5 - \row - \li Editdelegate - \li QTableView - \li - \li Custom delegate for editing based on QAbstractItemDelegate - \li Book 3, Chapter 5, figure 5-6 - \row - \li Singleitemview - \li Custom view based on QAbstractItemView - \li - \li Custom view - \li Book 3, - Chapter 5, - figure 5-7 - \row - \li listmodel - \li QTableView - \li Custom Model based on QAbstractTableModel - \li Read only - \li Book 3, Chapter 5, Figure 5-8 - \row - \li treemodel - \li QTreeView - \li Custom Model based on QAbstractItemModel - \li Read only - \li Book 3, Chapter 5, Figure 5-10 - \row - \li edit integers - \li QListView - \li Custom Model based on QAbstractListModel - \li Read / write - \li Book 3, Chapter 5, Listing 5-37, Figure 5-11 - \row - \li sorting - \li QTableView - \li QSortFilterProxyModel applied to QStringListModel - \li Demonstrates sorting - \li Book 3, Chapter 5, Figure 5-12 - \endtable - - - \section2 4.2 Qt Documentation - - Qt 5.0 comes with 19 examples for model/view. - The examples can be found on the \l{Item Views Examples} page. - - \table - \header - \li Example name - \li View class used - \li Model used - \li Aspects covered - \row - \li Address Book - \li QTableView - \li QAbstractTableModel - QSortFilterProxyModel - \li Usage of QSortFilterProxyModel to generate different - subsets from one data pool - \row - \li Basic Sort/Filter Model - \li QTreeView - \li QStandardItemModel - QSortFilterProxyModel - \li - \row - \li Chart - \li Custom view - \li QStandardItemModel - \li Designing custom views that cooperate with selection models - \row - \li Color Editor Factory - \li {2, 1} QTableWidget - \li Enhancing the standard delegate with a new custom editor to choose colours - \row - \li Combo Widget Mapper - \li QDataWidgetMapper to map QLineEdit, QTextEdit and QComboBox - \li QStandardItemModel - \li Shows how a QComboBox can serve as a view class - \row - \li Custom Sort/Filter Model - \li QTreeView - \li QStandardItemModel - QSortFilterProxyModel - \li Subclass QSortFilterProxyModel for advanced sorting and filtering - \row - \li Dir View - \li QTreeView - \li QFileSystemModel - \li Very small example to demonstrate how to assign a model to a view - \row - \li Editable Tree Model - \li QTreeView - \li Custom tree model - \li Comprehensive example for working with trees, demonstrates - editing cells and tree structure with an underlying custom - model - \row - \li Fetch More - \li QListView - \li Custom list model - \li Dynamically changing model - \row - \li Frozen Column - \li QTableView - \li QStandardItemModel - \li - \row - \li Interview - \li Multiple - \li Custom item model - \li Multiple views - \row - \li Pixelator - \li QTableView - \li Custom table model - \li Implementation of a custom delegate - \row - \li Puzzle - \li QListView - \li Custom list model - \li Model/view with drag and drop - \row - \li Simple DOM Model - \li QTreeView - \li Custom tree model - \li Read only example for a custom tree model - \row - \li Simple Tree Model - \li QTreeView - \li Custom tree model - \li Read only example for a custom tree model - \row - \li Simple Widget Mapper - \li QDataWidgetMapper to map QLineEdit, QTextEdit and QSpinBox - \li QStandardItemModel - \li Basic QDataWidgetMapper usage - \row - \li Spreadsheet - \li {2, 1} QTableView - \li Custom delegates - \row - \li Star Delegate - \li {2, 1} QTableWidget - \li Comprehensive custom delegate example. - \endtable + \section1 Examples - A \l{Model/View Programming}{reference document} for model/view technology - is also available. + Qt comes with multiple examples for model/view. You can find them on the + \l{Item Views Examples} page. */ /*! diff --git a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc index 841948b671f..84226fdb5b5 100644 --- a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc +++ b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc @@ -536,21 +536,20 @@ to preview style sheets. You can right-click on any widget in Designer and select \uicontrol{Change styleSheet...} to set the style sheet. - \image designer-stylesheet-options.png + \image designer-stylesheet-options.webp {Editing a form in Qt Widgets Designer} - In Qt 4.2 and later, \QD also includes a - style sheet syntax highlighter and validator. The validator indicates - if the syntax is valid or invalid, at the bottom left of the \uicontrol{Edit - Style Sheet} dialog. + \QD also includes a style sheet syntax highlighter and validator. The + validator indicates if the syntax is valid or invalid, at the bottom left + of the \uicontrol{Edit Style Sheet} dialog. - \image designer-validator-highlighter.png + \image designer-validator-highlighter.webp {Editing and validating a stylesheet} When you click \uicontrol{OK} or \uicontrol{Apply}, \QD will automatically display the widget with its new stylesheet. - \image designer-stylesheet-usage.png + \image designer-stylesheet-usage.webp {Preview of a form with the new stylesheet} */ diff --git a/src/widgets/itemviews/qabstractitemview.h b/src/widgets/itemviews/qabstractitemview.h index 973a9b044cb..ce509dc9e98 100644 --- a/src/widgets/itemviews/qabstractitemview.h +++ b/src/widgets/itemviews/qabstractitemview.h @@ -209,7 +209,7 @@ public: virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const override; - using QAbstractScrollArea::update; + using QWidget::update; public Q_SLOTS: virtual void reset(); diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 82d16cb1252..b2cfb27e814 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -3419,29 +3419,28 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC } QRect gr = subControlRect(cc, opt, SC_SliderGroove, w); - if (slider->subControls & SC_SliderGroove) { + if (slider->subControls & SC_SliderGroove) grooveSubRule.drawRule(p, gr); - } if (slider->subControls & SC_SliderHandle) { QRect hr = subControlRect(cc, opt, SC_SliderHandle, w); - QRenderRule subRule1 = renderRule(w, opt, PseudoElement_SliderSubPage); - if (subRule1.hasDrawable()) { - QRect r(gr.topLeft(), - slider->orientation == Qt::Horizontal - ? QPoint(hr.x()+hr.width()/2, gr.y()+gr.height() - 1) - : QPoint(gr.x()+gr.width() - 1, hr.y()+hr.height()/2)); - subRule1.drawRule(p, r); - } - - QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SliderAddPage); - if (subRule2.hasDrawable()) { - QRect r(slider->orientation == Qt::Horizontal - ? QPoint(hr.x()+hr.width()/2+1, gr.y()) - : QPoint(gr.x(), hr.y()+hr.height()/2+1), - gr.bottomRight()); - subRule2.drawRule(p, r); + if (slider->subControls & SC_SliderGroove) { + const bool isHor = slider->orientation == Qt::Horizontal; + QRenderRule subRule1 = renderRule(w, opt, PseudoElement_SliderSubPage); + if (subRule1.hasDrawable()) { + QRect r(gr.topLeft(), + isHor ? QPoint(hr.x() + hr.width() / 2, gr.y() + gr.height() - 1) + : QPoint(gr.x() + gr.width() - 1, hr.y() + hr.height() / 2)); + subRule1.drawRule(p, r); + } + QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SliderAddPage); + if (subRule2.hasDrawable()) { + QRect r(isHor ? QPoint(hr.x() + hr.width() / 2 + 1, gr.y()) + : QPoint(gr.x(), hr.y() + hr.height() / 2 + 1), + gr.bottomRight()); + subRule2.drawRule(p, r); + } } handleSubRule.drawRule(p, handleSubRule.boxRect(hr, Margin)); diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index 1ca6b8a47a1..7aff0da3327 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -2220,7 +2220,7 @@ QMdiSubWindow::QMdiSubWindow(QWidget *parent, Qt::WindowFlags flags) d->titleBarPalette = d->desktopPalette(); d->font = QApplication::font("QMdiSubWindowTitleBar"); // We don't want the menu icon by default on mac. -#ifndef Q_OS_MAC +#ifndef Q_OS_DARWIN if (windowIcon().isNull()) d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, nullptr, this); else @@ -2847,8 +2847,11 @@ bool QMdiSubWindow::event(QEvent *event) break; case QEvent::WindowIconChange: d->menuIcon = windowIcon(); + // We don't want the default menu icon on mac. +#ifndef Q_OS_DARWIN if (d->menuIcon.isNull()) d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, nullptr, this); +#endif if (d->controlContainer) d->controlContainer->updateWindowIcon(d->menuIcon); if (!maximizedSystemMenuIconWidget()) diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 69548c4e17e..92ff14dd44f 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -2972,7 +2972,7 @@ void QMenu::mouseReleaseEvent(QMouseEvent *e) #endif d->activateAction(action, QAction::Trigger); } - } else if ((!action || action->isEnabled()) && !action->isSeparator()) { + } else if (!action || (action->isEnabled() && !action->isSeparator())) { d->hideUpToMenuBar(); } } diff --git a/tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp b/tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp index ee50a311efa..185887e5d87 100644 --- a/tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp +++ b/tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp @@ -54,6 +54,7 @@ void tst_qxp_function_ref::basics() { Q_CONSTINIT static int invoked = 0; + invoked = 0; auto lambda = [](int i) noexcept { ++invoked; return i; }; const qxp::function_ref<int(int)> f = lambda; QCOMPARE(invoked, 0); @@ -85,6 +86,7 @@ void tst_qxp_function_ref::basics() } { Q_CONSTINIT static int invoked = 0; + invoked = 0; auto lambda = [] { ++invoked; return 42; }; const qxp::function_ref<int()> f = lambda; QCOMPARE(invoked, 0); @@ -106,6 +108,7 @@ void tst_qxp_function_ref::basics() } { Q_CONSTINIT static int invoked = 0; + invoked = 0; auto lambda = [] { ++invoked; }; const qxp::function_ref<void()> f = lambda; QCOMPARE(invoked, 0); @@ -157,6 +160,7 @@ void tst_qxp_function_ref::constOverloads() void tst_qxp_function_ref::constExpr() { Q_CONSTINIT static int invoked = 0; + invoked = 0; { Q_CONSTINIT static auto lambda = [] (int i) { ++invoked; return i; }; // the function object constructor is constexpr, so this should be constinit: diff --git a/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp index 593cae6ddfc..29b78415924 100644 --- a/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp +++ b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp @@ -92,8 +92,8 @@ private slots: void uncPaths(); #endif #ifndef Q_OS_WIN - void hiddenFiles(); - void hiddenDirs(); + void hidden_data(); + void hidden(); #endif void withStdAlgorithms(); @@ -871,45 +871,46 @@ void tst_QDirListing::uncPaths() // anything starting by a '.' is a hidden file. // For that reason these two tests aren't run on Windows. -void tst_QDirListing::hiddenFiles() +void tst_QDirListing::hidden_data() { - QStringList expected = { + QTest::addColumn<QDirListing::IteratorFlags>("flags"); + QTest::addColumn<QStringList>("expected"); + + using F = QDirListing::IteratorFlag; + + QTest::newRow("Recursive-ExcludeDirs") + << (F::ExcludeDirs | F::IncludeHidden | F::Recursive) + << QStringList{ "hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenFile"_L1, "hiddenDirs_hiddenFiles/.hiddenDirectory/normalFile"_L1, "hiddenDirs_hiddenFiles/.hiddenFile"_L1, "hiddenDirs_hiddenFiles/normalDirectory/.hiddenFile"_L1, "hiddenDirs_hiddenFiles/normalDirectory/normalFile"_L1, "hiddenDirs_hiddenFiles/normalFile"_L1, - }; - - constexpr auto flags = ItFlag::ExcludeDirs | ItFlag::IncludeHidden | ItFlag::Recursive; - QStringList list; - list.reserve(expected.size()); - for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, flags)) { - QVERIFY(dirEntry.isFile()); - list.emplace_back(dirEntry.filePath()); - } - list.sort(); - - QCOMPARE_EQ(list, expected); -} + }; -void tst_QDirListing::hiddenDirs() -{ - QStringList expected = { + QTest::newRow("Recursive-ExcludeFiles") + << (F::ExcludeFiles | F::IncludeHidden | F::Recursive) + << QStringList{ "hiddenDirs_hiddenFiles/.hiddenDirectory"_L1, "hiddenDirs_hiddenFiles/.hiddenDirectory/.hidden-subdir"_L1, "hiddenDirs_hiddenFiles/.hiddenDirectory/subdir"_L1, "hiddenDirs_hiddenFiles/normalDirectory"_L1, "hiddenDirs_hiddenFiles/normalDirectory/.hidden-subdir"_L1, "hiddenDirs_hiddenFiles/normalDirectory/subdir"_L1, - }; + }; +} + +void tst_QDirListing::hidden() +{ + QFETCH(QDirListing::IteratorFlags, flags); + QFETCH(QStringList, expected); - constexpr auto flags = ItFlag::ExcludeFiles | ItFlag::IncludeHidden | ItFlag::Recursive; QStringList list; list.reserve(expected.size()); + bool isDir = flags.testFlags(ItFlag::ExcludeFiles); for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, flags)) { - QVERIFY(dirEntry.isDir()); + QVERIFY(isDir ? dirEntry.isDir() : dirEntry.isFile()); list.emplace_back(dirEntry.filePath()); } list.sort(); diff --git a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp index 560d4196b28..6a898e7af0d 100644 --- a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp +++ b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp @@ -291,7 +291,7 @@ void tst_QProcessEnvironment::putenv() static bool testRan = false; if (testRan) - QFAIL("You cannot run this test more than once, since we modify the environment"); + QSKIP("You cannot run this test more than once, since we modify the environment"); testRan = true; QByteArray valBefore = qgetenv(envname); diff --git a/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp b/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp index 4fb54581a6d..6dceb583469 100644 --- a/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp +++ b/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp @@ -464,7 +464,12 @@ void tst_QRandomAccessAsyncFile::errorHandling_data() << qint64(0) << QIOOperation::Error::FileNotOpen; QTest::newRow("read_writeonly") << QIOOperation::Type::Read << QIODeviceBase::WriteOnly - << qint64(0) << QIOOperation::Error::Read; + << qint64(0) +#ifdef Q_OS_DARWIN + << QIOOperation::Error::FileNotOpen; +#else + << QIOOperation::Error::Read; +#endif QTest::newRow("read_negative_offset") << QIOOperation::Type::Read << QIODeviceBase::ReadOnly << qint64(-1) << QIOOperation::Error::IncorrectOffset; @@ -478,7 +483,12 @@ void tst_QRandomAccessAsyncFile::errorHandling_data() << qint64(0) << QIOOperation::Error::FileNotOpen; QTest::newRow("write_readonly") << QIOOperation::Type::Write << QIODeviceBase::ReadOnly - << qint64(0) << QIOOperation::Error::Write; + << qint64(0) +#ifdef Q_OS_DARWIN + << QIOOperation::Error::FileNotOpen; +#else + << QIOOperation::Error::Write; +#endif QTest::newRow("write_negative_offset") << QIOOperation::Type::Write << QIODeviceBase::WriteOnly << qint64(-1) << QIOOperation::Error::IncorrectOffset; @@ -545,8 +555,9 @@ void tst_QRandomAccessAsyncFile::fileClosedInProgress() } constexpr qint64 OneMb = 1024 * 1024; - std::array<QIOOperation *, 5> operations; - std::array<QByteArray, 5> buffers; + constexpr size_t NumOps = 5; + std::array<QIOOperation *, NumOps> operations; + std::array<QByteArray, NumOps> buffers; for (size_t i = 0; i < operations.size(); ++i) { const qint64 offset = i * OneMb; @@ -596,8 +607,9 @@ void tst_QRandomAccessAsyncFile::fileRemovedInProgress() QFETCH(const QIOOperation::Type, operation); constexpr qint64 OneMb = 1024 * 1024; - std::array<QIOOperation *, 5> operations; - std::array<QByteArray, 5> buffers; + constexpr size_t NumOps = 5; + std::array<QIOOperation *, NumOps> operations; + std::array<QByteArray, NumOps> buffers; { QRandomAccessAsyncFile file; @@ -655,8 +667,9 @@ void tst_QRandomAccessAsyncFile::operationsDeletedInProgress() } constexpr qint64 OneMb = 1024 * 1024; - std::array<QIOOperation *, 5> operations; - std::array<QByteArray, 5> buffers; + constexpr size_t NumOps = 5; + std::array<QIOOperation *, NumOps> operations; + std::array<QByteArray, NumOps> buffers; for (size_t i = 0; i < operations.size(); ++i) { const qint64 offset = i * OneMb; diff --git a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp index 472e03adcac..00672ef0b6a 100644 --- a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp +++ b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp @@ -678,8 +678,20 @@ void tst_QResourceEngine::registerNestedRccFile() "/registeredNestedRccFile")); QVERIFY2(QResource::registerResource(":/nestedrcc/runtime_resource.rcc", "/registeredNestedRccFile"), - "Second QResource::registerResource call failed."); + "Second QResource::registerResource call does not failed."); QVERIFY(QFile::exists(":/registeredNestedRccFile/runtime_resource/search_file.txt")); + + // clean up + QVERIFY(QResource::unregisterResource(":/nestedrcc/runtime_resource.rcc", + "/registeredNestedRccFile")); + + QVERIFY(QFile::exists(":/registeredNestedRccFile/runtime_resource/search_file.txt")); + + QVERIFY(QResource::unregisterResource(":/nestedrcc/runtime_resource.rcc", + "/registeredNestedRccFile")); + + // verify clean up + QVERIFY(!QFile::exists(":/registeredNestedRccFile/runtime_resource/search_file.txt")); } QTEST_MAIN(tst_QResourceEngine) diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 291b7136bce..bb7555f0329 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -1732,7 +1732,7 @@ void tst_QMetaObjectBuilder::usage_templateConnect() testObject.data(), &TestObject::voidSlotInt)); // Something that isn't a signal - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in TestObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(TestObject, TestObject): signal not found"); con = QObject::connect(testObject.data(), &TestObject::setIntProp, testObject.data(), &TestObject::intPropChanged); QVERIFY(!con); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 220d3af568b..afb0bf0169a 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -4864,18 +4864,18 @@ void tst_QObject::pointerConnect() QVERIFY(!QObject::disconnect(con)); //connect a slot to a signal (== error) - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(ReceiverObject, SenderObject): signal not found"); con = connect(&r1, &ReceiverObject::slot4 , &s, &SenderObject::signal4); QVERIFY(!con); QVERIFY(!QObject::disconnect(con)); //connect an arbitrary PMF to a slot - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(ReceiverObject, ReceiverObject): signal not found"); con = connect(&r1, &ReceiverObject::reset, &r1, &ReceiverObject::slot1); QVERIFY(!con); QVERIFY(!QObject::disconnect(con)); - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(ReceiverObject, ReceiverObject): signal not found"); con = connect(&r1, &ReceiverObject::reset, &r1, [](){}); QVERIFY(!con); QVERIFY(!QObject::disconnect(con)); diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index adff512e6a3..81316b061d0 100644 --- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp +++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp @@ -45,11 +45,11 @@ using namespace std::chrono_literals; class tst_QTimer : public QObject { Q_OBJECT -public: - static void initMain(); private slots: + void initTestCase(); void cleanupTestCase(); + void zeroTimer(); void singleShotTimeout(); void timeout(); @@ -1678,11 +1678,11 @@ struct StaticSingleShotUser }; // NOTE: to prevent any static initialization order fiasco, we implement -// initMain() to instantiate staticSingleShotUser before qApp +// initTestCase() to instantiate staticSingleShotUser before qApp static StaticSingleShotUser *s_staticSingleShotUser = nullptr; -void tst_QTimer::initMain() +void tst_QTimer::initTestCase() { s_staticSingleShotUser = new StaticSingleShotUser; } diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp index 2e0d8dd7be2..dbaf6873af2 100644 --- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp +++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp @@ -3595,7 +3595,7 @@ void tst_QtJson::bom() QCOMPARE(error.error, QJsonParseError::NoError); } -void tst_QtJson::nesting() +static void nesting_test() { // check that we abort parsing too deeply nested json documents. // this is to make sure we don't crash because the parser exhausts the @@ -3654,6 +3654,26 @@ void tst_QtJson::nesting() } +void tst_QtJson::nesting() +{ +#if defined(Q_OS_QNX) || defined(Q_OS_VXWORKS) || defined(Q_OS_WASM) + // This test misbehaving probably indicates a stack overflow due to the + // recursive parser in qjsonparser.cpp. The recursion prevention limit may + // be too high for this platform. Someone should investigate. + QSKIP("Test freezes or crashes - probably a stack overflow"); +#endif + + QThread *thr = QThread::create(nesting_test); +#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) || \ + defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer) + // force a larger stack size - 8 MB seems sufficient + thr->setStackSize(8192 * 1024); +#endif + thr->start(); + thr->wait(); + delete thr; +} + void tst_QtJson::longStrings() { // test around 15 and 16 bit boundaries, as these are limits diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 76fcee11b7b..806b6b43161 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -3998,7 +3998,7 @@ void tst_QFuture::signalConnect() #if defined(Q_CC_MSVC_ONLY) && (Q_CC_MSVC < 1940 || !defined(_DEBUG)) #define EXPECT_FUTURE_CONNECT_FAIL() QEXPECT_FAIL("", "QTBUG-101761, test fails on Windows/MSVC", Continue) #else - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in SenderObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, SenderObject): signal not found"); #define EXPECT_FUTURE_CONNECT_FAIL() #endif diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index b03750389bd..8ec9cb516d3 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -3153,6 +3153,8 @@ qHash(const StdHashKeyType<HasQHash> &s, size_t seed) template <typename T> void stdHashImpl() { + T::StdHashUsed = false; + QHash<T, int> hash; for (int i = 0; i < 1000; ++i) hash.insert(T{i}, i); diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index e84c35691da..a19cebe3faf 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -121,6 +121,84 @@ Q_DECLARE_TYPEINFO(Movable, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(Movable); +struct NoexceptMovable +{ + NoexceptMovable(char input = 'j') noexcept + : i(input) + { + counter.fetchAndAddRelaxed(1); + } + NoexceptMovable(const NoexceptMovable &other) noexcept + : i(other.i) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + } + NoexceptMovable(NoexceptMovable &&other) noexcept + : i(other.i) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + other.that = nullptr; + } + + ~NoexceptMovable() // implicitly noexcept + { + check(state, Constructed); + i = 0; + counter.fetchAndAddRelaxed(-1); + state = Destructed; // this is likely a dead store + } + + bool operator ==(const NoexceptMovable &other) const noexcept + { + check(state, Constructed); + check(other.state, Constructed); + return i == other.i; + } + + NoexceptMovable &operator=(const NoexceptMovable &other) noexcept + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = this; + return *this; + } + NoexceptMovable &operator=(NoexceptMovable &&other) noexcept + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = other.that; + other.that = nullptr; + return *this; + } + bool wasConstructedAt(const NoexceptMovable *other) const noexcept + { + return that == other; + } + char i; + static inline QAtomicInt counter; +private: + NoexceptMovable *that = this; // used to check if an instance was moved + + enum State { Constructed = 106, Destructed = 110 }; + State state = Constructed; + + static void check(const State state1, const State state2) noexcept + { + QCOMPARE(state1, state2); + } + + friend inline size_t qHash(const NoexceptMovable &key, size_t seed) noexcept + { return qHash(key.i, seed); } +}; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(NoexceptMovable, Q_RELOCATABLE_TYPE); +QT_END_NAMESPACE + struct Custom { Custom(char input = 'j') : i(input) @@ -225,37 +303,47 @@ private slots: void constructors_reserveAndInitialize() const; void copyConstructorInt() const { copyConstructor<int>(); } void copyConstructorMovable() const { copyConstructor<Movable>(); } + void copyConstructorNoexceptMovable() const { copyConstructor<NoexceptMovable>(); } void copyConstructorCustom() const { copyConstructor<Custom>(); } void assignmentInt() const { testAssignment<int>(); } void assignmentMovable() const { testAssignment<Movable>(); } + void assignmentNoexceptMovable() const { testAssignment<NoexceptMovable>(); } void assignmentCustom() const { testAssignment<Custom>(); } void assignFromInitializerListInt() const { assignFromInitializerList<int>(); } void assignFromInitializerListMovable() const { assignFromInitializerList<Movable>(); } + void assignFromInitializerListNoexceptMovable() const { assignFromInitializerList<NoexceptMovable>(); } void assignFromInitializerListCustom() const { assignFromInitializerList<Custom>(); } void addInt() const { add<int>(); } void addMovable() const { add<Movable>(); } + void addNoexceptMovable() const { add<NoexceptMovable>(); } void addCustom() const { add<Custom>(); } void appendInt() const { append<int>(); } void appendMovable() const { append<Movable>(); } + void appendNoexceptMovable() const { append<NoexceptMovable>(); } void appendCustom() const { append<Custom>(); } void appendRvalue() const; void appendList() const; void assignEmpty() const; void assignInt() const { assign<int>(); } void assignMovable() const { assign<Movable>(); } + void assignNoexceptMovable() const { assign<NoexceptMovable>(); } void assignCustom() const { assign<Custom>(); } void assignUsesPrependBuffer_int_data() { assignUsesPrependBuffer_data(); } void assignUsesPrependBuffer_int() const { assignUsesPrependBuffer<int>(); } void assignUsesPrependBuffer_Movable_data() { assignUsesPrependBuffer_data(); } void assignUsesPrependBuffer_Movable() const { assignUsesPrependBuffer<Movable>(); } + void assignUsesPrependBuffer_NoexceptMovable_data() { assignUsesPrependBuffer_data(); } + void assignUsesPrependBuffer_NoexceptMovable() const { assignUsesPrependBuffer<NoexceptMovable>(); } void assignUsesPrependBuffer_Custom_data() { assignUsesPrependBuffer_data(); } void assignUsesPrependBuffer_Custom() const { assignUsesPrependBuffer<Custom>(); } void at() const; void capacityInt() const { capacity<int>(); } void capacityMovable() const { capacity<Movable>(); } + void capacityNoexceptMovable() const { capacity<NoexceptMovable>(); } void capacityCustom() const { capacity<Custom>(); } void clearInt() const { clear<int>(); } void clearMovable() const { clear<Movable>(); } + void clearNoexceptMovable() const { clear<NoexceptMovable>(); } void clearCustom() const { clear<Custom>(); } void constData() const; void constFirst() const; @@ -263,43 +351,54 @@ private slots: void contains() const; void countInt() const { count<int>(); } void countMovable() const { count<Movable>(); } + void countNoexceptMovable() const { count<NoexceptMovable>(); } void countCustom() const { count<Custom>(); } void cpp17ctad() const; void data() const; void reinterpreted() const; void emptyInt() const { empty<int>(); } void emptyMovable() const { empty<Movable>(); } + void emptyNoexceptMovable() const { empty<NoexceptMovable>(); } void emptyCustom() const { empty<Custom>(); } void endsWith() const; void eraseEmptyInt() const { eraseEmpty<int>(); } void eraseEmptyMovable() const { eraseEmpty<Movable>(); } + void eraseEmptyNoexceptMovable() const { eraseEmpty<NoexceptMovable>(); } void eraseEmptyCustom() const { eraseEmpty<Custom>(); } void eraseEmptyReservedInt() const { eraseEmptyReserved<int>(); } void eraseEmptyReservedMovable() const { eraseEmptyReserved<Movable>(); } + void eraseEmptyReservedNoexceptMovable() const { eraseEmptyReserved<NoexceptMovable>(); } void eraseEmptyReservedCustom() const { eraseEmptyReserved<Custom>(); } void eraseInt() const { erase<int>(false); } void eraseIntShared() const { erase<int>(true); } void eraseMovable() const { erase<Movable>(false); } + void eraseNoexceptMovable() const { erase<NoexceptMovable>(false); } void eraseMovableShared() const { erase<Movable>(true); } + void eraseNoexceptMovableShared() const { erase<NoexceptMovable>(true); } void eraseCustom() const { erase<Custom>(false); } void eraseCustomShared() const { erase<Custom>(true); } void eraseReservedInt() const { eraseReserved<int>(); } void eraseReservedMovable() const { eraseReserved<Movable>(); } + void eraseReservedNoexceptMovable() const { eraseReserved<NoexceptMovable>(); } void eraseReservedCustom() const { eraseReserved<Custom>(); } void fillInt() const { fill<int>(); } void fillMovable() const { fill<Movable>(); } + void fillNoexceptMovable() const { fill<NoexceptMovable>(); } void fillCustom() const { fill<Custom>(); } void fillDetachInt() const { fillDetach<int>(); } void fillDetachMovable() const { fillDetach<Movable>(); } + void fillDetachNoexceptMovable() const { fillDetach<NoexceptMovable>(); } void fillDetachCustom() const { fillDetach<Custom>(); } void first() const; void freeSpaceAtBeginEventuallyShrinks() const; void fromListInt() const { fromList<int>(); } void fromListMovable() const { fromList<Movable>(); } + void fromListNoexceptMovable() const { fromList<NoexceptMovable>(); } void fromListCustom() const { fromList<Custom>(); } void indexOf() const; void insertInt() const { insert<int>(); } void insertMovable() const { insert<Movable>(); } + void insertNoexceptMovable() const { insert<NoexceptMovable>(); } void insertCustom() const { insert<Custom>(); } void insertZeroCount_data(); void insertZeroCount() const; @@ -310,17 +409,21 @@ private slots: void sliced() const; void moveInt() const { move<int>(); } void moveMovable() const { move<Movable>(); } + void moveNoexceptMovable() const { move<NoexceptMovable>(); } void moveCustom() const { move<Custom>(); } void prependInt() const { prepend<int>(); } void prependMovable() const { prepend<Movable>(); } + void prependNoexceptMovable() const { prepend<NoexceptMovable>(); } void prependCustom() const { prepend<Custom>(); } void prependRvalue() const; void qhashInt() const { qhash<int>(); } void qhashMovable() const { qhash<Movable>(); } + void qhashNoexceptMovable() const { qhash<NoexceptMovable>(); } void qhashCustom() const { qhash<Custom>(); } void removeAllWithAlias() const; void removeInt() const { remove<int>(); } void removeMovable() const { remove<Movable>(); } + void removeNoexceptMovable() const { remove<NoexceptMovable>(); } void removeCustom() const { remove<Custom>(); } void removeFirstLast() const; void resizePOD_data() const; @@ -339,10 +442,12 @@ private slots: void reverseIterators() const; void sizeInt() const { size<int>(); } void sizeMovable() const { size<Movable>(); } + void sizeNoexceptMovable() const { size<NoexceptMovable>(); } void sizeCustom() const { size<Custom>(); } void startsWith() const; void swapInt() const { swap<int>(); } void swapMovable() const { swap<Movable>(); } + void swapNoexceptMovable() const { swap<NoexceptMovable>(); } void swapCustom() const { swap<Custom>(); } void toAddress() const; void toList() const; @@ -356,10 +461,12 @@ private slots: void reserveZero(); void initializeListInt() { initializeList<int>(); } void initializeListMovable() { initializeList<Movable>(); } + void initializeListNoexceptMovable() { initializeList<NoexceptMovable>(); } void initializeListCustom() { initializeList<Custom>(); } void const_shared_null(); void detachInt() const { detach<int>(); } void detachMovable() const { detach<Movable>(); } + void detachNoexceptMovable() const { detach<NoexceptMovable>(); } void detachCustom() const { detach<Custom>(); } void detachThreadSafetyInt() const; void detachThreadSafetyMovable() const; @@ -369,9 +476,11 @@ private slots: void emplaceInt() { emplaceImpl<int>(); } void emplaceCustom() { emplaceImpl<Custom>(); } void emplaceMovable() { emplaceImpl<Movable>(); } + void emplaceNoexceptMovable() { emplaceImpl<NoexceptMovable>(); } void emplaceConsistentWithStdVectorInt() { emplaceConsistentWithStdVectorImpl<int>(); } void emplaceConsistentWithStdVectorCustom() { emplaceConsistentWithStdVectorImpl<Custom>(); } void emplaceConsistentWithStdVectorMovable() { emplaceConsistentWithStdVectorImpl<Movable>(); } + void emplaceConsistentWithStdVectorNoexceptMovable() { emplaceConsistentWithStdVectorImpl<NoexceptMovable>(); } void emplaceConsistentWithStdVectorQString() { emplaceConsistentWithStdVectorImpl<QString>(); } void emplaceReturnsIterator(); void emplaceFront() const; @@ -383,35 +492,45 @@ private slots: void replaceInt() const { replace<int>(); } void replaceCustom() const { replace<Custom>(); } void replaceMovable() const { replace<Movable>(); } + void replaceNoexceptMovable() const { replace<NoexceptMovable>(); } void fromReadOnlyData() const; void reallocateCustomAlignedType_qtbug90359() const; void reinsertToBeginInt_qtbug91360() const { reinsertToBegin<int>(); } void reinsertToBeginMovable_qtbug91360() const { reinsertToBegin<Movable>(); } + void reinsertToBeginNoexceptMovable_qtbug91360() const { reinsertToBegin<NoexceptMovable>(); } void reinsertToBeginCustom_qtbug91360() const { reinsertToBegin<Custom>(); } void reinsertToEndInt_qtbug91360() const { reinsertToEnd<int>(); } void reinsertToEndMovable_qtbug91360() const { reinsertToEnd<Movable>(); } + void reinsertToEndNoexceptMovable_qtbug91360() const { reinsertToEnd<NoexceptMovable>(); } void reinsertToEndCustom_qtbug91360() const { reinsertToEnd<Custom>(); } void reinsertRangeToEndInt_qtbug91360() const { reinsertRangeToEnd<int>(); } void reinsertRangeToEndMovable_qtbug91360() const { reinsertRangeToEnd<Movable>(); } + void reinsertRangeToEndNoexceptMovable_qtbug91360() const { reinsertRangeToEnd<NoexceptMovable>(); } void reinsertRangeToEndCustom_qtbug91360() const { reinsertRangeToEnd<Custom>(); } // QList reference stability tests: void stability_reserveInt() const { stability_reserve<int>(); } void stability_reserveMovable() const { stability_reserve<Movable>(); } + void stability_reserveNoexceptMovable() const { stability_reserve<NoexceptMovable>(); } void stability_reserveCustom() const { stability_reserve<Custom>(); } void stability_eraseInt() const { stability_erase<int>(); } void stability_eraseMovable() const { stability_erase<Movable>(); } + void stability_eraseNoexceptMovable() const { stability_erase<NoexceptMovable>(); } void stability_eraseCustom() const { stability_erase<Custom>(); } void stability_appendInt() const { stability_append<int>(); } void stability_appendMovable() const { stability_append<Movable>(); } + void stability_appendNoexceptMovable() const { stability_append<NoexceptMovable>(); } void stability_appendCustom() const { stability_append<Custom>(); } void stability_insertElementInt() const { stability_insertElement<int>(); } void stability_insertElementMovable() const { stability_insertElement<Movable>(); } + void stability_insertElementNoexceptMovable() const { stability_insertElement<NoexceptMovable>(); } void stability_insertElementCustom() const { stability_insertElement<Custom>(); } void stability_emplaceInt() const { stability_emplace<int>(); } void stability_emplaceMovable() const { stability_emplace<Movable>(); } + void stability_emplaceNoexceptMovable() const { stability_emplace<NoexceptMovable>(); } void stability_emplaceCustom() const { stability_emplace<Custom>(); } void stability_resizeInt() const { stability_resize<int>(); } void stability_resizeMovable() const { stability_resize<Movable>(); } + void stability_resizeNoexceptMovable() const { stability_resize<NoexceptMovable>(); } void stability_resizeCustom() const { stability_resize<Custom>(); } private: @@ -515,16 +634,9 @@ template<typename T> struct SimpleValue } static const uint MaxIndex = 6; - static const T Values[MaxIndex]; + static inline const T Values[MaxIndex] = { 110, 105, 101, 114, 111, 98 }; }; -template<> -const int SimpleValue<int>::Values[] = { 110, 105, 101, 114, 111, 98 }; -template<> -const Movable SimpleValue<Movable>::Values[] = { 110, 105, 101, 114, 111, 98 }; -template<> -const Custom SimpleValue<Custom>::Values[] = { 110, 105, 101, 114, 111, 98 }; - // Make some macros for the tests to use in order to be slightly more readable... #define T_FOO SimpleValue<T>::at(0) #define T_BAR SimpleValue<T>::at(1) @@ -656,12 +768,36 @@ void tst_QList::assignFromInitializerList() const T val2(SimpleValue<T>::at(2)); T val3(SimpleValue<T>::at(3)); - QList<T> v1 = {val1, val2, val3}; + QList<T> v1 = {}; + QCOMPARE(v1.size(), 0); + + v1 = {val1, val2, val3}; QCOMPARE(v1, QList<T>() << val1 << val2 << val3); QCOMPARE(v1, (QList<T> {val1, val2, val3})); v1 = {}; QCOMPARE(v1.size(), 0); + + // repeat, but make v1 shared before we assign + QList v2 = {val3, val1, val2}; + v1 = v2; + v1 = {val1, val2, val3}; + QCOMPARE(v1, QList<T>() << val1 << val2 << val3); + QCOMPARE(v1, (QList<T> {val1, val2, val3})); + v1 = v2; + v1 = {}; + QCOMPARE(v1.size(), 0); + + // repeat again, but now detached copies + v1 = v2; + v1.detach(); + v1 = {val1, val2, val3}; + QCOMPARE(v1, QList<T>() << val1 << val2 << val3); + QCOMPARE(v1, (QList<T> {val1, val2, val3})); + v1 = v2; + v1.detach(); + v1 = {}; + QCOMPARE(v1.size(), 0); } template<typename T> @@ -834,6 +970,65 @@ void tst_QList::assign() const QVERIFY(!myvecCopy.isSharedWith(myvec)); QCOMPARE(myvecCopy, QList<T>() << T_FOO << T_FOO); } + + // test assigning an empty range to an empty list + { + QList<T> myvec, empty; + + const T *ptr = nullptr; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + ptr = SimpleValue<T>::Values + 3; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + } + + // test assigning empty to non-empty + { + QList<T> nonempty = SimpleValue<T>::vector(2); + QList<T> myvec; + + const T *ptr = nullptr; + myvec = nonempty; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + ptr = SimpleValue<T>::Values + 3; + myvec = nonempty; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + // repeat, after having detached + ptr = nullptr; + myvec = nonempty; + myvec.detach(); + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + ptr = SimpleValue<T>::Values + 3; + myvec = nonempty; + myvec.detach(); + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + } + + // assign a smaller range + { + QList<T> myvec = SimpleValue<T>::vector(3); + QCOMPARE(myvec.size(), 3); + + myvec.assign(SimpleValue<T>::Values + 3, SimpleValue<T>::Values + 5); + QCOMPARE(myvec.size(), 2); + QCOMPARE(myvec.at(0), T_CAT); + QCOMPARE(myvec.at(1), T_DOG); + + // with detaching: + QList copy = myvec; + myvec.assign(SimpleValue<T>::Values, SimpleValue<T>::Values + 1); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.at(0), T_FOO); + } } inline namespace Scenarios { @@ -2108,10 +2303,15 @@ void tst_QList::qhash() const TST_QLIST_CHECK_LEAKS(T) QList<T> l1, l2; - QCOMPARE(qHash(l1), qHash(l2)); + QCOMPARE(qHash(l1, 0), qHash(l2, 0)); l1 << SimpleValue<T>::at(0); l2 << SimpleValue<T>::at(0); - QCOMPARE(qHash(l1), qHash(l2)); + QCOMPARE(qHash(l1, 0), qHash(l2, 0)); + + QCOMPARE(qHash(l1, 1), qHash(l2, 1)); + l1.clear(); + l2.clear(); + QCOMPARE(qHash(l1, 1), qHash(l2, 1)); } template <typename T> diff --git a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp index 1dadbb9efea..b3f96588304 100644 --- a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp +++ b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp @@ -26,6 +26,8 @@ private Q_SLOTS: void optionalGuard(); void leavingScope(); void exceptions(); + + void init(); }; void func() @@ -169,7 +171,6 @@ void tst_QScopeGuard::leavingScope() void tst_QScopeGuard::exceptions() { - s_globalState = 0; bool caught = false; QT_TRY { @@ -187,5 +188,10 @@ void tst_QScopeGuard::exceptions() } +void tst_QScopeGuard::init() +{ + s_globalState = 0; +} + QTEST_MAIN(tst_QScopeGuard) #include "tst_qscopeguard.moc" diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index 9c61cb503fb..f7c5af53310 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -3250,7 +3250,6 @@ void tst_QWindow::enterLeaveOnWindowShowHide() ++expectedEnter; QTRY_COMPARE_WITH_TIMEOUT(window.numEnterEvents, expectedEnter, 250); QCOMPARE(window.enterPosition, window.mapFromGlobal(QCursor::pos())); - QCOMPARE(QGuiApplicationPrivate::lastCursorPosition, cursorPos); QWindow secondary; secondary.setFlag(windowType); diff --git a/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp b/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp index bd9e00fc376..7b4193e3749 100644 --- a/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp +++ b/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp @@ -86,6 +86,9 @@ private slots: void intersectionPointOnEdge(); void boundsAtStartPoint(); + + void percentAtEmptyPath_data(); + void percentAtEmptyPath(); }; void tst_QPainterPath::cleanupTestCase() @@ -1637,6 +1640,29 @@ void tst_QPainterPath::boundsAtStartPoint() QCOMPARE(constructedPath.controlPointRect(), defaultPath.controlPointRect()); } +void tst_QPainterPath::percentAtEmptyPath_data() +{ + const QPointF qpf(2., 2.); + QTest::addColumn<QPainterPath>("path"); + + QTest::newRow("defaultConstructed") << QPainterPath(); + QTest::newRow("withStartPoint") << QPainterPath(qpf); + QPainterPath pp(qpf); + pp.lineTo(qpf); + QTest::newRow("equalPoints") << pp; +} + +void tst_QPainterPath::percentAtEmptyPath() +{ + QFETCH(QPainterPath, path); + + path.angleAtPercent(0.5); + path.pointAtPercent(0.5); + path.slopeAtPercent(0.5); + + path.percentAtLength(0); +} + QTEST_APPLESS_MAIN(tst_QPainterPath) #include "tst_qpainterpath.moc" diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp index 0d2b7bfde64..7417f0648e4 100644 --- a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp +++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp @@ -30,6 +30,7 @@ public: tst_QHostAddress(); private slots: + void constructor(); void constructor_QString_data(); void constructor_QString(); void setAddress_QString_data(); @@ -62,6 +63,43 @@ tst_QHostAddress::tst_QHostAddress() qRegisterMetaType<QHostAddress>("QHostAddress"); } +static void verifyClear(const QHostAddress &addr) +{ + QVERIFY(addr.isNull()); + QVERIFY(!addr.isLoopback()); + QVERIFY(!addr.isGlobal()); + QVERIFY(!addr.isLinkLocal()); + QVERIFY(!addr.isSiteLocal()); + QVERIFY(!addr.isUniqueLocalUnicast()); + QVERIFY(!addr.isMulticast()); + QVERIFY(!addr.isBroadcast()); + QVERIFY(!addr.isPrivateUse()); + QCOMPARE(addr, QHostAddress()); + QCOMPARE(addr, QHostAddress::Null); + QCOMPARE(addr.protocol(), QHostAddress::UnknownNetworkLayerProtocol); + + bool ok = true; + QCOMPARE(addr.toIPv4Address(&ok), 0); + QVERIFY(!ok); + + QCOMPARE(QByteArrayView::fromArray(addr.toIPv6Address().c), + QByteArrayView::fromArray(QIPv6Address{}.c)); + QCOMPARE(addr.scopeId(), QString()); + + QCOMPARE(addr.toString(), QString()); + + size_t seed = QHashSeed::globalSeed(); + QCOMPARE(qHash(addr, seed), qHash(QHostAddress(), seed)); + seed = 0; + QCOMPARE(qHash(addr, seed), qHash(QHostAddress(), seed)); +} + +void tst_QHostAddress::constructor() +{ + QHostAddress addr; + verifyClear(addr); +} + void tst_QHostAddress::constructor_QString_data() { setAddress_QString_data(); @@ -94,6 +132,9 @@ void tst_QHostAddress::constructor_QString() QVERIFY( hostAddr.isNull() ); QVERIFY( hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol ); } + + hostAddr.clear(); + verifyClear(hostAddr); } void tst_QHostAddress::setAddress_QString_data() @@ -201,6 +242,9 @@ void tst_QHostAddress::setAddress_QString() QVERIFY( hostAddr.isNull() ); QVERIFY( hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol ); } + + hostAddr.clear(); + verifyClear(hostAddr); } void tst_QHostAddress::specialAddresses_data() @@ -239,6 +283,7 @@ void tst_QHostAddress::specialAddresses() QFETCH(QHostAddress::SpecialAddress, address); QFETCH(bool, result); QCOMPARE(QHostAddress(text) == address, result); + size_t seed = QHashSeed::globalSeed(); //check special address equal to itself (QTBUG-22898), note two overloads of operator== QVERIFY(QHostAddress(address) == QHostAddress(address)); @@ -252,11 +297,19 @@ void tst_QHostAddress::specialAddresses() QHostAddress ha; ha.setAddress(address); QVERIFY(ha == address); + QCOMPARE(qHash(ha, seed), qHash(QHostAddress(address), seed)); + QCOMPARE(qHash(ha, 0), qHash(QHostAddress(address), 0)); } QHostAddress setter; setter.setAddress(text); - QCOMPARE(setter == address, result); + if (result) { + QCOMPARE(setter, address); + QCOMPARE(qHash(setter, seed), qHash(QHostAddress(address), seed)); + QCOMPARE(qHash(setter, 0), qHash(QHostAddress(address), 0)); + } else { + QCOMPARE_NE(setter, address); + } } @@ -288,8 +341,11 @@ void tst_QHostAddress::compare() QCOMPARE(first == second, result); QCOMPARE(second == first, result); - if (result == true) - QCOMPARE(qHash(first), qHash(second)); + if (result == true) { + size_t seed = QHashSeed::globalSeed(); + QCOMPARE(qHash(first, seed), qHash(second, seed)); + QCOMPARE(qHash(first, 0), qHash(second, 0)); + } } void tst_QHostAddress::isEqual_data() @@ -370,6 +426,11 @@ void tst_QHostAddress::scopeId() address2 = address; QCOMPARE(address2.scopeId(), QString("eth0")); QCOMPARE(address2.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%eth0")); + + address.clear(); + verifyClear(address); + address2.clear(); + verifyClear(address2); } void tst_QHostAddress::hashKey() diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp index 4814206cd75..40292f0c44d 100644 --- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -132,6 +132,7 @@ private: bool m_skipUnsupportedIPv6Tests; bool m_workaroundLinuxKernelBug; + int loopbackInterface = 0; // unknown by default QList<QHostAddress> allAddresses; QHostAddress multicastGroup4, multicastGroup6; QList<QHostAddress> linklocalMulticastGroups; @@ -140,6 +141,16 @@ private: QUdpSocket *m_asyncReceiver; }; +// Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of obtaining +// the packet's destination addresses. This means the destinationAddress() be +// empty, so whitelist the OSes for which we know we have an implementation. +// That's currently all of them, which means there probably is code in this +// test that assumes this works without checking this variable. +// +// Note: this applies to single-stack operations; dual stack implementations +// appear to be buggy or not present at all in some OSes. +static constexpr bool HasWorkingIPv4DestinationAddress = true; + #ifdef SHOULD_CHECK_SYSCALL_SUPPORT bool tst_QUdpSocket::ipv6SetsockoptionMissing(int level, int optname) { @@ -246,7 +257,23 @@ void tst_QUdpSocket::initTestCase() if (!QtNetworkSettings::verifyTestNetworkSettings()) QSKIP("No network test server available"); #endif - allAddresses = QNetworkInterface::allAddresses(); + + allAddresses.clear(); + for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) { + if (!iface.flags().testAnyFlags(QNetworkInterface::IsUp)) + continue; + if (iface.flags().testAnyFlags(QNetworkInterface::IsLoopBack)) + loopbackInterface = iface.index(); + + // add this interface's addresses + const QList<QNetworkAddressEntry> addresses = iface.addressEntries(); + for (const QNetworkAddressEntry &entry : addresses) { + allAddresses += entry.ip(); + if (!loopbackInterface && entry.ip().isLoopback()) + loopbackInterface = iface.index(); + } + } + m_skipUnsupportedIPv6Tests = shouldSkipIpv6TestsForBrokenSetsockopt(); // Create a pair of random multicast groups so we avoid clashing with any @@ -568,12 +595,12 @@ void tst_QUdpSocket::broadcasting() QVERIFY2(allAddresses.contains(dgram.senderAddress()), dgram.senderAddress().toString().toLatin1()); QCOMPARE(dgram.senderPort(), int(broadcastSocket.localPort())); - if (!dgram.destinationAddress().isNull()) { + if (HasWorkingIPv4DestinationAddress) { QVERIFY2(dgram.destinationAddress() == QHostAddress::Broadcast || broadcastAddresses.contains(dgram.destinationAddress()), dgram.destinationAddress().toString().toLatin1()); - QCOMPARE(dgram.destinationPort(), int(serverSocket.localPort())); } + QCOMPARE(dgram.destinationPort(), int(serverSocket.localPort())); int ttl = dgram.hopLimit(); if (ttl != -1) @@ -738,15 +765,7 @@ void tst_QUdpSocket::loop() QCOMPARE(paulDatagram.senderPort(), int(peter.localPort())); QCOMPARE(peterDatagram.senderPort(), int(paul.localPort())); - // Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of - // obtaining the packet's destination addresses. The destinationAddress and - // destinationPort calls could fail, so whitelist the OSes for which we - // know we have an implementation. -#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN) - QVERIFY(peterDatagram.destinationPort() != -1); - QVERIFY(paulDatagram.destinationPort() != -1); -#endif - if (peterDatagram.destinationPort() == -1) { + if (!HasWorkingIPv4DestinationAddress) { QCOMPARE(peterDatagram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol); QCOMPARE(paulDatagram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol); } else { @@ -754,6 +773,11 @@ void tst_QUdpSocket::loop() QCOMPARE(paulDatagram.destinationAddress(), makeNonAny(paul.localAddress())); QVERIFY(peterDatagram.destinationAddress().isEqual(makeNonAny(peter.localAddress()))); QVERIFY(paulDatagram.destinationAddress().isEqual(makeNonAny(paul.localAddress()))); + + if (loopbackInterface) { + QCOMPARE(peterDatagram.interfaceIndex(), loopbackInterface); + QCOMPARE(paulDatagram.interfaceIndex(), loopbackInterface); + } } } @@ -820,6 +844,11 @@ void tst_QUdpSocket::ipv6Loop() QCOMPARE(paulDatagram.destinationAddress(), makeNonAny(paul.localAddress())); QCOMPARE(peterDatagram.destinationPort(), peterPort); QCOMPARE(paulDatagram.destinationPort(), paulPort); + + if (loopbackInterface) { + QCOMPARE(peterDatagram.interfaceIndex(), loopbackInterface); + QCOMPARE(paulDatagram.interfaceIndex(), loopbackInterface); + } } void tst_QUdpSocket::dualStack() @@ -850,6 +879,8 @@ void tst_QUdpSocket::dualStack() QCOMPARE(dgram.destinationPort(), int(dualSock.localPort())); QVERIFY(dgram.destinationAddress().isEqual(makeNonAny(dualSock.localAddress(), QHostAddress::LocalHost))); } else { + // Observed on QNX: the IPV6_PKTINFO ancillary data appears to be + // missing if the sender is IPv4. qInfo("Getting IPv4 destination address failed."); } @@ -889,12 +920,11 @@ void tst_QUdpSocket::dualStack() QCOMPARE(dgram.data(), dualData); QCOMPARE(dgram.senderPort(), int(dualSock.localPort())); QCOMPARE(dgram.senderAddress(), makeNonAny(dualSock.localAddress(), QHostAddress::LocalHost)); -#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN) - QVERIFY(dgram.destinationPort() != -1); -#endif - if (dgram.destinationPort() != -1) { - QCOMPARE(dgram.destinationPort(), int(v4Sock.localPort())); + QCOMPARE(dgram.destinationPort(), int(v4Sock.localPort())); + if (HasWorkingIPv4DestinationAddress) { QCOMPARE(dgram.destinationAddress(), makeNonAny(v4Sock.localAddress(), QHostAddress::LocalHost)); + if (loopbackInterface) + QCOMPARE(dgram.interfaceIndex(), loopbackInterface); } } @@ -1748,10 +1778,10 @@ void tst_QUdpSocket::multicast() QVERIFY2(allAddresses.contains(dgram.senderAddress()), dgram.senderAddress().toString().toLatin1()); QCOMPARE(dgram.senderPort(), int(sender.localPort())); - if (!dgram.destinationAddress().isNull()) { + if (HasWorkingIPv4DestinationAddress) { QCOMPARE(dgram.destinationAddress(), groupAddress); - QCOMPARE(dgram.destinationPort(), int(receiver.localPort())); } + QCOMPARE(dgram.destinationPort(), int(receiver.localPort())); int ttl = dgram.hopLimit(); if (ttl != -1) @@ -1962,19 +1992,12 @@ void tst_QUdpSocket::linkLocalIPv4() QCOMPARE(dgram.data().size(), testData.size()); QCOMPARE(dgram.data(), testData); - // Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of - // obtaining the packet's destination addresses. The destinationAddress - // and destinationPort calls could fail, so whitelist the OSes we know - // we have an implementation. -#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN) - QVERIFY(dgram.destinationPort() != -1); -#endif - if (dgram.destinationPort() == -1) { + if (!HasWorkingIPv4DestinationAddress) { QCOMPARE(dgram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol); } else { QCOMPARE(dgram.destinationAddress(), s->localAddress()); - QCOMPARE(dgram.destinationPort(), int(neutral.localPort())); } + QCOMPARE(dgram.destinationPort(), int(neutral.localPort())); QVERIFY(neutral.writeDatagram(dgram.makeReply(testData))); QVERIFY2(s->waitForReadyRead(10000), QtNetworkSettings::msgSocketError(*s).constData()); diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST index 7bde411761b..8f8c065560a 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST @@ -9,6 +9,3 @@ android android [windowOpacity] macos # QTBUG-139950 -[clickFocus] -windows-10 # QTBUG-141386 - diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index 17acdea014e..2f0cbf78b25 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -25,6 +25,7 @@ #include <QtWidgets/qtablewidget.h> #include <QtGui/qevent.h> +#include <QtGui/qstylehints.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/qmimedata.h> @@ -3333,7 +3334,7 @@ void tst_QGraphicsProxyWidget::clickFocus() EventSpy proxySpy(proxy); EventSpy widgetSpy(proxy->widget()); - view.setFrameStyle(0); + view.setFrameStyle(QFrame::NoFrame); view.resize(300, 300); view.show(); QVERIFY(QTest::qWaitForWindowFocused(&view)); @@ -3469,10 +3470,12 @@ void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget() auto *hoverButton = new HoverButton(background); hoverButton->setText("Second button"_L1); hoverButton->setGeometry(10, 10, 200, 50); + hoverButton->setAttribute(Qt::WA_Hover, true); scene.addWidget(background); auto *hideButton = new QPushButton("I'm a button with a very very long text"_L1); hideButton->setGeometry(10, 10, 400, 50); + hideButton->setAttribute(Qt::WA_Hover, true); QGraphicsProxyWidget *topButton = scene.addWidget(hideButton); connect(hideButton, &QPushButton::clicked, &scene, [&]() { topButton->hide(); }); topButton->setFocus(); @@ -3659,6 +3662,11 @@ public: return QWidget::event(event); } + void paintEvent(QPaintEvent *) override + { + QPainter p(this); + p.fillRect(rect(), Qt::green); + } }; #if QT_CONFIG(wheelevent) @@ -3676,6 +3684,11 @@ public: */ void tst_QGraphicsProxyWidget::wheelEventPropagation() { + qApp->styleHints()->setWheelScrollLines(3); + const auto guard = qScopeGuard([&]() { + qApp->styleHints()->setWheelScrollLines(-1); + }); + QGraphicsScene scene(0, 0, 600, 600); auto *label = new QLabel("Direct"_L1); @@ -3697,6 +3710,11 @@ void tst_QGraphicsProxyWidget::wheelEventPropagation() int wheelEventCount = 0; protected: + void paintEvent(QPaintEvent *) override + { + QPainter p(this); + p.fillRect(rect(), Qt::green); + } void wheelEvent(QWheelEvent *) override { ++wheelEventCount; @@ -3710,10 +3728,13 @@ void tst_QGraphicsProxyWidget::wheelEventPropagation() QGraphicsView view(&scene); view.setFixedHeight(200); + view.setFixedWidth(700); + view.setFrameStyle(QFrame::NoFrame); view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.verticalScrollBar()->isVisible()); + QVERIFY(!view.horizontalScrollBar()->isVisible()); view.verticalScrollBar()->setValue(0); QSignalSpy scrollSpy(view.verticalScrollBar(), &QScrollBar::valueChanged); @@ -3729,6 +3750,8 @@ void tst_QGraphicsProxyWidget::wheelEventPropagation() phase); QCoreApplication::processEvents(); }; + auto vs = view.verticalScrollBar(); + vs->setSingleStep(9); // each wheel event: wheelScrollLines() * 9 = 27px qsizetype scrollCount = 0; // test non-kinetic events; they are not grabbed, and should scroll the view unless @@ -3816,6 +3839,7 @@ void tst_QGraphicsProxyWidget::forwardTouchEvent() QGraphicsView view(&scene); view.show(); + view.setFrameStyle(QFrame::NoFrame); QVERIFY(QTest::qWaitForWindowActive(&view)); EventSpy eventSpy(widget); @@ -3832,16 +3856,14 @@ void tst_QGraphicsProxyWidget::forwardTouchEvent() QTest::touchEvent(&view, device.get()).move(0, QPoint(16, 16), &view); QTest::touchEvent(&view, device.get()).release(0, QPoint(15, 15), &view); - QApplication::processEvents(); - - QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 1); - QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 2); - QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 1); + QTRY_COMPARE(eventSpy.counts[QEvent::TouchBegin], 1); + QTRY_COMPARE(eventSpy.counts[QEvent::TouchUpdate], 2); + QTRY_COMPARE(eventSpy.counts[QEvent::TouchEnd], 1); } void tst_QGraphicsProxyWidget::touchEventPropagation() { - QGraphicsScene scene(0, 0, 300, 200); + QGraphicsScene scene; auto *simpleWidget = new QWidget; simpleWidget->setObjectName("simpleWidget"); simpleWidget->setAttribute(Qt::WA_AcceptTouchEvents, true); @@ -3873,15 +3895,26 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() vbox->addWidget(touchWidget2); QGraphicsProxyWidget *formProxy = scene.addWidget(formWidget); formProxy->setAcceptTouchEvents(true); - formProxy->setGeometry(QRectF(50, 50, 200, 160)); + const auto minSize = formWidget->minimumSize(); + formProxy->setGeometry(QRectF(50, 50, minSize.width(), minSize.height())); + + // topLeft must be 0/0 + const auto sceneRect = scene.sceneRect(); + scene.setSceneRect(QRectF(0, 0, sceneRect.width(), sceneRect.height())); QGraphicsView view(&scene); + // by setting NoFrame, view and view.viewport() have the same + // coordinate system + view.setFrameStyle(QFrame::NoFrame); + // make sure to not have scrollbars view.setFixedSize(scene.width(), scene.height()); view.verticalScrollBar()->setValue(0); view.horizontalScrollBar()->setValue(0); view.viewport()->setObjectName("GraphicsView's Viewport"); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); class TouchEventSpy : public QObject { @@ -3913,8 +3946,12 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() case QEvent::TouchEnd: { auto *touchEvent = static_cast<QTouchEvent *>(event); // instead of detaching each QEventPoint, just store the relative positions - for (const auto &touchPoint : touchEvent->points()) - records[touchPoint.id()] << TouchRecord{receiver, event->type(), touchPoint.position()}; + for (const auto& touchPoint : touchEvent->points()) { + records[touchPoint.id()] + << TouchRecord{ receiver, event->type(), touchPoint.position() }; + qCDebug(lcTests) << touchPoint.id() << "recv:" << receiver << "type" + << event->type() << "pos" << touchPoint.position(); + } qCDebug(lcTests) << "Recording" << event << receiver; break; } @@ -3944,9 +3981,9 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() }; // verify that the embedded widget gets the correctly translated event - QTest::touchEvent(&view, touchDevice.get()).press(0, simpleCenter.toPoint()); + QTest::touchEvent(&view, touchDevice.get()).press(0, view.mapFromScene(simpleCenter)); // window, viewport, scene, simpleProxy, simpleWidget - QCOMPARE(eventSpy.count(), 5); + QTRY_COMPARE(eventSpy.count(), 5); QCOMPARE(eventSpy.at(0).receiver, view.windowHandle()); QCOMPARE(eventSpy.at(1).receiver, view.viewport()); QCOMPARE(eventSpy.at(2).receiver, &scene); @@ -3969,7 +4006,7 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() QCOMPARE(formWidget->childAt(touchWidget2->pos() + tw2Center), touchWidget2); // touch events are sent to the view, in view coordinates - const QPoint formProxyPox = view.mapFromScene(formProxy->pos().toPoint()); + const QPoint formProxyPox = view.mapFromScene(formProxy->pos()); const QPoint pb1TouchPos = pushButton1->pos() + pb1Center + formProxyPox; const QPoint pb2TouchPos = pushButton2->pos() + pb2Center + formProxyPox; const QPoint tw1TouchPos = touchWidget1->pos() + tw1Center + formProxyPox; diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index df19ea1568e..48310996c46 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -2058,28 +2058,21 @@ void tst_QGraphicsView::mapFromSceneRect() QWidget topLevel; QGraphicsView view(&scene,&topLevel); view.rotate(90); - view.setFixedSize(200, 200); - view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + const auto fw = view.frameWidth() * 2; + view.setFixedSize(200 + fw, 200 + fw); topLevel.show(); - QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(QTest::qWaitForWindowActive(&topLevel)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); - QPolygon polygon; - polygon << QPoint(98, 98); - polygon << QPoint(98, 108); - polygon << QPoint(88, 108); - polygon << QPoint(88, 98); - - - QPolygon viewPolygon = view.mapFromScene(0, 0, 10, 10); - for (int i = 0; i < 4; ++i) { - QVERIFY(qAbs(viewPolygon[i].x() - polygon[i].x()) < 3); - QVERIFY(qAbs(viewPolygon[i].y() - polygon[i].y()) < 3); - } + const QRectF input(0, 0, 10, 10); + // fixed size is 200x200 so center is 100x100 + const QPolygon polygon{ QPoint(100, 100), QPoint(100, 110), QPoint(90, 110), QPoint(90, 100) }; + const QPolygon viewPolygon = view.mapFromScene(input); + QCOMPARE(viewPolygon, polygon); QPoint pt = view.mapFromScene(QPointF()); - QPolygon p; - p << pt << pt << pt << pt; + QPolygon p{ pt, pt, pt, pt }; QCOMPARE(view.mapFromScene(QRectF()), p); } @@ -2088,28 +2081,18 @@ void tst_QGraphicsView::mapFromScenePoly() QGraphicsScene scene; QGraphicsView view(&scene); view.rotate(90); - view.setFixedSize(200, 200); - view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + const auto fw = view.frameWidth() * 2; + view.setFixedSize(200 + fw, 200 + fw); view.show(); - - QPolygonF polygon; - polygon << QPoint(0, 0); - polygon << QPoint(10, 0); - polygon << QPoint(10, 10); - polygon << QPoint(0, 10); - - QPolygon polygon2; - polygon2 << QPoint(98, 98); - polygon2 << QPoint(98, 108); - polygon2 << QPoint(88, 108); - polygon2 << QPoint(88, 98); - - QPolygon viewPolygon = view.mapFromScene(polygon); - for (int i = 0; i < 4; ++i) { - QVERIFY(qAbs(viewPolygon[i].x() - polygon2[i].x()) < 3); - QVERIFY(qAbs(viewPolygon[i].y() - polygon2[i].y()) < 3); - } + QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); + + const QPolygonF input{ QPoint(0, 0), QPoint(10, 0), QPoint(10, 10), QPoint(0, 10) }; + // fixed size is 200x200 so center is 100x100 + const QPolygon polygon{ QPoint(100, 100), QPoint(100, 110), QPoint(90, 110), QPoint(90, 100) }; + const QPolygon viewPolygon = view.mapFromScene(input); + QCOMPARE(viewPolygon, polygon); } void tst_QGraphicsView::mapFromScenePath() @@ -2117,34 +2100,22 @@ void tst_QGraphicsView::mapFromScenePath() QGraphicsScene scene; QGraphicsView view(&scene); view.rotate(90); - view.setFixedSize(200, 200); - view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + const auto fw = view.frameWidth() * 2; + view.setFixedSize(200 + fw, 200 + fw); view.show(); - - QPolygonF polygon; - polygon << QPoint(0, 0); - polygon << QPoint(10, 0); - polygon << QPoint(10, 10); - polygon << QPoint(0, 10); + QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); + + const QPolygonF input{ QPoint(0, 0), QPoint(10, 0), QPoint(10, 10), QPoint(0, 10) }; + QPainterPath inputPath; + inputPath.addPolygon(input); + // fixed size is 200x200 so center is 100x100 + const QPolygon polygon{ QPoint(100, 100), QPoint(100, 110), QPoint(90, 110), QPoint(90, 100) }; QPainterPath path; path.addPolygon(polygon); - - QPolygon polygon2; - polygon2 << QPoint(98, 98); - polygon2 << QPoint(98, 108); - polygon2 << QPoint(88, 108); - polygon2 << QPoint(88, 98); - QPainterPath path2; - path2.addPolygon(polygon2); - - QPolygonF pathPoly = view.mapFromScene(path).toFillPolygon(); - QPolygonF path2Poly = path2.toFillPolygon(); - - for (int i = 0; i < pathPoly.size(); ++i) { - QVERIFY(qAbs(pathPoly[i].x() - path2Poly[i].x()) < 3); - QVERIFY(qAbs(pathPoly[i].y() - path2Poly[i].y()) < 3); - } + QPainterPath viewPath = view.mapFromScene(inputPath); + QCOMPARE(viewPath, path); } void tst_QGraphicsView::sendEvent() |
