Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qxdgdesktopportalfiletransfer.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 David Redondo <kde@david-redondo.de>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/QFile>
7#include <QtCore/QMap>
8#include <QtCore/QUrl>
9#include <QtCore/QVariant>
10#include <QtDBus/QDBusConnection>
11#include <QtDBus/QDBusMessage>
12#include <QtDBus/QDBusReply>
13#include <QtDBus/QDBusUnixFileDescriptor>
14
15#include <QtCore/private/qcore_unix_p.h>
16
17#include <unistd.h>
18#include <fcntl.h>
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24Q_LOGGING_CATEGORY(lcQpaPortalFileTransfer, "qt.qpa.portalfiletransfer")
25
27
28constexpr auto portalService = "org.freedesktop.portal.Documents"_L1;
29constexpr auto portalPath = "/org/freedesktop/portal/documents"_L1;
30constexpr auto fileTransferInterface = "org.freedesktop.portal.FileTransfer"_L1;
31constexpr int portalWaitTimeMs = 10;
32
34{
35 return "application/vnd.portal.filetransfer"_L1;
36}
37
38QString exportFiles(const QList<QUrl> &urls)
39{
40 auto startMessage = QDBusMessage::createMethodCall(portalService, portalPath,
41 fileTransferInterface, "StartTransfer"_L1);
42 const QVariantMap options{ { "writable"_L1, true }, { "autostop"_L1, true } };
43 startMessage.setArguments({ options });
44 const QDBusReply<QString> key = QDBusConnection::sessionBus().call(startMessage);
45 if (!key.isValid()) {
46 qCWarning(lcQpaPortalFileTransfer) << "failed to start transfer" << key.error();
47 return QString();
48 }
49 QList<QDBusUnixFileDescriptor> fds;
50 for (const auto &url : urls) {
51 if (int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH); fd >= 0)
52 fds.emplaceBack().giveFileDescriptor(fd);
53 else
54 qCInfo(lcQpaPortalFileTransfer) << "failed to open" << url;
55 }
56 auto nextChunk = [&fds](auto lastEnd) {
57 constexpr qsizetype chunkSize = 16;
58 const auto remaining = fds.cend() - lastEnd;
59 return std::pair{lastEnd, std::next(lastEnd, std::min(remaining, chunkSize))};
60 };
61 for (auto chunk = nextChunk(fds.cbegin()); chunk.first != fds.cend(); chunk = nextChunk(chunk.second)) {
62 auto addFilesMessage = QDBusMessage::createMethodCall(portalService, portalPath, fileTransferInterface, "AddFiles"_L1);
63 addFilesMessage.setArguments({key.value(), QVariant::fromValue(QList<QDBusUnixFileDescriptor>{chunk.first, chunk.second}), QVariantMap{}});
64 const QDBusReply<void> addFilesResult = QDBusConnection::sessionBus().call(addFilesMessage, QDBus::Block, portalWaitTimeMs);
65 if (!addFilesResult.isValid()) {
66 qCWarning(lcQpaPortalFileTransfer) << "failed to add files" << addFilesResult.error();
67 break;
68 }
69 }
70 return key.value();
71}
72
73QList<QString> retrieveFiles(const QString &key)
74{
75 qCDebug(lcQpaPortalFileTransfer) << "retrieving files for" << key;
76 auto message = QDBusMessage::createMethodCall(portalService, portalPath, fileTransferInterface, "RetrieveFiles"_L1);
77 message.setArguments({key, QVariantMap()});
78 const QDBusReply<QStringList> reply = QDBusConnection::sessionBus().call(message, QDBus::Block, portalWaitTimeMs);
79 if (!reply.isValid()) {
80 qCDebug(lcQpaPortalFileTransfer) << "error retrieving files" << reply.error();
81 return {};
82 }
83 return reply.value();
84}
85
86} // namespace QXdgDesktopPortalFileTransfer
87
88QT_END_NAMESPACE
QList< QString > retrieveFiles(const QString &key)
QString exportFiles(const QList< QUrl > &urls)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define O_PATH