Forward touch events using QWindowSystemInterface API

QWindowSystemInterface is a more appropriate api to send touch events.

BUG: 450441
(cherry picked from commit 1f89b7ca81)
wilder/Plasma/6.2
Vlad Zahorodnii 2 years ago
parent 4ea62b9b4b
commit 33efc38450
  1. 84
      autotests/integration/internal_window.cpp
  2. 71
      src/input.cpp

@ -85,6 +85,9 @@ Q_SIGNALS:
void wheel(); void wheel();
void keyPressed(); void keyPressed();
void keyReleased(); void keyReleased();
void touchDown(int id, const QPointF &pos);
void touchUp(int id);
void touchMotion(int id, const QPointF &pos);
protected: protected:
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
@ -95,6 +98,7 @@ protected:
void wheelEvent(QWheelEvent *event) override; void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override; void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override;
void touchEvent(QTouchEvent *event) override;
private: private:
QPoint m_latestGlobalMousePos; QPoint m_latestGlobalMousePos;
@ -161,6 +165,28 @@ void HelperWindow::keyReleaseEvent(QKeyEvent *event)
Q_EMIT keyReleased(); Q_EMIT keyReleased();
} }
void HelperWindow::touchEvent(QTouchEvent *event)
{
for (int i = 0; i < event->pointCount(); ++i) {
QEventPoint &point = event->point(i);
switch (point.state()) {
case QEventPoint::Unknown:
case QEventPoint::Stationary:
break;
case QEventPoint::Pressed:
Q_EMIT touchDown(point.id(), point.position());
break;
case QEventPoint::Updated:
Q_EMIT touchMotion(point.id(), point.position());
break;
case QEventPoint::Released:
Q_EMIT touchUp(point.id());
break;
}
}
}
void InternalWindowTest::initTestCase() void InternalWindowTest::initTestCase()
{ {
qRegisterMetaType<KWin::Window *>(); qRegisterMetaType<KWin::Window *>();
@ -408,56 +434,24 @@ void InternalWindowTest::testTouch()
win.show(); win.show();
QTRY_COMPARE(windowAddedSpy.count(), 1); QTRY_COMPARE(windowAddedSpy.count(), 1);
QSignalSpy pressSpy(&win, &HelperWindow::mousePressed); QSignalSpy touchDownSpy(&win, &HelperWindow::touchDown);
QSignalSpy releaseSpy(&win, &HelperWindow::mouseReleased); QSignalSpy touchUpSpy(&win, &HelperWindow::touchUp);
QSignalSpy moveSpy(&win, &HelperWindow::mouseMoved); QSignalSpy touchMotionSpy(&win, &HelperWindow::touchMotion);
quint32 timestamp = 1; quint32 timestamp = 1;
QCOMPARE(win.pressedButtons(), Qt::MouseButtons()); Test::touchDown(1, QPointF(50, 50), timestamp++);
Test::touchDown(0, QPointF(50, 50), timestamp++); QCOMPARE(touchDownSpy.count(), 1);
QCOMPARE(pressSpy.count(), 1); QCOMPARE(touchDownSpy.last().at(0).toInt(), 1);
QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50)); QCOMPARE(touchDownSpy.last().at(1).toPointF(), QPointF(50, 50));
QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));
// further touch down should not trigger Test::touchMotion(1, QPointF(90, 80), timestamp++);
Test::touchDown(1, QPointF(75, 75), timestamp++); QCOMPARE(touchMotionSpy.count(), 1);
QCOMPARE(pressSpy.count(), 1); QCOMPARE(touchMotionSpy.last().at(0).toInt(), 1);
Test::touchUp(1, timestamp++); QCOMPARE(touchMotionSpy.last().at(1).toPointF(), QPointF(90, 80));
QCOMPARE(releaseSpy.count(), 0);
QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50));
QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));
// another press
Test::touchDown(1, QPointF(10, 10), timestamp++);
QCOMPARE(pressSpy.count(), 1);
QCOMPARE(win.latestGlobalMousePos(), QPoint(50, 50));
QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));
// simulate the move
QCOMPARE(moveSpy.count(), 0);
Test::touchMotion(0, QPointF(80, 90), timestamp++);
QCOMPARE(moveSpy.count(), 1);
QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));
// move on other ID should not do anything
Test::touchMotion(1, QPointF(20, 30), timestamp++);
QCOMPARE(moveSpy.count(), 1);
QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
QCOMPARE(win.pressedButtons(), Qt::MouseButtons(Qt::LeftButton));
// now up our main point
Test::touchUp(0, timestamp++);
QCOMPARE(releaseSpy.count(), 1);
QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
QCOMPARE(win.pressedButtons(), Qt::MouseButtons());
// and up the additional point
Test::touchUp(1, timestamp++); Test::touchUp(1, timestamp++);
QCOMPARE(releaseSpy.count(), 1); QCOMPARE(touchUpSpy.count(), 1);
QCOMPARE(moveSpy.count(), 1); QCOMPARE(touchUpSpy.last().at(0).toInt(), 1);
QCOMPARE(win.latestGlobalMousePos(), QPoint(80, 90));
QCOMPARE(win.pressedButtons(), Qt::MouseButtons());
} }
void InternalWindowTest::testOpacity() void InternalWindowTest::testOpacity()

@ -1208,6 +1208,10 @@ public:
InternalWindowEventFilter() InternalWindowEventFilter()
: InputEventFilter(InputFilterOrder::InternalWindow) : InputEventFilter(InputFilterOrder::InternalWindow)
{ {
m_touchDevice = std::make_unique<QPointingDevice>(QLatin1String("some touchscreen"), 0, QInputDevice::DeviceType::TouchScreen,
QPointingDevice::PointerType::Finger, QInputDevice::Capability::Position,
10, 0, kwinApp()->session()->seat(), QPointingDeviceUniqueId());
QWindowSystemInterface::registerInputDevice(m_touchDevice.get());
} }
bool pointerEvent(MouseEvent *event, quint32 nativeButton) override bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
{ {
@ -1315,19 +1319,23 @@ public:
return false; return false;
} }
touch->setInternalPressId(id); touch->setInternalPressId(id);
// Qt's touch event API is rather complex, let's do fake mouse events instead
QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle();
m_lastGlobalTouchPos = pos;
m_lastLocalTouchPos = pos - internal->position();
QEnterEvent enterEvent(m_lastLocalTouchPos, m_lastLocalTouchPos, pos); const qreal contactAreaWidth = 8;
QCoreApplication::sendEvent(internal, &enterEvent); const qreal contactAreaHeight = 8;
QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers()); auto &touchPoint = m_touchPoints.emplaceBack(QWindowSystemInterface::TouchPoint{});
e.setAccepted(false); touchPoint.id = id;
QCoreApplication::sendEvent(internal, &e); touchPoint.area = QRectF(pos.x() - contactAreaWidth / 2, pos.y() - contactAreaHeight / 2, contactAreaWidth, contactAreaHeight);
touchPoint.state = QEventPoint::State::Pressed;
touchPoint.pressure = 1;
QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle();
QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(internal, m_touchDevice.get(), m_touchPoints, input()->keyboardModifiers());
touchPoint.state = QEventPoint::State::Stationary;
return true; return true;
} }
bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
{ {
auto touch = input()->touch(); auto touch = input()->touch();
@ -1342,12 +1350,21 @@ public:
// ignore, but filter out // ignore, but filter out
return true; return true;
} }
auto it = std::ranges::find_if(m_touchPoints, [id](const auto &touchPoint) {
return touchPoint.id == id;
});
if (it == m_touchPoints.end()) {
return false;
}
it->area.moveCenter(pos);
it->state = QEventPoint::State::Updated;
QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle(); QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle();
m_lastGlobalTouchPos = pos; QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(internal, m_touchDevice.get(), m_touchPoints, input()->keyboardModifiers());
m_lastLocalTouchPos = pos - QPointF(internal->x(), internal->y());
QMouseEvent e(QEvent::MouseMove, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers()); it->state = QEventPoint::State::Stationary;
QCoreApplication::instance()->sendEvent(internal, &e);
return true; return true;
} }
bool touchUp(qint32 id, std::chrono::microseconds time) override bool touchUp(qint32 id, std::chrono::microseconds time) override
@ -1365,25 +1382,29 @@ public:
if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) { if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) {
return removed; return removed;
} }
QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle(); input()->touch()->setInternalPressId(-1);
// send mouse up
QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
e.setAccepted(false);
QCoreApplication::sendEvent(internal, &e);
QEvent leaveEvent(QEvent::Leave); auto it = std::ranges::find_if(m_touchPoints, [id](const auto &touchPoint) {
QCoreApplication::sendEvent(internal, &leaveEvent); return touchPoint.id == id;
});
if (it == m_touchPoints.end()) {
return false;
}
m_lastGlobalTouchPos = QPointF(); it->pressure = 0;
m_lastLocalTouchPos = QPointF(); it->state = QEventPoint::State::Released;
input()->touch()->setInternalPressId(-1);
QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle();
QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(internal, m_touchDevice.get(), m_touchPoints, input()->keyboardModifiers());
m_touchPoints.erase(it);
return true; return true;
} }
private: private:
std::unique_ptr<QPointingDevice> m_touchDevice;
QList<QWindowSystemInterface::TouchPoint> m_touchPoints;
QSet<qint32> m_pressedIds; QSet<qint32> m_pressedIds;
QPointF m_lastGlobalTouchPos;
QPointF m_lastLocalTouchPos;
}; };
class MouseWheelAccumulator class MouseWheelAccumulator

Loading…
Cancel
Save