diff --git a/autotests/mock_effectshandler.h b/autotests/mock_effectshandler.h --- a/autotests/mock_effectshandler.h +++ b/autotests/mock_effectshandler.h @@ -181,6 +181,8 @@ void reloadEffect(KWin::Effect *) override {} void removeSupportProperty(const QByteArray &, KWin::Effect *) override {} void reserveElectricBorder(KWin::ElectricBorder, KWin::Effect *) override {} + void registerTouchBorder(KWin::ElectricBorder, QAction *) override {} + void unregisterTouchBorder(KWin::ElectricBorder, QAction *) override {} QPainter *scenePainter() override { return nullptr; } diff --git a/autotests/test_screen_edges.cpp b/autotests/test_screen_edges.cpp --- a/autotests/test_screen_edges.cpp +++ b/autotests/test_screen_edges.cpp @@ -130,6 +130,8 @@ void testFullScreenBlocking(); void testClientEdge(); void testTouchEdge(); + void testTouchCallback_data(); + void testTouchCallback(); }; void TestScreenEdges::initTestCase() @@ -940,6 +942,98 @@ } +void TestScreenEdges::testTouchCallback_data() +{ + QTest::addColumn("border"); + QTest::addColumn("startPos"); + QTest::addColumn("delta"); + + QTest::newRow("left") << KWin::ElectricLeft << QPoint(0, 50) << QSizeF(250, 20); + QTest::newRow("top") << KWin::ElectricTop << QPoint(50, 0) << QSizeF(20, 250); + QTest::newRow("right") << KWin::ElectricRight << QPoint(99, 50) << QSizeF(-200, 0); + QTest::newRow("bottom") << KWin::ElectricBottom << QPoint(50, 99) << QSizeF(0, -200); +} + +void TestScreenEdges::testTouchCallback() +{ + qRegisterMetaType("ElectricBorder"); + using namespace KWin; + auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); + auto group = config->group("TouchEdges"); + group.writeEntry("Top", "none"); + group.writeEntry("Left", "none"); + group.writeEntry("Bottom", "none"); + group.writeEntry("Right", "none"); + config->sync(); + + auto s = ScreenEdges::self(); + s->setConfig(config); + s->init(); + + // none of our actions should be reserved + const QList edges = s->findChildren(QString(), Qt::FindDirectChildrenOnly); + QCOMPARE(edges.size(), 8); + for (auto e : edges) { + QCOMPARE(e->isReserved(), false); + QCOMPARE(e->activatesForPointer(), false); + QCOMPARE(e->activatesForTouchGesture(), false); + } + + // let's reserve an action + QAction action; + QSignalSpy actionTriggeredSpy(&action, &QAction::triggered); + QVERIFY(actionTriggeredSpy.isValid()); + QSignalSpy approachingSpy(s, &ScreenEdges::approaching); + QVERIFY(approachingSpy.isValid()); + + // reserve on edge + QFETCH(KWin::ElectricBorder, border); + s->reserveTouch(border, &action); + for (auto e : edges) { + QCOMPARE(e->isReserved(), e->border() == border); + QCOMPARE(e->activatesForPointer(), false); + QCOMPARE(e->activatesForTouchGesture(), e->border() == border); + } + + QVERIFY(approachingSpy.isEmpty()); + QFETCH(QPoint, startPos); + QCOMPARE(s->gestureRecognizer()->startSwipeGesture(startPos), 1); + QVERIFY(actionTriggeredSpy.isEmpty()); + QCOMPARE(approachingSpy.count(), 1); + QFETCH(QSizeF, delta); + s->gestureRecognizer()->updateSwipeGesture(delta); + QCOMPARE(approachingSpy.count(), 2); + QVERIFY(actionTriggeredSpy.isEmpty()); + s->gestureRecognizer()->endSwipeGesture(); + QVERIFY(actionTriggeredSpy.wait()); + QCOMPARE(actionTriggeredSpy.count(), 1); + QCOMPARE(approachingSpy.count(), 3); + + // unreserve again + s->unreserveTouch(border, &action); + for (auto e : edges) { + QCOMPARE(e->isReserved(), false); + QCOMPARE(e->activatesForPointer(), false); + QCOMPARE(e->activatesForTouchGesture(), false); + } + + // reserve another action + QScopedPointer action2(new QAction); + s->reserveTouch(border, action2.data()); + for (auto e : edges) { + QCOMPARE(e->isReserved(), e->border() == border); + QCOMPARE(e->activatesForPointer(), false); + QCOMPARE(e->activatesForTouchGesture(), e->border() == border); + } + // and unreserve by destroying + action2.reset(); + for (auto e : edges) { + QCOMPARE(e->isReserved(), false); + QCOMPARE(e->activatesForPointer(), false); + QCOMPARE(e->activatesForTouchGesture(), false); + } +} + Q_CONSTRUCTOR_FUNCTION(forceXcb) QTEST_MAIN(TestScreenEdges) #include "test_screen_edges.moc" diff --git a/effects.h b/effects.h --- a/effects.h +++ b/effects.h @@ -172,6 +172,9 @@ void reserveElectricBorder(ElectricBorder border, Effect *effect) override; void unreserveElectricBorder(ElectricBorder border, Effect *effect) override; + void registerTouchBorder(ElectricBorder border, QAction *action) override; + void unregisterTouchBorder(ElectricBorder border, QAction *action) override; + unsigned long xrenderBufferPicture() override; QPainter* scenePainter() override; void reconfigure() override; diff --git a/effects.cpp b/effects.cpp --- a/effects.cpp +++ b/effects.cpp @@ -1306,6 +1306,16 @@ ScreenEdges::self()->unreserve(border, effect); } +void EffectsHandlerImpl::registerTouchBorder(ElectricBorder border, QAction *action) +{ + ScreenEdges::self()->reserveTouch(border, action); +} + +void EffectsHandlerImpl::unregisterTouchBorder(ElectricBorder border, QAction *action) +{ + ScreenEdges::self()->unreserveTouch(border, action); +} + unsigned long EffectsHandlerImpl::xrenderBufferPicture() { #ifdef KWIN_HAVE_XRENDER_COMPOSITING diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -897,6 +897,28 @@ virtual void reserveElectricBorder(ElectricBorder border, Effect *effect) = 0; virtual void unreserveElectricBorder(ElectricBorder border, Effect *effect) = 0; + /** + * Registers the given @p action for the given @p border to be activated through + * a touch swipe gesture. + * + * If the @p border gets triggered through a touch swipe gesture the @link{QAction::triggered} + * signal gets invoked. + * + * To unregister the touch screen action either delete the @p action or + * invoke @link{unregisterTouchBorder}. + * + * @see unregisterTouchBorder + * @since 5.10 + **/ + virtual void registerTouchBorder(ElectricBorder border, QAction *action) = 0; + /** + * Unregisters the given @p action for the given touch @p border. + * + * @see registerTouchBorder + * @since 5.10 + **/ + virtual void unregisterTouchBorder(ElectricBorder border, QAction *action) = 0; + // functions that allow controlling windows/desktop virtual void activateWindow(KWin::EffectWindow* c) = 0; virtual KWin::EffectWindow* activeWindow() const = 0 ; diff --git a/screenedge.h b/screenedge.h --- a/screenedge.h +++ b/screenedge.h @@ -39,6 +39,7 @@ #include #include +class QAction; class QMouseEvent; namespace KWin { @@ -69,6 +70,11 @@ ElectricBorder border() const; void reserve(QObject *object, const char *slot); const QHash &callBacks() const; + void reserveTouchCallBack(QAction *action); + void unreserveTouchCallBack(QAction *action); + QVector touchCallBacks() const { + return m_touchActions; + } void startApproaching(); void stopApproaching(); bool isApproaching() const; @@ -128,6 +134,7 @@ return handleAction(m_touchAction); } bool handleByCallback(); + void handleTouchCallback(); void switchDesktop(const QPoint &cursorPos); void pushCursorBack(const QPoint &cursorPos); ScreenEdges *m_edges; @@ -147,6 +154,7 @@ bool m_pushBackBlocked; AbstractClient *m_client; SwipeGesture *m_gesture; + QVector m_touchActions; }; /** @@ -275,6 +283,25 @@ * @param border The border which the client wants to use, only proper borders are supported (no corners) **/ void reserve(KWin::AbstractClient *client, ElectricBorder border); + + /** + * Mark the specified screen edge as reserved for touch gestures. This method is provided for + * external activation like effects and scripts. + * When the effect/script does no longer need the edge it is supposed + * to call @link unreserveTouch. + * @param border the screen edge to mark as reserved + * @param action The action which gets triggered + * @see unreserveTouch + * @since 5.10 + **/ + void reserveTouch(ElectricBorder border, QAction *action); + /** + * Unreserves the specified @p border from activating the @p action for touch gestures. + * @see reserveTouch + * @since 5.10 + **/ + void unreserveTouch(ElectricBorder border, QAction *action); + /** * Reserve desktop switching for screen edges, if @p isToReserve is @c true. Unreserve otherwise. * @param reserve indicated weather desktop switching should be reserved or unreseved diff --git a/screenedge.cpp b/screenedge.cpp --- a/screenedge.cpp +++ b/screenedge.cpp @@ -48,6 +48,7 @@ // frameworks #include // Qt +#include #include #include #include @@ -86,6 +87,7 @@ return; } handleTouchAction(); + handleTouchCallback(); }, Qt::QueuedConnection ); connect(m_gesture, &SwipeGesture::started, this, &Edge::startApproaching); @@ -132,6 +134,29 @@ reserve(); } +void Edge::reserveTouchCallBack(QAction *action) +{ + if (m_touchActions.contains(action)) { + return; + } + connect(action, &QAction::destroyed, this, + [this, action] { + unreserveTouchCallBack(action); + } + ); + m_touchActions << action; + reserve(); +} + +void Edge::unreserveTouchCallBack(QAction *action) +{ + auto it = std::find_if(m_touchActions.begin(), m_touchActions.end(), [action] (QAction *a) { return a == action; }); + if (it != m_touchActions.end()) { + m_touchActions.erase(it); + unreserve(); + } +} + void Edge::unreserve() { m_reserved--; @@ -178,6 +203,9 @@ if (m_touchAction != ElectricActionNone) { return true; } + if (!m_touchActions.isEmpty()) { + return true; + } return false; } @@ -365,6 +393,14 @@ return false; } +void Edge::handleTouchCallback() +{ + if (m_touchActions.isEmpty()) { + return; + } + m_touchActions.first()->trigger(); +} + void Edge::switchDesktop(const QPoint &cursorPos) { QPoint pos(cursorPos); @@ -1002,6 +1038,10 @@ ++callback) { edge->reserve(callback.key(), callback.value().constData()); } + const auto touchCallBacks = oldEdge->touchCallBacks(); + for (auto a : touchCallBacks) { + edge->reserveTouchCallBack(a); + } } } qDeleteAll(oldEdges); @@ -1188,6 +1228,24 @@ } } +void ScreenEdges::reserveTouch(ElectricBorder border, QAction *action) +{ + for (auto it = m_edges.begin(); it != m_edges.end(); ++it) { + if ((*it)->border() == border) { + (*it)->reserveTouchCallBack(action); + } + } +} + +void ScreenEdges::unreserveTouch(ElectricBorder border, QAction *action) +{ + for (auto it = m_edges.begin(); it != m_edges.end(); ++it) { + if ((*it)->border() == border) { + (*it)->unreserveTouchCallBack(action); + } + } +} + void ScreenEdges::createEdgeForClient(AbstractClient *client, ElectricBorder border) { int y = 0;