diff --git a/src/input.cpp b/src/input.cpp index 272f793991..ca7e091c19 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -2070,57 +2070,6 @@ public: } }; -static SeatInterface *findSeat() -{ - auto server = waylandServer(); - if (!server) { - return nullptr; - } - return server->seat(); -} - -class SurfaceCursor : public Cursor -{ -public: - explicit SurfaceCursor(TabletToolV2Interface *tool) - : Cursor() - { - setParent(tool); - connect(tool, &TabletToolV2Interface::cursorChanged, this, [this](const TabletCursorSourceV2 &cursor) { - if (auto surfaceCursor = std::get_if(&cursor)) { - // If the cursor is unset, fallback to the cross cursor. - if ((*surfaceCursor) && (*surfaceCursor)->enteredSerial()) { - if (!m_surfaceSource) { - m_surfaceSource = std::make_unique(); - } - m_surfaceSource->update((*surfaceCursor)->surface(), (*surfaceCursor)->hotspot()); - setSource(m_surfaceSource.get()); - return; - } - } - - QByteArray shape; - if (auto shapeCursor = std::get_if(&cursor)) { - shape = *shapeCursor; - } else { - shape = QByteArrayLiteral("cross"); - } - - static WaylandCursorImage defaultCursor; - if (!m_shapeSource) { - m_shapeSource = std::make_unique(); - } - m_shapeSource->setTheme(defaultCursor.theme()); - m_shapeSource->setShape(shape); - setSource(m_shapeSource.get()); - }); - } - -private: - std::unique_ptr m_shapeSource; - std::unique_ptr m_surfaceSource; -}; - /** * Handles input coming from a tablet device (e.g. wacom) often with a pen */ @@ -2130,172 +2079,6 @@ public: TabletInputFilter() : InputEventFilter(InputFilterOrder::Tablet) { - const auto devices = input()->devices(); - for (InputDevice *device : devices) { - integrateDevice(device); - } - connect(input(), &InputRedirection::deviceAdded, this, &TabletInputFilter::integrateDevice); - connect(input(), &InputRedirection::deviceRemoved, this, &TabletInputFilter::removeDevice); - - auto tabletNextOutput = new QAction(this); - tabletNextOutput->setProperty("componentName", QStringLiteral("kwin")); - tabletNextOutput->setText(i18n("Move the tablet to the next output")); - tabletNextOutput->setObjectName(QStringLiteral("Move Tablet to Next Output")); - KGlobalAccel::setGlobalShortcut(tabletNextOutput, QList()); - connect(tabletNextOutput, &QAction::triggered, this, &TabletInputFilter::trackNextOutput); - } - - static TabletSeatV2Interface *findTabletSeat() - { - auto server = waylandServer(); - if (!server) { - return nullptr; - } - TabletManagerV2Interface *manager = server->tabletManagerV2(); - return manager->seat(findSeat()); - } - - void integrateDevice(InputDevice *inputDevice) - { - auto device = qobject_cast(inputDevice); - if (!device || (!device->isTabletTool() && !device->isTabletPad())) { - return; - } - - 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); - - auto deviceGroup = libinput_device_get_device_group(device->device()); - auto tablet = static_cast(libinput_device_group_get_user_data(deviceGroup)); - if (!tablet) { - tablet = tabletSeat->addTablet(device->vendor(), device->product(), device->sysName(), device->name(), {QString::fromUtf8(devnode)}); - libinput_device_group_set_user_data(deviceGroup, tablet); - } - - if (device->isTabletPad()) { - 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), tablet); - } - } - - static void trackNextOutput() - { - const auto outputs = workspace()->outputs(); - if (outputs.isEmpty()) { - return; - } - - int tabletToolCount = 0; - InputDevice *changedDevice = nullptr; - const auto devices = input()->devices(); - for (const auto device : devices) { - if (!device->isTabletTool()) { - continue; - } - - tabletToolCount++; - if (device->outputName().isEmpty()) { - device->setOutputName(outputs.constFirst()->name()); - changedDevice = device; - continue; - } - - auto it = std::find_if(outputs.begin(), outputs.end(), [device](const auto &output) { - return output->name() == device->outputName(); - }); - ++it; - auto nextOutput = it == outputs.end() ? outputs.first() : *it; - device->setOutputName(nextOutput->name()); - changedDevice = device; - } - const QString message = tabletToolCount == 1 ? i18n("Tablet moved to %1", changedDevice->outputName()) : i18n("Tablets switched outputs"); - OSD::show(message, QStringLiteral("input-tablet"), 5000); - } - - void removeDevice(InputDevice *inputDevice) - { - auto device = qobject_cast(inputDevice); - if (device) { - auto deviceGroup = libinput_device_get_device_group(device->device()); - libinput_device_group_set_user_data(deviceGroup, nullptr); - - TabletSeatV2Interface *tabletSeat = findTabletSeat(); - if (tabletSeat) { - tabletSeat->removeDevice(device->sysName()); - } else { - qCCritical(KWIN_CORE) << "Could not find tablet to remove" << device->sysName(); - } - } - } - - TabletToolV2Interface::Type getType(const TabletToolId &tabletToolId) - { - using Type = TabletToolV2Interface::Type; - switch (tabletToolId.m_toolType) { - case InputRedirection::Pen: - return Type::Pen; - case InputRedirection::Eraser: - return Type::Eraser; - case InputRedirection::Brush: - return Type::Brush; - case InputRedirection::Pencil: - return Type::Pencil; - case InputRedirection::Airbrush: - return Type::Airbrush; - case InputRedirection::Finger: - return Type::Finger; - case InputRedirection::Mouse: - return Type::Mouse; - case InputRedirection::Lens: - return Type::Lens; - case InputRedirection::Totem: - return Type::Totem; - } - return Type::Pen; - } - - TabletToolV2Interface *createTool(const TabletToolId &tabletToolId) - { - 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; - }; - QList ifaceCapabilities; - ifaceCapabilities.resize(tabletToolId.m_capabilities.size()); - std::transform(tabletToolId.m_capabilities.constBegin(), tabletToolId.m_capabilities.constEnd(), ifaceCapabilities.begin(), f); - - TabletToolV2Interface *tool = tabletSeat->addTool(getType(tabletToolId), tabletToolId.m_serialId, tabletToolId.m_uniqueId, ifaceCapabilities, tabletToolId.deviceSysName); - - const auto cursor = new SurfaceCursor(tool); - Cursors::self()->addCursor(cursor); - m_cursorByTool[tool] = cursor; - - return tool; } bool tabletToolEvent(TabletEvent *event) override @@ -2304,25 +2087,7 @@ public: return false; } - TabletSeatV2Interface *tabletSeat = findTabletSeat(); - if (!tabletSeat) { - qCCritical(KWIN_CORE) << "Could not find tablet manager"; - return false; - } - auto tool = tabletSeat->toolByHardwareSerial(event->tabletId().m_serialId, getType(event->tabletId())); - if (!tool) { - tool = createTool(event->tabletId()); - } - - switch (event->type()) { - case QEvent::TabletEnterProximity: - case QEvent::TabletPress: - case QEvent::TabletMove: - m_cursorByTool[tool]->setPos(event->globalPosition()); - break; - default: - break; - } + TabletToolV2Interface *tool = input()->tablet()->ensureTabletTool(event->tabletId()); // NOTE: tablet will be nullptr as the device is removed (see ::removeDevice) but events from the tool // may still happen (e.g. Release or ProximityOut events) @@ -2415,11 +2180,7 @@ public: bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time) override { - TabletSeatV2Interface *tabletSeat = findTabletSeat(); - auto tool = tabletSeat->toolByHardwareSerial(tabletToolId.m_serialId, getType(tabletToolId)); - if (!tool) { - tool = createTool(tabletToolId); - } + TabletToolV2Interface *tool = input()->tablet()->ensureTabletTool(tabletToolId); if (!tool->isClientSupported()) { return false; } @@ -2430,7 +2191,7 @@ public: TabletPadV2Interface *findAndAdoptPad(const TabletPadId &tabletPadId) const { Window *window = workspace()->activeWindow(); - auto seat = findTabletSeat(); + TabletSeatV2Interface *seat = waylandServer()->tabletManagerV2()->seat(waylandServer()->seat()); if (!window || !window->surface() || !seat->isClientSupported(window->surface()->client())) { return nullptr; } @@ -2492,8 +2253,6 @@ public: strip->sendFrame(std::chrono::duration_cast(time).count()); return true; } - - QHash m_cursorByTool; }; static AbstractDropHandler *dropHandler(Window *window) diff --git a/src/tablet_input.cpp b/src/tablet_input.cpp index d3f20a93f8..fdb61aa4f4 100644 --- a/src/tablet_input.cpp +++ b/src/tablet_input.cpp @@ -7,23 +7,71 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "tablet_input.h" +#include "backends/libinput/device.h" +#include "cursorsource.h" #include "decorations/decoratedclient.h" #include "input_event.h" #include "input_event_spy.h" +#include "osd.h" #include "pointer_input.h" -#include "wayland/seat.h" -#include "wayland/surface.h" +#include "wayland/tablet_v2.h" #include "wayland_server.h" #include "window.h" #include "workspace.h" -// KDecoration + #include -// Qt +#include +#include + +#include #include #include namespace KWin { + +class SurfaceCursor : public Cursor +{ +public: + explicit SurfaceCursor(TabletToolV2Interface *tool) + : Cursor() + { + setParent(tool); + connect(tool, &TabletToolV2Interface::cursorChanged, this, [this](const TabletCursorSourceV2 &cursor) { + if (auto surfaceCursor = std::get_if(&cursor)) { + // If the cursor is unset, fallback to the cross cursor. + if ((*surfaceCursor) && (*surfaceCursor)->enteredSerial()) { + if (!m_surfaceSource) { + m_surfaceSource = std::make_unique(); + } + m_surfaceSource->update((*surfaceCursor)->surface(), (*surfaceCursor)->hotspot()); + setSource(m_surfaceSource.get()); + return; + } + } + + QByteArray shape; + if (auto shapeCursor = std::get_if(&cursor)) { + shape = *shapeCursor; + } else { + shape = QByteArrayLiteral("cross"); + } + + static WaylandCursorImage defaultCursor; + if (!m_shapeSource) { + m_shapeSource = std::make_unique(); + } + m_shapeSource->setTheme(defaultCursor.theme()); + m_shapeSource->setShape(shape); + setSource(m_shapeSource.get()); + }); + } + +private: + std::unique_ptr m_shapeSource; + std::unique_ptr m_surfaceSource; +}; + TabletInputRedirection::TabletInputRedirection(InputRedirection *parent) : InputDeviceHandler(parent) { @@ -43,6 +91,176 @@ void TabletInputRedirection::init() connect(waylandServer(), &QObject::destroyed, this, [this] { setInited(false); }); + + const auto devices = input()->devices(); + for (InputDevice *device : devices) { + integrateDevice(device); + } + connect(input(), &InputRedirection::deviceAdded, this, &TabletInputRedirection::integrateDevice); + connect(input(), &InputRedirection::deviceRemoved, this, &TabletInputRedirection::removeDevice); + + auto tabletNextOutput = new QAction(this); + tabletNextOutput->setProperty("componentName", QStringLiteral("kwin")); + tabletNextOutput->setText(i18n("Move the tablet to the next output")); + tabletNextOutput->setObjectName(QStringLiteral("Move Tablet to Next Output")); + KGlobalAccel::setGlobalShortcut(tabletNextOutput, QList()); + connect(tabletNextOutput, &QAction::triggered, this, &TabletInputRedirection::trackNextOutput); +} + +static TabletSeatV2Interface *findTabletSeat() +{ + auto server = waylandServer(); + if (!server) { + return nullptr; + } + TabletManagerV2Interface *manager = server->tabletManagerV2(); + return manager->seat(waylandServer()->seat()); +} + +void TabletInputRedirection::integrateDevice(InputDevice *inputDevice) +{ + auto device = qobject_cast(inputDevice); + if (!device || (!device->isTabletTool() && !device->isTabletPad())) { + return; + } + + 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); + + auto deviceGroup = libinput_device_get_device_group(device->device()); + auto tablet = static_cast(libinput_device_group_get_user_data(deviceGroup)); + if (!tablet) { + tablet = tabletSeat->addTablet(device->vendor(), device->product(), device->sysName(), device->name(), {QString::fromUtf8(devnode)}); + libinput_device_group_set_user_data(deviceGroup, tablet); + } + + if (device->isTabletPad()) { + 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), tablet); + } +} + +void TabletInputRedirection::removeDevice(InputDevice *inputDevice) +{ + auto device = qobject_cast(inputDevice); + if (device) { + auto deviceGroup = libinput_device_get_device_group(device->device()); + libinput_device_group_set_user_data(deviceGroup, nullptr); + + TabletSeatV2Interface *tabletSeat = findTabletSeat(); + if (tabletSeat) { + tabletSeat->removeDevice(device->sysName()); + } else { + qCCritical(KWIN_CORE) << "Could not find tablet to remove" << device->sysName(); + } + } +} + +void TabletInputRedirection::trackNextOutput() +{ + const auto outputs = workspace()->outputs(); + if (outputs.isEmpty()) { + return; + } + + int tabletToolCount = 0; + InputDevice *changedDevice = nullptr; + const auto devices = input()->devices(); + for (const auto device : devices) { + if (!device->isTabletTool()) { + continue; + } + + tabletToolCount++; + if (device->outputName().isEmpty()) { + device->setOutputName(outputs.constFirst()->name()); + changedDevice = device; + continue; + } + + auto it = std::find_if(outputs.begin(), outputs.end(), [device](const auto &output) { + return output->name() == device->outputName(); + }); + ++it; + auto nextOutput = it == outputs.end() ? outputs.first() : *it; + device->setOutputName(nextOutput->name()); + changedDevice = device; + } + const QString message = tabletToolCount == 1 ? i18n("Tablet moved to %1", changedDevice->outputName()) : i18n("Tablets switched outputs"); + OSD::show(message, QStringLiteral("input-tablet"), 5000); +} + +static TabletToolV2Interface::Type getType(const TabletToolId &tabletToolId) +{ + using Type = TabletToolV2Interface::Type; + switch (tabletToolId.m_toolType) { + case InputRedirection::Pen: + return Type::Pen; + case InputRedirection::Eraser: + return Type::Eraser; + case InputRedirection::Brush: + return Type::Brush; + case InputRedirection::Pencil: + return Type::Pencil; + case InputRedirection::Airbrush: + return Type::Airbrush; + case InputRedirection::Finger: + return Type::Finger; + case InputRedirection::Mouse: + return Type::Mouse; + case InputRedirection::Lens: + return Type::Lens; + case InputRedirection::Totem: + return Type::Totem; + } + return Type::Pen; +} + +TabletToolV2Interface *TabletInputRedirection::ensureTabletTool(const TabletToolId &tabletToolId) +{ + TabletSeatV2Interface *tabletSeat = findTabletSeat(); + if (auto tool = tabletSeat->toolByHardwareSerial(tabletToolId.m_serialId, getType(tabletToolId))) { + return tool; + } + + 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; + }; + QList ifaceCapabilities; + ifaceCapabilities.resize(tabletToolId.m_capabilities.size()); + std::transform(tabletToolId.m_capabilities.constBegin(), tabletToolId.m_capabilities.constEnd(), ifaceCapabilities.begin(), f); + + TabletToolV2Interface *tool = tabletSeat->addTool(getType(tabletToolId), tabletToolId.m_serialId, tabletToolId.m_uniqueId, ifaceCapabilities, tabletToolId.deviceSysName); + + const auto cursor = new SurfaceCursor(tool); + Cursors::self()->addCursor(cursor); + m_cursorByTool[tool] = cursor; + + return tool; } void TabletInputRedirection::tabletToolEvent(KWin::InputRedirection::TabletEventType type, const QPointF &pos, @@ -69,6 +287,17 @@ void TabletInputRedirection::tabletToolEvent(KWin::InputRedirection::TabletEvent break; } + auto tool = ensureTabletTool(tabletToolId); + switch (t) { + case QEvent::TabletEnterProximity: + case QEvent::TabletPress: + case QEvent::TabletMove: + m_cursorByTool[tool]->setPos(pos); + break; + default: + break; + } + update(); workspace()->setActiveOutput(pos); diff --git a/src/tablet_input.h b/src/tablet_input.h index f35d8615e3..ca91185108 100644 --- a/src/tablet_input.h +++ b/src/tablet_input.h @@ -17,8 +17,10 @@ namespace KWin { +class Cursor; class Window; class TabletToolId; +class TabletToolV2Interface; namespace Decoration { @@ -61,10 +63,15 @@ public: return m_lastPosition; } + TabletToolV2Interface *ensureTabletTool(const TabletToolId &id); + private: void cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) override; void focusUpdate(Window *focusOld, Window *focusNow) override; + void integrateDevice(InputDevice *device); + void removeDevice(InputDevice *device); + void trackNextOutput(); bool m_tipDown = false; bool m_tipNear = false; @@ -72,6 +79,7 @@ private: QPointF m_lastPosition; QMetaObject::Connection m_decorationGeometryConnection; QMetaObject::Connection m_decorationDestroyedConnection; + QHash m_cursorByTool; }; }