Commit 1c0a103b authored by Aleix Pol Gonzalez's avatar Aleix Pol Gonzalez 🐧

tablet_v2: Further implements missing components

Namely TabletPadV2Interface, TabletPadRingV2Interface,
TabletPadStripV2Interface and TabletPadGroupV2Interface were entirely
missing.
parent 1cdcb4c5
......@@ -18,6 +18,7 @@
#include "KWayland/Client/event_queue.h"
#include "KWayland/Client/registry.h"
#include "KWayland/Client/seat.h"
#include "KWayland/Client/surface.h"
#include "qwayland-tablet-unstable-v2.h"
......@@ -32,6 +33,42 @@ public:
}
};
class TabletPad : public QObject, public QtWayland::zwp_tablet_pad_v2
{
Q_OBJECT
public:
TabletPad(::zwp_tablet_pad_v2 *t)
: QtWayland::zwp_tablet_pad_v2(t)
{
}
void zwp_tablet_pad_v2_done() override {
Q_ASSERT(!doneCalled);
doneCalled = true;
}
void zwp_tablet_pad_v2_buttons(uint32_t buttons) override {
Q_ASSERT(buttons == 1);
}
void zwp_tablet_pad_v2_enter(uint32_t /*serial*/, struct ::zwp_tablet_v2 */*tablet*/, struct ::wl_surface *surface) override {
m_currentSurface = surface;
}
void zwp_tablet_pad_v2_button(uint32_t /*time*/, uint32_t button, uint32_t state) override {
buttonStates[m_currentSurface][button] = state;
Q_EMIT buttonReceived();
}
::wl_surface *m_currentSurface = nullptr;
bool doneCalled = false;
QHash<::wl_surface *, QHash<uint32_t, uint32_t>> buttonStates;
Q_SIGNALS:
void buttonReceived();
};
class Tool : public QObject, public QtWayland::zwp_tablet_tool_v2
{
Q_OBJECT
......@@ -76,10 +113,18 @@ public:
Q_EMIT toolAdded();
}
void zwp_tablet_seat_v2_pad_added(struct ::zwp_tablet_pad_v2 *id) override
{
m_pads << new TabletPad(id);
Q_EMIT padAdded();
}
QVector<Tablet *> m_tablets;
QVector<TabletPad *> m_pads;
QVector<Tool *> m_tools;
Q_SIGNALS:
void padAdded();
void toolAdded();
void tabletAdded();
};
......@@ -96,6 +141,7 @@ public:
private Q_SLOTS:
void initTestCase();
void testAdd();
void testAddPad();
void testInteractSimple();
void testInteractSurfaceChange();
......@@ -112,8 +158,10 @@ private:
TabletSeat *m_tabletSeatClient = nullptr;
TabletManagerV2Interface *m_tabletManager;
QVector<KWayland::Client::Surface *> m_surfacesClient;
TabletV2Interface *m_tablet;
TabletPadV2Interface *m_tabletPad = nullptr;
TabletToolV2Interface *m_tool;
QVector<SurfaceInterface *> m_surfaces;
......@@ -178,7 +226,7 @@ void TestTabletInterface::initTestCase()
QSignalSpy surfaceSpy(m_serverCompositor, &CompositorInterface::surfaceCreated);
for (int i = 0; i < 3; ++i) {
m_clientCompositor->createSurface(this);
m_surfacesClient += m_clientCompositor->createSurface(this);
}
QVERIFY(surfaceSpy.count() < 3 && surfaceSpy.wait(200));
QVERIFY(m_surfaces.count() == 3);
......@@ -230,6 +278,32 @@ void TestTabletInterface::testAdd()
m_tool->setCurrentSurface(nullptr);
}
void TestTabletInterface::testAddPad()
{
TabletSeatV2Interface *seatInterface = m_tabletManager->seat(m_seat);
QVERIFY(seatInterface);
QSignalSpy tabletPadSpy(m_tabletSeatClient, &TabletSeat::padAdded);
m_tabletPad = seatInterface->addTabletPad(QStringLiteral("my tablet pad"), QStringLiteral("tabletpad"), {QStringLiteral("/test/event33")}, 1, 1, 1, 1, 0, m_tablet);
QVERIFY(m_tabletPad);
QVERIFY(tabletPadSpy.wait() || tabletPadSpy.count() == 1);
QCOMPARE(m_tabletSeatClient->m_pads.count(), 1);
QVERIFY(m_tabletSeatClient->m_pads[0]);
QVERIFY(m_tabletPad->ring(0));
QVERIFY(m_tabletPad->strip(0));
QCOMPARE(m_surfaces.count(), 3);
QVERIFY(m_tabletSeatClient->m_pads[0]->buttonStates.isEmpty());
QSignalSpy buttonSpy(m_tabletSeatClient->m_pads[0], &TabletPad::buttonReceived);
m_tabletPad->setCurrentSurface(m_surfaces[0], m_tablet);
m_tabletPad->sendButton(123, 0, QtWayland::zwp_tablet_pad_v2::button_state_pressed);
QVERIFY(buttonSpy.count() || buttonSpy.wait(100));
QCOMPARE(m_tabletSeatClient->m_pads[0]->doneCalled, true);
QCOMPARE(m_tabletSeatClient->m_pads[0]->buttonStates.count(), 1);
QCOMPARE(m_tabletSeatClient->m_pads[0]->buttonStates[*m_surfacesClient[0]][0], QtWayland::zwp_tablet_pad_v2::button_state_pressed);
}
static uint s_serial = 0;
void TestTabletInterface::testInteractSimple()
{
......
......@@ -46,6 +46,7 @@ public:
}
TabletV2Interface *const q;
TabletPadV2Interface *m_pad = nullptr;
const uint32_t m_vendorId;
const uint32_t m_productId;
const QString m_name;
......@@ -76,6 +77,11 @@ void TabletV2Interface::sendRemoved()
}
}
TabletPadV2Interface *TabletV2Interface::pad() const
{
return d->m_pad;
}
class TabletCursorV2Private
{
public:
......@@ -327,6 +333,280 @@ void TabletToolV2Interface::sendRemoved()
}
}
class TabletPadRingV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_ring_v2
{
public:
TabletPadRingV2InterfacePrivate(TabletPadRingV2Interface *q)
: zwp_tablet_pad_ring_v2()
, q(q)
{
}
wl_resource *resourceForSurface(SurfaceInterface *surface) const
{
ClientConnection *client = surface->client();
Resource *r = resourceMap().value(*client);
return r ? r->handle : nullptr;
}
void zwp_tablet_pad_ring_v2_destroy(Resource *resource) override {
wl_resource_destroy(resource->handle);
if (m_pad->isRemoved() && resourceMap().isEmpty()) {
delete q;
}
}
TabletPadRingV2Interface *const q;
TabletPadV2Interface *m_pad;
};
TabletPadRingV2Interface::TabletPadRingV2Interface(QObject *parent)
: QObject(parent)
, d(new TabletPadRingV2InterfacePrivate(this))
{
}
TabletPadRingV2Interface::~TabletPadRingV2Interface() = default;
void TabletPadRingV2Interface::sendAngle(qreal angle)
{
d->send_angle(d->resourceForSurface(d->m_pad->currentSurface()), wl_fixed_from_double(angle));
}
void TabletPadRingV2Interface::sendFrame(quint32 time)
{
d->send_frame(d->resourceForSurface(d->m_pad->currentSurface()), time);
}
void TabletPadRingV2Interface::sendSource(Source source)
{
d->send_source(d->resourceForSurface(d->m_pad->currentSurface()), source);
}
void TabletPadRingV2Interface::sendStop()
{
d->send_stop(d->resourceForSurface(d->m_pad->currentSurface()));
}
class TabletPadStripV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_strip_v2
{
public:
TabletPadStripV2InterfacePrivate(TabletPadStripV2Interface *q)
: zwp_tablet_pad_strip_v2()
, q(q)
{
}
wl_resource *resourceForSurface(SurfaceInterface *surface) const
{
ClientConnection *client = surface->client();
Resource *r = resourceMap().value(*client);
return r ? r->handle : nullptr;
}
void zwp_tablet_pad_strip_v2_destroy(Resource *resource) override {
wl_resource_destroy(resource->handle);
if (m_pad->isRemoved() && resourceMap().isEmpty()) {
delete q;
}
}
TabletPadV2Interface *m_pad = nullptr;
TabletPadStripV2Interface *const q;
};
TabletPadStripV2Interface::TabletPadStripV2Interface(QObject *parent)
: QObject(parent)
, d(new TabletPadStripV2InterfacePrivate(this))
{
}
TabletPadStripV2Interface::~TabletPadStripV2Interface() = default;
void TabletPadStripV2Interface::sendFrame(quint32 time)
{
d->send_frame(d->resourceForSurface(d->m_pad->currentSurface()), time);
}
void TabletPadStripV2Interface::sendPosition(quint32 position)
{
d->send_position(d->resourceForSurface(d->m_pad->currentSurface()), position);
}
void TabletPadStripV2Interface::sendSource(Source source)
{
d->send_source(d->resourceForSurface(d->m_pad->currentSurface()), source);
}
void TabletPadStripV2Interface::sendStop()
{
d->send_stop(d->resourceForSurface(d->m_pad->currentSurface()));
}
class TabletPadGroupV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_group_v2
{
public:
TabletPadGroupV2InterfacePrivate(quint32 currentMode, TabletPadGroupV2Interface *q)
: zwp_tablet_pad_group_v2()
, q(q)
, m_currentMode(currentMode)
{
}
wl_resource *resourceForSurface(SurfaceInterface *surface) const
{
ClientConnection *client = surface->client();
Resource *r = resourceMap().value(*client);
return r ? r->handle : nullptr;
}
void zwp_tablet_pad_group_v2_destroy(Resource * resource) override {
wl_resource_destroy(resource->handle);
if (m_pad->isRemoved() && resourceMap().isEmpty()) {
delete q;
}
}
TabletPadGroupV2Interface *const q;
TabletPadV2Interface *m_pad = nullptr;
quint32 m_currentMode;
};
TabletPadGroupV2Interface::TabletPadGroupV2Interface(quint32 currentMode, QObject *parent)
: QObject(parent)
, d(new TabletPadGroupV2InterfacePrivate(currentMode, this))
{
}
TabletPadGroupV2Interface::~TabletPadGroupV2Interface() = default;
void TabletPadGroupV2Interface::sendModeSwitch(quint32 time, quint32 serial, quint32 mode)
{
d->m_currentMode = mode;
d->send_mode_switch(d->resourceForSurface(d->m_pad->currentSurface()), time, serial, mode);
}
class TabletPadV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_v2
{
public:
TabletPadV2InterfacePrivate(const QString &path, quint32 buttons, quint32 rings, quint32 strips, quint32 modes, quint32 currentMode, Display *display, TabletPadV2Interface *q)
: zwp_tablet_pad_v2()
, q(q)
, m_path(path)
, m_buttons(buttons)
, m_modes(modes)
, m_padGroup(new TabletPadGroupV2Interface(currentMode, q))
, m_display(display)
{
for (uint i = 0; i < buttons; ++i) {
m_buttons[i] = i;
}
m_padGroup->d->m_pad = q;
m_rings.reserve(rings);
for (quint32 i = 0; i < rings; ++i) {
m_rings += new TabletPadRingV2Interface(q);
m_rings.constLast()->d->m_pad = q;
}
m_strips.reserve(strips);
for (quint32 i = 0; i < strips; ++i) {
m_strips += new TabletPadStripV2Interface(q);
m_strips.constLast()->d->m_pad = q;
}
}
void zwp_tablet_pad_v2_destroy(Resource *resource) override {
wl_resource_destroy(resource->handle);
if (m_removed && resourceMap().isEmpty()) {
delete q;
}
}
void zwp_tablet_pad_v2_set_feedback(Resource *resource, quint32 button, const QString &description, quint32 serial) override {
Q_EMIT q->feedback(m_display->getConnection(resource->client()), button, description, serial);
}
wl_resource *resourceForSurface(SurfaceInterface *surface) const
{
ClientConnection *client = surface->client();
Resource *r = resourceMap().value(*client);
return r ? r->handle : nullptr;
}
TabletPadV2Interface *const q;
const QString m_path;
QVector<quint32> m_buttons;
const int m_modes;
QVector<TabletPadRingV2Interface *> m_rings;
QVector<TabletPadStripV2Interface *> m_strips;
TabletPadGroupV2Interface *const m_padGroup;
TabletSeatV2Interface *m_seat = nullptr;
SurfaceInterface *m_currentSurface = nullptr;
bool m_removed = false;
Display *const m_display;
};
TabletPadV2Interface::TabletPadV2Interface(const QString &path, quint32 buttons, quint32 rings, quint32 strips, quint32 modes, quint32 currentMode, Display *display, QObject *parent)
: QObject(parent)
, d(new TabletPadV2InterfacePrivate(path, buttons, rings, strips, modes, currentMode, display, this))
{
}
TabletPadV2Interface::~TabletPadV2Interface() = default;
void TabletPadV2Interface::sendButton(quint32 time, quint32 button, bool pressed)
{
d->send_button(d->resourceForSurface(currentSurface()), time, button, pressed);
}
void TabletPadV2Interface::sendRemoved()
{
d->m_removed = true;
for (auto resource : d->resourceMap()) {
d->send_removed(resource->handle);
}
}
TabletPadRingV2Interface *TabletPadV2Interface::ring(uint at) const
{
return d->m_rings[at];
}
TabletPadStripV2Interface *TabletPadV2Interface::strip(uint at) const
{
return d->m_strips[at];
}
bool TabletPadV2Interface::isRemoved() const
{
return d->m_removed;
}
void TabletPadV2Interface::setCurrentSurface(SurfaceInterface *surface, TabletV2Interface *tablet)
{
if (surface == d->m_currentSurface) {
return;
}
if (d->m_currentSurface) {
d->send_leave(d->m_display->nextSerial(), surface->resource());
}
d->m_currentSurface = surface;
if (surface) {
wl_resource *tabletResource = tablet->d->resourceForSurface(surface);
d->send_enter(d->resourceForSurface(surface), d->m_display->nextSerial(), tabletResource, surface->resource());
d->m_padGroup->sendModeSwitch(0, d->m_display->nextSerial(), d->m_padGroup->d->m_currentMode);
}
}
SurfaceInterface *TabletPadV2Interface::currentSurface() const
{
return d->m_currentSurface;
}
class TabletSeatV2InterfacePrivate : public QtWaylandServer::zwp_tablet_seat_v2
{
public:
......@@ -339,8 +619,12 @@ public:
void zwp_tablet_seat_v2_bind_resource(Resource *resource) override
{
for (auto iface : qAsConst(m_tablets)) {
sendTabletAdded(resource, iface);
for (auto tablet : qAsConst(m_tablets)) {
sendTabletAdded(resource, tablet);
}
for (auto pad : qAsConst(m_pads)) {
sendPadAdded(resource, pad);
}
for (auto *tool : qAsConst(m_tools)) {
......@@ -384,9 +668,40 @@ public:
tablet->d->send_done(tabletResource);
}
void sendPadAdded(Resource *resource, TabletPadV2Interface *pad)
{
if (pad->d->m_removed)
return;
wl_resource *tabletResource = pad->d->add(resource->client(), resource->version())->handle;
send_pad_added(resource->handle, tabletResource);
pad->d->send_buttons(tabletResource, pad->d->m_buttons.size());
pad->d->send_path(tabletResource, pad->d->m_path);
auto groupResource = pad->d->m_padGroup->d->add(resource->client(), resource->version());
pad->d->send_group(tabletResource, groupResource->handle);
pad->d->m_padGroup->d->send_modes(groupResource->handle, pad->d->m_modes);
pad->d->m_padGroup->d->send_buttons(groupResource->handle, QByteArray::fromRawData(reinterpret_cast<const char *>(pad->d->m_buttons.data()), pad->d->m_buttons.size() * sizeof(quint32)));
for (auto ring : pad->d->m_rings) {
auto ringResource = ring->d->add(resource->client(), resource->version());
pad->d->m_padGroup->d->send_ring(groupResource->handle, ringResource->handle);
}
for (auto strip : pad->d->m_strips) {
auto stripResource = strip->d->add(resource->client(), resource->version());
pad->d->m_padGroup->d->send_strip(groupResource->handle, stripResource->handle);
}
pad->d->m_padGroup->d->send_done(groupResource->handle);
pad->d->send_done(tabletResource);
}
TabletSeatV2Interface *const q;
QVector<TabletToolV2Interface *> m_tools;
QHash<QString, TabletV2Interface *> m_tablets;
QHash<QString, TabletPadV2Interface *> m_pads;
Display *const m_display;
};
......@@ -425,6 +740,8 @@ TabletV2Interface *TabletSeatV2Interface::addTablet(uint32_t vendorId, uint32_t
const QString &name,
const QStringList &paths)
{
Q_ASSERT(!d->m_tablets.contains(sysname));
auto iface = new TabletV2Interface(vendorId, productId, name, paths, this);
for (QtWaylandServer::zwp_tablet_seat_v2::Resource *r : d->resourceMap()) {
......@@ -435,12 +752,32 @@ TabletV2Interface *TabletSeatV2Interface::addTablet(uint32_t vendorId, uint32_t
return iface;
}
void TabletSeatV2Interface::removeTablet(const QString &sysname)
TabletPadV2Interface *TabletSeatV2Interface::addTabletPad(const QString &sysname, const QString &name, const QStringList &paths, quint32 buttons, quint32 rings, quint32 strips, quint32 modes, quint32 currentMode, TabletV2Interface *tablet)
{
Q_UNUSED(name);
auto iface = new TabletPadV2Interface(paths.at(0), buttons, rings, strips, modes, currentMode, d->m_display, this);
iface->d->m_seat = this;
for (auto r : d->resourceMap()) {
d->sendPadAdded(r, iface);
}
tablet->d->m_pad = iface;
d->m_pads[sysname] = iface;
return iface;
}
void TabletSeatV2Interface::removeDevice(const QString &sysname)
{
auto tablet = d->m_tablets.take(sysname);
if (tablet) {
tablet->sendRemoved();
}
auto pad = d->m_pads.take(sysname);
if (pad) {
pad->sendRemoved();
}
}
TabletToolV2Interface *TabletSeatV2Interface::toolByHardwareId(quint64 hardwareId) const
......@@ -461,9 +798,10 @@ TabletToolV2Interface *TabletSeatV2Interface::toolByHardwareSerial(quint64 hardw
return nullptr;
}
TabletV2Interface *TabletSeatV2Interface::tabletByName(const QString &name) const
TabletPadV2Interface * TabletSeatV2Interface::padByName(const QString &name) const
{
return d->m_tablets.value(name);
Q_ASSERT(d->m_pads.contains(name));
return d->m_pads.value(name);
}
class TabletManagerV2InterfacePrivate : public QtWaylandServer::zwp_tablet_manager_v2
......@@ -508,6 +846,11 @@ TabletSeatV2Interface *TabletManagerV2Interface::seat(SeatInterface *seat) const
return d->get(seat);
}
bool TabletSeatV2Interface::isClientSupported(ClientConnection *client) const
{
return d->resourceMap().value(*client);
}
TabletManagerV2Interface::~TabletManagerV2Interface() = default;
} // namespace KWaylandServer
......@@ -14,6 +14,7 @@
namespace KWaylandServer
{
class ClientConnection;
class Display;
class SeatInterface;
class SurfaceInterface;
......@@ -25,6 +26,14 @@ class TabletSeatV2InterfacePrivate;
class TabletToolV2InterfacePrivate;
class TabletV2Interface;
class TabletV2InterfacePrivate;
class TabletPadV2Interface;
class TabletPadV2InterfacePrivate;
class TabletPadRingV2Interface;
class TabletPadRingV2InterfacePrivate;
class TabletPadStripV2Interface;
class TabletPadStripV2InterfacePrivate;
class TabletPadGroupV2Interface;
class TabletPadGroupV2InterfacePrivate;
/**
* This is an implementation of wayland-protocols/unstable/tablet/tablet-unstable-v2.xml
......@@ -130,6 +139,95 @@ private:
friend class TabletToolV2InterfacePrivate;
};
class KWAYLANDSERVER_EXPORT TabletPadV2Interface : public QObject
{
Q_OBJECT
public:
virtual ~TabletPadV2Interface();
TabletPadRingV2Interface *ring(uint at) const;
TabletPadStripV2Interface *strip(uint at) const;
void sendButton(quint32 time, quint32 button, bool pressed);
void sendRemoved();
bool isRemoved() const;
void setCurrentSurface(SurfaceInterface *surface, TabletV2Interface *tablet);
SurfaceInterface *currentSurface() const;
Q_SIGNALS:
void feedback(KWaylandServer::ClientConnection *client, quint32 button, const QString &description, quint32 serial);
private:
friend class TabletSeatV2Interface;
friend class TabletSeatV2InterfacePrivate;
explicit TabletPadV2Interface(const QString &path, quint32 buttons, quint32 rings, quint32 strips, quint32 modes, quint32 currentMode, Display *display, QObject *parent);
QScopedPointer<TabletPadV2InterfacePrivate> d;
};
class KWAYLANDSERVER_EXPORT TabletPadRingV2Interface : public QObject
{
Q_OBJECT
public:
virtual ~TabletPadRingV2Interface();