From 3064c1bc8cc04a40f6e468033d73823987687d40 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sat, 10 Dec 2011 16:30:18 +0200 Subject: [PATCH] Remove redundant touch processing in QtGui and widgets. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The duplicated hash tables in QGuiApplicationPrivate and QApplicationPrivate are now unified into one single hash table in QGuiApplicationPrivate. This also reduced the number of lookups. The extra processing needed to keep the touch points' first/lastPos values in sync is now done only once, in QGuiApplication. This eliminates the performance penalty (for widget-based apps) that was introduced during the QPA migration. As an added bonus the patch adds support for touch events arriving simultaenously from multiple devices. This was broken before: As there is no guarantee that two devices/drivers will not send touch points with the same ID, using structures with only the ID as key is wrong. The proper key is composed of the device ID (that is, a QTouchDevice pointer) and the touch point ID. The exported internal function qt_translateRawTouchEvent() has been removed. This function cannot work properly in the QPA world: It injected touches into the widget subsystem (QApplication) only which is wrong, and would result in half-filled touch events due to not routing the injected data through QGuiApplication. Autotests using this function are migrated to QWindowSystemInterface::handleTouchEvent(). Change-Id: I7632781d77f9e0ac4626fd7c9933511c94492156 Reviewed-by: Samuel Rødal --- dist/changes-5.0.0 | 4 + src/gui/kernel/qguiapplication.cpp | 46 +++- src/gui/kernel/qguiapplication_p.h | 20 +- src/testlib/qtesttouch.h | 56 +++-- src/widgets/kernel/qapplication.cpp | 135 ++++-------- src/widgets/kernel/qapplication_p.h | 9 +- .../gui/kernel/qtouchevent/tst_qtouchevent.cpp | 233 ++++++++++++++++++--- .../kernel/qapplication/tst_qapplication.cpp | 126 +++++++++-- 8 files changed, 436 insertions(+), 193 deletions(-) diff --git a/dist/changes-5.0.0 b/dist/changes-5.0.0 index daf68ee..8dbda70 100644 --- a/dist/changes-5.0.0 +++ b/dist/changes-5.0.0 @@ -113,6 +113,10 @@ information about a particular change. * The event type parameter is removed from handleTouchEvent(). +- The previously exported function qt_translateRawTouchEvent() has been removed. + Use QWindowSystemInterface::handleTouchEvent() instead. + + **************************************************************************** * General * **************************************************************************** diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index dc8e5ed..312d8f5 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -823,6 +823,18 @@ void QGuiApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::Cl QGuiApplication::sendSpontaneousEvent(e->window.data(), &event); } +Q_GUI_EXPORT uint qHash(const QGuiApplicationPrivate::ActiveTouchPointsKey &k) +{ + return qHash(k.device) + k.touchPointId; +} + +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) { QWindow *window = e->window.data(); @@ -840,13 +852,15 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To // update state QWeakPointer w; QTouchEvent::TouchPoint previousTouchPoint; + ActiveTouchPointsKey touchInfoKey(e->device, touchPoint.id()); + ActiveTouchPointsValue &touchInfo = d->activeTouchPoints[touchInfoKey]; switch (touchPoint.state()) { case Qt::TouchPointPressed: if (e->device->type() == QTouchDevice::TouchPad) { // on touch-pads, send all touch points to the same widget - w = d->windowForTouchPointId.isEmpty() + w = d->activeTouchPoints.isEmpty() ? QWeakPointer() - : d->windowForTouchPointId.constBegin().value(); + : d->activeTouchPoints.constBegin().value().window; } if (!w) { @@ -858,7 +872,7 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To w = window; } - d->windowForTouchPointId[touchPoint.id()] = w; + touchInfo.window = w; touchPoint.d->startScreenPos = touchPoint.screenPos(); touchPoint.d->lastScreenPos = touchPoint.screenPos(); touchPoint.d->startNormalizedPos = touchPoint.normalizedPos(); @@ -866,14 +880,15 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To if (touchPoint.pressure() < qreal(0.)) touchPoint.d->pressure = qreal(1.); - d->appCurrentTouchPoints.insert(touchPoint.id(), touchPoint); + touchInfo.touchPoint = touchPoint; break; case Qt::TouchPointReleased: - w = d->windowForTouchPointId.take(touchPoint.id()); + w = touchInfo.window; if (!w) continue; - previousTouchPoint = d->appCurrentTouchPoints.take(touchPoint.id()); + + previousTouchPoint = touchInfo.touchPoint; touchPoint.d->startScreenPos = previousTouchPoint.startScreenPos(); touchPoint.d->lastScreenPos = previousTouchPoint.screenPos(); touchPoint.d->startPos = previousTouchPoint.startPos(); @@ -882,14 +897,15 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To touchPoint.d->lastNormalizedPos = previousTouchPoint.normalizedPos(); if (touchPoint.pressure() < qreal(0.)) touchPoint.d->pressure = qreal(0.); + break; default: - w = d->windowForTouchPointId.value(touchPoint.id()); + w = touchInfo.window; if (!w) continue; - Q_ASSERT(d->appCurrentTouchPoints.contains(touchPoint.id())); - previousTouchPoint = d->appCurrentTouchPoints.value(touchPoint.id()); + + previousTouchPoint = touchInfo.touchPoint; touchPoint.d->startScreenPos = previousTouchPoint.startScreenPos(); touchPoint.d->lastScreenPos = previousTouchPoint.screenPos(); touchPoint.d->startPos = previousTouchPoint.startPos(); @@ -902,7 +918,7 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To // Stationary points might not be delivered down to the receiving item // and get their position transformed, keep the old values instead. if (touchPoint.state() != Qt::TouchPointStationary) - d->appCurrentTouchPoints[touchPoint.id()] = touchPoint; + touchInfo.touchPoint = touchPoint; break; } @@ -969,6 +985,16 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To QGuiApplication::sendSpontaneousEvent(w, &touchEvent); } + + // 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. + for (int i = 0; i < e->points.count(); ++i) { + QTouchEvent::TouchPoint touchPoint = e->points.at(i); + if (touchPoint.state() == Qt::TouchPointReleased) + d->activeTouchPoints.remove(ActiveTouchPointsKey(e->device, touchPoint.id())); + } } void QGuiApplicationPrivate::reportScreenOrientationChange(QWindowSystemInterfacePrivate::ScreenOrientationEvent *e) diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 9c8a2ca..2d13127 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -189,15 +189,29 @@ public: QShortcutMap shortcutMap; #endif + struct ActiveTouchPointsKey { + ActiveTouchPointsKey(QTouchDevice *dev, int id) : device(dev), touchPointId(id) { } + QTouchDevice *device; + int touchPointId; + }; + struct ActiveTouchPointsValue { + QWeakPointer window; + QWeakPointer target; + QTouchEvent::TouchPoint touchPoint; + }; + QHash activeTouchPoints; + private: void init(); static QGuiApplicationPrivate *self; - - QMap > windowForTouchPointId; - QMap appCurrentTouchPoints; }; +Q_GUI_EXPORT uint qHash(const QGuiApplicationPrivate::ActiveTouchPointsKey &k); + +Q_GUI_EXPORT bool operator==(const QGuiApplicationPrivate::ActiveTouchPointsKey &a, + const QGuiApplicationPrivate::ActiveTouchPointsKey &b); + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/testlib/qtesttouch.h b/src/testlib/qtesttouch.h index 57085ad..3763a9a 100644 --- a/src/testlib/qtesttouch.h +++ b/src/testlib/qtesttouch.h @@ -62,13 +62,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(Test) -#ifdef QT_WIDGETS_LIB -extern Q_GUI_EXPORT void qt_translateRawTouchEvent(QWidget *window, - QTouchDevice *device, - const QList &touchPoints, - ulong timestamp); -#endif - namespace QTest { @@ -137,19 +130,43 @@ namespace QTest if (targetWindow) { QWindowSystemInterface::handleTouchEvent(targetWindow, device, touchPointList(points.values())); - QTest::qWait(10); } #ifdef QT_WIDGETS_LIB else if (targetWidget) { - qt_translateRawTouchEvent(targetWidget, device, points.values(), 0); + QWindowSystemInterface::handleTouchEvent(targetWidget->windowHandle(), device, touchPointList(points.values())); } #endif } + QCoreApplication::processEvents(); previousPoints = points; points.clear(); } + static QWindowSystemInterface::TouchPoint touchPoint(const QTouchEvent::TouchPoint& pt) + { + QWindowSystemInterface::TouchPoint p; + p.id = pt.id(); + p.flags = pt.flags(); + p.normalPosition = pt.normalizedPos(); + p.area = pt.screenRect(); + p.pressure = pt.pressure(); + p.state = pt.state(); + p.velocity = pt.velocity(); + p.rawPositions = pt.rawScreenPositions(); + return p; + } + static QList touchPointList(const QList& pointList) + { + QList newList; + + Q_FOREACH (QTouchEvent::TouchPoint p, pointList) + { + newList.append(touchPoint(p)); + } + return newList; + } + private: #ifdef QT_WIDGETS_LIB QTouchEventSequence(QWidget *widget, QTouchDevice *aDevice) @@ -198,27 +215,6 @@ namespace QTest return window->mapToGlobal(pt); return targetWindow ? targetWindow->mapToGlobal(pt) : pt; } - QWindowSystemInterface::TouchPoint touchPoint(const QTouchEvent::TouchPoint& pt) - { - QWindowSystemInterface::TouchPoint p; - p.id = pt.id(); - p.flags = pt.flags(); - p.normalPosition = pt.screenRect().topLeft(); - p.area = pt.screenRect(); - p.pressure = pt.pressure(); - p.state = pt.state(); - return p; - } - QList touchPointList(const QList& pointList) - { - QList newList; - - Q_FOREACH (QTouchEvent::TouchPoint p, pointList) - { - newList.append(touchPoint(p)); - } - return newList; - } QMap previousPoints; QMap points; diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 4c4cb40..a4a1aa8 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -3904,7 +3904,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) // the first widget to accept the TouchBegin gets an implicit grab. for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i); - d->widgetForTouchPointId[touchPoint.id()] = widget; + d->activeTouchPoints[QGuiApplicationPrivate::ActiveTouchPointsKey(touchEvent->device(), touchPoint.id())].target = widget; } break; } else if (p.isNull() || widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) { @@ -5168,41 +5168,43 @@ void QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEven rect.moveCenter(widget->mapFromGlobal(screenPos.toPoint()) + delta); touchPoint.d->rect = rect; - if (touchPoint.state() == Qt::TouchPointPressed) { - touchPoint.d->startPos = widget->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta; - touchPoint.d->lastPos = widget->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta; - } + touchPoint.d->startPos = widget->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta; + touchPoint.d->lastPos = widget->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta; } } void QApplicationPrivate::initializeMultitouch() { - widgetForTouchPointId.clear(); - appCurrentTouchPoints.clear(); - initializeMultitouch_sys(); } void QApplicationPrivate::cleanupMultitouch() { cleanupMultitouch_sys(); - - widgetForTouchPointId.clear(); - appCurrentTouchPoints.clear(); } -int QApplicationPrivate::findClosestTouchPointId(const QPointF &screenPos) +QWidget *QApplicationPrivate::findClosestTouchPointTarget(QTouchDevice *device, const QPointF &screenPos) { int closestTouchPointId = -1; + QObject *closestTarget = 0; qreal closestDistance = qreal(0.); - foreach (const QTouchEvent::TouchPoint &touchPoint, appCurrentTouchPoints) { - qreal distance = QLineF(screenPos, touchPoint.screenPos()).length(); - if (closestTouchPointId == -1 || distance < closestDistance) { - closestTouchPointId = touchPoint.id(); - closestDistance = distance; + QHash::const_iterator it = activeTouchPoints.constBegin(), + ite = activeTouchPoints.constEnd(); + while (it != ite) { + if (it.key().device == device) { + const QTouchEvent::TouchPoint &touchPoint = it->touchPoint; + qreal dx = screenPos.x() - touchPoint.screenPos().x(); + qreal dy = screenPos.y() - touchPoint.screenPos().y(); + qreal distance = dx * dx + dy * dy; + if (closestTouchPointId == -1 || distance < closestDistance) { + closestTouchPointId = touchPoint.id(); + closestDistance = distance; + closestTarget = it.value().target.data(); + } } + ++it; } - return closestTouchPointId; + return static_cast(closestTarget); } void QApplicationPrivate::translateRawTouchEvent(QWidget *window, @@ -5222,91 +5224,46 @@ void QApplicationPrivate::translateRawTouchEvent(QWidget *window, touchPoint.d = touchPoint.d->detach(); // update state - QWeakPointer widget; - switch (touchPoint.state()) { - case Qt::TouchPointPressed: - { + QWeakPointer target; + ActiveTouchPointsKey touchInfoKey(device, touchPoint.id()); + ActiveTouchPointsValue &touchInfo = d->activeTouchPoints[touchInfoKey]; + if (touchPoint.state() == Qt::TouchPointPressed) { if (device->type() == QTouchDevice::TouchPad) { // on touch-pads, send all touch points to the same widget - widget = d->widgetForTouchPointId.isEmpty() - ? QWeakPointer() - : d->widgetForTouchPointId.constBegin().value(); + target = d->activeTouchPoints.isEmpty() + ? QWeakPointer() + : d->activeTouchPoints.constBegin().value().target; } - if (!widget) { + if (!target) { // determine which widget this event will go to if (!window) window = QApplication::topLevelAt(touchPoint.screenPos().toPoint()); if (!window) continue; - widget = window->childAt(window->mapFromGlobal(touchPoint.screenPos().toPoint())); - if (!widget) - widget = window; + target = window->childAt(window->mapFromGlobal(touchPoint.screenPos().toPoint())); + if (!target) + target = window; } if (device->type() == QTouchDevice::TouchScreen) { - int closestTouchPointId = d->findClosestTouchPointId(touchPoint.screenPos()); - QWidget *closestWidget = d->widgetForTouchPointId.value(closestTouchPointId).data(); + QWidget *closestWidget = d->findClosestTouchPointTarget(device, touchPoint.screenPos()); + QWidget *widget = static_cast(target.data()); if (closestWidget - && (widget.data()->isAncestorOf(closestWidget) || closestWidget->isAncestorOf(widget.data()))) { - widget = closestWidget; + && (widget->isAncestorOf(closestWidget) || closestWidget->isAncestorOf(widget))) { + target = closestWidget; } } - d->widgetForTouchPointId[touchPoint.id()] = widget; - touchPoint.d->startScreenPos = touchPoint.screenPos(); - touchPoint.d->lastScreenPos = touchPoint.screenPos(); - touchPoint.d->startNormalizedPos = touchPoint.normalizedPos(); - touchPoint.d->lastNormalizedPos = touchPoint.normalizedPos(); - if (touchPoint.pressure() < qreal(0.)) - touchPoint.d->pressure = qreal(1.); - - d->appCurrentTouchPoints.insert(touchPoint.id(), touchPoint); - break; - } - case Qt::TouchPointReleased: - { - widget = d->widgetForTouchPointId.take(touchPoint.id()); - if (!widget) - continue; - - QTouchEvent::TouchPoint previousTouchPoint = d->appCurrentTouchPoints.take(touchPoint.id()); - touchPoint.d->startScreenPos = previousTouchPoint.startScreenPos(); - touchPoint.d->lastScreenPos = previousTouchPoint.screenPos(); - touchPoint.d->startPos = previousTouchPoint.startPos(); - touchPoint.d->lastPos = previousTouchPoint.pos(); - touchPoint.d->startNormalizedPos = previousTouchPoint.startNormalizedPos(); - touchPoint.d->lastNormalizedPos = previousTouchPoint.normalizedPos(); - if (touchPoint.pressure() < qreal(0.)) - touchPoint.d->pressure = qreal(0.); - break; - } - default: - widget = d->widgetForTouchPointId.value(touchPoint.id()); - if (!widget) + touchInfo.target = target; + } else { + target = touchInfo.target; + if (!target) continue; - - Q_ASSERT(d->appCurrentTouchPoints.contains(touchPoint.id())); - QTouchEvent::TouchPoint previousTouchPoint = d->appCurrentTouchPoints.value(touchPoint.id()); - touchPoint.d->startScreenPos = previousTouchPoint.startScreenPos(); - touchPoint.d->lastScreenPos = previousTouchPoint.screenPos(); - touchPoint.d->startPos = previousTouchPoint.startPos(); - touchPoint.d->lastPos = previousTouchPoint.pos(); - touchPoint.d->startNormalizedPos = previousTouchPoint.startNormalizedPos(); - touchPoint.d->lastNormalizedPos = previousTouchPoint.normalizedPos(); - if (touchPoint.pressure() < qreal(0.)) - touchPoint.d->pressure = qreal(1.); - d->appCurrentTouchPoints[touchPoint.id()] = touchPoint; - break; } - Q_ASSERT(widget.data() != 0); - - // make the *scene* functions return the same as the *screen* functions - touchPoint.d->sceneRect = touchPoint.screenRect(); - touchPoint.d->startScenePos = touchPoint.startScreenPos(); - touchPoint.d->lastScenePos = touchPoint.lastScreenPos(); + Q_ASSERT(target.data() != 0); - StatesAndTouchPoints &maskAndPoints = widgetsNeedingEvents[widget.data()]; + StatesAndTouchPoints &maskAndPoints = widgetsNeedingEvents[static_cast(target.data())]; maskAndPoints.first |= touchPoint.state(); maskAndPoints.second.append(touchPoint); } @@ -5345,7 +5302,7 @@ void QApplicationPrivate::translateRawTouchEvent(QWidget *window, updateTouchPointsForWidget(widget, &touchEvent); touchEvent.setTimestamp(timestamp); touchEvent.setWindow(window->windowHandle()); - touchEvent.setTarget(window); + touchEvent.setTarget(widget); switch (touchEvent.type()) { case QEvent::TouchBegin: @@ -5367,14 +5324,6 @@ void QApplicationPrivate::translateRawTouchEvent(QWidget *window, } } -Q_WIDGETS_EXPORT void qt_translateRawTouchEvent(QWidget *window, - QTouchDevice *device, - const QList &touchPoints, - ulong timestamp) -{ - QApplicationPrivate::translateRawTouchEvent(window, device, touchPoints, timestamp); -} - #ifndef QT_NO_GESTURES QGestureManager* QGestureManager::instance() { diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h index 3841cef..ac4b4ad 100644 --- a/src/widgets/kernel/qapplication_p.h +++ b/src/widgets/kernel/qapplication_p.h @@ -472,14 +472,12 @@ public: QPixmap *ignore_cursor; #endif - QMap > widgetForTouchPointId; - QMap appCurrentTouchPoints; static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); void initializeMultitouch(); void initializeMultitouch_sys(); void cleanupMultitouch(); void cleanupMultitouch_sys(); - int findClosestTouchPointId(const QPointF &screenPos); + QWidget *findClosestTouchPointTarget(QTouchDevice *device, const QPointF &screenPos); void appendTouchPoint(const QTouchEvent::TouchPoint &touchPoint); void removeTouchPoint(int touchPointId); static void translateRawTouchEvent(QWidget *widget, @@ -554,11 +552,6 @@ private: static bool isAlien(QWidget *); }; -Q_WIDGETS_EXPORT void qt_translateRawTouchEvent(QWidget *window, - QTouchDevice *device, - const QList &touchPoints, - ulong timestamp); - #if defined(Q_WS_WIN) extern void qt_win_set_cursor(QWidget *, bool); #elif defined(Q_WS_X11) diff --git a/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp b/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp index 07afe4a..40e627d 100644 --- a/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp +++ b/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp @@ -209,6 +209,8 @@ private slots: void deleteInRawEventTranslation(); void crashInQGraphicsSceneAfterNotHandlingTouchBegin(); void touchBeginWithGraphicsWidget(); + void testQGuiAppDelivery(); + void testMultiDevice(); private: QTouchDevice *touchScreenDevice; @@ -597,10 +599,12 @@ void tst_QTouchEvent::basicRawEventTranslation() rawPosList << QPointF(12, 34) << QPointF(56, 78); rawTouchPoint.setRawScreenPositions(rawPosList); const ulong timestamp = 1234; - qt_translateRawTouchEvent(&touchWidget, - touchScreenDevice, - QList() << rawTouchPoint, - timestamp); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + timestamp, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList( + QList() << rawTouchPoint)); + QCoreApplication::processEvents(); QVERIFY(touchWidget.seenTouchBegin); QVERIFY(!touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -632,10 +636,11 @@ void tst_QTouchEvent::basicRawEventTranslation() rawTouchPoint.setState(Qt::TouchPointMoved); rawTouchPoint.setScreenPos(screenPos + delta); rawTouchPoint.setNormalizedPos(normalized(rawTouchPoint.pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, - touchScreenDevice, - QList() << rawTouchPoint, - 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(QList() << rawTouchPoint)); + QCoreApplication::processEvents(); QVERIFY(touchWidget.seenTouchBegin); QVERIFY(touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -664,10 +669,11 @@ void tst_QTouchEvent::basicRawEventTranslation() rawTouchPoint.setState(Qt::TouchPointReleased); rawTouchPoint.setScreenPos(screenPos + delta + delta); rawTouchPoint.setNormalizedPos(normalized(rawTouchPoint.pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, - touchScreenDevice, - QList() << rawTouchPoint, - 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(QList() << rawTouchPoint)); + QCoreApplication::processEvents(); QVERIFY(touchWidget.seenTouchBegin); QVERIFY(touchWidget.seenTouchUpdate); QVERIFY(touchWidget.seenTouchEnd); @@ -731,7 +737,11 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchScreen() rawTouchPoints[1].setState(Qt::TouchPointPressed); rawTouchPoints[1].setScreenPos(rightScreenPos); rawTouchPoints[1].setNormalizedPos(normalized(rawTouchPoints[1].pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, touchScreenDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!touchWidget.seenTouchBegin); QVERIFY(!touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -792,7 +802,11 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchScreen() rawTouchPoints[1].setState(Qt::TouchPointMoved); rawTouchPoints[1].setScreenPos(centerScreenPos); rawTouchPoints[1].setNormalizedPos(normalized(rawTouchPoints[1].pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, touchScreenDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!touchWidget.seenTouchBegin); QVERIFY(!touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -853,7 +867,11 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchScreen() rawTouchPoints[1].setState(Qt::TouchPointReleased); rawTouchPoints[1].setScreenPos(centerScreenPos); rawTouchPoints[1].setNormalizedPos(normalized(rawTouchPoints[1].pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, touchScreenDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!touchWidget.seenTouchBegin); QVERIFY(!touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -946,7 +964,11 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchPad() rawTouchPoints[1].setState(Qt::TouchPointPressed); rawTouchPoints[1].setScreenPos(rightScreenPos); rawTouchPoints[1].setNormalizedPos(normalized(rawTouchPoints[1].pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, touchPadDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchPadDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!touchWidget.seenTouchBegin); QVERIFY(!touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -1007,7 +1029,11 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchPad() rawTouchPoints[1].setState(Qt::TouchPointMoved); rawTouchPoints[1].setScreenPos(centerScreenPos); rawTouchPoints[1].setNormalizedPos(normalized(rawTouchPoints[1].pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, touchPadDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchPadDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!touchWidget.seenTouchBegin); QVERIFY(!touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -1068,7 +1094,11 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchPad() rawTouchPoints[1].setState(Qt::TouchPointReleased); rawTouchPoints[1].setScreenPos(centerScreenPos); rawTouchPoints[1].setNormalizedPos(normalized(rawTouchPoints[1].pos(), screenGeometry)); - qt_translateRawTouchEvent(&touchWidget, touchPadDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchPadDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!touchWidget.seenTouchBegin); QVERIFY(!touchWidget.seenTouchUpdate); QVERIFY(!touchWidget.seenTouchEnd); @@ -1326,20 +1356,32 @@ void tst_QTouchEvent::deleteInRawEventTranslation() rawTouchPoints[2].setNormalizedPos(normalized(rawTouchPoints[2].pos(), screenGeometry)); // generate begin events on all widgets, the left widget should die - qt_translateRawTouchEvent(&touchWidget, touchScreenDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(pl.isNull() && !pc.isNull() && !pr.isNull()); // generate update events on all widget, the center widget should die rawTouchPoints[0].setState(Qt::TouchPointMoved); rawTouchPoints[1].setState(Qt::TouchPointMoved); rawTouchPoints[2].setState(Qt::TouchPointMoved); - qt_translateRawTouchEvent(&touchWidget, touchScreenDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); // generate end events on all widget, the right widget should die rawTouchPoints[0].setState(Qt::TouchPointReleased); rawTouchPoints[1].setState(Qt::TouchPointReleased); rawTouchPoints[2].setState(Qt::TouchPointReleased); - qt_translateRawTouchEvent(&touchWidget, touchScreenDevice, rawTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(touchWidget.windowHandle(), + 0, + touchScreenDevice, + QTest::QTouchEventSequence::touchPointList(rawTouchPoints)); + QCoreApplication::processEvents(); } void tst_QTouchEvent::crashInQGraphicsSceneAfterNotHandlingTouchBegin() @@ -1390,12 +1432,12 @@ void tst_QTouchEvent::touchBeginWithGraphicsWidget() QTest::qWaitForWindowShown(&view); view.fitInView(scene.sceneRect()); - QTest::touchEvent(static_cast(0), touchScreenDevice) + QTest::touchEvent(&view, touchScreenDevice) .press(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()); - QTest::touchEvent(static_cast(0), touchScreenDevice) + QTest::touchEvent(&view, touchScreenDevice) .stationary(0) .press(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); - QTest::touchEvent(static_cast(0), touchScreenDevice) + QTest::touchEvent(&view, touchScreenDevice) .release(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()) .release(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); @@ -1407,12 +1449,12 @@ void tst_QTouchEvent::touchBeginWithGraphicsWidget() root->reset(); glassWidget->setWindowFlags(Qt::Window); // make the glassWidget a panel - QTest::touchEvent(static_cast(0), touchScreenDevice) + QTest::touchEvent(&view, touchScreenDevice) .press(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()); - QTest::touchEvent(static_cast(0), touchScreenDevice) + QTest::touchEvent(&view, touchScreenDevice) .stationary(0) .press(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); - QTest::touchEvent(static_cast(0), touchScreenDevice) + QTest::touchEvent(&view, touchScreenDevice) .release(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()) .release(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); @@ -1425,6 +1467,143 @@ void tst_QTouchEvent::touchBeginWithGraphicsWidget() delete glassWidget; } +class WindowTouchEventFilter : public QObject +{ + Q_OBJECT +public: + bool eventFilter(QObject *obj, QEvent *event); + struct TouchInfo { + QList points; + QEvent::Type lastSeenType; + }; + QMap d; +}; + +bool WindowTouchEventFilter::eventFilter(QObject *, QEvent *event) +{ + if (event->type() == QEvent::TouchBegin + || event->type() == QEvent::TouchUpdate + || event->type() == QEvent::TouchEnd) { + QTouchEvent *te = static_cast(event); + TouchInfo &td = d[te->device()]; + if (event->type() == QEvent::TouchBegin) + td.points.clear(); + td.points.append(te->touchPoints()); + td.lastSeenType = event->type(); + } + return false; +} + +void tst_QTouchEvent::testQGuiAppDelivery() +{ + QTouchDevice *device = new QTouchDevice; + device->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(device); + + QWindow *w = new QWindow; + w->setGeometry(100, 100, 100, 100); + w->show(); + QTest::qWaitForWindowShown(w); + + WindowTouchEventFilter filter; + w->installEventFilter(&filter); + + QList points; + + // Pass empty list, should be ignored. + QWindowSystemInterface::handleTouchEvent(w, 0, points); + QCoreApplication::processEvents(); + QCOMPARE(filter.d.isEmpty(), true); + + QWindowSystemInterface::TouchPoint tp; + tp.id = 0; + tp.state = Qt::TouchPointPressed; + tp.area = QRectF(120, 120, 20, 20); + points.append(tp); + + // Pass 0 as device, should be ignored. + QWindowSystemInterface::handleTouchEvent(w, 0, points); + QCoreApplication::processEvents(); + QCOMPARE(filter.d.isEmpty(), true); + + // Now the real thing. + QWindowSystemInterface::handleTouchEvent(w, device, points); // TouchBegin + QCoreApplication::processEvents(); + QCOMPARE(filter.d.count(), 1); + QCOMPARE(filter.d.contains(device), true); + QCOMPARE(filter.d.value(device).points.count(), 1); + QCOMPARE(filter.d.value(device).lastSeenType, QEvent::TouchBegin); + + points[0].state = Qt::TouchPointMoved; + QWindowSystemInterface::handleTouchEvent(w, device, points); // TouchUpdate + QCoreApplication::processEvents(); + QCOMPARE(filter.d.count(), 1); + QCOMPARE(filter.d.contains(device), true); + QCOMPARE(filter.d.value(device).points.count(), 2); + QCOMPARE(filter.d.value(device).lastSeenType, QEvent::TouchUpdate); + + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(w, device, points); // TouchEnd + QCoreApplication::processEvents(); + QCOMPARE(filter.d.count(), 1); + QCOMPARE(filter.d.contains(device), true); + QCOMPARE(filter.d.value(device).points.count(), 3); + QCOMPARE(filter.d.value(device).lastSeenType, QEvent::TouchEnd); +} + +void tst_QTouchEvent::testMultiDevice() +{ + QTouchDevice *deviceOne = new QTouchDevice; + deviceOne->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(deviceOne); + QTouchDevice *deviceTwo = new QTouchDevice; + deviceTwo->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(deviceTwo); + + QWindow *w = new QWindow; + w->setGeometry(100, 100, 100, 100); + w->show(); + QTest::qWaitForWindowShown(w); + + WindowTouchEventFilter filter; + w->installEventFilter(&filter); + + QList pointsOne, pointsTwo; + + // deviceOne reports a single point, deviceTwo reports the beginning of a multi-point sequence. + // Even though there is a point with id 0 for both devices, they should be delivered cleanly, independently. + QWindowSystemInterface::TouchPoint tp; + tp.id = 0; + tp.state = Qt::TouchPointPressed; + tp.area = QRectF(120, 120, 20, 20); + pointsOne.append(tp); + + pointsTwo.append(tp); + tp.id = 1; + tp.area = QRectF(140, 140, 20, 20); + pointsTwo.append(tp); + + QWindowSystemInterface::handleTouchEvent(w, deviceOne, pointsOne); + QWindowSystemInterface::handleTouchEvent(w, deviceTwo, pointsTwo); + QCoreApplication::processEvents(); + + QCOMPARE(filter.d.contains(deviceOne), true); + QCOMPARE(filter.d.contains(deviceTwo), true); + + QCOMPARE(filter.d.value(deviceOne).lastSeenType, QEvent::TouchBegin); + QCOMPARE(filter.d.value(deviceTwo).lastSeenType, QEvent::TouchBegin); + QCOMPARE(filter.d.value(deviceOne).points.count(), 1); + QCOMPARE(filter.d.value(deviceTwo).points.count(), 2); + + QCOMPARE(filter.d.value(deviceOne).points.at(0).screenRect(), pointsOne[0].area); + QCOMPARE(filter.d.value(deviceOne).points.at(0).state(), pointsOne[0].state); + + QCOMPARE(filter.d.value(deviceTwo).points.at(0).screenRect(), pointsTwo[0].area); + QCOMPARE(filter.d.value(deviceTwo).points.at(0).state(), pointsTwo[0].state); + QCOMPARE(filter.d.value(deviceTwo).points.at(1).screenRect(), pointsTwo[1].area); + QCOMPARE(filter.d.value(deviceTwo).points.at(1).state(), pointsTwo[1].state); +} + QTEST_MAIN(tst_QTouchEvent) #include "tst_qtouchevent.moc" diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index 81cdffe..4de05e1 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -1941,23 +1941,52 @@ void tst_QApplication::touchEventPropagation() // touch event behavior on a window TouchEventPropagationTestWidget window; window.setObjectName("1. window"); - - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + window.show(); // Must have an explicitly specified QWindow for handleTouchEvent, + // passing 0 would result in using topLevelAt() which is not ok in this case + // as the screen position in the point is bogus. + QTest::qWaitForWindowShown(&window); + // QPA always takes screen positions and since we map the TouchPoint back to QPA's structure first, + // we must ensure there is a screen position in the TouchPoint that maps to a local 0, 0. + pressedTouchPoints[0].setScreenPos(window.mapToGlobal(QPoint(0, 0))); + releasedTouchPoints[0].setScreenPos(window.mapToGlobal(QPoint(0, 0))); + + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); window.setAttribute(Qt::WA_AcceptTouchEvents); - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); window.reset(); window.acceptTouchEvent = true; - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(window.seenTouchEvent); QVERIFY(!window.seenMouseEvent); } @@ -1968,9 +1997,20 @@ void tst_QApplication::touchEventPropagation() window.setObjectName("2. window"); TouchEventPropagationTestWidget widget(&window); widget.setObjectName("2. widget"); - - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + window.show(); + QTest::qWaitForWindowShown(&window); + pressedTouchPoints[0].setScreenPos(window.mapToGlobal(QPoint(0, 0))); + releasedTouchPoints[0].setScreenPos(window.mapToGlobal(QPoint(0, 0))); + + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); @@ -1979,8 +2019,15 @@ void tst_QApplication::touchEventPropagation() window.reset(); widget.reset(); widget.setAttribute(Qt::WA_AcceptTouchEvents); - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); @@ -1989,8 +2036,15 @@ void tst_QApplication::touchEventPropagation() window.reset(); widget.reset(); widget.acceptMouseEvent = true; - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); @@ -1999,8 +2053,15 @@ void tst_QApplication::touchEventPropagation() window.reset(); widget.reset(); widget.acceptTouchEvent = true; - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(!window.seenTouchEvent); @@ -2010,8 +2071,15 @@ void tst_QApplication::touchEventPropagation() widget.reset(); widget.setAttribute(Qt::WA_AcceptTouchEvents, false); window.setAttribute(Qt::WA_AcceptTouchEvents); - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(window.seenTouchEvent); @@ -2020,8 +2088,15 @@ void tst_QApplication::touchEventPropagation() window.reset(); widget.reset(); window.acceptTouchEvent = true; - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(window.seenTouchEvent); @@ -2031,8 +2106,15 @@ void tst_QApplication::touchEventPropagation() widget.reset(); widget.acceptMouseEvent = true; // doesn't matter, touch events are propagated first window.acceptTouchEvent = true; - qt_translateRawTouchEvent(&window, device, pressedTouchPoints, 0); - qt_translateRawTouchEvent(&window, device, releasedTouchPoints, 0); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(pressedTouchPoints)); + QWindowSystemInterface::handleTouchEvent(window.windowHandle(), + 0, + device, + QTest::QTouchEventSequence::touchPointList(releasedTouchPoints)); + QCoreApplication::processEvents(); QVERIFY(!widget.seenTouchEvent); QVERIFY(!widget.seenMouseEvent); QVERIFY(window.seenTouchEvent); -- 2.7.4