summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qguiapplication.cpp
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-09-03 21:34:31 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2020-09-16 11:33:03 +0200
commit2692237bb1b0c0f50b7cc5d920eb8ab065063d47 (patch)
tree8f114b0313fcf04f128e265efaaabc8a130b2f6e /src/gui/kernel/qguiapplication.cpp
parent4ceef8a3a6d64474344cac4a6ed4b32b09d38367 (diff)
Track grab state in QPointingDevicePrivate::activePoints
QQuickEventPoint instances were very long-lived and got reused from one event to the next. That was initially done because they were "heavy" QObjects; but it also became useful to store state in them between events. But this is in conflict with the ubiquitous event replay code that assumes it's OK to hold an event instance (especially a QMouseEvent) for any length of time, and then send it to some widget, item or window. Clearly QEventPoints must be stored in the QPointerEvent, if we are to avoid the need for workarounds to keep such old code working. And now they have d-pointers, so copying is cheap. But replay code will need to detach() their QEventPoints now. QEventPoint is useful as an object to hold state, but we now store the truly persistent state separately in an EventPointData struct, in QPointingDevicePrivate::activePoints. Incoming events merely update the persistent points, then we deliver those instead. Thus when event handler code modifies state, it will be remembered even when the delivery is done and the QPA event is destroyed. This gets us a step closer to supporting multiple simultaneous mice. Within pointer events, the points are moved up to QPointerEvent itself: QList<QEventPoint> m_points; This means pointCount(), point(int i) and points() can be non-virtual. However in any QSinglePointEvent, the list only contains one point. We hope that pessimization is worthwhile for the sake of removing virtual functions, simplifying code in event classes themselves, and enabling the use of the range-for loop over points() with any kind of QPointerEvent, not just QTouchEvent. points() is a nicer API for the sake of range-for looping; but point() is more suited to being non-const. In QML it's expected to be OK to emit a signal with a QPointerEvent by value: that will involve copying the event. But QEventPoint instances are explicitly shared, so calling setAccepted() modifies the instance in activePoints (EventPointData.eventPoint.d->accept); and the grabbers are stored separately and thus preserved between events. In code such as MouseArea { onPressed: mouse.accepted = false } we can either continue to emit the QQuickMouseEvent wrapper or perhaps QEvent::setAccepted() could become virtual and set the eventpoint's accepted flag instead, so that it will survive after the event copy that QML sees is discarded. The grabChanged() signal is useful to keep QQuickWindow informed when items or handlers change exclusive or passive grabbers. When a release happens at a different location than the last move event, Qt synthesizes an additional move. But it would be "boring" if QEventPoint::lastXPosition() accessors in any released eventpoint always returned the same as the current QEventPoint::xPosition()s just because of that; and it would mean that the velocity() must always be zero on release, which would make it hard to use the final velocity to drive an animation. So now we expect the lastPositions to be different than current positions in a released eventpoint. De-inline some functions whose implementations might be subject to change later on. Improve documentation. Since we have an accessor for pressTimestamp(), we might as well add one for timestamp() too. That way users get enough information to calculate instantaneous velocity, since the plan is for velocity() to be somewhat smoothed. Change-Id: I2733d847139a1b1bea33c00275459dcd2a145ffc Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/gui/kernel/qguiapplication.cpp')
-rw-r--r--src/gui/kernel/qguiapplication.cpp249
1 files changed, 101 insertions, 148 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 15955e2287..2bbeef4c30 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -59,6 +59,7 @@
#include <QtCore/qmutex.h>
#include <QtCore/private/qthread_p.h>
#include <QtCore/private/qlocking_p.h>
+#include <QtCore/private/qflatmap_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qnumeric.h>
@@ -155,7 +156,7 @@ bool QGuiApplicationPrivate::highDpiScalingUpdated = false;
QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow;
-QList<QGuiApplicationPrivate::TabletPointData> QGuiApplicationPrivate::tabletDevicePoints;
+QList<QGuiApplicationPrivate::TabletPointData> QGuiApplicationPrivate::tabletDevicePoints; // TODO remove
QPlatformIntegration *QGuiApplicationPrivate::platform_integration = nullptr;
QPlatformTheme *QGuiApplicationPrivate::platform_theme = nullptr;
@@ -177,10 +178,7 @@ QString *QGuiApplicationPrivate::desktopFileName = nullptr;
QPalette *QGuiApplicationPrivate::app_pal = nullptr; // default application palette
-ulong QGuiApplicationPrivate::mousePressTime = 0;
Qt::MouseButton QGuiApplicationPrivate::mousePressButton = Qt::NoButton;
-int QGuiApplicationPrivate::mousePressX = 0; // TODO use QPointF and store it in QPointingDevicePrivate
-int QGuiApplicationPrivate::mousePressY = 0;
static int mouseDoubleClickDistance = -1;
static int touchDoubleTapDistance = -1;
@@ -716,8 +714,6 @@ QGuiApplication::~QGuiApplication()
QGuiApplicationPrivate::highDpiScalingUpdated = false;
QGuiApplicationPrivate::currentDragWindow = nullptr;
QGuiApplicationPrivate::tabletDevicePoints.clear();
- QGuiApplicationPrivate::mousePressTime = 0;
- QGuiApplicationPrivate::mousePressX = QGuiApplicationPrivate::mousePressY = 0;
}
QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv, int flags)
@@ -1195,6 +1191,7 @@ QString QGuiApplication::platformName()
}
Q_LOGGING_CATEGORY(lcQpaPluginLoading, "qt.qpa.plugin");
+Q_LOGGING_CATEGORY(lcPtrDispatch, "qt.pointer.dispatch");
static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
{
@@ -2132,9 +2129,13 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
QEvent::Type type = QEvent::None;
Qt::MouseButton button = Qt::NoButton;
QWindow *window = e->window.data();
+ const QPointingDevice *device = static_cast<const QPointingDevice *>(e->device);
+ Q_ASSERT(device);
+ QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(device));
bool positionChanged = QGuiApplicationPrivate::lastCursorPosition != e->globalPos;
bool mouseMove = false;
bool mousePress = false;
+ QPointF globalPoint = e->globalPos;
if (e->enhancedMouseEvent()) {
type = e->buttonType;
@@ -2207,27 +2208,24 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
modifier_buttons = e->modifiers;
QPointF localPoint = e->localPos;
- QPointF globalPoint = e->globalPos;
- const QPointF lastGlobalPosition = QGuiApplicationPrivate::lastCursorPosition;
bool doubleClick = false;
+ auto persistentEPD = devPriv->pointById(0);
+ const auto &persistentPoint = QMutableEventPoint::from(persistentEPD->eventPoint);
if (mouseMove) {
QGuiApplicationPrivate::lastCursorPosition = globalPoint;
const auto doubleClickDistance = (e->device && e->device->type() == QInputDevice::DeviceType::Mouse ?
mouseDoubleClickDistance : touchDoubleTapDistance);
- if (qAbs(globalPoint.x() - mousePressX) > doubleClickDistance ||
- qAbs(globalPoint.y() - mousePressY) > doubleClickDistance)
+ const auto pressPos = persistentPoint.globalPressPosition();
+ if (qAbs(globalPoint.x() - pressPos.x()) > doubleClickDistance ||
+ qAbs(globalPoint.y() - pressPos.y()) > doubleClickDistance)
mousePressButton = Qt::NoButton;
} else {
mouse_buttons = e->buttons;
if (mousePress) {
ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
- doubleClick = e->timestamp - mousePressTime < doubleClickInterval && button == mousePressButton;
- mousePressTime = e->timestamp;
+ doubleClick = e->timestamp - persistentPoint.pressTimestamp() < doubleClickInterval && button == mousePressButton;
mousePressButton = button;
- const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint();
- mousePressX = point.x();
- mousePressY = point.y();
}
}
@@ -2252,7 +2250,6 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
if (!window)
return;
- const QPointingDevice *device = static_cast<const QPointingDevice *>(e->device);
#ifndef QT_NO_CURSOR
if (!e->synthetic()) {
if (const QScreen *screen = window->screen())
@@ -2268,11 +2265,8 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
#endif
QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, e->buttons, e->modifiers, e->source, device);
+ // ev now contains a detached copy of the QEventPoint from QPointingDevicePrivate::activePoints
ev.setTimestamp(e->timestamp);
- QMutableEventPoint &mutPt = QMutableSinglePointEvent::from(ev).mutablePoint();
- mutPt.setGlobalLastPosition(lastGlobalPosition);
- mutPt.setGlobalPressPosition(QPointF(mousePressX, mousePressY));
-
if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) {
// a modal window is blocking this window, don't allow mouse events through
return;
@@ -2330,6 +2324,10 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
QGuiApplication::sendSpontaneousEvent(window, &dblClickEvent);
}
}
+ if (type == QEvent::MouseButtonRelease && e->buttons == Qt::NoButton) {
+ ev.setExclusiveGrabber(persistentPoint, nullptr);
+ ev.clearPassiveGrabbers(persistentPoint);
+ }
}
void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e)
@@ -2806,38 +2804,28 @@ void QGuiApplicationPrivate::processContextMenuEvent(QWindowSystemInterfacePriva
}
#endif
-Q_GUI_EXPORT size_t qHash(const QGuiApplicationPrivate::ActiveTouchPointsKey &k, size_t seed)
-{
- return (qHash(k.device) + k.touchPointId) ^ seed;
-}
-
-Q_GUI_EXPORT bool operator==(const QGuiApplicationPrivate::ActiveTouchPointsKey &a,
- const QGuiApplicationPrivate::ActiveTouchPointsKey &b)
-{
- return a.device == b.device
- && a.touchPointId == b.touchPointId;
-}
-
void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::TouchEvent *e)
{
- QGuiApplicationPrivate *d = self;
modifier_buttons = e->modifiers;
- const QPointingDevice *device = static_cast<const QPointingDevice *>(e->device);
+ QPointingDevice *device = const_cast<QPointingDevice *>(static_cast<const QPointingDevice *>(e->device));
+ QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(device);
if (e->touchType == QEvent::TouchCancel) {
// The touch sequence has been canceled (e.g. by the compositor).
// Send the TouchCancel to all windows with active touches and clean up.
QTouchEvent touchEvent(QEvent::TouchCancel, device, e->modifiers);
touchEvent.setTimestamp(e->timestamp);
- QHash<ActiveTouchPointsKey, ActiveTouchPointsValue>::const_iterator it
- = self->activeTouchPoints.constBegin(), ite = self->activeTouchPoints.constEnd();
QSet<QWindow *> windowsNeedingCancel;
- while (it != ite) {
- QWindow *w = it->window.data();
+
+ for (auto &epd : devPriv->activePoints.values()) {
+ auto &mut = QMutableEventPoint::from(const_cast<QEventPoint &>(epd.eventPoint));
+ QWindow *w = mut.window();
if (w)
windowsNeedingCancel.insert(w);
- ++it;
+ mut.setWindow(nullptr);
+ mut.setTarget(nullptr);
}
+
for (QSet<QWindow *>::const_iterator winIt = windowsNeedingCancel.constBegin(),
winItEnd = windowsNeedingCancel.constEnd(); winIt != winItEnd; ++winIt) {
QGuiApplication::sendSpontaneousEvent(*winIt, &touchEvent);
@@ -2863,7 +2851,6 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
}
self->synthesizedMousePoints.clear();
}
- self->activeTouchPoints.clear();
self->lastTouchType = e->touchType;
return;
}
@@ -2874,110 +2861,88 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
self->lastTouchType = e->touchType;
- QWindow *window = e->window.data();
- // TODO get rid of this QPair; we don't need to accumulate combined states here anymore
- typedef QPair<QEventPoint::States, QList<QEventPoint> > StatesAndTouchPoints;
- QHash<QWindow *, StatesAndTouchPoints> windowsNeedingEvents;
bool stationaryTouchPointChangedProperty = false;
-
- for (int i = 0; i < e->points.count(); ++i) {
- QMutableEventPoint touchPoint = QMutableEventPoint::from(e->points[i]);
-
+ QPointer<QWindow> window = e->window; // the platform hopefully tells us which window received the event
+ QVarLengthArray<QMutableTouchEvent, 2> touchEvents;
+
+ // For each temporary QEventPoint from the QPA TouchEvent:
+ // - update the persistent QEventPoint in QPointingDevicePrivate::activePoints with current values
+ // - determine which window to deliver it to
+ // - add it to the QTouchEvent instance for that window (QMutableTouchEvent::target() will be QWindow*, for now)
+ for (auto &tempPt : e->points) {
// update state
- QPointer<QWindow> w;
- QEventPoint previousTouchPoint;
- ActiveTouchPointsKey touchInfoKey(device, touchPoint.id());
- ActiveTouchPointsValue &touchInfo = d->activeTouchPoints[touchInfoKey];
- switch (touchPoint.state()) {
+ auto epd = devPriv->pointById(tempPt.id());
+ auto &mut = QMutableEventPoint::from(const_cast<QEventPoint &>(epd->eventPoint));
+ epd->eventPoint.setAccepted(false);
+ switch (tempPt.state()) {
case QEventPoint::State::Pressed:
- if (e->device && e->device->type() == QInputDevice::DeviceType::TouchPad) {
- // on touch-pads, send all touch points to the same widget
- w = d->activeTouchPoints.isEmpty()
- ? QPointer<QWindow>()
- : d->activeTouchPoints.constBegin().value().window;
- }
-
- if (!w) {
- // determine which window this event will go to
- if (!window)
- window = QGuiApplication::topLevelAt(touchPoint.globalPosition().toPoint());
- if (!window)
- continue;
- w = window;
- }
-
- touchInfo.window = w;
- touchPoint.setGlobalPressPosition(touchPoint.globalPosition());
- touchPoint.setGlobalLastPosition(touchPoint.globalPosition());
- if (touchPoint.pressure() < 0)
- touchPoint.setPressure(1);
-
- touchInfo.touchPoint = touchPoint;
+ // On touchpads, send all touch points to the same window.
+ if (!window && e->device && e->device->type() == QInputDevice::DeviceType::TouchPad)
+ window = devPriv->firstActiveWindow();
+ // If the QPA event didn't tell us which window, find the one under the touchpoint position.
+ if (!window)
+ window = QGuiApplication::topLevelAt(tempPt.globalPosition().toPoint());
+ mut.setWindow(window);
break;
case QEventPoint::State::Released:
- w = touchInfo.window;
- if (!w)
- continue;
-
- previousTouchPoint = touchInfo.touchPoint;
- touchPoint.setGlobalPressPosition(previousTouchPoint.globalPressPosition());
- touchPoint.setGlobalLastPosition(previousTouchPoint.globalPosition());
- touchPoint.setPressure(0);
-
+ if (Q_UNLIKELY(window != mut.window())) {
+ qCWarning(lcPtrDispatch) << "delivering touch release to same window" << mut.window() << "not" << window.data();
+ window = mut.window();
+ }
break;
- default:
- w = touchInfo.window;
- if (!w)
- continue;
-
- previousTouchPoint = touchInfo.touchPoint;
- touchPoint.setGlobalPressPosition(previousTouchPoint.globalPressPosition());
- touchPoint.setGlobalLastPosition(previousTouchPoint.globalPosition());
- if (touchPoint.pressure() < 0)
- touchPoint.setPressure(1);
-
- // Stationary points might not be delivered down to the receiving item
- // and get their position transformed, keep the old values instead.
- if (touchPoint.state() == QEventPoint::State::Stationary) {
- if (touchInfo.touchPoint.velocity() != touchPoint.velocity()) {
- touchInfo.touchPoint.setVelocity(touchPoint.velocity());
- touchPoint.setStationaryWithModifiedProperty();
- stationaryTouchPointChangedProperty = true;
- }
- if (!qFuzzyCompare(touchInfo.touchPoint.pressure(), touchPoint.pressure())) {
- touchInfo.touchPoint.setPressure(touchPoint.pressure());
- touchPoint.setStationaryWithModifiedProperty();
- stationaryTouchPointChangedProperty = true;
- }
- } else {
- touchInfo.touchPoint = touchPoint;
+ default: // update or stationary
+ if (Q_UNLIKELY(window != mut.window())) {
+ qCWarning(lcPtrDispatch) << "delivering touch update to same window" << mut.window() << "not" << window.data();
+ window = mut.window();
}
break;
}
+ // If we somehow still don't have a window, we can't deliver this touchpoint. (should never happen)
+ if (Q_UNLIKELY(!window)) {
+ qCWarning(lcPtrDispatch) << "skipping" << &tempPt << ": no target window";
+ continue;
+ }
+ mut.updateFrom(tempPt);
- Q_ASSERT(w.data() != nullptr);
+ Q_ASSERT(window.data() != nullptr);
// make the *scene* position the same as the *global* position
- // Note: touchPoint is a reference to the one from activeTouchPoints, so we can modify it.
- touchPoint.setScenePosition(touchPoint.globalPosition());
+ mut.setScenePosition(tempPt.globalPosition());
+
+ // store the scene position as local position, for now
+ mut.setPosition(window->mapFromGlobal(tempPt.globalPosition()));
- StatesAndTouchPoints &maskAndPoints = windowsNeedingEvents[w.data()];
- maskAndPoints.first |= touchPoint.state();
- maskAndPoints.second.append(touchPoint);
+ // setTimeStamp has side effects, so we do it last
+ mut.setTimestamp(e->timestamp);
+
+ // add the touchpoint to the event that will be delivered to the window
+ bool added = false;
+ for (QMutableTouchEvent &ev : touchEvents) {
+ if (ev.target() == window.data()) {
+ ev.addPoint(mut);
+ added = true;
+ break;
+ }
+ }
+ if (!added) {
+ QMutableTouchEvent mte(e->touchType, device, e->modifiers, {mut});
+ mte.setTimestamp(e->timestamp);
+ mte.setTarget(window.data());
+ touchEvents.append(mte);
+ }
}
- if (windowsNeedingEvents.isEmpty())
+ if (touchEvents.isEmpty())
return;
- QHash<QWindow *, StatesAndTouchPoints>::ConstIterator it = windowsNeedingEvents.constBegin();
- const QHash<QWindow *, StatesAndTouchPoints>::ConstIterator end = windowsNeedingEvents.constEnd();
- for (; it != end; ++it) {
- QWindow *w = it.key();
+ for (QMutableTouchEvent &touchEvent : touchEvents) {
+ QWindow *window = static_cast<QWindow *>(touchEvent.target());
+ auto &points = touchEvent.points();
QEvent::Type eventType;
- switch (it.value().first) {
+ switch (touchEvent.touchPointStates()) {
case QEventPoint::State::Pressed:
eventType = QEvent::TouchBegin;
break;
@@ -2994,32 +2959,22 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
break;
}
- if (w->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) {
+ if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) {
// a modal window is blocking this window, don't allow touch events through
- // QTBUG-37371 temporary fix; TODO: revisit in 5.4 when we have a forwarding solution
- if (eventType == QEvent::TouchEnd) {
+ // QTBUG-37371 temporary fix; TODO: revisit when we have a forwarding solution
+ if (touchEvent.type() == QEvent::TouchEnd) {
// but don't leave dangling state: e.g.
// QQuickWindowPrivate::itemForTouchPointId needs to be cleared.
- QTouchEvent touchEvent(QEvent::TouchCancel,
- device,
- e->modifiers);
+ QTouchEvent touchEvent(QEvent::TouchCancel, device, e->modifiers);
touchEvent.setTimestamp(e->timestamp);
- QGuiApplication::sendSpontaneousEvent(w, &touchEvent);
+ QGuiApplication::sendSpontaneousEvent(window, &touchEvent);
}
continue;
}
- const auto &touchpoints = it.value().second;
- QMutableTouchEvent touchEvent(eventType, device, e->modifiers, touchpoints);
- touchEvent.setTimestamp(e->timestamp);
-
- for (QEventPoint &pt : touchEvent.touchPoints()) {
- auto &touchPoint = QMutableEventPoint::from(pt);
- touchPoint.setPosition(w->mapFromGlobal(touchPoint.globalPosition()));
- }
+ QGuiApplication::sendSpontaneousEvent(window, &touchEvent);
- QGuiApplication::sendSpontaneousEvent(w, &touchEvent);
if (!e->synthetic() && !touchEvent.isAccepted() && qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) {
// exclude devices which generate their own mouse events
if (!(touchEvent.device()->capabilities().testFlag(QInputDevice::Capability::MouseEmulation))) {
@@ -3042,15 +2997,15 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
Qt::MouseButton button = mouseType == QEvent::MouseMove ? Qt::NoButton : Qt::LeftButton;
Qt::MouseButtons buttons = mouseType == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton;
- const auto &points = touchEvent.touchPoints();
for (const QEventPoint &touchPoint : points) {
if (touchPoint.id() == m_fakeMouseSourcePointId) {
if (eventType != QEvent::TouchEnd)
- self->synthesizedMousePoints.insert(w, SynthesizedMouseData(
- touchPoint.position(), touchPoint.globalPosition(), w));
+ self->synthesizedMousePoints.insert(window, SynthesizedMouseData(
+ touchPoint.position(), touchPoint.globalPosition(), window));
// All touch events that are not accepted by the application will be translated to
// left mouse button events instead (see AA_SynthesizeMouseForUnhandledTouchEvents docs).
- QWindowSystemInterfacePrivate::MouseEvent fake(w, e->timestamp,
+ // TODO why go through QPA? Why not just send a QMouseEvent right from here?
+ QWindowSystemInterfacePrivate::MouseEvent fake(window, e->timestamp,
touchPoint.position(),
touchPoint.globalPosition(),
buttons,
@@ -3069,13 +3024,11 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
}
}
- // Remove released points from the hash table only after the event is
- // delivered. When the receiver is a widget, QApplication will access
- // activeTouchPoints during delivery and therefore nothing can be removed
- // before sending the event.
+ // Remove released points from QPointingDevicePrivate::activePoints only after the event is
+ // delivered. Widgets and Qt Quick are allowed to access them at any time before this.
for (const QEventPoint &touchPoint : e->points) {
if (touchPoint.state() == QEventPoint::State::Released)
- d->activeTouchPoints.remove(ActiveTouchPointsKey(device, touchPoint.id()));
+ devPriv->removePointById(touchPoint.id());
}
}