diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -646,6 +646,9 @@ ) add_library(kwin SHARED ${kwin_SRCS}) +if (Libinput_VERSION_STRING VERSION_GREATER 1.14) + target_compile_definitions(kwin PRIVATE -DLIBINPUT_HAS_TOTEM) +endif () set_target_properties(kwin PROPERTIES VERSION ${PROJECT_VERSION} diff --git a/autotests/libinput/mock_libinput.h b/autotests/libinput/mock_libinput.h --- a/autotests/libinput/mock_libinput.h +++ b/autotests/libinput/mock_libinput.h @@ -105,6 +105,9 @@ enum libinput_config_click_method defaultClickMethod = LIBINPUT_CONFIG_CLICK_METHOD_NONE; enum libinput_config_click_method clickMethod = LIBINPUT_CONFIG_CLICK_METHOD_NONE; bool setClickMethodReturnValue = 0; + uint32_t buttonCount = 0; + uint32_t stripCount = 0; + uint32_t ringCount = 0; }; struct libinput_event { diff --git a/autotests/libinput/mock_libinput.cpp b/autotests/libinput/mock_libinput.cpp --- a/autotests/libinput/mock_libinput.cpp +++ b/autotests/libinput/mock_libinput.cpp @@ -916,3 +916,21 @@ } return nullptr; } + +int +libinput_device_tablet_pad_get_num_strips(struct libinput_device *device) +{ + return device->stripCount; +} + +int +libinput_device_tablet_pad_get_num_rings(struct libinput_device *device) +{ + return device->ringCount; +} + +int +libinput_device_tablet_pad_get_num_buttons(struct libinput_device *device) +{ + return device->buttonCount; +} diff --git a/debug_console.h b/debug_console.h --- a/debug_console.h +++ b/debug_console.h @@ -153,7 +153,7 @@ void switchEvent(SwitchEvent *event) override; - void tabletToolEvent(QTabletEvent *event) override; + void tabletToolEvent(TabletEvent *event) override; void tabletToolButtonEvent(const QSet &pressedButtons) override; void tabletPadButtonEvent(const QSet &pressedButtons) override; void tabletPadStripEvent(int number, int position, bool isFinger) override; diff --git a/debug_console.cpp b/debug_console.cpp --- a/debug_console.cpp +++ b/debug_console.cpp @@ -29,6 +29,7 @@ #include "wayland_server.h" #include "workspace.h" #include "keyboard_input.h" +#include "input_event.h" #include "libinput/connection.h" #include "libinput/device.h" #include @@ -487,7 +488,7 @@ m_textEdit->ensureCursorVisible(); } -void DebugConsoleFilter::tabletToolEvent(QTabletEvent *event) +void DebugConsoleFilter::tabletToolEvent(TabletEvent *event) { QString typeString; { diff --git a/input.h b/input.h --- a/input.h +++ b/input.h @@ -51,6 +51,8 @@ class TouchInputRedirection; class WindowSelectorFilter; class SwitchEvent; +class TabletEvent; +class TabletInputFilter; namespace Decoration { @@ -100,6 +102,26 @@ Proximity, Tip }; + enum TabletToolType { + Pen, + Eraser, + Brush, + Pencil, + Airbrush, + Finger, + Mouse, + Lens, + Totem, + }; + enum Capability { + Tilt, + Pressure, + Distance, + Rotation, + Slider, + Wheel, + }; + ~InputRedirection() override; void init(); @@ -297,6 +319,7 @@ PointerInputRedirection *m_pointer; TabletInputRedirection *m_tablet; TouchInputRedirection *m_touch; + TabletInputFilter *m_tabletSupport = nullptr; GlobalShortcutsManager *m_shortcuts; @@ -382,7 +405,7 @@ virtual bool switchEvent(SwitchEvent *event); - virtual bool tabletToolEvent(QTabletEvent *event); + virtual bool tabletToolEvent(TabletEvent *event); virtual bool tabletToolButtonEvent(const QSet &buttons); virtual bool tabletPadButtonEvent(const QSet &buttons); virtual bool tabletPadStripEvent(int number, int position, bool isFinger); diff --git a/input.cpp b/input.cpp --- a/input.cpp +++ b/input.cpp @@ -36,25 +36,27 @@ #ifdef KWIN_BUILD_TABBOX #include "tabbox/tabbox.h" #endif -#include "unmanaged.h" -#include "screenedge.h" -#include "screens.h" -#include "workspace.h" +#include "internal_client.h" #include "libinput/connection.h" #include "libinput/device.h" #include "platform.h" #include "popup_input_filter.h" -#include "xdgshellclient.h" +#include "screenedge.h" +#include "screens.h" +#include "unmanaged.h" #include "wayland_server.h" +#include "workspace.h" +#include "xdgshellclient.h" #include "xwl/xwayland_interface.h" -#include "internal_client.h" +#include +#include #include #include -#include #include +#include +#include +#include #include -#include -#include //screenlocker #include @@ -177,7 +179,7 @@ return false; } -bool InputEventFilter::tabletToolEvent(QTabletEvent *event) +bool InputEventFilter::tabletToolEvent(TabletEvent *event) { Q_UNUSED(event) return false; @@ -1532,17 +1534,158 @@ } }; +static KWayland::Server::SeatInterface *findSeat() +{ + auto server = waylandServer(); + if (!server) { + return nullptr; + } + return server->seat(); +} + /** * Useful when there's no proper tablet support on the clients */ -class FakeTabletInputFilter : public InputEventFilter +class TabletInputFilter : public QObject, public InputEventFilter { public: - FakeTabletInputFilter() + TabletInputFilter() + { + } + + static KWayland::Server::TabletSeatInterface *findTabletSeat() { + auto server = waylandServer(); + if (!server) { + return nullptr; + } + KWayland::Server::TabletManagerInterface *manager = server->tabletManager(); + return manager->seat(findSeat()); + } + + void integrateDevice(LibInput::Device *device) + { + if (device->isTabletTool()) { + KWayland::Server::TabletSeatInterface *tabletSeat = findTabletSeat(); + struct udev_device *const udev_device = libinput_device_get_udev_device(device->device()); + const char *devnode = udev_device_get_devnode(udev_device); + tabletSeat->addTablet(device->vendor(), device->product(), device->sysName(), device->name(), {QString::fromUtf8(devnode)}); + } + } + void removeDevice(const QString &sysname) + { + KWayland::Server::TabletSeatInterface *tabletSeat = findTabletSeat(); + tabletSeat->removeTablet(sysname); + } + + bool tabletToolEvent(TabletEvent *event) override + { + if (!workspace()) { + return false; + } + + KWayland::Server::TabletSeatInterface *tabletSeat = findTabletSeat(); + auto tool = tabletSeat->toolByHardwareId(event->serialId()); + if (!tool) { + using namespace KWayland::Server; + + const QVector capabilities = event->capabilities(); + const auto f = [](InputRedirection::Capability cap) { + switch (cap) { + case InputRedirection::Tilt: + return TabletToolInterface::Tilt; + case InputRedirection::Pressure: + return TabletToolInterface::Pressure; + case InputRedirection::Distance: + return TabletToolInterface::Distance; + case InputRedirection::Rotation: + return TabletToolInterface::Rotation; + case InputRedirection::Slider: + return TabletToolInterface::Slider; + case InputRedirection::Wheel: + return TabletToolInterface::Wheel; + } + return TabletToolInterface::Wheel; + }; + QVector ifaceCapabilities; + ifaceCapabilities.resize(capabilities.size()); + std::transform(capabilities.constBegin(), capabilities.constEnd(), ifaceCapabilities.begin(), f); + + TabletToolInterface::Type toolType = TabletToolInterface::Type::Pen; + switch (event->toolType()) { + case InputRedirection::Pen: + toolType = TabletToolInterface::Type::Pen; + break; + case InputRedirection::Eraser: + toolType = TabletToolInterface::Type::Eraser; + break; + case InputRedirection::Brush: + toolType = TabletToolInterface::Type::Brush; + break; + case InputRedirection::Pencil: + toolType = TabletToolInterface::Type::Pencil; + break; + case InputRedirection::Airbrush: + toolType = TabletToolInterface::Type::Airbrush; + break; + case InputRedirection::Finger: + toolType = TabletToolInterface::Type::Finger; + break; + case InputRedirection::Mouse: + toolType = TabletToolInterface::Type::Mouse; + break; + case InputRedirection::Lens: + toolType = TabletToolInterface::Type::Lens; + break; + case InputRedirection::Totem: + toolType = TabletToolInterface::Type::Totem; + break; + } + tool = tabletSeat->addTool(toolType, event->serialId(), event->uniqueId(), ifaceCapabilities); + } + + KWayland::Server::TabletInterface *tablet = tabletSeat->tabletByName(event->tabletSysName()); + + Toplevel *toplevel = input()->findToplevel(event->globalPos()); + if (!toplevel || !toplevel->surface()) { + return false; + } + + KWayland::Server::SurfaceInterface *surface = toplevel->surface(); + tool->setCurrentSurface(surface); + + if (!tool->isClientSupported() || !tablet->isSurfaceSupported(surface)) { + return emulateTabletEvent(event); + } + + switch (event->type()) { + case QEvent::TabletMove: + tool->sendMotion(event->globalPosF() - toplevel->pos()); + break; + case QEvent::TabletEnterProximity: + tool->sendProximityIn(tablet); + break; + case QEvent::TabletLeaveProximity: + tool->sendProximityOut(); + break; + case QEvent::TabletPress: + tool->sendDown(); + break; + case QEvent::TabletRelease: + tool->sendUp(); + break; + default: + qCWarning(KWIN_CORE) << "Unexpected tablet event type" << event; + break; + } + const quint32 MAX_VAL = 65535; + tool->sendPressure(MAX_VAL * event->pressure()); + tool->sendFrame(event->timestamp()); + waylandServer()->simulateUserActivity(); + return true; } - bool tabletToolEvent(QTabletEvent *event) override + bool emulateTabletEvent(TabletEvent *event) { if (!workspace()) { return false; @@ -1933,7 +2076,14 @@ if (waylandServer()) { installInputEventFilter(new WindowActionInputFilter); installInputEventFilter(new ForwardInputFilter); - installInputEventFilter(new FakeTabletInputFilter); + + m_tabletSupport = new TabletInputFilter; + for (LibInput::Device *dev : m_libInput->devices()) { + m_tabletSupport->integrateDevice(dev); + } + connect(m_libInput, &LibInput::Connection::deviceAdded, m_tabletSupport, &TabletInputFilter::integrateDevice); + connect(m_libInput, &LibInput::Connection::deviceRemovedSysName, m_tabletSupport, &TabletInputFilter::removeDevice); + installInputEventFilter(m_tabletSupport); } } @@ -1951,15 +2101,6 @@ } } -static KWayland::Server::SeatInterface *findSeat() -{ - auto server = waylandServer(); - if (!server) { - return nullptr; - } - return server->seat(); -} - void InputRedirection::setupLibInput() { if (!Application::usesLibinput()) { @@ -2088,6 +2229,7 @@ } ); } + setupTouchpadShortcuts(); } diff --git a/input_event.h b/input_event.h --- a/input_event.h +++ b/input_event.h @@ -176,6 +176,29 @@ LibInput::Device *m_device; }; +class TabletEvent : public QTabletEvent +{ +public: + 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 &capabilities, + quint64 serialId, const QString &tabletSysname); + + InputRedirection::TabletToolType toolType() const { return m_toolType; } + QVector capabilities() const { return m_capabilities; } + quint64 serialId() const { return m_serialId; } + QString tabletSysName() { return m_tabletSysName; } + +private: + const InputRedirection::TabletToolType m_toolType; + const QVector m_capabilities; + const quint64 m_serialId; + const QString m_tabletSysName; +}; + } #endif diff --git a/input_event.cpp b/input_event.cpp --- a/input_event.cpp +++ b/input_event.cpp @@ -65,4 +65,17 @@ setTimestamp(timestamp); } +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 &capabilities, quint64 serialId, const QString &tabletSysName) + : 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) +{ +} + } diff --git a/input_event_spy.h b/input_event_spy.h --- a/input_event_spy.h +++ b/input_event_spy.h @@ -33,7 +33,7 @@ class MouseEvent; class WheelEvent; class SwitchEvent; - +class TabletEvent; /** * Base class for spying on input events inside InputRedirection. @@ -85,7 +85,7 @@ virtual void switchEvent(SwitchEvent *event); - virtual void tabletToolEvent(QTabletEvent *event); + virtual void tabletToolEvent(TabletEvent *event); virtual void tabletToolButtonEvent(const QSet &pressedButtons); virtual void tabletPadButtonEvent(const QSet &pressedButtons); virtual void tabletPadStripEvent(int number, int position, bool isFinger); diff --git a/input_event_spy.cpp b/input_event_spy.cpp --- a/input_event_spy.cpp +++ b/input_event_spy.cpp @@ -121,7 +121,7 @@ Q_UNUSED(event) } -void InputEventSpy::tabletToolEvent(QTabletEvent *event) +void InputEventSpy::tabletToolEvent(TabletEvent *event) { Q_UNUSED(event) } diff --git a/libinput/connection.h b/libinput/connection.h --- a/libinput/connection.h +++ b/libinput/connection.h @@ -133,7 +133,11 @@ 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); + bool tipNear, quint64 serialId, quint64 toolId, + InputRedirection::TabletToolType toolType, + const QVector &capabilities, + quint32 time, + LibInput::Device *device); void tabletToolButtonEvent(uint button, bool isPressed); void tabletPadButtonEvent(uint button, bool isPressed); diff --git a/libinput/connection.cpp b/libinput/connection.cpp --- a/libinput/connection.cpp +++ b/libinput/connection.cpp @@ -483,12 +483,62 @@ } auto serial = libinput_tablet_tool_get_serial(tte->tool()); auto toolId = libinput_tablet_tool_get_tool_id(tte->tool()); + auto type = libinput_tablet_tool_get_type(tte->tool()); + InputRedirection::TabletToolType toolType; + switch (type) { + case LIBINPUT_TABLET_TOOL_TYPE_PEN: + toolType = InputRedirection::Pen; + break; + case LIBINPUT_TABLET_TOOL_TYPE_ERASER: + toolType = InputRedirection::Eraser; + break; + case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: + toolType = InputRedirection::Brush; + break; + case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: + toolType = InputRedirection::Pencil; + break; + case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: + toolType = InputRedirection::Airbrush; + break; + case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: + toolType = InputRedirection::Mouse; + break; + case LIBINPUT_TABLET_TOOL_TYPE_LENS: + toolType = InputRedirection::Lens; + break; +#ifdef LIBINPUT_HAS_TOTEM + case LIBINPUT_TABLET_TOOL_TYPE_TOTEM: + toolType = InputRedirection::Totem; + break; +#endif + } + QVector capabilities; + if (libinput_tablet_tool_has_pressure(tte->tool())) { + capabilities << InputRedirection::Pressure; + } + if (libinput_tablet_tool_has_distance(tte->tool())) { + capabilities << InputRedirection::Distance; + } + if (libinput_tablet_tool_has_rotation(tte->tool())) { + capabilities << InputRedirection::Rotation; + } + if (libinput_tablet_tool_has_tilt(tte->tool())) { + capabilities << InputRedirection::Tilt; + } + if (libinput_tablet_tool_has_slider(tte->tool())) { + capabilities << InputRedirection::Slider; + } + if (libinput_tablet_tool_has_wheel(tte->tool())) { + capabilities << InputRedirection::Wheel; + } emit tabletToolEvent(tabletEventType, tte->transformedPosition(m_size), tte->pressure(), tte->xTilt(), tte->yTilt(), tte->rotation(), tte->isTipDown(), tte->isNearby(), serial, - toolId, event->device()); + toolId, toolType, capabilities, tte->time(), + event->device()); break; } case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: { diff --git a/libinput/device.h b/libinput/device.h --- a/libinput/device.h +++ b/libinput/device.h @@ -472,6 +472,9 @@ return m_tabletSwitch; } + int stripsCount() const; + int ringsCount() const; + /** * All created Devices */ diff --git a/libinput/device.cpp b/libinput/device.cpp --- a/libinput/device.cpp +++ b/libinput/device.cpp @@ -158,12 +158,7 @@ , m_pointer(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_POINTER)) , m_touch(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_TOUCH)) , m_tabletTool(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_TABLET_TOOL)) -#if 0 - // next libinput version , m_tabletPad(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_TABLET_PAD)) -#else - , m_tabletPad(false) -#endif , m_supportsGesture(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_GESTURE)) , m_switch(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_SWITCH)) , m_lidSwitch(m_switch ? libinput_device_switch_has_switch(m_device, LIBINPUT_SWITCH_LID) : false) @@ -427,6 +422,16 @@ } } +int Device::stripsCount() const +{ + return libinput_device_tablet_pad_get_num_strips(m_device); +} + +int Device::ringsCount() const +{ + return libinput_device_tablet_pad_get_num_rings(m_device); +} + #define CONFIG(method, condition, function, variable, key) \ void Device::method(bool set) \ { \ diff --git a/libinput/events.h b/libinput/events.h --- a/libinput/events.h +++ b/libinput/events.h @@ -196,6 +196,10 @@ public: TabletToolEvent(libinput_event *event, libinput_event_type type); + uint32_t time() const + { + return libinput_event_tablet_tool_get_time(m_tabletToolEvent); + } bool xHasChanged() const { return libinput_event_tablet_tool_x_has_changed(m_tabletToolEvent); } diff --git a/modifier_only_shortcuts.cpp b/modifier_only_shortcuts.cpp --- a/modifier_only_shortcuts.cpp +++ b/modifier_only_shortcuts.cpp @@ -59,7 +59,7 @@ m_pressedKeys.remove(event->nativeScanCode()); if (m_pressedKeys.isEmpty() && event->modifiersRelevantForGlobalShortcuts() == Qt::NoModifier && - !workspace()->globalShortcutsDisabled()) { + workspace() && !workspace()->globalShortcutsDisabled()) { if (m_modifier != Qt::NoModifier) { const auto list = options->modifierOnlyDBusShortcut(m_modifier); if (list.size() >= 4) { diff --git a/tablet_input.h b/tablet_input.h --- a/tablet_input.h +++ b/tablet_input.h @@ -26,11 +26,9 @@ #include #include #include -#include namespace KWin { -class InputRedirection; class Toplevel; namespace Decoration @@ -54,7 +52,9 @@ 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); + bool tipNear, quint64 serialId, quint64 toolId, + InputRedirection::TabletToolType toolType, const QVector &capabilities, + quint32 time, LibInput::Device *device); void tabletToolButtonEvent(uint button, bool isPressed); void tabletPadButtonEvent(uint button, bool isPressed); diff --git a/tablet_input.cpp b/tablet_input.cpp --- a/tablet_input.cpp +++ b/tablet_input.cpp @@ -21,6 +21,7 @@ #include "abstract_client.h" #include "decorations/decoratedclient.h" #include "input.h" +#include "input_event.h" #include "input_event_spy.h" #include "libinput/device.h" #include "pointer_input.h" @@ -60,10 +61,11 @@ const QPointF &pos, qreal pressure, int xTilt, int yTilt, qreal rotation, bool tipDown, bool tipNear, quint64 serialId, - quint64 toolId, LibInput::Device *device) + quint64 toolId, + InputRedirection::TabletToolType toolType, + const QVector &capabilities, + quint32 time, LibInput::Device *device) { - Q_UNUSED(device) - Q_UNUSED(toolId) if (!inited()) { return; } @@ -83,13 +85,14 @@ } const auto button = m_tipDown ? Qt::LeftButton : Qt::NoButton; - QTabletEvent ev(t, pos, pos, QTabletEvent::Stylus, QTabletEvent::Pen, pressure, + TabletEvent ev(t, pos, pos, QTabletEvent::Stylus, QTabletEvent::Pen, pressure, xTilt, yTilt, 0, // tangentialPressure rotation, 0, // z - Qt::NoModifier, serialId, button, button); + Qt::NoModifier, toolId, button, button, toolType, capabilities, serialId, device->sysName()); + ev.setTimestamp(time); input()->processSpies(std::bind(&InputEventSpy::tabletToolEvent, std::placeholders::_1, &ev)); input()->processFilters( std::bind(&InputEventFilter::tabletToolEvent, std::placeholders::_1, &ev)); diff --git a/wayland_server.h b/wayland_server.h --- a/wayland_server.h +++ b/wayland_server.h @@ -69,6 +69,7 @@ class KeyStateInterface; class LinuxDmabufUnstableV1Interface; class LinuxDmabufUnstableV1Buffer; +class TabletManagerInterface; } } @@ -105,6 +106,10 @@ KWayland::Server::SeatInterface *seat() { return m_seat; } + KWayland::Server::TabletManagerInterface *tabletManager() + { + return m_tabletManager; + } KWayland::Server::DataDeviceManagerInterface *dataDeviceManager() { return m_dataDeviceManager; } @@ -246,6 +251,7 @@ KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositor = nullptr; KWayland::Server::SeatInterface *m_seat = nullptr; + KWayland::Server::TabletManagerInterface *m_tabletManager = nullptr; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManager = nullptr; KWayland::Server::XdgShellInterface *m_xdgShell6 = nullptr; KWayland::Server::XdgShellInterface *m_xdgShell = nullptr; diff --git a/wayland_server.cpp b/wayland_server.cpp --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -315,6 +315,8 @@ } ); + m_tabletManager = m_display->createTabletManagerInterface(m_display); + m_xdgShell6 = m_display->createXdgShell(XdgShellInterfaceVersion::UnstableV6, m_display); m_xdgShell6->create(); connect(m_xdgShell6, &XdgShellInterface::surfaceCreated, this, &WaylandServer::createSurface);