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 <unistd.h>
16#include <fcntl.h>
17
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21
22Q_LOGGING_CATEGORY(lcQpaPortalFileTransfer, "qt.qpa.portalfiletransfer")
23
25
26constexpr auto portalService = "org.freedesktop.portal.Documents"_L1;
27constexpr auto portalPath = "/org/freedesktop/portal/documents"_L1;
28constexpr auto fileTransferInterface = "org.freedesktop.portal.FileTransfer"_L1;
29constexpr int portalWaitTimeMs = 10;
30
32{
33 return "application/vnd.portal.filetransfer"_L1;
34}
35
36QString exportFiles(const QList<QUrl> &urls)
37{
38 auto startMessage = QDBusMessage::createMethodCall(portalService, portalPath,
39 fileTransferInterface, "StartTransfer"_L1);
40 const QVariantMap options{ { "writable"_L1, true }, { "autostop"_L1, true } };
41 startMessage.setArguments({ options });
42 const QDBusReply<QString> key = QDBusConnection::sessionBus().call(startMessage);
43 if (!key.isValid()) {
44 qCWarning(lcQpaPortalFileTransfer) << "failed to start transfer" << key.error();
45 return QString();
46 }
47 QList<QDBusUnixFileDescriptor> fds;
48 for (const auto &url : urls) {
49 if (int fd = open(QFile::encodeName(url.toLocalFile()), O_PATH | O_CLOEXEC); fd > 0)
50 fds.emplaceBack().giveFileDescriptor(fd);
51 else
52 qCInfo(lcQpaPortalFileTransfer) << "failed to open" << url;
53 }
54 auto nextChunk = [&fds](auto lastEnd) {
55 constexpr qsizetype chunkSize = 16;
56 const auto remaining = fds.cend() - lastEnd;
57 return std::pair{lastEnd, std::next(lastEnd, std::min(remaining, chunkSize))};
58 };
59 for (auto chunk = nextChunk(fds.cbegin()); chunk.first != fds.cend(); chunk = nextChunk(chunk.second)) {
60 auto addFilesMessage = QDBusMessage::createMethodCall(portalService, portalPath, fileTransferInterface, "AddFiles"_L1);
61 addFilesMessage.setArguments({key.value(), QVariant::fromValue(QList<QDBusUnixFileDescriptor>{chunk.first, chunk.second}), QVariantMap{}});
62 const QDBusReply<void> addFilesResult = QDBusConnection::sessionBus().call(addFilesMessage, QDBus::Block, portalWaitTimeMs);
63 if (!addFilesResult.isValid()) {
64 qCWarning(lcQpaPortalFileTransfer) << "failed to add files" << addFilesResult.error();
65 break;
66 }
67 }
68 return key.value();
69}
70
71QList<QString> retrieveFiles(const QString &key)
72{
73 qCDebug(lcQpaPortalFileTransfer) << "retrieving files for" << key;
74 auto message = QDBusMessage::createMethodCall(portalService, portalPath, fileTransferInterface, "RetrieveFiles"_L1);
75 message.setArguments({key, QVariantMap()});
76 const QDBusReply<QStringList> reply = QDBusConnection::sessionBus().call(message, QDBus::Block, portalWaitTimeMs);
77 if (!reply.isValid()) {
78 qCDebug(lcQpaPortalFileTransfer) << "error retrieving files" << reply.error();
79 return {};
80 }
81 const QStringList paths = reply.value();
82 return paths;
83}
84
85} // namespace QXdgDesktopPortalFileTransfer
86
87QT_END_NAMESPACE
QList< QString > retrieveFiles(const QString &key)
QString exportFiles(const QList< QUrl > &urls)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define O_PATH