tablet_v2: Further implements missing components

Namely TabletPadV2Interface, TabletPadRingV2Interface,
TabletPadStripV2Interface and TabletPadGroupV2Interface were entirely
missing.
This commit is contained in:
Aleix Pol 2020-12-16 02:46:33 +01:00
parent c80086ceda
commit 1d904daf65
3 changed files with 528 additions and 8 deletions

View file

@ -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()
{

View file

@ -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

View file

@ -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();
enum Source {
SourceFinger = 1, // finger
};
Q_ENUM(Source)
void sendSource(Source source);
void sendAngle(qreal angle);
void sendStop();
void sendFrame(quint32 time);
private:
friend class TabletPadGroupV2Interface;
friend class TabletPadV2InterfacePrivate;
friend class TabletSeatV2InterfacePrivate;
explicit TabletPadRingV2Interface(QObject *parent);
QScopedPointer<TabletPadRingV2InterfacePrivate> d;
};
class KWAYLANDSERVER_EXPORT TabletPadStripV2Interface : public QObject
{
Q_OBJECT
public:
virtual ~TabletPadStripV2Interface();
enum Source {
SourceFinger = 1, // finger
};
void sendSource(Source source);
void sendPosition(quint32 position);
void sendFrame(quint32 time);
void sendStop();
private:
friend class TabletPadGroupV2Interface;
friend class TabletPadV2InterfacePrivate;
friend class TabletSeatV2InterfacePrivate;
explicit TabletPadStripV2Interface(QObject *parent);
QScopedPointer<TabletPadStripV2InterfacePrivate> d;
};
class KWAYLANDSERVER_EXPORT TabletPadGroupV2Interface : public QObject
{
Q_OBJECT
public:
virtual ~TabletPadGroupV2Interface();
void sendModeSwitch(quint32 time, quint32 serial, quint32 mode);
private:
friend class TabletPadV2Interface;
friend class TabletPadV2InterfacePrivate;
friend class TabletSeatV2InterfacePrivate;
explicit TabletPadGroupV2Interface(quint32 currentMode, QObject *parent);
QScopedPointer<TabletPadGroupV2InterfacePrivate> d;
};
class KWAYLANDSERVER_EXPORT TabletV2Interface : public QObject
{
Q_OBJECT
@ -141,11 +239,13 @@ public:
*/
bool isSurfaceSupported(SurfaceInterface *surface) const;
TabletPadV2Interface *pad() const;
void sendRemoved();
private:
friend class TabletSeatV2Interface;
friend class TabletSeatV2InterfacePrivate;
friend class TabletPadV2Interface;
friend class TabletToolV2Interface;
explicit TabletV2Interface(quint32 vendorId, quint32 productId, const QString &name, const QStringList &paths, QObject *parent);
QScopedPointer<TabletV2InterfacePrivate> d;
@ -158,13 +258,16 @@ public:
virtual ~TabletSeatV2Interface();
TabletV2Interface *addTablet(quint32 vendorId, quint32 productId, const QString &sysname, const QString &name, const QStringList &paths);
TabletPadV2Interface *addTabletPad(const QString &sysname, const QString &name, const QStringList &paths, quint32 buttons, quint32 rings, quint32 strips, quint32 modes, quint32 currentMode, TabletV2Interface *tablet);
TabletToolV2Interface *addTool(TabletToolV2Interface::Type type, quint64 hardwareSerial, quint64 hardwareId, const QVector<TabletToolV2Interface::Capability> &capabilities);
TabletToolV2Interface *toolByHardwareId(quint64 hardwareId) const;
TabletToolV2Interface *toolByHardwareSerial(quint64 hardwareSerial) const;
TabletV2Interface *tabletByName(const QString &sysname) const;
TabletPadV2Interface *padByName(const QString &sysname) const;
void removeTablet(const QString &sysname);
void removeDevice(const QString &sysname);
bool isClientSupported(ClientConnection *client) const;
private:
friend class TabletManagerV2InterfacePrivate;