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 }