diff --git a/autotests/integration/internal_window.cpp b/autotests/integration/internal_window.cpp index 9a2407c8ba..58bbb36873 100644 --- a/autotests/integration/internal_window.cpp +++ b/autotests/integration/internal_window.cpp @@ -61,6 +61,8 @@ private Q_SLOTS: void testMove(); void testSkipCloseAnimation_data(); void testSkipCloseAnimation(); + void testModifierClickUnrestrictedMove(); + void testModifierScroll(); }; class HelperWindow : public QRasterWindow @@ -175,6 +177,7 @@ void InternalWindowTest::initTestCase() kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); + kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); @@ -212,6 +215,7 @@ void InternalWindowTest::testEnterLeave() ShellClient *c = clientAddedSpy.first().first().value(); QVERIFY(c->isInternal()); QCOMPARE(c->icon().name(), QStringLiteral("wayland")); + QVERIFY(!c->isDecorated()); QCOMPARE(workspace()->findToplevel(&win), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 100)); QVERIFY(c->isShown(false)); @@ -589,6 +593,84 @@ void InternalWindowTest::testSkipCloseAnimation() QCOMPARE(internalClient->skipsCloseAnimation(), initial); } +void InternalWindowTest::testModifierClickUnrestrictedMove() +{ + QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QVERIFY(clientAddedSpy.isValid()); + HelperWindow win; + win.setGeometry(0, 0, 100, 100); + win.setFlags(win.flags() & ~Qt::FramelessWindowHint); + win.show(); + QVERIFY(clientAddedSpy.wait()); + QCOMPARE(clientAddedSpy.count(), 1); + auto internalClient = clientAddedSpy.first().first().value(); + QVERIFY(internalClient); + QVERIFY(internalClient->isDecorated()); + + KConfigGroup group = kwinApp()->config()->group("MouseBindings"); + group.writeEntry("CommandAllKey", "Alt"); + group.writeEntry("CommandAll1", "Move"); + group.writeEntry("CommandAll2", "Move"); + group.writeEntry("CommandAll3", "Move"); + group.sync(); + workspace()->slotReconfigure(); + QCOMPARE(options->commandAllModifier(), Qt::AltModifier); + QCOMPARE(options->commandAll1(), Options::MouseUnrestrictedMove); + QCOMPARE(options->commandAll2(), Options::MouseUnrestrictedMove); + QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedMove); + + // move cursor on window + Cursor::setPos(internalClient->geometry().center()); + + // simulate modifier+click + quint32 timestamp = 1; + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++); + QVERIFY(!internalClient->isMove()); + kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++); + QVERIFY(internalClient->isMove()); + // release modifier should not change it + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++); + QVERIFY(internalClient->isMove()); + // but releasing the key should end move/resize + kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++); + QVERIFY(!internalClient->isMove()); +} + +void InternalWindowTest::testModifierScroll() +{ + QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QVERIFY(clientAddedSpy.isValid()); + HelperWindow win; + win.setGeometry(0, 0, 100, 100); + win.setFlags(win.flags() & ~Qt::FramelessWindowHint); + win.show(); + QVERIFY(clientAddedSpy.wait()); + QCOMPARE(clientAddedSpy.count(), 1); + auto internalClient = clientAddedSpy.first().first().value(); + QVERIFY(internalClient); + QVERIFY(internalClient->isDecorated()); + + KConfigGroup group = kwinApp()->config()->group("MouseBindings"); + group.writeEntry("CommandAllKey", "Alt"); + group.writeEntry("CommandAllWheel", "change opacity"); + group.sync(); + workspace()->slotReconfigure(); + + // move cursor on window + Cursor::setPos(internalClient->geometry().center()); + + // set the opacity to 0.5 + internalClient->setOpacity(0.5); + QCOMPARE(internalClient->opacity(), 0.5); + quint32 timestamp = 1; + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++); + kwinApp()->platform()->pointerAxisVertical(-5, timestamp++); + QCOMPARE(internalClient->opacity(), 0.6); + kwinApp()->platform()->pointerAxisVertical(5, timestamp++); + QCOMPARE(internalClient->opacity(), 0.5); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++); +} + } WAYLANDTEST_MAIN(KWin::InternalWindowTest) diff --git a/input.cpp b/input.cpp index bd12702eab..d6fce65b52 100644 --- a/input.cpp +++ b/input.cpp @@ -766,6 +766,64 @@ public: } }; + +namespace { + +enum class MouseAction { + ModifierOnly, + ModifierAndWindow +}; +std::pair performClientMouseAction(QMouseEvent *event, AbstractClient *client, MouseAction action = MouseAction::ModifierOnly) +{ + Options::MouseCommand command = Options::MouseNothing; + bool wasAction = false; + if (static_cast(event)->modifiersRelevantForGlobalShortcuts() == 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 { + if (action == MouseAction::ModifierAndWindow) { + command = client->getMouseCommand(event->button(), &wasAction); + } + } + if (wasAction) { + return std::make_pair(wasAction, !client->performMouseCommand(command, event->globalPos())); + } + return std::make_pair(wasAction, false); +} + +std::pair performClientWheelAction(QWheelEvent *event, AbstractClient *c, MouseAction action = MouseAction::ModifierOnly) +{ + bool wasAction = false; + Options::MouseCommand command = Options::MouseNothing; + if (static_cast(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) { + wasAction = true; + command = options->operationWindowMouseWheel(-1 * event->angleDelta().y()); + } else { + if (action == MouseAction::ModifierAndWindow) { + command = c->getWheelCommand(Qt::Vertical, &wasAction); + } + } + if (wasAction) { + return std::make_pair(wasAction, !c->performMouseCommand(command, event->globalPos())); + } + return std::make_pair(wasAction, false); +} + +} + class InternalWindowEventFilter : public InputEventFilter { bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override { Q_UNUSED(nativeButton) @@ -780,6 +838,24 @@ class InternalWindowEventFilter : public InputEventFilter { if (!internal) { return false; } + // find client + switch (event->type()) + { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: { + auto s = waylandServer()->findClient(internal); + if (s && s->isDecorated()) { + // only perform mouse commands on decorated internal windows + const auto actionResult = performClientMouseAction(event, s); + if (actionResult.first) { + return actionResult.second; + } + } + break; + } + default: + break; + } QMouseEvent e(event->type(), event->pos() - internal->position(), event->globalPos(), @@ -793,6 +869,16 @@ class InternalWindowEventFilter : public InputEventFilter { if (!internal) { return false; } + if (event->angleDelta().y() != 0) { + auto s = waylandServer()->findClient(internal); + if (s && s->isDecorated()) { + // client window action only on vertical scrolling + const auto actionResult = performClientWheelAction(event, s); + if (actionResult.first) { + return actionResult.second; + } + } + } const QPointF localPos = event->globalPosF() - QPointF(internal->x(), internal->y()); const Qt::Orientation orientation = (event->angleDelta().x() != 0) ? Qt::Horizontal : Qt::Vertical; const int delta = event->angleDelta().x() != 0 ? event->angleDelta().x() : event->angleDelta().y(); @@ -929,63 +1015,6 @@ private: QPointF m_lastLocalTouchPos; }; -namespace { - -enum class MouseAction { - ModifierOnly, - ModifierAndWindow -}; -std::pair performClientMouseAction(QMouseEvent *event, AbstractClient *client, MouseAction action = MouseAction::ModifierOnly) -{ - Options::MouseCommand command = Options::MouseNothing; - bool wasAction = false; - if (static_cast(event)->modifiersRelevantForGlobalShortcuts() == 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 { - if (action == MouseAction::ModifierAndWindow) { - command = client->getMouseCommand(event->button(), &wasAction); - } - } - if (wasAction) { - return std::make_pair(wasAction, !client->performMouseCommand(command, event->globalPos())); - } - return std::make_pair(wasAction, false); -} - -std::pair performClientWheelAction(QWheelEvent *event, AbstractClient *c, MouseAction action = MouseAction::ModifierOnly) -{ - bool wasAction = false; - Options::MouseCommand command = Options::MouseNothing; - if (static_cast(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) { - wasAction = true; - command = options->operationWindowMouseWheel(-1 * event->angleDelta().y()); - } else { - if (action == MouseAction::ModifierAndWindow) { - command = c->getWheelCommand(Qt::Vertical, &wasAction); - } - } - if (wasAction) { - return std::make_pair(wasAction, !c->performMouseCommand(command, event->globalPos())); - } - return std::make_pair(wasAction, false); -} - -} - class DecorationEventFilter : public InputEventFilter { public: bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {