Add the activity management protocol server implementation

This commit is contained in:
Kevin Ottens 2021-03-16 18:33:46 +01:00 committed by Kevin Ottens
parent b822c1e63a
commit d92fa7f140
4 changed files with 296 additions and 1 deletions

View file

@ -353,6 +353,17 @@ target_link_libraries( testPlasmaVirtualDesktop Qt::Test Qt::Gui KF5::WaylandCli
add_test(NAME kwayland-testPlasmaVirtualDesktop COMMAND testPlasmaVirtualDesktop)
ecm_mark_as_test(testPlasmaVirtualDesktop)
########################################################
# Test Activities
########################################################
set( testPlasmaActivities_SRCS
test_plasma_activities.cpp
)
add_executable(testPlasmaActivities ${testPlasmaActivities_SRCS})
target_link_libraries( testPlasmaActivities Qt::Test Qt::Gui KF5::WaylandClient Plasma::KWaylandServer)
add_test(NAME kwayland-testPlasmaActivities COMMAND testPlasmaActivities)
ecm_mark_as_test(testPlasmaActivities)
########################################################
# Test XDG Output
########################################################

View file

@ -0,0 +1,199 @@
/*
SPDX-FileCopyrightText: 2021 Kevin Ottens <kevin.ottens@enioka.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
// Qt
#include <QtTest>
// KWin
#include "KWayland/Client/compositor.h"
#include "KWayland/Client/connection_thread.h"
#include "KWayland/Client/event_queue.h"
#include "KWayland/Client/region.h"
#include "KWayland/Client/registry.h"
#include "KWayland/Client/surface.h"
#include "../../src/server/display.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/plasmawindowmanagement_interface.h"
#include "KWayland/Client/plasmawindowmanagement.h"
using namespace KWayland::Client;
class TestActivities : public QObject
{
Q_OBJECT
public:
explicit TestActivities(QObject *parent = nullptr);
private Q_SLOTS:
void init();
void cleanup();
void testEnterLeaveActivity();
private:
KWaylandServer::Display *m_display;
KWaylandServer::CompositorInterface *m_compositorInterface;
KWaylandServer::PlasmaWindowManagementInterface *m_windowManagementInterface;
KWaylandServer::PlasmaWindowInterface *m_windowInterface;
KWayland::Client::ConnectionThread *m_connection;
KWayland::Client::Compositor *m_compositor;
KWayland::Client::EventQueue *m_queue;
KWayland::Client::PlasmaWindowManagement *m_windowManagement;
KWayland::Client::PlasmaWindow *m_window;
QThread *m_thread;
};
static const QString s_socketName = QStringLiteral("kwayland-test-wayland-activities-0");
TestActivities::TestActivities(QObject *parent)
: QObject(parent)
, m_display(nullptr)
, m_compositorInterface(nullptr)
, m_connection(nullptr)
, m_compositor(nullptr)
, m_queue(nullptr)
, m_thread(nullptr)
{
}
void TestActivities::init()
{
using namespace KWaylandServer;
delete m_display;
m_display = new Display(this);
m_display->addSocketName(s_socketName);
m_display->start();
QVERIFY(m_display->isRunning());
// setup connection
m_connection = new KWayland::Client::ConnectionThread;
QSignalSpy connectedSpy(m_connection, &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());
Registry registry;
QSignalSpy compositorSpy(&registry, &Registry::compositorAnnounced);
QVERIFY(compositorSpy.isValid());
QSignalSpy windowManagementSpy(&registry, &Registry::plasmaWindowManagementAnnounced);
QVERIFY(windowManagementSpy.isValid());
QVERIFY(!registry.eventQueue());
registry.setEventQueue(m_queue);
QCOMPARE(registry.eventQueue(), m_queue);
registry.create(m_connection->display());
QVERIFY(registry.isValid());
registry.setup();
m_compositorInterface = new CompositorInterface(m_display, m_display);
QVERIFY(compositorSpy.wait());
m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
m_windowManagementInterface = new PlasmaWindowManagementInterface(m_display, m_display);
QVERIFY(windowManagementSpy.wait());
m_windowManagement = registry.createPlasmaWindowManagement(windowManagementSpy.first().first().value<quint32>(), windowManagementSpy.first().last().value<quint32>(), this);
QSignalSpy windowSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated);
QVERIFY(windowSpy.isValid());
m_windowInterface = m_windowManagementInterface->createWindow(this, QUuid::createUuid());
m_windowInterface->setPid(1337);
QVERIFY(windowSpy.wait());
m_window = windowSpy.first().first().value<PlasmaWindow *>();
}
void TestActivities::cleanup()
{
#define CLEANUP(variable) \
if (variable) { \
delete variable; \
variable = nullptr; \
}
CLEANUP(m_compositor)
CLEANUP(m_windowInterface)
CLEANUP(m_windowManagement)
CLEANUP(m_queue)
if (m_connection) {
m_connection->deleteLater();
m_connection = nullptr;
}
if (m_thread) {
m_thread->quit();
m_thread->wait();
delete m_thread;
m_thread = nullptr;
}
CLEANUP(m_compositorInterface)
CLEANUP(m_windowManagementInterface)
CLEANUP(m_display)
#undef CLEANUP
}
void TestActivities::testEnterLeaveActivity()
{
QSignalSpy enterRequestedSpy(m_windowInterface, &KWaylandServer::PlasmaWindowInterface::enterPlasmaActivityRequested);
m_window->requestEnterActivity(QStringLiteral("0-1"));
enterRequestedSpy.wait();
QCOMPARE(enterRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1"));
QSignalSpy activityEnteredSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaActivityEntered);
//agree to the request
m_windowInterface->addPlasmaActivity(QStringLiteral("0-1"));
QCOMPARE(m_windowInterface->plasmaActivities().length(), 1);
QCOMPARE(m_windowInterface->plasmaActivities().first(), QStringLiteral("0-1"));
//check if the client received the enter
activityEnteredSpy.wait();
QCOMPARE(activityEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-1"));
QCOMPARE(m_window->plasmaActivities().length(), 1);
QCOMPARE(m_window->plasmaActivities().first(), QStringLiteral("0-1"));
//add another activity, server side
m_windowInterface->addPlasmaActivity(QStringLiteral("0-3"));
activityEnteredSpy.wait();
QCOMPARE(activityEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-3"));
QCOMPARE(m_windowInterface->plasmaActivities().length(), 2);
QCOMPARE(m_window->plasmaActivities().length(), 2);
QCOMPARE(m_window->plasmaActivities()[1], QStringLiteral("0-3"));
//remove an activity
QSignalSpy leaveRequestedSpy(m_windowInterface, &KWaylandServer::PlasmaWindowInterface::leavePlasmaActivityRequested);
m_window->requestLeaveActivity(QStringLiteral("0-1"));
leaveRequestedSpy.wait();
QCOMPARE(leaveRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1"));
QSignalSpy activityLeftSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaActivityLeft);
//agree to the request
m_windowInterface->removePlasmaActivity(QStringLiteral("0-1"));
QCOMPARE(m_windowInterface->plasmaActivities().length(), 1);
QCOMPARE(m_windowInterface->plasmaActivities().first(), QStringLiteral("0-3"));
//check if the client received the leave
activityLeftSpy.wait();
QCOMPARE(activityLeftSpy.takeFirst().at(0).toString(), QStringLiteral("0-1"));
QCOMPARE(m_window->plasmaActivities().length(), 1);
QCOMPARE(m_window->plasmaActivities().first(), QStringLiteral("0-3"));
}
QTEST_GUILESS_MAIN(TestActivities)
#include "test_plasma_activities.moc"

View file

@ -23,7 +23,7 @@
namespace KWaylandServer
{
static const quint32 s_version = 13;
static const quint32 s_version = 14;
class PlasmaWindowManagementInterfacePrivate : public QtWaylandServer::org_kde_plasma_window_management
{
@ -78,6 +78,7 @@ public:
PlasmaWindowInterface *parentWindow = nullptr;
QMetaObject::Connection parentWindowDestroyConnection;
QStringList plasmaVirtualDesktops;
QStringList plasmaActivities;
QRect geometry;
PlasmaWindowInterface *q;
QString m_title;
@ -105,6 +106,8 @@ protected:
void org_kde_plasma_window_request_enter_virtual_desktop(Resource *resource, const QString &id) override;
void org_kde_plasma_window_request_enter_new_virtual_desktop(Resource *resource) override;
void org_kde_plasma_window_request_leave_virtual_desktop(Resource *resource, const QString &id) override;
void org_kde_plasma_window_request_enter_activity(Resource *resource, const QString &id) override;
void org_kde_plasma_window_request_leave_activity(Resource *resource, const QString &id) override;
};
PlasmaWindowManagementInterfacePrivate::PlasmaWindowManagementInterfacePrivate(PlasmaWindowManagementInterface *_q, Display *display)
@ -343,6 +346,11 @@ void PlasmaWindowInterfacePrivate::org_kde_plasma_window_bind_resource(Resource
for (const auto &desk : plasmaVirtualDesktops) {
send_virtual_desktop_entered(resource->handle, desk);
}
for (const auto &activity : plasmaActivities) {
if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ACTIVITY_ENTERED_SINCE_VERSION) {
send_activity_entered(resource->handle, activity);
}
}
if (!m_appId.isEmpty()) {
send_app_id_changed(resource->handle, m_appId);
}
@ -461,6 +469,18 @@ void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_leave_virtual_d
emit q->leavePlasmaVirtualDesktopRequested(id);
}
void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_enter_activity(Resource *resource, const QString &id)
{
Q_UNUSED(resource)
emit q->enterPlasmaActivityRequested(id);
}
void PlasmaWindowInterfacePrivate::org_kde_plasma_window_request_leave_activity(Resource *resource, const QString &id)
{
Q_UNUSED(resource)
emit q->leavePlasmaActivityRequested(id);
}
void PlasmaWindowInterfacePrivate::setTitle(const QString &title)
{
if (m_title == title) {
@ -915,6 +935,41 @@ QStringList PlasmaWindowInterface::plasmaVirtualDesktops() const
return d->plasmaVirtualDesktops;
}
void PlasmaWindowInterface::addPlasmaActivity(const QString &id)
{
if (d->plasmaActivities.contains(id)) {
return;
}
d->plasmaActivities << id;
const auto clientResources = d->resourceMap();
for (auto resource : clientResources) {
if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ACTIVITY_ENTERED_SINCE_VERSION) {
d->send_activity_entered(resource->handle, id);
}
}
}
void PlasmaWindowInterface::removePlasmaActivity(const QString &id)
{
if (!d->plasmaActivities.removeOne(id)) {
return;
}
const auto clientResources = d->resourceMap();
for (auto resource : clientResources) {
if (resource->version() >= ORG_KDE_PLASMA_WINDOW_ACTIVITY_LEFT_SINCE_VERSION) {
d->send_activity_left(resource->handle, id);
}
}
}
QStringList PlasmaWindowInterface::plasmaActivities() const
{
return d->plasmaActivities;
}
void PlasmaWindowInterface::setShadeable(bool set)
{
d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set);

View file

@ -174,6 +174,24 @@ public:
*/
QStringList plasmaVirtualDesktops() const;
/**
* Adds an activity to this window: a window can be on
* an arbitrary subset of activities.
* If it's on none it will be considered on all activities.
*/
void addPlasmaActivity(const QString &id);
/**
* Removes an activity from a window
*/
void removePlasmaActivity(const QString &id);
/**
* The ids of all the activities currently associated with this window.
* When an activity is deleted it will be automatically removed from this list
*/
QStringList plasmaActivities() const;
/**
* Set the application menu D-BUS service name and object path for the window.
*/
@ -242,6 +260,18 @@ Q_SIGNALS:
*/
void leavePlasmaVirtualDesktopRequested(const QString &desktop);
/**
* Emitted when the client wishes this window to enter an activity.
* The server will decide whether to consent this request
*/
void enterPlasmaActivityRequested(const QString &activity);
/**
* Emitted when the client wishes to remove this window from an activity.
* The server will decide whether to consent this request
*/
void leavePlasmaActivityRequested(const QString &activity);
private:
friend class PlasmaWindowManagementInterface;
friend class PlasmaWindowInterfacePrivate;