From f51e6552e361c0716f6301b4c5b03b12e8bfe0fe Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 29 Sep 2020 15:33:11 +0200 Subject: End touch->mouse synthesis when the chosen touchpoint is released During delivery of a TouchBegin event, if no widget accepts it, we begin treating the first touchpoint as a synth-mouse, as before. If a second touchpoint is pressed or released in any order, it's irrelevant: the fake mouse button is released as soon as the first touchpoint is released. This fixes the bug that such a scenario caused the mouse release not to be sent, so that a widget could get "stuck" in pressed state. Done-with: Tang Haixiang Fixes: QTBUG-86253 Pick-to: 5.15 Change-Id: I7fbbe120539d8ded8ef5e7cf712a27bd69391e02 Reviewed-by: Volker Hilsheimer --- src/gui/kernel/qguiapplication.cpp | 60 +++++++++++++++------------ tests/auto/gui/kernel/qwindow/tst_qwindow.cpp | 24 +++++++++++ 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index a022e9a151..627cf9dba1 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -189,7 +189,7 @@ static Qt::LayoutDirection layout_direction = Qt::LayoutDirectionAuto; static bool force_reverse = false; QGuiApplicationPrivate *QGuiApplicationPrivate::self = nullptr; -int QGuiApplicationPrivate::m_fakeMouseSourcePointId = 0; +int QGuiApplicationPrivate::m_fakeMouseSourcePointId = -1; #ifndef QT_NO_CLIPBOARD QClipboard *QGuiApplicationPrivate::qt_clipboard = nullptr; @@ -2950,7 +2950,6 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To for (QMutableTouchEvent &touchEvent : touchEvents) { QWindow *window = static_cast(touchEvent.target()); - auto &points = touchEvent.points(); QEvent::Type eventType; switch (touchEvent.touchPointStates()) { @@ -2985,47 +2984,54 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To // exclude devices which generate their own mouse events if (!(touchEvent.device()->capabilities().testFlag(QInputDevice::Capability::MouseEmulation))) { - if (eventType == QEvent::TouchEnd) - self->synthesizedMousePoints.clear(); - - if (eventType == QEvent::TouchBegin) + QEvent::Type mouseEventType = QEvent::MouseMove; + Qt::MouseButton button = Qt::NoButton; + Qt::MouseButtons buttons = Qt::LeftButton; + if (eventType == QEvent::TouchBegin && m_fakeMouseSourcePointId < 0) { m_fakeMouseSourcePointId = touchEvent.point(0).id(); - - const QEvent::Type mouseType = [&]() { - switch (eventType) { - case QEvent::TouchBegin: return QEvent::MouseButtonPress; - case QEvent::TouchUpdate: return QEvent::MouseMove; - case QEvent::TouchEnd: return QEvent::MouseButtonRelease; - default: Q_UNREACHABLE(); - } - }(); - - Qt::MouseButton button = mouseType == QEvent::MouseMove ? Qt::NoButton : Qt::LeftButton; - Qt::MouseButtons buttons = mouseType == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton; - - for (const QEventPoint &touchPoint : points) { - if (touchPoint.id() == m_fakeMouseSourcePointId) { - if (eventType != QEvent::TouchEnd) + qCDebug(lcPtrDispatch) << "synthesizing mouse events from touchpoint" << m_fakeMouseSourcePointId; + } + if (m_fakeMouseSourcePointId >= 0) { + const auto *touchPoint = touchEvent.pointById(m_fakeMouseSourcePointId); + if (touchPoint) { + switch (touchPoint->state()) { + case QEventPoint::State::Pressed: + mouseEventType = QEvent::MouseButtonPress; + button = Qt::LeftButton; + break; + case QEventPoint::State::Released: + mouseEventType = QEvent::MouseButtonRelease; + button = Qt::LeftButton; + buttons = Qt::NoButton; + Q_ASSERT(m_fakeMouseSourcePointId == touchPoint->id()); + m_fakeMouseSourcePointId = -1; + break; + default: + break; + } + if (touchPoint->state() != QEventPoint::State::Released) { self->synthesizedMousePoints.insert(window, SynthesizedMouseData( - touchPoint.position(), touchPoint.globalPosition(), window)); + 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). // TODO why go through QPA? Why not just send a QMouseEvent right from here? QWindowSystemInterfacePrivate::MouseEvent fake(window, e->timestamp, - touchPoint.position(), - touchPoint.globalPosition(), + touchPoint->position(), + touchPoint->globalPosition(), buttons, e->modifiers, button, - mouseType, + mouseEventType, Qt::MouseEventSynthesizedByQt, false, device); fake.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic; processMouseEvent(&fake); - break; } } + if (eventType == QEvent::TouchEnd) + self->synthesizedMousePoints.clear(); } } } diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index 655ba3ac33..602e5a0178 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -1242,6 +1242,30 @@ void tst_QWindow::touchToMouseTranslation() // mouse event synthesizing disabled QTRY_COMPARE(window.mousePressButton, 0); QTRY_COMPARE(window.mouseReleaseButton, 0); + + points.clear(); + points.append(tp2); + points[0].state = QEventPoint::State::Pressed; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp1); + points[0].state = QEventPoint::State::Pressed; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + QTRY_COMPARE(window.mousePressButton, 1); + + points.clear(); + points.append(tp2); + points[0].state = QEventPoint::State::Released; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp1); + points[0].state = QEventPoint::State::Released; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + QTRY_COMPARE(window.mouseReleaseButton, 1); } void tst_QWindow::touchToMouseTranslationForDevices() -- cgit v1.2.3