summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/double-conversion/0001-fix-decimal_point-initialization-to-suppress-warning.patch40
-rw-r--r--src/3rdparty/double-conversion/REUSE.toml2
-rw-r--r--src/3rdparty/double-conversion/double-conversion/double-to-string.cc3
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/assets/CMakeLists.txt6
-rw-r--r--src/assets/downloader/CMakeLists.txt32
-rw-r--r--src/assets/downloader/assetdownloader.cpp592
-rw-r--r--src/assets/downloader/assetdownloader.h118
-rw-r--r--src/assets/downloader/tasking/barrier.cpp54
-rw-r--r--src/assets/downloader/tasking/barrier.h126
-rw-r--r--src/assets/downloader/tasking/concurrentcall.h119
-rw-r--r--src/assets/downloader/tasking/conditional.cpp91
-rw-r--r--src/assets/downloader/tasking/conditional.h142
-rw-r--r--src/assets/downloader/tasking/networkquery.cpp63
-rw-r--r--src/assets/downloader/tasking/networkquery.h74
-rw-r--r--src/assets/downloader/tasking/qprocesstask.cpp280
-rw-r--r--src/assets/downloader/tasking/qprocesstask.h89
-rw-r--r--src/assets/downloader/tasking/tasking_global.h25
-rw-r--r--src/assets/downloader/tasking/tasktree.cpp3701
-rw-r--r--src/assets/downloader/tasking/tasktree.h757
-rw-r--r--src/assets/downloader/tasking/tasktreerunner.cpp45
-rw-r--r--src/assets/downloader/tasking/tasktreerunner.h63
-rw-r--r--src/assets/downloader/tasking/tcpsocket.cpp57
-rw-r--r--src/assets/downloader/tasking/tcpsocket.h65
-rw-r--r--src/corelib/CMakeLists.txt4
-rw-r--r--src/corelib/configure.cmake2
-rw-r--r--src/corelib/doc/images/javaiterators1.svg59
-rw-r--r--src/corelib/doc/images/javaiterators2.svg63
-rw-r--r--src/corelib/doc/src/java-style-iterators.qdoc4
-rw-r--r--src/corelib/doc/src/objectmodel/bindableproperties.qdoc4
-rw-r--r--src/corelib/io/qlockfile.cpp5
-rw-r--r--src/corelib/itemmodels/qrangemodel.cpp12
-rw-r--r--src/corelib/itemmodels/qrangemodel_impl.h70
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp2
-rw-r--r--src/corelib/kernel/qcoreapplication.h2
-rw-r--r--src/corelib/kernel/qpermissions.cpp4
-rw-r--r--src/corelib/mimetypes/qmimedatabase.cpp4
-rw-r--r--src/corelib/mimetypes/qmimeprovider.cpp19
-rw-r--r--src/corelib/mimetypes/qmimeprovider_p.h2
-rw-r--r--src/corelib/platform/wasm/qstdweb.cpp265
-rw-r--r--src/corelib/platform/wasm/qstdweb_p.h77
-rw-r--r--src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp47
-rw-r--r--src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h2
-rw-r--r--src/corelib/plugin/qlibrary.h1
-rw-r--r--src/corelib/plugin/qlibrary_p.h7
-rw-r--r--src/corelib/text/qstring.cpp95
-rw-r--r--src/corelib/thread/qthread.h28
-rw-r--r--src/corelib/time/qdatetime.cpp186
-rw-r--r--src/corelib/time/qgregoriancalendar.cpp1
-rw-r--r--src/corelib/time/qjalalicalendar.cpp1
-rw-r--r--src/corelib/time/qjuliancalendar.cpp1
-rw-r--r--src/corelib/time/qmilankoviccalendar.cpp1
-rw-r--r--src/corelib/time/qromancalendar.cpp1
-rw-r--r--src/corelib/time/qtimezone.cpp7
-rw-r--r--src/corelib/time/qtimezoneprivate_p.h2
-rw-r--r--src/corelib/tools/qiterator.qdoc20
-rw-r--r--src/gui/configure.cmake11
-rw-r--r--src/gui/doc/src/richtext.qdoc6
-rw-r--r--src/gui/image/qimage.cpp5
-rw-r--r--src/gui/image/qplatformpixmap.h7
-rw-r--r--src/gui/itemmodels/qfilesystemmodel.cpp9
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp2
-rw-r--r--src/gui/rhi/qrhigles2.cpp2
-rw-r--r--src/gui/rhi/qrhigles2_p.h1
-rw-r--r--src/gui/text/qfont_p.h4
-rw-r--r--src/gui/text/qtextengine.cpp61
-rw-r--r--src/gui/text/qtextengine_p.h4
-rw-r--r--src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp3
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibility.mm1
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm2
-rw-r--r--src/plugins/platforms/ios/qiostheme.h1
-rw-r--r--src/plugins/platforms/ios/qiostheme.mm6
-rw-r--r--src/plugins/platforms/ios/quiwindow.mm2
-rw-r--r--src/plugins/platforms/wasm/qwasmaccessibility.cpp9
-rw-r--r--src/plugins/platforms/wasm/qwasminputcontext.cpp65
-rw-r--r--src/plugins/platforms/wasm/qwasminputcontext.h2
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp13
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h2
-rw-r--r--src/plugins/platforms/wayland/CMakeLists.txt8
-rw-r--r--src/plugins/platforms/windows/qwindowsiconengine.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp17
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.h2
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp32
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp1
-rw-r--r--src/plugins/platforms/xcb/CMakeLists.txt24
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp175
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h38
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp615
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h43
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp2807
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h86
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp2087
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h131
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpolygonclipper_p.h278
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qt_x11_p.h163
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator.cpp1466
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator_p.h51
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp288
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h64
-rw-r--r--src/plugins/platforms/xcb/qxcbimage.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp31
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h2
-rw-r--r--src/plugins/styles/modernwindows/qwindows11style.cpp75
-rw-r--r--src/plugins/styles/modernwindows/qwindowsvistastyle.cpp14
-rw-r--r--src/tools/windeployqt/main.cpp8
-rw-r--r--src/widgets/CMakeLists.txt1
-rw-r--r--src/widgets/accessible/itemviews.cpp4
-rw-r--r--src/widgets/accessible/itemviews_p.h4
-rw-r--r--src/widgets/accessible/rangecontrols.cpp10
-rw-r--r--src/widgets/accessible/rangecontrols_p.h1
-rw-r--r--src/widgets/dialogs/qcolordialog.cpp255
-rw-r--r--src/widgets/dialogs/qcolorwell_p.h142
-rw-r--r--src/widgets/dialogs/qfiledialog.ui10
-rw-r--r--src/widgets/dialogs/qfontdialog.cpp2
-rw-r--r--src/widgets/styles/qcommonstyle.cpp21
-rw-r--r--src/widgets/styles/qwindowsstyle.cpp13
-rw-r--r--src/widgets/widgets/qlineedit_p.cpp7
-rw-r--r--src/widgets/widgets/qtabbar.cpp2
119 files changed, 1306 insertions, 15447 deletions
diff --git a/src/3rdparty/double-conversion/0001-fix-decimal_point-initialization-to-suppress-warning.patch b/src/3rdparty/double-conversion/0001-fix-decimal_point-initialization-to-suppress-warning.patch
new file mode 100644
index 00000000000..2dd58d4b818
--- /dev/null
+++ b/src/3rdparty/double-conversion/0001-fix-decimal_point-initialization-to-suppress-warning.patch
@@ -0,0 +1,40 @@
+From c75f7f48c8a3d9c6aaaeb13a48fb3c051b46ccab Mon Sep 17 00:00:00 2001
+From: Ivan Solovev <[email protected]>
+Date: Mon, 3 Nov 2025 12:33:26 +0100
+Subject: [PATCH] fix decimal_point initialization to suppress warnings in GCC
+ 14
+
+This patch was submitted upstream as [0], but there was no new release
+yet.
+
+[0]: https://github.com/google/double-conversion/commit/4aecc844c566d84a939fc35f4e62d58bd693f18d
+
+Change-Id: I97cc3103ff758f4c65b23d2f4c0fd82932e36df7
+---
+ .../double-conversion/double-conversion/double-to-string.cc | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/3rdparty/double-conversion/double-conversion/double-to-string.cc b/src/3rdparty/double-conversion/double-conversion/double-to-string.cc
+index 215eaa96d47..9ea3d18d5f7 100644
+--- a/src/3rdparty/double-conversion/double-conversion/double-to-string.cc
++++ b/src/3rdparty/double-conversion/double-conversion/double-to-string.cc
+@@ -180,7 +180,7 @@ bool DoubleToStringConverter::ToShortestIeeeNumber(
+ return HandleSpecialValues(value, result_builder);
+ }
+
+- int decimal_point;
++ int decimal_point = 0;
+ bool sign;
+ const int kDecimalRepCapacity = kBase10MaximalLength + 1;
+ char decimal_rep[kDecimalRepCapacity];
+@@ -405,6 +405,7 @@ void DoubleToStringConverter::DoubleToAscii(double v,
+ if (mode == PRECISION && requested_digits == 0) {
+ vector[0] = '\0';
+ *length = 0;
++ *point = 0;
+ return;
+ }
+
+--
+2.44.0
+
diff --git a/src/3rdparty/double-conversion/REUSE.toml b/src/3rdparty/double-conversion/REUSE.toml
index a7ebffce4f0..14e6e9b6706 100644
--- a/src/3rdparty/double-conversion/REUSE.toml
+++ b/src/3rdparty/double-conversion/REUSE.toml
@@ -1,7 +1,7 @@
version = 1
[[annotations]]
-path = ["double-conversion/*"]
+path = ["double-conversion/*", "0001-fix-decimal_point-initialization-to-suppress-warning.patch"]
precedence = "closest"
SPDX-FileCopyrightText = "Copyright 2006-2012, the V8 project authors"
SPDX-License-Identifier = "BSD-3-Clause"
diff --git a/src/3rdparty/double-conversion/double-conversion/double-to-string.cc b/src/3rdparty/double-conversion/double-conversion/double-to-string.cc
index 215eaa96d47..9ea3d18d5f7 100644
--- a/src/3rdparty/double-conversion/double-conversion/double-to-string.cc
+++ b/src/3rdparty/double-conversion/double-conversion/double-to-string.cc
@@ -180,7 +180,7 @@ bool DoubleToStringConverter::ToShortestIeeeNumber(
return HandleSpecialValues(value, result_builder);
}
- int decimal_point;
+ int decimal_point = 0;
bool sign;
const int kDecimalRepCapacity = kBase10MaximalLength + 1;
char decimal_rep[kDecimalRepCapacity];
@@ -405,6 +405,7 @@ void DoubleToStringConverter::DoubleToAscii(double v,
if (mode == PRECISION && requested_digits == 0) {
vector[0] = '\0';
*length = 0;
+ *point = 0;
return;
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index babf5bc31d2..2920c743243 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -71,7 +71,6 @@ add_subdirectory(tools)
if(QT_FEATURE_gui)
add_subdirectory(gui)
- add_subdirectory(assets)
if(QT_FEATURE_opengl)
add_subdirectory(opengl)
diff --git a/src/assets/CMakeLists.txt b/src/assets/CMakeLists.txt
deleted file mode 100644
index ab07d27696e..00000000000
--- a/src/assets/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-# Copyright (C) 2024 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-if (NOT INTEGRITY AND TARGET Qt6::Network AND TARGET Qt6::Concurrent)
- add_subdirectory(downloader)
-endif()
diff --git a/src/assets/downloader/CMakeLists.txt b/src/assets/downloader/CMakeLists.txt
deleted file mode 100644
index 6b0564e72af..00000000000
--- a/src/assets/downloader/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2024 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-qt_internal_add_module(ExamplesAssetDownloaderPrivate
- CONFIG_MODULE_NAME examples_asset_downloader
- STATIC
- INTERNAL_MODULE
- SOURCES
- assetdownloader.cpp assetdownloader.h
- tasking/barrier.cpp tasking/barrier.h
- tasking/concurrentcall.h
- tasking/conditional.cpp tasking/conditional.h
- tasking/networkquery.cpp tasking/networkquery.h
- tasking/qprocesstask.cpp tasking/qprocesstask.h
- tasking/tasking_global.h
- tasking/tasktree.cpp tasking/tasktree.h
- tasking/tasktreerunner.cpp tasking/tasktreerunner.h
- tasking/tcpsocket.cpp tasking/tcpsocket.h
- DEFINES
- QT_NO_CAST_FROM_ASCII
- LIBRARIES
- Qt6::CorePrivate
- PUBLIC_LIBRARIES
- Qt6::Concurrent
- Qt6::Core
- Qt6::Network
- NO_GENERATE_CPP_EXPORTS
-)
-
-if (NOT QT_FEATURE_process)
- set_source_files_properties(tasking/qprocesstask.h PROPERTIES SKIP_AUTOMOC TRUE)
-endif()
diff --git a/src/assets/downloader/assetdownloader.cpp b/src/assets/downloader/assetdownloader.cpp
deleted file mode 100644
index 7c2f525a66d..00000000000
--- a/src/assets/downloader/assetdownloader.cpp
+++ /dev/null
@@ -1,592 +0,0 @@
-// Copyright (C) 2024 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
-
-#include "assetdownloader.h"
-
-#include "tasking/concurrentcall.h"
-#include "tasking/networkquery.h"
-#include "tasking/tasktreerunner.h"
-
-#include <QtCore/private/qzipreader_p.h>
-
-#include <QtCore/QDir>
-#include <QtCore/QFile>
-#include <QtCore/QJsonArray>
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonObject>
-#include <QtCore/QStandardPaths>
-#include <QtCore/QTemporaryDir>
-#include <QtCore/QTemporaryFile>
-
-using namespace Tasking;
-
-QT_BEGIN_NAMESPACE
-
-namespace Assets::Downloader {
-
-struct DownloadableAssets
-{
- QUrl remoteUrl;
- QList<QUrl> files;
-};
-
-class AssetDownloaderPrivate
-{
-public:
- AssetDownloaderPrivate(AssetDownloader *q) : m_q(q) {}
- AssetDownloader *m_q = nullptr;
-
- std::unique_ptr<QNetworkAccessManager> m_manager;
- std::unique_ptr<QTemporaryDir> m_temporaryDir;
- TaskTreeRunner m_taskTreeRunner;
- QString m_lastProgressText;
- QDir m_localDownloadDir;
-
- QString m_jsonFileName;
- QString m_zipFileName;
- QDir m_preferredLocalDownloadDir =
- QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
- QUrl m_offlineAssetsFilePath;
- QUrl m_downloadBase;
- QStringList m_networkErrors;
- QStringList m_sslErrors;
-
- void setLocalDownloadDir(const QDir &dir)
- {
- if (m_localDownloadDir != dir) {
- m_localDownloadDir = dir;
- emit m_q->localDownloadDirChanged(QUrl::fromLocalFile(m_localDownloadDir.absolutePath()));
- }
- }
- void setProgress(int progressValue, int progressMaximum, const QString &progressText)
- {
- m_lastProgressText = progressText;
- emit m_q->progressChanged(progressValue, progressMaximum, progressText);
- }
- void updateProgress(int progressValue, int progressMaximum)
- {
- setProgress(progressValue, progressMaximum, m_lastProgressText);
- }
- void clearProgress(const QString &progressText)
- {
- setProgress(0, 0, progressText);
- }
-
- void setupDownload(NetworkQuery *query, const QString &progressText)
- {
- query->setNetworkAccessManager(m_manager.get());
- clearProgress(progressText);
- QObject::connect(query, &NetworkQuery::started, query, [this, query] {
- QNetworkReply *reply = query->reply();
- QObject::connect(reply, &QNetworkReply::downloadProgress,
- query, [this](qint64 bytesReceived, qint64 totalBytes) {
- updateProgress((totalBytes > 0) ? 100.0 * bytesReceived / totalBytes : 0, 100);
- });
- QObject::connect(reply, &QNetworkReply::errorOccurred, query, [this, reply] {
- m_networkErrors << reply->errorString();
- });
-#if QT_CONFIG(ssl)
- QObject::connect(reply, &QNetworkReply::sslErrors,
- query, [this](const QList<QSslError> &sslErrors) {
- for (const QSslError &sslError : sslErrors)
- m_sslErrors << sslError.errorString();
- });
-#endif
- });
- }
-};
-
-static bool isWritableDir(const QDir &dir)
-{
- if (dir.exists()) {
- QTemporaryFile file(dir.filePath(QString::fromLatin1("tmp")));
- return file.open();
- }
- return false;
-}
-
-static bool sameFileContent(const QFileInfo &first, const QFileInfo &second)
-{
- if (first.exists() ^ second.exists())
- return false;
-
- if (first.size() != second.size())
- return false;
-
- QFile firstFile(first.absoluteFilePath());
- QFile secondFile(second.absoluteFilePath());
-
- if (firstFile.open(QFile::ReadOnly) && secondFile.open(QFile::ReadOnly)) {
- char char1;
- char char2;
- int readBytes1 = 0;
- int readBytes2 = 0;
- while (!firstFile.atEnd()) {
- readBytes1 = firstFile.read(&char1, 1);
- readBytes2 = secondFile.read(&char2, 1);
- if (readBytes1 != readBytes2 || readBytes1 != 1)
- return false;
- if (char1 != char2)
- return false;
- }
- return true;
- }
-
- return false;
-}
-
-static bool createDirectory(const QDir &dir)
-{
- if (dir.exists())
- return true;
-
- if (!createDirectory(dir.absoluteFilePath(QString::fromUtf8(".."))))
- return false;
-
- return dir.mkpath(QString::fromUtf8("."));
-}
-
-static bool canBeALocalBaseDir(const QDir &dir)
-{
- if (dir.exists())
- return !dir.isEmpty() || isWritableDir(dir);
- return createDirectory(dir) && isWritableDir(dir);
-}
-
-static QDir baseLocalDir(const QDir &preferredLocalDir)
-{
- if (canBeALocalBaseDir(preferredLocalDir))
- return preferredLocalDir;
-
- qWarning().noquote() << "AssetDownloader: Cannot set \"" << preferredLocalDir
- << "\" as a local download directory!";
- return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
-}
-
-static QString pathFromUrl(const QUrl &url)
-{
- if (url.isLocalFile())
- return url.toLocalFile();
-
- if (url.scheme() == u"qrc")
- return u":" + url.path();
-
- return url.toString();
-}
-
-static QList<QUrl> filterDownloadableAssets(const QList<QUrl> &assetFiles, const QDir &expectedDir)
-{
- QList<QUrl> downloadList;
- std::copy_if(assetFiles.begin(), assetFiles.end(), std::back_inserter(downloadList),
- [&](const QUrl &assetPath) {
- return !QFileInfo::exists(expectedDir.absoluteFilePath(assetPath.toString()));
- });
- return downloadList;
-}
-
-static bool allAssetsPresent(const QList<QUrl> &assetFiles, const QDir &expectedDir)
-{
- return std::all_of(assetFiles.begin(), assetFiles.end(), [&](const QUrl &assetPath) {
- return QFileInfo::exists(expectedDir.absoluteFilePath(assetPath.toString()));
- });
-}
-
-AssetDownloader::AssetDownloader(QObject *parent)
- : QObject(parent)
- , d(new AssetDownloaderPrivate(this))
-{}
-
-AssetDownloader::~AssetDownloader() = default;
-
-QUrl AssetDownloader::downloadBase() const
-{
- return d->m_downloadBase;
-}
-
-void AssetDownloader::setDownloadBase(const QUrl &downloadBase)
-{
- if (d->m_downloadBase != downloadBase) {
- d->m_downloadBase = downloadBase;
- emit downloadBaseChanged(d->m_downloadBase);
- }
-}
-
-QUrl AssetDownloader::preferredLocalDownloadDir() const
-{
- return QUrl::fromLocalFile(d->m_preferredLocalDownloadDir.absolutePath());
-}
-
-void AssetDownloader::setPreferredLocalDownloadDir(const QUrl &localDir)
-{
- if (localDir.scheme() == u"qrc") {
- qWarning() << "Cannot set a qrc as preferredLocalDownloadDir";
- return;
- }
-
- const QString path = pathFromUrl(localDir);
- if (d->m_preferredLocalDownloadDir != path) {
- d->m_preferredLocalDownloadDir.setPath(path);
- emit preferredLocalDownloadDirChanged(preferredLocalDownloadDir());
- }
-}
-
-QUrl AssetDownloader::offlineAssetsFilePath() const
-{
- return d->m_offlineAssetsFilePath;
-}
-
-void AssetDownloader::setOfflineAssetsFilePath(const QUrl &offlineAssetsFilePath)
-{
- if (d->m_offlineAssetsFilePath != offlineAssetsFilePath) {
- d->m_offlineAssetsFilePath = offlineAssetsFilePath;
- emit offlineAssetsFilePathChanged(d->m_offlineAssetsFilePath);
- }
-}
-
-QString AssetDownloader::jsonFileName() const
-{
- return d->m_jsonFileName;
-}
-
-void AssetDownloader::setJsonFileName(const QString &jsonFileName)
-{
- if (d->m_jsonFileName != jsonFileName) {
- d->m_jsonFileName = jsonFileName;
- emit jsonFileNameChanged(d->m_jsonFileName);
- }
-}
-
-QString AssetDownloader::zipFileName() const
-{
- return d->m_zipFileName;
-}
-
-void AssetDownloader::setZipFileName(const QString &zipFileName)
-{
- if (d->m_zipFileName != zipFileName) {
- d->m_zipFileName = zipFileName;
- emit zipFileNameChanged(d->m_zipFileName);
- }
-}
-
-QUrl AssetDownloader::localDownloadDir() const
-{
- return QUrl::fromLocalFile(d->m_localDownloadDir.absolutePath());
-}
-
-QStringList AssetDownloader::networkErrors() const
-{
- return d->m_networkErrors;
-}
-
-QStringList AssetDownloader::sslErrors() const
-{
- return d->m_sslErrors;
-}
-
-static void precheckLocalFile(const QUrl &url)
-{
- if (url.isEmpty())
- return;
- QFile file(pathFromUrl(url));
- if (!file.open(QIODevice::ReadOnly))
- qWarning() << "Cannot open local file" << url;
-}
-
-static void readAssetsFileContent(QPromise<DownloadableAssets> &promise, const QByteArray &content)
-{
- const QJsonObject json = QJsonDocument::fromJson(content).object();
- const QJsonArray assetsArray = json[u"assets"].toArray();
- DownloadableAssets result;
- result.remoteUrl = json[u"url"].toString();
- for (const QJsonValue &asset : assetsArray) {
- if (promise.isCanceled())
- return;
- result.files.append(asset.toString());
- }
-
- if (result.files.isEmpty() || result.remoteUrl.isEmpty())
- promise.future().cancel();
- else
- promise.addResult(result);
-}
-
-static void unzip(QPromise<void> &promise, const QByteArray &content, const QDir &directory,
- const QString &fileName)
-{
- const QString zipFilePath = directory.absoluteFilePath(fileName);
- QFile zipFile(zipFilePath);
- if (!zipFile.open(QIODevice::WriteOnly)) {
- promise.future().cancel();
- return;
- }
- zipFile.write(content);
- zipFile.close();
-
- if (promise.isCanceled())
- return;
-
- QZipReader reader(zipFilePath);
- const bool extracted = reader.extractAll(directory.absolutePath());
- reader.close();
- if (extracted)
- QFile::remove(zipFilePath);
- else
- promise.future().cancel();
-}
-
-static void writeAsset(QPromise<void> &promise, const QByteArray &content, const QString &filePath)
-{
- const QFileInfo fileInfo(filePath);
- QFile file(fileInfo.absoluteFilePath());
- if (!createDirectory(fileInfo.dir()) || !file.open(QFile::WriteOnly)) {
- promise.future().cancel();
- return;
- }
-
- if (promise.isCanceled())
- return;
-
- file.write(content);
- file.close();
-}
-
-static void copyAndCheck(QPromise<void> &promise, const QString &sourcePath, const QString &destPath)
-{
- QFile sourceFile(sourcePath);
- QFile destFile(destPath);
- const QFileInfo sourceFileInfo(sourceFile.fileName());
- const QFileInfo destFileInfo(destFile.fileName());
-
- if (destFile.exists() && !destFile.remove()) {
- qWarning().noquote() << QString::fromLatin1("Unable to remove file \"%1\".")
- .arg(QFileInfo(destFile.fileName()).absoluteFilePath());
- promise.future().cancel();
- return;
- }
-
- if (!createDirectory(destFileInfo.absolutePath())) {
- qWarning().noquote() << QString::fromLatin1("Cannot create directory \"%1\".")
- .arg(destFileInfo.absolutePath());
- promise.future().cancel();
- return;
- }
-
- if (promise.isCanceled())
- return;
-
- if (!sourceFile.copy(destFile.fileName()) && !sameFileContent(sourceFileInfo, destFileInfo))
- promise.future().cancel();
-}
-
-void AssetDownloader::start()
-{
- if (d->m_taskTreeRunner.isRunning())
- return;
-
- struct StorageData
- {
- QDir tempDir;
- QByteArray jsonContent;
- DownloadableAssets assets;
- QList<QUrl> assetsToDownload;
- QByteArray zipContent;
- int doneCount = 0;
- };
-
- const Storage<StorageData> storage;
-
- const auto onSetup = [this, storage] {
- if (!d->m_manager)
- d->m_manager = std::make_unique<QNetworkAccessManager>();
- if (!d->m_temporaryDir)
- d->m_temporaryDir = std::make_unique<QTemporaryDir>();
- if (!d->m_temporaryDir->isValid()) {
- qWarning() << "Cannot create a temporary directory.";
- return SetupResult::StopWithError;
- }
- storage->tempDir = d->m_temporaryDir->path();
- d->setLocalDownloadDir(baseLocalDir(d->m_preferredLocalDownloadDir));
- d->m_networkErrors.clear();
- d->m_sslErrors.clear();
- precheckLocalFile(resolvedUrl(d->m_offlineAssetsFilePath));
- return SetupResult::Continue;
- };
-
- const auto onJsonDownloadSetup = [this](NetworkQuery &query) {
- query.setRequest(QNetworkRequest(d->m_downloadBase.resolved(d->m_jsonFileName)));
- d->setupDownload(&query, tr("Downloading JSON file..."));
- };
- const auto onJsonDownloadDone = [this, storage](const NetworkQuery &query, DoneWith result) {
- if (result == DoneWith::Success) {
- storage->jsonContent = query.reply()->readAll();
- return DoneResult::Success;
- }
- qWarning() << "Cannot download" << d->m_downloadBase.resolved(d->m_jsonFileName)
- << query.reply()->errorString();
- if (d->m_offlineAssetsFilePath.isEmpty()) {
- qWarning() << "Also there is no local file as a replacement";
- return DoneResult::Error;
- }
-
- QFile file(pathFromUrl(resolvedUrl(d->m_offlineAssetsFilePath)));
- if (!file.open(QIODevice::ReadOnly)) {
- qWarning() << "Also failed to open" << d->m_offlineAssetsFilePath;
- return DoneResult::Error;
- }
-
- storage->jsonContent = file.readAll();
- return DoneResult::Success;
- };
-
- const auto onReadAssetsFileSetup = [storage](ConcurrentCall<DownloadableAssets> &async) {
- async.setConcurrentCallData(readAssetsFileContent, storage->jsonContent);
- };
- const auto onReadAssetsFileDone = [storage](const ConcurrentCall<DownloadableAssets> &async) {
- storage->assets = async.result();
- storage->assetsToDownload = storage->assets.files;
- };
-
- const auto onSkipIfAllAssetsPresent = [this, storage] {
- return allAssetsPresent(storage->assets.files, d->m_localDownloadDir)
- ? SetupResult::StopWithSuccess : SetupResult::Continue;
- };
-
- const auto onZipDownloadSetup = [this, storage](NetworkQuery &query) {
- if (d->m_zipFileName.isEmpty())
- return SetupResult::StopWithSuccess;
-
- query.setRequest(QNetworkRequest(d->m_downloadBase.resolved(d->m_zipFileName)));
- d->setupDownload(&query, tr("Downloading zip file..."));
- return SetupResult::Continue;
- };
- const auto onZipDownloadDone = [storage](const NetworkQuery &query, DoneWith result) {
- if (result == DoneWith::Success)
- storage->zipContent = query.reply()->readAll();
- return DoneResult::Success; // Ignore zip download failure
- };
-
- const auto onUnzipSetup = [this, storage](ConcurrentCall<void> &async) {
- if (storage->zipContent.isEmpty())
- return SetupResult::StopWithSuccess;
-
- async.setConcurrentCallData(unzip, storage->zipContent, storage->tempDir, d->m_zipFileName);
- d->clearProgress(tr("Unzipping..."));
- return SetupResult::Continue;
- };
- const auto onUnzipDone = [storage](DoneWith result) {
- if (result == DoneWith::Success) {
- // Avoid downloading assets that are present in unzipped tree
- StorageData &storageData = *storage;
- storageData.assetsToDownload =
- filterDownloadableAssets(storageData.assets.files, storageData.tempDir);
- } else {
- qWarning() << "ZipFile failed";
- }
- return DoneResult::Success; // Ignore unzip failure
- };
-
- const LoopUntil downloadIterator([storage](int iteration) {
- return iteration < storage->assetsToDownload.count();
- });
-
- const Storage<QByteArray> assetStorage;
-
- const auto onAssetsDownloadGroupSetup = [this, storage] {
- d->setProgress(0, storage->assetsToDownload.size(), tr("Downloading assets..."));
- };
-
- const auto onAssetDownloadSetup = [this, storage, downloadIterator](NetworkQuery &query) {
- query.setNetworkAccessManager(d->m_manager.get());
- query.setRequest(QNetworkRequest(storage->assets.remoteUrl.resolved(
- storage->assetsToDownload.at(downloadIterator.iteration()))));
- };
- const auto onAssetDownloadDone = [assetStorage](const NetworkQuery &query, DoneWith result) {
- if (result == DoneWith::Success)
- *assetStorage = query.reply()->readAll();
- };
-
- const auto onAssetWriteSetup = [storage, downloadIterator, assetStorage](
- ConcurrentCall<void> &async) {
- const QString filePath = storage->tempDir.absoluteFilePath(
- storage->assetsToDownload.at(downloadIterator.iteration()).toString());
- async.setConcurrentCallData(writeAsset, *assetStorage, filePath);
- };
- const auto onAssetWriteDone = [this, storage](DoneWith result) {
- if (result != DoneWith::Success) {
- qWarning() << "Asset write failed";
- return;
- }
- StorageData &storageData = *storage;
- ++storageData.doneCount;
- d->updateProgress(storageData.doneCount, storageData.assetsToDownload.size());
- };
-
- const LoopUntil copyIterator([storage](int iteration) {
- return iteration < storage->assets.files.count();
- });
-
- const auto onAssetsCopyGroupSetup = [this, storage] {
- storage->doneCount = 0;
- d->setProgress(0, storage->assets.files.size(), tr("Copying assets..."));
- };
-
- const auto onAssetCopySetup = [this, storage, copyIterator](ConcurrentCall<void> &async) {
- const QString fileName = storage->assets.files.at(copyIterator.iteration()).toString();
- const QString sourcePath = storage->tempDir.absoluteFilePath(fileName);
- const QString destPath = d->m_localDownloadDir.absoluteFilePath(fileName);
- async.setConcurrentCallData(copyAndCheck, sourcePath, destPath);
- };
- const auto onAssetCopyDone = [this, storage] {
- StorageData &storageData = *storage;
- ++storageData.doneCount;
- d->updateProgress(storageData.doneCount, storageData.assets.files.size());
- };
-
- const auto onAssetsCopyGroupDone = [this, storage](DoneWith result) {
- if (result != DoneWith::Success) {
- d->setLocalDownloadDir(storage->tempDir);
- qWarning() << "Asset copy failed";
- return;
- }
- d->m_temporaryDir.reset();
- };
-
- const Group recipe {
- storage,
- onGroupSetup(onSetup),
- NetworkQueryTask(onJsonDownloadSetup, onJsonDownloadDone),
- ConcurrentCallTask<DownloadableAssets>(onReadAssetsFileSetup, onReadAssetsFileDone, CallDoneIf::Success),
- Group {
- onGroupSetup(onSkipIfAllAssetsPresent),
- NetworkQueryTask(onZipDownloadSetup, onZipDownloadDone),
- ConcurrentCallTask<void>(onUnzipSetup, onUnzipDone),
- For (downloadIterator) >> Do {
- parallelIdealThreadCountLimit,
- onGroupSetup(onAssetsDownloadGroupSetup),
- Group {
- assetStorage,
- NetworkQueryTask(onAssetDownloadSetup, onAssetDownloadDone),
- ConcurrentCallTask<void>(onAssetWriteSetup, onAssetWriteDone)
- }
- },
- For (copyIterator) >> Do {
- parallelIdealThreadCountLimit,
- onGroupSetup(onAssetsCopyGroupSetup),
- ConcurrentCallTask<void>(onAssetCopySetup, onAssetCopyDone, CallDoneIf::Success),
- onGroupDone(onAssetsCopyGroupDone)
- }
- }
- };
- d->m_taskTreeRunner.start(recipe, [this](TaskTree *) { emit started(); },
- [this](DoneWith result) { emit finished(result == DoneWith::Success); });
-}
-
-QUrl AssetDownloader::resolvedUrl(const QUrl &url) const
-{
- return url;
-}
-
-} // namespace Assets::Downloader
-
-QT_END_NAMESPACE
diff --git a/src/assets/downloader/assetdownloader.h b/src/assets/downloader/assetdownloader.h
deleted file mode 100644
index 3c9351ceafe..00000000000
--- a/src/assets/downloader/assetdownloader.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (C) 2024 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
-
-#ifndef ASSETDOWNLOADER_H
-#define ASSETDOWNLOADER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/QObject>
-#include <QtCore/QUrl>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-
-namespace Assets::Downloader {
-
-class AssetDownloaderPrivate;
-
-class AssetDownloader : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(
- QUrl downloadBase
- READ downloadBase
- WRITE setDownloadBase
- NOTIFY downloadBaseChanged)
-
- Q_PROPERTY(
- QUrl preferredLocalDownloadDir
- READ preferredLocalDownloadDir
- WRITE setPreferredLocalDownloadDir
- NOTIFY preferredLocalDownloadDirChanged)
-
- Q_PROPERTY(
- QUrl offlineAssetsFilePath
- READ offlineAssetsFilePath
- WRITE setOfflineAssetsFilePath
- NOTIFY offlineAssetsFilePathChanged)
-
- Q_PROPERTY(
- QString jsonFileName
- READ jsonFileName
- WRITE setJsonFileName
- NOTIFY jsonFileNameChanged)
-
- Q_PROPERTY(
- QString zipFileName
- READ zipFileName
- WRITE setZipFileName
- NOTIFY zipFileNameChanged)
-
- Q_PROPERTY(
- QUrl localDownloadDir
- READ localDownloadDir
- NOTIFY localDownloadDirChanged)
-
-public:
- AssetDownloader(QObject *parent = nullptr);
- ~AssetDownloader();
-
- QUrl downloadBase() const;
- void setDownloadBase(const QUrl &downloadBase);
-
- QUrl preferredLocalDownloadDir() const;
- void setPreferredLocalDownloadDir(const QUrl &localDir);
-
- QUrl offlineAssetsFilePath() const;
- void setOfflineAssetsFilePath(const QUrl &offlineAssetsFilePath);
-
- QString jsonFileName() const;
- void setJsonFileName(const QString &jsonFileName);
-
- QString zipFileName() const;
- void setZipFileName(const QString &zipFileName);
-
- QUrl localDownloadDir() const;
-
- Q_INVOKABLE QStringList networkErrors() const;
- Q_INVOKABLE QStringList sslErrors() const;
-
-public Q_SLOTS:
- void start();
-
-protected:
- virtual QUrl resolvedUrl(const QUrl &url) const;
-
-Q_SIGNALS:
- void started();
- void finished(bool success);
- void progressChanged(int progressValue, int progressMaximum, const QString &progressText);
- void localDownloadDirChanged(const QUrl &url);
-
- void downloadBaseChanged(const QUrl &);
- void preferredLocalDownloadDirChanged(const QUrl &url);
- void offlineAssetsFilePathChanged(const QUrl &);
- void jsonFileNameChanged(const QString &);
- void zipFileNameChanged(const QString &);
-
-private:
- std::unique_ptr<AssetDownloaderPrivate> d;
-};
-
-} // namespace Assets::Downloader
-
-QT_END_NAMESPACE
-
-#endif // ASSETDOWNLOADER_H
diff --git a/src/assets/downloader/tasking/barrier.cpp b/src/assets/downloader/tasking/barrier.cpp
deleted file mode 100644
index c9e5992bc78..00000000000
--- a/src/assets/downloader/tasking/barrier.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#include "barrier.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-// That's cut down qtcassert.{c,h} to avoid the dependency.
-#define QT_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QT_STRINGIFY(__LINE__))
-#define QT_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QT_STRING(#cond); action; } do {} while (0)
-
-void Barrier::setLimit(int value)
-{
- QT_ASSERT(!isRunning(), return);
- QT_ASSERT(value > 0, return);
-
- m_limit = value;
-}
-
-void Barrier::start()
-{
- QT_ASSERT(!isRunning(), return);
- m_current = 0;
- m_result.reset();
-}
-
-void Barrier::advance()
-{
- // Calling advance on finished is OK
- QT_ASSERT(isRunning() || m_result, return);
- if (!isRunning()) // no-op
- return;
- ++m_current;
- if (m_current == m_limit)
- stopWithResult(DoneResult::Success);
-}
-
-void Barrier::stopWithResult(DoneResult result)
-{
- // Calling stopWithResult on finished is OK when the same success is passed
- QT_ASSERT(isRunning() || (m_result && *m_result == result), return);
- if (!isRunning()) // no-op
- return;
- m_current = -1;
- m_result = result;
- emit done(result);
-}
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
diff --git a/src/assets/downloader/tasking/barrier.h b/src/assets/downloader/tasking/barrier.h
deleted file mode 100644
index d489d2722a4..00000000000
--- a/src/assets/downloader/tasking/barrier.h
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_BARRIER_H
-#define TASKING_BARRIER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasking_global.h"
-
-#include "tasktree.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-class TASKING_EXPORT Barrier final : public QObject
-{
- Q_OBJECT
-
-public:
- void setLimit(int value);
- int limit() const { return m_limit; }
-
- void start();
- void advance(); // If limit reached, stops with true
- void stopWithResult(DoneResult result); // Ignores limit
-
- bool isRunning() const { return m_current >= 0; }
- int current() const { return m_current; }
- std::optional<DoneResult> result() const { return m_result; }
-
-Q_SIGNALS:
- void done(DoneResult success);
-
-private:
- std::optional<DoneResult> m_result = {};
- int m_limit = 1;
- int m_current = -1;
-};
-
-using BarrierTask = SimpleCustomTask<Barrier>;
-
-template <int Limit = 1>
-class SharedBarrier
-{
-public:
- static_assert(Limit > 0, "SharedBarrier's limit should be 1 or more.");
- SharedBarrier() : m_barrier(new Barrier) {
- m_barrier->setLimit(Limit);
- m_barrier->start();
- }
- Barrier *barrier() const { return m_barrier.get(); }
-
-private:
- std::shared_ptr<Barrier> m_barrier;
-};
-
-template <int Limit = 1>
-using MultiBarrier = Storage<SharedBarrier<Limit>>;
-
-// Can't write: "MultiBarrier barrier;". Only "MultiBarrier<> barrier;" would work.
-// Can't have one alias with default type in C++17, getting the following error:
-// alias template deduction only available with C++20.
-using SingleBarrier = MultiBarrier<1>;
-
-template <int Limit>
-ExecutableItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
-{
- return BarrierTask([sharedBarrier](Barrier &barrier) {
- SharedBarrier<Limit> *activeBarrier = sharedBarrier.activeStorage();
- if (!activeBarrier) {
- qWarning("The barrier referenced from WaitForBarrier element "
- "is not reachable in the running tree. "
- "It is possible that no barrier was added to the tree, "
- "or the barrier is not reachable from where it is referenced. "
- "The WaitForBarrier task finishes with an error. ");
- return SetupResult::StopWithError;
- }
- Barrier *activeSharedBarrier = activeBarrier->barrier();
- const std::optional<DoneResult> result = activeSharedBarrier->result();
- if (result.has_value()) {
- return *result == DoneResult::Success ? SetupResult::StopWithSuccess
- : SetupResult::StopWithError;
- }
- QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult);
- return SetupResult::Continue;
- });
-}
-
-template <typename Signal>
-ExecutableItem signalAwaiter(const typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal)
-{
- return BarrierTask([sender, signal](Barrier &barrier) {
- QObject::connect(sender, signal, &barrier, &Barrier::advance, Qt::SingleShotConnection);
- });
-}
-
-using BarrierKickerGetter = std::function<ExecutableItem(const SingleBarrier &)>;
-
-class TASKING_EXPORT When final
-{
-public:
- explicit When(const BarrierKickerGetter &kicker) : m_barrierKicker(kicker) {}
-
-private:
- TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem);
-
- BarrierKickerGetter m_barrierKicker;
-};
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
-
-#endif // TASKING_BARRIER_H
diff --git a/src/assets/downloader/tasking/concurrentcall.h b/src/assets/downloader/tasking/concurrentcall.h
deleted file mode 100644
index cc701297d13..00000000000
--- a/src/assets/downloader/tasking/concurrentcall.h
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_CONCURRENTCALL_H
-#define TASKING_CONCURRENTCALL_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasktree.h"
-
-#include <QtConcurrent/QtConcurrent>
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-// This class introduces the dependency to Qt::Concurrent, otherwise Tasking namespace
-// is independent on Qt::Concurrent.
-// Possibly, it could be placed inside Qt::Concurrent library, as a wrapper around
-// QtConcurrent::run() call.
-
-template <typename ResultType>
-class ConcurrentCall
-{
- Q_DISABLE_COPY_MOVE(ConcurrentCall)
-
-public:
- ConcurrentCall() = default;
- template <typename Function, typename ...Args>
- void setConcurrentCallData(Function &&function, Args &&...args)
- {
- wrapConcurrent(std::forward<Function>(function), std::forward<Args>(args)...);
- }
- void setThreadPool(QThreadPool *pool) { m_threadPool = pool; }
- ResultType result() const
- {
- return m_future.resultCount() ? m_future.result() : ResultType();
- }
- QList<ResultType> results() const
- {
- return m_future.results();
- }
- QFuture<ResultType> future() const { return m_future; }
-
-private:
- template <typename Function, typename ...Args>
- void wrapConcurrent(Function &&function, Args &&...args)
- {
- m_startHandler = [this, function = std::forward<Function>(function), args...] {
- QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
- return QtConcurrent::run(threadPool, function, args...);
- };
- }
-
- template <typename Function, typename ...Args>
- void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args)
- {
- m_startHandler = [this, wrapper = std::forward<std::reference_wrapper<const Function>>(wrapper), args...] {
- QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
- return QtConcurrent::run(threadPool, std::forward<const Function>(wrapper.get()),
- args...);
- };
- }
-
- template <typename T>
- friend class ConcurrentCallTaskAdapter;
-
- std::function<QFuture<ResultType>()> m_startHandler;
- QThreadPool *m_threadPool = nullptr;
- QFuture<ResultType> m_future;
-};
-
-template <typename ResultType>
-class ConcurrentCallTaskAdapter : public TaskAdapter<ConcurrentCall<ResultType>>
-{
-public:
- ~ConcurrentCallTaskAdapter() {
- if (m_watcher) {
- m_watcher->cancel();
- m_watcher->waitForFinished();
- }
- }
-
- void start() final {
- if (!this->task()->m_startHandler) {
- emit this->done(DoneResult::Error); // TODO: Add runtime assert
- return;
- }
- m_watcher.reset(new QFutureWatcher<ResultType>);
- this->connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
- emit this->done(toDoneResult(!m_watcher->isCanceled()));
- m_watcher.release()->deleteLater();
- });
- this->task()->m_future = this->task()->m_startHandler();
- m_watcher->setFuture(this->task()->m_future);
- }
-
-private:
- std::unique_ptr<QFutureWatcher<ResultType>> m_watcher;
-};
-
-template <typename T>
-using ConcurrentCallTask = CustomTask<ConcurrentCallTaskAdapter<T>>;
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
-
-#endif // TASKING_CONCURRENTCALL_H
diff --git a/src/assets/downloader/tasking/conditional.cpp b/src/assets/downloader/tasking/conditional.cpp
deleted file mode 100644
index 24a03fb703e..00000000000
--- a/src/assets/downloader/tasking/conditional.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#include "conditional.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-static Group conditionRecipe(const Storage<bool> &bodyExecutedStorage, const ConditionData &condition)
-{
- const auto onSetup = [bodyExecutedStorage] {
- return *bodyExecutedStorage ? SetupResult::StopWithSuccess : SetupResult::Continue;
- };
-
- const auto onBodyDone = [bodyExecutedStorage] { *bodyExecutedStorage = true; };
-
- const Group bodyTask { condition.m_body, onGroupDone(onBodyDone) };
-
- return {
- onGroupSetup(onSetup),
- condition.m_condition ? Group{ !*condition.m_condition || bodyTask } : bodyTask
- };
-}
-
-static ExecutableItem conditionsRecipe(const QList<ConditionData> &conditions)
-{
- Storage<bool> bodyExecutedStorage;
-
- GroupItems recipes;
- for (const ConditionData &condition : conditions)
- recipes << conditionRecipe(bodyExecutedStorage, condition);
-
- return Group { bodyExecutedStorage, recipes };
-}
-
-ThenItem::operator ExecutableItem() const
-{
- return conditionsRecipe(m_conditions);
-}
-
-ThenItem::ThenItem(const If &ifItem, const Then &thenItem)
- : m_conditions{{ifItem.m_condition, thenItem.m_body}}
-{}
-
-ThenItem::ThenItem(const ElseIfItem &elseIfItem, const Then &thenItem)
- : m_conditions(elseIfItem.m_conditions)
-{
- m_conditions.append({elseIfItem.m_nextCondition, thenItem.m_body});
-}
-
-ElseItem::operator ExecutableItem() const
-{
- return conditionsRecipe(m_conditions);
-}
-
-ElseItem::ElseItem(const ThenItem &thenItem, const Else &elseItem)
- : m_conditions(thenItem.m_conditions)
-{
- m_conditions.append({{}, elseItem.m_body});
-}
-
-ElseIfItem::ElseIfItem(const ThenItem &thenItem, const ElseIf &elseIfItem)
- : m_conditions(thenItem.m_conditions)
- , m_nextCondition(elseIfItem.m_condition)
-{}
-
-ThenItem operator>>(const If &ifItem, const Then &thenItem)
-{
- return {ifItem, thenItem};
-}
-
-ThenItem operator>>(const ElseIfItem &elseIfItem, const Then &thenItem)
-{
- return {elseIfItem, thenItem};
-}
-
-ElseIfItem operator>>(const ThenItem &thenItem, const ElseIf &elseIfItem)
-{
- return {thenItem, elseIfItem};
-}
-
-ElseItem operator>>(const ThenItem &thenItem, const Else &elseItem)
-{
- return {thenItem, elseItem};
-}
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
diff --git a/src/assets/downloader/tasking/conditional.h b/src/assets/downloader/tasking/conditional.h
deleted file mode 100644
index 52fdfd64cb7..00000000000
--- a/src/assets/downloader/tasking/conditional.h
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_CONDITIONAL_H
-#define TASKING_CONDITIONAL_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasking_global.h"
-
-#include "tasktree.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-class Then;
-class ThenItem;
-class ElseItem;
-class ElseIfItem;
-
-class TASKING_EXPORT If
-{
-public:
- explicit If(const ExecutableItem &condition) : m_condition(condition) {}
-
- template <typename Handler,
- std::enable_if_t<!std::is_base_of_v<ExecutableItem, std::decay_t<Handler>>, bool> = true>
- explicit If(Handler &&handler) : m_condition(Sync(std::forward<Handler>(handler))) {}
-
-private:
- TASKING_EXPORT friend ThenItem operator>>(const If &ifItem, const Then &thenItem);
-
- friend class ThenItem;
- ExecutableItem m_condition;
-};
-
-class TASKING_EXPORT ElseIf
-{
-public:
- explicit ElseIf(const ExecutableItem &condition) : m_condition(condition) {}
-
- template <typename Handler,
- std::enable_if_t<!std::is_base_of_v<ExecutableItem, std::decay_t<Handler>>, bool> = true>
- explicit ElseIf(Handler &&handler) : m_condition(Sync(std::forward<Handler>(handler))) {}
-
-private:
- friend class ElseIfItem;
- ExecutableItem m_condition;
-};
-
-class TASKING_EXPORT Else
-{
-public:
- explicit Else(const GroupItems &children) : m_body({children}) {}
- explicit Else(std::initializer_list<GroupItem> children) : m_body({children}) {}
-
-private:
- friend class ElseItem;
- Group m_body;
-};
-
-class TASKING_EXPORT Then
-{
-public:
- explicit Then(const GroupItems &children) : m_body({children}) {}
- explicit Then(std::initializer_list<GroupItem> children) : m_body({children}) {}
-
-private:
- friend class ThenItem;
- Group m_body;
-};
-
-class ConditionData
-{
-public:
- std::optional<ExecutableItem> m_condition;
- Group m_body;
-};
-
-class ElseIfItem;
-
-class TASKING_EXPORT ThenItem
-{
-public:
- operator ExecutableItem() const;
-
-private:
- ThenItem(const If &ifItem, const Then &thenItem);
- ThenItem(const ElseIfItem &elseIfItem, const Then &thenItem);
-
- TASKING_EXPORT friend ElseItem operator>>(const ThenItem &thenItem, const Else &elseItem);
- TASKING_EXPORT friend ElseIfItem operator>>(const ThenItem &thenItem, const ElseIf &elseIfItem);
- TASKING_EXPORT friend ThenItem operator>>(const If &ifItem, const Then &thenItem);
- TASKING_EXPORT friend ThenItem operator>>(const ElseIfItem &elseIfItem, const Then &thenItem);
-
- friend class ElseItem;
- friend class ElseIfItem;
- QList<ConditionData> m_conditions;
-};
-
-class TASKING_EXPORT ElseItem
-{
-public:
- operator ExecutableItem() const;
-
-private:
- ElseItem(const ThenItem &thenItem, const Else &elseItem);
-
- TASKING_EXPORT friend ElseItem operator>>(const ThenItem &thenItem, const Else &elseItem);
-
- QList<ConditionData> m_conditions;
-};
-
-class TASKING_EXPORT ElseIfItem
-{
-private:
- ElseIfItem(const ThenItem &thenItem, const ElseIf &elseIfItem);
-
- TASKING_EXPORT friend ThenItem operator>>(const ElseIfItem &elseIfItem, const Then &thenItem);
- TASKING_EXPORT friend ElseIfItem operator>>(const ThenItem &thenItem, const ElseIf &elseIfItem);
-
- friend class ThenItem;
- QList<ConditionData> m_conditions;
- ExecutableItem m_nextCondition;
-};
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
-
-#endif // TASKING_CONDITIONAL_H
diff --git a/src/assets/downloader/tasking/networkquery.cpp b/src/assets/downloader/tasking/networkquery.cpp
deleted file mode 100644
index 3f15fed90e0..00000000000
--- a/src/assets/downloader/tasking/networkquery.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#include "networkquery.h"
-
-#include <QtNetwork/QNetworkAccessManager>
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-void NetworkQuery::start()
-{
- if (m_reply) {
- qWarning("The NetworkQuery is already running. Ignoring the call to start().");
- return;
- }
- if (!m_manager) {
- qWarning("Can't start the NetworkQuery without the QNetworkAccessManager. "
- "Stopping with an error.");
- emit done(DoneResult::Error);
- return;
- }
- switch (m_operation) {
- case NetworkOperation::Get:
- m_reply.reset(m_manager->get(m_request));
- break;
- case NetworkOperation::Put:
- m_reply.reset(m_manager->put(m_request, m_writeData));
- break;
- case NetworkOperation::Post:
- m_reply.reset(m_manager->post(m_request, m_writeData));
- break;
- case NetworkOperation::Delete:
- m_reply.reset(m_manager->deleteResource(m_request));
- break;
- }
-
- connect(m_reply.get(), &QNetworkReply::downloadProgress, this, &NetworkQuery::downloadProgress);
-#if QT_CONFIG(ssl)
- connect(m_reply.get(), &QNetworkReply::sslErrors, this, &NetworkQuery::sslErrors);
-#endif
- connect(m_reply.get(), &QNetworkReply::finished, this, [this] {
- disconnect(m_reply.get(), &QNetworkReply::finished, this, nullptr);
- emit done(toDoneResult(m_reply->error() == QNetworkReply::NoError));
- m_reply.release()->deleteLater();
- });
- if (m_reply->isRunning())
- emit started();
-}
-
-NetworkQuery::~NetworkQuery()
-{
- if (m_reply) {
- disconnect(m_reply.get(), nullptr, this, nullptr);
- m_reply->abort();
- }
-}
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
diff --git a/src/assets/downloader/tasking/networkquery.h b/src/assets/downloader/tasking/networkquery.h
deleted file mode 100644
index 2733fef8352..00000000000
--- a/src/assets/downloader/tasking/networkquery.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_NETWORKQUERY_H
-#define TASKING_NETWORKQUERY_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasking_global.h"
-
-#include "tasktree.h"
-
-#include <QtNetwork/QNetworkReply>
-#include <QtNetwork/QNetworkRequest>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-class QNetworkAccessManager;
-
-namespace Tasking {
-
-// This class introduces the dependency to Qt::Network, otherwise Tasking namespace
-// is independent on Qt::Network.
-// Possibly, it could be placed inside Qt::Network library, as a wrapper around QNetworkReply.
-
-enum class NetworkOperation { Get, Put, Post, Delete };
-
-class TASKING_EXPORT NetworkQuery final : public QObject
-{
- Q_OBJECT
-
-public:
- ~NetworkQuery();
- void setRequest(const QNetworkRequest &request) { m_request = request; }
- void setOperation(NetworkOperation operation) { m_operation = operation; }
- void setWriteData(const QByteArray &data) { m_writeData = data; }
- void setNetworkAccessManager(QNetworkAccessManager *manager) { m_manager = manager; }
- QNetworkReply *reply() const { return m_reply.get(); }
- void start();
-
-Q_SIGNALS:
- void started();
- void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
-#if QT_CONFIG(ssl)
- void sslErrors(const QList<QSslError> &errors);
-#endif
- void done(DoneResult result);
-
-private:
- QNetworkRequest m_request;
- NetworkOperation m_operation = NetworkOperation::Get;
- QByteArray m_writeData; // Used by Put and Post
- QNetworkAccessManager *m_manager = nullptr;
- std::unique_ptr<QNetworkReply> m_reply;
-};
-
-using NetworkQueryTask = SimpleCustomTask<NetworkQuery>;
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
-
-#endif // TASKING_NETWORKQUERY_H
diff --git a/src/assets/downloader/tasking/qprocesstask.cpp b/src/assets/downloader/tasking/qprocesstask.cpp
deleted file mode 100644
index a4696ebbbfb..00000000000
--- a/src/assets/downloader/tasking/qprocesstask.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#include "qprocesstask.h"
-
-#include <QtCore/QCoreApplication>
-#include <QtCore/QDebug>
-#include <QtCore/QElapsedTimer>
-#include <QtCore/QMutex>
-#include <QtCore/QThread>
-#include <QtCore/QTimer>
-#include <QtCore/QWaitCondition>
-
-QT_BEGIN_NAMESPACE
-
-#if QT_CONFIG(process)
-
-namespace Tasking {
-
-class ProcessReaperPrivate;
-
-class ProcessReaper final
-{
-public:
- static void reap(QProcess *process, int timeoutMs = 500);
- ProcessReaper();
- ~ProcessReaper();
-
-private:
- static ProcessReaper *instance();
-
- QThread m_thread;
- ProcessReaperPrivate *m_private;
-};
-
-static const int s_timeoutThreshold = 10000; // 10 seconds
-
-static QString execWithArguments(QProcess *process)
-{
- QStringList commandLine;
- commandLine.append(process->program());
- commandLine.append(process->arguments());
- return commandLine.join(QChar::Space);
-}
-
-struct ReaperSetup
-{
- QProcess *m_process = nullptr;
- int m_timeoutMs;
-};
-
-class Reaper : public QObject
-{
- Q_OBJECT
-
-public:
- Reaper(const ReaperSetup &reaperSetup) : m_reaperSetup(reaperSetup) {}
-
- void reap()
- {
- m_timer.start();
- connect(m_reaperSetup.m_process, &QProcess::finished, this, &Reaper::handleFinished);
- if (emitFinished())
- return;
- terminate();
- }
-
-Q_SIGNALS:
- void finished();
-
-private:
- void terminate()
- {
- m_reaperSetup.m_process->terminate();
- QTimer::singleShot(m_reaperSetup.m_timeoutMs, this, &Reaper::handleTerminateTimeout);
- }
-
- void kill() { m_reaperSetup.m_process->kill(); }
-
- bool emitFinished()
- {
- if (m_reaperSetup.m_process->state() != QProcess::NotRunning)
- return false;
-
- if (!m_finished) {
- const int timeout = m_timer.elapsed();
- if (timeout > s_timeoutThreshold) {
- qWarning() << "Finished parallel reaping of" << execWithArguments(m_reaperSetup.m_process)
- << "in" << (timeout / 1000.0) << "seconds.";
- }
-
- m_finished = true;
- emit finished();
- }
- return true;
- }
-
- void handleFinished()
- {
- if (emitFinished())
- return;
- qWarning() << "Finished process still running...";
- // In case the process is still running - wait until it has finished
- QTimer::singleShot(m_reaperSetup.m_timeoutMs, this, &Reaper::handleFinished);
- }
-
- void handleTerminateTimeout()
- {
- if (emitFinished())
- return;
- kill();
- }
-
- bool m_finished = false;
- QElapsedTimer m_timer;
- const ReaperSetup m_reaperSetup;
-};
-
-class ProcessReaperPrivate : public QObject
-{
- Q_OBJECT
-
-public:
- // Called from non-reaper's thread
- void scheduleReap(const ReaperSetup &reaperSetup)
- {
- if (QThread::currentThread() == thread())
- qWarning() << "Can't schedule reap from the reaper internal thread.";
-
- QMutexLocker locker(&m_mutex);
- m_reaperSetupList.append(reaperSetup);
- QMetaObject::invokeMethod(this, &ProcessReaperPrivate::flush);
- }
-
- // Called from non-reaper's thread
- void waitForFinished()
- {
- if (QThread::currentThread() == thread())
- qWarning() << "Can't wait for finished from the reaper internal thread.";
-
- QMetaObject::invokeMethod(this, &ProcessReaperPrivate::flush,
- Qt::BlockingQueuedConnection);
- QMutexLocker locker(&m_mutex);
- if (m_reaperList.isEmpty())
- return;
-
- m_waitCondition.wait(&m_mutex);
- }
-
-private:
- // All the private methods are called from the reaper's thread
- QList<ReaperSetup> takeReaperSetupList()
- {
- QMutexLocker locker(&m_mutex);
- return std::exchange(m_reaperSetupList, {});
- }
-
- void flush()
- {
- while (true) {
- const QList<ReaperSetup> reaperSetupList = takeReaperSetupList();
- if (reaperSetupList.isEmpty())
- return;
- for (const ReaperSetup &reaperSetup : reaperSetupList)
- reap(reaperSetup);
- }
- }
-
- void reap(const ReaperSetup &reaperSetup)
- {
- Reaper *reaper = new Reaper(reaperSetup);
- connect(reaper, &Reaper::finished, this, [this, reaper, process = reaperSetup.m_process] {
- QMutexLocker locker(&m_mutex);
- const bool isRemoved = m_reaperList.removeOne(reaper);
- if (!isRemoved)
- qWarning() << "Reaper list doesn't contain the finished process.";
-
- delete reaper;
- delete process;
- if (m_reaperList.isEmpty())
- m_waitCondition.wakeOne();
- }, Qt::QueuedConnection);
-
- {
- QMutexLocker locker(&m_mutex);
- m_reaperList.append(reaper);
- }
-
- reaper->reap();
- }
-
- QMutex m_mutex;
- QWaitCondition m_waitCondition;
- QList<ReaperSetup> m_reaperSetupList;
- QList<Reaper *> m_reaperList;
-};
-
-static ProcessReaper *s_instance = nullptr;
-static QMutex s_instanceMutex;
-
-// Call me with s_instanceMutex locked.
-ProcessReaper *ProcessReaper::instance()
-{
- if (!s_instance)
- s_instance = new ProcessReaper;
- return s_instance;
-}
-
-ProcessReaper::ProcessReaper()
- : m_private(new ProcessReaperPrivate)
-{
- m_private->moveToThread(&m_thread);
- QObject::connect(&m_thread, &QThread::finished, m_private, &QObject::deleteLater);
- m_thread.start();
- m_thread.moveToThread(qApp->thread());
-}
-
-ProcessReaper::~ProcessReaper()
-{
- if (!QThread::isMainThread())
- qWarning() << "Destructing process reaper from non-main thread.";
-
- instance()->m_private->waitForFinished();
- m_thread.quit();
- m_thread.wait();
-}
-
-void ProcessReaper::reap(QProcess *process, int timeoutMs)
-{
- if (!process)
- return;
-
- if (QThread::currentThread() != process->thread()) {
- qWarning() << "Can't reap process from non-process's thread.";
- return;
- }
-
- process->disconnect();
- if (process->state() == QProcess::NotRunning) {
- delete process;
- return;
- }
-
- // Neither can move object with a parent into a different thread
- // nor reaping the process with a parent makes any sense.
- process->setParent(nullptr);
-
- QMutexLocker locker(&s_instanceMutex);
- ProcessReaperPrivate *priv = instance()->m_private;
-
- process->moveToThread(priv->thread());
- ReaperSetup reaperSetup {process, timeoutMs};
- priv->scheduleReap(reaperSetup);
-}
-
-void QProcessDeleter::deleteAll()
-{
- QMutexLocker locker(&s_instanceMutex);
- delete s_instance;
- s_instance = nullptr;
-}
-
-void QProcessDeleter::operator()(QProcess *process)
-{
- ProcessReaper::reap(process);
-}
-
-} // namespace Tasking
-
-#endif // QT_CONFIG(process)
-
-QT_END_NAMESPACE
-
-#if QT_CONFIG(process)
-
-#include "qprocesstask.moc"
-
-#endif // QT_CONFIG(process)
-
diff --git a/src/assets/downloader/tasking/qprocesstask.h b/src/assets/downloader/tasking/qprocesstask.h
deleted file mode 100644
index 6f2ca4a18e2..00000000000
--- a/src/assets/downloader/tasking/qprocesstask.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_QPROCESSTASK_H
-#define TASKING_QPROCESSTASK_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasking_global.h"
-
-#include "tasktree.h"
-
-#include <QtCore/QProcess>
-
-QT_BEGIN_NAMESPACE
-
-#if QT_CONFIG(process)
-
-namespace Tasking {
-
-// Deleting a running QProcess may block the caller thread up to 30 seconds and issue warnings.
-// To avoid these issues we move the running QProcess into a separate thread
-// managed by the internal ProcessReaper, instead of deleting it immediately.
-// Inside the ProcessReaper's thread we try to finish the process in a most gentle way:
-// we call QProcess::terminate() with 500 ms timeout, and if the process is still running
-// after this timeout passed, we call QProcess::kill() and wait for the process to finish.
-// All these handlings are done is a separate thread, so the main thread doesn't block at all
-// when the QProcessTask is destructed.
-// Finally, on application quit, QProcessDeleter::deleteAll() should be called in order
-// to synchronize all the processes being still potentially reaped in a separate thread.
-// The call to QProcessDeleter::deleteAll() is blocking in case some processes
-// are still being reaped.
-// This strategy seems most sensible, since when passing the running QProcess into the
-// ProcessReaper we don't block immediately, but postpone the possible (not certain) block
-// until the end of an application.
-// In this way we terminate the running processes in the most safe way and keep the main thread
-// responsive. That's a common case when the running application wants to terminate the QProcess
-// immediately (e.g. on Cancel button pressed), without keeping and managing the handle
-// to the still running QProcess.
-
-// The implementation of the internal reaper is inspired by the Utils::ProcessReaper taken
-// from the QtCreator codebase.
-
-class TASKING_EXPORT QProcessDeleter
-{
-public:
- // Blocking, should be called after all QProcessAdapter instances are deleted.
- static void deleteAll();
- void operator()(QProcess *process);
-};
-
-class TASKING_EXPORT QProcessAdapter : public TaskAdapter<QProcess, QProcessDeleter>
-{
-private:
- void start() final {
- connect(task(), &QProcess::finished, this, [this] {
- const bool success = task()->exitStatus() == QProcess::NormalExit
- && task()->error() == QProcess::UnknownError
- && task()->exitCode() == 0;
- Q_EMIT done(toDoneResult(success));
- });
- connect(task(), &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
- if (error != QProcess::FailedToStart)
- return;
- Q_EMIT done(DoneResult::Error);
- });
- task()->start();
- }
-};
-
-using QProcessTask = CustomTask<QProcessAdapter>;
-
-} // namespace Tasking
-
-#endif // QT_CONFIG(process)
-
-QT_END_NAMESPACE
-
-#endif // TASKING_QPROCESSTASK_H
diff --git a/src/assets/downloader/tasking/tasking_global.h b/src/assets/downloader/tasking/tasking_global.h
deleted file mode 100644
index 57f0b7fefef..00000000000
--- a/src/assets/downloader/tasking/tasking_global.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2024 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
-
-#ifndef TASKING_GLOBAL_H
-#define TASKING_GLOBAL_H
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-// #if defined(QT_SHARED) || !defined(QT_STATIC)
-// # if defined(TASKING_LIBRARY)
-// # define TASKING_EXPORT Q_DECL_EXPORT
-// # else
-// # define TASKING_EXPORT Q_DECL_IMPORT
-// # endif
-// #else
-// # define TASKING_EXPORT
-// #endif
-
-#define TASKING_EXPORT
-
-QT_END_NAMESPACE
-
-#endif // TASKING_GLOBAL_H
diff --git a/src/assets/downloader/tasking/tasktree.cpp b/src/assets/downloader/tasking/tasktree.cpp
deleted file mode 100644
index 37064a3e714..00000000000
--- a/src/assets/downloader/tasking/tasktree.cpp
+++ /dev/null
@@ -1,3701 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#include "tasktree.h"
-
-#include "barrier.h"
-#include "conditional.h"
-
-#include <QtCore/QDebug>
-#include <QtCore/QEventLoop>
-#include <QtCore/QFutureWatcher>
-#include <QtCore/QHash>
-#include <QtCore/QMetaEnum>
-#include <QtCore/QMutex>
-#include <QtCore/QPointer>
-#include <QtCore/QPromise>
-#include <QtCore/QSet>
-#include <QtCore/QTime>
-#include <QtCore/QTimer>
-
-using namespace Qt::StringLiterals;
-using namespace std::chrono;
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-// That's cut down qtcassert.{c,h} to avoid the dependency.
-#define QT_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QT_STRINGIFY(__LINE__))
-#define QT_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QT_STRING(#cond); action; } do {} while (0)
-#define QT_CHECK(cond) if (cond) {} else { QT_STRING(#cond); } do {} while (0)
-
-class Guard
-{
- Q_DISABLE_COPY(Guard)
-public:
- Guard() = default;
- ~Guard() { QT_CHECK(m_lockCount == 0); }
- bool isLocked() const { return m_lockCount; }
-private:
- int m_lockCount = 0;
- friend class GuardLocker;
-};
-
-class GuardLocker
-{
- Q_DISABLE_COPY(GuardLocker)
-public:
- GuardLocker(Guard &guard) : m_guard(guard) { ++m_guard.m_lockCount; }
- ~GuardLocker() { --m_guard.m_lockCount; }
-private:
- Guard &m_guard;
-};
-
-/*!
- \module TaskingSolution
- \title Tasking Solution
- \ingroup solutions-modules
- \brief Contains a general purpose Tasking solution.
-
- The Tasking solution depends on Qt only, and doesn't depend on any \QC specific code.
-*/
-
-/*!
- \namespace Tasking
- \inmodule TaskingSolution
- \brief The Tasking namespace encloses all classes and global functions of the Tasking solution.
-*/
-
-/*!
- \class Tasking::TaskInterface
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief TaskInterface is the abstract base class for implementing custom task adapters.
- \reentrant
-
- To implement a custom task adapter, derive your adapter from the
- \c TaskAdapter<Task> class template. TaskAdapter automatically creates and destroys
- the custom task instance and associates the adapter with a given \c Task type.
-*/
-
-/*!
- \fn virtual void TaskInterface::start()
-
- This method is called by the running TaskTree for starting the \c Task instance.
- Reimplement this method in \c TaskAdapter<Task>'s subclass in order to start the
- associated task.
-
- Use TaskAdapter::task() to access the associated \c Task instance.
-
- \sa done(), TaskAdapter::task()
-*/
-
-/*!
- \fn void TaskInterface::done(DoneResult result)
-
- Emit this signal from the \c TaskAdapter<Task>'s subclass, when the \c Task is finished.
- Pass DoneResult::Success as a \a result argument when the task finishes with success;
- otherwise, when an error occurs, pass DoneResult::Error.
-*/
-
-/*!
- \class Tasking::TaskAdapter
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief A class template for implementing custom task adapters.
- \reentrant
-
- The TaskAdapter class template is responsible for creating a task of the \c Task type,
- starting it, and reporting success or an error when the task is finished.
- It also associates the adapter with a given \c Task type.
-
- Reimplement this class with the actual \c Task type to adapt the task's interface
- into the general TaskTree's interface for managing the \c Task instances.
-
- Each subclass needs to provide a public default constructor,
- implement the start() method, and emit the done() signal when the task is finished.
- Use task() to access the associated \c Task instance.
-
- To use your task adapter inside the task tree, create an alias to the
- Tasking::CustomTask template passing your task adapter as a template parameter:
- \code
- // Defines actual worker
- class Worker {...};
-
- // Adapts Worker's interface to work with task tree
- class WorkerTaskAdapter : public TaskAdapter<Worker> {...};
-
- // Defines WorkerTask as a new custom task type to be placed inside Group items
- using WorkerTask = CustomTask<WorkerTaskAdapter>;
- \endcode
-
- Optionally, you may pass a custom \c Deleter for the associated \c Task
- as a second template parameter of your \c TaskAdapter subclass.
- When the \c Deleter parameter is omitted, the \c std::default_delete<Task> is used by default.
- The custom \c Deleter is useful when the destructor of the running \c Task
- may potentially block the caller thread. Instead of blocking, the custom deleter may move
- the running task into a separate thread and implement the blocking destruction there.
- In this way, the fast destruction (seen from the caller thread) of the running task
- with a blocking destructor may be achieved.
-
- For more information on implementing the custom task adapters, refer to \l {Task Adapters}.
-
- \sa start(), done(), task()
-*/
-
-/*!
- \fn template <typename Task, typename Deleter = std::default_delete<Task>> TaskAdapter<Task, Deleter>::TaskAdapter<Task, Deleter>()
-
- Creates a task adapter for the given \c Task type.
-
- Internally, it creates an instance of \c Task, which is accessible via the task() method.
- The optionally provided \c Deleter is used instead of the \c Task destructor.
- When \c Deleter is omitted, the \c std::default_delete<Task> is used by default.
-
- \sa task()
-*/
-
-/*!
- \fn template <typename Task, typename Deleter = std::default_delete<Task>> Task *TaskAdapter<Task, Deleter>::task()
-
- Returns the pointer to the associated \c Task instance.
-*/
-
-/*!
- \fn template <typename Task, typename Deleter = std::default_delete<Task>> Task *TaskAdapter<Task, Deleter>::task() const
- \overload
-
- Returns the \c const pointer to the associated \c Task instance.
-*/
-
-/*!
- \class Tasking::Storage
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief A class template for custom data exchange in the running task tree.
- \reentrant
-
- The Storage class template is responsible for dynamically creating and destructing objects
- of the custom \c StorageStruct type. The creation and destruction are managed by the
- running task tree. If a Storage object is placed inside a \l {Tasking::Group} {Group} element,
- the running task tree creates the \c StorageStruct object when the group is started and before
- the group's setup handler is called. Later, whenever any handler inside this group is called,
- the task tree activates the previously created instance of the \c StorageStruct object.
- This includes all tasks' and groups' setup and done handlers inside the group where the
- Storage object was placed, also within the nested groups.
- When a copy of the Storage object is passed to the handler via the lambda capture,
- the handler may access the instance activated by the running task tree via the
- \l {Tasking::Storage::operator->()} {operator->()},
- \l {Tasking::Storage::operator*()} {operator*()}, or activeStorage() method.
- If two handlers capture the same Storage object, one of them may store a custom data there,
- and the other may read it afterwards.
- When the group is finished, the previously created instance of the \c StorageStruct
- object is destroyed after the group's done handler is called.
-
- An example of data exchange between tasks:
-
- \code
- const Storage<QString> storage;
-
- const auto onFirstDone = [storage](const Task &task) {
- // Assings QString, taken from the first task result, to the active QString instance
- // of the Storage object.
- *storage = task.getResultAsString();
- };
-
- const auto onSecondSetup = [storage](Task &task) {
- // Reads QString from the active QString instance of the Storage object and use it to
- // configure the second task before start.
- task.configureWithString(*storage);
- };
-
- const Group root {
- // The running task tree creates QString instance when root in entered
- storage,
- // The done handler of the first task stores the QString in the storage
- TaskItem(..., onFirstDone),
- // The setup handler of the second task reads the QString from the storage
- TaskItem(onSecondSetup, ...)
- };
- \endcode
-
- Since the root group executes its tasks sequentially, the \c onFirstDone handler
- is always called before the \c onSecondSetup handler. This means that the QString data,
- read from the \c storage inside the \c onSecondSetup handler's body,
- has already been set by the \c onFirstDone handler.
- You can always rely on it in \l {Tasking::sequential} {sequential} execution mode.
-
- The Storage internals are shared between all of its copies. That is why the copies of the
- Storage object inside the handlers' lambda captures still refer to the same Storage instance.
- You may place multiple Storage objects inside one \l {Tasking::Group} {Group} element,
- provided that they do not include copies of the same Storage object.
- Otherwise, an assert is triggered at runtime that includes an error message.
- However, you can place copies of the same Storage object in different
- \l {Tasking::Group} {Group} elements of the same recipe. In this case, the running task
- tree will create multiple instances of the \c StorageStruct objects (one for each copy)
- and storage shadowing will take place. Storage shadowing works in a similar way
- to C++ variable shadowing inside the nested blocks of code:
-
- \code
- Storage<QString> storage;
-
- const Group root {
- storage, // Top copy, 1st instance of StorageStruct
- onGroupSetup([storage] { ... }), // Top copy is active
- Group {
- storage, // Nested copy, 2nd instance of StorageStruct,
- // shadows Top copy
- onGroupSetup([storage] { ... }), // Nested copy is active
- },
- Group {
- onGroupSetup([storage] { ... }), // Top copy is active
- }
- };
- \endcode
-
- The Storage objects may also be used for passing the initial data to the executed task tree,
- and for reading the final data out of the task tree before it finishes.
- To do this, use \l {TaskTree::onStorageSetup()} {onStorageSetup()} or
- \l {TaskTree::onStorageDone()} {onStorageDone()}, respectively.
-
- \note If you use an unreachable Storage object inside the handler,
- because you forgot to place the storage in the recipe,
- or placed it, but not in any handler's ancestor group,
- you may expect a crash, preceded by the following message:
- \e {The referenced storage is not reachable in the running tree.
- A nullptr will be returned which might lead to a crash in the calling code.
- It is possible that no storage was added to the tree,
- or the storage is not reachable from where it is referenced.}
-*/
-
-/*!
- \fn template <typename StorageStruct> Storage<StorageStruct>::Storage<StorageStruct>()
-
- Creates a storage for the given \c StorageStruct type.
-
- \note All copies of \c this object are considered to be the same Storage instance.
-*/
-
-/*!
- \fn template <typename StorageStruct> StorageStruct &Storage<StorageStruct>::operator*() const noexcept
-
- Returns a \e reference to the active \c StorageStruct object, created by the running task tree.
- Use this function only from inside the handler body of any GroupItem element placed
- in the recipe, otherwise you may expect a crash.
- Make sure that Storage is placed in any group ancestor of the handler's group item.
-
- \note The returned reference is valid as long as the group that created this instance
- is still running.
-
- \sa activeStorage(), operator->()
-*/
-
-/*!
- \fn template <typename StorageStruct> StorageStruct *Storage<StorageStruct>::operator->() const noexcept
-
- Returns a \e pointer to the active \c StorageStruct object, created by the running task tree.
- Use this function only from inside the handler body of any GroupItem element placed
- in the recipe, otherwise you may expect a crash.
- Make sure that Storage is placed in any group ancestor of the handler's group item.
-
- \note The returned pointer is valid as long as the group that created this instance
- is still running.
-
- \sa activeStorage(), operator*()
-*/
-
-/*!
- \fn template <typename StorageStruct> StorageStruct *Storage<StorageStruct>::activeStorage() const
-
- Returns a \e pointer to the active \c StorageStruct object, created by the running task tree.
- Use this function only from inside the handler body of any GroupItem element placed
- in the recipe, otherwise you may expect a crash.
- Make sure that Storage is placed in any group ancestor of the handler's group item.
-
- \note The returned pointer is valid as long as the group that created this instance
- is still running.
-
- \sa operator->(), operator*()
-*/
-
-/*!
- \typealias Tasking::GroupItems
-
- Type alias for QList<GroupItem>.
-*/
-
-/*!
- \class Tasking::GroupItem
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief GroupItem represents the basic element that may be a part of any Group.
- \reentrant
-
- GroupItem is a basic element that may be a part of any \l {Tasking::Group} {Group}.
- It encapsulates the functionality provided by any GroupItem's subclass.
- It is a value type and it is safe to copy the GroupItem instance,
- even when it is originally created via the subclass' constructor.
-
- There are four main kinds of GroupItem:
- \table
- \header
- \li GroupItem Kind
- \li Brief Description
- \row
- \li \l CustomTask
- \li Defines asynchronous task type and task's start, done, and error handlers.
- Aliased with a unique task name, such as, \c ConcurrentCallTask<ResultType>
- or \c NetworkQueryTask. Asynchronous tasks are the main reason for using a task tree.
- \row
- \li \l {Tasking::Group} {Group}
- \li A container for other group items. Since the group is of the GroupItem type,
- it's possible to nest it inside another group. The group is seen by its parent
- as a single asynchronous task.
- \row
- \li GroupItem containing \l {Tasking::Storage} {Storage}
- \li Enables the child tasks of a group to exchange data. When GroupItem containing
- \l {Tasking::Storage} {Storage} is placed inside a group, the task tree instantiates
- the storage's data object just before the group is entered,
- and destroys it just after the group is left.
- \row
- \li Other group control items
- \li The items returned by \l {Tasking::parallelLimit()} {parallelLimit()} or
- \l {Tasking::workflowPolicy()} {workflowPolicy()} influence the group's behavior.
- The items returned by \l {Tasking::onGroupSetup()} {onGroupSetup()} or
- \l {Tasking::onGroupDone()} {onGroupDone()} define custom handlers called when
- the group starts or ends execution.
- \endtable
-*/
-
-/*!
- \fn template <typename StorageStruct> GroupItem::GroupItem(const Storage<StorageStruct> &storage)
-
- Constructs a \c GroupItem element holding the \a storage object.
-
- When the \l {Tasking::Group} {Group} element containing \e this GroupItem is entered
- by the running task tree, an instance of the \c StorageStruct is created dynamically.
-
- When that group is about to be left after its execution, the previously instantiated
- \c StorageStruct is deleted.
-
- The dynamically created instance of \c StorageStruct is accessible from inside any
- handler body of the parent \l {Tasking::Group} {Group} element,
- including nested groups and its tasks, via the
- \l {Tasking::Storage::operator->()} {Storage::operator->()},
- \l {Tasking::Storage::operator*()} {Storage::operator*()}, or Storage::activeStorage() method.
-
- \sa {Tasking::Storage} {Storage}
-*/
-
-/*!
- \fn GroupItem::GroupItem(const GroupItems &items)
-
- Constructs a \c GroupItem element with a given list of \a items.
-
- When this \c GroupItem element is parsed by the TaskTree, it is simply replaced with
- its \a items.
-
- This constructor is useful when constructing a \l {Tasking::Group} {Group} element with
- lists of \c GroupItem elements:
-
- \code
- static QList<GroupItems> getItems();
-
- ...
-
- const Group root {
- parallel,
- finishAllAndSuccess,
- getItems(), // OK, getItems() list is wrapped into a single GroupItem element
- onGroupSetup(...),
- onGroupDone(...)
- };
- \endcode
-
- If you want to create a subtree, use \l {Tasking::Group} {Group} instead.
-
- \note Don't confuse this \c GroupItem with the \l {Tasking::Group} {Group} element, as
- \l {Tasking::Group} {Group} keeps its children nested
- after being parsed by the task tree, while this \c GroupItem does not.
-
- \sa {Tasking::Group} {Group}
-*/
-
-/*!
- \fn Tasking::GroupItem(std::initializer_list<GroupItem> items)
- \overload
- \sa GroupItem(const GroupItems &items)
-*/
-
-/*!
- \class Tasking::Group
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief Group represents the basic element for composing declarative recipes describing
- how to execute and handle a nested tree of asynchronous tasks.
- \reentrant
-
- Group is a container for other group items. It encloses child tasks into one unit,
- which is seen by the group's parent as a single, asynchronous task.
- Since Group is of the GroupItem type, it may also be a child of Group.
-
- Insert child tasks into the group by using aliased custom task names, such as,
- \c ConcurrentCallTask<ResultType> or \c NetworkQueryTask:
-
- \code
- const Group group {
- NetworkQueryTask(...),
- ConcurrentCallTask<int>(...)
- };
- \endcode
-
- The group's behavior may be customized by inserting the items returned by
- \l {Tasking::parallelLimit()} {parallelLimit()} or
- \l {Tasking::workflowPolicy()} {workflowPolicy()} functions:
-
- \code
- const Group group {
- parallel,
- continueOnError,
- NetworkQueryTask(...),
- NetworkQueryTask(...)
- };
- \endcode
-
- The group may contain nested groups:
-
- \code
- const Group group {
- finishAllAndSuccess,
- NetworkQueryTask(...),
- Group {
- NetworkQueryTask(...),
- Group {
- parallel,
- NetworkQueryTask(...),
- NetworkQueryTask(...),
- }
- ConcurrentCallTask<QString>(...)
- }
- };
- \endcode
-
- The group may dynamically instantiate a custom storage structure, which may be used for
- inter-task data exchange:
-
- \code
- struct MyCustomStruct { QByteArray data; };
-
- Storage<MyCustomStruct> storage;
-
- const auto onFirstSetup = [](NetworkQuery &task) { ... };
- const auto onFirstDone = [storage](const NetworkQuery &task) {
- // storage-> gives a pointer to MyCustomStruct instance,
- // created dynamically by the running task tree.
- storage->data = task.reply()->readAll();
- };
- const auto onSecondSetup = [storage](ConcurrentCall<QImage> &task) {
- // storage-> gives a pointer to MyCustomStruct. Since the group is sequential,
- // the stored MyCustomStruct was already updated inside the onFirstDone handler.
- const QByteArray storedData = storage->data;
- };
-
- const Group group {
- // When the group is entered by a running task tree, it creates MyCustomStruct
- // instance dynamically. It is later accessible from all handlers via
- // the *storage or storage-> operators.
- sequential,
- storage,
- NetworkQueryTask(onFirstSetup, onFirstDone, CallDoneIf::Success),
- ConcurrentCallTask<QImage>(onSecondSetup)
- };
- \endcode
-*/
-
-/*!
- \fn Group::Group(const GroupItems &children)
-
- Constructs a group with a given list of \a children.
-
- This constructor is useful when the child items of the group are not known at compile time,
- but later, at runtime:
-
- \code
- const QStringList sourceList = ...;
-
- GroupItems groupItems { parallel };
-
- for (const QString &source : sourceList) {
- const NetworkQueryTask task(...); // use source for setup handler
- groupItems << task;
- }
-
- const Group group(groupItems);
- \endcode
-*/
-
-/*!
- \fn Group::Group(std::initializer_list<GroupItem> children)
-
- Constructs a group from \c std::initializer_list given by \a children.
-
- This constructor is useful when all child items of the group are known at compile time:
-
- \code
- const Group group {
- finishAllAndSuccess,
- NetworkQueryTask(...),
- Group {
- NetworkQueryTask(...),
- Group {
- parallel,
- NetworkQueryTask(...),
- NetworkQueryTask(...),
- }
- ConcurrentCallTask<QString>(...)
- }
- };
- \endcode
-*/
-
-/*!
- \class Tasking::Sync
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief Synchronously executes a custom handler between other tasks.
- \reentrant
-
- \c Sync is useful when you want to execute an additional handler between other tasks.
- \c Sync is seen by its parent \l {Tasking::Group} {Group} as any other task.
- Avoid long-running execution of the \c Sync's handler body, since it is executed
- synchronously from the caller thread. If that is unavoidable, consider using
- \c ConcurrentCallTask instead.
-*/
-
-/*!
- \fn template <typename Handler> Sync::Sync(Handler &&handler)
-
- Constructs an element that executes a passed \a handler synchronously.
- The \c Handler is of the \c std::function<DoneResult()> type.
- The DoneResult value, returned by the \a handler, is considered during parent group's
- \l {workflowPolicy} {workflow policy} resolution.
- Optionally, the shortened form of \c std::function<void()> is also accepted.
- In this case, it's assumed that the return value is DoneResult::Success.
-
- The passed \a handler executes synchronously from the caller thread, so avoid a long-running
- execution of the handler body. Otherwise, consider using \c ConcurrentCallTask.
-
- \note The \c Sync element is not counted as a task when reporting task tree progress,
- and is not included in TaskTree::taskCount() or TaskTree::progressMaximum().
-*/
-
-/*!
- \class Tasking::CustomTask
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief A class template used for declaring custom task items and defining their setup
- and done handlers.
- \reentrant
-
- Describes custom task items within task tree recipes.
-
- Custom task names are aliased with unique names using the \c CustomTask template
- with a given TaskAdapter subclass as a template parameter.
- For example, \c ConcurrentCallTask<T> is an alias to the \c CustomTask that is defined
- to work with \c ConcurrentCall<T> as an associated task class.
- The following table contains example custom tasks and their associated task classes:
-
- \table
- \header
- \li Aliased Task Name (Tasking Namespace)
- \li Associated Task Class
- \li Brief Description
- \row
- \li ConcurrentCallTask<ReturnType>
- \li ConcurrentCall<ReturnType>
- \li Starts an asynchronous task. Runs in a separate thread.
- \row
- \li NetworkQueryTask
- \li NetworkQuery
- \li Sends a network query.
- \row
- \li TaskTreeTask
- \li TaskTree
- \li Starts a nested task tree.
- \row
- \li TimeoutTask
- \li \c std::chrono::milliseconds
- \li Starts a timer.
- \row
- \li WaitForBarrierTask
- \li MultiBarrier<Limit>
- \li Starts an asynchronous task waiting for the barrier to pass.
- \endtable
-*/
-
-/*!
- \typealias Tasking::CustomTask::Task
-
- Type alias for the task type associated with the custom task's \c Adapter.
-*/
-
-/*!
- \typealias Tasking::CustomTask::Deleter
-
- Type alias for the task's type deleter associated with the custom task's \c Adapter.
-*/
-
-/*!
- \typealias Tasking::CustomTask::TaskSetupHandler
-
- Type alias for \c std::function<SetupResult(Task &)>.
-
- The \c TaskSetupHandler is an optional argument of a custom task element's constructor.
- Any function with the above signature, when passed as a task setup handler,
- will be called by the running task tree after the task is created and before it is started.
-
- Inside the body of the handler, you may configure the task according to your needs.
- The additional parameters, including storages, may be passed to the handler
- via the lambda capture.
- You can decide dynamically whether the task should be started or skipped with
- success or an error.
-
- \note Do not start the task inside the start handler by yourself. Leave it for TaskTree,
- otherwise the behavior is undefined.
-
- The return value of the handler instructs the running task tree on how to proceed
- after the handler's invocation is finished. The return value of SetupResult::Continue
- instructs the task tree to continue running, that is, to execute the associated \c Task.
- The return value of SetupResult::StopWithSuccess or SetupResult::StopWithError
- instructs the task tree to skip the task's execution and finish it immediately with
- success or an error, respectively.
-
- When the return type is either SetupResult::StopWithSuccess or SetupResult::StopWithError,
- the task's done handler (if provided) isn't called afterwards.
-
- The constructor of a custom task accepts also functions in the shortened form of
- \c std::function<void(Task &)>, that is, the return value is \c void.
- In this case, it's assumed that the return value is SetupResult::Continue.
-
- \sa CustomTask(), TaskDoneHandler, GroupSetupHandler
-*/
-
-/*!
- \typealias Tasking::CustomTask::TaskDoneHandler
-
- Type alias for \c std::function<DoneResult(const Task &, DoneWith)> or DoneResult.
-
- The \c TaskDoneHandler is an optional argument of a custom task element's constructor.
- Any function with the above signature, when passed as a task done handler,
- will be called by the running task tree after the task execution finished and before
- the final result of the execution is reported back to the parent group.
-
- Inside the body of the handler you may retrieve the final data from the finished task.
- The additional parameters, including storages, may be passed to the handler
- via the lambda capture.
- It is also possible to decide dynamically whether the task should finish with its return
- value, or the final result should be tweaked.
-
- The DoneWith argument is optional and your done handler may omit it.
- When provided, it holds the info about the final result of a task that will be
- reported to its parent.
-
- If you do not plan to read any data from the finished task,
- you may omit the \c {const Task &} argument.
-
- The returned DoneResult value is optional and your handler may return \c void instead.
- In this case, the final result of the task will be equal to the value indicated by
- the DoneWith argument. When the handler returns the DoneResult value,
- the task's final result may be tweaked inside the done handler's body by the returned value.
-
- For a \c TaskDoneHandler of the DoneResult type, no additional handling is executed,
- and the task finishes unconditionally with the passed value of DoneResult.
-
- \sa CustomTask(), TaskSetupHandler, GroupDoneHandler
-*/
-
-/*!
- \fn template <typename Adapter> template <typename SetupHandler = TaskSetupHandler, typename DoneHandler = TaskDoneHandler> CustomTask<Adapter>::CustomTask(SetupHandler &&setup = TaskSetupHandler(), DoneHandler &&done = TaskDoneHandler(), CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
-
- Constructs a \c CustomTask instance and attaches the \a setup and \a done handlers to the task.
- When the running task tree is about to start the task,
- it instantiates the associated \l Task object, invokes \a setup handler with a \e reference
- to the created task, and starts it. When the running task finishes,
- the task tree invokes a \a done handler, with a \c const \e reference to the created task.
-
- The passed \a setup handler is of the \l TaskSetupHandler type. For example:
-
- \code
- static void parseAndLog(const QString &input);
-
- ...
-
- const QString input = ...;
-
- const auto onFirstSetup = [input](ConcurrentCall<void> &task) {
- if (input == "Skip")
- return SetupResult::StopWithSuccess; // This task won't start, the next one will
- if (input == "Error")
- return SetupResult::StopWithError; // This task and the next one won't start
- task.setConcurrentCallData(parseAndLog, input);
- // This task will start, and the next one will start after this one finished with success
- return SetupResult::Continue;
- };
-
- const auto onSecondSetup = [input](ConcurrentCall<void> &task) {
- task.setConcurrentCallData(parseAndLog, input);
- };
-
- const Group group {
- ConcurrentCallTask<void>(onFirstSetup),
- ConcurrentCallTask<void>(onSecondSetup)
- };
- \endcode
-
- The \a done handler is of the \l TaskDoneHandler type.
- By default, the \a done handler is invoked whenever the task finishes.
- Pass a non-default value for the \a callDoneIf argument when you want the handler to be called
- only on a successful or failed execution.
-
- \sa TaskSetupHandler, TaskDoneHandler
-*/
-
-/*!
- \enum Tasking::WorkflowPolicy
-
- This enum describes the possible behavior of the Group element when any group's child task
- finishes its execution. It's also used when the running Group is canceled.
-
- \value StopOnError
- Default. Corresponds to the stopOnError global element.
- If any child task finishes with an error, the group stops and finishes with an error.
- If all child tasks finished with success, the group finishes with success.
- If a group is empty, it finishes with success.
- \value ContinueOnError
- Corresponds to the continueOnError global element.
- Similar to stopOnError, but in case any child finishes with an error,
- the execution continues until all tasks finish, and the group reports an error
- afterwards, even when some other tasks in the group finished with success.
- If all child tasks finish successfully, the group finishes with success.
- If a group is empty, it finishes with success.
- \value StopOnSuccess
- Corresponds to the stopOnSuccess global element.
- If any child task finishes with success, the group stops and finishes with success.
- If all child tasks finished with an error, the group finishes with an error.
- If a group is empty, it finishes with an error.
- \value ContinueOnSuccess
- Corresponds to the continueOnSuccess global element.
- Similar to stopOnSuccess, but in case any child finishes successfully,
- the execution continues until all tasks finish, and the group reports success
- afterwards, even when some other tasks in the group finished with an error.
- If all child tasks finish with an error, the group finishes with an error.
- If a group is empty, it finishes with an error.
- \value StopOnSuccessOrError
- Corresponds to the stopOnSuccessOrError global element.
- The group starts as many tasks as it can. When any task finishes,
- the group stops and reports the task's result.
- Useful only in parallel mode.
- In sequential mode, only the first task is started, and when finished,
- the group finishes too, so the other tasks are always skipped.
- If a group is empty, it finishes with an error.
- \value FinishAllAndSuccess
- Corresponds to the finishAllAndSuccess global element.
- The group executes all tasks and ignores their return results. When all
- tasks finished, the group finishes with success.
- If a group is empty, it finishes with success.
- \value FinishAllAndError
- Corresponds to the finishAllAndError global element.
- The group executes all tasks and ignores their return results. When all
- tasks finished, the group finishes with an error.
- If a group is empty, it finishes with an error.
-
- Whenever a child task's result causes the Group to stop, that is,
- in case of StopOnError, StopOnSuccess, or StopOnSuccessOrError policies,
- the Group cancels the other running child tasks (if any - for example in parallel mode),
- and skips executing tasks it has not started yet (for example, in the sequential mode -
- those, that are placed after the failed task). Both canceling and skipping child tasks
- may happen when parallelLimit() is used.
-
- The table below summarizes the differences between various workflow policies:
-
- \table
- \header
- \li \l WorkflowPolicy
- \li Executes all child tasks
- \li Result
- \li Result when the group is empty
- \row
- \li StopOnError
- \li Stops when any child task finished with an error and reports an error
- \li An error when at least one child task failed, success otherwise
- \li Success
- \row
- \li ContinueOnError
- \li Yes
- \li An error when at least one child task failed, success otherwise
- \li Success
- \row
- \li StopOnSuccess
- \li Stops when any child task finished with success and reports success
- \li Success when at least one child task succeeded, an error otherwise
- \li An error
- \row
- \li ContinueOnSuccess
- \li Yes
- \li Success when at least one child task succeeded, an error otherwise
- \li An error
- \row
- \li StopOnSuccessOrError
- \li Stops when any child task finished and reports child task's result
- \li Success or an error, depending on the finished child task's result
- \li An error
- \row
- \li FinishAllAndSuccess
- \li Yes
- \li Success
- \li Success
- \row
- \li FinishAllAndError
- \li Yes
- \li An error
- \li An error
- \endtable
-
- If a child of a group is also a group, the child group runs its tasks according to its own
- workflow policy. When a parent group stops the running child group because
- of parent group's workflow policy, that is, when the StopOnError, StopOnSuccess,
- or StopOnSuccessOrError policy was used for the parent,
- the child group's result is reported according to the
- \b Result column and to the \b {child group's workflow policy} row in the table above.
-*/
-
-/*!
- \variable Tasking::nullItem
-
- A convenient global group's element indicating a no-op item.
-
- This is useful in conditional expressions to indicate the absence of an optional element:
-
- \code
- const ExecutableItem task = ...;
- const std::optional<ExecutableItem> optionalTask = ...;
-
- Group group {
- task,
- optionalTask ? *optionalTask : nullItem
- };
- \endcode
-*/
-
-/*!
- \variable Tasking::successItem
-
- A convenient global executable element containing an empty, successful, synchronous task.
-
- This is useful in if-statements to indicate that a branch ends with success:
-
- \code
- const ExecutableItem conditionalTask = ...;
-
- Group group {
- stopOnDone,
- If (conditionalTask) >> Then {
- ...
- } >> Else {
- successItem
- },
- nextTask
- };
- \endcode
-
- In the above example, if the \c conditionalTask finishes with an error, the \c Else branch
- is chosen, which finishes immediately with success. This causes the \c nextTask to be skipped
- (because of the stopOnDone workflow policy of the \c group)
- and the \c group finishes with success.
-
- \sa errorItem
-*/
-
-/*!
- \variable Tasking::errorItem
-
- A convenient global executable element containing an empty, erroneous, synchronous task.
-
- This is useful in if-statements to indicate that a branch ends with an error:
-
- \code
- const ExecutableItem conditionalTask = ...;
-
- Group group {
- stopOnError,
- If (conditionalTask) >> Then {
- ...
- } >> Else {
- errorItem
- },
- nextTask
- };
- \endcode
-
- In the above example, if the \c conditionalTask finishes with an error, the \c Else branch
- is chosen, which finishes immediately with an error. This causes the \c nextTask to be skipped
- (because of the stopOnError workflow policy of the \c group)
- and the \c group finishes with an error.
-
- \sa successItem
-*/
-
-/*!
- \variable Tasking::sequential
- A convenient global group's element describing the sequential execution mode.
-
- This is the default execution mode of the Group element.
-
- When a Group has no execution mode, it runs in the sequential mode.
- All the direct child tasks of a group are started in a chain, so that when one task finishes,
- the next one starts. This enables you to pass the results from the previous task
- as input to the next task before it starts. This mode guarantees that the next task
- is started only after the previous task finishes.
-
- \sa parallel, parallelLimit()
-*/
-
-/*!
- \variable Tasking::parallel
- A convenient global group's element describing the parallel execution mode.
-
- All the direct child tasks of a group are started after the group is started,
- without waiting for the previous child tasks to finish.
- In this mode, all child tasks run simultaneously.
-
- \sa sequential, parallelLimit()
-*/
-
-/*!
- \variable Tasking::parallelIdealThreadCountLimit
- A convenient global group's element describing the parallel execution mode with a limited
- number of tasks running simultanously. The limit is equal to the ideal number of threads
- excluding the calling thread.
-
- This is a shortcut to:
- \code
- parallelLimit(qMax(QThread::idealThreadCount() - 1, 1))
- \endcode
-
- \sa parallel, parallelLimit()
-*/
-
-/*!
- \variable Tasking::stopOnError
- A convenient global group's element describing the StopOnError workflow policy.
-
- This is the default workflow policy of the Group element.
-*/
-
-/*!
- \variable Tasking::continueOnError
- A convenient global group's element describing the ContinueOnError workflow policy.
-*/
-
-/*!
- \variable Tasking::stopOnSuccess
- A convenient global group's element describing the StopOnSuccess workflow policy.
-*/
-
-/*!
- \variable Tasking::continueOnSuccess
- A convenient global group's element describing the ContinueOnSuccess workflow policy.
-*/
-
-/*!
- \variable Tasking::stopOnSuccessOrError
- A convenient global group's element describing the StopOnSuccessOrError workflow policy.
-*/
-
-/*!
- \variable Tasking::finishAllAndSuccess
- A convenient global group's element describing the FinishAllAndSuccess workflow policy.
-*/
-
-/*!
- \variable Tasking::finishAllAndError
- A convenient global group's element describing the FinishAllAndError workflow policy.
-*/
-
-/*!
- \enum Tasking::SetupResult
-
- This enum is optionally returned from the group's or task's setup handler function.
- It instructs the running task tree on how to proceed after the setup handler's execution
- finished.
- \value Continue
- Default. The group's or task's execution continues normally.
- When a group's or task's setup handler returns void, it's assumed that
- it returned Continue.
- \value StopWithSuccess
- The group's or task's execution stops immediately with success.
- When returned from the group's setup handler, all child tasks are skipped,
- and the group's onGroupDone() handler is invoked with DoneWith::Success.
- The group reports success to its parent. The group's workflow policy is ignored.
- When returned from the task's setup handler, the task isn't started,
- its done handler isn't invoked, and the task reports success to its parent.
- \value StopWithError
- The group's or task's execution stops immediately with an error.
- When returned from the group's setup handler, all child tasks are skipped,
- and the group's onGroupDone() handler is invoked with DoneWith::Error.
- The group reports an error to its parent. The group's workflow policy is ignored.
- When returned from the task's setup handler, the task isn't started,
- its error handler isn't invoked, and the task reports an error to its parent.
-*/
-
-/*!
- \enum Tasking::DoneResult
-
- This enum is optionally returned from the group's or task's done handler function.
- When the done handler doesn't return any value, that is, its return type is \c void,
- its final return value is automatically deduced by the running task tree and reported
- to its parent group.
-
- When the done handler returns the DoneResult, you can tweak the final return value
- inside the handler.
-
- When the DoneResult is returned by the group's done handler, the group's workflow policy
- is ignored.
-
- This enum is also used inside the TaskInterface::done() signal and it indicates whether
- the task finished with success or an error.
-
- \value Success
- The group's or task's execution ends with success.
- \value Error
- The group's or task's execution ends with an error.
-*/
-
-/*!
- \enum Tasking::DoneWith
-
- This enum is an optional argument for the group's or task's done handler.
- It indicates whether the group or task finished with success or an error, or it was canceled.
-
- It is also used as an argument inside the TaskTree::done() signal,
- indicating the final result of the TaskTree execution.
-
- \value Success
- The group's or task's execution ended with success.
- \value Error
- The group's or task's execution ended with an error.
- \value Cancel
- The group's or task's execution was canceled. This happens when the user calls
- TaskTree::cancel() for the running task tree or when the group's workflow policy
- results in canceling some of its running children.
- Tweaking the done handler's final result by returning Tasking::DoneResult from
- the handler is no-op when the group's or task's execution was canceled.
-*/
-
-/*!
- \enum Tasking::CallDoneIf
-
- This enum is an optional argument for the \l onGroupDone() element or custom task's constructor.
- It instructs the task tree on when the group's or task's done handler should be invoked.
-
- \value SuccessOrError
- The done handler is always invoked.
- \value Success
- The done handler is invoked only after successful execution,
- that is, when DoneWith::Success.
- \value Error
- The done handler is invoked only after failed execution,
- that is, when DoneWith::Error or when DoneWith::Cancel.
-*/
-
-/*!
- \typealias Tasking::GroupItem::GroupSetupHandler
-
- Type alias for \c std::function<SetupResult()>.
-
- The \c GroupSetupHandler is an argument of the onGroupSetup() element.
- Any function with the above signature, when passed as a group setup handler,
- will be called by the running task tree when the group execution starts.
-
- The return value of the handler instructs the running group on how to proceed
- after the handler's invocation is finished. The default return value of SetupResult::Continue
- instructs the group to continue running, that is, to start executing its child tasks.
- The return value of SetupResult::StopWithSuccess or SetupResult::StopWithError
- instructs the group to skip the child tasks' execution and finish immediately with
- success or an error, respectively.
-
- When the return type is either SetupResult::StopWithSuccess or SetupResult::StopWithError,
- the group's done handler (if provided) is called synchronously immediately afterwards.
-
- \note Even if the group setup handler returns StopWithSuccess or StopWithError,
- the group's done handler is invoked. This behavior differs from that of task done handler
- and might change in the future.
-
- The onGroupSetup() element accepts also functions in the shortened form of
- \c std::function<void()>, that is, the return value is \c void.
- In this case, it's assumed that the return value is SetupResult::Continue.
-
- \sa onGroupSetup(), GroupDoneHandler, CustomTask::TaskSetupHandler
-*/
-
-/*!
- \typealias Tasking::GroupItem::GroupDoneHandler
-
- Type alias for \c std::function<DoneResult(DoneWith)> or DoneResult.
-
- The \c GroupDoneHandler is an argument of the onGroupDone() element.
- Any function with the above signature, when passed as a group done handler,
- will be called by the running task tree when the group execution ends.
-
- The DoneWith argument is optional and your done handler may omit it.
- When provided, it holds the info about the final result of a group that will be
- reported to its parent.
-
- The returned DoneResult value is optional and your handler may return \c void instead.
- In this case, the final result of the group will be equal to the value indicated by
- the DoneWith argument. When the handler returns the DoneResult value,
- the group's final result may be tweaked inside the done handler's body by the returned value.
-
- For a \c GroupDoneHandler of the DoneResult type, no additional handling is executed,
- and the group finishes unconditionally with the passed value of DoneResult,
- ignoring the group's workflow policy.
-
- \sa onGroupDone(), GroupSetupHandler, CustomTask::TaskDoneHandler
-*/
-
-/*!
- \fn template <typename Handler> GroupItem onGroupSetup(Handler &&handler)
-
- Constructs a group's element holding the group setup handler.
- The \a handler is invoked whenever the group starts.
-
- The passed \a handler is either of the \c std::function<SetupResult()> or the
- \c std::function<void()> type. For more information on a possible handler type, refer to
- \l {GroupItem::GroupSetupHandler}.
-
- When the \a handler is invoked, none of the group's child tasks are running yet.
-
- If a group contains the Storage elements, the \a handler is invoked
- after the storages are constructed, so that the \a handler may already
- perform some initial modifications to the active storages.
-
- \sa GroupItem::GroupSetupHandler, onGroupDone()
-*/
-
-/*!
- \fn template <typename Handler> GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
-
- Constructs a group's element holding the group done handler.
- By default, the \a handler is invoked whenever the group finishes.
- Pass a non-default value for the \a callDoneIf argument when you want the handler to be called
- only on a successful or failed execution.
- Depending on the group's workflow policy, this handler may also be called
- when the running group is canceled (e.g. when stopOnError element was used).
-
- The passed \a handler is of the \c std::function<DoneResult(DoneWith)> type.
- Optionally, each of the return DoneResult type or the argument DoneWith type may be omitted
- (that is, its return type may be \c void). For more information on a possible handler type,
- refer to \l {GroupItem::GroupDoneHandler}.
-
- When the \a handler is invoked, all of the group's child tasks are already finished.
-
- If a group contains the Storage elements, the \a handler is invoked
- before the storages are destructed, so that the \a handler may still
- perform a last read of the active storages' data.
-
- \sa GroupItem::GroupDoneHandler, onGroupSetup()
-*/
-
-/*!
- Constructs a group's element describing the \l{Execution Mode}{execution mode}.
-
- The execution mode element in a Group specifies how the direct child tasks of
- the Group are started.
-
- For convenience, when appropriate, the \l sequential or \l parallel global elements
- may be used instead.
-
- The \a limit defines the maximum number of direct child tasks running in parallel:
-
- \list
- \li When \a limit equals to 0, there is no limit, and all direct child tasks are started
- together, in the oder in which they appear in a group. This means the fully parallel
- execution, and the \l parallel element may be used instead.
-
- \li When \a limit equals to 1, it means that only one child task may run at the time.
- This means the sequential execution, and the \l sequential element may be used instead.
- In this case, child tasks run in chain, so the next child task starts after
- the previous child task has finished.
-
- \li When other positive number is passed as \a limit, the group's child tasks run
- in parallel, but with a limited number of tasks running simultanously.
- The \e limit defines the maximum number of tasks running in parallel in a group.
- When the group is started, the first batch of tasks is started
- (the number of tasks in a batch equals to the passed \a limit, at most),
- while the others are kept waiting. When any running task finishes,
- the group starts the next remaining one, so that the \e limit of simultaneously
- running tasks inside a group isn't exceeded. This repeats on every child task's
- finish until all child tasks are started. This enables you to limit the maximum
- number of tasks that run simultaneously, for example if running too many processes might
- block the machine for a long time.
- \endlist
-
- In all execution modes, a group starts tasks in the oder in which they appear.
-
- If a child of a group is also a group, the child group runs its tasks according
- to its own execution mode.
-
- \sa sequential, parallel
-*/
-
-GroupItem parallelLimit(int limit)
-{
- struct ParallelLimit : GroupItem {
- ParallelLimit(int limit) : GroupItem({{}, limit}) {}
- };
- return ParallelLimit(limit);
-}
-
-/*!
- Constructs a group's \l {Workflow Policy} {workflow policy} element for a given \a policy.
-
- For convenience, global elements may be used instead.
-
- \sa stopOnError, continueOnError, stopOnSuccess, continueOnSuccess, stopOnSuccessOrError,
- finishAllAndSuccess, finishAllAndError, WorkflowPolicy
-*/
-GroupItem workflowPolicy(WorkflowPolicy policy)
-{
- struct WorkflowPolicyItem : GroupItem {
- WorkflowPolicyItem(WorkflowPolicy policy) : GroupItem({{}, {}, policy}) {}
- };
- return WorkflowPolicyItem(policy);
-}
-
-const GroupItem sequential = parallelLimit(1);
-const GroupItem parallel = parallelLimit(0);
-const GroupItem parallelIdealThreadCountLimit = parallelLimit(qMax(QThread::idealThreadCount() - 1, 1));
-
-const GroupItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError);
-const GroupItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError);
-const GroupItem stopOnSuccess = workflowPolicy(WorkflowPolicy::StopOnSuccess);
-const GroupItem continueOnSuccess = workflowPolicy(WorkflowPolicy::ContinueOnSuccess);
-const GroupItem stopOnSuccessOrError = workflowPolicy(WorkflowPolicy::StopOnSuccessOrError);
-const GroupItem finishAllAndSuccess = workflowPolicy(WorkflowPolicy::FinishAllAndSuccess);
-const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError);
-
-// Keep below the above in order to avoid static initialization fiasco.
-const GroupItem nullItem = Group {};
-const ExecutableItem successItem = Group { finishAllAndSuccess };
-const ExecutableItem errorItem = Group { finishAllAndError };
-
-Group operator>>(const For &forItem, const Do &doItem)
-{
- return {forItem.m_loop, doItem.m_children};
-}
-
-Group operator>>(const When &whenItem, const Do &doItem)
-{
- const SingleBarrier barrier;
-
- return {
- barrier,
- parallel,
- whenItem.m_barrierKicker(barrier),
- Group {
- waitForBarrierTask(barrier),
- doItem.m_children
- }
- };
-}
-
-// Please note the thread_local keyword below guarantees a separate instance per thread.
-// The s_activeTaskTrees is currently used internally only and is not exposed in the public API.
-// It serves for withLog() implementation now. Add a note here when a new usage is introduced.
-static thread_local QList<TaskTree *> s_activeTaskTrees = {};
-
-static TaskTree *activeTaskTree()
-{
- QT_ASSERT(s_activeTaskTrees.size(), return nullptr);
- return s_activeTaskTrees.back();
-}
-
-DoneResult toDoneResult(bool success)
-{
- return success ? DoneResult::Success : DoneResult::Error;
-}
-
-static SetupResult toSetupResult(bool success)
-{
- return success ? SetupResult::StopWithSuccess : SetupResult::StopWithError;
-}
-
-static DoneResult toDoneResult(DoneWith doneWith)
-{
- return doneWith == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
-}
-
-static DoneWith toDoneWith(DoneResult result)
-{
- return result == DoneResult::Success ? DoneWith::Success : DoneWith::Error;
-}
-
-class LoopThreadData
-{
- Q_DISABLE_COPY_MOVE(LoopThreadData)
-
-public:
- LoopThreadData() = default;
- void pushIteration(int index)
- {
- m_activeLoopStack.push_back(index);
- }
- void popIteration()
- {
- QT_ASSERT(m_activeLoopStack.size(), return);
- m_activeLoopStack.pop_back();
- }
- int iteration() const
- {
- QT_ASSERT(m_activeLoopStack.size(), qWarning(
- "The referenced loop is not reachable in the running tree. "
- "A -1 will be returned which might lead to a crash in the calling code. "
- "It is possible that no loop was added to the tree, "
- "or the loop is not reachable from where it is referenced."); return -1);
- return m_activeLoopStack.last();
- }
-
-private:
- QList<int> m_activeLoopStack;
-};
-
-class LoopData
-{
-public:
- LoopThreadData &threadData() {
- QMutexLocker lock(&m_threadDataMutex);
- return m_threadDataMap.try_emplace(QThread::currentThread()).first->second;
- }
-
- const std::optional<int> m_loopCount = {};
- const Loop::ValueGetter m_valueGetter = {};
- const Loop::Condition m_condition = {};
- QMutex m_threadDataMutex = {};
- // Use std::map on purpose, so that it doesn't invalidate references on modifications.
- // Don't optimize it by using std::unordered_map.
- std::map<QThread *, LoopThreadData> m_threadDataMap = {};
-};
-
-Loop::Loop()
- : m_loopData(new LoopData)
-{}
-
-Loop::Loop(int count, const ValueGetter &valueGetter)
- : m_loopData(new LoopData{count, valueGetter})
-{}
-
-Loop::Loop(const Condition &condition)
- : m_loopData(new LoopData{{}, {}, condition})
-{}
-
-int Loop::iteration() const
-{
- return m_loopData->threadData().iteration();
-}
-
-const void *Loop::valuePtr() const
-{
- return m_loopData->m_valueGetter(iteration());
-}
-
-using StoragePtr = void *;
-
-static constexpr QLatin1StringView s_activeStorageWarning =
- "The referenced storage is not reachable in the running tree. "
- "A nullptr will be returned which might lead to a crash in the calling code. "
- "It is possible that no storage was added to the tree, "
- "or the storage is not reachable from where it is referenced."_L1;
-
-class StorageThreadData
-{
- Q_DISABLE_COPY_MOVE(StorageThreadData)
-
-public:
- StorageThreadData() = default;
- void pushStorage(StoragePtr storagePtr)
- {
- m_activeStorageStack.push_back({storagePtr, activeTaskTree()});
- }
- void popStorage()
- {
- QT_ASSERT(m_activeStorageStack.size(), return);
- m_activeStorageStack.pop_back();
- }
- StoragePtr activeStorage() const
- {
- QT_ASSERT(m_activeStorageStack.size(),
- qWarning().noquote() << s_activeStorageWarning; return nullptr);
- const QPair<StoragePtr, TaskTree *> &top = m_activeStorageStack.last();
- QT_ASSERT(top.second == activeTaskTree(),
- qWarning().noquote() << s_activeStorageWarning; return nullptr);
- return top.first;
- }
-
-private:
- QList<QPair<StoragePtr, TaskTree *>> m_activeStorageStack;
-};
-
-class StorageData
-{
-public:
- StorageThreadData &threadData() {
- QMutexLocker lock(&m_threadDataMutex);
- return m_threadDataMap.try_emplace(QThread::currentThread()).first->second;
- }
-
- const StorageBase::StorageConstructor m_constructor = {};
- const StorageBase::StorageDestructor m_destructor = {};
- QMutex m_threadDataMutex = {};
- // Use std::map on purpose, so that it doesn't invalidate references on modifications.
- // Don't optimize it by using std::unordered_map.
- std::map<QThread *, StorageThreadData> m_threadDataMap = {};
-};
-
-StorageBase::StorageBase(const StorageConstructor &ctor, const StorageDestructor &dtor)
- : m_storageData(new StorageData{ctor, dtor})
-{}
-
-void *StorageBase::activeStorageVoid() const
-{
- return m_storageData->threadData().activeStorage();
-}
-
-void GroupItem::addChildren(const GroupItems &children)
-{
- QT_ASSERT(m_type == Type::Group || m_type == Type::List,
- qWarning("Only Group or List may have children, skipping..."); return);
- if (m_type == Type::List) {
- m_children.append(children);
- return;
- }
- for (const GroupItem &child : children) {
- switch (child.m_type) {
- case Type::List:
- addChildren(child.m_children);
- break;
- case Type::Group:
- m_children.append(child);
- break;
- case Type::GroupData:
- if (child.m_groupData.m_groupHandler.m_setupHandler) {
- QT_ASSERT(!m_groupData.m_groupHandler.m_setupHandler,
- qWarning("Group setup handler redefinition, overriding..."));
- m_groupData.m_groupHandler.m_setupHandler
- = child.m_groupData.m_groupHandler.m_setupHandler;
- }
- if (child.m_groupData.m_groupHandler.m_doneHandler) {
- QT_ASSERT(!m_groupData.m_groupHandler.m_doneHandler,
- qWarning("Group done handler redefinition, overriding..."));
- m_groupData.m_groupHandler.m_doneHandler
- = child.m_groupData.m_groupHandler.m_doneHandler;
- m_groupData.m_groupHandler.m_callDoneIf
- = child.m_groupData.m_groupHandler.m_callDoneIf;
- }
- if (child.m_groupData.m_parallelLimit) {
- QT_ASSERT(!m_groupData.m_parallelLimit,
- qWarning("Group execution mode redefinition, overriding..."));
- m_groupData.m_parallelLimit = child.m_groupData.m_parallelLimit;
- }
- if (child.m_groupData.m_workflowPolicy) {
- QT_ASSERT(!m_groupData.m_workflowPolicy,
- qWarning("Group workflow policy redefinition, overriding..."));
- m_groupData.m_workflowPolicy = child.m_groupData.m_workflowPolicy;
- }
- if (child.m_groupData.m_loop) {
- QT_ASSERT(!m_groupData.m_loop,
- qWarning("Group loop redefinition, overriding..."));
- m_groupData.m_loop = child.m_groupData.m_loop;
- }
- break;
- case Type::TaskHandler:
- QT_ASSERT(child.m_taskHandler.m_createHandler,
- qWarning("Task create handler can't be null, skipping..."); return);
- m_children.append(child);
- break;
- case Type::Storage:
- // Check for duplicates, as can't have the same storage twice on the same level.
- for (const StorageBase &storage : child.m_storageList) {
- if (m_storageList.contains(storage)) {
- QT_ASSERT(false, qWarning("Can't add the same storage into one Group twice, "
- "skipping..."));
- continue;
- }
- m_storageList.append(storage);
- }
- break;
- }
- }
-}
-
-/*!
- \class Tasking::ExecutableItem
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief Base class for executable task items.
- \reentrant
-
- \c ExecutableItem provides an additional interface for items containing executable tasks.
- Use withTimeout() to attach a timeout to a task.
- Use withLog() to include debugging information about the task startup and the execution result.
-*/
-
-/*!
- Attaches \c TimeoutTask to a copy of \c this ExecutableItem, elapsing after \a timeout
- in milliseconds, with an optionally provided timeout \a handler, and returns the coupled item.
-
- When the ExecutableItem finishes before \a timeout passes, the returned item finishes
- immediately with the task's result. Otherwise, \a handler is invoked (if provided),
- the task is canceled, and the returned item finishes with an error.
-*/
-Group ExecutableItem::withTimeout(milliseconds timeout,
- const std::function<void()> &handler) const
-{
- const auto onSetup = [timeout](milliseconds &timeoutData) { timeoutData = timeout; };
- return Group {
- parallel,
- stopOnSuccessOrError,
- Group {
- finishAllAndError,
- handler ? TimeoutTask(onSetup, [handler] { handler(); }, CallDoneIf::Success)
- : TimeoutTask(onSetup)
- },
- *this
- };
-}
-
-static QString currentTime() { return QTime::currentTime().toString(Qt::ISODateWithMs); }
-
-static QString logHeader(const QString &logName)
-{
- return QString::fromLatin1("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName);
-};
-
-/*!
- Attaches a custom debug printout to a copy of \c this ExecutableItem,
- issued on task startup and after the task is finished, and returns the coupled item.
-
- The debug printout includes a timestamp of the event (start or finish)
- and \a logName to identify the specific task in the debug log.
-
- The finish printout contains the additional information whether the execution was
- synchronous or asynchronous, its result (the value described by the DoneWith enum),
- and the total execution time in milliseconds.
-*/
-Group ExecutableItem::withLog(const QString &logName) const
-{
- struct LogStorage
- {
- time_point<system_clock, nanoseconds> start;
- int asyncCount = 0;
- };
- const Storage<LogStorage> storage;
- return Group {
- storage,
- onGroupSetup([storage, logName] {
- storage->start = system_clock::now();
- storage->asyncCount = activeTaskTree()->asyncCount();
- qDebug().noquote().nospace() << logHeader(logName) << " started.";
- }),
- *this,
- onGroupDone([storage, logName](DoneWith result) {
- const auto elapsed = duration_cast<milliseconds>(system_clock::now() - storage->start);
- const int asyncCountDiff = activeTaskTree()->asyncCount() - storage->asyncCount;
- QT_CHECK(asyncCountDiff >= 0);
- const QMetaEnum doneWithEnum = QMetaEnum::fromType<DoneWith>();
- const QString syncType = asyncCountDiff ? QString::fromLatin1("asynchronously")
- : QString::fromLatin1("synchronously");
- qDebug().noquote().nospace() << logHeader(logName) << " finished " << syncType
- << " with " << doneWithEnum.valueToKey(int(result))
- << " within " << elapsed.count() << "ms.";
- })
- };
-}
-
-/*!
- \fn Group ExecutableItem::operator!(const ExecutableItem &item)
-
- Returns a Group with the DoneResult of \a item negated.
-
- If \a item reports DoneResult::Success, the returned item reports DoneResult::Error.
- If \a item reports DoneResult::Error, the returned item reports DoneResult::Success.
-
- The returned item is equivalent to:
- \code
- Group {
- item,
- onGroupDone([](DoneWith doneWith) { return toDoneResult(doneWith == DoneWith::Error); })
- }
- \endcode
-
- \sa operator&&(), operator||()
-*/
-Group operator!(const ExecutableItem &item)
-{
- return {
- item,
- onGroupDone([](DoneWith doneWith) { return toDoneResult(doneWith == DoneWith::Error); })
- };
-}
-
-/*!
- \fn Group ExecutableItem::operator&&(const ExecutableItem &first, const ExecutableItem &second)
-
- Returns a Group with \a first and \a second tasks merged with conjunction.
-
- Both \a first and \a second tasks execute in sequence.
- If both tasks report DoneResult::Success, the returned item reports DoneResult::Success.
- Otherwise, the returned item reports DoneResult::Error.
-
- The returned item is
- \l {https://en.wikipedia.org/wiki/Short-circuit_evaluation}{short-circuiting}:
- if the \a first task reports DoneResult::Error, the \a second task is skipped,
- and the returned item reports DoneResult::Error immediately.
-
- The returned item is equivalent to:
- \code
- Group { stopOnError, first, second }
- \endcode
-
- \note Parallel execution of conjunction in a short-circuit manner can be achieved with the
- following code: \c {Group { parallel, stopOnError, first, second }}. In this case:
- if the \e {first finished} task reports DoneResult::Error,
- the \e other task is canceled, and the group reports DoneResult::Error immediately.
-
- \sa operator||(), operator!()
-*/
-Group operator&&(const ExecutableItem &first, const ExecutableItem &second)
-{
- return { stopOnError, first, second };
-}
-
-/*!
- \fn Group ExecutableItem::operator||(const ExecutableItem &first, const ExecutableItem &second)
-
- Returns a Group with \a first and \a second tasks merged with disjunction.
-
- Both \a first and \a second tasks execute in sequence.
- If both tasks report DoneResult::Error, the returned item reports DoneResult::Error.
- Otherwise, the returned item reports DoneResult::Success.
-
- The returned item is
- \l {https://en.wikipedia.org/wiki/Short-circuit_evaluation}{short-circuiting}:
- if the \a first task reports DoneResult::Success, the \a second task is skipped,
- and the returned item reports DoneResult::Success immediately.
-
- The returned item is equivalent to:
- \code
- Group { stopOnSuccess, first, second }
- \endcode
-
- \note Parallel execution of disjunction in a short-circuit manner can be achieved with the
- following code: \c {Group { parallel, stopOnSuccess, first, second }}. In this case:
- if the \e {first finished} task reports DoneResult::Success,
- the \e other task is canceled, and the group reports DoneResult::Success immediately.
-
- \sa operator&&(), operator!()
-*/
-Group operator||(const ExecutableItem &first, const ExecutableItem &second)
-{
- return { stopOnSuccess, first, second };
-}
-
-/*!
- \fn Group ExecutableItem::operator&&(const ExecutableItem &item, DoneResult result)
- \overload ExecutableItem::operator&&()
-
- Returns the \a item task if the \a result is DoneResult::Success; otherwise returns
- the \a item task with its done result tweaked to DoneResult::Error.
-
- The \c {task && DoneResult::Error} is an eqivalent to tweaking the task's done result
- into DoneResult::Error unconditionally.
-*/
-Group operator&&(const ExecutableItem &item, DoneResult result)
-{
- return { result == DoneResult::Success ? stopOnError : finishAllAndError, item };
-}
-
-/*!
- \fn Group ExecutableItem::operator||(const ExecutableItem &item, DoneResult result)
- \overload ExecutableItem::operator||()
-
- Returns the \a item task if the \a result is DoneResult::Error; otherwise returns
- the \a item task with its done result tweaked to DoneResult::Success.
-
- The \c {task || DoneResult::Success} is an eqivalent to tweaking the task's done result
- into DoneResult::Success unconditionally.
-*/
-Group operator||(const ExecutableItem &item, DoneResult result)
-{
- return { result == DoneResult::Error ? stopOnError : finishAllAndSuccess, item };
-}
-
-Group ExecutableItem::withCancelImpl(
- const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper,
- const GroupItems &postCancelRecipe) const
-{
- const Storage<bool> canceledStorage(false);
-
- const auto onSetup = [connectWrapper, canceledStorage](Barrier &barrier) {
- connectWrapper(&barrier, [barrierPtr = &barrier, canceled = canceledStorage.activeStorage()] {
- *canceled = true;
- barrierPtr->advance();
- });
- };
-
- const auto wasCanceled = [canceledStorage] { return *canceledStorage; };
-
- return {
- continueOnError,
- canceledStorage,
- Group {
- parallel,
- stopOnSuccessOrError,
- BarrierTask(onSetup) && errorItem,
- *this
- },
- If (wasCanceled) >> Then {
- postCancelRecipe
- }
- };
-}
-
-Group ExecutableItem::withAcceptImpl(
- const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const
-{
- const auto onSetup = [connectWrapper](Barrier &barrier) {
- connectWrapper(&barrier, [barrierPtr = &barrier] { barrierPtr->advance(); });
- };
- return Group {
- parallel,
- BarrierTask(onSetup),
- *this
- };
-}
-
-class TaskTreePrivate;
-class TaskNode;
-class RuntimeContainer;
-class RuntimeIteration;
-class RuntimeTask;
-
-class ExecutionContextActivator
-{
-public:
- ExecutionContextActivator(RuntimeIteration *iteration) {
- activateTaskTree(iteration);
- activateContext(iteration);
- }
- ExecutionContextActivator(RuntimeContainer *container) {
- activateTaskTree(container);
- activateContext(container);
- }
- ~ExecutionContextActivator() {
- for (int i = m_activeStorages.size() - 1; i >= 0; --i) // iterate in reverse order
- m_activeStorages[i].m_storageData->threadData().popStorage();
- for (int i = m_activeLoops.size() - 1; i >= 0; --i) // iterate in reverse order
- m_activeLoops[i].m_loopData->threadData().popIteration();
- QT_ASSERT(s_activeTaskTrees.size(), return);
- s_activeTaskTrees.pop_back();
- }
-
-private:
- void activateTaskTree(RuntimeIteration *iteration);
- void activateTaskTree(RuntimeContainer *container);
- void activateContext(RuntimeIteration *iteration);
- void activateContext(RuntimeContainer *container);
- QList<Loop> m_activeLoops;
- QList<StorageBase> m_activeStorages;
-};
-
-class ContainerNode
-{
- Q_DISABLE_COPY(ContainerNode)
-
-public:
- ContainerNode(ContainerNode &&other) = default;
- ContainerNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task);
-
- TaskTreePrivate *const m_taskTreePrivate = nullptr;
-
- const GroupItem::GroupHandler m_groupHandler;
- const int m_parallelLimit = 1;
- const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError;
- const std::optional<Loop> m_loop;
- const QList<StorageBase> m_storageList;
- std::vector<TaskNode> m_children;
- const int m_taskCount = 0;
-};
-
-class TaskNode
-{
- Q_DISABLE_COPY(TaskNode)
-
-public:
- TaskNode(TaskNode &&other) = default;
- TaskNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task)
- : m_taskHandler(task.m_taskHandler)
- , m_container(taskTreePrivate, task)
- {}
-
- bool isTask() const { return bool(m_taskHandler.m_createHandler); }
- int taskCount() const { return isTask() ? 1 : m_container.m_taskCount; }
-
- const GroupItem::TaskHandler m_taskHandler;
- ContainerNode m_container;
-};
-
-class TaskTreePrivate
-{
- Q_DISABLE_COPY_MOVE(TaskTreePrivate)
-
-public:
- explicit TaskTreePrivate(TaskTree *taskTree);
- ~TaskTreePrivate();
-
- void start();
- void stop();
- void bumpAsyncCount();
- void advanceProgress(int byValue);
- void emitDone(DoneWith result);
- void callSetupHandler(const StorageBase &storage, StoragePtr storagePtr) {
- callStorageHandler(storage, storagePtr, &StorageHandler::m_setupHandler);
- }
- void callDoneHandler(const StorageBase &storage, StoragePtr storagePtr) {
- callStorageHandler(storage, storagePtr, &StorageHandler::m_doneHandler);
- }
- struct StorageHandler {
- StorageBase::StorageHandler m_setupHandler = {};
- StorageBase::StorageHandler m_doneHandler = {};
- };
- typedef StorageBase::StorageHandler StorageHandler::*HandlerPtr; // ptr to class member
- void callStorageHandler(const StorageBase &storage, StoragePtr storagePtr, HandlerPtr ptr)
- {
- const auto it = m_storageHandlers.constFind(storage);
- if (it == m_storageHandlers.constEnd())
- return;
- const StorageHandler storageHandler = *it;
- if (storageHandler.*ptr) {
- GuardLocker locker(m_guard);
- (storageHandler.*ptr)(storagePtr);
- }
- }
-
- // Node related methods
-
- // If returned value != Continue, childDone() needs to be called in parent container (in caller)
- // in order to unwind properly.
- void startTask(const std::shared_ptr<RuntimeTask> &node);
- void stopTask(RuntimeTask *node);
- bool invokeTaskDoneHandler(RuntimeTask *node, DoneWith doneWith);
-
- // Container related methods
-
- void continueContainer(RuntimeContainer *container);
- void startChildren(RuntimeContainer *container);
- void childDone(RuntimeIteration *iteration, bool success);
- void stopContainer(RuntimeContainer *container);
- bool invokeDoneHandler(RuntimeContainer *container, DoneWith doneWith);
- bool invokeLoopHandler(RuntimeContainer *container);
-
- template <typename Container, typename Handler, typename ...Args,
- typename ReturnType = std::invoke_result_t<Handler, Args...>>
- ReturnType invokeHandler(Container *container, Handler &&handler, Args &&...args)
- {
- QT_ASSERT(!m_guard.isLocked(), qWarning("Nested execution of handlers detected."
- "This may happen when one task's handler has entered a nested event loop,"
- "and other task finished during nested event loop's processing, "
- "causing stopping (canceling) the task executing the nested event loop. "
- "This includes the case when QCoreApplication::processEvents() was called from "
- "the handler. It may also happen when the Barrier task is advanced directly "
- "from some other task handler. This will lead to a crash. "
- "Avoid event processing during handlers' execution. "
- "If it can't be avoided, make sure no other tasks are run in parallel when "
- "processing events from the handler."));
- ExecutionContextActivator activator(container);
- GuardLocker locker(m_guard);
- return std::invoke(std::forward<Handler>(handler), std::forward<Args>(args)...);
- }
-
- static int effectiveLoopCount(const std::optional<Loop> &loop)
- {
- return loop && loop->m_loopData->m_loopCount ? *loop->m_loopData->m_loopCount : 1;
- }
-
- TaskTree *q = nullptr;
- Guard m_guard;
- int m_progressValue = 0;
- int m_asyncCount = 0;
- QSet<StorageBase> m_storages;
- QHash<StorageBase, StorageHandler> m_storageHandlers;
- std::optional<TaskNode> m_root;
- std::shared_ptr<RuntimeTask> m_runtimeRoot; // Keep me last in order to destruct first
-};
-
-static bool initialSuccessBit(WorkflowPolicy workflowPolicy)
-{
- switch (workflowPolicy) {
- case WorkflowPolicy::StopOnError:
- case WorkflowPolicy::ContinueOnError:
- case WorkflowPolicy::FinishAllAndSuccess:
- return true;
- case WorkflowPolicy::StopOnSuccess:
- case WorkflowPolicy::ContinueOnSuccess:
- case WorkflowPolicy::StopOnSuccessOrError:
- case WorkflowPolicy::FinishAllAndError:
- return false;
- }
- QT_CHECK(false);
- return false;
-}
-
-static bool isProgressive(RuntimeContainer *container);
-
-class RuntimeIteration
-{
- Q_DISABLE_COPY(RuntimeIteration)
-
-public:
- RuntimeIteration(int index, RuntimeContainer *container);
- ~RuntimeIteration();
- std::optional<Loop> loop() const;
- void removeChild(RuntimeTask *node);
-
- const int m_iterationIndex = 0;
- const bool m_isProgressive = true;
- RuntimeContainer *m_container = nullptr;
- int m_doneCount = 0;
- std::vector<std::shared_ptr<RuntimeTask>> m_children = {}; // Owning.
-};
-
-class RuntimeContainer
-{
- Q_DISABLE_COPY(RuntimeContainer)
-
-public:
- RuntimeContainer(const ContainerNode &taskContainer, RuntimeTask *parentTask)
- : m_containerNode(taskContainer)
- , m_parentTask(parentTask)
- , m_storages(createStorages(taskContainer))
- , m_successBit(initialSuccessBit(taskContainer.m_workflowPolicy))
- , m_shouldIterate(taskContainer.m_loop)
- {}
-
- ~RuntimeContainer()
- {
- for (int i = m_containerNode.m_storageList.size() - 1; i >= 0; --i) { // iterate in reverse order
- const StorageBase storage = m_containerNode.m_storageList[i];
- StoragePtr storagePtr = m_storages.value(i);
- if (m_callStorageDoneHandlersOnDestruction)
- m_containerNode.m_taskTreePrivate->callDoneHandler(storage, storagePtr);
- storage.m_storageData->m_destructor(storagePtr);
- }
- }
-
- static QList<StoragePtr> createStorages(const ContainerNode &container);
- bool isStarting() const { return m_startGuard.isLocked(); }
- RuntimeIteration *parentIteration() const;
- bool updateSuccessBit(bool success);
- void deleteFinishedIterations();
- int progressiveLoopCount() const
- {
- return m_containerNode.m_taskTreePrivate->effectiveLoopCount(m_containerNode.m_loop);
- }
-
- const ContainerNode &m_containerNode; // Not owning.
- RuntimeTask *m_parentTask = nullptr; // Not owning.
- const QList<StoragePtr> m_storages; // Owning.
-
- bool m_successBit = true;
- bool m_callStorageDoneHandlersOnDestruction = false;
- Guard m_startGuard;
-
- int m_iterationCount = 0;
- int m_nextToStart = 0;
- int m_runningChildren = 0;
- bool m_shouldIterate = true;
- std::vector<std::unique_ptr<RuntimeIteration>> m_iterations; // Owning.
-};
-
-class RuntimeTask
-{
-public:
- ~RuntimeTask()
- {
- if (m_task) {
- // Ensures the running task's d'tor doesn't emit done() signal. QTCREATORBUG-30204.
- QObject::disconnect(m_task.get(), &TaskInterface::done, nullptr, nullptr);
- }
- }
-
- const TaskNode &m_taskNode; // Not owning.
- RuntimeIteration *m_parentIteration = nullptr; // Not owning.
- std::optional<RuntimeContainer> m_container = {}; // Owning.
- std::unique_ptr<TaskInterface> m_task = {}; // Owning.
- SetupResult m_setupResult = SetupResult::Continue;
-};
-
-RuntimeIteration::~RuntimeIteration() = default;
-
-TaskTreePrivate::TaskTreePrivate(TaskTree *taskTree)
- : q(taskTree) {}
-
-TaskTreePrivate::~TaskTreePrivate() = default;
-
-static bool isProgressive(RuntimeContainer *container)
-{
- RuntimeIteration *iteration = container->m_parentTask->m_parentIteration;
- return iteration ? iteration->m_isProgressive : true;
-}
-
-void ExecutionContextActivator::activateTaskTree(RuntimeIteration *iteration)
-{
- activateTaskTree(iteration->m_container);
-}
-
-void ExecutionContextActivator::activateTaskTree(RuntimeContainer *container)
-{
- s_activeTaskTrees.push_back(container->m_containerNode.m_taskTreePrivate->q);
-}
-
-void ExecutionContextActivator::activateContext(RuntimeIteration *iteration)
-{
- std::optional<Loop> loop = iteration->loop();
- if (loop) {
- loop->m_loopData->threadData().pushIteration(iteration->m_iterationIndex);
- m_activeLoops.append(*loop);
- }
- activateContext(iteration->m_container);
-}
-
-void ExecutionContextActivator::activateContext(RuntimeContainer *container)
-{
- const ContainerNode &containerNode = container->m_containerNode;
- for (int i = 0; i < containerNode.m_storageList.size(); ++i) {
- const StorageBase &storage = containerNode.m_storageList[i];
- if (m_activeStorages.contains(storage))
- continue; // Storage shadowing: The storage is already active, skipping it...
- m_activeStorages.append(storage);
- storage.m_storageData->threadData().pushStorage(container->m_storages.value(i));
- }
- // Go to the parent after activating this storages so that storage shadowing works
- // in the direction from child to parent root.
- if (container->parentIteration())
- activateContext(container->parentIteration());
-}
-
-void TaskTreePrivate::start()
-{
- QT_ASSERT(m_root, return);
- QT_ASSERT(!m_runtimeRoot, return);
- m_asyncCount = 0;
- m_progressValue = 0;
- {
- GuardLocker locker(m_guard);
- emit q->started();
- emit q->asyncCountChanged(m_asyncCount);
- emit q->progressValueChanged(m_progressValue);
- }
- // TODO: check storage handlers for not existing storages in tree
- for (auto it = m_storageHandlers.cbegin(); it != m_storageHandlers.cend(); ++it) {
- QT_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't "
- "exist in task tree. Its handlers will never be called."));
- }
- m_runtimeRoot.reset(new RuntimeTask{*m_root});
- startTask(m_runtimeRoot);
- bumpAsyncCount();
-}
-
-void TaskTreePrivate::stop()
-{
- QT_ASSERT(m_root, return);
- if (!m_runtimeRoot)
- return;
- stopTask(m_runtimeRoot.get());
- m_runtimeRoot.reset();
- emitDone(DoneWith::Cancel);
-}
-
-void TaskTreePrivate::bumpAsyncCount()
-{
- if (!m_runtimeRoot)
- return;
- ++m_asyncCount;
- GuardLocker locker(m_guard);
- emit q->asyncCountChanged(m_asyncCount);
-}
-
-void TaskTreePrivate::advanceProgress(int byValue)
-{
- if (byValue == 0)
- return;
- QT_CHECK(byValue > 0);
- QT_CHECK(m_progressValue + byValue <= m_root->taskCount());
- m_progressValue += byValue;
- GuardLocker locker(m_guard);
- emit q->progressValueChanged(m_progressValue);
-}
-
-void TaskTreePrivate::emitDone(DoneWith result)
-{
- QT_CHECK(m_progressValue == m_root->taskCount());
- GuardLocker locker(m_guard);
- emit q->done(result);
-}
-
-RuntimeIteration::RuntimeIteration(int index, RuntimeContainer *container)
- : m_iterationIndex(index)
- , m_isProgressive(index < container->progressiveLoopCount() && isProgressive(container))
- , m_container(container)
-{}
-
-std::optional<Loop> RuntimeIteration::loop() const
-{
- return m_container->m_containerNode.m_loop;
-}
-
-void RuntimeIteration::removeChild(RuntimeTask *task)
-{
- const auto it = std::find_if(m_children.cbegin(), m_children.cend(), [task](const auto &ptr) {
- return ptr.get() == task;
- });
- if (it != m_children.cend())
- m_children.erase(it);
-}
-
-static std::vector<TaskNode> createChildren(TaskTreePrivate *taskTreePrivate,
- const GroupItems &children)
-{
- std::vector<TaskNode> result;
- result.reserve(children.size());
- for (const GroupItem &child : children)
- result.emplace_back(taskTreePrivate, child);
- return result;
-}
-
-ContainerNode::ContainerNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task)
- : m_taskTreePrivate(taskTreePrivate)
- , m_groupHandler(task.m_groupData.m_groupHandler)
- , m_parallelLimit(task.m_groupData.m_parallelLimit.value_or(1))
- , m_workflowPolicy(task.m_groupData.m_workflowPolicy.value_or(WorkflowPolicy::StopOnError))
- , m_loop(task.m_groupData.m_loop)
- , m_storageList(task.m_storageList)
- , m_children(createChildren(taskTreePrivate, task.m_children))
- , m_taskCount(std::accumulate(m_children.cbegin(), m_children.cend(), 0,
- [](int r, const TaskNode &n) { return r + n.taskCount(); })
- * taskTreePrivate->effectiveLoopCount(m_loop))
-{
- for (const StorageBase &storage : m_storageList)
- m_taskTreePrivate->m_storages << storage;
-}
-
-QList<StoragePtr> RuntimeContainer::createStorages(const ContainerNode &container)
-{
- QList<StoragePtr> storages;
- for (const StorageBase &storage : container.m_storageList) {
- StoragePtr storagePtr = storage.m_storageData->m_constructor();
- storages.append(storagePtr);
- container.m_taskTreePrivate->callSetupHandler(storage, storagePtr);
- }
- return storages;
-}
-
-RuntimeIteration *RuntimeContainer::parentIteration() const
-{
- return m_parentTask->m_parentIteration;
-}
-
-bool RuntimeContainer::updateSuccessBit(bool success)
-{
- if (m_containerNode.m_workflowPolicy == WorkflowPolicy::FinishAllAndSuccess
- || m_containerNode.m_workflowPolicy == WorkflowPolicy::FinishAllAndError
- || m_containerNode.m_workflowPolicy == WorkflowPolicy::StopOnSuccessOrError) {
- if (m_containerNode.m_workflowPolicy == WorkflowPolicy::StopOnSuccessOrError)
- m_successBit = success;
- return m_successBit;
- }
-
- const bool donePolicy = m_containerNode.m_workflowPolicy == WorkflowPolicy::StopOnSuccess
- || m_containerNode.m_workflowPolicy == WorkflowPolicy::ContinueOnSuccess;
- m_successBit = donePolicy ? (m_successBit || success) : (m_successBit && success);
- return m_successBit;
-}
-
-void RuntimeContainer::deleteFinishedIterations()
-{
- for (auto it = m_iterations.cbegin(); it != m_iterations.cend(); ) {
- if (it->get()->m_doneCount == int(m_containerNode.m_children.size()))
- it = m_iterations.erase(it);
- else
- ++it;
- }
-}
-
-void TaskTreePrivate::continueContainer(RuntimeContainer *container)
-{
- RuntimeTask *parentTask = container->m_parentTask;
- if (parentTask->m_setupResult == SetupResult::Continue)
- startChildren(container);
- if (parentTask->m_setupResult == SetupResult::Continue)
- return;
-
- const bool bit = container->updateSuccessBit(parentTask->m_setupResult == SetupResult::StopWithSuccess);
- RuntimeIteration *parentIteration = container->parentIteration();
- QT_CHECK(parentTask);
- const bool result = invokeDoneHandler(container, bit ? DoneWith::Success : DoneWith::Error);
- parentTask->m_setupResult = toSetupResult(result);
- if (parentIteration) {
- parentIteration->removeChild(parentTask);
- if (!parentIteration->m_container->isStarting())
- childDone(parentIteration, result);
- } else {
- QT_CHECK(m_runtimeRoot.get() == parentTask);
- m_runtimeRoot.reset();
- emitDone(result ? DoneWith::Success : DoneWith::Error);
- }
-}
-
-void TaskTreePrivate::startChildren(RuntimeContainer *container)
-{
- const ContainerNode &containerNode = container->m_containerNode;
- const int childCount = int(containerNode.m_children.size());
-
- if (container->m_iterationCount == 0) {
- if (container->m_shouldIterate && !invokeLoopHandler(container)) {
- if (isProgressive(container))
- advanceProgress(containerNode.m_taskCount);
- container->m_parentTask->m_setupResult = toSetupResult(container->m_successBit);
- return;
- }
- container->m_iterations.emplace_back(
- std::make_unique<RuntimeIteration>(container->m_iterationCount, container));
- ++container->m_iterationCount;
- }
-
- GuardLocker locker(container->m_startGuard);
-
- while (containerNode.m_parallelLimit == 0
- || container->m_runningChildren < containerNode.m_parallelLimit) {
- container->deleteFinishedIterations();
- if (container->m_nextToStart == childCount) {
- if (invokeLoopHandler(container)) {
- container->m_nextToStart = 0;
- container->m_iterations.emplace_back(
- std::make_unique<RuntimeIteration>(container->m_iterationCount, container));
- ++container->m_iterationCount;
- } else if (container->m_iterations.empty()) {
- container->m_parentTask->m_setupResult = toSetupResult(container->m_successBit);
- return;
- } else {
- return;
- }
- }
- if (containerNode.m_children.size() == 0) // Empty loop body.
- continue;
-
- RuntimeIteration *iteration = container->m_iterations.back().get();
- const std::shared_ptr<RuntimeTask> task(
- new RuntimeTask{containerNode.m_children.at(container->m_nextToStart), iteration});
- iteration->m_children.emplace_back(task);
- ++container->m_runningChildren;
- ++container->m_nextToStart;
-
- startTask(task);
- if (task->m_setupResult == SetupResult::Continue)
- continue;
-
- task->m_parentIteration->removeChild(task.get());
- childDone(iteration, task->m_setupResult == SetupResult::StopWithSuccess);
- if (container->m_parentTask->m_setupResult != SetupResult::Continue)
- return;
- }
-}
-
-void TaskTreePrivate::childDone(RuntimeIteration *iteration, bool success)
-{
- RuntimeContainer *container = iteration->m_container;
- const WorkflowPolicy &workflowPolicy = container->m_containerNode.m_workflowPolicy;
- const bool shouldStop = workflowPolicy == WorkflowPolicy::StopOnSuccessOrError
- || (workflowPolicy == WorkflowPolicy::StopOnSuccess && success)
- || (workflowPolicy == WorkflowPolicy::StopOnError && !success);
- ++iteration->m_doneCount;
- --container->m_runningChildren;
- const bool updatedSuccess = container->updateSuccessBit(success);
- container->m_parentTask->m_setupResult = shouldStop ? toSetupResult(updatedSuccess) : SetupResult::Continue;
- if (shouldStop)
- stopContainer(container);
-
- if (container->isStarting())
- return;
- continueContainer(container);
-}
-
-void TaskTreePrivate::stopContainer(RuntimeContainer *container)
-{
- const ContainerNode &containerNode = container->m_containerNode;
- for (auto &iteration : container->m_iterations) {
- for (auto &child : iteration->m_children) {
- ++iteration->m_doneCount;
- stopTask(child.get());
- }
-
- if (iteration->m_isProgressive) {
- int skippedTaskCount = 0;
- for (int i = iteration->m_doneCount; i < int(containerNode.m_children.size()); ++i)
- skippedTaskCount += containerNode.m_children.at(i).taskCount();
- advanceProgress(skippedTaskCount);
- }
- }
- const int skippedIterations = container->progressiveLoopCount() - container->m_iterationCount;
- if (skippedIterations > 0) {
- advanceProgress(container->m_containerNode.m_taskCount / container->progressiveLoopCount()
- * skippedIterations);
- }
-}
-
-static bool shouldCall(CallDoneIf callDoneIf, DoneWith result)
-{
- if (result == DoneWith::Success)
- return callDoneIf != CallDoneIf::Error;
- return callDoneIf != CallDoneIf::Success;
-}
-
-bool TaskTreePrivate::invokeDoneHandler(RuntimeContainer *container, DoneWith doneWith)
-{
- DoneResult result = toDoneResult(doneWith);
- const GroupItem::GroupHandler &groupHandler = container->m_containerNode.m_groupHandler;
- if (groupHandler.m_doneHandler && shouldCall(groupHandler.m_callDoneIf, doneWith))
- result = invokeHandler(container, groupHandler.m_doneHandler, doneWith);
- container->m_callStorageDoneHandlersOnDestruction = true;
- return result == DoneResult::Success;
-}
-
-bool TaskTreePrivate::invokeLoopHandler(RuntimeContainer *container)
-{
- if (container->m_shouldIterate) {
- const LoopData *loopData = container->m_containerNode.m_loop->m_loopData.get();
- if (loopData->m_loopCount) {
- container->m_shouldIterate = container->m_iterationCount < loopData->m_loopCount;
- } else if (loopData->m_condition) {
- container->m_shouldIterate = invokeHandler(container, loopData->m_condition,
- container->m_iterationCount);
- }
- }
- return container->m_shouldIterate;
-}
-
-void TaskTreePrivate::startTask(const std::shared_ptr<RuntimeTask> &node)
-{
- if (!node->m_taskNode.isTask()) {
- const ContainerNode &containerNode = node->m_taskNode.m_container;
- node->m_container.emplace(containerNode, node.get());
- RuntimeContainer *container = &*node->m_container;
- if (containerNode.m_groupHandler.m_setupHandler) {
- container->m_parentTask->m_setupResult = invokeHandler(container, containerNode.m_groupHandler.m_setupHandler);
- if (container->m_parentTask->m_setupResult != SetupResult::Continue) {
- if (isProgressive(container))
- advanceProgress(containerNode.m_taskCount);
- // Non-Continue SetupResult takes precedence over the workflow policy.
- container->m_successBit = container->m_parentTask->m_setupResult == SetupResult::StopWithSuccess;
- }
- }
- continueContainer(container);
- return;
- }
-
- const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
- node->m_task.reset(handler.m_createHandler());
- node->m_setupResult = handler.m_setupHandler
- ? invokeHandler(node->m_parentIteration, handler.m_setupHandler, *node->m_task.get())
- : SetupResult::Continue;
- if (node->m_setupResult != SetupResult::Continue) {
- if (node->m_parentIteration->m_isProgressive)
- advanceProgress(1);
- node->m_parentIteration->removeChild(node.get());
- return;
- }
- QObject::connect(node->m_task.get(), &TaskInterface::done,
- q, [this, node](DoneResult doneResult) {
- const bool result = invokeTaskDoneHandler(node.get(), toDoneWith(doneResult));
- node->m_setupResult = toSetupResult(result);
- QObject::disconnect(node->m_task.get(), &TaskInterface::done, q, nullptr);
- node->m_task.release()->deleteLater();
- RuntimeIteration *parentIteration = node->m_parentIteration;
- if (parentIteration->m_container->isStarting())
- return;
-
- parentIteration->removeChild(node.get());
- childDone(parentIteration, result);
- bumpAsyncCount();
- });
- node->m_task->start();
-}
-
-void TaskTreePrivate::stopTask(RuntimeTask *node)
-{
- if (!node->m_task) {
- if (!node->m_container)
- return;
- stopContainer(&*node->m_container);
- node->m_container->updateSuccessBit(false);
- invokeDoneHandler(&*node->m_container, DoneWith::Cancel);
- return;
- }
-
- invokeTaskDoneHandler(node, DoneWith::Cancel);
- node->m_task.reset();
-}
-
-bool TaskTreePrivate::invokeTaskDoneHandler(RuntimeTask *node, DoneWith doneWith)
-{
- DoneResult result = toDoneResult(doneWith);
- const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
- if (handler.m_doneHandler && shouldCall(handler.m_callDoneIf, doneWith)) {
- result = invokeHandler(node->m_parentIteration,
- handler.m_doneHandler, *node->m_task.get(), doneWith);
- }
- if (node->m_parentIteration->m_isProgressive)
- advanceProgress(1);
- return result == DoneResult::Success;
-}
-
-/*!
- \class Tasking::TaskTree
- \inheaderfile solutions/tasking/tasktree.h
- \inmodule TaskingSolution
- \brief The TaskTree class runs an async task tree structure defined in a declarative way.
- \reentrant
-
- Use the Tasking namespace to build extensible, declarative task tree
- structures that contain possibly asynchronous tasks, such as QProcess,
- NetworkQuery, or ConcurrentCall<ReturnType>. TaskTree structures enable you
- to create a sophisticated mixture of a parallel or sequential flow of tasks
- in the form of a tree and to run it any time later.
-
- \section1 Root Element and Tasks
-
- The TaskTree has a mandatory Group root element, which may contain
- any number of tasks of various types, such as QProcessTask, NetworkQueryTask,
- or ConcurrentCallTask<ReturnType>:
-
- \code
- using namespace Tasking;
-
- const Group root {
- QProcessTask(...),
- NetworkQueryTask(...),
- ConcurrentCallTask<int>(...)
- };
-
- TaskTree *taskTree = new TaskTree(root);
- connect(taskTree, &TaskTree::done, ...); // finish handler
- taskTree->start();
- \endcode
-
- The task tree above has a top level element of the Group type that contains
- tasks of the QProcessTask, NetworkQueryTask, and ConcurrentCallTask<int> type.
- After taskTree->start() is called, the tasks are run in a chain, starting
- with QProcessTask. When the QProcessTask finishes successfully, the NetworkQueryTask
- task is started. Finally, when the network task finishes successfully, the
- ConcurrentCallTask<int> task is started.
-
- When the last running task finishes with success, the task tree is considered
- to have run successfully and the done() signal is emitted with DoneWith::Success.
- When a task finishes with an error, the execution of the task tree is stopped
- and the remaining tasks are skipped. The task tree finishes with an error and
- sends the TaskTree::done() signal with DoneWith::Error.
-
- \section1 Groups
-
- The parent of the Group sees it as a single task. Like other tasks,
- the group can be started and it can finish with success or an error.
- The Group elements can be nested to create a tree structure:
-
- \code
- const Group root {
- Group {
- parallel,
- QProcessTask(...),
- ConcurrentCallTask<int>(...)
- },
- NetworkQueryTask(...)
- };
- \endcode
-
- The example above differs from the first example in that the root element has
- a subgroup that contains the QProcessTask and ConcurrentCallTask<int>. The subgroup is a
- sibling element of the NetworkQueryTask in the root. The subgroup contains an
- additional \e parallel element that instructs its Group to execute its tasks
- in parallel.
-
- So, when the tree above is started, the QProcessTask and ConcurrentCallTask<int> start
- immediately and run in parallel. Since the root group doesn't contain a
- \e parallel element, its direct child tasks are run in sequence. Thus, the
- NetworkQueryTask starts when the whole subgroup finishes. The group is
- considered as finished when all its tasks have finished. The order in which
- the tasks finish is not relevant.
-
- So, depending on which task lasts longer (QProcessTask or ConcurrentCallTask<int>), the
- following scenarios can take place:
-
- \table
- \header
- \li Scenario 1
- \li Scenario 2
- \row
- \li Root Group starts
- \li Root Group starts
- \row
- \li Sub Group starts
- \li Sub Group starts
- \row
- \li QProcessTask starts
- \li QProcessTask starts
- \row
- \li ConcurrentCallTask<int> starts
- \li ConcurrentCallTask<int> starts
- \row
- \li ...
- \li ...
- \row
- \li \b {QProcessTask finishes}
- \li \b {ConcurrentCallTask<int> finishes}
- \row
- \li ...
- \li ...
- \row
- \li \b {ConcurrentCallTask<int> finishes}
- \li \b {QProcessTask finishes}
- \row
- \li Sub Group finishes
- \li Sub Group finishes
- \row
- \li NetworkQueryTask starts
- \li NetworkQueryTask starts
- \row
- \li ...
- \li ...
- \row
- \li NetworkQueryTask finishes
- \li NetworkQueryTask finishes
- \row
- \li Root Group finishes
- \li Root Group finishes
- \endtable
-
- The differences between the scenarios are marked with bold. Three dots mean
- that an unspecified amount of time passes between previous and next events
- (a task or tasks continue to run). No dots between events
- means that they occur synchronously.
-
- The presented scenarios assume that all tasks run successfully. If a task
- fails during execution, the task tree finishes with an error. In particular,
- when QProcessTask finishes with an error while ConcurrentCallTask<int> is still being executed,
- the ConcurrentCallTask<int> is automatically canceled, the subgroup finishes with an error,
- the NetworkQueryTask is skipped, and the tree finishes with an error.
-
- \section1 Task Types
-
- Each task type is associated with its corresponding task class that executes
- the task. For example, a QProcessTask inside a task tree is associated with
- the QProcess class that executes the process. The associated objects are
- automatically created, started, and destructed exclusively by the task tree
- at the appropriate time.
-
- If a root group consists of five sequential QProcessTask tasks, and the task tree
- executes the group, it creates an instance of QProcess for the first
- QProcessTask and starts it. If the QProcess instance finishes successfully,
- the task tree destructs it and creates a new QProcess instance for the
- second QProcessTask, and so on. If the first task finishes with an error, the task
- tree stops creating QProcess instances, and the root group finishes with an
- error.
-
- The following table shows examples of task types and their corresponding task
- classes:
-
- \table
- \header
- \li Task Type (Tasking Namespace)
- \li Associated Task Class
- \li Brief Description
- \row
- \li QProcessTask
- \li QProcess
- \li Starts process.
- \row
- \li ConcurrentCallTask<ReturnType>
- \li Tasking::ConcurrentCall<ReturnType>
- \li Starts asynchronous task, runs in separate thread.
- \row
- \li TaskTreeTask
- \li Tasking::TaskTree
- \li Starts nested task tree.
- \row
- \li NetworkQueryTask
- \li NetworkQuery
- \li Starts network download.
- \endtable
-
- \section1 Task Handlers
-
- Use Task handlers to set up a task for execution and to enable reading
- the output data from the task when it finishes with success or an error.
-
- \section2 Task's Start Handler
-
- When a corresponding task class object is created and before it's started,
- the task tree invokes an optionally user-provided setup handler. The setup
- handler should always take a \e reference to the associated task class object:
-
- \code
- const auto onSetup = [](QProcess &process) {
- process.setProgram("sleep");
- process.setArguments({"3"});
- };
- const Group root {
- QProcessTask(onSetup)
- };
- \endcode
-
- You can modify the passed QProcess in the setup handler, so that the task
- tree can start the process according to your configuration.
- You should not call \c {process.start();} in the setup handler,
- as the task tree calls it when needed. The setup handler is optional. When used,
- it must be the first argument of the task's constructor.
-
- Optionally, the setup handler may return a SetupResult. The returned
- SetupResult influences the further start behavior of a given task. The
- possible values are:
-
- \table
- \header
- \li SetupResult Value
- \li Brief Description
- \row
- \li Continue
- \li The task will be started normally. This is the default behavior when the
- setup handler doesn't return SetupResult (that is, its return type is
- void).
- \row
- \li StopWithSuccess
- \li The task won't be started and it will report success to its parent.
- \row
- \li StopWithError
- \li The task won't be started and it will report an error to its parent.
- \endtable
-
- This is useful for running a task only when a condition is met and the data
- needed to evaluate this condition is not known until previously started tasks
- finish. In this way, the setup handler dynamically decides whether to start the
- corresponding task normally or skip it and report success or an error.
- For more information about inter-task data exchange, see \l Storage.
-
- \section2 Task's Done Handler
-
- When a running task finishes, the task tree invokes an optionally provided done handler.
- The handler should take a \c const \e reference to the associated task class object:
-
- \code
- const auto onSetup = [](QProcess &process) {
- process.setProgram("sleep");
- process.setArguments({"3"});
- };
- const auto onDone = [](const QProcess &process, DoneWith result) {
- if (result == DoneWith::Success)
- qDebug() << "Success" << process.cleanedStdOut();
- else
- qDebug() << "Failure" << process.cleanedStdErr();
- };
- const Group root {
- QProcessTask(onSetup, onDone)
- };
- \endcode
-
- The done handler may collect output data from QProcess, and store it
- for further processing or perform additional actions.
-
- \note If the task setup handler returns StopWithSuccess or StopWithError,
- the done handler is not invoked.
-
- \section1 Group Handlers
-
- Similarly to task handlers, group handlers enable you to set up a group to
- execute and to apply more actions when the whole group finishes with
- success or an error.
-
- \section2 Group's Start Handler
-
- The task tree invokes the group start handler before it starts the child
- tasks. The group handler doesn't take any arguments:
-
- \code
- const auto onSetup = [] {
- qDebug() << "Entering the group";
- };
- const Group root {
- onGroupSetup(onSetup),
- QProcessTask(...)
- };
- \endcode
-
- The group setup handler is optional. To define a group setup handler, add an
- onGroupSetup() element to a group. The argument of onGroupSetup() is a user
- handler. If you add more than one onGroupSetup() element to a group, an assert
- is triggered at runtime that includes an error message.
-
- Like the task's start handler, the group start handler may return SetupResult.
- The returned SetupResult value affects the start behavior of the
- whole group. If you do not specify a group start handler or its return type
- is void, the default group's action is SetupResult::Continue, so that all
- tasks are started normally. Otherwise, when the start handler returns
- SetupResult::StopWithSuccess or SetupResult::StopWithError, the tasks are not
- started (they are skipped) and the group itself reports success or failure,
- depending on the returned value, respectively.
-
- \code
- const Group root {
- onGroupSetup([] { qDebug() << "Root setup"; }),
- Group {
- onGroupSetup([] { qDebug() << "Group 1 setup"; return SetupResult::Continue; }),
- QProcessTask(...) // Process 1
- },
- Group {
- onGroupSetup([] { qDebug() << "Group 2 setup"; return SetupResult::StopWithSuccess; }),
- QProcessTask(...) // Process 2
- },
- Group {
- onGroupSetup([] { qDebug() << "Group 3 setup"; return SetupResult::StopWithError; }),
- QProcessTask(...) // Process 3
- },
- QProcessTask(...) // Process 4
- };
- \endcode
-
- In the above example, all subgroups of a root group define their setup handlers.
- The following scenario assumes that all started processes finish with success:
-
- \table
- \header
- \li Scenario
- \li Comment
- \row
- \li Root Group starts
- \li Doesn't return SetupResult, so its tasks are executed.
- \row
- \li Group 1 starts
- \li Returns Continue, so its tasks are executed.
- \row
- \li Process 1 starts
- \li
- \row
- \li ...
- \li ...
- \row
- \li Process 1 finishes (success)
- \li
- \row
- \li Group 1 finishes (success)
- \li
- \row
- \li Group 2 starts
- \li Returns StopWithSuccess, so Process 2 is skipped and Group 2 reports
- success.
- \row
- \li Group 2 finishes (success)
- \li
- \row
- \li Group 3 starts
- \li Returns StopWithError, so Process 3 is skipped and Group 3 reports
- an error.
- \row
- \li Group 3 finishes (error)
- \li
- \row
- \li Root Group finishes (error)
- \li Group 3, which is a direct child of the root group, finished with an
- error, so the root group stops executing, skips Process 4, which has
- not started yet, and reports an error.
- \endtable
-
- \section2 Groups's Done Handler
-
- A Group's done handler is executed after the successful or failed execution of its tasks.
- The final value reported by the group depends on its \l {Workflow Policy}.
- The handler can apply other necessary actions.
- The done handler is defined inside the onGroupDone() element of a group.
- It may take the optional DoneWith argument, indicating the successful or failed execution:
-
- \code
- const Group root {
- onGroupSetup([] { qDebug() << "Root setup"; }),
- QProcessTask(...),
- onGroupDone([](DoneWith result) {
- if (result == DoneWith::Success)
- qDebug() << "Root finished with success";
- else
- qDebug() << "Root finished with an error";
- })
- };
- \endcode
-
- The group done handler is optional. If you add more than one onGroupDone() to a group,
- an assert is triggered at runtime that includes an error message.
-
- \note Even if the group setup handler returns StopWithSuccess or StopWithError,
- the group's done handler is invoked. This behavior differs from that of task done handler
- and might change in the future.
-
- \section1 Other Group Elements
-
- A group can contain other elements that describe the processing flow, such as
- the execution mode or workflow policy. It can also contain storage elements
- that are responsible for collecting and sharing custom common data gathered
- during group execution.
-
- \section2 Execution Mode
-
- The execution mode element in a Group specifies how the direct child tasks of
- the Group are started. The most common execution modes are \l sequential and
- \l parallel. It's also possible to specify the limit of tasks running
- in parallel by using the parallelLimit() function.
-
- In all execution modes, a group starts tasks in the oder in which they appear.
-
- If a child of a group is also a group, the child group runs its tasks
- according to its own execution mode.
-
- \section2 Workflow Policy
-
- The workflow policy element in a Group specifies how the group should behave
- when any of its \e direct child's tasks finish. For a detailed description of possible
- policies, refer to WorkflowPolicy.
-
- If a child of a group is also a group, the child group runs its tasks
- according to its own workflow policy.
-
- \section2 Storage
-
- Use the \l {Tasking::Storage} {Storage} element to exchange information between tasks.
- Especially, in the sequential execution mode, when a task needs data from another,
- already finished task, before it can start. For example, a task tree that copies data by reading
- it from a source and writing it to a destination might look as follows:
-
- \code
- static QByteArray load(const QString &fileName) { ... }
- static void save(const QString &fileName, const QByteArray &array) { ... }
-
- static Group copyRecipe(const QString &source, const QString &destination)
- {
- struct CopyStorage { // [1] custom inter-task struct
- QByteArray content; // [2] custom inter-task data
- };
-
- // [3] instance of custom inter-task struct manageable by task tree
- const Storage<CopyStorage> storage;
-
- const auto onLoaderSetup = [source](ConcurrentCall<QByteArray> &async) {
- async.setConcurrentCallData(&load, source);
- };
- // [4] runtime: task tree activates the instance from [7] before invoking handler
- const auto onLoaderDone = [storage](const ConcurrentCall<QByteArray> &async) {
- storage->content = async.result(); // [5] loader stores the result in storage
- };
-
- // [4] runtime: task tree activates the instance from [7] before invoking handler
- const auto onSaverSetup = [storage, destination](ConcurrentCall<void> &async) {
- const QByteArray content = storage->content; // [6] saver takes data from storage
- async.setConcurrentCallData(&save, destination, content);
- };
- const auto onSaverDone = [](const ConcurrentCall<void> &async) {
- qDebug() << "Save done successfully";
- };
-
- const Group root {
- // [7] runtime: task tree creates an instance of CopyStorage when root is entered
- storage,
- ConcurrentCallTask<QByteArray>(onLoaderSetup, onLoaderDone, CallDoneIf::Success),
- ConcurrentCallTask<void>(onSaverSetup, onSaverDone, CallDoneIf::Success)
- };
- return root;
- }
-
- const QString source = ...;
- const QString destination = ...;
- TaskTree taskTree(copyRecipe(source, destination));
- connect(&taskTree, &TaskTree::done,
- &taskTree, [](DoneWith result) {
- if (result == DoneWith::Success)
- qDebug() << "The copying finished successfully.";
- });
- tasktree.start();
- \endcode
-
- In the example above, the inter-task data consists of a QByteArray content
- variable [2] enclosed in a \c CopyStorage custom struct [1]. If the loader
- finishes successfully, it stores the data in a \c CopyStorage::content
- variable [5]. The saver then uses the variable to configure the saving task [6].
-
- To enable a task tree to manage the \c CopyStorage struct, an instance of
- \l {Tasking::Storage} {Storage}<\c CopyStorage> is created [3]. If a copy of this object is
- inserted as the group's child item [7], an instance of the \c CopyStorage struct is
- created dynamically when the task tree enters this group. When the task
- tree leaves this group, the existing instance of the \c CopyStorage struct is
- destructed as it's no longer needed.
-
- If several task trees holding a copy of the common
- \l {Tasking::Storage} {Storage}<\c CopyStorage> instance run simultaneously
- (including the case when the task trees are run in different threads),
- each task tree contains its own copy of the \c CopyStorage struct.
-
- You can access \c CopyStorage from any handler in the group with a storage object.
- This includes all handlers of all descendant tasks of the group with
- a storage object. To access the custom struct in a handler, pass the
- copy of the \l {Tasking::Storage} {Storage}<\c CopyStorage> object to the handler
- (for example, in a lambda capture) [4].
-
- When the task tree invokes a handler in a subtree containing the storage [7],
- the task tree activates its own \c CopyStorage instance inside the
- \l {Tasking::Storage} {Storage}<\c CopyStorage> object. Therefore, the \c CopyStorage struct
- may be accessed only from within the handler body. To access the currently active
- \c CopyStorage from within \l {Tasking::Storage} {Storage}<\c CopyStorage>, use the
- \l {Tasking::Storage::operator->()} {Storage::operator->()},
- \l {Tasking::Storage::operator*()} {Storage::operator*()}, or Storage::activeStorage() method.
-
- The following list summarizes how to employ a Storage object into the task
- tree:
- \list 1
- \li Define the custom structure \c MyStorage with custom data [1], [2]
- \li Create an instance of the \l {Tasking::Storage} {Storage}<\c MyStorage> storage [3]
- \li Pass the \l {Tasking::Storage} {Storage}<\c MyStorage> instance to handlers [4]
- \li Access the \c MyStorage instance in handlers [5], [6]
- \li Insert the \l {Tasking::Storage} {Storage}<\c MyStorage> instance into a group [7]
- \endlist
-
- \section1 TaskTree class
-
- TaskTree executes the tree structure of asynchronous tasks according to the
- recipe described by the Group root element.
-
- As TaskTree is also an asynchronous task, it can be a part of another TaskTree.
- To place a nested TaskTree inside another TaskTree, insert the TaskTreeTask
- element into another Group element.
-
- TaskTree reports progress of completed tasks when running. The progress value
- is increased when a task finishes or is skipped or canceled.
- When TaskTree is finished and the TaskTree::done() signal is emitted,
- the current value of the progress equals the maximum progress value.
- Maximum progress equals the total number of asynchronous tasks in a tree.
- A nested TaskTree is counted as a single task, and its child tasks are not
- counted in the top level tree. Groups themselves are not counted as tasks,
- but their tasks are counted. \l {Tasking::Sync} {Sync} tasks are not asynchronous,
- so they are not counted as tasks.
-
- To set additional initial data for the running tree, modify the storage
- instances in a tree when it creates them by installing a storage setup
- handler:
-
- \code
- Storage<CopyStorage> storage;
- const Group root = ...; // storage placed inside root's group and inside handlers
- TaskTree taskTree(root);
- auto initStorage = [](CopyStorage &storage) {
- storage.content = "initial content";
- };
- taskTree.onStorageSetup(storage, initStorage);
- taskTree.start();
- \endcode
-
- When the running task tree creates a \c CopyStorage instance, and before any
- handler inside a tree is called, the task tree calls the initStorage handler,
- to enable setting up initial data of the storage, unique to this particular
- run of taskTree.
-
- Similarly, to collect some additional result data from the running tree,
- read it from storage instances in the tree when they are about to be
- destroyed. To do this, install a storage done handler:
-
- \code
- Storage<CopyStorage> storage;
- const Group root = ...; // storage placed inside root's group and inside handlers
- TaskTree taskTree(root);
- auto collectStorage = [](const CopyStorage &storage) {
- qDebug() << "final content" << storage.content;
- };
- taskTree.onStorageDone(storage, collectStorage);
- taskTree.start();
- \endcode
-
- When the running task tree is about to destroy a \c CopyStorage instance, the
- task tree calls the collectStorage handler, to enable reading the final data
- from the storage, unique to this particular run of taskTree.
-
- \section1 Task Adapters
-
- To extend a TaskTree with a new task type, implement a simple adapter class
- derived from the TaskAdapter class template. The following class is an
- adapter for a single shot timer, which may be considered as a new asynchronous task:
-
- \code
- class TimerTaskAdapter : public TaskAdapter<QTimer>
- {
- public:
- TimerTaskAdapter() {
- task()->setSingleShot(true);
- task()->setInterval(1000);
- connect(task(), &QTimer::timeout, this, [this] { emit done(DoneResult::Success); });
- }
- private:
- void start() final { task()->start(); }
- };
-
- using TimerTask = CustomTask<TimerTaskAdapter>;
- \endcode
-
- You must derive the custom adapter from the TaskAdapter class template
- instantiated with a template parameter of the class implementing a running
- task. The code above uses QTimer to run the task. This class appears
- later as an argument to the task's handlers. The instance of this class
- parameter automatically becomes a member of the TaskAdapter template, and is
- accessible through the TaskAdapter::task() method. The constructor
- of \c TimerTaskAdapter initially configures the QTimer object and connects
- to the QTimer::timeout() signal. When the signal is triggered, \c TimerTaskAdapter
- emits the TaskInterface::done(DoneResult::Success) signal to inform the task tree that
- the task finished successfully. If it emits TaskInterface::done(DoneResult::Error),
- the task finished with an error.
- The TaskAdapter::start() method starts the timer.
-
- To make QTimer accessible inside TaskTree under the \c TimerTask name,
- define \c TimerTask to be an alias to the CustomTask<\c TimerTaskAdapter>.
- \c TimerTask becomes a new custom task type, using \c TimerTaskAdapter.
-
- The new task type is now registered, and you can use it in TaskTree:
-
- \code
- const auto onSetup = [](QTimer &task) { task.setInterval(2000); };
- const auto onDone = [] { qDebug() << "timer triggered"; };
- const Group root {
- TimerTask(onSetup, onDone)
- };
- \endcode
-
- When a task tree containing the root from the above example is started, it
- prints a debug message within two seconds and then finishes successfully.
-
- \note The class implementing the running task should have a default constructor,
- and objects of this class should be freely destructible. It should be allowed
- to destroy a running object, preferably without waiting for the running task
- to finish (that is, safe non-blocking destructor of a running task).
- To achieve a non-blocking destruction of a task that has a blocking destructor,
- consider using the optional \c Deleter template parameter of the TaskAdapter.
-*/
-
-/*!
- Constructs an empty task tree. Use setRecipe() to pass a declarative description
- on how the task tree should execute the tasks and how it should handle the finished tasks.
-
- Starting an empty task tree is no-op and the relevant warning message is issued.
-
- \sa setRecipe(), start()
-*/
-TaskTree::TaskTree()
- : d(new TaskTreePrivate(this))
-{}
-
-/*!
- \overload
-
- Constructs a task tree with a given \a recipe. After the task tree is started,
- it executes the tasks contained inside the \a recipe and
- handles finished tasks according to the passed description.
-
- \sa setRecipe(), start()
-*/
-TaskTree::TaskTree(const Group &recipe) : TaskTree()
-{
- setRecipe(recipe);
-}
-
-/*!
- Destroys the task tree.
-
- When the task tree is running while being destructed, it cancels all the running tasks
- immediately. In this case, no handlers are called, not even the groups' and
- tasks' done handlers or onStorageDone() handlers. The task tree also doesn't emit any
- signals from the destructor, not even done() or progressValueChanged() signals.
- This behavior may always be relied on.
- It is completely safe to destruct the running task tree.
-
- It's a usual pattern to destruct the running task tree.
- It's guaranteed that the destruction will run quickly, without having to wait for
- the currently running tasks to finish, provided that the used tasks implement
- their destructors in a non-blocking way.
-
- \note Do not call the destructor directly from any of the running task's handlers
- or task tree's signals. In these cases, use \l deleteLater() instead.
-
- \sa cancel()
-*/
-TaskTree::~TaskTree()
-{
- QT_ASSERT(!d->m_guard.isLocked(), qWarning("Deleting TaskTree instance directly from "
- "one of its handlers will lead to a crash!"));
- // TODO: delete storages explicitly here?
- delete d;
-}
-
-/*!
- Sets a given \a recipe for the task tree. After the task tree is started,
- it executes the tasks contained inside the \a recipe and
- handles finished tasks according to the passed description.
-
- \note When called for a running task tree, the call is ignored.
-
- \sa TaskTree(const Tasking::Group &recipe), start()
-*/
-void TaskTree::setRecipe(const Group &recipe)
-{
- QT_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return);
- QT_ASSERT(!d->m_guard.isLocked(), qWarning("The setRecipe() is called from one of the"
- "TaskTree handlers, ignoring..."); return);
- // TODO: Should we clear the m_storageHandlers, too?
- d->m_storages.clear();
- d->m_root.emplace(d, recipe);
-}
-
-/*!
- Starts the task tree.
-
- Use setRecipe() or the constructor to set the declarative description according to which
- the task tree will execute the contained tasks and handle finished tasks.
-
- When the task tree is empty, that is, constructed with a default constructor,
- a call to \c start() is no-op and the relevant warning message is issued.
-
- Otherwise, when the task tree is already running, a call to \e start() is ignored and the
- relevant warning message is issued.
-
- Otherwise, the task tree is started.
-
- The started task tree may finish synchronously,
- for example when the main group's start handler returns SetupResult::StopWithError.
- For this reason, the connection to the done signal should be established before calling
- \c start(). Use isRunning() in order to detect whether the task tree is still running
- after a call to \c start().
-
- The task tree implementation relies on the running event loop.
- Make sure you have a QEventLoop or QCoreApplication or one of its
- subclasses running (or about to be run) when calling this method.
-
- \sa TaskTree(const Tasking::Group &), setRecipe(), isRunning(), cancel()
-*/
-void TaskTree::start()
-{
- QT_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return);
- QT_ASSERT(!d->m_guard.isLocked(), qWarning("The start() is called from one of the"
- "TaskTree handlers, ignoring..."); return);
- d->start();
-}
-
-/*!
- \fn void TaskTree::started()
-
- This signal is emitted when the task tree is started. The emission of this signal is
- followed synchronously by the progressValueChanged() signal with an initial \c 0 value.
-
- \sa start(), done()
-*/
-
-/*!
- \fn void TaskTree::done(DoneWith result)
-
- This signal is emitted when the task tree finished, passing the final \a result
- of the execution. The task tree neither calls any handler,
- nor emits any signal anymore after this signal was emitted.
-
- \note Do not delete the task tree directly from this signal's handler.
- Use deleteLater() instead.
-
- \sa started()
-*/
-
-/*!
- Cancels the execution of the running task tree.
-
- Cancels all the running tasks immediately.
- All running tasks finish with an error, invoking their error handlers.
- All running groups dispatch their handlers according to their workflow policies,
- invoking their done handlers. The storages' onStorageDone() handlers are invoked, too.
- The progressValueChanged() signals are also being sent.
- This behavior may always be relied on.
-
- The \c cancel() function is executed synchronously, so that after a call to \c cancel()
- all running tasks are finished and the tree is already canceled.
- It's guaranteed that \c cancel() will run quickly, without any blocking wait for
- the currently running tasks to finish, provided the used tasks implement their destructors
- in a non-blocking way.
-
- When the task tree is empty, that is, constructed with a default constructor,
- a call to \c cancel() is no-op and the relevant warning message is issued.
-
- Otherwise, when the task tree wasn't started, a call to \c cancel() is ignored.
-
- \note Do not call this function directly from any of the running task's handlers
- or task tree's signals.
-
- \sa ~TaskTree()
-*/
-void TaskTree::cancel()
-{
- QT_ASSERT(!d->m_guard.isLocked(), qWarning("The cancel() is called from one of the"
- "TaskTree handlers, ignoring..."); return);
- d->stop();
-}
-
-/*!
- Returns \c true if the task tree is currently running; otherwise returns \c false.
-
- \sa start(), cancel()
-*/
-bool TaskTree::isRunning() const
-{
- return bool(d->m_runtimeRoot);
-}
-
-/*!
- Executes a local event loop with QEventLoop::ExcludeUserInputEvents and starts the task tree.
-
- Returns DoneWith::Success if the task tree finished successfully;
- otherwise returns DoneWith::Error.
-
- \note Avoid using this method from the main thread. Use asynchronous start() instead.
- This method is to be used in non-main threads or in auto tests.
-
- \sa start()
-*/
-DoneWith TaskTree::runBlocking()
-{
- QPromise<void> dummy;
- dummy.start();
- return runBlocking(dummy.future());
-}
-
-/*!
- \overload runBlocking()
-
- The passed \a future is used for listening to the cancel event.
- When the task tree is canceled, this method cancels the passed \a future.
-*/
-DoneWith TaskTree::runBlocking(const QFuture<void> &future)
-{
- if (future.isCanceled())
- return DoneWith::Cancel;
-
- DoneWith doneWith = DoneWith::Cancel;
- QEventLoop loop;
- connect(this, &TaskTree::done, &loop, [&loop, &doneWith](DoneWith result) {
- doneWith = result;
- // Otherwise, the tasks from inside the running tree that were deleteLater()
- // will be leaked. Refer to the QObject::deleteLater() docs.
- QMetaObject::invokeMethod(&loop, [&loop] { loop.quit(); }, Qt::QueuedConnection);
- });
- QFutureWatcher<void> watcher;
- connect(&watcher, &QFutureWatcherBase::canceled, this, &TaskTree::cancel);
- watcher.setFuture(future);
-
- QTimer::singleShot(0, this, &TaskTree::start);
-
- loop.exec(QEventLoop::ExcludeUserInputEvents);
- if (doneWith == DoneWith::Cancel) {
- auto nonConstFuture = future;
- nonConstFuture.cancel();
- }
- return doneWith;
-}
-
-/*!
- Constructs a temporary task tree using the passed \a recipe and runs it blocking.
-
- Returns DoneWith::Success if the task tree finished successfully;
- otherwise returns DoneWith::Error.
-
- \note Avoid using this method from the main thread. Use asynchronous start() instead.
- This method is to be used in non-main threads or in auto tests.
-
- \sa start()
-*/
-DoneWith TaskTree::runBlocking(const Group &recipe)
-{
- QPromise<void> dummy;
- dummy.start();
- return TaskTree::runBlocking(recipe, dummy.future());
-}
-
-/*!
- \overload runBlocking(const Group &recipe)
-
- The passed \a future is used for listening to the cancel event.
- When the task tree is canceled, this method cancels the passed \a future.
-*/
-DoneWith TaskTree::runBlocking(const Group &recipe, const QFuture<void> &future)
-{
- TaskTree taskTree(recipe);
- return taskTree.runBlocking(future);
-}
-
-/*!
- Returns the current real count of asynchronous chains of invocations.
-
- The returned value indicates how many times the control returns to the caller's
- event loop while the task tree is running. Initially, this value is 0.
- If the execution of the task tree finishes fully synchronously, this value remains 0.
- If the task tree contains any asynchronous tasks that are successfully started during
- a call to start(), this value is bumped to 1 just before the call to start() finishes.
- Later, when any asynchronous task finishes and any possible continuations are started,
- this value is bumped again. The bumping continues until the task tree finishes.
- When the task tree emits the done() signal, the bumping stops.
- The asyncCountChanged() signal is emitted on every bump of this value.
-
- \sa asyncCountChanged()
-*/
-int TaskTree::asyncCount() const
-{
- return d->m_asyncCount;
-}
-
-/*!
- \fn void TaskTree::asyncCountChanged(int count)
-
- This signal is emitted when the running task tree is about to return control to the caller's
- event loop. When the task tree is started, this signal is emitted with \a count value of 0,
- and emitted later on every asyncCount() value bump with an updated \a count value.
- Every signal sent (except the initial one with the value of 0) guarantees that the task tree
- is still running asynchronously after the emission.
-
- \sa asyncCount()
-*/
-
-/*!
- Returns the number of asynchronous tasks contained in the stored recipe.
-
- \note The returned number doesn't include \l {Tasking::Sync} {Sync} tasks.
- \note Any task or group that was set up using withTimeout() increases the total number of
- tasks by \c 1.
-
- \sa setRecipe(), progressMaximum()
-*/
-int TaskTree::taskCount() const
-{
- return d->m_root ? d->m_root->taskCount() : 0;
-}
-
-/*!
- \fn void TaskTree::progressValueChanged(int value)
-
- This signal is emitted when the running task tree finished, canceled, or skipped some tasks.
- The \a value gives the current total number of finished, canceled or skipped tasks.
- When the task tree is started, and after the started() signal was emitted,
- this signal is emitted with an initial \a value of \c 0.
- When the task tree is about to finish, and before the done() signal is emitted,
- this signal is emitted with the final \a value of progressMaximum().
-
- \sa progressValue(), progressMaximum()
-*/
-
-/*!
- \fn int TaskTree::progressMaximum() const
-
- Returns the maximum progressValue().
-
- \note Currently, it's the same as taskCount(). This might change in the future.
-
- \sa progressValue()
-*/
-
-/*!
- Returns the current progress value, which is between the \c 0 and progressMaximum().
-
- The returned number indicates how many tasks have been already finished, canceled, or skipped
- while the task tree is running.
- When the task tree is started, this number is set to \c 0.
- When the task tree is finished, this number always equals progressMaximum().
-
- \sa progressMaximum(), progressValueChanged()
-*/
-int TaskTree::progressValue() const
-{
- return d->m_progressValue;
-}
-
-/*!
- \fn template <typename StorageStruct, typename Handler> void TaskTree::onStorageSetup(const Storage<StorageStruct> &storage, Handler &&handler)
-
- Installs a storage setup \a handler for the \a storage to pass the initial data
- dynamically to the running task tree.
-
- The \c StorageHandler takes a \e reference to the \c StorageStruct instance:
-
- \code
- static void save(const QString &fileName, const QByteArray &array) { ... }
-
- Storage<QByteArray> storage;
-
- const auto onSaverSetup = [storage](ConcurrentCall<QByteArray> &concurrent) {
- concurrent.setConcurrentCallData(&save, "foo.txt", *storage);
- };
-
- const Group root {
- storage,
- ConcurrentCallTask(onSaverSetup)
- };
-
- TaskTree taskTree(root);
- auto initStorage = [](QByteArray &storage){
- storage = "initial content";
- };
- taskTree.onStorageSetup(storage, initStorage);
- taskTree.start();
- \endcode
-
- When the running task tree enters a Group where the \a storage is placed in,
- it creates a \c StorageStruct instance, ready to be used inside this group.
- Just after the \c StorageStruct instance is created, and before any handler of this group
- is called, the task tree invokes the passed \a handler. This enables setting up
- initial content for the given storage dynamically. Later, when any group's handler is invoked,
- the task tree activates the created and initialized storage, so that it's available inside
- any group's handler.
-
- \sa onStorageDone()
-*/
-
-/*!
- \fn template <typename StorageStruct, typename Handler> void TaskTree::onStorageDone(const Storage<StorageStruct> &storage, Handler &&handler)
-
- Installs a storage done \a handler for the \a storage to retrieve the final data
- dynamically from the running task tree.
-
- The \c StorageHandler takes a \c const \e reference to the \c StorageStruct instance:
-
- \code
- static QByteArray load(const QString &fileName) { ... }
-
- Storage<QByteArray> storage;
-
- const auto onLoaderSetup = [](ConcurrentCall<QByteArray> &concurrent) {
- concurrent.setConcurrentCallData(&load, "foo.txt");
- };
- const auto onLoaderDone = [storage](const ConcurrentCall<QByteArray> &concurrent) {
- *storage = concurrent.result();
- };
-
- const Group root {
- storage,
- ConcurrentCallTask(onLoaderSetup, onLoaderDone, CallDoneIf::Success)
- };
-
- TaskTree taskTree(root);
- auto collectStorage = [](const QByteArray &storage){
- qDebug() << "final content" << storage;
- };
- taskTree.onStorageDone(storage, collectStorage);
- taskTree.start();
- \endcode
-
- When the running task tree is about to leave a Group where the \a storage is placed in,
- it destructs a \c StorageStruct instance.
- Just before the \c StorageStruct instance is destructed, and after all possible handlers from
- this group were called, the task tree invokes the passed \a handler. This enables reading
- the final content of the given storage dynamically and processing it further outside of
- the task tree.
-
- This handler is called also when the running tree is canceled. However, it's not called
- when the running tree is destructed.
-
- \sa onStorageSetup()
-*/
-
-void TaskTree::setupStorageHandler(const StorageBase &storage,
- const StorageBase::StorageHandler &setupHandler,
- const StorageBase::StorageHandler &doneHandler)
-{
- auto it = d->m_storageHandlers.find(storage);
- if (it == d->m_storageHandlers.end()) {
- d->m_storageHandlers.insert(storage, {setupHandler, doneHandler});
- return;
- }
- if (setupHandler) {
- QT_ASSERT(!it->m_setupHandler,
- qWarning("The storage has its setup handler defined, overriding..."));
- it->m_setupHandler = setupHandler;
- }
- if (doneHandler) {
- QT_ASSERT(!it->m_doneHandler,
- qWarning("The storage has its done handler defined, overriding..."));
- it->m_doneHandler = doneHandler;
- }
-}
-
-TaskTreeTaskAdapter::TaskTreeTaskAdapter()
-{
- connect(task(), &TaskTree::done, this,
- [this](DoneWith result) { emit done(toDoneResult(result)); });
-}
-
-void TaskTreeTaskAdapter::start()
-{
- task()->start();
-}
-
-using TimeoutCallback = std::function<void()>;
-
-struct TimerData
-{
- system_clock::time_point m_deadline;
- QPointer<QObject> m_context;
- TimeoutCallback m_callback;
-};
-
-struct TimerThreadData
-{
- Q_DISABLE_COPY_MOVE(TimerThreadData)
-
- TimerThreadData() = default; // defult constructor is required for initializing with {} since C++20 by Mingw 11.20
- QHash<int, TimerData> m_timerIdToTimerData = {};
- QMap<system_clock::time_point, QList<int>> m_deadlineToTimerId = {};
- int m_timerIdCounter = 0;
-};
-
-// Please note the thread_local keyword below guarantees a separate instance per thread.
-static thread_local TimerThreadData s_threadTimerData = {};
-
-static void removeTimerId(int timerId)
-{
- const auto it = s_threadTimerData.m_timerIdToTimerData.constFind(timerId);
- QT_ASSERT(it != s_threadTimerData.m_timerIdToTimerData.cend(),
- qWarning("Removing active timerId failed."); return);
-
- const system_clock::time_point deadline = it->m_deadline;
- s_threadTimerData.m_timerIdToTimerData.erase(it);
-
- QList<int> &ids = s_threadTimerData.m_deadlineToTimerId[deadline];
- const int removedCount = ids.removeAll(timerId);
- QT_ASSERT(removedCount == 1, qWarning("Removing active timerId failed."); return);
- if (ids.isEmpty())
- s_threadTimerData.m_deadlineToTimerId.remove(deadline);
-}
-
-static void handleTimeout(int timerId)
-{
- const auto itData = s_threadTimerData.m_timerIdToTimerData.constFind(timerId);
- if (itData == s_threadTimerData.m_timerIdToTimerData.cend())
- return; // The timer was already activated.
-
- const auto deadline = itData->m_deadline;
- while (true) {
- auto itMap = s_threadTimerData.m_deadlineToTimerId.begin();
- if (itMap == s_threadTimerData.m_deadlineToTimerId.end())
- return;
-
- if (itMap.key() > deadline)
- return;
-
- std::optional<TimerData> timerData;
- QList<int> &idList = *itMap;
- if (!idList.isEmpty()) {
- const int first = idList.first();
- idList.removeFirst();
-
- const auto it = s_threadTimerData.m_timerIdToTimerData.constFind(first);
- if (it != s_threadTimerData.m_timerIdToTimerData.cend()) {
- timerData = it.value();
- s_threadTimerData.m_timerIdToTimerData.erase(it);
- } else {
- QT_CHECK(false);
- }
- } else {
- QT_CHECK(false);
- }
-
- if (idList.isEmpty())
- s_threadTimerData.m_deadlineToTimerId.erase(itMap);
- if (timerData && timerData->m_context)
- timerData->m_callback();
- }
-}
-
-static int scheduleTimeout(milliseconds timeout, QObject *context, const TimeoutCallback &callback)
-{
- const int timerId = ++s_threadTimerData.m_timerIdCounter;
- const system_clock::time_point deadline = system_clock::now() + timeout;
- QTimer::singleShot(timeout, context, [timerId] { handleTimeout(timerId); });
- s_threadTimerData.m_timerIdToTimerData.emplace(timerId, TimerData{deadline, context, callback});
- s_threadTimerData.m_deadlineToTimerId[deadline].append(timerId);
- return timerId;
-}
-
-TimeoutTaskAdapter::TimeoutTaskAdapter()
-{
- *task() = milliseconds::zero();
-}
-
-TimeoutTaskAdapter::~TimeoutTaskAdapter()
-{
- if (m_timerId)
- removeTimerId(*m_timerId);
-}
-
-void TimeoutTaskAdapter::start()
-{
- m_timerId = scheduleTimeout(*task(), this, [this] {
- m_timerId.reset();
- emit done(DoneResult::Success);
- });
-}
-
-ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout, DoneResult result)
-{
- return TimeoutTask([timeout](std::chrono::milliseconds &t) { t = timeout; }, result);
-}
-
-/*!
- \typealias Tasking::TaskTreeTask
-
- Type alias for the CustomTask, to be used inside recipes, associated with the TaskTree task.
-*/
-
-/*!
- \typealias Tasking::TimeoutTask
-
- Type alias for the CustomTask, to be used inside recipes, associated with the
- \c std::chrono::milliseconds type. \c std::chrono::milliseconds is used to set up the
- timeout duration. The default timeout is \c std::chrono::milliseconds::zero(), that is,
- the TimeoutTask finishes as soon as the control returns to the running event loop.
-
- Example usage:
-
- \code
- using namespace std::chrono;
- using namespace std::chrono_literals;
-
- const auto onSetup = [](milliseconds &timeout) { timeout = 1000ms; }
- const auto onDone = [] { qDebug() << "Timed out."; }
-
- const Group root {
- Timeout(onSetup, onDone)
- };
- \endcode
-*/
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
diff --git a/src/assets/downloader/tasking/tasktree.h b/src/assets/downloader/tasking/tasktree.h
deleted file mode 100644
index d46bd8eee3e..00000000000
--- a/src/assets/downloader/tasking/tasktree.h
+++ /dev/null
@@ -1,757 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_TASKTREE_H
-#define TASKING_TASKTREE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasking_global.h"
-
-#include <QtCore/QList>
-#include <QtCore/QObject>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-template <class T>
-class QFuture;
-
-namespace Tasking {
-
-class Do;
-class For;
-class Group;
-class GroupItem;
-using GroupItems = QList<GroupItem>;
-
-Q_NAMESPACE_EXPORT(TASKING_EXPORT)
-
-// WorkflowPolicy:
-// 1. When all children finished with success -> report success, otherwise:
-// a) Report error on first error and stop executing other children (including their subtree).
-// b) On first error - continue executing all children and report error afterwards.
-// 2. When all children finished with error -> report error, otherwise:
-// a) Report success on first success and stop executing other children (including their subtree).
-// b) On first success - continue executing all children and report success afterwards.
-// 3. Stops on first finished child. In sequential mode it will never run other children then the first one.
-// Useful only in parallel mode.
-// 4. Always run all children, let them finish, ignore their results and report success afterwards.
-// 5. Always run all children, let them finish, ignore their results and report error afterwards.
-
-enum class WorkflowPolicy
-{
- StopOnError, // 1a - Reports error on first child error, otherwise success (if all children were success).
- ContinueOnError, // 1b - The same, but children execution continues. Reports success when no children.
- StopOnSuccess, // 2a - Reports success on first child success, otherwise error (if all children were error).
- ContinueOnSuccess, // 2b - The same, but children execution continues. Reports error when no children.
- StopOnSuccessOrError, // 3 - Stops on first finished child and report its result.
- FinishAllAndSuccess, // 4 - Reports success after all children finished.
- FinishAllAndError // 5 - Reports error after all children finished.
-};
-Q_ENUM_NS(WorkflowPolicy)
-
-enum class SetupResult
-{
- Continue,
- StopWithSuccess,
- StopWithError
-};
-Q_ENUM_NS(SetupResult)
-
-enum class DoneResult
-{
- Success,
- Error
-};
-Q_ENUM_NS(DoneResult)
-
-enum class DoneWith
-{
- Success,
- Error,
- Cancel
-};
-Q_ENUM_NS(DoneWith)
-
-enum class CallDoneIf
-{
- SuccessOrError,
- Success,
- Error
-};
-Q_ENUM_NS(CallDoneIf)
-
-TASKING_EXPORT DoneResult toDoneResult(bool success);
-
-class LoopData;
-class StorageData;
-class TaskTreePrivate;
-
-class TASKING_EXPORT TaskInterface : public QObject
-{
- Q_OBJECT
-
-Q_SIGNALS:
- void done(DoneResult result);
-
-private:
- template <typename Task, typename Deleter> friend class TaskAdapter;
- friend class TaskTreePrivate;
- TaskInterface() = default;
-#ifdef Q_QDOC
-protected:
-#endif
- virtual void start() = 0;
-};
-
-class TASKING_EXPORT Loop
-{
-public:
- using Condition = std::function<bool(int)>; // Takes iteration, called prior to each iteration.
- using ValueGetter = std::function<const void *(int)>; // Takes iteration, returns ptr to ref.
-
- int iteration() const;
-
-protected:
- Loop(); // LoopForever
- Loop(int count, const ValueGetter &valueGetter = {}); // LoopRepeat, LoopList
- Loop(const Condition &condition); // LoopUntil
-
- const void *valuePtr() const;
-
-private:
- friend class ExecutionContextActivator;
- friend class TaskTreePrivate;
- std::shared_ptr<LoopData> m_loopData;
-};
-
-class TASKING_EXPORT LoopForever final : public Loop
-{
-public:
- LoopForever() : Loop() {}
-};
-
-class TASKING_EXPORT LoopRepeat final : public Loop
-{
-public:
- LoopRepeat(int count) : Loop(count) {}
-};
-
-class TASKING_EXPORT LoopUntil final : public Loop
-{
-public:
- LoopUntil(const Condition &condition) : Loop(condition) {}
-};
-
-template <typename T>
-class LoopList final : public Loop
-{
-public:
- LoopList(const QList<T> &list) : Loop(list.size(), [list](int i) { return &list.at(i); }) {}
- const T *operator->() const { return static_cast<const T *>(valuePtr()); }
- const T &operator*() const { return *static_cast<const T *>(valuePtr()); }
-};
-
-class TASKING_EXPORT StorageBase
-{
-private:
- using StorageConstructor = std::function<void *(void)>;
- using StorageDestructor = std::function<void(void *)>;
- using StorageHandler = std::function<void(void *)>;
-
- StorageBase(const StorageConstructor &ctor, const StorageDestructor &dtor);
-
- void *activeStorageVoid() const;
-
- friend bool operator==(const StorageBase &first, const StorageBase &second)
- { return first.m_storageData == second.m_storageData; }
-
- friend bool operator!=(const StorageBase &first, const StorageBase &second)
- { return first.m_storageData != second.m_storageData; }
-
- friend size_t qHash(const StorageBase &storage, uint seed = 0)
- { return size_t(storage.m_storageData.get()) ^ seed; }
-
- std::shared_ptr<StorageData> m_storageData;
-
- template <typename StorageStruct> friend class Storage;
- friend class ExecutionContextActivator;
- friend class StorageData;
- friend class RuntimeContainer;
- friend class TaskTree;
- friend class TaskTreePrivate;
-};
-
-template <typename StorageStruct>
-class Storage final : public StorageBase
-{
-public:
- Storage() : StorageBase(Storage::ctor(), Storage::dtor()) {}
-#if __cplusplus >= 201803L // C++20: Allow pack expansion in lambda init-capture.
- template <typename ...Args>
- Storage(const Args &...args)
- : StorageBase([...args = args] { return new StorageStruct(args...); }, Storage::dtor()) {}
-#else // C++17
- template <typename ...Args>
- Storage(const Args &...args)
- : StorageBase([argsTuple = std::tuple(args...)] {
- return std::apply([](const Args &...arguments) { return new StorageStruct(arguments...); }, argsTuple);
- }, Storage::dtor()) {}
-#endif
- StorageStruct &operator*() const noexcept { return *activeStorage(); }
- StorageStruct *operator->() const noexcept { return activeStorage(); }
- StorageStruct *activeStorage() const {
- return static_cast<StorageStruct *>(activeStorageVoid());
- }
-
-private:
- static StorageConstructor ctor() { return [] { return new StorageStruct(); }; }
- static StorageDestructor dtor() {
- return [](void *storage) { delete static_cast<StorageStruct *>(storage); };
- }
-};
-
-class TASKING_EXPORT GroupItem
-{
-public:
- // Called when group entered, after group's storages are created
- using GroupSetupHandler = std::function<SetupResult()>;
- // Called when group done, before group's storages are deleted
- using GroupDoneHandler = std::function<DoneResult(DoneWith)>;
-
- template <typename StorageStruct>
- GroupItem(const Storage<StorageStruct> &storage)
- : m_type(Type::Storage)
- , m_storageList{storage} {}
-
- // TODO: Add tests.
- GroupItem(const GroupItems &children) : m_type(Type::List) { addChildren(children); }
- GroupItem(std::initializer_list<GroupItem> children) : m_type(Type::List) { addChildren(children); }
-
-protected:
- GroupItem(const Loop &loop) : GroupItem(GroupData{{}, {}, {}, loop}) {}
- // Internal, provided by CustomTask
- using InterfaceCreateHandler = std::function<TaskInterface *(void)>;
- // Called prior to task start, just after createHandler
- using InterfaceSetupHandler = std::function<SetupResult(TaskInterface &)>;
- // Called on task done, just before deleteLater
- using InterfaceDoneHandler = std::function<DoneResult(const TaskInterface &, DoneWith)>;
-
- struct TaskHandler {
- InterfaceCreateHandler m_createHandler;
- InterfaceSetupHandler m_setupHandler = {};
- InterfaceDoneHandler m_doneHandler = {};
- CallDoneIf m_callDoneIf = CallDoneIf::SuccessOrError;
- };
-
- struct GroupHandler {
- GroupSetupHandler m_setupHandler;
- GroupDoneHandler m_doneHandler = {};
- CallDoneIf m_callDoneIf = CallDoneIf::SuccessOrError;
- };
-
- struct GroupData {
- GroupHandler m_groupHandler = {};
- std::optional<int> m_parallelLimit = {};
- std::optional<WorkflowPolicy> m_workflowPolicy = {};
- std::optional<Loop> m_loop = {};
- };
-
- enum class Type {
- List,
- Group,
- GroupData,
- Storage,
- TaskHandler
- };
-
- GroupItem() = default;
- GroupItem(Type type) : m_type(type) { }
- GroupItem(const GroupData &data)
- : m_type(Type::GroupData)
- , m_groupData(data) {}
- GroupItem(const TaskHandler &handler)
- : m_type(Type::TaskHandler)
- , m_taskHandler(handler) {}
- void addChildren(const GroupItems &children);
-
- static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); }
-
- // Checks if Function may be invoked with Args and if Function's return type is Result.
- template <typename Result, typename Function, typename ...Args,
- typename DecayedFunction = std::decay_t<Function>>
- static constexpr bool isInvocable()
- {
- // Note, that std::is_invocable_r_v doesn't check Result type properly.
- if constexpr (std::is_invocable_r_v<Result, DecayedFunction, Args...>)
- return std::is_same_v<Result, std::invoke_result_t<DecayedFunction, Args...>>;
- return false;
- }
-
-private:
- TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
- friend class ContainerNode;
- friend class TaskNode;
- friend class TaskTreePrivate;
- friend class ParallelLimitFunctor;
- friend class WorkflowPolicyFunctor;
- Type m_type = Type::Group;
- GroupItems m_children;
- GroupData m_groupData;
- QList<StorageBase> m_storageList;
- TaskHandler m_taskHandler;
-};
-
-class TASKING_EXPORT ExecutableItem : public GroupItem
-{
-public:
- Group withTimeout(std::chrono::milliseconds timeout,
- const std::function<void()> &handler = {}) const;
- Group withLog(const QString &logName) const;
- template <typename SenderSignalPairGetter>
- Group withCancel(SenderSignalPairGetter &&getter, std::initializer_list<GroupItem> postCancelRecipe = {}) const;
- template <typename SenderSignalPairGetter>
- Group withAccept(SenderSignalPairGetter &&getter) const;
-
-protected:
- ExecutableItem() = default;
- ExecutableItem(const TaskHandler &handler) : GroupItem(handler) {}
-
-private:
- TASKING_EXPORT friend Group operator!(const ExecutableItem &item);
- TASKING_EXPORT friend Group operator&&(const ExecutableItem &first,
- const ExecutableItem &second);
- TASKING_EXPORT friend Group operator||(const ExecutableItem &first,
- const ExecutableItem &second);
- TASKING_EXPORT friend Group operator&&(const ExecutableItem &item, DoneResult result);
- TASKING_EXPORT friend Group operator||(const ExecutableItem &item, DoneResult result);
-
- Group withCancelImpl(
- const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper,
- const GroupItems &postCancelRecipe) const;
- Group withAcceptImpl(
- const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const;
-};
-
-class TASKING_EXPORT Group : public ExecutableItem
-{
-public:
- Group(const GroupItems &children) { addChildren(children); }
- Group(std::initializer_list<GroupItem> children) { addChildren(children); }
-
- // GroupData related:
- template <typename Handler>
- static GroupItem onGroupSetup(Handler &&handler) {
- return groupHandler({wrapGroupSetup(std::forward<Handler>(handler))});
- }
- template <typename Handler>
- static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError) {
- return groupHandler({{}, wrapGroupDone(std::forward<Handler>(handler)), callDoneIf});
- }
-
-private:
- template <typename Handler>
- static GroupSetupHandler wrapGroupSetup(Handler &&handler)
- {
- // R, V stands for: Setup[R]esult, [V]oid
- static constexpr bool isR = isInvocable<SetupResult, Handler>();
- static constexpr bool isV = isInvocable<void, Handler>();
- static_assert(isR || isV,
- "Group setup handler needs to take no arguments and has to return void or SetupResult. "
- "The passed handler doesn't fulfill these requirements.");
- return [handler = std::move(handler)] {
- if constexpr (isR)
- return std::invoke(handler);
- std::invoke(handler);
- return SetupResult::Continue;
- };
- }
- template <typename Handler>
- static GroupDoneHandler wrapGroupDone(Handler &&handler)
- {
- static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<Handler>, DoneResult>;
- // R, B, V, D stands for: Done[R]esult, [B]ool, [V]oid, [D]oneWith
- static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
- static constexpr bool isR = isInvocable<DoneResult, Handler>();
- static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
- static constexpr bool isB = isInvocable<bool, Handler>();
- static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
- static constexpr bool isV = isInvocable<void, Handler>();
- static_assert(isDoneResultType || isRD || isR || isBD || isB || isVD || isV,
- "Group done handler needs to take (DoneWith) or (void) as an argument and has to "
- "return void, bool or DoneResult. Alternatively, it may be of DoneResult type. "
- "The passed handler doesn't fulfill these requirements.");
- return [handler = std::move(handler)](DoneWith result) {
- if constexpr (isDoneResultType)
- return handler;
- if constexpr (isRD)
- return std::invoke(handler, result);
- if constexpr (isR)
- return std::invoke(handler);
- if constexpr (isBD)
- return toDoneResult(std::invoke(handler, result));
- if constexpr (isB)
- return toDoneResult(std::invoke(handler));
- if constexpr (isVD)
- std::invoke(handler, result);
- else if constexpr (isV)
- std::invoke(handler);
- return toDoneResult(result == DoneWith::Success);
- };
- }
-};
-
-template <typename SenderSignalPairGetter>
-Group ExecutableItem::withCancel(SenderSignalPairGetter &&getter,
- std::initializer_list<GroupItem> postCancelRecipe) const
-{
- const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
- const auto senderSignalPair = getter();
- QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
- trigger();
- }, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
- };
- return withCancelImpl(connectWrapper, postCancelRecipe);
-}
-
-template <typename SenderSignalPairGetter>
-Group ExecutableItem::withAccept(SenderSignalPairGetter &&getter) const
-{
- const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
- const auto senderSignalPair = getter();
- QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
- trigger();
- }, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
- };
- return withAcceptImpl(connectWrapper);
-}
-
-template <typename Handler>
-static GroupItem onGroupSetup(Handler &&handler)
-{
- return Group::onGroupSetup(std::forward<Handler>(handler));
-}
-
-template <typename Handler>
-static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
-{
- return Group::onGroupDone(std::forward<Handler>(handler), callDoneIf);
-}
-
-// Default: 1 (sequential). 0 means unlimited (parallel).
-TASKING_EXPORT GroupItem parallelLimit(int limit);
-
-// Default: WorkflowPolicy::StopOnError.
-TASKING_EXPORT GroupItem workflowPolicy(WorkflowPolicy policy);
-
-TASKING_EXPORT extern const GroupItem sequential;
-TASKING_EXPORT extern const GroupItem parallel;
-TASKING_EXPORT extern const GroupItem parallelIdealThreadCountLimit;
-
-TASKING_EXPORT extern const GroupItem stopOnError;
-TASKING_EXPORT extern const GroupItem continueOnError;
-TASKING_EXPORT extern const GroupItem stopOnSuccess;
-TASKING_EXPORT extern const GroupItem continueOnSuccess;
-TASKING_EXPORT extern const GroupItem stopOnSuccessOrError;
-TASKING_EXPORT extern const GroupItem finishAllAndSuccess;
-TASKING_EXPORT extern const GroupItem finishAllAndError;
-
-TASKING_EXPORT extern const GroupItem nullItem;
-TASKING_EXPORT extern const ExecutableItem successItem;
-TASKING_EXPORT extern const ExecutableItem errorItem;
-
-class TASKING_EXPORT For final
-{
-public:
- explicit For(const Loop &loop) : m_loop(loop) {}
-
-private:
- TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
-
- Loop m_loop;
-};
-
-class When;
-
-class TASKING_EXPORT Do final
-{
-public:
- explicit Do(const GroupItems &children) : m_children(children) {}
- explicit Do(std::initializer_list<GroupItem> children) : m_children(children) {}
-
-private:
- TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
- TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem);
-
- GroupItem m_children;
-};
-
-class TASKING_EXPORT Forever final : public ExecutableItem
-{
-public:
- explicit Forever(const GroupItems &children)
- { addChildren({ For (LoopForever()) >> Do { children } } ); }
- explicit Forever(std::initializer_list<GroupItem> children)
- { addChildren({ For (LoopForever()) >> Do { children } } ); }
-};
-
-// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
-class TASKING_EXPORT Sync final : public ExecutableItem
-{
-public:
- template <typename Handler>
- Sync(Handler &&handler) {
- addChildren({ onGroupDone(wrapHandler(std::forward<Handler>(handler))) });
- }
-
-private:
- template <typename Handler>
- static auto wrapHandler(Handler &&handler) {
- // R, B, V stands for: Done[R]esult, [B]ool, [V]oid
- static constexpr bool isR = isInvocable<DoneResult, Handler>();
- static constexpr bool isB = isInvocable<bool, Handler>();
- static constexpr bool isV = isInvocable<void, Handler>();
- static_assert(isR || isB || isV,
- "Sync handler needs to take no arguments and has to return void, bool or DoneResult. "
- "The passed handler doesn't fulfill these requirements.");
- return handler;
- }
-};
-
-template <typename Task, typename Deleter = std::default_delete<Task>>
-class TaskAdapter : public TaskInterface
-{
-protected:
- TaskAdapter() : m_task(new Task) {}
- Task *task() { return m_task.get(); }
- const Task *task() const { return m_task.get(); }
-
-private:
- using TaskType = Task;
- using DeleterType = Deleter;
- template <typename Adapter> friend class CustomTask;
- std::unique_ptr<Task, Deleter> m_task;
-};
-
-template <typename Adapter>
-class CustomTask final : public ExecutableItem
-{
-public:
- using Task = typename Adapter::TaskType;
- using Deleter = typename Adapter::DeleterType;
- static_assert(std::is_base_of_v<TaskAdapter<Task, Deleter>, Adapter>,
- "The Adapter type for the CustomTask<Adapter> needs to be derived from "
- "TaskAdapter<Task>.");
- using TaskSetupHandler = std::function<SetupResult(Task &)>;
- using TaskDoneHandler = std::function<DoneResult(const Task &, DoneWith)>;
-
- template <typename SetupHandler = TaskSetupHandler, typename DoneHandler = TaskDoneHandler>
- CustomTask(SetupHandler &&setup = TaskSetupHandler(), DoneHandler &&done = TaskDoneHandler(),
- CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
- : ExecutableItem({&createAdapter, wrapSetup(std::forward<SetupHandler>(setup)),
- wrapDone(std::forward<DoneHandler>(done)), callDoneIf})
- {}
-
-private:
- static Adapter *createAdapter() { return new Adapter; }
-
- template <typename Handler>
- static InterfaceSetupHandler wrapSetup(Handler &&handler) {
- if constexpr (std::is_same_v<Handler, TaskSetupHandler>)
- return {}; // When user passed {} for the setup handler.
- // R, V stands for: Setup[R]esult, [V]oid
- static constexpr bool isR = isInvocable<SetupResult, Handler, Task &>();
- static constexpr bool isV = isInvocable<void, Handler, Task &>();
- static_assert(isR || isV,
- "Task setup handler needs to take (Task &) as an argument and has to return void or "
- "SetupResult. The passed handler doesn't fulfill these requirements.");
- return [handler = std::move(handler)](TaskInterface &taskInterface) {
- Adapter &adapter = static_cast<Adapter &>(taskInterface);
- if constexpr (isR)
- return std::invoke(handler, *adapter.task());
- std::invoke(handler, *adapter.task());
- return SetupResult::Continue;
- };
- }
-
- template <typename Handler>
- static InterfaceDoneHandler wrapDone(Handler &&handler) {
- if constexpr (std::is_same_v<Handler, TaskDoneHandler>)
- return {}; // User passed {} for the done handler.
- static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<Handler>, DoneResult>;
- // R, B, V, T, D stands for: Done[R]esult, [B]ool, [V]oid, [T]ask, [D]oneWith
- static constexpr bool isRTD = isInvocable<DoneResult, Handler, const Task &, DoneWith>();
- static constexpr bool isRT = isInvocable<DoneResult, Handler, const Task &>();
- static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
- static constexpr bool isR = isInvocable<DoneResult, Handler>();
- static constexpr bool isBTD = isInvocable<bool, Handler, const Task &, DoneWith>();
- static constexpr bool isBT = isInvocable<bool, Handler, const Task &>();
- static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
- static constexpr bool isB = isInvocable<bool, Handler>();
- static constexpr bool isVTD = isInvocable<void, Handler, const Task &, DoneWith>();
- static constexpr bool isVT = isInvocable<void, Handler, const Task &>();
- static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
- static constexpr bool isV = isInvocable<void, Handler>();
- static_assert(isDoneResultType || isRTD || isRT || isRD || isR
- || isBTD || isBT || isBD || isB
- || isVTD || isVT || isVD || isV,
- "Task done handler needs to take (const Task &, DoneWith), (const Task &), "
- "(DoneWith) or (void) as arguments and has to return void, bool or DoneResult. "
- "Alternatively, it may be of DoneResult type. "
- "The passed handler doesn't fulfill these requirements.");
- return [handler = std::move(handler)](const TaskInterface &taskInterface, DoneWith result) {
- if constexpr (isDoneResultType)
- return handler;
- const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
- if constexpr (isRTD)
- return std::invoke(handler, *adapter.task(), result);
- if constexpr (isRT)
- return std::invoke(handler, *adapter.task());
- if constexpr (isRD)
- return std::invoke(handler, result);
- if constexpr (isR)
- return std::invoke(handler);
- if constexpr (isBTD)
- return toDoneResult(std::invoke(handler, *adapter.task(), result));
- if constexpr (isBT)
- return toDoneResult(std::invoke(handler, *adapter.task()));
- if constexpr (isBD)
- return toDoneResult(std::invoke(handler, result));
- if constexpr (isB)
- return toDoneResult(std::invoke(handler));
- if constexpr (isVTD)
- std::invoke(handler, *adapter.task(), result);
- else if constexpr (isVT)
- std::invoke(handler, *adapter.task());
- else if constexpr (isVD)
- std::invoke(handler, result);
- else if constexpr (isV)
- std::invoke(handler);
- return toDoneResult(result == DoneWith::Success);
- };
- }
-};
-
-template <typename Task>
-class SimpleTaskAdapter final : public TaskAdapter<Task>
-{
-public:
- SimpleTaskAdapter() { this->connect(this->task(), &Task::done, this, &TaskInterface::done); }
- void start() final { this->task()->start(); }
-};
-
-// A convenient helper, when:
-// 1. Task is derived from QObject.
-// 2. Task::start() method starts the task.
-// 3. Task::done(DoneResult) signal is emitted when the task is finished.
-template <typename Task>
-using SimpleCustomTask = CustomTask<SimpleTaskAdapter<Task>>;
-
-class TASKING_EXPORT TaskTree final : public QObject
-{
- Q_OBJECT
-
-public:
- TaskTree();
- TaskTree(const Group &recipe);
- ~TaskTree();
-
- void setRecipe(const Group &recipe);
-
- void start();
- void cancel();
- bool isRunning() const;
-
- // Helper methods. They execute a local event loop with ExcludeUserInputEvents.
- // The passed future is used for listening to the cancel event.
- // Don't use it in main thread. To be used in non-main threads or in auto tests.
- DoneWith runBlocking();
- DoneWith runBlocking(const QFuture<void> &future);
- static DoneWith runBlocking(const Group &recipe);
- static DoneWith runBlocking(const Group &recipe, const QFuture<void> &future);
-
- int asyncCount() const;
- int taskCount() const;
- int progressMaximum() const { return taskCount(); }
- int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded
-
- template <typename StorageStruct, typename Handler>
- void onStorageSetup(const Storage<StorageStruct> &storage, Handler &&handler) {
- static_assert(std::is_invocable_v<std::decay_t<Handler>, StorageStruct &>,
- "Storage setup handler needs to take (Storage &) as an argument. "
- "The passed handler doesn't fulfill this requirement.");
- setupStorageHandler(storage,
- wrapHandler<StorageStruct>(std::forward<Handler>(handler)), {});
- }
- template <typename StorageStruct, typename Handler>
- void onStorageDone(const Storage<StorageStruct> &storage, Handler &&handler) {
- static_assert(std::is_invocable_v<std::decay_t<Handler>, const StorageStruct &>,
- "Storage done handler needs to take (const Storage &) as an argument. "
- "The passed handler doesn't fulfill this requirement.");
- setupStorageHandler(storage, {},
- wrapHandler<const StorageStruct>(std::forward<Handler>(handler)));
- }
-
-Q_SIGNALS:
- void started();
- void done(DoneWith result);
- void asyncCountChanged(int count);
- void progressValueChanged(int value); // updated whenever task finished / skipped / stopped
-
-private:
- void setupStorageHandler(const StorageBase &storage,
- const StorageBase::StorageHandler &setupHandler,
- const StorageBase::StorageHandler &doneHandler);
- template <typename StorageStruct, typename Handler>
- StorageBase::StorageHandler wrapHandler(Handler &&handler) {
- return [handler](void *voidStruct) {
- auto *storageStruct = static_cast<StorageStruct *>(voidStruct);
- std::invoke(handler, *storageStruct);
- };
- }
-
- TaskTreePrivate *d;
-};
-
-class TASKING_EXPORT TaskTreeTaskAdapter : public TaskAdapter<TaskTree>
-{
-public:
- TaskTreeTaskAdapter();
-
-private:
- void start() final;
-};
-
-class TASKING_EXPORT TimeoutTaskAdapter : public TaskAdapter<std::chrono::milliseconds>
-{
-public:
- TimeoutTaskAdapter();
- ~TimeoutTaskAdapter();
-
-private:
- void start() final;
- std::optional<int> m_timerId;
-};
-
-using TaskTreeTask = CustomTask<TaskTreeTaskAdapter>;
-using TimeoutTask = CustomTask<TimeoutTaskAdapter>;
-
-TASKING_EXPORT ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout,
- DoneResult result = DoneResult::Error);
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
-
-#endif // TASKING_TASKTREE_H
diff --git a/src/assets/downloader/tasking/tasktreerunner.cpp b/src/assets/downloader/tasking/tasktreerunner.cpp
deleted file mode 100644
index 6ed642b1bfd..00000000000
--- a/src/assets/downloader/tasking/tasktreerunner.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#include "tasktreerunner.h"
-
-#include "tasktree.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-TaskTreeRunner::~TaskTreeRunner() = default;
-
-void TaskTreeRunner::start(const Group &recipe,
- const SetupHandler &setupHandler,
- const DoneHandler &doneHandler)
-{
- m_taskTree.reset(new TaskTree(recipe));
- connect(m_taskTree.get(), &TaskTree::done, this, [this, doneHandler](DoneWith result) {
- m_taskTree.release()->deleteLater();
- if (doneHandler)
- doneHandler(result);
- emit done(result);
- });
- if (setupHandler)
- setupHandler(m_taskTree.get());
- emit aboutToStart(m_taskTree.get());
- m_taskTree->start();
-}
-
-void TaskTreeRunner::cancel()
-{
- if (m_taskTree)
- m_taskTree->cancel();
-}
-
-void TaskTreeRunner::reset()
-{
- m_taskTree.reset();
-}
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
diff --git a/src/assets/downloader/tasking/tasktreerunner.h b/src/assets/downloader/tasking/tasktreerunner.h
deleted file mode 100644
index f91e7608113..00000000000
--- a/src/assets/downloader/tasking/tasktreerunner.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_TASKTREERUNNER_H
-#define TASKING_TASKTREERUNNER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasking_global.h"
-#include "tasktree.h"
-
-#include <QtCore/QObject>
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-class TASKING_EXPORT TaskTreeRunner : public QObject
-{
- Q_OBJECT
-
-public:
- using SetupHandler = std::function<void(TaskTree *)>;
- using DoneHandler = std::function<void(DoneWith)>;
-
- ~TaskTreeRunner();
-
- bool isRunning() const { return bool(m_taskTree); }
-
- // When task tree is running it resets the old task tree.
- void start(const Group &recipe,
- const SetupHandler &setupHandler = {},
- const DoneHandler &doneHandler = {});
-
- // When task tree is running it emits done(DoneWith::Cancel) synchronously.
- void cancel();
-
- // No done() signal is emitted.
- void reset();
-
-Q_SIGNALS:
- void aboutToStart(TaskTree *taskTree);
- void done(DoneWith result);
-
-private:
- std::unique_ptr<TaskTree> m_taskTree;
-};
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
-
-#endif // TASKING_TASKTREERUNNER_H
diff --git a/src/assets/downloader/tasking/tcpsocket.cpp b/src/assets/downloader/tasking/tcpsocket.cpp
deleted file mode 100644
index 19aab8fc714..00000000000
--- a/src/assets/downloader/tasking/tcpsocket.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#include "tcpsocket.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-void TcpSocket::start()
-{
- if (m_socket) {
- qWarning("The TcpSocket is already running. Ignoring the call to start().");
- return;
- }
- if (m_address.isNull()) {
- qWarning("Can't start the TcpSocket with invalid address. "
- "Stopping with an error.");
- m_error = QAbstractSocket::HostNotFoundError;
- emit done(DoneResult::Error);
- return;
- }
-
- m_socket.reset(new QTcpSocket);
- connect(m_socket.get(), &QAbstractSocket::errorOccurred, this,
- [this](QAbstractSocket::SocketError error) {
- m_error = error;
- m_socket->disconnect();
- emit done(DoneResult::Error);
- m_socket.release()->deleteLater();
- });
- connect(m_socket.get(), &QAbstractSocket::connected, this, [this] {
- if (!m_writeData.isEmpty())
- m_socket->write(m_writeData);
- emit started();
- });
- connect(m_socket.get(), &QAbstractSocket::disconnected, this, [this] {
- m_socket->disconnect();
- emit done(DoneResult::Success);
- m_socket.release()->deleteLater();
- });
-
- m_socket->connectToHost(m_address, m_port);
-}
-
-TcpSocket::~TcpSocket()
-{
- if (m_socket) {
- m_socket->disconnect();
- m_socket->abort();
- }
-}
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
diff --git a/src/assets/downloader/tasking/tcpsocket.h b/src/assets/downloader/tasking/tcpsocket.h
deleted file mode 100644
index ec0069bf130..00000000000
--- a/src/assets/downloader/tasking/tcpsocket.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2024 Jarek Kobus
-// Copyright (C) 2024 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
-
-#ifndef TASKING_TCPSOCKET_H
-#define TASKING_TCPSOCKET_H
-
-#include "tasking_global.h"
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "tasktree.h"
-
-#include <QtNetwork/QTcpSocket>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-
-namespace Tasking {
-
-// This class introduces the dependency to Qt::Network, otherwise Tasking namespace
-// is independent on Qt::Network.
-// Possibly, it could be placed inside Qt::Network library, as a wrapper around QTcpSocket.
-
-class TASKING_EXPORT TcpSocket final : public QObject
-{
- Q_OBJECT
-
-public:
- ~TcpSocket();
- void setAddress(const QHostAddress &address) { m_address = address; }
- void setPort(quint16 port) { m_port = port; }
- void setWriteData(const QByteArray &data) { m_writeData = data; }
- QTcpSocket *socket() const { return m_socket.get(); }
- void start();
-
-Q_SIGNALS:
- void started();
- void done(DoneResult result);
-
-private:
- QHostAddress m_address;
- quint16 m_port = 0;
- QByteArray m_writeData;
- std::unique_ptr<QTcpSocket> m_socket;
- QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError;
-};
-
-using TcpSocketTask = SimpleCustomTask<TcpSocket>;
-
-} // namespace Tasking
-
-QT_END_NAMESPACE
-
-#endif // TASKING_TCPSOCKET_H
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index 1147205b79f..c4a3480b2f9 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -1028,7 +1028,7 @@ qt_internal_extend_target(Core
qt_internal_extend_target(Core
CONDITION
- QT_FEATURE_timezone AND UNIX
+ QT_FEATURE_timezone AND UNIX AND NOT VXWORKS
AND NOT ANDROID AND NOT APPLE AND NOT QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_tz.cpp
@@ -1037,7 +1037,7 @@ qt_internal_extend_target(Core
qt_internal_extend_target(Core
CONDITION
QT_FEATURE_icu AND QT_FEATURE_timezone
- AND NOT UNIX AND NOT QT_FEATURE_timezone_tzdb
+ AND (VXWORKS OR NOT UNIX) AND NOT QT_FEATURE_timezone_tzdb
SOURCES
time/qtimezoneprivate_icu.cpp
)
diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake
index d951b85c147..edcfba0f6ce 100644
--- a/src/corelib/configure.cmake
+++ b/src/corelib/configure.cmake
@@ -1150,7 +1150,7 @@ qt_feature("timezone" PUBLIC
SECTION "Utilities"
LABEL "QTimeZone"
PURPOSE "Provides support for time-zone handling."
- CONDITION NOT WASM AND NOT VXWORKS
+ CONDITION NOT WASM
)
qt_feature("timezone_locale" PRIVATE
SECTION "Utilities"
diff --git a/src/corelib/doc/images/javaiterators1.svg b/src/corelib/doc/images/javaiterators1.svg
new file mode 100644
index 00000000000..468dbe5371c
--- /dev/null
+++ b/src/corelib/doc/images/javaiterators1.svg
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ width="340"
+ height="110"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+
+<style>
+ svg .box-style { stroke: black; fill: white }
+ svg .line-style { stroke: red; fill: none }
+ svg .text-style { font: 20px arial; fill: black }
+ svg .fill-style { stroke: none; fill: red }
+
+ [data-theme="dark"] svg .box-style { stroke: #f2f2f2; fill: black }
+ [data-theme="dark"] svg .line-style { stroke: red; fill: none }
+ [data-theme="dark"] svg .text-style { font: 20px arial; fill: #f2f2f2 }
+ [data-theme="dark"] svg .fill-style { stroke: none; fill: red }
+
+ [data-theme="light"] svg .box-style { stroke: black; fill: white }
+ [data-theme="light"] svg .line-style { stroke: red; fill: none }
+ [data-theme="light"] svg .text-style { font: 20px arial; fill: black }
+ [data-theme="light"] svg .fill-style { stroke: none; fill: red }
+</style>
+
+<g transform="translate(10.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">A</text>
+<path d="M 0,60 v 30" class="line-style" />
+<path d="M 0,60 l -5,10 l 10,0 z" class="fill-style" />
+</g>
+
+<g transform="translate(90.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">B</text>
+<path d="M 0,60 v 30" class="line-style" />
+<path d="M 0,60 l -5,10 l 10,0 z" class="fill-style" />
+</g>
+
+<g transform="translate(170.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">C</text>
+<path d="M 0,60 v 30" class="line-style" />
+<path d="M 0,60 l -5,10 l 10,0 z" class="fill-style" />
+</g>
+
+<g transform="translate(250.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">D</text>
+<path d="M 0,60 v 30" class="line-style" />
+<path d="M 0,60 l -5,10 l 10,0 z" class="fill-style" />
+</g>
+
+<g transform="translate(330.5, 10.5)">
+<path d="M 0,60 v 30" class="line-style" />
+<path d="M 0,60 l -5,10 l 10,0 z" class="fill-style" />
+</g>
+
+</svg>
diff --git a/src/corelib/doc/images/javaiterators2.svg b/src/corelib/doc/images/javaiterators2.svg
new file mode 100644
index 00000000000..df4c6b352a6
--- /dev/null
+++ b/src/corelib/doc/images/javaiterators2.svg
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.1"
+ width="340"
+ height="130"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+
+<style>
+ svg .box-style { stroke: black; fill: white }
+ svg .line-style { stroke: red; fill: none }
+ svg .fill-style { stroke: none; fill: red }
+ svg .np-line-style { stroke: blue; fill: none }
+ svg .np-fill-style { stroke: none; fill: blue }
+ svg .text-style { font: 20px arial; fill: black }
+ svg .small-text-style { font: 12px monospace; fill: black }
+
+ [data-theme="dark"] svg .box-style { stroke: #f2f2f2; fill: black }
+ [data-theme="dark"] svg .line-style { stroke: red; fill: none }
+ [data-theme="dark"] svg .fill-style { stroke: none; fill: red }
+ [data-theme="dark"] svg .np-line-style { stroke: #4080ff; fill: none; stroke-width: 1.5 }
+ [data-theme="dark"] svg .np-fill-style { stroke: none; fill: #4080ff }
+ [data-theme="dark"] svg .text-style { font: 20px arial; fill: #f2f2f2 }
+ [data-theme="dark"] svg .small-text-style { font: 12px monospace; fill: #f2f2f2 }
+
+ [data-theme="light"] svg .box-style { stroke: black; fill: white }
+ [data-theme="light"] svg .line-style { stroke: red; fill: none }
+ [data-theme="light"] svg .fill-style { stroke: none; fill: red }
+ [data-theme="light"] svg .np-line-style { stroke: blue; fill: none }
+ [data-theme="light"] svg .np-fill-style { stroke: none; fill: blue }
+ [data-theme="light"] svg .text-style { font: 20px arial; fill: black }
+ [data-theme="light"] svg .small-text-style { font: 12px monospace; fill: black }
+</style>
+
+<g transform="translate(10.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">A</text>
+</g>
+
+<g transform="translate(90.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">B</text>
+<path d="M 0,60 c 0,30 50,40 80,30" class="np-line-style" />
+<path d="M 0,60 l -2,14 l 11,-4 z" class="np-fill-style" />
+<text x="-15" y="110" class="small-text-style">previous()</text>
+</g>
+
+<g transform="translate(170.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">C</text>
+<path d="M 0,60 v 50" class="line-style" />
+<path d="M 0,60 l -5,10 l 10,0 z" class="fill-style" />
+<text x="30" y="110" class="small-text-style">next()</text>
+</g>
+
+<g transform="translate(250.5, 10.5)">
+<path d="m 0,0 h 80 v 60 h -80 z" class="box-style" />
+<text x="35" y="36" class="text-style">D</text>
+<path d="M 0,60 c 0,30 -50,40 -80,30" class="np-line-style" />
+<path d="M 0,60 l 2,14 l -11,-4 z" class="np-fill-style" />
+</g>
+
+</svg>
diff --git a/src/corelib/doc/src/java-style-iterators.qdoc b/src/corelib/doc/src/java-style-iterators.qdoc
index 856005cb66c..a0e5c53d633 100644
--- a/src/corelib/doc/src/java-style-iterators.qdoc
+++ b/src/corelib/doc/src/java-style-iterators.qdoc
@@ -42,7 +42,7 @@
diagram below shows the valid iterator positions as red arrows for a list
containing four items:
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
Here's a typical loop for iterating through all the elements of a
QList<QString> in order:
@@ -70,7 +70,7 @@
\l{QListIterator::next()}{next()} and
\l{QListIterator::previous()}{previous()} on an iterator:
- \image javaiterators2.png
+ \image javaiterators2.png Iterating to the next and previous items
The following table summarizes the QListIterator API:
diff --git a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
index 9b3ea6ae660..5dc2e6dad12 100644
--- a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
+++ b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
@@ -172,7 +172,7 @@
The following illustrates this approach.
- \badcode
+ \code
void DerivedClass::setValue(int val)
{
// do something
@@ -247,7 +247,7 @@
be called for the current value of the property, register your callback using
subscribe() instead.
- \section1 Interaction with Q_PROPERTYs
+ \section1 Interaction with Q_PROPERTY
A \l {The Property System}{Q_PROPERTY} that defines \c BINDABLE can be bound and
used in binding expressions. You can implement such properties using \l {QProperty},
diff --git a/src/corelib/io/qlockfile.cpp b/src/corelib/io/qlockfile.cpp
index 075eb144e51..4d431b46213 100644
--- a/src/corelib/io/qlockfile.cpp
+++ b/src/corelib/io/qlockfile.cpp
@@ -379,8 +379,9 @@ QLockFilePrivate::~QLockFilePrivate()
QByteArray QLockFilePrivate::lockFileContents() const
{
// Use operator% from the fast builder to avoid multiple memory allocations.
- return QByteArray::number(QCoreApplication::applicationPid()) % '\n'
- % processNameByPid(QCoreApplication::applicationPid()).toUtf8() % '\n'
+ qint64 pid = QCoreApplication::applicationPid();
+ return QByteArray::number(pid) % '\n'
+ % processNameByPid(pid).toUtf8() % '\n'
% machineName().toUtf8() % '\n'
% QSysInfo::machineUniqueId() % '\n'
% QSysInfo::bootUniqueId() % '\n';
diff --git a/src/corelib/itemmodels/qrangemodel.cpp b/src/corelib/itemmodels/qrangemodel.cpp
index 05e3a39e589..b0c6c46b125 100644
--- a/src/corelib/itemmodels/qrangemodel.cpp
+++ b/src/corelib/itemmodels/qrangemodel.cpp
@@ -20,6 +20,8 @@ public:
private:
std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl;
+ friend class QRangeModelImplBase;
+
mutable QHash<int, QByteArray> m_roleNames;
};
@@ -28,6 +30,16 @@ QRangeModel::QRangeModel(QRangeModelImplBase *impl, QObject *parent)
{
}
+QRangeModelImplBase *QRangeModelImplBase::getImplementation(QRangeModel *model)
+{
+ return model->d_func()->impl.get();
+}
+
+const QRangeModelImplBase *QRangeModelImplBase::getImplementation(const QRangeModel *model)
+{
+ return model->d_func()->impl.get();
+}
+
/*!
\class QRangeModel
\inmodule QtCore
diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h
index f38dc88c0d9..c2f473542d7 100644
--- a/src/corelib/itemmodels/qrangemodel_impl.h
+++ b/src/corelib/itemmodels/qrangemodel_impl.h
@@ -842,6 +842,11 @@ namespace QRangeModelDetails
{
using protocol = wrapped_t<Protocol>;
using row = typename range_traits<wrapped_t<Range>>::value_type;
+ static constexpr bool is_tree = std::conjunction_v<protocol_parentRow<protocol, row>,
+ protocol_childRows<protocol, row>>;
+ static constexpr bool is_list = static_size_v<row> == 0
+ && (!has_metaobject_v<row> || row_category<row>::isMultiRole);
+ static constexpr bool is_table = !is_list && !is_tree;
static constexpr bool has_newRow = protocol_newRow<protocol>();
static constexpr bool has_deleteRow = protocol_deleteRow<protocol, row>();
@@ -1046,6 +1051,9 @@ public:
typename C::MultiData
>;
+ static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
+ static Q_CORE_EXPORT const QRangeModelImplBase *getImplementation(const QRangeModel *model);
+
private:
friend class QRangeModelPrivate;
QRangeModel *m_rangeModel;
@@ -1147,13 +1155,6 @@ protected:
}
}
- static constexpr bool isMutable()
- {
- return range_features::is_mutable && row_features::is_mutable
- && std::is_reference_v<row_reference>
- && Structure::is_mutable_impl;
- }
-
static constexpr int static_row_count = QRangeModelDetails::static_size_v<range_type>;
static constexpr bool rows_are_raw_pointers = std::is_pointer_v<row_type>;
static constexpr bool rows_are_owning_or_raw_pointers =
@@ -1161,9 +1162,6 @@ protected:
static constexpr int static_column_count = QRangeModelDetails::static_size_v<row_type>;
static constexpr bool one_dimensional_range = static_column_count == 0;
- static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
- static constexpr bool dynamicColumns() { return static_column_count < 0; }
-
// A row might be a value (or range of values), or a pointer.
// row_ptr is always a pointer, and const_row_ptr is a pointer to const.
using row_ptr = wrapped_row_type *;
@@ -1205,6 +1203,15 @@ protected:
"The range holding a move-only row-type must support insert(pos, start, end)");
public:
+ static constexpr bool isMutable()
+ {
+ return range_features::is_mutable && row_features::is_mutable
+ && std::is_reference_v<row_reference>
+ && Structure::is_mutable_impl;
+ }
+ static constexpr bool dynamicRows() { return isMutable() && static_row_count < 0; }
+ static constexpr bool dynamicColumns() { return static_column_count < 0; }
+
explicit QRangeModelImpl(Range &&model, Protocol&& protocol, QRangeModel *itemModel)
: Ancestor(itemModel)
, ProtocolStorage{std::forward<Protocol>(protocol)}
@@ -1236,7 +1243,7 @@ public:
if (row == index.row() && column == index.column())
return index;
- if (column < 0 || column >= this->itemModel().columnCount())
+ if (column < 0 || column >= this->columnCount({}))
return {};
if (row == index.row())
@@ -1974,8 +1981,8 @@ public:
}
if (sourceRow == destRow || sourceRow == destRow - 1 || count <= 0
- || sourceRow < 0 || sourceRow + count - 1 >= this->itemModel().rowCount(sourceParent)
- || destRow < 0 || destRow > this->itemModel().rowCount(destParent)) {
+ || sourceRow < 0 || sourceRow + count - 1 >= this->rowCount(sourceParent)
+ || destRow < 0 || destRow > this->rowCount(destParent)) {
return false;
}
@@ -1995,11 +2002,14 @@ public:
}
}
- QModelIndex parent(const QModelIndex &child) const { return that().parent(child); }
+ const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
+ protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
+
+ QModelIndex parent(const QModelIndex &child) const { return that().parentImpl(child); }
- int rowCount(const QModelIndex &parent) const { return that().rowCount(parent); }
+ int rowCount(const QModelIndex &parent) const { return that().rowCountImpl(parent); }
- int columnCount(const QModelIndex &parent) const { return that().columnCount(parent); }
+ int columnCount(const QModelIndex &parent) const { return that().columnCountImpl(parent); }
void destroy() { delete std::addressof(that()); }
@@ -2035,6 +2045,11 @@ public:
protected:
~QRangeModelImpl()
{
+ deleteOwnedRows();
+ }
+
+ void deleteOwnedRows()
+ {
// We delete row objects if we are not operating on a reference or pointer
// to a range, as in that case, the owner of the referenced/pointed to
// range also owns the row entries.
@@ -2335,9 +2350,6 @@ protected:
}
- const protocol_type& protocol() const { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
- protocol_type& protocol() { return QRangeModelDetails::refTo(ProtocolStorage::object()); }
-
ModelData m_data;
};
@@ -2385,7 +2397,7 @@ protected:
return this->createIndex(row, column, QRangeModelDetails::pointerTo(*it));
}
- QModelIndex parent(const QModelIndex &child) const
+ QModelIndex parentImpl(const QModelIndex &child) const
{
if (!child.isValid())
return {};
@@ -2410,12 +2422,12 @@ protected:
return {};
}
- int rowCount(const QModelIndex &parent) const
+ int rowCountImpl(const QModelIndex &parent) const
{
return Base::size(this->childRange(parent));
}
- int columnCount(const QModelIndex &) const
+ int columnCountImpl(const QModelIndex &) const
{
// all levels of a tree have to have the same, static, column count
if constexpr (Base::one_dimensional_range)
@@ -2662,6 +2674,9 @@ class QGenericTableItemModelImpl
using Base = QRangeModelImpl<QGenericTableItemModelImpl<Range>, Range>;
friend class QRangeModelImpl<QGenericTableItemModelImpl<Range>, Range>;
+ static constexpr bool is_mutable_impl = true;
+
+public:
using range_type = typename Base::range_type;
using range_features = typename Base::range_features;
using row_type = typename Base::row_type;
@@ -2669,9 +2684,6 @@ class QGenericTableItemModelImpl
using row_traits = typename Base::row_traits;
using row_features = typename Base::row_features;
- static constexpr bool is_mutable_impl = true;
-
-public:
explicit QGenericTableItemModelImpl(Range &&model, QRangeModel *itemModel)
: Base(std::forward<Range>(model), {}, itemModel)
{}
@@ -2692,19 +2704,19 @@ protected:
}
}
- QModelIndex parent(const QModelIndex &) const
+ QModelIndex parentImpl(const QModelIndex &) const
{
return {};
}
- int rowCount(const QModelIndex &parent) const
+ int rowCountImpl(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return int(Base::size(*this->m_data.model()));
}
- int columnCount(const QModelIndex &parent) const
+ int columnCountImpl(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
@@ -2760,7 +2772,7 @@ protected:
// dynamically sized rows all have to have the same column count
if constexpr (Base::dynamicColumns() && row_features::has_resize) {
if (QRangeModelDetails::isValid(empty_row))
- QRangeModelDetails::refTo(empty_row).resize(this->itemModel().columnCount());
+ QRangeModelDetails::refTo(empty_row).resize(this->columnCount({}));
}
return empty_row;
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 0c7e7db3188..de7c4df6d1d 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -2512,7 +2512,7 @@ QString QCoreApplication::applicationFilePath()
Returns the current process ID for the application.
*/
-qint64 QCoreApplication::applicationPid()
+qint64 QCoreApplication::applicationPid() noexcept
{
#if defined(Q_OS_WIN)
return GetCurrentProcessId();
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index 55c8097adc5..054e53a4a41 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -119,7 +119,7 @@ public:
static QString applicationDirPath();
static QString applicationFilePath();
- Q_DECL_CONST_FUNCTION static qint64 applicationPid();
+ Q_DECL_CONST_FUNCTION static qint64 applicationPid() noexcept;
#if QT_CONFIG(permissions) || defined(Q_QDOC)
Qt::PermissionStatus checkPermission(const QPermission &permission);
diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp
index 8c95431d01f..bbcea8338ca 100644
--- a/src/corelib/kernel/qpermissions.cpp
+++ b/src/corelib/kernel/qpermissions.cpp
@@ -132,6 +132,10 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
\annotatedlist permissions
+ \note The available permission types cover core functionality of Qt modules
+ like Qt Multimedia and Qt Positioning, but do not encompass all platform-specific
+ permissions. Custom permission types are not currently supported.
+
\section1 Best Practices
To ensure the best possible user experience for the end user we recommend
diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp
index 06dc614c352..93f905afc48 100644
--- a/src/corelib/mimetypes/qmimedatabase.cpp
+++ b/src/corelib/mimetypes/qmimedatabase.cpp
@@ -158,7 +158,7 @@ void QMimeDatabasePrivate::loadProviders()
const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers()
{
-#ifndef Q_OS_WASM // stub implementation always returns true
+#if QT_CONFIG(thread) // stub implementation always returns true
Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex
#endif
if (m_providers.empty()) {
@@ -289,7 +289,9 @@ QStringList QMimeDatabasePrivate::mimeParents(const QString &mimeName)
QStringList QMimeDatabasePrivate::parents(const QString &mimeName)
{
+#if QT_CONFIG(thread) // stub implementation always returns true
Q_ASSERT(!mutex.tryLock());
+#endif
QStringList result;
for (const auto &provider : providers())
provider->addParents(mimeName, result);
diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp
index 4ccc58b3f39..de7043e8c1d 100644
--- a/src/corelib/mimetypes/qmimeprovider.cpp
+++ b/src/corelib/mimetypes/qmimeprovider.cpp
@@ -377,7 +377,6 @@ void QMimeBinaryProvider::findByMagic(const QByteArray &data, QMimeMagicResult &
void QMimeBinaryProvider::addParents(const QString &mime, QStringList &result)
{
- const QByteArray mimeStr = mime.toLatin1();
const int parentListOffset = m_cacheFile->getUint32(PosParentListOffset);
const int numEntries = m_cacheFile->getUint32(parentListOffset);
@@ -388,7 +387,7 @@ void QMimeBinaryProvider::addParents(const QString &mime, QStringList &result)
const int off = parentListOffset + 4 + 8 * medium;
const int mimeOffset = m_cacheFile->getUint32(off);
const char *aMime = m_cacheFile->getCharStar(mimeOffset);
- const int cmp = qstrcmp(aMime, mimeStr);
+ const int cmp = QLatin1StringView(aMime).compare(mime);
if (cmp < 0) {
begin = medium + 1;
} else if (cmp > 0) {
@@ -409,7 +408,6 @@ void QMimeBinaryProvider::addParents(const QString &mime, QStringList &result)
QString QMimeBinaryProvider::resolveAlias(const QString &name)
{
- const QByteArray input = name.toLatin1();
const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset);
const int numEntries = m_cacheFile->getUint32(aliasListOffset);
int begin = 0;
@@ -419,7 +417,7 @@ QString QMimeBinaryProvider::resolveAlias(const QString &name)
const int off = aliasListOffset + 4 + 8 * medium;
const int aliasOffset = m_cacheFile->getUint32(off);
const char *alias = m_cacheFile->getCharStar(aliasOffset);
- const int cmp = qstrcmp(alias, input);
+ const int cmp = QLatin1StringView(alias).compare(name);
if (cmp < 0) {
begin = medium + 1;
} else if (cmp > 0) {
@@ -435,7 +433,6 @@ QString QMimeBinaryProvider::resolveAlias(const QString &name)
void QMimeBinaryProvider::addAliases(const QString &name, QStringList &result)
{
- const QByteArray input = name.toLatin1();
const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset);
const int numEntries = m_cacheFile->getUint32(aliasListOffset);
for (int pos = 0; pos < numEntries; ++pos) {
@@ -443,7 +440,7 @@ void QMimeBinaryProvider::addAliases(const QString &name, QStringList &result)
const int mimeOffset = m_cacheFile->getUint32(off + 4);
const char *mimeType = m_cacheFile->getCharStar(mimeOffset);
- if (input == mimeType) {
+ if (name == QLatin1StringView(mimeType)) {
const int aliasOffset = m_cacheFile->getUint32(off);
const char *alias = m_cacheFile->getCharStar(aliasOffset);
const QString strAlias = QString::fromLatin1(alias);
@@ -586,7 +583,7 @@ QMimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName)
// Binary search in the icons or generic-icons list
QLatin1StringView QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset,
- const QByteArray &inputMime)
+ QStringView inputMime)
{
const int iconsListOffset = cacheFile->getUint32(posListOffset);
const int numIcons = cacheFile->getUint32(iconsListOffset);
@@ -597,7 +594,7 @@ QLatin1StringView QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int pos
const int off = iconsListOffset + 4 + 8 * medium;
const int mimeOffset = cacheFile->getUint32(off);
const char *mime = cacheFile->getCharStar(mimeOffset);
- const int cmp = qstrcmp(mime, inputMime);
+ const int cmp = QLatin1StringView(mime).compare(inputMime);
if (cmp < 0)
begin = medium + 1;
else if (cmp > 0)
@@ -612,14 +609,12 @@ QLatin1StringView QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int pos
QString QMimeBinaryProvider::icon(const QString &name)
{
- const QByteArray inputMime = name.toLatin1();
- return iconForMime(m_cacheFile.get(), PosIconsListOffset, inputMime);
+ return iconForMime(m_cacheFile.get(), PosIconsListOffset, name);
}
QString QMimeBinaryProvider::genericIcon(const QString &name)
{
- const QByteArray inputMime = name.toLatin1();
- return iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, inputMime);
+ return iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, name);
}
////
diff --git a/src/corelib/mimetypes/qmimeprovider_p.h b/src/corelib/mimetypes/qmimeprovider_p.h
index aede727d710..d597651b362 100644
--- a/src/corelib/mimetypes/qmimeprovider_p.h
+++ b/src/corelib/mimetypes/qmimeprovider_p.h
@@ -111,7 +111,7 @@ private:
bool caseSensitiveCheck);
bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset,
const QByteArray &data);
- QLatin1StringView iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime);
+ QLatin1StringView iconForMime(CacheFile *cacheFile, int posListOffset, QStringView inputMime);
void loadMimeTypeList();
bool checkCacheChanged();
diff --git a/src/corelib/platform/wasm/qstdweb.cpp b/src/corelib/platform/wasm/qstdweb.cpp
index d1fff5388c6..287138bb915 100644
--- a/src/corelib/platform/wasm/qstdweb.cpp
+++ b/src/corelib/platform/wasm/qstdweb.cpp
@@ -177,15 +177,13 @@ Blob Blob::slice(uint32_t begin, uint32_t end) const
ArrayBuffer Blob::arrayBuffer_sync() const
{
- QEventLoop loop;
emscripten::val buffer;
- qstdweb::Promise::make(m_blob, QStringLiteral("arrayBuffer"), {
- .thenFunc = [&loop, &buffer](emscripten::val arrayBuffer) {
+ uint32_t handlerIndex = qstdweb::Promise::make(m_blob, QStringLiteral("arrayBuffer"), {
+ .thenFunc = [&buffer](emscripten::val arrayBuffer) {
buffer = arrayBuffer;
- loop.quit();
}
});
- loop.exec();
+ Promise::suspendExclusive(handlerIndex);
return ArrayBuffer(buffer);
}
@@ -443,7 +441,7 @@ EventCallback::EventCallback(emscripten::val element, const std::string &name,
}
-void Promise::adoptPromise(emscripten::val promise, PromiseCallbacks callbacks)
+uint32_t Promise::adoptPromise(emscripten::val promise, PromiseCallbacks callbacks)
{
Q_ASSERT_X(!!callbacks.catchFunc || !!callbacks.finallyFunc || !!callbacks.thenFunc,
"Promise::adoptPromise", "must provide at least one callback function");
@@ -499,13 +497,23 @@ void Promise::adoptPromise(emscripten::val promise, PromiseCallbacks callbacks)
promise = promise.call<emscripten::val>("finally",
suspendResume->jsEventHandlerAt(*finallyIndex));
+
+ return *finallyIndex;
+}
+
+void Promise::suspendExclusive(uint32_t handlerIndex)
+{
+ QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get();
+ Q_ASSERT(suspendResume);
+ suspendResume->suspendExclusive(handlerIndex);
+ suspendResume->sendPendingEvents();
}
void Promise::all(std::vector<emscripten::val> promises, PromiseCallbacks callbacks)
{
auto arr = emscripten::val::array(promises);
auto all = val::global("Promise").call<emscripten::val>("all", arr);
- return adoptPromise(all, callbacks);
+ adoptPromise(all, callbacks);
}
// Asyncify and thread blocking: Normally, it's not possible to block the main
@@ -630,6 +638,249 @@ qint64 Uint8ArrayIODevice::writeData(const char *data, qint64 maxSize)
return size;
}
+FileSystemWritableFileStreamIODevice::FileSystemWritableFileStreamIODevice(FileSystemWritableFileStream stream)
+ : m_stream(std::move(stream))
+{
+}
+
+bool FileSystemWritableFileStreamIODevice::open(QIODevice::OpenMode mode)
+{
+ if (mode.testFlag(QIODevice::ReadOnly))
+ return false;
+ return QIODevice::open(mode);
+}
+
+void FileSystemWritableFileStreamIODevice::close()
+{
+ if (!isOpen()) {
+ QIODevice::close();
+ return;
+ }
+
+ uint32_t handlerIndex = Promise::make(m_stream.val(), QStringLiteral("close"), {
+ .thenFunc = [](emscripten::val) {
+ }
+ });
+ Promise::suspendExclusive(handlerIndex);
+
+ QIODevice::close();
+}
+
+bool FileSystemWritableFileStreamIODevice::isSequential() const
+{
+ return false;
+}
+
+qint64 FileSystemWritableFileStreamIODevice::size() const
+{
+ return m_size;
+}
+
+bool FileSystemWritableFileStreamIODevice::seek(qint64 pos)
+{
+ bool success = false;
+
+ emscripten::val seekParams = emscripten::val::object();
+ seekParams.set("type", std::string("seek"));
+ seekParams.set("position", static_cast<double>(pos));
+ uint32_t handlerIndex = Promise::make(m_stream.val(), QStringLiteral("write"), {
+ .thenFunc = [&success](emscripten::val) {
+ success = true;
+ },
+ .catchFunc = [](emscripten::val) {
+ }
+ }, seekParams);
+ Promise::suspendExclusive(handlerIndex);
+
+ if (!success)
+ return false;
+
+ return QIODevice::seek(pos);
+}
+
+qint64 FileSystemWritableFileStreamIODevice::readData(char *, qint64)
+{
+ Q_UNREACHABLE();
+}
+
+qint64 FileSystemWritableFileStreamIODevice::writeData(const char *data, qint64 size)
+{
+ bool success = false;
+
+ Uint8Array array = Uint8Array::copyFrom(data, size);
+ uint32_t handlerIndex = Promise::make(m_stream.val(), QStringLiteral("write"), {
+ .thenFunc = [&success](emscripten::val) {
+ success = true;
+ },
+ .catchFunc = [](emscripten::val) {
+ }
+ }, array.val());
+ Promise::suspendExclusive(handlerIndex);
+
+ if (success) {
+ qint64 newPos = pos() + size;
+ m_size = std::max(m_size, newPos);
+ return size;
+ }
+ return -1;
+}
+
+FileSystemWritableFileStream::FileSystemWritableFileStream(const emscripten::val &writableStream)
+ : m_writableStream(writableStream)
+{
+}
+
+emscripten::val FileSystemWritableFileStream::val() const
+{
+ return m_writableStream;
+}
+
+FileSystemFileHandle::FileSystemFileHandle(const emscripten::val &fileHandle)
+ : m_fileHandle(fileHandle)
+{
+}
+
+std::string FileSystemFileHandle::name() const
+{
+ return m_fileHandle["name"].as<std::string>();
+}
+
+std::string FileSystemFileHandle::kind() const
+{
+ return m_fileHandle["kind"].as<std::string>();
+}
+
+emscripten::val FileSystemFileHandle::val() const
+{
+ return m_fileHandle;
+}
+
+FileSystemFileIODevice::FileSystemFileIODevice(FileSystemFileHandle fileHandle)
+ : m_fileHandle(fileHandle)
+{
+}
+
+bool FileSystemFileIODevice::open(QIODevice::OpenMode mode)
+{
+ if (isOpen())
+ return false;
+
+ // Read mode: get the File and create a BlobIODevice
+ if (mode & QIODevice::ReadOnly) {
+ File file;
+ bool success = false;
+
+ uint32_t handlerIndex = Promise::make(m_fileHandle.val(), QStringLiteral("getFile"), {
+ .thenFunc = [&file, &success](emscripten::val fileVal) {
+ file = File(fileVal);
+ success = true;
+ },
+ .catchFunc = [](emscripten::val) {
+ }
+ });
+ Promise::suspendExclusive(handlerIndex);
+
+ if (success) {
+ m_blobDevice = std::make_unique<BlobIODevice>(file.slice(0, file.size()));
+ m_size = file.size();
+
+ if (!m_blobDevice->open(mode))
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ // Write mode: create a writable stream
+ if (mode & QIODevice::WriteOnly) {
+ FileSystemWritableFileStream writableStream;
+ bool success = false;
+
+ uint32_t handlerIndex = Promise::make(m_fileHandle.val(), QStringLiteral("createWritable"), {
+ .thenFunc = [&writableStream, &success](emscripten::val writable) {
+ writableStream = FileSystemWritableFileStream(writable);
+ success = true;
+ },
+ .catchFunc = [](emscripten::val) {
+ }
+ });
+ Promise::suspendExclusive(handlerIndex);
+
+ if (success) {
+ m_writableDevice = std::make_unique<FileSystemWritableFileStreamIODevice>(writableStream);
+ if (!m_writableDevice->open(mode))
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ return QIODevice::open(mode);
+}
+
+void FileSystemFileIODevice::close()
+{
+ if (!isOpen()) {
+ QIODevice::close();
+ return;
+ }
+
+ if (m_writableDevice) {
+ m_writableDevice->close();
+ m_writableDevice.reset();
+ }
+ if (m_blobDevice) {
+ m_blobDevice->close();
+ m_blobDevice.reset();
+ }
+
+ QIODevice::close();
+}
+
+bool FileSystemFileIODevice::isSequential() const
+{
+ return false;
+}
+
+qint64 FileSystemFileIODevice::size() const
+{
+ return m_size;
+}
+
+bool FileSystemFileIODevice::seek(qint64 pos)
+{
+ if (m_blobDevice) {
+ if (!m_blobDevice->seek(pos))
+ return false;
+ }
+ if (m_writableDevice) {
+ if (!m_writableDevice->seek(pos))
+ return false;
+ }
+ return QIODevice::seek(pos);
+}
+
+qint64 FileSystemFileIODevice::readData(char *data, qint64 maxSize)
+{
+ if (!m_blobDevice)
+ return -1;
+
+ return m_blobDevice->read(data, maxSize);
+}
+
+qint64 FileSystemFileIODevice::writeData(const char *data, qint64 size)
+{
+ if (!m_writableDevice)
+ return -1;
+
+ qint64 written = m_writableDevice->write(data, size);
+ if (written > 0) {
+ qint64 newPos = pos() + written;
+ m_size = std::max(m_size, newPos);
+ }
+ return written;
+}
+
} // namespace qstdweb
QT_END_NAMESPACE
diff --git a/src/corelib/platform/wasm/qstdweb_p.h b/src/corelib/platform/wasm/qstdweb_p.h
index 711751f65ab..9a97370448e 100644
--- a/src/corelib/platform/wasm/qstdweb_p.h
+++ b/src/corelib/platform/wasm/qstdweb_p.h
@@ -195,6 +195,30 @@ namespace qstdweb {
emscripten::val m_uint8Array = emscripten::val::undefined();
};
+ class Q_CORE_EXPORT FileSystemWritableFileStream {
+ public:
+ FileSystemWritableFileStream() = default;
+ explicit FileSystemWritableFileStream(const emscripten::val &writableStream);
+ emscripten::val val() const;
+
+ private:
+ emscripten::val m_writableStream = emscripten::val::undefined();
+ };
+
+ class Q_CORE_EXPORT FileSystemFileHandle {
+ public:
+ FileSystemFileHandle() = default;
+ explicit FileSystemFileHandle(const emscripten::val &fileHandle);
+
+ std::string name() const;
+ std::string kind() const;
+
+ emscripten::val val() const;
+
+ private:
+ emscripten::val m_fileHandle = emscripten::val::undefined();
+ };
+
// EventCallback here for source compatibility; prefer using QWasmEventHandler directly
class Q_CORE_EXPORT EventCallback : public QWasmEventHandler
{
@@ -214,13 +238,13 @@ namespace qstdweb {
};
namespace Promise {
- void Q_CORE_EXPORT adoptPromise(emscripten::val promise, PromiseCallbacks callbacks);
+ uint32_t Q_CORE_EXPORT adoptPromise(emscripten::val promise, PromiseCallbacks callbacks);
template<typename... Args>
- void make(emscripten::val target,
- QString methodName,
- PromiseCallbacks callbacks,
- Args... args)
+ uint32_t make(emscripten::val target,
+ QString methodName,
+ PromiseCallbacks callbacks,
+ Args... args)
{
emscripten::val promiseObject = target.call<emscripten::val>(
methodName.toStdString().c_str(), std::forward<Args>(args)...);
@@ -228,9 +252,10 @@ namespace qstdweb {
qFatal("This function did not return a promise");
}
- adoptPromise(std::move(promiseObject), std::move(callbacks));
+ return adoptPromise(std::move(promiseObject), std::move(callbacks));
}
+ void Q_CORE_EXPORT suspendExclusive(uint32_t handlerIndex);
void Q_CORE_EXPORT all(std::vector<emscripten::val> promises, PromiseCallbacks callbacks);
};
@@ -274,6 +299,46 @@ namespace qstdweb {
Uint8Array m_array;
};
+ class Q_CORE_EXPORT FileSystemWritableFileStreamIODevice: public QIODevice
+ {
+ public:
+ FileSystemWritableFileStreamIODevice(FileSystemWritableFileStream stream);
+ bool open(QIODevice::OpenMode mode) override;
+ void close() override;
+ bool isSequential() const override;
+ qint64 size() const override;
+ bool seek(qint64 pos) override;
+
+ protected:
+ qint64 readData(char *data, qint64 maxSize) override;
+ qint64 writeData(const char *data, qint64 size) override;
+
+ private:
+ FileSystemWritableFileStream m_stream;
+ qint64 m_size = 0;
+ };
+
+ class Q_CORE_EXPORT FileSystemFileIODevice: public QIODevice
+ {
+ public:
+ FileSystemFileIODevice(FileSystemFileHandle fileHandle);
+ bool open(QIODevice::OpenMode mode) override;
+ void close() override;
+ bool isSequential() const override;
+ qint64 size() const override;
+ bool seek(qint64 pos) override;
+
+ protected:
+ qint64 readData(char *data, qint64 maxSize) override;
+ qint64 writeData(const char *data, qint64 size) override;
+
+ private:
+ FileSystemFileHandle m_fileHandle;
+ std::unique_ptr<BlobIODevice> m_blobDevice;
+ std::unique_ptr<FileSystemWritableFileStreamIODevice> m_writableDevice;
+ qint64 m_size = 0;
+ };
+
inline emscripten::val window()
{
static emscripten::val savedWindow = emscripten::val::global("window");
diff --git a/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp b/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp
index 961bbb53e54..093898c520a 100644
--- a/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp
+++ b/src/corelib/platform/wasm/qwasmsuspendresumecontrol.cpp
@@ -49,6 +49,7 @@ void qtSuspendResumeControlClearJs() {
asyncifyEnabled: false, // asyncify 1 or JSPI enabled
eventHandlers: {},
pendingEvents: [],
+ exclusiveEventHandler: 0,
});
});
}
@@ -118,7 +119,15 @@ void qtRegisterEventHandlerJs(int index) {
});
// Handle the event based on instance state and asyncify flag
- if (control.resume) {
+ if (control.exclusiveEventHandler > 0) {
+ // In exclusive mode, resume on exclusive event handler match only
+ if (index != control.exclusiveEventHandler)
+ return;
+
+ const resume = control.resume;
+ control.resume = null;
+ resume();
+ } else if (control.resume) {
// The instance is suspended in processEvents(), resume and process the event
const resume = control.resume;
control.resume = null;
@@ -148,14 +157,12 @@ QWasmSuspendResumeControl::QWasmSuspendResumeControl()
#endif
qtSuspendResumeControlClearJs();
suspendResumeControlJs().set("asyncifyEnabled", qstdweb::haveAsyncify());
- Q_ASSERT(!QWasmSuspendResumeControl::s_suspendResumeControl);
QWasmSuspendResumeControl::s_suspendResumeControl = this;
}
QWasmSuspendResumeControl::~QWasmSuspendResumeControl()
{
qtSuspendResumeControlClearJs();
- Q_ASSERT(QWasmSuspendResumeControl::s_suspendResumeControl);
QWasmSuspendResumeControl::s_suspendResumeControl = nullptr;
}
@@ -199,13 +206,24 @@ void QWasmSuspendResumeControl::suspend()
qtSuspendJs();
}
-// Sends any pending events. Returns true if an event was sent, false otherwise.
+void QWasmSuspendResumeControl::suspendExclusive(uint32_t eventHandlerIndex)
+{
+ suspendResumeControlJs().set("exclusiveEventHandler", eventHandlerIndex);
+ qtSuspendJs();
+}
+
+// Sends any pending events. Returns the number of sent events.
int QWasmSuspendResumeControl::sendPendingEvents()
{
#if QT_CONFIG(thread)
Q_ASSERT(emscripten_is_main_runtime_thread());
#endif
- emscripten::val pendingEvents = suspendResumeControlJs()["pendingEvents"];
+ emscripten::val control = suspendResumeControlJs();
+ emscripten::val pendingEvents = control["pendingEvents"];
+
+ if (control["exclusiveEventHandler"].as<int>() > 0)
+ return sendPendingExclusiveEvent();
+
if (pendingEvents["length"].as<int>() == 0)
return 0;
@@ -214,13 +232,28 @@ int QWasmSuspendResumeControl::sendPendingEvents()
// Grab one event (handler and arg), and call it
emscripten::val event = pendingEvents.call<val>("shift");
auto it = m_eventHandlers.find(event["index"].as<int>());
- Q_ASSERT(it != m_eventHandlers.end());
- it->second(event["arg"]);
+ if (it != m_eventHandlers.end())
+ it->second(event["arg"]);
++count;
}
return count;
}
+// Sends the pending exclusive event, and resets the "exclusive" state
+int QWasmSuspendResumeControl::sendPendingExclusiveEvent()
+{
+ emscripten::val control = suspendResumeControlJs();
+ int exclusiveHandlerIndex = control["exclusiveEventHandler"].as<int>();
+ control.set("exclusiveEventHandler", 0);
+ emscripten::val event = control["pendingEvents"].call<val>("pop");
+ int eventHandlerIndex = event["index"].as<int>();
+ Q_ASSERT(exclusiveHandlerIndex == eventHandlerIndex);
+ auto it = m_eventHandlers.find(eventHandlerIndex);
+ Q_ASSERT(it != m_eventHandlers.end());
+ it->second(event["arg"]);
+ return 1;
+}
+
void qtSendPendingEvents()
{
if (QWasmSuspendResumeControl::s_suspendResumeControl)
diff --git a/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h b/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h
index 2b9962e4be1..37c71ed8123 100644
--- a/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h
+++ b/src/corelib/platform/wasm/qwasmsuspendresumecontrol_p.h
@@ -38,7 +38,9 @@ public:
static emscripten::val suspendResumeControlJs();
void suspend();
+ void suspendExclusive(uint32_t eventHandlerIndex);
int sendPendingEvents();
+ int sendPendingExclusiveEvent();
private:
friend void qtSendPendingEvents();
diff --git a/src/corelib/plugin/qlibrary.h b/src/corelib/plugin/qlibrary.h
index f31047b214a..95c14178b22 100644
--- a/src/corelib/plugin/qlibrary.h
+++ b/src/corelib/plugin/qlibrary.h
@@ -63,6 +63,7 @@ private:
Loaded
};
+ friend class QLibraryPrivate;
QTaggedPointer<QLibraryPrivate, LoadStatusTag> d = nullptr;
Q_DISABLE_COPY(QLibrary)
};
diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h
index b4e29a79e28..2331c86d844 100644
--- a/src/corelib/plugin/qlibrary_p.h
+++ b/src/corelib/plugin/qlibrary_p.h
@@ -67,7 +67,7 @@ public:
bool load();
QtPluginInstanceFunction loadPlugin(); // loads and resolves instance
- bool unload(UnloadFlag flag = UnloadSys);
+ Q_AUTOTEST_EXPORT bool unload(UnloadFlag flag = UnloadSys);
void release();
QFunctionPointer resolve(const char *);
@@ -103,6 +103,11 @@ public:
void updatePluginState();
bool isPlugin();
+ static QLibraryPrivate* get(QLibrary* lib)
+ {
+ return lib->d.data();
+ }
+
private:
explicit QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints);
~QLibraryPrivate();
diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp
index 711b70ebf8f..c01c1a9999d 100644
--- a/src/corelib/text/qstring.cpp
+++ b/src/corelib/text/qstring.cpp
@@ -3681,95 +3681,24 @@ QString &QString::remove(QChar ch, Qt::CaseSensitivity cs)
\sa remove()
*/
-
-/*! \internal
- Instead of detaching, or reallocating if "before" is shorter than "after"
- and there isn't enough capacity, create a new string, copy characters to it
- as needed, then swap it with "str".
-*/
-static void replace_with_copy(QString &str, QSpan<size_t> indices, qsizetype blen,
- QStringView after)
-{
- const qsizetype alen = after.size();
- const char16_t *after_b = after.utf16();
-
- const QString::DataPointer &str_d = str.data_ptr();
- auto src_start = str_d.begin();
- const qsizetype newSize = str_d.size + indices.size() * (alen - blen);
- QString copy{ newSize, Qt::Uninitialized };
- QString::DataPointer &copy_d = copy.data_ptr();
- auto dst = copy_d.begin();
- for (size_t index : indices) {
- auto hit = str_d.begin() + index;
- dst = std::copy(src_start, hit, dst);
- dst = std::copy_n(after_b, alen, dst);
- src_start = hit + blen;
- }
- dst = std::copy(src_start, str_d.end(), dst);
- str.swap(copy);
-}
-
-// No detaching or reallocation is needed
-static void replace_in_place(QString &str, QSpan<size_t> indices,
- qsizetype blen, QStringView after)
-{
- const qsizetype alen = after.size();
- const char16_t *after_b = after.utf16();
- const char16_t *after_e = after.utf16() + after.size();
-
- if (blen == alen) { // Replace in place
- for (size_t index : indices)
- std::copy_n(after_b, alen, str.data_ptr().begin() + index);
- } else if (blen > alen) { // Replace from front
- char16_t *begin = str.data_ptr().begin();
- char16_t *hit = begin + indices.front();
- char16_t *to = hit;
- to = std::copy_n(after_b, alen, to);
- char16_t *movestart = hit + blen;
- for (size_t index : indices.sliced(1)) {
- hit = begin + index;
- to = std::move(movestart, hit, to);
- to = std::copy_n(after_b, alen, to);
- movestart = hit + blen;
- }
- to = std::move(movestart, str.data_ptr().end(), to);
- str.resize(std::distance(begin, to));
- } else { // blen < alen, Replace from back
- const qsizetype oldSize = str.data_ptr().size;
- const qsizetype adjust = indices.size() * (alen - blen);
- const qsizetype newSize = oldSize + adjust;
-
- str.resize(newSize);
- char16_t *begin = str.data_ptr().begin();
- char16_t *moveend = begin + oldSize;
- char16_t *to = str.data_ptr().end();
-
- for (auto it = indices.rbegin(), end = indices.rend(); it != end; ++it) {
- char16_t *hit = begin + *it;
- char16_t *movestart = hit + blen;
- to = std::move_backward(movestart, moveend, to);
- to = std::copy_backward(after_b, after_e, to);
- moveend = hit;
- }
- }
-}
-
-static void replace_helper(QString &str, QSpan<size_t> indices, qsizetype blen, QStringView after)
+static void replace_helper(QString &str, QSpan<qsizetype> indices, qsizetype blen, QStringView after)
{
const qsizetype oldSize = str.data_ptr().size;
const qsizetype adjust = indices.size() * (after.size() - blen);
const qsizetype newSize = oldSize + adjust;
+ using A = QStringAlgorithms<QString>;
if (str.data_ptr().needsDetach() || needsReallocate(str, newSize)) {
- replace_with_copy(str, indices, blen, after);
+ A::replace_helper(str, blen, after, indices);
return;
}
- if (QtPrivate::q_points_into_range(after.begin(), str))
+ if (QtPrivate::q_points_into_range(after.begin(), str)) {
// Copy after if it lies inside our own d.b area (which we could
// possibly invalidate via a realloc or modify by replacement)
- replace_in_place(str, indices, blen, QVarLengthArray(after.begin(), after.end()));
- else
- replace_in_place(str, indices, blen, after);
+ A::replace_helper(str, blen, QVarLengthArray(after.begin(), after.end()), indices);
+ } else {
+ A::replace_helper(str, blen, after, indices);
+ }
}
/*!
@@ -3811,8 +3740,8 @@ QString &QString::replace(qsizetype pos, qsizetype len, const QChar *after, qsiz
if (len > this->size() - pos)
len = this->size() - pos;
- size_t index = pos;
- replace_helper(*this, QSpan(&index, 1), len, QStringView{after, alen});
+ qsizetype indices[] = {pos};
+ replace_helper(*this, indices, len, QStringView{after, alen});
return *this;
}
@@ -3890,7 +3819,7 @@ QString &QString::replace(const QChar *before, qsizetype blen,
qsizetype index = 0;
- QVarLengthArray<size_t> indices;
+ QVarLengthArray<qsizetype> indices;
while ((index = matcher.indexIn(*this, index)) != -1) {
indices.push_back(index);
if (blen) // Step over before:
@@ -3925,7 +3854,7 @@ QString& QString::replace(QChar ch, const QString &after, Qt::CaseSensitivity cs
const char16_t cc = (cs == Qt::CaseSensitive ? ch.unicode() : ch.toCaseFolded().unicode());
- QVarLengthArray<size_t> indices;
+ QVarLengthArray<qsizetype> indices;
if (cs == Qt::CaseSensitive) {
const char16_t *begin = d.begin();
const char16_t *end = d.end();
diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h
index bb70678a958..6e4e532fd7d 100644
--- a/src/corelib/thread/qthread.h
+++ b/src/corelib/thread/qthread.h
@@ -172,27 +172,15 @@ inline Qt::HANDLE QThread::currentThreadId() noexcept
#elif defined(Q_PROCESSOR_X86_64) && ((defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD))
// x86_64 Linux, BSD uses FS
__asm__("mov %%fs:%c1, %0" : "=r" (tid) : "i" (2 * sizeof(void*)) : );
-#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_WIN)
+#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_WIN) && defined(Q_CC_MSVC)
// See https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
- // First get the pointer to the TIB
- quint8 *tib;
-# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
- __asm__("movq %%gs:0x30, %0" : "=r" (tib) : :);
-# else
- tib = reinterpret_cast<quint8 *>(__readgsqword(0x30));
-# endif
- // Then read the thread ID
- tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x48);
-#elif defined(Q_PROCESSOR_X86_32) && defined(Q_OS_WIN)
- // First get the pointer to the TIB
- quint8 *tib;
-# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
- __asm__("movl %%fs:0x18, %0" : "=r" (tib) : :);
-# else
- tib = reinterpret_cast<quint8 *>(__readfsdword(0x18));
-# endif
- // Then read the thread ID
- tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x24);
+ tid = reinterpret_cast<Qt::HANDLE>(__readgsqword(0x48));
+#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_WIN) // !Q_CC_MSVC
+ __asm__("mov %%gs:0x48, %0" : "=r" (tid));
+#elif defined(Q_PROCESSOR_X86_32) && defined(Q_OS_WIN) && defined(Q_CC_MSVC)
+ tid = reinterpret_cast<Qt::HANDLE>(__readfsdword(0x24));
+#elif defined(Q_PROCESSOR_X86_32) && defined(Q_OS_WIN) // !Q_CC_MSVC
+ __asm__("mov %%fs:0x24, %0" : "=r" (tid));
#else
#undef QT_HAS_FAST_CURRENT_THREAD_ID
tid = currentThreadIdImpl();
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index 03eeed84465..974c486b915 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -517,6 +517,7 @@ QDate::QDate(int y, int m, int d, QCalendar cal)
*/
/*!
+ \overload primary
\fn bool QDate::isValid() const
Returns \c true if this date is valid; otherwise returns \c false.
@@ -525,6 +526,8 @@ QDate::QDate(int y, int m, int d, QCalendar cal)
*/
/*!
+ \overload primary
+
Returns the year of this date.
Uses \a cal as calendar, if supplied, else the Gregorian calendar.
@@ -557,8 +560,8 @@ int QDate::year(QCalendar cal) const
}
/*!
- \overload
- */
+ \overload year()
+*/
int QDate::year() const
{
@@ -571,6 +574,8 @@ int QDate::year() const
}
/*!
+ \overload primary
+
Returns the month-number for the date.
Numbers the months of the year starting with 1 for the first. Uses \a cal
@@ -609,8 +614,8 @@ int QDate::month(QCalendar cal) const
}
/*!
- \overload
- */
+ \overload month()
+*/
int QDate::month() const
{
@@ -623,6 +628,8 @@ int QDate::month() const
}
/*!
+ \overload primary
+
Returns the day of the month for this date.
Uses \a cal as calendar if supplied, else the Gregorian calendar (for which
@@ -642,8 +649,8 @@ int QDate::day(QCalendar cal) const
}
/*!
- \overload
- */
+ \overload day()
+*/
int QDate::day() const
{
@@ -656,6 +663,8 @@ int QDate::day() const
}
/*!
+ \overload primary
+
Returns the weekday (1 = Monday to 7 = Sunday) for this date.
Uses \a cal as calendar if supplied, else the Gregorian calendar. Returns 0
@@ -674,8 +683,8 @@ int QDate::dayOfWeek(QCalendar cal) const
}
/*!
- \overload
- */
+ \overload dayOfWeek()
+*/
int QDate::dayOfWeek() const
{
@@ -683,6 +692,8 @@ int QDate::dayOfWeek() const
}
/*!
+ \overload primary
+
Returns the day of the year (1 for the first day) for this date.
Uses \a cal as calendar if supplied, else the Gregorian calendar.
@@ -702,8 +713,8 @@ int QDate::dayOfYear(QCalendar cal) const
}
/*!
- \overload
- */
+ \overload dayOfYear()
+*/
int QDate::dayOfYear() const
{
@@ -715,6 +726,8 @@ int QDate::dayOfYear() const
}
/*!
+ \overload primary
+
Returns the number of days in the month for this date.
Uses \a cal as calendar if supplied, else the Gregorian calendar (for which
@@ -735,8 +748,8 @@ int QDate::daysInMonth(QCalendar cal) const
}
/*!
- \overload
- */
+ \overload daysInMonth()
+*/
int QDate::daysInMonth() const
{
@@ -749,6 +762,8 @@ int QDate::daysInMonth() const
}
/*!
+ \overload primary
+
Returns the number of days in the year for this date.
Uses \a cal as calendar if supplied, else the Gregorian calendar (for which
@@ -766,8 +781,8 @@ int QDate::daysInYear(QCalendar cal) const
}
/*!
- \overload
- */
+ \overload daysInYear()
+*/
int QDate::daysInYear() const
{
@@ -918,6 +933,7 @@ static QDateTime toEarliest(QDate day, const QTimeZone &zone)
/*!
\since 5.14
+ \overload primary
Returns the start-moment of the day.
@@ -972,8 +988,8 @@ QDateTime QDate::startOfDay(const QTimeZone &zone) const
}
/*!
- \overload
\since 6.5
+ \overload startOfDay()
*/
QDateTime QDate::startOfDay() const
{
@@ -982,8 +998,8 @@ QDateTime QDate::startOfDay() const
#if QT_DEPRECATED_SINCE(6, 9)
/*!
- \overload
\since 5.14
+ \overload startOfDay()
\deprecated [6.9] Use \c{startOfDay(const QTimeZone &)} instead.
Returns the start-moment of the day.
@@ -1073,6 +1089,7 @@ static QDateTime toLatest(QDate day, const QTimeZone &zone)
/*!
\since 5.14
+ \overload primary
Returns the end-moment of the day.
@@ -1127,8 +1144,8 @@ QDateTime QDate::endOfDay(const QTimeZone &zone) const
}
/*!
- \overload
\since 6.5
+ \overload endOfDay()
*/
QDateTime QDate::endOfDay() const
{
@@ -1137,8 +1154,8 @@ QDateTime QDate::endOfDay() const
#if QT_DEPRECATED_SINCE(6, 9)
/*!
- \overload
\since 5.14
+ \overload endOfDay()
\deprecated [6.9] Use \c{endOfDay(const QTimeZone &) instead.
Returns the end-moment of the day.
@@ -1199,7 +1216,7 @@ static QString toStringIsoDate(QDate date)
}
/*!
- \overload
+ \overload toString()
Returns the date as a string. The \a format parameter determines the format
of the string.
@@ -1245,9 +1262,10 @@ QString QDate::toString(Qt::DateFormat format) const
}
/*!
+ \since 5.14
+ \overload primary
\fn QString QDate::toString(const QString &format, QCalendar cal) const
\fn QString QDate::toString(QStringView format, QCalendar cal) const
- \since 5.14
Returns the date as a string. The \a format parameter determines the format
of the result string. If \a cal is supplied, it determines the calendar used
@@ -1313,8 +1331,8 @@ QString QDate::toString(QStringView format, QCalendar cal) const
// Out-of-line no-calendar overloads, since QCalendar is a non-trivial type
/*!
- \overload
\since 5.10
+ \overload toString()
*/
QString QDate::toString(QStringView format) const
{
@@ -1322,8 +1340,8 @@ QString QDate::toString(QStringView format) const
}
/*!
- \overload
\since 4.6
+ \overload toString()
*/
QString QDate::toString(const QString &format) const
{
@@ -1414,9 +1432,8 @@ QDate QDate::addDays(qint64 ndays) const
}
/*!
- \fn QDate QDate::addDuration(std::chrono::days ndays) const
-
\since 6.4
+ \fn QDate QDate::addDuration(std::chrono::days ndays) const
Returns a QDate object containing a date \a ndays later than the
date of this object (or earlier if \a ndays is negative).
@@ -1436,6 +1453,8 @@ QDate QDate::addDays(qint64 ndays) const
*/
/*!
+ \overload primary
+
Returns a QDate object containing a date \a nmonths later than the
date of this object (or earlier if \a nmonths is negative).
@@ -1477,7 +1496,7 @@ QDate QDate::addMonths(int nmonths, QCalendar cal) const
}
/*!
- \overload
+ \overload addMonths()
*/
QDate QDate::addMonths(int nmonths) const
@@ -1509,6 +1528,8 @@ QDate QDate::addMonths(int nmonths) const
}
/*!
+ \overload primary
+
Returns a QDate object containing a date \a nyears later than the
date of this object (or earlier if \a nyears is negative).
@@ -1542,7 +1563,7 @@ QDate QDate::addYears(int nyears, QCalendar cal) const
}
/*!
- \overload
+ \overload addYears()
*/
QDate QDate::addYears(int nyears) const
@@ -1638,6 +1659,7 @@ qint64 QDate::daysTo(QDate d) const
#if QT_CONFIG(datestring) // depends on, so implies, textdate
/*!
+ \overload
\fn QDate QDate::fromString(const QString &string, Qt::DateFormat format)
Returns the QDate represented by the \a string, using the
@@ -1651,8 +1673,8 @@ qint64 QDate::daysTo(QDate d) const
*/
/*!
- \overload
\since 6.0
+ \overload fromString()
*/
QDate QDate::fromString(QStringView string, Qt::DateFormat format)
{
@@ -1702,6 +1724,7 @@ QDate QDate::fromString(QStringView string, Qt::DateFormat format)
}
/*!
+ \overload primary
\fn QDate QDate::fromString(const QString &string, const QString &format, int baseYear, QCalendar cal)
Returns the QDate represented by the \a string, using the \a
@@ -1833,14 +1856,14 @@ QDate QDate::fromString(QStringView string, Qt::DateFormat format)
*/
/*!
- \fn QDate QDate::fromString(QStringView string, QStringView format, QCalendar cal)
- \overload
\since 6.0
+ \overload fromString()
+ \fn QDate QDate::fromString(QStringView string, QStringView format, QCalendar cal)
*/
/*!
- \overload
\since 6.0
+ \overload fromString()
*/
QDate QDate::fromString(const QString &string, QStringView format, int baseYear, QCalendar cal)
{
@@ -1860,34 +1883,34 @@ QDate QDate::fromString(const QString &string, QStringView format, int baseYear,
}
/*!
- \fn QDate QDate::fromString(const QString &string, const QString &format, QCalendar cal)
- \overload
\since 5.14
+ \overload fromString()
+ \fn QDate QDate::fromString(const QString &string, const QString &format, QCalendar cal)
*/
/*!
- \fn QDate QDate::fromString(const QString &string, QStringView format, QCalendar cal)
- \overload
\since 6.0
+ \overload fromString()
+ \fn QDate QDate::fromString(const QString &string, QStringView format, QCalendar cal)
*/
/*!
- \fn QDate QDate::fromString(QStringView string, QStringView format, int baseYear, QCalendar cal)
- \overload
\since 6.7
+ \overload fromString()
+ \fn QDate QDate::fromString(QStringView string, QStringView format, int baseYear, QCalendar cal)
*/
/*!
- \fn QDate QDate::fromString(QStringView string, QStringView format, int baseYear)
- \overload
\since 6.7
+ \overload fromString()
+ \fn QDate QDate::fromString(QStringView string, QStringView format, int baseYear)
Uses a default-constructed QCalendar.
*/
/*!
- \overload
\since 6.7
+ \overload fromString()
Uses a default-constructed QCalendar.
*/
@@ -1897,16 +1920,16 @@ QDate QDate::fromString(const QString &string, QStringView format, int baseYear)
}
/*!
- \fn QDate QDate::fromString(const QString &string, const QString &format, int baseYear)
- \overload
\since 6.7
+ \overload fromString()
+ \fn QDate QDate::fromString(const QString &string, const QString &format, int baseYear)
Uses a default-constructed QCalendar.
*/
#endif // datestring
/*!
- \overload
+ \overload isValid()
Returns \c true if the specified date (\a year, \a month, and \a day) is
valid in the Gregorian calendar; otherwise returns \c false.
@@ -2037,6 +2060,8 @@ QTime::QTime(int h, int m, int s, int ms)
*/
/*!
+ \overload primary
+
Returns \c true if the time is valid; otherwise returns \c false. For example,
the time 23:30:55.746 is valid, but 24:12:30 is invalid.
@@ -2115,7 +2140,7 @@ int QTime::msec() const
#if QT_CONFIG(datestring) // depends on, so implies, textdate
/*!
- \overload
+ \overload toString()
Returns the time as a string. The \a format parameter determines
the format of the string.
@@ -2155,6 +2180,7 @@ QString QTime::toString(Qt::DateFormat format) const
}
/*!
+ \overload primary
\fn QString QTime::toString(const QString &format) const
\fn QString QTime::toString(QStringView format) const
@@ -2261,11 +2287,11 @@ QString QTime::toString(Qt::DateFormat format) const
\sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString()
*/
-// ### Qt 7 The 't' format specifiers should be specific to QDateTime (compare fromString).
QString QTime::toString(QStringView format) const
{
return QLocale::c().toString(*this, format);
}
+// ### Qt 7 The 't' format specifiers should be specific to QDateTime (compare fromString).
#endif // datestring
/*!
@@ -2557,6 +2583,7 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
}
/*!
+ \overload
\fn QTime QTime::fromString(const QString &string, Qt::DateFormat format)
Returns the time represented in the \a string as a QTime using the
@@ -2566,8 +2593,8 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
*/
/*!
- \overload
\since 6.0
+ \overload fromString()
*/
QTime QTime::fromString(QStringView string, Qt::DateFormat format)
{
@@ -2586,6 +2613,7 @@ QTime QTime::fromString(QStringView string, Qt::DateFormat format)
}
/*!
+ \overload primary
\fn QTime QTime::fromString(const QString &string, const QString &format)
Returns the QTime represented by the \a string, using the \a
@@ -2664,14 +2692,14 @@ QTime QTime::fromString(QStringView string, Qt::DateFormat format)
*/
/*!
- \fn QTime QTime::fromString(QStringView string, QStringView format)
- \overload
\since 6.0
+ \overload fromString()
+ \fn QTime QTime::fromString(QStringView string, QStringView format)
*/
/*!
- \overload
\since 6.0
+ \overload fromString()
*/
QTime QTime::fromString(const QString &string, QStringView format)
{
@@ -2691,7 +2719,7 @@ QTime QTime::fromString(const QString &string, QStringView format)
/*!
- \overload
+ \overload isValid()
Returns \c true if the specified time is valid; otherwise returns
false.
@@ -4012,6 +4040,7 @@ QDateTime::QDateTime(QDate date, QTime time, Qt::TimeSpec spec, int offsetSecond
/*!
\since 5.2
+ \overload primary
Constructs a datetime with the given \a date and \a time, using the time
representation described by \a timeZone.
@@ -4620,7 +4649,7 @@ void QDateTime::setSecsSinceEpoch(qint64 secs)
#if QT_CONFIG(datestring) // depends on, so implies, textdate
/*!
- \overload
+ \overload toString()
Returns the datetime as a string in the \a format given.
@@ -4712,9 +4741,10 @@ QString QDateTime::toString(Qt::DateFormat format) const
}
/*!
+ \since 5.14
+ \overload primary
\fn QString QDateTime::toString(const QString &format, QCalendar cal) const
\fn QString QDateTime::toString(QStringView format, QCalendar cal) const
- \since 5.14
Returns the datetime as a string. The \a format parameter determines the
format of the result string. If \a cal is supplied, it determines the
@@ -4761,8 +4791,8 @@ QString QDateTime::toString(QStringView format, QCalendar cal) const
// Out-of-line no-calendar overloads, since QCalendar is a non-trivial type
/*!
- \overload
\since 5.10
+ \overload toString()
*/
QString QDateTime::toString(QStringView format) const
{
@@ -4770,8 +4800,8 @@ QString QDateTime::toString(QStringView format) const
}
/*!
- \overload
\since 4.6
+ \overload toString()
*/
QString QDateTime::toString(const QString &format) const
{
@@ -5370,6 +5400,7 @@ Qt::weak_ordering compareThreeWay(const QDateTime &lhs, const QDateTime &rhs)
/*!
\since 6.5
+ \overload primary
\fn QDateTime QDateTime::currentDateTime(const QTimeZone &zone)
Returns the system clock's current datetime, using the time representation
@@ -5379,8 +5410,8 @@ Qt::weak_ordering compareThreeWay(const QDateTime &lhs, const QDateTime &rhs)
*/
/*!
- \overload
\since 0.90
+ \overload currentDateTime()
*/
QDateTime QDateTime::currentDateTime()
{
@@ -5426,8 +5457,9 @@ QDateTime QDateTime::currentDateTimeUtc()
*/
/*!
- \fn template <typename Clock, typename Duration> QDateTime QDateTime::fromStdTimePoint(const std::chrono::time_point<Clock, Duration> &time)
\since 6.4
+ \overload primary
+ \fn template <typename Clock, typename Duration> QDateTime QDateTime::fromStdTimePoint(const std::chrono::time_point<Clock, Duration> &time)
Constructs a datetime representing the same point in time as \a time,
using Qt::UTC as its time representation.
@@ -5451,7 +5483,7 @@ QDateTime QDateTime::currentDateTimeUtc()
/*!
\since 6.4
- \overload
+ \overload fromStdTimePoint()
Constructs a datetime representing the same point in time as \a time,
using Qt::UTC as its time representation.
@@ -5627,7 +5659,7 @@ qint64 QDateTime::currentSecsSinceEpoch() noexcept
#if QT_DEPRECATED_SINCE(6, 9)
/*!
\since 5.2
- \overload
+ \overload fromMSecsSinceEpoch()
\deprecated [6.9] Pass a \l QTimeZone instead, or omit \a spec and \a offsetSeconds.
Returns a datetime representing a moment the given number \a msecs of
@@ -5656,7 +5688,7 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int of
/*!
\since 5.8
- \overload
+ \overload fromSecsSinceEpoch
\deprecated [6.9] Pass a \l QTimeZone instead, or omit \a spec and \a offsetSeconds.
Returns a datetime representing a moment the given number \a secs of seconds
@@ -5686,6 +5718,7 @@ QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offs
/*!
\since 5.2
+ \overload primary
Returns a datetime representing a moment the given number \a msecs of
milliseconds after the start, in UTC, of the year 1970, described as
@@ -5707,7 +5740,7 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone
}
/*!
- \overload
+ \overload fromMSecsSinceEpoch()
*/
QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs)
{
@@ -5716,6 +5749,7 @@ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs)
/*!
\since 5.8
+ \overload primary
Returns a datetime representing a moment the given number \a secs of seconds
after the start, in UTC, of the year 1970, described as specified by \a
@@ -5737,7 +5771,7 @@ QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs, const QTimeZone &timeZone)
}
/*!
- \overload
+ \overload fromSecsSinceEpoch()
*/
QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs)
{
@@ -5747,6 +5781,7 @@ QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs)
#if QT_CONFIG(datestring) // depends on, so implies, textdate
/*!
+ \overload
\fn QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format)
Returns the QDateTime represented by the \a string, using the
@@ -5759,8 +5794,8 @@ QDateTime QDateTime::fromSecsSinceEpoch(qint64 secs)
*/
/*!
- \overload
\since 6.0
+ \overload fromString()
*/
QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format)
{
@@ -5901,6 +5936,7 @@ QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format)
}
/*!
+ \overload primary
\fn QDateTime QDateTime::fromString(const QString &string, const QString &format, int baseYear, QCalendar cal)
Returns the QDateTime represented by the \a string, using the \a
@@ -5984,14 +6020,14 @@ QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format)
*/
/*!
- \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, QCalendar cal)
- \overload
\since 6.0
+ \overload fromString()
+ \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, QCalendar cal)
*/
/*!
- \overload
\since 6.0
+ \overload fromString()
*/
QDateTime QDateTime::fromString(const QString &string, QStringView format, int baseYear,
QCalendar cal)
@@ -6015,34 +6051,34 @@ QDateTime QDateTime::fromString(const QString &string, QStringView format, int b
}
/*!
- \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, QCalendar cal)
- \overload
\since 5.14
+ \overload fromString()
+ \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, QCalendar cal)
*/
/*!
- \fn QDateTime QDateTime::fromString(const QString &string, QStringView format, QCalendar cal)
- \overload
\since 6.0
+ \overload fromString()
+ \fn QDateTime QDateTime::fromString(const QString &string, QStringView format, QCalendar cal)
*/
/*!
- \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, int baseYear, QCalendar cal)
- \overload
\since 6.7
+ \overload fromString()
+ \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, int baseYear, QCalendar cal)
*/
/*!
- \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, int baseYear)
- \overload
\since 6.7
+ \overload fromString()
+ \fn QDateTime QDateTime::fromString(QStringView string, QStringView format, int baseYear)
Uses a default-constructed QCalendar.
*/
/*!
- \overload
\since 6.7
+ \overload fromString()
Uses a default-constructed QCalendar.
*/
@@ -6052,9 +6088,9 @@ QDateTime QDateTime::fromString(const QString &string, QStringView format, int b
}
/*!
- \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, int baseYear)
- \overload
\since 6.7
+ \overload fromString()
+ \fn QDateTime QDateTime::fromString(const QString &string, const QString &format, int baseYear)
Uses a default-constructed QCalendar.
*/
diff --git a/src/corelib/time/qgregoriancalendar.cpp b/src/corelib/time/qgregoriancalendar.cpp
index d46d24ac30d..dfb99c5073d 100644
--- a/src/corelib/time/qgregoriancalendar.cpp
+++ b/src/corelib/time/qgregoriancalendar.cpp
@@ -31,6 +31,7 @@ static_assert(qDivMod<86400>(-172800).remainder == 0);
/*!
\since 5.14
+ \internal
\class QGregorianCalendar
\inmodule QtCore
diff --git a/src/corelib/time/qjalalicalendar.cpp b/src/corelib/time/qjalalicalendar.cpp
index 8bc9fe125e7..683ce6e7712 100644
--- a/src/corelib/time/qjalalicalendar.cpp
+++ b/src/corelib/time/qjalalicalendar.cpp
@@ -39,6 +39,7 @@ qint64 firstDayOfYear(int year, int cycleNo)
/*!
\since 5.14
+ \internal
\class QJalaliCalendar
\inmodule QtCore
diff --git a/src/corelib/time/qjuliancalendar.cpp b/src/corelib/time/qjuliancalendar.cpp
index 47da952b84a..cf3718f471d 100644
--- a/src/corelib/time/qjuliancalendar.cpp
+++ b/src/corelib/time/qjuliancalendar.cpp
@@ -13,6 +13,7 @@ using namespace QRoundingDown;
/*!
\since 5.14
+ \internal
\class QJulianCalendar
\inmodule QtCore
diff --git a/src/corelib/time/qmilankoviccalendar.cpp b/src/corelib/time/qmilankoviccalendar.cpp
index a3ffa2a3053..14aef83afe3 100644
--- a/src/corelib/time/qmilankoviccalendar.cpp
+++ b/src/corelib/time/qmilankoviccalendar.cpp
@@ -13,6 +13,7 @@ using namespace QRoundingDown;
/*!
\since 5.14
+ \internal
\class QMilankovicCalendar
\inmodule QtCore
diff --git a/src/corelib/time/qromancalendar.cpp b/src/corelib/time/qromancalendar.cpp
index ae113cf8323..a8ce027275a 100644
--- a/src/corelib/time/qromancalendar.cpp
+++ b/src/corelib/time/qromancalendar.cpp
@@ -9,6 +9,7 @@ QT_BEGIN_NAMESPACE
/*!
\since 5.14
+ \internal
\class QRomanCalendar
\inmodule QtCore
diff --git a/src/corelib/time/qtimezone.cpp b/src/corelib/time/qtimezone.cpp
index f44c681ea80..7b43aab22d1 100644
--- a/src/corelib/time/qtimezone.cpp
+++ b/src/corelib/time/qtimezone.cpp
@@ -29,7 +29,7 @@ static QTimeZonePrivate *newBackendTimeZone()
return new QMacTimeZonePrivate();
#elif defined(Q_OS_ANDROID)
return new QAndroidTimeZonePrivate();
-#elif defined(Q_OS_UNIX)
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
return new QTzTimeZonePrivate();
#elif QT_CONFIG(icu)
return new QIcuTimeZonePrivate();
@@ -50,7 +50,7 @@ static QTimeZonePrivate *newBackendTimeZone(const QByteArray &ianaId)
return new QMacTimeZonePrivate(ianaId);
#elif defined(Q_OS_ANDROID)
return new QAndroidTimeZonePrivate(ianaId);
-#elif defined(Q_OS_UNIX)
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
return new QTzTimeZonePrivate(ianaId);
#elif QT_CONFIG(icu)
return new QIcuTimeZonePrivate(ianaId);
@@ -1482,7 +1482,8 @@ QTimeZone QTimeZone::utc()
bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId)
{
-#if defined(Q_OS_UNIX) && !(defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN))
+#if defined(Q_OS_UNIX) && !(QT_CONFIG(timezone_tzdb) || defined(Q_OS_DARWIN) \
+ || defined(Q_OS_ANDROID) || defined(Q_OS_VXWORKS))
// Keep #if-ery consistent with selection of QTzTimeZonePrivate in
// newBackendTimeZone(). Skip the pre-check, as the TZ backend accepts POSIX
// zone IDs, which need not be valid IANA IDs. See also QTBUG-112006.
diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h
index 804c28af372..0f19d1fd025 100644
--- a/src/corelib/time/qtimezoneprivate_p.h
+++ b/src/corelib/time/qtimezoneprivate_p.h
@@ -388,7 +388,7 @@ private:
QJniObject androidTimeZone;
};
-#elif defined(Q_OS_UNIX)
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
struct QTzTransitionTime
{
qint64 atMSecsSinceEpoch;
diff --git a/src/corelib/tools/qiterator.qdoc b/src/corelib/tools/qiterator.qdoc
index 3d8ea595167..d517457027a 100644
--- a/src/corelib/tools/qiterator.qdoc
+++ b/src/corelib/tools/qiterator.qdoc
@@ -165,7 +165,7 @@
position between the second and third item, and returns the second
item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
Here's how to iterate over the elements in reverse order:
@@ -211,7 +211,7 @@
position between the second and third item, returning the second
item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
If you want to find all occurrences of a particular value, use
findNext() in a loop.
@@ -260,7 +260,7 @@
position between the second and third item, returning the second
item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
Here's how to iterate over the elements in reverse order:
@@ -321,7 +321,7 @@
position between the second and third item, returning the second
item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
If you want to remove items as you iterate over the set, use
remove().
@@ -718,7 +718,7 @@
next() advances the iterator to the position between the second
and third item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
Here's how to iterate over the elements in reverse order:
@@ -768,7 +768,7 @@
next() advances the iterator to the position between the second
and third item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
Here's how to iterate over the elements in reverse order:
@@ -819,7 +819,7 @@
next() advances the iterator to the position between the second
and third item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
If you want to find all occurrences of a particular value, use
findNext() in a loop. For example:
@@ -867,7 +867,7 @@
next() advances the iterator to the position between the second
and third item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
Here's how to iterate over the elements in reverse order:
@@ -931,7 +931,7 @@
next() advances the iterator to the position between the second
and third item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
Here's how to iterate over the elements in reverse order:
@@ -994,7 +994,7 @@
next() advances the iterator to the position between the second
and third item; and so on.
- \image javaiterators1.png
+ \image javaiterators1.svg Java-style iterators point between items
If you want to find all occurrences of a particular value, use
findNext() in a loop. For example:
diff --git a/src/gui/configure.cmake b/src/gui/configure.cmake
index ad76bb095a0..5001f2deeec 100644
--- a/src/gui/configure.cmake
+++ b/src/gui/configure.cmake
@@ -1217,17 +1217,6 @@ qt_feature("xcb-egl-plugin" PRIVATE
CONDITION QT_FEATURE_egl AND QT_FEATURE_opengl
EMIT_IF QT_FEATURE_xcb
)
-qt_feature("xcb-native-painting" PRIVATE
- LABEL "Native painting (experimental)"
- AUTODETECT OFF
- CONDITION QT_FEATURE_xcb_xlib AND QT_FEATURE_fontconfig AND XRender_FOUND
- EMIT_IF QT_FEATURE_xcb
-)
-qt_feature("xrender" PRIVATE
- LABEL "XRender for native painting"
- CONDITION QT_FEATURE_xcb_native_painting
- EMIT_IF QT_FEATURE_xcb AND QT_FEATURE_xcb_native_painting
-)
qt_feature("xcb-xlib" PRIVATE
LABEL "XCB Xlib"
CONDITION QT_FEATURE_xlib AND X11_XCB_FOUND
diff --git a/src/gui/doc/src/richtext.qdoc b/src/gui/doc/src/richtext.qdoc
index 2fa49a31e03..f94c436bd80 100644
--- a/src/gui/doc/src/richtext.qdoc
+++ b/src/gui/doc/src/richtext.qdoc
@@ -1047,6 +1047,12 @@
\li \c type (\c 1, \c a, \c A, \c square, \c disc, \c circle)
\endlist
+ Additionally, the following attribute is supported by the \c ol tag:
+
+ \list
+ \li \c start
+ \endlist
+
\section1 Table Cell Attributes
The following attributes are supported by the \c td and \c th
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 6ba113ef8fb..4a8b409379c 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -47,6 +47,9 @@
#include <memory>
+#define QT_XFORM_TYPE_MSBFIRST 0
+#define QT_XFORM_TYPE_LSBFIRST 1
+
QT_BEGIN_NAMESPACE
class QCmyk32;
@@ -4447,6 +4450,8 @@ int QImage::metric(PaintDeviceMetric metric) const
trigx += m11; \
trigy += m12;
// END OF MACRO
+
+static
bool qt_xForm_helper(const QTransform &trueMat, int xoffset, int type, int depth,
uchar *dptr, qsizetype dbpl, int p_inc, int dHeight,
const uchar *sptr, qsizetype sbpl, int sWidth, int sHeight)
diff --git a/src/gui/image/qplatformpixmap.h b/src/gui/image/qplatformpixmap.h
index 5621afa4da5..3346ca85375 100644
--- a/src/gui/image/qplatformpixmap.h
+++ b/src/gui/image/qplatformpixmap.h
@@ -33,7 +33,7 @@ public:
enum ClassId { RasterClass, DirectFBClass,
BlitterClass, Direct2DClass,
- X11Class, CustomClass = 1024 };
+ CustomClass = 1024 };
QPlatformPixmap(PixelType pixelType, int classId);
virtual ~QPlatformPixmap();
@@ -111,7 +111,6 @@ protected:
private:
friend class QPixmap;
- friend class QX11PlatformPixmap;
friend class QImagePixmapCleanupHooks; // Needs to set is_cached
int detach_no;
@@ -122,10 +121,6 @@ private:
uint is_cached;
};
-# define QT_XFORM_TYPE_MSBFIRST 0
-# define QT_XFORM_TYPE_LSBFIRST 1
-Q_GUI_EXPORT bool qt_xForm_helper(const QTransform&, int, int, int, uchar*, qsizetype, int, int, const uchar*, qsizetype, int, int);
-
QT_END_NAMESPACE
#endif // QPLATFORMPIXMAP_H
diff --git a/src/gui/itemmodels/qfilesystemmodel.cpp b/src/gui/itemmodels/qfilesystemmodel.cpp
index 622c2e5bc92..9eb31f1b6d4 100644
--- a/src/gui/itemmodels/qfilesystemmodel.cpp
+++ b/src/gui/itemmodels/qfilesystemmodel.cpp
@@ -1787,14 +1787,17 @@ bool QFileSystemModel::event(QEvent *event)
bool QFileSystemModel::rmdir(const QModelIndex &aindex)
{
+ Q_D(QFileSystemModel);
+
QString path = filePath(aindex);
const bool success = QDir().rmdir(path);
-#if QT_CONFIG(filesystemwatcher)
if (success) {
- QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func());
+#if QT_CONFIG(filesystemwatcher)
d->fileInfoGatherer->removePath(path);
- }
#endif
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(aindex.parent());
+ d->removeNode(parentNode, fileName(aindex));
+ }
return success;
}
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
index d27539b2419..c9df5d28a45 100644
--- a/src/gui/painting/qtextureglyphcache.cpp
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -301,6 +301,8 @@ void QImageTextureGlyphCache::fillTexture(const Coord &c,
const QFixedPoint &subPixelPosition)
{
QImage mask = textureMapForGlyph(g, subPixelPosition);
+ if (mask.isNull())
+ return;
#ifdef CACHE_DEBUG
printf("fillTexture of %dx%d at %d,%d in the cache of %dx%d\n", c.w, c.h, c.x, c.y, m_image.width(), m_image.height());
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 15f5cd8f7e8..81d2719117e 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -3959,6 +3959,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break;
case QGles2CommandBuffer::Command::InvalidateFramebuffer:
if (caps.gles && caps.ctxMajor >= 3) {
+ f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.invalidateFramebuffer.fbo);
f->glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
cmd.args.invalidateFramebuffer.attCount,
cmd.args.invalidateFramebuffer.att);
@@ -4881,6 +4882,7 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
if (mayDiscardDepthStencil) {
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::InvalidateFramebuffer;
+ cmd.args.invalidateFramebuffer.fbo = rtTex->framebuffer;
if (caps.needsDepthStencilCombinedAttach) {
cmd.args.invalidateFramebuffer.attCount = 1;
cmd.args.invalidateFramebuffer.att[0] = GL_DEPTH_STENCIL_ATTACHMENT;
diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h
index 725e71a3665..ea061f9d218 100644
--- a/src/gui/rhi/qrhigles2_p.h
+++ b/src/gui/rhi/qrhigles2_p.h
@@ -547,6 +547,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
GLbitfield barriers;
} barrier;
struct {
+ GLuint fbo;
int attCount;
GLenum att[3];
} invalidateFramebuffer;
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
index d1509e4a251..75550439521 100644
--- a/src/gui/text/qfont_p.h
+++ b/src/gui/text/qfont_p.h
@@ -166,6 +166,7 @@ public:
QFontPrivate();
QFontPrivate(const QFontPrivate &other);
+ QFontPrivate &operator=(const QFontPrivate &) = delete;
~QFontPrivate();
QFontEngine *engineForScript(int script) const;
@@ -206,9 +207,6 @@ public:
void setVariableAxis(QFont::Tag tag, float value);
void unsetVariableAxis(QFont::Tag tag);
bool hasVariableAxis(QFont::Tag tag, float value) const;
-
-private:
- QFontPrivate &operator=(const QFontPrivate &) { return *this; }
};
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 11c79f23d25..29fda652ef6 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:critical reason:data-parser
+#include <QtCore/private/qflatmap_p.h>
#include <QtGui/private/qtguiglobal_p.h>
#include "qdebug.h"
#include "qtextformat.h"
@@ -1613,11 +1614,15 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st
{
uint glyphs_shaped = 0;
- hb_buffer_t *buffer = hb_buffer_create();
- hb_buffer_set_unicode_funcs(buffer, hb_qt_get_unicode_funcs());
+ if (!buffer) {
+ buffer = hb_buffer_create();
+ hb_buffer_set_unicode_funcs(buffer, hb_qt_get_unicode_funcs());
+ }
+
hb_buffer_pre_allocate(buffer, itemLength);
if (Q_UNLIKELY(!hb_buffer_allocation_successful(buffer))) {
hb_buffer_destroy(buffer);
+ buffer = nullptr;
return 0;
}
@@ -1671,26 +1676,26 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st
bool dontLigate = hasLetterSpacing && !scriptRequiresOpenType;
- QHash<QFont::Tag, quint32> features;
- features.insert(QFont::Tag("kern"), !!kerningEnabled);
+ QVarLengthFlatMap<QFont::Tag, hb_feature_t, 16> features;
+ auto insertFeature = [&features](QFont::Tag tag, quint32 value) {
+ features.insert(tag, { tag.value(),
+ value,
+ HB_FEATURE_GLOBAL_START,
+ HB_FEATURE_GLOBAL_END });
+ };
+ // fontFeatures have precedence
+ for (const auto &[tag, value]: fontFeatures.asKeyValueRange())
+ insertFeature(tag, value);
+ insertFeature(QFont::Tag("kern"), !!kerningEnabled);
if (dontLigate) {
- features.insert(QFont::Tag("liga"), false);
- features.insert(QFont::Tag("clig"), false);
- features.insert(QFont::Tag("dlig"), false);
- features.insert(QFont::Tag("hlig"), false);
- }
- features.insert(fontFeatures);
-
- QVarLengthArray<hb_feature_t, 16> featureArray;
- for (auto it = features.constBegin(); it != features.constEnd(); ++it) {
- featureArray.append({ it.key().value(),
- it.value(),
- HB_FEATURE_GLOBAL_START,
- HB_FEATURE_GLOBAL_END });
+ insertFeature(QFont::Tag("liga"), false);
+ insertFeature(QFont::Tag("clig"), false);
+ insertFeature(QFont::Tag("dlig"), false);
+ insertFeature(QFont::Tag("hlig"), false);
}
// whitelist cross-platforms shapers only
- static const char *shaper_list[] = {
+ constexpr const char *shaper_list[] = {
"graphite2",
"ot",
"fallback",
@@ -1699,13 +1704,11 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st
bool shapedOk = hb_shape_full(hb_font,
buffer,
- featureArray.constData(),
- features.size(),
+ features.values().constData(),
+ features.values().size(),
shaper_list);
- if (Q_UNLIKELY(!shapedOk)) {
- hb_buffer_destroy(buffer);
+ if (Q_UNLIKELY(!shapedOk))
return 0;
- }
if (Q_UNLIKELY(HB_DIRECTION_IS_BACKWARD(props.direction)))
hb_buffer_reverse(buffer);
@@ -1718,10 +1721,8 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st
num_glyphs = 1;
// ensure we have enough space for shaped glyphs and metrics
- if (Q_UNLIKELY(!ensureSpace(glyphs_shaped + num_glyphs))) {
- hb_buffer_destroy(buffer);
+ if (Q_UNLIKELY(!ensureSpace(glyphs_shaped + num_glyphs)))
return 0;
- }
// fetch the shaped glyphs and metrics
QGlyphLayout g = availableGlyphs(&si).mid(glyphs_shaped, num_glyphs);
@@ -1781,8 +1782,6 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st
glyphs_shaped += num_glyphs;
}
- hb_buffer_destroy(buffer);
-
return glyphs_shaped;
}
@@ -1826,6 +1825,12 @@ QTextEngine::~QTextEngine()
delete layoutData;
delete specialData;
resetFontEngineCache();
+#if QT_CONFIG(harfbuzz)
+ if (buffer) {
+ hb_buffer_destroy(buffer);
+ buffer = nullptr;
+ }
+#endif
}
const QCharAttributes *QTextEngine::attributes() const
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index dffbc129b3a..e513fd598ba 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -40,6 +40,8 @@
#include <stdlib.h>
#include <vector>
+struct hb_buffer_t;
+
QT_BEGIN_NAMESPACE
class QFontPrivate;
@@ -583,6 +585,8 @@ private:
void indexFormats();
void resolveFormats() const;
+ mutable hb_buffer_t *buffer = nullptr;
+
public:
bool atWordSeparator(int position) const;
diff --git a/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp b/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp
index 62d24eefd33..e5ada714ba5 100644
--- a/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp
+++ b/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp
@@ -110,7 +110,8 @@ QGlibNetworkInformationBackend::QGlibNetworkInformationBackend()
connectivityHandlerId = g_signal_connect_swapped(networkMonitor, "notify::connectivity",
G_CALLBACK(updateConnectivity), this);
- networkHandlerId = g_signal_connect_swapped(networkMonitor, "network-changed",
+ // needed until GLib 2.86 for netlink support
+ networkHandlerId = g_signal_connect_swapped(networkMonitor, "notify::network-available",
G_CALLBACK(updateConnectivity), this);
meteredHandlerId = g_signal_connect_swapped(networkMonitor, "notify::network-metered",
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
index 08c9f5d5ba2..2989b4d6df3 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
@@ -161,6 +161,7 @@ static void populateRoleMap()
roleMap[QAccessible::Graphic] = NSAccessibilityImageRole;
roleMap[QAccessible::Tree] = NSAccessibilityOutlineRole;
roleMap[QAccessible::BlockQuote] = NSAccessibilityGroupRole;
+ roleMap[QAccessible::LayeredPane] = NSAccessibilityGroupRole;
}
/*
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
index e0ef6cec794..4c4e5fac962 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -491,7 +491,7 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions;
return;
if (m_panel.visible) {
- const QString selection = QString::fromNSString(m_panel.URL.path);
+ const QString selection = QString::fromNSString(m_panel.URL.path).normalized(QString::NormalizationForm_C);
if (selection != m_currentSelection) {
m_currentSelection = selection;
emit m_helper->currentChanged(QUrl::fromLocalFile(selection));
diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h
index b7ff3bb2a58..7af9cf80355 100644
--- a/src/plugins/platforms/ios/qiostheme.h
+++ b/src/plugins/platforms/ios/qiostheme.h
@@ -26,6 +26,7 @@ public:
Qt::ColorScheme colorScheme() const override;
void requestColorScheme(Qt::ColorScheme scheme) override;
+ Qt::ContrastPreference contrastPreference() const override;
#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
QPlatformMenuItem* createPlatformMenuItem() const override;
diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm
index cdf669f921c..435c21bf579 100644
--- a/src/plugins/platforms/ios/qiostheme.mm
+++ b/src/plugins/platforms/ios/qiostheme.mm
@@ -202,6 +202,12 @@ void QIOSTheme::requestColorScheme(Qt::ColorScheme scheme)
#endif
}
+Qt::ContrastPreference QIOSTheme::contrastPreference() const
+{
+ return UIAccessibilityDarkerSystemColorsEnabled() ? Qt::ContrastPreference::HighContrast : Qt::ContrastPreference::NoPreference;
+}
+
+
void QIOSTheme::applyTheme(UIWindow *window)
{
const UIUserInterfaceStyle style = []{
diff --git a/src/plugins/platforms/ios/quiwindow.mm b/src/plugins/platforms/ios/quiwindow.mm
index bb4268e1c88..62fd9c5d770 100644
--- a/src/plugins/platforms/ios/quiwindow.mm
+++ b/src/plugins/platforms/ios/quiwindow.mm
@@ -54,7 +54,9 @@
if (self.screen == UIScreen.mainScreen) {
// Check if the current userInterfaceStyle reports a different appearance than
// the platformTheme's appearance. We might have set that one based on the UIScreen
+ // Check for changes in the "Increase contrast" setting too.
if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle
+ || previousTraitCollection.accessibilityContrast != self.traitCollection.accessibilityContrast
|| QGuiApplicationPrivate::platformTheme()->colorScheme() != colorScheme) {
QIOSTheme::initializeSystemPalette();
QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
index a87c33c8346..5807d157636 100644
--- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp
+++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
@@ -586,14 +586,7 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface)
void QWasmAccessibility::setHtmlElementVisibility(QAccessibleInterface *iface, bool visible)
{
emscripten::val element = getHtmlElement(iface);
-
- if (visible) {
- setAttribute(element, "aria-hidden", false);
- setAttribute(element, "tabindex", "");
- } else {
- setAttribute(element, "aria-hidden", true); // aria-hidden mean completely hidden; maybe some sort of soft-hidden should be used.
- setAttribute(element, "tabindex", "-1");
- }
+ setAttribute(element, "aria-hidden", !visible);
}
void QWasmAccessibility::setHtmlElementGeometry(QAccessibleInterface *iface)
diff --git a/src/plugins/platforms/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp
index 18a457198f1..a0546fdc215 100644
--- a/src/plugins/platforms/wasm/qwasminputcontext.cpp
+++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp
@@ -40,8 +40,23 @@ void QWasmInputContext::inputCallback(emscripten::val event)
// Some of them should be implemented here later.
qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "inputType : " << inputTypeString;
if (!inputTypeString.compare("deleteContentBackward")) {
- QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);
- QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier);
+
+ QInputMethodQueryEvent queryEvent(Qt::ImQueryAll);
+ QCoreApplication::sendEvent(m_focusObject, &queryEvent);
+ int cursorPosition = queryEvent.value(Qt::ImCursorPosition).toInt();
+
+ int deleteLength = rangesPair.second - rangesPair.first;
+ int deleteFrom = -1;
+ if (cursorPosition > rangesPair.first) {
+ deleteFrom = -(cursorPosition - rangesPair.first);
+ }
+ QInputMethodEvent e;
+ e.setCommitString(QString(), deleteFrom, deleteLength);
+ QCoreApplication::sendEvent(m_focusObject, &e);
+
+ rangesPair.first = 0;
+ rangesPair.second = 0;
+
event.call<void>("stopImmediatePropagation");
return;
} else if (!inputTypeString.compare("deleteContentForward")) {
@@ -138,41 +153,23 @@ void QWasmInputContext::compositionUpdateCallback(emscripten::val event)
const auto compositionStr = QString::fromEcmaString(event["data"]);
qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << compositionStr;
- // WA for IOS.
- // Not sure now because I cannot test it anymore.
-// int replaceSize = 0;
-// emscripten::val win = emscripten::val::global("window");
-// emscripten::val sel = win.call<emscripten::val>("getSelection");
-// if (!sel.isNull() && !sel.isUndefined()
-// && sel["rangeCount"].as<int>() > 0) {
-// QInputMethodQueryEvent queryEvent(Qt::ImQueryAll);
-// QCoreApplication::sendEvent(QGuiApplication::focusObject(), &queryEvent);
-// qCDebug(qLcQpaWasmInputContext) << "Qt surrounding text: " << queryEvent.value(Qt::ImSurroundingText).toString();
-// qCDebug(qLcQpaWasmInputContext) << "Qt current selection: " << queryEvent.value(Qt::ImCurrentSelection).toString();
-// qCDebug(qLcQpaWasmInputContext) << "Qt text before cursor: " << queryEvent.value(Qt::ImTextBeforeCursor).toString();
-// qCDebug(qLcQpaWasmInputContext) << "Qt text after cursor: " << queryEvent.value(Qt::ImTextAfterCursor).toString();
-//
-// const QString &selectedStr = QString::fromEcmaString(sel.call<emscripten::val>("toString"));
-// const auto &preeditStr = preeditString();
-// qCDebug(qLcQpaWasmInputContext) << "Selection.type : " << sel["type"].as<std::string>();
-// qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "Selected: " << selectedStr;
-// qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "PreeditString: " << preeditStr;
-// if (!sel["type"].as<std::string>().compare("Range")) {
-// QString surroundingTextBeforeCursor = queryEvent.value(Qt::ImTextBeforeCursor).toString();
-// if (surroundingTextBeforeCursor.endsWith(selectedStr)) {
-// replaceSize = selectedStr.size();
-// qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "Current Preedit: " << preeditStr << replaceSize;
-// }
-// }
-// emscripten::val range = sel.call<emscripten::val>("getRangeAt", 0);
-// qCDebug(qLcQpaWasmInputContext) << "Range.startOffset : " << range["startOffset"].as<int>();
-// qCDebug(qLcQpaWasmInputContext) << "Range.endOffset : " << range["endOffset"].as<int>();
-// }
-//
-// setPreeditString(compositionStr, replaceSize);
setPreeditString(compositionStr, 0);
}
+void QWasmInputContext::beforeInputCallback(emscripten::val event)
+{
+ emscripten::val ranges = event.call<emscripten::val>("getTargetRanges");
+
+ auto length = ranges["length"].as<int>();
+ for (auto i = 0; i < length; i++) {
+ emscripten::val range = ranges[i];
+ qCDebug(qLcQpaWasmInputContext) << "startOffset" << range["startOffset"].as<int>();
+ qCDebug(qLcQpaWasmInputContext) << "endOffset" << range["endOffset"].as<int>();
+ rangesPair.first = range["startOffset"].as<int>();
+ rangesPair.second = range["endOffset"].as<int>();
+ }
+}
+
QWasmInputContext::QWasmInputContext()
{
qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
diff --git a/src/plugins/platforms/wasm/qwasminputcontext.h b/src/plugins/platforms/wasm/qwasminputcontext.h
index 6d24c7fea0d..97415451b2a 100644
--- a/src/plugins/platforms/wasm/qwasminputcontext.h
+++ b/src/plugins/platforms/wasm/qwasminputcontext.h
@@ -45,6 +45,7 @@ public:
void compositionEndCallback(emscripten::val event);
void compositionStartCallback(emscripten::val event);
void compositionUpdateCallback(emscripten::val event);
+ void beforeInputCallback(emscripten::val event);
void updateGeometry();
@@ -62,6 +63,7 @@ private:
bool m_inputMethodAccepted = false;
QObject *m_focusObject = nullptr;
emscripten::val m_inputElement = emscripten::val::null();
+ QPair<int, int> rangesPair;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 04f52ac9b1b..e49c1dc49f1 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -110,6 +110,7 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
// Set up m_inputElement, which takes focus whenever a Qt text input UI element has
// foucus.
m_inputElement["classList"].call<void>("add", emscripten::val("qt-window-input-element"));
+ m_inputElement.call<void>("setAttribute", std::string("contenteditable"), std::string("true"));
m_inputElement.set("type", "text");
m_inputElement["style"].set("position", "absolute");
m_inputElement["style"].set("left", 0);
@@ -227,6 +228,8 @@ void QWasmWindow::registerEventHandlers()
[this](emscripten::val event){ handleCompositionStartEvent(event); });
m_compositionEndCallback = QWasmEventHandler(m_window, "compositionend",
[this](emscripten::val event){ handleCompositionEndEvent(event); });
+ m_beforeInputCallback = QWasmEventHandler(m_window, "beforeinput",
+ [this](emscripten::val event){ handleBeforeInputEvent(event); });
}
QWasmWindow::~QWasmWindow()
@@ -789,6 +792,16 @@ void QWasmWindow::handleCompositionEndEvent(emscripten::val event)
m_focusHelper.set("innerHTML", std::string());
}
+void QWasmWindow::handleBeforeInputEvent(emscripten::val event)
+{
+ qWarning() << Q_FUNC_INFO;
+
+ if (QWasmInputContext *inputContext = QWasmIntegration::get()->wasmInputContext(); inputContext->isActive())
+ inputContext->beforeInputCallback(event);
+ // else
+ // m_focusHelper.set("innerHTML", std::string());
+}
+
void QWasmWindow::handlePointerEnterLeaveEvent(const PointerEvent &event)
{
if (processPointerEnterLeave(event))
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index cbe930dce89..8e6e5021dcf 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -145,6 +145,7 @@ private:
void handleCompositionStartEvent(emscripten::val event);
void handleCompositionUpdateEvent(emscripten::val event);
void handleCompositionEndEvent(emscripten::val event);
+ void handleBeforeInputEvent(emscripten::val event);
void handlePointerEnterLeaveEvent(const PointerEvent &event);
bool processPointerEnterLeave(const PointerEvent &event);
@@ -183,6 +184,7 @@ private:
QWasmEventHandler m_compositionStartCallback;
QWasmEventHandler m_compositionUpdateCallback;
QWasmEventHandler m_compositionEndCallback;
+ QWasmEventHandler m_beforeInputCallback;
QWasmEventHandler m_pointerDownCallback;
QWasmEventHandler m_pointerMoveCallback;
diff --git a/src/plugins/platforms/wayland/CMakeLists.txt b/src/plugins/platforms/wayland/CMakeLists.txt
index 7e3589def6b..0ce9a4b091c 100644
--- a/src/plugins/platforms/wayland/CMakeLists.txt
+++ b/src/plugins/platforms/wayland/CMakeLists.txt
@@ -17,14 +17,6 @@ qt_internal_add_module(WaylandGlobalPrivate
NO_GENERATE_CPP_EXPORTS
)
-# Work around 115101.
-# If nothing depends on the WaylandGlobalPrivate target it doesn't run custom commands that the
-# target depends on. WaylandGlobalPrivate_ensure_sync_headers makes sure that 'all' depends on
-# WaylandGlobalPrivate_sync_headers.
-# TODO: This needs to be removed once the fix for QTBUG-115101 is merged in qtbase.
-add_custom_target(WaylandGlobalPrivate_ensure_sync_headers ALL)
-add_dependencies(WaylandGlobalPrivate_ensure_sync_headers WaylandGlobalPrivate_sync_headers)
-
# special case begin
# TODO: Ideally these macros would be part of the qtwaylandscanner tool, and not the compositor/client
include(../../../../src/tools/qtwaylandscanner/Qt6WaylandClientMacros.cmake)
diff --git a/src/plugins/platforms/windows/qwindowsiconengine.cpp b/src/plugins/platforms/windows/qwindowsiconengine.cpp
index 71103183183..edc1ea3a9dc 100644
--- a/src/plugins/platforms/windows/qwindowsiconengine.cpp
+++ b/src/plugins/platforms/windows/qwindowsiconengine.cpp
@@ -63,8 +63,8 @@ static QString getGlyphs(QStringView iconName)
{"go-home"_L1, u"\ue80f"},
// {"go-jump"_L1, u"\uf719"},
//{"go-last"_L1, u"\ue5dd"},
- {"go-next"_L1, u"\ue893"},
- {"go-previous"_L1, u"\ue892"},
+ {"go-next"_L1, u"\ue72a"},
+ {"go-previous"_L1, u"\ue72b"},
//{"go-top"_L1, u"\ue25a"},
{"go-up"_L1, u"\ue74a"},
{"help-about"_L1, u"\ue946"},
@@ -100,7 +100,7 @@ static QString getGlyphs(QStringView iconName)
//{"object-flip-vertical"_L1, u"\u"},
{"object-rotate-left"_L1, u"\ue80c"},
{"object-rotate-right"_L1, u"\ue80d"},
- //{"process-stop"_L1, u"\ue5c9"},
+ {"process-stop"_L1, u"\uf140"},
{"system-lock-screen"_L1, u"\uee3f"},
{"system-log-out"_L1, u"\uf3b1"},
//{"system-run"_L1, u"\u"},
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index 482810d5b7e..7908f22d99d 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -874,9 +874,8 @@ const QWindowsScreen *QWindowsScreenManager::screenAtDp(const QPoint &p) const
return nullptr;
}
-const QWindowsScreen *QWindowsScreenManager::screenForHwnd(HWND hwnd) const
+const QWindowsScreen *QWindowsScreenManager::screenForMonitor(HMONITOR hMonitor) const
{
- HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
if (hMonitor == nullptr)
return nullptr;
const auto it =
@@ -889,4 +888,18 @@ const QWindowsScreen *QWindowsScreenManager::screenForHwnd(HWND hwnd) const
return it != m_screens.cend() ? *it : nullptr;
}
+const QWindowsScreen *QWindowsScreenManager::screenForHwnd(HWND hwnd) const
+{
+ HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
+ return screenForMonitor(hMonitor);
+}
+
+const QWindowsScreen *QWindowsScreenManager::screenForRect(const RECT *rect) const
+{
+ if (rect == nullptr)
+ return nullptr;
+ HMONITOR hMonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONULL);
+ return screenForMonitor(hMonitor);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h
index 8d555998388..0032fd462cb 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.h
+++ b/src/plugins/platforms/windows/qwindowsscreen.h
@@ -115,12 +115,14 @@ public:
const QWindowsScreen *screenAtDp(const QPoint &p) const;
const QWindowsScreen *screenForHwnd(HWND hwnd) const;
+ const QWindowsScreen *screenForRect(const RECT *rect) const;
static bool isSingleScreen();
private:
void addScreen(const QWindowsScreenData &screenData);
void removeScreen(int index);
+ const QWindowsScreen *screenForMonitor(HMONITOR monitor) const;
HWND m_displayChangeObserver = nullptr;
WindowsScreenList m_screens;
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 01716fba60c..72daffb56b1 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -466,15 +466,17 @@ static bool applyBlurBehindWindow(HWND hwnd)
return result;
}
+static bool shouldShowTitlebarButton(Qt::WindowFlags flags, Qt::WindowFlags button)
+{
+ return !flags.testFlag(Qt::CustomizeWindowHint) || flags.testFlags(Qt::CustomizeWindowHint | button);
+}
+
// from qwidget_win.cpp, pass flags separately in case they have been "autofixed".
static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags)
{
- if ((flags & Qt::MSWindowsFixedSizeDialogHint) || !(flags & Qt::WindowMaximizeButtonHint))
- return false;
- // if the user explicitly asked for the maximize button, we try to add
- // it even if the window has fixed size.
- return (flags & Qt::CustomizeWindowHint) ||
- w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX);
+ return !flags.testFlag(Qt::MSWindowsFixedSizeDialogHint) &&
+ (shouldShowTitlebarButton(flags, Qt::WindowMaximizeButtonHint) ||
+ w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX));
}
bool QWindowsWindow::hasNoNativeFrame(HWND hwnd, Qt::WindowFlags flags)
@@ -805,6 +807,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag
if (topLevel) {
if ((type == Qt::Window || dialog || tool)) {
+ const bool defaultTitlebar = !flags.testFlag(Qt::CustomizeWindowHint);
if (!(flags & Qt::FramelessWindowHint)) {
style |= WS_POPUP;
if (flags & Qt::MSWindowsFixedSizeDialogHint) {
@@ -812,16 +815,16 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag
} else {
style |= WS_THICKFRAME;
}
- if (flags & Qt::WindowTitleHint)
+ if (defaultTitlebar || flags.testFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint))
style |= WS_CAPTION; // Contains WS_DLGFRAME
}
- if (flags & Qt::WindowSystemMenuHint)
+ if (defaultTitlebar || flags.testFlags(Qt::CustomizeWindowHint | Qt::WindowSystemMenuHint))
style |= WS_SYSMENU;
- else if (dialog && (flags & Qt::WindowCloseButtonHint) && !(flags & Qt::FramelessWindowHint)) {
+ else if (dialog && (defaultTitlebar || flags.testFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint)) && !(flags & Qt::FramelessWindowHint)) {
style |= WS_SYSMENU | WS_BORDER; // QTBUG-2027, dialogs without system menu.
exStyle |= WS_EX_DLGMODALFRAME;
}
- const bool showMinimizeButton = flags & Qt::WindowMinimizeButtonHint;
+ const bool showMinimizeButton = shouldShowTitlebarButton(flags, Qt::WindowMinimizeButtonHint);
if (showMinimizeButton)
style |= WS_MINIMIZEBOX;
const bool showMaximizeButton = shouldShowMaximizeButton(w, flags);
@@ -2113,7 +2116,8 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
QWindowsThemeCache::clearThemeCache(hwnd);
// Send screen change first, so that the new screen is set during any following resize
- checkForScreenChanged(QWindowsWindow::FromDpiChange);
+ const auto prcNewWindow = reinterpret_cast<const RECT *>(lParam);
+ checkForScreenChanged(QWindowsWindow::FromDpiChange, !m_inSetgeometry ? prcNewWindow : nullptr);
if (!IsZoomed(hwnd))
m_data.restoreGeometry.setSize(m_data.restoreGeometry.size() * scale);
@@ -2136,7 +2140,6 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
// making the SetWindowPos() call.
if (!m_inSetgeometry) {
updateFullFrameMargins();
- const auto prcNewWindow = reinterpret_cast<RECT *>(lParam);
SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
prcNewWindow->right - prcNewWindow->left,
prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
@@ -2351,14 +2354,15 @@ static inline bool equalDpi(const QDpi &d1, const QDpi &d2)
return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second);
}
-void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode)
+void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode, const RECT *suggestedRect)
{
if ((parent() && !parent()->isForeignWindow()) || QWindowsScreenManager::isSingleScreen())
return;
QPlatformScreen *currentScreen = screen();
auto topLevel = isTopLevel_sys() ? m_data.hwnd : GetAncestor(m_data.hwnd, GA_ROOT);
- const QWindowsScreen *newScreen =
+ const QWindowsScreen *newScreen = suggestedRect ?
+ QWindowsContext::instance()->screenManager().screenForRect(suggestedRect) :
QWindowsContext::instance()->screenManager().screenForHwnd(topLevel);
if (newScreen == nullptr || newScreen == currentScreen)
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 499b2a9ca86..ee348f1ac16 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -342,7 +342,7 @@ public:
void stopAlertWindow();
enum ScreenChangeMode { FromGeometryChange, FromDpiChange, FromScreenAdded };
- void checkForScreenChanged(ScreenChangeMode mode = FromGeometryChange);
+ void checkForScreenChanged(ScreenChangeMode mode = FromGeometryChange, const RECT *suggestedRect = nullptr);
void registerTouchWindow();
static void setHasBorderInFullScreenStatic(QWindow *window, bool border);
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
index b2675d5b884..835499d3554 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
@@ -154,6 +154,7 @@ long roleToControlTypeId(QAccessible::Role role)
{QAccessible::WebDocument, UIA_DocumentControlTypeId},
{QAccessible::Heading, UIA_TextControlTypeId},
{QAccessible::BlockQuote, UIA_GroupControlTypeId},
+ {QAccessible::LayeredPane, UIA_PaneControlTypeId},
};
long controlType = mapping.value(role, UIA_CustomControlTypeId);
diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt
index 492d274c2fd..204acd72ba0 100644
--- a/src/plugins/platforms/xcb/CMakeLists.txt
+++ b/src/plugins/platforms/xcb/CMakeLists.txt
@@ -120,30 +120,6 @@ qt_internal_extend_target(XcbQpaPrivate CONDITION CLANG
-ftemplate-depth=1024
)
-qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_xcb_native_painting
- SOURCES
- nativepainting/qbackingstore_x11.cpp nativepainting/qbackingstore_x11_p.h
- nativepainting/qcolormap_x11.cpp nativepainting/qcolormap_x11_p.h
- nativepainting/qpaintengine_x11.cpp nativepainting/qpaintengine_x11_p.h
- nativepainting/qpixmap_x11.cpp nativepainting/qpixmap_x11_p.h
- nativepainting/qpolygonclipper_p.h
- nativepainting/qt_x11_p.h
- nativepainting/qtessellator.cpp nativepainting/qtessellator_p.h
- nativepainting/qxcbnativepainting.cpp nativepainting/qxcbnativepainting.h
- INCLUDE_DIRECTORIES
- nativepainting
-)
-
-qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_xcb_native_painting AND QT_FEATURE_xrender
- PUBLIC_LIBRARIES
- PkgConfig::XRender
-)
-
-qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_fontconfig AND QT_FEATURE_xcb_native_painting
- LIBRARIES
- WrapFreetype::WrapFreetype
-)
-
if(QT_FEATURE_system_xcb_xinput)
qt_internal_extend_target(XcbQpaPrivate LIBRARIES XCB::XINPUT)
else()
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
deleted file mode 100644
index 1ac5f6a64bc..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright (C) 2018 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 "qbackingstore_x11_p.h"
-#include "qxcbwindow.h"
-#include "qpixmap_x11_p.h"
-
-#include <private/qhighdpiscaling_p.h>
-#include <QPainter>
-
-#if QT_CONFIG(xrender)
-# include <X11/extensions/Xrender.h>
-#endif
-
-#define register /* C++17 deprecated register */
-#include <X11/Xlib.h>
-#undef register
-
-QT_BEGIN_NAMESPACE
-
-QXcbNativeBackingStore::QXcbNativeBackingStore(QWindow *window)
- : QPlatformBackingStore(window)
- , m_translucentBackground(false)
-{
- if (QXcbWindow *w = static_cast<QXcbWindow *>(window->handle())) {
- m_translucentBackground = w->connection()->hasXRender() &&
- QImage::toPixelFormat(w->imageFormat()).alphaUsage() == QPixelFormat::UsesAlpha;
- }
-}
-
-QXcbNativeBackingStore::~QXcbNativeBackingStore()
-{}
-
-QPaintDevice *QXcbNativeBackingStore::paintDevice()
-{
- return &m_pixmap;
-}
-
-void QXcbNativeBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
-{
- if (m_pixmap.isNull())
- return;
-
- QSize pixmapSize = m_pixmap.size();
-
- QRegion clipped = region;
- clipped &= QRect(QPoint(), QHighDpi::toNativePixels(window->size(), window));
- clipped &= QRect(0, 0, pixmapSize.width(), pixmapSize.height()).translated(-offset);
-
- QRect br = clipped.boundingRect();
- if (br.isNull())
- return;
-
- QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
- if (!platformWindow) {
- qWarning("QXcbBackingStore::flush: QWindow has no platform window (QTBUG-32681)");
- return;
- }
-
- Window wid = platformWindow->xcb_window();
- Pixmap pid = qt_x11PixmapHandle(m_pixmap);
-
- QList<XRectangle> clipRects = qt_region_to_xrectangles(clipped);
-
-#if QT_CONFIG(xrender)
- if (m_translucentBackground)
- {
- XWindowAttributes attrib;
- XGetWindowAttributes(display(), wid, &attrib);
- XRenderPictFormat *format = XRenderFindVisualFormat(display(), attrib.visual);
-
- Picture srcPic = qt_x11PictureHandle(m_pixmap);
- Picture dstPic = XRenderCreatePicture(display(), wid, format, 0, 0);
-
- XRenderSetPictureClipRectangles(display(), dstPic, 0, 0, clipRects.constData(), clipRects.size());
-
- XRenderComposite(display(), PictOpSrc, srcPic, 0L /*None*/, dstPic, br.x() + offset.x(),
- br.y() + offset.y(), 0, 0, br.x(), br.y(), br.width(), br.height());
-
- XRenderFreePicture(display(), dstPic);
- }
- else
-#endif
- {
- GC gc = XCreateGC(display(), wid, 0, nullptr);
-
- if (clipRects.size() != 1)
- XSetClipRectangles(display(), gc, 0, 0, clipRects.data(), clipRects.size(), YXBanded);
-
- XCopyArea(display(), pid, wid, gc, br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), br.x(), br.y());
- XFreeGC(display(), gc);
- }
-
-
- if (platformWindow->needsSync()) {
- platformWindow->updateSyncRequestCounter();
- } else {
- XFlush(display());
- }
-}
-
-QImage QXcbNativeBackingStore::toImage() const
-{
- return m_pixmap.toImage();
-}
-
-void QXcbNativeBackingStore::resize(const QSize &size, const QRegion &staticContents)
-{
- if (size == m_pixmap.size())
- return;
-
- QPixmap newPixmap(size);
-
-#if QT_CONFIG(xrender)
- if (m_translucentBackground && newPixmap.depth() != 32)
- qt_x11Pixmap(newPixmap)->convertToARGB32();
-#endif
-
- if (!m_pixmap.isNull()) {
- Pixmap from = qt_x11PixmapHandle(m_pixmap);
- Pixmap to = qt_x11PixmapHandle(newPixmap);
- QRect br = staticContents.boundingRect().intersected(QRect(QPoint(0, 0), size));
-
- if (!br.isEmpty()) {
- GC gc = XCreateGC(display(), to, 0, nullptr);
- XCopyArea(display(), from, to, gc, br.x(), br.y(), br.width(), br.height(), br.x(), br.y());
- XFreeGC(display(), gc);
- }
- }
-
- m_pixmap = newPixmap;
-}
-
-bool QXcbNativeBackingStore::scroll(const QRegion &area, int dx, int dy)
-{
- if (m_pixmap.isNull())
- return false;
-
- QRect rect = area.boundingRect();
- Pixmap pix = qt_x11PixmapHandle(m_pixmap);
-
- GC gc = XCreateGC(display(), pix, 0, nullptr);
- XCopyArea(display(), pix, pix, gc,
- rect.x(), rect.y(), rect.width(), rect.height(),
- rect.x()+dx, rect.y()+dy);
- XFreeGC(display(), gc);
- return true;
-}
-
-void QXcbNativeBackingStore::beginPaint(const QRegion &region)
-{
- QX11PlatformPixmap *x11pm = qt_x11Pixmap(m_pixmap);
- if (x11pm)
- x11pm->setIsBackingStore(true);
-
-#if QT_CONFIG(xrender)
- if (m_translucentBackground) {
- const QList<XRectangle> xrects = qt_region_to_xrectangles(region);
- const XRenderColor color = { 0, 0, 0, 0 };
- XRenderFillRectangles(display(), PictOpSrc,
- qt_x11PictureHandle(m_pixmap), &color,
- xrects.constData(), xrects.size());
- }
-#else
- Q_UNUSED(region);
-#endif
-}
-
-Display *QXcbNativeBackingStore::display() const
-{
- return static_cast<Display *>(static_cast<QXcbWindow *>(window()->handle())->connection()->xlib_display());
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h
deleted file mode 100644
index b730b076d3f..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2018 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
-
-#pragma once
-
-#include <qpa/qplatformbackingstore.h>
-
-typedef struct _XDisplay Display;
-
-QT_BEGIN_NAMESPACE
-
-class QXcbWindow;
-
-class QXcbNativeBackingStore : public QPlatformBackingStore
-{
-public:
- QXcbNativeBackingStore(QWindow *window);
- ~QXcbNativeBackingStore();
-
- QPaintDevice *paintDevice() override;
- void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
-
- QImage toImage() const override;
-
- void resize(const QSize &size, const QRegion &staticContents) override;
- bool scroll(const QRegion &area, int dx, int dy) override;
-
- void beginPaint(const QRegion &region) override;
-
-private:
- Display *display() const;
-
- QPixmap m_pixmap;
- bool m_translucentBackground;
-};
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp
deleted file mode 100644
index fddd3e03989..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp
+++ /dev/null
@@ -1,615 +0,0 @@
-// Copyright (C) 2018 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 <QVarLengthArray>
-
-#include <private/qguiapplication_p.h>
-
-#include "qcolormap_x11_p.h"
-#include "qxcbnativepainting.h"
-#include "qt_x11_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QXcbColormapPrivate
-{
-public:
- QXcbColormapPrivate()
- : ref(1), mode(QXcbColormap::Direct), depth(0),
- colormap(0), defaultColormap(true),
- visual(0), defaultVisual(true),
- r_max(0), g_max(0), b_max(0),
- r_shift(0), g_shift(0), b_shift(0)
- {}
-
- QAtomicInt ref;
-
- QXcbColormap::Mode mode;
- int depth;
-
- Colormap colormap;
- bool defaultColormap;
-
- Visual *visual;
- bool defaultVisual;
-
- int r_max;
- int g_max;
- int b_max;
-
- uint r_shift;
- uint g_shift;
- uint b_shift;
-
- QList<QColor> colors;
- QList<int> pixels;
-};
-
-static uint right_align(uint v)
-{
- while (!(v & 0x1))
- v >>= 1;
- return v;
-}
-
-static int cube_root(int v)
-{
- if (v == 1)
- return 1;
- // brute force algorithm
- int i = 1;
- for (;;) {
- const int b = i * i * i;
- if (b <= v) {
- ++i;
- } else {
- --i;
- break;
- }
- }
- return i;
-}
-
-static Visual *find_visual(Display *display,
- int screen,
- int visual_class,
- int visual_id,
- int *depth,
- bool *defaultVisual)
-{
- XVisualInfo *vi, rvi;
- int count;
-
- uint mask = VisualScreenMask;
- rvi.screen = screen;
-
- if (visual_class != -1) {
- rvi.c_class = visual_class;
- mask |= VisualClassMask;
- }
- if (visual_id != -1) {
- rvi.visualid = visual_id;
- mask |= VisualIDMask;
- }
-
- Visual *visual = DefaultVisual(display, screen);
- *defaultVisual = true;
- *depth = DefaultDepth(display, screen);
-
- vi = XGetVisualInfo(display, mask, &rvi, &count);
- if (vi) {
- int best = 0;
- for (int x = 0; x < count; ++x) {
- if (vi[x].depth > vi[best].depth)
- best = x;
- }
- if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
- visual = vi[best].visual;
- *defaultVisual = (visual == DefaultVisual(display, screen));
- *depth = vi[best].depth;
- }
- }
- if (vi)
- XFree((char *)vi);
- return visual;
-}
-
-static void query_colormap(QXcbColormapPrivate *d, int screen)
-{
- Display *display = X11->display;
-
- // query existing colormap
- int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
- XColor queried[256];
- memset(queried, 0, sizeof(queried));
- for (int x = 0; x < q_colors; ++x)
- queried[x].pixel = x;
- XQueryColors(display, d->colormap, queried, q_colors);
-
- d->colors.resize(q_colors);
- for (int x = 0; x < q_colors; ++x) {
- if (queried[x].red == 0
- && queried[x].green == 0
- && queried[x].blue == 0
- && queried[x].pixel != BlackPixel(display, screen)) {
- // unallocated color cell, skip it
- continue;
- }
-
- d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
- queried[x].green / float(USHRT_MAX),
- queried[x].blue / float(USHRT_MAX));
- }
-
- // for missing colors, find the closest color in the existing colormap
- Q_ASSERT(d->pixels.size());
- for (int x = 0; x < d->pixels.size(); ++x) {
- if (d->pixels.at(x) != -1)
- continue;
-
- QRgb rgb;
- if (d->mode == QXcbColormap::Indexed) {
- const int r = (x / (d->g_max * d->b_max)) % d->r_max;
- const int g = (x / d->b_max) % d->g_max;
- const int b = x % d->b_max;
- rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
- (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
- (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
- } else {
- rgb = qRgb(x, x, x);
- }
-
- // find closest color
- int mindist = INT_MAX, best = -1;
- for (int y = 0; y < q_colors; ++y) {
- int r = qRed(rgb) - (queried[y].red >> 8);
- int g = qGreen(rgb) - (queried[y].green >> 8);
- int b = qBlue(rgb) - (queried[y].blue >> 8);
- int dist = (r * r) + (g * g) + (b * b);
- if (dist < mindist) {
- mindist = dist;
- best = y;
- }
- }
-
- Q_ASSERT(best >= 0 && best < q_colors);
- if (d->visual->c_class & 1) {
- XColor xcolor;
- xcolor.red = queried[best].red;
- xcolor.green = queried[best].green;
- xcolor.blue = queried[best].blue;
- xcolor.pixel = queried[best].pixel;
-
- if (XAllocColor(display, d->colormap, &xcolor)) {
- d->pixels[x] = xcolor.pixel;
- } else {
- // some weird stuff is going on...
- d->pixels[x] = (qGray(rgb) < 127
- ? BlackPixel(display, screen)
- : WhitePixel(display, screen));
- }
- } else {
- d->pixels[x] = best;
- }
- }
-}
-
-static void init_gray(QXcbColormapPrivate *d, int screen)
-{
- d->pixels.resize(d->r_max);
-
- for (int g = 0; g < d->g_max; ++g) {
- const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
- const QRgb rgb = qRgb(gray, gray, gray);
-
- d->pixels[g] = -1;
-
- if (d->visual->c_class & 1) {
- XColor xcolor;
- xcolor.red = qRed(rgb) * 0x101;
- xcolor.green = qGreen(rgb) * 0x101;
- xcolor.blue = qBlue(rgb) * 0x101;
- xcolor.pixel = 0ul;
-
- if (XAllocColor(X11->display, d->colormap, &xcolor))
- d->pixels[g] = xcolor.pixel;
- }
- }
-
- query_colormap(d, screen);
-}
-
-static void init_indexed(QXcbColormapPrivate *d, int screen)
-{
- d->pixels.resize(d->r_max * d->g_max * d->b_max);
-
- // create color cube
- for (int x = 0, r = 0; r < d->r_max; ++r) {
- for (int g = 0; g < d->g_max; ++g) {
- for (int b = 0; b < d->b_max; ++b, ++x) {
- const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
- (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
- (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
-
- d->pixels[x] = -1;
-
- if (d->visual->c_class & 1) {
- XColor xcolor;
- xcolor.red = qRed(rgb) * 0x101;
- xcolor.green = qGreen(rgb) * 0x101;
- xcolor.blue = qBlue(rgb) * 0x101;
- xcolor.pixel = 0ul;
-
- if (XAllocColor(X11->display, d->colormap, &xcolor))
- d->pixels[x] = xcolor.pixel;
- }
- }
- }
- }
-
- query_colormap(d, screen);
-}
-
-static void init_direct(QXcbColormapPrivate *d, bool ownColormap)
-{
- if (d->visual->c_class != DirectColor || !ownColormap)
- return;
-
- // preallocate 768 on the stack, so that we don't have to malloc
- // for the common case (<= 24 bpp)
- QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
- int i = 0;
-
- for (int r = 0; r < d->r_max; ++r) {
- colorTable[i].red = r << 8 | r;
- colorTable[i].pixel = r << d->r_shift;
- colorTable[i].flags = DoRed;
- ++i;
- }
-
- for (int g = 0; g < d->g_max; ++g) {
- colorTable[i].green = g << 8 | g;
- colorTable[i].pixel = g << d->g_shift;
- colorTable[i].flags = DoGreen;
- ++i;
- }
-
- for (int b = 0; b < d->b_max; ++b) {
- colorTable[i].blue = (b << 8 | b);
- colorTable[i].pixel = b << d->b_shift;
- colorTable[i].flags = DoBlue;
- ++i;
- }
-
- XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
-}
-
-static QXcbColormap **cmaps = nullptr;
-
-void QXcbColormap::initialize()
-{
- Display *display = X11->display;
- const int screens = ScreenCount(display);
-
- cmaps = new QXcbColormap*[screens];
-
- for (int i = 0; i < screens; ++i) {
- cmaps[i] = new QXcbColormap;
- QXcbColormapPrivate * const d = cmaps[i]->d;
-
- bool use_stdcmap = false;
- int color_count = X11->color_count;
-
- // defaults
- d->depth = DefaultDepth(display, i);
- d->colormap = DefaultColormap(display, i);
- d->defaultColormap = true;
- d->visual = DefaultVisual(display, i);
- d->defaultVisual = true;
-
- Visual *argbVisual = nullptr;
-
- if (X11->visual && i == DefaultScreen(display)) {
- // only use the outside colormap on the default screen
- d->visual = find_visual(display, i, X11->visual->c_class,
- XVisualIDFromVisual(X11->visual),
- &d->depth, &d->defaultVisual);
- } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
- || (X11->visual_id != -1)) {
- // look for a specific visual or type of visual
- d->visual = find_visual(display, i, X11->visual_class, X11->visual_id,
- &d->depth, &d->defaultVisual);
- } else if (!X11->custom_cmap) {
- XStandardColormap *stdcmap = nullptr;
- int ncmaps = 0;
-
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- int nvi;
- XVisualInfo templ;
- templ.screen = i;
- templ.depth = 32;
- templ.c_class = TrueColor;
- XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
- VisualDepthMask |
- VisualClassMask, &templ, &nvi);
- for (int idx = 0; idx < nvi; ++idx) {
- XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
- xvi[idx].visual);
- if (format->type == PictTypeDirect && format->direct.alphaMask) {
- argbVisual = xvi[idx].visual;
- break;
- }
- }
- XFree(xvi);
- }
-#endif
- if (XGetRGBColormaps(display, RootWindow(display, i),
- &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
- if (stdcmap) {
- for (int c = 0; c < ncmaps; ++c) {
- if (!stdcmap[c].red_max ||
- !stdcmap[c].green_max ||
- !stdcmap[c].blue_max ||
- !stdcmap[c].red_mult ||
- !stdcmap[c].green_mult ||
- !stdcmap[c].blue_mult)
- continue; // invalid stdcmap
-
- XVisualInfo proto;
- proto.visualid = stdcmap[c].visualid;
- proto.screen = i;
-
- int nvisuals = 0;
- XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
- &proto, &nvisuals);
- if (vi) {
- if (nvisuals > 0) {
- use_stdcmap = true;
-
- d->mode = ((vi[0].visual->c_class < StaticColor)
- ? Gray
- : ((vi[0].visual->c_class < TrueColor)
- ? Indexed
- : Direct));
-
- d->depth = vi[0].depth;
- d->colormap = stdcmap[c].colormap;
- d->defaultColormap = true;
- d->visual = vi[0].visual;
- d->defaultVisual = (d->visual == DefaultVisual(display, i));
-
- d->r_max = stdcmap[c].red_max + 1;
- d->g_max = stdcmap[c].green_max + 1;
- d->b_max = stdcmap[c].blue_max + 1;
-
- if (d->mode == Direct) {
- // calculate offsets
- d->r_shift = lowest_bit(d->visual->red_mask);
- d->g_shift = lowest_bit(d->visual->green_mask);
- d->b_shift = lowest_bit(d->visual->blue_mask);
- } else {
- d->r_shift = 0;
- d->g_shift = 0;
- d->b_shift = 0;
- }
- }
- XFree(vi);
- }
- break;
- }
- XFree(stdcmap);
- }
- }
- }
- if (!use_stdcmap) {
- switch (d->visual->c_class) {
- case StaticGray:
- d->mode = Gray;
-
- d->r_max = d->g_max = d->b_max = d->visual->map_entries;
- break;
-
- case XGrayScale:
- d->mode = Gray;
-
- // follow precedent set in libXmu...
- if (color_count != 0)
- d->r_max = d->g_max = d->b_max = color_count;
- else if (d->visual->map_entries > 65000)
- d->r_max = d->g_max = d->b_max = 4096;
- else if (d->visual->map_entries > 4000)
- d->r_max = d->g_max = d->b_max = 512;
- else if (d->visual->map_entries > 250)
- d->r_max = d->g_max = d->b_max = 12;
- else
- d->r_max = d->g_max = d->b_max = 4;
- break;
-
- case StaticColor:
- d->mode = Indexed;
-
- d->r_max = right_align(d->visual->red_mask) + 1;
- d->g_max = right_align(d->visual->green_mask) + 1;
- d->b_max = right_align(d->visual->blue_mask) + 1;
- break;
-
- case PseudoColor:
- d->mode = Indexed;
-
- // follow precedent set in libXmu...
- if (color_count != 0)
- d->r_max = d->g_max = d->b_max = cube_root(color_count);
- else if (d->visual->map_entries > 65000)
- d->r_max = d->g_max = d->b_max = 27;
- else if (d->visual->map_entries > 4000)
- d->r_max = d->g_max = d->b_max = 12;
- else if (d->visual->map_entries > 250)
- d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
- else
- d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
- break;
-
- case TrueColor:
- case DirectColor:
- d->mode = Direct;
-
- d->r_max = right_align(d->visual->red_mask) + 1;
- d->g_max = right_align(d->visual->green_mask) + 1;
- d->b_max = right_align(d->visual->blue_mask) + 1;
-
- d->r_shift = lowest_bit(d->visual->red_mask);
- d->g_shift = lowest_bit(d->visual->green_mask);
- d->b_shift = lowest_bit(d->visual->blue_mask);
- break;
- }
- }
-
- bool ownColormap = false;
- if (X11->colormap && i == DefaultScreen(display)) {
- // only use the outside colormap on the default screen
- d->colormap = X11->colormap;
- d->defaultColormap = (d->colormap == DefaultColormap(display, i));
- } else if ((!use_stdcmap
- && (((d->visual->c_class & 1) && X11->custom_cmap)
- || d->visual != DefaultVisual(display, i)))
- || d->visual->c_class == DirectColor) {
- // allocate custom colormap (we always do this when using DirectColor visuals)
- d->colormap =
- XCreateColormap(display, RootWindow(display, i), d->visual,
- d->visual->c_class == DirectColor ? AllocAll : AllocNone);
- d->defaultColormap = false;
- ownColormap = true;
- }
-
- switch (d->mode) {
- case Gray:
- init_gray(d, i);
- break;
- case Indexed:
- init_indexed(d, i);
- break;
- case Direct:
- init_direct(d, ownColormap);
- break;
- }
-
- QX11InfoData *screen = X11->screens + i;
- screen->depth = d->depth;
- screen->visual = d->visual;
- screen->defaultVisual = d->defaultVisual;
- screen->colormap = d->colormap;
- screen->defaultColormap = d->defaultColormap;
- screen->cells = screen->visual->map_entries;
-
- if (argbVisual) {
- X11->argbVisuals[i] = argbVisual;
- X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
- }
-
- // ###
- // We assume that 8bpp == pseudocolor, but this is not
- // always the case (according to the X server), so we need
- // to make sure that our internal data is setup in a way
- // that is compatible with our assumptions
- if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
- screen->cells = 256;
- }
-}
-
-void QXcbColormap::cleanup()
-{
- Display *display = X11->display;
- const int screens = ScreenCount(display);
-
- for (int i = 0; i < screens; ++i)
- delete cmaps[i];
-
- delete [] cmaps;
- cmaps = 0;
-}
-
-
-QXcbColormap QXcbColormap::instance(int screen)
-{
- if (screen == -1)
- screen = QXcbX11Info::appScreen();
- return *cmaps[screen];
-}
-
-/*! \internal
- Constructs a new colormap.
-*/
-QXcbColormap::QXcbColormap()
- : d(new QXcbColormapPrivate)
-{}
-
-QXcbColormap::QXcbColormap(const QXcbColormap &colormap)
- :d (colormap.d)
-{ d->ref.ref(); }
-
-QXcbColormap::~QXcbColormap()
-{
- if (!d->ref.deref()) {
- if (!d->defaultColormap)
- XFreeColormap(X11->display, d->colormap);
- delete d;
- }
-}
-
-QXcbColormap::Mode QXcbColormap::mode() const
-{ return d->mode; }
-
-int QXcbColormap::depth() const
-{ return d->depth; }
-
-int QXcbColormap::size() const
-{
- return (d->mode == Gray
- ? d->r_max
- : (d->mode == Indexed
- ? d->r_max * d->g_max * d->b_max
- : -1));
-}
-
-uint QXcbColormap::pixel(const QColor &color) const
-{
- const QRgba64 rgba64 = color.rgba64();
- // XXX We emulate the raster engine here by getting the
- // 8-bit values, but we could instead use the 16-bit
- // values for slightly better color accuracy
- const uint r = (rgba64.red8() * d->r_max) >> 8;
- const uint g = (rgba64.green8() * d->g_max) >> 8;
- const uint b = (rgba64.blue8() * d->b_max) >> 8;
- if (d->mode != Direct) {
- if (d->mode == Gray)
- return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
- return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
- }
- return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
-}
-
-const QColor QXcbColormap::colorAt(uint pixel) const
-{
- if (d->mode != Direct) {
- Q_ASSERT(pixel <= (uint)d->colors.size());
- return d->colors.at(pixel);
- }
-
- const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
- const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
- const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
- return QColor(r, g, b);
-}
-
-const QList<QColor> QXcbColormap::colormap() const
-{ return d->colors; }
-
-QXcbColormap &QXcbColormap::operator=(const QXcbColormap &colormap)
-{
- qAtomicAssign(d, colormap.d);
- return *this;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h
deleted file mode 100644
index 9c9cce424c9..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2018 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
-
-#pragma once
-
-#include <QColor>
-#include <QList>
-
-QT_BEGIN_NAMESPACE
-
-class QXcbColormapPrivate;
-class QXcbColormap
-{
-public:
- enum Mode { Direct, Indexed, Gray };
-
- static void initialize();
- static void cleanup();
-
- static QXcbColormap instance(int screen = -1);
-
- QXcbColormap(const QXcbColormap &colormap);
- ~QXcbColormap();
-
- QXcbColormap &operator=(const QXcbColormap &colormap);
-
- Mode mode() const;
-
- int depth() const;
- int size() const;
-
- uint pixel(const QColor &color) const;
- const QColor colorAt(uint pixel) const;
-
- const QList<QColor> colormap() const;
-
-private:
- QXcbColormap();
- QXcbColormapPrivate *d;
-};
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
deleted file mode 100644
index 2f3827025f3..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
+++ /dev/null
@@ -1,2807 +0,0 @@
-// Copyright (C) 2018 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 <QtCore/qrandom.h>
-
-#include <private/qpixmapcache_p.h>
-#include <private/qpaintengine_p.h>
-#include <private/qpainterpath_p.h>
-#include <private/qdrawhelper_p.h>
-#include <private/qfontengineglyphcache_p.h>
-
-#if QT_CONFIG(fontconfig)
-#include <private/qfontengine_ft_p.h>
-#endif
-
-#include "qpaintengine_x11_p.h"
-#include "qpolygonclipper_p.h"
-#include "qtessellator_p.h"
-#include "qpixmap_x11_p.h"
-#include "qcolormap_x11_p.h"
-#include "qt_x11_p.h"
-#include "qxcbexport.h"
-#include "qxcbnativepainting.h"
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-#if QT_CONFIG(xrender)
-
-class QXRenderTessellator : public QTessellator
-{
-public:
- QXRenderTessellator() : traps(0), allocated(0), size(0) {}
- ~QXRenderTessellator() { free(traps); }
- XTrapezoid *traps;
- int allocated;
- int size;
- void addTrap(const Trapezoid &trap) override;
- QRect tessellate(const QPointF *points, int nPoints, bool winding) {
- size = 0;
- setWinding(winding);
- return QTessellator::tessellate(points, nPoints).toRect();
- }
- void done() {
- if (allocated > 64) {
- free(traps);
- traps = 0;
- allocated = 0;
- }
- }
-};
-
-void QXRenderTessellator::addTrap(const Trapezoid &trap)
-{
- if (size == allocated) {
- allocated = qMax(2*allocated, 64);
- traps = q_check_ptr((XTrapezoid *)realloc(traps, allocated * sizeof(XTrapezoid)));
- }
- traps[size].top = Q27Dot5ToXFixed(trap.top);
- traps[size].bottom = Q27Dot5ToXFixed(trap.bottom);
- traps[size].left.p1.x = Q27Dot5ToXFixed(trap.topLeft->x);
- traps[size].left.p1.y = Q27Dot5ToXFixed(trap.topLeft->y);
- traps[size].left.p2.x = Q27Dot5ToXFixed(trap.bottomLeft->x);
- traps[size].left.p2.y = Q27Dot5ToXFixed(trap.bottomLeft->y);
- traps[size].right.p1.x = Q27Dot5ToXFixed(trap.topRight->x);
- traps[size].right.p1.y = Q27Dot5ToXFixed(trap.topRight->y);
- traps[size].right.p2.x = Q27Dot5ToXFixed(trap.bottomRight->x);
- traps[size].right.p2.y = Q27Dot5ToXFixed(trap.bottomRight->y);
- ++size;
-}
-
-#endif // QT_CONFIG(xrender)
-
-class QX11PaintEnginePrivate : public QPaintEnginePrivate
-{
- Q_DECLARE_PUBLIC(QX11PaintEngine)
-public:
- QX11PaintEnginePrivate()
- {
- opacity = 1.0;
- scrn = -1;
- hd = 0;
- picture = 0;
- gc = gc_brush = 0;
- dpy = 0;
- xinfo = 0;
- txop = QTransform::TxNone;
- has_clipping = false;
- render_hints = 0;
- xform_scale = 1;
-#if QT_CONFIG(xrender)
- tessellator = 0;
-#endif
- }
- enum GCMode {
- PenGC,
- BrushGC
- };
-
- void init();
- void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode,
- QPaintEngine::PolygonDrawMode mode);
- void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode,
- QPaintEngine::PolygonDrawMode mode);
- void fillPath(const QPainterPath &path, GCMode gcmode, bool transform);
- void strokePolygon_dev(const QPointF *points, int pointCount, bool close);
- void strokePolygon_translated(const QPointF *points, int pointCount, bool close);
- void setupAdaptedOrigin(const QPoint &p);
- void resetAdaptedOrigin();
- void decidePathFallback() {
- use_path_fallback = has_alpha_brush
- || has_alpha_pen
- || has_custom_pen
- || has_complex_xform
- || (render_hints & QPainter::Antialiasing);
- }
- void decideCoordAdjust() {
- adjust_coords = false;
- }
- void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly);
- void systemStateChanged() override;
- inline bool isCosmeticPen() const {
- return cpen.isCosmetic();
- }
-
- Display *dpy;
- int scrn;
- int pdev_depth;
- unsigned long hd;
- QPixmap brush_pm;
-#if QT_CONFIG(xrender)
- unsigned long picture;
- unsigned long current_brush;
- QPixmap bitmap_texture;
- int composition_mode;
-#else
- unsigned long picture;
-#endif
- GC gc;
- GC gc_brush;
-
- QPen cpen;
- QBrush cbrush;
- QRegion crgn;
- QTransform matrix;
- qreal opacity;
-
- uint has_complex_xform : 1;
- uint has_scaling_xform : 1;
- uint has_non_scaling_xform : 1;
- uint has_custom_pen : 1;
- uint use_path_fallback : 1;
- uint adjust_coords : 1;
- uint has_clipping : 1;
- uint adapted_brush_origin : 1;
- uint adapted_pen_origin : 1;
- uint has_pen : 1;
- uint has_brush : 1;
- uint has_texture : 1;
- uint has_alpha_texture : 1;
- uint has_pattern : 1;
- uint has_alpha_pen : 1;
- uint has_alpha_brush : 1;
- uint use_sysclip : 1;
- uint render_hints;
-
- const QXcbX11Info *xinfo;
- QPointF bg_origin;
- QTransform::TransformationType txop;
- qreal xform_scale;
-
- struct qt_float_point
- {
- qreal x, y;
- };
- QPolygonClipper<qt_float_point, qt_float_point, float> polygonClipper;
-
- int xlibMaxLinePoints;
-#if QT_CONFIG(xrender)
- QXRenderTessellator *tessellator;
-#endif
-};
-
-#if QT_CONFIG(xrender)
-class QXRenderGlyphCache : public QFontEngineGlyphCache
-{
-public:
- QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix);
- ~QXRenderGlyphCache();
-
- bool addGlyphs(const QTextItemInt &ti,
- const QVarLengthArray<glyph_t> &glyphs,
- const QVarLengthArray<QFixedPoint> &positions);
- bool draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti);
-
- inline GlyphSet glyphSet();
- inline int glyphBufferSize(const QFontEngineFT::Glyph &glyph) const;
-
- inline QImage::Format imageFormat() const;
- inline const XRenderPictFormat *renderPictFormat() const;
-
- static inline QFontEngine::GlyphFormat glyphFormatForDepth(QFontEngine *fontEngine, int depth);
- static inline Glyph glyphId(glyph_t glyph, QFixed subPixelPosition);
-
- static inline bool isValidCoordinate(const QFixedPoint &fp);
-
-private:
- QXcbX11Info xinfo;
- GlyphSet gset;
- QSet<Glyph> cachedGlyphs;
-};
-#endif // QT_CONFIG(xrender)
-
-extern QPixmap qt_pixmapForBrush(int brushStyle, bool invert); //in qbrush.cpp
-extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
-
-extern "C" {
-Q_XCB_EXPORT Drawable qt_x11Handle(const QPaintDevice *pd)
-{
- if (!pd)
- return 0;
-
-// if (pd->devType() == QInternal::Widget)
-// return static_cast<const QWidget *>(pd)->handle();
-
- if (pd->devType() == QInternal::Pixmap)
- return qt_x11PixmapHandle(*static_cast<const QPixmap *>(pd));
-
- return 0;
-}
-}
-
-static const QXcbX11Info *qt_x11Info(const QPaintDevice *pd)
-{
- if (!pd)
- return 0;
-
-// if (pd->devType() == QInternal::Widget)
-// return &static_cast<const QWidget *>(pd)->x11Info();
-
- if (pd->devType() == QInternal::Pixmap)
- return &qt_x11Info(*static_cast<const QPixmap *>(pd));
-
- return 0;
-}
-
-// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
-static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
-
-#undef X11 // defined in qt_x11_p.h
-extern "C" {
-/*!
- Returns the X11 specific pen GC for the painter \a p. Note that
- QPainter::begin() must be called before this function returns a
- valid GC.
-*/
-GC Q_XCB_EXPORT qt_x11_get_pen_gc(QPainter *p)
-{
- if (p && p->paintEngine()
- && p->paintEngine()->isActive()
- && p->paintEngine()->type() == QPaintEngine::X11) {
- return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc;
- }
- return 0;
-}
-
-/*!
- Returns the X11 specific brush GC for the painter \a p. Note that
- QPainter::begin() must be called before this function returns a
- valid GC.
-*/
-GC Q_XCB_EXPORT qt_x11_get_brush_gc(QPainter *p)
-{
- if (p && p->paintEngine()
- && p->paintEngine()->isActive()
- && p->paintEngine()->type() == QPaintEngine::X11) {
- return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc_brush;
- }
- return 0;
-}
-}
-#define X11 qt_x11Data
-
-// internal helper. Converts an integer value to an unique string token
-template <typename T>
- struct HexString
-{
- inline HexString(const T t)
- : val(t)
- {}
-
- inline void write(QChar *&dest) const
- {
- const ushort hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- const char *c = reinterpret_cast<const char *>(&val);
- for (uint i = 0; i < sizeof(T); ++i) {
- *dest++ = hexChars[*c & 0xf];
- *dest++ = hexChars[(*c & 0xf0) >> 4];
- ++c;
- }
- }
- const T val;
-};
-
-// specialization to enable fast concatenating of our string tokens to a string
-template <typename T>
- struct QConcatenable<HexString<T> >
-{
- typedef HexString<T> type;
- enum { ExactSize = true };
- static int size(const HexString<T> &) { return sizeof(T) * 2; }
- static inline void appendTo(const HexString<T> &str, QChar *&out) { str.write(out); }
- typedef QString ConvertTo;
-};
-
-#if QT_CONFIG(xrender)
-static const int compositionModeToRenderOp[QPainter::CompositionMode_Xor + 1] = {
- PictOpOver, //CompositionMode_SourceOver,
- PictOpOverReverse, //CompositionMode_DestinationOver,
- PictOpClear, //CompositionMode_Clear,
- PictOpSrc, //CompositionMode_Source,
- PictOpDst, //CompositionMode_Destination,
- PictOpIn, //CompositionMode_SourceIn,
- PictOpInReverse, //CompositionMode_DestinationIn,
- PictOpOut, //CompositionMode_SourceOut,
- PictOpOutReverse, //CompositionMode_DestinationOut,
- PictOpAtop, //CompositionMode_SourceAtop,
- PictOpAtopReverse, //CompositionMode_DestinationAtop,
- PictOpXor //CompositionMode_Xor
-};
-
-static inline int qpainterOpToXrender(QPainter::CompositionMode mode)
-{
- Q_ASSERT(mode <= QPainter::CompositionMode_Xor);
- return compositionModeToRenderOp[mode];
-}
-
-static inline bool complexPictOp(int op)
-{
- return op != PictOpOver && op != PictOpSrc;
-}
-#endif
-
-static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2,
-#if QT_CONFIG(xrender)
- Picture picture,
-#else
- Qt::HANDLE picture,
-#endif
- const QRegion &r)
-{
-// int num;
-// XRectangle *rects = (XRectangle *)qt_getClipRects(r, num);
- QList<XRectangle> rects = qt_region_to_xrectangles(r);
- int num = rects.size();
-
- if (gc)
- XSetClipRectangles( dpy, gc, 0, 0, rects.data(), num, Unsorted );
- if (gc2)
- XSetClipRectangles( dpy, gc2, 0, 0, rects.data(), num, Unsorted );
-
-#if QT_CONFIG(xrender)
- if (picture)
- XRenderSetPictureClipRectangles(dpy, picture, 0, 0, rects.data(), num);
-#else
- Q_UNUSED(picture);
-#endif // QT_CONFIG(xrender)
-}
-
-
-static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2,
-#if QT_CONFIG(xrender)
- Picture picture
-#else
- Qt::HANDLE picture
-#endif
- )
-{
- if (gc)
- XSetClipMask(dpy, gc, XNone);
- if (gc2)
- XSetClipMask(dpy, gc2, XNone);
-
-#if QT_CONFIG(xrender)
- if (picture) {
- XRenderPictureAttributes attrs;
- attrs.clip_mask = XNone;
- XRenderChangePicture (dpy, picture, CPClipMask, &attrs);
- }
-#else
- Q_UNUSED(picture);
-#endif // QT_CONFIG(xrender)
-}
-
-
-#define DITHER_SIZE 16
-static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
- { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
- { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
- { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
- { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
- { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
- { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
- { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
- { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
- { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
- { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
- { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
- { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
- { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
- { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
- { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
- { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
-};
-
-static QPixmap qt_patternForAlpha(uchar alpha, int screen)
-{
- QPixmap pm;
- QString key = "$qt-alpha-brush$"_L1
- % HexString<uchar>(alpha)
- % HexString<int>(screen);
-
- if (!QPixmapCache::find(key, &pm)) {
- // #### why not use a mono image here????
- QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
- pattern.fill(0xffffffff);
- for (int y = 0; y < DITHER_SIZE; ++y) {
- for (int x = 0; x < DITHER_SIZE; ++x) {
- if (base_dither_matrix[x][y] <= alpha)
- pattern.setPixel(x, y, 0x00000000);
- }
- }
- pm = QBitmap::fromImage(pattern);
- qt_x11SetScreen(pm, screen);
- //pm.x11SetScreen(screen);
- QPixmapCache::insert(key, pm);
- }
- return pm;
-}
-
-
-#if QT_CONFIG(xrender)
-static Picture getPatternFill(int screen, const QBrush &b)
-{
- if (!X11->use_xrender)
- return XNone;
-
- XRenderColor color = X11->preMultiply(b.color());
- XRenderColor bg_color;
-
- bg_color = X11->preMultiply(QColor(0, 0, 0, 0));
-
- for (int i = 0; i < X11->pattern_fill_count; ++i) {
- if (X11->pattern_fills[i].screen == screen
- && X11->pattern_fills[i].opaque == false
- && X11->pattern_fills[i].style == b.style()
- && X11->pattern_fills[i].color.alpha == color.alpha
- && X11->pattern_fills[i].color.red == color.red
- && X11->pattern_fills[i].color.green == color.green
- && X11->pattern_fills[i].color.blue == color.blue
- && X11->pattern_fills[i].bg_color.alpha == bg_color.alpha
- && X11->pattern_fills[i].bg_color.red == bg_color.red
- && X11->pattern_fills[i].bg_color.green == bg_color.green
- && X11->pattern_fills[i].bg_color.blue == bg_color.blue)
- return X11->pattern_fills[i].picture;
- }
- // none found, replace one
- int i = QRandomGenerator::global()->generate() % 16;
-
- if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) {
- XRenderFreePicture (QXcbX11Info::display(), X11->pattern_fills[i].picture);
- X11->pattern_fills[i].picture = 0;
- }
-
- if (!X11->pattern_fills[i].picture) {
- Pixmap pixmap = XCreatePixmap (QXcbX11Info::display(), RootWindow (QXcbX11Info::display(), screen), 8, 8, 32);
- XRenderPictureAttributes attrs;
- attrs.repeat = True;
- X11->pattern_fills[i].picture = XRenderCreatePicture (QXcbX11Info::display(), pixmap,
- XRenderFindStandardFormat(QXcbX11Info::display(), PictStandardARGB32),
- CPRepeat, &attrs);
- XFreePixmap (QXcbX11Info::display(), pixmap);
- }
-
- X11->pattern_fills[i].screen = screen;
- X11->pattern_fills[i].color = color;
- X11->pattern_fills[i].bg_color = bg_color;
- X11->pattern_fills[i].opaque = false;
- X11->pattern_fills[i].style = b.style();
-
- XRenderFillRectangle(QXcbX11Info::display(), PictOpSrc, X11->pattern_fills[i].picture, &bg_color, 0, 0, 8, 8);
-
- QPixmap pattern(qt_pixmapForBrush(b.style(), true));
- XRenderPictureAttributes attrs;
- attrs.repeat = true;
- XRenderChangePicture(QXcbX11Info::display(), qt_x11PictureHandle(pattern), CPRepeat, &attrs);
-
- Picture fill_fg = X11->getSolidFill(screen, b.color());
- XRenderComposite(QXcbX11Info::display(), PictOpOver, fill_fg, qt_x11PictureHandle(pattern),
- X11->pattern_fills[i].picture,
- 0, 0, 0, 0, 0, 0, 8, 8);
-
- return X11->pattern_fills[i].picture;
-}
-
-static void qt_render_bitmap(Display *dpy, int scrn, Picture src, Picture dst,
- int sx, int sy, int x, int y, int sw, int sh,
- const QPen &pen)
-{
- Picture fill_fg = X11->getSolidFill(scrn, pen.color());
- XRenderComposite(dpy, PictOpOver,
- fill_fg, src, dst, sx, sy, sx, sy, x, y, sw, sh);
-}
-#endif
-
-void QX11PaintEnginePrivate::init()
-{
- dpy = 0;
- scrn = 0;
- hd = 0;
- picture = 0;
- xinfo = 0;
-#if QT_CONFIG(xrender)
- current_brush = 0;
- composition_mode = PictOpOver;
- tessellator = new QXRenderTessellator;
-#endif
-}
-
-void QX11PaintEnginePrivate::setupAdaptedOrigin(const QPoint &p)
-{
- if (adapted_pen_origin)
- XSetTSOrigin(dpy, gc, p.x(), p.y());
- if (adapted_brush_origin)
- XSetTSOrigin(dpy, gc_brush, p.x(), p.y());
-}
-
-void QX11PaintEnginePrivate::resetAdaptedOrigin()
-{
- if (adapted_pen_origin)
- XSetTSOrigin(dpy, gc, 0, 0);
- if (adapted_brush_origin)
- XSetTSOrigin(dpy, gc_brush, 0, 0);
-}
-
-void QX11PaintEnginePrivate::clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
-{
- int clipped_count = 0;
- qt_float_point *clipped_points = 0;
- polygonClipper.clipPolygon((qt_float_point *) poly.data(), poly.size(),
- &clipped_points, &clipped_count);
- clipped_poly->resize(clipped_count);
- for (int i=0; i<clipped_count; ++i)
- (*clipped_poly)[i] = *((QPointF *)(&clipped_points[i]));
-}
-
-void QX11PaintEnginePrivate::systemStateChanged()
-{
- Q_Q(QX11PaintEngine);
- QPainter *painter = q->state ? q->state->painter() : nullptr;
- if (painter && painter->hasClipping()) {
- if (q->testDirty(QPaintEngine::DirtyTransform))
- q->updateMatrix(q->state->transform());
- QPolygonF clip_poly_dev(matrix.map(painter->clipPath().toFillPolygon()));
- QPolygonF clipped_poly_dev;
- clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
- q->updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
- } else {
- q->updateClipRegion_dev(QRegion(), Qt::NoClip);
- }
-}
-
-static QPaintEngine::PaintEngineFeatures qt_decide_features()
-{
- QPaintEngine::PaintEngineFeatures features =
- QPaintEngine::PrimitiveTransform
- | QPaintEngine::PatternBrush
- | QPaintEngine::AlphaBlend
- | QPaintEngine::PainterPaths
- | QPaintEngine::RasterOpModes;
-
- if (X11->use_xrender) {
- features |= QPaintEngine::Antialiasing;
- features |= QPaintEngine::PorterDuff;
- features |= QPaintEngine::MaskedBrush;
-#if 0
- if (X11->xrender_version > 10) {
- features |= QPaintEngine::LinearGradientFill;
- // ###
- }
-#endif
- }
-
- return features;
-}
-
-/*
- * QX11PaintEngine members
- */
-
-QX11PaintEngine::QX11PaintEngine()
- : QPaintEngine(*(new QX11PaintEnginePrivate), qt_decide_features())
-{
- Q_D(QX11PaintEngine);
- d->init();
-}
-
-QX11PaintEngine::QX11PaintEngine(QX11PaintEnginePrivate &dptr)
- : QPaintEngine(dptr, qt_decide_features())
-{
- Q_D(QX11PaintEngine);
- d->init();
-}
-
-QX11PaintEngine::~QX11PaintEngine()
-{
-#if QT_CONFIG(xrender)
- Q_D(QX11PaintEngine);
- delete d->tessellator;
-#endif
-}
-
-bool QX11PaintEngine::begin(QPaintDevice *pdev)
-{
- Q_D(QX11PaintEngine);
- d->xinfo = qt_x11Info(pdev);
-#if QT_CONFIG(xrender)
- if (pdev->devType() == QInternal::Pixmap) {
- const QPixmap *pm = static_cast<const QPixmap *>(pdev);
- QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(pm->handle());
- if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
- data->convertToARGB32();
- d->picture = qt_x11PictureHandle(*static_cast<const QPixmap *>(pdev));
- }
-#else
- d->picture = 0;
-#endif
- d->hd = qt_x11Handle(pdev);
-
- Q_ASSERT(d->xinfo != 0);
- d->dpy = d->xinfo->display(); // get display variable
- d->scrn = d->xinfo->screen(); // get screen variable
-
- d->crgn = QRegion();
- d->gc = XCreateGC(d->dpy, d->hd, 0, 0);
- d->gc_brush = XCreateGC(d->dpy, d->hd, 0, 0);
- d->has_alpha_brush = false;
- d->has_alpha_pen = false;
- d->has_clipping = false;
- d->has_complex_xform = false;
- d->has_scaling_xform = false;
- d->has_non_scaling_xform = true;
- d->xform_scale = 1;
- d->has_custom_pen = false;
- d->matrix = QTransform();
- d->pdev_depth = d->pdev->depth();
- d->render_hints = 0;
- d->txop = QTransform::TxNone;
- d->use_path_fallback = false;
-#if QT_CONFIG(xrender)
- d->composition_mode = PictOpOver;
-#endif
- d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
- d->opacity = 1;
-
- QX11PlatformPixmap *x11pm = paintDevice()->devType() == QInternal::Pixmap ? qt_x11Pixmap(*static_cast<QPixmap *>(paintDevice())) : nullptr;
- d->use_sysclip = paintDevice()->devType() == QInternal::Widget || (x11pm ? x11pm->isBackingStore() : false);
-
- // Set up the polygon clipper. Note: This will only work in
- // polyline mode as long as we have a buffer zone, since a
- // polyline may be clipped into several non-connected polylines.
- const int BUFFERZONE = 1000;
- QRect devClipRect(-BUFFERZONE, -BUFFERZONE,
- pdev->width() + 2*BUFFERZONE, pdev->height() + 2*BUFFERZONE);
- d->polygonClipper.setBoundingRect(devClipRect);
-
- setSystemClip(systemClip());
- d->systemStateChanged();
-
- qt_x11SetDefaultScreen(d->xinfo->screen());
-
- updatePen(QPen(Qt::black));
- updateBrush(QBrush(Qt::white), QPoint());
-
- setDirty(QPaintEngine::DirtyClipRegion);
- setDirty(QPaintEngine::DirtyPen);
- setDirty(QPaintEngine::DirtyBrush);
- setDirty(QPaintEngine::DirtyBackground);
-
- setActive(true);
- return true;
-}
-
-bool QX11PaintEngine::end()
-{
- Q_D(QX11PaintEngine);
-
-#if QT_CONFIG(xrender)
- if (d->picture) {
- // reset clipping/subwindow mode on our render picture
- XRenderPictureAttributes attrs;
- attrs.subwindow_mode = ClipByChildren;
- attrs.clip_mask = XNone;
- XRenderChangePicture(d->dpy, d->picture, CPClipMask|CPSubwindowMode, &attrs);
- }
-#endif
-
- if (d->gc_brush && d->pdev->painters < 2) {
- XFreeGC(d->dpy, d->gc_brush);
- d->gc_brush = 0;
- }
-
- if (d->gc && d->pdev->painters < 2) {
- XFreeGC(d->dpy, d->gc);
- d->gc = 0;
- }
-
- // Restore system clip for alien widgets painting outside the paint event.
-// if (d->pdev->devType() == QInternal::Widget && !static_cast<QWidget *>(d->pdev)->internalWinId())
- d->currentClipDevice = nullptr;
- setSystemClip(QRegion());
-
- setActive(false);
- return true;
-}
-
-static bool clipLine(QLineF *line, const QRect &rect)
-{
- qreal x1 = line->x1();
- qreal x2 = line->x2();
- qreal y1 = line->y1();
- qreal y2 = line->y2();
-
- qreal left = rect.x();
- qreal right = rect.x() + rect.width() - 1;
- qreal top = rect.y();
- qreal bottom = rect.y() + rect.height() - 1;
-
- enum { Left, Right, Top, Bottom };
- // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
- int p1 = ((x1 < left) << Left)
- | ((x1 > right) << Right)
- | ((y1 < top) << Top)
- | ((y1 > bottom) << Bottom);
- int p2 = ((x2 < left) << Left)
- | ((x2 > right) << Right)
- | ((y2 < top) << Top)
- | ((y2 > bottom) << Bottom);
-
- if (p1 & p2)
- // completely outside
- return false;
-
- if (p1 | p2) {
- qreal dx = x2 - x1;
- qreal dy = y2 - y1;
-
- // clip x coordinates
- if (x1 < left) {
- y1 += dy/dx * (left - x1);
- x1 = left;
- } else if (x1 > right) {
- y1 -= dy/dx * (x1 - right);
- x1 = right;
- }
- if (x2 < left) {
- y2 += dy/dx * (left - x2);
- x2 = left;
- } else if (x2 > right) {
- y2 -= dy/dx * (x2 - right);
- x2 = right;
- }
- p1 = ((y1 < top) << Top)
- | ((y1 > bottom) << Bottom);
- p2 = ((y2 < top) << Top)
- | ((y2 > bottom) << Bottom);
- if (p1 & p2)
- return false;
- // clip y coordinates
- if (y1 < top) {
- x1 += dx/dy * (top - y1);
- y1 = top;
- } else if (y1 > bottom) {
- x1 -= dx/dy * (y1 - bottom);
- y1 = bottom;
- }
- if (y2 < top) {
- x2 += dx/dy * (top - y2);
- y2 = top;
- } else if (y2 > bottom) {
- x2 -= dx/dy * (y2 - bottom);
- y2 = bottom;
- }
- *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
- }
- return true;
-}
-
-void QX11PaintEngine::drawLines(const QLine *lines, int lineCount)
-{
- Q_ASSERT(lines);
- Q_ASSERT(lineCount);
- Q_D(QX11PaintEngine);
-
- if (d->has_alpha_brush
- || d->has_alpha_pen
- || d->has_custom_pen
- || (d->cpen.widthF() > 0 && d->has_complex_xform
- && !d->has_non_scaling_xform)
- || (d->render_hints & QPainter::Antialiasing)) {
- for (int i = 0; i < lineCount; ++i) {
- QPainterPath path(lines[i].p1());
- path.lineTo(lines[i].p2());
- drawPath(path);
- }
- return;
- }
-
- if (d->has_pen) {
- for (int i = 0; i < lineCount; ++i) {
- QLineF linef;
- if (d->txop == QTransform::TxNone) {
- linef = lines[i];
- } else {
- linef = d->matrix.map(QLineF(lines[i]));
- }
- if (clipLine(&linef, d->polygonClipper.boundingRect())) {
- int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
- int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
- int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
- int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
-
- XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
- }
- }
- }
-}
-
-void QX11PaintEngine::drawLines(const QLineF *lines, int lineCount)
-{
- Q_ASSERT(lines);
- Q_ASSERT(lineCount);
- Q_D(QX11PaintEngine);
-
- if (d->has_alpha_brush
- || d->has_alpha_pen
- || d->has_custom_pen
- || (d->cpen.widthF() > 0 && d->has_complex_xform
- && !d->has_non_scaling_xform)
- || (d->render_hints & QPainter::Antialiasing)) {
- for (int i = 0; i < lineCount; ++i) {
- QPainterPath path(lines[i].p1());
- path.lineTo(lines[i].p2());
- drawPath(path);
- }
- return;
- }
-
- if (d->has_pen) {
- for (int i = 0; i < lineCount; ++i) {
- QLineF linef = d->matrix.map(lines[i]);
- if (clipLine(&linef, d->polygonClipper.boundingRect())) {
- int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
- int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
- int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
- int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
-
- XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
- }
- }
- }
-}
-
-static inline QLine clipStraightLine(const QRect &clip, const QLine &l)
-{
- if (l.p1().x() == l.p2().x()) {
- int x = qBound(clip.left(), l.p1().x(), clip.right());
- int y1 = qBound(clip.top(), l.p1().y(), clip.bottom());
- int y2 = qBound(clip.top(), l.p2().y(), clip.bottom());
-
- return QLine(x, y1, x, y2);
- } else {
- Q_ASSERT(l.p1().y() == l.p2().y());
-
- int x1 = qBound(clip.left(), l.p1().x(), clip.right());
- int x2 = qBound(clip.left(), l.p2().x(), clip.right());
- int y = qBound(clip.top(), l.p1().y(), clip.bottom());
-
- return QLine(x1, y, x2, y);
- }
-}
-
-void QX11PaintEngine::drawRects(const QRectF *rects, int rectCount)
-{
- Q_D(QX11PaintEngine);
- Q_ASSERT(rects);
- Q_ASSERT(rectCount);
-
- if (rectCount != 1
- || d->has_pen
- || d->has_alpha_brush
- || d->has_complex_xform
- || d->has_custom_pen
- || d->cbrush.style() != Qt::SolidPattern
-#if QT_CONFIG(xrender)
- || complexPictOp(d->composition_mode)
-#endif
- )
- {
- QPaintEngine::drawRects(rects, rectCount);
- return;
- }
-
- QPoint alignedOffset;
- if (d->txop == QTransform::TxTranslate) {
- QPointF offset(d->matrix.dx(), d->matrix.dy());
- alignedOffset = offset.toPoint();
- if (offset != QPointF(alignedOffset)) {
- QPaintEngine::drawRects(rects, rectCount);
- return;
- }
- }
-
- const QRectF& r = rects[0];
- QRect alignedRect = r.toAlignedRect();
- if (r != QRectF(alignedRect)) {
- QPaintEngine::drawRects(rects, rectCount);
- return;
- }
- alignedRect.translate(alignedOffset);
-
- QRect clip(d->polygonClipper.boundingRect());
- alignedRect = alignedRect.intersected(clip);
- if (alignedRect.isEmpty())
- return;
-
- // simple-case:
- // the rectangle is pixel-aligned
- // the fill brush is just a solid non-alpha color
- // the painter transform is only integer translation
- // ignore: antialiasing and just XFillRectangles directly
- XRectangle xrect;
- xrect.x = short(alignedRect.x());
- xrect.y = short(alignedRect.y());
- xrect.width = ushort(alignedRect.width());
- xrect.height = ushort(alignedRect.height());
- XFillRectangles(d->dpy, d->hd, d->gc_brush, &xrect, 1);
-}
-
-void QX11PaintEngine::drawRects(const QRect *rects, int rectCount)
-{
- Q_D(QX11PaintEngine);
- Q_ASSERT(rects);
- Q_ASSERT(rectCount);
-
- if (d->has_alpha_pen
- || d->has_complex_xform
- || d->has_custom_pen
- || (d->render_hints & QPainter::Antialiasing))
- {
- for (int i = 0; i < rectCount; ++i) {
- QPainterPath path;
- path.addRect(rects[i]);
- drawPath(path);
- }
- return;
- }
-
- QRect clip(d->polygonClipper.boundingRect());
- QPoint offset(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
-#if QT_CONFIG(xrender)
- ::Picture pict = d->picture;
-
- if (X11->use_xrender && pict && d->has_brush && d->pdev_depth != 1
- && (d->has_texture || d->has_alpha_brush || complexPictOp(d->composition_mode)))
- {
- XRenderColor xc;
- if (!d->has_texture && !d->has_pattern)
- xc = X11->preMultiply(d->cbrush.color());
-
- for (int i = 0; i < rectCount; ++i) {
- QRect r(rects[i]);
- if (d->txop == QTransform::TxTranslate)
- r.translate(offset);
-
- if (r.width() == 0 || r.height() == 0) {
- if (d->has_pen) {
- const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
- XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
- }
- continue;
- }
-
- r = r.intersected(clip);
- if (r.isEmpty())
- continue;
- if (d->has_texture || d->has_pattern) {
- XRenderComposite(d->dpy, d->composition_mode, d->current_brush, 0, pict,
- qRound(r.x() - d->bg_origin.x()), qRound(r.y() - d->bg_origin.y()),
- 0, 0, r.x(), r.y(), r.width(), r.height());
- } else {
- XRenderFillRectangle(d->dpy, d->composition_mode, pict, &xc, r.x(), r.y(), r.width(), r.height());
- }
- if (d->has_pen)
- XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
- }
- } else
-#endif // QT_CONFIG(xrender)
- {
- if (d->has_brush && d->has_pen) {
- for (int i = 0; i < rectCount; ++i) {
- QRect r(rects[i]);
- if (d->txop == QTransform::TxTranslate)
- r.translate(offset);
-
- if (r.width() == 0 || r.height() == 0) {
- const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
- XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
- continue;
- }
-
- r = r.intersected(clip);
- if (r.isEmpty())
- continue;
- d->setupAdaptedOrigin(r.topLeft());
- XFillRectangle(d->dpy, d->hd, d->gc_brush, r.x(), r.y(), r.width(), r.height());
- XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
- }
- d->resetAdaptedOrigin();
- } else {
- QVarLengthArray<XRectangle> xrects(rectCount);
- int numClipped = rectCount;
- for (int i = 0; i < rectCount; ++i) {
- QRect r(rects[i]);
- if (d->txop == QTransform::TxTranslate)
- r.translate(offset);
-
- if (r.width() == 0 || r.height() == 0) {
- --numClipped;
- if (d->has_pen) {
- const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
- XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
- }
- continue;
- }
-
- r = r.intersected(clip);
- if (r.isEmpty()) {
- --numClipped;
- continue;
- }
- xrects[i].x = short(r.x());
- xrects[i].y = short(r.y());
- xrects[i].width = ushort(r.width());
- xrects[i].height = ushort(r.height());
- }
- if (numClipped) {
- d->setupAdaptedOrigin(rects[0].topLeft());
- if (d->has_brush)
- XFillRectangles(d->dpy, d->hd, d->gc_brush, xrects.data(), numClipped);
- else if (d->has_pen)
- XDrawRectangles(d->dpy, d->hd, d->gc, xrects.data(), numClipped);
- d->resetAdaptedOrigin();
- }
- }
- }
-}
-
-static inline void setCapStyle(int cap_style, GC gc)
-{
- ulong mask = GCCapStyle;
- XGCValues vals;
- vals.cap_style = cap_style;
- XChangeGC(QXcbX11Info::display(), gc, mask, &vals);
-}
-
-void QX11PaintEngine::drawPoints(const QPoint *points, int pointCount)
-{
- Q_ASSERT(points);
- Q_ASSERT(pointCount);
- Q_D(QX11PaintEngine);
-
- if (!d->has_pen)
- return;
-
- // use the same test here as in drawPath to ensure that we don't use the path fallback
- // and end up in XDrawLines for pens with width <= 1
- if (d->cpen.widthF() > 1.0f
- || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
- || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
- {
- Qt::PenCapStyle capStyle = d->cpen.capStyle();
- if (capStyle == Qt::FlatCap) {
- setCapStyle(CapProjecting, d->gc);
- d->cpen.setCapStyle(Qt::SquareCap);
- }
- const QPoint *end = points + pointCount;
- while (points < end) {
- QPainterPath path;
- path.moveTo(*points);
- path.lineTo(points->x()+.005, points->y());
- drawPath(path);
- ++points;
- }
-
- if (capStyle == Qt::FlatCap) {
- setCapStyle(CapButt, d->gc);
- d->cpen.setCapStyle(capStyle);
- }
- return;
- }
-
- static const int BUF_SIZE = 1024;
- XPoint xPoints[BUF_SIZE];
- int i = 0, j = 0;
- while (i < pointCount) {
- while (i < pointCount && j < BUF_SIZE) {
- const QPoint &xformed = d->matrix.map(points[i]);
- int x = xformed.x();
- int y = xformed.y();
- if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
- xPoints[j].x = x;
- xPoints[j].y = y;
- ++j;
- }
- ++i;
- }
- if (j)
- XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
-
- j = 0;
- }
-}
-
-void QX11PaintEngine::drawPoints(const QPointF *points, int pointCount)
-{
- Q_ASSERT(points);
- Q_ASSERT(pointCount);
- Q_D(QX11PaintEngine);
-
- if (!d->has_pen)
- return;
-
- // use the same test here as in drawPath to ensure that we don't use the path fallback
- // and end up in XDrawLines for pens with width <= 1
- if (d->cpen.widthF() > 1.0f
- || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
- || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
- {
- Qt::PenCapStyle capStyle = d->cpen.capStyle();
- if (capStyle == Qt::FlatCap) {
- setCapStyle(CapProjecting, d->gc);
- d->cpen.setCapStyle(Qt::SquareCap);
- }
-
- const QPointF *end = points + pointCount;
- while (points < end) {
- QPainterPath path;
- path.moveTo(*points);
- path.lineTo(points->x() + 0.005, points->y());
- drawPath(path);
- ++points;
- }
- if (capStyle == Qt::FlatCap) {
- setCapStyle(CapButt, d->gc);
- d->cpen.setCapStyle(capStyle);
- }
- return;
- }
-
- static const int BUF_SIZE = 1024;
- XPoint xPoints[BUF_SIZE];
- int i = 0, j = 0;
- while (i < pointCount) {
- while (i < pointCount && j < BUF_SIZE) {
- const QPointF &xformed = d->matrix.map(points[i]);
- int x = qFloor(xformed.x());
- int y = qFloor(xformed.y());
-
- if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
- xPoints[j].x = x;
- xPoints[j].y = y;
- ++j;
- }
- ++i;
- }
- if (j)
- XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
-
- j = 0;
- }
-}
-
-QPainter::RenderHints QX11PaintEngine::supportedRenderHints() const
-{
-#if QT_CONFIG(xrender)
- if (X11->use_xrender)
- return QPainter::Antialiasing;
-#endif
- return QFlag(0);
-}
-
-void QX11PaintEngine::updateState(const QPaintEngineState &state)
-{
- Q_D(QX11PaintEngine);
- QPaintEngine::DirtyFlags flags = state.state();
-
-
- if (flags & DirtyOpacity) {
- d->opacity = state.opacity();
- // Force update pen/brush as to get proper alpha colors propagated
- flags |= DirtyPen;
- flags |= DirtyBrush;
- }
-
- if (flags & DirtyTransform) updateMatrix(state.transform());
- if (flags & DirtyPen) updatePen(state.pen());
- if (flags & (DirtyBrush | DirtyBrushOrigin)) updateBrush(state.brush(), state.brushOrigin());
- if (flags & DirtyFont) updateFont(state.font());
-
- if (state.state() & DirtyClipEnabled) {
- if (state.isClipEnabled()) {
- QPolygonF clip_poly_dev(d->matrix.map(painter()->clipPath().toFillPolygon()));
- QPolygonF clipped_poly_dev;
- d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
- updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
- } else {
- updateClipRegion_dev(QRegion(), Qt::NoClip);
- }
- }
-
- if (flags & DirtyClipPath) {
- QPolygonF clip_poly_dev(d->matrix.map(state.clipPath().toFillPolygon()));
- QPolygonF clipped_poly_dev;
- d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
- updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon(), state.clipPath().fillRule()),
- state.clipOperation());
- } else if (flags & DirtyClipRegion) {
- extern QPainterPath qt_regionToPath(const QRegion &region);
- QPainterPath clip_path = qt_regionToPath(state.clipRegion());
- QPolygonF clip_poly_dev(d->matrix.map(clip_path.toFillPolygon()));
- QPolygonF clipped_poly_dev;
- d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
- updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), state.clipOperation());
- }
- if (flags & DirtyHints) updateRenderHints(state.renderHints());
- if (flags & DirtyCompositionMode) {
- int function = GXcopy;
- if (state.compositionMode() >= QPainter::RasterOp_SourceOrDestination) {
- switch (state.compositionMode()) {
- case QPainter::RasterOp_SourceOrDestination:
- function = GXor;
- break;
- case QPainter::RasterOp_SourceAndDestination:
- function = GXand;
- break;
- case QPainter::RasterOp_SourceXorDestination:
- function = GXxor;
- break;
- case QPainter::RasterOp_NotSourceAndNotDestination:
- function = GXnor;
- break;
- case QPainter::RasterOp_NotSourceOrNotDestination:
- function = GXnand;
- break;
- case QPainter::RasterOp_NotSourceXorDestination:
- function = GXequiv;
- break;
- case QPainter::RasterOp_NotSource:
- function = GXcopyInverted;
- break;
- case QPainter::RasterOp_SourceAndNotDestination:
- function = GXandReverse;
- break;
- case QPainter::RasterOp_NotSourceAndDestination:
- function = GXandInverted;
- break;
- default:
- function = GXcopy;
- }
- }
-#if QT_CONFIG(xrender)
- else {
- d->composition_mode =
- qpainterOpToXrender(state.compositionMode());
- }
-#endif
- XSetFunction(X11->display, d->gc, function);
- XSetFunction(X11->display, d->gc_brush, function);
- }
- d->decidePathFallback();
- d->decideCoordAdjust();
-}
-
-void QX11PaintEngine::updateRenderHints(QPainter::RenderHints hints)
-{
- Q_D(QX11PaintEngine);
- d->render_hints = hints;
-
-#if QT_CONFIG(xrender)
- if (X11->use_xrender && d->picture) {
- XRenderPictureAttributes attrs;
- attrs.poly_edge = (hints & QPainter::Antialiasing) ? PolyEdgeSmooth : PolyEdgeSharp;
- XRenderChangePicture(d->dpy, d->picture, CPPolyEdge, &attrs);
- }
-#endif
-}
-
-void QX11PaintEngine::updatePen(const QPen &pen)
-{
- Q_D(QX11PaintEngine);
- d->cpen = pen;
- int cp = CapButt;
- int jn = JoinMiter;
- int ps = pen.style();
-
- if (d->opacity < 1.0) {
- QColor c = d->cpen.color();
- c.setAlpha(qRound(c.alpha()*d->opacity));
- d->cpen.setColor(c);
- }
-
- d->has_pen = (ps != Qt::NoPen);
- d->has_alpha_pen = (pen.color().alpha() != 255);
-
- switch (pen.capStyle()) {
- case Qt::SquareCap:
- cp = CapProjecting;
- break;
- case Qt::RoundCap:
- cp = CapRound;
- break;
- case Qt::FlatCap:
- default:
- cp = CapButt;
- break;
- }
- switch (pen.joinStyle()) {
- case Qt::BevelJoin:
- jn = JoinBevel;
- break;
- case Qt::RoundJoin:
- jn = JoinRound;
- break;
- case Qt::MiterJoin:
- default:
- jn = JoinMiter;
- break;
- }
-
- d->adapted_pen_origin = false;
-
- char dashes[10]; // custom pen dashes
- int dash_len = 0; // length of dash list
- int xStyle = LineSolid;
-
- /*
- We are emulating Windows here. Windows treats cpen.width() == 1
- (or 0) as a very special case. The fudge variable unifies this
- case with the general case.
- */
- qreal pen_width = pen.widthF();
- int scale = qRound(pen_width < 1 ? 1 : pen_width);
- int space = (pen_width < 1 && pen_width > 0 ? 1 : (2 * scale));
- int dot = 1 * scale;
- int dash = 4 * scale;
-
- d->has_custom_pen = false;
-
- switch (ps) {
- case Qt::NoPen:
- case Qt::SolidLine:
- xStyle = LineSolid;
- break;
- case Qt::DashLine:
- dashes[0] = dash;
- dashes[1] = space;
- dash_len = 2;
- xStyle = LineOnOffDash;
- break;
- case Qt::DotLine:
- dashes[0] = dot;
- dashes[1] = space;
- dash_len = 2;
- xStyle = LineOnOffDash;
- break;
- case Qt::DashDotLine:
- dashes[0] = dash;
- dashes[1] = space;
- dashes[2] = dot;
- dashes[3] = space;
- dash_len = 4;
- xStyle = LineOnOffDash;
- break;
- case Qt::DashDotDotLine:
- dashes[0] = dash;
- dashes[1] = space;
- dashes[2] = dot;
- dashes[3] = space;
- dashes[4] = dot;
- dashes[5] = space;
- dash_len = 6;
- xStyle = LineOnOffDash;
- break;
- case Qt::CustomDashLine:
- d->has_custom_pen = true;
- break;
- }
-
- ulong mask = GCForeground | GCBackground | GCGraphicsExposures | GCLineWidth
- | GCCapStyle | GCJoinStyle | GCLineStyle;
- XGCValues vals;
- vals.graphics_exposures = false;
- if (d->pdev_depth == 1) {
- vals.foreground = qGray(pen.color().rgb()) > 127 ? 0 : 1;
- vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
- } else if (d->pdev->devType() == QInternal::Pixmap && d->pdev_depth == 32
- && X11->use_xrender) {
- vals.foreground = pen.color().rgba();
- vals.background = QColor(Qt::transparent).rgba();
- } else {
- QXcbColormap cmap = QXcbColormap::instance(d->scrn);
- vals.foreground = cmap.pixel(pen.color());
- vals.background = cmap.pixel(QColor(Qt::transparent));
- }
-
-
- vals.line_width = qRound(pen.widthF());
- vals.cap_style = cp;
- vals.join_style = jn;
- vals.line_style = xStyle;
-
- XChangeGC(d->dpy, d->gc, mask, &vals);
-
- if (dash_len) { // make dash list
- XSetDashes(d->dpy, d->gc, 0, dashes, dash_len);
- }
-
- if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
- QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
- if (!sysClip.isEmpty())
- x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
- else
- x11ClearClipRegion(d->dpy, d->gc, 0, d->picture);
- }
-}
-
-void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
-{
- Q_D(QX11PaintEngine);
- d->cbrush = brush;
- d->bg_origin = origin;
- d->adapted_brush_origin = false;
-#if QT_CONFIG(xrender)
- d->current_brush = 0;
-#endif
- if (d->opacity < 1.0) {
- QColor c = d->cbrush.color();
- c.setAlpha(qRound(c.alpha()*d->opacity));
- d->cbrush.setColor(c);
- }
-
- int s = FillSolid;
- int bs = d->cbrush.style();
- d->has_brush = (bs != Qt::NoBrush);
- d->has_pattern = bs >= Qt::Dense1Pattern && bs <= Qt::DiagCrossPattern;
- d->has_texture = bs == Qt::TexturePattern;
- d->has_alpha_brush = brush.color().alpha() != 255;
- d->has_alpha_texture = d->has_texture && d->cbrush.texture().hasAlphaChannel();
-
- ulong mask = GCForeground | GCBackground | GCGraphicsExposures
- | GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle;
- XGCValues vals;
- vals.graphics_exposures = false;
- if (d->pdev_depth == 1) {
- vals.foreground = qGray(d->cbrush.color().rgb()) > 127 ? 0 : 1;
- vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
- } else if (X11->use_xrender && d->pdev->devType() == QInternal::Pixmap
- && d->pdev_depth == 32) {
- vals.foreground = d->cbrush.color().rgba();
- vals.background = QColor(Qt::transparent).rgba();
- } else {
- QXcbColormap cmap = QXcbColormap::instance(d->scrn);
- vals.foreground = cmap.pixel(d->cbrush.color());
- vals.background = cmap.pixel(QColor(Qt::transparent));
-
- if (!X11->use_xrender && d->has_brush && !d->has_pattern && !brush.isOpaque()) {
- QPixmap pattern = qt_patternForAlpha(brush.color().alpha(), d->scrn);
- mask |= GCStipple;
- vals.stipple = qt_x11PixmapHandle(pattern);
- s = FillStippled;
- d->adapted_brush_origin = true;
- }
- }
- vals.cap_style = CapButt;
- vals.join_style = JoinMiter;
- vals.line_style = LineSolid;
-
- if (d->has_pattern || d->has_texture) {
- if (bs == Qt::TexturePattern) {
- d->brush_pm = qt_toX11Pixmap(d->cbrush.texture());
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- XRenderPictureAttributes attrs;
- attrs.repeat = true;
- XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->brush_pm), CPRepeat, &attrs);
- QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
- if (data->mask_picture)
- XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
- }
-#endif
- } else {
- d->brush_pm = qt_toX11Pixmap(qt_pixmapForBrush(bs, true));
- }
- qt_x11SetScreen(d->brush_pm, d->scrn);
- if (d->brush_pm.depth() == 1) {
- mask |= GCStipple;
- vals.stipple = qt_x11PixmapHandle(d->brush_pm);
- s = FillStippled;
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- d->bitmap_texture = QPixmap(d->brush_pm.size());
- d->bitmap_texture.fill(Qt::transparent);
- d->bitmap_texture = qt_toX11Pixmap(d->bitmap_texture);
- qt_x11SetScreen(d->bitmap_texture, d->scrn);
-
- ::Picture src = X11->getSolidFill(d->scrn, d->cbrush.color());
- XRenderComposite(d->dpy, PictOpSrc, src, qt_x11PictureHandle(d->brush_pm),
- qt_x11PictureHandle(d->bitmap_texture),
- 0, 0, d->brush_pm.width(), d->brush_pm.height(),
- 0, 0, d->brush_pm.width(), d->brush_pm.height());
-
- XRenderPictureAttributes attrs;
- attrs.repeat = true;
- XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->bitmap_texture), CPRepeat, &attrs);
-
- d->current_brush = qt_x11PictureHandle(d->bitmap_texture);
- }
-#endif
- } else {
- mask |= GCTile;
-#if QT_CONFIG(xrender)
- if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
- d->brush_pm.detach();
- QX11PlatformPixmap *brushData = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
- brushData->convertToARGB32();
- }
-#endif
- vals.tile = (d->brush_pm.depth() == d->pdev_depth
- ? qt_x11PixmapHandle(d->brush_pm)
- : static_cast<QX11PlatformPixmap*>(d->brush_pm.handle())->x11ConvertToDefaultDepth());
- s = FillTiled;
-#if QT_CONFIG(xrender)
- d->current_brush = qt_x11PictureHandle(d->cbrush.texture());
-#endif
- }
-
- mask |= GCTileStipXOrigin | GCTileStipYOrigin;
- vals.ts_x_origin = qRound(origin.x());
- vals.ts_y_origin = qRound(origin.y());
- }
-#if QT_CONFIG(xrender)
- else if (d->has_alpha_brush) {
- d->current_brush = X11->getSolidFill(d->scrn, d->cbrush.color());
- }
-#endif
-
- vals.fill_style = s;
- XChangeGC(d->dpy, d->gc_brush, mask, &vals);
- if (!d->has_clipping) {
- QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
- if (!sysClip.isEmpty())
- x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
- else
- x11ClearClipRegion(d->dpy, d->gc_brush, 0, d->picture);
- }
-}
-
-void QX11PaintEngine::drawEllipse(const QRectF &rect)
-{
- QRect aligned = rect.toAlignedRect();
- if (aligned == rect)
- drawEllipse(aligned);
- else
- QPaintEngine::drawEllipse(rect);
-}
-
-void QX11PaintEngine::drawEllipse(const QRect &rect)
-{
- if (rect.isEmpty()) {
- drawRects(&rect, 1);
- return;
- }
-
- Q_D(QX11PaintEngine);
- QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1);
- QRect r(rect);
- if (d->txop < QTransform::TxRotate) {
- r = d->matrix.mapRect(rect);
- } else if (d->txop == QTransform::TxRotate && rect.width() == rect.height()) {
- QPainterPath path;
- path.addEllipse(rect);
- r = d->matrix.map(path).boundingRect().toRect();
- }
-
- if (d->has_alpha_brush || d->has_alpha_pen || d->has_custom_pen || (d->render_hints & QPainter::Antialiasing)
- || d->has_alpha_texture || devclip.intersected(r) != r
- || (d->has_complex_xform
- && !(d->has_non_scaling_xform && rect.width() == rect.height())))
- {
- QPainterPath path;
- path.addEllipse(rect);
- drawPath(path);
- return;
- }
-
- int x = r.x();
- int y = r.y();
- int w = r.width();
- int h = r.height();
- if (w < 1 || h < 1)
- return;
- if (w == 1 && h == 1) {
- XDrawPoint(d->dpy, d->hd, d->has_pen ? d->gc : d->gc_brush, x, y);
- return;
- }
- d->setupAdaptedOrigin(rect.topLeft());
- if (d->has_brush) { // draw filled ellipse
- XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64);
- if (!d->has_pen) // make smoother outline
- XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
- }
- if (d->has_pen) // draw outline
- XDrawArc(d->dpy, d->hd, d->gc, x, y, w, h, 0, 360*64);
- d->resetAdaptedOrigin();
-}
-
-
-
-void QX11PaintEnginePrivate::fillPolygon_translated(const QPointF *polygonPoints, int pointCount,
- QX11PaintEnginePrivate::GCMode gcMode,
- QPaintEngine::PolygonDrawMode mode)
-{
-
- QVarLengthArray<QPointF> translated_points(pointCount);
- QPointF offset(matrix.dx(), matrix.dy());
-
- qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
- if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing))
- offset += QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
-
- for (int i = 0; i < pointCount; ++i) {
- translated_points[i] = polygonPoints[i] + offset;
-
- translated_points[i].rx() = qRound(translated_points[i].x()) + offs;
- translated_points[i].ry() = qRound(translated_points[i].y()) + offs;
- }
-
- fillPolygon_dev(translated_points.data(), pointCount, gcMode, mode);
-}
-
-#if QT_CONFIG(xrender)
-static void qt_XRenderCompositeTrapezoids(Display *dpy,
- int op,
- Picture src,
- Picture dst,
- _Xconst XRenderPictFormat *maskFormat,
- int xSrc,
- int ySrc,
- const XTrapezoid *traps, int size)
-{
- const int MAX_TRAPS = 50000;
- while (size) {
- int to_draw = size;
- if (to_draw > MAX_TRAPS)
- to_draw = MAX_TRAPS;
- XRenderCompositeTrapezoids(dpy, op, src, dst,
- maskFormat,
- xSrc, ySrc,
- traps, to_draw);
- size -= to_draw;
- traps += to_draw;
- }
-}
-#endif
-
-void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
- QX11PaintEnginePrivate::GCMode gcMode,
- QPaintEngine::PolygonDrawMode mode)
-{
- Q_Q(QX11PaintEngine);
-
- int clippedCount = 0;
- qt_float_point *clippedPoints = 0;
-
-#if QT_CONFIG(xrender)
- //can change if we switch to pen if gcMode != BrushGC
- bool has_fill_texture = has_texture;
- bool has_fill_pattern = has_pattern;
- ::Picture src;
-#endif
- QBrush fill;
- GC fill_gc;
- if (gcMode == BrushGC) {
- fill = cbrush;
- fill_gc = gc_brush;
-#if QT_CONFIG(xrender)
- if (current_brush)
- src = current_brush;
- else
- src = X11->getSolidFill(scrn, fill.color());
-#endif
- } else {
- fill = QBrush(cpen.brush());
- fill_gc = gc;
-#if QT_CONFIG(xrender)
- //we use the pens brush
- has_fill_texture = (fill.style() == Qt::TexturePattern);
- has_fill_pattern = (fill.style() >= Qt::Dense1Pattern && fill.style() <= Qt::DiagCrossPattern);
- if (has_fill_texture)
- src = qt_x11PictureHandle(fill.texture());
- else if (has_fill_pattern)
- src = getPatternFill(scrn, fill);
- else
- src = X11->getSolidFill(scrn, fill.color());
-#endif
- }
-
- polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
- &clippedPoints, &clippedCount);
-
-#if QT_CONFIG(xrender)
- bool solid_fill = fill.color().alpha() == 255;
- if (has_fill_texture && fill.texture().depth() == 1 && solid_fill) {
- has_fill_texture = false;
- has_fill_pattern = true;
- }
-
- bool antialias = render_hints & QPainter::Antialiasing;
-
- if (X11->use_xrender
- && picture
- && !has_fill_pattern
- && (clippedCount > 0)
- && (fill.style() != Qt::NoBrush)
- && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush))
- {
- tessellator->tessellate((QPointF *)clippedPoints, clippedCount,
- mode == QPaintEngine::WindingMode);
- if (tessellator->size > 0) {
- XRenderPictureAttributes attrs;
- attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp;
- XRenderChangePicture(dpy, picture, CPPolyEdge, &attrs);
- int x_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.x) - bg_origin.x());
- int y_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.y) - bg_origin.y());
- qt_XRenderCompositeTrapezoids(dpy, composition_mode, src, picture,
- antialias
- ? XRenderFindStandardFormat(dpy, PictStandardA8)
- : XRenderFindStandardFormat(dpy, PictStandardA1),
- x_offset, y_offset,
- tessellator->traps, tessellator->size);
- tessellator->done();
- }
- } else
-#endif
- if (fill.style() != Qt::NoBrush) {
- if (clippedCount > 200000) {
- QPolygon poly;
- for (int i = 0; i < clippedCount; ++i)
- poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y));
-
- const QRect bounds = poly.boundingRect();
- const QRect aligned = bounds
- & QRect(QPoint(), QSize(pdev->width(), pdev->height()));
-
- QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied);
- img.fill(0);
-
- QPainter painter(&img);
- painter.translate(-aligned.x(), -aligned.y());
- painter.setPen(Qt::NoPen);
- painter.setBrush(fill);
- if (gcMode == BrushGC)
- painter.setBrushOrigin(q->painter()->brushOriginF());
- painter.drawPolygon(poly);
- painter.end();
-
- q->drawImage(aligned, img, img.rect(), Qt::AutoColor);
- } else if (clippedCount > 0) {
- QVarLengthArray<XPoint> xpoints(clippedCount);
- for (int i = 0; i < clippedCount; ++i) {
- xpoints[i].x = qFloor(clippedPoints[i].x);
- xpoints[i].y = qFloor(clippedPoints[i].y);
- }
- if (mode == QPaintEngine::WindingMode)
- XSetFillRule(dpy, fill_gc, WindingRule);
- setupAdaptedOrigin(QPoint(xpoints[0].x, xpoints[0].y));
- XFillPolygon(dpy, hd, fill_gc,
- xpoints.data(), clippedCount,
- mode == QPaintEngine::ConvexMode ? Convex : Complex, CoordModeOrigin);
- resetAdaptedOrigin();
- if (mode == QPaintEngine::WindingMode)
- XSetFillRule(dpy, fill_gc, EvenOddRule);
- }
- }
-}
-
-void QX11PaintEnginePrivate::strokePolygon_translated(const QPointF *polygonPoints, int pointCount, bool close)
-{
- QVarLengthArray<QPointF> translated_points(pointCount);
- QPointF offset(matrix.dx(), matrix.dy());
- for (int i = 0; i < pointCount; ++i)
- translated_points[i] = polygonPoints[i] + offset;
- strokePolygon_dev(translated_points.data(), pointCount, close);
-}
-
-void QX11PaintEnginePrivate::strokePolygon_dev(const QPointF *polygonPoints, int pointCount, bool close)
-{
- int clippedCount = 0;
- qt_float_point *clippedPoints = 0;
- polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
- &clippedPoints, &clippedCount, close);
-
- if (clippedCount > 0) {
- QVarLengthArray<XPoint> xpoints(clippedCount);
- for (int i = 0; i < clippedCount; ++i) {
- xpoints[i].x = qRound(clippedPoints[i].x + aliasedCoordinateDelta);
- xpoints[i].y = qRound(clippedPoints[i].y + aliasedCoordinateDelta);
- }
- uint numberPoints = qMin(clippedCount, xlibMaxLinePoints);
- XPoint *pts = xpoints.data();
- XDrawLines(dpy, hd, gc, pts, numberPoints, CoordModeOrigin);
- pts += numberPoints;
- clippedCount -= numberPoints;
- numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
- while (clippedCount) {
- XDrawLines(dpy, hd, gc, pts-1, numberPoints+1, CoordModeOrigin);
- pts += numberPoints;
- clippedCount -= numberPoints;
- numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
- }
- }
-}
-
-void QX11PaintEngine::drawPolygon(const QPointF *polygonPoints, int pointCount, PolygonDrawMode mode)
-{
- Q_D(QX11PaintEngine);
-
- if (d->use_path_fallback) {
- QPainterPath path(polygonPoints[0]);
- for (int i = 1; i < pointCount; ++i)
- path.lineTo(polygonPoints[i]);
- if (mode == PolylineMode) {
- QBrush oldBrush = d->cbrush;
- d->cbrush = QBrush(Qt::NoBrush);
- path.setFillRule(Qt::WindingFill);
- drawPath(path);
- d->cbrush = oldBrush;
- } else {
- path.setFillRule(mode == OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill);
- path.closeSubpath();
- drawPath(path);
- }
- return;
- }
- if (mode != PolylineMode && d->has_brush)
- d->fillPolygon_translated(polygonPoints, pointCount, QX11PaintEnginePrivate::BrushGC, mode);
-
- if (d->has_pen)
- d->strokePolygon_translated(polygonPoints, pointCount, mode != PolylineMode);
-}
-
-
-void QX11PaintEnginePrivate::fillPath(const QPainterPath &path, QX11PaintEnginePrivate::GCMode gc_mode, bool transform)
-{
- qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
-
- QPainterPath clippedPath;
- QPainterPath clipPath;
- clipPath.addRect(polygonClipper.boundingRect());
-
- if (transform)
- clippedPath = (path*matrix).intersected(clipPath);
- else
- clippedPath = path.intersected(clipPath);
-
- QList<QPolygonF> polys = clippedPath.toFillPolygons();
- for (int i = 0; i < polys.size(); ++i) {
- QVarLengthArray<QPointF> translated_points(polys.at(i).size());
-
- for (int j = 0; j < polys.at(i).size(); ++j) {
- translated_points[j] = polys.at(i).at(j);
- if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing)) {
- translated_points[j].rx() = qRound(translated_points[j].rx() + aliasedCoordinateDelta) + offs;
- translated_points[j].ry() = qRound(translated_points[j].ry() + aliasedCoordinateDelta) + offs;
- }
- }
-
- fillPolygon_dev(translated_points.data(), polys.at(i).size(), gc_mode,
- path.fillRule() == Qt::OddEvenFill ? QPaintEngine::OddEvenMode : QPaintEngine::WindingMode);
- }
-}
-
-void QX11PaintEngine::drawPath(const QPainterPath &path)
-{
- Q_D(QX11PaintEngine);
- if (path.isEmpty())
- return;
-
- if (d->has_brush)
- d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true);
- if (d->has_pen
- && ((X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
- || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate
- && !d->has_non_scaling_xform)
- || (d->cpen.style() == Qt::CustomDashLine))) {
- QPainterPathStroker stroker;
- if (d->cpen.style() == Qt::CustomDashLine) {
- stroker.setDashPattern(d->cpen.dashPattern());
- stroker.setDashOffset(d->cpen.dashOffset());
- } else {
- stroker.setDashPattern(d->cpen.style());
- }
- stroker.setCapStyle(d->cpen.capStyle());
- stroker.setJoinStyle(d->cpen.joinStyle());
- QPainterPath stroke;
- qreal width = d->cpen.widthF();
- QPolygonF poly;
- QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height());
- // necessary to get aliased alphablended primitives to be drawn correctly
- if (d->isCosmeticPen() || d->has_scaling_xform) {
- if (d->isCosmeticPen())
- stroker.setWidth(width == 0 ? 1 : width);
- else
- stroker.setWidth(width * d->xform_scale);
- stroker.d_ptr->stroker.setClipRect(deviceRect);
- stroke = stroker.createStroke(path * d->matrix);
- if (stroke.isEmpty())
- return;
- stroke.setFillRule(Qt::WindingFill);
- d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false);
- } else {
- stroker.setWidth(width);
- stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect));
- stroke = stroker.createStroke(path);
- if (stroke.isEmpty())
- return;
- stroke.setFillRule(Qt::WindingFill);
- d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, true);
- }
- } else if (d->has_pen) {
- // if we have a cosmetic pen - use XDrawLine() for speed
- QList<QPolygonF> polys = path.toSubpathPolygons(d->matrix);
- for (int i = 0; i < polys.size(); ++i)
- d->strokePolygon_dev(polys.at(i).data(), polys.at(i).size(), false);
- }
-}
-
-Q_GUI_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image,
- Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
-{
- Q_ASSERT(image.format() == QImage::Format_RGB32);
- Q_ASSERT(image.depth() == 32);
-
- XImage *xi;
- // Note: this code assumes either RGB or BGR, 8 bpc server layouts
- const uint red_mask = (uint) visual->red_mask;
- bool bgr_layout = (red_mask == 0xff);
-
- const int w = rect.width();
- const int h = rect.height();
-
- QImage im;
- int image_byte_order = ImageByteOrder(QXcbX11Info::display());
- if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout))
- || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)
- || (image_byte_order == LSBFirst && bgr_layout))
- {
- im = image.copy(rect);
- const qsizetype iw = im.bytesPerLine() / 4;
- uint *data = (uint *)im.bits();
- for (int i=0; i < h; i++) {
- uint *p = data;
- uint *end = p + w;
- if (bgr_layout && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
- while (p < end) {
- *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff);
- p++;
- }
- } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
- || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
- while (p < end) {
- *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
- | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
- p++;
- }
- } else if ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
- || (image_byte_order == LSBFirst && bgr_layout))
- {
- while (p < end) {
- *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
- | ((*p ) & 0xff00ff00);
- p++;
- }
- }
- data += iw;
- }
- xi = XCreateImage(dpy, visual, depth, ZPixmap,
- 0, (char *) im.bits(), w, h, 32, im.bytesPerLine());
- } else {
- xi = XCreateImage(dpy, visual, depth, ZPixmap,
- 0, (char *) image.scanLine(rect.y())+rect.x()*sizeof(uint), w, h, 32, image.bytesPerLine());
- }
- XPutImage(dpy, hd, gc, xi, 0, 0, pos.x(), pos.y(), w, h);
- xi->data = 0; // QImage owns these bits
- XDestroyImage(xi);
-}
-
-void QX11PaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags)
-{
- Q_D(QX11PaintEngine);
-
- if (image.format() == QImage::Format_RGB32
- && d->pdev_depth >= 24 && image.depth() == 32
- && r.size() == sr.size())
- {
- int sx = qRound(sr.x());
- int sy = qRound(sr.y());
- int x = qRound(r.x());
- int y = qRound(r.y());
- int w = qRound(r.width());
- int h = qRound(r.height());
-
- qt_x11_drawImage(QRect(sx, sy, w, h), QPoint(x, y), image, d->hd, d->gc, d->dpy,
- (Visual *)d->xinfo->visual(), d->pdev_depth);
- } else {
- QPaintEngine::drawImage(r, image, sr, flags);
- }
-}
-
-void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRectF &_sr)
-{
- Q_D(QX11PaintEngine);
- QRectF sr = _sr;
- int x = qRound(r.x());
- int y = qRound(r.y());
- int sx = qRound(sr.x());
- int sy = qRound(sr.y());
- int sw = qRound(sr.width());
- int sh = qRound(sr.height());
-
- QPixmap pixmap = qt_toX11Pixmap(px);
- if (pixmap.isNull())
- return;
-
- if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
- || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
- qt_x11SetScreen(pixmap, d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
- }
-
- qt_x11SetDefaultScreen(qt_x11Info(pixmap).screen());
-
-#if QT_CONFIG(xrender)
- ::Picture src_pict = qt_x11PictureHandle(pixmap);
- if (src_pict && d->picture) {
- const int pDepth = pixmap.depth();
- if (pDepth == 1 && (d->has_alpha_pen)) {
- qt_render_bitmap(d->dpy, d->scrn, src_pict, d->picture,
- sx, sy, x, y, sw, sh, d->cpen);
- return;
- } else if (pDepth != 1 && (pDepth == 32 || pDepth != d->pdev_depth)) {
- XRenderComposite(d->dpy, d->composition_mode,
- src_pict, 0, d->picture, sx, sy, 0, 0, x, y, sw, sh);
- return;
- }
- }
-#endif
-
- bool mono_src = pixmap.depth() == 1;
- bool mono_dst = d->pdev_depth == 1;
- bool restore_clip = false;
-
- if (static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) { // pixmap has a mask
- QBitmap comb(sw, sh);
- GC cgc = XCreateGC(d->dpy, qt_x11PixmapHandle(comb), 0, 0);
- XSetForeground(d->dpy, cgc, 0);
- XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
- XSetBackground(d->dpy, cgc, 0);
- XSetForeground(d->dpy, cgc, 1);
- if (!d->crgn.isEmpty()) {
- QList<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
- XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
- } else if (d->has_clipping) {
- XSetClipRectangles(d->dpy, cgc, 0, 0, 0, 0, Unsorted);
- }
- XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
- XSetTSOrigin(d->dpy, cgc, -sx, -sy);
- XSetStipple(d->dpy, cgc,
- static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask);
- XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
- XFreeGC(d->dpy, cgc);
-
- XSetClipOrigin(d->dpy, d->gc, x, y);
- XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(comb));
- restore_clip = true;
- }
-
- if (mono_src) {
- if (!d->crgn.isEmpty()) {
- Pixmap comb = XCreatePixmap(d->dpy, d->hd, sw, sh, 1);
- GC cgc = XCreateGC(d->dpy, comb, 0, 0);
- XSetForeground(d->dpy, cgc, 0);
- XFillRectangle(d->dpy, comb, cgc, 0, 0, sw, sh);
- QList<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
- XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
- XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), comb, cgc, sx, sy, sw, sh, 0, 0);
- XFreeGC(d->dpy, cgc);
-
- XSetClipMask(d->dpy, d->gc, comb);
- XSetClipOrigin(d->dpy, d->gc, x, y);
- XFreePixmap(d->dpy, comb);
- } else {
- XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
- XSetClipOrigin(d->dpy, d->gc, x - sx, y - sy);
- }
-
- if (mono_dst) {
- XSetForeground(d->dpy, d->gc, qGray(d->cpen.color().rgb()) > 127 ? 0 : 1);
- } else {
- QXcbColormap cmap = QXcbColormap::instance(d->scrn);
- XSetForeground(d->dpy, d->gc, cmap.pixel(d->cpen.color()));
- }
- XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh);
- restore_clip = true;
- } else if (mono_dst && !mono_src) {
- QBitmap bitmap = QBitmap::fromPixmap(pixmap);
- XCopyArea(d->dpy, qt_x11PixmapHandle(bitmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
- } else {
- XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
- }
-
- if (d->pdev->devType() == QInternal::Pixmap) {
- const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
- Pixmap src_mask = static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask;
- Pixmap dst_mask = static_cast<QX11PlatformPixmap*>(px->handle())->x11_mask;
- if (dst_mask) {
- GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
- XSetClipOrigin(d->dpy, cgc, x, y);
- XSetClipMask(d->dpy, cgc, src_mask);
- if (src_mask) { // copy src mask into dst mask
- XCopyArea(d->dpy, src_mask, dst_mask, cgc, sx, sy, sw, sh, x, y);
- } else { // no src mask, but make sure the area copied is opaque in dest
- XSetBackground(d->dpy, cgc, 0);
- XSetForeground(d->dpy, cgc, 1);
- XFillRectangle(d->dpy, dst_mask, cgc, x, y, sw, sh);
- }
- XFreeGC(d->dpy, cgc);
- }
- }
-
- if (restore_clip) {
- XSetClipOrigin(d->dpy, d->gc, 0, 0);
- QList<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
- if (rects.isEmpty())
- XSetClipMask(d->dpy, d->gc, XNone);
- else
- XSetClipRectangles(d->dpy, d->gc, 0, 0, rects.data(), rects.size(), Unsorted);
- }
-}
-
-void QX11PaintEngine::updateMatrix(const QTransform &mtx)
-{
- Q_D(QX11PaintEngine);
- d->txop = mtx.type();
- d->matrix = mtx;
-
- d->has_complex_xform = (d->txop > QTransform::TxTranslate);
-
- extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
- bool scaling = qt_scaleForTransform(d->matrix, &d->xform_scale);
- d->has_scaling_xform = scaling && d->xform_scale != 1.0;
- d->has_non_scaling_xform = scaling && d->xform_scale == 1.0;
-}
-
-/*
- NB! the clip region is expected to be in dev coordinates
-*/
-void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
-{
- Q_D(QX11PaintEngine);
- QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
- if (op == Qt::NoClip) {
- d->has_clipping = false;
- d->crgn = sysClip;
- if (!sysClip.isEmpty()) {
- x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, sysClip);
- } else {
- x11ClearClipRegion(d->dpy, d->gc, d->gc_brush, d->picture);
- }
- return;
- }
-
- switch (op) {
- case Qt::IntersectClip:
- if (d->has_clipping) {
- d->crgn &= clipRegion;
- break;
- }
- // fall through
- case Qt::ReplaceClip:
- if (!sysClip.isEmpty())
- d->crgn = clipRegion.intersected(sysClip);
- else
- d->crgn = clipRegion;
- break;
-// case Qt::UniteClip:
-// d->crgn |= clipRegion;
-// if (!sysClip.isEmpty())
-// d->crgn = d->crgn.intersected(sysClip);
-// break;
- default:
- break;
- }
- d->has_clipping = true;
- x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, d->crgn);
-}
-
-void QX11PaintEngine::updateFont(const QFont &)
-{
-}
-
-Drawable QX11PaintEngine::handle() const
-{
- Q_D(const QX11PaintEngine);
- Q_ASSERT(isActive());
- Q_ASSERT(d->hd);
- return d->hd;
-}
-
-extern void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &,
- qreal, qreal);
-
-void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
-{
- int x = qRound(r.x());
- int y = qRound(r.y());
- int w = qRound(r.width());
- int h = qRound(r.height());
- int sx = qRound(p.x());
- int sy = qRound(p.y());
-
- bool mono_src = pixmap.depth() == 1;
- Q_D(QX11PaintEngine);
-
- if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
- || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
- QPixmap* p = const_cast<QPixmap *>(&pixmap);
- qt_x11SetScreen(*p, d->xinfo ? d->xinfo->screen() : DefaultScreen(QXcbX11Info::display()));
- }
-
- qt_x11SetDefaultScreen(qt_x11Info(pixmap).screen());
-
-#if QT_CONFIG(xrender)
- if (X11->use_xrender && d->picture && qt_x11PictureHandle(pixmap)) {
- const int numTiles = (w / pixmap.width()) * (h / pixmap.height());
- if (numTiles < 100) {
- // this is essentially qt_draw_tile(), inlined for
- // the XRenderComposite call
- int yPos, xPos, drawH, drawW, yOff, xOff;
- yPos = y;
- yOff = sy;
- while (yPos < y + h) {
- drawH = pixmap.height() - yOff; // Cropping first row
- if (yPos + drawH > y + h) // Cropping last row
- drawH = y + h - yPos;
- xPos = x;
- xOff = sx;
- while (xPos < x + w) {
- drawW = pixmap.width() - xOff; // Cropping first column
- if (xPos + drawW > x + w) // Cropping last column
- drawW = x + w - xPos;
- if (mono_src) {
- qt_render_bitmap(d->dpy, d->scrn, qt_x11PictureHandle(pixmap), d->picture,
- xOff, yOff, xPos, yPos, drawW, drawH, d->cpen);
- } else {
- XRenderComposite(d->dpy, d->composition_mode,
- qt_x11PictureHandle(pixmap), XNone, d->picture,
- xOff, yOff, 0, 0, xPos, yPos, drawW, drawH);
- }
- xPos += drawW;
- xOff = 0;
- }
- yPos += drawH;
- yOff = 0;
- }
- } else {
- w = qMin(w, d->pdev->width() - x);
- h = qMin(h, d->pdev->height() - y);
- if (w <= 0 || h <= 0)
- return;
-
- const int pw = w + sx;
- const int ph = h + sy;
- QPixmap pm(pw, ph);
- if (pixmap.hasAlpha() || mono_src)
- pm.fill(Qt::transparent);
-
- const int mode = pixmap.hasAlpha() ? PictOpOver : PictOpSrc;
- const ::Picture pmPicture = qt_x11PictureHandle(pm);
-
- // first tile
- XRenderComposite(d->dpy, mode,
- qt_x11PictureHandle(pixmap), XNone, pmPicture,
- 0, 0, 0, 0, 0, 0, qMin(pw, pixmap.width()), qMin(ph, pixmap.height()));
-
- // first row of tiles
- int xPos = pixmap.width();
- const int sh = qMin(ph, pixmap.height());
- while (xPos < pw) {
- const int sw = qMin(xPos, pw - xPos);
- XRenderComposite(d->dpy, mode,
- pmPicture, XNone, pmPicture,
- 0, 0, 0, 0, xPos, 0, sw, sh);
- xPos *= 2;
- }
-
- // remaining rows
- int yPos = pixmap.height();
- const int sw = pw;
- while (yPos < ph) {
- const int sh = qMin(yPos, ph - yPos);
- XRenderComposite(d->dpy, mode,
- pmPicture, XNone, pmPicture,
- 0, 0, 0, 0, 0, yPos, sw, sh);
- yPos *= 2;
- }
-
- // composite
- if (mono_src)
- qt_render_bitmap(d->dpy, d->scrn, pmPicture, d->picture,
- sx, sy, x, y, w, h, d->cpen);
- else
- XRenderComposite(d->dpy, d->composition_mode,
- pmPicture, XNone, d->picture,
- sx, sy, 0, 0, x, y, w, h);
- }
- } else
-#endif // QT_CONFIG(xrender)
- if (pixmap.depth() > 1 && !static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) {
- XSetTile(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
- XSetFillStyle(d->dpy, d->gc, FillTiled);
- XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
- XFillRectangle(d->dpy, d->hd, d->gc, x, y, w, h);
- XSetTSOrigin(d->dpy, d->gc, 0, 0);
- XSetFillStyle(d->dpy, d->gc, FillSolid);
- } else {
- qt_draw_tile(this, x, y, w, h, pixmap, sx, sy);
- }
-}
-
-bool QX11PaintEngine::drawCachedGlyphs(const QTransform &transform, const QTextItemInt &ti)
-{
-#if QT_CONFIG(xrender)
- Q_D(QX11PaintEngine);
- Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
-
- if (!X11->use_xrender)
- return false;
-
- QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
- QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
-
- if (!set || set->outline_drawing)
- return false;
-
- QFontEngine::GlyphFormat glyphFormat = QXRenderGlyphCache::glyphFormatForDepth(ft, d->pdev_depth);
-
- QXRenderGlyphCache *cache = static_cast<QXRenderGlyphCache *>(ft->glyphCache(set, glyphFormat, transform));
- if (!cache) {
- cache = new QXRenderGlyphCache(QXcbX11Info(), glyphFormat, transform);
- ft->setGlyphCache(set, cache);
- }
-
- return cache->draw(X11->getSolidFill(d->scrn, d->cpen.color()), d->picture, transform, ti);
-#else // !QT_CONFIG(xrender)
- return false;
-#endif // QT_CONFIG(xrender)
-}
-
-void QX11PaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
-{
- Q_D(QX11PaintEngine);
- const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
-
- switch (ti.fontEngine->type()) {
- case QFontEngine::TestFontEngine:
- case QFontEngine::Box:
- d->drawBoxTextItem(p, ti);
- break;
-#if QT_CONFIG(fontconfig)
- case QFontEngine::Freetype:
- drawFreetype(p, ti);
- break;
-#endif
- default:
- Q_ASSERT(false);
- }
-}
-
-#if QT_CONFIG(fontconfig)
-static bool path_for_glyphs(QPainterPath *path,
- const QVarLengthArray<glyph_t> &glyphs,
- const QVarLengthArray<QFixedPoint> &positions,
- const QFontEngineFT *ft)
-{
- bool result = true;
- *path = QPainterPath();
- path->setFillRule(Qt::WindingFill);
- ft->lockFace();
- int i = 0;
- while (i < glyphs.size()) {
- QFontEngineFT::Glyph *glyph = ft->loadGlyph(glyphs[i], QFixedPoint(), QFontEngineFT::Format_Mono);
- // #### fix case where we don't get a glyph
- if (!glyph || glyph->format != QFontEngineFT::Format_Mono) {
- result = false;
- break;
- }
-
- int n = 0;
- int h = glyph->height;
- int xp = qRound(positions[i].x);
- int yp = qRound(positions[i].y);
-
- xp += glyph->x;
- yp += -glyph->y + glyph->height;
- int pitch = ((glyph->width + 31) & ~31) >> 3;
-
- uchar *src = glyph->data;
- while (h--) {
- for (int x = 0; x < glyph->width; ++x) {
- bool set = src[x >> 3] & (0x80 >> (x & 7));
- if (set) {
- QRect r(xp + x, yp - h, 1, 1);
- while (x+1 < glyph->width && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) {
- ++x;
- r.setRight(r.right()+1);
- }
-
- path->addRect(r);
- ++n;
- }
- }
- src += pitch;
- }
- ++i;
- }
- ft->unlockFace();
- return result;
-}
-
-void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti)
-{
- Q_D(QX11PaintEngine);
-
- if (!ti.glyphs.numGlyphs)
- return;
-
- if (!d->cpen.isSolid()) {
- QPaintEngine::drawTextItem(p, ti);
- return;
- }
-
- const bool xrenderPath = (X11->use_xrender
- && !(d->pdev->devType() == QInternal::Pixmap
- && static_cast<const QPixmap *>(d->pdev)->handle()->pixelType() == QPlatformPixmap::BitmapType));
-
- if (xrenderPath) {
- QTransform transform = d->matrix;
- transform.translate(p.x(), p.y());
-
- if (drawCachedGlyphs(transform, ti))
- return;
- }
-
- QTransform transform;
- transform.translate(p.x(), p.y());
-
- QVarLengthArray<QFixedPoint> positions;
- QVarLengthArray<glyph_t> glyphs;
- ti.fontEngine->getGlyphPositions(ti.glyphs, transform, ti.flags, glyphs, positions);
-
- if (glyphs.count() == 0)
- return;
-
- QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
- QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
- QPainterPath path;
-
- if (!set || set->outline_drawing || !path_for_glyphs(&path, glyphs, positions, ft)) {
- QPaintEngine::drawTextItem(p, ti);
- return;
- }
-
- if (path.elementCount() <= 1)
- return;
-
- Q_ASSERT((path.elementCount() % 5) == 0);
- if (d->txop >= QTransform::TxScale) {
- painter()->save();
- painter()->setBrush(d->cpen.brush());
- painter()->setPen(Qt::NoPen);
- painter()->drawPath(path);
- painter()->restore();
- return;
- }
-
- const int rectcount = 256;
- XRectangle rects[rectcount];
- int num_rects = 0;
-
- QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
- QRect clip(d->polygonClipper.boundingRect());
- for (int i=0; i < path.elementCount(); i+=5) {
- int x = qRound(path.elementAt(i).x);
- int y = qRound(path.elementAt(i).y);
- int w = qRound(path.elementAt(i+1).x) - x;
- int h = qRound(path.elementAt(i+2).y) - y;
-
- QRect rect = QRect(x + delta.x(), y + delta.y(), w, h);
- rect = rect.intersected(clip);
- if (rect.isEmpty())
- continue;
-
- rects[num_rects].x = short(rect.x());
- rects[num_rects].y = short(rect.y());
- rects[num_rects].width = ushort(rect.width());
- rects[num_rects].height = ushort(rect.height());
- ++num_rects;
- if (num_rects == rectcount) {
- XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
- num_rects = 0;
- }
- }
- if (num_rects > 0)
- XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
-}
-#endif // QT_CONFIG(fontconfig)
-
-#if QT_CONFIG(xrender)
-QXRenderGlyphCache::QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix)
- : QFontEngineGlyphCache(format, matrix)
- , xinfo(x)
- , gset(XNone)
-{}
-
-QXRenderGlyphCache::~QXRenderGlyphCache()
-{
- if (gset != XNone)
- XRenderFreeGlyphSet(xinfo.display(), gset);
-}
-
-bool QXRenderGlyphCache::addGlyphs(const QTextItemInt &ti,
- const QVarLengthArray<glyph_t> &glyphs,
- const QVarLengthArray<QFixedPoint> &positions)
-{
- Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
-
- QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
- QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform());
-
- XGlyphInfo xglyphinfo;
-
- for (int i = 0; i < glyphs.size(); ++i) {
- const QFixed sppx = ft->subPixelPositionForX(positions[i].x);
- const QFixedPoint spp(sppx, 0);
- QFontEngineFT::Glyph *glyph = set->getGlyph(glyphs[i], spp);
- Glyph xglyphid = qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyphs[i], spp));
-
- if (glyph && glyph->format == glyphFormat()) {
- if (cachedGlyphs.contains(xglyphid)) {
- continue;
- } else {
- set->setGlyph(glyphs[i], spp, nullptr);
- delete glyph;
- glyph = 0;
- }
- }
-
- glyph = ft->loadGlyphFor(glyphs[i], spp, glyphFormat(), transform(), QColor());
-
- if (glyph == 0 || glyph->format != glyphFormat())
- return false;
-
- if (glyph->format == QFontEngine::Format_Mono) {
- // Must convert bitmap from msb to lsb bit order
- QImage img(glyph->data, glyph->width, glyph->height, QImage::Format_Mono);
- img = img.convertToFormat(QImage::Format_MonoLSB);
- memcpy(glyph->data, img.constBits(), static_cast<size_t>(img.sizeInBytes()));
- }
-
- set->setGlyph(glyphs[i], spp, glyph);
- Q_ASSERT(glyph->data || glyph->width == 0 || glyph->height == 0);
-
- xglyphinfo.width = glyph->width;
- xglyphinfo.height = glyph->height;
- xglyphinfo.x = -glyph->x;
- xglyphinfo.y = glyph->y;
- xglyphinfo.xOff = glyph->advance;
- xglyphinfo.yOff = 0;
-
- XRenderAddGlyphs(xinfo.display(), glyphSet(), &xglyphid, &xglyphinfo, 1, (const char *) glyph->data, glyphBufferSize(*glyph));
- cachedGlyphs.insert(xglyphid);
- }
-
- return true;
-}
-
-bool QXRenderGlyphCache::draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti)
-{
- Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
-
- if (ti.glyphs.numGlyphs == 0)
- return true;
-
- QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
- QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(matrix);
-
- QVarLengthArray<glyph_t> glyphs;
- QVarLengthArray<QFixedPoint> positions;
- ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
-
- if (glyphs.isEmpty())
- return true;
-
- if (!addGlyphs(ti, glyphs, positions))
- return false;
-
- QVarLengthArray<unsigned int> chars(glyphs.size());
-
- for (int i = 0; i < glyphs.size(); ++i)
- chars[i] = glyphId(glyphs[i], ft->subPixelPositionForX(positions[i].x));
-
- int i = 0;
- while (i < glyphs.size() && !isValidCoordinate(positions[i]))
- ++i;
-
- if (i >= glyphs.size())
- return true;
-
- QFixed xp = positions[i].x;
- QFixed yp = positions[i].y;
- QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
-
- XGlyphElt32 elt;
- elt.glyphset = gset;
- elt.chars = &chars[i];
- elt.nchars = 1;
- elt.xOff = qRound(xp + offs);
- elt.yOff = qRound(yp + offs);
-
- ++i;
-
- for (; i < glyphs.size(); ++i) {
- if (!isValidCoordinate(positions[i]))
- break;
-
- const QFixed sppx = ft->subPixelPositionForX(positions[i].x);
- const QFixedPoint spp(sppx, 0);
- QFontEngineFT::Glyph *g = set->getGlyph(glyphs[i], spp);
-
- if (g
- && positions[i].x == xp + g->advance
- && positions[i].y == yp
- && elt.nchars < 253 // don't draw more than 253 characters as some X servers
- // hang with it
- ) {
- elt.nchars++;
- xp += g->advance;
- } else {
- xp = positions[i].x;
- yp = positions[i].y;
-
- XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
- renderPictFormat(), 0, 0, 0, 0,
- &elt, 1);
- elt.chars = &chars[i];
- elt.nchars = 1;
- elt.xOff = qRound(xp + offs);
- elt.yOff = qRound(yp + offs);
- }
- }
-
- XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
- renderPictFormat(), 0, 0, 0, 0, &elt, 1);
-
- return true;
-}
-
-GlyphSet QXRenderGlyphCache::glyphSet()
-{
- if (gset == XNone)
- gset = XRenderCreateGlyphSet(xinfo.display(), renderPictFormat());
-
- Q_ASSERT(gset != XNone);
- return gset;
-}
-
-int QXRenderGlyphCache::glyphBufferSize(const QFontEngineFT::Glyph &glyph) const
-{
- int pitch = 0;
-
- switch (glyphFormat()) {
- case QFontEngine::Format_Mono:
- pitch = ((glyph.width + 31) & ~31) >> 3;
- break;
- case QFontEngine::Format_A8:
- pitch = (glyph.width + 3) & ~3;
- break;
- default:
- pitch = glyph.width * 4;
- break;
- }
-
- return pitch * glyph.height;
-}
-
-QImage::Format QXRenderGlyphCache::imageFormat() const
-{
- switch (glyphFormat()) {
- case QFontEngine::Format_None:
- Q_UNREACHABLE();
- break;
- case QFontEngine::Format_Mono:
- return QImage::Format_Mono;
- break;
- case QFontEngine::Format_A8:
- return QImage::Format_Alpha8;
- break;
- case QFontEngine::Format_A32:
- case QFontEngine::Format_ARGB:
- return QImage::Format_ARGB32_Premultiplied;
- break;
- }
-
- Q_UNREACHABLE();
-}
-
-const XRenderPictFormat *QXRenderGlyphCache::renderPictFormat() const
-{
- switch (glyphFormat()) {
- case QFontEngine::Format_None:
- Q_UNREACHABLE();
- break;
- case QFontEngine::Format_Mono:
- return XRenderFindStandardFormat(xinfo.display(), PictStandardA1);
- break;
- case QFontEngine::Format_A8:
- return XRenderFindStandardFormat(xinfo.display(), PictStandardA8);
- break;
- case QFontEngine::Format_A32:
- case QFontEngine::Format_ARGB:
- return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32);
- break;
- }
-
- Q_UNREACHABLE();
-}
-
-QFontEngine::GlyphFormat QXRenderGlyphCache::glyphFormatForDepth(QFontEngine *fontEngine, int depth)
-{
- QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat;
-
- if (glyphFormat == QFontEngine::Format_None) {
- switch (depth) {
- case 32:
- glyphFormat = QFontEngine::Format_ARGB;
- break;
- case 24:
- glyphFormat = QFontEngine::Format_A32;
- break;
- case 1:
- glyphFormat = QFontEngine::Format_Mono;
- break;
- default:
- glyphFormat = QFontEngine::Format_A8;
- break;
- }
- }
-
- return glyphFormat;
-}
-
-Glyph QXRenderGlyphCache::glyphId(glyph_t glyph, QFixed subPixelPosition)
-{
- return qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyph, QFixedPoint(subPixelPosition, 0)));
-}
-
-bool QXRenderGlyphCache::isValidCoordinate(const QFixedPoint &fp)
-{
- enum { t_min = SHRT_MIN, t_max = SHRT_MAX };
- return (fp.x < t_min || fp.x > t_max || fp.y < t_min || fp.y > t_max) ? false : true;
-}
-#endif // QT_CONFIG(xrender)
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
deleted file mode 100644
index bcbf84682c6..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2018 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
-
-#pragma once
-
-#include <QtGui/QPaintEngine>
-
-typedef unsigned long XID;
-typedef XID Drawable;
-typedef struct _XGC *GC;
-
-QT_BEGIN_NAMESPACE
-
-extern "C" {
-Drawable qt_x11Handle(const QPaintDevice *pd);
-GC qt_x11_get_pen_gc(QPainter *);
-GC qt_x11_get_brush_gc(QPainter *);
-}
-
-class QX11PaintEnginePrivate;
-class QX11PaintEngine : public QPaintEngine
-{
- Q_DECLARE_PRIVATE(QX11PaintEngine)
-public:
- QX11PaintEngine();
- ~QX11PaintEngine();
-
- bool begin(QPaintDevice *pdev) override;
- bool end() override;
-
- void updateState(const QPaintEngineState &state) override;
-
- void updatePen(const QPen &pen);
- void updateBrush(const QBrush &brush, const QPointF &pt);
- void updateRenderHints(QPainter::RenderHints hints);
- void updateFont(const QFont &font);
- void updateMatrix(const QTransform &matrix);
- void updateClipRegion_dev(const QRegion &region, Qt::ClipOperation op);
-
- void drawLines(const QLine *lines, int lineCount) override;
- void drawLines(const QLineF *lines, int lineCount) override;
-
- void drawRects(const QRect *rects, int rectCount) override;
- void drawRects(const QRectF *rects, int rectCount) override;
-
- void drawPoints(const QPoint *points, int pointCount) override;
- void drawPoints(const QPointF *points, int pointCount) override;
-
- void drawEllipse(const QRect &r) override;
- void drawEllipse(const QRectF &r) override;
-
- virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override;
- inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) override
- { QPaintEngine::drawPolygon(points, pointCount, mode); }
-
- void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
- void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) override;
- void drawPath(const QPainterPath &path) override;
- void drawTextItem(const QPointF &p, const QTextItem &textItem) override;
- void drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
- Qt::ImageConversionFlags flags = Qt::AutoColor) override;
-
- virtual Drawable handle() const;
- inline Type type() const override { return QPaintEngine::X11; }
-
- QPainter::RenderHints supportedRenderHints() const;
-
-protected:
- QX11PaintEngine(QX11PaintEnginePrivate &dptr);
-
-#if QT_CONFIG(fontconfig)
- void drawFreetype(const QPointF &p, const QTextItemInt &ti);
- bool drawCachedGlyphs(const QTransform &transform, const QTextItemInt &ti);
-#endif // QT_CONFIG(fontconfig)
-
- friend class QPixmap;
- friend class QFontEngineBox;
- friend GC qt_x11_get_pen_gc(QPainter *);
- friend GC qt_x11_get_brush_gc(QPainter *);
-
-private:
- Q_DISABLE_COPY_MOVE(QX11PaintEngine)
-};
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
deleted file mode 100644
index b47bd3f5dcc..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
+++ /dev/null
@@ -1,2087 +0,0 @@
-// Copyright (C) 2018 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 <QGuiApplication>
-
-#include <private/qdrawhelper_p.h>
-#include <private/qimage_p.h>
-#include <private/qimagepixmapcleanuphooks_p.h>
-
-#include "qxcbnativepainting.h"
-#include "qpixmap_x11_p.h"
-#include "qcolormap_x11_p.h"
-#include "qpaintengine_x11_p.h"
-
-QT_BEGIN_NAMESPACE
-
-#if QT_POINTER_SIZE == 8 // 64-bit versions
-
-Q_ALWAYS_INLINE uint PREMUL(uint x) {
- uint a = x >> 24;
- quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
- t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
- t &= 0x000000ff00ff00ff;
- return (uint(t)) | (uint(t >> 24)) | (a << 24);
-}
-
-#else // 32-bit versions
-
-Q_ALWAYS_INLINE uint PREMUL(uint x) {
- uint a = x >> 24;
- uint t = (x & 0xff00ff) * a;
- t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
- t &= 0xff00ff;
-
- x = ((x >> 8) & 0xff) * a;
- x = (x + ((x >> 8) & 0xff) + 0x80);
- x &= 0xff00;
- x |= t | (a << 24);
- return x;
-}
-#endif
-
-
-
-struct QXImageWrapper
-{
- XImage *xi;
-};
-
-QPixmap qt_toX11Pixmap(const QImage &image)
-{
- QPlatformPixmap *data =
- new QX11PlatformPixmap(image.depth() == 1
- ? QPlatformPixmap::BitmapType
- : QPlatformPixmap::PixmapType);
-
- data->fromImage(image, Qt::AutoColor);
-
- return QPixmap(data);
-}
-
-QPixmap qt_toX11Pixmap(const QPixmap &pixmap)
-{
- if (pixmap.isNull())
- return QPixmap();
-
- if (QPixmap(pixmap).data_ptr()->classId() == QPlatformPixmap::X11Class)
- return pixmap;
-
- return qt_toX11Pixmap(pixmap.toImage());
-}
-
-// For thread-safety:
-// image->data does not belong to X11, so we must free it ourselves.
-
-inline static void qSafeXDestroyImage(XImage *x)
-{
- if (x->data) {
- free(x->data);
- x->data = 0;
- }
- XDestroyImage(x);
-}
-
-QBitmap QX11PlatformPixmap::mask_to_bitmap(int screen) const
-{
- if (!x11_mask)
- return QBitmap();
- qt_x11SetDefaultScreen(screen);
- QBitmap bm(w, h);
- QX11PlatformPixmap *that = qt_x11Pixmap(bm);
- const QXcbX11Info *x = that->x11_info();
- GC gc = XCreateGC(x->display(), that->handle(), 0, 0);
- XCopyArea(x->display(), x11_mask, that->handle(), gc, 0, 0,
- that->width(), that->height(), 0, 0);
- XFreeGC(x->display(), gc);
- return bm;
-}
-
-void QX11PlatformPixmap::bitmapFromImage(const QImage &image)
-{
- w = image.width();
- h = image.height();
- d = 1;
- is_null = (w <= 0 || h <= 0);
- hd = createBitmapFromImage(image);
-#if QT_CONFIG(xrender)
- if (X11->use_xrender)
- picture = XRenderCreatePicture(xinfo.display(), hd,
- XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
-#endif // QT_CONFIG(xrender)
-}
-
-bool QX11PlatformPixmap::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const
-{
- XImage *xi = xiWrapper.xi;
-
- if (xi->format != ZPixmap)
- return false;
-
- // ARGB32_Premultiplied
- if (picture && depth() == 32)
- return true;
-
- // RGB32
- if (depth() == 24 && xi->bits_per_pixel == 32 && xi->red_mask == 0xff0000
- && xi->green_mask == 0xff00 && xi->blue_mask == 0xff)
- return true;
-
- // RGB16
- if (depth() == 16 && xi->bits_per_pixel == 16 && xi->red_mask == 0xf800
- && xi->green_mask == 0x7e0 && xi->blue_mask == 0x1f)
- return true;
-
- return false;
-}
-
-QImage QX11PlatformPixmap::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const
-{
- XImage *xi = xiWrapper.xi;
-
- QImage::Format format = QImage::Format_ARGB32_Premultiplied;
- if (depth() == 24)
- format = QImage::Format_RGB32;
- else if (depth() == 16)
- format = QImage::Format_RGB16;
-
- QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format);
- image.setDevicePixelRatio(devicePixelRatio());
- // take ownership
- image.data_ptr()->own_data = true;
- xi->data = 0;
-
- // we may have to swap the byte order
- if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
- || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst))
- {
- for (int i=0; i < image.height(); i++) {
- if (depth() == 16) {
- ushort *p = (ushort*)image.scanLine(i);
- ushort *end = p + image.width();
- while (p < end) {
- *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
- p++;
- }
- } else {
- uint *p = (uint*)image.scanLine(i);
- uint *end = p + image.width();
- while (p < end) {
- *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
- | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
- p++;
- }
- }
- }
- }
-
- // fix-up alpha channel
- if (format == QImage::Format_RGB32) {
- QRgb *p = (QRgb *)image.bits();
- for (int y = 0; y < xi->height; ++y) {
- for (int x = 0; x < xi->width; ++x)
- p[x] |= 0xff000000;
- p += xi->bytes_per_line / 4;
- }
- }
-
- XDestroyImage(xi);
- return image;
-}
-
-XID QX11PlatformPixmap::bitmap_to_mask(const QBitmap &bitmap, int screen)
-{
- if (bitmap.isNull())
- return 0;
- QBitmap bm = bitmap;
- qt_x11SetScreen(bm, screen);
-
- QX11PlatformPixmap *that = qt_x11Pixmap(bm);
- const QXcbX11Info *x = that->x11_info();
- Pixmap mask = XCreatePixmap(x->display(), RootWindow(x->display(), screen),
- that->width(), that->height(), 1);
- GC gc = XCreateGC(x->display(), mask, 0, 0);
- XCopyArea(x->display(), that->handle(), mask, gc, 0, 0,
- that->width(), that->height(), 0, 0);
- XFreeGC(x->display(), gc);
- return mask;
-}
-
-Drawable qt_x11Handle(const QPixmap &pixmap)
-{
- if (pixmap.isNull())
- return XNone;
-
- if (pixmap.handle()->classId() != QPlatformPixmap::X11Class)
- return XNone;
-
- return static_cast<const QX11PlatformPixmap *>(pixmap.handle())->handle();
-}
-
-
-/*****************************************************************************
- Internal functions
- *****************************************************************************/
-
-//extern const uchar *qt_get_bitflip_array(); // defined in qimage.cpp
-
-// Returns position of highest bit set or -1 if none
-static int highest_bit(uint v)
-{
- int i;
- uint b = (uint)1 << 31;
- for (i=31; ((b & v) == 0) && i>=0; i--)
- b >>= 1;
- return i;
-}
-
-// Counts the number of bits set in 'v'
-static uint n_bits(uint v)
-{
- int i = 0;
- while (v) {
- v = v & (v - 1);
- i++;
- }
- return i;
-}
-
-static uint *red_scale_table = nullptr;
-static uint *green_scale_table = nullptr;
-static uint *blue_scale_table = nullptr;
-
-static void cleanup_scale_tables()
-{
- delete[] red_scale_table;
- delete[] green_scale_table;
- delete[] blue_scale_table;
-}
-
-/*
- Could do smart bitshifting, but the "obvious" algorithm only works for
- nBits >= 4. This is more robust.
-*/
-static void build_scale_table(uint **table, uint nBits)
-{
- if (nBits > 7) {
- qWarning("build_scale_table: internal error, nBits = %i", nBits);
- return;
- }
- if (!*table) {
- static bool firstTable = true;
- if (firstTable) {
- qAddPostRoutine(cleanup_scale_tables);
- firstTable = false;
- }
- *table = new uint[256];
- }
- int maxVal = (1 << nBits) - 1;
- int valShift = 8 - nBits;
- int i;
- for (i = 0 ; i < maxVal + 1 ; i++)
- (*table)[i << valShift] = i*255/maxVal;
-}
-
-static int defaultScreen = -1;
-
-int qt_x11SetDefaultScreen(int screen)
-{
- int old = defaultScreen;
- defaultScreen = screen;
- return old;
-}
-
-void qt_x11SetScreen(QPixmap &pixmap, int screen)
-{
- if (pixmap.paintingActive()) {
- qWarning("qt_x11SetScreen(): Cannot change screens during painting");
- return;
- }
-
- if (pixmap.isNull())
- return;
-
- if (pixmap.handle()->classId() != QPlatformPixmap::X11Class)
- return;
-
- if (screen < 0)
- screen = QXcbX11Info::appScreen();
-
- QX11PlatformPixmap *pm = static_cast<QX11PlatformPixmap *>(pixmap.handle());
- if (screen == pm->xinfo.screen())
- return; // nothing to do
-
- if (pixmap.isNull()) {
- pm->xinfo = QXcbX11Info::fromScreen(screen);
- return;
- }
-
-#if 0
- qDebug("qt_x11SetScreen for %p from %d to %d. Size is %d/%d", pm, pm->xinfo.screen(), screen, pm->width(), pm->height());
-#endif
-
- qt_x11SetDefaultScreen(screen);
- pixmap = qt_toX11Pixmap(pixmap.toImage());
-}
-
-/*****************************************************************************
- QPixmap member functions
- *****************************************************************************/
-
-QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0);
-int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0;
-
-QX11PlatformPixmap::QX11PlatformPixmap(PixelType pixelType)
- : QPlatformPixmap(pixelType, X11Class), hd(0),
- flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0),
- dpr(1.0), pengine(0)
-{}
-
-QX11PlatformPixmap::~QX11PlatformPixmap()
-{
- // Cleanup hooks have to be called before the handles are freed
- if (is_cached) {
- QImagePixmapCleanupHooks::executePlatformPixmapDestructionHooks(this);
- is_cached = false;
- }
-
- release();
-}
-
-QPlatformPixmap *QX11PlatformPixmap::createCompatiblePlatformPixmap() const
-{
- QX11PlatformPixmap *p = new QX11PlatformPixmap(pixelType());
- p->setDevicePixelRatio(devicePixelRatio());
- return p;
-}
-
-void QX11PlatformPixmap::resize(int width, int height)
-{
- setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
-
- w = width;
- h = height;
- is_null = (w <= 0 || h <= 0);
-
- if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
- xinfo = QXcbX11Info::fromScreen(defaultScreen);
- }
-
- int dd = xinfo.depth();
-
- if (qt_x11_preferred_pixmap_depth)
- dd = qt_x11_preferred_pixmap_depth;
-
- bool make_null = w <= 0 || h <= 0; // create null pixmap
- d = (pixelType() == BitmapType ? 1 : dd);
- if (make_null || d == 0) {
- w = 0;
- h = 0;
- is_null = true;
- hd = 0;
- picture = 0;
- d = 0;
- if (!make_null)
- qWarning("QPixmap: Invalid pixmap parameters");
- return;
- }
- hd = XCreatePixmap(xinfo.display(),
- RootWindow(xinfo.display(), xinfo.screen()),
- w, h, d);
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- XRenderPictFormat *format = d == 1
- ? XRenderFindStandardFormat(xinfo.display(), PictStandardA1)
- : XRenderFindVisualFormat(xinfo.display(), (Visual *) xinfo.visual());
- picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0);
- }
-#endif // QT_CONFIG(xrender)
-}
-
-struct QX11AlphaDetector
-{
- bool hasAlpha() const {
- if (checked)
- return has;
- // Will implicitly also check format and return quickly for opaque types...
- checked = true;
- has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels();
- return has;
- }
-
- bool hasXRenderAndAlpha() const {
- if (!X11->use_xrender)
- return false;
- return hasAlpha();
- }
-
- QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags)
- : image(i), checked(false), has(false)
- {
- if (flags & Qt::NoOpaqueDetection) {
- checked = true;
- has = image->hasAlphaChannel();
- }
- }
-
- const QImage *image;
- mutable bool checked;
- mutable bool has;
-};
-
-void QX11PlatformPixmap::fromImage(const QImage &img, Qt::ImageConversionFlags flags)
-{
- setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
-
- w = img.width();
- h = img.height();
- d = img.depth();
- is_null = (w <= 0 || h <= 0);
- setDevicePixelRatio(img.devicePixelRatio());
-
- if (is_null) {
- w = h = 0;
- return;
- }
-
- if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
- xinfo = QXcbX11Info::fromScreen(defaultScreen);
- }
-
- if (pixelType() == BitmapType) {
- bitmapFromImage(img);
- return;
- }
-
- if (uint(w) >= 32768 || uint(h) >= 32768) {
- w = h = 0;
- is_null = true;
- return;
- }
-
- QX11AlphaDetector alphaCheck(&img, flags);
- int dd = alphaCheck.hasXRenderAndAlpha() ? 32 : xinfo.depth();
-
- if (qt_x11_preferred_pixmap_depth)
- dd = qt_x11_preferred_pixmap_depth;
-
- QImage image = img;
-
- // must be monochrome
- if (dd == 1 || (flags & Qt::ColorMode_Mask) == Qt::MonoOnly) {
- if (d != 1) {
- // dither
- image = image.convertToFormat(QImage::Format_MonoLSB, flags);
- d = 1;
- }
- } else { // can be both
- bool conv8 = false;
- if (d > 8 && dd <= 8) { // convert to 8 bit
- if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
- flags = (flags & ~Qt::DitherMode_Mask)
- | Qt::PreferDither;
- conv8 = true;
- } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
- conv8 = (d == 1); // native depth wanted
- } else if (d == 1) {
- if (image.colorCount() == 2) {
- QRgb c0 = image.color(0); // Auto: convert to best
- QRgb c1 = image.color(1);
- conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
- } else {
- // eg. 1-color monochrome images (they do exist).
- conv8 = true;
- }
- }
- if (conv8) {
- image = image.convertToFormat(QImage::Format_Indexed8, flags);
- d = 8;
- }
- }
-
- if (d == 1 || image.format() > QImage::Format_ARGB32_Premultiplied) {
- QImage::Format fmt = QImage::Format_RGB32;
- if (alphaCheck.hasXRenderAndAlpha() && d > 1)
- fmt = QImage::Format_ARGB32_Premultiplied;
- image = image.convertToFormat(fmt, flags);
- fromImage(image, Qt::AutoColor);
- return;
- }
-
- Display *dpy = xinfo.display();
- Visual *visual = (Visual *)xinfo.visual();
- XImage *xi = nullptr;
- bool trucol = (visual->c_class >= TrueColor);
- size_t nbytes = image.sizeInBytes();
- uchar *newbits= nullptr;
-
-#if QT_CONFIG(xrender)
- if (alphaCheck.hasXRenderAndAlpha()) {
- const QImage &cimage = image;
-
- d = 32;
-
- if (QXcbX11Info::appDepth() != d) {
- xinfo.setDepth(d);
- }
-
- hd = XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()), w, h, d);
- picture = XRenderCreatePicture(dpy, hd,
- XRenderFindStandardFormat(dpy, PictStandardARGB32), 0, 0);
-
- xi = XCreateImage(dpy, visual, d, ZPixmap, 0, 0, w, h, 32, 0);
- Q_CHECK_PTR(xi);
- newbits = (uchar *)malloc(xi->bytes_per_line*h);
- Q_CHECK_PTR(newbits);
- xi->data = (char *)newbits;
-
- switch (cimage.format()) {
- case QImage::Format_Indexed8: {
- QList<QRgb> colorTable = cimage.colorTable();
- uint *xidata = (uint *)xi->data;
- for (int y = 0; y < h; ++y) {
- const uchar *p = cimage.scanLine(y);
- for (int x = 0; x < w; ++x) {
- const QRgb rgb = colorTable[p[x]];
- const int a = qAlpha(rgb);
- if (a == 0xff)
- *xidata = rgb;
- else
- // RENDER expects premultiplied alpha
- *xidata = qRgba(qt_div_255(qRed(rgb) * a),
- qt_div_255(qGreen(rgb) * a),
- qt_div_255(qBlue(rgb) * a),
- a);
- ++xidata;
- }
- }
- }
- break;
- case QImage::Format_RGB32: {
- uint *xidata = (uint *)xi->data;
- for (int y = 0; y < h; ++y) {
- const QRgb *p = (const QRgb *) cimage.scanLine(y);
- for (int x = 0; x < w; ++x)
- *xidata++ = p[x] | 0xff000000;
- }
- }
- break;
- case QImage::Format_ARGB32: {
- uint *xidata = (uint *)xi->data;
- for (int y = 0; y < h; ++y) {
- const QRgb *p = (const QRgb *) cimage.scanLine(y);
- for (int x = 0; x < w; ++x) {
- const QRgb rgb = p[x];
- const int a = qAlpha(rgb);
- if (a == 0xff)
- *xidata = rgb;
- else
- // RENDER expects premultiplied alpha
- *xidata = qRgba(qt_div_255(qRed(rgb) * a),
- qt_div_255(qGreen(rgb) * a),
- qt_div_255(qBlue(rgb) * a),
- a);
- ++xidata;
- }
- }
-
- }
- break;
- case QImage::Format_ARGB32_Premultiplied: {
- uint *xidata = (uint *)xi->data;
- for (int y = 0; y < h; ++y) {
- const QRgb *p = (const QRgb *) cimage.scanLine(y);
- memcpy(xidata, p, w*sizeof(QRgb));
- xidata += w;
- }
- }
- break;
- default:
- Q_ASSERT(false);
- }
-
- if ((xi->byte_order == MSBFirst) != (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
- uint *xidata = (uint *)xi->data;
- uint *xiend = xidata + w*h;
- while (xidata < xiend) {
- *xidata = (*xidata >> 24)
- | ((*xidata >> 8) & 0xff00)
- | ((*xidata << 8) & 0xff0000)
- | (*xidata << 24);
- ++xidata;
- }
- }
-
- GC gc = XCreateGC(dpy, hd, 0, 0);
- XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
- XFreeGC(dpy, gc);
-
- qSafeXDestroyImage(xi);
-
- return;
- }
-#endif // QT_CONFIG(xrender)
-
- if (trucol) { // truecolor display
- if (image.format() == QImage::Format_ARGB32_Premultiplied)
- image = image.convertToFormat(QImage::Format_ARGB32);
-
- const QImage &cimage = image;
- QRgb pix[256]; // pixel translation table
- const bool d8 = (d == 8);
- const uint red_mask = (uint)visual->red_mask;
- const uint green_mask = (uint)visual->green_mask;
- const uint blue_mask = (uint)visual->blue_mask;
- const int red_shift = highest_bit(red_mask) - 7;
- const int green_shift = highest_bit(green_mask) - 7;
- const int blue_shift = highest_bit(blue_mask) - 7;
- const uint rbits = highest_bit(red_mask) - lowest_bit(red_mask) + 1;
- const uint gbits = highest_bit(green_mask) - lowest_bit(green_mask) + 1;
- const uint bbits = highest_bit(blue_mask) - lowest_bit(blue_mask) + 1;
-
- if (d8) { // setup pixel translation
- QList<QRgb> ctable = cimage.colorTable();
- for (int i=0; i < cimage.colorCount(); i++) {
- int r = qRed (ctable[i]);
- int g = qGreen(ctable[i]);
- int b = qBlue (ctable[i]);
- r = red_shift > 0 ? r << red_shift : r >> -red_shift;
- g = green_shift > 0 ? g << green_shift : g >> -green_shift;
- b = blue_shift > 0 ? b << blue_shift : b >> -blue_shift;
- pix[i] = (b & blue_mask) | (g & green_mask) | (r & red_mask)
- | ~(blue_mask | green_mask | red_mask);
- }
- }
-
- xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
- Q_CHECK_PTR(xi);
- newbits = (uchar *)malloc(xi->bytes_per_line*h);
- Q_CHECK_PTR(newbits);
- if (!newbits) // no memory
- return;
- int bppc = xi->bits_per_pixel;
-
- bool contig_bits = n_bits(red_mask) == rbits &&
- n_bits(green_mask) == gbits &&
- n_bits(blue_mask) == bbits;
- bool dither_tc =
- // Want it?
- (flags & Qt::Dither_Mask) != Qt::ThresholdDither &&
- (flags & Qt::DitherMode_Mask) != Qt::AvoidDither &&
- // Need it?
- bppc < 24 && !d8 &&
- // Can do it? (Contiguous bits?)
- contig_bits;
-
- static bool init=false;
- static int D[16][16];
- if (dither_tc && !init) {
- // I also contributed this code to XV - WWA.
- /*
- The dither matrix, D, is obtained with this formula:
-
- D2 = [0 2]
- [3 1]
-
-
- D2*n = [4*Dn 4*Dn+2*Un]
- [4*Dn+3*Un 4*Dn+1*Un]
- */
- int n,i,j;
- init=1;
-
- /* Set D2 */
- D[0][0]=0;
- D[1][0]=2;
- D[0][1]=3;
- D[1][1]=1;
-
- /* Expand using recursive definition given above */
- for (n=2; n<16; n*=2) {
- for (i=0; i<n; i++) {
- for (j=0; j<n; j++) {
- D[i][j]*=4;
- D[i+n][j]=D[i][j]+2;
- D[i][j+n]=D[i][j]+3;
- D[i+n][j+n]=D[i][j]+1;
- }
- }
- }
- init=true;
- }
-
- enum { BPP8,
- BPP16_565, BPP16_555,
- BPP16_MSB, BPP16_LSB,
- BPP24_888,
- BPP24_MSB, BPP24_LSB,
- BPP32_8888,
- BPP32_MSB, BPP32_LSB
- } mode = BPP8;
-
- bool same_msb_lsb = (xi->byte_order == MSBFirst) == (QSysInfo::ByteOrder == QSysInfo::BigEndian);
-
- if (bppc == 8) // 8 bit
- mode = BPP8;
- else if (bppc == 16) { // 16 bit MSB/LSB
- if (red_shift == 8 && green_shift == 3 && blue_shift == -3 && !d8 && same_msb_lsb)
- mode = BPP16_565;
- else if (red_shift == 7 && green_shift == 2 && blue_shift == -3 && !d8 && same_msb_lsb)
- mode = BPP16_555;
- else
- mode = (xi->byte_order == LSBFirst) ? BPP16_LSB : BPP16_MSB;
- } else if (bppc == 24) { // 24 bit MSB/LSB
- if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
- mode = BPP24_888;
- else
- mode = (xi->byte_order == LSBFirst) ? BPP24_LSB : BPP24_MSB;
- } else if (bppc == 32) { // 32 bit MSB/LSB
- if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
- mode = BPP32_8888;
- else
- mode = (xi->byte_order == LSBFirst) ? BPP32_LSB : BPP32_MSB;
- } else
- qFatal("Logic error 3");
-
-#define GET_PIXEL \
- uint pixel; \
- if (d8) pixel = pix[*src++]; \
- else { \
- int r = qRed (*p); \
- int g = qGreen(*p); \
- int b = qBlue (*p++); \
- r = red_shift > 0 \
- ? r << red_shift : r >> -red_shift; \
- g = green_shift > 0 \
- ? g << green_shift : g >> -green_shift; \
- b = blue_shift > 0 \
- ? b << blue_shift : b >> -blue_shift; \
- pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask) \
- | ~(blue_mask | green_mask | red_mask); \
- }
-
-#define GET_PIXEL_DITHER_TC \
- int r = qRed (*p); \
- int g = qGreen(*p); \
- int b = qBlue (*p++); \
- const int thres = D[x%16][y%16]; \
- if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
- > thres) \
- r += (1<<(8-rbits)); \
- if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
- > thres) \
- g += (1<<(8-gbits)); \
- if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
- > thres) \
- b += (1<<(8-bbits)); \
- r = red_shift > 0 \
- ? r << red_shift : r >> -red_shift; \
- g = green_shift > 0 \
- ? g << green_shift : g >> -green_shift; \
- b = blue_shift > 0 \
- ? b << blue_shift : b >> -blue_shift; \
- uint pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask);
-
-// again, optimized case
-// can't be optimized that much :(
-#define GET_PIXEL_DITHER_TC_OPT(red_shift,green_shift,blue_shift,red_mask,green_mask,blue_mask, \
- rbits,gbits,bbits) \
- const int thres = D[x%16][y%16]; \
- int r = qRed (*p); \
- if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
- > thres) \
- r += (1<<(8-rbits)); \
- int g = qGreen(*p); \
- if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
- > thres) \
- g += (1<<(8-gbits)); \
- int b = qBlue (*p++); \
- if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
- > thres) \
- b += (1<<(8-bbits)); \
- uint pixel = ((r red_shift) & red_mask) \
- | ((g green_shift) & green_mask) \
- | ((b blue_shift) & blue_mask);
-
-#define CYCLE(body) \
- for (int y=0; y<h; y++) { \
- const uchar* src = cimage.scanLine(y); \
- uchar* dst = newbits + xi->bytes_per_line*y; \
- const QRgb* p = (const QRgb *)src; \
- body \
- }
-
- if (dither_tc) {
- switch (mode) {
- case BPP16_565:
- CYCLE(
- quint16* dst16 = (quint16*)dst;
- for (int x=0; x<w; x++) {
- GET_PIXEL_DITHER_TC_OPT(<<8,<<3,>>3,0xf800,0x7e0,0x1f,5,6,5)
- *dst16++ = pixel;
- }
- )
- break;
- case BPP16_555:
- CYCLE(
- quint16* dst16 = (quint16*)dst;
- for (int x=0; x<w; x++) {
- GET_PIXEL_DITHER_TC_OPT(<<7,<<2,>>3,0x7c00,0x3e0,0x1f,5,5,5)
- *dst16++ = pixel;
- }
- )
- break;
- case BPP16_MSB: // 16 bit MSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL_DITHER_TC
- *dst++ = (pixel >> 8);
- *dst++ = pixel;
- }
- )
- break;
- case BPP16_LSB: // 16 bit LSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL_DITHER_TC
- *dst++ = pixel;
- *dst++ = pixel >> 8;
- }
- )
- break;
- default:
- qFatal("Logic error");
- }
- } else {
- switch (mode) {
- case BPP8: // 8 bit
- CYCLE(
- Q_UNUSED(p);
- for (int x=0; x<w; x++)
- *dst++ = pix[*src++];
- )
- break;
- case BPP16_565:
- CYCLE(
- quint16* dst16 = (quint16*)dst;
- for (int x = 0; x < w; x++) {
- *dst16++ = ((*p >> 8) & 0xf800)
- | ((*p >> 5) & 0x7e0)
- | ((*p >> 3) & 0x1f);
- ++p;
- }
- )
- break;
- case BPP16_555:
- CYCLE(
- quint16* dst16 = (quint16*)dst;
- for (int x=0; x<w; x++) {
- *dst16++ = ((*p >> 9) & 0x7c00)
- | ((*p >> 6) & 0x3e0)
- | ((*p >> 3) & 0x1f);
- ++p;
- }
- )
- break;
- case BPP16_MSB: // 16 bit MSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL
- *dst++ = (pixel >> 8);
- *dst++ = pixel;
- }
- )
- break;
- case BPP16_LSB: // 16 bit LSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL
- *dst++ = pixel;
- *dst++ = pixel >> 8;
- }
- )
- break;
- case BPP24_888:
- CYCLE(
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
- for (int x=0; x<w; x++) {
- *dst++ = qRed (*p);
- *dst++ = qGreen(*p);
- *dst++ = qBlue (*p++);
- }
- } else {
- for (int x=0; x<w; x++) {
- *dst++ = qBlue (*p);
- *dst++ = qGreen(*p);
- *dst++ = qRed (*p++);
- }
- }
- )
- break;
- case BPP24_MSB: // 24 bit MSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL
- *dst++ = pixel >> 16;
- *dst++ = pixel >> 8;
- *dst++ = pixel;
- }
- )
- break;
- case BPP24_LSB: // 24 bit LSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL
- *dst++ = pixel;
- *dst++ = pixel >> 8;
- *dst++ = pixel >> 16;
- }
- )
- break;
- case BPP32_8888:
- CYCLE(
- memcpy(dst, p, w * 4);
- )
- break;
- case BPP32_MSB: // 32 bit MSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL
- *dst++ = pixel >> 24;
- *dst++ = pixel >> 16;
- *dst++ = pixel >> 8;
- *dst++ = pixel;
- }
- )
- break;
- case BPP32_LSB: // 32 bit LSB
- CYCLE(
- for (int x=0; x<w; x++) {
- GET_PIXEL
- *dst++ = pixel;
- *dst++ = pixel >> 8;
- *dst++ = pixel >> 16;
- *dst++ = pixel >> 24;
- }
- )
- break;
- default:
- qFatal("Logic error 2");
- }
- }
- xi->data = (char *)newbits;
- }
-
- if (d == 8 && !trucol) { // 8 bit pixmap
- int pop[256]; // pixel popularity
-
- if (image.colorCount() == 0)
- image.setColorCount(1);
-
- const QImage &cimage = image;
- memset(pop, 0, sizeof(int)*256); // reset popularity array
- for (int i = 0; i < h; i++) { // for each scanline...
- const uchar* p = cimage.scanLine(i);
- const uchar *end = p + w;
- while (p < end) // compute popularity
- pop[*p++]++;
- }
-
- newbits = (uchar *)malloc(nbytes); // copy image into newbits
- Q_CHECK_PTR(newbits);
- if (!newbits) // no memory
- return;
- uchar* p = newbits;
- memcpy(p, cimage.bits(), nbytes); // copy image data into newbits
-
- /*
- * The code below picks the most important colors. It is based on the
- * diversity algorithm, implemented in XV 3.10. XV is (C) by John Bradley.
- */
-
- struct PIX { // pixel sort element
- uchar r,g,b,n; // color + pad
- int use; // popularity
- int index; // index in colormap
- int mindist;
- };
- int ncols = 0;
- for (int i=0; i< cimage.colorCount(); i++) { // compute number of colors
- if (pop[i] > 0)
- ncols++;
- }
- for (int i = cimage.colorCount(); i < 256; i++) // ignore out-of-range pixels
- pop[i] = 0;
-
- // works since we make sure above to have at least
- // one color in the image
- if (ncols == 0)
- ncols = 1;
-
- PIX pixarr[256]; // pixel array
- PIX pixarr_sorted[256]; // pixel array (sorted)
- memset(pixarr, 0, ncols*sizeof(PIX));
- PIX *px = &pixarr[0];
- int maxpop = 0;
- int maxpix = 0;
- uint j = 0;
- QList<QRgb> ctable = cimage.colorTable();
- for (int i = 0; i < 256; i++) { // init pixel array
- if (pop[i] > 0) {
- px->r = qRed (ctable[i]);
- px->g = qGreen(ctable[i]);
- px->b = qBlue (ctable[i]);
- px->n = 0;
- px->use = pop[i];
- if (pop[i] > maxpop) { // select most popular entry
- maxpop = pop[i];
- maxpix = j;
- }
- px->index = i;
- px->mindist = 1000000;
- px++;
- j++;
- }
- }
- pixarr_sorted[0] = pixarr[maxpix];
- pixarr[maxpix].use = 0;
-
- for (int i = 1; i < ncols; i++) { // sort pixels
- int minpix = -1, mindist = -1;
- px = &pixarr_sorted[i-1];
- int r = px->r;
- int g = px->g;
- int b = px->b;
- int dist;
- if ((i & 1) || i<10) { // sort on max distance
- for (int j=0; j<ncols; j++) {
- px = &pixarr[j];
- if (px->use) {
- dist = (px->r - r)*(px->r - r) +
- (px->g - g)*(px->g - g) +
- (px->b - b)*(px->b - b);
- if (px->mindist > dist)
- px->mindist = dist;
- if (px->mindist > mindist) {
- mindist = px->mindist;
- minpix = j;
- }
- }
- }
- } else { // sort on max popularity
- for (int j=0; j<ncols; j++) {
- px = &pixarr[j];
- if (px->use) {
- dist = (px->r - r)*(px->r - r) +
- (px->g - g)*(px->g - g) +
- (px->b - b)*(px->b - b);
- if (px->mindist > dist)
- px->mindist = dist;
- if (px->use > mindist) {
- mindist = px->use;
- minpix = j;
- }
- }
- }
- }
- pixarr_sorted[i] = pixarr[minpix];
- pixarr[minpix].use = 0;
- }
-
- QXcbColormap cmap = QXcbColormap::instance(xinfo.screen());
- uint pix[256]; // pixel translation table
- px = &pixarr_sorted[0];
- for (int i = 0; i < ncols; i++) { // allocate colors
- QColor c(px->r, px->g, px->b);
- pix[px->index] = cmap.pixel(c);
- px++;
- }
-
- p = newbits;
- for (size_t i = 0; i < nbytes; i++) { // translate pixels
- *p = pix[*p];
- p++;
- }
- }
-
- if (!xi) { // X image not created
- xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
- if (xi->bits_per_pixel == 16) { // convert 8 bpp ==> 16 bpp
- ushort *p2;
- int p2inc = xi->bytes_per_line/sizeof(ushort);
- ushort *newerbits = (ushort *)malloc(xi->bytes_per_line * h);
- Q_CHECK_PTR(newerbits);
- if (!newerbits) // no memory
- return;
- uchar* p = newbits;
- for (int y = 0; y < h; y++) { // OOPS: Do right byte order!!
- p2 = newerbits + p2inc*y;
- for (int x = 0; x < w; x++)
- *p2++ = *p++;
- }
- free(newbits);
- newbits = (uchar *)newerbits;
- } else if (xi->bits_per_pixel != 8) {
- qWarning("QPixmap::fromImage: Display not supported "
- "(bpp=%d)", xi->bits_per_pixel);
- }
- xi->data = (char *)newbits;
- }
-
- hd = XCreatePixmap(dpy,
- RootWindow(dpy, xinfo.screen()),
- w, h, dd);
-
- GC gc = XCreateGC(dpy, hd, 0, 0);
- XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
- XFreeGC(dpy, gc);
-
- qSafeXDestroyImage(xi);
- d = dd;
-
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- XRenderPictFormat *format = d == 1
- ? XRenderFindStandardFormat(dpy, PictStandardA1)
- : XRenderFindVisualFormat(dpy, (Visual *)xinfo.visual());
- picture = XRenderCreatePicture(dpy, hd, format, 0, 0);
- }
-#endif
-
- if (alphaCheck.hasAlpha()) {
- QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags));
- setMask(m);
- }
-}
-
-void QX11PlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect)
-{
- if (data->pixelType() == BitmapType) {
- fromImage(data->toImage().copy(rect), Qt::AutoColor);
- return;
- }
-
- const QX11PlatformPixmap *x11Data = static_cast<const QX11PlatformPixmap*>(data);
-
- setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
-
- flags &= ~Uninitialized;
- xinfo = x11Data->xinfo;
- d = x11Data->d;
- w = rect.width();
- h = rect.height();
- is_null = (w <= 0 || h <= 0);
- hd = XCreatePixmap(xinfo.display(),
- RootWindow(xinfo.display(), x11Data->xinfo.screen()),
- w, h, d);
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- XRenderPictFormat *format = d == 32
- ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32)
- : XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual());
- picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0);
- }
-#endif // QT_CONFIG(xrender)
- if (x11Data->x11_mask) {
- x11_mask = XCreatePixmap(xinfo.display(), hd, w, h, 1);
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask,
- XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
- XRenderPictureAttributes attrs;
- attrs.alpha_map = x11Data->mask_picture;
- XRenderChangePicture(xinfo.display(), x11Data->picture, CPAlphaMap, &attrs);
- }
-#endif
- }
-
-#if QT_CONFIG(xrender)
- if (x11Data->picture && x11Data->d == 32) {
- XRenderComposite(xinfo.display(), PictOpSrc,
- x11Data->picture, 0, picture,
- rect.x(), rect.y(), 0, 0, 0, 0, w, h);
- } else
-#endif
- {
- GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
- XCopyArea(xinfo.display(), x11Data->hd, hd, gc,
- rect.x(), rect.y(), w, h, 0, 0);
- if (x11Data->x11_mask) {
- GC monogc = XCreateGC(xinfo.display(), x11_mask, 0, 0);
- XCopyArea(xinfo.display(), x11Data->x11_mask, x11_mask, monogc,
- rect.x(), rect.y(), w, h, 0, 0);
- XFreeGC(xinfo.display(), monogc);
- }
- XFreeGC(xinfo.display(), gc);
- }
-}
-
-bool QX11PlatformPixmap::scroll(int dx, int dy, const QRect &rect)
-{
- GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
- XCopyArea(xinfo.display(), hd, hd, gc,
- rect.left(), rect.top(), rect.width(), rect.height(),
- rect.left() + dx, rect.top() + dy);
- XFreeGC(xinfo.display(), gc);
- return true;
-}
-
-int QX11PlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
-{
- switch (metric) {
- case QPaintDevice::PdmDevicePixelRatio:
- return devicePixelRatio();
- break;
- case QPaintDevice::PdmDevicePixelRatioScaled:
- return devicePixelRatio() * QPaintDevice::devicePixelRatioFScale();
- break;
- case QPaintDevice::PdmWidth:
- return w;
- case QPaintDevice::PdmHeight:
- return h;
- case QPaintDevice::PdmNumColors:
- return 1 << d;
- case QPaintDevice::PdmDepth:
- return d;
- case QPaintDevice::PdmWidthMM: {
- const int screen = xinfo.screen();
- const int mm = DisplayWidthMM(xinfo.display(), screen) * w
- / DisplayWidth(xinfo.display(), screen);
- return mm;
- }
- case QPaintDevice::PdmHeightMM: {
- const int screen = xinfo.screen();
- const int mm = (DisplayHeightMM(xinfo.display(), screen) * h)
- / DisplayHeight(xinfo.display(), screen);
- return mm;
- }
- case QPaintDevice::PdmDpiX:
- case QPaintDevice::PdmPhysicalDpiX:
- return QXcbX11Info::appDpiX(xinfo.screen());
- case QPaintDevice::PdmDpiY:
- case QPaintDevice::PdmPhysicalDpiY:
- return QXcbX11Info::appDpiY(xinfo.screen());
- default:
- qWarning("QX11PlatformPixmap::metric(): Invalid metric");
- return 0;
- }
-}
-
-void QX11PlatformPixmap::fill(const QColor &fillColor)
-{
- if (fillColor.alpha() != 255) {
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- if (!picture || d != 32)
- convertToARGB32(/*preserveContents = */false);
-
- ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor);
- XRenderComposite(xinfo.display(), PictOpSrc, src, 0, picture,
- 0, 0, width(), height(),
- 0, 0, width(), height());
- } else
-#endif
- {
- QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
- im.fill(PREMUL(fillColor.rgba()));
- release();
- fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
- }
- return;
- }
-
- GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
- if (depth() == 1) {
- XSetForeground(xinfo.display(), gc, qGray(fillColor.rgb()) > 127 ? 0 : 1);
- } else if (X11->use_xrender && d >= 24) {
- XSetForeground(xinfo.display(), gc, fillColor.rgba());
- } else {
- XSetForeground(xinfo.display(), gc,
- QXcbColormap::instance(xinfo.screen()).pixel(fillColor));
- }
- XFillRectangle(xinfo.display(), hd, gc, 0, 0, width(), height());
- XFreeGC(xinfo.display(), gc);
-}
-
-QBitmap QX11PlatformPixmap::mask() const
-{
- QBitmap mask;
-#if QT_CONFIG(xrender)
- if (picture && d == 32) {
- // #### slow - there must be a better way..
- mask = QBitmap::fromImage(toImage().createAlphaMask());
- } else
-#endif
- if (d == 1) {
- QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
- mask = QBitmap::fromPixmap(QPixmap(that));
- } else {
- mask = mask_to_bitmap(xinfo.screen());
- }
- return mask;
-}
-
-void QX11PlatformPixmap::setMask(const QBitmap &newmask)
-{
- if (newmask.isNull()) { // clear mask
-#if QT_CONFIG(xrender)
- if (picture && d == 32) {
- QX11PlatformPixmap newData(pixelType());
- newData.resize(w, h);
- newData.fill(Qt::black);
- XRenderComposite(xinfo.display(), PictOpOver,
- picture, 0, newData.picture,
- 0, 0, 0, 0, 0, 0, w, h);
- release();
- *this = newData;
- // the new QX11PlatformPixmap object isn't referenced yet, so
- // ref it
- ref.ref();
-
- // the below is to make sure the QX11PlatformPixmap destructor
- // doesn't delete our newly created render picture
- newData.hd = 0;
- newData.x11_mask = 0;
- newData.picture = 0;
- newData.mask_picture = 0;
- newData.hd2 = 0;
- } else
-#endif
- if (x11_mask) {
-#if QT_CONFIG(xrender)
- if (picture) {
- XRenderPictureAttributes attrs;
- attrs.alpha_map = 0;
- XRenderChangePicture(xinfo.display(), picture, CPAlphaMap,
- &attrs);
- }
- if (mask_picture)
- XRenderFreePicture(xinfo.display(), mask_picture);
- mask_picture = 0;
-#endif
- XFreePixmap(xinfo.display(), x11_mask);
- x11_mask = 0;
- }
- return;
- }
-
-#if QT_CONFIG(xrender)
- if (picture && d == 32) {
- XRenderComposite(xinfo.display(), PictOpSrc,
- picture, qt_x11Pixmap(newmask)->x11PictureHandle(),
- picture, 0, 0, 0, 0, 0, 0, w, h);
- } else
-#endif
- if (depth() == 1) {
- XGCValues vals;
- vals.function = GXand;
- GC gc = XCreateGC(xinfo.display(), hd, GCFunction, &vals);
- XCopyArea(xinfo.display(), qt_x11Pixmap(newmask)->handle(), hd, gc, 0, 0,
- width(), height(), 0, 0);
- XFreeGC(xinfo.display(), gc);
- } else {
- // ##### should or the masks together
- if (x11_mask) {
- XFreePixmap(xinfo.display(), x11_mask);
-#if QT_CONFIG(xrender)
- if (mask_picture)
- XRenderFreePicture(xinfo.display(), mask_picture);
-#endif
- }
- x11_mask = QX11PlatformPixmap::bitmap_to_mask(newmask, xinfo.screen());
-#if QT_CONFIG(xrender)
- if (picture) {
- mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask,
- XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
- XRenderPictureAttributes attrs;
- attrs.alpha_map = mask_picture;
- XRenderChangePicture(xinfo.display(), picture, CPAlphaMap, &attrs);
- }
-#endif
- }
-}
-
-bool QX11PlatformPixmap::hasAlphaChannel() const
-{
- if (picture && d == 32)
- return true;
-
- if (x11_mask && d == 1)
- return true;
-
- return false;
-}
-
-QPixmap QX11PlatformPixmap::transformed(const QTransform &transform, Qt::TransformationMode mode) const
-{
- if (mode == Qt::SmoothTransformation || transform.type() >= QTransform::TxProject) {
- QImage image = toImage();
- return QPixmap::fromImage(image.transformed(transform, mode));
- }
-
- uint w = 0;
- uint h = 0; // size of target pixmap
- uint ws, hs; // size of source pixmap
- uchar *dptr; // data in target pixmap
- uint dbpl, dbytes; // bytes per line/bytes total
- uchar *sptr; // data in original pixmap
- int sbpl; // bytes per line in original
- int bpp; // bits per pixel
- bool depth1 = depth() == 1;
- Display *dpy = xinfo.display();
-
- ws = width();
- hs = height();
-
- QTransform mat(transform.m11(), transform.m12(), transform.m13(),
- transform.m21(), transform.m22(), transform.m23(),
- 0., 0., 1);
- bool complex_xform = false;
-
- if (mat.type() <= QTransform::TxScale) {
- h = qRound(qAbs(mat.m22()) * hs);
- w = qRound(qAbs(mat.m11()) * ws);
- } else { // rotation or shearing
- QPolygonF a(QRectF(0, 0, ws, hs));
- a = mat.map(a);
- QRect r = a.boundingRect().toAlignedRect();
- w = r.width();
- h = r.height();
- complex_xform = true;
- }
- mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix
-
- bool invertible;
- mat = mat.inverted(&invertible); // invert matrix
-
- if (h == 0 || w == 0 || !invertible
- || qAbs(h) >= 32768 || qAbs(w) >= 32768 )
- // error, return null pixmap
- return QPixmap();
-
- XImage *xi = XGetImage(xinfo.display(), handle(), 0, 0, ws, hs, AllPlanes,
- depth1 ? XYPixmap : ZPixmap);
-
- if (!xi)
- return QPixmap();
-
- sbpl = xi->bytes_per_line;
- sptr = (uchar *)xi->data;
- bpp = xi->bits_per_pixel;
-
- if (depth1)
- dbpl = (w+7)/8;
- else
- dbpl = ((w*bpp+31)/32)*4;
- dbytes = dbpl*h;
-
- dptr = (uchar *)malloc(dbytes); // create buffer for bits
- Q_CHECK_PTR(dptr);
- if (depth1) // fill with zeros
- memset(dptr, 0, dbytes);
- else if (bpp == 8) // fill with background color
- memset(dptr, WhitePixel(xinfo.display(), xinfo.screen()), dbytes);
- else
- memset(dptr, 0, dbytes);
-
- // #define QT_DEBUG_XIMAGE
-#if defined(QT_DEBUG_XIMAGE)
- qDebug("----IMAGE--INFO--------------");
- qDebug("width............. %d", xi->width);
- qDebug("height............ %d", xi->height);
- qDebug("xoffset........... %d", xi->xoffset);
- qDebug("format............ %d", xi->format);
- qDebug("byte order........ %d", xi->byte_order);
- qDebug("bitmap unit....... %d", xi->bitmap_unit);
- qDebug("bitmap bit order.. %d", xi->bitmap_bit_order);
- qDebug("depth............. %d", xi->depth);
- qDebug("bytes per line.... %d", xi->bytes_per_line);
- qDebug("bits per pixel.... %d", xi->bits_per_pixel);
-#endif
-
- int type;
- if (xi->bitmap_bit_order == MSBFirst)
- type = QT_XFORM_TYPE_MSBFIRST;
- else
- type = QT_XFORM_TYPE_LSBFIRST;
- int xbpl, p_inc;
- if (depth1) {
- xbpl = (w+7)/8;
- p_inc = dbpl - xbpl;
- } else {
- xbpl = (w*bpp)/8;
- p_inc = dbpl - xbpl;
- }
-
- if (!qt_xForm_helper(mat, xi->xoffset, type, bpp, dptr, xbpl, p_inc, h, sptr, sbpl, ws, hs)){
- qWarning("QPixmap::transform: display not supported (bpp=%d)",bpp);
- QPixmap pm;
- free(dptr);
- return pm;
- }
-
- qSafeXDestroyImage(xi);
-
- if (depth1) { // mono bitmap
- QBitmap bm = QBitmap::fromData(QSize(w, h), dptr,
- BitmapBitOrder(xinfo.display()) == MSBFirst
- ? QImage::Format_Mono
- : QImage::Format_MonoLSB);
- free(dptr);
- return bm;
- } else { // color pixmap
- QX11PlatformPixmap *x11Data = new QX11PlatformPixmap(QPlatformPixmap::PixmapType);
- QPixmap pm(x11Data);
- x11Data->flags &= ~QX11PlatformPixmap::Uninitialized;
- x11Data->xinfo = xinfo;
- x11Data->d = d;
- x11Data->w = w;
- x11Data->h = h;
- x11Data->is_null = (w <= 0 || h <= 0);
- x11Data->hd = XCreatePixmap(xinfo.display(),
- RootWindow(xinfo.display(), xinfo.screen()),
- w, h, d);
- x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
-
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- XRenderPictFormat *format = x11Data->d == 32
- ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32)
- : XRenderFindVisualFormat(xinfo.display(), (Visual *) x11Data->xinfo.visual());
- x11Data->picture = XRenderCreatePicture(xinfo.display(), x11Data->hd, format, 0, 0);
- }
-#endif // QT_CONFIG(xrender)
-
- GC gc = XCreateGC(xinfo.display(), x11Data->hd, 0, 0);
- xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(),
- x11Data->d,
- ZPixmap, 0, (char *)dptr, w, h, 32, 0);
- XPutImage(dpy, qt_x11Pixmap(pm)->handle(), gc, xi, 0, 0, 0, 0, w, h);
- qSafeXDestroyImage(xi);
- XFreeGC(xinfo.display(), gc);
-
- if (x11_mask) { // xform mask, too
- pm.setMask(mask_to_bitmap(xinfo.screen()).transformed(transform));
- } else if (d != 32 && complex_xform) { // need a mask!
- QBitmap mask(ws, hs);
- mask.fill(Qt::color1);
- pm.setMask(mask.transformed(transform));
- }
- return pm;
- }
-}
-
-QImage QX11PlatformPixmap::toImage() const
-{
- return toImage(QRect(0, 0, w, h));
-}
-
-QImage QX11PlatformPixmap::toImage(const QRect &rect) const
-{
- Window root_return;
- int x_return;
- int y_return;
- unsigned int width_return;
- unsigned int height_return;
- unsigned int border_width_return;
- unsigned int depth_return;
-
- XGetGeometry(xinfo.display(), hd, &root_return, &x_return, &y_return, &width_return, &height_return, &border_width_return, &depth_return);
-
- QXImageWrapper xiWrapper;
- xiWrapper.xi = XGetImage(xinfo.display(), hd, rect.x(), rect.y(), rect.width(), rect.height(),
- AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap);
-
- Q_CHECK_PTR(xiWrapper.xi);
- if (!xiWrapper.xi)
- return QImage();
-
- if (!x11_mask && canTakeQImageFromXImage(xiWrapper))
- return takeQImageFromXImage(xiWrapper);
-
- QImage image = toImage(xiWrapper, rect);
- qSafeXDestroyImage(xiWrapper.xi);
- return image;
-}
-
-#if QT_CONFIG(xrender)
-static XRenderPictFormat *qt_renderformat_for_depth(const QXcbX11Info &xinfo, int depth)
-{
- if (depth == 1)
- return XRenderFindStandardFormat(xinfo.display(), PictStandardA1);
- else if (depth == 32)
- return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32);
- else
- return XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual());
-}
-#endif
-
-Q_GLOBAL_STATIC(QX11PaintEngine, qt_x11_paintengine)
-
-QPaintEngine *QX11PlatformPixmap::paintEngine() const
-{
- QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
-
- if ((flags & Readonly)/* && share_mode == QPixmap::ImplicitlyShared*/) {
- // if someone wants to draw onto us, copy the shared contents
- // and turn it into a fully fledged QPixmap
- ::Pixmap hd_copy = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()),
- w, h, d);
-#if QT_CONFIG(xrender)
- if (picture && d == 32) {
- XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d);
- ::Picture picture_copy = XRenderCreatePicture(xinfo.display(),
- hd_copy, format,
- 0, 0);
-
- XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, picture_copy,
- 0, 0, 0, 0, 0, 0, w, h);
- XRenderFreePicture(xinfo.display(), picture);
- that->picture = picture_copy;
- } else
-#endif
- {
- GC gc = XCreateGC(xinfo.display(), hd_copy, 0, 0);
- XCopyArea(xinfo.display(), hd, hd_copy, gc, 0, 0, w, h, 0, 0);
- XFreeGC(xinfo.display(), gc);
- }
- that->hd = hd_copy;
- that->flags &= ~QX11PlatformPixmap::Readonly;
- }
-
- if (qt_x11_paintengine->isActive()) {
- if (!that->pengine)
- that->pengine = new QX11PaintEngine;
-
- return that->pengine;
- }
-
- return qt_x11_paintengine();
-}
-
-qreal QX11PlatformPixmap::devicePixelRatio() const
-{
- return dpr;
-}
-
-void QX11PlatformPixmap::setDevicePixelRatio(qreal scaleFactor)
-{
- dpr = scaleFactor;
-}
-
-Pixmap QX11PlatformPixmap::x11ConvertToDefaultDepth()
-{
-#if QT_CONFIG(xrender)
- if (d == xinfo.appDepth() || !X11->use_xrender)
- return hd;
- if (!hd2) {
- hd2 = XCreatePixmap(xinfo.display(), hd, w, h, xinfo.appDepth());
- XRenderPictFormat *format = XRenderFindVisualFormat(xinfo.display(),
- (Visual*) xinfo.visual());
- Picture pic = XRenderCreatePicture(xinfo.display(), hd2, format, 0, 0);
- XRenderComposite(xinfo.display(), PictOpSrc, picture,
- XNone, pic, 0, 0, 0, 0, 0, 0, w, h);
- XRenderFreePicture(xinfo.display(), pic);
- }
- return hd2;
-#else
- return hd;
-#endif
-}
-
-XID QX11PlatformPixmap::createBitmapFromImage(const QImage &image)
-{
- QImage img = image.convertToFormat(QImage::Format_MonoLSB);
- const QRgb c0 = QColor(Qt::black).rgb();
- const QRgb c1 = QColor(Qt::white).rgb();
- if (img.color(0) == c0 && img.color(1) == c1) {
- img.invertPixels();
- img.setColor(0, c1);
- img.setColor(1, c0);
- }
-
- char *bits;
- uchar *tmp_bits;
- int w = img.width();
- int h = img.height();
- int bpl = (w + 7) / 8;
- qsizetype ibpl = img.bytesPerLine();
- if (bpl != ibpl) {
- tmp_bits = new uchar[bpl*h];
- bits = (char *)tmp_bits;
- uchar *p, *b;
- int y;
- b = tmp_bits;
- p = img.scanLine(0);
- for (y = 0; y < h; y++) {
- memcpy(b, p, bpl);
- b += bpl;
- p += ibpl;
- }
- } else {
- bits = (char *)img.bits();
- tmp_bits = 0;
- }
- XID hd = XCreateBitmapFromData(QXcbX11Info::display(),
- QXcbX11Info::appRootWindow(),
- bits, w, h);
- if (tmp_bits) // Avoid purify complaint
- delete [] tmp_bits;
- return hd;
-}
-
-bool QX11PlatformPixmap::isBackingStore() const
-{
- return (flags & IsBackingStore);
-}
-
-void QX11PlatformPixmap::setIsBackingStore(bool on)
-{
- if (on)
- flags |= IsBackingStore;
- else {
- flags &= ~IsBackingStore;
- }
-}
-
-#if QT_CONFIG(xrender)
-void QX11PlatformPixmap::convertToARGB32(bool preserveContents)
-{
- if (!X11->use_xrender)
- return;
-
- // Q_ASSERT(count == 1);
- if ((flags & Readonly)/* && share_mode == QPixmap::ExplicitlyShared*/)
- return;
-
- Pixmap pm = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()),
- w, h, 32);
- Picture p = XRenderCreatePicture(xinfo.display(), pm,
- XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32), 0, 0);
- if (picture) {
- if (preserveContents)
- XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h);
- if (!(flags & Readonly))
- XRenderFreePicture(xinfo.display(), picture);
- }
- if (hd && !(flags & Readonly))
- XFreePixmap(xinfo.display(), hd);
- if (x11_mask) {
- XFreePixmap(xinfo.display(), x11_mask);
- if (mask_picture)
- XRenderFreePicture(xinfo.display(), mask_picture);
- x11_mask = 0;
- mask_picture = 0;
- }
- hd = pm;
- picture = p;
-
- d = 32;
- xinfo.setDepth(32);
-
- XVisualInfo visinfo;
- if (XMatchVisualInfo(xinfo.display(), xinfo.screen(), 32, TrueColor, &visinfo))
- xinfo.setVisual(visinfo.visual);
-}
-#endif
-
-void QX11PlatformPixmap::release()
-{
- delete pengine;
- pengine = 0;
-
- if (/*!X11*/ QCoreApplication::closingDown()) {
- // At this point, the X server will already have freed our resources,
- // so there is nothing to do.
- return;
- }
-
- if (x11_mask) {
-#if QT_CONFIG(xrender)
- if (mask_picture)
- XRenderFreePicture(xinfo.display(), mask_picture);
- mask_picture = 0;
-#endif
- XFreePixmap(xinfo.display(), x11_mask);
- x11_mask = 0;
- }
-
- if (hd) {
-#if QT_CONFIG(xrender)
- if (picture) {
- XRenderFreePicture(xinfo.display(), picture);
- picture = 0;
- }
-#endif // QT_CONFIG(xrender)
-
- if (hd2) {
- XFreePixmap(xinfo.display(), hd2);
- hd2 = 0;
- }
- if (!(flags & Readonly))
- XFreePixmap(xinfo.display(), hd);
- hd = 0;
- }
-}
-
-QImage QX11PlatformPixmap::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const
-{
- XImage *xi = xiWrapper.xi;
-
- int d = depth();
- Visual *visual = (Visual *)xinfo.visual();
- bool trucol = (visual->c_class >= TrueColor) && d > 1;
-
- QImage::Format format = QImage::Format_Mono;
- if (d > 1 && d <= 8) {
- d = 8;
- format = QImage::Format_Indexed8;
- }
- // we could run into the situation where d == 8 AND trucol is true, which can
- // cause problems when converting to and from images. in this case, always treat
- // the depth as 32...
- if (d > 8 || trucol) {
- d = 32;
- format = QImage::Format_RGB32;
- }
-
- if (d == 1 && xi->bitmap_bit_order == LSBFirst)
- format = QImage::Format_MonoLSB;
- if (x11_mask && format == QImage::Format_RGB32)
- format = QImage::Format_ARGB32;
-
- QImage image(xi->width, xi->height, format);
- image.setDevicePixelRatio(devicePixelRatio());
- if (image.isNull()) // could not create image
- return image;
-
- QImage alpha;
- if (x11_mask) {
- if (rect.contains(QRect(0, 0, w, h)))
- alpha = mask().toImage();
- else
- alpha = mask().toImage().copy(rect);
- }
- bool ale = alpha.format() == QImage::Format_MonoLSB;
-
- if (trucol) { // truecolor
- const uint red_mask = (uint)visual->red_mask;
- const uint green_mask = (uint)visual->green_mask;
- const uint blue_mask = (uint)visual->blue_mask;
- const int red_shift = highest_bit(red_mask) - 7;
- const int green_shift = highest_bit(green_mask) - 7;
- const int blue_shift = highest_bit(blue_mask) - 7;
-
- const uint red_bits = n_bits(red_mask);
- const uint green_bits = n_bits(green_mask);
- const uint blue_bits = n_bits(blue_mask);
-
- static uint red_table_bits = 0;
- static uint green_table_bits = 0;
- static uint blue_table_bits = 0;
-
- if (red_bits < 8 && red_table_bits != red_bits) {
- build_scale_table(&red_scale_table, red_bits);
- red_table_bits = red_bits;
- }
- if (blue_bits < 8 && blue_table_bits != blue_bits) {
- build_scale_table(&blue_scale_table, blue_bits);
- blue_table_bits = blue_bits;
- }
- if (green_bits < 8 && green_table_bits != green_bits) {
- build_scale_table(&green_scale_table, green_bits);
- green_table_bits = green_bits;
- }
-
- int r, g, b;
-
- QRgb *dst;
- uchar *src;
- uint pixel;
- int bppc = xi->bits_per_pixel;
-
- if (bppc > 8 && xi->byte_order == LSBFirst)
- bppc++;
-
- for (int y = 0; y < xi->height; ++y) {
- uchar* asrc = x11_mask ? alpha.scanLine(y) : 0;
- dst = (QRgb *)image.scanLine(y);
- src = (uchar *)xi->data + xi->bytes_per_line*y;
- for (int x = 0; x < xi->width; x++) {
- switch (bppc) {
- case 8:
- pixel = *src++;
- break;
- case 16: // 16 bit MSB
- pixel = src[1] | (uint)src[0] << 8;
- src += 2;
- break;
- case 17: // 16 bit LSB
- pixel = src[0] | (uint)src[1] << 8;
- src += 2;
- break;
- case 24: // 24 bit MSB
- pixel = src[2] | (uint)src[1] << 8 | (uint)src[0] << 16;
- src += 3;
- break;
- case 25: // 24 bit LSB
- pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16;
- src += 3;
- break;
- case 32: // 32 bit MSB
- pixel = src[3] | (uint)src[2] << 8 | (uint)src[1] << 16 | (uint)src[0] << 24;
- src += 4;
- break;
- case 33: // 32 bit LSB
- pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16 | (uint)src[3] << 24;
- src += 4;
- break;
- default: // should not really happen
- x = xi->width; // leave loop
- y = xi->height;
- pixel = 0; // eliminate compiler warning
- qWarning("QPixmap::convertToImage: Invalid depth %d", bppc);
- }
- if (red_shift > 0)
- r = (pixel & red_mask) >> red_shift;
- else
- r = (pixel & red_mask) << -red_shift;
- if (green_shift > 0)
- g = (pixel & green_mask) >> green_shift;
- else
- g = (pixel & green_mask) << -green_shift;
- if (blue_shift > 0)
- b = (pixel & blue_mask) >> blue_shift;
- else
- b = (pixel & blue_mask) << -blue_shift;
-
- if (red_bits < 8)
- r = red_scale_table[r];
- if (green_bits < 8)
- g = green_scale_table[g];
- if (blue_bits < 8)
- b = blue_scale_table[b];
-
- if (x11_mask) {
- if (ale) {
- *dst++ = (asrc[x >> 3] & (1 << (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
- } else {
- *dst++ = (asrc[x >> 3] & (0x80 >> (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
- }
- } else {
- *dst++ = qRgb(r, g, b);
- }
- }
- }
- } else if (xi->bits_per_pixel == d) { // compatible depth
- char *xidata = xi->data; // copy each scanline
- qsizetype bpl = qMin(image.bytesPerLine(),xi->bytes_per_line);
- for (int y=0; y<xi->height; y++) {
- memcpy(image.scanLine(y), xidata, bpl);
- xidata += xi->bytes_per_line;
- }
- } else {
- /* Typically 2 or 4 bits display depth */
- qWarning("QPixmap::convertToImage: Display not supported (bpp=%d)",
- xi->bits_per_pixel);
- return QImage();
- }
-
- if (d == 1) { // bitmap
- image.setColorCount(2);
- image.setColor(0, qRgb(255,255,255));
- image.setColor(1, qRgb(0,0,0));
- } else if (!trucol) { // pixmap with colormap
- uchar *p;
- uchar *end;
- uchar use[256]; // pixel-in-use table
- uchar pix[256]; // pixel translation table
- int ncols;
- memset(use, 0, 256);
- memset(pix, 0, 256);
- qsizetype bpl = image.bytesPerLine();
-
- if (x11_mask) { // which pixels are used?
- for (int i = 0; i < xi->height; i++) {
- uchar* asrc = alpha.scanLine(i);
- p = image.scanLine(i);
- if (ale) {
- for (int x = 0; x < xi->width; x++) {
- if (asrc[x >> 3] & (1 << (x & 7)))
- use[*p] = 1;
- ++p;
- }
- } else {
- for (int x = 0; x < xi->width; x++) {
- if (asrc[x >> 3] & (0x80 >> (x & 7)))
- use[*p] = 1;
- ++p;
- }
- }
- }
- } else {
- for (int i = 0; i < xi->height; i++) {
- p = image.scanLine(i);
- end = p + bpl;
- while (p < end)
- use[*p++] = 1;
- }
- }
- ncols = 0;
- for (int i = 0; i < 256; i++) { // build translation table
- if (use[i])
- pix[i] = ncols++;
- }
- for (int i = 0; i < xi->height; i++) { // translate pixels
- p = image.scanLine(i);
- end = p + bpl;
- while (p < end) {
- *p = pix[*p];
- p++;
- }
- }
- if (x11_mask) {
- int trans;
- if (ncols < 256) {
- trans = ncols++;
- image.setColorCount(ncols); // create color table
- image.setColor(trans, 0x00000000);
- } else {
- image.setColorCount(ncols); // create color table
- // oh dear... no spare "transparent" pixel.
- // use first pixel in image (as good as any).
- trans = image.scanLine(0)[0];
- }
- for (int i = 0; i < xi->height; i++) {
- uchar* asrc = alpha.scanLine(i);
- p = image.scanLine(i);
- if (ale) {
- for (int x = 0; x < xi->width; x++) {
- if (!(asrc[x >> 3] & (1 << (x & 7))))
- *p = trans;
- ++p;
- }
- } else {
- for (int x = 0; x < xi->width; x++) {
- if (!(asrc[x >> 3] & (1 << (7 -(x & 7)))))
- *p = trans;
- ++p;
- }
- }
- }
- } else {
- image.setColorCount(ncols); // create color table
- }
- QList<QColor> colors = QXcbColormap::instance(xinfo.screen()).colormap();
- int j = 0;
- for (int i=0; i<colors.size(); i++) { // translate pixels
- if (use[i])
- image.setColor(j++, 0xff000000 | colors.at(i).rgb());
- }
- }
-
- return image;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
deleted file mode 100644
index 0755a34b4a8..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (C) 2018 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
-
-#pragma once
-
-#include <QBitmap>
-#include <QPixmap>
-
-#include <qpa/qplatformpixmap.h>
-#include "qxcbnativepainting.h"
-
-typedef unsigned long XID;
-typedef XID Drawable;
-typedef XID Picture;
-typedef XID Pixmap;
-
-QT_BEGIN_NAMESPACE
-
-class QX11PaintEngine;
-struct QXImageWrapper;
-
-class QX11PlatformPixmap : public QPlatformPixmap
-{
-public:
- QX11PlatformPixmap(PixelType pixelType);
- ~QX11PlatformPixmap();
-
- QPlatformPixmap *createCompatiblePlatformPixmap() const override;
- void resize(int width, int height) override;
- void fromImage(const QImage &img, Qt::ImageConversionFlags flags) override;
- void copy(const QPlatformPixmap *data, const QRect &rect) override;
- bool scroll(int dx, int dy, const QRect &rect) override;
- int metric(QPaintDevice::PaintDeviceMetric metric) const override;
- void fill(const QColor &fillColor) override;
- QBitmap mask() const override;
- void setMask(const QBitmap &mask) override;
- bool hasAlphaChannel() const override;
- QPixmap transformed(const QTransform &matrix, Qt::TransformationMode mode) const override;
- QImage toImage() const override;
- QImage toImage(const QRect &rect) const override;
- QPaintEngine *paintEngine() const override;
- qreal devicePixelRatio() const override;
- void setDevicePixelRatio(qreal scaleFactor) override;
-
- inline Drawable handle() const { return hd; }
- inline Picture x11PictureHandle() const { return picture; }
- inline const QXcbX11Info *x11_info() const { return &xinfo; }
-
- Pixmap x11ConvertToDefaultDepth();
- static XID createBitmapFromImage(const QImage &image);
-
-#if QT_CONFIG(xrender)
- void convertToARGB32(bool preserveContents = true);
-#endif
-
- bool isBackingStore() const;
- void setIsBackingStore(bool on);
-private:
- friend class QX11PaintEngine;
- friend const QXcbX11Info &qt_x11Info(const QPixmap &pixmap);
- friend void qt_x11SetScreen(QPixmap &pixmap, int screen);
-
- void release();
- QImage toImage(const QXImageWrapper &xi, const QRect &rect) const;
- QBitmap mask_to_bitmap(int screen) const;
- static Pixmap bitmap_to_mask(const QBitmap &, int screen);
- void bitmapFromImage(const QImage &image);
- bool canTakeQImageFromXImage(const QXImageWrapper &xi) const;
- QImage takeQImageFromXImage(const QXImageWrapper &xi) const;
-
- Pixmap hd = 0;
-
- enum Flag {
- NoFlags = 0x0,
- Uninitialized = 0x1,
- Readonly = 0x2,
- InvertedWhenBoundToTexture = 0x4,
- GlSurfaceCreatedWithAlpha = 0x8,
- IsBackingStore = 0x10
- };
- uint flags;
-
- QXcbX11Info xinfo;
- Pixmap x11_mask;
- Picture picture;
- Picture mask_picture;
- Pixmap hd2; // sorted in the default display depth
- //QPixmap::ShareMode share_mode;
- qreal dpr;
-
- QX11PaintEngine *pengine;
-};
-
-inline QX11PlatformPixmap *qt_x11Pixmap(const QPixmap &pixmap)
-{
- return (pixmap.handle() && pixmap.handle()->classId() == QPlatformPixmap::X11Class)
- ? static_cast<QX11PlatformPixmap *>(pixmap.handle())
- : nullptr;
-}
-
-inline Picture qt_x11PictureHandle(const QPixmap &pixmap)
-{
- if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap))
- return pm->x11PictureHandle();
-
- return 0;
-}
-
-inline Pixmap qt_x11PixmapHandle(const QPixmap &pixmap)
-{
- if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap))
- return pm->handle();
-
- return 0;
-}
-
-inline const QXcbX11Info &qt_x11Info(const QPixmap &pixmap)
-{
- if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap)) {
- return pm->xinfo;
- } else {
- static QXcbX11Info nullX11Info;
- return nullX11Info;
- }
-}
-
-int qt_x11SetDefaultScreen(int screen);
-void qt_x11SetScreen(QPixmap &pixmap, int screen);
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpolygonclipper_p.h b/src/plugins/platforms/xcb/nativepainting/qpolygonclipper_p.h
deleted file mode 100644
index e1e31722d76..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qpolygonclipper_p.h
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright (C) 2016 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
-
-#pragma once
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qrect.h>
-#include <QtGui/private/qdatabuffer_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/* based on sutherland-hodgman line-by-line clipping, as described in
- Computer Graphics and Principles */
-template <typename InType, typename OutType, typename CastType> class QPolygonClipper
-{
-public:
- QPolygonClipper() :
- buffer1(0), buffer2(0)
- {
- x1 = y1 = x2 = y2 = 0;
- }
-
- ~QPolygonClipper()
- {
- }
-
- void setBoundingRect(const QRect bounds)
- {
- x1 = bounds.x();
- x2 = bounds.x() + bounds.width();
- y1 = bounds.y();
- y2 = bounds.y() + bounds.height();
- }
-
- QRect boundingRect()
- {
- return QRect(QPoint(x1, y1), QPoint(x2, y2));
- }
-
- inline OutType intersectLeft(const OutType &p1, const OutType &p2)
- {
- OutType t;
- qreal dy = (p1.y - p2.y) / qreal(p1.x - p2.x);
- t.x = x1;
- t.y = static_cast<CastType>(p2.y + (x1 - p2.x) * dy);
- return t;
- }
-
-
- inline OutType intersectRight(const OutType &p1, const OutType &p2)
- {
- OutType t;
- qreal dy = (p1.y - p2.y) / qreal(p1.x - p2.x);
- t.x = x2;
- t.y = static_cast<CastType>(p2.y + (x2 - p2.x) * dy);
- return t;
- }
-
-
- inline OutType intersectTop(const OutType &p1, const OutType &p2)
- {
- OutType t;
- qreal dx = (p1.x - p2.x) / qreal(p1.y - p2.y);
- t.x = static_cast<CastType>(p2.x + (y1 - p2.y) * dx);
- t.y = y1;
- return t;
- }
-
-
- inline OutType intersectBottom(const OutType &p1, const OutType &p2)
- {
- OutType t;
- qreal dx = (p1.x - p2.x) / qreal(p1.y - p2.y);
- t.x = static_cast<CastType>(p2.x + (y2 - p2.y) * dx);
- t.y = y2;
- return t;
- }
-
-
- void clipPolygon(const InType *inPoints, int inCount, OutType **outPoints, int *outCount,
- bool closePolygon = true)
- {
- Q_ASSERT(outPoints);
- Q_ASSERT(outCount);
-
- if (inCount < 2) {
- *outCount = 0;
- return;
- }
-
- buffer1.reset();
- buffer2.reset();
-
- QDataBuffer<OutType> *source = &buffer1;
- QDataBuffer<OutType> *clipped = &buffer2;
-
- // Gather some info since we are iterating through the points anyway..
- bool doLeft = false, doRight = false, doTop = false, doBottom = false;
- OutType ot;
- for (int i=0; i<inCount; ++i) {
- ot = inPoints[i];
- clipped->add(ot);
-
- if (ot.x < x1)
- doLeft = true;
- else if (ot.x > x2)
- doRight = true;
- if (ot.y < y1)
- doTop = true;
- else if (ot.y > y2)
- doBottom = true;
- }
-
- if (doLeft && clipped->size() > 1) {
- QDataBuffer<OutType> *tmp = source;
- source = clipped;
- clipped = tmp;
- clipped->reset();
- int lastPos, start;
- if (closePolygon) {
- lastPos = source->size() - 1;
- start = 0;
- } else {
- lastPos = 0;
- start = 1;
- if (source->at(0).x >= x1)
- clipped->add(source->at(0));
- }
- for (int i=start; i<inCount; ++i) {
- const OutType &cpt = source->at(i);
- const OutType &ppt = source->at(lastPos);
-
- if (cpt.x >= x1) {
- if (ppt.x >= x1) {
- clipped->add(cpt);
- } else {
- clipped->add(intersectLeft(cpt, ppt));
- clipped->add(cpt);
- }
- } else if (ppt.x >= x1) {
- clipped->add(intersectLeft(cpt, ppt));
- }
- lastPos = i;
- }
- }
-
- if (doRight && clipped->size() > 1) {
- QDataBuffer<OutType> *tmp = source;
- source = clipped;
- clipped = tmp;
- clipped->reset();
- int lastPos, start;
- if (closePolygon) {
- lastPos = source->size() - 1;
- start = 0;
- } else {
- lastPos = 0;
- start = 1;
- if (source->at(0).x <= x2)
- clipped->add(source->at(0));
- }
- for (int i=start; i<source->size(); ++i) {
- const OutType &cpt = source->at(i);
- const OutType &ppt = source->at(lastPos);
-
- if (cpt.x <= x2) {
- if (ppt.x <= x2) {
- clipped->add(cpt);
- } else {
- clipped->add(intersectRight(cpt, ppt));
- clipped->add(cpt);
- }
- } else if (ppt.x <= x2) {
- clipped->add(intersectRight(cpt, ppt));
- }
-
- lastPos = i;
- }
-
- }
-
- if (doTop && clipped->size() > 1) {
- QDataBuffer<OutType> *tmp = source;
- source = clipped;
- clipped = tmp;
- clipped->reset();
- int lastPos, start;
- if (closePolygon) {
- lastPos = source->size() - 1;
- start = 0;
- } else {
- lastPos = 0;
- start = 1;
- if (source->at(0).y >= y1)
- clipped->add(source->at(0));
- }
- for (int i=start; i<source->size(); ++i) {
- const OutType &cpt = source->at(i);
- const OutType &ppt = source->at(lastPos);
-
- if (cpt.y >= y1) {
- if (ppt.y >= y1) {
- clipped->add(cpt);
- } else {
- clipped->add(intersectTop(cpt, ppt));
- clipped->add(cpt);
- }
- } else if (ppt.y >= y1) {
- clipped->add(intersectTop(cpt, ppt));
- }
-
- lastPos = i;
- }
- }
-
- if (doBottom && clipped->size() > 1) {
- QDataBuffer<OutType> *tmp = source;
- source = clipped;
- clipped = tmp;
- clipped->reset();
- int lastPos, start;
- if (closePolygon) {
- lastPos = source->size() - 1;
- start = 0;
- } else {
- lastPos = 0;
- start = 1;
- if (source->at(0).y <= y2)
- clipped->add(source->at(0));
- }
- for (int i=start; i<source->size(); ++i) {
- const OutType &cpt = source->at(i);
- const OutType &ppt = source->at(lastPos);
-
- if (cpt.y <= y2) {
- if (ppt.y <= y2) {
- clipped->add(cpt);
- } else {
- clipped->add(intersectBottom(cpt, ppt));
- clipped->add(cpt);
- }
- } else if (ppt.y <= y2) {
- clipped->add(intersectBottom(cpt, ppt));
- }
- lastPos = i;
- }
- }
-
- if (closePolygon && clipped->size() > 0) {
- // close clipped polygon
- if (clipped->at(0).x != clipped->at(clipped->size()-1).x ||
- clipped->at(0).y != clipped->at(clipped->size()-1).y) {
- OutType ot = clipped->at(0);
- clipped->add(ot);
- }
- }
- *outCount = clipped->size();
- *outPoints = clipped->data();
- }
-
-private:
- int x1, x2, y1, y2;
- QDataBuffer<OutType> buffer1;
- QDataBuffer<OutType> buffer2;
-};
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
deleted file mode 100644
index 2986b8f1453..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (C) 2018 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
-
-#pragma once
-
-#define register /* C++17 deprecated register */
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-#undef register
-
-#if QT_CONFIG(xrender)
-# include "qtessellator_p.h"
-# include <X11/extensions/Xrender.h>
-#endif
-
-#if QT_CONFIG(fontconfig)
-#include <fontconfig/fontconfig.h>
-#endif
-
-#if defined(FT_LCD_FILTER_H)
-#include FT_LCD_FILTER_H
-#endif
-
-#if defined(FC_LCD_FILTER)
-
-#ifndef FC_LCD_FILTER_NONE
-#define FC_LCD_FILTER_NONE FC_LCD_NONE
-#endif
-
-#ifndef FC_LCD_FILTER_DEFAULT
-#define FC_LCD_FILTER_DEFAULT FC_LCD_DEFAULT
-#endif
-
-#ifndef FC_LCD_FILTER_LIGHT
-#define FC_LCD_FILTER_LIGHT FC_LCD_LIGHT
-#endif
-
-#ifndef FC_LCD_FILTER_LEGACY
-#define FC_LCD_FILTER_LEGACY FC_LCD_LEGACY
-#endif
-
-#endif
-
-QT_BEGIN_NAMESPACE
-
-// rename a couple of X defines to get rid of name clashes
-// resolve the conflict between X11's FocusIn and QEvent::FocusIn
-enum {
- XFocusOut = FocusOut,
- XFocusIn = FocusIn,
- XKeyPress = KeyPress,
- XKeyRelease = KeyRelease,
- XNone = None,
- XRevertToParent = RevertToParent,
- XGrayScale = GrayScale,
- XCursorShape = CursorShape,
-};
-#undef FocusOut
-#undef FocusIn
-#undef KeyPress
-#undef KeyRelease
-#undef None
-#undef RevertToParent
-#undef GrayScale
-#undef CursorShape
-
-#ifdef FontChange
-#undef FontChange
-#endif
-
-Q_DECLARE_TYPEINFO(XPoint, Q_PRIMITIVE_TYPE);
-Q_DECLARE_TYPEINFO(XRectangle, Q_PRIMITIVE_TYPE);
-Q_DECLARE_TYPEINFO(XChar2b, Q_PRIMITIVE_TYPE);
-#if QT_CONFIG(xrender)
-Q_DECLARE_TYPEINFO(XGlyphElt32, Q_PRIMITIVE_TYPE);
-#endif
-
-struct QX11InfoData;
-
-enum DesktopEnvironment {
- DE_UNKNOWN,
- DE_KDE,
- DE_GNOME,
- DE_CDE,
- DE_MEEGO_COMPOSITOR,
- DE_4DWM
-};
-
-struct QXcbX11Data {
- Display *display = nullptr;
-
- // true if Qt is compiled w/ RENDER support and RENDER is supported on the connected Display
- bool use_xrender = false;
- int xrender_major = 0;
- int xrender_version = 0;
-
- QX11InfoData *screens = nullptr;
- Visual **argbVisuals = nullptr;
- Colormap *argbColormaps = nullptr;
- int screenCount = 0;
- int defaultScreen = 0;
-
- // options
- int visual_class = 0;
- int visual_id = 0;
- int color_count = 0;
- bool custom_cmap = false;
-
- // outside visual/colormap
- Visual *visual = nullptr;
- Colormap colormap = 0;
-
-#if QT_CONFIG(xrender)
- enum { solid_fill_count = 16 };
- struct SolidFills {
- XRenderColor color;
- int screen;
- Picture picture;
- } solid_fills[solid_fill_count];
- enum { pattern_fill_count = 16 };
- struct PatternFills {
- XRenderColor color;
- XRenderColor bg_color;
- int screen;
- int style;
- bool opaque;
- Picture picture;
- } pattern_fills[pattern_fill_count];
- Picture getSolidFill(int screen, const QColor &c);
- XRenderColor preMultiply(const QColor &c);
-#endif
-
- bool fc_antialias = true;
- int fc_hint_style = 0;
-
- DesktopEnvironment desktopEnvironment = DE_GNOME;
-};
-
-extern QXcbX11Data *qt_x11Data;
-#define X11 qt_x11Data
-
-struct QX11InfoData {
- int screen;
- int dpiX;
- int dpiY;
- int depth;
- int cells;
- Colormap colormap;
- Visual *visual;
- bool defaultColormap;
- bool defaultVisual;
- int subpixel = 0;
-};
-
-template <class T>
-constexpr inline int lowest_bit(T v) noexcept
-{
- int result = qCountTrailingZeroBits(v);
- return ((result >> 3) == sizeof(T)) ? -1 : result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
deleted file mode 100644
index dd83f8852b7..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
+++ /dev/null
@@ -1,1466 +0,0 @@
-// Copyright (C) 2018 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 "qtessellator_p.h"
-
-#include <QRect>
-#include <QList>
-#include <QMap>
-#include <QDebug>
-
-#include <qmath.h>
-#include <limits.h>
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-//#define DEBUG
-#ifdef DEBUG
-#define QDEBUG qDebug
-#else
-#define QDEBUG if (1){} else qDebug
-#endif
-
-static const bool emit_clever = true;
-static const bool mark_clever = false;
-
-enum VertexFlags {
- LineBeforeStarts = 0x1,
- LineBeforeEnds = 0x2,
- LineBeforeHorizontal = 0x4,
- LineAfterStarts = 0x8,
- LineAfterEnds = 0x10,
- LineAfterHorizontal = 0x20
-};
-
-
-
-class QTessellatorPrivate {
-public:
- struct Vertices;
-
- QTessellatorPrivate() {}
-
- QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
- void cancelCoincidingEdges();
-
- void emitEdges(QTessellator *tessellator);
- void processIntersections();
- void removeEdges();
- void addEdges();
- void addIntersections();
-
- struct Vertex : public QTessellator::Vertex
- {
- int flags;
- };
-
- struct Intersection
- {
- Q27Dot5 y;
- int edge;
- bool operator <(const Intersection &other) const {
- if (y != other.y)
- return y < other.y;
- return edge < other.edge;
- }
- };
- struct IntersectionLink
- {
- int next;
- int prev;
- };
- typedef QMap<Intersection, IntersectionLink> Intersections;
-
- struct Edge {
- Edge(const Vertices &v, int _edge);
- int edge;
- const Vertex *v0;
- const Vertex *v1;
- Q27Dot5 y_left;
- Q27Dot5 y_right;
- signed int winding : 8;
- bool mark;
- bool free;
- bool intersect_left;
- bool intersect_right;
- bool isLeftOf(const Edge &other, Q27Dot5 y) const;
- Q27Dot5 positionAt(Q27Dot5 y) const;
- bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
-
- };
-
- class EdgeSorter
- {
- public:
- EdgeSorter(int _y) : y(_y) {}
- bool operator() (const Edge *e1, const Edge *e2);
- int y;
- };
-
- class Scanline {
- public:
- Scanline();
- ~Scanline();
-
- void init(int maxActiveEdges);
- void done();
-
- int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
- int findEdgePosition(const Edge &e) const;
- int findEdge(int edge) const;
- void clearMarks();
-
- void swap(int p1, int p2) {
- Edge *tmp = edges[p1];
- edges[p1] = edges[p2];
- edges[p2] = tmp;
- }
- void insert(int pos, const Edge &e);
- void removeAt(int pos);
- void markEdges(int pos1, int pos2);
-
- void prepareLine();
- void lineDone();
-
- Edge **old;
- int old_size;
-
- Edge **edges;
- int size;
-
- private:
- Edge *edge_table;
- int first_unused;
- int max_edges;
- enum { default_alloc = 32 };
- };
-
- struct Vertices {
- enum { default_alloc = 128 };
- Vertices();
- ~Vertices();
- void init(int maxVertices);
- void done();
- Vertex *storage;
- Vertex **sorted;
-
- Vertex *operator[] (int i) { return storage + i; }
- const Vertex *operator[] (int i) const { return storage + i; }
- int position(const Vertex *v) const {
- return v - storage;
- }
- Vertex *next(Vertex *v) {
- ++v;
- if (v == storage + nPoints)
- v = storage;
- return v;
- }
- const Vertex *next(const Vertex *v) const {
- ++v;
- if (v == storage + nPoints)
- v = storage;
- return v;
- }
- int nextPos(const Vertex *v) const {
- ++v;
- if (v == storage + nPoints)
- return 0;
- return v - storage;
- }
- Vertex *prev(Vertex *v) {
- if (v == storage)
- v = storage + nPoints;
- --v;
- return v;
- }
- const Vertex *prev(const Vertex *v) const {
- if (v == storage)
- v = storage + nPoints;
- --v;
- return v;
- }
- int prevPos(const Vertex *v) const {
- if (v == storage)
- v = storage + nPoints;
- --v;
- return v - storage;
- }
- int nPoints;
- int allocated;
- };
- Vertices vertices;
- Intersections intersections;
- Scanline scanline;
- bool winding;
- Q27Dot5 y;
- int currentVertex;
-
-private:
- void addIntersection(const Edge *e1, const Edge *e2);
- bool edgeInChain(Intersection i, int edge);
-};
-
-
-QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, int edge)
-{
- this->edge = edge;
- intersect_left = intersect_right = true;
- mark = false;
- free = false;
-
- v0 = vertices[edge];
- v1 = vertices.next(v0);
-
- Q_ASSERT(v0->y != v1->y);
-
- if (v0->y > v1->y) {
- qSwap(v0, v1);
- winding = -1;
- } else {
- winding = 1;
- }
- y_left = y_right = v0->y;
-}
-
-// This is basically the algorithm from graphics gems. The algorithm
-// is cubic in the coordinates at one place. Since we use 64bit
-// integers, this implies, that the allowed range for our coordinates
-// is limited to 21 bits. With 5 bits behind the decimal, this
-// implies that differences in coordaintes can range from 2*SHORT_MIN
-// to 2*SHORT_MAX, giving us efficiently a coordinate system from
-// SHORT_MIN to SHORT_MAX.
-//
-
-// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
-// exactly the same algorithm to calculate yi. It's also important to be sure the algorithms
-// are transitive (ie. the conditions below are true for all input data):
-//
-// a.intersect(b) == b.intersect(a)
-// a.isLeftOf(b) != b.isLeftOf(a)
-//
-// This is tricky to get right, so be very careful when changing anything in here!
-
-static inline bool sameSign(qint64 a, qint64 b) {
- return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
-}
-
-bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
-{
- qint64 a1 = v1->y - v0->y;
- qint64 b1 = v0->x - v1->x;
-
- qint64 a2 = other.v1->y - other.v0->y;
- qint64 b2 = other.v0->x - other.v1->x;
-
- qint64 det = a1 * b2 - a2 * b1;
- if (det == 0)
- return false;
-
- qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
-
- qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
- qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
-
- // Check signs of r3 and r4. If both point 3 and point 4 lie on
- // same side of line 1, the line segments do not intersect.
- QDEBUG() << " " << r3 << r4;
- if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
- return false;
-
- qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
-
- qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
- qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
-
- // Check signs of r1 and r2. If both point 1 and point 2 lie
- // on same side of second line segment, the line segments do not intersect.
- QDEBUG() << " " << r1 << r2;
- if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
- return false;
-
- // The det/2 is to get rounding instead of truncating. It
- // is added or subtracted to the numerator, depending upon the
- // sign of the numerator.
- qint64 offset = det < 0 ? -det : det;
- offset >>= 1;
-
- qint64 num = a2 * c1 - a1 * c2;
- *y = ( num < 0 ? num - offset : num + offset ) / det;
-
- *det_positive = (det > 0);
-
- return true;
-}
-
-#undef SAME_SIGNS
-
-bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
-{
-// QDEBUG() << "isLeftOf" << edge << other.edge << y;
- qint64 a1 = v1->y - v0->y;
- qint64 b1 = v0->x - v1->x;
- qint64 a2 = other.v1->y - other.v0->y;
- qint64 b2 = other.v0->x - other.v1->x;
-
- qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
-
- qint64 det = a1 * b2 - a2 * b1;
- if (det == 0) {
- // lines are parallel. Only need to check side of one point
- // fixed ordering for coincident edges
- qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
-// QDEBUG() << "det = 0" << r1;
- if (r1 == 0)
- return edge < other.edge;
- return (r1 < 0);
- }
-
- // not parallel, need to find the y coordinate of the intersection point
- qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
-
- qint64 offset = det < 0 ? -det : det;
- offset >>= 1;
-
- qint64 num = a2 * c1 - a1 * c2;
- qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
-// QDEBUG() << " num=" << num << "offset=" << offset << "det=" << det;
-
- return ((yi > y) ^ (det < 0));
-}
-
-static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
- const QTessellatorPrivate::Vertex *p2)
-{
- if (p1->y == p2->y) {
- if (p1->x == p2->x)
- return p1 < p2;
- return p1->x < p2->x;
- }
- return p1->y < p2->y;
-}
-
-Q27Dot5 QTessellatorPrivate::Edge::positionAt(Q27Dot5 y) const
-{
- if (y == v0->y)
- return v0->x;
- else if (y == v1->y)
- return v1->x;
-
- qint64 d = v1->x - v0->x;
- return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
-}
-
-bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
-{
- return e1->isLeftOf(*e2, y);
-}
-
-
-QTessellatorPrivate::Scanline::Scanline()
-{
- edges = 0;
- edge_table = 0;
- old = 0;
-}
-
-void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
-{
- maxActiveEdges *= 2;
- if (!edges || maxActiveEdges > default_alloc) {
- max_edges = maxActiveEdges;
- int s = qMax(maxActiveEdges + 1, default_alloc + 1);
- edges = q_check_ptr((Edge **)realloc(edges, s*sizeof(Edge *)));
- edge_table = q_check_ptr((Edge *)realloc(edge_table, s*sizeof(Edge)));
- old = q_check_ptr((Edge **)realloc(old, s*sizeof(Edge *)));
- }
- size = 0;
- old_size = 0;
- first_unused = 0;
- for (int i = 0; i < maxActiveEdges; ++i)
- edge_table[i].edge = i+1;
- edge_table[maxActiveEdges].edge = -1;
-}
-
-void QTessellatorPrivate::Scanline::done()
-{
- if (max_edges > default_alloc) {
- free(edges);
- free(old);
- free(edge_table);
- edges = 0;
- old = 0;
- edge_table = 0;
- }
-}
-
-QTessellatorPrivate::Scanline::~Scanline()
-{
- free(edges);
- free(old);
- free(edge_table);
-}
-
-int QTessellatorPrivate::Scanline::findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
-{
- int min = 0;
- int max = size - 1;
- while (min < max) {
- int pos = min + ((max - min + 1) >> 1);
- Q27Dot5 ax = edges[pos]->positionAt(y);
- if (ax > x) {
- max = pos - 1;
- } else {
- min = pos;
- }
- }
- return min;
-}
-
-int QTessellatorPrivate::Scanline::findEdgePosition(const Edge &e) const
-{
-// qDebug() << ">> findEdgePosition";
- int min = 0;
- int max = size;
- while (min < max) {
- int pos = min + ((max - min) >> 1);
-// qDebug() << " " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
- if (edges[pos]->isLeftOf(e, e.v0->y)) {
- min = pos + 1;
- } else {
- max = pos;
- }
- }
-// qDebug() << "<< findEdgePosition got" << min;
- return min;
-}
-
-int QTessellatorPrivate::Scanline::findEdge(int edge) const
-{
- for (int i = 0; i < size; ++i) {
- int item_edge = edges[i]->edge;
- if (item_edge == edge)
- return i;
- }
- //Q_ASSERT(false);
- return -1;
-}
-
-void QTessellatorPrivate::Scanline::clearMarks()
-{
- for (int i = 0; i < size; ++i) {
- edges[i]->mark = false;
- edges[i]->intersect_left = false;
- edges[i]->intersect_right = false;
- }
-}
-
-void QTessellatorPrivate::Scanline::prepareLine()
-{
- Edge **end = edges + size;
- Edge **e = edges;
- Edge **o = old;
- while (e < end) {
- *o = *e;
- ++o;
- ++e;
- }
- old_size = size;
-}
-
-void QTessellatorPrivate::Scanline::lineDone()
-{
- Edge **end = old + old_size;
- Edge **e = old;
- while (e < end) {
- if ((*e)->free) {
- (*e)->edge = first_unused;
- first_unused = (*e - edge_table);
- }
- ++e;
- }
-}
-
-void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
-{
- Edge *edge = edge_table + first_unused;
- first_unused = edge->edge;
- Q_ASSERT(first_unused != -1);
- *edge = e;
- memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
- edges[pos] = edge;
- ++size;
-}
-
-void QTessellatorPrivate::Scanline::removeAt(int pos)
-{
- Edge *e = edges[pos];
- e->free = true;
- --size;
- memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
-}
-
-void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
-{
- if (pos2 < pos1)
- return;
-
- for (int i = pos1; i <= pos2; ++i)
- edges[i]->mark = true;
-}
-
-
-QTessellatorPrivate::Vertices::Vertices()
-{
- storage = 0;
- sorted = 0;
- allocated = 0;
- nPoints = 0;
-}
-
-QTessellatorPrivate::Vertices::~Vertices()
-{
- if (storage) {
- free(storage);
- free(sorted);
- }
-}
-
-void QTessellatorPrivate::Vertices::init(int maxVertices)
-{
- if (!storage || maxVertices > allocated) {
- int size = qMax((int)default_alloc, maxVertices);
- storage = q_check_ptr((Vertex *)realloc(storage, size*sizeof(Vertex)));
- sorted = q_check_ptr((Vertex **)realloc(sorted, size*sizeof(Vertex *)));
- allocated = maxVertices;
- }
-}
-
-void QTessellatorPrivate::Vertices::done()
-{
- if (allocated > default_alloc) {
- free(storage);
- free(sorted);
- storage = 0;
- sorted = 0;
- allocated = 0;
- }
-}
-
-
-
-static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
- const QTessellatorPrivate::Vertices &vertices,
- QTessellator::Trapezoid *trap)
-{
- trap->top = y1;
- trap->bottom = y2;
- const QTessellatorPrivate::Vertex *v = vertices[left];
- trap->topLeft = v;
- trap->bottomLeft = vertices.next(v);
- if (trap->topLeft->y > trap->bottomLeft->y)
- qSwap(trap->topLeft,trap->bottomLeft);
- v = vertices[right];
- trap->topRight = v;
- trap->bottomRight = vertices.next(v);
- if (trap->topRight->y > trap->bottomRight->y)
- qSwap(trap->topRight, trap->bottomRight);
-}
-
-QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
-{
- *maxActiveEdges = 0;
- Vertex *v = vertices.storage;
- Vertex **vv = vertices.sorted;
-
- qreal xmin(points[0].x());
- qreal xmax(points[0].x());
- qreal ymin(points[0].y());
- qreal ymax(points[0].y());
-
- // collect vertex data
- Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
- Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
- Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
- int j = 0;
- int i = 0;
- while (i < vertices.nPoints) {
- Q27Dot5 y_curr = y_next;
-
- *vv = v;
-
- v->x = x_next;
- v->y = y_next;
- v->flags = 0;
-
- next_point:
-
- xmin = qMin(xmin, points[i+1].x());
- xmax = qMax(xmax, points[i+1].x());
- ymin = qMin(ymin, points[i+1].y());
- ymax = qMax(ymax, points[i+1].y());
-
- y_next = FloatToQ27Dot5(points[i+1].y());
- x_next = FloatToQ27Dot5(points[i+1].x());
-
- // skip vertices on top of each other
- if (v->x == x_next && v->y == y_next) {
- ++i;
- if (i < vertices.nPoints)
- goto next_point;
- Vertex *v0 = vertices.storage;
- v0->flags &= ~(LineBeforeStarts|LineBeforeEnds|LineBeforeHorizontal);
- if (y_prev < y_curr)
- v0->flags |= LineBeforeEnds;
- else if (y_prev > y_curr)
- v0->flags |= LineBeforeStarts;
- else
- v0->flags |= LineBeforeHorizontal;
- if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
- && !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
- *maxActiveEdges += 2;
- break;
- }
-
- if (y_prev < y_curr)
- v->flags |= LineBeforeEnds;
- else if (y_prev > y_curr)
- v->flags |= LineBeforeStarts;
- else
- v->flags |= LineBeforeHorizontal;
-
-
- if (y_curr < y_next)
- v->flags |= LineAfterStarts;
- else if (y_curr > y_next)
- v->flags |= LineAfterEnds;
- else
- v->flags |= LineAfterHorizontal;
- // ### could probably get better limit by looping over sorted list and counting down on ending edges
- if ((v->flags & (LineBeforeStarts|LineAfterStarts))
- && !(v->flags & (LineAfterEnds|LineBeforeEnds)))
- *maxActiveEdges += 2;
- y_prev = y_curr;
- ++v;
- ++vv;
- ++j;
- ++i;
- }
- vertices.nPoints = j;
-
- QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
- vv = vertices.sorted;
- std::sort(vv, vv + vertices.nPoints, compareVertex);
-
- return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
-}
-
-struct QCoincidingEdge {
- QTessellatorPrivate::Vertex *start;
- QTessellatorPrivate::Vertex *end;
- bool used;
- bool before;
-
- inline bool operator<(const QCoincidingEdge &e2) const
- {
- return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
- }
-};
-
-static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
-{
- if (e1.before) {
- e1.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
- e1.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
- } else {
- e1.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
- e1.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
- }
- if (e2.before) {
- e2.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
- e2.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
- } else {
- e2.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
- e2.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
- }
- e1.used = e2.used = true;
-}
-
-void QTessellatorPrivate::cancelCoincidingEdges()
-{
- Vertex **vv = vertices.sorted;
-
- QCoincidingEdge *tl = nullptr;
- int tlSize = 0;
-
- for (int i = 0; i < vertices.nPoints - 1; ++i) {
- Vertex *v = vv[i];
- int testListSize = 0;
- while (i < vertices.nPoints - 1) {
- Vertex *n = vv[i];
- if (v->x != n->x || v->y != n->y)
- break;
-
- if (testListSize > tlSize - 2) {
- tlSize = qMax(tlSize*2, 16);
- tl = q_check_ptr((QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge)));
- }
- if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
- tl[testListSize].start = n;
- tl[testListSize].end = vertices.prev(n);
- tl[testListSize].used = false;
- tl[testListSize].before = true;
- ++testListSize;
- }
- if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
- tl[testListSize].start = n;
- tl[testListSize].end = vertices.next(n);
- tl[testListSize].used = false;
- tl[testListSize].before = false;
- ++testListSize;
- }
- ++i;
- }
- if (!testListSize)
- continue;
-
- std::sort(tl, tl + testListSize);
-
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_CLANG("-Wfor-loop-analysis")
- for (int j = 0; j < testListSize; ++j) {
- if (tl[j].used)
- continue;
-
- for (int k = j + 1; k < testListSize; ++k) {
- if (tl[j].end->x != tl[k].end->x
- || tl[j].end->y != tl[k].end->y
- || tl[k].used)
- break;
-
- if (!winding || tl[j].before != tl[k].before) {
- cancelEdges(tl[j], tl[k]);
- break;
- }
- ++k;
- }
- ++j;
- }
-QT_WARNING_POP
- }
- free(tl);
-}
-
-
-void QTessellatorPrivate::emitEdges(QTessellator *tessellator)
-{
- //QDEBUG() << "TRAPS:";
- if (!scanline.old_size)
- return;
-
- // emit edges
- if (winding) {
- // winding fill rule
- int w = 0;
-
- scanline.old[0]->y_left = y;
-
- for (int i = 0; i < scanline.old_size - 1; ++i) {
- Edge *left = scanline.old[i];
- Edge *right = scanline.old[i+1];
- w += left->winding;
-// qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
- if (w == 0) {
- left->y_right = y;
- right->y_left = y;
- } else if (!emit_clever || left->mark || right->mark) {
- Q27Dot5 top = qMax(left->y_right, right->y_left);
- if (top != y) {
- QTessellator::Trapezoid trap;
- fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
- tessellator->addTrap(trap);
-// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
- }
- right->y_left = y;
- left->y_right = y;
- }
- left->mark = false;
- }
- if (scanline.old[scanline.old_size - 1]->mark) {
- scanline.old[scanline.old_size - 1]->y_right = y;
- scanline.old[scanline.old_size - 1]->mark = false;
- }
- } else {
- // odd-even fill rule
- for (int i = 0; i < scanline.old_size; i += 2) {
- Edge *left = scanline.old[i];
- Edge *right = scanline.old[i+1];
- if (!emit_clever || left->mark || right->mark) {
- Q27Dot5 top = qMax(left->y_right, right->y_left);
- if (top != y) {
- QTessellator::Trapezoid trap;
- fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
- tessellator->addTrap(trap);
- }
-// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
- left->y_left = y;
- left->y_right = y;
- right->y_left = y;
- right->y_right = y;
- left->mark = right->mark = false;
- }
- }
- }
-}
-
-
-void QTessellatorPrivate::processIntersections()
-{
- QDEBUG() << "PROCESS INTERSECTIONS";
- // process intersections
- while (!intersections.isEmpty()) {
- Intersections::iterator it = intersections.begin();
- if (it.key().y != y)
- break;
-
- // swap edges
- QDEBUG() << " swapping intersecting edges ";
- int min = scanline.size;
- int max = 0;
- Q27Dot5 xmin = INT_MAX;
- Q27Dot5 xmax = INT_MIN;
- int num = 0;
- while (1) {
- const Intersection i = it.key();
- int next = it->next;
-
- int edgePos = scanline.findEdge(i.edge);
- if (edgePos >= 0) {
- ++num;
- min = qMin(edgePos, min);
- max = qMax(edgePos, max);
- Edge *edge = scanline.edges[edgePos];
- xmin = qMin(xmin, edge->positionAt(y));
- xmax = qMax(xmax, edge->positionAt(y));
- }
- Intersection key;
- key.y = y;
- key.edge = next;
- it = intersections.find(key);
- intersections.remove(i);
- if (it == intersections.end())
- break;
- }
- if (num < 2)
- continue;
-
- Q_ASSERT(min != max);
- QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
- while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
- QDEBUG() << " adding edge on left";
- --min;
- }
- while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <= xmax) {
- QDEBUG() << " adding edge on right";
- ++max;
- }
-
- std::sort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
-#ifdef DEBUG
- for (int i = min; i <= max; ++i)
- QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i;
-#endif
- for (int i = min; i <= max; ++i) {
- Edge *edge = scanline.edges[i];
- edge->intersect_left = true;
- edge->intersect_right = true;
- edge->mark = true;
- }
- }
-}
-
-void QTessellatorPrivate::removeEdges()
-{
- int cv = currentVertex;
- while (cv < vertices.nPoints) {
- const Vertex *v = vertices.sorted[cv];
- if (v->y > y)
- break;
- if (v->flags & LineBeforeEnds) {
- QDEBUG() << " removing edge" << vertices.prevPos(v);
- int pos = scanline.findEdge(vertices.prevPos(v));
- if (pos == -1)
- continue;
- scanline.edges[pos]->mark = true;
- if (pos > 0)
- scanline.edges[pos - 1]->intersect_right = true;
- if (pos < scanline.size - 1)
- scanline.edges[pos + 1]->intersect_left = true;
- scanline.removeAt(pos);
- }
- if (v->flags & LineAfterEnds) {
- QDEBUG() << " removing edge" << vertices.position(v);
- int pos = scanline.findEdge(vertices.position(v));
- if (pos == -1)
- continue;
- scanline.edges[pos]->mark = true;
- if (pos > 0)
- scanline.edges[pos - 1]->intersect_right = true;
- if (pos < scanline.size - 1)
- scanline.edges[pos + 1]->intersect_left = true;
- scanline.removeAt(pos);
- }
- ++cv;
- }
-}
-
-void QTessellatorPrivate::addEdges()
-{
- while (currentVertex < vertices.nPoints) {
- const Vertex *v = vertices.sorted[currentVertex];
- if (v->y > y)
- break;
- if (v->flags & LineBeforeStarts) {
- // add new edge
- int start = vertices.prevPos(v);
- Edge e(vertices, start);
- int pos = scanline.findEdgePosition(e);
- QDEBUG() << " adding edge" << start << "at position" << pos;
- scanline.insert(pos, e);
- if (!mark_clever || !(v->flags & LineAfterEnds)) {
- if (pos > 0)
- scanline.edges[pos - 1]->mark = true;
- if (pos < scanline.size - 1)
- scanline.edges[pos + 1]->mark = true;
- }
- }
- if (v->flags & LineAfterStarts) {
- Edge e(vertices, vertices.position(v));
- int pos = scanline.findEdgePosition(e);
- QDEBUG() << " adding edge" << vertices.position(v) << "at position" << pos;
- scanline.insert(pos, e);
- if (!mark_clever || !(v->flags & LineBeforeEnds)) {
- if (pos > 0)
- scanline.edges[pos - 1]->mark = true;
- if (pos < scanline.size - 1)
- scanline.edges[pos + 1]->mark = true;
- }
- }
- if (v->flags & LineAfterHorizontal) {
- int pos1 = scanline.findEdgePosition(v->x, v->y);
- const Vertex *next = vertices.next(v);
- Q_ASSERT(v->y == next->y);
- int pos2 = scanline.findEdgePosition(next->x, next->y);
- if (pos2 < pos1)
- qSwap(pos1, pos2);
- if (pos1 > 0)
- --pos1;
- if (pos2 == scanline.size)
- --pos2;
- //QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
- scanline.markEdges(pos1, pos2);
- }
- ++currentVertex;
- }
-}
-
-#ifdef DEBUG
-static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
- QTessellatorPrivate::Intersection i)
-{
-// qDebug() << " Link chain: ";
- int end = i.edge;
- while (1) {
- QTessellatorPrivate::IntersectionLink l = intersections.value(i);
-// qDebug() << " " << i.edge << "next=" << l.next << "prev=" << l.prev;
- if (l.next == end)
- break;
- Q_ASSERT(l.next != -1);
- Q_ASSERT(l.prev != -1);
-
- QTessellatorPrivate::Intersection i2 = i;
- i2.edge = l.next;
- QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
-
- Q_ASSERT(l2.next != -1);
- Q_ASSERT(l2.prev != -1);
- Q_ASSERT(l.next == i2.edge);
- Q_ASSERT(l2.prev == i.edge);
- i = i2;
- }
-}
-#endif
-
-bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
-{
- int end = i.edge;
- while (1) {
- if (i.edge == edge)
- return true;
- IntersectionLink l = intersections.value(i);
- if (l.next == end)
- break;
- Q_ASSERT(l.next != -1);
- Q_ASSERT(l.prev != -1);
-
- Intersection i2 = i;
- i2.edge = l.next;
-
-#ifndef QT_NO_DEBUG
- IntersectionLink l2 = intersections.value(i2);
- Q_ASSERT(l2.next != -1);
- Q_ASSERT(l2.prev != -1);
- Q_ASSERT(l.next == i2.edge);
- Q_ASSERT(l2.prev == i.edge);
-#endif
- i = i2;
- }
- return false;
-}
-
-
-void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
-{
- const IntersectionLink emptyLink = {-1, -1};
-
- int next = vertices.nextPos(vertices[e1->edge]);
- if (e2->edge == next)
- return;
- int prev = vertices.prevPos(vertices[e1->edge]);
- if (e2->edge == prev)
- return;
-
- Q27Dot5 yi;
- bool det_positive;
- bool isect = e1->intersect(*e2, &yi, &det_positive);
- QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
- if (!isect) {
- QDEBUG() << " no intersection";
- return;
- }
-
- // don't emit an intersection if it's at the start of a line segment or above us
- if (yi <= y) {
- if (!det_positive)
- return;
- QDEBUG() << " ----->>>>>> WRONG ORDER!";
- yi = y;
- }
- QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point ("
- << Q27Dot5ToDouble(yi) << ')';
-
- Intersection i1;
- i1.y = yi;
- i1.edge = e1->edge;
- IntersectionLink link1 = intersections.value(i1, emptyLink);
- Intersection i2;
- i2.y = yi;
- i2.edge = e2->edge;
- IntersectionLink link2 = intersections.value(i2, emptyLink);
-
- // new pair of edges
- if (link1.next == -1 && link2.next == -1) {
- link1.next = link1.prev = i2.edge;
- link2.next = link2.prev = i1.edge;
- } else if (link1.next == i2.edge || link1.prev == i2.edge
- || link2.next == i1.edge || link2.prev == i1.edge) {
-#ifdef DEBUG
- checkLinkChain(intersections, i1);
- checkLinkChain(intersections, i2);
- Q_ASSERT(edgeInChain(i1, i2.edge));
-#endif
- return;
- } else if (link1.next == -1 || link2.next == -1) {
- if (link2.next == -1) {
- qSwap(i1, i2);
- qSwap(link1, link2);
- }
- Q_ASSERT(link1.next == -1);
-#ifdef DEBUG
- checkLinkChain(intersections, i2);
-#endif
- // only i2 in list
- link1.next = i2.edge;
- link1.prev = link2.prev;
- link2.prev = i1.edge;
- Intersection other;
- other.y = yi;
- other.edge = link1.prev;
- IntersectionLink link = intersections.value(other, emptyLink);
- Q_ASSERT(link.next == i2.edge);
- Q_ASSERT(link.prev != -1);
- link.next = i1.edge;
- intersections.insert(other, link);
- } else {
- bool connected = edgeInChain(i1, i2.edge);
- if (connected)
- return;
-#ifdef DEBUG
- checkLinkChain(intersections, i1);
- checkLinkChain(intersections, i2);
-#endif
- // both already in some list. Have to make sure they are connected
- // this can be done by cutting open the ring(s) after the two eges and
- // connecting them again
- Intersection other1;
- other1.y = yi;
- other1.edge = link1.next;
- IntersectionLink linko1 = intersections.value(other1, emptyLink);
- Intersection other2;
- other2.y = yi;
- other2.edge = link2.next;
- IntersectionLink linko2 = intersections.value(other2, emptyLink);
-
- linko1.prev = i2.edge;
- link2.next = other1.edge;
-
- linko2.prev = i1.edge;
- link1.next = other2.edge;
- intersections.insert(other1, linko1);
- intersections.insert(other2, linko2);
- }
- intersections.insert(i1, link1);
- intersections.insert(i2, link2);
-#ifdef DEBUG
- checkLinkChain(intersections, i1);
- checkLinkChain(intersections, i2);
- Q_ASSERT(edgeInChain(i1, i2.edge));
-#endif
- return;
-
-}
-
-
-void QTessellatorPrivate::addIntersections()
-{
- if (scanline.size) {
- QDEBUG() << "INTERSECTIONS";
- // check marked edges for intersections
-#ifdef DEBUG
- for (int i = 0; i < scanline.size; ++i) {
- Edge *e = scanline.edges[i];
- QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
- << ')';
- }
-#endif
-
- for (int i = 0; i < scanline.size - 1; ++i) {
- Edge *e1 = scanline.edges[i];
- Edge *e2 = scanline.edges[i + 1];
- // check for intersection
- if (e1->intersect_right || e2->intersect_left)
- addIntersection(e1, e2);
- }
- }
-#if 0
- if (intersections.constBegin().key().y == y) {
- QDEBUG() << "----------------> intersection on same line";
- scanline.clearMarks();
- scanline.processIntersections(y, &intersections);
- goto redo;
- }
-#endif
-}
-
-
-QTessellator::QTessellator()
-{
- d = new QTessellatorPrivate;
-}
-
-QTessellator::~QTessellator()
-{
- delete d;
-}
-
-void QTessellator::setWinding(bool w)
-{
- d->winding = w;
-}
-
-
-QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
-{
- Q_ASSERT(points[0] == points[nPoints-1]);
- --nPoints;
-
-#ifdef DEBUG
- QDEBUG()<< "POINTS:";
- for (int i = 0; i < nPoints; ++i) {
- QDEBUG() << points[i];
- }
-#endif
-
- // collect edges and calculate bounds
- d->vertices.nPoints = nPoints;
- d->vertices.init(nPoints);
-
- int maxActiveEdges = 0;
- QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
- d->cancelCoincidingEdges();
-
-#ifdef DEBUG
- QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
- QDEBUG()<< "VERTICES:";
- for (int i = 0; i < d->vertices.nPoints; ++i) {
- QDEBUG() << " " << i << ": "
- << "point=" << d->vertices.position(d->vertices.sorted[i])
- << "flags=" << d->vertices.sorted[i]->flags
- << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/'
- << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')';
- }
-#endif
-
- d->scanline.init(maxActiveEdges);
- d->y = INT_MIN/256;
- d->currentVertex = 0;
-
- while (d->currentVertex < d->vertices.nPoints) {
- d->scanline.clearMarks();
-
- d->y = d->vertices.sorted[d->currentVertex]->y;
- if (!d->intersections.isEmpty())
- d->y = qMin(d->y, d->intersections.constBegin().key().y);
-
- QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
-
- d->scanline.prepareLine();
- d->processIntersections();
- d->removeEdges();
- d->addEdges();
- d->addIntersections();
- d->emitEdges(this);
- d->scanline.lineDone();
-
-#ifdef DEBUG
- QDEBUG()<< "===== edges:";
- for (int i = 0; i < d->scanline.size; ++i) {
- QDEBUG() << " " << d->scanline.edges[i]->edge
- << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
- << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
- << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
- << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ')'
- << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
- << "isLeftOfNext="
- << ((i < d->scanline.size - 1)
- ? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
- : true);
- }
-#endif
-}
-
- d->scanline.done();
- d->intersections.clear();
- return br;
-}
-
-// tessellates the given convex polygon
-void QTessellator::tessellateConvex(const QPointF *points, int nPoints)
-{
- Q_ASSERT(points[0] == points[nPoints-1]);
- --nPoints;
-
- d->vertices.nPoints = nPoints;
- d->vertices.init(nPoints);
-
- for (int i = 0; i < nPoints; ++i) {
- d->vertices[i]->x = FloatToQ27Dot5(points[i].x());
- d->vertices[i]->y = FloatToQ27Dot5(points[i].y());
- }
-
- int left = 0, right = 0;
-
- int top = 0;
- for (int i = 1; i < nPoints; ++i) {
- if (d->vertices[i]->y < d->vertices[top]->y)
- top = i;
- }
-
- left = (top + nPoints - 1) % nPoints;
- right = (top + 1) % nPoints;
-
- while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right)
- left = (left + nPoints - 1) % nPoints;
-
- while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right)
- right = (right + 1) % nPoints;
-
- if (left == right)
- return;
-
- int dir = 1;
-
- Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x,
- d->vertices[top]->y - d->vertices[left]->y };
-
- Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x,
- d->vertices[right]->y - d->vertices[top]->y };
-
- Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x;
-
- // flip direction if polygon is clockwise
- if (cross < 0 || (cross == 0 && dLeft.x > 0)) {
- qSwap(left, right);
- dir = -1;
- }
-
- Vertex *lastLeft = d->vertices[top];
- Vertex *lastRight = d->vertices[top];
-
- QTessellator::Trapezoid trap;
-
- while (lastLeft->y == d->vertices[left]->y && left != right) {
- lastLeft = d->vertices[left];
- left = (left + nPoints - dir) % nPoints;
- }
-
- while (lastRight->y == d->vertices[right]->y && left != right) {
- lastRight = d->vertices[right];
- right = (right + nPoints + dir) % nPoints;
- }
-
- while (true) {
- trap.top = qMax(lastRight->y, lastLeft->y);
- trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y);
- trap.topLeft = lastLeft;
- trap.topRight = lastRight;
- trap.bottomLeft = d->vertices[left];
- trap.bottomRight = d->vertices[right];
-
- if (trap.bottom > trap.top)
- addTrap(trap);
-
- if (left == right)
- break;
-
- if (d->vertices[right]->y < d->vertices[left]->y) {
- do {
- lastRight = d->vertices[right];
- right = (right + nPoints + dir) % nPoints;
- }
- while (lastRight->y == d->vertices[right]->y && left != right);
- } else {
- do {
- lastLeft = d->vertices[left];
- left = (left + nPoints - dir) % nPoints;
- }
- while (lastLeft->y == d->vertices[left]->y && left != right);
- }
- }
-}
-
-// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap
-void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width)
-{
- Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) };
- Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) };
-
- QPointF pa = a_, pb = b_;
-
- if (a.y > b.y) {
- qSwap(a, b);
- qSwap(pa, pb);
- }
-
- Vertex delta = { b.x - a.x, b.y - a.y };
-
- if (delta.x == 0 && delta.y == 0)
- return;
-
- qreal hw = 0.5 * width;
-
- if (delta.x == 0) {
- Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
-
- if (halfWidth == 0)
- return;
-
- Vertex topLeft = { a.x - halfWidth, a.y };
- Vertex topRight = { a.x + halfWidth, a.y };
- Vertex bottomLeft = { a.x - halfWidth, b.y };
- Vertex bottomRight = { a.x + halfWidth, b.y };
-
- QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
- addTrap(trap);
- } else if (delta.y == 0) {
- Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
-
- if (halfWidth == 0)
- return;
-
- if (a.x > b.x)
- qSwap(a.x, b.x);
-
- Vertex topLeft = { a.x, a.y - halfWidth };
- Vertex topRight = { b.x, a.y - halfWidth };
- Vertex bottomLeft = { a.x, a.y + halfWidth };
- Vertex bottomRight = { b.x, a.y + halfWidth };
-
- QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
- addTrap(trap);
- } else {
- QPointF perp(pb.y() - pa.y(), pa.x() - pb.x());
- qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y());
-
- if (qFuzzyIsNull(length))
- return;
-
- // need the half of the width
- perp *= hw / length;
-
- QPointF pta = pa + perp;
- QPointF ptb = pa - perp;
- QPointF ptc = pb - perp;
- QPointF ptd = pb + perp;
-
- Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) };
- Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) };
- Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) };
- Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) };
-
- if (ta.y < tb.y) {
- if (tb.y < td.y) {
- QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td };
- QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc };
- addTrap(top);
- addTrap(bottom);
-
- QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td };
- addTrap(middle);
- } else {
- QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td };
- QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc };
- addTrap(top);
- addTrap(bottom);
-
- if (tb.y != td.y) {
- QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc };
- addTrap(middle);
- }
- }
- } else {
- if (ta.y < tc.y) {
- QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta };
- QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td };
- addTrap(top);
- addTrap(bottom);
-
- QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td };
- addTrap(middle);
- } else {
- QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta };
- QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td };
- addTrap(top);
- addTrap(bottom);
-
- if (ta.y != tc.y) {
- QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta };
- addTrap(middle);
- }
- }
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h b/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h
deleted file mode 100644
index ba1d3a971a3..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2018 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
-
-#pragma once
-
-#include <QPoint>
-#include <QRect>
-
-QT_BEGIN_NAMESPACE
-
-class QTessellatorPrivate;
-
-typedef int Q27Dot5;
-#define Q27Dot5ToDouble(i) ((i)/32.)
-#define FloatToQ27Dot5(i) (int)((i) * 32)
-#define IntToQ27Dot5(i) ((i) << 5)
-#define Q27Dot5ToXFixed(i) ((i) << 11)
-#define Q27Dot5Factor 32
-
-class QTessellator {
-public:
- QTessellator();
- virtual ~QTessellator();
-
- QRectF tessellate(const QPointF *points, int nPoints);
- void tessellateConvex(const QPointF *points, int nPoints);
- void tessellateRect(const QPointF &a, const QPointF &b, qreal width);
-
- void setWinding(bool w);
-
- struct Vertex {
- Q27Dot5 x;
- Q27Dot5 y;
- };
- struct Trapezoid {
- Q27Dot5 top;
- Q27Dot5 bottom;
- const Vertex *topLeft;
- const Vertex *bottomLeft;
- const Vertex *topRight;
- const Vertex *bottomRight;
- };
- virtual void addTrap(const Trapezoid &trap) = 0;
-
-private:
- friend class QTessellatorPrivate;
- QTessellatorPrivate *d;
-};
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp
deleted file mode 100644
index 23155ef2e62..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright (C) 2018 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 <QtCore/qrandom.h>
-
-#include "qxcbconnection.h"
-#include "qcolormap_x11_p.h"
-#include "qxcbnativepainting.h"
-#include "qt_x11_p.h"
-
-QT_BEGIN_NAMESPACE
-
-QXcbX11Data *qt_x11Data = nullptr;
-
-void qt_xcb_native_x11_info_init(QXcbConnection *conn)
-{
- qt_x11Data = new QXcbX11Data;
- X11->display = static_cast<Display *>(conn->xlib_display());
- X11->defaultScreen = DefaultScreen(X11->display);
- X11->screenCount = ScreenCount(X11->display);
-
- X11->screens = new QX11InfoData[X11->screenCount];
- X11->argbVisuals = new Visual *[X11->screenCount];
- X11->argbColormaps = new Colormap[X11->screenCount];
-
- for (int s = 0; s < X11->screenCount; s++) {
- QX11InfoData *screen = X11->screens + s;
- //screen->ref = 1; // ensures it doesn't get deleted
- screen->screen = s;
-
- int widthMM = DisplayWidthMM(X11->display, s);
- if (widthMM != 0) {
- screen->dpiX = (DisplayWidth(X11->display, s) * 254 + widthMM * 5) / (widthMM * 10);
- } else {
- screen->dpiX = 72;
- }
-
- int heightMM = DisplayHeightMM(X11->display, s);
- if (heightMM != 0) {
- screen->dpiY = (DisplayHeight(X11->display, s) * 254 + heightMM * 5) / (heightMM * 10);
- } else {
- screen->dpiY = 72;
- }
-
- X11->argbVisuals[s] = 0;
- X11->argbColormaps[s] = 0;
- }
-
- X11->use_xrender = conn->hasXRender() && !qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING_NO_XRENDER");
-
-#if QT_CONFIG(xrender)
- memset(X11->solid_fills, 0, sizeof(X11->solid_fills));
- for (int i = 0; i < X11->solid_fill_count; ++i)
- X11->solid_fills[i].screen = -1;
- memset(X11->pattern_fills, 0, sizeof(X11->pattern_fills));
- for (int i = 0; i < X11->pattern_fill_count; ++i)
- X11->pattern_fills[i].screen = -1;
-#endif
-
- QXcbColormap::initialize();
-
-#if QT_CONFIG(xrender)
- if (X11->use_xrender) {
- // XRender is supported, let's see if we have a PictFormat for the
- // default visual
- XRenderPictFormat *format =
- XRenderFindVisualFormat(X11->display,
- (Visual *) QXcbX11Info::appVisual(X11->defaultScreen));
-
- if (!format) {
- X11->use_xrender = false;
- }
- }
-#endif // QT_CONFIG(xrender)
-}
-
-QList<XRectangle> qt_region_to_xrectangles(const QRegion &r)
-{
- const int numRects = r.rectCount();
- const auto input = r.begin();
- QList<XRectangle> output(numRects);
- for (int i = 0; i < numRects; ++i) {
- const QRect &in = input[i];
- XRectangle &out = output[i];
- out.x = qMax(SHRT_MIN, in.x());
- out.y = qMax(SHRT_MIN, in.y());
- out.width = qMin((int)USHRT_MAX, in.width());
- out.height = qMin((int)USHRT_MAX, in.height());
- }
- return output;
-}
-
-class QXcbX11InfoData : public QSharedData, public QX11InfoData
-{};
-
-QXcbX11Info::QXcbX11Info()
- : d(nullptr)
-{}
-
-QXcbX11Info::~QXcbX11Info()
-{}
-
-QXcbX11Info::QXcbX11Info(const QXcbX11Info &other)
- : d(other.d)
-{}
-
-QXcbX11Info &QXcbX11Info::operator=(const QXcbX11Info &other)
-{
- d = other.d;
- return *this;
-}
-
-QXcbX11Info QXcbX11Info::fromScreen(int screen)
-{
- QXcbX11InfoData *xd = new QXcbX11InfoData;
- xd->screen = screen;
- xd->depth = QXcbX11Info::appDepth(screen);
- xd->cells = QXcbX11Info::appCells(screen);
- xd->colormap = QXcbX11Info::appColormap(screen);
- xd->defaultColormap = QXcbX11Info::appDefaultColormap(screen);
- xd->visual = (Visual *)QXcbX11Info::appVisual(screen);
- xd->defaultVisual = QXcbX11Info::appDefaultVisual(screen);
-
- QXcbX11Info info;
- info.d = xd;
- return info;
-}
-
-void QXcbX11Info::setDepth(int depth)
-{
- if (!d)
- *this = fromScreen(appScreen());
-
- d->depth = depth;
-}
-
-Display *QXcbX11Info::display()
-{
- return X11 ? X11->display : 0;
-}
-
-int QXcbX11Info::screen() const
-{
- return d ? d->screen : QXcbX11Info::appScreen();
-}
-
-int QXcbX11Info::depth() const
-{
- return d ? d->depth : QXcbX11Info::appDepth();
-}
-
-Colormap QXcbX11Info::colormap() const
-{
- return d ? d->colormap : QXcbX11Info::appColormap();
-}
-
-void *QXcbX11Info::visual() const
-{
- return d ? d->visual : QXcbX11Info::appVisual();
-}
-
-void QXcbX11Info::setVisual(void *visual)
-{
- if (!d)
- *this = fromScreen(appScreen());
-
- d->visual = (Visual *) visual;
-}
-
-int QXcbX11Info::appScreen()
-{
- return X11 ? X11->defaultScreen : 0;
-}
-
-int QXcbX11Info::appDepth(int screen)
-{
- return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].depth : 32;
-}
-
-int QXcbX11Info::appCells(int screen)
-{
- return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].cells : 0;
-}
-
-Colormap QXcbX11Info::appColormap(int screen)
-{
- return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].colormap : 0;
-}
-
-void *QXcbX11Info::appVisual(int screen)
-{
- return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].visual : 0;
-}
-
-Window QXcbX11Info::appRootWindow(int screen)
-{
- return X11 ? RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen) : 0;
-}
-
-bool QXcbX11Info::appDefaultColormap(int screen)
-{
- return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultColormap : true;
-}
-
-bool QXcbX11Info::appDefaultVisual(int screen)
-{
- return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultVisual : true;
-}
-
-int QXcbX11Info::appDpiX(int screen)
-{
- if (!X11)
- return 75;
- if (screen < 0)
- screen = X11->defaultScreen;
- if (screen > X11->screenCount)
- return 0;
- return X11->screens[screen].dpiX;
-}
-
-int QXcbX11Info::appDpiY(int screen)
-{
- if (!X11)
- return 75;
- if (screen < 0)
- screen = X11->defaultScreen;
- if (screen > X11->screenCount)
- return 0;
- return X11->screens[screen].dpiY;
-}
-
-#if QT_CONFIG(xrender)
-Picture QXcbX11Data::getSolidFill(int screen, const QColor &c)
-{
- if (!X11->use_xrender)
- return XNone;
-
- XRenderColor color = preMultiply(c);
- for (int i = 0; i < X11->solid_fill_count; ++i) {
- if (X11->solid_fills[i].screen == screen
- && X11->solid_fills[i].color.alpha == color.alpha
- && X11->solid_fills[i].color.red == color.red
- && X11->solid_fills[i].color.green == color.green
- && X11->solid_fills[i].color.blue == color.blue)
- return X11->solid_fills[i].picture;
- }
- // none found, replace one
- int i = QRandomGenerator::global()->generate() % 16;
-
- if (X11->solid_fills[i].screen != screen && X11->solid_fills[i].picture) {
- XRenderFreePicture (X11->display, X11->solid_fills[i].picture);
- X11->solid_fills[i].picture = 0;
- }
-
- if (!X11->solid_fills[i].picture) {
- Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 1, 1, 32);
- XRenderPictureAttributes attrs;
- attrs.repeat = True;
- X11->solid_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
- XRenderFindStandardFormat(X11->display, PictStandardARGB32),
- CPRepeat, &attrs);
- XFreePixmap (X11->display, pixmap);
- }
-
- X11->solid_fills[i].color = color;
- X11->solid_fills[i].screen = screen;
- XRenderFillRectangle (X11->display, PictOpSrc, X11->solid_fills[i].picture, &color, 0, 0, 1, 1);
- return X11->solid_fills[i].picture;
-}
-
-XRenderColor QXcbX11Data::preMultiply(const QColor &c)
-{
- XRenderColor color;
- const uint A = c.alpha(),
- R = c.red(),
- G = c.green(),
- B = c.blue();
- color.alpha = (A | A << 8);
- color.red = (R | R << 8) * color.alpha / 0x10000;
- color.green = (G | G << 8) * color.alpha / 0x10000;
- color.blue = (B | B << 8) * color.alpha / 0x10000;
- return color;
-}
-#endif // QT_CONFIG(xrender)
-
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h
deleted file mode 100644
index dac9b101c43..00000000000
--- a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2018 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
-
-#pragma once
-
-#include <QSharedDataPointer>
-#include "qt_x11_p.h"
-
-typedef struct _FcPattern FcPattern;
-typedef unsigned long XID;
-typedef XID Colormap;
-typedef XID Window;
-typedef struct _XDisplay Display;
-
-QT_BEGIN_NAMESPACE
-
-class QXcbConnection;
-class QPixmap;
-
-void qt_xcb_native_x11_info_init(QXcbConnection *conn);
-QList<XRectangle> qt_region_to_xrectangles(const QRegion &r);
-
-class QXcbX11InfoData;
-class QXcbX11Info
-{
-public:
- QXcbX11Info();
- ~QXcbX11Info();
- QXcbX11Info(const QXcbX11Info &other);
- QXcbX11Info &operator=(const QXcbX11Info &other);
-
- static QXcbX11Info fromScreen(int screen);
- static Display *display();
-
- int depth() const;
- void setDepth(int depth);
-
- int screen() const;
- Colormap colormap() const;
-
- void *visual() const;
- void setVisual(void *visual);
-
- static int appScreen();
- static int appDepth(int screen = -1);
- static int appCells(int screen = -1);
- static Colormap appColormap(int screen = -1);
- static void *appVisual(int screen = -1);
- static Window appRootWindow(int screen = -1);
- static bool appDefaultColormap(int screen = -1);
- static bool appDefaultVisual(int screen = -1);
- static int appDpiX(int screen = -1);
- static int appDpiY(int screen = -1);
-
-private:
- QSharedDataPointer<QXcbX11InfoData> d;
-
- friend class QX11PaintEngine;
- friend class QX11PlatformPixmap;
- friend void qt_x11SetScreen(QPixmap &pixmap, int screen);
-};
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbimage.cpp b/src/plugins/platforms/xcb/qxcbimage.cpp
index 5b78c2c08ab..4c978152975 100644
--- a/src/plugins/platforms/xcb/qxcbimage.cpp
+++ b/src/plugins/platforms/xcb/qxcbimage.cpp
@@ -80,12 +80,6 @@ bool qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, cons
*imageFormat = QImage::Format_Grayscale8;
return true;
}
-#if QT_CONFIG(xcb_native_painting)
- if (QXcbIntegration::instance() && QXcbIntegration::instance()->nativePaintingEnabled()) {
- *imageFormat = QImage::Format_Indexed8;
- return true;
- }
-#endif
return false;
}
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index c5c1fb1e638..d3a31dd35c7 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -37,11 +37,6 @@
#include <X11/Xlib.h>
#undef register
#endif
-#if QT_CONFIG(xcb_native_painting)
-#include "qxcbnativepainting.h"
-#include "qpixmap_x11_p.h"
-#include "qbackingstore_x11_p.h"
-#endif
#include <qpa/qplatforminputcontextfactory_p.h>
#include <private/qgenericunixtheme_p.h>
@@ -180,13 +175,6 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
m_services->setConnection(m_connection);
m_fontDatabase.reset(new QGenericUnixFontDatabase());
-
-#if QT_CONFIG(xcb_native_painting)
- if (nativePaintingEnabled()) {
- qCDebug(lcQpaXcb, "QXCB USING NATIVE PAINTING");
- qt_xcb_native_x11_info_init(connection());
- }
-#endif
}
QXcbIntegration::~QXcbIntegration()
@@ -198,11 +186,6 @@ QXcbIntegration::~QXcbIntegration()
QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const
{
-#if QT_CONFIG(xcb_native_painting)
- if (nativePaintingEnabled())
- return new QX11PlatformPixmap(type);
-#endif
-
return QPlatformIntegration::createPlatformPixmap(type);
}
@@ -281,10 +264,6 @@ QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *wind
const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window);
if (isTrayIconWindow) {
backingStore = new QXcbSystemTrayBackingStore(window);
-#if QT_CONFIG(xcb_native_painting)
- } else if (nativePaintingEnabled()) {
- backingStore = new QXcbNativeBackingStore(window);
-#endif
} else {
backingStore = new QXcbBackingStore(window);
}
@@ -576,16 +555,6 @@ void QXcbIntegration::beep() const
xcb_flush(connection);
}
-bool QXcbIntegration::nativePaintingEnabled() const
-{
-#if QT_CONFIG(xcb_native_painting)
- static bool enabled = qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING");
- return enabled;
-#else
- return false;
-#endif
-}
-
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index 10cc67af55f..041908e4552 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -94,8 +94,6 @@ public:
void beep() const override;
- bool nativePaintingEnabled() const;
-
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
#endif
diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp
index 0ed8e2890d9..2944c02fd79 100644
--- a/src/plugins/styles/modernwindows/qwindows11style.cpp
+++ b/src/plugins/styles/modernwindows/qwindows11style.cpp
@@ -580,21 +580,18 @@ void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOpt
if (combobox->frame)
drawLineEditFrame(painter, frameRect, combobox, combobox->editable);
- const bool isMouseOver = state & State_MouseOver;
const bool hasFocus = state & State_HasFocus;
- if (isMouseOver && !hasFocus && !highContrastTheme)
- drawRoundedRect(painter, frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor));
+ QStyleOption opt(*option);
+ opt.state.setFlag(QStyle::State_On, false);
+ drawRoundedRect(painter, frameRect, Qt::NoPen, controlFillBrush(&opt, ControlType::Control));
if (sub & SC_ComboBoxArrow) {
QRectF rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget).adjusted(4, 0, -4, 1);
painter->setFont(d->assetFont);
- painter->setPen(combobox->palette.text().color());
+ painter->setPen(controlTextColor(option));
painter->drawText(rect, Qt::AlignCenter, ChevronDownMed);
}
- if (state & State_HasFocus) {
- drawPrimitive(PE_FrameFocusRect, option, painter, widget);
- }
- if (state & State_KeyboardFocusChange && state & State_HasFocus) {
+ if (state & State_KeyboardFocusChange && hasFocus) {
QStyleOptionFocusRect fropt;
fropt.QStyleOption::operator=(*option);
proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
@@ -1048,12 +1045,27 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption
if (rect.width() <= 0)
break;
- painter->setPen(Qt::NoPen);
- if (vopt->features & QStyleOptionViewItem::Alternate)
- painter->setBrush(vopt->palette.alternateBase());
- else
- painter->setBrush(vopt->palette.base());
- painter->drawRect(rect);
+ if (vopt->features & QStyleOptionViewItem::Alternate) {
+ QPalette::ColorGroup cg =
+ (widget ? widget->isEnabled() : (vopt->state & QStyle::State_Enabled))
+ ? QPalette::Normal
+ : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ painter->fillRect(rect, option->palette.brush(cg, QPalette::AlternateBase));
+ }
+
+ if (option->state & State_Selected && !highContrastTheme) {
+ // keep in sync with CE_ItemViewItem QListView indicator painting
+ const auto col = option->palette.accent().color();
+ painter->setBrush(col);
+ painter->setPen(col);
+ const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f;
+ const auto yOfs = rect.height() / 4.;
+ QRectF r(QPointF(xPos, rect.y() + yOfs),
+ QPointF(xPos + 1, rect.y() + rect.height() - yOfs));
+ painter->drawRoundedRect(r, 1, 1);
+ }
const bool isTreeDecoration = vopt->features.testFlag(
QStyleOptionViewItem::IsDecorationForRootColumn);
@@ -1182,11 +1194,14 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
painter->setRenderHint(QPainter::Antialiasing);
switch (element) {
case QStyle::CE_ComboBoxLabel:
+#if QT_CONFIG(combobox)
if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ painter->setPen(controlTextColor(option));
QStyleOptionComboBox newOption = *cb;
newOption.rect.adjust(4,0,-4,0);
QCommonStyle::drawControl(element, &newOption, painter, widget);
}
+#endif // QT_CONFIG(combobox)
break;
case QStyle::CE_TabBarTabShape:
#if QT_CONFIG(tabbar)
@@ -2062,6 +2077,20 @@ QRect QWindows11Style::subControlRect(ComplexControl control, const QStyleOption
}
break;
}
+ case CC_ComboBox: {
+ if (subControl == SC_ComboBoxArrow) {
+ const auto indicatorWidth =
+ proxy()->pixelMetric(PM_MenuButtonIndicator, option, widget);
+ const int endX = option->rect.right() - contentHMargin - 2;
+ const int startX = endX - indicatorWidth;
+ const QRect rect(QPoint(startX, option->rect.top()),
+ QPoint(endX, option->rect.bottom()));
+ ret = visualRect(option->direction, option->rect, rect);
+ } else {
+ ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
+ }
+ break;
+ }
default:
ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget);
}
@@ -2152,14 +2181,18 @@ QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *o
break;
}
#endif
+#if QT_CONFIG(combobox)
case CT_ComboBox:
if (const auto *comboBoxOpt = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); // don't rely on QWindowsThemeData
contentSize += QSize(4, 4); // default win11 style margins
- if (comboBoxOpt->subControls & SC_ComboBoxArrow)
- contentSize += QSize(8, 0); // arrow margins
+ if (comboBoxOpt->subControls & SC_ComboBoxArrow) {
+ const auto w = proxy()->pixelMetric(PM_MenuButtonIndicator, option, widget);
+ contentSize.rwidth() += w + contentItemHMargin;
+ }
}
break;
+#endif
case CT_HeaderSection:
// windows vista does not honor the indicator (as it was drawn above the text, not on the
// side) so call QWindowsStyle::styleHint directly to get the correct size hint
@@ -2290,6 +2323,9 @@ int QWindows11Style::pixelMetric(PixelMetric metric, const QStyleOption *option,
case PM_ButtonShiftVertical:
res = 0;
break;
+ case PM_TreeViewIndentation:
+ res = 30;
+ break;
default:
res = QWindowsVistaStyle::pixelMetric(metric, option, widget);
}
@@ -2377,6 +2413,13 @@ void QWindows11Style::unpolish(QWidget *widget)
widget->setProperty("_q_original_menubar_maxheight", QVariant());
}
#endif
+ const auto comboBoxContainer = qobject_cast<const QComboBoxPrivateContainer *>(widget);
+ if (comboBoxContainer) {
+ widget->setAttribute(Qt::WA_OpaquePaintEvent, true);
+ widget->setAttribute(Qt::WA_TranslucentBackground, false);
+ widget->setWindowFlag(Qt::FramelessWindowHint, false);
+ widget->setWindowFlag(Qt::NoDropShadowWindowHint, false);
+ }
if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
scrollarea
diff --git a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
index abe0bde540f..22ca18b10bf 100644
--- a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
+++ b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
@@ -1626,6 +1626,12 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt
break;
case PE_Frame:
+ if (widget && widget->inherits("QComboBoxPrivateContainer")){
+ QStyleOption copy = *option;
+ copy.state |= State_Raised;
+ proxy()->drawPrimitive(PE_PanelMenu, &copy, painter, widget);
+ break;
+ }
#if QT_CONFIG(accessibility)
if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText)
|| QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) ||
@@ -1704,6 +1710,14 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt
return;
}
+ case PE_PanelMenu:
+ if (widget && widget->inherits("QComboBoxPrivateContainer")){
+ //fill combobox popup background
+ QWindowsThemeData popupbackgroundTheme(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
+ MENU_POPUPBACKGROUND, stateId, option->rect);
+ d->drawBackground(popupbackgroundTheme);
+ }
+
case PE_PanelMenuBar:
break;
diff --git a/src/tools/windeployqt/main.cpp b/src/tools/windeployqt/main.cpp
index e35330ebeb3..6cb935ef47d 100644
--- a/src/tools/windeployqt/main.cpp
+++ b/src/tools/windeployqt/main.cpp
@@ -592,15 +592,17 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
}
// default to deployment of compiler runtime for windows desktop configurations
- if (options->platform == WindowsDesktopMinGW || options->platform.testFlags(WindowsDesktopMsvc)
- || parser->isSet(compilerRunTimeOption))
+ if (options->platform == WindowsDesktopMinGW || options->platform == WindowsDesktopClangMinGW
+ || options->platform.testFlags(WindowsDesktopMsvc) || parser->isSet(compilerRunTimeOption))
options->compilerRunTime = true;
if (parser->isSet(noCompilerRunTimeOption))
options->compilerRunTime = false;
if (options->compilerRunTime && options->platform != WindowsDesktopMinGW
+ && options->platform != WindowsDesktopClangMinGW
&& !options->platform.testFlags(WindowsDesktopMsvc)) {
- *errorMessage = QStringLiteral("Deployment of the compiler runtime is implemented for Desktop MSVC/g++ only.");
+ *errorMessage = QStringLiteral("Deployment of the compiler runtime is implemented for "
+ "Desktop MSVC and MinGW (g++ and Clang) only.");
return CommandLineParseError;
}
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index ce30f50616b..c0be3debe49 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -681,6 +681,7 @@ qt_internal_extend_target(Widgets CONDITION MACOS AND (QT_FEATURE_menu OR QT_FEA
qt_internal_extend_target(Widgets CONDITION QT_FEATURE_colordialog
SOURCES
dialogs/qcolordialog.cpp dialogs/qcolordialog.h
+ dialogs/qcolorwell_p.h
)
qt_internal_extend_target(Widgets CONDITION QT_FEATURE_dialog
diff --git a/src/widgets/accessible/itemviews.cpp b/src/widgets/accessible/itemviews.cpp
index fc969e17380..265c523eae0 100644
--- a/src/widgets/accessible/itemviews.cpp
+++ b/src/widgets/accessible/itemviews.cpp
@@ -60,7 +60,7 @@ int QAccessibleTable::logicalIndex(const QModelIndex &index) const
}
QAccessibleTable::QAccessibleTable(QWidget *w)
- : QAccessibleObject(w)
+ : QAccessibleWidgetV2(w)
{
Q_ASSERT(view());
@@ -677,7 +677,7 @@ void *QAccessibleTable::interface_cast(QAccessible::InterfaceType t)
return static_cast<QAccessibleSelectionInterface*>(this);
if (t == QAccessible::TableInterface)
return static_cast<QAccessibleTableInterface*>(this);
- return nullptr;
+ return QAccessibleWidgetV2::interface_cast(t);
}
void QAccessibleTable::modelChange(QAccessibleTableModelChangeEvent *event)
diff --git a/src/widgets/accessible/itemviews_p.h b/src/widgets/accessible/itemviews_p.h
index 9b30f36ced3..79f9a7f2f05 100644
--- a/src/widgets/accessible/itemviews_p.h
+++ b/src/widgets/accessible/itemviews_p.h
@@ -31,7 +31,9 @@ QT_BEGIN_NAMESPACE
class QAccessibleTableCell;
class QAccessibleTableHeaderCell;
-class QAccessibleTable :public QAccessibleTableInterface, public QAccessibleSelectionInterface, public QAccessibleObject
+class QAccessibleTable : public QAccessibleTableInterface,
+ public QAccessibleSelectionInterface,
+ public QAccessibleWidgetV2
{
public:
explicit QAccessibleTable(QWidget *w);
diff --git a/src/widgets/accessible/rangecontrols.cpp b/src/widgets/accessible/rangecontrols.cpp
index c0de5357c9a..1f7b20833dd 100644
--- a/src/widgets/accessible/rangecontrols.cpp
+++ b/src/widgets/accessible/rangecontrols.cpp
@@ -65,6 +65,16 @@ QAccessibleInterface *QAccessibleAbstractSpinBox::lineEditIface() const
#endif
}
+QAccessible::State QAccessibleAbstractSpinBox::state() const
+{
+ QAccessible::State state = QAccessibleWidgetV2::state();
+ if (abstractSpinBox()->isReadOnly())
+ state.readOnly = true;
+ else
+ state.editable = true;
+ return state;
+}
+
QString QAccessibleAbstractSpinBox::text(QAccessible::Text t) const
{
if (t == QAccessible::Value)
diff --git a/src/widgets/accessible/rangecontrols_p.h b/src/widgets/accessible/rangecontrols_p.h
index dd5a6a4531c..5a023d2f00b 100644
--- a/src/widgets/accessible/rangecontrols_p.h
+++ b/src/widgets/accessible/rangecontrols_p.h
@@ -42,6 +42,7 @@ public:
explicit QAccessibleAbstractSpinBox(QWidget *w);
virtual ~QAccessibleAbstractSpinBox();
+ QAccessible::State state() const override;
QString text(QAccessible::Text t) const override;
void *interface_cast(QAccessible::InterfaceType t) override;
diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp
index eac5de33d32..f8125204045 100644
--- a/src/widgets/dialogs/qcolordialog.cpp
+++ b/src/widgets/dialogs/qcolordialog.cpp
@@ -39,6 +39,7 @@
#include "qwindow.h"
#include "private/qdialog_p.h"
+#include "private/qcolorwell_p.h"
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformservices.h>
@@ -56,16 +57,12 @@ namespace QtPrivate {
class QColorLuminancePicker;
class QColorPicker;
class QColorShower;
-class QWellArray;
-class QColorWell;
class QColorPickingEventFilter;
} // namespace QtPrivate
using QColorLuminancePicker = QtPrivate::QColorLuminancePicker;
using QColorPicker = QtPrivate::QColorPicker;
using QColorShower = QtPrivate::QColorShower;
-using QWellArray = QtPrivate::QWellArray;
-using QColorWell = QtPrivate::QColorWell;
using QColorPickingEventFilter = QtPrivate::QColorPickingEventFilter;
class QColorDialogPrivate : public QDialogPrivate
@@ -162,95 +159,6 @@ private:
//////////// QWellArray BEGIN
-namespace QtPrivate {
-
-class QWellArray : public QWidget
-{
- Q_OBJECT
- Q_PROPERTY(int selectedColumn READ selectedColumn)
- Q_PROPERTY(int selectedRow READ selectedRow)
-
-public:
- QWellArray(int rows, int cols, QWidget* parent=nullptr);
- ~QWellArray() {}
-
- int selectedColumn() const { return selCol; }
- int selectedRow() const { return selRow; }
-
- virtual void setCurrent(int row, int col);
- virtual void setSelected(int row, int col);
-
- QSize sizeHint() const override;
-
- inline int cellWidth() const
- { return cellw; }
-
- inline int cellHeight() const
- { return cellh; }
-
- inline int rowAt(int y) const
- { return y / cellh; }
-
- inline int columnAt(int x) const
- { if (isRightToLeft()) return ncols - (x / cellw) - 1; return x / cellw; }
-
- inline int rowY(int row) const
- { return cellh * row; }
-
- inline int columnX(int column) const
- { if (isRightToLeft()) return cellw * (ncols - column - 1); return cellw * column; }
-
- inline int numRows() const
- { return nrows; }
-
- inline int numCols() const
- {return ncols; }
-
- inline QRect cellRect() const
- { return QRect(0, 0, cellw, cellh); }
-
- inline QSize gridSize() const
- { return QSize(ncols * cellw, nrows * cellh); }
-
- QRect cellGeometry(int row, int column)
- {
- QRect r;
- if (row >= 0 && row < nrows && column >= 0 && column < ncols)
- r.setRect(columnX(column), rowY(row), cellw, cellh);
- return r;
- }
-
- inline void updateCell(int row, int column) { update(cellGeometry(row, column)); }
-
-signals:
- void selected(int row, int col);
- void currentChanged(int row, int col);
- void colorChanged(int index, QRgb color);
-
-protected:
- virtual void paintCell(QPainter *, int row, int col, const QRect&);
- virtual void paintCellContents(QPainter *, int row, int col, const QRect&);
-
- void mousePressEvent(QMouseEvent*) override;
- void mouseReleaseEvent(QMouseEvent*) override;
- void keyPressEvent(QKeyEvent*) override;
- void focusInEvent(QFocusEvent*) override;
- void focusOutEvent(QFocusEvent*) override;
- void paintEvent(QPaintEvent *) override;
-
-private:
- Q_DISABLE_COPY(QWellArray)
-
- int nrows;
- int ncols;
- int cellw;
- int cellh;
- int curRow;
- int curCol;
- int selRow;
- int selCol;
-};
-
void QWellArray::paintEvent(QPaintEvent *e)
{
QRect r = e->rect();
@@ -475,11 +383,12 @@ void QWellArray::keyPressEvent(QKeyEvent* e)
e->ignore(); // we don't accept the event
return;
}
-
-} // namespace QtPrivate
+}
//////////// QWellArray END
+namespace QtPrivate {
+
// Event filter to be installed on the dialog while in color-picking mode.
class QColorPickingEventFilter : public QObject {
public:
@@ -510,7 +419,7 @@ private:
QColorDialogPrivate *m_dp;
};
-} // unnamed namespace
+} // namespace QtPrivate
/*!
Returns the number of custom colors supported by QColorDialog. All
@@ -570,35 +479,6 @@ static inline void rgb2hsv(QRgb rgb, int &h, int &s, int &v)
c.getHsv(&h, &s, &v);
}
-namespace QtPrivate {
-
-class QColorWell : public QWellArray
-{
-public:
- QColorWell(QWidget *parent, int r, int c, const QRgb *vals)
- :QWellArray(r, c, parent), values(vals), mousePressed(false), oldCurrent(-1, -1)
- { setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); }
-
-protected:
- void paintCellContents(QPainter *, int row, int col, const QRect&) override;
- void mousePressEvent(QMouseEvent *e) override;
- void mouseMoveEvent(QMouseEvent *e) override;
- void mouseReleaseEvent(QMouseEvent *e) override;
-#if QT_CONFIG(draganddrop)
- void dragEnterEvent(QDragEnterEvent *e) override;
- void dragLeaveEvent(QDragLeaveEvent *e) override;
- void dragMoveEvent(QDragMoveEvent *e) override;
- void dropEvent(QDropEvent *e) override;
-#endif
-
-private:
- const QRgb *values;
- bool mousePressed;
- QPoint pressPos;
- QPoint oldCurrent;
-
-};
-
void QColorWell::paintCellContents(QPainter *p, int row, int col, const QRect &r)
{
int i = row + col*numRows();
@@ -686,6 +566,8 @@ void QColorWell::mouseReleaseEvent(QMouseEvent *e)
mousePressed = false;
}
+namespace QtPrivate {
+
class QColorPicker : public QFrame
{
Q_OBJECT
@@ -703,18 +585,21 @@ signals:
protected:
QSize sizeHint() const override;
void paintEvent(QPaintEvent*) override;
+ void keyPressEvent(QKeyEvent *event) override;
void mouseMoveEvent(QMouseEvent *) override;
void mousePressEvent(QMouseEvent *) override;
void resizeEvent(QResizeEvent *) override;
private:
- int hue;
- int sat;
+ QPoint m_pos;
- QPoint colPt();
- int huePt(const QPoint &pt);
- int satPt(const QPoint &pt);
- void setCol(const QPoint &pt);
+ QPixmap createColorsPixmap();
+ QPoint colPt(int hue, int sat);
+ int huePt(const QPoint &pt, const QSize &widgetSize);
+ int huePt(const QPoint &pt) { return huePt(pt, size()); }
+ int satPt(const QPoint &pt, const QSize &widgetSize);
+ int satPt(const QPoint &pt) { return satPt(pt, size()); }
+ void setCol(const QPoint &pt, bool notify = true);
QPixmap pix;
bool crossVisible;
@@ -743,6 +628,7 @@ signals:
protected:
void paintEvent(QPaintEvent*) override;
+ void keyPressEvent(QKeyEvent *event) override;
void mouseMoveEvent(QMouseEvent *) override;
void mousePressEvent(QMouseEvent *) override;
@@ -778,6 +664,7 @@ QColorLuminancePicker::QColorLuminancePicker(QWidget* parent)
hue = 100; val = 100; sat = 100;
pix = nullptr;
// setAttribute(WA_NoErase, true);
+ setFocusPolicy(Qt::StrongFocus);
}
QColorLuminancePicker::~QColorLuminancePicker()
@@ -785,6 +672,21 @@ QColorLuminancePicker::~QColorLuminancePicker()
delete pix;
}
+void QColorLuminancePicker::keyPressEvent(QKeyEvent *event)
+{
+ switch (event->key()) {
+ case Qt::Key_Down:
+ setVal(std::clamp(val - 1, 0, 255));
+ break;
+ case Qt::Key_Up:
+ setVal(std::clamp(val + 1, 0, 255));
+ break;
+ default:
+ QWidget::keyPressEvent(event);
+ break;
+ }
+}
+
void QColorLuminancePicker::mouseMoveEvent(QMouseEvent *m)
{
if (m->buttons() == Qt::NoButton) {
@@ -855,38 +757,53 @@ void QColorLuminancePicker::setCol(int h, int s , int v)
repaint();
}
-QPoint QColorPicker::colPt()
+QPoint QColorPicker::colPt(int hue, int sat)
{
QRect r = contentsRect();
return QPoint((360 - hue) * (r.width() - 1) / 360, (255 - sat) * (r.height() - 1) / 255);
}
-int QColorPicker::huePt(const QPoint &pt)
+int QColorPicker::huePt(const QPoint &pt, const QSize &widgetSize)
{
- QRect r = contentsRect();
- return 360 - pt.x() * 360 / (r.width() - 1);
+ QRect r = QRect(QPoint(0, 0), widgetSize) - contentsMargins();
+ return std::clamp(360 - pt.x() * 360 / (r.width() - 1), 0, 359);
}
-int QColorPicker::satPt(const QPoint &pt)
+int QColorPicker::satPt(const QPoint &pt, const QSize &widgetSize)
{
- QRect r = contentsRect();
- return 255 - pt.y() * 255 / (r.height() - 1);
+ QRect r = QRect(QPoint(0, 0), widgetSize) - contentsMargins();
+ return std::clamp(255 - pt.y() * 255 / (r.height() - 1), 0, 255);
}
-void QColorPicker::setCol(const QPoint &pt)
+void QColorPicker::setCol(const QPoint &pt, bool notify)
{
- setCol(huePt(pt), satPt(pt));
+ if (pt == m_pos)
+ return;
+
+ QRect r(m_pos, QSize(20, 20));
+ m_pos.setX(std::clamp(pt.x(), 0, pix.width() - 1));
+ m_pos.setY(std::clamp(pt.y(), 0, pix.height() - 1));
+ r = r.united(QRect(m_pos, QSize(20, 20)));
+ r.translate(contentsRect().x() - 9, contentsRect().y() - 9);
+ // update(r);
+ repaint(r);
+
+ if (notify)
+ emit newCol(huePt(m_pos), satPt(m_pos));
}
QColorPicker::QColorPicker(QWidget* parent)
: QFrame(parent)
, crossVisible(true)
{
- hue = 0; sat = 0;
- setCol(150, 255);
-
setAttribute(Qt::WA_NoSystemBackground);
+ setFocusPolicy(Qt::StrongFocus);
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) );
+ adjustSize();
+
+ pix = createColorsPixmap();
+
+ setCol(150, 255);
}
QColorPicker::~QColorPicker()
@@ -910,15 +827,31 @@ void QColorPicker::setCol(int h, int s)
{
int nhue = qMin(qMax(0,h), 359);
int nsat = qMin(qMax(0,s), 255);
- if (nhue == hue && nsat == sat)
+ if (nhue == huePt(m_pos) && nsat == satPt(m_pos))
return;
- QRect r(colPt(), QSize(20,20));
- hue = nhue; sat = nsat;
- r = r.united(QRect(colPt(), QSize(20,20)));
- r.translate(contentsRect().x()-9, contentsRect().y()-9);
- // update(r);
- repaint(r);
+ setCol(colPt(nhue, nsat), false);
+}
+
+void QColorPicker::keyPressEvent(QKeyEvent *event)
+{
+ switch (event->key()) {
+ case Qt::Key_Down:
+ setCol(m_pos + QPoint(0, 1));
+ break;
+ case Qt::Key_Left:
+ setCol(m_pos + QPoint(-1, 0));
+ break;
+ case Qt::Key_Right:
+ setCol(m_pos + QPoint(1, 0));
+ break;
+ case Qt::Key_Up:
+ setCol(m_pos + QPoint(0, -1));
+ break;
+ default:
+ QFrame::keyPressEvent(event);
+ break;
+ }
}
void QColorPicker::mouseMoveEvent(QMouseEvent *m)
@@ -929,14 +862,12 @@ void QColorPicker::mouseMoveEvent(QMouseEvent *m)
return;
}
setCol(p);
- emit newCol(hue, sat);
}
void QColorPicker::mousePressEvent(QMouseEvent *m)
{
QPoint p = m->position().toPoint() - contentsRect().topLeft();
setCol(p);
- emit newCol(hue, sat);
}
void QColorPicker::paintEvent(QPaintEvent* )
@@ -948,7 +879,7 @@ void QColorPicker::paintEvent(QPaintEvent* )
p.drawPixmap(r.topLeft(), pix);
if (crossVisible) {
- QPoint pt = colPt() + r.topLeft();
+ QPoint pt = m_pos + r.topLeft();
p.setPen(Qt::black);
p.fillRect(pt.x()-9, pt.y(), 20, 2, Qt::black);
p.fillRect(pt.x(), pt.y()-9, 2, 20, Qt::black);
@@ -959,6 +890,21 @@ void QColorPicker::resizeEvent(QResizeEvent *ev)
{
QFrame::resizeEvent(ev);
+ pix = createColorsPixmap();
+
+ const QSize &oldSize = ev->oldSize();
+ if (!oldSize.isValid())
+ return;
+
+ // calculate hue/saturation based on previous widget size
+ // and update position accordingly
+ const int hue = huePt(m_pos, oldSize);
+ const int sat = satPt(m_pos, oldSize);
+ setCol(hue, sat);
+}
+
+QPixmap QColorPicker::createColorsPixmap()
+{
int w = width() - frameWidth() * 2;
int h = height() - frameWidth() * 2;
QImage img(w, h, QImage::Format_RGB32);
@@ -976,10 +922,9 @@ void QColorPicker::resizeEvent(QResizeEvent *ev)
++x;
}
}
- pix = QPixmap::fromImage(img);
+ return QPixmap::fromImage(img);
}
-
class QColSpinBox : public QSpinBox
{
public:
diff --git a/src/widgets/dialogs/qcolorwell_p.h b/src/widgets/dialogs/qcolorwell_p.h
new file mode 100644
index 00000000000..31d69fabb13
--- /dev/null
+++ b/src/widgets/dialogs/qcolorwell_p.h
@@ -0,0 +1,142 @@
+// 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
+
+#ifndef QCOLORWELL_P_H
+#define QCOLORWELL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qrect.h>
+#include <QtWidgets/qwidget.h>
+
+QT_REQUIRE_CONFIG(colordialog);
+
+QT_BEGIN_NAMESPACE
+
+class QWellArray : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int selectedColumn READ selectedColumn)
+ Q_PROPERTY(int selectedRow READ selectedRow)
+
+public:
+ QWellArray(int rows, int cols, QWidget *parent = nullptr);
+ ~QWellArray() { }
+
+ int selectedColumn() const { return selCol; }
+ int selectedRow() const { return selRow; }
+
+ virtual void setCurrent(int row, int col);
+ virtual void setSelected(int row, int col);
+
+ QSize sizeHint() const override;
+
+ inline int cellWidth() const { return cellw; }
+
+ inline int cellHeight() const { return cellh; }
+
+ inline int rowAt(int y) const { return y / cellh; }
+
+ inline int columnAt(int x) const
+ {
+ if (isRightToLeft())
+ return ncols - (x / cellw) - 1;
+ return x / cellw;
+ }
+
+ inline int rowY(int row) const { return cellh * row; }
+
+ inline int columnX(int column) const
+ {
+ if (isRightToLeft())
+ return cellw * (ncols - column - 1);
+ return cellw * column;
+ }
+
+ inline int numRows() const { return nrows; }
+
+ inline int numCols() const { return ncols; }
+
+ inline QRect cellRect() const { return QRect(0, 0, cellw, cellh); }
+
+ inline QSize gridSize() const { return QSize(ncols * cellw, nrows * cellh); }
+
+ QRect cellGeometry(int row, int column)
+ {
+ QRect r;
+ if (row >= 0 && row < nrows && column >= 0 && column < ncols)
+ r.setRect(columnX(column), rowY(row), cellw, cellh);
+ return r;
+ }
+
+ inline void updateCell(int row, int column) { update(cellGeometry(row, column)); }
+
+signals:
+ void selected(int row, int col);
+ void currentChanged(int row, int col);
+ void colorChanged(int index, QRgb color);
+
+protected:
+ virtual void paintCell(QPainter *, int row, int col, const QRect &);
+ virtual void paintCellContents(QPainter *, int row, int col, const QRect &);
+
+ void mousePressEvent(QMouseEvent *) override;
+ void mouseReleaseEvent(QMouseEvent *) override;
+ void keyPressEvent(QKeyEvent *) override;
+ void focusInEvent(QFocusEvent *) override;
+ void focusOutEvent(QFocusEvent *) override;
+ void paintEvent(QPaintEvent *) override;
+
+private:
+ Q_DISABLE_COPY(QWellArray)
+
+ int nrows;
+ int ncols;
+ int cellw;
+ int cellh;
+ int curRow;
+ int curCol;
+ int selRow;
+ int selCol;
+};
+
+class QColorWell : public QWellArray
+{
+public:
+ QColorWell(QWidget *parent, int r, int c, const QRgb *vals)
+ : QWellArray(r, c, parent), values(vals), mousePressed(false), oldCurrent(-1, -1)
+ {
+ setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
+ }
+
+protected:
+ void paintCellContents(QPainter *, int row, int col, const QRect &) override;
+ void mousePressEvent(QMouseEvent *e) override;
+ void mouseMoveEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
+#if QT_CONFIG(draganddrop)
+ void dragEnterEvent(QDragEnterEvent *e) override;
+ void dragLeaveEvent(QDragLeaveEvent *e) override;
+ void dragMoveEvent(QDragMoveEvent *e) override;
+ void dropEvent(QDropEvent *e) override;
+#endif
+
+private:
+ const QRgb *values;
+ bool mousePressed;
+ QPoint pressPos;
+ QPoint oldCurrent;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORWELL_P_H
diff --git a/src/widgets/dialogs/qfiledialog.ui b/src/widgets/dialogs/qfiledialog.ui
index f275e20c633..97f39fa6194 100644
--- a/src/widgets/dialogs/qfiledialog.ui
+++ b/src/widgets/dialogs/qfiledialog.ui
@@ -20,7 +20,10 @@
<item row="0" column="0">
<widget class="QLabel" name="lookInLabel">
<property name="text">
- <string>Look in:</string>
+ <string>&amp;Look in:</string>
+ </property>
+ <property name="buddy">
+ <cstring>lookInCombo</cstring>
</property>
</widget>
</item>
@@ -284,7 +287,10 @@
</sizepolicy>
</property>
<property name="text">
- <string>Files of type:</string>
+ <string>Files of &amp;type:</string>
+ </property>
+ <property name="buddy">
+ <cstring>fileTypeCombo</cstring>
</property>
</widget>
</item>
diff --git a/src/widgets/dialogs/qfontdialog.cpp b/src/widgets/dialogs/qfontdialog.cpp
index 870b0c40faf..c4f8af1a639 100644
--- a/src/widgets/dialogs/qfontdialog.cpp
+++ b/src/widgets/dialogs/qfontdialog.cpp
@@ -172,7 +172,7 @@ void QFontDialogPrivate::init()
sizeAccel = new QLabel(q);
#ifndef QT_NO_SHORTCUT
- sizeAccel->setBuddy(sizeEdit);
+ sizeAccel->setBuddy(sizeList);
#endif
sizeAccel->setIndent(2);
diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp
index c4b78539114..bb9f7ab27fc 100644
--- a/src/widgets/styles/qcommonstyle.cpp
+++ b/src/widgets/styles/qcommonstyle.cpp
@@ -5,15 +5,12 @@
#include "qcommonstyle.h"
#include "qcommonstyle_p.h"
-#include <qfile.h>
#if QT_CONFIG(itemviews)
#include <qabstractitemview.h>
#endif
#include <qapplication.h>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h>
-#include <qbitmap.h>
-#include <qcache.h>
#if QT_CONFIG(dockwidget)
#include <qdockwidget.h>
#endif
@@ -61,7 +58,6 @@
#endif
#include <private/qcommonstylepixmaps_p.h>
#include <private/qmath_p.h>
-#include <qdebug.h>
#include <qtextformat.h>
#if QT_CONFIG(wizard)
#include <qwizard.h>
@@ -69,11 +65,6 @@
#if QT_CONFIG(filedialog)
#include <qsidebar_p.h>
#endif
-#include <qfileinfo.h>
-#include <qdir.h>
-#if QT_CONFIG(settings)
-#include <qsettings.h>
-#endif
#include <qvariant.h>
#include <qpixmapcache.h>
#if QT_CONFIG(animation)
@@ -6199,17 +6190,17 @@ QPixmap QCommonStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &p
return QPixmap::fromImage(std::move(im));
}
case QIcon::Selected: {
- QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
QColor color = opt->palette.color(QPalette::Normal, QPalette::Highlight);
color.setAlphaF(0.3f);
- QPainter painter(&img);
+ QPixmap ret(pixmap);
+ QPainter painter(&ret);
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
- painter.fillRect(0, 0, img.width(), img.height(), color);
+ painter.fillRect(0, 0, pixmap.width(), pixmap.height(), color);
painter.end();
- return QPixmap::fromImage(std::move(img)); }
+ return ret;
+ }
case QIcon::Active:
- return pixmap;
- default:
+ case QIcon::Normal:
break;
}
return pixmap;
diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp
index 9b06822c218..b9143a59ee7 100644
--- a/src/widgets/styles/qwindowsstyle.cpp
+++ b/src/widgets/styles/qwindowsstyle.cpp
@@ -851,6 +851,12 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt,
}
#ifndef QT_NO_FRAME
case PE_Frame:
+ if (w && w->inherits("QComboBoxPrivateContainer")){
+ QStyleOption copy = *opt;
+ copy.state |= State_Raised;
+ proxy()->drawPrimitive(PE_PanelMenu, &copy, p, w);
+ break;
+ }
case PE_FrameMenu:
if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
if (frame->lineWidth == 2 || pe == PE_Frame) {
@@ -873,6 +879,7 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt,
}
} else {
QPalette popupPal = opt->palette;
+ p->drawRect(opt->rect);
popupPal.setColor(QPalette::Light, opt->palette.window().color());
popupPal.setColor(QPalette::Midlight, opt->palette.light().color());
qDrawWinPanel(p, opt->rect, popupPal, opt->state & State_Sunken);
@@ -899,6 +906,12 @@ void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt,
p->drawRect(opt->rect);
}
break; }
+ case PE_PanelMenu:
+ if (w && w->inherits("QComboBoxPrivateContainer")){
+ const QBrush menuBackground = opt->palette.base().color();
+ QColor borderColor = opt->palette.window().color();
+ qDrawPlainRect(p, opt->rect, borderColor, 1, &menuBackground);
+ }
case PE_FrameWindow: {
QPalette popupPal = opt->palette;
popupPal.setColor(QPalette::Light, opt->palette.window().color());
diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp
index 55e6137dba9..ee80cca649c 100644
--- a/src/widgets/widgets/qlineedit_p.cpp
+++ b/src/widgets/widgets/qlineedit_p.cpp
@@ -353,17 +353,16 @@ QLineEditPrivate *QLineEditIconButton::lineEditPrivate() const
void QLineEditIconButton::paintEvent(QPaintEvent *)
{
QPainter painter(this);
- QIcon::Mode state = QIcon::Disabled;
+ QIcon::Mode mode = QIcon::Disabled;
if (isEnabled())
- state = isDown() ? QIcon::Active : QIcon::Normal;
+ mode = isDown() ? QIcon::Active : QIcon::Normal;
const QLineEditPrivate *lep = lineEditPrivate();
const int iconWidth = lep ? lep->sideWidgetParameters().iconSize : 16;
const QSize iconSize(iconWidth, iconWidth);
- const QPixmap iconPixmap = icon().pixmap(iconSize, devicePixelRatio(), state, QIcon::Off);
QRect pixmapRect = QRect(QPoint(0, 0), iconSize);
pixmapRect.moveCenter(rect().center());
painter.setOpacity(m_opacity);
- painter.drawPixmap(pixmapRect, iconPixmap);
+ icon().paint(&painter, pixmapRect, Qt::AlignCenter, mode, QIcon::Off);
}
void QLineEditIconButton::actionEvent(QActionEvent *e)
diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp
index 0b562a47879..8e6f497d7f5 100644
--- a/src/widgets/widgets/qtabbar.cpp
+++ b/src/widgets/widgets/qtabbar.cpp
@@ -1003,7 +1003,7 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
++tab->lastTab;
}
- if (tabAt(d->mousePosition) == index) {
+ if (isVisible() && tabAt(d->mousePosition) == index) {
d->hoverIndex = index;
d->hoverRect = tabRect(index);
}