File indexing completed on 2024-04-28 05:27:35
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, QList<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<QList<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 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 {QLatin1String("persist_mode"), uint(m_persistMode)}}; 0112 0113 if (!m_restoreToken.isEmpty()) { 0114 sourcesParameters[QLatin1String("restore_token")] = m_restoreToken; 0115 } 0116 0117 auto reply = iface->SelectSources(m_path, sourcesParameters); 0118 reply.waitForFinished(); 0119 0120 if (reply.isError()) { 0121 qWarning() << "Could not select sources" << reply.error(); 0122 exit(1); 0123 return; 0124 } 0125 qDebug() << "select sources done" << reply.value().path(); 0126 } 0127 } 0128 0129 void RecordMe::response(uint code, const QVariantMap& results) 0130 { 0131 if (code > 0) { 0132 qWarning() << "error!!!" << code << results; 0133 exit(666); 0134 return; 0135 } 0136 0137 if (results.contains(QLatin1String("restore_token"))) { 0138 qDebug() << "Restore token:" << results[QLatin1String("restore_token")].toString(); 0139 } 0140 0141 const auto streamsIt = results.constFind(QStringLiteral("streams")); 0142 if (streamsIt != results.constEnd()) { 0143 QList<Stream> streams; 0144 streamsIt->value<QDBusArgument>() >> streams; 0145 0146 handleStreams(streams); 0147 return; 0148 } 0149 0150 const auto handleIt = results.constFind(QStringLiteral("session_handle")); 0151 if (handleIt != results.constEnd()) { 0152 init(QDBusObjectPath(handleIt->toString())); 0153 return; 0154 } 0155 0156 qDebug() << "params" << results << code; 0157 if (results.isEmpty()) { 0158 start(); 0159 return; 0160 } 0161 } 0162 0163 void RecordMe::start() 0164 { 0165 const QVariantMap startParameters = { 0166 { QLatin1String("handle_token"), m_handleToken } 0167 }; 0168 0169 auto reply = iface->Start(m_path, QStringLiteral("org.freedesktop.RecordMe"), startParameters); 0170 reply.waitForFinished(); 0171 0172 if (reply.isError()) { 0173 qWarning() << "Could not start stream" << reply.error(); 0174 exit(1); 0175 return; 0176 } 0177 qDebug() << "started!" << reply.value().path(); 0178 } 0179 0180 void RecordMe::handleStreams(const QList<Stream> &streams) 0181 { 0182 const QVariantMap startParameters = { 0183 { QLatin1String("handle_token"), m_handleToken } 0184 }; 0185 0186 auto reply = iface->OpenPipeWireRemote(m_path, startParameters); 0187 reply.waitForFinished(); 0188 0189 if (reply.isError()) { 0190 qWarning() << "Could not start stream" << reply.error(); 0191 exit(1); 0192 return; 0193 } 0194 0195 const int fd = reply.value().takeFileDescriptor(); 0196 0197 const auto roots = m_engine->rootObjects(); 0198 for (const auto &stream : streams) { 0199 for (auto root : roots) { 0200 auto mo = root->metaObject(); 0201 qDebug() << "feeding..." << stream.id << fd; 0202 mo->invokeMethod(root, 0203 "addStream", 0204 Q_ARG(QVariant, QVariant::fromValue<quint32>(stream.id)), 0205 Q_ARG(QVariant, m_handleToken), 0206 Q_ARG(QVariant, fd), 0207 Q_ARG(QVariant, true)); 0208 } 0209 } 0210 } 0211 0212 void RecordMe::setPersistMode(PersistMode persistMode) 0213 { 0214 m_persistMode = persistMode; 0215 } 0216 0217 void RecordMe::setRestoreToken(const QString &restoreToken) 0218 { 0219 m_restoreToken = restoreToken; 0220 } 0221 0222 void RecordMe::setDuration(int duration) 0223 { 0224 m_durationTimer->setInterval(duration); 0225 } 0226 0227 #include "moc_RecordMe.cpp"