Add keyboard_shortcuts_inhibit protocol

Reviewers: zzag, davidedmundson, apol

Subscribers: romangg, crossi, kde-frameworks-devel

Tags: #frameworks

Differential Revision: https://phabricator.kde.org/D29231
This commit is contained in:
Benjamin Port 2020-05-04 11:45:15 +02:00
parent ca210bf2b4
commit a452ff1642
7 changed files with 515 additions and 0 deletions

View file

@ -25,6 +25,7 @@ set(SERVER_LIB_SRCS
idleinhibit_interface.cpp
idleinhibit_interface_v1.cpp
keyboard_interface.cpp
keyboard_shortcuts_inhibit_interface.cpp
keystate_interface.cpp
linuxdmabuf_v1_interface.cpp
output_interface.cpp
@ -242,6 +243,11 @@ ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
BASENAME wlr-data-control-unstable-v1
)
ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml
BASENAME keyboard-shortcuts-inhibit-unstable-v1
)
set(SERVER_GENERATED_SRCS
${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h
@ -301,6 +307,7 @@ set(SERVER_GENERATED_SRCS
${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-keyboard-shortcuts-inhibit-unstable-v1.h
)
set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON)
@ -363,6 +370,7 @@ set(SERVER_LIB_HEADERS
idle_interface.h
idleinhibit_interface.h
keyboard_interface.h
keyboard_shortcuts_inhibit_interface.h
keystate_interface.h
linuxdmabuf_v1_interface.h
output_interface.h

View file

@ -51,3 +51,16 @@ add_executable(testDataControlInterface test_datacontrol_interface.cpp ${DATACON
target_link_libraries( testDataControlInterface Qt5::Test Plasma::KWaylandServer KF5::WaylandClient Wayland::Client)
add_test(NAME kwayland-testDataControlInterface COMMAND testDataControlInterface)
ecm_mark_as_test(testDataControlInterface)
########################################################
# Test Keyboard Shortcuts Inhibitor Interface
########################################################
ecm_add_qtwayland_client_protocol(KEYBOARD_SHORTCUTS_INHIBITOR_SRCS
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml
BASENAME keyboard-shortcuts-inhibit-unstable-v1
)
add_executable(testKeyboardShortcutsInhibitorInterface test_keyboard_shortcuts_inhibitor_interface.cpp ${KEYBOARD_SHORTCUTS_INHIBITOR_SRCS})
target_link_libraries(testKeyboardShortcutsInhibitorInterface Qt5::Test Plasma::KWaylandServer KF5::WaylandClient Wayland::Client)
add_test(NAME kwayland-testKeyboardShortcutsInhibitorInterface COMMAND testKeyboardShortcutsInhibitorInterface)
ecm_mark_as_test(testKeyboardShortcutsInhibitorInterface)

View file

@ -0,0 +1,217 @@
/*
SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
// Qt
#include <QHash>
#include <QThread>
#include <QtTest>
// WaylandServer
#include "../../src/server/compositor_interface.h"
#include "../../src/server/display.h"
#include "../../src/server/keyboard_shortcuts_inhibit_interface.h"
#include "../../src/server/seat_interface.h"
#include "KWayland/Client/compositor.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/surface.h"
#include "qwayland-keyboard-shortcuts-inhibit-unstable-v1.h"
using namespace KWaylandServer;
class KeyboardShortcutsInhibitManager : public QObject, public QtWayland::zwp_keyboard_shortcuts_inhibit_manager_v1
{
Q_OBJECT
public:
KeyboardShortcutsInhibitManager(wl_registry *registry, quint32 id, quint32 version)
: QtWayland::zwp_keyboard_shortcuts_inhibit_manager_v1(registry, id, version)
{
}
};
class KeyboardShortcutsInhibitor : public QObject, public QtWayland::zwp_keyboard_shortcuts_inhibitor_v1
{
Q_OBJECT
public:
KeyboardShortcutsInhibitor(::zwp_keyboard_shortcuts_inhibitor_v1 *inhibitorV1)
: QtWayland::zwp_keyboard_shortcuts_inhibitor_v1(inhibitorV1)
{
}
void zwp_keyboard_shortcuts_inhibitor_v1_active() override
{
emit inhibitorActive();
}
void zwp_keyboard_shortcuts_inhibitor_v1_inactive() override
{
emit inhibitorInactive();
}
Q_SIGNALS:
void inhibitorActive();
void inhibitorInactive();
};
class TestKeyboardShortcutsInhibitorInterface : public QObject
{
Q_OBJECT
public:
TestKeyboardShortcutsInhibitorInterface()
{
}
~TestKeyboardShortcutsInhibitorInterface() override;
private Q_SLOTS:
void initTestCase();
void testKeyboardShortcuts();
private:
KWayland::Client::ConnectionThread *m_connection;
KWayland::Client::EventQueue *m_queue;
KWayland::Client::Compositor *m_clientCompositor;
KWayland::Client::Seat *m_clientSeat = nullptr;
QThread *m_thread;
Display m_display;
SeatInterface *m_seat;
CompositorInterface *m_serverCompositor;
KeyboardShortcutsInhibitManagerInterface *m_manager;
QVector<SurfaceInterface *> m_surfaces;
QVector<wl_surface *> m_clientSurfaces;
KeyboardShortcutsInhibitManager *m_inhibitManagerClient = nullptr;
QVector<KeyboardShortcutsInhibitor *> *m_inhibitors;
};
static const QString s_socketName = QStringLiteral("kwin-wayland-server-keyboard-shortcuts-inhibitor-test-0");
void TestKeyboardShortcutsInhibitorInterface::initTestCase()
{
m_display.setSocketName(s_socketName);
m_display.start();
QVERIFY(m_display.isRunning());
m_seat = m_display.createSeat(this);
m_seat->create();
m_serverCompositor = m_display.createCompositor(this);
m_serverCompositor->create();
m_manager = m_display.createKeyboardShortcutsInhibitManager(this);
QVERIFY(m_serverCompositor->isValid());
connect(m_serverCompositor, &CompositorInterface::surfaceCreated, this, [this](SurfaceInterface *surface) {
m_surfaces += surface;
});
// setup connection
m_connection = new KWayland::Client::ConnectionThread;
QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
m_connection->setSocketName(s_socketName);
m_thread = new QThread(this);
m_connection->moveToThread(m_thread);
m_thread->start();
m_connection->initConnection();
QVERIFY(connectedSpy.wait());
QVERIFY(!m_connection->connections().isEmpty());
m_queue = new KWayland::Client::EventQueue(this);
QVERIFY(!m_queue->isValid());
m_queue->setup(m_connection);
QVERIFY(m_queue->isValid());
auto registry = new KWayland::Client::Registry(this);
connect(registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, registry](const QByteArray &interface, quint32 id, quint32 version) {
if (interface == "zwp_keyboard_shortcuts_inhibit_manager_v1") {
m_inhibitManagerClient = new KeyboardShortcutsInhibitManager(registry->registry(), id, version);
}
});
connect(registry, &KWayland::Client::Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) {
m_clientSeat = registry->createSeat(name, version);
});
registry->setEventQueue(m_queue);
QSignalSpy compositorSpy(registry, &KWayland::Client::Registry::compositorAnnounced);
registry->create(m_connection->display());
QVERIFY(registry->isValid());
registry->setup();
wl_display_flush(m_connection->display());
QVERIFY(compositorSpy.wait());
m_clientCompositor = registry->createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
QVERIFY(m_clientCompositor->isValid());
QSignalSpy surfaceSpy(m_serverCompositor, &CompositorInterface::surfaceCreated);
for (int i = 0; i < 3; ++i) {
KWayland::Client::Surface *s = m_clientCompositor->createSurface(this);
m_clientSurfaces += s->operator wl_surface *();
}
QVERIFY(surfaceSpy.count() < 3 && surfaceSpy.wait(200));
QVERIFY(m_surfaces.count() == 3);
QVERIFY(m_inhibitManagerClient);
}
TestKeyboardShortcutsInhibitorInterface::~TestKeyboardShortcutsInhibitorInterface()
{
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;
}
void TestKeyboardShortcutsInhibitorInterface::testKeyboardShortcuts()
{
auto clientSurface = m_clientSurfaces[0];
auto surface = m_surfaces[0];
// Test creation
auto inhibitorClientV1 = m_inhibitManagerClient->inhibit_shortcuts(clientSurface, m_clientSeat->operator wl_seat *());
auto inhibitorClient = new KeyboardShortcutsInhibitor(inhibitorClientV1);
QSignalSpy inhibitorActiveSpy(inhibitorClient, &KeyboardShortcutsInhibitor::inhibitorActive);
QSignalSpy inhibitorInactiveSpy(inhibitorClient, &KeyboardShortcutsInhibitor::inhibitorInactive);
QSignalSpy inhibitorCreatedSpy(m_manager, &KeyboardShortcutsInhibitManagerInterface::inhibitorCreated);
QVERIFY(inhibitorCreatedSpy.wait() || inhibitorCreatedSpy.count() == 1);
auto inhibitorServer = m_manager->findInhibitor(surface, m_seat);
// Test deactivate
inhibitorServer->setActive(false);
QVERIFY(inhibitorInactiveSpy.wait() || inhibitorInactiveSpy.count() == 1);
// Test activate
inhibitorServer->setActive(true);
QVERIFY(inhibitorInactiveSpy.wait() || inhibitorInactiveSpy.count() == 1);
// Test creating for another surface
m_inhibitManagerClient->inhibit_shortcuts(m_clientSurfaces[1], m_clientSeat->operator wl_seat *());
QVERIFY(inhibitorCreatedSpy.wait() || inhibitorCreatedSpy.count() == 2);
// Test destroy is working
inhibitorClient->destroy();
m_inhibitManagerClient->inhibit_shortcuts(clientSurface, m_clientSeat->operator wl_seat *());
QVERIFY(inhibitorCreatedSpy.wait() || inhibitorCreatedSpy.count() == 3);
// Test creating with same surface / seat (expect error)
QSignalSpy errorOccured(m_connection, &KWayland::Client::ConnectionThread::errorOccurred);
m_inhibitManagerClient->inhibit_shortcuts(m_clientSurfaces[0], m_clientSeat->operator wl_seat *());
QVERIFY(errorOccured.wait() || errorOccured.count() == 1);
}
QTEST_GUILESS_MAIN(TestKeyboardShortcutsInhibitorInterface)
#include "test_keyboard_shortcuts_inhibitor_interface.moc"

View file

@ -15,6 +15,7 @@
#include "fakeinput_interface.h"
#include "idle_interface.h"
#include "idleinhibit_interface_p.h"
#include "keyboard_shortcuts_inhibit_interface.h"
#include "keystate_interface.h"
#include "linuxdmabuf_v1_interface.h"
#include "logging.h"
@ -524,6 +525,13 @@ DataControlDeviceManagerV1Interface *Display::createDataControlDeviceManagerV1(Q
return m;
}
KeyboardShortcutsInhibitManagerInterface *Display::createKeyboardShortcutsInhibitManager(QObject *parent)
{
auto d = new KeyboardShortcutsInhibitManagerInterface(this, parent);
connect(this, &Display::aboutToTerminate, d, [d] { delete d; });
return d;
}
void Display::createShm()
{
Q_ASSERT(d->display);

View file

@ -78,6 +78,7 @@ class KeyStateInterface;
class LinuxDmabufUnstableV1Interface;
class TabletManagerInterface;
class DataControlDeviceManagerV1Interface;
class KeyboardShortcutsInhibitManagerInterface;
/**
* @brief Class holding the Wayland server display loop.
@ -321,6 +322,11 @@ public:
*/
TabletManagerInterface *createTabletManagerInterface(QObject *parent = nullptr);
/**
* Creates the KeyboardShortcutsInhibitorInterface
*/
KeyboardShortcutsInhibitManagerInterface *createKeyboardShortcutsInhibitManager(QObject *object = nullptr);
/**
* Gets the ClientConnection for the given @p client.
* If there is no ClientConnection yet for the given @p client, it will be created.

View file

@ -0,0 +1,182 @@
/*
SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "keyboard_shortcuts_inhibit_interface.h"
#include <qwayland-server-keyboard-shortcuts-inhibit-unstable-v1.h>
#include "display.h"
#include "seat_interface.h"
#include "surface_interface.h"
static const int s_version = 1;
namespace KWaylandServer
{
class KeyboardShortcutsInhibitorInterfacePrivate : public QtWaylandServer::zwp_keyboard_shortcuts_inhibitor_v1
{
public:
KeyboardShortcutsInhibitorInterfacePrivate(SurfaceInterface *surface, SeatInterface *seat,
KeyboardShortcutsInhibitManagerInterface *manager,
KeyboardShortcutsInhibitorInterface *q, wl_resource *resource);
KeyboardShortcutsInhibitorInterface *q;
QPointer<KeyboardShortcutsInhibitManagerInterface> m_manager;
SurfaceInterface *const m_surface;
SeatInterface *const m_seat;
bool m_active;
protected:
void zwp_keyboard_shortcuts_inhibitor_v1_destroy_resource(Resource *resource) override;
void zwp_keyboard_shortcuts_inhibitor_v1_destroy(Resource *resource) override;
};
class KeyboardShortcutsInhibitManagerInterfacePrivate
: public QtWaylandServer::zwp_keyboard_shortcuts_inhibit_manager_v1
{
public:
KeyboardShortcutsInhibitManagerInterfacePrivate(Display *display, KeyboardShortcutsInhibitManagerInterface *q);
void zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(Resource *resource, uint32_t id,
struct ::wl_resource *surface_resource,
struct ::wl_resource *seat_resource) override;
KeyboardShortcutsInhibitorInterface *findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const;
void removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat);
KeyboardShortcutsInhibitManagerInterface *q;
Display *const m_display;
QHash<QPair<SurfaceInterface *, SeatInterface *>, KeyboardShortcutsInhibitorInterface *> m_inhibitors;
protected:
void zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(Resource *resource) override;
};
KeyboardShortcutsInhibitorInterfacePrivate::KeyboardShortcutsInhibitorInterfacePrivate(SurfaceInterface *surface,
SeatInterface *seat,
KeyboardShortcutsInhibitManagerInterface *manager,
KeyboardShortcutsInhibitorInterface *q,
wl_resource *resource)
: zwp_keyboard_shortcuts_inhibitor_v1(resource), q(q), m_manager(manager), m_surface(surface), m_seat(seat),
m_active(false)
{
}
void KeyboardShortcutsInhibitorInterfacePrivate::zwp_keyboard_shortcuts_inhibitor_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void
KeyboardShortcutsInhibitorInterfacePrivate::zwp_keyboard_shortcuts_inhibitor_v1_destroy_resource(Resource *resource)
{
Q_UNUSED(resource)
// Ensure manager don't track anymore this inhibitor
if (m_manager) {
m_manager->removeInhibitor(m_surface, m_seat);
}
delete q;
}
KeyboardShortcutsInhibitorInterface::KeyboardShortcutsInhibitorInterface(SurfaceInterface *surface, SeatInterface *seat,
KeyboardShortcutsInhibitManagerInterface *manager,
wl_resource *resource)
: QObject(nullptr), d(new KeyboardShortcutsInhibitorInterfacePrivate(surface, seat, manager, this, resource))
{
}
KeyboardShortcutsInhibitorInterface::~KeyboardShortcutsInhibitorInterface() = default;
void KeyboardShortcutsInhibitorInterface::setActive(bool active)
{
if (d->m_active == active) {
return;
}
d->m_active = active;
if (active) {
d->send_active();
} else {
d->send_inactive();
}
}
bool KeyboardShortcutsInhibitorInterface::isActive() const
{
return d->m_active;
}
SeatInterface *KeyboardShortcutsInhibitorInterface::seat() const
{
return d->m_seat;
}
SurfaceInterface *KeyboardShortcutsInhibitorInterface::surface() const
{
return d->m_surface;
}
KeyboardShortcutsInhibitManagerInterfacePrivate::KeyboardShortcutsInhibitManagerInterfacePrivate(Display *display,
KeyboardShortcutsInhibitManagerInterface *q)
: zwp_keyboard_shortcuts_inhibit_manager_v1(*display, s_version), q(q), m_display(display)
{
}
void KeyboardShortcutsInhibitManagerInterfacePrivate::zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
Resource *resource, uint32_t id, wl_resource *surface_resource, wl_resource *seat_resource)
{
SeatInterface *seat = SeatInterface::get(seat_resource);
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
if (m_inhibitors.contains({surface, seat})) {
wl_resource_post_error(resource->handle, error::error_already_inhibited,
"the shortcuts are already inhibited for this surface and seat");
return;
}
wl_resource *inhibitorResource = wl_resource_create(resource->client(),
&zwp_keyboard_shortcuts_inhibitor_v1_interface,
resource->version(), id);
auto inhibitor = new KeyboardShortcutsInhibitorInterface(surface, seat, q, inhibitorResource);
m_inhibitors[{surface, seat}] = inhibitor;
Q_EMIT q->inhibitorCreated(inhibitor);
inhibitor->setActive(true);
}
KeyboardShortcutsInhibitorInterface *KeyboardShortcutsInhibitManagerInterfacePrivate::findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const
{
return m_inhibitors.value({surface, seat}, nullptr);
}
void KeyboardShortcutsInhibitManagerInterfacePrivate::zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void KeyboardShortcutsInhibitManagerInterfacePrivate::removeInhibitor(SurfaceInterface *const surface,
SeatInterface *const seat)
{
m_inhibitors.remove({surface, seat});
}
KeyboardShortcutsInhibitManagerInterface::KeyboardShortcutsInhibitManagerInterface(Display *display, QObject *parent)
: QObject(parent), d(new KeyboardShortcutsInhibitManagerInterfacePrivate(display, this))
{
}
KeyboardShortcutsInhibitManagerInterface::~KeyboardShortcutsInhibitManagerInterface() = default;
KeyboardShortcutsInhibitorInterface *KeyboardShortcutsInhibitManagerInterface::findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const
{
return d->findInhibitor(surface, seat);
}
void KeyboardShortcutsInhibitManagerInterface::removeInhibitor(SurfaceInterface *surface, SeatInterface *seat)
{
d->removeInhibitor(surface, seat);
}
}

View file

@ -0,0 +1,81 @@
/*
SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef WAYLAND_SERVER_KEYBOARD_SHORTCUTS_INHIBIT_INTERFACE_H
#define WAYLAND_SERVER_KEYBOARD_SHORTCUTS_INHIBIT_INTERFACE_H
#include <KWaylandServer/kwaylandserver_export.h>
#include <QObject>
#include "seat_interface.h"
#include "surface_interface.h"
namespace KWaylandServer
{
/**
* This is an implementation of wayland-protocols/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml
*
* This class is just the means to get a @class KeyboardShortcutsInhibitorInterface, which is
* the class that will have all of the information we need.
*/
class KeyboardShortcutsInhibitManagerInterface;
class KeyboardShortcutsInhibitorInterfacePrivate;
class KeyboardShortcutsInhibitManagerInterfacePrivate;
class KWAYLANDSERVER_EXPORT KeyboardShortcutsInhibitorInterface : public QObject
{
Q_OBJECT
public:
~KeyboardShortcutsInhibitorInterface() override;
SurfaceInterface *surface() const;
SeatInterface *seat() const;
void setActive(bool active);
bool isActive() const;
private:
friend class KeyboardShortcutsInhibitManagerInterfacePrivate;
explicit KeyboardShortcutsInhibitorInterface(SurfaceInterface *surface, SeatInterface *seat, KeyboardShortcutsInhibitManagerInterface *manager, wl_resource *resource);
QScopedPointer<KeyboardShortcutsInhibitorInterfacePrivate> d;
};
/**
* The KeyboardShortcutsInhibitManagerInterface allows clients to inhibit global shortcuts.
*
* KeyboardShortcutsInhibitManagerInterface correponds to the wayland interface zwp_keyboard_shortcuts_inhibit_manager_v1.
*
* @since 5.20
*/
class KWAYLANDSERVER_EXPORT KeyboardShortcutsInhibitManagerInterface : public QObject
{
Q_OBJECT
public:
~KeyboardShortcutsInhibitManagerInterface() override;
/**
* return shortucts inhibitor associated with surface and seat, if no shortcut are associated, return nullptr
*/
KeyboardShortcutsInhibitorInterface *findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const;
Q_SIGNALS:
/**
* This signal is emitted when a keyboard shortcuts inhibitor @a inhibitor is created.
*/
void inhibitorCreated(KeyboardShortcutsInhibitorInterface *inhibitor);
private:
friend class Display;
friend class KeyboardShortcutsInhibitorInterfacePrivate;
explicit KeyboardShortcutsInhibitManagerInterface(Display *d, QObject *parent = nullptr);
void removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat);
QScopedPointer<KeyboardShortcutsInhibitManagerInterfacePrivate> d;
};
}
#endif