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

tablet_v2: Support remaining controls

Adds support for tablet buttons, rings and strips.
parent 8f9fcd7e
......@@ -501,53 +501,51 @@ void DebugConsoleFilter::tabletToolEvent(TabletEvent *event)
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletToolButtonEvent(const QSet<uint> &pressedButtons)
void DebugConsoleFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId)
{
QString buttons;
for (uint b : pressedButtons) {
buttons += QString::number(b) + ' ';
}
QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool Button"))
+ tableRow(i18n("Pressed Buttons"), buttons)
+ tableRow(i18n("Button"), button)
+ tableRow(i18n("Pressed"), pressed)
+ tableRow(i18n("Tablet"), tabletToolId.m_tabletSysName)
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletPadButtonEvent(const QSet<uint> &pressedButtons)
void DebugConsoleFilter::tabletPadButtonEvent(uint button, bool pressed, const QString &deviceSysName)
{
QString buttons;
for (uint b : pressedButtons) {
buttons += QString::number(b) + ' ';
}
QString text = s_hr + s_tableStart
+ tableHeaderRow(i18n("Tablet Pad Button"))
+ tableRow(i18n("Pressed Buttons"), buttons)
+ tableRow(i18n("Button"), button)
+ tableRow(i18n("Pressed"), pressed)
+ tableRow(i18n("Tablet"), deviceSysName)
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletPadStripEvent(int number, int position, bool isFinger)
void DebugConsoleFilter::tabletPadStripEvent(int number, int position, bool isFinger, const QString &deviceSysName)
{
QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Strip"))
+ tableRow(i18n("Number"), number)
+ tableRow(i18n("Position"), position)
+ tableRow(i18n("isFinger"), isFinger)
+ tableRow(i18n("Tablet"), deviceSysName)
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletPadRingEvent(int number, int position, bool isFinger)
void DebugConsoleFilter::tabletPadRingEvent(int number, int position, bool isFinger, const QString &deviceSysName)
{
QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Ring"))
+ tableRow(i18n("Number"), number)
+ tableRow(i18n("Position"), position)
+ tableRow(i18n("isFinger"), isFinger)
+ tableRow(i18n("Tablet"), deviceSysName)
+ s_tableEnd;
m_textEdit->insertHtml(text);
......
......@@ -148,10 +148,10 @@ public:
void switchEvent(SwitchEvent *event) override;
void tabletToolEvent(TabletEvent *event) override;
void tabletToolButtonEvent(const QSet<uint> &pressedButtons) override;
void tabletPadButtonEvent(const QSet<uint> &pressedButtons) override;
void tabletPadStripEvent(int number, int position, bool isFinger) override;
void tabletPadRingEvent(int number, int position, bool isFinger) override;
void tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId) override;
void tabletPadButtonEvent(uint button, bool pressed, const QString &deviceSysName) override;
void tabletPadStripEvent(int number, int position, bool isFinger, const QString &deviceSysName) override;
void tabletPadRingEvent(int number, int position, bool isFinger, const QString &deviceSysName) override;
private:
QTextEdit *m_textEdit;
......
......@@ -175,31 +175,37 @@ bool InputEventFilter::tabletToolEvent(TabletEvent *event)
return false;
}
bool InputEventFilter::tabletToolButtonEvent(const QSet<uint> &pressedButtons)
bool InputEventFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletId)
{
Q_UNUSED(pressedButtons)
Q_UNUSED(button)
Q_UNUSED(pressed)
Q_UNUSED(tabletId)
return false;
}
bool InputEventFilter::tabletPadButtonEvent(const QSet<uint> &pressedButtons)
bool InputEventFilter::tabletPadButtonEvent(uint button, bool pressed, const QString &deviceSysName)
{
Q_UNUSED(pressedButtons)
Q_UNUSED(button)
Q_UNUSED(pressed)
Q_UNUSED(deviceSysName)
return false;
}
bool InputEventFilter::tabletPadStripEvent(int number, int position, bool isFinger)
bool InputEventFilter::tabletPadStripEvent(int number, int position, bool isFinger, const QString &deviceSysName)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
Q_UNUSED(deviceSysName)
return false;
}
bool InputEventFilter::tabletPadRingEvent(int number, int position, bool isFinger)
bool InputEventFilter::tabletPadRingEvent(int number, int position, bool isFinger, const QString &deviceSysName)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
Q_UNUSED(deviceSysName)
return false;
}
......@@ -1575,23 +1581,138 @@ public:
if (device->isTabletTool()) {
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
if (!tabletSeat) {
qCCritical(KWIN_CORE) << "Could not find tablet manager";
qCCritical(KWIN_CORE) << "Could not find tablet seat";
return;
}
struct udev_device *const udev_device = libinput_device_get_udev_device(device->device());
const char *devnode = udev_device_get_devnode(udev_device);
const char *devnode = udev_device_get_syspath(udev_device);
tabletSeat->addTablet(device->vendor(), device->product(), device->sysName(), device->name(), {QString::fromUtf8(devnode)});
}
if (device->isTabletPad()) {
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
if (!tabletSeat) {
qCCritical(KWIN_CORE) << "Could not find tablet seat";
return;
}
struct udev_device *const udev_device = libinput_device_get_udev_device(device->device());
const char *devnode = udev_device_get_syspath(udev_device);
const int buttonsCount = libinput_device_tablet_pad_get_num_buttons(device->device());
const int ringsCount = libinput_device_tablet_pad_get_num_rings(device->device());
const int stripsCount = libinput_device_tablet_pad_get_num_strips(device->device());
const int modes = libinput_device_tablet_pad_get_num_mode_groups(device->device());
auto firstGroup = libinput_device_tablet_pad_get_mode_group(device->device(), 0);
tabletSeat->addTabletPad(device->sysName(), device->name(), {QString::fromUtf8(devnode)}, buttonsCount, ringsCount, stripsCount, modes, libinput_tablet_pad_mode_group_get_mode(firstGroup));
}
}
void removeDevice(const QString &sysname)
{
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
if (tabletSeat)
tabletSeat->removeTablet(sysname);
tabletSeat->removeDevice(sysname);
else
qCCritical(KWIN_CORE) << "Could not find tablet to remove" << sysname;
}
KWaylandServer::TabletToolV2Interface *createTool(const KWin::TabletToolId &tabletToolId)
{
using namespace KWaylandServer;
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
const auto f = [](InputRedirection::Capability cap) {
switch (cap) {
case InputRedirection::Tilt:
return TabletToolV2Interface::Tilt;
case InputRedirection::Pressure:
return TabletToolV2Interface::Pressure;
case InputRedirection::Distance:
return TabletToolV2Interface::Distance;
case InputRedirection::Rotation:
return TabletToolV2Interface::Rotation;
case InputRedirection::Slider:
return TabletToolV2Interface::Slider;
case InputRedirection::Wheel:
return TabletToolV2Interface::Wheel;
}
return TabletToolV2Interface::Wheel;
};
QVector<TabletToolV2Interface::Capability> ifaceCapabilities;
ifaceCapabilities.resize(tabletToolId.m_capabilities.size());
std::transform(tabletToolId.m_capabilities.constBegin(), tabletToolId.m_capabilities.constEnd(), ifaceCapabilities.begin(), f);
TabletToolV2Interface::Type toolType = TabletToolV2Interface::Type::Pen;
switch (tabletToolId.m_toolType) {
case InputRedirection::Pen:
toolType = TabletToolV2Interface::Type::Pen;
break;
case InputRedirection::Eraser:
toolType = TabletToolV2Interface::Type::Eraser;
break;
case InputRedirection::Brush:
toolType = TabletToolV2Interface::Type::Brush;
break;
case InputRedirection::Pencil:
toolType = TabletToolV2Interface::Type::Pencil;
break;
case InputRedirection::Airbrush:
toolType = TabletToolV2Interface::Type::Airbrush;
break;
case InputRedirection::Finger:
toolType = TabletToolV2Interface::Type::Finger;
break;
case InputRedirection::Mouse:
toolType = TabletToolV2Interface::Type::Mouse;
break;
case InputRedirection::Lens:
toolType = TabletToolV2Interface::Type::Lens;
break;
case InputRedirection::Totem:
toolType = TabletToolV2Interface::Type::Totem;
break;
}
TabletToolV2Interface *tool = tabletSeat->addTool(toolType, tabletToolId.m_serialId, tabletToolId.m_uniqueId, ifaceCapabilities);
const auto cursor = new Cursor(tool);
Cursors::self()->addCursor(cursor);
m_cursorByTool[tool] = cursor;
connect(tool, &TabletToolV2Interface::cursorChanged, cursor, &Cursor::cursorChanged);
connect(tool, &TabletToolV2Interface::cursorChanged, cursor, [cursor] (TabletCursorV2 *tcursor) {
static const auto createDefaultCursor = [] {
WaylandCursorImage defaultCursor;
WaylandCursorImage::Image ret;
defaultCursor.loadThemeCursor(CursorShape(Qt::CrossCursor), &ret);
return ret;
};
static const auto defaultCursor = createDefaultCursor();
if (!tcursor) {
cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
return;
}
auto cursorSurface = tcursor->surface();
if (!cursorSurface) {
cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
return;
}
auto buffer = cursorSurface->buffer();
if (!buffer) {
cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
return;
}
QImage cursorImage;
cursorImage = buffer->data().copy();
cursorImage.setDevicePixelRatio(cursorSurface->bufferScale());
cursor->updateCursor(cursorImage, tcursor->hotspot());
});
emit cursor->cursorChanged();
return tool;
}
bool tabletToolEvent(TabletEvent *event) override
{
if (!workspace()) {
......@@ -1603,102 +1724,12 @@ public:
qCCritical(KWIN_CORE) << "Could not find tablet manager";
return false;
}
auto tool = tabletSeat->toolByHardwareSerial(event->serialId());
auto tool = tabletSeat->toolByHardwareSerial(event->tabletId().m_serialId);
if (!tool) {
using namespace KWaylandServer;
const QVector<InputRedirection::Capability> capabilities = event->capabilities();
const auto f = [](InputRedirection::Capability cap) {
switch (cap) {
case InputRedirection::Tilt:
return TabletToolV2Interface::Tilt;
case InputRedirection::Pressure:
return TabletToolV2Interface::Pressure;
case InputRedirection::Distance:
return TabletToolV2Interface::Distance;
case InputRedirection::Rotation:
return TabletToolV2Interface::Rotation;
case InputRedirection::Slider:
return TabletToolV2Interface::Slider;
case InputRedirection::Wheel:
return TabletToolV2Interface::Wheel;
}
return TabletToolV2Interface::Wheel;
};
QVector<TabletToolV2Interface::Capability> ifaceCapabilities;
ifaceCapabilities.resize(capabilities.size());
std::transform(capabilities.constBegin(), capabilities.constEnd(), ifaceCapabilities.begin(), f);
TabletToolV2Interface::Type toolType = TabletToolV2Interface::Type::Pen;
switch (event->toolType()) {
case InputRedirection::Pen:
toolType = TabletToolV2Interface::Type::Pen;
break;
case InputRedirection::Eraser:
toolType = TabletToolV2Interface::Type::Eraser;
break;
case InputRedirection::Brush:
toolType = TabletToolV2Interface::Type::Brush;
break;
case InputRedirection::Pencil:
toolType = TabletToolV2Interface::Type::Pencil;
break;
case InputRedirection::Airbrush:
toolType = TabletToolV2Interface::Type::Airbrush;
break;
case InputRedirection::Finger:
toolType = TabletToolV2Interface::Type::Finger;
break;
case InputRedirection::Mouse:
toolType = TabletToolV2Interface::Type::Mouse;
break;
case InputRedirection::Lens:
toolType = TabletToolV2Interface::Type::Lens;
break;
case InputRedirection::Totem:
toolType = TabletToolV2Interface::Type::Totem;
break;
}
tool = tabletSeat->addTool(toolType, event->serialId(), event->uniqueId(), ifaceCapabilities);
const auto cursor = new Cursor(tool);
Cursors::self()->addCursor(cursor);
m_cursorByTool[tool] = cursor;
connect(tool, &TabletToolV2Interface::cursorChanged, cursor, &Cursor::cursorChanged);
connect(tool, &TabletToolV2Interface::cursorChanged, cursor, [cursor] (TabletCursorV2 *tcursor) {
static const auto createDefaultCursor = [] {
WaylandCursorImage defaultCursor;
WaylandCursorImage::Image ret;
defaultCursor.loadThemeCursor(CursorShape(Qt::CrossCursor), &ret);
return ret;
};
static const auto defaultCursor = createDefaultCursor();
if (!tcursor) {
cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
return;
}
auto cursorSurface = tcursor->surface();
if (!cursorSurface) {
cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
return;
}
auto buffer = cursorSurface->buffer();
if (!buffer) {
cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
return;
}
QImage cursorImage;
cursorImage = buffer->data().copy();
cursorImage.setDevicePixelRatio(cursorSurface->bufferScale());
cursor->updateCursor(cursorImage, tcursor->hotspot());
});
emit cursor->cursorChanged();
tool = createTool(event->tabletId());
}
KWaylandServer::TabletV2Interface *tablet = tabletSeat->tabletByName(event->tabletSysName());
KWaylandServer::TabletV2Interface *tablet = tabletSeat->tabletByName(event->tabletId().m_tabletSysName);
Toplevel *toplevel = input()->findToplevel(event->globalPos());
if (!toplevel || !toplevel->surface()) {
......@@ -1773,6 +1804,74 @@ public:
waylandServer()->simulateUserActivity();
return true;
}
bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId) override
{
KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
auto tool = tabletSeat->toolByHardwareSerial(tabletToolId.m_serialId);
if (!tool) {
tool = createTool(tabletToolId);
}
tool->sendButton(button, pressed);
return true;
}
KWaylandServer::TabletPadV2Interface *findPad(const QString &deviceSysName) const
{
Toplevel *toplevel = workspace()->activeClient();
auto seat = findTabletSeat();
if (!toplevel || !toplevel->surface() || !seat->isClientSupported(toplevel->surface()->client())) {
return nullptr;
}
KWaylandServer::SurfaceInterface *surface = toplevel->surface();
auto pad = seat->padByName(deviceSysName);
pad->setCurrentSurface(surface);
return pad;
}
bool tabletPadButtonEvent(uint button, bool pressed, const QString &deviceSysName) override
{
auto pad = findPad(deviceSysName);
if (!pad) {
return false;
}
pad->sendButton(QDateTime::currentMSecsSinceEpoch(), button, pressed);
return true;
}
bool tabletPadRingEvent(int number, int angle, bool isFinger, const QString &deviceSysName) override
{
auto pad = findPad(deviceSysName);
if (!pad) {
return false;
}
auto ring = pad->ring(number);
ring->sendAngle(angle);
if (isFinger) {
ring->sendSource(KWaylandServer::TabletPadRingV2Interface::SourceFinger);
}
ring->sendFrame(QDateTime::currentMSecsSinceEpoch());
return true;
}
bool tabletPadStripEvent(int number, int position, bool isFinger, const QString &deviceSysName) override
{
auto pad = findPad(deviceSysName);
if (!pad) {
return false;
}
auto strip = pad->strip(number);
strip->sendPosition(position);
if (isFinger) {
strip->sendSource(KWaylandServer::TabletPadStripV2Interface::SourceFinger);
}
strip->sendFrame(QDateTime::currentMSecsSinceEpoch());
return true;
}
QHash<KWaylandServer::TabletToolV2Interface*, Cursor*> m_cursorByTool;
};
......
......@@ -43,6 +43,7 @@ class WindowSelectorFilter;
class SwitchEvent;
class TabletEvent;
class TabletInputFilter;
class TabletToolId;
namespace Decoration
{
......@@ -400,10 +401,10 @@ public:
virtual bool switchEvent(SwitchEvent *event);
virtual bool tabletToolEvent(TabletEvent *event);
virtual bool tabletToolButtonEvent(const QSet<uint> &buttons);
virtual bool tabletPadButtonEvent(const QSet<uint> &buttons);
virtual bool tabletPadStripEvent(int number, int position, bool isFinger);
virtual bool tabletPadRingEvent(int number, int position, bool isFinger);
virtual bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId);
virtual bool tabletPadButtonEvent(uint button, bool pressed, const QString &deviceSysName);
virtual bool tabletPadStripEvent(int number, int position, bool isFinger, const QString &deviceSysName);
virtual bool tabletPadRingEvent(int number, int position, bool isFinger, const QString &deviceSysName);
protected:
void passToWaylandServer(QKeyEvent *event);
......
......@@ -58,12 +58,9 @@ TabletEvent::TabletEvent(Type t, const QPointF &pos, const QPointF &globalPos,
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z,
Qt::KeyboardModifiers keyState, qint64 uniqueID,
Qt::MouseButton button, Qt::MouseButtons buttons, InputRedirection::TabletToolType toolType, const QVector<InputRedirection::Capability> &capabilities, quint64 serialId, const QString &tabletSysName)
Qt::MouseButton button, Qt::MouseButtons buttons, const TabletToolId &tabletId)
: QTabletEvent(t, pos, globalPos, device, pointerType, pressure, xTilt, yTilt, tangentialPressure, rotation, z, keyState, uniqueID, button, buttons)
, m_toolType(toolType)
, m_capabilities(capabilities)
, m_serialId(serialId)
, m_tabletSysName(tabletSysName)
, m_id(tabletId)
{
}
......
......@@ -165,6 +165,16 @@ private:
LibInput::Device *m_device;
};
class TabletToolId
{
public:
const InputRedirection::TabletToolType m_toolType;
const QVector<InputRedirection::Capability> m_capabilities;
const quint64 m_serialId;
const quint64 m_uniqueId;
const QString m_tabletSysName;
};
class TabletEvent : public QTabletEvent
{
public:
......@@ -172,20 +182,14 @@ public:
int device, int pointerType, qreal pressure, int xTilt, int yTilt,
qreal tangentialPressure, qreal rotation, int z,
Qt::KeyboardModifiers keyState, qint64 uniqueID,
Qt::MouseButton button, Qt::MouseButtons buttons, InputRedirection::TabletToolType toolType,
const QVector<InputRedirection::Capability> &capabilities,
quint64 serialId, const QString &tabletSysname);
Qt::MouseButton button, Qt::MouseButtons buttons, const TabletToolId &tabletId);
InputRedirection::TabletToolType toolType() const { return m_toolType; }
QVector<InputRedirection::Capability> capabilities() const { return m_capabilities; }
quint64 serialId() const { return m_serialId; }
QString tabletSysName() { return m_tabletSysName; }
const TabletToolId &tabletId() const {
return m_id;
}
private:
const InputRedirection::TabletToolType m_toolType;
const QVector<InputRedirection::Capability> m_capabilities;
const quint64 m_serialId;
const QString m_tabletSysName;
const TabletToolId m_id;
};
}
......
......@@ -115,27 +115,33 @@ void InputEventSpy::tabletToolEvent(TabletEvent *event)
Q_UNUSED(event)
}
void InputEventSpy::tabletToolButtonEvent(const QSet<uint> &pressedButtons)
void InputEventSpy::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId)
{
Q_UNUSED(pressedButtons)
Q_UNUSED(button)
Q_UNUSED(pressed)
Q_UNUSED(tabletToolId)
}
void InputEventSpy::tabletPadButtonEvent(const QSet<uint> &pressedButtons)
void InputEventSpy::tabletPadButtonEvent(uint button, bool pressed, const QString &deviceSysName)
{
Q_UNUSED(pressedButtons)
Q_UNUSED(button)
Q_UNUSED(pressed)
Q_UNUSED(deviceSysName)
}
void InputEventSpy::tabletPadStripEvent(int number, int position, bool isFinger)
void InputEventSpy::tabletPadStripEvent(int number, int position, bool isFinger, const QString &deviceSysName)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
Q_UNUSED(deviceSysName)
}
void InputEventSpy::tabletPadRingEvent(int number, int position, bool isFinger)
void InputEventSpy::tabletPadRingEvent(int number, int position, bool isFinger, const QString &deviceSysName)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
Q_UNUSED(deviceSysName)
}
}
......@@ -23,6 +23,7 @@ class MouseEvent;
class WheelEvent;
class SwitchEvent;
class TabletEvent;
class TabletToolId;
/**
* Base class for spying on input events inside InputRedirection.
......@@ -75,10 +76,10 @@ public:
virtual void switchEvent(SwitchEvent *event);
virtual void tabletToolEvent(TabletEvent *event);
virtual void tabletToolButtonEvent(const QSet<uint> &pressedButtons);
virtual void tabletPadButtonEvent(const QSet<uint> &pressedButtons);
virtual void tabletPadStripEvent(int number, int position, bool isFinger);
virtual void tabletPadRingEvent(int number, int position, bool isFinger);
virtual void tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId);
virtual void tabletPadButtonEvent(uint button, bool pressed, const QString &deviceSysName);
virtual void tabletPadStripEvent(int number, int position, bool isFinger, const QString &deviceSysName);
virtual void tabletPadRingEvent(int number, int position, bool isFinger, const QString &deviceSysName);
};
......
......@@ -19,6 +19,7 @@
#include "../screens.h"