summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml6
-rw-r--r--src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h7
-rw-r--r--src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp27
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.cpp757
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.h64
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp9
-rw-r--r--src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp11
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp31
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.h6
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp2
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.mm29
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm1
-rw-r--r--src/plugins/platforms/cocoa/qnsview_complextext.mm36
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfscursor.cpp51
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfscursor_p.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp15
-rw-r--r--src/plugins/platforms/ios/qiosscreen.mm4
-rw-r--r--src/plugins/platforms/ios/quiview_accessibility.mm4
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp25
-rw-r--r--src/plugins/platforms/windows/qwindowsdialoghelpers.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp15
-rw-r--r--src/plugins/platforms/windows/qwindowsmime.cpp5
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp18
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp5
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp7
-rw-r--r--src/plugins/platforms/xcb/qt_xlib_wrapper.c40
-rw-r--r--src/plugins/platforms/xcb/qt_xlib_wrapper.h40
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp17
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3theme.cpp2
-rw-r--r--src/plugins/sqldrivers/.cmake.conf2
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql.cpp9
-rw-r--r--src/plugins/sqldrivers/oci/qsql_oci.cpp13
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc.cpp224
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm14
-rw-r--r--src/plugins/tls/openssl/qtls_openssl.cpp6
-rw-r--r--src/plugins/tls/openssl/qtls_openssl_p.h2
-rw-r--r--src/plugins/tls/openssl/qtlsbackend_openssl.cpp8
-rw-r--r--src/plugins/tls/securetransport/qtls_st.cpp3
-rw-r--r--src/plugins/tls/shared/qsslsocket_mac_shared.cpp64
47 files changed, 1254 insertions, 362 deletions
diff --git a/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml b/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml
index 9c67a38c57..30c326d06f 100644
--- a/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml
+++ b/src/plugins/platforminputcontexts/ibus/interfaces/org.freedesktop.IBus.InputContext.xml
@@ -14,6 +14,12 @@
<arg name="w" direction="in" type="i"/>
<arg name="h" direction="in" type="i"/>
</method>
+ <method name='SetCursorLocationRelative'>
+ <arg name="x" direction="in" type="i"/>
+ <arg name="y" direction="in" type="i"/>
+ <arg name="w" direction="in" type="i"/>
+ <arg name="h" direction="in" type="i"/>
+ </method>
<method name="FocusIn"/>
<method name="FocusOut"/>
<method name="Reset"/>
diff --git a/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h b/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h
index 3d11706c06..31a181eec2 100644
--- a/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h
+++ b/src/plugins/platforminputcontexts/ibus/qibusinputcontextproxy.h
@@ -114,6 +114,13 @@ public Q_SLOTS: // METHODS
return asyncCallWithArgumentList(QLatin1String("SetCursorLocation"), argumentList);
}
+ inline QDBusPendingReply<> SetCursorLocationRelative(int x, int y, int w, int h)
+ {
+ QList<QVariant> argumentList;
+ argumentList << QVariant::fromValue(x) << QVariant::fromValue(y) << QVariant::fromValue(w) << QVariant::fromValue(h);
+ return asyncCallWithArgumentList(QLatin1String("SetCursorLocationRelative"), argumentList);
+ }
+
inline QDBusPendingReply<> SetEngine(const QString &name)
{
QList<QVariant> argumentList;
diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
index b3fb88c907..774051c314 100644
--- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
+++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
@@ -257,10 +257,31 @@ void QIBusPlatformInputContext::cursorRectChanged()
QWindow *inputWindow = qApp->focusWindow();
if (!inputWindow)
return;
- r.moveTopLeft(inputWindow->mapToGlobal(r.topLeft()));
+ if (!inputWindow->screen())
+ return;
+
+ if (QGuiApplication::platformName().startsWith("wayland")) {
+ auto margins = inputWindow->frameMargins();
+ r.translate(margins.left(), margins.top());
+ qreal scale = inputWindow->devicePixelRatio();
+ QRect newRect = QRect(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale);
+ if (debug)
+ qDebug() << "microFocus" << newRect;
+ d->context->SetCursorLocationRelative(newRect.x(), newRect.y(),
+ newRect.width(), newRect.height());
+ return;
+ }
+
+ // x11/xcb
+ auto screenGeometry = inputWindow->screen()->geometry();
+ auto point = inputWindow->mapToGlobal(r.topLeft());
+ qreal scale = inputWindow->devicePixelRatio();
+ auto native = (point - screenGeometry.topLeft()) * scale + screenGeometry.topLeft();
+ QRect newRect(native, r.size() * scale);
if (debug)
- qDebug() << "microFocus" << r;
- d->context->SetCursorLocation(r.x(), r.y(), r.width(), r.height());
+ qDebug() << "microFocus" << newRect;
+ d->context->SetCursorLocation(newRect.x(), newRect.y(),
+ newRect.width(), newRect.height());
}
void QIBusPlatformInputContext::setFocusObject(QObject *object)
diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
index 087112eee9..0f58b037e7 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.cpp
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -43,15 +43,28 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjniobject.h>
-
-#include <QDebug>
+#include <QtCore/qurl.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmimedatabase.h>
using namespace QNativeInterface;
-AndroidContentFileEngine::AndroidContentFileEngine(const QString &f)
- : m_file(f)
+static QJniObject &contentResolverInstance()
+{
+ static QJniObject contentResolver;
+ if (!contentResolver.isValid()) {
+ contentResolver = QJniObject(QNativeInterface::QAndroidApplication::context())
+ .callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
+ }
+
+ return contentResolver;
+}
+
+AndroidContentFileEngine::AndroidContentFileEngine(const QString &filename)
+ : m_initialFile(filename),
+ m_documentFile(DocumentFile::parseFromAnyUri(filename))
{
- setFileName(f);
+ setFileName(filename);
}
bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
@@ -62,6 +75,27 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
}
if (openMode & QFileDevice::WriteOnly) {
openModeStr += QLatin1Char('w');
+ if (!m_documentFile->exists()) {
+ if (QUrl(m_initialFile).path().startsWith(QLatin1String("/tree/"))) {
+ const int lastSeparatorIndex = m_initialFile.lastIndexOf(QLatin1Char('/'));
+ const QString fileName = m_initialFile.mid(lastSeparatorIndex + 1);
+
+ QString mimeType;
+ const auto mimeTypes = QMimeDatabase().mimeTypesForFileName(fileName);
+ if (!mimeTypes.empty())
+ mimeType = mimeTypes.first().name();
+ else
+ mimeType = QLatin1String("application/octet-stream");
+
+ if (m_documentFile->parent()) {
+ auto createdFile = m_documentFile->parent()->createFile(mimeType, fileName);
+ if (createdFile)
+ m_documentFile = createdFile;
+ }
+ } else {
+ qWarning() << "open(): non-existent content URI with a document type provided";
+ }
+ }
}
if (openMode & QFileDevice::Truncate) {
openModeStr += QLatin1Char('t');
@@ -69,12 +103,10 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
openModeStr += QLatin1Char('a');
}
- m_pfd = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative",
- "openParcelFdForContentUrl",
- "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
- QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object(),
- QJniObject::fromString(openModeStr).object());
+ m_pfd = contentResolverInstance().callObjectMethod("openFileDescriptor",
+ "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
+ m_documentFile->uri().object(),
+ QJniObject::fromString(openModeStr).object());
if (!m_pfd.isValid())
return false;
@@ -82,8 +114,7 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
const auto fd = m_pfd.callMethod<jint>("getFd", "()I");
if (fd < 0) {
- m_pfd.callMethod<void>("close", "()V");
- m_pfd = QJniObject();
+ closeNativeFileDescriptor();
return false;
}
@@ -92,47 +123,127 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
bool AndroidContentFileEngine::close()
{
+ closeNativeFileDescriptor();
+ return QFSFileEngine::close();
+}
+
+void AndroidContentFileEngine::closeNativeFileDescriptor()
+{
if (m_pfd.isValid()) {
m_pfd.callMethod<void>("close", "()V");
m_pfd = QJniObject();
}
-
- return QFSFileEngine::close();
}
qint64 AndroidContentFileEngine::size() const
{
- const jlong size = QJniObject::callStaticMethod<jlong>(
- "org/qtproject/qt/android/QtNative", "getSize",
- "(Landroid/content/Context;Ljava/lang/String;)J", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- return (qint64)size;
+ return m_documentFile->length();
+}
+
+bool AndroidContentFileEngine::remove()
+{
+ return m_documentFile->remove();
+}
+
+bool AndroidContentFileEngine::rename(const QString &newName)
+{
+ if (m_documentFile->rename(newName)) {
+ m_initialFile = m_documentFile->uri().toString();
+ return true;
+ }
+ return false;
+}
+
+bool AndroidContentFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
+{
+ QString tmp = dirName;
+ tmp.remove(m_initialFile);
+
+ QStringList dirParts = tmp.split(QLatin1Char('/'));
+ dirParts.removeAll("");
+
+ if (dirParts.isEmpty())
+ return false;
+
+ auto createdDir = m_documentFile;
+ bool allDirsCreated = true;
+ for (const auto &dir : dirParts) {
+ // Find if the sub-dir already exists and then don't re-create it
+ bool subDirExists = false;
+ for (const DocumentFilePtr &subDir : m_documentFile->listFiles()) {
+ if (dir == subDir->name() && subDir->isDirectory()) {
+ createdDir = subDir;
+ subDirExists = true;
+ }
+ }
+
+ if (!subDirExists) {
+ createdDir = createdDir->createDirectory(dir);
+ if (!createdDir) {
+ allDirsCreated = false;
+ break;
+ }
+ }
+
+ if (!createParentDirectories)
+ break;
+ }
+
+ return allDirsCreated;
+}
+
+bool AndroidContentFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const
+{
+ if (recurseParentDirectories)
+ qWarning() << "rmpath(): Unsupported for Content URIs";
+
+ const QString dirFileName = QUrl(dirName).fileName();
+ bool deleted = false;
+ for (const DocumentFilePtr &dir : m_documentFile->listFiles()) {
+ if (dirFileName == dir->name() && dir->isDirectory()) {
+ deleted = dir->remove();
+ break;
+ }
+ }
+
+ return deleted;
+}
+
+QByteArray AndroidContentFileEngine::id() const
+{
+ return m_documentFile->id().toUtf8();
+}
+
+QDateTime AndroidContentFileEngine::fileTime(FileTime time) const
+{
+ switch (time) {
+ case FileTime::ModificationTime:
+ return m_documentFile->lastModified();
+ break;
+ default:
+ break;
+ }
+
+ return QDateTime();
}
AndroidContentFileEngine::FileFlags AndroidContentFileEngine::fileFlags(FileFlags type) const
{
- FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
FileFlags flags;
- const bool isDir = QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkIfDir",
- "(Landroid/content/Context;Ljava/lang/String;)Z", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- // If it is a directory then we know it exists so there is no reason to explicitly check
- const bool exists = isDir ? true : QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkFileExists",
- "(Landroid/content/Context;Ljava/lang/String;)Z", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- if (!exists && !isDir)
+ if (!m_documentFile->exists())
return flags;
- if (isDir) {
- flags = DirectoryType | commonFlags;
+
+ flags = ExistsFlag;
+ if (!m_documentFile->canRead())
+ return flags;
+
+ flags |= ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm;
+
+ if (m_documentFile->isDirectory()) {
+ flags |= DirectoryType;
} else {
- flags = FileType | commonFlags;
- const bool writable = QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkIfWritable",
- "(Landroid/content/Context;Ljava/lang/String;)Z", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- if (writable)
+ flags |= FileType;
+ if (m_documentFile->canWrite())
flags |= WriteOwnerPerm|WriteUserPerm|WriteGroupPerm|WriteOtherPerm;
}
return type & flags;
@@ -147,18 +258,18 @@ QString AndroidContentFileEngine::fileName(FileName f) const
case DefaultName:
case AbsoluteName:
case CanonicalName:
- return m_file;
+ return m_documentFile->uri().toString();
case BaseName:
- {
- const int pos = m_file.lastIndexOf(QChar(QLatin1Char('/')));
- return m_file.mid(pos);
- }
+ return m_documentFile->name();
default:
- return QString();
+ break;
}
+
+ return QString();
}
-QAbstractFileEngine::Iterator *AndroidContentFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
+QAbstractFileEngine::Iterator *AndroidContentFileEngine::beginEntryList(QDir::Filters filters,
+ const QStringList &filterNames)
{
return new AndroidContentFileEngineIterator(filters, filterNames);
}
@@ -200,37 +311,539 @@ QString AndroidContentFileEngineIterator::next()
bool AndroidContentFileEngineIterator::hasNext() const
{
- if (m_index == -1) {
- if (path().isEmpty())
+ if (m_index == -1 && m_files.isEmpty()) {
+ const auto currentPath = path();
+ if (currentPath.isEmpty())
return false;
- const bool isDir = QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkIfDir",
- "(Landroid/content/Context;Ljava/lang/String;)Z",
- QAndroidApplication::context(),
- QJniObject::fromString(path()).object());
- if (isDir) {
- QJniObject objArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative",
- "listContentsFromTreeUri",
- "(Landroid/content/Context;Ljava/lang/String;)[Ljava/lang/String;",
- QAndroidApplication::context(),
- QJniObject::fromString(path()).object());
- if (objArray.isValid()) {
- QJniEnvironment env;
- const jsize length = env->GetArrayLength(objArray.object<jarray>());
- for (int i = 0; i != length; ++i) {
- m_entries << QJniObject(env->GetObjectArrayElement(
- objArray.object<jobjectArray>(), i)).toString();
- }
- }
- }
- m_index = 0;
+
+ const auto iterDoc = DocumentFile::parseFromAnyUri(currentPath);
+ if (iterDoc->isDirectory())
+ for (const auto &doc : iterDoc->listFiles())
+ m_files.append(doc);
}
- return m_index < m_entries.size();
+
+ return m_index < (m_files.size() - 1);
}
QString AndroidContentFileEngineIterator::currentFileName() const
{
- if (m_index <= 0 || m_index > m_entries.size())
+ if (m_index < 0 || m_index > m_files.size())
return QString();
- return m_entries.at(m_index - 1);
+ // Returns a full path since contstructing a content path from the file name
+ // and a tree URI only will not point to a valid file URI.
+ return m_files.at(m_index)->uri().toString();
+}
+
+QString AndroidContentFileEngineIterator::currentFilePath() const
+{
+ return currentFileName();
+}
+
+// Start of Cursor
+
+class Cursor
+{
+public:
+ explicit Cursor(const QJniObject &object)
+ : m_object{object} { }
+
+ ~Cursor()
+ {
+ if (m_object.isValid())
+ m_object.callMethod<void>("close");
+ }
+
+ enum Type {
+ FIELD_TYPE_NULL = 0x00000000,
+ FIELD_TYPE_INTEGER = 0x00000001,
+ FIELD_TYPE_FLOAT = 0x00000002,
+ FIELD_TYPE_STRING = 0x00000003,
+ FIELD_TYPE_BLOB = 0x00000004
+ };
+
+ QVariant data(int columnIndex) const
+ {
+ int type = m_object.callMethod<jint>("getType", "(I)I", columnIndex);
+ switch (type) {
+ case FIELD_TYPE_NULL:
+ return {};
+ case FIELD_TYPE_INTEGER:
+ return QVariant::fromValue(m_object.callMethod<jlong>("getLong", "(I)J", columnIndex));
+ case FIELD_TYPE_FLOAT:
+ return QVariant::fromValue(m_object.callMethod<jdouble>("getDouble", "(I)D",
+ columnIndex));
+ case FIELD_TYPE_STRING:
+ return QVariant::fromValue(m_object.callObjectMethod("getString",
+ "(I)Ljava/lang/String;",
+ columnIndex).toString());
+ case FIELD_TYPE_BLOB: {
+ auto blob = m_object.callObjectMethod("getBlob", "(I)[B", columnIndex);
+ QJniEnvironment env;
+ const auto blobArray = blob.object<jbyteArray>();
+ const int size = env->GetArrayLength(blobArray);
+ const auto byteArray = env->GetByteArrayElements(blobArray, nullptr);
+ QByteArray data{reinterpret_cast<const char *>(byteArray), size};
+ env->ReleaseByteArrayElements(blobArray, byteArray, 0);
+ return QVariant::fromValue(data);
+ }
+ }
+ return {};
+ }
+
+ static std::unique_ptr<Cursor> queryUri(const QJniObject &uri,
+ const QStringList &projection = {},
+ const QString &selection = {},
+ const QStringList &selectionArgs = {},
+ const QString &sortOrder = {})
+ {
+ auto cursor = contentResolverInstance().callObjectMethod("query",
+ "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;",
+ uri.object(),
+ projection.isEmpty() ? nullptr : fromStringList(projection).object(),
+ selection.isEmpty() ? nullptr : QJniObject::fromString(selection).object(),
+ selectionArgs.isEmpty() ? nullptr : fromStringList(selectionArgs).object(),
+ sortOrder.isEmpty() ? nullptr : QJniObject::fromString(sortOrder).object());
+ if (!cursor.isValid())
+ return {};
+ return std::make_unique<Cursor>(cursor);
+ }
+
+ static QVariant queryColumn(const QJniObject &uri, const QString &column)
+ {
+ const auto query = queryUri(uri, {column});
+ if (!query)
+ return {};
+
+ if (query->rowCount() != 1 || query->columnCount() != 1)
+ return {};
+ query->moveToFirst();
+ return query->data(0);
+ }
+
+ bool isNull(int columnIndex) const
+ {
+ return m_object.callMethod<jboolean>("isNull", "(I)Z", columnIndex);
+ }
+
+ int columnCount() const { return m_object.callMethod<jint>("getColumnCount"); }
+ int rowCount() const { return m_object.callMethod<jint>("getCount"); }
+ int row() const { return m_object.callMethod<jint>("getPosition"); }
+ bool isFirst() const { return m_object.callMethod<jboolean>("isFirst"); }
+ bool isLast() const { return m_object.callMethod<jboolean>("isLast"); }
+ bool moveToFirst() { return m_object.callMethod<jboolean>("moveToFirst"); }
+ bool moveToLast() { return m_object.callMethod<jboolean>("moveToLast"); }
+ bool moveToNext() { return m_object.callMethod<jboolean>("moveToNext"); }
+
+private:
+ static QJniObject fromStringList(const QStringList &list)
+ {
+ QJniEnvironment env;
+ auto array = env->NewObjectArray(list.size(), env->FindClass("java/lang/String"), nullptr);
+ for (int i = 0; i < list.size(); ++i)
+ env->SetObjectArrayElement(array, i, QJniObject::fromString(list[i]).object());
+ return QJniObject::fromLocalRef(array);
+ }
+
+ QJniObject m_object;
+};
+
+// End of Cursor
+
+// Start of DocumentsContract
+
+/*!
+ *
+ * DocumentsContract Api.
+ * Check https://developer.android.com/reference/android/provider/DocumentsContract
+ * for more information.
+ *
+ * \note This does not implement all facilities of the native API.
+ *
+ */
+namespace DocumentsContract
+{
+
+namespace Document {
+const QLatin1String COLUMN_DISPLAY_NAME("_display_name");
+const QLatin1String COLUMN_DOCUMENT_ID("document_id");
+const QLatin1String COLUMN_FLAGS("flags");
+const QLatin1String COLUMN_LAST_MODIFIED("last_modified");
+const QLatin1String COLUMN_MIME_TYPE("mime_type");
+const QLatin1String COLUMN_SIZE("_size");
+
+constexpr int FLAG_DIR_SUPPORTS_CREATE = 0x00000008;
+constexpr int FLAG_SUPPORTS_DELETE = 0x00000004;
+constexpr int FLAG_SUPPORTS_MOVE = 0x00000100;
+constexpr int FLAG_SUPPORTS_RENAME = 0x00000040;
+constexpr int FLAG_SUPPORTS_WRITE = 0x00000002;
+constexpr int FLAG_VIRTUAL_DOCUMENT = 0x00000200;
+
+const QLatin1String MIME_TYPE_DIR("vnd.android.document/directory");
+} // namespace Document
+
+QString documentId(const QJniObject &uri)
+{
+ return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
+ "getDocumentId",
+ "(Landroid/net/Uri;)Ljava/lang/String;",
+ uri.object()).toString();
+}
+
+QString treeDocumentId(const QJniObject &uri)
+{
+ return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
+ "getTreeDocumentId",
+ "(Landroid/net/Uri;)Ljava/lang/String;",
+ uri.object()).toString();
+}
+
+QJniObject buildChildDocumentsUriUsingTree(const QJniObject &uri, const QString &parentDocumentId)
+{
+ return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
+ "buildChildDocumentsUriUsingTree",
+ "(Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;",
+ uri.object(),
+ QJniObject::fromString(parentDocumentId).object());
+
+}
+
+QJniObject buildDocumentUriUsingTree(const QJniObject &treeUri, const QString &documentId)
+{
+ return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
+ "buildDocumentUriUsingTree",
+ "(Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;",
+ treeUri.object(),
+ QJniObject::fromString(documentId).object());
+}
+
+bool isDocumentUri(const QJniObject &uri)
+{
+ return QJniObject::callStaticMethod<jboolean>("android/provider/DocumentsContract",
+ "isDocumentUri",
+ "(Landroid/content/Context;Landroid/net/Uri;)Z",
+ QNativeInterface::QAndroidApplication::context(),
+ uri.object());
+}
+
+bool isTreeUri(const QJniObject &uri)
+{
+ return QJniObject::callStaticMethod<jboolean>("android/provider/DocumentsContract",
+ "isTreeUri",
+ "(Landroid/net/Uri;)Z",
+ uri.object());
+}
+
+QJniObject createDocument(const QJniObject &parentDocumentUri, const QString &mimeType,
+ const QString &displayName)
+{
+ return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
+ "createDocument",
+ "(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;",
+ contentResolverInstance().object(),
+ parentDocumentUri.object(),
+ QJniObject::fromString(mimeType).object(),
+ QJniObject::fromString(displayName).object());
+}
+
+bool deleteDocument(const QJniObject &documentUri)
+{
+ const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
+ if (!(flags & Document::FLAG_SUPPORTS_DELETE))
+ return {};
+
+ return QJniObject::callStaticMethod<jboolean>("android/provider/DocumentsContract",
+ "deleteDocument",
+ "(Landroid/content/ContentResolver;Landroid/net/Uri;)Z",
+ contentResolverInstance().object(),
+ documentUri.object());
+}
+
+QJniObject moveDocument(const QJniObject &sourceDocumentUri,
+ const QJniObject &sourceParentDocumentUri,
+ const QJniObject &targetParentDocumentUri)
+{
+ const int flags = Cursor::queryColumn(sourceDocumentUri, Document::COLUMN_FLAGS).toInt();
+ if (!(flags & Document::FLAG_SUPPORTS_MOVE))
+ return {};
+
+ return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
+ "moveDocument",
+ "(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;",
+ contentResolverInstance().object(),
+ sourceDocumentUri.object(),
+ sourceParentDocumentUri.object(),
+ targetParentDocumentUri.object());
+}
+
+QJniObject renameDocument(const QJniObject &documentUri, const QString &displayName)
+{
+ const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
+ if (!(flags & Document::FLAG_SUPPORTS_RENAME))
+ return {};
+
+ return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
+ "renameDocument",
+ "(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;",
+ contentResolverInstance().object(),
+ documentUri.object(),
+ QJniObject::fromString(displayName).object());
+}
+} // End DocumentsContract namespace
+
+// Start of DocumentFile
+
+using namespace DocumentsContract;
+
+namespace {
+class MakeableDocumentFile : public DocumentFile
+{
+public:
+ MakeableDocumentFile(const QJniObject &uri, const DocumentFilePtr &parent = {})
+ : DocumentFile(uri, parent)
+ {}
+};
+}
+
+DocumentFile::DocumentFile(const QJniObject &uri,
+ const DocumentFilePtr &parent)
+ : m_uri{uri}
+ , m_parent{parent}
+{}
+
+QJniObject parseUri(const QString &uri)
+{
+ return QJniObject::callStaticObjectMethod("android/net/Uri",
+ "parse",
+ "(Ljava/lang/String;)Landroid/net/Uri;",
+ QJniObject::fromString(uri).object());
+}
+
+DocumentFilePtr DocumentFile::parseFromAnyUri(const QString &fileName)
+{
+ const QJniObject uri = parseUri(fileName);
+
+ if (DocumentsContract::isDocumentUri(uri))
+ return fromSingleUri(uri);
+
+ const QString documentType = QLatin1String("/document/");
+ const QString treeType = QLatin1String("/tree/");
+
+ const int treeIndex = fileName.indexOf(treeType);
+ const int documentIndex = fileName.indexOf(documentType);
+ const int index = fileName.lastIndexOf(QLatin1Char('/'));
+
+ if (index <= treeIndex + treeType.size() || index <= documentIndex + documentType.size())
+ return fromTreeUri(uri);
+
+ const QString parentUrl = fileName.left(index);
+ DocumentFilePtr parentDocFile = fromTreeUri(parseUri(parentUrl));
+
+ const QString baseName = fileName.mid(index);
+ const QString fileUrl = parentUrl + QUrl::toPercentEncoding(baseName);
+
+ DocumentFilePtr docFile = std::make_shared<MakeableDocumentFile>(parseUri(fileUrl));
+ if (parentDocFile && parentDocFile->isDirectory())
+ docFile->m_parent = parentDocFile;
+
+ return docFile;
+}
+
+DocumentFilePtr DocumentFile::fromSingleUri(const QJniObject &uri)
+{
+ return std::make_shared<MakeableDocumentFile>(uri);
+}
+
+DocumentFilePtr DocumentFile::fromTreeUri(const QJniObject &treeUri)
+{
+ QString docId;
+ if (isDocumentUri(treeUri))
+ docId = documentId(treeUri);
+ else
+ docId = treeDocumentId(treeUri);
+
+ return std::make_shared<MakeableDocumentFile>(buildDocumentUriUsingTree(treeUri, docId));
+}
+
+DocumentFilePtr DocumentFile::createFile(const QString &mimeType, const QString &displayName)
+{
+ if (isDirectory()) {
+ return std::make_shared<MakeableDocumentFile>(
+ createDocument(m_uri, mimeType, displayName),
+ shared_from_this());
+ }
+ return {};
+}
+
+DocumentFilePtr DocumentFile::createDirectory(const QString &displayName)
+{
+ if (isDirectory()) {
+ return std::make_shared<MakeableDocumentFile>(
+ createDocument(m_uri, Document::MIME_TYPE_DIR, displayName),
+ shared_from_this());
+ }
+ return {};
+}
+
+const QJniObject &DocumentFile::uri() const
+{
+ return m_uri;
+}
+
+const DocumentFilePtr &DocumentFile::parent() const
+{
+ return m_parent;
+}
+
+QString DocumentFile::name() const
+{
+ return Cursor::queryColumn(m_uri, Document::COLUMN_DISPLAY_NAME).toString();
+}
+
+QString DocumentFile::id() const
+{
+ return DocumentsContract::documentId(uri());
+}
+
+QString DocumentFile::mimeType() const
+{
+ return Cursor::queryColumn(m_uri, Document::COLUMN_MIME_TYPE).toString();
+}
+
+bool DocumentFile::isDirectory() const
+{
+ return mimeType() == Document::MIME_TYPE_DIR;
+}
+
+bool DocumentFile::isFile() const
+{
+ const QString type = mimeType();
+ return type != Document::MIME_TYPE_DIR && !type.isEmpty();
+}
+
+bool DocumentFile::isVirtual() const
+{
+ return isDocumentUri(m_uri) && (Cursor::queryColumn(m_uri,
+ Document::COLUMN_FLAGS).toInt() & Document::FLAG_VIRTUAL_DOCUMENT);
+}
+
+QDateTime DocumentFile::lastModified() const
+{
+ const auto timeVariant = Cursor::queryColumn(m_uri, Document::COLUMN_LAST_MODIFIED);
+ if (timeVariant.isValid())
+ return QDateTime::fromMSecsSinceEpoch(timeVariant.toLongLong());
+ return {};
+}
+
+int64_t DocumentFile::length() const
+{
+ return Cursor::queryColumn(m_uri, Document::COLUMN_SIZE).toLongLong();
+}
+
+namespace {
+constexpr int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
+constexpr int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002;
}
+
+bool DocumentFile::canRead() const
+{
+ const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
+ const bool selfUriPermission = context.callMethod<jint>("checkCallingOrSelfUriPermission",
+ "(Landroid/net/Uri;I)I",
+ m_uri.object(),
+ FLAG_GRANT_READ_URI_PERMISSION);
+ if (selfUriPermission != 0)
+ return false;
+
+ return !mimeType().isEmpty();
+}
+
+bool DocumentFile::canWrite() const
+{
+ const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
+ const bool selfUriPermission = context.callMethod<jint>("checkCallingOrSelfUriPermission",
+ "(Landroid/net/Uri;I)I",
+ m_uri.object(),
+ FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (selfUriPermission != 0)
+ return false;
+
+ const QString type = mimeType();
+ if (type.isEmpty())
+ return false;
+
+ const int flags = Cursor::queryColumn(m_uri, Document::COLUMN_FLAGS).toInt();
+ if (flags & Document::FLAG_SUPPORTS_DELETE)
+ return true;
+
+ const bool supportsWrite = (flags & Document::FLAG_SUPPORTS_WRITE);
+ const bool isDir = (type == Document::MIME_TYPE_DIR);
+ const bool dirSupportsCreate = (isDir && (flags & Document::FLAG_DIR_SUPPORTS_CREATE));
+
+ return dirSupportsCreate || supportsWrite;
+}
+
+bool DocumentFile::remove()
+{
+ return deleteDocument(m_uri);
+}
+
+bool DocumentFile::exists() const
+{
+ return !name().isEmpty();
+}
+
+std::vector<DocumentFilePtr> DocumentFile::listFiles()
+{
+ std::vector<DocumentFilePtr> res;
+ const auto childrenUri = buildChildDocumentsUriUsingTree(m_uri, documentId(m_uri));
+ const auto query = Cursor::queryUri(childrenUri, {Document::COLUMN_DOCUMENT_ID});
+ if (!query)
+ return res;
+
+ while (query->moveToNext()) {
+ const auto uri = buildDocumentUriUsingTree(m_uri, query->data(0).toString());
+ res.push_back(std::make_shared<MakeableDocumentFile>(uri, shared_from_this()));
+ }
+ return res;
+}
+
+bool DocumentFile::rename(const QString &newName)
+{
+ QJniObject uri;
+ if (newName.startsWith(QLatin1String("content://"))) {
+ auto lastSeparatorIndex = [](const QString &file) {
+ int posDecoded = file.lastIndexOf(QLatin1Char('/'));
+ int posEncoded = file.lastIndexOf(QUrl::toPercentEncoding(QLatin1String("/")));
+ return posEncoded > posDecoded ? posEncoded : posDecoded;
+ };
+
+ // first try to see if the new file is under the same tree and thus used rename only
+ const QString parent = m_uri.toString().left(lastSeparatorIndex(m_uri.toString()));
+ if (newName.contains(parent)) {
+ QString displayName = newName.mid(lastSeparatorIndex(newName));
+ if (displayName.startsWith(QLatin1Char('/')))
+ displayName.remove(0, 1);
+ else if (displayName.startsWith(QUrl::toPercentEncoding(QLatin1String("/"))))
+ displayName.remove(0, 3);
+
+ uri = renameDocument(m_uri, displayName);
+ } else {
+ // Move
+ QJniObject srcParentUri = fromTreeUri(parseUri(parent))->uri();
+ const QString destParent = newName.left(lastSeparatorIndex(newName));
+ QJniObject targetParentUri = fromTreeUri(parseUri(destParent))->uri();
+ uri = moveDocument(m_uri, srcParentUri, targetParentUri);
+ }
+ } else {
+ uri = renameDocument(m_uri, newName);
+ }
+
+ if (uri.isValid()) {
+ m_uri = uri;
+ return true;
+ }
+
+ return false;
+}
+
+// End of DocumentFile
diff --git a/src/plugins/platforms/android/androidcontentfileengine.h b/src/plugins/platforms/android/androidcontentfileengine.h
index 37831a2259..f3813b55a1 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.h
+++ b/src/plugins/platforms/android/androidcontentfileengine.h
@@ -41,7 +41,11 @@
#define ANDROIDCONTENTFILEENGINE_H
#include <private/qfsfileengine_p.h>
+
#include <QtCore/qjniobject.h>
+#include <QtCore/qlist.h>
+
+using DocumentFilePtr = std::shared_ptr<class DocumentFile>;
class AndroidContentFileEngine : public QFSFileEngine
{
@@ -50,14 +54,24 @@ public:
bool open(QIODevice::OpenMode openMode) override;
bool close() override;
qint64 size() const override;
+ bool remove() override;
+ bool rename(const QString &newName) override;
+ bool mkdir(const QString &dirName, bool createParentDirectories) const override;
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
+ QByteArray id() const override;
+ bool caseSensitive() const override { return true; }
+ QDateTime fileTime(FileTime time) const override;
FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
QString fileName(FileName file = DefaultName) const override;
QAbstractFileEngine::Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
QAbstractFileEngine::Iterator *endEntryList() override;
+
private:
- QString m_file;
- QJniObject m_pfd;
+ void closeNativeFileDescriptor();
+ QString m_initialFile;
+ QJniObject m_pfd;
+ DocumentFilePtr m_documentFile;
};
class AndroidContentFileEngineHandler : public QAbstractFileEngineHandler
@@ -76,9 +90,51 @@ public:
QString next() override;
bool hasNext() const override;
QString currentFileName() const override;
+ QString currentFilePath() const override;
private:
- mutable QStringList m_entries;
- mutable int m_index = -1;
+ mutable QList<DocumentFilePtr> m_files;
+ mutable qsizetype m_index = -1;
+};
+
+/*!
+ *
+ * DocumentFile Api.
+ * Check https://developer.android.com/reference/androidx/documentfile/provider/DocumentFile
+ * for more information.
+ *
+ */
+class DocumentFile : public std::enable_shared_from_this<DocumentFile>
+{
+public:
+ static DocumentFilePtr parseFromAnyUri(const QString &filename);
+ static DocumentFilePtr fromSingleUri(const QJniObject &uri);
+ static DocumentFilePtr fromTreeUri(const QJniObject &treeUri);
+
+ DocumentFilePtr createFile(const QString &mimeType, const QString &displayName);
+ DocumentFilePtr createDirectory(const QString &displayName);
+ const QJniObject &uri() const;
+ const DocumentFilePtr &parent() const;
+ QString name() const;
+ QString id() const;
+ QString mimeType() const;
+ bool isDirectory() const;
+ bool isFile() const;
+ bool isVirtual() const;
+ QDateTime lastModified() const;
+ int64_t length() const;
+ bool canRead() const;
+ bool canWrite() const;
+ bool remove();
+ bool exists() const;
+ std::vector<DocumentFilePtr> listFiles();
+ bool rename(const QString &newName);
+
+protected:
+ DocumentFile(const QJniObject &uri, const std::shared_ptr<DocumentFile> &parent);
+
+protected:
+ QJniObject m_uri;
+ DocumentFilePtr m_parent;
};
#endif // ANDROIDCONTENTFILEENGINE_H
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp
index 731937dd8f..54579447d3 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.cpp
+++ b/src/plugins/platforms/android/androidjniaccessibility.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
+#include "androiddeadlockprotector.h"
#include "androidjniaccessibility.h"
#include "androidjnimain.h"
#include "qandroidplatformintegration.h"
@@ -94,6 +95,14 @@ namespace QtAndroidAccessibility
template <typename Func, typename Ret>
void runInObjectContext(QObject *context, Func &&func, Ret *retVal)
{
+ AndroidDeadlockProtector protector;
+ if (!protector.acquire()) {
+ __android_log_print(ANDROID_LOG_WARN, m_qtTag,
+ "Could not run accessibility call in object context, accessing "
+ "main thread could lead to deadlock");
+ return;
+ }
+
if (!QtAndroid::blockEventLoopsWhenSuspended()
|| QGuiApplication::applicationState() != Qt::ApplicationSuspended) {
QMetaObject::invokeMethod(context, func, Qt::BlockingQueuedConnection, retVal);
diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
index 90988f6b0d..43b381d4ce 100644
--- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
+++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
@@ -142,6 +142,8 @@ public:
FolderIterator(const QString &path)
: m_path(path)
{
+ // Note that empty dirs in the assets dir before the build are not going to be
+ // included in the final apk, so no empty folders should expected to be listed.
QJniObject files = QJniObject::callStaticObjectMethod(QtAndroid::applicationClass(),
"listAssetContent",
"(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
@@ -220,7 +222,7 @@ public:
return m_currentIterator->currentFileName();
}
- virtual QString currentFilePath() const
+ QString currentFilePath() const override
{
if (!m_currentIterator)
return {};
@@ -392,8 +394,13 @@ public:
} else {
auto *assetDir = AAssetManager_openDir(m_assetManager, m_fileName.toUtf8());
if (assetDir) {
- if (AAssetDir_getNextFileName(assetDir))
+ if (AAssetDir_getNextFileName(assetDir)
+ || (!FolderIterator::fromCache(m_fileName, false)->empty())) {
+ // If AAssetDir_getNextFileName is not valid, it still can be a directory that
+ // contains only other directories (no files). FolderIterator will not be called
+ // on the directory containing files so it should not be too time consuming now.
m_assetInfo->type = AssetItem::Type::Folder;
+ }
AAssetDir_close(assetDir);
}
}
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
index 1ec5867a38..07c5198092 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
@@ -41,8 +41,9 @@
#include "qandroidplatformfiledialoghelper.h"
#include <androidjnimain.h>
-#include <QtCore/QJniObject>
+#include <QCoreApplication>
+#include <QtCore/QJniObject>
#include <QMimeDatabase>
#include <QMimeType>
#include <QRegularExpression>
@@ -129,6 +130,22 @@ void QAndroidPlatformFileDialogHelper::setInitialFileName(const QString &title)
extraTitle.object(), QJniObject::fromString(title).object());
}
+void QAndroidPlatformFileDialogHelper::setInitialDirectoryUri(const QString &directory)
+{
+ if (directory.isEmpty())
+ return;
+
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 26)
+ return;
+
+ const auto extraInitialUri = QJniObject::getStaticObjectField(
+ "android/provider/DocumentsContract", "EXTRA_INITIAL_URI", "Ljava/lang/String;");
+ m_intent.callObjectMethod("putExtra",
+ "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
+ extraInitialUri.object(),
+ QJniObject::fromString(directory).object());
+}
+
void QAndroidPlatformFileDialogHelper::setOpenableCategory()
{
const QJniObject CATEGORY_OPENABLE = QJniObject::getStaticObjectField(
@@ -211,11 +228,8 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT");
const QList<QUrl> selectedFiles = options()->initiallySelectedFiles();
- if (selectedFiles.size() > 0) {
- // TODO: The initial folder to show at the start should be handled by EXTRA_INITIAL_URI
- // Take only the file name.
+ if (selectedFiles.size() > 0)
setInitialFileName(selectedFiles.first().fileName());
- }
} else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
switch (options()->fileMode()) {
case QFileDialogOptions::FileMode::DirectoryOnly:
@@ -239,6 +253,8 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
setMimeTypes();
}
+ setInitialDirectoryUri(m_directory.toString());
+
QtAndroidPrivate::registerActivityResultListener(this);
m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
m_intent.object(), REQUEST_CODE);
@@ -252,6 +268,11 @@ void QAndroidPlatformFileDialogHelper::hide()
QtAndroidPrivate::unregisterActivityResultListener(this);
}
+void QAndroidPlatformFileDialogHelper::setDirectory(const QUrl &directory)
+{
+ m_directory = directory;
+}
+
void QAndroidPlatformFileDialogHelper::exec()
{
m_eventLoop.exec(QEventLoop::DialogExec);
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
index f573e72fef..6071821c38 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
@@ -68,8 +68,8 @@ public:
void setFilter() override {}
QList<QUrl> selectedFiles() const override { return m_selectedFile; }
void selectFile(const QUrl &) override {}
- QUrl directory() const override { return QUrl(); }
- void setDirectory(const QUrl &) override {}
+ QUrl directory() const override { return m_directory; }
+ void setDirectory(const QUrl &directory) override;
bool defaultNameFilterDisables() const override { return false; }
bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override;
@@ -77,12 +77,14 @@ private:
QJniObject getFileDialogIntent(const QString &intentType);
void takePersistableUriPermission(const QJniObject &uri);
void setInitialFileName(const QString &title);
+ void setInitialDirectoryUri(const QString &directory);
void setOpenableCategory();
void setAllowMultipleSelections(bool allowMultiple);
void setMimeTypes();
QEventLoop m_eventLoop;
QList<QUrl> m_selectedFile;
+ QUrl m_directory;
QJniObject m_intent;
const QJniObject m_activity;
};
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp
index d9f4c6ab88..7b2d981a2d 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp
+++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp
@@ -502,7 +502,7 @@ static const int androidLogicalDpi = 72;
QDpi QAndroidPlatformScreen::logicalDpi() const
{
- qreal lDpi = QtAndroid::scaledDensity() * androidLogicalDpi;
+ qreal lDpi = QtAndroid::pixelDensity() * androidLogicalDpi;
return QDpi(lDpi, lDpi);
}
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
index e69bafce7b..b85fa3a555 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
@@ -67,12 +67,6 @@ public Q_SLOTS:
void onAppFocusWindowChanged(QWindow *window);
private:
- /*
- Function to return the default background pixmap.
- Needed by QWizard in the Qt widget module.
- */
- Q_INVOKABLE QPixmap defaultBackgroundPixmapForQWizard();
-
Q_INVOKABLE void clearCurrentThreadCocoaEventDispatcherInterruptFlag();
static void registerDraggedTypes(const QStringList &types);
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
index 7dc5e9099d..91457d7235 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
@@ -114,35 +114,6 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QCocoaNativeInter
return nullptr;
}
-QPixmap QCocoaNativeInterface::defaultBackgroundPixmapForQWizard()
-{
- // Note: starting with macOS 10.14, the KeyboardSetupAssistant app bundle no
- // longer contains the "Background.png" image. This function then returns a
- // null pixmap.
- const int ExpectedImageWidth = 242;
- const int ExpectedImageHeight = 414;
- QCFType<CFArrayRef> urls = LSCopyApplicationURLsForBundleIdentifier(
- CFSTR("com.apple.KeyboardSetupAssistant"), nullptr);
- if (urls && CFArrayGetCount(urls) > 0) {
- CFURLRef url = (CFURLRef)CFArrayGetValueAtIndex(urls, 0);
- QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, url);
- if (bundle) {
- url = CFBundleCopyResourceURL(bundle, CFSTR("Background"), CFSTR("png"), nullptr);
- if (url) {
- QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithURL(url, nullptr);
- QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, nullptr);
- if (image) {
- int width = CGImageGetWidth(image);
- int height = CGImageGetHeight(image);
- if (width == ExpectedImageWidth && height == ExpectedImageHeight)
- return QPixmap::fromImage(qt_mac_toQImage(image));
- }
- }
- }
- }
- return QPixmap();
-}
-
void QCocoaNativeInterface::clearCurrentThreadCocoaEventDispatcherInterruptFlag()
{
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index d3f1a211de..fa7cff9be9 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -117,6 +117,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper);
@end
@interface QNSView (ComplexText) <NSTextInputClient>
+@property (readonly) QObject* focusObject;
@end
@implementation QNSView {
diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm
index 24d289f6d3..e84aa88bf6 100644
--- a/src/plugins/platforms/cocoa/qnsview_complextext.mm
+++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm
@@ -43,6 +43,17 @@
// ------------- Text insertion -------------
+- (QObject*)focusObject
+{
+ // The text input system may still hold a reference to our QNSView,
+ // even after QCocoaWindow has been destructed, delivering text input
+ // events to us, so we need to guard for this situation explicitly.
+ if (!m_platformWindow)
+ return nullptr;
+
+ return m_platformWindow->window()->focusObject();
+}
+
/*
Inserts the given text, potentially replacing existing text.
@@ -88,8 +99,7 @@
}
}
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (queryInputMethod(focusObject)) {
+ if (queryInputMethod(self.focusObject)) {
QInputMethodEvent inputMethodEvent;
const bool isAttributedString = [text isKindOfClass:NSAttributedString.class];
@@ -111,7 +121,7 @@
inputMethodEvent.setCommitString(commitString, replaceFrom, replaceLength);
}
- QCoreApplication::sendEvent(focusObject, &inputMethodEvent);
+ QCoreApplication::sendEvent(self.focusObject, &inputMethodEvent);
}
m_composingText.clear();
@@ -122,6 +132,9 @@
{
Q_UNUSED(sender);
+ if (!m_platformWindow)
+ return;
+
// Depending on the input method, pressing enter may
// result in simply dismissing the input method editor,
// without confirming the composition. In other cases
@@ -278,7 +291,7 @@
// Update the composition, now that we've computed the replacement range
m_composingText = preeditString;
- if (QObject *focusObject = m_platformWindow->window()->focusObject()) {
+ if (QObject *focusObject = self.focusObject) {
m_composingFocusObject = focusObject;
if (queryInputMethod(focusObject)) {
QInputMethodEvent event(preeditString, preeditAttributes);
@@ -320,8 +333,7 @@
*/
- (NSRange)markedRange
{
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (auto queryResult = queryInputMethod(focusObject, Qt::ImAbsolutePosition)) {
+ if (auto queryResult = queryInputMethod(self.focusObject, Qt::ImAbsolutePosition)) {
int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt();
// The cursor position as reflected by Qt::ImAbsolutePosition is not
@@ -356,7 +368,7 @@
<< "for focus object" << m_composingFocusObject;
if (!m_composingText.isEmpty()) {
- QObject *focusObject = m_platformWindow->window()->focusObject();
+ QObject *focusObject = self.focusObject;
if (queryInputMethod(focusObject)) {
QInputMethodEvent e;
e.setCommitString(m_composingText);
@@ -417,8 +429,7 @@
*/
- (NSRange)selectedRange
{
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (auto queryResult = queryInputMethod(focusObject,
+ if (auto queryResult = queryInputMethod(self.focusObject,
Qt::ImCursorPosition | Qt::ImAbsolutePosition | Qt::ImAnchorPosition)) {
// Unfortunately the Qt::InputMethodQuery values are all relative
@@ -465,8 +476,7 @@
*/
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (auto queryResult = queryInputMethod(focusObject,
+ if (auto queryResult = queryInputMethod(self.focusObject,
Qt::ImAbsolutePosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor)) {
const int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt();
const QString textBeforeCursor = queryResult.value(Qt::ImTextBeforeCursor).toString();
@@ -502,8 +512,8 @@
Q_UNUSED(range);
Q_UNUSED(actualRange);
- QWindow *window = m_platformWindow->window();
- if (queryInputMethod(window->focusObject())) {
+ QWindow *window = m_platformWindow ? m_platformWindow->window() : nullptr;
+ if (window && queryInputMethod(window->focusObject())) {
QRect cursorRect = qApp->inputMethod()->cursorRectangle().toRect();
cursorRect.moveBottomLeft(window->mapToGlobal(cursorRect.bottomLeft()));
return QCocoaScreen::mapToNative(cursorRect);
diff --git a/src/plugins/platforms/eglfs/api/qeglfscursor.cpp b/src/plugins/platforms/eglfs/api/qeglfscursor.cpp
index ee7749d57b..9c132dd1e6 100644
--- a/src/plugins/platforms/eglfs/api/qeglfscursor.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfscursor.cpp
@@ -44,6 +44,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
#include <QtCore/QFile>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonArray>
@@ -151,16 +152,18 @@ void QEglFSCursor::createShaderPrograms()
void QEglFSCursor::createCursorTexture(uint *texture, const QImage &image)
{
+ Q_ASSERT(QOpenGLContext::currentContext());
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
if (!*texture)
- glGenTextures(1, texture);
- glBindTexture(GL_TEXTURE_2D, *texture);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, image.width(), image.height(), 0 /* border */,
- GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
+ f->glGenTextures(1, texture);
+ f->glBindTexture(GL_TEXTURE_2D, *texture);
+ f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ f->glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, image.width(), image.height(), 0 /* border */,
+ GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
}
void QEglFSCursor::initCursorAtlas()
@@ -372,8 +375,8 @@ void QEglFSCursor::paintOnScreen()
// to deal with the changes we make.
struct StateSaver
{
- StateSaver() {
- f = QOpenGLContext::currentContext()->functions();
+ StateSaver(QOpenGLFunctions* func) {
+ f = func;
vaoHelper = QOpenGLVertexArrayObjectHelper::vertexArrayObjectHelperForContext(QOpenGLContext::currentContext());
static bool windowsChecked = false;
@@ -464,11 +467,9 @@ struct StateSaver
void QEglFSCursor::draw(const QRectF &r)
{
- StateSaver stateSaver;
-
- // one time initialization
- if (!QOpenGLFunctions::d_ptr)
- initializeOpenGLFunctions();
+ Q_ASSERT(QOpenGLContext::currentContext());
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ StateSaver stateSaver(f);
QEglFSCursorData &gfx = static_cast<QEglFSContext*>(QOpenGLContext::currentContext()->handle())->cursorData;
if (!gfx.program) {
@@ -517,13 +518,13 @@ void QEglFSCursor::draw(const QRectF &r)
s2, t1
};
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, cursorTexture);
+ f->glActiveTexture(GL_TEXTURE0);
+ f->glBindTexture(GL_TEXTURE_2D, cursorTexture);
if (stateSaver.vaoHelper->isValid())
stateSaver.vaoHelper->glBindVertexArray(0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+ f->glBindBuffer(GL_ARRAY_BUFFER, 0);
gfx.program->enableAttributeArray(0);
gfx.program->enableAttributeArray(1);
@@ -533,13 +534,13 @@ void QEglFSCursor::draw(const QRectF &r)
gfx.program->setUniformValue(gfx.textureEntry, 0);
gfx.program->setUniformValue(gfx.matEntry, m_rotationMatrix);
- glDisable(GL_CULL_FACE);
- glFrontFace(GL_CCW);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glDisable(GL_DEPTH_TEST); // disable depth testing to make sure cursor is always on top
+ f->glDisable(GL_CULL_FACE);
+ f->glFrontFace(GL_CCW);
+ f->glEnable(GL_BLEND);
+ f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ f->glDisable(GL_DEPTH_TEST); // disable depth testing to make sure cursor is always on top
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
gfx.program->disableAttributeArray(0);
gfx.program->disableAttributeArray(1);
diff --git a/src/plugins/platforms/eglfs/api/qeglfscursor_p.h b/src/plugins/platforms/eglfs/api/qeglfscursor_p.h
index ea1212ace8..ba35899348 100644
--- a/src/plugins/platforms/eglfs/api/qeglfscursor_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfscursor_p.h
@@ -56,7 +56,6 @@
#include <qpa/qplatformscreen.h>
#include <QtOpenGL/QOpenGLShaderProgram>
#include <QtGui/QMatrix4x4>
-#include <QtGui/QOpenGLFunctions>
#include <QtGui/private/qinputdevicemanager_p.h>
#include <QtCore/qlist.h>
@@ -94,7 +93,6 @@ struct QEglFSCursorData {
};
class Q_EGLFS_EXPORT QEglFSCursor : public QPlatformCursor
- , protected QOpenGLFunctions
{
Q_OBJECT
public:
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
index ba9b35f36f..07465641ee 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
@@ -387,7 +387,17 @@ void QEglFSKmsGbmScreen::flip()
return;
}
+ auto gbmRelease = qScopeGuard([this]{
+ m_flipPending = false;
+ gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next);
+ m_gbm_bo_next = nullptr;
+ });
+
FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next);
+ if (!fb) {
+ qWarning("FrameBuffer not available. Cannot flip");
+ return;
+ }
ensureModeSet(fb->fb);
const QKmsOutput &thisOutput(output());
@@ -419,9 +429,6 @@ void QEglFSKmsGbmScreen::flip()
this);
if (ret) {
qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
- m_flipPending = false;
- gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next);
- m_gbm_bo_next = nullptr;
return;
}
}
@@ -464,6 +471,8 @@ void QEglFSKmsGbmScreen::flip()
#if QT_CONFIG(drm_atomic)
device()->threadLocalAtomicCommit(this);
#endif
+
+ gbmRelease.dismiss();
}
void QEglFSKmsGbmScreen::flipFinished()
diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm
index 915fc4791f..1bafa33902 100644
--- a/src/plugins/platforms/ios/qiosscreen.mm
+++ b/src/plugins/platforms/ios/qiosscreen.mm
@@ -108,8 +108,8 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen)
+ (void)screenConnected:(NSNotification*)notification
{
- Q_ASSERT_X(QIOSIntegration::instance(), Q_FUNC_INFO,
- "Screen connected before QIOSIntegration creation");
+ if (!QIOSIntegration::instance())
+ return; // Will be added when QIOSIntegration is created
QWindowSystemInterface::handleScreenAdded(new QIOSScreen([notification object]));
}
diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm
index 9b76a8328a..bf5e1f1c3a 100644
--- a/src/plugins/platforms/ios/quiview_accessibility.mm
+++ b/src/plugins/platforms/ios/quiview_accessibility.mm
@@ -59,9 +59,11 @@
if (!iface || iface->state().invisible)
return;
- [self createAccessibleElement: iface];
for (int i = 0; i < iface->childCount(); ++i)
[self createAccessibleContainer: iface->child(i)];
+
+ // The container element must go last, so that it underlays all its children
+ [self createAccessibleElement:iface];
}
- (void)initAccessibility
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index dd63d4f3ac..83fecff43c 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -83,6 +83,7 @@
#include <QtCore/quuid.h>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h>
+#include <QtCore/private/qfactorycacheregistration_p.h>
#include <QtGui/private/qwindowsguieventdispatcher_p.h>
@@ -327,8 +328,12 @@ QWindowsContext::~QWindowsContext()
DestroyWindow(d->m_powerDummyWindow);
unregisterWindowClasses();
- if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE)
+ if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) {
+#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
+ detail::QWinRTFactoryCacheRegistration::clearAllCaches();
+#endif
OleUninitialize();
+ }
d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows.
if (d->m_displayContext)
@@ -1338,12 +1343,16 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::ExposeEvent:
return platformWindow->handleWmPaint(hwnd, message, wParam, lParam, result);
case QtWindows::NonClientMouseEvent:
- if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
+ if (!platformWindow->frameStrutEventsEnabled())
+ break;
+ if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
else
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
case QtWindows::NonClientPointerEvent:
- if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
+ if (!platformWindow->frameStrutEventsEnabled())
+ break;
+ if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
break;
case QtWindows::EnterSizeMoveEvent:
@@ -1587,9 +1596,13 @@ void QWindowsContext::handleExitSizeMove(QWindow *window)
? QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease;
for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) {
if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) {
- QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
- currentButtons, button, type,
- keyboardModifiers);
+ if (type == QEvent::NonClientAreaMouseButtonRelease) {
+ QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos,
+ currentButtons, button, type, keyboardModifiers);
+ } else {
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
+ currentButtons, button, type, keyboardModifiers);
+ }
}
}
if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index 1457e5048d..cd75b7c96f 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -282,7 +282,9 @@ private:
void QWindowsDialogThread::run()
{
qCDebug(lcQpaDialogs) << '>' << __FUNCTION__;
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
m_dialog->exec(m_owner);
+ CoUninitialize();
qCDebug(lcQpaDialogs) << '<' << __FUNCTION__;
}
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp
index 24d4cf3043..b47ded766f 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.cpp
+++ b/src/plugins/platforms/windows/qwindowsdrag.cpp
@@ -684,7 +684,8 @@ IDropTargetHelper* QWindowsDrag::dropHelper() {
// We process pointer messages for touch/pen and generate mouse input through SendInput() to trigger DoDragDrop()
static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect)
{
- HWND hwnd = ::GetFocus();
+ QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
+ const HWND hwnd = underMouse ? reinterpret_cast<HWND>(underMouse->winId()) : ::GetFocus();
bool starting = false;
for (;;) {
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index 9a8506d971..9c90b5497a 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -580,11 +580,14 @@ static int choosePixelFormat(HDC hdc,
iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB;
iAttributes[i++] = 1;
}
+ // must be the one before the last one
const int samples = format.samples();
const bool sampleBuffersRequested = samples > 1
&& testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers);
+ int sampleBuffersKeyPosition = 0;
int samplesValuePosition = 0;
if (sampleBuffersRequested) {
+ sampleBuffersKeyPosition = i;
iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB;
iAttributes[i++] = TRUE;
iAttributes[i++] = WGL_SAMPLES_ARB;
@@ -596,9 +599,9 @@ static int choosePixelFormat(HDC hdc,
}
// must be the last
bool srgbRequested = format.colorSpace() == QColorSpace::SRgb;
- int srgbValuePosition = 0;
+ int srgbCapableKeyPosition = 0;
if (srgbRequested) {
- srgbValuePosition = i;
+ srgbCapableKeyPosition = i;
iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT;
iAttributes[i++] = TRUE;
}
@@ -612,8 +615,9 @@ static int choosePixelFormat(HDC hdc,
&& numFormats >= 1;
if (valid || (!sampleBuffersRequested && !srgbRequested))
break;
+ // NB reductions must be done in reverse order (disable the last first, then move on to the one before that, etc.)
if (srgbRequested) {
- iAttributes[srgbValuePosition] = 0;
+ iAttributes[srgbCapableKeyPosition] = 0;
srgbRequested = false;
} else if (sampleBuffersRequested) {
if (iAttributes[samplesValuePosition] > 1) {
@@ -621,11 +625,8 @@ static int choosePixelFormat(HDC hdc,
} else if (iAttributes[samplesValuePosition] == 1) {
// Fallback in case it is unable to initialize with any
// samples to avoid falling back to the GDI path
- // NB: The sample attributes needs to be at the end for this
- // to work correctly
- iAttributes[samplesValuePosition - 1] = FALSE;
+ iAttributes[sampleBuffersKeyPosition] = 0;
iAttributes[samplesValuePosition] = 0;
- iAttributes[samplesValuePosition + 1] = 0;
} else {
break;
}
diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp
index 51efb095fc..768e243e12 100644
--- a/src/plugins/platforms/windows/qwindowsmime.cpp
+++ b/src/plugins/platforms/windows/qwindowsmime.cpp
@@ -480,7 +480,7 @@ public:
bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
{
int cf = getCf(formatetc);
- return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText();
+ return (cf == CF_UNICODETEXT || (cf == CF_TEXT && GetACP() != CP_UTF8)) && mimeData->hasText();
}
/*
@@ -581,7 +581,8 @@ QList<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const
QList<FORMATETC> formatics;
if (mimeType.startsWith(u"text/plain") && mimeData->hasText()) {
formatics += setCf(CF_UNICODETEXT);
- formatics += setCf(CF_TEXT);
+ if (GetACP() != CP_UTF8)
+ formatics += setCf(CF_TEXT);
}
return formatics;
}
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
index 0a96d5a0f5..14bd152621 100644
--- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
@@ -210,6 +210,7 @@ void QWindowsSystemTrayIcon::updateIcon(const QIcon &icon)
qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << icon << ')' << this;
if (icon.cacheKey() == m_icon.cacheKey())
return;
+ m_icon = icon;
const HICON hIconToDestroy = createIcon(icon);
if (ensureInstalled())
sendTrayMessage(NIM_MODIFY);
@@ -448,8 +449,15 @@ bool QWindowsSystemTrayIcon::winEvent(const MSG &message, long *result)
QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam));
break;
default:
- if (message.message == MYWM_TASKBARCREATED) // self-registered message id (tray crashed)
+ if (message.message == MYWM_TASKBARCREATED) {
+ // self-registered message id to handle that
+ // - screen resolution/DPR changed
+ const QIcon oldIcon = m_icon;
+ m_icon = QIcon(); // updateIcon is a no-op if the icon doesn't change
+ updateIcon(oldIcon);
+ // - or tray crashed
sendTrayMessage(NIM_ADD);
+ }
break;
}
return false;
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index bb639a59bc..c7951b3026 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -593,20 +593,22 @@ void QWindowsTheme::refreshFonts()
clearFonts();
if (!QGuiApplication::desktopSettingsAware())
return;
+
+ const int dpi = 96;
NONCLIENTMETRICS ncm;
- auto &screenManager = QWindowsContext::instance()->screenManager();
- QWindowsContext::nonClientMetricsForScreen(&ncm, screenManager.screens().value(0));
+ QWindowsContext::nonClientMetrics(&ncm, dpi);
qCDebug(lcQpaWindows) << __FUNCTION__ << ncm;
- const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont);
- const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
- const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont);
- const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont);
+
+ const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont, dpi);
+ const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont, dpi);
+ const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont, dpi);
+ const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont, dpi);
QFont fixedFont(QStringLiteral("Courier New"), messageBoxFont.pointSize());
fixedFont.setStyleHint(QFont::TypeWriter);
LOGFONT lfIconTitleFont;
- SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
- const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont);
+ SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi);
+ const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi);
m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont());
m_fonts[MenuFont] = new QFont(menuFont);
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index 3f6a63bb9e..1618e20b43 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -74,10 +74,13 @@ QT_BEGIN_NAMESPACE
using namespace QWindowsUiAutomation;
+QMutex QWindowsUiaMainProvider::m_mutex;
// Returns a cached instance of the provider for a specific acessible interface.
QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible)
{
+ QMutexLocker locker(&m_mutex);
+
if (!accessible)
return nullptr;
@@ -276,6 +279,8 @@ ULONG QWindowsUiaMainProvider::AddRef()
ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release()
{
+ QMutexLocker locker(&m_mutex);
+
if (!--m_ref) {
delete this;
return 0;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
index f7320388f7..8aadabd227 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
@@ -47,6 +47,7 @@
#include <QtCore/qpointer.h>
#include <QtCore/qsharedpointer.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qt_windows.h>
#include <QtGui/qaccessible.h>
@@ -98,6 +99,7 @@ public:
private:
QString automationIdForAccessible(const QAccessibleInterface *accessible);
ULONG m_ref;
+ static QMutex m_mutex;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
index c55e827a46..0483cf4263 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
@@ -75,7 +75,7 @@ void QWindowsUiaProviderCache::insert(QAccessible::Id id, QWindowsUiaBaseProvide
m_providerTable[id] = provider;
m_inverseTable[provider] = id;
// Connects the destroyed signal to our slot, to remove deleted objects from the cache.
- QObject::connect(provider, &QObject::destroyed, this, &QWindowsUiaProviderCache::objectDestroyed);
+ QObject::connect(provider, &QObject::destroyed, this, &QWindowsUiaProviderCache::objectDestroyed, Qt::DirectConnection);
}
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
index 81ece71f02..7ffd63a65d 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
@@ -431,14 +431,19 @@ QGLXContext::QGLXContext(Display *display, GLXContext context, void *visualInfo,
int numConfigs = 0;
static const int attribs[] = { GLX_FBCONFIG_ID, configId, None };
configs = glXChooseFBConfig(m_display, screenNumber, attribs, &numConfigs);
- if (!configs || numConfigs < 1) {
+ if (!configs) {
+ qWarning("QGLXContext: Failed to find config(invalid arguments for glXChooseFBConfig)");
+ return;
+ } else if (numConfigs < 1) {
qWarning("QGLXContext: Failed to find config");
+ XFree(configs);
return;
}
if (configs && numConfigs > 1) // this is suspicious so warn but let it continue
qWarning("QGLXContext: Multiple configs for FBConfig ID %d", configId);
m_config = configs[0];
+ XFree(configs);
}
Q_ASSERT(vinfo || m_config);
diff --git a/src/plugins/platforms/xcb/qt_xlib_wrapper.c b/src/plugins/platforms/xcb/qt_xlib_wrapper.c
index 3ff7120291..d45f468e8c 100644
--- a/src/plugins/platforms/xcb/qt_xlib_wrapper.c
+++ b/src/plugins/platforms/xcb/qt_xlib_wrapper.c
@@ -5,8 +5,7 @@
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:COMM$
-**
+** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
@@ -15,25 +14,26 @@
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
-** $QT_END_LICENSE$
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
**
+** $QT_END_LICENSE$
**
******************************************************************************/
diff --git a/src/plugins/platforms/xcb/qt_xlib_wrapper.h b/src/plugins/platforms/xcb/qt_xlib_wrapper.h
index 3f77068348..a656c2e50a 100644
--- a/src/plugins/platforms/xcb/qt_xlib_wrapper.h
+++ b/src/plugins/platforms/xcb/qt_xlib_wrapper.h
@@ -5,8 +5,7 @@
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:COMM$
-**
+** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
@@ -15,25 +14,26 @@
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
-** $QT_END_LICENSE$
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
-**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
**
+** $QT_END_LICENSE$
**
******************************************************************************/
diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp
index 1785513ac8..d485bcfd8a 100644
--- a/src/plugins/platforms/xcb/qxcbatom.cpp
+++ b/src/plugins/platforms/xcb/qxcbatom.cpp
@@ -90,6 +90,8 @@ static const char *xcb_atomnames = {
"_QT_CLOSE_CONNECTION\0"
+ "_QT_GET_TIMESTAMP\0"
+
"_MOTIF_WM_HINTS\0"
"DTWM_IS_RUNNING\0"
diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h
index 217c048ca4..ca6f8e7089 100644
--- a/src/plugins/platforms/xcb/qxcbatom.h
+++ b/src/plugins/platforms/xcb/qxcbatom.h
@@ -91,6 +91,8 @@ public:
// Qt/XCB specific
_QT_CLOSE_CONNECTION,
+ _QT_GET_TIMESTAMP,
+
_MOTIF_WM_HINTS,
DTWM_IS_RUNNING,
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index f4b829350a..22f7f5cde8 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -837,8 +837,8 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
{
// send a dummy event to myself to get the timestamp from X server.
xcb_window_t window = rootWindow();
- xcb_atom_t dummyAtom = atom(QXcbAtom::CLIP_TEMPORARY);
- xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, window, dummyAtom,
+ xcb_atom_t dummyAtom = atom(QXcbAtom::_QT_GET_TIMESTAMP);
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, dummyAtom,
XCB_ATOM_INTEGER, 32, 0, nullptr);
connection()->flush();
@@ -870,8 +870,6 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
xcb_timestamp_t timestamp = pn->time;
free(event);
- xcb_delete_property(xcb_connection(), window, dummyAtom);
-
return timestamp;
}
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 51a905ba75..10f260c292 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -287,11 +287,6 @@ void QXcbWindow::create()
return;
}
- QPlatformWindow::setGeometry(rect);
-
- if (platformScreen != currentScreen)
- QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen());
-
const QSize minimumSize = windowMinimumSize();
if (rect.width() > 0 || rect.height() > 0) {
rect.setWidth(qBound(1, rect.width(), XCOORD_MAX));
@@ -303,6 +298,11 @@ void QXcbWindow::create()
rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen()));
}
+ QPlatformWindow::setGeometry(rect);
+
+ if (platformScreen != currentScreen)
+ QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen());
+
xcb_window_t xcb_parent_id = platformScreen->root();
if (parent()) {
xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window();
@@ -1329,9 +1329,10 @@ void QXcbWindow::setWindowIcon(const QIcon &icon)
if (!icon_data.isEmpty()) {
// Ignore icon exceeding maximum xcb request length
- if (icon_data.size() > xcb_get_maximum_request_length(xcb_connection())) {
- qWarning("Ignoring window icon: Size %llu exceeds maximum xcb request length %u.",
- icon_data.size(), xcb_get_maximum_request_length(xcb_connection()));
+ if (quint64(icon_data.size()) > quint64(xcb_get_maximum_request_length(xcb_connection()))) {
+ qWarning() << "Ignoring window icon" << icon_data.size()
+ << "exceeds maximum xcb request length"
+ << xcb_get_maximum_request_length(xcb_connection());
return;
}
xcb_change_property(xcb_connection(),
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
index 6533288b2f..2385407030 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
@@ -122,7 +122,7 @@ QGtk3Theme::QGtk3Theme()
if (qEnvironmentVariableIsEmpty("XCURSOR_SIZE")) {
const int cursorSize = gtkSetting<gint>("gtk-cursor-theme-size");
if (cursorSize > 0)
- qputenv("XCURSOR_SIZE", QString::number(cursorSize).toUtf8());
+ qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize));
}
if (qEnvironmentVariableIsEmpty("XCURSOR_THEME")) {
const QString cursorTheme = gtkSetting("gtk-cursor-theme-name");
diff --git a/src/plugins/sqldrivers/.cmake.conf b/src/plugins/sqldrivers/.cmake.conf
index f6f7fed367..55ff681c65 100644
--- a/src/plugins/sqldrivers/.cmake.conf
+++ b/src/plugins/sqldrivers/.cmake.conf
@@ -1 +1 @@
-set(QT_REPO_MODULE_VERSION "6.2.7")
+set(QT_REPO_MODULE_VERSION "6.2.8")
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
index a2bf992b1a..30df01c85e 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
@@ -64,6 +64,11 @@ Q_DECLARE_METATYPE(MYSQL_RES*)
Q_DECLARE_METATYPE(MYSQL*)
Q_DECLARE_METATYPE(MYSQL_STMT*)
+// MYSQL_TYPE_JSON was introduced with MySQL 5.7.9
+#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID < 50709
+#define MYSQL_TYPE_JSON 245
+#endif
+
// MySQL above version 8 removed my_bool typedef while MariaDB kept it,
// by redefining it we can regain source compatibility.
using my_bool = decltype(mysql_stmt_bind_result(nullptr, nullptr));
@@ -262,6 +267,7 @@ static QMetaType qDecodeMYSQLType(int mysqltype, uint flags)
case FIELD_TYPE_MEDIUM_BLOB :
case FIELD_TYPE_LONG_BLOB :
case FIELD_TYPE_GEOMETRY :
+ case MYSQL_TYPE_JSON :
type = (flags & BINARY_FLAG) ? QMetaType::QByteArray : QMetaType::QString;
break;
default:
@@ -300,7 +306,8 @@ static bool qIsBlob(int t)
return t == MYSQL_TYPE_TINY_BLOB
|| t == MYSQL_TYPE_BLOB
|| t == MYSQL_TYPE_MEDIUM_BLOB
- || t == MYSQL_TYPE_LONG_BLOB;
+ || t == MYSQL_TYPE_LONG_BLOB
+ || t == MYSQL_TYPE_JSON;
}
static bool qIsInteger(int t)
diff --git a/src/plugins/sqldrivers/oci/qsql_oci.cpp b/src/plugins/sqldrivers/oci/qsql_oci.cpp
index 359fd33d2f..3d229f03a1 100644
--- a/src/plugins/sqldrivers/oci/qsql_oci.cpp
+++ b/src/plugins/sqldrivers/oci/qsql_oci.cpp
@@ -163,6 +163,15 @@ public:
~QOCIDateTime();
OCIDateTime *dateTime;
static QDateTime fromOCIDateTime(OCIEnv *env, OCIError *err, OCIDateTime *dt);
+ static QString toOffsetString(const QDateTime &dt)
+ {
+ const auto offset = dt.offsetFromUtc();
+ const auto offsetAbs = qAbs(offset) / 60;
+ return QString::asprintf("%c%02d:%02d",
+ offset >= 0 ? '+' : '-',
+ offsetAbs / 60,
+ offsetAbs % 60);
+ }
};
QOCIDateTime::QOCIDateTime(OCIEnv *env, OCIError *err, const QDateTime &dt)
@@ -172,8 +181,8 @@ QOCIDateTime::QOCIDateTime(OCIEnv *env, OCIError *err, const QDateTime &dt)
if (dt.isValid()) {
const QDate date = dt.date();
const QTime time = dt.time();
- // Zone in +hh:mm format (stripping UTC prefix from OffsetName)
- QString timeZone = dt.timeZone().displayName(dt, QTimeZone::OffsetName).mid(3);
+ // Zone in +hh:mm format
+ const QString timeZone = toOffsetString(dt);
const OraText *tz = reinterpret_cast<const OraText *>(timeZone.utf16());
OCIDateTimeConstruct(env, err, dateTime, date.year(), date.month(), date.day(), time.hour(),
time.minute(), time.second(), time.msec() * 1000000,
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
index a789ebf5a1..be764da13e 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
@@ -92,24 +92,39 @@ inline static QString fromSQLTCHAR(const QVarLengthArray<SQLTCHAR>& input, qsize
return result;
}
+template <size_t SizeOfChar = sizeof(SQLTCHAR)>
+void toSQLTCHARImpl(QVarLengthArray<SQLTCHAR> &result, const QString &input); // primary template undefined
+
+template <typename Container>
+void do_append(QVarLengthArray<SQLTCHAR> &result, const Container &c)
+{
+ result.append(reinterpret_cast<const SQLTCHAR *>(c.data()), c.size());
+}
+
+template <>
+void toSQLTCHARImpl<1>(QVarLengthArray<SQLTCHAR> &result, const QString &input)
+{
+ const auto u8 = input.toUtf8();
+ do_append(result, u8);
+}
+
+template <>
+void toSQLTCHARImpl<2>(QVarLengthArray<SQLTCHAR> &result, const QString &input)
+{
+ do_append(result, input);
+}
+
+template <>
+void toSQLTCHARImpl<4>(QVarLengthArray<SQLTCHAR> &result, const QString &input)
+{
+ const auto u32 = input.toUcs4();
+ do_append(result, u32);
+}
+
inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(const QString &input)
{
QVarLengthArray<SQLTCHAR> result;
- result.resize(input.size());
- switch(sizeof(SQLTCHAR)) {
- case 1:
- memcpy(result.data(), input.toUtf8().data(), input.size());
- break;
- case 2:
- memcpy(result.data(), input.unicode(), input.size() * 2);
- break;
- case 4:
- memcpy(result.data(), input.toUcs4().data(), input.size() * 4);
- break;
- default:
- qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle this.", int(sizeof(SQLTCHAR)));
- }
- result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't.
+ toSQLTCHARImpl(result, input);
return result;
}
@@ -730,10 +745,15 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMess
f.setAutoValue(isAutoValue(hStmt, i));
QVarLengthArray<SQLTCHAR> tableName(TABLENAMESIZE);
SQLSMALLINT tableNameLen;
- r = SQLColAttribute(hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName.data(),
- TABLENAMESIZE, &tableNameLen, 0);
+ r = SQLColAttribute(hStmt,
+ i + 1,
+ SQL_DESC_BASE_TABLE_NAME,
+ tableName.data(),
+ SQLSMALLINT(tableName.size() * sizeof(SQLTCHAR)), // SQLColAttribute needs/returns size in bytes
+ &tableNameLen,
+ 0);
if (r == SQL_SUCCESS)
- f.setTableName(fromSQLTCHAR(tableName, tableNameLen));
+ f.setTableName(fromSQLTCHAR(tableName, tableNameLen / sizeof(SQLTCHAR)));
return f;
}
@@ -763,6 +783,14 @@ QChar QODBCDriverPrivate::quoteChar()
return quote;
}
+static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr, const QString &val)
+{
+ auto encoded = toSQLTCHAR(val);
+ return SQLSetConnectAttr(handle, attr,
+ encoded.data(),
+ SQLINTEGER(encoded.size() * sizeof(SQLTCHAR))); // size in bytes
+}
+
bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
{
@@ -798,10 +826,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
v = val.toUInt();
r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) size_t(v), 0);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_CURRENT_CATALOG")) {
- val.utf16(); // 0 terminate
- r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
- toSQLTCHAR(val).data(),
- SQLINTEGER(val.length() * sizeof(SQLTCHAR)));
+ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG, val);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
if (val.toUpper() == QLatin1String("SQL_TRUE")) {
v = SQL_TRUE;
@@ -816,10 +841,7 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
v = val.toUInt();
r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER) size_t(v), 0);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) {
- val.utf16(); // 0 terminate
- r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
- toSQLTCHAR(val).data(),
- SQLINTEGER(val.length() * sizeof(SQLTCHAR)));
+ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val);
} else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
v = SQL_OPT_TRACE_OFF;
@@ -1022,9 +1044,12 @@ bool QODBCResult::reset (const QString& query)
return false;
}
- r = SQLExecDirect(d->hStmt,
- toSQLTCHAR(query).data(),
- (SQLINTEGER) query.length());
+ {
+ auto encoded = toSQLTCHAR(query);
+ r = SQLExecDirect(d->hStmt,
+ encoded.data(),
+ SQLINTEGER(encoded.size()));
+ }
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
"Unable to execute statement"), QSqlError::StatementError, d));
@@ -1373,9 +1398,12 @@ bool QODBCResult::prepare(const QString& query)
return false;
}
- r = SQLPrepare(d->hStmt,
- toSQLTCHAR(query).data(),
- (SQLINTEGER) query.length());
+ {
+ auto encoded = toSQLTCHAR(query);
+ r = SQLPrepare(d->hStmt,
+ encoded.data(),
+ SQLINTEGER(encoded.size()));
+ }
if (r != SQL_SUCCESS) {
setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
@@ -1403,7 +1431,7 @@ bool QODBCResult::exec()
SQLCloseCursor(d->hStmt);
QVariantList &values = boundValues();
- QByteArrayList tmpStorage(values.count(), QByteArray()); // holds temporary buffers
+ QByteArrayList tmpStorage(values.count(), QByteArray()); // targets for SQLBindParameter()
QVarLengthArray<SQLLEN, 32> indicators(values.count());
memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
@@ -1618,36 +1646,36 @@ bool QODBCResult::exec()
case QMetaType::QString:
if (d->unicode) {
QByteArray &ba = tmpStorage[i];
- QString str = val.toString();
+ {
+ const auto encoded = toSQLTCHAR(val.toString());
+ ba = QByteArray(reinterpret_cast<const char *>(encoded.data()),
+ encoded.size() * sizeof(SQLTCHAR));
+ }
+
if (*ind != SQL_NULL_DATA)
- *ind = str.length() * sizeof(SQLTCHAR);
- const qsizetype strSize = str.length() * sizeof(SQLTCHAR);
+ *ind = ba.size();
if (bindValueType(i) & QSql::Out) {
- const QVarLengthArray<SQLTCHAR> a(toSQLTCHAR(str));
- ba = QByteArray((const char *)a.constData(), int(a.size() * sizeof(SQLTCHAR)));
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[bindValueType(i) & QSql::InOut],
SQL_C_TCHAR,
- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
0, // god knows... don't change this!
0,
- ba.data(),
+ const_cast<char *>(ba.constData()), // don't detach
ba.size(),
ind);
break;
}
- ba = QByteArray(reinterpret_cast<const char *>(toSQLTCHAR(str).constData()),
- int(strSize));
r = SQLBindParameter(d->hStmt,
i + 1,
qParamType[bindValueType(i) & QSql::InOut],
SQL_C_TCHAR,
- strSize > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
- strSize,
+ ba.size() > 254 ? SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ ba.size(),
0,
- const_cast<char *>(ba.constData()),
+ const_cast<char *>(ba.constData()), // don't detach
ba.size(),
ind);
break;
@@ -1758,10 +1786,11 @@ bool QODBCResult::exec()
case QMetaType::QString:
if (d->unicode) {
if (bindValueType(i) & QSql::Out) {
- const QByteArray &first = tmpStorage.at(i);
- QVarLengthArray<SQLTCHAR> array;
- array.append((const SQLTCHAR *)first.constData(), first.size());
- values[i] = fromSQLTCHAR(array, first.size()/sizeof(SQLTCHAR));
+ const QByteArray &bytes = tmpStorage.at(i);
+ const auto strSize = bytes.size() / sizeof(SQLTCHAR);
+ QVarLengthArray<SQLTCHAR> string(strSize);
+ memcpy(string.data(), bytes.data(), strSize * sizeof(SQLTCHAR));
+ values[i] = fromSQLTCHAR(string);
}
break;
}
@@ -2008,14 +2037,16 @@ bool QODBCDriver::open(const QString & db,
SQLSMALLINT cb;
QVarLengthArray<SQLTCHAR> connOut(1024);
memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
- r = SQLDriverConnect(d->hDbc,
- NULL,
- toSQLTCHAR(connQStr).data(),
- (SQLSMALLINT)connQStr.length(),
- connOut.data(),
- 1024,
- &cb,
- /*SQL_DRIVER_NOPROMPT*/0);
+ {
+ auto encoded = toSQLTCHAR(connQStr);
+ r = SQLDriverConnect(d->hDbc,
+ nullptr,
+ encoded.data(), SQLSMALLINT(encoded.size()),
+ connOut.data(),
+ 1024,
+ &cb,
+ /*SQL_DRIVER_NOPROMPT*/0);
+ }
if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
@@ -2125,7 +2156,10 @@ void QODBCDriverPrivate::checkUnicode()
hDbc,
&hStmt);
- r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
+ {
+ auto encoded = toSQLTCHAR(QLatin1String("select 'test'"));
+ r = SQLExecDirect(hStmt, encoded.data(), SQLINTEGER(encoded.size()));
+ }
if (r == SQL_SUCCESS) {
r = SQLFetch(hStmt);
if (r == SQL_SUCCESS) {
@@ -2394,17 +2428,15 @@ QStringList QODBCDriver::tables(QSql::TableType type) const
if (tableType.isEmpty())
return tl;
- QString joinedTableTypeString = tableType.join(QLatin1Char(','));
+ {
+ auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
- r = SQLTables(hStmt,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- 0,
- toSQLTCHAR(joinedTableTypeString).data(),
- joinedTableTypeString.length() /* characters, not bytes */);
+ r = SQLTables(hStmt,
+ nullptr, 0,
+ nullptr, 0,
+ nullptr, 0,
+ joinedTableTypeString.data(), joinedTableTypeString.size());
+ }
if (r != SQL_SUCCESS)
qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute table list"), d);
@@ -2477,28 +2509,30 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
SQL_IS_UINTEGER);
- r = SQLPrimaryKeys(hStmt,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length() /* in characters, not in bytes */);
+ {
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLPrimaryKeys(hStmt,
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size());
+ }
// if the SQLPrimaryKeys() call does not succeed (e.g the driver
// does not support it) - try an alternative method to get hold of
// the primary index (e.g MS Access and FoxPro)
if (r != SQL_SUCCESS) {
- r = SQLSpecialColumns(hStmt,
- SQL_BEST_ROWID,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length(),
- SQL_SCOPE_CURROW,
- SQL_NULLABLE);
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLSpecialColumns(hStmt,
+ SQL_BEST_ROWID,
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size(),
+ SQL_SCOPE_CURROW,
+ SQL_NULLABLE);
if (r != SQL_SUCCESS) {
qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable to execute primary key list"), d);
@@ -2579,15 +2613,17 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
SQL_IS_UINTEGER);
- r = SQLColumns(hStmt,
- catalog.length() == 0 ? NULL : toSQLTCHAR(catalog).data(),
- catalog.length(),
- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
- schema.length(),
- toSQLTCHAR(table).data(),
- table.length(),
- NULL,
- 0);
+ {
+ auto c = toSQLTCHAR(catalog);
+ auto s = toSQLTCHAR(schema);
+ auto t = toSQLTCHAR(table);
+ r = SQLColumns(hStmt,
+ catalog.isEmpty() ? nullptr : c.data(), c.size(),
+ schema.isEmpty() ? nullptr : s.data(), s.size(),
+ t.data(), t.size(),
+ nullptr,
+ 0);
+ }
if (r != SQL_SUCCESS)
qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute column list"), d);
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
index ae0029d415..168ff49d64 100644
--- a/src/plugins/styles/mac/qmacstyle_mac.mm
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -2589,10 +2589,13 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
case PM_ToolBarFrameWidth:
ret = 1;
break;
- case PM_ScrollView_ScrollBarOverlap:
- ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay ?
- pixelMetric(PM_ScrollBarExtent, opt, widget) : 0;
+ case PM_ScrollView_ScrollBarOverlap: {
+ const QStyle *realStyle = widget ? widget->style() : proxy();
+ ret = realStyle->styleHint(SH_ScrollBar_Transient, opt, widget)
+ ? realStyle->pixelMetric(PM_ScrollBarExtent, opt, widget)
+ : 0;
break;
+ }
default:
ret = QCommonStyle::pixelMetric(metric, opt, widget);
break;
@@ -3320,7 +3323,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
NSButtonCell *triangleCell = static_cast<NSButtonCell *>(d->cocoaCell(cw));
[triangleCell setState:(opt->state & State_Open) ? NSControlStateValueOn : NSControlStateValueOff];
bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus);
- [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight];
+ [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleEmphasized : NSBackgroundStyleNormal];
d->setupNSGraphicsContext(cg, NO);
@@ -5256,7 +5259,8 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
const auto cocoaSize = d->effectiveAquaSizeConstrain(opt, widget);
const CGFloat maxExpandScale = expandedKnobWidths[cocoaSize] / knobWidths[cocoaSize];
- const bool isTransient = proxy()->styleHint(SH_ScrollBar_Transient, opt, widget);
+ const QStyle *realStyle = widget ? widget->style() : proxy();
+ const bool isTransient = realStyle->styleHint(SH_ScrollBar_Transient, opt, widget);
if (!isTransient)
d->stopAnimation(opt->styleObject);
bool wasActive = false;
diff --git a/src/plugins/tls/openssl/qtls_openssl.cpp b/src/plugins/tls/openssl/qtls_openssl.cpp
index 9d9e23cc42..b3870d39ac 100644
--- a/src/plugins/tls/openssl/qtls_openssl.cpp
+++ b/src/plugins/tls/openssl/qtls_openssl.cpp
@@ -524,7 +524,7 @@ void TlsCryptographOpenSSL::init(QSslSocket *qObj, QSslSocketPrivate *dObj)
handshakeInterrupted = false;
fetchAuthorityInformation = false;
- caToFetch = QSslCertificate{};
+ caToFetch.reset();
}
void TlsCryptographOpenSSL::checkSettingSslContext(QSharedPointer<QSslContext> tlsContext)
@@ -783,7 +783,7 @@ void TlsCryptographOpenSSL::enableHandshakeContinuation()
void TlsCryptographOpenSSL::cancelCAFetch()
{
fetchAuthorityInformation = false;
- caToFetch = QSslCertificate{};
+ caToFetch.reset();
}
void TlsCryptographOpenSSL::continueHandshake()
@@ -1834,7 +1834,7 @@ void TlsCryptographOpenSSL::caRootLoaded(QSslCertificate cert, QSslCertificate t
Q_ASSERT(q);
//Done, fetched already:
- caToFetch = QSslCertificate{};
+ caToFetch.reset();
if (fetchAuthorityInformation) {
if (!q->sslConfiguration().caCertificates().contains(trustedRoot))
diff --git a/src/plugins/tls/openssl/qtls_openssl_p.h b/src/plugins/tls/openssl/qtls_openssl_p.h
index 8728f27f78..9a1dbf0c66 100644
--- a/src/plugins/tls/openssl/qtls_openssl_p.h
+++ b/src/plugins/tls/openssl/qtls_openssl_p.h
@@ -156,7 +156,7 @@ private:
bool handshakeInterrupted = false;
bool fetchAuthorityInformation = false;
- QSslCertificate caToFetch;
+ std::optional<QSslCertificate> caToFetch;
bool inSetAndEmitError = false;
bool pendingFatalAlert = false;
diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
index 04a2725fe8..6c9f4319c4 100644
--- a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
+++ b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
@@ -64,7 +64,13 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl");
+#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
+constexpr auto DefaultWarningLevel = QtCriticalMsg;
+#else
+constexpr auto DefaultWarningLevel = QtDebugMsg;
+#endif
+
+Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl", DefaultWarningLevel);
Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex)
diff --git a/src/plugins/tls/securetransport/qtls_st.cpp b/src/plugins/tls/securetransport/qtls_st.cpp
index 97731e79e3..60b30e6f8c 100644
--- a/src/plugins/tls/securetransport/qtls_st.cpp
+++ b/src/plugins/tls/securetransport/qtls_st.cpp
@@ -407,6 +407,7 @@ void TlsCryptographSecureTransport::disconnectFromHost()
if (context) {
if (!shutdown) {
SSLClose(context);
+ context.reset(nullptr);
shutdown = true;
}
}
@@ -1148,8 +1149,6 @@ bool TlsCryptographSecureTransport::verifyPeerTrust()
QCFType<CFDataRef> certData = cert.toDer().toCFData();
if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData))
CFArrayAppendValue(certArray, secRef);
- else
- qCWarning(lcSecureTransport, "Failed to create SecCertificate from QSslCertificate");
}
SecTrustSetAnchorCertificates(trust, certArray);
diff --git a/src/plugins/tls/shared/qsslsocket_mac_shared.cpp b/src/plugins/tls/shared/qsslsocket_mac_shared.cpp
index cdecdee9b2..fba52fa1a8 100644
--- a/src/plugins/tls/shared/qsslsocket_mac_shared.cpp
+++ b/src/plugins/tls/shared/qsslsocket_mac_shared.cpp
@@ -42,6 +42,7 @@
#include <QtNetwork/qsslcertificate.h>
+#include <QtCore/qloggingcategory.h>
#include <QtCore/qglobal.h>
#include <QtCore/qdebug.h>
@@ -57,6 +58,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcX509, "qt.mac.shared.x509");
+
#ifdef Q_OS_MACOS
namespace {
@@ -110,6 +113,52 @@ bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain)
return false;
}
+bool canDERBeParsed(CFDataRef derData, const QSslCertificate &qtCert)
+{
+ // We are observing certificates, that while accepted when we copy them
+ // from the keychain(s), later give us 'Failed to create SslCertificate
+ // from QSslCertificate'. It's interesting to know at what step the failure
+ // occurred. Let's check it and skip it below if it's not valid.
+
+ auto checkDer = [](CFDataRef derData, const char *source)
+ {
+ Q_ASSERT(source);
+ Q_ASSERT(derData);
+
+ const auto cfLength = CFDataGetLength(derData);
+ if (cfLength <= 0) {
+ qCWarning(lcX509) << source << "returned faulty DER data with invalid length.";
+ return false;
+ }
+
+ QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, derData);
+ if (!secRef) {
+ qCWarning(lcX509) << source << "returned faulty DER data which cannot be parsed back.";
+ return false;
+ }
+ return true;
+ };
+
+ if (!checkDer(derData, "SecCertificateCopyData")) {
+ qCDebug(lcX509) << "Faulty QSslCertificate is:" << qtCert;// Just in case we managed to parse something.
+ return false;
+ }
+
+ // Generic parser failed?
+ if (qtCert.isNull()) {
+ qCWarning(lcX509, "QSslCertificate failed to parse DER");
+ return false;
+ }
+
+ const QCFType<CFDataRef> qtDerData = qtCert.toDer().toCFData();
+ if (!checkDer(qtDerData, "QSslCertificate")) {
+ qCWarning(lcX509) << "Faulty QSslCertificate is:" << qtCert;
+ return false;
+ }
+
+ return true;
+}
+
} // unnamed namespace
#endif // Q_OS_MACOS
@@ -130,8 +179,19 @@ QList<QSslCertificate> systemCaCertificates()
SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
if (isCaCertificateTrusted(cfCert, dom)) {
- if (derData)
- systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ if (derData) {
+ const auto newCert = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ if (!canDERBeParsed(derData, newCert)) {
+ // Last attempt to get some information about the certificate:
+ CFShow(cfCert);
+ continue;
+ }
+ systemCerts << newCert;
+ } else {
+ // "Returns NULL if the data passed in the certificate parameter
+ // is not a valid certificate object."
+ qCWarning(lcX509, "SecCertificateCopyData returned invalid DER data (nullptr).");
+ }
}
}
}