1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qpointingdevice.h"
41#include "qpointingdevice_p.h"
42#include <QList>
43#include <QLoggingCategory>
44#include <QMutex>
45#include <QCoreApplication>
46
47#include <private/qdebug_p.h>
48
49QT_BEGIN_NAMESPACE
50
51Q_DECLARE_LOGGING_CATEGORY(lcQpaInputDevices)
52Q_DECLARE_LOGGING_CATEGORY(lcPointerGrab)
53
54/*!
55 \class QPointingDevice
56 \brief The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
57 \since 6.0
58 \ingroup events
59 \inmodule QtGui
60
61 Each QPointerEvent contains a QPointingDevice pointer to allow accessing
62 device-specific properties like type and capabilities. It is the
63 responsibility of the platform or generic plug-ins to register the
64 available pointing devices via QWindowSystemInterface before generating any
65 pointer events. Applications do not need to instantiate this class, they
66 should just access the global instances pointed to by QPointerEvent::device().
67*/
68
69/*! \enum QInputDevice::DeviceType
70
71 This enum represents the type of device that generated a QPointerEvent.
72
73 \value Unknown
74 The device cannot be identified.
75
76 \value Mouse
77 A mouse.
78
79 \value TouchScreen
80 In this type of device, the touch surface and display are integrated.
81 This means the surface and display typically have the same size, such
82 that there is a direct relationship between the touch points' physical
83 positions and the coordinate reported by QEventPoint. As a
84 result, Qt allows the user to interact directly with multiple QWidgets,
85 QGraphicsItems, or Qt Quick Items at the same time.
86
87 \value TouchPad
88 In this type of device, the touch surface is separate from the display.
89 There is not a direct relationship between the physical touch location
90 and the on-screen coordinates. Instead, they are calculated relative to
91 the current mouse position, and the user must use the touch-pad to move
92 this reference point. Unlike touch-screens, Qt allows users to only
93 interact with a single QWidget or QGraphicsItem at a time.
94
95 \value Stylus
96 A pen-like device used on a graphics tablet such as a Wacom tablet,
97 or on a touchscreen that provides a separate stylus sensing capability.
98
99 \value Airbrush
100 A stylus with a thumbwheel to adjust
101 \l {QTabletEvent::tangentialPressure}{tangentialPressure}.
102
103 \value Puck
104 A device that is similar to a flat mouse with a transparent circle with
105 cross-hairs.
106
107 \value Keyboard
108 A keyboard.
109
110 \value AllDevices
111 Any of the above (used as a default filter value).
112*/
113
114/*! \enum QPointingDevice::PointerType
115
116 This enum represents what is interacting with the pointing device.
117
118 There is some redundancy between this property and \l {QInputDevice::DeviceType}.
119 For example, if a touchscreen is used, then the \c DeviceType is
120 \c TouchScreen and \c PointerType is \c Finger (always). But on a graphics
121 tablet, it's often possible for both ends of the stylus to be used, and
122 programs need to distinguish them. Therefore the concept is extended so
123 that every QPointerEvent has a PointerType, and it can simplify some event
124 handling code to ignore the DeviceType and react differently depending on
125 the PointerType alone.
126
127 Valid values are:
128
129 \value Unknown
130 The pointer type is unknown.
131 \value Generic
132 A mouse or something acting like a mouse (the core pointer on X11).
133 \value Finger
134 The user's finger.
135 \value Pen
136 The drawing end of a stylus.
137 \value Eraser
138 The other end of the stylus (if it has a virtual eraser on the other end).
139 \value Cursor
140 A transparent circle with cross-hairs as found on a
141 \l {QInputDevice::DeviceType}{Puck} device.
142 \value AllPointerTypes
143 Any of the above (used as a default filter value).
144*/
145
146/*!
147 Creates a new invalid pointing device instance.
148*/
149QPointingDevice::QPointingDevice()
150 : QInputDevice(*(new QPointingDevicePrivate(QLatin1String("unknown"), -1,
151 DeviceType::Unknown, PointerType::Unknown,
152 Capability::None, 0, 0)))
153{
154}
155
156QPointingDevice::~QPointingDevice()
157{
158}
159
160/*!
161 Creates a new pointing device instance with the given
162 \a name, \a deviceType, \a pointerType, \a capabilities, \a maxPoints,
163 \a buttonCount, \a seatName, \a uniqueId and \a parent.
164*/
165QPointingDevice::QPointingDevice(const QString &name, qint64 id, QInputDevice::DeviceType deviceType,
166 QPointingDevice::PointerType pointerType, Capabilities capabilities, int maxPoints, int buttonCount,
167 const QString &seatName, QPointingDeviceUniqueId uniqueId, QObject *parent)
168 : QInputDevice(*(new QPointingDevicePrivate(name, id, deviceType, pointerType, capabilities, maxPoints, buttonCount, seatName, uniqueId)), parent)
169{
170}
171
172/*!
173 \internal
174*/
175QPointingDevice::QPointingDevice(QPointingDevicePrivate &d, QObject *parent)
176 : QInputDevice(d, parent)
177{
178}
179
180/*!
181 \internal
182 \deprecated Please use the constructor rather than setters.
183
184 Sets the device type \a devType and infers the pointer type.
185*/
186void QPointingDevice::setType(DeviceType devType)
187{
188 Q_D(QPointingDevice);
189 d->deviceType = devType;
190 if (d->pointerType == PointerType::Unknown)
191 switch (devType) {
192 case DeviceType::Mouse:
193 d->pointerType = PointerType::Generic;
194 break;
195 case DeviceType::TouchScreen:
196 case DeviceType::TouchPad:
197 d->pointerType = PointerType::Finger;
198 break;
199 case DeviceType::Puck:
200 d->pointerType = PointerType::Cursor;
201 break;
202 case DeviceType::Stylus:
203 case DeviceType::Airbrush:
204 d->pointerType = PointerType::Pen;
205 break;
206 default:
207 break;
208 }
209}
210
211/*!
212 \internal
213 \deprecated Please use the constructor rather than setters.
214*/
215void QPointingDevice::setCapabilities(QInputDevice::Capabilities caps)
216{
217 Q_D(QPointingDevice);
218 d->capabilities = caps;
219}
220
221/*!
222 \internal
223 \deprecated Please use the constructor rather than setters.
224*/
225void QPointingDevice::setMaximumTouchPoints(int c)
226{
227 Q_D(QPointingDevice);
228 d->maximumTouchPoints = c;
229}
230
231/*!
232 Returns the pointer type.
233*/
234QPointingDevice::PointerType QPointingDevice::pointerType() const
235{
236 Q_D(const QPointingDevice);
237 return d->pointerType;
238}
239
240/*!
241 Returns the maximum number of simultaneous touch points (fingers) that
242 can be detected.
243*/
244int QPointingDevice::maximumPoints() const
245{
246 Q_D(const QPointingDevice);
247 return d->maximumTouchPoints;
248}
249
250/*!
251 Returns the maximum number of on-device buttons that can be detected.
252*/
253int QPointingDevice::buttonCount() const
254{
255 Q_D(const QPointingDevice);
256 return d->buttonCount;
257}
258
259/*!
260 Returns a unique ID (of dubious utility) for the device.
261
262 You probably should rather be concerned with QPointerEventPoint::uniqueId().
263*/
264QPointingDeviceUniqueId QPointingDevice::uniqueId() const
265{
266 Q_D(const QPointingDevice);
267 return d->uniqueId;
268}
269
270/*!
271 Returns the primary pointing device (the core pointer, traditionally
272 assumed to be a mouse) on the given seat \a seatName.
273
274 If multiple pointing devices are registered, this function prefers a mouse
275 or touchpad that matches the given \a seatName and that does not have
276 another device as its parent. Usually only one master or core device does
277 not have a parent device. But if such a device is not found, this function
278 creates a new virtual "core pointer" mouse. Thus Qt continues to work on
279 platforms that are not yet doing input device discovery and registration.
280*/
281const QPointingDevice *QPointingDevice::primaryPointingDevice(const QString& seatName)
282{
283 const auto v = devices();
284 const QPointingDevice *mouse = nullptr;
285 const QPointingDevice *touchpad = nullptr;
286 for (const QInputDevice *dev : v) {
287 if (dev->seatName() != seatName)
288 continue;
289 if (dev->type() == QInputDevice::DeviceType::Mouse) {
290 if (!mouse)
291 mouse = static_cast<const QPointingDevice *>(dev);
292 // the core pointer is likely a mouse, and its parent is not another input device
293 if (!mouse->parent() || !qobject_cast<const QInputDevice *>(mouse->parent()))
294 return mouse;
295 } else if (dev->type() == QInputDevice::DeviceType::TouchPad) {
296 if (!touchpad || !dev->parent() || dev->parent()->metaObject() != dev->metaObject())
297 touchpad = static_cast<const QPointingDevice *>(dev);
298 }
299 }
300 if (!mouse && !touchpad) {
301 qCDebug(lcQpaInputDevices) << "no mouse-like devices registered for seat" << seatName
302 << "The platform plugin should have provided one via "
303 "QWindowSystemInterface::registerInputDevice(). Creating a default mouse for now.";
304 mouse = new QPointingDevice(QLatin1String("core pointer"), 1, DeviceType::Mouse,
305 PointerType::Generic, Capability::Position, 1, 3, seatName,
306 QPointingDeviceUniqueId(), QCoreApplication::instance());
307 QInputDevicePrivate::registerDevice(mouse);
308 return mouse;
309 }
310 if (v.length() > 1)
311 qCDebug(lcQpaInputDevices) << "core pointer ambiguous for seat" << seatName;
312 if (mouse)
313 return mouse;
314 return touchpad;
315}
316
317/*!
318 \internal
319 Finds the device instance belonging to the drawing or eraser end of a particular stylus,
320 identified by its \a deviceType, \a pointerType, \a uniqueId and \a systemId.
321 Returns the device found, or \c nullptr if none was found.
322
323 If \a systemId is \c 0, it's not significant for the search.
324
325 If an instance matching the given \a deviceType and \a pointerType but with
326 only a default-constructed \c uniqueId is found, it will be assumed to be
327 the one we're looking for, and its \c uniqueId will be updated to match the
328 given \a uniqueId. This is for the benefit of any platform plugin that can
329 discover the tablet itself at startup, along with the supported stylus types,
330 but then discovers specific styli later on as they come into proximity.
331*/
332const QPointingDevice *QPointingDevicePrivate::queryTabletDevice(QInputDevice::DeviceType deviceType,
333 QPointingDevice::PointerType pointerType,
334 QPointingDeviceUniqueId uniqueId,
335 qint64 systemId)
336{
337 const auto &devices = QInputDevice::devices();
338 for (const QInputDevice *dev : devices) {
339 if (dev->type() < QPointingDevice::DeviceType::Puck || dev->type() > QPointingDevice::DeviceType::Airbrush)
340 continue;
341 const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
342 const auto devPriv = QPointingDevicePrivate::get(pdev);
343 bool uniqueIdDiscovered = (devPriv->uniqueId.numericId() == 0 && uniqueId.numericId() != 0);
344 if (devPriv->deviceType == deviceType && devPriv->pointerType == pointerType &&
345 (!systemId || devPriv->systemId == systemId) &&
346 (devPriv->uniqueId == uniqueId || uniqueIdDiscovered)) {
347 if (uniqueIdDiscovered) {
348 const_cast<QPointingDevicePrivate *>(devPriv)->uniqueId = uniqueId;
349 qCDebug(lcQpaInputDevices) << "discovered unique ID of tablet tool" << pdev;
350 }
351 return pdev;
352 }
353 }
354 return nullptr;
355}
356
357/*!
358 \internal
359 First, ensure that the \a cancelEvent's QTouchEvent::points() list contains
360 all points that have exclusive grabs. Then send the event to each object
361 that has an exclusive grab of any of the points.
362*/
363void QPointingDevicePrivate::sendTouchCancelEvent(QTouchEvent *cancelEvent)
364{
365 // An incoming TouchCancel event will typically not contain any points, but
366 // QQuickPointerHandler::onGrabChanged needs to be called for each point
367 // that has an exclusive grabber. Adding those points to the event makes it
368 // an easy iteration there.
369 if (cancelEvent->points().isEmpty()) {
370 for (auto &epd : activePoints.values()) {
371 if (epd.exclusiveGrabber)
372 QMutableTouchEvent::from(cancelEvent)->addPoint(epd.eventPoint);
373 }
374 }
375 for (auto &epd : activePoints.values()) {
376 if (epd.exclusiveGrabber)
377 QCoreApplication::sendEvent(epd.exclusiveGrabber, cancelEvent);
378 // The next touch event can only be a TouchBegin, so clean up.
379 cancelEvent->setExclusiveGrabber(epd.eventPoint, nullptr);
380 cancelEvent->clearPassiveGrabbers(epd.eventPoint);
381 }
382}
383
384/*! \internal
385 Returns the active EventPointData instance with the given \a id, if available,
386 or \c nullptr if not.
387*/
388QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::queryPointById(int id) const
389{
390 auto it = activePoints.find(id);
391 if (it == activePoints.end())
392 return nullptr;
393 return &it.value();
394}
395
396/*! \internal
397 Returns the active EventPointData instance with the given \a id, if available;
398 if not, appends a new instance and returns it.
399*/
400QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::pointById(int id) const
401{
402 auto it = activePoints.find(id);
403 if (it == activePoints.end()) {
404 Q_Q(const QPointingDevice);
405 QPointingDevicePrivate::EventPointData epd;
406 QMutableEventPoint::from(epd.eventPoint).setId(id);
407 QMutableEventPoint::from(epd.eventPoint).setDevice(q);
408 return &activePoints.insert(id, epd).first.value();
409 }
410 return &it.value();
411}
412
413/*! \internal
414 Remove the active EventPointData instance with the given \a id.
415*/
416void QPointingDevicePrivate::removePointById(int id)
417{
418 activePoints.remove(id);
419}
420
421/*!
422 \internal
423 Find the first non-null target (widget) via QMutableEventPoint::target()
424 in the active points. This is the widget that will receive any event that
425 comes from a touchpad, even if some of the touchpoints fall spatially on
426 other windows.
427*/
428QObject *QPointingDevicePrivate::firstActiveTarget() const
429{
430 for (auto &pt : activePoints.values()) {
431 if (auto target = QMutableEventPoint::constFrom(pt.eventPoint).target())
432 return target;
433 }
434 return nullptr;
435}
436
437/*! \internal
438 Find the first non-null QWindow instance via QMutableEventPoint::window()
439 in the active points. This is the window that will receive any event that
440 comes from a touchpad, even if some of the touchpoints fall spatially on
441 other windows.
442*/
443QWindow *QPointingDevicePrivate::firstActiveWindow() const
444{
445 for (auto &pt : activePoints.values()) {
446 if (auto window = QMutableEventPoint::constFrom(pt.eventPoint).window())
447 return window;
448 }
449 return nullptr;
450}
451
452/*! \internal
453 Return the exclusive grabber of the first point in activePoints.
454 This is mainly for autotests that try to verify the "current" grabber
455 outside the context of event delivery, which is something that the rest
456 of the codebase should not be doing.
457*/
458QObject *QPointingDevicePrivate::firstPointExclusiveGrabber() const
459{
460 if (activePoints.isEmpty())
461 return nullptr;
462 return activePoints.values().first().exclusiveGrabber;
463}
464
465void QPointingDevicePrivate::setExclusiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *exclusiveGrabber)
466{
467 Q_Q(QPointingDevice);
468 auto persistentPoint = queryPointById(point.id());
469 if (!persistentPoint) {
470 qWarning() << "point is not in activePoints" << point;
471 return;
472 }
473 if (persistentPoint->exclusiveGrabber == exclusiveGrabber)
474 return;
475 auto oldGrabber = persistentPoint->exclusiveGrabber;
476 persistentPoint->exclusiveGrabber = exclusiveGrabber;
477 if (oldGrabber)
478 emit q->grabChanged(oldGrabber, exclusiveGrabber ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
479 event, persistentPoint->eventPoint);
480 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
481 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
482 << "@" << point.scenePosition()
483 << ": grab" << oldGrabber << "->" << exclusiveGrabber;
484 }
485 QMutableEventPoint::from(persistentPoint->eventPoint).setGlobalGrabPosition(point.globalPosition());
486 if (exclusiveGrabber)
487 emit q->grabChanged(exclusiveGrabber, QPointingDevice::GrabExclusive, event, point);
488}
489
490/*!
491 \internal
492 Call QEventPoint::setExclusiveGrabber(nullptr) on each active point that has a grabber.
493*/
494bool QPointingDevicePrivate::removeExclusiveGrabber(const QPointerEvent *event, const QObject *grabber)
495{
496 bool ret = false;
497 for (auto &pt : activePoints.values()) {
498 if (pt.exclusiveGrabber == grabber) {
499 setExclusiveGrabber(event, pt.eventPoint, nullptr);
500 ret = true;
501 }
502 }
503 return ret;
504}
505
506bool QPointingDevicePrivate::addPassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
507{
508 Q_Q(QPointingDevice);
509 auto persistentPoint = queryPointById(point.id());
510 if (!persistentPoint) {
511 qWarning() << "point is not in activePoints" << point;
512 return false;
513 }
514 if (persistentPoint->passiveGrabbers.contains(grabber))
515 return false;
516 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
517 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
518 << ": grab (passive)" << grabber;
519 }
520 persistentPoint->passiveGrabbers << grabber;
521 emit q->grabChanged(grabber, QPointingDevice::GrabPassive, event, point);
522 return true;
523}
524
525bool QPointingDevicePrivate::removePassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
526{
527 Q_Q(QPointingDevice);
528 auto persistentPoint = queryPointById(point.id());
529 if (!persistentPoint) {
530 qWarning() << "point is not in activePoints" << point;
531 return false;
532 }
533 int i = persistentPoint->passiveGrabbers.indexOf(grabber);
534 if (i >= 0) {
535 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
536 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
537 << ": removing passive grabber" << grabber;
538 }
539 emit q->grabChanged(grabber, QPointingDevice::UngrabPassive, event, point);
540 persistentPoint->passiveGrabbers.removeAt(i);
541 return true;
542 }
543 return false;
544}
545
546void QPointingDevicePrivate::clearPassiveGrabbers(const QPointerEvent *event, const QEventPoint &point)
547{
548 Q_Q(QPointingDevice);
549 auto persistentPoint = queryPointById(point.id());
550 if (!persistentPoint) {
551 qWarning() << "point is not in activePoints" << point;
552 return;
553 }
554 if (persistentPoint->passiveGrabbers.isEmpty())
555 return;
556 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
557 qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
558 << ": clearing" << persistentPoint->passiveGrabbers;
559 }
560 for (auto g : persistentPoint->passiveGrabbers)
561 emit q->grabChanged(g, QPointingDevice::UngrabPassive, event, point);
562 persistentPoint->passiveGrabbers.clear();
563}
564
565/*!
566 \internal
567 Removes the given \a grabber as both passive and exclusive grabber from all
568 points in activePoints where it's currently found. If \a cancel is \c true,
569 the transition emitted from the grabChanged() signal will be
570 \c CancelGrabExclusive or \c CancelGrabPassive. Otherwise it will be
571 \c UngrabExclusive or \c UngrabPassive.
572
573 \note This function provides a way to work around the limitation that we
574 normally change grabbers only during event delivery; but it's also more expensive.
575*/
576void QPointingDevicePrivate::removeGrabber(QObject *grabber, bool cancel)
577{
578 Q_Q(QPointingDevice);
579 for (auto ap : activePoints) {
580 auto &epd = ap.second;
581 if (epd.exclusiveGrabber.data() == grabber) {
582 qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
583 << "@" << epd.eventPoint.scenePosition()
584 << ": grab" << grabber << "-> nullptr";
585 epd.exclusiveGrabber.clear();
586 emit q->grabChanged(grabber,
587 cancel ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
588 nullptr, epd.eventPoint);
589 }
590 int pi = epd.passiveGrabbers.indexOf(grabber);
591 if (pi >= 0) {
592 qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
593 << ": removing passive grabber" << grabber;
594 epd.passiveGrabbers.removeAt(pi);
595 emit q->grabChanged(grabber,
596 cancel ? QPointingDevice::CancelGrabPassive : QPointingDevice::UngrabPassive,
597 nullptr, epd.eventPoint);
598 }
599 }
600}
601
602/*!
603 \internal
604 Finds the device instance belonging to the drawing or eraser end of a particular stylus,
605 identified by its \a deviceType, \a pointerType and \a uniqueId. If an existing device
606 is not found, a new one is created and registered, with a warning.
607
608 This function is called from QWindowSystemInterface. Platform plugins should use
609 \l queryTabletDeviceInstance() to check whether a tablet stylus coming into proximity
610 is previously known; if not known, the plugin should create and register the stylus.
611*/
612const QPointingDevice *QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType deviceType,
613 QPointingDevice::PointerType pointerType,
614 QPointingDeviceUniqueId uniqueId)
615{
616 const QPointingDevice *dev = queryTabletDevice(deviceType, pointerType, uniqueId);
617 if (!dev) {
618 qCDebug(lcQpaInputDevices) << "failed to find registered tablet device"
619 << deviceType << pointerType << Qt::hex << uniqueId.numericId()
620 << "The platform plugin should have provided one via "
621 "QWindowSystemInterface::registerInputDevice(). Creating a default one for now.";
622 dev = new QPointingDevice(QLatin1String("fake tablet"), 2, deviceType, pointerType,
623 QInputDevice::Capability::Position | QInputDevice::Capability::Pressure,
624 1, 1, QString(), uniqueId, QCoreApplication::instance());
625 QInputDevicePrivate::registerDevice(dev);
626 }
627 return dev;
628}
629
630bool QPointingDevice::operator==(const QPointingDevice &other) const
631{
632 // Wacom tablets generate separate instances for each end of each stylus;
633 // QInputDevice::operator==() says they are all the same, but we use
634 // the stylus unique serial number and pointerType to distinguish them
635 return QInputDevice::operator==(other) &&
636 pointerType() == other.pointerType() &&
637 uniqueId() == other.uniqueId();
638}
639
640#ifndef QT_NO_DEBUG_STREAM
641QDebug operator<<(QDebug debug, const QPointingDevice *device)
642{
643 QDebugStateSaver saver(debug);
644 debug.nospace();
645 debug.noquote();
646 debug << "QPointingDevice(";
647 if (device) {
648 debug << '"' << device->name() << "\", type=";
649 QtDebugUtils::formatQEnum(debug, device->type());
650 debug << ", id=" << Qt::hex << device->systemId() << Qt::dec << ", seat=" << device->seatName();
651 debug << ", pointerType=";
652 QtDebugUtils::formatQEnum(debug, device->pointerType());
653 debug << ", capabilities=";
654 QtDebugUtils::formatQFlags(debug, device->capabilities());
655 debug << ", maximumTouchPoints=" << device->maximumPoints();
656 if (device->uniqueId().numericId())
657 debug << ", uniqueId=" << Qt::hex << device->uniqueId().numericId() << Qt::dec;
658 } else {
659 debug << '0';
660 }
661 debug << ')';
662 return debug;
663}
664#endif // !QT_NO_DEBUG_STREAM
665
666QT_END_NAMESPACE
667