File indexing completed on 2024-04-14 04:51:52
0001 /** 0002 * SPDX-FileCopyrightText: 2021 Aleix Pol i Gonzalez <aleixpol@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "virtualmonitorplugin.h" 0008 0009 #include <KPluginFactory> 0010 0011 #include "plugin_virtualmonitor_debug.h" 0012 #include <QDesktopServices> 0013 #include <QGuiApplication> 0014 #include <QJsonArray> 0015 #include <QProcess> 0016 #include <QScreen> 0017 #include <QStandardPaths> 0018 0019 K_PLUGIN_CLASS_WITH_JSON(VirtualMonitorPlugin, "kdeconnect_virtualmonitor.json") 0020 #define QS QLatin1String 0021 0022 VirtualMonitorPlugin::~VirtualMonitorPlugin() 0023 { 0024 stop(); 0025 } 0026 0027 void VirtualMonitorPlugin::stop() 0028 { 0029 if (!m_process) 0030 return; 0031 0032 m_process->terminate(); 0033 if (!m_process->waitForFinished()) { 0034 m_process->kill(); 0035 m_process->waitForFinished(); 0036 } 0037 delete m_process; 0038 m_process = nullptr; 0039 } 0040 0041 void VirtualMonitorPlugin::connected() 0042 { 0043 auto screen = QGuiApplication::primaryScreen(); 0044 auto resolution = screen->size(); 0045 QString resolutionString = QString::number(resolution.width()) + QLatin1Char('x') + QString::number(resolution.height()); 0046 NetworkPacket np(PACKET_TYPE_VIRTUALMONITOR, 0047 { 0048 {QS("resolutions"), 0049 QJsonArray{QJsonObject{ 0050 {QS("resolution"), resolutionString}, 0051 {QS("scale"), screen->devicePixelRatio()}, 0052 }}}, 0053 }); 0054 sendPacket(np); 0055 } 0056 0057 void VirtualMonitorPlugin::receivePacket(const NetworkPacket &received) 0058 { 0059 if (received.type() == PACKET_TYPE_VIRTUALMONITOR_REQUEST && received.has(QS("url"))) { 0060 QUrl url(received.get<QString>(QS("url"))); 0061 if (!QDesktopServices::openUrl(url)) { 0062 qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Failed to open" << url.toDisplayString(); 0063 NetworkPacket np(PACKET_TYPE_VIRTUALMONITOR, {{QS("failed"), 0}}); 0064 sendPacket(np); 0065 } 0066 } else if (received.type() == PACKET_TYPE_VIRTUALMONITOR) { 0067 if (received.has(QS("resolutions"))) { 0068 m_remoteResolution = received.get<QJsonArray>(QS("resolutions")).first().toObject(); 0069 } 0070 if (received.has(QS("failed"))) { 0071 stop(); 0072 } 0073 } 0074 } 0075 0076 QString VirtualMonitorPlugin::dbusPath() const 0077 { 0078 // Don't offer the feature if krfb-virtualmonitor is not around 0079 if (QStandardPaths::findExecutable(QS("krfb-virtualmonitor")).isEmpty()) 0080 return {}; 0081 0082 return QLatin1String("/modules/kdeconnect/devices/%1/virtualmonitor").arg(device()->id()); 0083 } 0084 0085 bool VirtualMonitorPlugin::requestVirtualMonitor() 0086 { 0087 stop(); 0088 if (m_remoteResolution.isEmpty()) { 0089 qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Cannot start a request without a resolution"; 0090 return false; 0091 } 0092 0093 qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Requesting virtual display " << device()->name(); 0094 0095 QUuid uuid = QUuid::createUuid(); 0096 static int s_port = 5901; 0097 const QString port = QString::number(s_port++); 0098 0099 m_process = new QProcess(this); 0100 m_process->setProgram(QS("krfb-virtualmonitor")); 0101 const double scale = m_remoteResolution.value(QLatin1String("scale")).toDouble(); 0102 m_process->setArguments({QS("--name"), 0103 device()->name(), 0104 QS("--resolution"), 0105 m_remoteResolution.value(QLatin1String("resolution")).toString(), 0106 QS("--scale"), 0107 QString::number(scale), 0108 QS("--password"), 0109 uuid.toString(), 0110 QS("--port"), 0111 port}); 0112 connect(m_process, &QProcess::finished, this, [this](int exitCode, QProcess::ExitStatus exitStatus) { 0113 qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "virtual display finished with" << device()->name() << m_process->readAllStandardError(); 0114 0115 if (m_retries < 5 && (exitCode == 1 || exitStatus == QProcess::CrashExit)) { 0116 m_retries++; 0117 requestVirtualMonitor(); 0118 } else { 0119 m_process->deleteLater(); 0120 m_process = nullptr; 0121 } 0122 }); 0123 0124 m_process->start(); 0125 0126 if (!m_process->waitForStarted()) { 0127 qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Failed to start krfb-virtualmonitor" << m_process->error() << m_process->errorString(); 0128 return false; 0129 } 0130 0131 QUrl url; 0132 url.setScheme(QS("vnc")); 0133 url.setUserName(QS("user")); 0134 url.setPassword(uuid.toString()); 0135 url.setHost(device()->getLocalIpAddress().toString()); 0136 0137 NetworkPacket np(PACKET_TYPE_VIRTUALMONITOR_REQUEST, {{QS("url"), url.toEncoded()}}); 0138 sendPacket(np); 0139 return true; 0140 } 0141 0142 #include "moc_virtualmonitorplugin.cpp" 0143 #include "virtualmonitorplugin.moc"