wayland: Fix handling of synthetic touch cancel events

In case the compositor wants to cancel a touch sequence, we need to
ignore subsequent touch motion and touch up events until a new sequence
is initiated by the user.

Previously, it was implicitly handled by clearing the mapping table
between the touch slots and touch ids generated by kwayland-server.
This commit is contained in:
Vlad Zahorodnii 2021-02-16 13:51:23 +02:00
parent 8f2520e00e
commit d23dab7be9
4 changed files with 20 additions and 21 deletions

View file

@ -275,8 +275,6 @@ void TouchInputTest::testTouchPointCount()
kwinApp()->platform()->touchUp(1, timestamp++); kwinApp()->platform()->touchUp(1, timestamp++);
QCOMPARE(kwinApp()->platform()->touchPointCount(), 2); QCOMPARE(kwinApp()->platform()->touchPointCount(), 2);
kwinApp()->platform()->cancelTouchSequence();
QCOMPARE(kwinApp()->platform()->touchPointCount(), 1);
kwinApp()->platform()->cancelTouchSequence(); kwinApp()->platform()->cancelTouchSequence();
QCOMPARE(kwinApp()->platform()->touchPointCount(), 0); QCOMPARE(kwinApp()->platform()->touchPointCount(), 0);
} }

View file

@ -2303,7 +2303,7 @@ void InputRedirection::setupLibInput()
connect(conn, &LibInput::Connection::touchDown, m_touch, &TouchInputRedirection::processDown); connect(conn, &LibInput::Connection::touchDown, m_touch, &TouchInputRedirection::processDown);
connect(conn, &LibInput::Connection::touchUp, m_touch, &TouchInputRedirection::processUp); connect(conn, &LibInput::Connection::touchUp, m_touch, &TouchInputRedirection::processUp);
connect(conn, &LibInput::Connection::touchMotion, m_touch, &TouchInputRedirection::processMotion); connect(conn, &LibInput::Connection::touchMotion, m_touch, &TouchInputRedirection::processMotion);
connect(conn, &LibInput::Connection::touchCanceled, m_touch, &TouchInputRedirection::processCancel); connect(conn, &LibInput::Connection::touchCanceled, m_touch, &TouchInputRedirection::cancel);
connect(conn, &LibInput::Connection::touchFrame, m_touch, &TouchInputRedirection::frame); connect(conn, &LibInput::Connection::touchFrame, m_touch, &TouchInputRedirection::frame);
auto handleSwitchEvent = [this] (SwitchEvent::State state, quint32 time, quint64 timeMicroseconds, LibInput::Device *device) { auto handleSwitchEvent = [this] (SwitchEvent::State state, quint32 time, quint64 timeMicroseconds, LibInput::Device *device) {
SwitchEvent event(state, time, timeMicroseconds, device); SwitchEvent event(state, time, timeMicroseconds, device);
@ -2496,7 +2496,7 @@ void InputRedirection::processTouchMotion(qint32 id, const QPointF &pos, quint32
void InputRedirection::cancelTouchSequence() void InputRedirection::cancelTouchSequence()
{ {
m_touch->processCancel(); m_touch->cancel();
} }
void InputRedirection::cancelTouch() void InputRedirection::cancelTouch()

View file

@ -66,7 +66,7 @@ bool TouchInputRedirection::focusUpdatesBlocked()
if (waylandServer()->seat()->isDragTouch()) { if (waylandServer()->seat()->isDragTouch()) {
return true; return true;
} }
if (m_touches > 1) { if (m_activeTouchPoints.count() > 1) {
// first touch defines focus // first touch defines focus
return true; return true;
} }
@ -75,9 +75,8 @@ bool TouchInputRedirection::focusUpdatesBlocked()
bool TouchInputRedirection::positionValid() const bool TouchInputRedirection::positionValid() const
{ {
Q_ASSERT(m_touches >= 0);
// we can only determine a position with at least one touch point // we can only determine a position with at least one touch point
return m_touches; return !m_activeTouchPoints.isEmpty();
} }
void TouchInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow) void TouchInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow)
@ -144,8 +143,8 @@ void TouchInputRedirection::processDown(qint32 id, const QPointF &pos, quint32 t
} }
m_lastPosition = pos; m_lastPosition = pos;
m_windowUpdatedInCycle = false; m_windowUpdatedInCycle = false;
m_touches++; m_activeTouchPoints.insert(id);
if (m_touches == 1) { if (m_activeTouchPoints.count() == 1) {
update(); update();
} }
input()->processSpies(std::bind(&InputEventSpy::touchDown, std::placeholders::_1, id, pos, time)); input()->processSpies(std::bind(&InputEventSpy::touchDown, std::placeholders::_1, id, pos, time));
@ -159,12 +158,14 @@ void TouchInputRedirection::processUp(qint32 id, quint32 time, LibInput::Device
if (!inited()) { if (!inited()) {
return; return;
} }
if (!m_activeTouchPoints.remove(id)) {
return;
}
m_windowUpdatedInCycle = false; m_windowUpdatedInCycle = false;
input()->processSpies(std::bind(&InputEventSpy::touchUp, std::placeholders::_1, id, time)); input()->processSpies(std::bind(&InputEventSpy::touchUp, std::placeholders::_1, id, time));
input()->processFilters(std::bind(&InputEventFilter::touchUp, std::placeholders::_1, id, time)); input()->processFilters(std::bind(&InputEventFilter::touchUp, std::placeholders::_1, id, time));
m_windowUpdatedInCycle = false; m_windowUpdatedInCycle = false;
m_touches--; if (m_activeTouchPoints.count() == 0) {
if (m_touches == 0) {
update(); update();
} }
} }
@ -175,6 +176,9 @@ void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, quint32
if (!inited()) { if (!inited()) {
return; return;
} }
if (!m_activeTouchPoints.contains(id)) {
return;
}
m_lastPosition = pos; m_lastPosition = pos;
m_windowUpdatedInCycle = false; m_windowUpdatedInCycle = false;
input()->processSpies(std::bind(&InputEventSpy::touchMotion, std::placeholders::_1, id, pos, time)); input()->processSpies(std::bind(&InputEventSpy::touchMotion, std::placeholders::_1, id, pos, time));
@ -182,17 +186,16 @@ void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, quint32
m_windowUpdatedInCycle = false; m_windowUpdatedInCycle = false;
} }
void TouchInputRedirection::processCancel()
{
m_touches--;
cancel();
}
void TouchInputRedirection::cancel() void TouchInputRedirection::cancel()
{ {
if (!inited()) { if (!inited()) {
return; return;
} }
// If the touch sequence is artificially cancelled by the compositor, touch motion and touch
// up events will be silently ignored and won't be passed down through the event filter chain.
// If the touch sequence is cancelled because we received a TOUCH_CANCEL event from libinput,
// the compositor will not receive any TOUCH_MOTION or TOUCH_UP events for that slot.
m_activeTouchPoints.clear();
waylandServer()->seat()->cancelTouchSequence(); waylandServer()->seat()->cancelTouchSequence();
} }

View file

@ -46,7 +46,6 @@ public:
void processDown(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device = nullptr); void processDown(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device = nullptr);
void processUp(qint32 id, quint32 time, LibInput::Device *device = nullptr); void processUp(qint32 id, quint32 time, LibInput::Device *device = nullptr);
void processMotion(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device = nullptr); void processMotion(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device = nullptr);
void processCancel();
void cancel(); void cancel();
void frame(); void frame();
@ -68,7 +67,7 @@ public:
} }
int touchPointCount() const { int touchPointCount() const {
return m_touches; return m_activeTouchPoints.count();
} }
private: private:
@ -77,14 +76,13 @@ private:
void focusUpdate(Toplevel *focusOld, Toplevel *focusNow) override; void focusUpdate(Toplevel *focusOld, Toplevel *focusNow) override;
QSet<qint32> m_activeTouchPoints;
bool m_inited = false; bool m_inited = false;
qint32 m_decorationId = -1; qint32 m_decorationId = -1;
qint32 m_internalId = -1; qint32 m_internalId = -1;
QMetaObject::Connection m_focusGeometryConnection; QMetaObject::Connection m_focusGeometryConnection;
bool m_windowUpdatedInCycle = false; bool m_windowUpdatedInCycle = false;
QPointF m_lastPosition; QPointF m_lastPosition;
int m_touches = 0;
}; };
} }