Implement zkde_screencast_stream_unstable_v1

This commit is contained in:
Aleix Pol 2020-06-16 16:58:21 +02:00 committed by Aleix Pol Gonzalez
parent 97219ec2d5
commit e089092b54
7 changed files with 396 additions and 0 deletions

View file

@ -68,6 +68,7 @@ set(SERVER_LIB_SRCS
xdgforeign_v2_interface.cpp xdgforeign_v2_interface.cpp
xdgoutput_interface.cpp xdgoutput_interface.cpp
xdgshell_interface.cpp xdgshell_interface.cpp
screencast_interface.cpp
) )
ecm_qt_declare_logging_category(SERVER_LIB_SRCS ecm_qt_declare_logging_category(SERVER_LIB_SRCS
@ -258,6 +259,11 @@ ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
BASENAME wp-primary-selection-unstable-v1 BASENAME wp-primary-selection-unstable-v1
) )
ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/screencast.xml
BASENAME zkde-screencast-unstable-v1
)
set(SERVER_GENERATED_SRCS set(SERVER_GENERATED_SRCS
${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h
@ -316,6 +322,8 @@ set(SERVER_GENERATED_SRCS
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-tablet-unstable-v2.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-tablet-unstable-v2.cpp
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-keyboard-shortcuts-inhibit-unstable-v1.h ${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-keyboard-shortcuts-inhibit-unstable-v1.h
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-screencast-unstable-v1.h
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-screencast-unstable-v1.cpp
) )
set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON) set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON)
@ -397,6 +405,7 @@ set(SERVER_LIB_HEADERS
relativepointer_interface.h relativepointer_interface.h
remote_access_interface.h remote_access_interface.h
resource.h resource.h
screencast_interface.h
seat_interface.h seat_interface.h
server_decoration_interface.h server_decoration_interface.h
server_decoration_palette_interface.h server_decoration_palette_interface.h

View file

@ -75,3 +75,16 @@ add_executable(testViewporterInterface test_viewporter_interface.cpp ${VIEWPORTE
target_link_libraries(testViewporterInterface Qt5::Test Plasma::KWaylandServer KF5::WaylandClient Wayland::Client) target_link_libraries(testViewporterInterface Qt5::Test Plasma::KWaylandServer KF5::WaylandClient Wayland::Client)
add_test(NAME kwayland-testViewporterInterface COMMAND testViewporterInterface) add_test(NAME kwayland-testViewporterInterface COMMAND testViewporterInterface)
ecm_mark_as_test(testViewporterInterface) ecm_mark_as_test(testViewporterInterface)
########################################################
# Test ScreencastInterface
########################################################
ecm_add_qtwayland_client_protocol(SCREENCAST_SRCS
PROTOCOL PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/screencast.xml
BASENAME zkde-screencast-unstable-v1
)
add_executable(testScreencastInterface test_screencast.cpp ${SCREENCAST_SRCS})
target_link_libraries(testScreencastInterface Qt5::Test Plasma::KWaylandServer Wayland::Client KF5::WaylandClient)
add_test(NAME kwayland-testScreencastInterface COMMAND testScreencastInterface)
ecm_mark_as_test(testScreencastInterface)

View file

@ -0,0 +1,176 @@
/*
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
*/
// Qt
#include <QHash>
#include <QThread>
#include <QtTest>
#include <wayland-client.h>
// WaylandServer
#include "../../src/server/compositor_interface.h"
#include "../../src/server/display.h"
#include "../../src/server/seat_interface.h"
#include "../../src/server/screencast_interface.h"
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/event_queue.h>
#include <KWayland/Client/registry.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/compositor.h>
#include "qwayland-zkde-screencast-unstable-v1.h"
class ScreencastStream : public QObject, public QtWayland::zkde_screencast_stream_unstable_v1
{
Q_OBJECT
public:
ScreencastStream(::zkde_screencast_stream_unstable_v1 *obj, QObject *parent)
: QObject(parent)
, zkde_screencast_stream_unstable_v1(obj)
{
}
void zkde_screencast_stream_unstable_v1_created(uint32_t node) override {
Q_EMIT created(node);
}
Q_SIGNALS:
void created(quint32 node);
};
class Screencast : public QObject, public QtWayland::zkde_screencast_unstable_v1
{
Q_OBJECT
public:
Screencast(QObject* parent)
: QObject(parent)
{
}
ScreencastStream* createWindowStream(const QString &uuid) {
return new ScreencastStream(stream_window(uuid, 2), this);
}
};
class TestScreencastInterface : public QObject
{
Q_OBJECT
public:
TestScreencastInterface()
{
}
~TestScreencastInterface() override;
private Q_SLOTS:
void initTestCase();
void testCreate();
private:
KWayland::Client::ConnectionThread *m_connection;
KWayland::Client::EventQueue *m_queue = nullptr;
Screencast *m_screencast = nullptr;
KWaylandServer::ScreencastInterface *m_screencastInterface = nullptr;
QPointer<KWaylandServer::ScreencastStreamInterface> m_triggered = nullptr;
QThread *m_thread;
KWaylandServer::Display *m_display = nullptr;
};
static const QString s_socketName = QStringLiteral("kwin-wayland-server-screencast-test-0");
void TestScreencastInterface::initTestCase()
{
delete m_display;
m_display = new KWaylandServer::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 screencastSpy(&registry, &KWayland::Client::Registry::interfacesAnnounced);
QVERIFY(screencastSpy.isValid());
m_screencastInterface = m_display->createScreencastInterface(this);
connect(m_screencastInterface, &KWaylandServer::ScreencastInterface::windowScreencastRequested, this, [this] (KWaylandServer::ScreencastStreamInterface* stream, const QString &winid) {
Q_UNUSED(winid);
stream->sendCreated(123);
m_triggered = stream;
});
connect(&registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, &registry] (const QByteArray &interfaceName, quint32 name, quint32 version) {
if (interfaceName != "zkde_screencast_unstable_v1")
return;
Q_ASSERT(version == 1);
m_screencast = new Screencast(this);
m_screencast->init(&*registry, name, version);
});
registry.setEventQueue(m_queue);
registry.create(m_connection->display());
QVERIFY(registry.isValid());
registry.setup();
wl_display_flush(m_connection->display());
QVERIFY(m_screencastInterface);
QVERIFY(m_screencast || screencastSpy.wait());
QVERIFY(m_screencast);
}
TestScreencastInterface::~TestScreencastInterface()
{
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 TestScreencastInterface::testCreate()
{
auto stream = m_screencast->createWindowStream("3");
QVERIFY(stream);
QSignalSpy spyWorking(stream, &ScreencastStream::created);
QVERIFY(spyWorking.count() || spyWorking.wait());
QVERIFY(m_triggered);
QSignalSpy spyStop(m_triggered, &KWaylandServer::ScreencastStreamInterface::finished);
stream->close();
QVERIFY(spyStop.count() || spyStop.wait());
}
QTEST_GUILESS_MAIN(TestScreencastInterface)
#include "test_screencast.moc"

View file

@ -33,6 +33,7 @@
#include "relativepointer_interface_p.h" #include "relativepointer_interface_p.h"
#include "remote_access_interface.h" #include "remote_access_interface.h"
#include "seat_interface.h" #include "seat_interface.h"
#include "screencast_interface.h"
#include "server_decoration_interface.h" #include "server_decoration_interface.h"
#include "server_decoration_palette_interface.h" #include "server_decoration_palette_interface.h"
#include "shadow_interface.h" #include "shadow_interface.h"
@ -341,6 +342,13 @@ ServerSideDecorationManagerInterface *Display::createServerSideDecorationManager
return d; return d;
} }
ScreencastInterface *Display::createScreencastInterface(QObject *parent)
{
auto s = new ScreencastInterface(this, parent);
connect(this, &Display::aboutToTerminate, s, [s] { delete s; });
return s;
}
TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent) TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent)
{ {
TextInputManagerInterface *t = nullptr; TextInputManagerInterface *t = nullptr;

View file

@ -81,6 +81,7 @@ class DataControlDeviceManagerV1Interface;
class PrimarySelectionDeviceManagerV1Interface; class PrimarySelectionDeviceManagerV1Interface;
class KeyboardShortcutsInhibitManagerV1Interface; class KeyboardShortcutsInhibitManagerV1Interface;
class ViewporterInterface; class ViewporterInterface;
class ScreencastInterface;
/** /**
* @brief Class holding the Wayland server display loop. * @brief Class holding the Wayland server display loop.
@ -333,6 +334,11 @@ public:
*/ */
PrimarySelectionDeviceManagerV1Interface *createPrimarySelectionDeviceManagerV1(QObject *parent = nullptr); PrimarySelectionDeviceManagerV1Interface *createPrimarySelectionDeviceManagerV1(QObject *parent = nullptr);
/**
* Creates an interface to request video feeds of different compositor resources
*/
ScreencastInterface *createScreencastInterface(QObject *parent = nullptr);
/** /**
* Gets the ClientConnection for the given @p client. * Gets the ClientConnection for the given @p client.
* If there is no ClientConnection yet for the given @p client, it will be created. * If there is no ClientConnection yet for the given @p client, it will be created.

View file

@ -0,0 +1,111 @@
/*
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 "screencast_interface.h"
#include "display.h"
#include <QDebug>
#include "qwayland-server-zkde-screencast-unstable-v1.h"
using namespace KWaylandServer;
static int s_version = 1;
class KWaylandServer::ScreencastStreamInterfacePrivate : public QtWaylandServer::zkde_screencast_stream_unstable_v1
{
public:
ScreencastStreamInterfacePrivate(ScreencastStreamInterface *q)
: q(q)
{}
void zkde_screencast_stream_unstable_v1_destroy_resource(Resource *resource) override
{
Q_UNUSED(resource);
if (!stopped) {
Q_EMIT q->finished();
}
q->deleteLater();
}
void zkde_screencast_stream_unstable_v1_close(Resource * resource) override
{
Q_UNUSED(resource);
Q_EMIT q->finished();
stopped = true;
wl_resource_destroy(resource->handle);
}
bool stopped = false;
ScreencastStreamInterface *const q;
};
ScreencastStreamInterface::ScreencastStreamInterface(QObject* parent)
: QObject(parent)
, d(new ScreencastStreamInterfacePrivate(this))
{
}
ScreencastStreamInterface::~ScreencastStreamInterface() = default;
void ScreencastStreamInterface::sendCreated(quint32 nodeid)
{
d->send_created(nodeid);
}
void ScreencastStreamInterface::sendFailed(const QString& error)
{
d->send_failed(error);
}
void ScreencastStreamInterface::sendClosed()
{
if (!d->stopped) {
d->send_closed();
}
}
class KWaylandServer::ScreencastInterfacePrivate : public QtWaylandServer::zkde_screencast_unstable_v1
{
public:
ScreencastInterfacePrivate(Display *display, ScreencastInterface* q)
: QtWaylandServer::zkde_screencast_unstable_v1(*display, s_version)
, q(q)
{
}
ScreencastStreamInterface *createStream(Resource *resource, quint32 streamid) const
{
auto stream = new ScreencastStreamInterface(q);
stream->d->init(resource->client(), streamid, resource->version());
return stream;
}
void zkde_screencast_unstable_v1_stream_output(Resource *resource, uint32_t streamid, struct ::wl_resource *output, uint32_t pointer) override
{
Q_EMIT q->outputScreencastRequested(createStream(resource, streamid), output, ScreencastInterface::CursorMode(pointer));
}
void zkde_screencast_unstable_v1_stream_window(Resource *resource, uint32_t streamid, const QString &uuid, uint32_t pointer) override
{
Q_EMIT q->windowScreencastRequested(createStream(resource, streamid), uuid, ScreencastInterface::CursorMode(pointer));
}
void zkde_screencast_unstable_v1_destroy(Resource * resource) override
{
wl_resource_destroy(resource->handle);
}
ScreencastInterface *const q;
};
ScreencastInterface::ScreencastInterface(Display *display, QObject *parent)
: QObject(parent)
, d(new ScreencastInterfacePrivate(display, this))
{
}
ScreencastInterface::~ScreencastInterface() = default;

View file

@ -0,0 +1,73 @@
/*
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 <QRect>
#include <QScopedPointer>
#include <QSharedPointer>
#include <functional>
#include <KWaylandServer/kwaylandserver_export.h>
#include <wayland-server.h>
struct wl_resource;
namespace KWaylandServer
{
class Display;
class OutputInterface;
class Screencast;
class ScreencastSourcePrivate;
class ScreencastInterfacePrivate;
class ScreencastStreamInterfacePrivate;
class ScreencastStreamInterface;
class KWAYLANDSERVER_EXPORT ScreencastStreamInterface : public QObject
{
Q_OBJECT
public:
~ScreencastStreamInterface() override;
void sendCreated(quint32 nodeid);
void sendFailed(const QString &error);
void sendClosed();
Q_SIGNALS:
void finished();
private:
friend class ScreencastInterfacePrivate;
explicit ScreencastStreamInterface(QObject *parent);
QScopedPointer<ScreencastStreamInterfacePrivate> d;
};
class KWAYLANDSERVER_EXPORT ScreencastInterface : public QObject
{
Q_OBJECT
public:
virtual ~ScreencastInterface();
enum CursorMode {
Hidden = 1,
Embedded = 2,
Metadata = 4,
};
Q_ENUM(CursorMode);
Q_SIGNALS:
void outputScreencastRequested(ScreencastStreamInterface* stream, ::wl_resource *output, CursorMode mode);
void windowScreencastRequested(ScreencastStreamInterface* stream, const QString &winid, CursorMode mode);
private:
explicit ScreencastInterface(Display *display, QObject *parent = nullptr);
friend class Display;
QScopedPointer<ScreencastInterfacePrivate> d;
};
}