Add screencast wayland integration
This commit is contained in:
parent
026f5825f3
commit
94289f1074
|
|
@ -1,8 +1,34 @@
|
|||
add_subdirectory(kcm)
|
||||
|
||||
# Ensure C sources (wayland-*-protocol.c) are compiled
|
||||
enable_language(C)
|
||||
|
||||
# ecm_add_qml_module ?
|
||||
# ecm_target_qml_sources ?
|
||||
kcoreaddons_add_plugin(breezy_desktop_effect INSTALL_NAMESPACE "kwin/effects/plugins/")
|
||||
|
||||
find_package(Wayland COMPONENTS Client)
|
||||
find_package(KWayland REQUIRED)
|
||||
find_package(Qt6 CONFIG REQUIRED COMPONENTS WaylandClient)
|
||||
|
||||
# Try to locate kde-screencast-unstable-v1.xml from PlasmaWaylandProtocols
|
||||
find_package(PlasmaWaylandProtocols REQUIRED)
|
||||
set(PWP_PROTOCOL_DIR "/usr/share/plasma-wayland-protocols")
|
||||
set(PWP_PROTOCOL_XML "${PWP_PROTOCOL_DIR}/zkde-screencast-unstable-v1.xml")
|
||||
if(EXISTS "${PWP_PROTOCOL_XML}")
|
||||
set(PROTOCOL_XML "${PWP_PROTOCOL_XML}")
|
||||
else()
|
||||
set(PROTOCOL_XML "${CMAKE_CURRENT_SOURCE_DIR}/zkde-screencast-unstable-v1.xml")
|
||||
endif()
|
||||
|
||||
qt6_generate_wayland_protocol_client_sources(breezy_desktop_effect
|
||||
PRIVATE_CODE FILES "${PROTOCOL_XML}"
|
||||
)
|
||||
|
||||
target_sources(breezy_desktop_effect PRIVATE
|
||||
breezydesktopeffect.cpp
|
||||
kwaylandclient.cpp
|
||||
screencasting.cpp
|
||||
main.cpp
|
||||
)
|
||||
kconfig_add_kcfg_files(breezy_desktop_effect breezydesktopconfig.kcfgc)
|
||||
|
|
@ -12,6 +38,7 @@ target_link_libraries(breezy_desktop_effect
|
|||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Quick
|
||||
Qt6::WaylandClient
|
||||
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigGui
|
||||
|
|
@ -21,6 +48,7 @@ target_link_libraries(breezy_desktop_effect
|
|||
KF6::WindowSystem
|
||||
|
||||
KWin::kwin
|
||||
Plasma::KWaylandClient
|
||||
)
|
||||
|
||||
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop_effect)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,14 @@
|
|||
#include "breezydesktopconfig.h"
|
||||
#include "effect/effect.h"
|
||||
#include "effect/effecthandler.h"
|
||||
#include "kwaylandclient.h"
|
||||
#include "opengl/glutils.h"
|
||||
#include "core/rendertarget.h"
|
||||
#include "core/renderviewport.h"
|
||||
|
||||
#include <kwin/main.h>
|
||||
#include <core/outputbackend.h>
|
||||
|
||||
#include <functional>
|
||||
#include <QAction>
|
||||
#include <QFile>
|
||||
|
|
@ -193,6 +197,10 @@ void BreezyDesktopEffect::deactivate()
|
|||
disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
|
||||
m_cursorUpdateTimer->stop();
|
||||
showCursor();
|
||||
if (m_waylandClient->isStreamingEnabled()) {
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - deactivating - stopping streaming";
|
||||
m_waylandClient->stopStreaming(m_stream.nodeId); // Stop any active streaming
|
||||
}
|
||||
|
||||
// this triggers realDeactivate with a delay so if it's triggered from QML it gives the QML function time to
|
||||
// exit, avoiding a crash
|
||||
|
|
@ -207,7 +215,25 @@ void BreezyDesktopEffect::realDeactivate()
|
|||
|
||||
void BreezyDesktopEffect::recenter()
|
||||
{
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - recenter";
|
||||
if (m_waylandClient == nullptr) {
|
||||
m_waylandClient = new KWin::Wayland::Client();
|
||||
m_waylandClient->init();
|
||||
}
|
||||
|
||||
if (m_waylandClient->isConnectionReady() && m_waylandClient->isStreamingAvailable()) {
|
||||
static int displayCounter = 0;
|
||||
QString uniqueDisplayName = QStringLiteral("BreezyDesktopEffect_%1").arg(++displayCounter);
|
||||
QString uniqueDisplayDesc = QStringLiteral("Breezy Desktop Effect %1").arg(displayCounter);
|
||||
m_stream = m_waylandClient->startVirtualDisplay(
|
||||
uniqueDisplayName,
|
||||
uniqueDisplayDesc,
|
||||
QSize(2560, 1440),
|
||||
Screencasting::CursorMode::Hidden
|
||||
);
|
||||
} else {
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - recenter - no streaming enabled";
|
||||
}
|
||||
|
||||
QFile controlFile(QStringLiteral("/dev/shm/xr_driver_control"));
|
||||
if (controlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
controlFile.write("recenter_screen=true\n");
|
||||
|
|
@ -465,6 +491,7 @@ void BreezyDesktopEffect::updateCursorPos()
|
|||
Q_EMIT cursorPosChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_breezydesktopeffect.cpp"
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "kcm/shortcuts.h"
|
||||
#include "kwaylandclient.h"
|
||||
#include <effect/quickeffect.h>
|
||||
|
||||
#include <QAction>
|
||||
|
|
@ -89,6 +90,8 @@ namespace KWin
|
|||
QTimer *m_shutdownTimer;
|
||||
QString m_cursorImageSource;
|
||||
|
||||
KWin::Wayland::Client *m_waylandClient = nullptr;
|
||||
KWin::Wayland::Stream m_stream;
|
||||
bool m_enabled = false;
|
||||
bool m_imuResetState;
|
||||
QList<QQuaternion> m_imuRotations;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
#include "kwaylandclient.h"
|
||||
#include "wayland-zkde-screencast-unstable-v1-client-protocol.h"
|
||||
|
||||
#include <KWayland/Client/connection_thread.h>
|
||||
#include <KWayland/Client/registry.h>
|
||||
#include <QLoggingCategory>
|
||||
#include <QTimer>
|
||||
|
||||
Q_LOGGING_CATEGORY(KWAYLAND, "kwayland.xr")
|
||||
|
||||
using namespace KWin::Wayland;
|
||||
|
||||
void Client::init()
|
||||
{
|
||||
auto connection = new KWayland::Client::ConnectionThread;
|
||||
connection->initConnection();
|
||||
|
||||
connect(connection, &KWayland::Client::ConnectionThread::connected, this, [this, connection]() {
|
||||
qCCritical(KWAYLAND) << "Connected to Wayland display";
|
||||
m_registry = new KWayland::Client::Registry(this);
|
||||
|
||||
connect(m_registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this](const QByteArray &interfaceName, quint32 name, quint32 version) {
|
||||
if (interfaceName != "zkde_screencast_unstable_v1")
|
||||
return;
|
||||
|
||||
qCCritical(KWAYLAND) << "Found screencasting interface" << interfaceName << name << version;
|
||||
m_screencasting = new Screencasting(m_registry, name, version, this);
|
||||
m_connectionReady = true;
|
||||
});
|
||||
connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] {
|
||||
m_registryInitialized = true;
|
||||
qCCritical(KWAYLAND) << "Registry initialized";
|
||||
});
|
||||
|
||||
m_registry->create(connection);
|
||||
m_registry->setup();
|
||||
});
|
||||
}
|
||||
|
||||
bool Client::isConnectionReady()
|
||||
{
|
||||
return m_connectionReady;
|
||||
}
|
||||
|
||||
Stream Client::startVirtualDisplay(const QString &name,
|
||||
const QString &description,
|
||||
const QSize &size,
|
||||
Screencasting::CursorMode mode)
|
||||
{
|
||||
return startStreaming(m_screencasting->createVirtualOutputStream(name, description, size, 1, mode),
|
||||
{
|
||||
{QLatin1String("size"), size},
|
||||
{QLatin1String("source_type"), static_cast<uint>(Screencasting::Virtual)},
|
||||
});
|
||||
}
|
||||
|
||||
Stream Client::startStreaming(ScreencastingStream *stream, const QVariantMap &streamOptions)
|
||||
{
|
||||
QEventLoop loop;
|
||||
Stream ret;
|
||||
|
||||
connect(stream, &ScreencastingStream::failed, &loop, [&](const QString &error) {
|
||||
qCCritical(KWAYLAND) << "failed to start streaming" << stream << error;
|
||||
|
||||
loop.quit();
|
||||
});
|
||||
connect(stream, &ScreencastingStream::created, &loop, [&](uint32_t nodeid) {
|
||||
ret.stream = stream;
|
||||
ret.nodeId = nodeid;
|
||||
ret.map = streamOptions;
|
||||
m_streams.append(ret);
|
||||
|
||||
connect(stream, &ScreencastingStream::closed, this, [this, nodeid] {
|
||||
stopStreaming(nodeid);
|
||||
});
|
||||
Q_ASSERT(ret.isValid());
|
||||
|
||||
loop.quit();
|
||||
});
|
||||
QTimer::singleShot(3000, &loop, [&loop, stream] {
|
||||
stream->deleteLater();
|
||||
loop.quit();
|
||||
});
|
||||
loop.exec();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Client::stopStreaming(uint32_t nodeid)
|
||||
{
|
||||
for (auto it = m_streams.begin(), itEnd = m_streams.end(); it != itEnd; ++it) {
|
||||
if (it->nodeId == nodeid) {
|
||||
it->close();
|
||||
m_streams.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::isStreamingEnabled()
|
||||
{
|
||||
return !m_streams.isEmpty();
|
||||
}
|
||||
|
||||
bool Client::isStreamingAvailable()
|
||||
{
|
||||
return m_screencasting;
|
||||
}
|
||||
|
||||
void Stream::close()
|
||||
{
|
||||
stream->deleteLater();
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <KWayland/Client/registry.h>
|
||||
#include "screencasting.h"
|
||||
#include "wayland-zkde-screencast-unstable-v1-client-protocol.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Wayland
|
||||
{
|
||||
struct Stream {
|
||||
ScreencastingStream *stream = nullptr;
|
||||
uint nodeId;
|
||||
QVariantMap map;
|
||||
bool isValid() const
|
||||
{
|
||||
return stream != nullptr;
|
||||
}
|
||||
|
||||
void close();
|
||||
};
|
||||
typedef QList<Stream> Streams;
|
||||
|
||||
class Client : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void init();
|
||||
bool isConnectionReady();
|
||||
bool isStreamingEnabled();
|
||||
bool isStreamingAvailable();
|
||||
Stream startVirtualDisplay(const QString &name, const QString &description, const QSize &size, Screencasting::CursorMode mode);
|
||||
void stopStreaming(uint node);
|
||||
|
||||
Q_SIGNALS:
|
||||
void connected();
|
||||
void disconnected();
|
||||
void errorOccurred(const QString &error);
|
||||
|
||||
private:
|
||||
Stream startStreaming(ScreencastingStream *stream, const QVariantMap &streamOptions);
|
||||
bool m_registryInitialized = false;
|
||||
bool m_connectionReady = false;
|
||||
KWayland::Client::Registry *m_registry = nullptr;
|
||||
QList<Stream> m_streams;
|
||||
Screencasting *m_screencasting = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Wayland
|
||||
} // namespace KWin
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::Wayland::Stream)
|
||||
Q_DECLARE_METATYPE(KWin::Wayland::Streams)
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
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() override
|
||||
{
|
||||
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;
|
||||
QRect m_geometry;
|
||||
QPointer<ScreencastingStream> q;
|
||||
};
|
||||
|
||||
ScreencastingStream::ScreencastingStream(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new ScreencastingStreamPrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
ScreencastingStream::~ScreencastingStream() = default;
|
||||
|
||||
quint32 ScreencastingStream::nodeid() const
|
||||
{
|
||||
return d->m_nodeid;
|
||||
}
|
||||
|
||||
QRect ScreencastingStream::geometry() const
|
||||
{
|
||||
return d->m_geometry;
|
||||
}
|
||||
|
||||
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() override
|
||||
{
|
||||
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::createVirtualOutputStream(const QString &name, const QString &description, const QSize &s, qreal scale, Screencasting::CursorMode mode)
|
||||
{
|
||||
auto stream = new ScreencastingStream(this);
|
||||
if (d->version() >= ZKDE_SCREENCAST_UNSTABLE_V1_STREAM_VIRTUAL_OUTPUT_WITH_DESCRIPTION_SINCE_VERSION) {
|
||||
stream->d->init(d->stream_virtual_output_with_description(name, description, s.width(), s.height(), wl_fixed_from_double(scale), mode));
|
||||
} else {
|
||||
stream->d->init(d->stream_virtual_output(name, s.width(), s.height(), wl_fixed_from_double(scale), mode));
|
||||
}
|
||||
stream->d->m_geometry = QRect(QPoint(0, 0), s);
|
||||
return stream;
|
||||
}
|
||||
|
||||
void Screencasting::setup(::zkde_screencast_unstable_v1 *screencasting)
|
||||
{
|
||||
d.reset(new ScreencastingPrivate(screencasting, this));
|
||||
}
|
||||
|
||||
void Screencasting::destroy()
|
||||
{
|
||||
d.reset(nullptr);
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
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 <QList>
|
||||
#include <QObject>
|
||||
#include <QScreen>
|
||||
#include <QSharedPointer>
|
||||
#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;
|
||||
QRect geometry() 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 SourceType {
|
||||
Any = 0,
|
||||
Monitor = 1,
|
||||
Window = 2,
|
||||
Virtual = 4,
|
||||
};
|
||||
Q_ENUM(SourceType)
|
||||
Q_DECLARE_FLAGS(SourceTypes, SourceType)
|
||||
|
||||
enum CursorMode {
|
||||
Hidden = 1,
|
||||
Embedded = 2,
|
||||
Metadata = 4,
|
||||
};
|
||||
Q_ENUM(CursorMode)
|
||||
|
||||
ScreencastingStream *createVirtualOutputStream(const QString &name, const QString &description, const QSize &size, qreal scale, CursorMode mode);
|
||||
|
||||
void setup(zkde_screencast_unstable_v1 *screencasting);
|
||||
void destroy();
|
||||
|
||||
Q_SIGNALS:
|
||||
void removed();
|
||||
void sourcesChanged();
|
||||
|
||||
private:
|
||||
QScopedPointer<ScreencastingPrivate> d;
|
||||
};
|
||||
Loading…
Reference in New Issue