summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/corelib/CMakeLists.txt13
-rw-r--r--src/corelib/configure.cmake2
-rw-r--r--src/corelib/io/qrandomaccessasyncfile_darwin.mm728
-rw-r--r--src/corelib/io/qrandomaccessasyncfile_p_p.h112
-rw-r--r--src/corelib/kernel/qobject.cpp39
-rw-r--r--src/corelib/text/qunicodetools.cpp3
-rw-r--r--src/corelib/time/qtimezoneprivate.cpp6
-rw-r--r--src/corelib/time/qtimezoneprivate_p.h2
-rw-r--r--src/corelib/tools/qlist.h7
-rw-r--r--src/gui/rhi/qrhivulkan.cpp12
-rw-r--r--src/gui/vulkan/qvulkanwindow.cpp13
-rw-r--r--src/network/kernel/qhostaddress.cpp7
-rw-r--r--src/network/kernel/qhostaddress_p.h8
-rw-r--r--src/network/kernel/qnetworkinformation.cpp1
-rw-r--r--src/widgets/doc/images/designer-stylesheet-options.pngbin18914 -> 0 bytes
-rw-r--r--src/widgets/doc/images/designer-stylesheet-options.webpbin0 -> 20214 bytes
-rw-r--r--src/widgets/doc/images/designer-stylesheet-usage.pngbin8128 -> 0 bytes
-rw-r--r--src/widgets/doc/images/designer-stylesheet-usage.webpbin0 -> 6816 bytes
-rw-r--r--src/widgets/doc/images/designer-validator-highlighter.pngbin27153 -> 0 bytes
-rw-r--r--src/widgets/doc/images/designer-validator-highlighter.webpbin0 -> 20718 bytes
-rw-r--r--src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc13
21 files changed, 905 insertions, 61 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/text/qunicodetools.cpp b/src/corelib/text/qunicodetools.cpp
index 14c611bdb5b..2d0b65fcc76 100644
--- a/src/corelib/text/qunicodetools.cpp
+++ b/src/corelib/text/qunicodetools.cpp
@@ -1124,6 +1124,7 @@ static void getLineBreaks(const char16_t *string, qsizetype len, QCharAttributes
static void getWhiteSpaces(const char16_t *string, qsizetype len, QCharAttributes *attributes)
{
for (qsizetype i = 0; i != len; ++i) {
+ const auto pos = i;
uint ucs4 = string[i];
if (QChar::isHighSurrogate(ucs4) && i + 1 != len) {
ushort low = string[i + 1];
@@ -1134,7 +1135,7 @@ static void getWhiteSpaces(const char16_t *string, qsizetype len, QCharAttribute
}
if (Q_UNLIKELY(QChar::isSpace(ucs4)))
- attributes[i].whiteSpace = true;
+ attributes[pos].whiteSpace = true;
}
}
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/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/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index b946f8777b6..c5167a6e7de 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -8824,6 +8824,18 @@ bool QVkSwapChain::ensureSurface()
if (ok) {
colorFormat = formats[i].format;
colorSpace = formats[i].colorSpace;
+#if QT_CONFIG(wayland)
+ // On Wayland, only one color management surface can be created at a time without
+ // triggering a protocol error, and we create one ourselves in some situations.
+ // To avoid this problem, use VK_COLOR_SPACE_PASS_THROUGH_EXT when supported,
+ // so that the driver doesn't create a color management surface as well.
+ const bool hasPassThrough = std::any_of(formats.begin(), formats.end(), [this](const VkSurfaceFormatKHR &fmt) {
+ return fmt.format == colorFormat && fmt.colorSpace == VK_COLOR_SPACE_PASS_THROUGH_EXT;
+ });
+ if (hasPassThrough) {
+ colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
+ }
+#endif
break;
}
}
diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp
index a1457006888..1e52e460d38 100644
--- a/src/gui/vulkan/qvulkanwindow.cpp
+++ b/src/gui/vulkan/qvulkanwindow.cpp
@@ -871,6 +871,19 @@ void QVulkanWindowPrivate::init()
}
}
+#if QT_CONFIG(wayland)
+ // On Wayland, only one color management surface can be created at a time without
+ // triggering a protocol error, and we create one ourselves in some situations.
+ // To avoid this problem, use VK_COLOR_SPACE_PASS_THROUGH_EXT when supported,
+ // so that the driver doesn't create a color management surface as well.
+ const bool hasPassthrough = std::any_of(formats.cbegin(), formats.cend(), [this](const VkSurfaceFormatKHR &format) {
+ return format.format == colorFormat && format.colorSpace == VK_COLOR_SPACE_PASS_THROUGH_EXT;
+ });
+ if (hasPassthrough) {
+ colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
+ }
+#endif
+
const VkFormat dsFormatCandidates[] = {
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
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/widgets/doc/images/designer-stylesheet-options.png b/src/widgets/doc/images/designer-stylesheet-options.png
deleted file mode 100644
index a6893e770bc..00000000000
--- a/src/widgets/doc/images/designer-stylesheet-options.png
+++ /dev/null
Binary files differ
diff --git a/src/widgets/doc/images/designer-stylesheet-options.webp b/src/widgets/doc/images/designer-stylesheet-options.webp
new file mode 100644
index 00000000000..14d1ad368fc
--- /dev/null
+++ b/src/widgets/doc/images/designer-stylesheet-options.webp
Binary files differ
diff --git a/src/widgets/doc/images/designer-stylesheet-usage.png b/src/widgets/doc/images/designer-stylesheet-usage.png
deleted file mode 100644
index f6875900def..00000000000
--- a/src/widgets/doc/images/designer-stylesheet-usage.png
+++ /dev/null
Binary files differ
diff --git a/src/widgets/doc/images/designer-stylesheet-usage.webp b/src/widgets/doc/images/designer-stylesheet-usage.webp
new file mode 100644
index 00000000000..79dd6803853
--- /dev/null
+++ b/src/widgets/doc/images/designer-stylesheet-usage.webp
Binary files differ
diff --git a/src/widgets/doc/images/designer-validator-highlighter.png b/src/widgets/doc/images/designer-validator-highlighter.png
deleted file mode 100644
index a6661d5c955..00000000000
--- a/src/widgets/doc/images/designer-validator-highlighter.png
+++ /dev/null
Binary files differ
diff --git a/src/widgets/doc/images/designer-validator-highlighter.webp b/src/widgets/doc/images/designer-validator-highlighter.webp
new file mode 100644
index 00000000000..7ca6cdf6eb3
--- /dev/null
+++ b/src/widgets/doc/images/designer-validator-highlighter.webp
Binary files differ
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}
*/