Coverage Report

Created: 2026-02-26 07:48

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