Coverage Report

Created: 2026-03-31 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/kernel/qeventpoint.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 "qeventpoint.h"
6
#include "private/qeventpoint_p.h"
7
#include "private/qpointingdevice_p.h"
8
9
QT_BEGIN_NAMESPACE
10
11
Q_LOGGING_CATEGORY(lcPointerVel, "qt.pointer.velocity")
12
Q_LOGGING_CATEGORY(lcEPDetach, "qt.pointer.eventpoint.detach")
13
14
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QEventPointPrivate)
15
16
/*! \class QEventPoint
17
    \brief The QEventPoint class provides information about a point in a QPointerEvent.
18
    \since 6.0
19
    \inmodule QtGui
20
*/
21
22
/*!
23
    \enum QEventPoint::State
24
25
    Specifies the state of this event point.
26
27
    \value  Unknown
28
            Unknown state.
29
30
    \value  Stationary
31
            The event point did not move.
32
33
    \value  Pressed
34
            The touch point or button is pressed.
35
36
    \value  Updated
37
            The event point was updated.
38
39
    \value  Released
40
            The touch point or button was released.
41
*/
42
43
/*!
44
    \internal
45
    Constructs an invalid event point with the given \a id and the \a device
46
    from which it originated.
47
48
    This acts as a default constructor in usages like QMap<int, QEventPoint>,
49
    as in qgraphicsscene_p.h.
50
*/
51
QEventPoint::QEventPoint(int id, const QPointingDevice *device)
52
0
    : d(new QEventPointPrivate(id, device)) {}
53
54
/*!
55
    Constructs an event point with the given \a pointId, \a state,
56
    \a scenePosition and \a globalPosition.
57
*/
58
QEventPoint::QEventPoint(int pointId, State state, const QPointF &scenePosition, const QPointF &globalPosition)
59
0
    : d(new QEventPointPrivate(pointId, state, scenePosition, globalPosition)) {}
60
61
/*!
62
    Constructs an event point by making a shallow copy of \a other.
63
*/
64
0
QEventPoint::QEventPoint(const QEventPoint &other) noexcept = default;
65
66
/*!
67
    Assigns \a other to this event point and returns a reference to this
68
    event point.
69
*/
70
0
QEventPoint &QEventPoint::operator=(const QEventPoint &other) noexcept = default;
71
72
/*!
73
    \fn QEventPoint::QEventPoint(QEventPoint &&other) noexcept
74
75
    Constructs an event point by moving \a other.
76
*/
77
78
/*!
79
    \fn QEventPoint &QEventPoint::operator=(QEventPoint &&other) noexcept
80
81
    Move-assigns \a other to this event point instance.
82
*/
83
84
/*!
85
    Returns \c true if this event point is equal to \a other, otherwise
86
    return \c false.
87
*/
88
bool QEventPoint::operator==(const QEventPoint &other) const noexcept
89
0
{
90
0
    if (d == other.d)
91
0
        return true;
92
0
    if (!d || !other.d)
93
0
        return false;
94
0
    return *d == *other.d;
95
0
}
96
97
/*!
98
    \fn bool QEventPoint::operator!=(const QEventPoint &other) const noexcept
99
100
    Returns \c true if this event point is not equal to \a other, otherwise
101
    return \c false.
102
*/
103
104
/*!
105
    Destroys the event point.
106
*/
107
0
QEventPoint::~QEventPoint() = default;
108
109
/*! \fn QPointF QEventPoint::pos() const
110
    \deprecated [6.0] Use position() instead.
111
112
    Returns the position of this point, relative to the widget
113
    or item that received the event.
114
*/
115
116
/*!
117
    \property QEventPoint::position
118
    \brief the position of this point.
119
120
    The position is relative to the widget or item that received the event.
121
*/
122
QPointF QEventPoint::position() const
123
0
{ return d ? d->pos : QPointF(); }
124
125
/*!
126
    \property QEventPoint::pressPosition
127
    \brief the position at which this point was pressed.
128
129
    The position is relative to the widget or item that received the event.
130
131
    \sa position
132
*/
133
QPointF QEventPoint::pressPosition() const
134
0
{ return d ? d->globalPressPos - d->globalPos + d->pos : QPointF(); }
135
136
/*!
137
    \property QEventPoint::grabPosition
138
    \brief the position at which this point was grabbed.
139
140
    The position is relative to the widget or item that received the event.
141
142
    \sa position
143
*/
144
QPointF QEventPoint::grabPosition() const
145
0
{ return d ? d->globalGrabPos - d->globalPos + d->pos : QPointF(); }
146
147
/*!
148
    \property QEventPoint::lastPosition
149
    \brief the position of this point from the previous press or move event.
150
151
    The position is relative to the widget or item that received the event.
152
153
    \sa position, pressPosition
154
*/
155
QPointF QEventPoint::lastPosition() const
156
0
{ return d ? d->globalLastPos - d->globalPos + d->pos : QPointF(); }
157
158
/*!
159
    \property QEventPoint::scenePosition
160
    \brief the scene position of this point.
161
162
    The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(),
163
    in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(),
164
    or the window position in widget applications.
165
166
    \sa scenePressPosition, position, globalPosition
167
*/
168
QPointF QEventPoint::scenePosition() const
169
0
{ return d ? d->scenePos : QPointF(); }
170
171
/*!
172
    \property QEventPoint::scenePressPosition
173
    \brief the scene position at which this point was pressed.
174
175
    The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(),
176
    in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(),
177
    or the window position in widget applications.
178
179
    \sa scenePosition, pressPosition, globalPressPosition
180
*/
181
QPointF QEventPoint::scenePressPosition() const
182
0
{ return d ? d->globalPressPos - d->globalPos + d->scenePos : QPointF(); }
183
184
/*!
185
    \property QEventPoint::sceneGrabPosition
186
    \brief the scene position at which this point was grabbed.
187
188
    The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(),
189
    in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(),
190
    or the window position in widget applications.
191
192
    \sa scenePosition, grabPosition, globalGrabPosition
193
*/
194
QPointF QEventPoint::sceneGrabPosition() const
195
0
{ return d ? d->globalGrabPos - d->globalPos + d->scenePos : QPointF(); }
196
197
/*!
198
    \property QEventPoint::sceneLastPosition
199
    \brief the scene position of this point from the previous press or move event.
200
201
    The scene position is the position relative to QQuickWindow if handled in QQuickItem::event(),
202
    in QGraphicsScene coordinates if handled by an override of QGraphicsItem::touchEvent(),
203
    or the window position in widget applications.
204
205
    \sa scenePosition, scenePressPosition
206
*/
207
QPointF QEventPoint::sceneLastPosition() const
208
0
{ return d ? d->globalLastPos - d->globalPos + d->scenePos : QPointF(); }
209
210
/*!
211
    \property QEventPoint::globalPosition
212
    \brief the global position of this point.
213
214
    The global position is relative to the screen or virtual desktop.
215
216
    \sa globalPressPosition, position, scenePosition
217
*/
218
QPointF QEventPoint::globalPosition() const
219
0
{ return d ? d->globalPos : QPointF(); }
220
221
/*!
222
    \property QEventPoint::globalPressPosition
223
    \brief the global position at which this point was pressed.
224
225
    The global position is relative to the screen or virtual desktop.
226
227
    \sa globalPosition, pressPosition, scenePressPosition
228
*/
229
QPointF QEventPoint::globalPressPosition() const
230
0
{ return d ? d->globalPressPos : QPointF(); }
231
232
/*!
233
    \property QEventPoint::globalGrabPosition
234
    \brief the global position at which this point was grabbed.
235
236
    The global position is relative to the screen or virtual desktop.
237
238
    \sa globalPosition, grabPosition, sceneGrabPosition
239
*/
240
QPointF QEventPoint::globalGrabPosition() const
241
0
{ return d ? d->globalGrabPos : QPointF(); }
242
243
/*!
244
    \property QEventPoint::globalLastPosition
245
    \brief the global position of this point from the previous press or move event.
246
247
    The global position is relative to the screen or virtual desktop.
248
249
    \sa globalPosition, lastPosition, sceneLastPosition
250
*/
251
QPointF QEventPoint::globalLastPosition() const
252
0
{ return d ? d->globalLastPos : QPointF(); }
253
254
/*!
255
    \property QEventPoint::velocity
256
    \brief a velocity vector, in units of pixels per second, in the coordinate.
257
    system of the screen or desktop.
258
259
    \note If the device's capabilities include QInputDevice::Velocity, it means
260
    velocity comes from the operating system (perhaps the touch hardware or
261
    driver provides it). But usually the \c Velocity capability is not set,
262
    indicating that the velocity is calculated by Qt, using a simple Kalman
263
    filter to provide a smoothed average velocity rather than an instantaneous
264
    value. Effectively it tells how fast and in what direction the user has
265
    been dragging this point over the last few events, with the most recent
266
    event having the strongest influence.
267
268
    \sa QInputDevice::capabilities(), QInputEvent::device()
269
*/
270
QVector2D QEventPoint::velocity() const
271
0
{ return d ? d->velocity : QVector2D(); }
272
273
/*!
274
    \property QEventPoint::state
275
    \brief the current state of the event point.
276
*/
277
QEventPoint::State QEventPoint::state() const
278
0
{ return d ? d->state : QEventPoint::State::Unknown; }
279
280
/*!
281
    \property QEventPoint::device
282
    \brief the pointing device from which this event point originates.
283
*/
284
const QPointingDevice *QEventPoint::device() const
285
0
{ return d ? d->device : nullptr; }
286
287
/*!
288
    \property QEventPoint::id
289
    \brief the ID number of this event point.
290
291
    \note Do not assume that ID numbers start at zero or that they are
292
          sequential. Such an assumption is often false due to the way
293
          the underlying drivers work.
294
*/
295
int QEventPoint::id() const
296
0
{ return d ? d->pointId : -1; }
297
298
/*!
299
    \property QEventPoint::uniqueId
300
    \brief the unique ID of this point or token, if any.
301
302
    It is often invalid (see \l {QPointingDeviceUniqueId::isValid()} {isValid()}),
303
    because touchscreens cannot uniquely identify fingers.
304
305
    When it comes from a QTabletEvent, it identifies the serial number of the
306
    stylus in use.
307
308
    It may identify a specific token (fiducial object) when the TUIO driver is
309
    in use with a touchscreen that supports them.
310
*/
311
QPointingDeviceUniqueId QEventPoint::uniqueId() const
312
0
{ return d ? d->uniqueId : QPointingDeviceUniqueId(); }
313
314
/*!
315
    \property QEventPoint::timestamp
316
    \brief the most recent time at which this point was included in a QPointerEvent.
317
318
    \sa QPointerEvent::timestamp()
319
*/
320
ulong QEventPoint::timestamp() const
321
0
{ return d ? d->timestamp : 0; }
322
323
/*!
324
    \property QEventPoint::lastTimestamp
325
    \brief the time from the previous QPointerEvent that contained this point.
326
327
    \sa globalLastPosition
328
*/
329
ulong QEventPoint::lastTimestamp() const
330
0
{ return d ? d->lastTimestamp : 0; }
331
332
/*!
333
    \property QEventPoint::pressTimestamp
334
    \brief the most recent time at which this point was pressed.
335
336
    \sa timestamp
337
*/
338
ulong QEventPoint::pressTimestamp() const
339
0
{ return d ? d->pressTimestamp : 0; }
340
341
/*!
342
    \property QEventPoint::timeHeld
343
    \brief the duration, in seconds, since this point was pressed and not released.
344
345
    \sa pressTimestamp, timestamp
346
*/
347
qreal QEventPoint::timeHeld() const
348
0
{ return d ? (d->timestamp - d->pressTimestamp) / qreal(1000) : 0.0; }
349
350
/*!
351
    \property QEventPoint::pressure
352
    \brief the pressure of this point.
353
354
    The return value is in the range \c 0.0 to \c 1.0.
355
*/
356
qreal QEventPoint::pressure() const
357
0
{ return d ? d->pressure : 0.0; }
358
359
/*!
360
    \property QEventPoint::rotation
361
    \brief the angular orientation of this point.
362
363
    The return value is in degrees, where zero (the default) indicates the finger,
364
    token or stylus is pointing upwards, a negative angle means it's rotated to the
365
    left, and a positive angle means it's rotated to the right.
366
    Most touchscreens do not detect rotation, so zero is the most common value.
367
*/
368
qreal QEventPoint::rotation() const
369
0
{ return d ? d->rotation : 0.0; }
370
371
/*!
372
    \property QEventPoint::ellipseDiameters
373
    \brief the width and height of the bounding ellipse of the touch point.
374
375
    The return value is in logical pixels. Most touchscreens do not detect the
376
    shape of the contact point, and no mice or tablet devices can detect it,
377
    so a null size is the most common value. On some touchscreens the diameters
378
    may be nonzero and always equal (the ellipse is approximated as a circle).
379
*/
380
QSizeF QEventPoint::ellipseDiameters() const
381
0
{ return d ? d->ellipseDiameters : QSizeF(); }
382
383
/*!
384
    \property QEventPoint::accepted
385
    \brief the accepted state of the event point.
386
387
    In widget-based applications, this property is not used, as it's only meaningful
388
    for a widget to accept or reject a complete QInputEvent.
389
390
    In Qt Quick however, it's normal for an Item or Event Handler to accept
391
    only the individual points in a QTouchEvent that are actually participating
392
    in a gesture, while other points can be delivered to other items or
393
    handlers. For the sake of consistency, that applies to any QPointerEvent;
394
    and delivery is done only when all points in a QPointerEvent have been
395
    accepted.
396
397
    \sa QEvent::accepted
398
*/
399
void QEventPoint::setAccepted(bool accepted)
400
0
{
401
0
    if (d)
402
0
        d->accept = accepted;
403
0
}
404
405
bool QEventPoint::isAccepted() const
406
0
{ return d ? d->accept : false; }
407
408
409
/*!
410
    \fn QPointF QEventPoint::normalizedPos() const
411
    \deprecated [6.0] Use normalizedPosition() instead.
412
*/
413
414
/*!
415
    Returns the normalized position of this point.
416
417
    The coordinates are calculated by transforming globalPosition() into the
418
    space of QInputDevice::availableVirtualGeometry(), i.e. \c (0, 0) is the
419
    top-left corner and \c (1, 1) is the bottom-right corner.
420
421
    \sa globalPosition
422
*/
423
QPointF QEventPoint::normalizedPosition() const
424
0
{
425
0
    if (!d)
426
0
        return {};
427
428
0
    auto geom = d->device->availableVirtualGeometry();
429
0
    if (geom.isNull())
430
0
        return QPointF();
431
0
    return (globalPosition() - geom.topLeft()) / geom.width();
432
0
}
433
434
#if QT_DEPRECATED_SINCE(6, 0)
435
/*!
436
    \deprecated [6.0] Use globalPressPosition() instead.
437
438
    Returns the normalized press position of this point.
439
*/
440
QPointF QEventPoint::startNormalizedPos() const
441
0
{
442
0
    if (!d)
443
0
        return {};
444
445
0
    auto geom = d->device->availableVirtualGeometry();
446
0
    if (geom.isNull())
447
0
        return QPointF();
448
0
    return (globalPressPosition() - geom.topLeft()) / geom.width();
449
0
}
450
451
/*!
452
    \deprecated [6.0] Use globalLastPosition() instead.
453
454
    Returns the normalized position of this point from the previous press or
455
    move event.
456
457
    The coordinates are normalized to QInputDevice::availableVirtualGeometry(),
458
    i.e. \c (0, 0) is the top-left corner and \c (1, 1) is the bottom-right corner.
459
460
    \sa normalizedPosition(), globalPressPosition()
461
*/
462
QPointF QEventPoint::lastNormalizedPos() const
463
0
{
464
0
    if (!d)
465
0
        return {};
466
467
0
    auto geom = d->device->availableVirtualGeometry();
468
0
    if (geom.isNull())
469
0
        return QPointF();
470
0
    return (globalLastPosition() - geom.topLeft()) / geom.width();
471
0
}
472
#endif // QT_DEPRECATED_SINCE(6, 0)
473
474
/*! \internal
475
    This class is explicitly shared, which means if you construct an event and
476
    then the point(s) that it holds are modified before the event is delivered,
477
    the event will be seen to hold the modified points. The workaround is that
478
    any code which modifies an eventpoint that could already be included in an
479
    event, or code that wants to save an eventpoint for later, has
480
    responsibility to detach before calling any setters, so as to hold and
481
    modify an independent copy. (The independent copy can then be used in a
482
    subsequent event.)
483
*/
484
void QMutableEventPoint::detach(QEventPoint &p)
485
0
{
486
0
    if (p.d)
487
0
        p.d.detach();
488
0
    else
489
0
        p.d.reset(new QEventPointPrivate(-1, nullptr));
490
0
}
491
492
/*! \internal
493
    Update \a target state from the \a other point, assuming that \a target
494
    contains state from the previous event and \a other contains new
495
    values that came in from a device.
496
497
    That is: global position and other valuators will be updated, but
498
    the following properties will not be updated:
499
500
    \list
501
    \li properties that are not likely to be set after a fresh touchpoint
502
    has been received from a device
503
    \li properties that should be persistent between events (such as grabbers)
504
    \endlist
505
*/
506
void QMutableEventPoint::update(const QEventPoint &other, QEventPoint &target)
507
0
{
508
0
    detach(target);
509
0
    setPressure(target, other.pressure());
510
511
0
    switch (other.state()) {
512
0
    case QEventPoint::State::Pressed:
513
0
        setGlobalPressPosition(target, other.globalPosition());
514
0
        setGlobalLastPosition(target, other.globalPosition());
515
0
        if (target.pressure() < 0)
516
0
            setPressure(target, 1);
517
0
        break;
518
519
0
    case QEventPoint::State::Released:
520
0
        if (target.globalPosition() != other.globalPosition())
521
0
            setGlobalLastPosition(target, target.globalPosition());
522
0
        setPressure(target, 0);
523
0
        break;
524
525
0
    default: // update or stationary
526
0
        if (target.globalPosition() != other.globalPosition())
527
0
            setGlobalLastPosition(target, target.globalPosition());
528
0
        if (target.pressure() < 0)
529
0
            setPressure(target, 1);
530
0
        break;
531
0
    }
532
533
0
    setState(target, other.state());
534
0
    setPosition(target, other.position());
535
0
    setScenePosition(target, other.scenePosition());
536
0
    setGlobalPosition(target, other.globalPosition());
537
0
    setEllipseDiameters(target, other.ellipseDiameters());
538
0
    setRotation(target, other.rotation());
539
0
    setVelocity(target, other.velocity());
540
0
    setUniqueId(target, other.uniqueId()); // for TUIO
541
0
}
542
543
/*! \internal
544
    Set the timestamp from the event that updated this point's positions,
545
    and calculate a new value for velocity().
546
547
    The velocity calculation is done here because none of the QPointerEvent
548
    subclass constructors take the timestamp directly, and because
549
    QGuiApplication traditionally constructs an event first and then sets its
550
    timestamp (see for example QGuiApplicationPrivate::processMouseEvent()).
551
552
    This function looks up the corresponding instance in QPointingDevicePrivate::activePoints,
553
    and assumes that its timestamp() still holds the previous time when this point
554
    was updated, its velocity() holds this point's last-known velocity, and
555
    its globalPosition() and globalLastPosition() hold this point's current
556
    and previous positions, respectively.  We assume timestamps are in milliseconds.
557
558
    The velocity calculation is skipped if the platform has promised to
559
    provide velocities already by setting the QInputDevice::Velocity capability.
560
*/
561
void QMutableEventPoint::setTimestamp(QEventPoint &p, ulong t)
562
0
{
563
    // On mouse press, if the mouse has moved from its last-known location,
564
    // QGuiApplicationPrivate::processMouseEvent() sends first a mouse move and
565
    // then a press. Both events will get the same timestamp. So we need to set
566
    // the press timestamp and position even when the timestamp isn't advancing,
567
    // but skip setting lastTimestamp and velocity because those need a time delta.
568
0
    if (p.d) {
569
0
        if (p.state() == QEventPoint::State::Pressed) {
570
0
            p.d->pressTimestamp = t;
571
0
            p.d->globalPressPos = p.d->globalPos;
572
0
        }
573
0
        if (p.d->timestamp == t)
574
0
            return;
575
0
    }
576
0
    detach(p);
577
0
    if (p.device()) {
578
        // get the persistent instance out of QPointingDevicePrivate::activePoints
579
        // (which sometimes might be the same as this instance)
580
0
        QEventPointPrivate *pd = QPointingDevicePrivate::get(
581
0
                    const_cast<QPointingDevice *>(p.d->device))->pointById(p.id())->eventPoint.d.get();
582
0
        if (t > pd->timestamp) {
583
0
            pd->lastTimestamp = pd->timestamp;
584
0
            pd->timestamp = t;
585
0
            if (p.state() == QEventPoint::State::Pressed)
586
0
                pd->pressTimestamp = t;
587
0
            if (pd->lastTimestamp > 0 && !p.device()->capabilities().testFlag(QInputDevice::Capability::Velocity)) {
588
                // calculate instantaneous velocity according to time and distance moved since the previous point
589
0
                QVector2D newVelocity = QVector2D(pd->globalPos - pd->globalLastPos) / (t - pd->lastTimestamp) * 1000;
590
                // VERY simple kalman filter: does a weighted average
591
                // where the older velocities get less and less significant
592
0
                static const float KalmanGain = 0.7f;
593
0
                pd->velocity = newVelocity * KalmanGain + pd->velocity * (1.0f - KalmanGain);
594
0
                qCDebug(lcPointerVel) << "velocity" << newVelocity << "filtered" << pd->velocity <<
595
0
                                         "based on movement" << pd->globalLastPos << "->" << pd->globalPos <<
596
0
                                         "over time" << pd->lastTimestamp << "->" << pd->timestamp;
597
0
            }
598
0
            if (p.d != pd) {
599
0
                p.d->lastTimestamp = pd->lastTimestamp;
600
0
                p.d->velocity = pd->velocity;
601
0
            }
602
0
        }
603
0
    }
604
0
    p.d->timestamp = t;
605
0
}
606
607
/*!
608
    \fn void QMutableEventPoint::setPosition(QPointF pos)
609
    \internal
610
611
    Sets the localized position.
612
    Often events need to be localized before delivery to specific widgets or
613
    items. This can be done directly, or in a copy (for which we have a copy
614
    constructor), depending on whether the original point needs to be retained.
615
    Usually it's calculated by mapping scenePosition() to the target anyway.
616
*/
617
618
QT_END_NAMESPACE
619
620
#include "moc_qeventpoint.cpp"