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