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