File indexing completed on 2024-04-21 16:13:08

0001 /*
0002     SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "RecordMe.h"
0008 #include <QLoggingCategory>
0009 #include <QQmlApplicationEngine>
0010 #include <QQmlContext>
0011 #include <QTimer>
0012 
0013 #include "xdp_dbus_screencast_interface.h"
0014 
0015 Q_DECLARE_METATYPE(Stream)
0016 
0017 QDebug operator<<(QDebug debug, const Stream& plug)
0018 {
0019     QDebugStateSaver saver(debug);
0020     debug.nospace() << "Stream(id: " << plug.id << ", opts: " << plug.opts << ')';
0021     return debug;
0022 }
0023 
0024 
0025 const QDBusArgument &operator<<(const QDBusArgument &argument, const Stream &/*stream*/)
0026 {
0027     argument.beginStructure();
0028 //     argument << stream.id << stream.opts;
0029     argument.endStructure();
0030     return argument;
0031 }
0032 
0033 const QDBusArgument &operator>>(const QDBusArgument &argument, Stream &stream)
0034 {
0035     argument.beginStructure();
0036     argument >> stream.id >> stream.opts;
0037     argument.endStructure();
0038     return argument;
0039 }
0040 
0041 const QDBusArgument &operator>>(const QDBusArgument &argument, QVector<Stream> &stream)
0042 {
0043     argument.beginArray();
0044     while ( !argument.atEnd() ) {
0045         Stream element;
0046         argument >> element;
0047         stream.append( element );
0048     }
0049     argument.endArray();
0050     return argument;
0051 }
0052 
0053 RecordMe::RecordMe(QObject* parent)
0054     : QObject(parent)
0055     , iface(new OrgFreedesktopPortalScreenCastInterface(
0056         QLatin1String("org.freedesktop.portal.Desktop"), QLatin1String("/org/freedesktop/portal/desktop"), QDBusConnection::sessionBus(), this))
0057     , m_durationTimer(new QTimer(this))
0058     , m_handleToken(QStringLiteral("RecordMe%1").arg(QRandomGenerator::global()->generate()))
0059     , m_engine(new QQmlApplicationEngine(this))
0060 {
0061     m_engine->rootContext()->setContextProperty(QStringLiteral("app"), this);
0062     m_engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
0063 
0064     // create session
0065     const auto sessionParameters = QVariantMap {
0066         { QLatin1String("session_handle_token"), m_handleToken },
0067         { QLatin1String("handle_token"), m_handleToken }
0068     };
0069     auto sessionReply = iface->CreateSession(sessionParameters);
0070     sessionReply.waitForFinished();
0071     if (!sessionReply.isValid()) {
0072         qWarning("Couldn't initialize the remote control session");
0073         exit(1);
0074         return;
0075     }
0076 
0077     const bool ret = QDBusConnection::sessionBus().connect(QString(),
0078                                                            sessionReply.value().path(),
0079                                                            QLatin1String("org.freedesktop.portal.Request"),
0080                                                            QLatin1String("Response"),
0081                                                            this,
0082                                                            SLOT(response(uint, QVariantMap)));
0083     if (!ret) {
0084         qWarning() << "failed to create session";
0085         exit(2);
0086         return;
0087     }
0088 
0089     qDBusRegisterMetaType<Stream>();
0090     qDBusRegisterMetaType<QVector<Stream>>();
0091 
0092     m_durationTimer->setSingleShot(true);
0093 }
0094 
0095 RecordMe::~RecordMe() = default;
0096 
0097 void RecordMe::init(const QDBusObjectPath& path)
0098 {
0099     m_path = path;
0100     {
0101         uint32_t cursor_mode;
0102         if (iface->availableCursorModes() & Metadata) {
0103             cursor_mode = Metadata;
0104         } else {
0105             cursor_mode = Hidden;
0106         }
0107         const QVariantMap sourcesParameters = {{QLatin1String("handle_token"), m_handleToken},
0108                                                {QLatin1String("types"), iface->availableSourceTypes()},
0109                                                {QLatin1String("multiple"), false}, // for now?
0110                                                {QLatin1String("cursor_mode"), uint(cursor_mode)}};
0111 
0112         auto reply = iface->SelectSources(m_path, sourcesParameters);
0113         reply.waitForFinished();
0114 
0115         if (reply.isError()) {
0116             qWarning() << "Could not select sources" << reply.error();
0117             exit(1);
0118             return;
0119         }
0120         qDebug() << "select sources done" << reply.value().path();
0121     }
0122 }
0123 
0124 void RecordMe::response(uint code, const QVariantMap& results)
0125 {
0126     if (code > 0) {
0127         qWarning() << "error!!!" << code << results;
0128         exit(666);
0129         return;
0130     }
0131 
0132     const auto streamsIt = results.constFind(QStringLiteral("streams"));
0133     if (streamsIt != results.constEnd()) {
0134         QVector<Stream> streams;
0135         streamsIt->value<QDBusArgument>() >> streams;
0136 
0137         handleStreams(streams);
0138         return;
0139     }
0140 
0141     const auto handleIt = results.constFind(QStringLiteral("session_handle"));
0142     if (handleIt != results.constEnd()) {
0143         init(QDBusObjectPath(handleIt->toString()));
0144         return;
0145     }
0146 
0147     qDebug() << "params" << results << code;
0148     if (results.isEmpty()) {
0149         start();
0150         return;
0151     }
0152 }
0153 
0154 void RecordMe::start()
0155 {
0156     const QVariantMap startParameters = {
0157         { QLatin1String("handle_token"), m_handleToken }
0158     };
0159 
0160     auto reply = iface->Start(m_path, QStringLiteral("org.freedesktop.RecordMe"), startParameters);
0161     reply.waitForFinished();
0162 
0163     if (reply.isError()) {
0164         qWarning() << "Could not start stream" << reply.error();
0165         exit(1);
0166         return;
0167     }
0168     qDebug() << "started!" << reply.value().path();
0169 }
0170 
0171 void RecordMe::handleStreams(const QVector<Stream> &streams)
0172 {
0173     const QVariantMap startParameters = {
0174         { QLatin1String("handle_token"), m_handleToken }
0175     };
0176 
0177     auto reply = iface->OpenPipeWireRemote(m_path, startParameters);
0178     reply.waitForFinished();
0179 
0180     if (reply.isError()) {
0181         qWarning() << "Could not start stream" << reply.error();
0182         exit(1);
0183         return;
0184     }
0185 
0186     const int fd = reply.value().takeFileDescriptor();
0187 
0188     const auto roots = m_engine->rootObjects();
0189     for (const auto &stream : streams) {
0190         for (auto root : roots) {
0191             auto mo = root->metaObject();
0192             qDebug() << "feeding..." << stream.id << fd;
0193             mo->invokeMethod(root, "addStream", Q_ARG(QVariant, QVariant::fromValue<quint32>(stream.id)), Q_ARG(QVariant, m_handleToken), Q_ARG(QVariant, fd));
0194         }
0195     }
0196 }
0197 
0198 void RecordMe::setDuration(int duration)
0199 {
0200     m_durationTimer->setInterval(duration);
0201 }