Coverage Report

Created: 2026-09-28 08:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/kernel/qpointingdevice.cpp
Line
Count
Source
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
5
#include "qpointingdevice.h"
6
#include "qpointingdevice_p.h"
7
#include "qwindowsysteminterface_p.h"
8
#include "qeventpoint_p.h"
9
10
#include <QList>
11
#include <QLoggingCategory>
12
#include <QMutex>
13
#include <QCoreApplication>
14
15
#include <private/qdebug_p.h>
16
17
QT_BEGIN_NAMESPACE
18
19
using namespace Qt::StringLiterals;
20
21
Q_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 [since 6.12] 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
*/
162
QPointingDevice::QPointingDevice(QObject *parent)
163
0
    : QInputDevice(*(new QPointingDevicePrivate("unknown"_L1, -1,
164
0
                                              DeviceType::Unknown, PointerType::Unknown,
165
0
                                              Capability::None, 0, 0)), parent)
166
0
{
167
0
}
168
169
QPointingDevice::~QPointingDevice()
170
0
{
171
0
}
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
*/
178
QPointingDevice::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
0
    : QInputDevice(*(new QPointingDevicePrivate(name, id, deviceType, pointerType, capabilities, maxPoints, buttonCount, seatName, uniqueId)), parent)
182
0
{
183
0
}
184
185
/*!
186
    \internal
187
*/
188
QPointingDevice::QPointingDevice(QPointingDevicePrivate &d, QObject *parent)
189
0
    : QInputDevice(d, parent)
190
0
{
191
0
}
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
*/
200
void QPointingDevice::setType(DeviceType devType)
201
0
{
202
0
    Q_D(QPointingDevice);
203
0
    d->deviceType = devType;
204
0
    if (d->pointerType == PointerType::Unknown)
205
0
        switch (devType) {
206
0
        case DeviceType::Mouse:
207
0
            d->pointerType = PointerType::Generic;
208
0
            break;
209
0
        case DeviceType::TouchScreen:
210
0
        case DeviceType::TouchPad:
211
0
            d->pointerType = PointerType::Finger;
212
0
            break;
213
0
        case DeviceType::Puck:
214
0
            d->pointerType = PointerType::Cursor;
215
0
            break;
216
0
        case DeviceType::Stylus:
217
0
        case DeviceType::Airbrush:
218
0
            d->pointerType = PointerType::Pen;
219
0
            break;
220
0
        default:
221
0
            break;
222
0
        }
223
0
}
224
225
/*!
226
    \internal
227
    \deprecated [6.0] Please use the constructor rather than setters.
228
*/
229
void QPointingDevice::setCapabilities(QInputDevice::Capabilities caps)
230
0
{
231
0
    Q_D(QPointingDevice);
232
0
    d->setCapabilities(caps);
233
0
}
234
235
/*!
236
    \internal
237
    \deprecated [6.0] Please use the constructor rather than setters.
238
*/
239
void QPointingDevice::setMaximumTouchPoints(int c)
240
0
{
241
0
    Q_D(QPointingDevice);
242
0
    d->maximumTouchPoints = c;
243
0
}
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
*/
254
QPointingDevice::PointerType QPointingDevice::pointerType() const
255
0
{
256
0
    Q_D(const QPointingDevice);
257
0
    return d->pointerType;
258
0
}
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
*/
270
int QPointingDevice::maximumPoints() const
271
0
{
272
0
    Q_D(const QPointingDevice);
273
0
    return d->maximumTouchPoints;
274
0
}
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
*/
284
int QPointingDevice::buttonCount() const
285
0
{
286
0
    Q_D(const QPointingDevice);
287
0
    return d->buttonCount;
288
0
}
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
*/
302
QPointingDeviceUniqueId QPointingDevice::uniqueId() const
303
0
{
304
0
    Q_D(const QPointingDevice);
305
0
    return d->uniqueId;
306
0
}
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
*/
319
const QPointingDevice *QPointingDevice::primaryPointingDevice(const QString& seatName)
320
0
{
321
0
    const auto v = devices();
322
0
    const QPointingDevice *mouse = nullptr;
323
0
    const QPointingDevice *touchpad = nullptr;
324
0
    for (const QInputDevice *dev : v) {
325
0
        if (!seatName.isNull() && dev->seatName() != seatName)
326
0
            continue;
327
0
        if (dev->type() == QInputDevice::DeviceType::Mouse) {
328
0
            if (!mouse)
329
0
                mouse = static_cast<const QPointingDevice *>(dev);
330
            // the core pointer is likely a mouse, and its parent is not another input device
331
0
            if (!mouse->parent() || !qobject_cast<const QInputDevice *>(mouse->parent()))
332
0
                return mouse;
333
0
        } else if (dev->type() == QInputDevice::DeviceType::TouchPad) {
334
0
            if (!touchpad || !dev->parent() || dev->parent()->metaObject() != dev->metaObject())
335
0
                touchpad = static_cast<const QPointingDevice *>(dev);
336
0
        }
337
0
    }
338
0
    if (!mouse && !touchpad) {
339
0
        qCDebug(lcQpaInputDevices) << "no mouse-like devices registered for seat" << seatName
340
0
                                   << "The platform plugin should have provided one via "
341
0
                                      "QWindowSystemInterface::registerInputDevice(). Creating a default mouse for now.";
342
0
        mouse = new QPointingDevice("core pointer"_L1, 1, DeviceType::Mouse,
343
0
                                    PointerType::Generic, Capability::Position, 1, 3, seatName,
344
0
                                    QPointingDeviceUniqueId(), QCoreApplication::instance());
345
0
        QInputDevicePrivate::registerDevice(mouse);
346
0
        return mouse;
347
0
    }
348
0
    if (v.size() > 1)
349
0
        qCDebug(lcQpaInputDevices) << "core pointer ambiguous for seat" << seatName;
350
0
    if (mouse)
351
0
        return mouse;
352
0
    return touchpad;
353
0
}
354
355
0
QPointingDevicePrivate::~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
*/
374
const QPointingDevice *QPointingDevicePrivate::queryTabletDevice(QInputDevice::DeviceType deviceType,
375
                                                                 QPointingDevice::PointerType pointerType,
376
                                                                 QPointingDeviceUniqueId uniqueId,
377
                                                                 QPointingDevice::Capabilities capabilities,
378
                                                                 qint64 systemId)
379
0
{
380
0
    const auto &devices = QInputDevice::devices();
381
0
    for (const QInputDevice *dev : devices) {
382
0
        if (dev->type() < QPointingDevice::DeviceType::Puck || dev->type() > QPointingDevice::DeviceType::Airbrush)
383
0
            continue;
384
0
        const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
385
0
        const auto devPriv = QPointingDevicePrivate::get(pdev);
386
0
        bool uniqueIdDiscovered = (devPriv->uniqueId.numericId() == 0 && uniqueId.numericId() != 0);
387
0
        if (devPriv->deviceType == deviceType && devPriv->pointerType == pointerType &&
388
0
                (!systemId || devPriv->systemId == systemId) &&
389
0
                (devPriv->uniqueId == uniqueId || uniqueIdDiscovered)) {
390
0
            if (uniqueIdDiscovered) {
391
0
                const_cast<QPointingDevicePrivate *>(devPriv)->uniqueId = uniqueId;
392
0
                if (capabilities)
393
0
                    const_cast<QPointingDevicePrivate *>(devPriv)->capabilities = capabilities;
394
0
                qCDebug(lcQpaInputDevices) << "discovered unique ID and capabilities of tablet tool" << pdev;
395
0
            }
396
0
            return pdev;
397
0
        }
398
0
    }
399
0
    return nullptr;
400
0
}
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
*/
407
const QPointingDevice *QPointingDevicePrivate::pointingDeviceById(qint64 systemId)
408
0
{
409
0
    const auto &devices = QInputDevice::devices();
410
0
    for (const QInputDevice *dev : devices) {
411
0
        if (dev->type() >= QPointingDevice::DeviceType::Keyboard)
412
0
            continue;
413
0
        const QPointingDevice *pdev = static_cast<const QPointingDevice *>(dev);
414
0
        const auto devPriv = QPointingDevicePrivate::get(pdev);
415
0
        if (devPriv->systemId == systemId)
416
0
            return pdev;
417
0
    }
418
0
    return nullptr;
419
0
}
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
*/
427
void QPointingDevicePrivate::sendTouchCancelEvent(QTouchEvent *cancelEvent)
428
0
{
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
0
    if (cancelEvent->points().isEmpty()) {
434
0
        for (auto &epd : activePoints.values()) {
435
0
            if (epd.exclusiveGrabber)
436
0
                QMutableTouchEvent::addPoint(cancelEvent, epd.eventPoint);
437
0
        }
438
0
    }
439
0
    for (auto &epd : activePoints.values()) {
440
0
        if (epd.exclusiveGrabber)
441
0
            QCoreApplication::sendEvent(epd.exclusiveGrabber, cancelEvent);
442
        // The next touch event can only be a TouchBegin, so clean up.
443
0
        cancelEvent->setExclusiveGrabber(epd.eventPoint, nullptr);
444
0
        cancelEvent->clearPassiveGrabbers(epd.eventPoint);
445
0
    }
446
0
}
447
448
/*! \internal
449
    Returns the active EventPointData instance with the given \a id, if available,
450
    or \c nullptr if not.
451
*/
452
QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::queryPointById(int id) const
453
0
{
454
0
    auto it = activePoints.find(id);
455
0
    if (it == activePoints.end())
456
0
        return nullptr;
457
0
    return &it.value();
458
0
}
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
*/
464
QPointingDevicePrivate::EventPointData *QPointingDevicePrivate::pointById(int id) const
465
0
{
466
0
    const auto [it, inserted] = activePoints.try_emplace(id);
467
0
    if (inserted) {
468
0
        Q_Q(const QPointingDevice);
469
0
        auto &epd = it.value();
470
0
        QMutableEventPoint::setId(epd.eventPoint, id);
471
0
        QMutableEventPoint::setDevice(epd.eventPoint, q);
472
0
    }
473
0
    return &it.value();
474
0
}
475
476
/*! \internal
477
    Remove the active EventPointData instance with the given \a id.
478
*/
479
void QPointingDevicePrivate::removePointById(int id)
480
0
{
481
0
    activePoints.remove(id);
482
0
}
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
*/
491
QObject *QPointingDevicePrivate::firstActiveTarget() const
492
0
{
493
0
    for (auto &pt : activePoints.values()) {
494
0
        if (auto target = QMutableEventPoint::target(pt.eventPoint))
495
0
            return target;
496
0
    }
497
0
    return nullptr;
498
0
}
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
*/
506
QWindow *QPointingDevicePrivate::firstActiveWindow() const
507
0
{
508
0
    for (auto &pt : activePoints.values()) {
509
0
        if (auto window = QMutableEventPoint::window(pt.eventPoint))
510
0
            return window;
511
0
    }
512
0
    return nullptr;
513
0
}
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
*/
521
QObject *QPointingDevicePrivate::firstPointExclusiveGrabber() const
522
0
{
523
0
    if (activePoints.isEmpty())
524
0
        return nullptr;
525
0
    return activePoints.values().first().exclusiveGrabber;
526
0
}
527
528
void QPointingDevicePrivate::setExclusiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *exclusiveGrabber)
529
0
{
530
0
    Q_Q(QPointingDevice);
531
0
    auto persistentPoint = queryPointById(point.id());
532
0
    if (!persistentPoint) {
533
0
        qWarning() << "point is not in activePoints" << point;
534
0
        return;
535
0
    }
536
0
    Q_ASSERT(persistentPoint->eventPoint.id() == point.id());
537
0
    if (persistentPoint->exclusiveGrabber == exclusiveGrabber)
538
0
        return;
539
0
    auto oldGrabber = persistentPoint->exclusiveGrabber;
540
0
    persistentPoint->exclusiveGrabber = exclusiveGrabber;
541
0
    if (oldGrabber)
542
0
        emit q->grabChanged(oldGrabber, exclusiveGrabber ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
543
0
                            event, persistentPoint->eventPoint);
544
0
    if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
545
0
        qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
546
0
                               << "@" << point.scenePosition()
547
0
                               << ": grab" << oldGrabber << "->" << exclusiveGrabber;
548
0
    }
549
0
    QMutableEventPoint::setGlobalGrabPosition(persistentPoint->eventPoint, point.globalPosition());
550
0
    if (exclusiveGrabber)
551
0
        emit q->grabChanged(exclusiveGrabber, QPointingDevice::GrabExclusive, event, point);
552
0
    else
553
0
        persistentPoint->exclusiveGrabberContext.clear();
554
0
}
555
556
/*!
557
    \internal
558
    Call QEventPoint::setExclusiveGrabber(nullptr) on each active point that has a grabber.
559
*/
560
bool QPointingDevicePrivate::removeExclusiveGrabber(const QPointerEvent *event, const QObject *grabber)
561
0
{
562
0
    bool ret = false;
563
0
    for (auto &pt : activePoints.values()) {
564
0
        if (pt.exclusiveGrabber == grabber) {
565
0
            setExclusiveGrabber(event, pt.eventPoint, nullptr);
566
0
            ret = true;
567
0
        }
568
0
    }
569
0
    return ret;
570
0
}
571
572
bool QPointingDevicePrivate::addPassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
573
0
{
574
0
    Q_Q(QPointingDevice);
575
0
    auto persistentPoint = queryPointById(point.id());
576
0
    if (!persistentPoint) {
577
0
        qWarning() << "point is not in activePoints" << point;
578
0
        return false;
579
0
    }
580
0
    if (persistentPoint->passiveGrabbers.contains(grabber))
581
0
        return false;
582
0
    if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
583
0
        qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
584
0
                               << ": grab (passive)" << grabber;
585
0
    }
586
0
    persistentPoint->passiveGrabbers << grabber;
587
0
    emit q->grabChanged(grabber, QPointingDevice::GrabPassive, event, point);
588
0
    return true;
589
0
}
590
591
bool QPointingDevicePrivate::setPassiveGrabberContext(QPointingDevicePrivate::EventPointData *epd, QObject *grabber, QObject *context)
592
0
{
593
0
    qsizetype i = epd->passiveGrabbers.indexOf(grabber);
594
0
    if (i < 0)
595
0
        return false;
596
0
    if (epd->passiveGrabbersContext.size() <= i)
597
0
        epd->passiveGrabbersContext.resize(i + 1);
598
0
    epd->passiveGrabbersContext[i] = context;
599
0
    return true;
600
0
}
601
602
bool QPointingDevicePrivate::removePassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber)
603
0
{
604
0
    Q_Q(QPointingDevice);
605
0
    auto persistentPoint = queryPointById(point.id());
606
0
    if (!persistentPoint) {
607
0
        qWarning() << "point is not in activePoints" << point;
608
0
        return false;
609
0
    }
610
0
    qsizetype i = persistentPoint->passiveGrabbers.indexOf(grabber);
611
0
    if (i >= 0) {
612
0
        if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
613
0
            qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
614
0
                                   << ": removing passive grabber" << grabber;
615
0
        }
616
0
        emit q->grabChanged(grabber, QPointingDevice::UngrabPassive, event, point);
617
0
        persistentPoint->passiveGrabbers.removeAt(i);
618
0
        if (persistentPoint->passiveGrabbersContext.size()) {
619
0
            Q_ASSERT(persistentPoint->passiveGrabbersContext.size() > i);
620
0
            persistentPoint->passiveGrabbersContext.removeAt(i);
621
0
        }
622
0
        return true;
623
0
    }
624
0
    return false;
625
0
}
626
627
void QPointingDevicePrivate::clearPassiveGrabbers(const QPointerEvent *event, const QEventPoint &point)
628
0
{
629
0
    Q_Q(QPointingDevice);
630
0
    auto persistentPoint = queryPointById(point.id());
631
0
    if (!persistentPoint) {
632
0
        qWarning() << "point is not in activePoints" << point;
633
0
        return;
634
0
    }
635
0
    if (persistentPoint->passiveGrabbers.isEmpty())
636
0
        return;
637
0
    if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
638
0
        qCDebug(lcPointerGrab) << name << "point" << point.id() << point.state()
639
0
                               << ": clearing" << persistentPoint->passiveGrabbers;
640
0
    }
641
0
    for (auto g : persistentPoint->passiveGrabbers)
642
0
        emit q->grabChanged(g, QPointingDevice::UngrabPassive, event, point);
643
0
    persistentPoint->passiveGrabbers.clear();
644
0
    persistentPoint->passiveGrabbersContext.clear();
645
0
}
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
*/
658
void QPointingDevicePrivate::removeGrabber(QObject *grabber, bool cancel)
659
0
{
660
0
    Q_Q(QPointingDevice);
661
0
    for (auto ap : activePoints) {
662
0
        auto &epd = ap.second;
663
0
        if (epd.exclusiveGrabber.data() == grabber) {
664
0
            qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
665
0
                                   << "@" << epd.eventPoint.scenePosition()
666
0
                                   << ": grab" << grabber << "-> nullptr";
667
0
            epd.exclusiveGrabber.clear();
668
0
            epd.exclusiveGrabberContext.clear();
669
0
            emit q->grabChanged(grabber,
670
0
                                cancel ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive,
671
0
                                nullptr, epd.eventPoint);
672
0
        }
673
0
        qsizetype pi = epd.passiveGrabbers.indexOf(grabber);
674
0
        if (pi >= 0) {
675
0
            qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state()
676
0
                                   << ": removing passive grabber" << grabber;
677
0
            epd.passiveGrabbers.removeAt(pi);
678
0
            if (epd.passiveGrabbersContext.size()) {
679
0
                Q_ASSERT(epd.passiveGrabbersContext.size() > pi);
680
0
                epd.passiveGrabbersContext.removeAt(pi);
681
0
            }
682
0
            emit q->grabChanged(grabber,
683
0
                                cancel ? QPointingDevice::CancelGrabPassive : QPointingDevice::UngrabPassive,
684
0
                                nullptr, epd.eventPoint);
685
0
        }
686
0
    }
687
0
}
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
*/
699
const QPointingDevice *QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType deviceType,
700
                                                            QPointingDevice::PointerType pointerType,
701
                                                            QPointingDeviceUniqueId uniqueId)
702
0
{
703
0
    const QPointingDevice *dev = queryTabletDevice(deviceType, pointerType, uniqueId);
704
0
    if (!dev) {
705
0
        qCDebug(lcQpaInputDevices) << "failed to find registered tablet device"
706
0
                                   << deviceType << pointerType << Qt::hex << uniqueId.numericId()
707
0
                                   << "The platform plugin should have provided one via "
708
0
                                      "QWindowSystemInterface::registerInputDevice(). Creating a default one for now.";
709
0
        dev = new QPointingDevice("fake tablet"_L1, 2, deviceType, pointerType,
710
0
                                  QInputDevice::Capability::Position | QInputDevice::Capability::Pressure,
711
0
                                  1, 1, QString(), uniqueId, QCoreApplication::instance());
712
0
        QInputDevicePrivate::registerDevice(dev);
713
0
    }
714
0
    return dev;
715
0
}
716
717
bool QPointingDevice::operator==(const QPointingDevice &other) const
718
0
{
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
0
    return QInputDevice::operator==(other) &&
723
0
            pointerType() == other.pointerType() &&
724
0
            uniqueId() == other.uniqueId();
725
0
}
726
727
#ifndef QT_NO_DEBUG_STREAM
728
QDebug operator<<(QDebug debug, const QPointingDevice *device)
729
0
{
730
0
    QDebugStateSaver saver(debug);
731
0
    debug.nospace();
732
0
    debug.noquote();
733
0
    debug << "QPointingDevice(";
734
0
    if (device) {
735
0
        debug << '"' << device->name() << "\" ";
736
0
        QtDebugUtils::formatQEnum(debug, device->type());
737
0
        debug << " id=" << device->systemId();
738
0
        if (!device->seatName().isEmpty())
739
0
            debug << " seat=" << device->seatName();
740
0
        if (device->pointerType() != QPointingDevice::PointerType::Generic) {
741
0
            debug << " ptrType=";
742
0
            QtDebugUtils::formatQEnum(debug, device->pointerType());
743
0
        }
744
0
        if (int(device->capabilities()) != int(QInputDevice::Capability::Position)) {
745
0
            debug << " caps=";
746
0
            QtDebugUtils::formatQFlags(debug, device->capabilities());
747
0
        }
748
0
        if (device->buttonCount() > 0)
749
0
            debug << " buttonCount=" << device->buttonCount();
750
0
        if (device->maximumPoints() > 1)
751
0
            debug << " maxPts=" << device->maximumPoints();
752
0
        if (device->uniqueId().isValid())
753
0
            debug << " uniqueId=" << Qt::hex << device->uniqueId().numericId() << Qt::dec;
754
0
    } else {
755
0
        debug << "0x0";
756
0
    }
757
0
    debug << ')';
758
0
    return debug;
759
0
}
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
*/
790
QPointingDeviceUniqueId QPointingDeviceUniqueId::fromNumericId(qint64 id)
791
0
{
792
0
    QPointingDeviceUniqueId result;
793
0
    result.m_numericId = id;
794
0
    return result;
795
0
}
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
*/
817
qint64 QPointingDeviceUniqueId::numericId() const noexcept
818
0
{
819
0
    return m_numericId;
820
0
}
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
*/
842
size_t qHash(QPointingDeviceUniqueId key, size_t seed) noexcept
843
0
{
844
0
    return qHash(key.numericId(), seed);
845
0
}
846
847
QT_END_NAMESPACE
848
849
#include "moc_qpointingdevice.cpp"