[OpenDocString] kdeconnect-kde (cpp)
virtualmonitorplugin.cpp
VirtualMonitorPlugin::VirtualMonitorPlugin(QObject *parent, const QVariantList &args)
    : KdeConnectPlugin(parent, args)
{
}
Constructs a virtual monitor plugin object and assigns its internal state to its parent object.
VirtualMonitorPlugin::~VirtualMonitorPlugin()
{
    stop();
}
Stops the monitoring process.
void VirtualMonitorPlugin::stop()
{
    if (!m_process)
        return;

    m_process->terminate();
    if (!m_process->waitForFinished()) {
        m_process->kill();
        m_process->waitForFinished();
    }
    delete m_process;
    m_process = nullptr;
}
This removes the virtual monitor process object upon destruction. It waits for the process to finish before exiting. If the process is not running, it kills the process and waits for it to finish.
void VirtualMonitorPlugin::connected()
{
    auto screen = QGuiApplication::primaryScreen();
    auto resolution = screen->size();
    QString resolutionString = QString::number(resolution.width()) + QLatin1Char('x') + QString::number(resolution.height());
    NetworkPacket np(PACKET_TYPE_VIRTUALMONITOR,
                     {
                         {QS("resolutions"),
                          QJsonArray{QJsonObject{
                              {QS("resolution"), resolutionString},
                              {QS("scale"), screen->devicePixelRatio()},
                          }}},
                     });
    sendPacket(np);
}
This sends a network packet that sets the resolution and scale values to the current screen size.
bool VirtualMonitorPlugin::receivePacket(const NetworkPacket &received)
{
    if (received.type() == PACKET_TYPE_VIRTUALMONITOR_REQUEST && received.has(QS("url"))) {
        QUrl url(received.get(QS("url")));
        if (!QDesktopServices::openUrl(url)) {
            qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Failed to open" << url.toDisplayString();
            NetworkPacket np(PACKET_TYPE_VIRTUALMONITOR, {{QS("failed"), 0}});
            sendPacket(np);
        }
    } else if (received.type() == PACKET_TYPE_VIRTUALMONITOR) {
        if (received.has(QS("resolutions"))) {
            m_remoteResolution = received.get(QS("resolutions")).first().toObject();
        }
        if (received.has(QS("failed"))) {
            stop();
        }
    }
    return true;
}
This function receives a network packet object and processes it. The function first checks if the packet is a Virtmonster request and if it is a Virtmonster request, it opens the URL of the plugin and opens the URL if it doesn't exist, it sends the packet to the end user. If the packet is a Virtmonster request, it sends the packet to the remote endpoint using the resolutions key, and stops the process. If the packet is not a Virtster request, it returns true.
QString VirtualMonitorPlugin::dbusPath() const
{
    // Don't offer the feature if krfb-virtualmonitor is not around
    if (QStandardPaths::findExecutable(QS("krfb-virtualmonitor")).isEmpty())
        return {};

    return QS("/modules/kdeconnect/devices/") + device()->id() + QS("/virtualmonitor");
}
Returns the dbus path as a QString. It doesn't work if the kernel is not around. It returns an empty array if the kernel is not around.
bool VirtualMonitorPlugin::requestVirtualMonitor()
{
    stop();
    if (m_remoteResolution.isEmpty()) {
        qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Cannot start a request without a resolution";
        return false;
    }

    qCDebug(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "Requesting virtual display " << device()->name();

    QUuid uuid = QUuid::createUuid();
    static int s_port = 5901;
    const QString port = QString::number(s_port++);

    m_process = new QProcess(this);
    m_process->setProgram(QS("krfb-virtualmonitor"));
    const double scale = m_remoteResolution.value(QLatin1String("scale")).toDouble();
    m_process->setArguments({QS("--name"),
                             device()->name(),
                             QS("--resolution"),
                             m_remoteResolution.value(QLatin1String("resolution")).toString(),
                             QS("--scale"),
                             QString::number(scale),
                             QS("--password"),
                             uuid.toString(),
                             QS("--port"),
                             port});
    connect(m_process, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
        qCWarning(KDECONNECT_PLUGIN_VIRTUALMONITOR) << "virtual display finished with" << device()->name() << m_process->readAllStandardError();

        if (m_retries < 5 && (exitCode == 1 || exitStatus == QProcess::CrashExit)) {
            m_retries++;
            requestVirtualMonitor();
        } 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;
    }

    QUrl url;
    url.setScheme(QS("vnc"));
    url.setUserName(QS("user"));
    url.setPassword(uuid.toString());
    url.setHost(device()->getLocalIpAddress().toString());

    NetworkPacket np(PACKET_TYPE_VIRTUALMONITOR_REQUEST, {{QS("url"), url.toEncoded()}});
    sendPacket(np);
    return true;
}
This requests that the virtual display be started. It creates a new process object, sets the name, resolution, scale and password of the current device, and requests a virtual monitor to connect to it. It returns true on the successful request.