File indexing completed on 2024-05-19 16:35:29

0001 /*
0002     SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "tablet_v2_interface.h"
0008 #include "display.h"
0009 #include "seat_interface.h"
0010 #include "surface_interface.h"
0011 
0012 #include "qwayland-server-tablet-unstable-v2.h"
0013 #include <QHash>
0014 
0015 namespace KWaylandServer
0016 {
0017 static int s_version = 1;
0018 
0019 class TabletV2InterfacePrivate : public QtWaylandServer::zwp_tablet_v2
0020 {
0021 public:
0022     TabletV2InterfacePrivate(TabletV2Interface *q, uint32_t vendorId, uint32_t productId, const QString &name, const QStringList &paths)
0023         : zwp_tablet_v2()
0024         , q(q)
0025         , m_vendorId(vendorId)
0026         , m_productId(productId)
0027         , m_name(name)
0028         , m_paths(paths)
0029     {
0030     }
0031 
0032     wl_resource *resourceForSurface(SurfaceInterface *surface) const
0033     {
0034         ClientConnection *client = surface->client();
0035         Resource *r = resourceMap().value(*client);
0036         return r ? r->handle : nullptr;
0037     }
0038 
0039     TabletV2Interface *const q;
0040     TabletPadV2Interface *m_pad = nullptr;
0041     const uint32_t m_vendorId;
0042     const uint32_t m_productId;
0043     const QString m_name;
0044     const QStringList m_paths;
0045 };
0046 
0047 TabletV2Interface::TabletV2Interface(uint32_t vendorId, uint32_t productId, const QString &name, const QStringList &paths, QObject *parent)
0048     : QObject(parent)
0049     , d(new TabletV2InterfacePrivate(this, vendorId, productId, name, paths))
0050 {
0051 }
0052 
0053 TabletV2Interface::~TabletV2Interface()
0054 {
0055     const auto tabletResources = d->resourceMap();
0056     for (TabletV2InterfacePrivate::Resource *resource : tabletResources) {
0057         d->send_removed(resource->handle);
0058     }
0059 }
0060 
0061 bool TabletV2Interface::isSurfaceSupported(SurfaceInterface *surface) const
0062 {
0063     return d->resourceForSurface(surface);
0064 }
0065 
0066 TabletPadV2Interface *TabletV2Interface::pad() const
0067 {
0068     return d->m_pad;
0069 }
0070 
0071 class TabletCursorV2Private
0072 {
0073 public:
0074     TabletCursorV2Private(TabletCursorV2 *q)
0075         : q(q)
0076     {
0077     }
0078 
0079     void update(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot)
0080     {
0081         const bool diff = m_serial != serial || m_surface != surface || m_hotspot != hotspot;
0082         if (diff) {
0083             m_serial = serial;
0084             m_surface = surface;
0085             m_hotspot = hotspot;
0086 
0087             Q_EMIT q->changed();
0088         }
0089     }
0090 
0091     TabletCursorV2 *const q;
0092 
0093     quint32 m_serial = 0;
0094     QPointer<SurfaceInterface> m_surface;
0095     QPoint m_hotspot;
0096 };
0097 
0098 TabletCursorV2::TabletCursorV2()
0099     : QObject()
0100     , d(new TabletCursorV2Private(this))
0101 {
0102 }
0103 
0104 TabletCursorV2::~TabletCursorV2() = default;
0105 
0106 QPoint TabletCursorV2::hotspot() const
0107 {
0108     return d->m_hotspot;
0109 }
0110 
0111 quint32 TabletCursorV2::enteredSerial() const
0112 {
0113     return d->m_serial;
0114 }
0115 
0116 SurfaceInterface *TabletCursorV2::surface() const
0117 {
0118     return d->m_surface;
0119 }
0120 
0121 class TabletToolV2InterfacePrivate : public QtWaylandServer::zwp_tablet_tool_v2
0122 {
0123 public:
0124     TabletToolV2InterfacePrivate(TabletToolV2Interface *q,
0125                                  Display *display,
0126                                  TabletToolV2Interface::Type type,
0127                                  uint32_t hsh,
0128                                  uint32_t hsl,
0129                                  uint32_t hih,
0130                                  uint32_t hil,
0131                                  const QVector<TabletToolV2Interface::Capability> &capabilities)
0132         : zwp_tablet_tool_v2()
0133         , m_display(display)
0134         , m_type(type)
0135         , m_hardwareSerialHigh(hsh)
0136         , m_hardwareSerialLow(hsl)
0137         , m_hardwareIdHigh(hih)
0138         , m_hardwareIdLow(hil)
0139         , m_capabilities(capabilities)
0140         , q(q)
0141     {
0142     }
0143 
0144     wl_resource *targetResource()
0145     {
0146         if (!m_surface)
0147             return nullptr;
0148 
0149         ClientConnection *client = m_surface->client();
0150         const Resource *r = resourceMap().value(*client);
0151         return r ? r->handle : nullptr;
0152     }
0153 
0154     quint64 hardwareId() const
0155     {
0156         return quint64(quint64(m_hardwareIdHigh) << 32) + m_hardwareIdLow;
0157     }
0158     quint64 hardwareSerial() const
0159     {
0160         return quint64(quint64(m_hardwareSerialHigh) << 32) + m_hardwareSerialLow;
0161     }
0162 
0163     void zwp_tablet_tool_v2_bind_resource(QtWaylandServer::zwp_tablet_tool_v2::Resource *resource) override
0164     {
0165         TabletCursorV2 *&c = m_cursors[resource->handle];
0166         if (!c)
0167             c = new TabletCursorV2;
0168     }
0169 
0170     void zwp_tablet_tool_v2_set_cursor(Resource *resource, uint32_t serial, struct ::wl_resource *_surface, int32_t hotspot_x, int32_t hotspot_y) override
0171     {
0172         TabletCursorV2 *c = m_cursors[resource->handle];
0173         c->d->update(serial, SurfaceInterface::get(_surface), {hotspot_x, hotspot_y});
0174         if (resource->handle == targetResource())
0175             q->cursorChanged(c);
0176     }
0177 
0178     void zwp_tablet_tool_v2_destroy_resource(Resource *resource) override
0179     {
0180         delete m_cursors.take(resource->handle);
0181         if (m_removed && resourceMap().isEmpty()) {
0182             delete q;
0183         }
0184     }
0185 
0186     void zwp_tablet_tool_v2_destroy(Resource *resource) override
0187     {
0188         wl_resource_destroy(resource->handle);
0189     }
0190 
0191     Display *const m_display;
0192     bool m_cleanup = false;
0193     bool m_removed = false;
0194     QPointer<SurfaceInterface> m_surface;
0195     QPointer<TabletV2Interface> m_lastTablet;
0196     const uint32_t m_type;
0197     const uint32_t m_hardwareSerialHigh, m_hardwareSerialLow;
0198     const uint32_t m_hardwareIdHigh, m_hardwareIdLow;
0199     const QVector<TabletToolV2Interface::Capability> m_capabilities;
0200     QHash<wl_resource *, TabletCursorV2 *> m_cursors;
0201     TabletToolV2Interface *const q;
0202 };
0203 
0204 TabletToolV2Interface::TabletToolV2Interface(Display *display,
0205                                              Type type,
0206                                              uint32_t hsh,
0207                                              uint32_t hsl,
0208                                              uint32_t hih,
0209                                              uint32_t hil,
0210                                              const QVector<Capability> &capabilities)
0211     : d(new TabletToolV2InterfacePrivate(this, display, type, hsh, hsl, hih, hil, capabilities))
0212 {
0213 }
0214 
0215 TabletToolV2Interface::~TabletToolV2Interface()
0216 {
0217     const auto toolResources = d->resourceMap();
0218     for (TabletToolV2InterfacePrivate::Resource *resource : toolResources) {
0219         d->send_removed(resource->handle);
0220     }
0221 }
0222 
0223 bool TabletToolV2Interface::hasCapability(Capability capability) const
0224 {
0225     return d->m_capabilities.contains(capability);
0226 }
0227 
0228 void TabletToolV2Interface::setCurrentSurface(SurfaceInterface *surface)
0229 {
0230     if (d->m_surface == surface)
0231         return;
0232 
0233     TabletV2Interface *const lastTablet = d->m_lastTablet;
0234     if (d->m_surface && d->resourceMap().contains(*d->m_surface->client())) {
0235         sendProximityOut();
0236         sendFrame(0);
0237     }
0238 
0239     d->m_surface = surface;
0240 
0241     if (lastTablet && lastTablet->d->resourceForSurface(surface)) {
0242         sendProximityIn(lastTablet);
0243     } else {
0244         d->m_lastTablet = lastTablet;
0245     }
0246 
0247     Q_EMIT cursorChanged(d->m_cursors.value(d->targetResource()));
0248 }
0249 
0250 bool TabletToolV2Interface::isClientSupported() const
0251 {
0252     return d->m_surface && d->targetResource();
0253 }
0254 
0255 void TabletToolV2Interface::sendButton(uint32_t button, bool pressed)
0256 {
0257     d->send_button(d->targetResource(),
0258                    d->m_display->nextSerial(),
0259                    button,
0260                    pressed ? QtWaylandServer::zwp_tablet_tool_v2::button_state_pressed : QtWaylandServer::zwp_tablet_tool_v2::button_state_released);
0261 }
0262 
0263 void TabletToolV2Interface::sendMotion(const QPointF &pos)
0264 {
0265     const QPointF surfacePos = d->m_surface->toSurfaceLocal(pos);
0266     d->send_motion(d->targetResource(), wl_fixed_from_double(surfacePos.x()), wl_fixed_from_double(surfacePos.y()));
0267 }
0268 
0269 void TabletToolV2Interface::sendDistance(uint32_t distance)
0270 {
0271     d->send_distance(d->targetResource(), distance);
0272 }
0273 
0274 void TabletToolV2Interface::sendFrame(uint32_t time)
0275 {
0276     d->send_frame(d->targetResource(), time);
0277 
0278     if (d->m_cleanup) {
0279         d->m_surface = nullptr;
0280         d->m_lastTablet = nullptr;
0281         d->m_cleanup = false;
0282     }
0283 }
0284 
0285 void TabletToolV2Interface::sendPressure(uint32_t pressure)
0286 {
0287     d->send_pressure(d->targetResource(), pressure);
0288 }
0289 
0290 void TabletToolV2Interface::sendRotation(qreal rotation)
0291 {
0292     d->send_rotation(d->targetResource(), wl_fixed_from_double(rotation));
0293 }
0294 
0295 void TabletToolV2Interface::sendSlider(int32_t position)
0296 {
0297     d->send_slider(d->targetResource(), position);
0298 }
0299 
0300 void TabletToolV2Interface::sendTilt(qreal degreesX, qreal degreesY)
0301 {
0302     d->send_tilt(d->targetResource(), wl_fixed_from_double(degreesX), wl_fixed_from_double(degreesY));
0303 }
0304 
0305 void TabletToolV2Interface::sendWheel(int32_t degrees, int32_t clicks)
0306 {
0307     d->send_wheel(d->targetResource(), degrees, clicks);
0308 }
0309 
0310 void TabletToolV2Interface::sendProximityIn(TabletV2Interface *tablet)
0311 {
0312     wl_resource *tabletResource = tablet->d->resourceForSurface(d->m_surface);
0313     d->send_proximity_in(d->targetResource(), d->m_display->nextSerial(), tabletResource, d->m_surface->resource());
0314     d->m_lastTablet = tablet;
0315 }
0316 
0317 void TabletToolV2Interface::sendProximityOut()
0318 {
0319     d->send_proximity_out(d->targetResource());
0320     d->m_cleanup = true;
0321 }
0322 
0323 void TabletToolV2Interface::sendDown()
0324 {
0325     d->send_down(d->targetResource(), d->m_display->nextSerial());
0326 }
0327 
0328 void TabletToolV2Interface::sendUp()
0329 {
0330     d->send_up(d->targetResource());
0331 }
0332 
0333 class TabletPadRingV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_ring_v2
0334 {
0335 public:
0336     TabletPadRingV2InterfacePrivate(TabletPadRingV2Interface *q)
0337         : zwp_tablet_pad_ring_v2()
0338         , q(q)
0339     {
0340     }
0341 
0342     wl_resource *resourceForSurface(SurfaceInterface *surface) const
0343     {
0344         ClientConnection *client = surface->client();
0345         Resource *r = resourceMap().value(*client);
0346         return r ? r->handle : nullptr;
0347     }
0348 
0349     void zwp_tablet_pad_ring_v2_destroy(Resource *resource) override
0350     {
0351         wl_resource_destroy(resource->handle);
0352     }
0353     TabletPadRingV2Interface *const q;
0354     TabletPadV2Interface *m_pad;
0355 };
0356 
0357 TabletPadRingV2Interface::TabletPadRingV2Interface(TabletPadV2Interface *parent)
0358     : QObject(parent)
0359     , d(new TabletPadRingV2InterfacePrivate(this))
0360 {
0361     d->m_pad = parent;
0362 }
0363 
0364 TabletPadRingV2Interface::~TabletPadRingV2Interface() = default;
0365 
0366 void TabletPadRingV2Interface::sendAngle(qreal angle)
0367 {
0368     d->send_angle(d->resourceForSurface(d->m_pad->currentSurface()), wl_fixed_from_double(angle));
0369 }
0370 
0371 void TabletPadRingV2Interface::sendFrame(quint32 time)
0372 {
0373     d->send_frame(d->resourceForSurface(d->m_pad->currentSurface()), time);
0374 }
0375 
0376 void TabletPadRingV2Interface::sendSource(Source source)
0377 {
0378     d->send_source(d->resourceForSurface(d->m_pad->currentSurface()), source);
0379 }
0380 
0381 void TabletPadRingV2Interface::sendStop()
0382 {
0383     d->send_stop(d->resourceForSurface(d->m_pad->currentSurface()));
0384 }
0385 
0386 class TabletPadStripV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_strip_v2
0387 {
0388 public:
0389     TabletPadStripV2InterfacePrivate(TabletPadStripV2Interface *q)
0390         : zwp_tablet_pad_strip_v2()
0391         , q(q)
0392     {
0393     }
0394 
0395     wl_resource *resourceForSurface(SurfaceInterface *surface) const
0396     {
0397         ClientConnection *client = surface->client();
0398         Resource *r = resourceMap().value(*client);
0399         return r ? r->handle : nullptr;
0400     }
0401 
0402     void zwp_tablet_pad_strip_v2_destroy(Resource *resource) override
0403     {
0404         wl_resource_destroy(resource->handle);
0405     }
0406     TabletPadV2Interface *m_pad = nullptr;
0407     TabletPadStripV2Interface *const q;
0408 };
0409 
0410 TabletPadStripV2Interface::TabletPadStripV2Interface(TabletPadV2Interface *parent)
0411     : QObject(parent)
0412     , d(new TabletPadStripV2InterfacePrivate(this))
0413 {
0414     d->m_pad = parent;
0415 }
0416 
0417 TabletPadStripV2Interface::~TabletPadStripV2Interface() = default;
0418 
0419 void TabletPadStripV2Interface::sendFrame(quint32 time)
0420 {
0421     d->send_frame(d->resourceForSurface(d->m_pad->currentSurface()), time);
0422 }
0423 
0424 void TabletPadStripV2Interface::sendPosition(quint32 position)
0425 {
0426     d->send_position(d->resourceForSurface(d->m_pad->currentSurface()), position);
0427 }
0428 
0429 void TabletPadStripV2Interface::sendSource(Source source)
0430 {
0431     d->send_source(d->resourceForSurface(d->m_pad->currentSurface()), source);
0432 }
0433 
0434 void TabletPadStripV2Interface::sendStop()
0435 {
0436     d->send_stop(d->resourceForSurface(d->m_pad->currentSurface()));
0437 }
0438 
0439 class TabletPadGroupV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_group_v2
0440 {
0441 public:
0442     TabletPadGroupV2InterfacePrivate(quint32 currentMode, TabletPadGroupV2Interface *q)
0443         : zwp_tablet_pad_group_v2()
0444         , q(q)
0445         , m_currentMode(currentMode)
0446     {
0447     }
0448 
0449     wl_resource *resourceForSurface(SurfaceInterface *surface) const
0450     {
0451         ClientConnection *client = surface->client();
0452         Resource *r = resourceMap().value(*client);
0453         return r ? r->handle : nullptr;
0454     }
0455 
0456     void zwp_tablet_pad_group_v2_destroy(Resource *resource) override
0457     {
0458         wl_resource_destroy(resource->handle);
0459     }
0460 
0461     TabletPadGroupV2Interface *const q;
0462     TabletPadV2Interface *m_pad = nullptr;
0463     quint32 m_currentMode;
0464 };
0465 
0466 TabletPadGroupV2Interface::TabletPadGroupV2Interface(quint32 currentMode, TabletPadV2Interface *parent)
0467     : QObject(parent)
0468     , d(new TabletPadGroupV2InterfacePrivate(currentMode, this))
0469 {
0470     d->m_pad = parent;
0471 }
0472 
0473 TabletPadGroupV2Interface::~TabletPadGroupV2Interface() = default;
0474 
0475 void TabletPadGroupV2Interface::sendModeSwitch(quint32 time, quint32 serial, quint32 mode)
0476 {
0477     d->m_currentMode = mode;
0478     d->send_mode_switch(d->resourceForSurface(d->m_pad->currentSurface()), time, serial, mode);
0479 }
0480 
0481 class TabletPadV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_v2
0482 {
0483 public:
0484     TabletPadV2InterfacePrivate(const QString &path,
0485                                 quint32 buttons,
0486                                 quint32 rings,
0487                                 quint32 strips,
0488                                 quint32 modes,
0489                                 quint32 currentMode,
0490                                 Display *display,
0491                                 TabletPadV2Interface *q)
0492         : zwp_tablet_pad_v2()
0493         , q(q)
0494         , m_path(path)
0495         , m_buttons(buttons)
0496         , m_modes(modes)
0497         , m_padGroup(new TabletPadGroupV2Interface(currentMode, q))
0498         , m_display(display)
0499     {
0500         for (uint i = 0; i < buttons; ++i) {
0501             m_buttons[i] = i;
0502         }
0503 
0504         m_rings.reserve(rings);
0505         for (quint32 i = 0; i < rings; ++i) {
0506             m_rings += new TabletPadRingV2Interface(q);
0507         }
0508 
0509         m_strips.reserve(strips);
0510         for (quint32 i = 0; i < strips; ++i) {
0511             m_strips += new TabletPadStripV2Interface(q);
0512         }
0513     }
0514 
0515     ~TabletPadV2InterfacePrivate() override
0516     {
0517         qDeleteAll(m_rings);
0518         qDeleteAll(m_strips);
0519     }
0520 
0521     void zwp_tablet_pad_v2_destroy(Resource *resource) override
0522     {
0523         wl_resource_destroy(resource->handle);
0524     }
0525 
0526     void zwp_tablet_pad_v2_set_feedback(Resource *resource, quint32 button, const QString &description, quint32 serial) override
0527     {
0528         Q_EMIT q->feedback(m_display->getConnection(resource->client()), button, description, serial);
0529     }
0530 
0531     wl_resource *resourceForSurface(SurfaceInterface *surface) const
0532     {
0533         ClientConnection *client = surface->client();
0534         Resource *r = resourceMap().value(*client);
0535         return r ? r->handle : nullptr;
0536     }
0537 
0538     TabletPadV2Interface *const q;
0539 
0540     const QString m_path;
0541     QVector<quint32> m_buttons;
0542     const int m_modes;
0543 
0544     QVector<TabletPadRingV2Interface *> m_rings;
0545     QVector<TabletPadStripV2Interface *> m_strips;
0546     TabletPadGroupV2Interface *const m_padGroup;
0547     TabletSeatV2Interface *m_seat = nullptr;
0548     QPointer<SurfaceInterface> m_currentSurface;
0549     Display *const m_display;
0550 };
0551 
0552 TabletPadV2Interface::TabletPadV2Interface(const QString &path,
0553                                            quint32 buttons,
0554                                            quint32 rings,
0555                                            quint32 strips,
0556                                            quint32 modes,
0557                                            quint32 currentMode,
0558                                            Display *display,
0559                                            TabletSeatV2Interface *parent)
0560     : QObject(parent)
0561     , d(new TabletPadV2InterfacePrivate(path, buttons, rings, strips, modes, currentMode, display, this))
0562 {
0563     d->m_seat = parent;
0564 }
0565 
0566 TabletPadV2Interface::~TabletPadV2Interface()
0567 {
0568     const auto tabletPadResources = d->resourceMap();
0569     for (TabletPadV2InterfacePrivate::Resource *resource : tabletPadResources) {
0570         d->send_removed(resource->handle);
0571     }
0572 }
0573 
0574 void TabletPadV2Interface::sendButton(std::chrono::microseconds time, quint32 button, bool pressed)
0575 {
0576     d->send_button(d->resourceForSurface(currentSurface()), std::chrono::duration_cast<std::chrono::milliseconds>(time).count(), button, pressed);
0577 }
0578 
0579 TabletPadRingV2Interface *TabletPadV2Interface::ring(uint at) const
0580 {
0581     return d->m_rings[at];
0582 }
0583 
0584 TabletPadStripV2Interface *TabletPadV2Interface::strip(uint at) const
0585 {
0586     return d->m_strips[at];
0587 }
0588 
0589 void TabletPadV2Interface::setCurrentSurface(SurfaceInterface *surface, TabletV2Interface *tablet)
0590 {
0591     if (surface == d->m_currentSurface) {
0592         return;
0593     }
0594 
0595     if (d->m_currentSurface) {
0596         d->send_leave(d->resourceForSurface(d->m_currentSurface), d->m_display->nextSerial(), d->m_currentSurface->resource());
0597     }
0598 
0599     d->m_currentSurface = surface;
0600     if (surface) {
0601         wl_resource *tabletResource = tablet->d->resourceForSurface(surface);
0602 
0603         d->send_enter(d->resourceForSurface(surface), d->m_display->nextSerial(), tabletResource, surface->resource());
0604         d->m_padGroup->sendModeSwitch(0, d->m_display->nextSerial(), d->m_padGroup->d->m_currentMode);
0605     }
0606 }
0607 
0608 SurfaceInterface *TabletPadV2Interface::currentSurface() const
0609 {
0610     return d->m_currentSurface;
0611 }
0612 
0613 class TabletSeatV2InterfacePrivate : public QtWaylandServer::zwp_tablet_seat_v2
0614 {
0615 public:
0616     TabletSeatV2InterfacePrivate(Display *display, TabletSeatV2Interface *q)
0617         : zwp_tablet_seat_v2()
0618         , q(q)
0619         , m_display(display)
0620     {
0621     }
0622 
0623     void zwp_tablet_seat_v2_bind_resource(Resource *resource) override
0624     {
0625         for (auto tablet : std::as_const(m_tablets)) {
0626             sendTabletAdded(resource, tablet);
0627         }
0628 
0629         for (auto pad : std::as_const(m_pads)) {
0630             sendPadAdded(resource, pad);
0631         }
0632 
0633         for (const auto &tools : std::as_const(m_tools)) {
0634             for (auto *tool : tools) {
0635                 sendToolAdded(resource, tool);
0636             }
0637         }
0638     }
0639 
0640     void zwp_tablet_seat_v2_destroy(Resource *resource) override
0641     {
0642         wl_resource_destroy(resource->handle);
0643     }
0644 
0645     void sendToolAdded(Resource *resource, TabletToolV2Interface *tool)
0646     {
0647         wl_resource *toolResource = tool->d->add(resource->client(), resource->version())->handle;
0648         send_tool_added(resource->handle, toolResource);
0649 
0650         tool->d->send_type(toolResource, tool->d->m_type);
0651         tool->d->send_hardware_serial(toolResource, tool->d->m_hardwareSerialHigh, tool->d->m_hardwareSerialLow);
0652         tool->d->send_hardware_id_wacom(toolResource, tool->d->m_hardwareIdHigh, tool->d->m_hardwareIdLow);
0653         for (uint32_t cap : std::as_const(tool->d->m_capabilities)) {
0654             tool->d->send_capability(toolResource, cap);
0655         }
0656         tool->d->send_done(toolResource);
0657     }
0658     void sendTabletAdded(Resource *resource, TabletV2Interface *tablet)
0659     {
0660         wl_resource *tabletResource = tablet->d->add(resource->client(), resource->version())->handle;
0661         send_tablet_added(resource->handle, tabletResource);
0662 
0663         tablet->d->send_name(tabletResource, tablet->d->m_name);
0664         if (tablet->d->m_vendorId && tablet->d->m_productId) {
0665             tablet->d->send_id(tabletResource, tablet->d->m_vendorId, tablet->d->m_productId);
0666         }
0667         for (const QString &path : std::as_const(tablet->d->m_paths)) {
0668             tablet->d->send_path(tabletResource, path);
0669         }
0670         tablet->d->send_done(tabletResource);
0671     }
0672 
0673     void sendPadAdded(Resource *resource, TabletPadV2Interface *pad)
0674     {
0675         wl_resource *tabletResource = pad->d->add(resource->client(), resource->version())->handle;
0676         send_pad_added(resource->handle, tabletResource);
0677 
0678         pad->d->send_buttons(tabletResource, pad->d->m_buttons.size());
0679         pad->d->send_path(tabletResource, pad->d->m_path);
0680 
0681         auto groupResource = pad->d->m_padGroup->d->add(resource->client(), resource->version());
0682         pad->d->send_group(tabletResource, groupResource->handle);
0683         pad->d->m_padGroup->d->send_modes(groupResource->handle, pad->d->m_modes);
0684 
0685         pad->d->m_padGroup->d->send_buttons(
0686             groupResource->handle,
0687             QByteArray::fromRawData(reinterpret_cast<const char *>(pad->d->m_buttons.data()), pad->d->m_buttons.size() * sizeof(quint32)));
0688 
0689         for (auto ring : std::as_const(pad->d->m_rings)) {
0690             auto ringResource = ring->d->add(resource->client(), resource->version());
0691             pad->d->m_padGroup->d->send_ring(groupResource->handle, ringResource->handle);
0692         }
0693 
0694         for (auto strip : std::as_const(pad->d->m_strips)) {
0695             auto stripResource = strip->d->add(resource->client(), resource->version());
0696             pad->d->m_padGroup->d->send_strip(groupResource->handle, stripResource->handle);
0697         }
0698         pad->d->m_padGroup->d->send_done(groupResource->handle);
0699         pad->d->send_done(tabletResource);
0700     }
0701 
0702     TabletSeatV2Interface *const q;
0703     QHash<QString, QVector<TabletToolV2Interface *>> m_tools;
0704     QHash<QString, TabletV2Interface *> m_tablets;
0705     QHash<QString, TabletPadV2Interface *> m_pads;
0706     Display *const m_display;
0707 };
0708 
0709 TabletSeatV2Interface::TabletSeatV2Interface(Display *display, QObject *parent)
0710     : QObject(parent)
0711     , d(new TabletSeatV2InterfacePrivate(display, this))
0712 {
0713 }
0714 
0715 TabletSeatV2Interface::~TabletSeatV2Interface() = default;
0716 
0717 TabletToolV2Interface *TabletSeatV2Interface::addTool(TabletToolV2Interface::Type type,
0718                                                       quint64 hardwareSerial,
0719                                                       quint64 hardwareId,
0720                                                       const QVector<TabletToolV2Interface::Capability> &capabilities,
0721                                                       const QString &deviceSysName)
0722 {
0723     constexpr auto MAX_UINT_32 = std::numeric_limits<quint32>::max();
0724     auto tool = new TabletToolV2Interface(d->m_display,
0725                                           type,
0726                                           hardwareSerial >> 32,
0727                                           hardwareSerial & MAX_UINT_32,
0728                                           hardwareId >> 32,
0729                                           hardwareId & MAX_UINT_32,
0730                                           capabilities);
0731     for (QtWaylandServer::zwp_tablet_seat_v2::Resource *resource : d->resourceMap()) {
0732         d->sendToolAdded(resource, tool);
0733     }
0734 
0735     d->m_tools[deviceSysName].append(tool);
0736     return tool;
0737 }
0738 
0739 TabletV2Interface *
0740 TabletSeatV2Interface::addTablet(uint32_t vendorId, uint32_t productId, const QString &sysname, const QString &name, const QStringList &paths)
0741 {
0742     Q_ASSERT(!d->m_tablets.contains(sysname));
0743 
0744     auto iface = new TabletV2Interface(vendorId, productId, name, paths, this);
0745 
0746     for (QtWaylandServer::zwp_tablet_seat_v2::Resource *r : d->resourceMap()) {
0747         d->sendTabletAdded(r, iface);
0748     }
0749 
0750     d->m_tablets[sysname] = iface;
0751     return iface;
0752 }
0753 
0754 TabletPadV2Interface *TabletSeatV2Interface::addTabletPad(const QString &sysname,
0755                                                           const QString &name,
0756                                                           const QStringList &paths,
0757                                                           quint32 buttons,
0758                                                           quint32 rings,
0759                                                           quint32 strips,
0760                                                           quint32 modes,
0761                                                           quint32 currentMode,
0762                                                           TabletV2Interface *tablet)
0763 {
0764     auto iface = new TabletPadV2Interface(paths.at(0), buttons, rings, strips, modes, currentMode, d->m_display, this);
0765     iface->d->m_seat = this;
0766     for (auto r : d->resourceMap()) {
0767         d->sendPadAdded(r, iface);
0768     }
0769 
0770     tablet->d->m_pad = iface;
0771 
0772     d->m_pads[sysname] = iface;
0773     return iface;
0774 }
0775 
0776 void TabletSeatV2Interface::removeDevice(const QString &sysname)
0777 {
0778     delete d->m_tablets.take(sysname);
0779     delete d->m_pads.take(sysname);
0780 
0781     qDeleteAll(d->m_tools.take(sysname));
0782 }
0783 
0784 TabletToolV2Interface *TabletSeatV2Interface::toolByHardwareId(quint64 hardwareId) const
0785 {
0786     for (const auto &tools : std::as_const(d->m_tools)) {
0787         for (TabletToolV2Interface *tool : tools) {
0788             if (tool->d->hardwareId() == hardwareId) {
0789                 return tool;
0790             }
0791         }
0792     }
0793     return nullptr;
0794 }
0795 
0796 TabletToolV2Interface *TabletSeatV2Interface::toolByHardwareSerial(quint64 hardwareSerial, TabletToolV2Interface::Type type) const
0797 {
0798     for (const auto &tools : std::as_const(d->m_tools)) {
0799         for (TabletToolV2Interface *tool : tools) {
0800             if (tool->d->hardwareSerial() == hardwareSerial && tool->d->m_type == type) {
0801                 return tool;
0802             }
0803         }
0804     }
0805     return nullptr;
0806 }
0807 
0808 TabletPadV2Interface *TabletSeatV2Interface::padByName(const QString &name) const
0809 {
0810     Q_ASSERT(d->m_pads.contains(name));
0811     return d->m_pads.value(name);
0812 }
0813 
0814 class TabletManagerV2InterfacePrivate : public QtWaylandServer::zwp_tablet_manager_v2
0815 {
0816 public:
0817     TabletManagerV2InterfacePrivate(Display *display, TabletManagerV2Interface *q)
0818         : zwp_tablet_manager_v2(*display, s_version)
0819         , q(q)
0820         , m_display(display)
0821     {
0822     }
0823 
0824     void zwp_tablet_manager_v2_get_tablet_seat(Resource *resource, uint32_t tablet_seat, struct ::wl_resource *seat_resource) override
0825     {
0826         SeatInterface *seat = SeatInterface::get(seat_resource);
0827         TabletSeatV2Interface *tsi = get(seat);
0828         tsi->d->add(resource->client(), tablet_seat, s_version);
0829     }
0830 
0831     TabletSeatV2Interface *get(SeatInterface *seat)
0832     {
0833         TabletSeatV2Interface *&tabletSeat = m_seats[seat];
0834         if (!tabletSeat) {
0835             tabletSeat = new TabletSeatV2Interface(m_display, q);
0836         }
0837         return tabletSeat;
0838     }
0839 
0840     TabletManagerV2Interface *const q;
0841     Display *const m_display;
0842     QHash<SeatInterface *, TabletSeatV2Interface *> m_seats;
0843 };
0844 
0845 TabletManagerV2Interface::TabletManagerV2Interface(Display *display, QObject *parent)
0846     : QObject(parent)
0847     , d(new TabletManagerV2InterfacePrivate(display, this))
0848 {
0849 }
0850 
0851 TabletSeatV2Interface *TabletManagerV2Interface::seat(SeatInterface *seat) const
0852 {
0853     return d->get(seat);
0854 }
0855 
0856 bool TabletSeatV2Interface::isClientSupported(ClientConnection *client) const
0857 {
0858     return d->resourceMap().value(*client);
0859 }
0860 
0861 TabletManagerV2Interface::~TabletManagerV2Interface() = default;
0862 
0863 } // namespace KWaylandServer