Coverage Report

Created: 2026-02-14 07:09

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