From f8f13a7fbaf6025656641f89e5908f0df1487898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 17 Feb 2016 17:04:11 +0100 Subject: [PATCH] Implement modifier+click in InputRedirection for Wayland This change implements the mouse command for modifier (alt/meta) plus click in InputRedirection so that it also works on Wayland. Modifier plus mouse wheel is not implemented yet. For easier code in Options a new method is added which provides the configured modifier as a Qt::KeyboardModifier instead of a Qt::Key code. Test case is added which simulates all variants of modifiers plus supported mouse buttons to trigger move. --- autotests/wayland/pointer_input.cpp | 87 +++++++++++++++++++++++++++++ input.cpp | 21 ++++++- options.h | 10 ++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/autotests/wayland/pointer_input.cpp b/autotests/wayland/pointer_input.cpp index cb9abeb524..dac2db62c1 100644 --- a/autotests/wayland/pointer_input.cpp +++ b/autotests/wayland/pointer_input.cpp @@ -41,6 +41,8 @@ along with this program. If not, see . #include +#include + namespace KWin { @@ -56,6 +58,8 @@ private Q_SLOTS: void testWarpingUpdatesFocus(); void testWarpingGeneratesPointerMotion(); void testUpdateFocusAfterScreenChange(); + void testModifierClickUnrestrictedMove_data(); + void testModifierClickUnrestrictedMove(); private: void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); @@ -79,6 +83,8 @@ void PointerInputTest::initTestCase() QMetaObject::invokeMethod(waylandServer()->backend(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); waylandServer()->init(s_socketName.toLocal8Bit()); + kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); + kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); QCOMPARE(screens()->count(), 2); @@ -310,6 +316,87 @@ void PointerInputTest::testUpdateFocusAfterScreenChange() QCOMPARE(enteredSpy.count(), 1); } +void PointerInputTest::testModifierClickUnrestrictedMove_data() +{ + QTest::addColumn("modifierKey"); + QTest::addColumn("mouseButton"); + QTest::addColumn("modKey"); + + const QString alt = QStringLiteral("Alt"); + const QString meta = QStringLiteral("Meta"); + + QTest::newRow("Left Alt + Left Click") << KEY_LEFTALT << BTN_LEFT << alt; + QTest::newRow("Left Alt + Right Click") << KEY_LEFTALT << BTN_RIGHT << alt; + QTest::newRow("Left Alt + Middle Click") << KEY_LEFTALT << BTN_MIDDLE << alt; + QTest::newRow("Right Alt + Left Click") << KEY_RIGHTALT << BTN_LEFT << alt; + QTest::newRow("Right Alt + Right Click") << KEY_RIGHTALT << BTN_RIGHT << alt; + QTest::newRow("Right Alt + Middle Click") << KEY_RIGHTALT << BTN_MIDDLE << alt; + // now everything with meta + QTest::newRow("Left Meta + Left Click") << KEY_LEFTMETA << BTN_LEFT << meta; + QTest::newRow("Left Meta + Right Click") << KEY_LEFTMETA << BTN_RIGHT << meta; + QTest::newRow("Left Meta + Middle Click") << KEY_LEFTMETA << BTN_MIDDLE << meta; + QTest::newRow("Right Meta + Left Click") << KEY_RIGHTMETA << BTN_LEFT << meta; + QTest::newRow("Right Meta + Right Click") << KEY_RIGHTMETA << BTN_RIGHT << meta; + QTest::newRow("Right Meta + Middle Click") << KEY_RIGHTMETA << BTN_MIDDLE << meta; +} + +void PointerInputTest::testModifierClickUnrestrictedMove() +{ + // this test ensures that Alt+mouse button press triggers unrestricted move + using namespace KWayland::Client; + // create pointer and signal spy for button events + auto pointer = m_seat->createPointer(m_seat); + QVERIFY(pointer); + QVERIFY(pointer->isValid()); + QSignalSpy buttonSpy(pointer, &Pointer::buttonStateChanged); + QVERIFY(buttonSpy.isValid()); + + // first modify the config for this run + QFETCH(QString, modKey); + KConfigGroup group = kwinApp()->config()->group("MouseBindings"); + group.writeEntry("CommandAllKey", modKey); + group.writeEntry("CommandAll1", "Move"); + group.writeEntry("CommandAll2", "Move"); + group.writeEntry("CommandAll3", "Move"); + group.sync(); + workspace()->slotReconfigure(); + + // create a window + QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QVERIFY(clientAddedSpy.isValid()); + Surface *surface = m_compositor->createSurface(m_compositor); + QVERIFY(surface); + ShellSurface *shellSurface = m_shell->createSurface(surface, surface); + QVERIFY(shellSurface); + render(surface); + QVERIFY(clientAddedSpy.wait()); + AbstractClient *window = workspace()->activeClient(); + QVERIFY(window); + + // move cursor on window + Cursor::setPos(window->geometry().center()); + + // simulate modifier+click + quint32 timestamp = 1; + QFETCH(int, modifierKey); + QFETCH(int, mouseButton); + waylandServer()->backend()->keyboardKeyPressed(modifierKey, timestamp++); + QVERIFY(!window->isMove()); + waylandServer()->backend()->pointerButtonPressed(mouseButton, timestamp++); + QVERIFY(window->isMove()); + // release modifier should not change it + waylandServer()->backend()->keyboardKeyReleased(modifierKey, timestamp++); + QVERIFY(window->isMove()); + // but releasing the key should end move/resize + waylandServer()->backend()->pointerButtonReleased(mouseButton, timestamp++); + QVERIFY(!window->isMove()); + + // all of that should not have triggered button events on the surface + QCOMPARE(buttonSpy.count(), 0); + // also waiting shouldn't give us the event + QVERIFY(!buttonSpy.wait(100)); +} + } WAYLANDTEST_MAIN(KWin::PointerInputTest) diff --git a/input.cpp b/input.cpp index 8bb324e166..bf94644c88 100644 --- a/input.cpp +++ b/input.cpp @@ -520,7 +520,26 @@ public: bool passThrough = true; if (AbstractClient *c = dynamic_cast(input()->pointer()->window().data())) { bool wasAction = false; - const Options::MouseCommand command = c->getMouseCommand(event->button(), &wasAction); + Options::MouseCommand command = Options::MouseNothing; + if (event->modifiers() == options->commandAllModifier()) { + wasAction = true; + switch (event->button()) { + case Qt::LeftButton: + command = options->commandAll1(); + break; + case Qt::MiddleButton: + command = options->commandAll2(); + break; + case Qt::RightButton: + command = options->commandAll3(); + break; + default: + // nothing + break; + } + } else { + c->getMouseCommand(event->button(), &wasAction); + } if (wasAction) { passThrough = c->performMouseCommand(command, event->globalPos()); } diff --git a/options.h b/options.h index 96ad4d333a..4c09b9edc1 100644 --- a/options.h +++ b/options.h @@ -459,6 +459,16 @@ public: uint keyCmdAllModKey() const { return CmdAllModKey; } + Qt::KeyboardModifier commandAllModifier() const { + switch (CmdAllModKey) { + case Qt::Key_Alt: + return Qt::AltModifier; + case Qt::Key_Meta: + return Qt::MetaModifier; + default: + Q_UNREACHABLE(); + } + } static WindowOperation windowOperation(const QString &name, bool restricted); static MouseCommand mouseCommand(const QString &name, bool restricted);