1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qwaylandtabletv2_p.h" |
5 | #include "qwaylandinputdevice_p.h" |
6 | #include "qwaylanddisplay_p.h" |
7 | #include "qwaylandsurface_p.h" |
8 | #include "qwaylandscreen_p.h" |
9 | #include "qwaylandbuffer_p.h" |
10 | #include "qwaylandcursorsurface_p.h" |
11 | #include "qwaylandcursor_p.h" |
12 | |
13 | #include <QtGui/private/qguiapplication_p.h> |
14 | #include <QtGui/private/qpointingdevice_p.h> |
15 | #include <QtGui/qpa/qplatformtheme.h> |
16 | #include <QtGui/qpa/qwindowsysteminterface_p.h> |
17 | |
18 | #include <wayland-cursor.h> |
19 | |
20 | QT_BEGIN_NAMESPACE |
21 | |
22 | namespace QtWaylandClient { |
23 | |
24 | using namespace Qt::StringLiterals; |
25 | |
26 | #if QT_CONFIG(cursor) |
27 | int QWaylandTabletToolV2::idealCursorScale() const |
28 | { |
29 | if (m_tabletSeat->seat()->mQDisplay->compositor()->version() < 3) { |
30 | return 1; |
31 | } |
32 | |
33 | if (auto *s = mCursor.surface.data()) { |
34 | if (s->outputScale() > 0) |
35 | return s->outputScale(); |
36 | } |
37 | |
38 | return m_tabletSeat->seat()->mCursor.fallbackOutputScale; |
39 | } |
40 | |
41 | void QWaylandTabletToolV2::updateCursorTheme() |
42 | { |
43 | QString cursorThemeName; |
44 | QSize cursorSize; |
45 | |
46 | if (const QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme()) { |
47 | cursorThemeName = platformTheme->themeHint(hint: QPlatformTheme::MouseCursorTheme).toString(); |
48 | cursorSize = platformTheme->themeHint(hint: QPlatformTheme::MouseCursorSize).toSize(); |
49 | } |
50 | |
51 | if (cursorThemeName.isEmpty()) |
52 | cursorThemeName = QStringLiteral("default" ); |
53 | if (cursorSize.isEmpty()) |
54 | cursorSize = QSize(24, 24); |
55 | |
56 | int scale = idealCursorScale(); |
57 | int pixelSize = cursorSize.width() * scale; |
58 | auto *display = m_tabletSeat->seat()->mQDisplay; |
59 | mCursor.theme = display->loadCursorTheme(name: cursorThemeName, pixelSize); |
60 | |
61 | if (!mCursor.theme) |
62 | return; // A warning has already been printed in loadCursorTheme |
63 | |
64 | if (auto *arrow = mCursor.theme->cursor(shape: Qt::ArrowCursor)) { |
65 | int arrowPixelSize = qMax(a: arrow->images[0]->width, |
66 | b: arrow->images[0]->height); // Not all cursor themes are square |
67 | while (scale > 1 && arrowPixelSize / scale < cursorSize.width()) |
68 | --scale; |
69 | } else { |
70 | qCWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor" ; |
71 | } |
72 | mCursor.themeBufferScale = scale; |
73 | } |
74 | |
75 | void QWaylandTabletToolV2::updateCursor() |
76 | { |
77 | if (mEnterSerial == 0) |
78 | return; |
79 | |
80 | auto shape = m_tabletSeat->seat()->mCursor.shape; |
81 | |
82 | if (shape == Qt::BlankCursor) { |
83 | if (mCursor.surface) |
84 | mCursor.surface->reset(); |
85 | set_cursor(mEnterSerial, nullptr, 0, 0); |
86 | return; |
87 | } |
88 | |
89 | if (shape == Qt::BitmapCursor) { |
90 | auto buffer = m_tabletSeat->seat()->mCursor.bitmapBuffer; |
91 | if (!buffer) { |
92 | qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor" ; |
93 | return; |
94 | } |
95 | auto hotspot = m_tabletSeat->seat()->mCursor.hotspot; |
96 | int bufferScale = m_tabletSeat->seat()->mCursor.bitmapScale; |
97 | getOrCreateCursorSurface()->update(buffer: buffer->buffer(), hotspot, size: buffer->size(), bufferScale); |
98 | return; |
99 | } |
100 | |
101 | if (mCursor.shape) { |
102 | if (mCursor.surface) { |
103 | mCursor.surface->reset(); |
104 | } |
105 | mCursor.shape->setShape(serial: mEnterSerial, shape); |
106 | return; |
107 | } |
108 | |
109 | if (!mCursor.theme || idealCursorScale() != mCursor.themeBufferScale) |
110 | updateCursorTheme(); |
111 | |
112 | if (!mCursor.theme) |
113 | return; |
114 | |
115 | // Set from shape using theme |
116 | uint time = m_tabletSeat->seat()->mCursor.animationTimer.elapsed(); |
117 | |
118 | if (struct ::wl_cursor *waylandCursor = mCursor.theme->cursor(shape)) { |
119 | uint duration = 0; |
120 | int frame = wl_cursor_frame_and_duration(cursor: waylandCursor, time, duration: &duration); |
121 | ::wl_cursor_image *image = waylandCursor->images[frame]; |
122 | |
123 | struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); |
124 | if (!buffer) { |
125 | qCWarning(lcQpaWayland) << "Could not find buffer for cursor" << shape; |
126 | return; |
127 | } |
128 | int bufferScale = mCursor.themeBufferScale; |
129 | QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale; |
130 | QSize size = QSize(image->width, image->height) / bufferScale; |
131 | bool animated = duration > 0; |
132 | if (animated) { |
133 | mCursor.gotFrameCallback = false; |
134 | mCursor.gotTimerCallback = false; |
135 | mCursor.frameTimer.start(msec: duration); |
136 | } |
137 | getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale, animated); |
138 | return; |
139 | } |
140 | |
141 | qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape; |
142 | } |
143 | |
144 | CursorSurface<QWaylandTabletToolV2> *QWaylandTabletToolV2::getOrCreateCursorSurface() |
145 | { |
146 | if (!mCursor.surface) |
147 | mCursor.surface.reset( |
148 | other: new CursorSurface<QWaylandTabletToolV2>(this, m_tabletSeat->seat()->mQDisplay)); |
149 | return mCursor.surface.get(); |
150 | } |
151 | |
152 | void QWaylandTabletToolV2::cursorTimerCallback() |
153 | { |
154 | mCursor.gotTimerCallback = true; |
155 | if (mCursor.gotFrameCallback) |
156 | updateCursor(); |
157 | } |
158 | |
159 | void QWaylandTabletToolV2::cursorFrameCallback() |
160 | { |
161 | mCursor.gotFrameCallback = true; |
162 | if (mCursor.gotTimerCallback) |
163 | updateCursor(); |
164 | } |
165 | |
166 | #endif // QT_CONFIG(cursor) |
167 | |
168 | QWaylandTabletManagerV2::QWaylandTabletManagerV2(QWaylandDisplay *display, uint id, uint version) |
169 | : zwp_tablet_manager_v2(display->wl_registry(), id, qMin(version, uint(1))) |
170 | { |
171 | qCDebug(lcQpaInputDevices, "new tablet manager: ID %d version %d" , id, version); |
172 | } |
173 | |
174 | QWaylandTabletManagerV2::~QWaylandTabletManagerV2() |
175 | { |
176 | destroy(); |
177 | } |
178 | |
179 | QWaylandTabletSeatV2::QWaylandTabletSeatV2(QWaylandTabletManagerV2 *manager, QWaylandInputDevice *seat) |
180 | : QtWayland::zwp_tablet_seat_v2(manager->get_tablet_seat(seat->wl_seat())) |
181 | , m_seat(seat) |
182 | { |
183 | qCDebug(lcQpaInputDevices) << "new tablet seat" << seat->seatname() << "id" << seat->id(); |
184 | } |
185 | |
186 | QWaylandTabletSeatV2::~QWaylandTabletSeatV2() |
187 | { |
188 | qDeleteAll(c: m_tablets); |
189 | qDeleteAll(c: m_tools); |
190 | qDeleteAll(c: m_deadTools); |
191 | qDeleteAll(c: m_pads); |
192 | destroy(); |
193 | } |
194 | |
195 | void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tablet_added(zwp_tablet_v2 *id) |
196 | { |
197 | auto *tablet = new QWaylandTabletV2(id, m_seat->seatname()); |
198 | qCDebug(lcQpaInputDevices) << "seat" << this << id << "has tablet" << tablet; |
199 | tablet->setParent(this); |
200 | m_tablets.push_back(tablet); |
201 | connect(tablet, &QWaylandTabletV2::destroyed, this, [this, tablet] { m_tablets.removeOne(tablet); }); |
202 | } |
203 | |
204 | void QWaylandTabletSeatV2::zwp_tablet_seat_v2_tool_added(zwp_tablet_tool_v2 *id) |
205 | { |
206 | qDeleteAll(c: m_deadTools); |
207 | auto *tool = new QWaylandTabletToolV2(this, id); |
208 | if (m_tablets.size() == 1) { |
209 | tool->setParent(m_tablets.first()); |
210 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(tool); |
211 | d->name = m_tablets.first()->name() + u" stylus" ; |
212 | } else { |
213 | qCDebug(lcQpaInputDevices) << "seat" << this << "has tool" << tool << "for one of these tablets:" << m_tablets; |
214 | // TODO identify which tablet if there are several; then tool->setParent(tablet) |
215 | } |
216 | m_tools.push_back(tool); |
217 | connect(tool, &QWaylandTabletToolV2::destroyed, this, [this, tool] { |
218 | m_tools.removeOne(tool); |
219 | m_deadTools.removeOne(tool); |
220 | }); |
221 | } |
222 | |
223 | void QWaylandTabletSeatV2::zwp_tablet_seat_v2_pad_added(zwp_tablet_pad_v2 *id) |
224 | { |
225 | auto *pad = new QWaylandTabletPadV2(id); |
226 | if (m_tablets.size() == 1) { |
227 | pad->setParent(m_tablets.first()); |
228 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(pad); |
229 | d->name = m_tablets.first()->name() + u" touchpad" ; |
230 | } else { |
231 | qCDebug(lcQpaInputDevices) << "seat" << this << "has touchpad" << pad << "for one of these tablets:" << m_tablets; |
232 | // TODO identify which tablet if there are several |
233 | } |
234 | m_pads.push_back(pad); |
235 | connect(pad, &QWaylandTabletPadV2::destroyed, this, [this, pad] { m_pads.removeOne(pad); }); |
236 | } |
237 | |
238 | QWaylandTabletV2::QWaylandTabletV2(::zwp_tablet_v2 *tablet, const QString &seatName) |
239 | : QPointingDevice(u"unknown"_s , -1, DeviceType::Stylus, PointerType::Pen, |
240 | Capability::Position | Capability::Hover, |
241 | 1, 1) |
242 | , QtWayland::zwp_tablet_v2(tablet) |
243 | { |
244 | qCDebug(lcQpaInputDevices) << "new tablet on seat" << seatName; |
245 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(q: this); |
246 | d->seatName = seatName; |
247 | } |
248 | |
249 | QWaylandTabletV2::~QWaylandTabletV2() |
250 | { |
251 | destroy(); |
252 | } |
253 | |
254 | void QWaylandTabletV2::zwp_tablet_v2_name(const QString &name) |
255 | { |
256 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(q: this); |
257 | d->name = name; |
258 | } |
259 | |
260 | void QWaylandTabletV2::zwp_tablet_v2_id(uint32_t vid, uint32_t pid) |
261 | { |
262 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(q: this); |
263 | d->systemId = (quint64(vid) << 32) | pid; |
264 | qCDebug(lcQpaInputDevices) << "vid" << vid << "pid" << pid << "stored as systemId in" << this; |
265 | } |
266 | |
267 | void QWaylandTabletV2::zwp_tablet_v2_path(const QString &path) |
268 | { |
269 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(q: this); |
270 | d->busId = path; |
271 | } |
272 | |
273 | void QWaylandTabletV2::zwp_tablet_v2_done() |
274 | { |
275 | QWindowSystemInterface::registerInputDevice(device: this); |
276 | } |
277 | |
278 | void QWaylandTabletSeatV2::updateCursor() |
279 | { |
280 | for (auto tool : m_tools) |
281 | tool->updateCursor(); |
282 | } |
283 | |
284 | void QWaylandTabletSeatV2::toolRemoved(QWaylandTabletToolV2 *tool) |
285 | { |
286 | m_tools.removeOne(t: tool); |
287 | m_deadTools.append(t: tool); |
288 | } |
289 | |
290 | void QWaylandTabletV2::zwp_tablet_v2_removed() |
291 | { |
292 | deleteLater(); |
293 | } |
294 | |
295 | QWaylandTabletToolV2::QWaylandTabletToolV2(QWaylandTabletSeatV2 *tabletSeat, ::zwp_tablet_tool_v2 *tool) |
296 | : QPointingDevice(u"tool"_s , -1, DeviceType::Stylus, PointerType::Pen, |
297 | Capability::Position | Capability::Hover, |
298 | 1, 1) |
299 | , QtWayland::zwp_tablet_tool_v2(tool) |
300 | , m_tabletSeat(tabletSeat) |
301 | { |
302 | // TODO get the number of buttons somehow? |
303 | |
304 | #if QT_CONFIG(cursor) |
305 | if (auto cursorShapeManager = m_tabletSeat->seat()->mQDisplay->cursorShapeManager()) { |
306 | mCursor.shape.reset( |
307 | other: new QWaylandCursorShape(cursorShapeManager->get_tablet_tool_v2(object()))); |
308 | } |
309 | |
310 | mCursor.frameTimer.setSingleShot(true); |
311 | mCursor.frameTimer.callOnTimeout(args: this, args: [&]() { cursorTimerCallback(); }); |
312 | #endif |
313 | } |
314 | |
315 | QWaylandTabletToolV2::~QWaylandTabletToolV2() |
316 | { |
317 | destroy(); |
318 | } |
319 | |
320 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_type(uint32_t tool_type) |
321 | { |
322 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); |
323 | |
324 | switch (tool_type) { |
325 | case type_airbrush: |
326 | case type_brush: |
327 | case type_pencil: |
328 | case type_pen: |
329 | d->pointerType = QPointingDevice::PointerType::Pen; |
330 | break; |
331 | case type_eraser: |
332 | d->pointerType = QPointingDevice::PointerType::Eraser; |
333 | break; |
334 | case type_mouse: |
335 | case type_lens: |
336 | d->pointerType = QPointingDevice::PointerType::Cursor; |
337 | break; |
338 | case type_finger: |
339 | d->pointerType = QPointingDevice::PointerType::Unknown; |
340 | break; |
341 | } |
342 | |
343 | switch (tool_type) { |
344 | case type::type_airbrush: |
345 | d->deviceType = QInputDevice::DeviceType::Airbrush; |
346 | d->capabilities.setFlag(flag: QInputDevice::Capability::TangentialPressure); |
347 | break; |
348 | case type::type_brush: |
349 | case type::type_pencil: |
350 | case type::type_pen: |
351 | case type::type_eraser: |
352 | d->deviceType = QInputDevice::DeviceType::Stylus; |
353 | break; |
354 | case type::type_lens: |
355 | d->deviceType = QInputDevice::DeviceType::Puck; |
356 | break; |
357 | case type::type_mouse: |
358 | case type::type_finger: |
359 | d->deviceType = QInputDevice::DeviceType::Unknown; |
360 | break; |
361 | } |
362 | } |
363 | |
364 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_serial(uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) |
365 | { |
366 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); |
367 | d->uniqueId = QPointingDeviceUniqueId::fromNumericId(id: (quint64(hardware_serial_hi) << 32) + hardware_serial_lo); |
368 | } |
369 | |
370 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_hardware_id_wacom(uint32_t hardware_id_hi, uint32_t hardware_id_lo) |
371 | { |
372 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); |
373 | d->systemId = (quint64(hardware_id_hi) << 32) + hardware_id_lo; |
374 | } |
375 | |
376 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_capability(uint32_t capability) |
377 | { |
378 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); |
379 | switch (capability) { |
380 | case capability_tilt: |
381 | // no distinction... we have to assume it has both axes |
382 | d->capabilities.setFlag(flag: QInputDevice::Capability::XTilt); |
383 | d->capabilities.setFlag(flag: QInputDevice::Capability::YTilt); |
384 | break; |
385 | case capability_pressure: |
386 | d->capabilities.setFlag(flag: QInputDevice::Capability::Pressure); |
387 | break; |
388 | case capability_distance: |
389 | d->capabilities.setFlag(flag: QInputDevice::Capability::ZPosition); |
390 | break; |
391 | case capability_rotation: |
392 | d->capabilities.setFlag(flag: QInputDevice::Capability::Rotation); |
393 | break; |
394 | case capability_slider: |
395 | // nothing to represent that so far |
396 | break; |
397 | case capability_wheel: |
398 | d->capabilities.setFlag(flag: QInputDevice::Capability::Scroll); |
399 | d->capabilities.setFlag(flag: QInputDevice::Capability::PixelScroll); |
400 | break; |
401 | } |
402 | qCDebug(lcQpaInputDevices) << capability << "->" << this; |
403 | } |
404 | |
405 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_done() |
406 | { |
407 | QWindowSystemInterface::registerInputDevice(this); |
408 | } |
409 | |
410 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_removed() |
411 | { |
412 | m_tabletSeat->toolRemoved(tool: this); |
413 | } |
414 | |
415 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_in(uint32_t serial, zwp_tablet_v2 *tablet, wl_surface *surface) |
416 | { |
417 | Q_UNUSED(tablet); |
418 | |
419 | m_tabletSeat->seat()->mSerial = serial; |
420 | mEnterSerial = serial; |
421 | |
422 | if (Q_UNLIKELY(!surface)) { |
423 | qCDebug(lcQpaWayland) << "Ignoring zwp_tablet_tool_v2_proximity_v2 with no surface" ; |
424 | return; |
425 | } |
426 | m_pending.enteredSurface = true; |
427 | m_pending.proximitySurface = QWaylandSurface::fromWlSurface(surface); |
428 | |
429 | #if QT_CONFIG(cursor) |
430 | // Depends on mEnterSerial being updated |
431 | updateCursor(); |
432 | #endif |
433 | } |
434 | |
435 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_proximity_out() |
436 | { |
437 | m_pending.enteredSurface = false; |
438 | m_pending.proximitySurface = nullptr; |
439 | } |
440 | |
441 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_down(uint32_t serial) |
442 | { |
443 | m_pending.down = true; |
444 | |
445 | m_tabletSeat->seat()->mSerial = serial; |
446 | |
447 | if (m_pending.proximitySurface) { |
448 | if (QWaylandWindow *window = m_pending.proximitySurface->waylandWindow()) { |
449 | QWaylandInputDevice *seat = m_tabletSeat->seat(); |
450 | seat->display()->setLastInputDevice(device: seat, serial, window); |
451 | } |
452 | } |
453 | } |
454 | |
455 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_up() |
456 | { |
457 | m_pending.down = false; |
458 | } |
459 | |
460 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_motion(wl_fixed_t x, wl_fixed_t y) |
461 | { |
462 | m_pending.surfacePosition = QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)); |
463 | } |
464 | |
465 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_pressure(uint32_t pressure) |
466 | { |
467 | const int maxPressure = 65535; |
468 | m_pending.pressure = qreal(pressure)/maxPressure; |
469 | } |
470 | |
471 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_distance(uint32_t distance) |
472 | { |
473 | m_pending.distance = distance; |
474 | } |
475 | |
476 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_tilt(wl_fixed_t tilt_x, wl_fixed_t tilt_y) |
477 | { |
478 | m_pending.xTilt = wl_fixed_to_double(tilt_x); |
479 | m_pending.yTilt = wl_fixed_to_double(tilt_y); |
480 | } |
481 | |
482 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_rotation(wl_fixed_t degrees) |
483 | { |
484 | m_pending.rotation = wl_fixed_to_double(degrees); |
485 | } |
486 | |
487 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_slider(int32_t position) |
488 | { |
489 | m_pending.slider = qreal(position) / 65535; |
490 | } |
491 | |
492 | static Qt::MouseButton mouseButtonFromTablet(uint button) |
493 | { |
494 | switch (button) { |
495 | case 0x110: return Qt::MouseButton::LeftButton; // BTN_LEFT |
496 | case 0x14b: return Qt::MouseButton::MiddleButton; // BTN_STYLUS |
497 | case 0x14c: return Qt::MouseButton::RightButton; // BTN_STYLUS2 |
498 | default: |
499 | return Qt::NoButton; |
500 | } |
501 | } |
502 | |
503 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_button(uint32_t serial, uint32_t button, uint32_t state) |
504 | { |
505 | m_tabletSeat->seat()->mSerial = serial; |
506 | |
507 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(this); |
508 | Qt::MouseButton mouseButton = mouseButtonFromTablet(button); |
509 | if (state == button_state_pressed) |
510 | m_pending.buttons |= mouseButton; |
511 | else |
512 | m_pending.buttons &= ~mouseButton; |
513 | // ideally we'd get button count when the tool is discovered; seems to be a shortcoming in tablet-unstable-v2 |
514 | // but if we get events from buttons we didn't know existed, increase it |
515 | if (mouseButton == Qt::RightButton) |
516 | d->buttonCount = qMax(a: d->buttonCount, b: 2); |
517 | else if (mouseButton == Qt::MiddleButton) |
518 | d->buttonCount = qMax(a: d->buttonCount, b: 3); |
519 | } |
520 | |
521 | void QWaylandTabletToolV2::zwp_tablet_tool_v2_frame(uint32_t time) |
522 | { |
523 | if (!m_pending.proximitySurface) { |
524 | if (m_applied.enteredSurface) { |
525 | // leaving proximity |
526 | QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(nullptr, this, false); |
527 | m_pending = State(); // Don't leave pressure etc. lying around when we enter the next surface |
528 | m_applied = State(); |
529 | } else { |
530 | qCWarning(lcQpaWayland) << "Can't send tablet event with no proximity surface, ignoring" ; |
531 | } |
532 | return; |
533 | } |
534 | |
535 | QWaylandWindow *waylandWindow = QWaylandWindow::fromWlSurface(surface: m_pending.proximitySurface->object()); |
536 | QWindow *window = waylandWindow->window(); |
537 | |
538 | if (!m_applied.proximitySurface) { |
539 | // TODO get position etc. as below |
540 | QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, this, true); |
541 | m_applied.proximitySurface = m_pending.proximitySurface; |
542 | } |
543 | |
544 | if (!(m_pending == m_applied)) { |
545 | ulong timestamp = time; |
546 | const QPointF localPosition = waylandWindow->mapFromWlSurface(surfacePosition: m_pending.surfacePosition); |
547 | |
548 | QPointF delta = localPosition - localPosition.toPoint(); |
549 | QPointF globalPosition = window->mapToGlobal(pos: localPosition.toPoint()); |
550 | globalPosition += delta; |
551 | |
552 | Qt::MouseButtons buttons = m_pending.down ? Qt::MouseButton::LeftButton : Qt::MouseButton::NoButton; |
553 | buttons |= m_pending.buttons; |
554 | qreal pressure = m_pending.pressure; |
555 | int xTilt = int(m_pending.xTilt); |
556 | int yTilt = int(m_pending.yTilt); |
557 | qreal tangentialPressure = m_pending.slider; |
558 | qreal rotation = m_pending.rotation; |
559 | int z = int(m_pending.distance); |
560 | |
561 | // do not use localPosition here since that is in Qt window coordinates |
562 | // but we need surface coordinates to include the decoration |
563 | bool decorationHandledEvent = waylandWindow->handleTabletEventDecoration( |
564 | inputDevice: m_tabletSeat->seat(), local: m_pending.surfacePosition, |
565 | global: window->mapToGlobal(pos: m_pending.surfacePosition) + delta, buttons, |
566 | modifiers: m_tabletSeat->seat()->modifiers()); |
567 | |
568 | if (!decorationHandledEvent) { |
569 | QWindowSystemInterface::handleTabletEvent(window, timestamp, this, localPosition, globalPosition, |
570 | buttons, pressure, |
571 | xTilt, yTilt, tangentialPressure, rotation, z, |
572 | m_tabletSeat->seat()->modifiers()); |
573 | } |
574 | } |
575 | |
576 | m_applied = m_pending; |
577 | } |
578 | |
579 | // TODO: delete when upgrading to c++20 |
580 | bool QWaylandTabletToolV2::State::operator==(const QWaylandTabletToolV2::State &o) const { |
581 | return |
582 | down == o.down && |
583 | proximitySurface.data() == o.proximitySurface.data() && |
584 | enteredSurface == o.enteredSurface && |
585 | surfacePosition == o.surfacePosition && |
586 | distance == o.distance && |
587 | pressure == o.pressure && |
588 | rotation == o.rotation && |
589 | xTilt == o.xTilt && |
590 | yTilt == o.yTilt && |
591 | slider == o.slider && |
592 | buttons == o.buttons; |
593 | } |
594 | |
595 | QWaylandTabletPadV2::QWaylandTabletPadV2(::zwp_tablet_pad_v2 *pad) |
596 | : QPointingDevice(u"tablet touchpad"_s , -1, DeviceType::TouchPad, PointerType::Finger, |
597 | Capability::Position, |
598 | 1, 1) |
599 | , QtWayland::zwp_tablet_pad_v2(pad) |
600 | { |
601 | } |
602 | |
603 | QWaylandTabletPadV2::~QWaylandTabletPadV2() |
604 | { |
605 | destroy(); |
606 | } |
607 | |
608 | void QWaylandTabletPadV2::zwp_tablet_pad_v2_path(const QString &path) |
609 | { |
610 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(q: this); |
611 | d->busId = path; |
612 | } |
613 | |
614 | void QWaylandTabletPadV2::zwp_tablet_pad_v2_buttons(uint32_t buttons) |
615 | { |
616 | QPointingDevicePrivate *d = QPointingDevicePrivate::get(q: this); |
617 | d->buttonCount = buttons; |
618 | } |
619 | |
620 | void QWaylandTabletPadV2::zwp_tablet_pad_v2_group(zwp_tablet_pad_group_v2 *pad_group) |
621 | { |
622 | // As of writing Qt does not handle tablet pads group and the controls on it |
623 | // This proxy is server created so it is just deleted here to not leak it |
624 | zwp_tablet_pad_group_v2_destroy(pad_group); |
625 | } |
626 | |
627 | void QWaylandTabletPadV2::zwp_tablet_pad_v2_done() |
628 | { |
629 | QWindowSystemInterface::registerInputDevice(device: this); |
630 | } |
631 | |
632 | void QWaylandTabletPadV2::zwp_tablet_pad_v2_removed() |
633 | { |
634 | delete this; |
635 | } |
636 | |
637 | } // namespace QtWaylandClient |
638 | |
639 | QT_END_NAMESPACE |
640 | |
641 | #include "moc_qwaylandtabletv2_p.cpp" |
642 | |