1// Copyright (C) 2020 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 "qpointingdevice.h"
5#include "qpointingdevice_p.h"
6#include "qwindowsysteminterface_p.h"
7#include "qeventpoint_p.h"
8
9#include <QList>
10#include <QLoggingCategory>
11#include <QMutex>
12#include <QCoreApplication>
13
14#include <private/qdebug_p.h>
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20Q_LOGGING_CATEGORY(lcPointerGrab, "qt.pointer.grab");
21
22/*!
23 \class QPointingDevice
24 \brief The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
25 \since 6.0
26 \ingroup events
27 \inmodule QtGui
28
29 Each QPointerEvent contains a QPointingDevice pointer to allow accessing
30 device-specific properties like type and capabilities. It is the
31 responsibility of the platform or generic plug-ins to register the
32 available pointing devices via QWindowSystemInterface before generating any
33 pointer events. Applications do not need to instantiate this class, they
34 should just access the global instances pointed to by QPointerEvent::device().
35*/
36
37/*! \enum QInputDevice::DeviceType
38
39 This enum represents the type of device that generated a QPointerEvent.
40
41 \value Unknown
42 The device cannot be identified.
43
44 \value Mouse
45 A mouse.
46
47 \value TouchScreen
48 In this type of device, the touch surface and display are integrated.
49 This means the surface and display typically have the same size, such
50 that there is a direct relationship between the touch points' physical
51 positions and the coordinate reported by QEventPoint. As a
52 result, Qt allows the user to interact directly with multiple QWidgets,
53 QGraphicsItems, or Qt Quick Items at the same time.
54
55 \value TouchPad
56 In this type of device, the touch surface is separate from the display.
57 There is not a direct relationship between the physical touch location
58 and the on-screen coordinates. Instead, they are calculated relative to
59 the current mouse position, and the user must use the touch-pad to move
60 this reference point. Unlike touch-screens, Qt allows users to only
61 interact with a single QWidget or QGraphicsItem at a time.
62
63 \value Stylus
64 A pen-like device used on a graphics tablet such as a Wacom tablet,
65 or on a touchscreen that provides a separate stylus sensing capability.
66
67 \value Airbrush
68 A stylus with a thumbwheel to adjust
69 \l {QTabletEvent::tangentialPressure}{tangentialPressure}.
70
71 \value Puck
72 A device that is similar to a flat mouse with a transparent circle with
73 cross-hairs.
74
75 \value Keyboard
76 A keyboard.
77
78 \value AllDevices
79 Any of the above (used as a default filter value).
80*/
81
82/*! \enum QPointingDevice::PointerType
83
84 This enum represents what is interacting with the pointing device.
85
86 There is some redundancy between this property and \l {QInputDevice::DeviceType}.
87 For example, if a touchscreen is used, then the \c DeviceType is
88 \c TouchScreen and \c PointerType is \c Finger (always). But on a graphics
89 tablet, it's often possible for both ends of the stylus to be used, and
90 programs need to distinguish them. Therefore the concept is extended so
91 that every QPointerEvent has a PointerType, and it can simplify some event
92 handling code to ignore the DeviceType and react differently depending on
93 the PointerType alone.
94
95 Valid values are:
96
97 \value Unknown
98 The pointer type is unknown.
99 \value Generic
100 A mouse or something acting like a mouse (the core pointer on X11).
101 \value Finger
102 The user's finger.
103 \value Pen
104 The drawing end of a stylus.
105 \value Eraser
106 The other end of the stylus (if it has a virtual eraser on the other end).
107 \value Cursor
108 A transparent circle with cross-hairs as found on a
109 \l {QInputDevice::DeviceType}{Puck} device.
110 \value AllPointerTypes
111 Any of the above (used as a default filter value).
112*/
113
114/*! \enum QPointingDevice::GrabTransition
115
116 This enum represents a transition of exclusive or passive grab
117 from one object (possibly \c nullptr) to another (possibly \c nullptr).
118 It is emitted as an argument of the QPointingDevice::grabChanged() signal.
119
120 Valid values are:
121
122 \value GrabExclusive
123 Emitted after QPointerEvent::setExclusiveGrabber().
124 \value UngrabExclusive
125 Emitted after QPointerEvent::setExclusiveGrabber() when the grabber is
126 set to \c nullptr, to notify that the grab has terminated normally.
127 \value CancelGrabExclusive
128 Emitted after QPointerEvent::setExclusiveGrabber() when the grabber is set
129 to a different object, to notify that the old grabber's grab is "stolen".
130 \value GrabPassive
131 Emitted after QPointerEvent::addPassiveGrabber().
132 \value UngrabPassive
133 Emitted when a passive grab is terminated normally,
134 for example after QPointerEvent::removePassiveGrabber().
135 \value CancelGrabPassive
136 Emitted when a passive grab is terminated abnormally (a gesture is canceled).
137 \value OverrideGrabPassive
138 This value is not currently used.
139*/
140
141/*! \fn void QPointingDevice::grabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point) const
142
143 This signal is emitted when the \a grabber object gains or loses an
144 exclusive or passive grab of \a point during delivery of \a event.
145 The \a transition tells what happened, from the perspective of the
146 \c grabber object.
147
148 \note A grab transition from one object to another results in two signals,
149 to notify that one object has lost its grab, and to notify that there is
150 another grabber. In other cases, when transitioning to or from a non-grabbing
151 state, only one signal is emitted: the \a grabber argument is never \c nullptr.
152
153 \sa QPointerEvent::setExclusiveGrabber(), QPointerEvent::addPassiveGrabber(), QPointerEvent::removePassiveGrabber()
154*/
155
156/*!
157 Creates a new invalid pointing device instance as a child of \a parent.
158*/
159QPointingDevice::QPointingDevice(QObject *parent)
160 : QInputDevice(*(new QPointingDevicePrivate("unknown"_L1, -1,
161 DeviceType::Unknown, PointerType::Unknown,
162 Capability::None, 0, 0)), parent)
163{
164}
165
166QPointingDevice::~QPointingDevice()
167{
168}
169
170/*!
171 Creates a new pointing device instance with the given
172 \a name, \a deviceType, \a pointerType, \a capabilities, \a maxPoints,
173 \a buttonCount, \a seatName, \a uniqueId and \a parent.
174*/
175QPointingDevice::QPointingDevice(const QString &name, qint64 id, QInputDevice::DeviceType deviceType,
176 QPointingDevice::PointerType pointerType, Capabilities capabilities, int maxPoints, int buttonCount,
177 const QString &seatName, QPointingDeviceUniqueId uniqueId, QObject *parent)
178 : QInputDevice(*(new QPointingDevicePrivate(name, id, deviceType, pointerType, capabilities, maxPoints, buttonCount, seatName, uniqueId)), parent)
179{
180}
181
182/*!
183 \internal
184*/
185QPointingDevice::QPointingDevice(QPointingDevicePrivate &d, QObject *parent)
186 : QInputDevice(d, parent)
187{
188}
189
190#if QT_DEPRECATED_SINCE(6, 0)
191/*!
192 \internal
193 \deprecated [6.0] Please use the constructor rather than setters.
194
195 Sets the device type \a devType and infers the pointer type.
196*/
197void QPointingDevice::setType(DeviceType devType)
198{
199 Q_D(QPointingDevice);
200 d->deviceType = devType;
201 if (d->pointerType == PointerType::Unknown)
202 switch (devType) {
203 case DeviceType::Mouse:
204 d->pointerType = PointerType::Generic;
205 break;
206 case DeviceType::TouchScreen:
207 case DeviceType::TouchPad:
208 d->pointerType = PointerType::Finger;
209 break;
210 case DeviceType::Puck:
211 d->pointerType = PointerType::Cursor;
212 break;
213 case DeviceType::Stylus:
214 case DeviceType::Airbrush:
215 d->pointerType = PointerType::Pen;
216 break;
217 default:
218 break;
219 }
220}
221
222/*!
223 \internal
224 \deprecated [6.0] Please use the constructor rather than setters.
225*/
226void QPointingDevice::setCapabilities(QInputDevice::Capabilities caps)
227{
228 Q_D(QPointingDevice);
229 d->capabilities = caps;
230}
231
232/*!
233 \internal
234 \deprecated [6.0] Please use the constructor rather than setters.
235*/
236void QPointingDevice::setMaximumTouchPoints(int c)
237{
238 Q_D(QPointingDevice);
239 d->maximumTouchPoints = c;
240}
241#endif // QT_DEPRECATED_SINCE(6, 0)
242
243/*!
244 Returns the pointer type.
245*/
246QPointingDevice::PointerType QPointingDevice::pointerType() const
247{
248 Q_D(const QPointingDevice);
249 return d->pointerType;
250}
251
252/*!
253 Returns the maximum number of simultaneous touch points (fingers) that
254 can be detected.
255*/
256int QPointingDevice::maximumPoints() const
257{
258 Q_D(const QPointingDevice);
259 return d->maximumTouchPoints;
260}
261
262/*!
263 Returns the maximum number of on-device buttons that can be detected.
264*/
265int QPointingDevice::buttonCount() const
266{
267 Q_D(const QPointingDevice);
268 return d->buttonCount;
269}
270
271/*!
272 Returns a unique ID (of dubious utility) for the device.
273
274 You probably should rather be concerned with QPointerEventPoint::uniqueId().
275*/
276QPointingDeviceUniqueId QPointingDevice::uniqueId() const
277{
278 Q_D(const QPointingDevice);
279 return d->uniqueId;
280}
281
282/*!
283 Returns the primary pointing device (the core pointer, traditionally
284 assumed to be a mouse) on the given seat \a seatName.
285
286 If multiple pointing devices are registered, this function prefers a mouse
287 or touchpad that matches the given \a seatName and that does not have
288 another device as its parent. Usually only one master or core device does
289 not have a parent device. But if such a device is not found, this function
290 creates a new virtual "core pointer" mouse. Thus Qt continues to work on
291 platforms that are not yet doing input device discovery and registration.
292*/
293const QPointingDevice *QPointingDevice::primaryPointingDevice(const QString& seatName)
294{
295 const auto v = devices();
296 const QPointingDevice *mouse = nullptr;
297 const QPointingDevice *touchpad = nullptr;
298 for (const QInputDevice *dev : v) {
299 if (!seatName.isNull() && dev->seatName() != seatName)
300 continue;
301 if (dev->type() == QInputDevice::DeviceType::Mouse) {
302 if (!mouse)
303 mouse = static_cast<const QPointingDevice *>(dev);
304 // the core pointer is likely a mouse, and its parent is not another input device
305 if (!mouse->parent() || !qobject_cast<const QInputDevice *>(object: mouse->parent()))
306 return mouse;
307 } else if (dev->type() == QInputDevice::DeviceType::TouchPad) {
308 if (!touchpad || !dev->parent() || dev->parent()->metaObject() != dev->metaObject())
309 touchpad = static_cast<const QPointingDevice *>(dev);
310 }
311 }
312 if (!mouse && !touchpad) {
313 qCDebug(lcQpaInputDevices) << "no mouse-like devices registered for seat" << seatName
314 << "The platform plugin should have provided one via "
315 "QWindowSystemInterface::registerInputDevice(). Creating a default mouse for now.";
316 mouse = new QPointingDevice("core pointer"_L1, 1, DeviceType::Mouse,
317 PointerType::Generic, Capability::Position, 1, 3, seatName,
318 QPointingDeviceUniqueId(), QCoreApplication::instance());
319 QInputDevicePrivate::registerDevice(dev: mouse);
320 return mouse;
321 }
322 if (v.size() > 1)
323 qCDebug(lcQpaInputDevices) << "core pointer ambiguous for seat" << seatName;
324 if (mouse)
325 return mouse;
326 return touchpad;
327}
328
329QPointingDevicePrivate::~QPointingDevicePrivate()
330 = default;
331
332/*!
333 \internal
334 Finds the device instance belonging to the drawing or eraser end of a particular stylus,
335 identified by its \a deviceType, \a pointerType, \a uniqueId and \a systemId.
336 Returns the device found, or \c nullptr if none was found.
337
338 If \a systemId is \c 0, it's not significant for the search.
339
340 If an instance matching the given \a deviceType and \a pointerType but with
341 only a default-constructed \c uniqueId is found, it will be assumed to be
342 the one we're looking for, its \c uniqueId will be updated to match the
343 given \a uniqueId, and its \c capabilities will be updated to match the
344 given \a capabilities. This is for the benefit of any platform plugin that can
345 discover the tablet itself at startup, along with the supported stylus types,
346 but then discovers specific styli later on as they come into proximity.
347*/
348const QPointingDevice *QPointingDevicePrivate::queryTabletDevice(QInputDevice::DeviceType deviceType,
349 QPointingDevice::PointerType pointerType,
350 QPointingDeviceUniqueId uniqueId,
351 QPointingDevice::Capabilities capabilities,
352 qint64 systemId)
353{
354 const auto &devices = QInputDevice::devices();
355 for (const QInputDevice *dev : devices) {
356 if (dev->type() < QPointingDevice::DeviceType::Puck || dev->type() > QPointingDevice::DeviceType::Airbrush)
357 continue;
358 const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
359 const auto devPriv = QPointingDevicePrivate::get(q: pdev);
360 bool uniqueIdDiscovered = (devPriv->uniqueId.numericId() == 0 && uniqueId.numericId() != 0);
361 if (devPriv->deviceType == deviceType && devPriv->pointerType == pointerType &&
362 (!systemId || devPriv->systemId == systemId) &&
363 (devPriv->uniqueId == uniqueId || uniqueIdDiscovered)) {
364 if (uniqueIdDiscovered) {
365 const_cast<QPointingDevicePrivate *>(devPriv)->uniqueId = uniqueId;
366 if (capabilities)
367 const_cast<QPointingDevicePrivate *>(devPriv)->capabilities = capabilities;
368 qCDebug(lcQpaInputDevices) << "discovered unique ID and capabilities of tablet tool" << pdev;
369 }
370 return pdev;
371 }
372 }
373 return nullptr;
374}
375
376/*!
377 \internal
378 Finds the device instance identified by its \a systemId.
379 Returns the device found, or \c nullptr if none was found.
380*/
381const QPointingDevice *QPointingDevicePrivate::pointingDeviceById(qint64 systemId)
382{
383 const auto &devices = QInputDevice::devices();
384 for (const QInputDevice *dev : devices) {
385 if (dev->type() >= QPointingDevice::DeviceType::Keyboard)
386 continue;
387 const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
388 const auto devPriv = QPointingDevicePrivate::get(q: pdev);
389 if (devPriv->systemId == systemId)
390 return pdev;
391 }
392 return nullptr;
393}
394
395/*!
396 \internal
397 First, ensure that the \a cancelEvent's QTouchEvent::points() list contains
398 all points that have exclusive grabs. Then send the event to each object
399 that has an exclusive grab of any of the points.
400*/
401void QPointingDevicePrivate::sendTouchCancelEvent(QTouchEvent *cancelEvent)
402{
403 // An incoming TouchCancel event will typically not contain any points, but
404 // QQuickPointerHandler::onGrabChanged needs to be called for each point
405 // that has an exclusive grabber. Adding those points to the event makes it
406 // an easy iteration there.
407 if (cancelEvent->points().isEmpty()) {
408 for (auto &epd : activePoints.values()) {
409 if (epd.exclusiveGrabber)
410 QMutableTouchEvent::from(e: cancelEvent)->addPoint(point: epd.eventPoint);
411 }
412 }
413 for (auto &epd : activePoints.values()) {
414 if (epd.exclusiveGrabber)
415 QCoreApplication::sendEvent(receiver: epd.exclusiveGrabber, event: cancelEvent);
416 // The next touch event can only be a TouchBegin, so clean up.
417 cancelEvent->setExclusiveGrabber(point: epd.eventPoint, exclusiveGrabber: nullptr);
418 cancelEvent->clearPassiveGrabbers(point: epd.eventPoint);
419 }
420}
421
422/*! \internal
423 Returns the active EventPointData instance with the given \a id, if available,
424 or \c nullptr if not.
425*/
426QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::queryPointById(int id) const
427{
428 auto it = activePoints.find(key: id);
429 if (it == activePoints.end())
430 return nullptr;
431 return &it.value();
432}
433
434/*! \internal
435 Returns the active EventPointData instance with the given \a id, if available;
436 if not, appends a new instance and returns it.
437*/
438QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::pointById(int id) const
439{
440 const auto [it, inserted] = activePoints.try_emplace(key: id);
441 if (inserted) {
442 Q_Q(const QPointingDevice);
443 auto &epd = it.value();
444 QMutableEventPoint::setId(p&: epd.eventPoint, arg: id);
445 QMutableEventPoint::setDevice(p&: epd.eventPoint, arg: q);
446 }
447 return &it.value();
448}
449
450/*! \internal
451 Remove the active EventPointData instance with the given \a id.
452*/
453void QPointingDevicePrivate::removePointById(int id)
454{
455 activePoints.remove(key: id);
456}
457
458/*!
459 \internal
460 Find the first non-null target (widget) via QMutableEventPoint::target()
461 in the active points. This is the widget that will receive any event that
462 comes from a touchpad, even if some of the touchpoints fall spatially on
463 other windows.
464*/
465QObject *QPointingDevicePrivate::firstActiveTarget() const
466{
467 for (auto &pt : activePoints.values()) {
468 if (auto target = QMutableEventPoint::target(p: pt.eventPoint))
469 return target;
470 }
471 return nullptr;
472}
473
474/*! \internal
475 Find the first non-null QWindow instance via QMutableEventPoint::window()
476 in the active points. This is the window that will receive any event that
477 comes from a touchpad, even if some of the touchpoints fall spatially on
478 other windows.
479*/
480QWindow *QPointingDevicePrivate::firstActiveWindow() const
481{
482 for (auto &pt : activePoints.values()) {
483 if (auto window = QMutableEventPoint::window(p: pt.eventPoint))
484 return window;
485 }
486 return nullptr;
487}
488
489/*! \internal
490 Return the exclusive grabber of the first point in activePoints.
491 This is mainly for autotests that try to verify the "current" grabber
492 outside the context of event delivery, which is something that the rest
493 of the codebase should not be doing.
494*/
495QObject *QPointingDevicePrivate::firstPointExclusiveGrabber() const
496{
497 if (activePoints.isEmpty())
498 return nullptr;
499 return activePoints.values().first().exclusiveGrabber;
500}
501
502void QPointingDevicePrivate::setExclusiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *exclusiveGrabber)
503{
504 Q_Q(QPointingDevice);
505 auto persistentPoint = queryPointById(id: point.id());
506 if (!persistentPoint) {
507 qWarning() << "point is not in activePoints" << point;
508 return;
509 }
510 Q_ASSERT(persistentPoint->eventPoint.id() == point.id());
511 if (persistentPoint->exclusiveGrabber == exclusiveGrabber)
512 return;
513 auto oldGrabber = persistentPoint->exclusiveGrabber;
514 persistentPoint->exclusiveGrabber = exclusiveGrabber;
515 if (oldGrabber)
516 emit q->grabChanged(grabber: oldGrabber, transition: exclusiveGrabber ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
517 event, point: persistentPoint->eventPoint);
518 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
519 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
520 << "@" << point.scenePosition()
521 << ": grab" << oldGrabber << "->" << exclusiveGrabber;
522 }
523 QMutableEventPoint::setGlobalGrabPosition(p&: persistentPoint->eventPoint, arg: point.globalPosition());
524 if (exclusiveGrabber)
525 emit q->grabChanged(grabber: exclusiveGrabber, transition: QPointingDevice::GrabExclusive, event, point);
526 else
527 persistentPoint->exclusiveGrabberContext.clear();
528}
529
530/*!
531 \internal
532 Call QEventPoint::setExclusiveGrabber(nullptr) on each active point that has a grabber.
533*/
534bool QPointingDevicePrivate::removeExclusiveGrabber(const QPointerEvent *event, const QObject *grabber)
535{
536 bool ret = false;
537 for (auto &pt : activePoints.values()) {
538 if (pt.exclusiveGrabber == grabber) {
539 setExclusiveGrabber(event, point: pt.eventPoint, exclusiveGrabber: nullptr);
540 ret = true;
541 }
542 }
543 return ret;
544}
545
546bool QPointingDevicePrivate::addPassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
547{
548 Q_Q(QPointingDevice);
549 auto persistentPoint = queryPointById(id: point.id());
550 if (!persistentPoint) {
551 qWarning() << "point is not in activePoints" << point;
552 return false;
553 }
554 if (persistentPoint->passiveGrabbers.contains(t: grabber))
555 return false;
556 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
557 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
558 << ": grab (passive)" << grabber;
559 }
560 persistentPoint->passiveGrabbers << grabber;
561 emit q->grabChanged(grabber, transition: QPointingDevice::GrabPassive, event, point);
562 return true;
563}
564
565bool QPointingDevicePrivate::setPassiveGrabberContext(QPointingDevicePrivate::EventPointData *epd, QObject *grabber, QObject *context)
566{
567 qsizetype i = epd->passiveGrabbers.indexOf(t: grabber);
568 if (i < 0)
569 return false;
570 if (epd->passiveGrabbersContext.size() <= i)
571 epd->passiveGrabbersContext.resize(size: i + 1);
572 epd->passiveGrabbersContext[i] = context;
573 return true;
574}
575
576bool QPointingDevicePrivate::removePassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
577{
578 Q_Q(QPointingDevice);
579 auto persistentPoint = queryPointById(id: point.id());
580 if (!persistentPoint) {
581 qWarning() << "point is not in activePoints" << point;
582 return false;
583 }
584 qsizetype i = persistentPoint->passiveGrabbers.indexOf(t: grabber);
585 if (i >= 0) {
586 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
587 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
588 << ": removing passive grabber" << grabber;
589 }
590 emit q->grabChanged(grabber, transition: QPointingDevice::UngrabPassive, event, point);
591 persistentPoint->passiveGrabbers.removeAt(i);
592 if (persistentPoint->passiveGrabbersContext.size()) {
593 Q_ASSERT(persistentPoint->passiveGrabbersContext.size() > i);
594 persistentPoint->passiveGrabbersContext.removeAt(i);
595 }
596 return true;
597 }
598 return false;
599}
600
601void QPointingDevicePrivate::clearPassiveGrabbers(const QPointerEvent *event, const QEventPoint &point)
602{
603 Q_Q(QPointingDevice);
604 auto persistentPoint = queryPointById(id: point.id());
605 if (!persistentPoint) {
606 qWarning() << "point is not in activePoints" << point;
607 return;
608 }
609 if (persistentPoint->passiveGrabbers.isEmpty())
610 return;
611 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
612 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
613 << ": clearing" << persistentPoint->passiveGrabbers;
614 }
615 for (auto g : persistentPoint->passiveGrabbers)
616 emit q->grabChanged(grabber: g, transition: QPointingDevice::UngrabPassive, event, point);
617 persistentPoint->passiveGrabbers.clear();
618 persistentPoint->passiveGrabbersContext.clear();
619}
620
621/*!
622 \internal
623 Removes the given \a grabber as both passive and exclusive grabber from all
624 points in activePoints where it's currently found. If \a cancel is \c true,
625 the transition emitted from the grabChanged() signal will be
626 \c CancelGrabExclusive or \c CancelGrabPassive. Otherwise it will be
627 \c UngrabExclusive or \c UngrabPassive.
628
629 \note This function provides a way to work around the limitation that we
630 normally change grabbers only during event delivery; but it's also more expensive.
631*/
632void QPointingDevicePrivate::removeGrabber(QObject *grabber, bool cancel)
633{
634 Q_Q(QPointingDevice);
635 for (auto ap : activePoints) {
636 auto &epd = ap.second;
637 if (epd.exclusiveGrabber.data() == grabber) {
638 qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
639 << "@" << epd.eventPoint.scenePosition()
640 << ": grab" << grabber << "-> nullptr";
641 epd.exclusiveGrabber.clear();
642 epd.exclusiveGrabberContext.clear();
643 emit q->grabChanged(grabber,
644 transition: cancel ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
645 event: nullptr, point: epd.eventPoint);
646 }
647 qsizetype pi = epd.passiveGrabbers.indexOf(t: grabber);
648 if (pi >= 0) {
649 qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
650 << ": removing passive grabber" << grabber;
651 epd.passiveGrabbers.removeAt(i: pi);
652 if (epd.passiveGrabbersContext.size()) {
653 Q_ASSERT(epd.passiveGrabbersContext.size() > pi);
654 epd.passiveGrabbersContext.removeAt(i: pi);
655 }
656 emit q->grabChanged(grabber,
657 transition: cancel ? QPointingDevice::CancelGrabPassive : QPointingDevice::UngrabPassive,
658 event: nullptr, point: epd.eventPoint);
659 }
660 }
661}
662
663/*!
664 \internal
665 Finds the device instance belonging to the drawing or eraser end of a particular stylus,
666 identified by its \a deviceType, \a pointerType and \a uniqueId. If an existing device
667 is not found, a new one is created and registered, with a warning.
668
669 This function is called from QWindowSystemInterface. Platform plugins should use
670 \l queryTabletDeviceInstance() to check whether a tablet stylus coming into proximity
671 is previously known; if not known, the plugin should create and register the stylus.
672*/
673const QPointingDevice *QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType deviceType,
674 QPointingDevice::PointerType pointerType,
675 QPointingDeviceUniqueId uniqueId)
676{
677 const QPointingDevice *dev = queryTabletDevice(deviceType, pointerType, uniqueId);
678 if (!dev) {
679 qCDebug(lcQpaInputDevices) << "failed to find registered tablet device"
680 << deviceType << pointerType << Qt::hex << uniqueId.numericId()
681 << "The platform plugin should have provided one via "
682 "QWindowSystemInterface::registerInputDevice(). Creating a default one for now.";
683 dev = new QPointingDevice("fake tablet"_L1, 2, deviceType, pointerType,
684 QInputDevice::Capability::Position | QInputDevice::Capability::Pressure,
685 1, 1, QString(), uniqueId, QCoreApplication::instance());
686 QInputDevicePrivate::registerDevice(dev);
687 }
688 return dev;
689}
690
691bool QPointingDevice::operator==(const QPointingDevice &other) const
692{
693 // Wacom tablets generate separate instances for each end of each stylus;
694 // QInputDevice::operator==() says they are all the same, but we use
695 // the stylus unique serial number and pointerType to distinguish them
696 return QInputDevice::operator==(other) &&
697 pointerType() == other.pointerType() &&
698 uniqueId() == other.uniqueId();
699}
700
701#ifndef QT_NO_DEBUG_STREAM
702QDebug operator<<(QDebug debug, const QPointingDevice *device)
703{
704 QDebugStateSaver saver(debug);
705 debug.nospace();
706 debug.noquote();
707 debug << "QPointingDevice(";
708 if (device) {
709 debug << '"' << device->name() << "\" ";
710 QtDebugUtils::formatQEnum(debug, value: device->type());
711 debug << " id=" << device->systemId();
712 if (!device->seatName().isEmpty())
713 debug << " seat=" << device->seatName();
714 if (device->pointerType() != QPointingDevice::PointerType::Generic) {
715 debug << " ptrType=";
716 QtDebugUtils::formatQEnum(debug, value: device->pointerType());
717 }
718 if (int(device->capabilities()) != int(QInputDevice::Capability::Position)) {
719 debug << " caps=";
720 QtDebugUtils::formatQFlags(debug, value: device->capabilities());
721 }
722 if (device->maximumPoints() > 1)
723 debug << " maxPts=" << device->maximumPoints();
724 if (device->uniqueId().isValid())
725 debug << " uniqueId=" << Qt::hex << device->uniqueId().numericId() << Qt::dec;
726 } else {
727 debug << '0';
728 }
729 debug << ')';
730 return debug;
731}
732#endif // !QT_NO_DEBUG_STREAM
733
734/*!
735 \class QPointingDeviceUniqueId
736 \since 5.8
737 \ingroup events
738 \inmodule QtGui
739
740 \brief QPointingDeviceUniqueId identifies a unique object, such as a tagged token
741 or stylus, which is used with a pointing device.
742
743 QPointingDeviceUniqueIds can be compared for equality, and can be used as keys in a QHash.
744 You get access to the numerical ID via numericId(), if the device supports such IDs.
745 For future extensions, though, you should not use that function, but compare objects
746 of this type using the equality operator.
747
748 This class is a thin wrapper around an integer ID. You pass it into and out of
749 functions by value.
750
751 \sa QEventPoint
752*/
753
754/*!
755 \fn QPointingDeviceUniqueId::QPointingDeviceUniqueId()
756 Constructs an invalid unique pointer ID.
757*/
758
759/*!
760 Constructs a unique pointer ID from numeric ID \a id.
761*/
762QPointingDeviceUniqueId QPointingDeviceUniqueId::fromNumericId(qint64 id)
763{
764 QPointingDeviceUniqueId result;
765 result.m_numericId = id;
766 return result;
767}
768
769/*!
770 \fn bool QPointingDeviceUniqueId::isValid() const
771
772 Returns whether this unique pointer ID is valid, that is, it represents an actual
773 pointer.
774*/
775
776/*!
777 \property QPointingDeviceUniqueId::numericId
778 \brief the numeric unique ID of the token represented by a touchpoint
779
780 If the device provides a numeric ID, isValid() returns true, and this
781 property provides the numeric ID;
782 otherwise it is -1.
783
784 You should not use the value of this property in portable code, but
785 instead rely on equality to identify pointers.
786
787 \sa isValid()
788*/
789qint64 QPointingDeviceUniqueId::numericId() const noexcept
790{
791 return m_numericId;
792}
793
794/*!
795 \fn bool QPointingDeviceUniqueId::operator==(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs)
796 \since 5.8
797
798 Returns whether the two unique pointer IDs \a lhs and \a rhs identify the same pointer
799 (\c true) or not (\c false).
800*/
801
802/*!
803 \fn bool QPointingDeviceUniqueId::operator!=(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs)
804 \since 5.8
805
806 Returns whether the two unique pointer IDs \a lhs and \a rhs identify different pointers
807 (\c true) or not (\c false).
808*/
809
810/*!
811 \relates QPointingDeviceUniqueId
812 \since 5.8
813
814 Returns the hash value for \a key, using \a seed to seed the calculation.
815*/
816size_t qHash(QPointingDeviceUniqueId key, size_t seed) noexcept
817{
818 return qHash(key: key.numericId(), seed);
819}
820
821QT_END_NAMESPACE
822
823#include "moc_qpointingdevice.cpp"
824

source code of qtbase/src/gui/kernel/qpointingdevice.cpp