File indexing completed on 2024-04-21 13:03:22

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 "PlasmaRecordMe.h"
0008 #include <QCoreApplication>
0009 #include <QDir>
0010 #include <QGuiApplication>
0011 #include <QLoggingCategory>
0012 #include <QProcess>
0013 #include <QQmlApplicationEngine>
0014 #include <QQmlContext>
0015 #include <QQuickItem>
0016 #include <QQuickView>
0017 #include <QRegularExpression>
0018 #include <QScreen>
0019 #include <QThread>
0020 #include <QTimer>
0021 
0022 #include <KWayland/Client/event_queue.h>
0023 #include <KWayland/Client/connection_thread.h>
0024 #include <KWayland/Client/output.h>
0025 #include <KWayland/Client/xdgoutput.h>
0026 #include <KWayland/Client/registry.h>
0027 #include <KWayland/Client/plasmawindowmanagement.h>
0028 
0029 using namespace KWayland::Client;
0030 
0031 PlasmaRecordMe::PlasmaRecordMe(Screencasting::CursorMode cursorMode, const QString &source, QObject *parent)
0032     : QObject(parent)
0033     , m_cursorMode(cursorMode)
0034     , m_durationTimer(new QTimer(this))
0035     , m_sourceName(source)
0036     , m_engine(new QQmlApplicationEngine(this))
0037 {
0038     m_engine->setInitialProperties({
0039         { QStringLiteral("app"), QVariant::fromValue<QObject *>(this) },
0040     });
0041     m_engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
0042 
0043     auto connection = ConnectionThread::fromApplication(this);
0044     if (!connection) {
0045         qWarning() << "Failed getting Wayland connection from QPA";
0046         QCoreApplication::exit(1);
0047         return;
0048     }
0049 
0050     m_durationTimer->setSingleShot(true);
0051     auto registry = new Registry(qApp);
0052     connect(registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, this, [this, registry] (quint32 name, quint32 version) {
0053         m_management = registry->createPlasmaWindowManagement(name, version, this);
0054         auto addWindow = [this] (KWayland::Client::PlasmaWindow *window) {
0055             const QRegularExpression rx(m_sourceName);
0056             const auto match = rx.match(window->appId());
0057             if (match.hasMatch()) {
0058                 auto f = [this, window] {
0059                     start(m_screencasting->createWindowStream(window, m_cursorMode));
0060                 };
0061                 qDebug() << "window" << window << window->uuid() << m_sourceName << m_screencasting;
0062                 if (m_screencasting)
0063                     f();
0064                 else
0065                     m_delayed << f;
0066             }
0067         };
0068         for (auto w : m_management->windows())
0069             addWindow(w);
0070         connect(m_management, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, addWindow);
0071     });
0072 
0073     connect(registry, &KWayland::Client::Registry::outputAnnounced, this, [this, registry] (quint32 name, quint32 version) {
0074             auto output = new KWayland::Client::Output(this);
0075             output->setup(registry->bindOutput(name, version));
0076 
0077             connect(output, &Output::changed, this, [this, output] {
0078                 const QRegularExpression rx(m_sourceName);
0079                 const auto match = rx.match(output->model());
0080                 if (match.hasMatch()) {
0081                     auto f = [this, output] {
0082                         start(m_screencasting->createOutputStream(output, m_cursorMode));
0083                     };
0084                     connect(this, &PlasmaRecordMe::cursorModeChanged, output, f);
0085                     if (m_screencasting)
0086                         f();
0087                     else
0088                         m_delayed << f;
0089                 }
0090             });
0091     });
0092 
0093     if (m_sourceName.isEmpty() || m_sourceName == QLatin1String("region")) {
0094         connect(this, &PlasmaRecordMe::workspaceChanged, this, [this] {
0095             delete m_workspaceStream;
0096             m_workspaceStream = m_screencasting->createRegionStream(m_workspace, 1, m_cursorMode);
0097             start(m_workspaceStream);
0098         });
0099     }
0100 
0101     registry->create(connection);
0102     registry->setup();
0103 
0104     m_screencasting = new Screencasting(this);
0105 
0106     bool ok = false;
0107     auto node = m_sourceName.toInt(&ok);
0108     if (ok) {
0109         const auto roots = m_engine->rootObjects();
0110         for (auto root : roots) {
0111             auto mo = root->metaObject();
0112             mo->invokeMethod(root,
0113                              "addStream",
0114                              Q_ARG(QVariant, QVariant::fromValue<int>(node)),
0115                              Q_ARG(QVariant, QStringLiteral("raw node %1").arg(node)),
0116                              Q_ARG(QVariant, 0));
0117         }
0118     }
0119 
0120     for (auto screen : qGuiApp->screens()) {
0121         m_workspace |= screen->geometry();
0122     }
0123     Q_EMIT workspaceChanged();
0124 }
0125 
0126 PlasmaRecordMe::~PlasmaRecordMe()
0127 {
0128 }
0129 
0130 void PlasmaRecordMe::start(ScreencastingStream *stream)
0131 {
0132     qDebug() << "start" << stream;
0133     connect(stream, &ScreencastingStream::failed, this, [this] (const QString &error) {
0134         qWarning() << "stream failed" << error;const auto roots = m_engine->rootObjects();
0135         for (auto root : roots) {
0136             auto mo = root->metaObject();
0137             mo->invokeMethod(root, "showPassiveNotification", Qt::QueuedConnection, Q_ARG(QVariant, QVariant(error))
0138                                                                                   , Q_ARG(QVariant, {})
0139                                                                                   , Q_ARG(QVariant, {})
0140                                                                                   , Q_ARG(QVariant, {})
0141             );
0142         }
0143     });
0144     connect(stream, &ScreencastingStream::closed, this, [this, stream] {
0145         auto nodeId = stream->property("nodeid").toInt();
0146         qDebug() << "bye bye" << stream << nodeId;
0147 
0148         const auto roots = m_engine->rootObjects();
0149         for (auto root : roots) {
0150             auto mo = root->metaObject();
0151             mo->invokeMethod(root, "removeStream", Qt::QueuedConnection, Q_ARG(QVariant, QVariant::fromValue<quint32>(nodeId)));
0152         }
0153     });
0154     connect(stream, &ScreencastingStream::created, this, [this, stream] (quint32 nodeId)
0155         {
0156             stream->setProperty("nodeid", nodeId);
0157             qDebug() << "starting..." << nodeId;
0158             const auto roots = m_engine->rootObjects();
0159             for (auto root : roots) {
0160                 auto mo = root->metaObject();
0161                 mo->invokeMethod(root,
0162                                  "addStream",
0163                                  Q_ARG(QVariant, QVariant::fromValue<quint32>(nodeId)),
0164                                  Q_ARG(QVariant, stream->objectName()),
0165                                  Q_ARG(QVariant, 0));
0166             }
0167         }
0168     );
0169     connect(this, &PlasmaRecordMe::cursorModeChanged, stream, &ScreencastingStream::closed);
0170 }
0171 
0172 void PlasmaRecordMe::setDuration(int duration)
0173 {
0174     m_durationTimer->setInterval(duration);
0175 }
0176 
0177 void PlasmaRecordMe::createVirtualMonitor()
0178 {
0179     m_screencasting->createVirtualMonitorStream(QStringLiteral("recordme"), {1920, 1080}, 1, m_cursorMode);
0180 }