diff --git a/autotests/server/CMakeLists.txt b/autotests/server/CMakeLists.txt --- a/autotests/server/CMakeLists.txt +++ b/autotests/server/CMakeLists.txt @@ -57,3 +57,9 @@ target_link_libraries( testTabletInterface Qt5::Test KF5::WaylandServer KF5::WaylandClient Wayland::Client) add_test(NAME kwayland-testTabletInterface COMMAND testTabletInterface) ecm_mark_as_test(testTabletInterface) + +add_executable(testScreencastingInterface test_screencasting.cpp) +target_link_libraries( testScreencastingInterface Qt5::Test KF5::WaylandServer Wayland::Client KF5::WaylandClient) +# target_include_directories(testScreencastingInterface PUBLIC ${CMAKE_BINARY_DIR}/src/client/ ${CMAKE_BINARY_DIR}/src/client/) +add_test(NAME kwayland-testScreencastingInterface COMMAND testScreencastingInterface) +ecm_mark_as_test(testScreencastingInterface) diff --git a/autotests/server/test_screencasting.cpp b/autotests/server/test_screencasting.cpp new file mode 100644 --- /dev/null +++ b/autotests/server/test_screencasting.cpp @@ -0,0 +1,158 @@ +/******************************************************************** +Copyright 2020 Aleix Pol Gonzalez + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +// Qt +#include +#include +#include + +#include + +// WaylandServer +#include "../../src/server/compositor_interface.h" +#include "../../src/server/display.h" +#include "../../src/server/seat_interface.h" +#include "../../src/server/screencasting_interface.h" + +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/registry.h" +#include "../../src/client/seat.h" +#include "../../src/client/screencasting.h" +#include "../../src/client/compositor.h" +#include "../../src/client/screencasting.h" + +class TestScreencastingInterface : public QObject +{ + Q_OBJECT +public: + TestScreencastingInterface() + {} + + ~TestScreencastingInterface() override; + +private Q_SLOTS: + void initTestCase(); + void testAdd(); + +private: + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::EventQueue *m_queue; + KWayland::Client::Compositor *m_compositor; + KWayland::Client::Screencasting *m_screencasting = nullptr; + + KWayland::Server::ScreencastingInterface *m_screencastingInterface = nullptr; + + QThread *m_thread; + KWayland::Server::Display *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; +}; + +static const QString s_socketName = QStringLiteral("kwin-wayland-server-tablet-test-0"); + +void TestScreencastingInterface::initTestCase() +{ + delete m_display; + m_display = new KWayland::Server::Display(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); + QVERIFY(connectedSpy.isValid()); + m_connection->setSocketName(s_socketName); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); + + KWayland::Client::Registry registry; + QSignalSpy compositorSpy(®istry, &KWayland::Client::Registry::compositorAnnounced); + QVERIFY(compositorSpy.isValid()); + + QSignalSpy screencastingSpy(®istry, &KWayland::Client::Registry::screencastingAnnounced); + QVERIFY(screencastingSpy.isValid()); + m_screencastingInterface = m_display->createScreencastingInterface(this); + + QVERIFY(!registry.eventQueue()); + registry.setEventQueue(m_queue); + QCOMPARE(registry.eventQueue(), m_queue); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + m_compositorInterface = m_display->createCompositor(m_display); + m_compositorInterface->create(); + QVERIFY(m_compositorInterface->isValid()); + + QVERIFY(compositorSpy.wait()); + m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); + + connect(®istry, &KWayland::Client::Registry::screencastingAnnounced, this, [this, ®istry] (quint32 name, quint32 version) { + Q_ASSERT(version == 1); + qDebug() << "boop"; + m_screencasting = registry.createScreencasting(name, version, this); + }); + QVERIFY(m_screencastingInterface); + QVERIFY(screencastingSpy.wait()); + QVERIFY(m_screencasting); +} + +TestScreencastingInterface::~TestScreencastingInterface() +{ + if (m_queue) { + delete m_queue; + m_queue = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + m_connection->deleteLater(); + m_connection = nullptr; + + delete m_display; +} + +void TestScreencastingInterface::testAdd() +{ + QCOMPARE(m_screencasting->sources().count(), 0); + bool triggered = false; + auto f = [&] (const KWayland::Server::ScreencastingSource &, wl_resource *){ + triggered = true; + }; + auto serverSource = KWayland::Server::ScreencastingSource(1, "potatoes", "video-display", true, {0,0,200,200}, f); + m_screencastingInterface->addSource(serverSource); + QCOMPARE(m_screencasting->sources().count(), 1); +} + +QTEST_GUILESS_MAIN(TestScreencastingInterface) +#include "test_screencasting.moc" 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 @@ -259,6 +265,7 @@ PUBLIC Qt5::Gui PRIVATE Wayland::Client Qt5::Concurrent + Qt5::WaylandClient ) set_target_properties(KF5WaylandClient PROPERTIES VERSION ${KWAYLAND_VERSION_STRING} @@ -289,6 +296,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/registry.h b/src/client/registry.h --- a/src/client/registry.h +++ b/src/client/registry.h @@ -36,6 +36,7 @@ struct org_kde_kwin_blur_manager; struct org_kde_kwin_contrast_manager; struct org_kde_kwin_slide_manager; +struct org_kde_kwin_screencast_unstable_v1; struct org_kde_plasma_shell; struct org_kde_plasma_virtual_desktop_management; struct org_kde_plasma_window_management; @@ -83,6 +84,7 @@ class BlurManager; class ContrastManager; class SlideManager; +class Screencasting; class Shell; class ShmPool; class ServerSideDecorationManager; @@ -174,6 +176,7 @@ XdgShellStable, ///refers to xdg_wm_base @since 5.48 XdgDecorationUnstableV1, ///refers to zxdg_decoration_manager_v1, @since 5.54 Keystate,/// #include #include +#include /***** * How to add another interface: @@ -372,6 +374,13 @@ &org_kde_kwin_keystate_interface, &Registry::keystateAnnounced, &Registry::keystateRemoved + }}, + {Registry::Interface::Screencasting, { + 1, + QByteArrayLiteral("org_kde_kwin_screencast_unstable_v1"), + &org_kde_kwin_screencast_unstable_v1_interface, + &Registry::screencastingAnnounced, + &Registry::screencastingRemoved }} }; @@ -680,6 +689,7 @@ BIND(XdgImporterUnstableV2, zxdg_importer_v2) BIND(IdleInhibitManagerUnstableV1, zwp_idle_inhibit_manager_v1) BIND(Keystate, org_kde_kwin_keystate) +BIND(Screencasting, org_kde_kwin_screencast_unstable_v1) BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager) BIND2(BlurManager, Blur, org_kde_kwin_blur_manager) BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager) @@ -742,6 +752,7 @@ CREATE2(ShmPool, Shm) CREATE(AppMenuManager) CREATE(Keystate) +CREATE(Screencasting) CREATE(ServerSideDecorationPaletteManager) #undef CREATE 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,81 @@ +/* + 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_screencast_unstable_v1; + +namespace KWayland +{ +namespace Client +{ +class EventQueue; +class ScreencastingPrivate; +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 ScreencastingPrivate; + 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); + + void setEventQueue(EventQueue *queue); + void setup(org_kde_kwin_screencast_unstable_v1* screencasting); + void destroy(); + +Q_SIGNALS: + void initialized(); + void created(const ScreencastingSource& source, quint32 nodeid); + void failed(const ScreencastingSource& source, const QString &error); + void closed(quint32 nodeid); + void removed(); + 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,163 @@ +/* + 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 +#include +#include + +using namespace KWayland::Client; + +static const int s_version = 1; + +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::ScreencastingPrivate : public QtWayland::org_kde_kwin_screencast_unstable_v1, public QWaylandClientExtensionTemplate +{ +public: + ScreencastingPrivate(Screencasting *q) + : QWaylandClientExtensionTemplate(s_version) + , 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 ScreencastingPrivate(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; +} + +void Screencasting::setEventQueue(EventQueue *queue) +{ + Q_UNUSED(queue); +} + +void Screencasting::setup(::org_kde_kwin_screencast_unstable_v1* screencasting) +{ + d->init(screencasting); +} + +void Screencasting::destroy() +{ +} 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; diff --git a/src/server/screencasting_interface.h b/src/server/screencasting_interface.h new file mode 100644 --- /dev/null +++ b/src/server/screencasting_interface.h @@ -0,0 +1,73 @@ +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; +class OutputInterface; +class Screencasting; +class ScreencastingSourcePrivate; +class ScreencastingInterfacePrivate; + +class KWAYLANDSERVER_EXPORT ScreencastingSource +{ + Q_GADGET +public: + ScreencastingSource(); + ScreencastingSource(quint32 sourceId, const QString &description, const QString &iconName, bool isOutput, const QRect &geometry, std::function); + ScreencastingSource(const ScreencastingSource& other); + virtual ~ScreencastingSource(); + + quint32 sourceId() const; + QString iconName() const; + QString description() const; + bool isOutput() const; + QRect geometry() const; + void call(wl_resource *r); + + bool operator!=(const ScreencastingSource &other) const; + +private: + friend class ScreencastingSourcePrivate; + QSharedPointer d; +}; + +class KWAYLANDSERVER_EXPORT ScreencastingInterface : public QObject +{ + Q_OBJECT +public: + virtual ~ScreencastingInterface(); + + void addSource(const ScreencastingSource &source); + void removeSource(const ScreencastingSource &source); + + void sendCreated(wl_resource *resource, quint32 nodeid, quint32 sourceid); + void sendFailed(wl_resource *resource, quint32 sourceid, const QString &error); + void sendStopped(wl_resource *resource); + +Q_SIGNALS: + void stop(quint32 nodeid); + +private: + explicit ScreencastingInterface(Display *display, QObject *parent = nullptr); + friend class Display; + QScopedPointer d; +}; + +} +} diff --git a/src/server/screencasting_interface.cpp b/src/server/screencasting_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/screencasting_interface.cpp @@ -0,0 +1,162 @@ +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "screencasting_interface.h" +#include "display.h" +#include + +#include "qwayland-server-org-kde-kwin-screencast-unstable-v1.h" + +using namespace KWayland::Server; + +static int s_version = 1; + +class KWayland::Server::ScreencastingSourcePrivate +{ +public: + ScreencastingSourcePrivate(quint32 sourceId, const QString &description, const QString &iconName, bool isOutput, const QRect &geometry, std::function call) + : sourceId(sourceId) + , description(description) + , iconName(iconName) + , isOutput(isOutput) + , geometry(geometry) + , call(call) + {} + + const quint32 sourceId; + const QString description; + const QString iconName; + const bool isOutput; + const QRect geometry; + const std::function call; +}; + +ScreencastingSource::ScreencastingSource() + : d(new ScreencastingSourcePrivate(0, {}, {}, false, {}, [] (const ScreencastingSource &, wl_resource *) {})) +{} + +ScreencastingSource::ScreencastingSource(quint32 sourceId, const QString &description, const QString &iconName, bool isOutput, const QRect &geometry, std::function call) + : d(new ScreencastingSourcePrivate(sourceId, description, iconName, isOutput, geometry, call)) +{} + +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; +} + +void KWayland::Server::ScreencastingSource::call(wl_resource* r) +{ + d->call(*this, r); +} + +class KWayland::Server::ScreencastingInterfacePrivate : public QtWaylandServer::org_kde_kwin_screencast_unstable_v1 +{ +public: + ScreencastingInterfacePrivate(Display *display, ScreencastingInterface* q) + : QtWaylandServer::org_kde_kwin_screencast_unstable_v1(*display, s_version) + , q(q) + {} + + void org_kde_kwin_screencast_unstable_v1_bind_resource(Resource *resource) override { + for (auto source : qAsConst(m_sources)) { + sendSource(resource, source); + } + send_done(); + } + + void org_kde_kwin_screencast_unstable_v1_create(Resource *resource, uint32_t sourceId) override { + auto it = std::find_if(m_sources.begin(), m_sources.end(), [sourceId] (const ScreencastingSource &source) { return source.sourceId() == sourceId; }); + if (it == m_sources.end()) { + q->sendFailed(resource->handle, sourceId, QObject::tr("Source %1 does not exist").arg(sourceId)); + return; + } + + it->call(resource->handle); + } + + void org_kde_kwin_screencast_unstable_v1_close(Resource *, uint32_t nodeId) override { + Q_EMIT q->stop(nodeId); + } + + void sendSource(Resource* resource, const ScreencastingSource &source) { + const QRect geometry = source.geometry(); + send_addSource(resource->handle, source.sourceId(), source.description(), source.iconName(), source.isOutput(), geometry.x(), geometry.y(), geometry.width(), geometry.height()); + } + QVector m_sources; + ScreencastingInterface *const q; +}; + +ScreencastingInterface::ScreencastingInterface(Display *display, QObject *parent) + : QObject(parent) + , d(new ScreencastingInterfacePrivate(display, this)) +{ +} + +ScreencastingInterface::~ScreencastingInterface() = default; + +void ScreencastingInterface::addSource(const ScreencastingSource &source) +{ + d->m_sources << source; + + for (auto r : d->resourceMap()) { + d->sendSource(r, source); + } +} + +void ScreencastingInterface::removeSource(const ScreencastingSource &sourceToRemove) +{ + auto it = std::find_if(d->m_sources.begin(), d->m_sources.end(), [&sourceToRemove] (const ScreencastingSource &source) { return source.sourceId() == sourceToRemove.sourceId(); }); + if (it == d->m_sources.end()) { + return; + } + + d->m_sources.erase(it); + + for (auto r : d->resourceMap()) { + d->send_removeSource(r->handle, sourceToRemove.sourceId()); + } +} + +void ScreencastingInterface::sendCreated(wl_resource *resource, quint32 nodeid, quint32 sourceid) +{ + d->send_created(resource, nodeid, sourceid); +} + +void ScreencastingInterface::sendFailed(wl_resource* resource, quint32 sourceid, const QString& error) +{ + d->send_failed(resource, sourceid, error); +}