Commit f5a73b87 authored by Aleix Pol Gonzalez's avatar Aleix Pol Gonzalez 🐧
Browse files

Initial support for tablets on Wayland

Summary:
This includes support for them on libinput and turns it into fake
pointer actions.
This doesn't implement zwp_tablet, this will have to happen in an
iteration later.

Test Plan:
Been playing around with it, see video.
https://www.youtube.com/watch?v=GF1WbO8FVvU

Reviewers: #plasma, #kwin, romangg

Reviewed By: #plasma, #kwin, romangg

Subscribers: zzag, davidedmundson, romangg, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D25663
parent 5114494b
......@@ -479,6 +479,7 @@ set(kwin_KDEINIT_SRCS
thumbnailitem.cpp
toplevel.cpp
touch_hide_cursor_spy.cpp
tablet_input.cpp
touch_input.cpp
udev.cpp
unmanaged.cpp
......
......@@ -897,3 +897,22 @@ uint64_t libinput_event_switch_get_time_usec(struct libinput_event_switch *event
{
return event->timeMicroseconds;
}
struct libinput_event_tablet_pad *libinput_event_get_tablet_pad_event(struct libinput_event *event)
{
if (event->type == LIBINPUT_EVENT_TABLET_PAD_BUTTON) {
return reinterpret_cast<libinput_event_tablet_pad *>(event);
}
return nullptr;
}
struct libinput_event_tablet_tool *
libinput_event_get_tablet_tool_event(struct libinput_event *event)
{
if (event->type == LIBINPUT_EVENT_TABLET_TOOL_AXIS ||
event->type == LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY ||
event->type == LIBINPUT_EVENT_TABLET_TOOL_TIP) {
return reinterpret_cast<libinput_event_tablet_tool *>(event);
}
return nullptr;
}
......@@ -482,6 +482,83 @@ void DebugConsoleFilter::switchEvent(SwitchEvent *event)
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletToolEvent(QTabletEvent *event)
{
QString typeString;
{
QDebug d(&typeString);
d << event->type();
}
QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool"))
+ tableRow(i18n("EventType"), typeString)
+ tableRow(i18n("Position"),
QStringLiteral("%1,%2").arg(event->pos().x()).arg(event->pos().y()))
+ tableRow(i18n("Tilt"),
QStringLiteral("%1,%2").arg(event->xTilt()).arg(event->yTilt()))
+ tableRow(i18n("Rotation"), QString::number(event->rotation()))
+ tableRow(i18n("Pressure"), QString::number(event->pressure()))
+ tableRow(i18n("Buttons"), QString::number(event->buttons()))
+ tableRow(i18n("Modifiers"), QString::number(event->modifiers()))
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletToolButtonEvent(const QSet<uint> &pressedButtons)
{
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)
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletPadButtonEvent(const QSet<uint> &pressedButtons)
{
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)
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletPadStripEvent(int number, int position, bool isFinger)
{
QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Strip"))
+ tableRow(i18n("Number"), number)
+ tableRow(i18n("Position"), position)
+ tableRow(i18n("isFinger"), isFinger)
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
void DebugConsoleFilter::tabletPadRingEvent(int number, int position, bool isFinger)
{
QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Ring"))
+ tableRow(i18n("Number"), number)
+ tableRow(i18n("Position"), position)
+ tableRow(i18n("isFinger"), isFinger)
+ s_tableEnd;
m_textEdit->insertHtml(text);
m_textEdit->ensureCursorVisible();
}
DebugConsole::DebugConsole()
: QWidget()
, m_ui(new Ui::DebugConsole)
......
......@@ -153,6 +153,12 @@ public:
void switchEvent(SwitchEvent *event) override;
void tabletToolEvent(QTabletEvent *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;
private:
QTextEdit *m_textEdit;
};
......
......@@ -20,18 +20,19 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "input.h"
#include "effects.h"
#include "gestures.h"
#include "globalshortcuts.h"
#include "input_event.h"
#include "input_event_spy.h"
#include "keyboard_input.h"
#include "logind.h"
#include "main.h"
#include "pointer_input.h"
#include "touch_input.h"
#include "tablet_input.h"
#include "touch_hide_cursor_spy.h"
#include "touch_input.h"
#include "x11client.h"
#include "effects.h"
#include "gestures.h"
#include "globalshortcuts.h"
#include "logind.h"
#include "main.h"
#ifdef KWIN_BUILD_TABBOX
#include "tabbox/tabbox.h"
#endif
......@@ -176,6 +177,40 @@ bool InputEventFilter::switchEvent(SwitchEvent *event)
return false;
}
bool InputEventFilter::tabletToolEvent(QTabletEvent *event)
{
Q_UNUSED(event)
return false;
}
bool InputEventFilter::tabletToolButtonEvent(const QSet<uint> &pressedButtons)
{
Q_UNUSED(pressedButtons)
return false;
}
bool InputEventFilter::tabletPadButtonEvent(const QSet<uint> &pressedButtons)
{
Q_UNUSED(pressedButtons)
return false;
}
bool InputEventFilter::tabletPadStripEvent(int number, int position, bool isFinger)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
return false;
}
bool InputEventFilter::tabletPadRingEvent(int number, int position, bool isFinger)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
return false;
}
void InputEventFilter::passToWaylandServer(QKeyEvent *event)
{
Q_ASSERT(waylandServer());
......@@ -1623,6 +1658,7 @@ InputRedirection::InputRedirection(QObject *parent)
: QObject(parent)
, m_keyboard(new KeyboardInputRedirection(this))
, m_pointer(new PointerInputRedirection(this))
, m_tablet(new TabletInputRedirection(this))
, m_touch(new TouchInputRedirection(this))
, m_shortcuts(new GlobalShortcutsManager(this))
{
......@@ -1807,6 +1843,7 @@ void InputRedirection::setupWorkspace()
m_keyboard->init();
m_pointer->init();
m_touch->init();
m_tablet->init();
}
setupInputFilters();
}
......@@ -1934,6 +1971,18 @@ void InputRedirection::setupLibInput()
std::bind(handleSwitchEvent, SwitchEvent::State::On, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
connect(conn, &LibInput::Connection::switchToggledOff, this,
std::bind(handleSwitchEvent, SwitchEvent::State::Off, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
connect(conn, &LibInput::Connection::tabletToolEvent,
m_tablet, &TabletInputRedirection::tabletToolEvent);
connect(conn, &LibInput::Connection::tabletToolButtonEvent,
m_tablet, &TabletInputRedirection::tabletToolButtonEvent);
connect(conn, &LibInput::Connection::tabletPadButtonEvent,
m_tablet, &TabletInputRedirection::tabletPadButtonEvent);
connect(conn, &LibInput::Connection::tabletPadRingEvent,
m_tablet, &TabletInputRedirection::tabletPadRingEvent);
connect(conn, &LibInput::Connection::tabletPadStripEvent,
m_tablet, &TabletInputRedirection::tabletPadStripEvent);
if (screens()) {
setupLibInputWithScreens();
} else {
......
......@@ -29,6 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <config-kwin.h>
#include <KSharedConfig>
#include <QSet>
#include <functional>
......@@ -46,6 +47,7 @@ class InputEventFilter;
class InputEventSpy;
class KeyboardInputRedirection;
class PointerInputRedirection;
class TabletInputRedirection;
class TouchInputRedirection;
class WindowSelectorFilter;
class SwitchEvent;
......@@ -58,6 +60,7 @@ class DecoratedClientImpl;
namespace LibInput
{
class Connection;
class Device;
}
/**
......@@ -92,6 +95,11 @@ public:
KeyboardKeyPressed,
KeyboardKeyAutoRepeat
};
enum TabletEventType {
Axis,
Proximity,
Tip
};
~InputRedirection() override;
void init();
......@@ -221,6 +229,9 @@ public:
PointerInputRedirection *pointer() const {
return m_pointer;
}
TabletInputRedirection *tablet() const {
return m_tablet;
}
TouchInputRedirection *touch() const {
return m_touch;
}
......@@ -284,6 +295,7 @@ private:
void installInputEventFilter(InputEventFilter *filter);
KeyboardInputRedirection *m_keyboard;
PointerInputRedirection *m_pointer;
TabletInputRedirection *m_tablet;
TouchInputRedirection *m_touch;
GlobalShortcutsManager *m_shortcuts;
......@@ -370,6 +382,12 @@ public:
virtual bool switchEvent(SwitchEvent *event);
virtual bool tabletToolEvent(QTabletEvent *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);
protected:
void passToWaylandServer(QKeyEvent *event);
};
......
......@@ -121,4 +121,32 @@ void InputEventSpy::switchEvent(SwitchEvent *event)
Q_UNUSED(event)
}
void InputEventSpy::tabletToolEvent(QTabletEvent *event)
{
Q_UNUSED(event)
}
void InputEventSpy::tabletToolButtonEvent(const QSet<uint> &pressedButtons)
{
Q_UNUSED(pressedButtons)
}
void InputEventSpy::tabletPadButtonEvent(const QSet<uint> &pressedButtons)
{
Q_UNUSED(pressedButtons)
}
void InputEventSpy::tabletPadStripEvent(int number, int position, bool isFinger)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
}
void InputEventSpy::tabletPadRingEvent(int number, int position, bool isFinger)
{
Q_UNUSED(number)
Q_UNUSED(position)
Q_UNUSED(isFinger)
}
}
......@@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class QPointF;
class QSizeF;
class QTabletEvent;
namespace KWin
{
......@@ -84,6 +85,11 @@ public:
virtual void switchEvent(SwitchEvent *event);
virtual void tabletToolEvent(QTabletEvent *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);
};
......
......@@ -463,6 +463,62 @@ void Connection::processEvents()
}
break;
}
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
case LIBINPUT_EVENT_TABLET_TOOL_TIP: {
auto *tte = static_cast<TabletToolEvent *>(event.data());
KWin::InputRedirection::TabletEventType tabletEventType;
switch (event->type()) {
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
tabletEventType = KWin::InputRedirection::Axis;
break;
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
tabletEventType = KWin::InputRedirection::Proximity;
break;
case LIBINPUT_EVENT_TABLET_TOOL_TIP:
default:
tabletEventType = KWin::InputRedirection::Tip;
break;
}
auto serial = libinput_tablet_tool_get_serial(tte->tool());
auto toolId = libinput_tablet_tool_get_tool_id(tte->tool());
emit tabletToolEvent(tabletEventType,
tte->transformedPosition(m_size), tte->pressure(),
tte->xTilt(), tte->yTilt(), tte->rotation(),
tte->isTipDown(), tte->isNearby(), serial,
toolId, event->device());
break;
}
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: {
auto *tabletEvent = static_cast<TabletToolButtonEvent *>(event.data());
emit tabletToolButtonEvent(tabletEvent->buttonId(),
tabletEvent->isButtonPressed());
break;
}
case LIBINPUT_EVENT_TABLET_PAD_BUTTON: {
auto *tabletEvent = static_cast<TabletPadButtonEvent *>(event.data());
emit tabletPadButtonEvent(tabletEvent->buttonId(),
tabletEvent->isButtonPressed());
break;
}
case LIBINPUT_EVENT_TABLET_PAD_RING: {
auto *tabletEvent = static_cast<TabletPadRingEvent *>(event.data());
emit tabletPadRingEvent(tabletEvent->number(),
tabletEvent->position(),
tabletEvent->source() ==
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
break;
}
case LIBINPUT_EVENT_TABLET_PAD_STRIP: {
auto *tabletEvent = static_cast<TabletPadStripEvent *>(event.data());
emit tabletPadStripEvent(tabletEvent->number(),
tabletEvent->position(),
tabletEvent->source() ==
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
break;
}
default:
// nothing
break;
......
......@@ -131,6 +131,15 @@ Q_SIGNALS:
void switchToggledOn(quint32 time, quint64 timeMicroseconds, KWin::LibInput::Device *device);
void switchToggledOff(quint32 time, quint64 timeMicroseconds, KWin::LibInput::Device *device);
void tabletToolEvent(KWin::InputRedirection::TabletEventType type, const QPointF &pos,
qreal pressure, int xTilt, int yTilt, qreal rotation, bool tipDown,
bool tipNear, quint64 serialId, quint64 toolId, LibInput::Device *device);
void tabletToolButtonEvent(uint button, bool isPressed);
void tabletPadButtonEvent(uint button, bool isPressed);
void tabletPadStripEvent(int number, int position, bool isFinger);
void tabletPadRingEvent(int number, int position, bool isFinger);
void eventsRead();
private Q_SLOTS:
......
......@@ -57,6 +57,18 @@ Event *Event::create(libinput_event *event)
case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
case LIBINPUT_EVENT_GESTURE_PINCH_END:
return new PinchGestureEvent(event, t);
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
case LIBINPUT_EVENT_TABLET_TOOL_TIP:
return new TabletToolEvent(event, t);
case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
return new TabletToolButtonEvent(event, t);
case LIBINPUT_EVENT_TABLET_PAD_RING:
return new TabletPadRingEvent(event, t);
case LIBINPUT_EVENT_TABLET_PAD_STRIP:
return new TabletPadStripEvent(event, t);
case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
return new TabletPadButtonEvent(event, t);
case LIBINPUT_EVENT_SWITCH_TOGGLE:
return new SwitchEvent(event, t);
default:
......@@ -352,5 +364,34 @@ quint64 SwitchEvent::timeMicroseconds() const
return libinput_event_switch_get_time_usec(m_switchEvent);
}
TabletToolEvent::TabletToolEvent(libinput_event *event, libinput_event_type type)
: Event(event, type)
, m_tabletToolEvent(libinput_event_get_tablet_tool_event(event))
{
}
TabletToolButtonEvent::TabletToolButtonEvent(libinput_event *event, libinput_event_type type)
: Event(event, type)
, m_tabletToolEvent(libinput_event_get_tablet_tool_event(event))
{
}
TabletPadButtonEvent::TabletPadButtonEvent(libinput_event *event, libinput_event_type type)
: Event(event, type)
, m_tabletPadEvent(libinput_event_get_tablet_pad_event(event))
{
}
TabletPadStripEvent::TabletPadStripEvent(libinput_event *event, libinput_event_type type)
: Event(event, type)
, m_tabletPadEvent(libinput_event_get_tablet_pad_event(event))
{
}
TabletPadRingEvent::TabletPadRingEvent(libinput_event *event, libinput_event_type type)
: Event(event, type)
, m_tabletPadEvent(libinput_event_get_tablet_pad_event(event))
{
}
}
}
......@@ -191,6 +191,177 @@ private:
libinput_event_switch *m_switchEvent;
};
class TabletToolEvent : public Event
{
public:
TabletToolEvent(libinput_event *event, libinput_event_type type);
bool xHasChanged() const {
return libinput_event_tablet_tool_x_has_changed(m_tabletToolEvent);
}
bool yHasChanged() const {
return libinput_event_tablet_tool_y_has_changed(m_tabletToolEvent);
}
bool pressureHasChanged() const {
return libinput_event_tablet_tool_pressure_has_changed(m_tabletToolEvent);
}
bool distanceHasChanged() const {
return libinput_event_tablet_tool_distance_has_changed(m_tabletToolEvent);
}
bool tiltXHasChanged() const {
return libinput_event_tablet_tool_tilt_x_has_changed(m_tabletToolEvent);
}
bool tiltYHasChanged() const {
return libinput_event_tablet_tool_tilt_y_has_changed(m_tabletToolEvent);
}
bool rotationHasChanged() const {
return libinput_event_tablet_tool_rotation_has_changed(m_tabletToolEvent);
}
bool sliderHasChanged() const {
return libinput_event_tablet_tool_slider_has_changed(m_tabletToolEvent);
}
// uncomment when depending on libinput 1.14 or when implementing totems
// bool sizeMajorHasChanged() const { return
// libinput_event_tablet_tool_size_major_has_changed(m_tabletToolEvent); } bool
// sizeMinorHasChanged() const { return
// libinput_event_tablet_tool_size_minor_has_changed(m_tabletToolEvent); }
bool wheelHasChanged() const {
return libinput_event_tablet_tool_wheel_has_changed(m_tabletToolEvent);
}
QPointF position() const {
return {libinput_event_tablet_tool_get_x(m_tabletToolEvent),
libinput_event_tablet_tool_get_y(m_tabletToolEvent)};
}
QPointF delta() const {
return {libinput_event_tablet_tool_get_dx(m_tabletToolEvent),
libinput_event_tablet_tool_get_dy(m_tabletToolEvent)};
}
qreal pressure() const {
return libinput_event_tablet_tool_get_pressure(m_tabletToolEvent);
}
qreal distance() const {
return libinput_event_tablet_tool_get_distance(m_tabletToolEvent);
}
int xTilt() const {
return libinput_event_tablet_tool_get_tilt_x(m_tabletToolEvent);
}
int yTilt() const {
return libinput_event_tablet_tool_get_tilt_y(m_tabletToolEvent);
}
qreal rotation() const {
return libinput_event_tablet_tool_get_rotation(m_tabletToolEvent);
}
qreal sliderPosition() const {
return libinput_event_tablet_tool_get_slider_position(m_tabletToolEvent);
}
// Uncomment when depending on libinput 1.14 or when implementing totems
// qreal sizeMajor() const { return
// libinput_event_tablet_tool_get_size_major(m_tabletToolEvent); }
// qreal sizeMinor() const {
// return libinput_event_tablet_tool_get_size_minor(m_tabletToolEvent); }
qreal wheelDelta() const {
return libinput_event_tablet_tool_get_wheel_delta(m_tabletToolEvent);
}
int wheelDeltaDiscrete() const {
return libinput_event_tablet_tool_get_wheel_delta_discrete(m_tabletToolEvent);
}
bool isTipDown() const {
const auto state = libinput_event_tablet_tool_get_tip_state(m_tabletToolEvent);
return state == LIBINPUT_TABLET_TOOL_TIP_DOWN;
}
bool isNearby() const {
const auto state = libinput_event_tablet_tool_get_proximity_state(m_tabletToolEvent);
return state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN;
}
QPointF transformedPosition(const QSize &size) const {
return {libinput_event_tablet_tool_get_x_transformed(m_tabletToolEvent, size.width()),
libinput_event_tablet_tool_get_y_transformed(m_tabletToolEvent, size.height())};
}
struct libinput_tablet_tool *tool() {
return libinput_event_tablet_tool_get_tool(m_tabletToolEvent);
}
private:
libinput_event_tablet_tool *m_tabletToolEvent;
};
class TabletToolButtonEvent : public Event
{
public:
TabletToolButtonEvent(libinput_event *event, libinput_event_type type);
uint buttonId() const {
return libinput_event_tablet_tool_get_button(m_tabletToolEvent);
}
bool isButtonPressed() const {
const auto state = libinput_event_tablet_tool_get_button_state(m_tabletToolEvent);
return state == LIBINPUT_BUTTON_STATE_PRESSED;
}
private:
libinput_event_tablet_tool *m_tabletToolEvent;
};
class TabletPadRingEvent : public Event
{
public:
TabletPadRingEvent(libinput_event *event, libinput_event_type type);
int position() const {
return libinput_event_tablet_pad_get_ring_position(m_tabletPadEvent);
}
int number() const {
return libinput_event_tablet_pad_get_ring_number(m_tabletPadEvent);
}
libinput_tablet_pad_ring_axis_source source() const {
return libinput_event_tablet_pad_get_ring_source(m_tabletPadEvent);
}