diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -61,6 +61,7 @@ xdgshell_v6.cpp xdgshell_stable.cpp xdgoutput.cpp + screencasting.cpp ../compat/wayland-xdg-shell-v5-protocol.c ) @@ -202,6 +203,11 @@ BASENAME keystate ) +ecm_add_qtwayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/screencast.xml + BASENAME org-kde-kwin-screencast-unstable-v1 +) + set(CLIENT_GENERATED_FILES ${CMAKE_CURRENT_BINARY_DIR}/wayland-fullscreen-shell-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-client-protocol.h @@ -289,6 +295,7 @@ keyboard.h keystate.h remote_access.h + screencasting.h outputconfiguration.h outputmanagement.h outputdevice.h diff --git a/src/client/protocols/screencast.xml b/src/client/protocols/screencast.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/screencast.xml @@ -0,0 +1,56 @@ + + + + + SPDX-License-Identifier: LGPL-2.1-or-later + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/client/screencasting.h b/src/client/screencasting.h new file mode 100644 --- /dev/null +++ b/src/client/screencasting.h @@ -0,0 +1,78 @@ +/* + SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +struct org_kde_kwin_remote_access_manager; +struct org_kde_kwin_remote_buffer; +struct wl_output; + +namespace KWayland +{ +namespace Client +{ +class EventQueue; +class VideoStreamingPrivate; +class ScreencastingSourcePrivate; + +class KWAYLANDCLIENT_EXPORT ScreencastingSource +{ + Q_GADGET +public: + ScreencastingSource(); + ScreencastingSource(const ScreencastingSource& other); + virtual ~ScreencastingSource(); + + quint32 sourceId() const; + QString iconName() const; + QString description() const; + bool isOutput() const; + QRect geometry() const; + + bool operator!=(const ScreencastingSource& other) const; + +protected: + ScreencastingSource(quint32 sourceId, const QString &description, const QString &iconName, bool isOutput, const QRect &geometry); + +private: + friend class VideoStreamingPrivate; + QSharedPointer d; +}; + +class KWAYLANDCLIENT_EXPORT Screencasting : public QObject +{ + Q_OBJECT +public: + explicit Screencasting(QObject *parent = nullptr); + ~Screencasting() override; + + QVector sources() const; + + void create(const ScreencastingSource& source); + void close(quint32 nodeid); + +Q_SIGNALS: + void initialized(); + void created(const ScreencastingSource& source, quint32 nodeid); + void failed(const ScreencastingSource& source, const QString &error); + void closed(quint32 nodeid); + void sourcesChanged(); + +private: + QScopedPointer d; +}; + +} +} + +Q_DECLARE_METATYPE(KWayland::Client::ScreencastingSource); diff --git a/src/client/screencasting.cpp b/src/client/screencasting.cpp new file mode 100644 --- /dev/null +++ b/src/client/screencasting.cpp @@ -0,0 +1,143 @@ +/* + SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "screencasting.h" +#include "qwayland-org-kde-kwin-screencast-unstable-v1.h" +#include + +using namespace KWayland::Client; + +class KWayland::Client::ScreencastingSourcePrivate +{ +public: + ScreencastingSourcePrivate(quint32 sourceId, const QString &description, const QString &iconName, bool isOutput, const QRect &geometry) + : sourceId(sourceId) + , description(description) + , iconName(iconName) + , isOutput(isOutput) + , geometry(geometry) + {} + + const quint32 sourceId; + const QString description; + const QString iconName; + const bool isOutput; + const QRect geometry; +}; + +ScreencastingSource::ScreencastingSource() + : d(new ScreencastingSourcePrivate(0, {}, {}, false, {})) +{} + +ScreencastingSource::ScreencastingSource(quint32 sourceId, const QString &description, const QString &iconName, bool isOutput, const QRect &geometry) + : d(new ScreencastingSourcePrivate(sourceId, description, iconName, isOutput, geometry)) +{} + +ScreencastingSource::ScreencastingSource(const ScreencastingSource& other) + : d(other.d) +{} + +ScreencastingSource::~ScreencastingSource() = default; + +quint32 ScreencastingSource::sourceId() const +{ + return d->sourceId; +} + +QString ScreencastingSource::description() const +{ + return d->description; +} + +QString ScreencastingSource::iconName() const +{ + return d->iconName; +} + +bool ScreencastingSource::isOutput() const +{ + return d->isOutput; +} + +QRect ScreencastingSource::geometry() const +{ + return d->geometry; +} + +bool ScreencastingSource::operator!=(const ScreencastingSource& other) const +{ + return d != other.d; +} + +class KWayland::Client::VideoStreamingPrivate : public QtWayland::org_kde_kwin_screencast_unstable_v1 +{ +public: + VideoStreamingPrivate(Screencasting *q) + : q(q) + {} + + void org_kde_kwin_screencast_unstable_v1_addSource(uint32_t sourceId, const QString & description, const QString & iconName, uint32_t type, + int32_t geometryX, int32_t geometryY, int32_t geometryWidth, int32_t geometryHeight) override + { + m_sources << ScreencastingSource(sourceId, description, iconName, type==SourceType_monitor, + {geometryX, geometryY, geometryWidth, geometryHeight}); + if (m_done) + Q_EMIT q->sourcesChanged(); + } + void org_kde_kwin_screencast_unstable_v1_removeSource(uint32_t sourceId) override { + auto it = std::find_if(m_sources.begin(), m_sources.end(), [sourceId] (const ScreencastingSource &source) { + return source.sourceId() == sourceId; + }); + Q_ASSERT(it != m_sources.end()); + m_sources.erase(it); + Q_EMIT q->sourcesChanged(); + } + + void org_kde_kwin_screencast_unstable_v1_done() override { + m_done = true; + Q_EMIT q->initialized(); + Q_EMIT q->sourcesChanged(); + } + + void org_kde_kwin_screencast_unstable_v1_created(uint32_t nodeid, uint32_t sourceId) override { + Q_EMIT q->created(sourceById(sourceId), nodeid); + } + + ScreencastingSource sourceById(quint32 sourceId) { + auto it = std::find_if(m_sources.begin(), m_sources.end(), [sourceId] (const ScreencastingSource &source) { + return source.sourceId() == sourceId; + }); + if (it == m_sources.constEnd()) + return {}; + return *it; + } + + bool m_done = false; + QVector m_sources; + Screencasting *const q; +}; + +Screencasting::Screencasting(QObject* parent) + : QObject(parent) + , d(new VideoStreamingPrivate(this)) +{} + +Screencasting::~Screencasting() = default; + +void Screencasting::close(quint32 nodeid) +{ + d->close(nodeid); +} + +void Screencasting::create(const ScreencastingSource& source) +{ + d->create(source.sourceId()); +} + +QVector Screencasting::sources() const +{ + return d->m_sources; +} diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -64,6 +64,7 @@ xdgshell_v5_interface.cpp xdgshell_v5_interface.cpp xdgshell_v6_interface.cpp + screencasting_interface.cpp ) ecm_qt_declare_logging_category(SERVER_LIB_SRCS @@ -234,6 +235,11 @@ BASENAME tablet-unstable-v2 ) +ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/screencast.xml + BASENAME org-kde-kwin-screencast-unstable-v1 +) + set(SERVER_GENERATED_SRCS ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h @@ -293,6 +299,8 @@ ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-shell-v6-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-tablet-unstable-v2.h ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-tablet-unstable-v2.cpp + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-screecast-unstable-v1.h + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-screecast-unstable-v1.cpp ) set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON) @@ -369,6 +377,7 @@ relativepointer_interface.h remote_access_interface.h resource.h + screencasting_interface.h seat_interface.h server_decoration_interface.h server_decoration_palette_interface.h diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -80,6 +80,7 @@ class KeyStateInterface; class LinuxDmabufUnstableV1Interface; class TabletManagerInterface; +class ScreencastingInterface; /** * @brief Class holding the Wayland server display loop. @@ -318,6 +319,13 @@ */ TabletManagerInterface *createTabletManagerInterface(QObject *parent = nullptr); + /** + * Creates an interface to request video feeds of different compositor resources + * + * @since 5.70 + */ + ScreencastingInterface *createScreencastingInterface(QObject *parent = nullptr); + /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. diff --git a/src/server/display.cpp b/src/server/display.cpp --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -31,6 +31,7 @@ #include "relativepointer_interface_p.h" #include "remote_access_interface.h" #include "seat_interface.h" +#include "screencasting_interface.h" #include "server_decoration_interface.h" #include "server_decoration_palette_interface.h" #include "shadow_interface.h" @@ -378,6 +379,13 @@ return d; } +ScreencastingInterface *Display::createScreencastingInterface(QObject *parent) +{ + auto s = new ScreencastingInterface(this, parent); + connect(this, &Display::aboutToTerminate, s, [s] { delete s; }); + return s; +} + TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent) { TextInputManagerInterface *t = nullptr;