diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index 79bfd6d11fd..a34ea340b81 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -385,6 +385,7 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 9)) , mQDisplay(display) , mDisplay(display->wl_display()) + , mId(id) { #if QT_CONFIG(wayland_datadevice) if (mQDisplay->dndSelectionHandler()) { diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h index 06a9f55d163..88f2d7085a1 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h +++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h @@ -83,6 +83,7 @@ public: QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id); ~QWaylandInputDevice() override; + uint32_t id() const { return mId; } uint32_t capabilities() const { return mCaps; } QString seatname() const { return mSeatName; } @@ -144,6 +145,7 @@ protected: QWaylandDisplay *mQDisplay = nullptr; struct wl_display *mDisplay = nullptr; + uint32_t mId = -1; uint32_t mCaps = 0; QString mSeatName; diff --git a/src/plugins/platforms/wayland/qwaylandtabletv2.cpp b/src/plugins/platforms/wayland/qwaylandtabletv2.cpp index e15185f08ef..72fe1664d36 100644 --- a/src/plugins/platforms/wayland/qwaylandtabletv2.cpp +++ b/src/plugins/platforms/wayland/qwaylandtabletv2.cpp @@ -5,14 +5,21 @@ #include "qwaylandinputdevice_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandsurface_p.h" +#include QT_BEGIN_NAMESPACE namespace QtWaylandClient { +using namespace Qt::StringLiterals; + +Q_LOGGING_CATEGORY(lcQpaInputDevices, "qt.qpa.input.devices") +Q_DECLARE_LOGGING_CATEGORY(lcQpaWaylandInput) + QWaylandTabletManagerV2::QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version) : zwp_tablet_manager_v2(display->wl_registry(), id, qMin(version, uint(1))) { + qCDebug(lcQpaInputDevices, "new tablet manager: ID %d version %d", id, version); } QWaylandTabletManagerV2::~QWaylandTabletManagerV2() @@ -24,6 +31,7 @@ QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWa : QtWayland::zwp_tablet_seat_v2(manager->get_tablet_seat(seat->wl_seat())) , m_seat(seat) { + qCDebug(lcQpaInputDevices) << "new tablet seat" << seat->seatname() << "id" << seat->id(); } QWaylandTabletSeatV2::~QWaylandTabletSeatV2() @@ -36,108 +44,205 @@ QWaylandTabletSeatV2::~QWaylandTabletSeatV2() pad->destroy(); qDeleteAll(m_tablets); qDeleteAll(m_tools); + qDeleteAll(m_deadTools); qDeleteAll(m_pads); destroy(); } void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 *id) { - auto *tablet = new QWaylandTabletV2(id); + auto *tablet = new QWaylandTabletV2(id, m_seat->seatname()); + qCDebug(lcQpaInputDevices) << "seat" << this << id << "has tablet" << tablet; + tablet->setParent(this); m_tablets.push_back(tablet); connect(tablet, &QWaylandTabletV2::destroyed, this, [this, tablet] { m_tablets.removeOne(tablet); }); } void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 *id) { + qDeleteAll(m_deadTools); auto *tool = new QWaylandTabletToolV2(this, id); + if (m_tablets.size() == 1) { + tool->setParent(m_tablets.first()); + QPointingDevicePrivate *d = QPointingDevicePrivate::get(tool); + d->name = m_tablets.first()->name() + u" stylus"; + } else { + qCDebug(lcQpaInputDevices) << "seat" << this << "has tool" << tool << "for one of these tablets:" << m_tablets; + // TODO identify which tablet if there are several; then tool->setParent(tablet) + } m_tools.push_back(tool); - connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { m_tools.removeOne(tool); }); + connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { + m_tools.removeOne(tool); + m_deadTools.removeOne(tool); + }); } void QWaylandTabletSeatV2::zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 *id) { auto *pad = new QWaylandTabletPadV2(id); + if (m_tablets.size() == 1) { + pad->setParent(m_tablets.first()); + QPointingDevicePrivate *d = QPointingDevicePrivate::get(pad); + d->name = m_tablets.first()->name() + u" touchpad"; + } else { + qCDebug(lcQpaInputDevices) << "seat" << this << "has touchpad" << pad << "for one of these tablets:" << m_tablets; + // TODO identify which tablet if there are several + } m_pads.push_back(pad); connect(pad, &QWaylandTabletPadV2::destroyed, this, [this, pad] { m_pads.removeOne(pad); }); } -QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet) - : QtWayland::zwp_tablet_v2(tablet) +QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet, const QString &seatName) + : QPointingDevice(u"unknown"_s, -1, DeviceType::Stylus, PointerType::Pen, + Capability::Position | Capability::Hover, + 1, 1) + , QtWayland::zwp_tablet_v2(tablet) { + qCDebug(lcQpaInputDevices) << "new tablet on seat" << seatName; + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->seatName = seatName; +} + +void QWaylandTabletV2::zwp_tablet_v2_name(const QString &name) +{ + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->name = name; +} + +void QWaylandTabletV2::zwp_tablet_v2_id(uint32_t vid, uint32_t pid) +{ + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->systemId = (quint64(vid) << 32) | pid; + qCDebug(lcQpaInputDevices) << "vid" << vid << "pid" << pid << "stored as systemId in" << this; +} + +void QWaylandTabletV2::zwp_tablet_v2_path(const QString &path) +{ + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->busId = path; +} + +void QWaylandTabletV2::zwp_tablet_v2_done() +{ + QWindowSystemInterface::registerInputDevice(this); +} + +void QWaylandTabletSeatV2::toolRemoved(QWaylandTabletToolV2 *tool) +{ + m_tools.removeOne(tool); + m_deadTools.append(tool); } void QWaylandTabletV2::zwp_tablet_v2_removed() { destroy(); - delete this; + deleteLater(); } QWaylandTabletToolV2::QWaylandTabletToolV2(QWaylandTabletSeatV2 *tabletSeat, ::zwp_tablet_tool_v2 *tool) - : QtWayland::zwp_tablet_tool_v2(tool) + : QPointingDevice(u"tool"_s, -1, DeviceType::Stylus, PointerType::Pen, + Capability::Position | Capability::Hover, + 1, 1) + , QtWayland::zwp_tablet_tool_v2(tool) , m_tabletSeat(tabletSeat) { + // TODO get the number of buttons somehow? } void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type) { - m_toolType = type(tool_type); + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + + switch (tool_type) { + case type_airbrush: + case type_brush: + case type_pencil: + case type_pen: + d->pointerType = QPointingDevice::PointerType::Pen; + break; + case type_eraser: + d->pointerType = QPointingDevice::PointerType::Eraser; + break; + case type_mouse: + case type_lens: + d->pointerType = QPointingDevice::PointerType::Cursor; + break; + case type_finger: + d->pointerType = QPointingDevice::PointerType::Unknown; + break; + } + + switch (tool_type) { + case type::type_airbrush: + d->deviceType = QInputDevice::DeviceType::Airbrush; + d->capabilities.setFlag(QInputDevice::Capability::TangentialPressure); + break; + case type::type_brush: + case type::type_pencil: + case type::type_pen: + case type::type_eraser: + d->deviceType = QInputDevice::DeviceType::Stylus; + break; + case type::type_lens: + d->deviceType = QInputDevice::DeviceType::Puck; + break; + case type::type_mouse: + case type::type_finger: + d->deviceType = QInputDevice::DeviceType::Unknown; + break; + } } void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) { - m_uid = (quint64(hardware_serial_hi) << 32) + hardware_serial_lo; + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->uniqueId = QPointingDeviceUniqueId::fromNumericId((quint64(hardware_serial_hi) << 32) + hardware_serial_lo); +} + +void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_id_wacom(uint32_t hardware_id_hi, uint32_t hardware_id_lo) +{ + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->systemId = (quint64(hardware_id_hi) << 32) + hardware_id_lo; } void QWaylandTabletToolV2::zwp_tablet_tool_v2_capability(uint32_t capability) { - if (capability == capability_rotation) - m_hasRotation = true; + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + switch (capability) { + case capability_tilt: + // no distinction... we have to assume it has both axes + d->capabilities.setFlag(QInputDevice::Capability::XTilt); + d->capabilities.setFlag(QInputDevice::Capability::YTilt); + break; + case capability_pressure: + d->capabilities.setFlag(QInputDevice::Capability::Pressure); + break; + case capability_distance: + d->capabilities.setFlag(QInputDevice::Capability::ZPosition); + break; + case capability_rotation: + d->capabilities.setFlag(QInputDevice::Capability::Rotation); + break; + case capability_slider: + // nothing to represent that so far + break; + case capability_wheel: + d->capabilities.setFlag(QInputDevice::Capability::Scroll); + d->capabilities.setFlag(QInputDevice::Capability::PixelScroll); + break; + } + qCDebug(lcQpaInputDevices) << capability << "->" << this; } void QWaylandTabletToolV2::zwp_tablet_tool_v2_done() { - switch (m_toolType) { - case type::type_airbrush: - case type::type_brush: - case type::type_pencil: - case type::type_pen: - m_pointerType = QPointingDevice::PointerType::Pen; - break; - case type::type_eraser: - m_pointerType = QPointingDevice::PointerType::Eraser; - break; - case type::type_mouse: - case type::type_lens: - m_pointerType = QPointingDevice::PointerType::Cursor; - break; - case type::type_finger: - m_pointerType = QPointingDevice::PointerType::Unknown; - break; - } - switch (m_toolType) { - case type::type_airbrush: - m_tabletDevice = QInputDevice::DeviceType::Airbrush; - break; - case type::type_brush: - case type::type_pencil: - case type::type_pen: - case type::type_eraser: - m_tabletDevice = QInputDevice::DeviceType::Stylus; - break; - case type::type_lens: - m_tabletDevice = QInputDevice::DeviceType::Puck; - break; - case type::type_mouse: - case type::type_finger: - m_tabletDevice = QInputDevice::DeviceType::Unknown; - break; - } + QWindowSystemInterface::registerInputDevice(this); } void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed() { destroy(); - delete this; + m_tabletSeat->toolRemoved(this); } void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_in(uint32_t serial, zwp_tablet_v2 *tablet, wl_surface *surface) @@ -226,27 +331,41 @@ void QWaylandTabletToolV2::zwp_tablet_tool_v2_button(uint32_t serial, uint32_t b { m_tabletSeat->seat()->mSerial = serial; + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); Qt::MouseButton mouseButton = mouseButtonFromTablet(button); if (state == button_state_pressed) m_pending.buttons |= mouseButton; else m_pending.buttons &= ~mouseButton; + // ideally we'd get button count when the tool is discovered; seems to be a shortcoming in tablet-unstable-v2 + // but if we get events from buttons we didn't know existed, increase it + if (mouseButton == Qt::RightButton) + d->buttonCount = qMax(d->buttonCount, 2); + else if (mouseButton == Qt::MiddleButton) + d->buttonCount = qMax(d->buttonCount, 3); } void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time) { - if (m_pending.proximitySurface && !m_applied.proximitySurface) { - QWindowSystemInterface::handleTabletEnterProximityEvent(int(m_tabletDevice), int(m_pointerType), m_uid); + if (!m_pending.proximitySurface) { + qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring"; + if (m_applied.enteredSurface) { + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(nullptr, this, false); + m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface + } + return; + } + + QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(m_pending.proximitySurface->object()); + QWindow *window = waylandWindow->window(); + + if (!m_applied.proximitySurface) { + // TODO get position etc. as below + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, this, true); m_applied.proximitySurface = m_pending.proximitySurface; } - if (!(m_pending == m_applied) && m_pending.proximitySurface) { - if (!m_pending.proximitySurface) { - qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring"; - return; - } - QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(m_pending.proximitySurface->object()); - QWindow *window = waylandWindow->window(); + if (!(m_pending == m_applied)) { ulong timestamp = time; const QPointF localPosition = waylandWindow->mapFromWlSurface(m_pending.surfacePosition); @@ -263,15 +382,10 @@ void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time) qreal rotation = m_pending.rotation; int z = int(m_pending.distance); - QWindowSystemInterface::handleTabletEvent( - window, timestamp, localPosition, globalPosition, int(m_tabletDevice), - int(m_pointerType), buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, - z, m_uid, m_tabletSeat->seat()->modifiers()); - } - - if (!m_pending.proximitySurface && m_applied.enteredSurface) { - QWindowSystemInterface::handleTabletLeaveProximityEvent(int(m_tabletDevice), int(m_pointerType), m_uid); - m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface + QWindowSystemInterface::handleTabletEvent(window, timestamp, this, localPosition, globalPosition, + buttons, pressure, + xTilt, yTilt, tangentialPressure, rotation, z, + m_tabletSeat->seat()->modifiers()); } m_applied = m_pending; @@ -294,10 +408,30 @@ bool QWaylandTabletToolV2::State::operator==(const QWaylandTabletToolV2::State & } QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad) - : QtWayland::zwp_tablet_pad_v2(pad) + : QPointingDevice(u"tablet touchpad"_s, -1, DeviceType::TouchPad, PointerType::Finger, + Capability::Position, + 1, 1) + , QtWayland::zwp_tablet_pad_v2(pad) { } +void QWaylandTabletPadV2::zwp_tablet_pad_v2_path(const QString &path) +{ + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->busId = path; +} + +void QWaylandTabletPadV2::zwp_tablet_pad_v2_buttons(uint32_t buttons) +{ + QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); + d->buttonCount = buttons; +} + +void QWaylandTabletPadV2::zwp_tablet_pad_v2_done() +{ + QWindowSystemInterface::registerInputDevice(this); +} + void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed() { destroy(); diff --git a/src/plugins/platforms/wayland/qwaylandtabletv2_p.h b/src/plugins/platforms/wayland/qwaylandtabletv2_p.h index f6f1daa9886..b101826e4ac 100644 --- a/src/plugins/platforms/wayland/qwaylandtabletv2_p.h +++ b/src/plugins/platforms/wayland/qwaylandtabletv2_p.h @@ -54,6 +54,8 @@ public: QWaylandInputDevice *seat() const { return m_seat; } + void toolRemoved(QWaylandTabletToolV2 *tool); + protected: void zwp_tablet_seat_v2_tablet_added(struct ::zwp_tablet_v2 *id) override; void zwp_tablet_seat_v2_tool_added(struct ::zwp_tablet_tool_v2 *id) override; @@ -63,24 +65,26 @@ private: QWaylandInputDevice *m_seat; QList m_tablets; QList m_tools; + QList m_deadTools; QList m_pads; }; -class Q_WAYLANDCLIENT_EXPORT QWaylandTabletV2 : public QObject, public QtWayland::zwp_tablet_v2 +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletV2 : public QPointingDevice, public QtWayland::zwp_tablet_v2 { Q_OBJECT public: - explicit QWaylandTabletV2(::zwp_tablet_v2 *tablet); + explicit QWaylandTabletV2(::zwp_tablet_v2 *tablet, const QString &seatName); protected: -// void zwp_tablet_v2_name(const QString &name) override; -// void zwp_tablet_v2_id(uint32_t vid, uint32_t pid) override; -// void zwp_tablet_v2_path(const QString &path) override; -// void zwp_tablet_v2_done() override; + // callbacks which act as setters + void zwp_tablet_v2_name(const QString &name) override; + void zwp_tablet_v2_id(uint32_t vid, uint32_t pid) override; + void zwp_tablet_v2_path(const QString &path) override; + void zwp_tablet_v2_done() override; void zwp_tablet_v2_removed() override; }; -class Q_WAYLANDCLIENT_EXPORT QWaylandTabletToolV2 : public QObject, public QtWayland::zwp_tablet_tool_v2 +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletToolV2 : public QPointingDevice, public QtWayland::zwp_tablet_tool_v2 { Q_OBJECT public: @@ -89,7 +93,7 @@ public: protected: void zwp_tablet_tool_v2_type(uint32_t tool_type) override; void zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) override; -// void zwp_tablet_tool_v2_hardware_id_wacom(uint32_t hardware_id_hi, uint32_t hardware_id_lo) override; + void zwp_tablet_tool_v2_hardware_id_wacom(uint32_t hardware_id_hi, uint32_t hardware_id_lo) override; void zwp_tablet_tool_v2_capability(uint32_t capability) override; void zwp_tablet_tool_v2_done() override; void zwp_tablet_tool_v2_removed() override; @@ -110,13 +114,6 @@ protected: private: QWaylandTabletSeatV2 *m_tabletSeat; - // Static state (sent before done event) - QPointingDevice::PointerType m_pointerType = QPointingDevice::PointerType::Unknown; - QInputDevice::DeviceType m_tabletDevice = QInputDevice::DeviceType::Unknown; - type m_toolType = type_pen; - bool m_hasRotation = false; - quint64 m_uid = 0; - // Accumulated state (applied on frame event) struct State { bool down = false; @@ -135,8 +132,7 @@ private: } m_pending, m_applied; }; -// We don't actually use this, but need to handle the "removed" event to comply with the protocol -class Q_WAYLANDCLIENT_EXPORT QWaylandTabletPadV2 : public QObject, public QtWayland::zwp_tablet_pad_v2 +class Q_WAYLANDCLIENT_EXPORT QWaylandTabletPadV2 : public QPointingDevice, public QtWayland::zwp_tablet_pad_v2 { Q_OBJECT public: @@ -144,9 +140,9 @@ public: protected: // void zwp_tablet_pad_v2_group(struct ::zwp_tablet_pad_group_v2 *pad_group) override; -// void zwp_tablet_pad_v2_path(const QString &path) override; -// void zwp_tablet_pad_v2_buttons(uint32_t buttons) override; -// void zwp_tablet_pad_v2_done() override; + void zwp_tablet_pad_v2_path(const QString &path) override; + void zwp_tablet_pad_v2_buttons(uint32_t buttons) override; + void zwp_tablet_pad_v2_done() override; // void zwp_tablet_pad_v2_button(uint32_t time, uint32_t button, uint32_t state) override; // void zwp_tablet_pad_v2_enter(uint32_t serial, struct ::zwp_tablet_v2 *tablet, struct ::wl_surface *surface) override; // void zwp_tablet_pad_v2_leave(uint32_t serial, struct ::wl_surface *surface) override;