tablet_v2_interface.cpp 15.1 KB
Newer Older
1 2
/*
    SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
3

4 5
    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
6

7
#include "tablet_v2_interface.h"
8 9 10 11 12 13 14
#include "display.h"
#include "seat_interface.h"
#include "surface_interface.h"

#include "qwayland-server-tablet-unstable-v2.h"
#include <QHash>

15 16
namespace KWaylandServer
{
17 18 19

static int s_version = 1;

20
class TabletV2InterfacePrivate : public QtWaylandServer::zwp_tablet_v2
21 22
{
public:
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
23
    TabletV2InterfacePrivate(TabletV2Interface *q, uint32_t vendorId, uint32_t productId, const QString &name, const QStringList &paths)
24 25 26 27 28 29 30 31 32 33 34 35
        : zwp_tablet_v2()
        , q(q)
        , m_vendorId(vendorId)
        , m_productId(productId)
        , m_name(name)
        , m_paths(paths)
    {
    }

    wl_resource *resourceForSurface(SurfaceInterface *surface) const
    {
        ClientConnection *client = surface->client();
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
36
        Resource *r = resourceMap().value(*client);
37 38 39 40 41 42
        return r ? r->handle : nullptr;
    }

    void zwp_tablet_v2_destroy_resource(QtWaylandServer::zwp_tablet_v2::Resource * resource) override
    {
        Q_UNUSED(resource);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
43
        if (m_removed && resourceMap().isEmpty()) {
44 45 46 47
            delete q;
        }
    }

48
    TabletV2Interface *const q;
49 50 51 52
    const uint32_t m_vendorId;
    const uint32_t m_productId;
    const QString m_name;
    const QStringList m_paths;
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
53
    bool m_removed = false;
54 55
};

56 57 58
TabletV2Interface::TabletV2Interface(uint32_t vendorId, uint32_t productId,
                                     const QString &name, const QStringList &paths,
                                     QObject *parent)
59
    : QObject(parent)
60
    , d(new TabletV2InterfacePrivate(this, vendorId, productId, name, paths))
61 62 63
{
}

64
TabletV2Interface::~TabletV2Interface() = default;
65

66
bool TabletV2Interface::isSurfaceSupported(SurfaceInterface *surface) const
67 68 69 70
{
    return d->resourceForSurface(surface);
}

71
void TabletV2Interface::sendRemoved()
72
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
73
    d->m_removed = true;
74 75 76 77 78
    for (QtWaylandServer::zwp_tablet_v2::Resource *resource : d->resourceMap()) {
        d->send_removed(resource->handle);
    }
}

79
class TabletCursorV2Private
80 81
{
public:
82
    TabletCursorV2Private(TabletCursorV2 *q) : q(q) {}
83 84 85

    void update(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot)
    {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
86 87 88 89 90 91
        const bool diff = m_serial != serial || m_surface != surface || m_hotspot != hotspot;
        if (diff) {
            m_serial = serial;
            m_surface = surface;
            m_hotspot = hotspot;

92
            Q_EMIT q->changed();
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
93
        }
94 95
    }

96
    TabletCursorV2 *const q;
97 98 99 100 101 102

    quint32 m_serial = 0;
    SurfaceInterface* m_surface = nullptr;
    QPoint m_hotspot;
};

103
TabletCursorV2::TabletCursorV2()
104
    : QObject()
105
    , d(new TabletCursorV2Private(this))
106 107 108
{
}

109
TabletCursorV2::~TabletCursorV2() = default;
110

111
QPoint TabletCursorV2::hotspot() const
112 113 114 115
{
    return d->m_hotspot;
}

116
quint32 TabletCursorV2::enteredSerial() const
117 118 119 120
{
    return d->m_serial;
}

121
SurfaceInterface *TabletCursorV2::surface() const
122 123 124 125
{
    return d->m_surface;
}

126
class TabletToolV2InterfacePrivate : public QtWaylandServer::zwp_tablet_tool_v2
127 128
{
public:
129 130 131 132
    TabletToolV2InterfacePrivate(TabletToolV2Interface *q, Display *display,
                                 TabletToolV2Interface::Type type, uint32_t hsh, uint32_t hsl,
                                 uint32_t hih, uint32_t hil,
                                 const QVector<TabletToolV2Interface::Capability>& capabilities)
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
        : zwp_tablet_tool_v2()
        , m_display(display)
        , m_type(type)
        , m_hardwareSerialHigh(hsh)
        , m_hardwareSerialLow(hsl)
        , m_hardwareIdHigh(hih)
        , m_hardwareIdLow(hil)
        , m_capabilities(capabilities)
        , q(q)
    {
    }


    wl_resource *targetResource()
    {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
148 149 150
        if (!m_surface)
            return nullptr;

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
        ClientConnection *client = m_surface->client();
        const Resource *r = resourceMap().value(*client);
        return r ? r->handle : nullptr;
    }

    quint64 hardwareId() const
    {
        return quint64(quint64(m_hardwareIdHigh) << 32) + m_hardwareIdLow;
    }
    quint64 hardwareSerial() const
    {
        return quint64(quint64(m_hardwareSerialHigh) << 32) + m_hardwareSerialLow;
    }

    void zwp_tablet_tool_v2_bind_resource(QtWaylandServer::zwp_tablet_tool_v2::Resource * resource) override
    {
167
        TabletCursorV2 *&c = m_cursors[resource->handle];
168
        if (!c)
169
            c = new TabletCursorV2;
170 171 172 173
    }

    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
    {
174
        TabletCursorV2 *c = m_cursors[resource->handle];
175 176 177 178 179 180 181 182 183 184 185 186
        c->d->update(serial, SurfaceInterface::get(_surface), {hotspot_x, hotspot_y});
        if (resource->handle == targetResource())
            q->cursorChanged(c);
    }

    void zwp_tablet_tool_v2_destroy_resource(Resource * resource) override {
        delete m_cursors.take(resource->handle);
    }

    Display *const m_display;
    bool m_cleanup = false;
    QPointer<SurfaceInterface> m_surface;
187
    QPointer<TabletV2Interface> m_lastTablet;
188 189 190
    const uint32_t m_type;
    const uint32_t m_hardwareSerialHigh, m_hardwareSerialLow;
    const uint32_t m_hardwareIdHigh, m_hardwareIdLow;
191
    const QVector<TabletToolV2Interface::Capability> m_capabilities;
192 193
    QHash<wl_resource *, TabletCursorV2 *> m_cursors;
    TabletToolV2Interface *const q;
194 195
};

196 197 198 199
TabletToolV2Interface::TabletToolV2Interface(Display *display, Type type, uint32_t hsh,
                                             uint32_t hsl, uint32_t hih, uint32_t hil,
                                             const QVector<Capability>& capabilities,
                                             QObject *parent)
200
    : QObject(parent)
201
    , d(new TabletToolV2InterfacePrivate(this, display, type, hsh, hsl, hih, hil, capabilities))
202 203 204
{
}

205
TabletToolV2Interface::~TabletToolV2Interface() = default;
206

207
void TabletToolV2Interface::setCurrentSurface(SurfaceInterface *surface)
208 209 210 211
{
    if (d->m_surface == surface)
        return;

212
    TabletV2Interface *const lastTablet = d->m_lastTablet;
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    if (d->m_surface && d->resourceMap().contains(*d->m_surface->client())) {
        sendProximityOut();
        sendFrame(0);
    }

    d->m_surface = surface;

    if (lastTablet && lastTablet->d->resourceForSurface(surface)) {
        sendProximityIn(lastTablet);
    } else {
        d->m_lastTablet = lastTablet;
    }

    Q_EMIT cursorChanged(d->m_cursors.value(d->targetResource()));
}

229
bool TabletToolV2Interface::isClientSupported() const
230 231 232 233
{
    return d->m_surface && d->targetResource();
}

234
void TabletToolV2Interface::sendButton(uint32_t button, bool pressed)
235 236 237 238 239 240
{
    d->send_button(d->targetResource(), d->m_display->nextSerial(), button,
                   pressed ? QtWaylandServer::zwp_tablet_tool_v2::button_state_pressed
                           : QtWaylandServer::zwp_tablet_tool_v2::button_state_released);
}

241
void TabletToolV2Interface::sendMotion(const QPointF &pos)
242 243 244 245 246
{
    d->send_motion(d->targetResource(), wl_fixed_from_double(pos.x()),
                                        wl_fixed_from_double(pos.y()));
}

247
void TabletToolV2Interface::sendDistance(uint32_t distance)
248 249 250 251
{
    d->send_distance(d->targetResource(), distance);
}

252
void TabletToolV2Interface::sendFrame(uint32_t time)
253 254 255 256 257 258 259 260 261 262
{
    d->send_frame(d->targetResource(), time);

    if (d->m_cleanup) {
        d->m_surface = nullptr;
        d->m_lastTablet = nullptr;
        d->m_cleanup = false;
    }
}

263
void TabletToolV2Interface::sendPressure(uint32_t pressure)
264 265 266 267
{
    d->send_pressure(d->targetResource(), pressure);
}

268
void TabletToolV2Interface::sendRotation(qreal rotation)
269 270 271 272
{
    d->send_rotation(d->targetResource(), wl_fixed_from_double(rotation));
}

273
void TabletToolV2Interface::sendSlider(int32_t position)
274 275 276 277
{
    d->send_slider(d->targetResource(), position);
}

278
void TabletToolV2Interface::sendTilt(qreal degreesX, qreal degreesY)
279 280 281 282 283
{
    d->send_tilt(d->targetResource(), wl_fixed_from_double(degreesX),
                                      wl_fixed_from_double(degreesY));
}

284
void TabletToolV2Interface::sendWheel(int32_t degrees, int32_t clicks)
285 286 287 288
{
    d->send_wheel(d->targetResource(), degrees, clicks);
}

289
void TabletToolV2Interface::sendProximityIn(TabletV2Interface *tablet)
290 291 292 293 294 295 296
{
    wl_resource* tabletResource = tablet->d->resourceForSurface(d->m_surface);
    d->send_proximity_in(d->targetResource(), d->m_display->nextSerial(),
                         tabletResource, d->m_surface->resource());
    d->m_lastTablet = tablet;
}

297
void TabletToolV2Interface::sendProximityOut()
298 299 300 301 302
{
    d->send_proximity_out(d->targetResource());
    d->m_cleanup = true;
}

303
void TabletToolV2Interface::sendDown()
304 305 306 307
{
    d->send_down(d->targetResource(), d->m_display->nextSerial());
}

308
void TabletToolV2Interface::sendUp()
309 310 311 312
{
    d->send_up(d->targetResource());
}

313
void TabletToolV2Interface::sendRemoved()
314 315 316 317 318 319
{
    for (QtWaylandServer::zwp_tablet_tool_v2::Resource *resource : d->resourceMap()) {
        d->send_removed(resource->handle);
    }
}

320
class TabletSeatV2InterfacePrivate : public QtWaylandServer::zwp_tablet_seat_v2
321 322
{
public:
323
    TabletSeatV2InterfacePrivate(Display *display, TabletSeatV2Interface *q)
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
        : zwp_tablet_seat_v2()
        , q(q)
        , m_display(display)
    {
    }

    void zwp_tablet_seat_v2_bind_resource(Resource *resource) override
    {
        for (auto iface : qAsConst(m_tablets)) {
            sendTabletAdded(resource, iface);
        }

        for (auto *tool : qAsConst(m_tools)) {
            sendToolAdded(resource, tool);
        }
    }

341
    void sendToolAdded(Resource *resource, TabletToolV2Interface *tool)
342 343 344 345 346 347 348 349 350 351 352 353 354 355
    {
        wl_resource *toolResource = tool->d->add(resource->client(), resource->version())->handle;
        send_tool_added(resource->handle, toolResource);

        tool->d->send_type(toolResource, tool->d->m_type);
        tool->d->send_hardware_serial(toolResource, tool->d->m_hardwareSerialHigh,
                                                    tool->d->m_hardwareSerialLow);
        tool->d->send_hardware_id_wacom(toolResource, tool->d->m_hardwareIdHigh,
                                                      tool->d->m_hardwareIdLow);
        for (uint32_t cap : qAsConst(tool->d->m_capabilities)) {
            tool->d->send_capability(toolResource, cap);
        }
        tool->d->send_done(toolResource);
    }
356
    void sendTabletAdded(Resource *resource, TabletV2Interface *tablet)
357 358 359 360 361 362 363 364 365 366 367 368 369 370
    {
        wl_resource *tabletResource = tablet->d->add(resource->client(), resource->version())->handle;
        send_tablet_added(resource->handle, tabletResource);

        tablet->d->send_name(tabletResource, tablet->d->m_name);
        if (tablet->d->m_vendorId && tablet->d->m_productId) {
            tablet->d->send_id(tabletResource, tablet->d->m_vendorId, tablet->d->m_productId);
        }
        for (const QString &path : qAsConst(tablet->d->m_paths)) {
            tablet->d->send_path(tabletResource, path);
        }
        tablet->d->send_done(tabletResource);
    }

371 372 373
    TabletSeatV2Interface *const q;
    QVector<TabletToolV2Interface *> m_tools;
    QHash<QString, TabletV2Interface *> m_tablets;
374 375 376
    Display *const m_display;
};

377
TabletSeatV2Interface::TabletSeatV2Interface(Display *display, QObject *parent)
378
    : QObject(parent)
379
    , d(new TabletSeatV2InterfacePrivate(display, this))
380 381 382
{
}

383
TabletSeatV2Interface::~TabletSeatV2Interface() = default;
384

385 386 387 388
TabletToolV2Interface *TabletSeatV2Interface::addTool(TabletToolV2Interface::Type type,
                                                      quint64 hardwareSerial,
                                                      quint64 hardwareId,
                                                      const QVector<TabletToolV2Interface::Capability> &capabilities)
389 390
{
    constexpr auto MAX_UINT_32 = std::numeric_limits<quint32>::max();
391 392 393
    auto tool = new TabletToolV2Interface(d->m_display,
                                          type, hardwareSerial >> 32, hardwareSerial & MAX_UINT_32,
                                                 hardwareId >> 32, hardwareId & MAX_UINT_32, capabilities, this);
394 395 396 397 398 399
    for (QtWaylandServer::zwp_tablet_seat_v2::Resource *resource : d->resourceMap()) {
        d->sendToolAdded(resource, tool);
    }

    d->m_tools.append(tool);
    QObject::connect(tool, &QObject::destroyed, this, [this](QObject *object) {
400
        auto tti = static_cast<TabletToolV2Interface *>(object);
401 402 403 404 405 406
        tti->d->send_removed();
        d->m_tools.removeAll(tti);
    });
    return tool;
}

407 408 409 410
TabletV2Interface *TabletSeatV2Interface::addTablet(uint32_t vendorId, uint32_t productId,
                                                    const QString &sysname,
                                                    const QString &name,
                                                    const QStringList &paths)
411
{
412
    auto iface = new TabletV2Interface(vendorId, productId, name, paths, this);
413 414 415 416 417 418 419 420 421

    for (QtWaylandServer::zwp_tablet_seat_v2::Resource *r : d->resourceMap()) {
        d->sendTabletAdded(r, iface);
    }

    d->m_tablets[sysname] = iface;
    return iface;
}

422
void TabletSeatV2Interface::removeTablet(const QString &sysname)
423 424 425 426 427 428 429
{
    auto tablet = d->m_tablets.take(sysname);
    if (tablet) {
        tablet->sendRemoved();
    }
}

430
TabletToolV2Interface *TabletSeatV2Interface::toolByHardwareId(quint64 hardwareId) const
431
{
432
    for (TabletToolV2Interface *tool : d->m_tools) {
433 434 435 436 437 438
        if (tool->d->hardwareId() == hardwareId)
            return tool;
    }
    return nullptr;
}

439
TabletToolV2Interface *TabletSeatV2Interface::toolByHardwareSerial(quint64 hardwareSerial) const
440
{
441
    for (TabletToolV2Interface *tool : d->m_tools) {
442 443 444 445 446 447
        if (tool->d->hardwareSerial() == hardwareSerial)
            return tool;
    }
    return nullptr;
}

448
TabletV2Interface *TabletSeatV2Interface::tabletByName(const QString &name) const
449 450 451 452
{
    return d->m_tablets.value(name);
}

453
class TabletManagerV2InterfacePrivate : public QtWaylandServer::zwp_tablet_manager_v2
454 455
{
public:
456
    TabletManagerV2InterfacePrivate(Display *display, TabletManagerV2Interface *q)
457 458 459 460 461 462 463 464 465
        : zwp_tablet_manager_v2(*display, s_version)
        , q(q)
        , m_display(display)
    {
    }

    void zwp_tablet_manager_v2_get_tablet_seat(Resource *resource, uint32_t tablet_seat,
                                               struct ::wl_resource *seat_resource) override {
        SeatInterface* seat = SeatInterface::get(seat_resource);
466
        TabletSeatV2Interface *tsi = get(seat);
467 468 469
        tsi->d->add(resource->client(), tablet_seat, s_version);
    }

470
    TabletSeatV2Interface *get(SeatInterface *seat)
471
    {
472
        TabletSeatV2Interface *&tabletSeat = m_seats[seat];
473
        if (!tabletSeat) {
474
            tabletSeat = new TabletSeatV2Interface(m_display, q);
475 476 477 478
        }
        return tabletSeat;
    }

479
    TabletManagerV2Interface *const q;
480
    Display *const m_display;
481
    QHash<SeatInterface *, TabletSeatV2Interface *> m_seats;
482 483
};

484
TabletManagerV2Interface::TabletManagerV2Interface(Display *display, QObject *parent)
485
    : QObject(parent)
486
    , d(new TabletManagerV2InterfacePrivate(display, this))
487 488 489
{
}

490
TabletSeatV2Interface *TabletManagerV2Interface::seat(SeatInterface *seat) const
491 492 493 494
{
    return d->get(seat);
}

495
TabletManagerV2Interface::~TabletManagerV2Interface() = default;
496 497

} // namespace KWaylandServer