aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Gilg <[email protected]>2018-09-15 02:00:24 +0200
committerRoman Gilg <[email protected]>2018-12-02 21:36:11 +0100
commit2e29711323789319bffb3c39e20079fa667f7f6e (patch)
tree7174f98b956a38c1f7a73237f51655e72282bee2
parenteab71a8a19b23fd26b69c347182dda2d520c06eb (diff)
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
-rw-r--r--autotests/integration/decoration_input_test.cpp4
-rw-r--r--autotests/integration/internal_window.cpp6
-rw-r--r--autotests/integration/pointer_constraints_test.cpp10
-rw-r--r--autotests/integration/pointer_input.cpp4
-rw-r--r--autotests/integration/window_selection_test.cpp26
-rw-r--r--input.cpp370
-rw-r--r--input.h88
-rw-r--r--pointer_input.cpp378
-rw-r--r--pointer_input.h16
-rw-r--r--touch_input.cpp143
-rw-r--r--touch_input.h17
11 files changed, 635 insertions, 427 deletions
diff --git a/autotests/integration/decoration_input_test.cpp b/autotests/integration/decoration_input_test.cpp
index 34eb190..68b0e69 100644
--- a/autotests/integration/decoration_input_test.cpp
+++ b/autotests/integration/decoration_input_test.cpp
@@ -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()
diff --git a/autotests/integration/internal_window.cpp b/autotests/integration/internal_window.cpp
index a1fb1a6..8936b91 100644
--- a/autotests/integration/internal_window.cpp
+++ b/autotests/integration/internal_window.cpp
@@ -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);
diff --git a/autotests/integration/pointer_constraints_test.cpp b/autotests/integration/pointer_constraints_test.cpp
index 43b813c..c2cb021 100644
--- a/autotests/integration/pointer_constraints_test.cpp
+++ b/autotests/integration/pointer_constraints_test.cpp
@@ -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());
diff --git a/autotests/integration/pointer_input.cpp b/autotests/integration/pointer_input.cpp
index 1302fbf..dfc859f 100644
--- a/autotests/integration/pointer_input.cpp
+++ b/autotests/integration/pointer_input.cpp
@@ -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);
}
diff --git a/autotests/integration/window_selection_test.cpp b/autotests/integration/window_selection_test.cpp
index 3b70a37..d163c80 100644
--- a/autotests/integration/window_selection_test.cpp
+++ b/autotests/integration/window_selection_test.cpp
@@ -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) {
diff --git a/input.cpp b/input.cpp
index 98f7406..d1baacf 100644
--- a/input.cpp
+++ b/input.cpp
@@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2013 Martin Gräßlin <[email protected]>
+Copyright (C) 2018 Roman Gilg <[email protected]>
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();
}
- } else {
- needsReset = true;
}
- if (needsReset) {
- m_decoration.clear();
+
+ if (m_focus.decoration == oldDeco) {
+ // no change to decoration
+ return false;
}
+ cleanupDecoration(oldDeco.data(), m_focus.decoration.data());
+ emit decorationChanged();
+ return true;
+}
- bool leftSend = false;
- auto oldWindow = qobject_cast<AbstractClient*>(window().data());
- if (oldWindow && (m_decoration && m_decoration->client() != oldWindow)) {
- leftSend = true;
- oldWindow->leaveEvent();
+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);
+}
- if (oldDeco && oldDeco != m_decoration) {
- if (oldDeco->client() != t && !leftSend) {
- leftSend = true;
- oldDeco->client()->leaveEvent();
- }
- // send leave
- QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
- QCoreApplication::instance()->sendEvent(oldDeco->decoration(), &event);
+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 {
+ toplevel = input()->findToplevel(pos);
+ }
+
+ // Always set the toplevel at the position of the input device.
+ setAt(toplevel);
+
+ if (focusUpdatesBlocked()) {
+ return;
}
- if (m_decoration) {
- if (m_decoration->client() != oldWindow) {
- m_decoration->client()->enterEvent(pos.toPoint());
- workspace()->updateFocusMousePosition(pos.toPoint());
+
+ 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();
}
- 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());
+ return;
+ }
+ 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();
- 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 (waylandServer()->isScreenLocked()) {
+ return nullptr;
}
- if (needsReset) {
- m_internalWindow.clear();
+
+ const auto &internalClients = waylandServer()->internalClients();
+ if (internalClients.isEmpty()) {
+ return nullptr;
}
- 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
diff --git a/input.h b/input.h
index 78f0642..ac4a785 100644
--- a/input.h
+++ b/input.h
@@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2013 Martin Gräßlin <[email protected]>
+Copyright (C) 2018 Roman Gilg <[email protected]>
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
diff --git a/pointer_input.cpp b/pointer_input.cpp
index 00fc376..eb1b203 100644
--- a/pointer_input.cpp
+++ b/pointer_input.cpp
@@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2013, 2016 Martin Gräßlin <[email protected]>
+Copyright (C) 2018 Roman Gilg <[email protected]>
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 (now) {
+ m_internalWindowConnection = connect(internalWindow().data(), &QWindow::visibleChanged, this,
+ [this] (bool visible) {
+ if (!visible) {
+ update();
+ }
+ }
+ );
}
- if (decoration() != oldDeco) {
- emit decorationChanged();
+}
+
+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);
}
- auto oldWindow = window();
- if (!oldWindow.isNull() && t == window().data()) {
+ if (!now) {
+ // left decoration
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());
+
+ 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();
}
- 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());
- }
+ disconnect(m_focusGeometryConnection);
+ m_focusGeometryConnection = QMetaObject::Connection();
+
+ if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusNow)) {
+ ac->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;
- }
- // 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();
+
+ 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;
}
diff --git a/pointer_input.h b/pointer_input.h
index f94269d..8bdc0d9 100644
--- a/pointer_input.h
+++ b/pointer_input.h
@@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2013, 2016 Martin Gräßlin <[email protected]>
+Copyright (C) 2018 Roman Gilg <[email protected]>
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;
diff --git a/touch_input.cpp b/touch_input.cpp
index 5d14abd..4cf685c 100644
--- a/touch_input.cpp
+++ b/touch_input.cpp
@@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2013, 2016 Martin Gräßlin <[email protected]>
+Copyright (C) 2018 Roman Gilg <[email protected]>
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;
- // 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 (m_touches > 0) {
+ // first touch defines focus
+ return true;
}
- if (decoration() || internalWindow()) {
- t = nullptr;
- } else if (!decoration()) {
- m_decorationId = -1;
- } else if (!internalWindow()) {
- m_internalId = -1;
+ return false;
+}
+
+void TouchInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow)
+{
+ // TODO: handle pointer grab aka popups
+
+ if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusOld)) {
+ ac->leaveEvent();
}
- 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();
diff --git a/touch_input.h b/touch_input.h
index 900c005..53a0316 100644
--- a/touch_input.h
+++ b/touch_input.h
@@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2013, 2016 Martin Gräßlin <[email protected]>
+Copyright (C) 2018 Roman Gilg <[email protected]>
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;
};
}