mirror of
https://github.com/KDE/krfb
synced 2026-07-01 07:41:17 -07:00
Introduce krfb-virtualmonitor
It implements a KWin protocol that is oriented towards serving a virtual display specifically. It requests KWin a stream that will act as a monitor that we can feed into remote clients.
This commit is contained in:
committed by
Aleix Pol Gonzalez
parent
608762c7ac
commit
4707bde236
@@ -5,8 +5,14 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}
|
||||
set (krfb_framebuffer_pw_SRCS
|
||||
pw_framebuffer.cpp
|
||||
pw_framebufferplugin.cpp
|
||||
|
||||
screencasting.cpp
|
||||
)
|
||||
|
||||
ecm_add_qtwayland_client_protocol(krfb_framebuffer_pw_SRCS
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/screencast.xml
|
||||
BASENAME zkde-screencast-unstable-v1
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(krfb_framebuffer_pw_SRCS
|
||||
HEADER krfb_fb_pipewire_debug.h
|
||||
@@ -39,6 +45,8 @@ target_link_libraries(krfb_framebuffer_pw
|
||||
Qt5::Gui
|
||||
Qt5::DBus
|
||||
KF5::CoreAddons
|
||||
KF5::WaylandClient
|
||||
Wayland::Client
|
||||
krfbprivate
|
||||
PkgConfig::PipeWire
|
||||
)
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <QDebug>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#include <KWayland/Client/connection_thread.h>
|
||||
#include <KWayland/Client/registry.h>
|
||||
|
||||
// pipewire
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
@@ -38,6 +41,7 @@
|
||||
#include "xdp_dbus_screencast_interface.h"
|
||||
#include "xdp_dbus_remotedesktop_interface.h"
|
||||
#include "krfb_fb_pipewire_debug.h"
|
||||
#include "screencasting.h"
|
||||
|
||||
#if HAVE_DMA_BUF
|
||||
#include <fcntl.h>
|
||||
@@ -904,10 +908,6 @@ PWFrameBuffer::PWFrameBuffer(WId winid, QObject *parent)
|
||||
: FrameBuffer (winid, parent),
|
||||
d(new Private(this))
|
||||
{
|
||||
// D-Bus is most important in init chain, no toys for us if something is wrong with XDP
|
||||
// PipeWire connectivity is initialized after D-Bus session is started
|
||||
d->initDbus();
|
||||
|
||||
fb = nullptr;
|
||||
}
|
||||
|
||||
@@ -917,6 +917,38 @@ PWFrameBuffer::~PWFrameBuffer()
|
||||
fb = nullptr;
|
||||
}
|
||||
|
||||
void PWFrameBuffer::initDBus()
|
||||
{
|
||||
d->initDbus();
|
||||
}
|
||||
|
||||
void PWFrameBuffer::startVirtualMonitor(const QString& name, const QSize& resolution, qreal dpr)
|
||||
{
|
||||
d->videoSize = resolution * dpr;
|
||||
using namespace KWayland::Client;
|
||||
auto connection = ConnectionThread::fromApplication(this);
|
||||
if (!connection) {
|
||||
qWarning() << "Failed getting Wayland connection from QPA";
|
||||
QCoreApplication::exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
auto registry = new Registry(this);
|
||||
connect(registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, registry, name, dpr, resolution] (const QByteArray &interfaceName, quint32 wlname, quint32 version) {
|
||||
if (interfaceName != "zkde_screencast_unstable_v1")
|
||||
return;
|
||||
|
||||
auto screencasting = new Screencasting(registry, wlname, version, this);
|
||||
auto r = screencasting->createVirtualMonitorStream(name, resolution, dpr, Screencasting::Metadata);
|
||||
connect(r, &ScreencastingStream::created, this, [this] (quint32 nodeId) {
|
||||
d->pwStreamNodeId = nodeId;
|
||||
d->initPw();
|
||||
});
|
||||
});
|
||||
registry->create(connection);
|
||||
registry->setup();
|
||||
}
|
||||
|
||||
int PWFrameBuffer::depth()
|
||||
{
|
||||
return 32;
|
||||
|
||||
@@ -33,6 +33,9 @@ public:
|
||||
PWFrameBuffer(WId winid, QObject *parent = nullptr);
|
||||
virtual ~PWFrameBuffer() override;
|
||||
|
||||
void initDBus();
|
||||
void startVirtualMonitor(const QString &name, const QSize &resolution, qreal dpr);
|
||||
|
||||
int depth() override;
|
||||
int height() override;
|
||||
int width() override;
|
||||
|
||||
@@ -41,6 +41,13 @@ FrameBuffer *PWFrameBufferPlugin::frameBuffer(WId id, const QVariantMap &args)
|
||||
//NOTE WId is irrelevant in Wayland
|
||||
|
||||
auto pwfb = new PWFrameBuffer(id);
|
||||
if (args.contains(QLatin1String("name"))) {
|
||||
pwfb->startVirtualMonitor(args[QStringLiteral("name")].toString(), args[QStringLiteral("resolution")].toSize(), args[QStringLiteral("scale")].toDouble());
|
||||
} else {
|
||||
// D-Bus is most important in XDG-Desktop-Portals init chain, no toys for us if something is wrong with XDP
|
||||
// PipeWire connectivity is initialized after D-Bus session is started
|
||||
pwfb->initDBus();
|
||||
}
|
||||
|
||||
// sanity check for dbus/wayland/pipewire errors
|
||||
if (!pwfb->isValid()) {
|
||||
|
||||
136
framebuffers/pipewire/screencasting.cpp
Normal file
136
framebuffers/pipewire/screencasting.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "screencasting.h"
|
||||
#include "qwayland-zkde-screencast-unstable-v1.h"
|
||||
#include <KWayland/Client/output.h>
|
||||
#include <KWayland/Client/plasmawindowmanagement.h>
|
||||
#include <KWayland/Client/registry.h>
|
||||
#include <QDebug>
|
||||
#include <QRect>
|
||||
|
||||
using namespace KWayland::Client;
|
||||
|
||||
class ScreencastingStreamPrivate : public QtWayland::zkde_screencast_stream_unstable_v1
|
||||
{
|
||||
public:
|
||||
ScreencastingStreamPrivate(ScreencastingStream *q)
|
||||
: q(q)
|
||||
{
|
||||
}
|
||||
~ScreencastingStreamPrivate()
|
||||
{
|
||||
close();
|
||||
q->deleteLater();
|
||||
}
|
||||
|
||||
void zkde_screencast_stream_unstable_v1_created(uint32_t node) override
|
||||
{
|
||||
m_nodeId = node;
|
||||
Q_EMIT q->created(node);
|
||||
}
|
||||
|
||||
void zkde_screencast_stream_unstable_v1_closed() override
|
||||
{
|
||||
Q_EMIT q->closed();
|
||||
}
|
||||
|
||||
void zkde_screencast_stream_unstable_v1_failed(const QString &error) override
|
||||
{
|
||||
Q_EMIT q->failed(error);
|
||||
}
|
||||
|
||||
uint m_nodeId = 0;
|
||||
QPointer<ScreencastingStream> q;
|
||||
};
|
||||
|
||||
ScreencastingStream::ScreencastingStream(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new ScreencastingStreamPrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
ScreencastingStream::~ScreencastingStream() = default;
|
||||
|
||||
quint32 ScreencastingStream::nodeId() const
|
||||
{
|
||||
return d->m_nodeId;
|
||||
}
|
||||
|
||||
class ScreencastingPrivate : public QtWayland::zkde_screencast_unstable_v1
|
||||
{
|
||||
public:
|
||||
ScreencastingPrivate(Registry *registry, int id, int version, Screencasting *q)
|
||||
: QtWayland::zkde_screencast_unstable_v1(*registry, id, version)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
ScreencastingPrivate(::zkde_screencast_unstable_v1 *screencasting, Screencasting *q)
|
||||
: QtWayland::zkde_screencast_unstable_v1(screencasting)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
~ScreencastingPrivate()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
Screencasting *const q;
|
||||
};
|
||||
|
||||
Screencasting::Screencasting(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
Screencasting::Screencasting(Registry *registry, int id, int version, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new ScreencastingPrivate(registry, id, version, this))
|
||||
{
|
||||
}
|
||||
|
||||
Screencasting::~Screencasting() = default;
|
||||
|
||||
ScreencastingStream *Screencasting::createOutputStream(Output *output, CursorMode mode)
|
||||
{
|
||||
auto stream = new ScreencastingStream(this);
|
||||
stream->setObjectName(output->model());
|
||||
stream->d->init(d->stream_output(*output, mode));
|
||||
return stream;
|
||||
}
|
||||
|
||||
ScreencastingStream *Screencasting::createWindowStream(PlasmaWindow *window, CursorMode mode)
|
||||
{
|
||||
auto stream = createWindowStream(QString::fromUtf8(window->uuid()), mode);
|
||||
stream->setObjectName(window->appId());
|
||||
return stream;
|
||||
}
|
||||
|
||||
ScreencastingStream *Screencasting::createWindowStream(const QString &uuid, CursorMode mode)
|
||||
{
|
||||
auto stream = new ScreencastingStream(this);
|
||||
stream->d->init(d->stream_window(uuid, mode));
|
||||
return stream;
|
||||
}
|
||||
|
||||
ScreencastingStream * Screencasting::createVirtualMonitorStream(const QString& name, const QSize& resolution, qreal dpr, Screencasting::CursorMode mode)
|
||||
{
|
||||
auto stream = new ScreencastingStream(this);
|
||||
stream->d->init(d->stream_virtual_output(name, resolution.width(), resolution.height(), wl_fixed_from_double(dpr), mode));
|
||||
return stream;
|
||||
}
|
||||
|
||||
void Screencasting::setup(::zkde_screencast_unstable_v1 *screencasting)
|
||||
{
|
||||
d.reset(new ScreencastingPrivate(screencasting, this));
|
||||
}
|
||||
|
||||
void Screencasting::destroy()
|
||||
{
|
||||
d.reset(nullptr);
|
||||
}
|
||||
78
framebuffers/pipewire/screencasting.h
Normal file
78
framebuffers/pipewire/screencasting.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QVector>
|
||||
#include <optional>
|
||||
|
||||
struct zkde_screencast_unstable_v1;
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Client
|
||||
{
|
||||
class PlasmaWindow;
|
||||
class Registry;
|
||||
class Output;
|
||||
}
|
||||
}
|
||||
|
||||
class ScreencastingPrivate;
|
||||
class ScreencastingSourcePrivate;
|
||||
class ScreencastingStreamPrivate;
|
||||
class ScreencastingStream : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScreencastingStream(QObject *parent);
|
||||
~ScreencastingStream() override;
|
||||
|
||||
quint32 nodeId() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void created(quint32 nodeid);
|
||||
void failed(const QString &error);
|
||||
void closed();
|
||||
|
||||
private:
|
||||
friend class Screencasting;
|
||||
QScopedPointer<ScreencastingStreamPrivate> d;
|
||||
};
|
||||
|
||||
class Screencasting : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Screencasting(QObject *parent = nullptr);
|
||||
explicit Screencasting(KWayland::Client::Registry *registry, int id, int version, QObject *parent = nullptr);
|
||||
~Screencasting() override;
|
||||
|
||||
enum CursorMode {
|
||||
Hidden = 1,
|
||||
Embedded = 2,
|
||||
Metadata = 4,
|
||||
};
|
||||
Q_ENUM(CursorMode);
|
||||
|
||||
ScreencastingStream *createOutputStream(KWayland::Client::Output *output, CursorMode mode);
|
||||
ScreencastingStream *createWindowStream(KWayland::Client::PlasmaWindow *window, CursorMode mode);
|
||||
ScreencastingStream *createWindowStream(const QString &uuid, CursorMode mode);
|
||||
ScreencastingStream *createVirtualMonitorStream(const QString &name, const QSize &resolution, qreal dpr, CursorMode mode);
|
||||
|
||||
void setup(zkde_screencast_unstable_v1 *screencasting);
|
||||
void destroy();
|
||||
|
||||
Q_SIGNALS:
|
||||
void initialized();
|
||||
void removed();
|
||||
void sourcesChanged();
|
||||
|
||||
private:
|
||||
QScopedPointer<ScreencastingPrivate> d;
|
||||
};
|
||||
Reference in New Issue
Block a user