File indexing completed on 2024-04-28 05:27:34
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 #include <QUuid> 0022 0023 0024 PlasmaRecordMe::PlasmaRecordMe(Screencasting::CursorMode cursorMode, const QStringList &sources, bool doSelection, QObject *parent) 0025 : QObject(parent) 0026 , m_cursorMode(cursorMode) 0027 , m_durationTimer(new QTimer(this)) 0028 , m_sources(sources) 0029 , m_engine(new QQmlApplicationEngine(this)) 0030 { 0031 connect(m_engine, &QQmlEngine::quit, qGuiApp, &QCoreApplication::quit); 0032 m_engine->setInitialProperties({ 0033 { QStringLiteral("app"), QVariant::fromValue<QObject *>(this) }, 0034 }); 0035 m_engine->load(QUrl(QStringLiteral("qrc:/main.qml"))); 0036 0037 m_screencasting = new Screencasting(this); 0038 0039 m_durationTimer->setSingleShot(true); 0040 0041 for (auto screen : qGuiApp->screens()) { 0042 addScreen(screen); 0043 } 0044 connect(qGuiApp, &QGuiApplication::screenAdded, this, &PlasmaRecordMe::addScreen); 0045 0046 if (m_sources.isEmpty() || m_sources.contains(QLatin1String("region"))) { 0047 connect(this, &PlasmaRecordMe::workspaceChanged, this, [this] { 0048 qDeleteAll(m_workspaceStreams); 0049 m_workspaceStreams += m_screencasting->createRegionStream(m_workspace, 1, m_cursorMode); 0050 m_workspaceStreams += m_screencasting->createRegionStream(m_workspace, 1, m_cursorMode); 0051 start(m_workspaceStreams[0], true); 0052 start(m_workspaceStreams[1], false); 0053 }); 0054 } 0055 0056 if (doSelection) { 0057 requestSelection(); 0058 } 0059 0060 for (const auto &source : m_sources) { 0061 bool ok = false; 0062 auto node = source.toInt(&ok); 0063 if (ok) { 0064 startNode(node); 0065 } 0066 } 0067 0068 for (auto screen : qGuiApp->screens()) { 0069 m_workspace |= screen->geometry(); 0070 } 0071 Q_EMIT workspaceChanged(); 0072 } 0073 0074 PlasmaRecordMe::~PlasmaRecordMe() 0075 { 0076 } 0077 0078 bool PlasmaRecordMe::matches(const QString &value) 0079 { 0080 if (m_sources.isEmpty()) { 0081 return true; 0082 } 0083 0084 for (const auto &source : m_sources) { 0085 const QRegularExpression rx(source); 0086 if (rx.match(value).hasMatch()) { 0087 return true; 0088 } 0089 } 0090 return false; 0091 } 0092 0093 void PlasmaRecordMe::addWindow(const QVariantList &uuid, const QString &appId) 0094 { 0095 if (matches(appId) && uuid.size() > 0) { 0096 QString uuidString = uuid.at(0).value<QString>(); 0097 auto stream = m_screencasting->createWindowStream(uuidString, m_cursorMode); 0098 stream->setObjectName(appId); 0099 start(stream, true); 0100 stream = m_screencasting->createWindowStream(uuidString, m_cursorMode); 0101 stream->setObjectName(appId); 0102 start(stream, false); 0103 } 0104 } 0105 0106 void PlasmaRecordMe::addStream(int nodeid, const QString &displayText, int fd, bool allowDmaBuf) 0107 { 0108 const auto roots = m_engine->rootObjects(); 0109 for (auto root : roots) { 0110 auto mo = root->metaObject(); 0111 mo->invokeMethod(root, 0112 "addStream", 0113 Q_ARG(QVariant, QVariant::fromValue<int>(nodeid)), 0114 Q_ARG(QVariant, displayText), 0115 Q_ARG(QVariant, fd), 0116 Q_ARG(QVariant, allowDmaBuf)); 0117 } 0118 } 0119 0120 void PlasmaRecordMe::startNode(int node) 0121 { 0122 addStream(node, QStringLiteral("raw node %1").arg(node), 0, true); 0123 } 0124 0125 void PlasmaRecordMe::addScreen(QScreen *screen) 0126 { 0127 auto f = [this, screen] { 0128 start(m_screencasting->createOutputStream(screen, m_cursorMode), false); 0129 start(m_screencasting->createOutputStream(screen, m_cursorMode), true); 0130 }; 0131 if (matches(screen->name())) { 0132 connect(this, &PlasmaRecordMe::cursorModeChanged, screen, f); 0133 f(); 0134 } else if (matches(screen->model())) { 0135 connect(this, &PlasmaRecordMe::cursorModeChanged, screen, f); 0136 f(); 0137 } 0138 } 0139 0140 void PlasmaRecordMe::start(ScreencastingStream *stream, bool allowDmaBuf) 0141 { 0142 qDebug() << "start" << stream; 0143 connect(stream, &ScreencastingStream::failed, this, [this] (const QString &error) { 0144 qWarning() << "stream failed" << error;const auto roots = m_engine->rootObjects(); 0145 for (auto root : roots) { 0146 auto mo = root->metaObject(); 0147 mo->invokeMethod(root, "showPassiveNotification", Qt::QueuedConnection, Q_ARG(QVariant, QVariant(error)) 0148 , Q_ARG(QVariant, {}) 0149 , Q_ARG(QVariant, {}) 0150 , Q_ARG(QVariant, {}) 0151 ); 0152 } 0153 }); 0154 connect(stream, &ScreencastingStream::closed, this, [this, stream] { 0155 auto nodeId = stream->property("nodeid").toInt(); 0156 qDebug() << "bye bye" << stream << nodeId; 0157 0158 const auto roots = m_engine->rootObjects(); 0159 for (auto root : roots) { 0160 auto mo = root->metaObject(); 0161 mo->invokeMethod(root, "removeStream", Qt::QueuedConnection, Q_ARG(QVariant, QVariant::fromValue<quint32>(nodeId))); 0162 } 0163 }); 0164 connect(stream, &ScreencastingStream::created, this, [this, stream, allowDmaBuf](quint32 nodeId) { 0165 stream->setProperty("nodeid", nodeId); 0166 qDebug() << "starting..." << nodeId; 0167 addStream(nodeId, stream->objectName(), 0, allowDmaBuf); 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 } 0181 0182 void PlasmaRecordMe::setRegionPressed(const QString &screenName, int x, int y) 0183 { 0184 const auto screens = QGuiApplication::screens(); 0185 auto screenIt = std::find_if(screens.begin(), screens.end(), [&screenName](auto screen) { 0186 return screen->name() == screenName; 0187 }); 0188 m_region.setTopLeft((*screenIt)->geometry().topLeft() + QPoint{x, y}); 0189 } 0190 0191 void PlasmaRecordMe::setRegionMoved(const QString &screenName, int x, int y) 0192 { 0193 const auto screens = QGuiApplication::screens(); 0194 auto screenIt = std::find_if(screens.begin(), screens.end(), [&screenName](auto screen) { 0195 return screen->name() == screenName; 0196 }); 0197 m_region.setBottomRight((*screenIt)->geometry().topLeft() + QPoint{x, y}); 0198 Q_EMIT regionChanged(m_region); 0199 } 0200 0201 void PlasmaRecordMe::setRegionReleased(const QString &screenName, int x, int y) 0202 { 0203 setRegionMoved(screenName, x, y); 0204 m_region = m_region.normalized(); 0205 Q_EMIT regionFinal(m_region); 0206 } 0207 0208 void PlasmaRecordMe::requestSelection() 0209 { 0210 for (auto *screen : qApp->screens()) { 0211 auto view = new QQuickView(m_engine, nullptr); 0212 view->setScreen(screen); 0213 view->setInitialProperties({{QStringLiteral("app"), QVariant::fromValue<QObject *>(this)}}); 0214 view->setSource(QUrl(QStringLiteral("qrc:/RegionSelector.qml"))); 0215 view->setColor(Qt::transparent); 0216 view->showFullScreen(); 0217 connect(this, &PlasmaRecordMe::regionFinal, view, &QQuickView::deleteLater); 0218 } 0219 0220 connect(this, &PlasmaRecordMe::regionFinal, this, [this](const QRect ®ion) { 0221 for (auto stream : m_regionStreams) { 0222 Q_EMIT stream->closed(); 0223 } 0224 qDeleteAll(m_regionStreams); 0225 m_regionStreams += m_screencasting->createRegionStream(region, 1, m_cursorMode); 0226 m_regionStreams += m_screencasting->createRegionStream(region, 1, m_cursorMode); 0227 start(m_regionStreams[0], true); 0228 start(m_regionStreams[1], false); 0229 }); 0230 } 0231 0232 QRect PlasmaRecordMe::region() const 0233 { 0234 return m_region.normalized(); 0235 } 0236 0237 #include "moc_PlasmaRecordMe.cpp"