From b8bb9d7561b6be0557cde93ba3c5390a50f14087 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 24 Apr 2012 10:51:59 +0300 Subject: [PATCH] Make touch event delivery in the canvas reentrant At the moment the unlikely case of spinning the event loop from a QQuickItem event handler for TouchBegin is not handled properly: It will end up not delivering the subsequent TouchUpdate and TouchEnd events to the item, leaving it in a state that should not normally happen. Change-Id: I668d065c9cf1a299bfd9268a9125d7a7e32f6786 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickcanvas.cpp | 11 +++-- tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp | 52 +++++++++++++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp index f4aa735..0410658 100644 --- a/src/quick/items/qquickcanvas.cpp +++ b/src/quick/items/qquickcanvas.cpp @@ -1493,14 +1493,19 @@ bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even touchEvent.setTouchPoints(eventPoints); touchEvent.setTimestamp(event->timestamp()); + for (int i = 0; i < matchingPoints.count(); ++i) + itemForTouchPointId[matchingPoints[i].id()] = item; + touchEvent.accept(); q->sendEvent(item, &touchEvent); if (touchEvent.isAccepted()) { - for (int i=0; iinsert(matchingPoints[i].id()); - } + } else { + for (int i = 0; i < matchingPoints.count(); ++i) + if (itemForTouchPointId.value(matchingPoints[i].id()) == item) + itemForTouchPointId.remove(matchingPoints[i].id()); } } } diff --git a/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp b/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp index ca2165d..ed3dceb 100644 --- a/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp +++ b/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp @@ -144,7 +144,8 @@ class TestTouchItem : public QQuickRectangle Q_OBJECT public: TestTouchItem(QQuickItem *parent = 0) - : QQuickRectangle(parent), acceptEvents(true), mousePressId(0) + : QQuickRectangle(parent), acceptEvents(true), mousePressId(0), + spinLoopWhenPressed(false), touchEventCount(0) { border()->setWidth(1); setAcceptedMouseButtons(Qt::LeftButton); @@ -164,17 +165,29 @@ public: mousePressNum = 0; } + void clearTouchEventCounter() + { + touchEventCount = 0; + } + bool acceptEvents; TouchEventData lastEvent; int mousePressId; + bool spinLoopWhenPressed; + int touchEventCount; + protected: virtual void touchEvent(QTouchEvent *event) { if (!acceptEvents) { event->ignore(); return; } + ++touchEventCount; lastEvent = makeTouchData(event->type(), event->window(), event->touchPointStates(), event->touchPoints()); event->accept(); + if (spinLoopWhenPressed && event->touchPointStates().testFlag(Qt::TouchPointPressed)) { + QCoreApplication::processEvents(); + } } virtual void mousePressEvent(QMouseEvent *) { @@ -230,6 +243,7 @@ private slots: void touchEvent_propagation(); void touchEvent_propagation_data(); void touchEvent_cancel(); + void touchEvent_reentrant(); void clearCanvas(); @@ -540,6 +554,42 @@ void tst_qquickcanvas::touchEvent_cancel() delete canvas; } +void tst_qquickcanvas::touchEvent_reentrant() +{ + TestTouchItem::clearMousePressCounter(); + + QQuickCanvas *canvas = new QQuickCanvas; + canvas->resize(250, 250); + canvas->setPos(100, 100); + canvas->show(); + + TestTouchItem *item = new TestTouchItem(canvas->rootItem()); + + item->spinLoopWhenPressed = true; // will call processEvents() from the touch handler + + item->setPos(QPointF(50, 50)); + item->setSize(QSizeF(150, 150)); + QPointF pos(60, 60); + + // None of these should commit from the dtor. + QTest::QTouchEventSequence press = QTest::touchEvent(canvas, touchDevice, false).press(0, pos.toPoint(), canvas); + pos += QPointF(2, 2); + QTest::QTouchEventSequence move = QTest::touchEvent(canvas, touchDevice, false).move(0, pos.toPoint(), canvas); + QTest::QTouchEventSequence release = QTest::touchEvent(canvas, touchDevice, false).release(0, pos.toPoint(), canvas); + + // Now commit (i.e. call QWindowSystemInterface::handleTouchEvent), but do not process the events yet. + press.commit(false); + move.commit(false); + release.commit(false); + + QCoreApplication::processEvents(); + + QTRY_COMPARE(item->touchEventCount, 3); + + delete item; + delete canvas; +} + void tst_qquickcanvas::clearCanvas() { QQuickCanvas *canvas = new QQuickCanvas; -- 2.7.4