diff --git a/autotests/wayland/decoration_input_test.cpp b/autotests/wayland/decoration_input_test.cpp
index e57194dee3..b8b1dd8e31 100644
--- a/autotests/wayland/decoration_input_test.cpp
+++ b/autotests/wayland/decoration_input_test.cpp
@@ -22,6 +22,7 @@ along with this program. If not, see .
#include "abstract_client.h"
#include "cursor.h"
#include "pointer_input.h"
+#include "touch_input.h"
#include "screenedge.h"
#include "screens.h"
#include "wayland_server.h"
@@ -63,9 +64,13 @@ private Q_SLOTS:
void testAxis();
void testDoubleClick_data();
void testDoubleClick();
+ void testDoubleTap_data();
+ void testDoubleTap();
void testHover();
void testPressToMove_data();
void testPressToMove();
+ void testTapToMove_data();
+ void testTapToMove();
private:
AbstractClient *showWindow();
@@ -344,6 +349,55 @@ void KWin::DecorationInputTest::testDoubleClick()
QVERIFY(c->isOnAllDesktops());
}
+void DecorationInputTest::testDoubleTap_data()
+{
+ QTest::addColumn("decoPoint");
+ QTest::addColumn("expectedSection");
+
+ QTest::newRow("topLeft") << QPoint(0, 0) << Qt::TopLeftSection;
+ QTest::newRow("top") << QPoint(250, 0) << Qt::TopSection;
+ QTest::newRow("topRight") << QPoint(499, 0) << Qt::TopRightSection;
+}
+
+void KWin::DecorationInputTest::testDoubleTap()
+{
+ AbstractClient *c = showWindow();
+ QVERIFY(c);
+ QVERIFY(c->isDecorated());
+ QVERIFY(!c->noBorder());
+ QVERIFY(!c->isOnAllDesktops());
+ quint32 timestamp = 1;
+ const QPoint tapPoint(c->geometry().center().x(), c->clientPos().y() / 2);
+
+ // double tap
+ kwinApp()->platform()->touchDown(0, tapPoint, timestamp++);
+ kwinApp()->platform()->touchUp(0, timestamp++);
+ kwinApp()->platform()->touchDown(0, tapPoint, timestamp++);
+ kwinApp()->platform()->touchUp(0, timestamp++);
+ QVERIFY(c->isOnAllDesktops());
+ // double tap again
+ kwinApp()->platform()->touchDown(0, tapPoint, timestamp++);
+ kwinApp()->platform()->touchUp(0, timestamp++);
+ QVERIFY(c->isOnAllDesktops());
+ kwinApp()->platform()->touchDown(0, tapPoint, timestamp++);
+ kwinApp()->platform()->touchUp(0, timestamp++);
+ QVERIFY(!c->isOnAllDesktops());
+
+ // test top most deco pixel, BUG: 362860
+ c->move(0, 0);
+ QFETCH(QPoint, decoPoint);
+ // double click
+ kwinApp()->platform()->touchDown(0, decoPoint, timestamp++);
+ QVERIFY(!input()->touch()->decoration().isNull());
+ QCOMPARE(input()->touch()->decoration()->client(), c);
+ QTEST(input()->touch()->decoration()->decoration()->sectionUnderMouse(), "expectedSection");
+ kwinApp()->platform()->touchUp(0, timestamp++);
+ QVERIFY(!c->isOnAllDesktops());
+ kwinApp()->platform()->touchDown(0, decoPoint, timestamp++);
+ kwinApp()->platform()->touchUp(0, timestamp++);
+ QVERIFY(c->isOnAllDesktops());
+}
+
void DecorationInputTest::testHover()
{
AbstractClient *c = showWindow();
@@ -439,6 +493,66 @@ void DecorationInputTest::testPressToMove()
QCOMPARE(c->pos(), oldPos + offset2 + offset3);
}
+void DecorationInputTest::testTapToMove_data()
+{
+ QTest::addColumn("offset");
+ QTest::addColumn("offset2");
+ QTest::addColumn("offset3");
+
+ QTest::newRow("To right") << QPoint(10, 0) << QPoint(20, 0) << QPoint(30, 0);
+ QTest::newRow("To left") << QPoint(-10, 0) << QPoint(-20, 0) << QPoint(-30, 0);
+ QTest::newRow("To bottom") << QPoint(0, 10) << QPoint(0, 20) << QPoint(0, 30);
+ QTest::newRow("To top") << QPoint(0, -10) << QPoint(0, -20) << QPoint(0, -30);
+}
+
+void DecorationInputTest::testTapToMove()
+{
+ AbstractClient *c = showWindow();
+ QVERIFY(c);
+ QVERIFY(c->isDecorated());
+ QVERIFY(!c->noBorder());
+ c->move(screens()->geometry(0).center() - QPoint(c->width()/2, c->height()/2));
+ QSignalSpy startMoveResizedSpy(c, &AbstractClient::clientStartUserMovedResized);
+ QVERIFY(startMoveResizedSpy.isValid());
+ QSignalSpy clientFinishUserMovedResizedSpy(c, &AbstractClient::clientFinishUserMovedResized);
+ QVERIFY(clientFinishUserMovedResizedSpy.isValid());
+
+ quint32 timestamp = 1;
+ QPoint p = QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2);
+
+ kwinApp()->platform()->touchDown(0, p, timestamp++);
+ QVERIFY(!c->isMove());
+ QFETCH(QPoint, offset);
+ QCOMPARE(input()->touch()->decorationPressId(), 0);
+ kwinApp()->platform()->touchMotion(0, p + offset, timestamp++);
+ const QPoint oldPos = c->pos();
+ QVERIFY(c->isMove());
+ QCOMPARE(startMoveResizedSpy.count(), 1);
+
+ kwinApp()->platform()->touchUp(0, timestamp++);
+ QTRY_VERIFY(!c->isMove());
+ QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1);
+ QEXPECT_FAIL("", "Just trigger move doesn't move the window", Continue);
+ QCOMPARE(c->pos(), oldPos + offset);
+
+ // again
+ kwinApp()->platform()->touchDown(1, p + offset, timestamp++);
+ QCOMPARE(input()->touch()->decorationPressId(), 1);
+ QVERIFY(!c->isMove());
+ QFETCH(QPoint, offset2);
+ kwinApp()->platform()->touchMotion(1, QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2) + offset2, timestamp++);
+ QVERIFY(c->isMove());
+ QCOMPARE(startMoveResizedSpy.count(), 2);
+ QFETCH(QPoint, offset3);
+ kwinApp()->platform()->touchMotion(1, QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2) + offset3, timestamp++);
+
+ kwinApp()->platform()->touchUp(1, timestamp++);
+ QTRY_VERIFY(!c->isMove());
+ QCOMPARE(clientFinishUserMovedResizedSpy.count(), 2);
+ // TODO: the offset should also be included
+ QCOMPARE(c->pos(), oldPos + offset2 + offset3);
+}
+
}
WAYLANDTEST_MAIN(KWin::DecorationInputTest)
diff --git a/input.cpp b/input.cpp
index 4b9029436b..e020703009 100644
--- a/input.cpp
+++ b/input.cpp
@@ -491,6 +491,79 @@ public:
}
return true;
}
+ bool touchDown(quint32 id, const QPointF &pos, quint32 time) override {
+ auto seat = waylandServer()->seat();
+ if (seat->isTouchSequence()) {
+ return false;
+ }
+ if (input()->touch()->decorationPressId() != -1) {
+ // already on a decoration, ignore further touch points, but filter out
+ 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();
+ QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
+ e.setAccepted(false);
+ QCoreApplication::sendEvent(decoration->decoration(), &e);
+ if (!e.isAccepted()) {
+ decoration->client()->processDecorationButtonPress(&e);
+ }
+ return true;
+ }
+ bool touchMotion(quint32 id, const QPointF &pos, quint32 time) override {
+ Q_UNUSED(time)
+ auto decoration = input()->touch()->decoration();
+ if (!decoration) {
+ return false;
+ }
+ if (input()->touch()->decorationPressId() == -1) {
+ return false;
+ }
+ if (input()->touch()->decorationPressId() != id) {
+ // ignore, but filter out
+ return true;
+ }
+ m_lastGlobalTouchPos = pos;
+ m_lastLocalTouchPos = pos - decoration->client()->pos();
+ 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 {
+ Q_UNUSED(time);
+ auto decoration = input()->touch()->decoration();
+ if (!decoration) {
+ return false;
+ }
+ if (input()->touch()->decorationPressId() == -1) {
+ return false;
+ }
+ if (input()->touch()->decorationPressId() != id) {
+ // ignore, but filter out
+ return true;
+ }
+ // send mouse up
+ 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);
+
+ m_lastGlobalTouchPos = QPointF();
+ m_lastLocalTouchPos = QPointF();
+ input()->touch()->setDecorationPressId(-1);
+ return true;
+ }
+private:
+ QPointF m_lastGlobalTouchPos;
+ QPointF m_lastLocalTouchPos;
};
#ifdef KWIN_BUILD_TABBOX
@@ -1206,4 +1279,63 @@ QPointF InputRedirection::globalPointer() const
return m_pointer->pos();
}
+InputDeviceHandler::InputDeviceHandler(InputRedirection *input)
+ : QObject(input)
+ , m_input(input)
+{
+}
+
+InputDeviceHandler::~InputDeviceHandler() = default;
+
+void InputDeviceHandler::updateDecoration(Toplevel *t, const QPointF &pos)
+{
+ const auto oldDeco = m_decoration;
+ bool needsReset = waylandServer()->isScreenLocked();
+ if (AbstractClient *c = dynamic_cast(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;
+ }
+ } else {
+ needsReset = true;
+ }
+ if (needsReset) {
+ m_decoration.clear();
+ }
+
+ bool leftSend = false;
+ auto oldWindow = qobject_cast(m_window.data());
+ if (oldWindow && (m_decoration && m_decoration->client() != oldWindow)) {
+ leftSend = true;
+ oldWindow->leaveEvent();
+ }
+
+ 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);
+ }
+ 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());
+ }
+}
+
} // namespace
diff --git a/input.h b/input.h
index b68b8abcc8..b31e18aebf 100644
--- a/input.h
+++ b/input.h
@@ -23,6 +23,7 @@ along with this program. If not, see .
#include
#include
#include
+#include
#include
#include
@@ -42,6 +43,11 @@ class KeyboardInputRedirection;
class PointerInputRedirection;
class TouchInputRedirection;
+namespace Decoration
+{
+class DecoratedClientImpl;
+}
+
namespace LibInput
{
class Connection;
@@ -280,6 +286,36 @@ public:
virtual bool touchUp(quint32 id, quint32 time);
};
+class InputDeviceHandler : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~InputDeviceHandler();
+
+ QPointer window() const {
+ return m_window;
+ }
+ QPointer decoration() const {
+ return m_decoration;
+ }
+
+Q_SIGNALS:
+ void decorationChanged();
+
+protected:
+ explicit InputDeviceHandler(InputRedirection *parent);
+ void updateDecoration(Toplevel *t, const QPointF &pos);
+ InputRedirection *m_input;
+ /**
+ * @brief The Toplevel which currently receives events
+ */
+ QPointer m_window;
+ /**
+ * @brief The Decoration which currently receives events.
+ **/
+ QPointer m_decoration;
+};
+
inline
InputRedirection *input()
{
diff --git a/pointer_input.cpp b/pointer_input.cpp
index b92ce3110f..5ac76a18d5 100644
--- a/pointer_input.cpp
+++ b/pointer_input.cpp
@@ -108,8 +108,7 @@ static bool screenContainsPos(const QPointF &pos)
}
PointerInputRedirection::PointerInputRedirection(InputRedirection* parent)
- : QObject(parent)
- , m_input(parent)
+ : InputDeviceHandler(parent)
, m_cursor(nullptr)
, m_supportsWarping(Application::usesLibinput())
{
@@ -236,7 +235,7 @@ void PointerInputRedirection::update()
const auto oldDeco = m_decoration;
updateInternalWindow();
if (!m_internalWindow) {
- updateDecoration(t);
+ updateDecoration(t, m_pos);
} else {
// TODO: send hover leave to decoration
if (m_decoration) {
@@ -356,57 +355,6 @@ void PointerInputRedirection::updateInternalWindow()
}
}
-void PointerInputRedirection::updateDecoration(Toplevel *t)
-{
- const auto oldDeco = m_decoration;
- bool needsReset = waylandServer()->isScreenLocked();
- if (AbstractClient *c = dynamic_cast(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(m_pos.toPoint())) {
- m_decoration = c->decoratedClient();
- } else {
- needsReset = true;
- }
- } else {
- needsReset = true;
- }
- } else {
- needsReset = true;
- }
- if (needsReset) {
- m_decoration.clear();
- }
-
- bool leftSend = false;
- auto oldWindow = qobject_cast(m_window.data());
- if (oldWindow && (m_decoration && m_decoration->client() != oldWindow)) {
- leftSend = true;
- oldWindow->leaveEvent();
- }
-
- 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);
- }
- if (m_decoration) {
- if (m_decoration->client() != oldWindow) {
- m_decoration->client()->enterEvent(m_pos.toPoint());
- workspace()->updateFocusMousePosition(m_pos.toPoint());
- }
- const QPointF p = m_pos - t->pos();
- QHoverEvent event(QEvent::HoverMove, p, p);
- QCoreApplication::instance()->sendEvent(m_decoration->decoration(), &event);
- m_decoration->client()->processDecorationMove(p.toPoint(), m_pos.toPoint());
- }
-}
-
void PointerInputRedirection::updatePosition(const QPointF &pos)
{
// verify that at least one screen contains the pointer position
diff --git a/pointer_input.h b/pointer_input.h
index ff0476d1c0..b3be557686 100644
--- a/pointer_input.h
+++ b/pointer_input.h
@@ -42,7 +42,7 @@ namespace Decoration
class DecoratedClientImpl;
}
-class KWIN_EXPORT PointerInputRedirection : public QObject
+class KWIN_EXPORT PointerInputRedirection : public InputDeviceHandler
{
Q_OBJECT
public:
@@ -62,12 +62,6 @@ public:
Qt::MouseButtons buttons() const {
return m_qtButtons;
}
- QPointer window() const {
- return m_window;
- }
- QPointer decoration() const {
- return m_decoration;
- }
QPointer internalWindow() const {
return m_internalWindow;
}
@@ -91,30 +85,16 @@ public:
*/
void processAxis(InputRedirection::PointerAxis axis, qreal delta, uint32_t time);
-Q_SIGNALS:
- void decorationChanged();
-
private:
void updatePosition(const QPointF &pos);
void updateButton(uint32_t button, InputRedirection::PointerButtonState state);
void updateInternalWindow();
- void updateDecoration(Toplevel *t);
- InputRedirection *m_input;
CursorImage *m_cursor;
bool m_inited = false;
bool m_supportsWarping;
QPointF m_pos;
QHash m_buttons;
Qt::MouseButtons m_qtButtons;
- /**
- * @brief The Toplevel which currently receives pointer events
- */
- QPointer m_window;
- /**
- * @brief The Decoration which currently receives pointer events.
- * Decoration belongs to the pointerWindow
- **/
- QPointer m_decoration;
QPointer m_internalWindow;
QMetaObject::Connection m_windowGeometryConnection;
QMetaObject::Connection m_internalWindowConnection;
diff --git a/touch_input.cpp b/touch_input.cpp
index ec4f99cb19..801fbefc15 100644
--- a/touch_input.cpp
+++ b/touch_input.cpp
@@ -18,21 +18,26 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*********************************************************************/
#include "touch_input.h"
+#include "abstract_client.h"
#include "input.h"
#include "toplevel.h"
#include "wayland_server.h"
#include "workspace.h"
+#include "decorations/decoratedclient.h"
+// KDecoration
+#include
// KWayland
#include
// screenlocker
#include
+// Qt
+#include
namespace KWin
{
TouchInputRedirection::TouchInputRedirection(InputRedirection *parent)
- : QObject(parent)
- , m_input(parent)
+ : InputDeviceHandler(parent)
{
}
@@ -64,6 +69,12 @@ void TouchInputRedirection::update(const QPointF &pos)
// TODO: handle pointer grab aka popups
Toplevel *t = m_input->findToplevel(pos.toPoint());
auto oldWindow = m_window;
+ updateDecoration(t, pos);
+ if (m_decoration) {
+ t = nullptr;
+ } else {
+ m_decorationId = -1;
+ }
if (!oldWindow.isNull() && t == oldWindow.data()) {
return;
}
diff --git a/touch_input.h b/touch_input.h
index d4e5e2f328..f550a55404 100644
--- a/touch_input.h
+++ b/touch_input.h
@@ -19,6 +19,7 @@ along with this program. If not, see .
*********************************************************************/
#ifndef KWIN_TOUCH_INPUT_H
#define KWIN_TOUCH_INPUT_H
+#include "input.h"
#include
#include
@@ -31,7 +32,12 @@ namespace KWin
class InputRedirection;
class Toplevel;
-class TouchInputRedirection : public QObject
+namespace Decoration
+{
+class DecoratedClientImpl;
+}
+
+class TouchInputRedirection : public InputDeviceHandler
{
Q_OBJECT
public:
@@ -51,20 +57,16 @@ public:
void removeId(quint32 internalId);
qint32 mappedId(quint32 internalId);
- /**
- * @brief The Toplevel which currently receives touch events
- */
- QPointer window() const {
- return m_window;
+ void setDecorationPressId(qint32 id) {
+ m_decorationId = id;
+ }
+ qint32 decorationPressId() const {
+ return m_decorationId;
}
private:
- InputRedirection *m_input;
bool m_inited = false;
- /**
- * @brief The Toplevel which currently receives touch events
- */
- QPointer m_window;
+ qint32 m_decorationId = -1;
/**
* external/kwayland
**/