Commit f44ff394 authored by Marco Martin's avatar Marco Martin
Browse files

Add KWayland virtual desktop protocol

Summary:
Implement the virtual desktop protocol discussed in
T4457 xml protocol, client and server part.

The PlasmaVirtualDesktopManagement interface manages the desktops
instantiation and layout, each desktop is a PlasmaVirtualDesktop
instance which contains unique id, name and position.

PlasmaWindow has new events: plasmaVirtualDesktopEntered
and plasmaVirtualDesktopLeft when a window enters or leaves a desktop,
and desktops as the list of desktops is in. A window can be on
any subset of desktops, if the list is empty, it's considered on all desktops.

Test Plan: Autotest

Reviewers: #kwin, #plasma, graesslin, hein, davidedmundson

Reviewed By: #kwin, #plasma, davidedmundson

Subscribers: davidedmundson, zzag, bshah, romangg, kde-frameworks-devel

Tags: #frameworks

Maniphest Tasks: T4457

Differential Revision: https://phabricator.kde.org/D12820
parent 2ba9f0c3
......@@ -25,6 +25,7 @@ set(SERVER_LIB_SRCS
output_interface.cpp
pointer_interface.cpp
plasmashell_interface.cpp
plasmavirtualdesktop_interface.cpp
plasmawindowmanagement_interface.cpp
pointerconstraints_interface.cpp
pointerconstraints_interface_v1.cpp
......@@ -75,6 +76,11 @@ ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
BASENAME plasma-shell
)
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-virtual-desktop.xml
BASENAME plasma-virtual-desktop
)
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml
BASENAME plasma-window-management
......@@ -196,6 +202,9 @@ set(SERVER_GENERATED_SRCS
${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-virtual-desktop-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-virtual-desktop-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-client-protocol.h
......@@ -300,6 +309,7 @@ set(SERVER_LIB_HEADERS
pointerconstraints_interface.h
pointergestures_interface.h
plasmashell_interface.h
plasmavirtualdesktop_interface.h
plasmawindowmanagement_interface.h
qtsurfaceextension_interface.h
region_interface.h
......
......@@ -441,6 +441,17 @@ target_link_libraries( testRemoteAccess Qt5::Test Qt5::Gui KF5::WaylandClient KF
add_test(NAME kwayland-testRemoteAccess COMMAND testRemoteAccess)
ecm_mark_as_test(testRemoteAccess)
########################################################
# Test VirtualDesktop
########################################################
set( testPlasmaVirtualDesktop_SRCS
test_plasma_virtual_desktop.cpp
)
add_executable(testPlasmaVirtualDesktop ${testPlasmaVirtualDesktop_SRCS})
target_link_libraries( testPlasmaVirtualDesktop Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer)
add_test(NAME kwayland-testPlasmaVirtualDesktop COMMAND testPlasmaVirtualDesktop)
ecm_mark_as_test(testPlasmaVirtualDesktop)
########################################################
# Test XDG Output
########################################################
......@@ -451,3 +462,4 @@ add_executable(testXdgOutput ${testXdgOutput_SRCS})
target_link_libraries( testXdgOutput Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server)
add_test(NAME kwayland-testXdgOutput COMMAND testXdgOutput)
ecm_mark_as_test(testXdgOutput)
This diff is collapsed.
......@@ -51,6 +51,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include "xdgshell_stable_interface_p.h"
#include "appmenu_interface.h"
#include "server_decoration_palette_interface.h"
#include "plasmavirtualdesktop_interface.h"
#include "xdgoutput_interface.h"
#include <QCoreApplication>
......@@ -462,6 +463,14 @@ ServerSideDecorationPaletteManagerInterface *Display::createServerSideDecoration
return b;
}
PlasmaVirtualDesktopManagementInterface *Display::createPlasmaVirtualDesktopManagement(QObject *parent)
{
auto b = new PlasmaVirtualDesktopManagementInterface(this, parent);
connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; });
return b;
}
XdgOutputManagerInterface *Display::createXdgOutputManager(QObject *parent)
{
auto b = new XdgOutputManagerInterface(this, parent);
......
......@@ -86,6 +86,7 @@ class PointerConstraintsInterface;
class XdgForeignInterface;
class AppMenuManagerInterface;
class ServerSideDecorationPaletteManagerInterface;
class PlasmaVirtualDesktopManagementInterface;
class XdgOutputManagerInterface;
/**
......@@ -268,6 +269,14 @@ public:
XdgOutputManagerInterface *createXdgOutputManager(QObject *parent = nullptr);
/**
* Creates the PlasmaVirtualDesktopManagementInterface in interface @p version.
*
* @returns The created manager object
* @since 5.46
**/
PlasmaVirtualDesktopManagementInterface *createPlasmaVirtualDesktopManagement(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.
......
/****************************************************************************
Copyright 2018 Marco Martin <notmart@gmail.com>
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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#include "plasmavirtualdesktop_interface.h"
#include "display.h"
#include "global_p.h"
#include "resource_p.h"
#include <QDebug>
#include <QTimer>
#include <wayland-server.h>
#include <wayland-plasma-virtual-desktop-server-protocol.h>
namespace KWayland
{
namespace Server
{
class Q_DECL_HIDDEN PlasmaVirtualDesktopInterface::Private
{
public:
Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c);
~Private();
void createResource(wl_resource *parent, quint32 serial);
PlasmaVirtualDesktopInterface *q;
PlasmaVirtualDesktopManagementInterface *vdm;
QVector<wl_resource*> resources;
QString id;
QString name;
bool active = false;
private:
static void unbind(wl_resource *resource);
static void requestActivateCallback(wl_client *client, wl_resource *resource);
static Private *cast(wl_resource *resource) {
return reinterpret_cast<Private*>(wl_resource_get_user_data(resource));
}
wl_listener listener;
static const struct org_kde_plasma_virtual_desktop_interface s_interface;
};
class Q_DECL_HIDDEN PlasmaVirtualDesktopManagementInterface::Private : public Global::Private
{
public:
Private(PlasmaVirtualDesktopManagementInterface *q, Display *d);
QVector<wl_resource*> resources;
QList<PlasmaVirtualDesktopInterface*> desktops;
quint32 rows = 0;
quint32 columns = 0;
inline QList<PlasmaVirtualDesktopInterface*>::const_iterator constFindDesktop(const QString &id);
inline QList<PlasmaVirtualDesktopInterface*>::iterator findDesktop(const QString &id);
private:
void bind(wl_client *client, uint32_t version, uint32_t id) override;
static void unbind(wl_resource *resource);
static Private *cast(wl_resource *r) {
return reinterpret_cast<Private*>(wl_resource_get_user_data(r));
}
static void getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id);
static void requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position);
static void requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id);
PlasmaVirtualDesktopManagementInterface *q;
static const struct org_kde_plasma_virtual_desktop_management_interface s_interface;
static const quint32 s_version;
};
const quint32 PlasmaVirtualDesktopManagementInterface::Private::s_version = 1;
#ifndef DOXYGEN_SHOULD_SKIP_THIS
const struct org_kde_plasma_virtual_desktop_management_interface PlasmaVirtualDesktopManagementInterface::Private::s_interface = {
getVirtualDesktopCallback,
requestCreateVirtualDesktopCallback,
requestRemoveVirtualDesktopCallback
};
#endif
inline QList<PlasmaVirtualDesktopInterface*>::const_iterator PlasmaVirtualDesktopManagementInterface::Private::constFindDesktop(const QString &id)
{
return std::find_if( desktops.constBegin(),
desktops.constEnd(),
[id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } );
}
inline QList<PlasmaVirtualDesktopInterface*>::iterator PlasmaVirtualDesktopManagementInterface::Private::findDesktop(const QString &id)
{
return std::find_if( desktops.begin(),
desktops.end(),
[id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } );
}
void PlasmaVirtualDesktopManagementInterface::Private::getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id)
{
Q_UNUSED(client)
auto s = cast(resource);
auto i = s->constFindDesktop(QString::fromUtf8(id));
if (i == s->desktops.constEnd()) {
return;
}
(*i)->d->createResource(resource, serial);
}
void PlasmaVirtualDesktopManagementInterface::Private::requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position)
{
Q_UNUSED(client)
auto s = cast(resource);
emit s->q->desktopCreateRequested(QString::fromUtf8(name), qBound<quint32>(0, position, (quint32)s->desktops.count()));
}
void PlasmaVirtualDesktopManagementInterface::Private::requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id)
{
Q_UNUSED(client)
auto s = cast(resource);
emit s->q->desktopRemoveRequested(QString::fromUtf8(id));
}
PlasmaVirtualDesktopManagementInterface::Private::Private(PlasmaVirtualDesktopManagementInterface *q, Display *d)
: Global::Private(d, &org_kde_plasma_virtual_desktop_management_interface, s_version)
, q(q)
{
}
void PlasmaVirtualDesktopManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
{
auto c = display->getConnection(client);
wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_management_interface, qMin(version, s_version), id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
resources << resource;
wl_resource_set_implementation(resource, &s_interface, this, unbind);
quint32 i = 0;
for (auto it = desktops.constBegin(); it != desktops.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_management_send_desktop_created(resource, (*it)->id().toUtf8().constData(), i++);
}
org_kde_plasma_virtual_desktop_management_send_done(resource);
}
void PlasmaVirtualDesktopManagementInterface::Private::unbind(wl_resource *resource)
{
auto dm = reinterpret_cast<Private*>(wl_resource_get_user_data(resource));
dm->resources.removeAll(resource);
}
PlasmaVirtualDesktopManagementInterface::PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent)
: Global(new Private(this, display), parent)
{
}
PlasmaVirtualDesktopManagementInterface::~PlasmaVirtualDesktopManagementInterface()
{}
PlasmaVirtualDesktopManagementInterface::Private *PlasmaVirtualDesktopManagementInterface::d_func() const
{
return reinterpret_cast<Private*>(d.data());
}
PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::desktop(const QString &id)
{
Q_D();
auto i = d->constFindDesktop(id);
if (i != d->desktops.constEnd()) {
return *i;
}
return nullptr;
}
PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::createDesktop(const QString &id, quint32 position)
{
Q_D();
auto i = d->constFindDesktop(id);
if (i != d->desktops.constEnd()) {
return *i;
}
const quint32 actualPosition = qMin(position, (quint32)d->desktops.count());
PlasmaVirtualDesktopInterface *desktop = new PlasmaVirtualDesktopInterface(this);
desktop->d->id = id;
for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_send_desktop_id(*it, id.toUtf8().constData());
}
//activate the first desktop TODO: to be done here?
if (d->desktops.isEmpty()) {
desktop->d->active = true;
}
d->desktops.insert(actualPosition, desktop);
//NOTE: this in case the desktop has been deleted but not trough removedesktop
connect(desktop, &QObject::destroyed, this,
[this, id] {
Q_D();
auto i = d->findDesktop(id);
if (i != d->desktops.end()) {
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData());
}
d->desktops.erase(i);
}
}
);
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_management_send_desktop_created(*it, id.toUtf8().constData(), actualPosition);
}
return desktop;
}
void PlasmaVirtualDesktopManagementInterface::removeDesktop(const QString &id)
{
Q_D();
auto deskIt = d->findDesktop(id);
if (deskIt == d->desktops.end()) {
return;
}
for (auto it = (*deskIt)->d->resources.constBegin(); it != (*deskIt)->d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_send_removed(*it);
}
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData());
}
d->desktops.erase(deskIt);
(*deskIt)->deleteLater();
}
QList <PlasmaVirtualDesktopInterface *> PlasmaVirtualDesktopManagementInterface::desktops() const
{
Q_D();
return d->desktops;
}
void PlasmaVirtualDesktopManagementInterface::sendDone()
{
Q_D();
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_management_send_done(*it);
}
}
//// PlasmaVirtualDesktopInterface
#ifndef DOXYGEN_SHOULD_SKIP_THIS
const struct org_kde_plasma_virtual_desktop_interface PlasmaVirtualDesktopInterface::Private::s_interface = {
requestActivateCallback
};
#endif
void PlasmaVirtualDesktopInterface::Private::requestActivateCallback(wl_client *client, wl_resource *resource)
{
Q_UNUSED(client)
auto s = cast(resource);
emit s->q->activateRequested();
}
PlasmaVirtualDesktopInterface::Private::Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c)
: q(q),
vdm(c)
{
}
PlasmaVirtualDesktopInterface::Private::~Private()
{
// need to copy, as destroy goes through the destroy listener and modifies the list as we iterate
const auto c = resources;
for (const auto &r : c) {
auto client = wl_resource_get_client(r);
org_kde_plasma_virtual_desktop_send_removed(r);
wl_resource_destroy(r);
wl_client_flush(client);
}
}
void PlasmaVirtualDesktopInterface::Private::unbind(wl_resource *resource)
{
Private *p = reinterpret_cast<Private*>(wl_resource_get_user_data(resource));
p->resources.removeAll(resource);
}
void PlasmaVirtualDesktopInterface::Private::createResource(wl_resource *parent, quint32 serial)
{
ClientConnection *c = vdm->display()->getConnection(wl_resource_get_client(parent));
wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_interface, wl_resource_get_version(parent), serial);
if (!resource) {
return;
}
wl_resource_set_implementation(resource, &s_interface, this, unbind);
resources << resource;
org_kde_plasma_virtual_desktop_send_desktop_id(resource, id.toUtf8().constData());
if (!name.isEmpty()) {
org_kde_plasma_virtual_desktop_send_name(resource, name.toUtf8().constData());
}
if (active) {
org_kde_plasma_virtual_desktop_send_activated(resource);
}
c->flush();
}
PlasmaVirtualDesktopInterface::PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent)
: QObject(parent),
d(new Private(this, parent))
{
}
PlasmaVirtualDesktopInterface::~PlasmaVirtualDesktopInterface()
{}
QString PlasmaVirtualDesktopInterface::id() const
{
return d->id;
}
void PlasmaVirtualDesktopInterface::setName(const QString &name)
{
if (d->name == name) {
return;
}
d->name = name;
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_send_name(*it, name.toUtf8().constData());
}
}
QString PlasmaVirtualDesktopInterface::name() const
{
return d->name;
}
void PlasmaVirtualDesktopInterface::setActive(bool active)
{
if (d->active == active) {
return;
}
d->active = active;
if (active) {
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_send_activated(*it);
}
} else {
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_send_deactivated(*it);
}
}
}
bool PlasmaVirtualDesktopInterface::isActive() const
{
return d->active;
}
void PlasmaVirtualDesktopInterface::sendDone()
{
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
org_kde_plasma_virtual_desktop_send_done(*it);
}
}
}
}
/****************************************************************************
Copyright 2018 Marco Martin <notmart@gmail.com>
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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H
#define KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H
#include "global.h"
#include "resource.h"
#include <KWayland/Server/kwaylandserver_export.h>
namespace KWayland
{
namespace Server
{
class Display;
class PlasmaVirtualDesktopInterface;
/**
* @short Wrapper for the org_kde_plasma_virtual_desktop_management interface.
*
* This class provides a convenient wrapper for the org_kde_plasma_virtual_desktop_management interface.
* @since 5.46
*/
class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopManagementInterface : public Global
{
Q_OBJECT
public:
virtual ~PlasmaVirtualDesktopManagementInterface();
/**
* Sets a new layout for this desktop grid.
*/
void setLayout(quint32 rows, quint32 columns);
/**
* @returns A desktop identified uniquely by this id.
* If not found, nullptr will be returned.
* @see createDesktop
*/
PlasmaVirtualDesktopInterface *desktop(const QString &id);
/**
* @returns A desktop identified uniquely by this id, if not found
* a new desktop will be created for this id at a given position.
* @param id the id for the desktop
* @param position the position the desktop will be in, if not provided,
* it will be appended at the end. If the desktop was already
* existing, position is ignored.
*/
PlasmaVirtualDesktopInterface *createDesktop(const QString &id, quint32 position = std::numeric_limits<uint32_t>::max());
/**
* Removed and destroys the desktop identified by id, if present
*/
void removeDesktop(const QString &id);
/**
* @returns All tghe desktops present.
*/
QList <PlasmaVirtualDesktopInterface *> desktops() const;
/**
* Inform the clients that all the properties have been sent, and
* their client-side representation is complete.
*/
void sendDone();
Q_SIGNALS:
/**
* A desktop has been activated
*/
void desktopActivated(const QString &id);