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