Clear only one device from g_pointIdMap when all points released

The new test tst_QTouchEvent::touchOnMultipleTouchscreens()
needs the touchpoint IDs to be predictable, but another test currently
has a QEXPECT_FAIL; without release events, g_pointIdMap continued to
hold the touchpoints that were there when the test failed.  So it's
necessary to add QWindowSystemInterfacePrivate::clearPointIdMap()
to be able to call it in the test cleanup function.

Fixes: QTBUG-73830
Change-Id: Ia6a70d028be95cd2b6676db6363ec408c0b116bc
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
Shawn Rutledge 2019-02-20 13:55:03 +01:00
parent 7e60858cbc
commit 1500d2283e
3 changed files with 174 additions and 2 deletions

View File

@ -695,13 +695,29 @@ QList<QTouchEvent::TouchPoint>
}
if (states == Qt::TouchPointReleased) {
g_nextPointId = 1;
g_pointIdMap->clear();
// All points on deviceId have been released.
// Remove all points associated with that device from g_pointIdMap.
// (On other devices, some touchpoints might still be pressed.
// But this function is only called with points from one device at a time.)
for (auto it = g_pointIdMap->begin(); it != g_pointIdMap->end();) {
if (it.key() >> 32 == quint64(deviceId))
it = g_pointIdMap->erase(it);
else
++it;
}
if (g_pointIdMap->isEmpty())
g_nextPointId = 1;
}
return touchPoints;
}
void QWindowSystemInterfacePrivate::clearPointIdMap()
{
g_pointIdMap->clear();
g_nextPointId = 1;
}
QList<QWindowSystemInterface::TouchPoint>
QWindowSystemInterfacePrivate::toNativeTouchPoints(const QList<QTouchEvent::TouchPoint>& pointList,
const QWindow *window)

View File

@ -537,6 +537,7 @@ public:
static QList<QWindowSystemInterface::TouchPoint>
toNativeTouchPoints(const QList<QTouchEvent::TouchPoint>& pointList,
const QWindow *window);
static void clearPointIdMap();
static void installWindowSystemEventHandler(QWindowSystemEventHandler *handler);
static void removeWindowSystemEventhandler(QWindowSystemEventHandler *handler);

View File

@ -204,6 +204,7 @@ private slots:
void basicRawEventTranslationOfIds();
void multiPointRawEventTranslationOnTouchScreen();
void multiPointRawEventTranslationOnTouchPad();
void touchOnMultipleTouchscreens();
void deleteInEventHandler();
void deleteInRawEventTranslation();
void crashInQGraphicsSceneAfterNotHandlingTouchBegin();
@ -213,11 +214,13 @@ private slots:
private:
QTouchDevice *touchScreenDevice;
QTouchDevice *secondaryTouchScreenDevice;
QTouchDevice *touchPadDevice;
};
tst_QTouchEvent::tst_QTouchEvent()
: touchScreenDevice(QTest::createTouchDevice())
, secondaryTouchScreenDevice(QTest::createTouchDevice())
, touchPadDevice(QTest::createTouchDevice(QTouchDevice::TouchPad))
{
}
@ -225,6 +228,7 @@ tst_QTouchEvent::tst_QTouchEvent()
void tst_QTouchEvent::cleanup()
{
QVERIFY(QGuiApplication::topLevelWindows().isEmpty());
QWindowSystemInterfacePrivate::clearPointIdMap();
}
void tst_QTouchEvent::qPointerUniqueId()
@ -951,6 +955,157 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchScreen()
}
}
void tst_QTouchEvent::touchOnMultipleTouchscreens()
{
tst_QTouchEventWidget touchWidget;
touchWidget.setWindowTitle(QTest::currentTestFunction());
touchWidget.setAttribute(Qt::WA_AcceptTouchEvents);
touchWidget.setGeometry(100, 100, 400, 300);
touchWidget.show();
QVERIFY(QTest::qWaitForWindowExposed(&touchWidget));
QWindow *window = touchWidget.windowHandle();
QPointF pos = touchWidget.rect().center();
QPointF screenPos = touchWidget.mapToGlobal(pos.toPoint());
QPointF delta(10, 10);
QRectF screenGeometry = QApplication::desktop()->screenGeometry(&touchWidget);
QVector<QTouchEvent::TouchPoint> rawTouchPoints(3);
rawTouchPoints[0].setId(0);
rawTouchPoints[1].setId(10);
rawTouchPoints[2].setId(11);
// this should be translated to a TouchBegin
rawTouchPoints[0].setState(Qt::TouchPointPressed);
rawTouchPoints[0].setScreenPos(screenPos);
rawTouchPoints[0].setNormalizedPos(normalized(rawTouchPoints[0].pos(), screenGeometry));
rawTouchPoints[0].setRawScreenPositions({{12, 34}, {56, 78}});
ulong timestamp = 1234;
QList<QWindowSystemInterface::TouchPoint> nativeTouchPoints =
QWindowSystemInterfacePrivate::toNativeTouchPoints(QList<QTouchEvent::TouchPoint>() << rawTouchPoints[0], window);
QWindowSystemInterface::handleTouchEvent(window, timestamp, touchScreenDevice, nativeTouchPoints);
QCoreApplication::processEvents();
QVERIFY(touchWidget.seenTouchBegin);
QVERIFY(!touchWidget.seenTouchUpdate);
QVERIFY(!touchWidget.seenTouchEnd);
QCOMPARE(touchWidget.touchBeginPoints.count(), 1);
QCOMPARE(touchWidget.timestamp, timestamp);
QTouchEvent::TouchPoint touchBeginPoint = touchWidget.touchBeginPoints.first();
const int touchPointId = (QTouchDevicePrivate::get(touchScreenDevice)->id << 24) + 1;
const int secTouchPointId = (QTouchDevicePrivate::get(secondaryTouchScreenDevice)->id << 24) + 2;
QCOMPARE(touchBeginPoint.id(), touchPointId);
QCOMPARE(touchBeginPoint.state(), rawTouchPoints[0].state());
QCOMPARE(touchBeginPoint.pos(), pos);
// press a point on secondaryTouchScreenDevice
touchWidget.seenTouchBegin = false;
rawTouchPoints[1].setState(Qt::TouchPointPressed);
rawTouchPoints[1].setScreenPos(screenPos);
rawTouchPoints[1].setNormalizedPos(normalized(rawTouchPoints[1].pos(), screenGeometry));
rawTouchPoints[1].setRawScreenPositions({{90, 100}, {110, 120}});
nativeTouchPoints =
QWindowSystemInterfacePrivate::toNativeTouchPoints(QList<QTouchEvent::TouchPoint>() << rawTouchPoints[1], window);
QWindowSystemInterface::handleTouchEvent(window, ++timestamp, secondaryTouchScreenDevice, nativeTouchPoints);
QCoreApplication::processEvents();
QVERIFY(!touchWidget.seenTouchEnd);
QCOMPARE(touchWidget.touchBeginPoints.count(), 1);
QCOMPARE(touchWidget.timestamp, timestamp);
touchBeginPoint = touchWidget.touchBeginPoints[0];
QCOMPARE(touchBeginPoint.id(), (QTouchDevicePrivate::get(secondaryTouchScreenDevice)->id << 24) + 2);
QCOMPARE(touchBeginPoint.state(), rawTouchPoints[1].state());
QCOMPARE(touchBeginPoint.pos(), pos);
// press another point on secondaryTouchScreenDevice
touchWidget.seenTouchBegin = false;
rawTouchPoints[2].setState(Qt::TouchPointPressed);
rawTouchPoints[2].setScreenPos(screenPos);
rawTouchPoints[2].setNormalizedPos(normalized(rawTouchPoints[2].pos(), screenGeometry));
rawTouchPoints[2].setRawScreenPositions({{130, 140}, {150, 160}});
nativeTouchPoints =
QWindowSystemInterfacePrivate::toNativeTouchPoints(QList<QTouchEvent::TouchPoint>() << rawTouchPoints[2], window);
QWindowSystemInterface::handleTouchEvent(window, ++timestamp, secondaryTouchScreenDevice, nativeTouchPoints);
QCoreApplication::processEvents();
QVERIFY(!touchWidget.seenTouchEnd);
QCOMPARE(touchWidget.touchBeginPoints.count(), 1);
QCOMPARE(touchWidget.timestamp, timestamp);
touchBeginPoint = touchWidget.touchBeginPoints[0];
QCOMPARE(touchBeginPoint.id(), (QTouchDevicePrivate::get(secondaryTouchScreenDevice)->id << 24) + 3);
QCOMPARE(touchBeginPoint.state(), rawTouchPoints[2].state());
QCOMPARE(touchBeginPoint.pos(), pos);
// moving the first point should translate to TouchUpdate
rawTouchPoints[0].setState(Qt::TouchPointMoved);
rawTouchPoints[0].setScreenPos(screenPos + delta);
rawTouchPoints[0].setNormalizedPos(normalized(rawTouchPoints[0].pos(), screenGeometry));
nativeTouchPoints =
QWindowSystemInterfacePrivate::toNativeTouchPoints(QList<QTouchEvent::TouchPoint>() << rawTouchPoints[0], window);
QWindowSystemInterface::handleTouchEvent(window, ++timestamp, touchScreenDevice, nativeTouchPoints);
QCoreApplication::processEvents();
QVERIFY(touchWidget.seenTouchBegin);
QVERIFY(touchWidget.seenTouchUpdate);
QVERIFY(!touchWidget.seenTouchEnd);
QCOMPARE(touchWidget.touchUpdatePoints.count(), 1);
QTouchEvent::TouchPoint touchUpdatePoint = touchWidget.touchUpdatePoints.first();
QCOMPARE(touchUpdatePoint.id(), touchPointId);
QCOMPARE(touchUpdatePoint.state(), rawTouchPoints[0].state());
QCOMPARE(touchUpdatePoint.pos(), pos + delta);
// releasing the first point translates to TouchEnd
rawTouchPoints[0].setState(Qt::TouchPointReleased);
rawTouchPoints[0].setScreenPos(screenPos + delta + delta);
rawTouchPoints[0].setNormalizedPos(normalized(rawTouchPoints[0].pos(), screenGeometry));
nativeTouchPoints =
QWindowSystemInterfacePrivate::toNativeTouchPoints(QList<QTouchEvent::TouchPoint>() << rawTouchPoints[0], window);
QWindowSystemInterface::handleTouchEvent(window, ++timestamp, touchScreenDevice, nativeTouchPoints);
QCoreApplication::processEvents();
QVERIFY(touchWidget.seenTouchBegin);
QVERIFY(touchWidget.seenTouchUpdate);
QVERIFY(touchWidget.seenTouchEnd);
QCOMPARE(touchWidget.touchEndPoints.count(), 1);
QTouchEvent::TouchPoint touchEndPoint = touchWidget.touchEndPoints.first();
QCOMPARE(touchEndPoint.id(), touchPointId);
QCOMPARE(touchEndPoint.state(), rawTouchPoints[0].state());
QCOMPARE(touchEndPoint.pos(), pos + delta + delta);
// Widgets don't normally handle this case: if a TouchEnd was seen before, then
// WA_WState_AcceptedTouchBeginEvent will be false, and
// QApplicationPrivate::translateRawTouchEvent will ignore touch events that aren't TouchBegin.
// So we have to set it true. It _did_ in fact accept the touch begin from the secondary device,
// but it also got a TouchEnd from the primary device in the meantime.
touchWidget.setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent, true);
// Releasing one point on the secondary touchscreen does not yet generate TouchEnd.
touchWidget.seenTouchEnd = false;
touchWidget.touchEndPoints.clear();
rawTouchPoints[1].setState(Qt::TouchPointReleased);
rawTouchPoints[2].setState(Qt::TouchPointStationary);
nativeTouchPoints =
QWindowSystemInterfacePrivate::toNativeTouchPoints(QList<QTouchEvent::TouchPoint>() << rawTouchPoints[1] << rawTouchPoints[2], window);
QWindowSystemInterface::handleTouchEvent(window, ++timestamp, secondaryTouchScreenDevice, nativeTouchPoints);
QCoreApplication::processEvents();
QVERIFY(touchWidget.seenTouchBegin);
QVERIFY(touchWidget.seenTouchUpdate);
QVERIFY(!touchWidget.seenTouchEnd);
QCOMPARE(touchWidget.touchUpdatePoints.count(), 2);
QCOMPARE(touchWidget.touchUpdatePoints[0].id(), secTouchPointId);
QCOMPARE(touchWidget.touchUpdatePoints[1].id(), secTouchPointId + 1);
// releasing the last point on the secondary touchscreen translates to TouchEnd
touchWidget.seenTouchEnd = false;
rawTouchPoints[2].setState(Qt::TouchPointReleased);
nativeTouchPoints =
QWindowSystemInterfacePrivate::toNativeTouchPoints(QList<QTouchEvent::TouchPoint>() << rawTouchPoints[2], window);
QWindowSystemInterface::handleTouchEvent(window, ++timestamp, secondaryTouchScreenDevice, nativeTouchPoints);
QCoreApplication::processEvents();
QVERIFY(touchWidget.seenTouchBegin);
QVERIFY(touchWidget.seenTouchUpdate);
QVERIFY(touchWidget.seenTouchEnd);
QCOMPARE(touchWidget.touchEndPoints.count(), 1);
touchEndPoint = touchWidget.touchEndPoints.first();
QCOMPARE(touchEndPoint.id(), secTouchPointId + 1);
QCOMPARE(touchEndPoint.state(), rawTouchPoints[2].state());
}
void tst_QTouchEvent::multiPointRawEventTranslationOnTouchPad()
{
tst_QTouchEventWidget touchWidget;