virtualmonitor: Allow using krdpserver to extend to another device
This commit is contained in:
parent
0e389f0826
commit
a2ec47b2a6
2 changed files with 106 additions and 5 deletions
|
@ -46,13 +46,33 @@ void VirtualMonitorPlugin::stop()
|
||||||
m_process = nullptr;
|
m_process = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool krdpHasVirtualMonitor()
|
||||||
|
{
|
||||||
|
static std::optional<bool> hasIt;
|
||||||
|
if (!hasIt.has_value()) {
|
||||||
|
QProcess p;
|
||||||
|
p.start(QS("krdpserver"), {QS("--help")});
|
||||||
|
if (!p.waitForStarted()) {
|
||||||
|
*hasIt = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!p.waitForFinished(1000)) {
|
||||||
|
*hasIt = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hasIt = p.readAllStandardOutput().contains("--virtual-monitor");
|
||||||
|
}
|
||||||
|
return *hasIt;
|
||||||
|
}
|
||||||
|
|
||||||
void VirtualMonitorPlugin::connected()
|
void VirtualMonitorPlugin::connected()
|
||||||
{
|
{
|
||||||
// Get local capabilities
|
// Get local capabilities
|
||||||
// We test again, since the user may have installed the necessary packages in the meantime
|
// We test again, since the user may have installed the necessary packages in the meantime
|
||||||
m_capabilitiesLocal.vncClient = checkVncClient();
|
m_capabilitiesLocal.vncClient = checkVncClient();
|
||||||
m_capabilitiesLocal.virtualMonitor = !QStandardPaths::findExecutable(QS("krfb-virtualmonitor")).isEmpty();
|
m_capabilitiesLocal.rdpClient = checkRdpClient();
|
||||||
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Local device supports VNC:" << m_capabilitiesLocal.vncClient;
|
m_capabilitiesLocal.virtualMonitor = !QStandardPaths::findExecutable(QS("krfb-virtualmonitor")).isEmpty() || krdpHasVirtualMonitor();
|
||||||
|
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Local device supports VNC:" << m_capabilitiesLocal.vncClient << "RDP: " << m_capabilitiesLocal.rdpClient;
|
||||||
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Local device supports Virtual Monitor:" << m_capabilitiesLocal.virtualMonitor;
|
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Local device supports Virtual Monitor:" << m_capabilitiesLocal.virtualMonitor;
|
||||||
|
|
||||||
auto screen = QGuiApplication::primaryScreen();
|
auto screen = QGuiApplication::primaryScreen();
|
||||||
|
@ -66,6 +86,7 @@ void VirtualMonitorPlugin::connected()
|
||||||
{QS("scale"), screen->devicePixelRatio()},
|
{QS("scale"), screen->devicePixelRatio()},
|
||||||
}}},
|
}}},
|
||||||
{QS("supports_vnc"), m_capabilitiesLocal.vncClient},
|
{QS("supports_vnc"), m_capabilitiesLocal.vncClient},
|
||||||
|
{QS("supports_rdp"), m_capabilitiesLocal.rdpClient},
|
||||||
{QS("supports_virt_mon"), m_capabilitiesLocal.virtualMonitor},
|
{QS("supports_virt_mon"), m_capabilitiesLocal.virtualMonitor},
|
||||||
});
|
});
|
||||||
sendPacket(np);
|
sendPacket(np);
|
||||||
|
@ -88,7 +109,11 @@ void VirtualMonitorPlugin::receivePacket(const NetworkPacket &received)
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl url;
|
QUrl url;
|
||||||
url.setScheme(received.get<QString>(QS("protocol"), QS("vnc")));
|
if (m_capabilitiesRemote.rdpClient) {
|
||||||
|
url.setScheme(received.get<QString>(QS("protocol"), QS("rdp")));
|
||||||
|
} else {
|
||||||
|
url.setScheme(received.get<QString>(QS("protocol"), QS("vnc")));
|
||||||
|
}
|
||||||
url.setUserName(received.get<QString>(QS("username"), QS("user")));
|
url.setUserName(received.get<QString>(QS("username"), QS("user")));
|
||||||
url.setPassword(received.get<QString>(QS("password")));
|
url.setPassword(received.get<QString>(QS("password")));
|
||||||
url.setPort(received.get<int>(QS("port"), DEFAULT_PORT));
|
url.setPort(received.get<int>(QS("port"), DEFAULT_PORT));
|
||||||
|
@ -111,6 +136,7 @@ void VirtualMonitorPlugin::receivePacket(const NetworkPacket &received)
|
||||||
|
|
||||||
// Get device's capabilities
|
// Get device's capabilities
|
||||||
m_capabilitiesRemote.vncClient = received.get<bool>(QS("supports_vnc"), false);
|
m_capabilitiesRemote.vncClient = received.get<bool>(QS("supports_vnc"), false);
|
||||||
|
m_capabilitiesRemote.rdpClient = received.get<bool>(QS("supports_rdp"), false);
|
||||||
m_capabilitiesRemote.virtualMonitor = received.get<bool>(QS("supports_virt_mon"), false);
|
m_capabilitiesRemote.virtualMonitor = received.get<bool>(QS("supports_virt_mon"), false);
|
||||||
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Remote device supports VNC:" << m_capabilitiesRemote.vncClient;
|
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Remote device supports VNC:" << m_capabilitiesRemote.vncClient;
|
||||||
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Remote device supports Virtual Monitor:" << m_capabilitiesRemote.virtualMonitor;
|
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Remote device supports Virtual Monitor:" << m_capabilitiesRemote.virtualMonitor;
|
||||||
|
@ -131,9 +157,19 @@ bool VirtualMonitorPlugin::requestVirtualMonitor()
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Requesting virtual display " << device()->name();
|
qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Requesting virtual display " << device()->name();
|
||||||
|
bool ret = false;
|
||||||
|
if (m_capabilitiesRemote.rdpClient && krdpHasVirtualMonitor()) {
|
||||||
|
ret = requestRdp();
|
||||||
|
} else {
|
||||||
|
ret = requestVnc();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint VirtualMonitorPlugin::s_port = DEFAULT_PORT;
|
||||||
|
bool VirtualMonitorPlugin::requestVnc()
|
||||||
|
{
|
||||||
QUuid uuid = QUuid::createUuid();
|
QUuid uuid = QUuid::createUuid();
|
||||||
static int s_port = DEFAULT_PORT;
|
|
||||||
const QString port = QString::number(s_port++);
|
const QString port = QString::number(s_port++);
|
||||||
|
|
||||||
m_process = new QProcess(this);
|
m_process = new QProcess(this);
|
||||||
|
@ -154,7 +190,7 @@ bool VirtualMonitorPlugin::requestVirtualMonitor()
|
||||||
|
|
||||||
if (m_retries < 5 && (exitCode == 1 || exitStatus == QProcess::CrashExit)) {
|
if (m_retries < 5 && (exitCode == 1 || exitStatus == QProcess::CrashExit)) {
|
||||||
m_retries++;
|
m_retries++;
|
||||||
requestVirtualMonitor();
|
requestVnc();
|
||||||
} else {
|
} else {
|
||||||
m_process->deleteLater();
|
m_process->deleteLater();
|
||||||
m_process = nullptr;
|
m_process = nullptr;
|
||||||
|
@ -177,6 +213,55 @@ bool VirtualMonitorPlugin::requestVirtualMonitor()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VirtualMonitorPlugin::requestRdp()
|
||||||
|
{
|
||||||
|
QUuid uuid = QUuid::createUuid();
|
||||||
|
const QString port = QString::number(s_port++);
|
||||||
|
|
||||||
|
m_process = new QProcess(this);
|
||||||
|
m_process->setProgram(QS("krdpserver"));
|
||||||
|
const QString scale = m_remoteResolution.value(QLatin1String("scale")).toString();
|
||||||
|
QStringList args = {QS("--virtual-monitor"),
|
||||||
|
m_remoteResolution.value(QLatin1String("resolution")).toString() + u'@' + scale,
|
||||||
|
QS("--username"),
|
||||||
|
QS("user"),
|
||||||
|
QS("--password"),
|
||||||
|
uuid.toString(),
|
||||||
|
QS("--port"),
|
||||||
|
port};
|
||||||
|
|
||||||
|
static const bool s_plasma = qgetenv("XDG_CURRENT_DESKTOP").compare("KDE", Qt::CaseInsensitive) == 0;
|
||||||
|
if (s_plasma) {
|
||||||
|
args << QS("--plasma");
|
||||||
|
}
|
||||||
|
m_process->setArguments(args);
|
||||||
|
connect(m_process, &QProcess::finished, this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
|
qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "krdp virtual display finished with" << device()->name() << m_process->readAllStandardError();
|
||||||
|
if (m_retries < 5 && (exitCode == 1 || exitStatus == QProcess::CrashExit)) {
|
||||||
|
m_retries++;
|
||||||
|
requestVnc();
|
||||||
|
} else {
|
||||||
|
m_process->deleteLater();
|
||||||
|
m_process = nullptr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_process->start();
|
||||||
|
|
||||||
|
if (!m_process->waitForStarted()) {
|
||||||
|
qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Failed to start krfb-virtualmonitor" << m_process->error() << m_process->errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkPacket np(PACKET_TYPE_VIRTUALMONITOR_REQUEST);
|
||||||
|
np.set(QS("protocol"), QS("rdp"));
|
||||||
|
np.set(QS("username"), QS("user"));
|
||||||
|
np.set(QS("password"), uuid.toString());
|
||||||
|
np.set(QS("port"), port);
|
||||||
|
sendPacket(np);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool VirtualMonitorPlugin::checkVncClient() const
|
bool VirtualMonitorPlugin::checkVncClient() const
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
|
@ -192,6 +277,16 @@ bool VirtualMonitorPlugin::checkVncClient() const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VirtualMonitorPlugin::checkRdpClient() const
|
||||||
|
{
|
||||||
|
// krdc is default on KDE
|
||||||
|
if (!QStandardPaths::findExecutable(QS("krdc")).isEmpty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if another client is available
|
||||||
|
return checkDefaultSchemeHandler(QS("rdp"));
|
||||||
|
}
|
||||||
|
|
||||||
bool VirtualMonitorPlugin::checkDefaultSchemeHandler(const QString &scheme) const
|
bool VirtualMonitorPlugin::checkDefaultSchemeHandler(const QString &scheme) const
|
||||||
{
|
{
|
||||||
// TODO: macOS built-in tool: defaults read com.apple.LaunchServices/com.apple.launchservices.secure
|
// TODO: macOS built-in tool: defaults read com.apple.LaunchServices/com.apple.launchservices.secure
|
||||||
|
|
|
@ -48,11 +48,15 @@ public:
|
||||||
private:
|
private:
|
||||||
void stop();
|
void stop();
|
||||||
bool checkVncClient() const;
|
bool checkVncClient() const;
|
||||||
|
bool checkRdpClient() const;
|
||||||
bool checkDefaultSchemeHandler(const QString &scheme) const;
|
bool checkDefaultSchemeHandler(const QString &scheme) const;
|
||||||
|
bool requestVnc();
|
||||||
|
bool requestRdp();
|
||||||
|
|
||||||
struct Capabilities {
|
struct Capabilities {
|
||||||
bool virtualMonitor = false;
|
bool virtualMonitor = false;
|
||||||
bool vncClient = false;
|
bool vncClient = false;
|
||||||
|
bool rdpClient = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Capabilities m_capabilitiesLocal;
|
Capabilities m_capabilitiesLocal;
|
||||||
|
@ -61,4 +65,6 @@ private:
|
||||||
QProcess *m_process = nullptr;
|
QProcess *m_process = nullptr;
|
||||||
QJsonObject m_remoteResolution;
|
QJsonObject m_remoteResolution;
|
||||||
uint m_retries = 0;
|
uint m_retries = 0;
|
||||||
|
|
||||||
|
static uint s_port;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue