KWayland

tablet_interface.cpp
1 /********************************************************************
2 Copyright 2019 Aleix Pol Gonzalez <[email protected]>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include "tablet_interface.h"
21 #include "display.h"
22 #include "resource_p.h"
23 #include "seat_interface.h"
24 #include "surface_interface.h"
25 
26 #include "qwayland-server-tablet-unstable-v2.h"
27 #include <QHash>
28 
29 using namespace KWayland;
30 using namespace Server;
31 
32 static int s_version = 1;
33 
34 class TabletInterface::Private : public QtWaylandServer::zwp_tablet_v2
35 {
36 public:
37  Private(TabletInterface *q, uint32_t vendorId, uint32_t productId, const QString name, const QStringList &paths)
38  : zwp_tablet_v2()
39  , q(q)
40  , m_vendorId(vendorId)
41  , m_productId(productId)
42  , m_name(name)
43  , m_paths(paths)
44  {
45  }
46 
47  wl_resource *resourceForSurface(SurfaceInterface *surface) const
48  {
49  ClientConnection *client = surface->client();
50  QtWaylandServer::zwp_tablet_v2::Resource *r = resourceMap().value(*client);
51  return r ? r->handle : nullptr;
52  }
53 
54  void zwp_tablet_v2_destroy_resource(QtWaylandServer::zwp_tablet_v2::Resource *resource) override
55  {
56  Q_UNUSED(resource);
57  if (removed && resourceMap().isEmpty()) {
58  delete q;
59  }
60  }
61 
62  TabletInterface *const q;
63  const uint32_t m_vendorId;
64  const uint32_t m_productId;
65  const QString m_name;
66  const QStringList m_paths;
67  bool removed = false;
68 };
69 
70 TabletInterface::TabletInterface(uint32_t vendorId, uint32_t productId, const QString &name, const QStringList &paths, QObject *parent)
71  : QObject(parent)
72  , d(new Private(this, vendorId, productId, name, paths))
73 {
74 }
75 
76 TabletInterface::~TabletInterface() = default;
77 
78 bool TabletInterface::isSurfaceSupported(SurfaceInterface *surface) const
79 {
80  return d->resourceForSurface(surface);
81 }
82 
83 void TabletInterface::sendRemoved()
84 {
85  d->removed = true;
86  for (QtWaylandServer::zwp_tablet_v2::Resource *resource : d->resourceMap()) {
87  d->send_removed(resource->handle);
88  }
89 }
90 
91 class TabletCursor::Private
92 {
93 public:
94  Private(TabletCursor *q)
95  : q(q)
96  {
97  }
98 
99  void update(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot)
100  {
101  const bool diff = m_serial != serial && m_surface != surface && m_hotspot != hotspot;
102  m_serial = serial;
103  m_surface = surface;
104  m_hotspot = hotspot;
105  if (diff) {
106  Q_EMIT q->changed();
107  }
108  }
109 
110  TabletCursor *const q;
111 
112  quint32 m_serial = 0;
113  SurfaceInterface *m_surface = nullptr;
114  QPoint m_hotspot;
115 };
116 
117 TabletCursor::TabletCursor()
118  : QObject()
119  , d(new Private(this))
120 {
121 }
122 
123 TabletCursor::~TabletCursor() = default;
124 
125 QPoint TabletCursor::hotspot() const
126 {
127  return d->m_hotspot;
128 }
129 
130 quint32 TabletCursor::enteredSerial() const
131 {
132  return d->m_serial;
133 }
134 
135 SurfaceInterface *TabletCursor::surface() const
136 {
137  return d->m_surface;
138 }
139 
140 class TabletToolInterface::Private : public QtWaylandServer::zwp_tablet_tool_v2
141 {
142 public:
143  Private(TabletToolInterface *q,
144  Display *display,
145  Type type,
146  uint32_t hsh,
147  uint32_t hsl,
148  uint32_t hih,
149  uint32_t hil,
150  const QVector<Capability> &capabilities)
151  : zwp_tablet_tool_v2()
152  , m_display(display)
153  , m_type(type)
154  , m_hardwareSerialHigh(hsh)
155  , m_hardwareSerialLow(hsl)
156  , m_hardwareIdHigh(hih)
157  , m_hardwareIdLow(hil)
158  , m_capabilities(capabilities)
159  , q(q)
160  {
161  }
162 
163  wl_resource *targetResource()
164  {
165  if (!m_surface) {
166  return nullptr;
167  }
168 
169  ClientConnection *client = m_surface->client();
170  const Resource *r = resourceMap().value(*client);
171  return r ? r->handle : nullptr;
172  }
173 
174  quint64 hardwareId() const
175  {
176  return quint64(quint64(m_hardwareIdHigh) << 32) + m_hardwareIdLow;
177  }
178  quint64 hardwareSerial() const
179  {
180  return quint64(quint64(m_hardwareSerialHigh) << 32) + m_hardwareSerialLow;
181  }
182 
183  void zwp_tablet_tool_v2_bind_resource(QtWaylandServer::zwp_tablet_tool_v2::Resource *resource) override
184  {
185  TabletCursor *&c = m_cursors[resource->handle];
186  if (!c) {
187  c = new TabletCursor;
188  }
189  }
190 
191  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
192  {
193  TabletCursor *c = m_cursors[resource->handle];
194  c->d->update(serial, SurfaceInterface::get(_surface), {hotspot_x, hotspot_y});
195  if (resource->handle == targetResource()) {
196  q->cursorChanged(c);
197  }
198  }
199 
200  void zwp_tablet_tool_v2_destroy_resource(Resource *resource) override
201  {
202  delete m_cursors.take(resource->handle);
203  }
204 
205  Display *const m_display;
206  bool m_cleanup = false;
207  QPointer<SurfaceInterface> m_surface;
208  QPointer<TabletInterface> m_lastTablet;
209  const uint32_t m_type;
210  const uint32_t m_hardwareSerialHigh, m_hardwareSerialLow;
211  const uint32_t m_hardwareIdHigh, m_hardwareIdLow;
212  const QVector<Capability> m_capabilities;
214  TabletToolInterface *const q;
215 };
216 
217 TabletToolInterface::TabletToolInterface(Display *display,
218  Type type,
219  uint32_t hsh,
220  uint32_t hsl,
221  uint32_t hih,
222  uint32_t hil,
223  const QVector<Capability> &capabilities,
224  QObject *parent)
225  : QObject(parent)
226  , d(new Private(this, display, type, hsh, hsl, hih, hil, capabilities))
227 {
228 }
229 
230 TabletToolInterface::~TabletToolInterface() = default;
231 
232 void TabletToolInterface::setCurrentSurface(SurfaceInterface *surface)
233 {
234  if (d->m_surface == surface) {
235  return;
236  }
237 
238  TabletInterface *const lastTablet = d->m_lastTablet;
239  if (d->m_surface && d->resourceMap().contains(*d->m_surface->client())) {
240  sendProximityOut();
241  sendFrame(0);
242  }
243 
244  d->m_surface = surface;
245 
246  if (lastTablet && lastTablet->d->resourceForSurface(surface)) {
247  sendProximityIn(lastTablet);
248  } else {
249  d->m_lastTablet = lastTablet;
250  }
251 
252  Q_EMIT cursorChanged(d->m_cursors.value(d->targetResource()));
253 }
254 
255 bool TabletToolInterface::isClientSupported() const
256 {
257  return d->m_surface && d->targetResource();
258 }
259 
260 void TabletToolInterface::sendButton(uint32_t button, bool pressed)
261 {
262  d->send_button(d->targetResource(),
263  d->m_display->nextSerial(),
264  button,
265  pressed ? QtWaylandServer::zwp_tablet_tool_v2::button_state_pressed : QtWaylandServer::zwp_tablet_tool_v2::button_state_released);
266 }
267 
268 void TabletToolInterface::sendMotion(const QPointF &pos)
269 {
270  d->send_motion(d->targetResource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
271 }
272 
273 void TabletToolInterface::sendDistance(uint32_t distance)
274 {
275  d->send_distance(d->targetResource(), distance);
276 }
277 
278 void TabletToolInterface::sendFrame(uint32_t time)
279 {
280  d->send_frame(d->targetResource(), time);
281 
282  if (d->m_cleanup) {
283  d->m_surface = nullptr;
284  d->m_lastTablet = nullptr;
285  d->m_cleanup = false;
286  }
287 }
288 
289 void TabletToolInterface::sendPressure(uint32_t pressure)
290 {
291  d->send_pressure(d->targetResource(), pressure);
292 }
293 
294 void TabletToolInterface::sendRotation(qreal rotation)
295 {
296  d->send_rotation(d->targetResource(), wl_fixed_from_double(rotation));
297 }
298 
299 void TabletToolInterface::sendSlider(int32_t position)
300 {
301  d->send_slider(d->targetResource(), position);
302 }
303 
304 void TabletToolInterface::sendTilt(qreal degreesX, qreal degreesY)
305 {
306  d->send_tilt(d->targetResource(), wl_fixed_from_double(degreesX), wl_fixed_from_double(degreesY));
307 }
308 
309 void TabletToolInterface::sendWheel(int32_t degrees, int32_t clicks)
310 {
311  d->send_wheel(d->targetResource(), degrees, clicks);
312 }
313 
314 void TabletToolInterface::sendProximityIn(TabletInterface *tablet)
315 {
316  wl_resource *tabletResource = tablet->d->resourceForSurface(d->m_surface);
317  d->send_proximity_in(d->targetResource(), d->m_display->nextSerial(), tabletResource, d->m_surface->resource());
318  d->m_lastTablet = tablet;
319 }
320 
321 void TabletToolInterface::sendProximityOut()
322 {
323  d->send_proximity_out(d->targetResource());
324  d->m_cleanup = true;
325 }
326 
327 void TabletToolInterface::sendDown()
328 {
329  d->send_down(d->targetResource(), d->m_display->nextSerial());
330 }
331 
332 void TabletToolInterface::sendUp()
333 {
334  d->send_up(d->targetResource());
335 }
336 
337 void TabletToolInterface::sendRemoved()
338 {
339  for (QtWaylandServer::zwp_tablet_tool_v2::Resource *resource : d->resourceMap()) {
340  d->send_removed(resource->handle);
341  }
342 }
343 
344 class TabletSeatInterface::Private : public QtWaylandServer::zwp_tablet_seat_v2
345 {
346 public:
347  Private(Display *display, TabletSeatInterface *q)
348  : zwp_tablet_seat_v2()
349  , q(q)
350  , m_display(display)
351  {
352  }
353 
354  void zwp_tablet_seat_v2_bind_resource(Resource *resource) override
355  {
356  for (auto iface : qAsConst(m_tablets)) {
357  sendTabletAdded(resource, iface);
358  }
359 
360  for (auto *tool : qAsConst(m_tools)) {
361  sendToolAdded(resource, tool);
362  }
363  }
364 
365  void sendToolAdded(Resource *resource, TabletToolInterface *tool)
366  {
367  wl_resource *toolResource = tool->d->add(resource->client(), resource->version())->handle;
368  send_tool_added(resource->handle, toolResource);
369 
370  tool->d->send_type(toolResource, tool->d->m_type);
371  tool->d->send_hardware_serial(toolResource, tool->d->m_hardwareSerialHigh, tool->d->m_hardwareSerialLow);
372  tool->d->send_hardware_id_wacom(toolResource, tool->d->m_hardwareIdHigh, tool->d->m_hardwareIdLow);
373  for (uint32_t cap : qAsConst(tool->d->m_capabilities)) {
374  tool->d->send_capability(toolResource, cap);
375  }
376  tool->d->send_done(toolResource);
377  }
378  void sendTabletAdded(Resource *resource, TabletInterface *tablet)
379  {
380  wl_resource *tabletResource = tablet->d->add(resource->client(), resource->version())->handle;
381  send_tablet_added(resource->handle, tabletResource);
382 
383  tablet->d->send_name(tabletResource, tablet->d->m_name);
384  if (tablet->d->m_vendorId && tablet->d->m_productId) {
385  tablet->d->send_id(tabletResource, tablet->d->m_vendorId, tablet->d->m_productId);
386  }
387  for (const QString &path : qAsConst(tablet->d->m_paths)) {
388  tablet->d->send_path(tabletResource, path);
389  }
390  tablet->d->send_done(tabletResource);
391  }
392 
393  TabletSeatInterface *const q;
396  Display *const m_display;
397 };
398 
399 TabletSeatInterface::TabletSeatInterface(Display *display, QObject *parent)
400  : QObject(parent)
401  , d(new Private(display, this))
402 {
403 }
404 
405 TabletSeatInterface::~TabletSeatInterface() = default;
406 
407 TabletToolInterface *TabletSeatInterface::addTool(TabletToolInterface::Type type,
408  quint64 hardwareSerial,
409  quint64 hardwareId,
410  const QVector<TabletToolInterface::Capability> &capabilities)
411 {
412  constexpr auto MAX_UINT_32 = std::numeric_limits<quint32>::max();
413  auto tool = new TabletToolInterface(d->m_display,
414  type,
415  hardwareSerial >> 32,
416  hardwareSerial & MAX_UINT_32,
417  hardwareId >> 32,
418  hardwareId & MAX_UINT_32,
419  capabilities,
420  this);
421  for (QtWaylandServer::zwp_tablet_seat_v2::Resource *resource : d->resourceMap()) {
422  d->sendToolAdded(resource, tool);
423  }
424 
425  d->m_tools.append(tool);
426  QObject::connect(tool, &QObject::destroyed, this, [this](QObject *object) {
427  auto tti = static_cast<TabletToolInterface *>(object);
428  tti->d->send_removed();
429  d->m_tools.removeAll(tti);
430  });
431  return tool;
432 }
433 
434 TabletInterface *TabletSeatInterface::addTablet(uint32_t vendorId, uint32_t productId, const QString &sysname, const QString &name, const QStringList &paths)
435 {
436  auto iface = new TabletInterface(vendorId, productId, name, paths, this);
437 
438  for (QtWaylandServer::zwp_tablet_seat_v2::Resource *r : d->resourceMap()) {
439  d->sendTabletAdded(r, iface);
440  }
441 
442  d->m_tablets[sysname] = iface;
443  return iface;
444 }
445 
446 void TabletSeatInterface::removeTablet(const QString &sysname)
447 {
448  auto tablet = d->m_tablets.take(sysname);
449  if (tablet) {
450  tablet->sendRemoved();
451  }
452 }
453 
454 TabletToolInterface *TabletSeatInterface::toolByHardwareId(quint64 hardwareId) const
455 {
456  for (TabletToolInterface *tool : d->m_tools) {
457  if (tool->d->hardwareId() == hardwareId) {
458  return tool;
459  }
460  }
461  return nullptr;
462 }
463 
464 TabletToolInterface *TabletSeatInterface::toolByHardwareSerial(quint64 hardwareSerial) const
465 {
466  for (TabletToolInterface *tool : d->m_tools) {
467  if (tool->d->hardwareSerial() == hardwareSerial) {
468  return tool;
469  }
470  }
471  return nullptr;
472 }
473 
474 TabletInterface *TabletSeatInterface::tabletByName(const QString &name) const
475 {
476  return d->m_tablets.value(name);
477 }
478 
479 class TabletManagerInterface::Private : public QtWaylandServer::zwp_tablet_manager_v2
480 {
481 public:
482  Private(Display *display, TabletManagerInterface *q)
483  : zwp_tablet_manager_v2(*display, s_version)
484  , q(q)
485  , m_display(display)
486  {
487  }
488 
489  void zwp_tablet_manager_v2_get_tablet_seat(Resource *resource, uint32_t tablet_seat, struct ::wl_resource *seat_resource) override
490  {
491  SeatInterface *seat = SeatInterface::get(seat_resource);
492  TabletSeatInterface *tsi = get(seat);
493  tsi->d->add(resource->client(), tablet_seat, s_version);
494  }
495 
497  {
498  TabletSeatInterface *&tabletSeat = m_seats[seat];
499  if (!tabletSeat) {
500  tabletSeat = new TabletSeatInterface(m_display, q);
501  }
502  return tabletSeat;
503  }
504 
505  TabletManagerInterface *const q;
506  Display *const m_display;
508 };
509 
510 TabletManagerInterface::TabletManagerInterface(Display *display, QObject *parent)
511  : QObject(parent)
512  , d(new Private(display, this))
513 {
514 }
515 
516 TabletSeatInterface *TabletManagerInterface::seat(SeatInterface *seat) const
517 {
518  return d->get(seat);
519 }
520 
521 TabletManagerInterface::~TabletManagerInterface() = default;
T * get() const const
This is an implementation of wayland-protocols/unstable/tablet/tablet-unstable-v2.xml.
qreal x() const const
qreal y() const const
Represents a Seat on the Wayland Display.
static SurfaceInterface * get(wl_resource *native)
Resource representing a wl_surface.
Class holding the Wayland server display loop.
Definition: display.h:86
Represents a bound Resource.
Definition: resource.h:31
Convenient Class which represents a wl_client.
void update(Part *part, const QByteArray &data, qint64 dataSize)
ClientConnection * client()
Definition: resource.cpp:76
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
void destroyed(QObject *obj)
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Sep 23 2021 22:51:10 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.