Rework InputDeviceHandler focus tracking
Summary: This patch aims at improving the Toplevel, internal window and decoration focus tracking. In detail the goals are: * Clean tracking of beneath and focus Toplevel as well as decoration and internal windows. Splitting this up in well defined sub routines. * Minimal find Toplevel operations on window stack. * Reduce code duplication in pointer and touch child classes. * Reuse tracking in drag operations. * Allow direct usage of Wayland input interfaces for decoration and internal windows in the future. * Update touch focus on external events like VD switches correctly. Test Plan: Manually and existing autotests. Reviewers: #kwin Subscribers: kwin, zzag Tags: #kwin Differential Revision: https://phabricator.kde.org/D15595
This commit is contained in:
parent
eab71a8a19
commit
2e29711323
11 changed files with 639 additions and 431 deletions
|
@ -836,8 +836,8 @@ void DecorationInputTest::testTouchEvents()
|
|||
QCOMPARE(hoverMoveSpy.count(), 3);
|
||||
QCOMPARE(hoverLeaveSpy.count(), 1);
|
||||
kwinApp()->platform()->touchUp(0, timestamp++);
|
||||
QCOMPARE(hoverMoveSpy.count(), 4);
|
||||
QCOMPARE(hoverLeaveSpy.count(), 1);
|
||||
QCOMPARE(hoverMoveSpy.count(), 3);
|
||||
QCOMPARE(hoverLeaveSpy.count(), 2);
|
||||
}
|
||||
|
||||
void DecorationInputTest::testTooltipDoesntEatKeyEvents_data()
|
||||
|
|
|
@ -230,11 +230,11 @@ void InternalWindowTest::testEnterLeave()
|
|||
|
||||
quint32 timestamp = 1;
|
||||
kwinApp()->platform()->pointerMotion(QPoint(50, 50), timestamp++);
|
||||
QTRY_COMPARE(enterSpy.count(), 1);
|
||||
QTRY_COMPARE(moveSpy.count(), 1);
|
||||
|
||||
kwinApp()->platform()->pointerMotion(QPoint(60, 50), timestamp++);
|
||||
QTRY_COMPARE(moveSpy.count(), 1);
|
||||
QCOMPARE(moveSpy.first().first().toPoint(), QPoint(60, 50));
|
||||
QTRY_COMPARE(moveSpy.count(), 2);
|
||||
QCOMPARE(moveSpy[1].first().toPoint(), QPoint(60, 50));
|
||||
|
||||
kwinApp()->platform()->pointerMotion(QPoint(101, 50), timestamp++);
|
||||
QTRY_COMPARE(leaveSpy.count(), 1);
|
||||
|
|
|
@ -232,7 +232,7 @@ void TestPointerConstraints::testConfinedPointer()
|
|||
QVERIFY(unconfinedSpy2.isValid());
|
||||
|
||||
// activate it again, this confines again
|
||||
workspace()->activateClient(static_cast<AbstractClient*>(input()->pointer()->window().data()));
|
||||
workspace()->activateClient(static_cast<AbstractClient*>(input()->pointer()->focus().data()));
|
||||
QVERIFY(confinedSpy2.wait());
|
||||
QCOMPARE(input()->pointer()->isConstrained(), true);
|
||||
|
||||
|
@ -241,7 +241,7 @@ void TestPointerConstraints::testConfinedPointer()
|
|||
QVERIFY(unconfinedSpy2.wait());
|
||||
QCOMPARE(input()->pointer()->isConstrained(), false);
|
||||
// activate it again, this confines again
|
||||
workspace()->activateClient(static_cast<AbstractClient*>(input()->pointer()->window().data()));
|
||||
workspace()->activateClient(static_cast<AbstractClient*>(input()->pointer()->focus().data()));
|
||||
QVERIFY(confinedSpy2.wait());
|
||||
QCOMPARE(input()->pointer()->isConstrained(), true);
|
||||
|
||||
|
@ -272,7 +272,7 @@ void TestPointerConstraints::testConfinedPointer()
|
|||
confinedPointer.reset(nullptr);
|
||||
Test::flushWaylandConnection();
|
||||
|
||||
QSignalSpy constraintsChangedSpy(input()->pointer()->window()->surface(), &KWayland::Server::SurfaceInterface::pointerConstraintsChanged);
|
||||
QSignalSpy constraintsChangedSpy(input()->pointer()->focus()->surface(), &KWayland::Server::SurfaceInterface::pointerConstraintsChanged);
|
||||
QVERIFY(constraintsChangedSpy.isValid());
|
||||
QVERIFY(constraintsChangedSpy.wait());
|
||||
|
||||
|
@ -348,7 +348,7 @@ void TestPointerConstraints::testLockedPointer()
|
|||
QVERIFY(lockedSpy2.isValid());
|
||||
|
||||
// activate the client again, this should lock again
|
||||
workspace()->activateClient(static_cast<AbstractClient*>(input()->pointer()->window().data()));
|
||||
workspace()->activateClient(static_cast<AbstractClient*>(input()->pointer()->focus().data()));
|
||||
QVERIFY(lockedSpy2.wait());
|
||||
QCOMPARE(input()->pointer()->isConstrained(), true);
|
||||
|
||||
|
@ -361,7 +361,7 @@ void TestPointerConstraints::testLockedPointer()
|
|||
lockedPointer.reset(nullptr);
|
||||
Test::flushWaylandConnection();
|
||||
|
||||
QSignalSpy constraintsChangedSpy(input()->pointer()->window()->surface(), &KWayland::Server::SurfaceInterface::pointerConstraintsChanged);
|
||||
QSignalSpy constraintsChangedSpy(input()->pointer()->focus()->surface(), &KWayland::Server::SurfaceInterface::pointerConstraintsChanged);
|
||||
QVERIFY(constraintsChangedSpy.isValid());
|
||||
QVERIFY(constraintsChangedSpy.wait());
|
||||
|
||||
|
|
|
@ -963,7 +963,7 @@ void PointerInputTest::testCursorImage()
|
|||
|
||||
// move cursor to center of window, this should first set a null pointer, so we still show old cursor
|
||||
Cursor::setPos(window->geometry().center());
|
||||
QCOMPARE(p->window().data(), window);
|
||||
QCOMPARE(p->focus().data(), window);
|
||||
QCOMPARE(p->cursorImage(), fallbackCursor);
|
||||
QVERIFY(enteredSpy.wait());
|
||||
|
||||
|
@ -1018,7 +1018,7 @@ void PointerInputTest::testCursorImage()
|
|||
|
||||
// move cursor somewhere else, should reset to fallback cursor
|
||||
Cursor::setPos(window->geometry().bottomLeft() + QPoint(20, 20));
|
||||
QVERIFY(p->window().isNull());
|
||||
QVERIFY(p->focus().isNull());
|
||||
QVERIFY(!p->cursorImage().isNull());
|
||||
QCOMPARE(p->cursorImage(), fallbackCursor);
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ void TestWindowSelection::testSelectOnWindowPointer()
|
|||
QVERIFY(client);
|
||||
QVERIFY(keyboardEnteredSpy.wait());
|
||||
KWin::Cursor::setPos(client->geometry().center());
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
|
||||
Toplevel *selectedWindow = nullptr;
|
||||
|
@ -142,11 +142,11 @@ void TestWindowSelection::testSelectOnWindowPointer()
|
|||
// should not have ended the mode
|
||||
QCOMPARE(input()->isSelectingWindow(), true);
|
||||
QVERIFY(!selectedWindow);
|
||||
QVERIFY(input()->pointer()->window().isNull());
|
||||
QVERIFY(input()->pointer()->focus().isNull());
|
||||
|
||||
// updating the pointer should not change anything
|
||||
input()->pointer()->update();
|
||||
QVERIFY(input()->pointer()->window().isNull());
|
||||
QVERIFY(input()->pointer()->focus().isNull());
|
||||
// updating keyboard should also not change
|
||||
input()->keyboard()->update();
|
||||
|
||||
|
@ -160,7 +160,7 @@ void TestWindowSelection::testSelectOnWindowPointer()
|
|||
kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(input()->isSelectingWindow(), false);
|
||||
QCOMPARE(selectedWindow, client);
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
// should give back keyboard and pointer
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
if (keyboardEnteredSpy.count() != 2) {
|
||||
|
@ -240,7 +240,7 @@ void TestWindowSelection::testSelectOnWindowKeyboard()
|
|||
kwinApp()->platform()->keyboardKeyPressed(key, timestamp++);
|
||||
QCOMPARE(input()->isSelectingWindow(), false);
|
||||
QCOMPARE(selectedWindow, client);
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
// should give back keyboard and pointer
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
if (keyboardEnteredSpy.count() != 2) {
|
||||
|
@ -336,7 +336,7 @@ void TestWindowSelection::testCancelOnWindowPointer()
|
|||
QVERIFY(client);
|
||||
QVERIFY(keyboardEnteredSpy.wait());
|
||||
KWin::Cursor::setPos(client->geometry().center());
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
|
||||
Toplevel *selectedWindow = nullptr;
|
||||
|
@ -363,7 +363,7 @@ void TestWindowSelection::testCancelOnWindowPointer()
|
|||
kwinApp()->platform()->pointerButtonReleased(BTN_RIGHT, timestamp++);
|
||||
QCOMPARE(input()->isSelectingWindow(), false);
|
||||
QVERIFY(!selectedWindow);
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
// should give back keyboard and pointer
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
if (keyboardEnteredSpy.count() != 2) {
|
||||
|
@ -395,7 +395,7 @@ void TestWindowSelection::testCancelOnWindowKeyboard()
|
|||
QVERIFY(client);
|
||||
QVERIFY(keyboardEnteredSpy.wait());
|
||||
KWin::Cursor::setPos(client->geometry().center());
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
|
||||
Toplevel *selectedWindow = nullptr;
|
||||
|
@ -421,7 +421,7 @@ void TestWindowSelection::testCancelOnWindowKeyboard()
|
|||
kwinApp()->platform()->keyboardKeyPressed(KEY_ESC, timestamp++);
|
||||
QCOMPARE(input()->isSelectingWindow(), false);
|
||||
QVERIFY(!selectedWindow);
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
// should give back keyboard and pointer
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
if (keyboardEnteredSpy.count() != 2) {
|
||||
|
@ -454,7 +454,7 @@ void TestWindowSelection::testSelectPointPointer()
|
|||
QVERIFY(client);
|
||||
QVERIFY(keyboardEnteredSpy.wait());
|
||||
KWin::Cursor::setPos(client->geometry().center());
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
|
||||
QPoint point;
|
||||
|
@ -488,11 +488,11 @@ void TestWindowSelection::testSelectPointPointer()
|
|||
// should not have ended the mode
|
||||
QCOMPARE(input()->isSelectingWindow(), true);
|
||||
QCOMPARE(point, QPoint());
|
||||
QVERIFY(input()->pointer()->window().isNull());
|
||||
QVERIFY(input()->pointer()->focus().isNull());
|
||||
|
||||
// updating the pointer should not change anything
|
||||
input()->pointer()->update();
|
||||
QVERIFY(input()->pointer()->window().isNull());
|
||||
QVERIFY(input()->pointer()->focus().isNull());
|
||||
// updating keyboard should also not change
|
||||
input()->keyboard()->update();
|
||||
|
||||
|
@ -506,7 +506,7 @@ void TestWindowSelection::testSelectPointPointer()
|
|||
kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(input()->isSelectingWindow(), false);
|
||||
QCOMPARE(point, input()->globalPointer().toPoint());
|
||||
QCOMPARE(input()->pointer()->window().data(), client);
|
||||
QCOMPARE(input()->pointer()->focus().data(), client);
|
||||
// should give back keyboard and pointer
|
||||
QVERIFY(pointerEnteredSpy.wait());
|
||||
if (keyboardEnteredSpy.count() != 2) {
|
||||
|
|
370
input.cpp
370
input.cpp
|
@ -3,6 +3,7 @@
|
|||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
Copyright (C) 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -227,12 +228,14 @@ public:
|
|||
auto seat = waylandServer()->seat();
|
||||
seat->setTimestamp(event->timestamp());
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
input()->pointer()->update();
|
||||
if (pointerSurfaceAllowed()) {
|
||||
// TODO: should the pointer position always stay in sync, i.e. not do the check?
|
||||
seat->setPointerPos(event->screenPos().toPoint());
|
||||
}
|
||||
} else if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) {
|
||||
if (pointerSurfaceAllowed()) {
|
||||
// TODO: can we leak presses/releases here when we move the mouse in between from an allowed surface to
|
||||
// disallowed one or vice versa?
|
||||
event->type() == QEvent::MouseButtonPress ? seat->pointerButtonPressed(nativeButton) : seat->pointerButtonReleased(nativeButton);
|
||||
}
|
||||
}
|
||||
|
@ -293,9 +296,6 @@ public:
|
|||
}
|
||||
auto seat = waylandServer()->seat();
|
||||
seat->setTimestamp(time);
|
||||
if (!seat->isTouchSequence()) {
|
||||
input()->touch()->update(pos);
|
||||
}
|
||||
if (touchSurfaceAllowed()) {
|
||||
input()->touch()->insertId(id, seat->touchDown(pos));
|
||||
}
|
||||
|
@ -477,6 +477,52 @@ public:
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchDown(quint32 id, const QPointF &pos, quint32 time) override {
|
||||
Q_UNUSED(id)
|
||||
Q_UNUSED(pos)
|
||||
Q_UNUSED(time)
|
||||
AbstractClient *c = workspace()->getMovingClient();
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchMotion(quint32 id, const QPointF &pos, quint32 time) override {
|
||||
Q_UNUSED(time)
|
||||
AbstractClient *c = workspace()->getMovingClient();
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
if (!m_set) {
|
||||
m_id = id;
|
||||
m_set = true;
|
||||
}
|
||||
if (m_id == id) {
|
||||
c->updateMoveResize(pos.toPoint());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchUp(quint32 id, quint32 time) override {
|
||||
Q_UNUSED(time)
|
||||
AbstractClient *c = workspace()->getMovingClient();
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
if (m_id == id || !m_set) {
|
||||
c->endMoveResize();
|
||||
m_set = false;
|
||||
// pass through to update decoration filter later on
|
||||
return false;
|
||||
}
|
||||
m_set = false;
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
quint32 m_id = 0;
|
||||
bool m_set = false;
|
||||
};
|
||||
|
||||
class WindowSelectorFilter : public InputEventFilter {
|
||||
|
@ -764,13 +810,6 @@ class InternalWindowEventFilter : public InputEventFilter {
|
|||
if (!internal) {
|
||||
return false;
|
||||
}
|
||||
if (event->buttons() == Qt::NoButton) {
|
||||
// update pointer window only if no button is pressed
|
||||
input()->pointer()->update();
|
||||
}
|
||||
if (!internal) {
|
||||
return false;
|
||||
}
|
||||
// find client
|
||||
switch (event->type())
|
||||
{
|
||||
|
@ -883,12 +922,11 @@ class InternalWindowEventFilter : public InputEventFilter {
|
|||
}
|
||||
auto touch = input()->touch();
|
||||
if (touch->internalPressId() != -1) {
|
||||
// already on a decoration, ignore further touch points, but filter out
|
||||
// already on internal window, ignore further touch points, but filter out
|
||||
return true;
|
||||
}
|
||||
// a new touch point
|
||||
seat->setTimestamp(time);
|
||||
touch->update(pos);
|
||||
auto internal = touch->internalWindow();
|
||||
if (!internal) {
|
||||
return false;
|
||||
|
@ -897,6 +935,10 @@ class InternalWindowEventFilter : public InputEventFilter {
|
|||
// Qt's touch event API is rather complex, let's do fake mouse events instead
|
||||
m_lastGlobalTouchPos = pos;
|
||||
m_lastLocalTouchPos = pos - QPointF(internal->x(), internal->y());
|
||||
|
||||
QEnterEvent enterEvent(m_lastLocalTouchPos, m_lastLocalTouchPos, pos);
|
||||
QCoreApplication::sendEvent(internal.data(), &enterEvent);
|
||||
|
||||
QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
|
||||
e.setAccepted(false);
|
||||
QCoreApplication::sendEvent(internal.data(), &e);
|
||||
|
@ -918,6 +960,7 @@ class InternalWindowEventFilter : public InputEventFilter {
|
|||
}
|
||||
m_lastGlobalTouchPos = pos;
|
||||
m_lastLocalTouchPos = pos - QPointF(internal->x(), internal->y());
|
||||
|
||||
QMouseEvent e(QEvent::MouseMove, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
|
||||
QCoreApplication::instance()->sendEvent(internal.data(), &e);
|
||||
return true;
|
||||
|
@ -941,6 +984,9 @@ class InternalWindowEventFilter : public InputEventFilter {
|
|||
e.setAccepted(false);
|
||||
QCoreApplication::sendEvent(internal.data(), &e);
|
||||
|
||||
QEvent leaveEvent(QEvent::Leave);
|
||||
QCoreApplication::sendEvent(internal.data(), &leaveEvent);
|
||||
|
||||
m_lastGlobalTouchPos = QPointF();
|
||||
m_lastLocalTouchPos = QPointF();
|
||||
input()->touch()->setInternalPressId(-1);
|
||||
|
@ -962,9 +1008,6 @@ public:
|
|||
const QPointF p = event->globalPos() - decoration->client()->pos();
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseMove: {
|
||||
if (event->buttons() == Qt::NoButton) {
|
||||
return false;
|
||||
}
|
||||
QHoverEvent e(QEvent::HoverMove, p, p);
|
||||
QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
|
||||
decoration->client()->processDecorationMove(p.toPoint(), event->globalPos());
|
||||
|
@ -1034,14 +1077,18 @@ public:
|
|||
return true;
|
||||
}
|
||||
seat->setTimestamp(time);
|
||||
input()->touch()->update(pos);
|
||||
auto decoration = input()->touch()->decoration();
|
||||
if (!decoration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
input()->touch()->setDecorationPressId(id);
|
||||
m_lastGlobalTouchPos = pos;
|
||||
m_lastLocalTouchPos = pos - decoration->client()->pos();
|
||||
|
||||
QHoverEvent hoverEvent(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
|
||||
QCoreApplication::sendEvent(decoration->decoration(), &hoverEvent);
|
||||
|
||||
QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
|
||||
e.setAccepted(false);
|
||||
QCoreApplication::sendEvent(decoration->decoration(), &e);
|
||||
|
@ -1065,13 +1112,10 @@ public:
|
|||
}
|
||||
m_lastGlobalTouchPos = pos;
|
||||
m_lastLocalTouchPos = pos - decoration->client()->pos();
|
||||
if (auto c = workspace()->getMovingClient()) {
|
||||
c->updateMoveResize(pos);
|
||||
} else {
|
||||
QHoverEvent e(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
|
||||
QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
|
||||
decoration->client()->processDecorationMove(m_lastLocalTouchPos.toPoint(), pos.toPoint());
|
||||
}
|
||||
|
||||
QHoverEvent e(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
|
||||
QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
|
||||
decoration->client()->processDecorationMove(m_lastLocalTouchPos.toPoint(), pos.toPoint());
|
||||
return true;
|
||||
}
|
||||
bool touchUp(quint32 id, quint32 time) override {
|
||||
|
@ -1087,25 +1131,15 @@ public:
|
|||
// ignore, but filter out
|
||||
return true;
|
||||
}
|
||||
|
||||
// send mouse up
|
||||
if (auto c = workspace()->getMovingClient()) {
|
||||
c->endMoveResize();
|
||||
} else {
|
||||
QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
|
||||
e.setAccepted(false);
|
||||
QCoreApplication::sendEvent(decoration->decoration(), &e);
|
||||
decoration->client()->processDecorationButtonRelease(&e);
|
||||
if (input()->pointer()->decoration() == decoration) {
|
||||
// send motion to current pointer position
|
||||
const QPointF p = input()->pointer()->pos() - decoration->client()->pos();
|
||||
QHoverEvent event(QEvent::HoverMove, p, p);
|
||||
QCoreApplication::instance()->sendEvent(decoration->decoration(), &event);
|
||||
} else {
|
||||
// send leave
|
||||
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
||||
QCoreApplication::instance()->sendEvent(decoration->decoration(), &event);
|
||||
}
|
||||
}
|
||||
QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
|
||||
e.setAccepted(false);
|
||||
QCoreApplication::sendEvent(decoration->decoration(), &e);
|
||||
decoration->client()->processDecorationButtonRelease(&e);
|
||||
|
||||
QHoverEvent leaveEvent(QEvent::HoverLeave, QPointF(), QPointF());
|
||||
QCoreApplication::sendEvent(decoration->decoration(), &leaveEvent);
|
||||
|
||||
m_lastGlobalTouchPos = QPointF();
|
||||
m_lastLocalTouchPos = QPointF();
|
||||
|
@ -1218,7 +1252,7 @@ public:
|
|||
if (event->type() != QEvent::MouseButtonPress) {
|
||||
return false;
|
||||
}
|
||||
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->window().data());
|
||||
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->focus().data());
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1233,7 +1267,7 @@ public:
|
|||
// only actions on vertical scroll
|
||||
return false;
|
||||
}
|
||||
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->window().data());
|
||||
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->focus().data());
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1250,8 +1284,7 @@ public:
|
|||
if (seat->isTouchSequence()) {
|
||||
return false;
|
||||
}
|
||||
input()->touch()->update(pos);
|
||||
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->touch()->window().data());
|
||||
AbstractClient *c = dynamic_cast<AbstractClient*>(input()->touch()->focus().data());
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1275,11 +1308,6 @@ public:
|
|||
seat->setTimestamp(event->timestamp());
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseMove: {
|
||||
if (event->buttons() == Qt::NoButton) {
|
||||
// update pointer window only if no button is pressed
|
||||
input()->pointer()->update();
|
||||
input()->pointer()->updatePointerConstraints();
|
||||
}
|
||||
seat->setPointerPos(event->globalPos());
|
||||
MouseEvent *e = static_cast<MouseEvent*>(event);
|
||||
if (e->delta() != QSizeF()) {
|
||||
|
@ -1292,9 +1320,6 @@ public:
|
|||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
seat->pointerButtonReleased(nativeButton);
|
||||
if (event->buttons() == Qt::NoButton) {
|
||||
input()->pointer()->update();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1328,9 +1353,6 @@ public:
|
|||
}
|
||||
auto seat = waylandServer()->seat();
|
||||
seat->setTimestamp(time);
|
||||
if (!seat->isTouchSequence()) {
|
||||
input()->touch()->update(pos);
|
||||
}
|
||||
input()->touch()->insertId(id, seat->touchDown(pos));
|
||||
return true;
|
||||
}
|
||||
|
@ -1447,7 +1469,7 @@ public:
|
|||
case QEvent::MouseMove: {
|
||||
const auto pos = input()->globalPointer();
|
||||
seat->setPointerPos(pos);
|
||||
if (Toplevel *t = input()->findToplevel(pos.toPoint())) {
|
||||
if (Toplevel *t = input()->pointer()->at()) {
|
||||
// TODO: consider decorations
|
||||
if (t->surface() != seat->dragSurface()) {
|
||||
if (AbstractClient *c = qobject_cast<AbstractClient*>(t)) {
|
||||
|
@ -1670,8 +1692,8 @@ void InputRedirection::setupInputFilters()
|
|||
installInputEventFilter(new TabBoxInputFilter);
|
||||
#endif
|
||||
installInputEventFilter(new GlobalShortcutFilter);
|
||||
installInputEventFilter(new InternalWindowEventFilter);
|
||||
installInputEventFilter(new DecorationEventFilter);
|
||||
installInputEventFilter(new InternalWindowEventFilter);
|
||||
if (waylandServer()) {
|
||||
installInputEventFilter(new WindowActionInputFilter);
|
||||
installInputEventFilter(new ForwardInputFilter);
|
||||
|
@ -2083,109 +2105,167 @@ InputDeviceHandler::InputDeviceHandler(InputRedirection *input)
|
|||
|
||||
InputDeviceHandler::~InputDeviceHandler() = default;
|
||||
|
||||
void InputDeviceHandler::updateDecoration(Toplevel *t, const QPointF &pos)
|
||||
void InputDeviceHandler::init()
|
||||
{
|
||||
const auto oldDeco = m_decoration;
|
||||
bool needsReset = waylandServer()->isScreenLocked();
|
||||
if (AbstractClient *c = dynamic_cast<AbstractClient*>(t)) {
|
||||
// check whether it's on a Decoration
|
||||
if (c->decoratedClient()) {
|
||||
const QRect clientRect = QRect(c->clientPos(), c->clientSize()).translated(c->pos());
|
||||
if (!clientRect.contains(pos.toPoint())) {
|
||||
m_decoration = c->decoratedClient();
|
||||
} else {
|
||||
needsReset = true;
|
||||
}
|
||||
} else {
|
||||
needsReset = true;
|
||||
connect(workspace(), &Workspace::stackingOrderChanged, this, &InputDeviceHandler::update);
|
||||
connect(workspace(), &Workspace::clientMinimizedChanged, this, &InputDeviceHandler::update);
|
||||
connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, this, &InputDeviceHandler::update);
|
||||
}
|
||||
|
||||
bool InputDeviceHandler::setAt(Toplevel *toplevel)
|
||||
{
|
||||
if (m_at == toplevel) {
|
||||
return false;
|
||||
}
|
||||
auto old = m_at;
|
||||
m_at = toplevel;
|
||||
emit atChanged(old, toplevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputDeviceHandler::setFocus(Toplevel *toplevel)
|
||||
{
|
||||
m_focus.focus = toplevel;
|
||||
//TODO: call focusUpdate?
|
||||
}
|
||||
|
||||
void InputDeviceHandler::setDecoration(QPointer<Decoration::DecoratedClientImpl> decoration)
|
||||
{
|
||||
auto oldDeco = m_focus.decoration;
|
||||
m_focus.decoration = decoration;
|
||||
cleanupDecoration(oldDeco.data(), m_focus.decoration.data());
|
||||
emit decorationChanged();
|
||||
}
|
||||
|
||||
void InputDeviceHandler::setInternalWindow(QWindow *window)
|
||||
{
|
||||
m_focus.internalWindow = window;
|
||||
//TODO: call internalWindowUpdate?
|
||||
}
|
||||
|
||||
void InputDeviceHandler::updateFocus()
|
||||
{
|
||||
auto oldFocus = m_focus.focus;
|
||||
m_focus.focus = m_at;
|
||||
focusUpdate(oldFocus, m_focus.focus);
|
||||
}
|
||||
|
||||
bool InputDeviceHandler::updateDecoration()
|
||||
{
|
||||
const auto oldDeco = m_focus.decoration;
|
||||
m_focus.decoration = nullptr;
|
||||
|
||||
auto *ac = qobject_cast<AbstractClient*>(m_at);
|
||||
if (ac && ac->decoratedClient()) {
|
||||
const QRect clientRect = QRect(ac->clientPos(), ac->clientSize()).translated(ac->pos());
|
||||
if (!clientRect.contains(position().toPoint())) {
|
||||
// input device above decoration
|
||||
m_focus.decoration = ac->decoratedClient();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_focus.decoration == oldDeco) {
|
||||
// no change to decoration
|
||||
return false;
|
||||
}
|
||||
cleanupDecoration(oldDeco.data(), m_focus.decoration.data());
|
||||
emit decorationChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputDeviceHandler::updateInternalWindow(QWindow *window)
|
||||
{
|
||||
if (m_focus.internalWindow == window) {
|
||||
// no change
|
||||
return;
|
||||
}
|
||||
const auto oldInternal = m_focus.internalWindow;
|
||||
m_focus.internalWindow = window;
|
||||
cleanupInternalWindow(oldInternal, window);
|
||||
}
|
||||
|
||||
void InputDeviceHandler::update()
|
||||
{
|
||||
if (!m_inited) {
|
||||
return;
|
||||
}
|
||||
const auto pos = position().toPoint();
|
||||
auto internalWindow = findInternalWindow(pos);
|
||||
|
||||
Toplevel *toplevel;
|
||||
if (internalWindow) {
|
||||
toplevel = waylandServer()->findClient(internalWindow);
|
||||
} else {
|
||||
needsReset = true;
|
||||
}
|
||||
if (needsReset) {
|
||||
m_decoration.clear();
|
||||
toplevel = input()->findToplevel(pos);
|
||||
}
|
||||
|
||||
bool leftSend = false;
|
||||
auto oldWindow = qobject_cast<AbstractClient*>(window().data());
|
||||
if (oldWindow && (m_decoration && m_decoration->client() != oldWindow)) {
|
||||
leftSend = true;
|
||||
oldWindow->leaveEvent();
|
||||
// Always set the toplevel at the position of the input device.
|
||||
setAt(toplevel);
|
||||
|
||||
if (focusUpdatesBlocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldDeco && oldDeco != m_decoration) {
|
||||
if (oldDeco->client() != t && !leftSend) {
|
||||
leftSend = true;
|
||||
oldDeco->client()->leaveEvent();
|
||||
if (internalWindow) {
|
||||
if (m_focus.internalWindow != internalWindow) {
|
||||
// changed internal window
|
||||
updateDecoration();
|
||||
updateInternalWindow(internalWindow);
|
||||
updateFocus();
|
||||
} else if (updateDecoration()) {
|
||||
// went onto or off from decoration, update focus
|
||||
updateFocus();
|
||||
}
|
||||
// send leave
|
||||
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
||||
QCoreApplication::instance()->sendEvent(oldDeco->decoration(), &event);
|
||||
return;
|
||||
}
|
||||
if (m_decoration) {
|
||||
if (m_decoration->client() != oldWindow) {
|
||||
m_decoration->client()->enterEvent(pos.toPoint());
|
||||
workspace()->updateFocusMousePosition(pos.toPoint());
|
||||
}
|
||||
const QPointF p = pos - t->pos();
|
||||
QHoverEvent event(QEvent::HoverMove, p, p);
|
||||
QCoreApplication::instance()->sendEvent(m_decoration->decoration(), &event);
|
||||
m_decoration->client()->processDecorationMove(p.toPoint(), pos.toPoint());
|
||||
updateInternalWindow(nullptr);
|
||||
|
||||
if (m_focus.focus != m_at) {
|
||||
// focus change
|
||||
updateDecoration();
|
||||
updateFocus();
|
||||
return;
|
||||
}
|
||||
// check if switched to/from decoration while staying on the same Toplevel
|
||||
if (updateDecoration()) {
|
||||
// went onto or off from decoration, update focus
|
||||
updateFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void InputDeviceHandler::updateInternalWindow(const QPointF &pos)
|
||||
QWindow* InputDeviceHandler::findInternalWindow(const QPoint &pos) const
|
||||
{
|
||||
const auto oldInternalWindow = m_internalWindow;
|
||||
bool found = false;
|
||||
// TODO: screen locked check without going through wayland server
|
||||
bool needsReset = waylandServer()->isScreenLocked();
|
||||
if (waylandServer()->isScreenLocked()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto &internalClients = waylandServer()->internalClients();
|
||||
const bool change = m_internalWindow.isNull() || !(m_internalWindow->flags().testFlag(Qt::Popup) && m_internalWindow->isVisible());
|
||||
if (!internalClients.isEmpty() && change) {
|
||||
auto it = internalClients.end();
|
||||
do {
|
||||
it--;
|
||||
if (QWindow *w = (*it)->internalWindow()) {
|
||||
if (!w->isVisible()) {
|
||||
continue;
|
||||
}
|
||||
if ((*it)->geometry().contains(pos.toPoint())) {
|
||||
// check input mask
|
||||
const QRegion mask = w->mask().translated(w->geometry().topLeft());
|
||||
if (!mask.isEmpty() && !mask.contains(pos.toPoint())) {
|
||||
continue;
|
||||
}
|
||||
if (w->property("outputOnly").toBool()) {
|
||||
continue;
|
||||
}
|
||||
m_internalWindow = QPointer<QWindow>(w);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (it != internalClients.begin());
|
||||
if (!found) {
|
||||
needsReset = true;
|
||||
}
|
||||
if (internalClients.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (needsReset) {
|
||||
m_internalWindow.clear();
|
||||
}
|
||||
if (oldInternalWindow != m_internalWindow) {
|
||||
// changed
|
||||
if (oldInternalWindow) {
|
||||
QEvent event(QEvent::Leave);
|
||||
QCoreApplication::sendEvent(oldInternalWindow.data(), &event);
|
||||
|
||||
auto it = internalClients.end();
|
||||
do {
|
||||
--it;
|
||||
QWindow *w = (*it)->internalWindow();
|
||||
if (!w || !w->isVisible()) {
|
||||
continue;
|
||||
}
|
||||
if (m_internalWindow) {
|
||||
QEnterEvent event(pos - m_internalWindow->position(),
|
||||
pos - m_internalWindow->position(),
|
||||
pos);
|
||||
QCoreApplication::sendEvent(m_internalWindow.data(), &event);
|
||||
if (!(*it)->geometry().contains(pos)) {
|
||||
continue;
|
||||
}
|
||||
emit internalWindowChanged();
|
||||
}
|
||||
// check input mask
|
||||
const QRegion mask = w->mask().translated(w->geometry().topLeft());
|
||||
if (!mask.isEmpty() && !mask.contains(pos)) {
|
||||
continue;
|
||||
}
|
||||
if (w->property("outputOnly").toBool()) {
|
||||
continue;
|
||||
}
|
||||
return w;
|
||||
} while (it != internalClients.begin());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
88
input.h
88
input.h
|
@ -3,6 +3,7 @@
|
|||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
Copyright (C) 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -365,50 +366,91 @@ protected:
|
|||
void passToWaylandServer(QKeyEvent *event);
|
||||
};
|
||||
|
||||
class InputDeviceHandler : public QObject
|
||||
class KWIN_EXPORT InputDeviceHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~InputDeviceHandler();
|
||||
virtual void init();
|
||||
|
||||
QPointer<Toplevel> window() const {
|
||||
return m_window;
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief First Toplevel currently at the position of the input device
|
||||
* according to the stacking order.
|
||||
* @return Toplevel* at device position.
|
||||
*/
|
||||
QPointer<Toplevel> at() const {
|
||||
return m_at;
|
||||
}
|
||||
/**
|
||||
* @brief Toplevel currently having pointer input focus (this might
|
||||
* be different from the Toplevel at the position of the pointer).
|
||||
* @return Toplevel* with pointer focus.
|
||||
*/
|
||||
QPointer<Toplevel> focus() const {
|
||||
return m_focus.focus;
|
||||
}
|
||||
/**
|
||||
* @brief The Decoration currently receiving events.
|
||||
* @return decoration with pointer focus.
|
||||
**/
|
||||
QPointer<Decoration::DecoratedClientImpl> decoration() const {
|
||||
return m_decoration;
|
||||
return m_focus.decoration;
|
||||
}
|
||||
/**
|
||||
* @brief The internal window currently receiving events.
|
||||
* @return QWindow with pointer focus.
|
||||
**/
|
||||
QPointer<QWindow> internalWindow() const {
|
||||
return m_internalWindow;
|
||||
return m_focus.internalWindow;
|
||||
}
|
||||
|
||||
virtual QPointF position() const = 0;
|
||||
|
||||
void setFocus(Toplevel *toplevel);
|
||||
void setDecoration(QPointer<Decoration::DecoratedClientImpl> decoration);
|
||||
void setInternalWindow(QWindow *window);
|
||||
|
||||
Q_SIGNALS:
|
||||
void atChanged(Toplevel *old, Toplevel *now);
|
||||
void decorationChanged();
|
||||
void internalWindowChanged();
|
||||
|
||||
protected:
|
||||
explicit InputDeviceHandler(InputRedirection *parent);
|
||||
void updateDecoration(Toplevel *t, const QPointF &pos);
|
||||
void updateInternalWindow(const QPointF &pos);
|
||||
void setWindow(QPointer<Toplevel> window = QPointer<Toplevel>()) {
|
||||
m_window = window;
|
||||
|
||||
virtual void cleanupInternalWindow(QWindow *old, QWindow *now) = 0;
|
||||
virtual void cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) = 0;
|
||||
|
||||
virtual void focusUpdate(Toplevel *old, Toplevel *now) = 0;
|
||||
|
||||
virtual bool focusUpdatesBlocked() {
|
||||
return false;
|
||||
}
|
||||
void clearDecoration() {
|
||||
m_decoration.clear();
|
||||
|
||||
inline bool inited() const {
|
||||
return m_inited;
|
||||
}
|
||||
void clearInternalWindow() {
|
||||
m_internalWindow.clear();
|
||||
inline void setInited(bool set) {
|
||||
m_inited = set;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief The Toplevel which currently receives events
|
||||
*/
|
||||
QPointer<Toplevel> m_window;
|
||||
/**
|
||||
* @brief The Decoration which currently receives events.
|
||||
**/
|
||||
QPointer<Decoration::DecoratedClientImpl> m_decoration;
|
||||
QPointer<QWindow> m_internalWindow;
|
||||
bool setAt(Toplevel *toplevel);
|
||||
void updateFocus();
|
||||
bool updateDecoration();
|
||||
void updateInternalWindow(QWindow *window);
|
||||
|
||||
QWindow* findInternalWindow(const QPoint &pos) const;
|
||||
|
||||
QPointer<Toplevel> m_at;
|
||||
struct {
|
||||
QPointer<Toplevel> focus;
|
||||
QPointer<Decoration::DecoratedClientImpl> decoration;
|
||||
QPointer<QWindow> internalWindow;
|
||||
} m_focus;
|
||||
|
||||
bool m_inited = false;
|
||||
};
|
||||
|
||||
inline
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
Copyright (C) 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -134,13 +135,14 @@ PointerInputRedirection::~PointerInputRedirection() = default;
|
|||
|
||||
void PointerInputRedirection::init()
|
||||
{
|
||||
Q_ASSERT(!m_inited);
|
||||
Q_ASSERT(!inited());
|
||||
m_cursor = new CursorImage(this);
|
||||
m_inited = true;
|
||||
setInited(true);
|
||||
InputDeviceHandler::init();
|
||||
|
||||
connect(m_cursor, &CursorImage::changed, kwinApp()->platform(), &Platform::cursorChanged);
|
||||
emit m_cursor->changed();
|
||||
connect(workspace(), &Workspace::stackingOrderChanged, this, &PointerInputRedirection::update);
|
||||
connect(workspace(), &Workspace::clientMinimizedChanged, this, &PointerInputRedirection::update);
|
||||
|
||||
connect(screens(), &Screens::changed, this, &PointerInputRedirection::updateAfterScreenChange);
|
||||
if (waylandServer()->hasScreenLockerIntegration()) {
|
||||
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this,
|
||||
|
@ -151,51 +153,16 @@ void PointerInputRedirection::init()
|
|||
}
|
||||
);
|
||||
}
|
||||
connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||
connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||
connect(workspace(), &QObject::destroyed, this, [this] { setInited(false); });
|
||||
connect(waylandServer(), &QObject::destroyed, this, [this] { setInited(false); });
|
||||
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::dragEnded, this,
|
||||
[this] {
|
||||
// need to force a focused pointer change
|
||||
waylandServer()->seat()->setFocusedPointerSurface(nullptr);
|
||||
setWindow();
|
||||
setFocus(nullptr);
|
||||
update();
|
||||
}
|
||||
);
|
||||
connect(this, &PointerInputRedirection::internalWindowChanged, this,
|
||||
[this] {
|
||||
disconnect(m_internalWindowConnection);
|
||||
m_internalWindowConnection = QMetaObject::Connection();
|
||||
if (internalWindow()) {
|
||||
m_internalWindowConnection = connect(internalWindow().data(), &QWindow::visibleChanged, this,
|
||||
[this] (bool visible) {
|
||||
if (!visible) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
connect(this, &PointerInputRedirection::decorationChanged, this,
|
||||
[this] {
|
||||
disconnect(m_decorationGeometryConnection);
|
||||
m_decorationGeometryConnection = QMetaObject::Connection();
|
||||
if (decoration()) {
|
||||
m_decorationGeometryConnection = connect(decoration()->client(), &AbstractClient::geometryChanged, this,
|
||||
[this] {
|
||||
// ensure maximize button gets the leave event when maximizing/restore a window, see BUG 385140
|
||||
const auto oldDeco = decoration();
|
||||
update();
|
||||
if (oldDeco && oldDeco == decoration() && !decoration()->client()->isMove() && !decoration()->client()->isResize() && !areButtonsPressed()) {
|
||||
// position of window did not change, we need to send HoverMotion manually
|
||||
const QPointF p = m_pos - decoration()->client()->pos();
|
||||
QHoverEvent event(QEvent::HoverMove, p, p);
|
||||
QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
);
|
||||
// connect the move resize of all window
|
||||
auto setupMoveResizeConnection = [this] (AbstractClient *c) {
|
||||
connect(c, &AbstractClient::clientStartUserMovedResized, this, &PointerInputRedirection::updateOnStartMoveResize);
|
||||
|
@ -213,9 +180,9 @@ void PointerInputRedirection::init()
|
|||
|
||||
void PointerInputRedirection::updateOnStartMoveResize()
|
||||
{
|
||||
breakPointerConstraints(window() ? window()->surface() : nullptr);
|
||||
breakPointerConstraints(focus() ? focus()->surface() : nullptr);
|
||||
disconnectPointerConstraintsConnection();
|
||||
setWindow();
|
||||
setFocus(nullptr);
|
||||
waylandServer()->seat()->setFocusedPointerSurface(nullptr);
|
||||
}
|
||||
|
||||
|
@ -226,22 +193,22 @@ void PointerInputRedirection::updateToReset()
|
|||
m_internalWindowConnection = QMetaObject::Connection();
|
||||
QEvent event(QEvent::Leave);
|
||||
QCoreApplication::sendEvent(internalWindow().data(), &event);
|
||||
clearInternalWindow();
|
||||
setInternalWindow(nullptr);
|
||||
}
|
||||
if (decoration()) {
|
||||
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
||||
QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
|
||||
clearDecoration();
|
||||
setDecoration(nullptr);
|
||||
}
|
||||
if (window()) {
|
||||
if (AbstractClient *c = qobject_cast<AbstractClient*>(window().data())) {
|
||||
if (focus()) {
|
||||
if (AbstractClient *c = qobject_cast<AbstractClient*>(focus().data())) {
|
||||
c->leaveEvent();
|
||||
}
|
||||
disconnect(m_windowGeometryConnection);
|
||||
m_windowGeometryConnection = QMetaObject::Connection();
|
||||
breakPointerConstraints(window()->surface());
|
||||
disconnect(m_focusGeometryConnection);
|
||||
m_focusGeometryConnection = QMetaObject::Connection();
|
||||
breakPointerConstraints(focus()->surface());
|
||||
disconnectPointerConstraintsConnection();
|
||||
setWindow();
|
||||
setFocus(nullptr);
|
||||
}
|
||||
waylandServer()->seat()->setFocusedPointerSurface(nullptr);
|
||||
}
|
||||
|
@ -296,7 +263,7 @@ QVector<PositionUpdateBlocker::ScheduledPosition> PositionUpdateBlocker::s_sched
|
|||
|
||||
void PointerInputRedirection::processMotion(const QPointF &pos, const QSizeF &delta, const QSizeF &deltaNonAccelerated, uint32_t time, quint64 timeUsec, LibInput::Device *device)
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
if (PositionUpdateBlocker::isPositionBlocked()) {
|
||||
|
@ -311,14 +278,13 @@ void PointerInputRedirection::processMotion(const QPointF &pos, const QSizeF &de
|
|||
delta, deltaNonAccelerated, timeUsec, device);
|
||||
event.setModifiersRelevantForGlobalShortcuts(input()->modifiersRelevantForGlobalShortcuts());
|
||||
|
||||
update();
|
||||
input()->processSpies(std::bind(&InputEventSpy::pointerEvent, std::placeholders::_1, &event));
|
||||
input()->processFilters(std::bind(&InputEventFilter::pointerEvent, std::placeholders::_1, &event, 0));
|
||||
}
|
||||
|
||||
void PointerInputRedirection::processButton(uint32_t button, InputRedirection::PointerButtonState state, uint32_t time, LibInput::Device *device)
|
||||
{
|
||||
updateButton(button, state);
|
||||
|
||||
QEvent::Type type;
|
||||
switch (state) {
|
||||
case InputRedirection::PointerButtonReleased:
|
||||
|
@ -326,12 +292,15 @@ void PointerInputRedirection::processButton(uint32_t button, InputRedirection::P
|
|||
break;
|
||||
case InputRedirection::PointerButtonPressed:
|
||||
type = QEvent::MouseButtonPress;
|
||||
update();
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
updateButton(button, state);
|
||||
|
||||
MouseEvent event(type, m_pos, buttonToQtMouseButton(button), m_qtButtons,
|
||||
input()->keyboardModifiers(), time, QSizeF(), QSizeF(), 0, device);
|
||||
event.setModifiersRelevantForGlobalShortcuts(input()->modifiersRelevantForGlobalShortcuts());
|
||||
|
@ -339,11 +308,15 @@ void PointerInputRedirection::processButton(uint32_t button, InputRedirection::P
|
|||
|
||||
input()->processSpies(std::bind(&InputEventSpy::pointerEvent, std::placeholders::_1, &event));
|
||||
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
input()->processFilters(std::bind(&InputEventFilter::pointerEvent, std::placeholders::_1, &event, button));
|
||||
|
||||
if (state == InputRedirection::PointerButtonReleased) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerInputRedirection::processAxis(InputRedirection::PointerAxis axis, qreal delta, uint32_t time, LibInput::Device *device)
|
||||
|
@ -351,6 +324,7 @@ void PointerInputRedirection::processAxis(InputRedirection::PointerAxis axis, qr
|
|||
if (delta == 0) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
emit input()->pointerAxisChanged(axis, delta);
|
||||
|
||||
|
@ -361,7 +335,7 @@ void PointerInputRedirection::processAxis(InputRedirection::PointerAxis axis, qr
|
|||
|
||||
input()->processSpies(std::bind(&InputEventSpy::wheelEvent, std::placeholders::_1, &wheelEvent));
|
||||
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
input()->processFilters(std::bind(&InputEventFilter::wheelEvent, std::placeholders::_1, &wheelEvent));
|
||||
|
@ -370,7 +344,7 @@ void PointerInputRedirection::processAxis(InputRedirection::PointerAxis axis, qr
|
|||
void PointerInputRedirection::processSwipeGestureBegin(int fingerCount, quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -381,9 +355,10 @@ void PointerInputRedirection::processSwipeGestureBegin(int fingerCount, quint32
|
|||
void PointerInputRedirection::processSwipeGestureUpdate(const QSizeF &delta, quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::swipeGestureUpdate, std::placeholders::_1, delta, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::swipeGestureUpdate, std::placeholders::_1, delta, time));
|
||||
|
@ -392,9 +367,10 @@ void PointerInputRedirection::processSwipeGestureUpdate(const QSizeF &delta, qui
|
|||
void PointerInputRedirection::processSwipeGestureEnd(quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::swipeGestureEnd, std::placeholders::_1, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::swipeGestureEnd, std::placeholders::_1, time));
|
||||
|
@ -403,9 +379,10 @@ void PointerInputRedirection::processSwipeGestureEnd(quint32 time, KWin::LibInpu
|
|||
void PointerInputRedirection::processSwipeGestureCancelled(quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::swipeGestureCancelled, std::placeholders::_1, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::swipeGestureCancelled, std::placeholders::_1, time));
|
||||
|
@ -414,9 +391,10 @@ void PointerInputRedirection::processSwipeGestureCancelled(quint32 time, KWin::L
|
|||
void PointerInputRedirection::processPinchGestureBegin(int fingerCount, quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::pinchGestureBegin, std::placeholders::_1, fingerCount, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::pinchGestureBegin, std::placeholders::_1, fingerCount, time));
|
||||
|
@ -425,9 +403,10 @@ void PointerInputRedirection::processPinchGestureBegin(int fingerCount, quint32
|
|||
void PointerInputRedirection::processPinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::pinchGestureUpdate, std::placeholders::_1, scale, angleDelta, delta, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::pinchGestureUpdate, std::placeholders::_1, scale, angleDelta, delta, time));
|
||||
|
@ -436,9 +415,10 @@ void PointerInputRedirection::processPinchGestureUpdate(qreal scale, qreal angle
|
|||
void PointerInputRedirection::processPinchGestureEnd(quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::pinchGestureEnd, std::placeholders::_1, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::pinchGestureEnd, std::placeholders::_1, time));
|
||||
|
@ -447,9 +427,10 @@ void PointerInputRedirection::processPinchGestureEnd(quint32 time, KWin::LibInpu
|
|||
void PointerInputRedirection::processPinchGestureCancelled(quint32 time, KWin::LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
||||
input()->processSpies(std::bind(&InputEventSpy::pinchGestureCancelled, std::placeholders::_1, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::pinchGestureCancelled, std::placeholders::_1, time));
|
||||
|
@ -465,104 +446,160 @@ bool PointerInputRedirection::areButtonsPressed() const
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool s_cursorUpdateBlocking = false;
|
||||
|
||||
void PointerInputRedirection::update()
|
||||
bool PointerInputRedirection::focusUpdatesBlocked()
|
||||
{
|
||||
if (!m_inited) {
|
||||
return;
|
||||
if (!inited()) {
|
||||
return true;
|
||||
}
|
||||
if (waylandServer()->seat()->isDragPointer()) {
|
||||
// ignore during drag and drop
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (waylandServer()->seat()->isTouchSequence()) {
|
||||
// ignore during touch operations
|
||||
return true;
|
||||
}
|
||||
if (input()->isSelectingWindow()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (areButtonsPressed()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
Toplevel *t = input()->findToplevel(m_pos.toPoint());
|
||||
const auto oldDeco = decoration();
|
||||
updateInternalWindow(m_pos);
|
||||
if (!internalWindow()) {
|
||||
updateDecoration(t, m_pos);
|
||||
} else {
|
||||
updateDecoration(waylandServer()->findClient(internalWindow()), m_pos);
|
||||
if (decoration()) {
|
||||
disconnect(m_internalWindowConnection);
|
||||
m_internalWindowConnection = QMetaObject::Connection();
|
||||
QEvent event(QEvent::Leave);
|
||||
QCoreApplication::sendEvent(internalWindow().data(), &event);
|
||||
clearInternalWindow();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PointerInputRedirection::cleanupInternalWindow(QWindow *old, QWindow *now)
|
||||
{
|
||||
disconnect(m_internalWindowConnection);
|
||||
m_internalWindowConnection = QMetaObject::Connection();
|
||||
|
||||
if (old) {
|
||||
// leave internal window
|
||||
// TODO: do this instead via Wayland protocol as below
|
||||
QEvent leaveEvent(QEvent::Leave);
|
||||
QCoreApplication::sendEvent(old, &leaveEvent);
|
||||
}
|
||||
if (decoration() || internalWindow()) {
|
||||
t = nullptr;
|
||||
}
|
||||
if (decoration() != oldDeco) {
|
||||
emit decorationChanged();
|
||||
}
|
||||
auto oldWindow = window();
|
||||
if (!oldWindow.isNull() && t == window().data()) {
|
||||
return;
|
||||
}
|
||||
auto seat = waylandServer()->seat();
|
||||
// disconnect old surface
|
||||
if (oldWindow) {
|
||||
if (AbstractClient *c = qobject_cast<AbstractClient*>(oldWindow.data())) {
|
||||
c->leaveEvent();
|
||||
}
|
||||
disconnect(m_windowGeometryConnection);
|
||||
m_windowGeometryConnection = QMetaObject::Connection();
|
||||
breakPointerConstraints(oldWindow->surface());
|
||||
disconnectPointerConstraintsConnection();
|
||||
}
|
||||
if (AbstractClient *c = qobject_cast<AbstractClient*>(t)) {
|
||||
// only send enter if it wasn't on deco for the same client before
|
||||
if (decoration().isNull() || decoration()->client() != c) {
|
||||
c->enterEvent(m_pos.toPoint());
|
||||
workspace()->updateFocusMousePosition(m_pos.toPoint());
|
||||
}
|
||||
}
|
||||
if (t && t->surface()) {
|
||||
setWindow(t);
|
||||
// TODO: add convenient API to update global pos together with updating focused surface
|
||||
warpXcbOnSurfaceLeft(t->surface());
|
||||
s_cursorUpdateBlocking = true;
|
||||
seat->setFocusedPointerSurface(nullptr);
|
||||
s_cursorUpdateBlocking = false;
|
||||
seat->setPointerPos(m_pos.toPoint());
|
||||
seat->setFocusedPointerSurface(t->surface(), t->inputTransformation());
|
||||
m_windowGeometryConnection = connect(t, &Toplevel::geometryChanged, this,
|
||||
[this] {
|
||||
if (window().isNull()) {
|
||||
return;
|
||||
|
||||
if (now) {
|
||||
m_internalWindowConnection = connect(internalWindow().data(), &QWindow::visibleChanged, this,
|
||||
[this] (bool visible) {
|
||||
if (!visible) {
|
||||
update();
|
||||
}
|
||||
// TODO: can we check on the client instead?
|
||||
if (workspace()->getMovingClient()) {
|
||||
// don't update while moving
|
||||
return;
|
||||
}
|
||||
auto seat = waylandServer()->seat();
|
||||
if (window().data()->surface() != seat->focusedPointerSurface()) {
|
||||
return;
|
||||
}
|
||||
seat->setFocusedPointerSurfaceTransformation(window().data()->inputTransformation());
|
||||
}
|
||||
);
|
||||
m_constraintsConnection = connect(window()->surface(), &KWayland::Server::SurfaceInterface::pointerConstraintsChanged,
|
||||
this, &PointerInputRedirection::updatePointerConstraints);
|
||||
m_constraintsActivatedConnection = connect(workspace(), &Workspace::clientActivated,
|
||||
this, &PointerInputRedirection::updatePointerConstraints);
|
||||
// check whether a pointer confinement/lock fires
|
||||
updatePointerConstraints();
|
||||
} else {
|
||||
setWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now)
|
||||
{
|
||||
disconnect(m_decorationGeometryConnection);
|
||||
m_decorationGeometryConnection = QMetaObject::Connection();
|
||||
workspace()->updateFocusMousePosition(position().toPoint());
|
||||
|
||||
if (old) {
|
||||
// send leave event to old decoration
|
||||
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
||||
QCoreApplication::instance()->sendEvent(old->decoration(), &event);
|
||||
}
|
||||
if (!now) {
|
||||
// left decoration
|
||||
return;
|
||||
}
|
||||
|
||||
waylandServer()->seat()->setFocusedPointerSurface(nullptr);
|
||||
|
||||
auto pos = m_pos - now->client()->pos();
|
||||
QHoverEvent event(QEvent::HoverEnter, pos, pos);
|
||||
QCoreApplication::instance()->sendEvent(now->decoration(), &event);
|
||||
now->client()->processDecorationMove(pos.toPoint(), m_pos.toPoint());
|
||||
|
||||
m_decorationGeometryConnection = connect(decoration()->client(), &AbstractClient::geometryChanged, this,
|
||||
[this] {
|
||||
// ensure maximize button gets the leave event when maximizing/restore a window, see BUG 385140
|
||||
const auto oldDeco = decoration();
|
||||
update();
|
||||
if (oldDeco &&
|
||||
oldDeco == decoration() &&
|
||||
!decoration()->client()->isMove() &&
|
||||
!decoration()->client()->isResize() &&
|
||||
!areButtonsPressed()) {
|
||||
// position of window did not change, we need to send HoverMotion manually
|
||||
const QPointF p = m_pos - decoration()->client()->pos();
|
||||
QHoverEvent event(QEvent::HoverMove, p, p);
|
||||
QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
static bool s_cursorUpdateBlocking = false;
|
||||
|
||||
void PointerInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow)
|
||||
{
|
||||
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusOld)) {
|
||||
ac->leaveEvent();
|
||||
breakPointerConstraints(ac->surface());
|
||||
disconnectPointerConstraintsConnection();
|
||||
}
|
||||
disconnect(m_focusGeometryConnection);
|
||||
m_focusGeometryConnection = QMetaObject::Connection();
|
||||
|
||||
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusNow)) {
|
||||
ac->enterEvent(m_pos.toPoint());
|
||||
workspace()->updateFocusMousePosition(m_pos.toPoint());
|
||||
}
|
||||
|
||||
auto seat = waylandServer()->seat();
|
||||
if (!focusNow || !focusNow->surface() || decoration()) {
|
||||
// no new surface or internal window or on decoration -> cleanup
|
||||
warpXcbOnSurfaceLeft(nullptr);
|
||||
seat->setFocusedPointerSurface(nullptr);
|
||||
t = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (internalWindow()) {
|
||||
// enter internal window
|
||||
// TODO: do this instead via Wayland protocol as below
|
||||
const auto pos = at()->pos();
|
||||
QEnterEvent enterEvent(pos, pos, m_pos);
|
||||
QCoreApplication::sendEvent(internalWindow().data(), &enterEvent);
|
||||
}
|
||||
|
||||
// TODO: add convenient API to update global pos together with updating focused surface
|
||||
warpXcbOnSurfaceLeft(focusNow->surface());
|
||||
|
||||
// TODO: why? in order to reset the cursor icon?
|
||||
s_cursorUpdateBlocking = true;
|
||||
seat->setFocusedPointerSurface(nullptr);
|
||||
s_cursorUpdateBlocking = false;
|
||||
|
||||
seat->setPointerPos(m_pos.toPoint());
|
||||
seat->setFocusedPointerSurface(focusNow->surface(), focusNow->inputTransformation());
|
||||
|
||||
m_focusGeometryConnection = connect(focusNow, &Toplevel::geometryChanged, this,
|
||||
[this] {
|
||||
// TODO: why no assert possible?
|
||||
if (!focus()) {
|
||||
return;
|
||||
}
|
||||
// TODO: can we check on the client instead?
|
||||
if (workspace()->getMovingClient()) {
|
||||
// don't update while moving
|
||||
return;
|
||||
}
|
||||
auto seat = waylandServer()->seat();
|
||||
if (focus()->surface() != seat->focusedPointerSurface()) {
|
||||
return;
|
||||
}
|
||||
seat->setFocusedPointerSurfaceTransformation(focus()->inputTransformation());
|
||||
}
|
||||
);
|
||||
|
||||
m_constraintsConnection = connect(focusNow->surface(), &KWayland::Server::SurfaceInterface::pointerConstraintsChanged,
|
||||
this, &PointerInputRedirection::updatePointerConstraints);
|
||||
m_constraintsActivatedConnection = connect(workspace(), &Workspace::clientActivated,
|
||||
this, &PointerInputRedirection::updatePointerConstraints);
|
||||
updatePointerConstraints();
|
||||
}
|
||||
|
||||
void PointerInputRedirection::breakPointerConstraints(KWayland::Server::SurfaceInterface *surface)
|
||||
|
@ -624,10 +661,10 @@ void PointerInputRedirection::setEnableConstraints(bool set)
|
|||
|
||||
void PointerInputRedirection::updatePointerConstraints()
|
||||
{
|
||||
if (window().isNull()) {
|
||||
if (focus().isNull()) {
|
||||
return;
|
||||
}
|
||||
const auto s = window()->surface();
|
||||
const auto s = focus()->surface();
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
@ -637,7 +674,7 @@ void PointerInputRedirection::updatePointerConstraints()
|
|||
if (!supportsWarping()) {
|
||||
return;
|
||||
}
|
||||
const bool canConstrain = m_enableConstraints && window() == workspace()->activeClient();
|
||||
const bool canConstrain = m_enableConstraints && focus() == workspace()->activeClient();
|
||||
const auto cf = s->confinedPointer();
|
||||
if (cf) {
|
||||
if (cf->isConfined()) {
|
||||
|
@ -648,21 +685,21 @@ void PointerInputRedirection::updatePointerConstraints()
|
|||
}
|
||||
return;
|
||||
}
|
||||
const QRegion r = getConstraintRegion(window().data(), cf.data());
|
||||
const QRegion r = getConstraintRegion(focus().data(), cf.data());
|
||||
if (canConstrain && r.contains(m_pos.toPoint())) {
|
||||
cf->setConfined(true);
|
||||
m_confined = true;
|
||||
m_confinedPointerRegionConnection = connect(cf.data(), &KWayland::Server::ConfinedPointerInterface::regionChanged, this,
|
||||
[this] {
|
||||
if (!window()) {
|
||||
if (!focus()) {
|
||||
return;
|
||||
}
|
||||
const auto s = window()->surface();
|
||||
const auto s = focus()->surface();
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
const auto cf = s->confinedPointer();
|
||||
if (!getConstraintRegion(window().data(), cf.data()).contains(m_pos.toPoint())) {
|
||||
if (!getConstraintRegion(focus().data(), cf.data()).contains(m_pos.toPoint())) {
|
||||
// pointer no longer in confined region, break the confinement
|
||||
cf->setConfined(false);
|
||||
m_confined = false;
|
||||
|
@ -688,13 +725,13 @@ void PointerInputRedirection::updatePointerConstraints()
|
|||
lock->setLocked(false);
|
||||
m_locked = false;
|
||||
disconnectLockedPointerAboutToBeUnboundConnection();
|
||||
if (! (hint.x() < 0 || hint.y() < 0) && window()) {
|
||||
processMotion(window()->pos() - window()->clientContentPos() + hint, waylandServer()->seat()->timestamp());
|
||||
if (! (hint.x() < 0 || hint.y() < 0) && focus()) {
|
||||
processMotion(focus()->pos() - focus()->clientContentPos() + hint, waylandServer()->seat()->timestamp());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const QRegion r = getConstraintRegion(window().data(), lock.data());
|
||||
const QRegion r = getConstraintRegion(focus().data(), lock.data());
|
||||
if (canConstrain && r.contains(m_pos.toPoint())) {
|
||||
lock->setLocked(true);
|
||||
m_locked = true;
|
||||
|
@ -704,10 +741,10 @@ void PointerInputRedirection::updatePointerConstraints()
|
|||
m_lockedPointerAboutToBeUnboundConnection = connect(lock.data(), &KWayland::Server::LockedPointerInterface::aboutToBeUnbound, this,
|
||||
[this, lock]() {
|
||||
const auto hint = lock->cursorPositionHint();
|
||||
if (hint.x() < 0 || hint.y() < 0 || !window()) {
|
||||
if (hint.x() < 0 || hint.y() < 0 || !focus()) {
|
||||
return;
|
||||
}
|
||||
auto globalHint = window()->pos() - window()->clientContentPos() + hint;
|
||||
auto globalHint = focus()->pos() - focus()->clientContentPos() + hint;
|
||||
|
||||
// When the resource finally goes away, reposition the cursor according to the hint
|
||||
connect(lock.data(), &KWayland::Server::LockedPointerInterface::unbound, this,
|
||||
|
@ -755,10 +792,10 @@ void PointerInputRedirection::warpXcbOnSurfaceLeft(KWayland::Server::SurfaceInte
|
|||
|
||||
QPointF PointerInputRedirection::applyPointerConfinement(const QPointF &pos) const
|
||||
{
|
||||
if (!window()) {
|
||||
if (!focus()) {
|
||||
return pos;
|
||||
}
|
||||
auto s = window()->surface();
|
||||
auto s = focus()->surface();
|
||||
if (!s) {
|
||||
return pos;
|
||||
}
|
||||
|
@ -770,7 +807,7 @@ QPointF PointerInputRedirection::applyPointerConfinement(const QPointF &pos) con
|
|||
return pos;
|
||||
}
|
||||
|
||||
const QRegion confinementRegion = getConstraintRegion(window().data(), cf.data());
|
||||
const QRegion confinementRegion = getConstraintRegion(focus().data(), cf.data());
|
||||
if (confinementRegion.contains(pos.toPoint())) {
|
||||
return pos;
|
||||
}
|
||||
|
@ -843,7 +880,7 @@ void PointerInputRedirection::warp(const QPointF &pos)
|
|||
|
||||
bool PointerInputRedirection::supportsWarping() const
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return false;
|
||||
}
|
||||
if (m_supportsWarping) {
|
||||
|
@ -857,7 +894,7 @@ bool PointerInputRedirection::supportsWarping() const
|
|||
|
||||
void PointerInputRedirection::updateAfterScreenChange()
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
if (screenContainsPos(m_pos)) {
|
||||
|
@ -872,7 +909,7 @@ void PointerInputRedirection::updateAfterScreenChange()
|
|||
|
||||
QImage PointerInputRedirection::cursorImage() const
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return QImage();
|
||||
}
|
||||
return m_cursor->image();
|
||||
|
@ -880,7 +917,7 @@ QImage PointerInputRedirection::cursorImage() const
|
|||
|
||||
QPoint PointerInputRedirection::cursorHotSpot() const
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return QPoint();
|
||||
}
|
||||
return m_cursor->hotSpot();
|
||||
|
@ -888,15 +925,20 @@ QPoint PointerInputRedirection::cursorHotSpot() const
|
|||
|
||||
void PointerInputRedirection::markCursorAsRendered()
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
m_cursor->markAsRendered();
|
||||
}
|
||||
|
||||
QPointF PointerInputRedirection::position() const
|
||||
{
|
||||
return m_pos.toPoint();
|
||||
}
|
||||
|
||||
void PointerInputRedirection::setEffectsOverrideCursor(Qt::CursorShape shape)
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
// current pointer focus window should get a leave event
|
||||
|
@ -906,7 +948,7 @@ void PointerInputRedirection::setEffectsOverrideCursor(Qt::CursorShape shape)
|
|||
|
||||
void PointerInputRedirection::removeEffectsOverrideCursor()
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
// cursor position might have changed while there was an effect in place
|
||||
|
@ -916,7 +958,7 @@ void PointerInputRedirection::removeEffectsOverrideCursor()
|
|||
|
||||
void PointerInputRedirection::setWindowSelectionCursor(const QByteArray &shape)
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
// send leave to current pointer focus window
|
||||
|
@ -926,7 +968,7 @@ void PointerInputRedirection::setWindowSelectionCursor(const QByteArray &shape)
|
|||
|
||||
void PointerInputRedirection::removeWindowSelectionCursor()
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
update();
|
||||
|
@ -1302,7 +1344,7 @@ void CursorImage::reevaluteSource()
|
|||
setSource(CursorSource::Decoration);
|
||||
return;
|
||||
}
|
||||
if (!m_pointer->window().isNull() && waylandServer()->seat()->focusedPointer()) {
|
||||
if (!m_pointer->focus().isNull() && waylandServer()->seat()->focusedPointer()) {
|
||||
setSource(CursorSource::PointerSurface);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
Copyright (C) 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -64,7 +65,6 @@ public:
|
|||
|
||||
void init();
|
||||
|
||||
void update();
|
||||
void updateAfterScreenChange();
|
||||
bool supportsWarping() const;
|
||||
void warp(const QPointF &pos);
|
||||
|
@ -75,6 +75,7 @@ public:
|
|||
Qt::MouseButtons buttons() const {
|
||||
return m_qtButtons;
|
||||
}
|
||||
bool areButtonsPressed() const;
|
||||
|
||||
QImage cursorImage() const;
|
||||
QPoint cursorHotSpot() const;
|
||||
|
@ -92,6 +93,8 @@ public:
|
|||
return m_confined || m_locked;
|
||||
}
|
||||
|
||||
bool focusUpdatesBlocked() override;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -142,6 +145,13 @@ public:
|
|||
void processPinchGestureCancelled(quint32 time, KWin::LibInput::Device *device = nullptr);
|
||||
|
||||
private:
|
||||
void cleanupInternalWindow(QWindow *old, QWindow *now) override;
|
||||
void cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) override;
|
||||
|
||||
void focusUpdate(Toplevel *focusOld, Toplevel *focusNow) override;
|
||||
|
||||
QPointF position() const override;
|
||||
|
||||
void updateOnStartMoveResize();
|
||||
void updateToReset();
|
||||
void updatePosition(const QPointF &pos);
|
||||
|
@ -152,14 +162,12 @@ private:
|
|||
void disconnectLockedPointerAboutToBeUnboundConnection();
|
||||
void disconnectPointerConstraintsConnection();
|
||||
void breakPointerConstraints(KWayland::Server::SurfaceInterface *surface);
|
||||
bool areButtonsPressed() const;
|
||||
CursorImage *m_cursor;
|
||||
bool m_inited = false;
|
||||
bool m_supportsWarping;
|
||||
QPointF m_pos;
|
||||
QHash<uint32_t, InputRedirection::PointerButtonState> m_buttons;
|
||||
Qt::MouseButtons m_qtButtons;
|
||||
QMetaObject::Connection m_windowGeometryConnection;
|
||||
QMetaObject::Connection m_focusGeometryConnection;
|
||||
QMetaObject::Connection m_internalWindowConnection;
|
||||
QMetaObject::Connection m_constraintsConnection;
|
||||
QMetaObject::Connection m_constraintsActivatedConnection;
|
||||
|
|
143
touch_input.cpp
143
touch_input.cpp
|
@ -3,6 +3,7 @@
|
|||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
Copyright (C) 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "touch_input.h"
|
||||
#include "abstract_client.h"
|
||||
#include "input.h"
|
||||
#include "pointer_input.h"
|
||||
#include "input_event_spy.h"
|
||||
#include "toplevel.h"
|
||||
#include "wayland_server.h"
|
||||
|
@ -47,8 +49,9 @@ TouchInputRedirection::~TouchInputRedirection() = default;
|
|||
|
||||
void TouchInputRedirection::init()
|
||||
{
|
||||
Q_ASSERT(!m_inited);
|
||||
m_inited = true;
|
||||
Q_ASSERT(!inited());
|
||||
setInited(true);
|
||||
InputDeviceHandler::init();
|
||||
|
||||
if (waylandServer()->hasScreenLockerIntegration()) {
|
||||
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this,
|
||||
|
@ -59,73 +62,83 @@ void TouchInputRedirection::init()
|
|||
}
|
||||
);
|
||||
}
|
||||
connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||
connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||
connect(workspace(), &QObject::destroyed, this, [this] { setInited(false); });
|
||||
connect(waylandServer(), &QObject::destroyed, this, [this] { setInited(false); });
|
||||
}
|
||||
|
||||
void TouchInputRedirection::update(const QPointF &pos)
|
||||
bool TouchInputRedirection::focusUpdatesBlocked()
|
||||
{
|
||||
if (!m_inited) {
|
||||
return;
|
||||
if (!inited()) {
|
||||
return true;
|
||||
}
|
||||
if (m_windowUpdatedInCycle) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
m_windowUpdatedInCycle = true;
|
||||
if (m_touches > 0) {
|
||||
// first touch defines focus
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TouchInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow)
|
||||
{
|
||||
// TODO: handle pointer grab aka popups
|
||||
Toplevel *t = input()->findToplevel(pos.toPoint());
|
||||
auto oldWindow = window();
|
||||
updateInternalWindow(pos);
|
||||
if (!internalWindow()) {
|
||||
updateDecoration(t, pos);
|
||||
} else {
|
||||
// TODO: send hover leave to decoration
|
||||
if (decoration()) {
|
||||
decoration()->client()->leaveEvent();
|
||||
}
|
||||
clearDecoration();
|
||||
|
||||
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusOld)) {
|
||||
ac->leaveEvent();
|
||||
}
|
||||
if (decoration() || internalWindow()) {
|
||||
t = nullptr;
|
||||
} else if (!decoration()) {
|
||||
m_decorationId = -1;
|
||||
} else if (!internalWindow()) {
|
||||
m_internalId = -1;
|
||||
}
|
||||
if (!oldWindow.isNull() && t == oldWindow.data()) {
|
||||
return;
|
||||
disconnect(m_focusGeometryConnection);
|
||||
m_focusGeometryConnection = QMetaObject::Connection();
|
||||
|
||||
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusNow)) {
|
||||
ac->enterEvent(m_lastPosition.toPoint());
|
||||
workspace()->updateFocusMousePosition(m_lastPosition.toPoint());
|
||||
}
|
||||
|
||||
auto seat = waylandServer()->seat();
|
||||
// disconnect old surface
|
||||
if (oldWindow) {
|
||||
disconnect(m_windowGeometryConnection);
|
||||
m_windowGeometryConnection = QMetaObject::Connection();
|
||||
}
|
||||
if (t && t->surface()) {
|
||||
// FIXME: add input transformation API to KWayland::Server::SeatInterface for touch input
|
||||
seat->setFocusedTouchSurface(t->surface(), -1 * t->inputTransformation().map(t->pos()) + t->pos());
|
||||
m_windowGeometryConnection = connect(t, &Toplevel::geometryChanged, this,
|
||||
[this] {
|
||||
if (window().isNull()) {
|
||||
return;
|
||||
}
|
||||
auto seat = waylandServer()->seat();
|
||||
if (window().data()->surface() != seat->focusedTouchSurface()) {
|
||||
return;
|
||||
}
|
||||
auto t = window().data();
|
||||
seat->setFocusedTouchSurfacePosition(-1 * t->inputTransformation().map(t->pos()) + t->pos());
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (!focusNow || !focusNow->surface() || decoration()) {
|
||||
// no new surface or internal window or on decoration -> cleanup
|
||||
seat->setFocusedTouchSurface(nullptr);
|
||||
t = nullptr;
|
||||
}
|
||||
if (!t) {
|
||||
setWindow();
|
||||
return;
|
||||
}
|
||||
setWindow(t);
|
||||
|
||||
// TODO: invalidate pointer focus?
|
||||
|
||||
// FIXME: add input transformation API to KWayland::Server::SeatInterface for touch input
|
||||
seat->setFocusedTouchSurface(focusNow->surface(), -1 * focusNow->inputTransformation().map(focusNow->pos()) + focusNow->pos());
|
||||
m_focusGeometryConnection = connect(focusNow, &Toplevel::geometryChanged, this,
|
||||
[this] {
|
||||
if (focus().isNull()) {
|
||||
return;
|
||||
}
|
||||
auto seat = waylandServer()->seat();
|
||||
if (focus().data()->surface() != seat->focusedTouchSurface()) {
|
||||
return;
|
||||
}
|
||||
seat->setFocusedTouchSurfacePosition(-1 * focus()->inputTransformation().map(focus()->pos()) + focus()->pos());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void TouchInputRedirection::cleanupInternalWindow(QWindow *old, QWindow *now)
|
||||
{
|
||||
Q_UNUSED(old);
|
||||
Q_UNUSED(now);
|
||||
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void TouchInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now)
|
||||
{
|
||||
Q_UNUSED(now);
|
||||
|
||||
if (old) {
|
||||
// send leave event to old decoration
|
||||
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
||||
QCoreApplication::instance()->sendEvent(old->decoration(), &event);
|
||||
}
|
||||
}
|
||||
|
||||
void TouchInputRedirection::insertId(quint32 internalId, qint32 kwaylandId)
|
||||
|
@ -150,10 +163,15 @@ void TouchInputRedirection::removeId(quint32 internalId)
|
|||
void TouchInputRedirection::processDown(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
m_lastPosition = pos;
|
||||
m_windowUpdatedInCycle = false;
|
||||
if (m_touches == 0) {
|
||||
update();
|
||||
}
|
||||
m_touches++;
|
||||
input()->processSpies(std::bind(&InputEventSpy::touchDown, std::placeholders::_1, id, pos, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::touchDown, std::placeholders::_1, id, pos, time));
|
||||
m_windowUpdatedInCycle = false;
|
||||
|
@ -162,21 +180,26 @@ void TouchInputRedirection::processDown(qint32 id, const QPointF &pos, quint32 t
|
|||
void TouchInputRedirection::processUp(qint32 id, quint32 time, LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
m_windowUpdatedInCycle = false;
|
||||
input()->processSpies(std::bind(&InputEventSpy::touchUp, std::placeholders::_1, id, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::touchUp, std::placeholders::_1, id, time));
|
||||
m_windowUpdatedInCycle = false;
|
||||
m_touches--;
|
||||
if (m_touches == 0) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device)
|
||||
{
|
||||
Q_UNUSED(device)
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
m_lastPosition = pos;
|
||||
m_windowUpdatedInCycle = false;
|
||||
input()->processSpies(std::bind(&InputEventSpy::touchMotion, std::placeholders::_1, id, pos, time));
|
||||
input()->processFilters(std::bind(&InputEventFilter::touchMotion, std::placeholders::_1, id, pos, time));
|
||||
|
@ -185,7 +208,7 @@ void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, quint32
|
|||
|
||||
void TouchInputRedirection::cancel()
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
waylandServer()->seat()->cancelTouchSequence();
|
||||
|
@ -194,7 +217,7 @@ void TouchInputRedirection::cancel()
|
|||
|
||||
void TouchInputRedirection::frame()
|
||||
{
|
||||
if (!m_inited) {
|
||||
if (!inited()) {
|
||||
return;
|
||||
}
|
||||
waylandServer()->seat()->touchFrame();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
Copyright (C) 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -49,7 +50,7 @@ public:
|
|||
explicit TouchInputRedirection(InputRedirection *parent);
|
||||
virtual ~TouchInputRedirection();
|
||||
|
||||
void update(const QPointF &pos = QPointF());
|
||||
bool focusUpdatesBlocked() override;
|
||||
void init();
|
||||
|
||||
void processDown(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device = nullptr);
|
||||
|
@ -75,7 +76,16 @@ public:
|
|||
return m_internalId;
|
||||
}
|
||||
|
||||
QPointF position() const override {
|
||||
return m_lastPosition;
|
||||
}
|
||||
|
||||
private:
|
||||
void cleanupInternalWindow(QWindow *old, QWindow *now) override;
|
||||
void cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) override;
|
||||
|
||||
void focusUpdate(Toplevel *focusOld, Toplevel *focusNow) override;
|
||||
|
||||
bool m_inited = false;
|
||||
qint32 m_decorationId = -1;
|
||||
qint32 m_internalId = -1;
|
||||
|
@ -83,8 +93,11 @@ private:
|
|||
* external/kwayland
|
||||
**/
|
||||
QHash<qint32, qint32> m_idMapper;
|
||||
QMetaObject::Connection m_windowGeometryConnection;
|
||||
QMetaObject::Connection m_focusGeometryConnection;
|
||||
bool m_windowUpdatedInCycle = false;
|
||||
QPointF m_lastPosition;
|
||||
|
||||
int m_touches = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue