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