diff --git a/autotests/test_virtual_desktops.cpp b/autotests/test_virtual_desktops.cpp index 28f5bc5bd2..4b09514c5c 100644 --- a/autotests/test_virtual_desktops.cpp +++ b/autotests/test_virtual_desktops.cpp @@ -38,6 +38,10 @@ void InputRedirection::registerTouchpadSwipeShortcut(SwipeDirection, uint finger { } +void InputRedirection::registerTouchscreenSwipeShortcut(SwipeDirection, uint fingerCount, QAction*) +{ +} + } Q_DECLARE_METATYPE(Qt::Orientation) diff --git a/src/effects.cpp b/src/effects.cpp index 95bd4c3eaf..8803e0eac9 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -792,6 +792,11 @@ void EffectsHandlerImpl::registerTouchpadPinchShortcut(PinchDirection direction, input()->registerTouchpadPinchShortcut(direction, fingerCount, action); } +void EffectsHandlerImpl::registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action) +{ + input()->registerTouchscreenSwipeShortcut(direction, fingerCount, action); +} + void *EffectsHandlerImpl::getProxy(QString name) { for (QVector::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) diff --git a/src/effects.h b/src/effects.h index 30298d40ea..a110f0fe96 100644 --- a/src/effects.h +++ b/src/effects.h @@ -117,6 +117,7 @@ public: void registerTouchpadSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action) override; void registerRealtimeTouchpadPinchShortcut(PinchDirection dir, uint fingerCount, QAction *onUp, std::function progressCallback) override; void registerTouchpadPinchShortcut(PinchDirection direction, uint fingerCount, QAction *action) override; + void registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action) override; void *getProxy(QString name) override; void startMousePolling() override; void stopMousePolling() override; diff --git a/src/effects/overview/overvieweffect.cpp b/src/effects/overview/overvieweffect.cpp index 21c4649493..c530cf44e8 100644 --- a/src/effects/overview/overvieweffect.cpp +++ b/src/effects/overview/overvieweffect.cpp @@ -40,6 +40,8 @@ OverviewEffect::OverviewEffect() m_toggleShortcut = KGlobalAccel::self()->shortcut(m_toggleAction); effects->registerGlobalShortcut({defaultToggleShortcut}, m_toggleAction); effects->registerTouchpadSwipeShortcut(SwipeDirection::Up, 4, m_toggleAction); + effects->registerTouchpadSwipeShortcut(SwipeDirection::Up, 3, m_toggleAction); + effects->registerTouchpadSwipeShortcut(SwipeDirection::Down, 3, m_toggleAction); connect(effects, &EffectsHandler::screenAboutToLock, this, &OverviewEffect::realDeactivate); diff --git a/src/globalshortcuts.cpp b/src/globalshortcuts.cpp index 637147f09f..31f02aa291 100644 --- a/src/globalshortcuts.cpp +++ b/src/globalshortcuts.cpp @@ -106,7 +106,8 @@ PinchGesture *GlobalShortcut::pinchGesture() const GlobalShortcutsManager::GlobalShortcutsManager(QObject *parent) : QObject(parent) - , m_gestureRecognizer(new GestureRecognizer(this)) + , m_touchpadGestureRecognizer(new GestureRecognizer(this)) + , m_touchscreenGestureRecognizer(new GestureRecognizer(this)) { } @@ -141,7 +142,7 @@ void GlobalShortcutsManager::objectDeleted(QObject *object) } } -bool GlobalShortcutsManager::addIfNotExists(GlobalShortcut sc) +bool GlobalShortcutsManager::addIfNotExists(GlobalShortcut sc, DeviceType device) { for (const auto &cs : qAsConst(m_shortcuts)) { if (sc.shortcut() == cs.shortcut()) { @@ -149,10 +150,11 @@ bool GlobalShortcutsManager::addIfNotExists(GlobalShortcut sc) } } + const auto &recognizer = device == DeviceType::Touchpad ? m_touchpadGestureRecognizer : m_touchscreenGestureRecognizer; if (std::holds_alternative(sc.shortcut()) || std::holds_alternative(sc.shortcut())) { - m_gestureRecognizer->registerSwipeGesture(sc.swipeGesture()); + recognizer->registerSwipeGesture(sc.swipeGesture()); } else if (std::holds_alternative(sc.shortcut()) || std::holds_alternative(sc.shortcut())) { - m_gestureRecognizer->registerPinchGesture(sc.pinchGesture()); + recognizer->registerPinchGesture(sc.pinchGesture()); } connect(sc.action(), &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted); m_shortcuts.push_back(std::move(sc)); @@ -171,22 +173,27 @@ void GlobalShortcutsManager::registerAxisShortcut(QAction *action, Qt::KeyboardM void GlobalShortcutsManager::registerTouchpadSwipe(QAction *action, SwipeDirection direction, uint fingerCount) { - addIfNotExists(GlobalShortcut(SwipeShortcut{direction, fingerCount}, action)); + addIfNotExists(GlobalShortcut(SwipeShortcut{DeviceType::Touchpad, direction, fingerCount}, action), DeviceType::Touchpad); } void GlobalShortcutsManager::registerRealtimeTouchpadSwipe(QAction *action, std::function progressCallback, SwipeDirection direction, uint fingerCount) { - addIfNotExists(GlobalShortcut(RealtimeFeedbackSwipeShortcut{direction, progressCallback, fingerCount}, action)); + addIfNotExists(GlobalShortcut(RealtimeFeedbackSwipeShortcut{DeviceType::Touchpad, direction, progressCallback, fingerCount}, action)), DeviceType::Touchpad; } void GlobalShortcutsManager::registerTouchpadPinch(QAction *action, PinchDirection direction, uint fingerCount) { - addIfNotExists(GlobalShortcut(PinchShortcut{direction, fingerCount}, action)); + addIfNotExists(GlobalShortcut(PinchShortcut{direction, fingerCount}, action), DeviceType::Touchpad); } void GlobalShortcutsManager::registerRealtimeTouchpadPinch(QAction *onUp, std::function progressCallback, PinchDirection direction, uint fingerCount) { - addIfNotExists(GlobalShortcut(RealtimeFeedbackPinchShortcut{direction, progressCallback, fingerCount}, onUp)); + addIfNotExists(GlobalShortcut(RealtimeFeedbackPinchShortcut{direction, progressCallback, fingerCount}, onUp), DeviceType::Touchpad); +} + +void GlobalShortcutsManager::registerTouchscreenSwipe(QAction *action, SwipeDirection direction, uint fingerCount) +{ + addIfNotExists(GlobalShortcut(SwipeShortcut{DeviceType::Touchscreen, direction, fingerCount}, action), DeviceType::Touchscreen); } bool GlobalShortcutsManager::processKey(Qt::KeyboardModifiers mods, int keyQt) @@ -250,45 +257,61 @@ bool GlobalShortcutsManager::processAxis(Qt::KeyboardModifiers mods, PointerAxis return match(m_shortcuts, mods, axis); } -void GlobalShortcutsManager::processSwipeStart(uint fingerCount) +void GlobalShortcutsManager::processSwipeStart(DeviceType device, uint fingerCount) { - m_gestureRecognizer->startSwipeGesture(fingerCount); + if (device == DeviceType::Touchpad) { + m_touchpadGestureRecognizer->startSwipeGesture(fingerCount); + } else { + m_touchscreenGestureRecognizer->startSwipeGesture(fingerCount); + } } -void GlobalShortcutsManager::processSwipeUpdate(const QSizeF &delta) +void GlobalShortcutsManager::processSwipeUpdate(DeviceType device, const QSizeF &delta) { - m_gestureRecognizer->updateSwipeGesture(delta); + if (device == DeviceType::Touchpad) { + m_touchpadGestureRecognizer->updateSwipeGesture(delta); + } else { + m_touchscreenGestureRecognizer->updateSwipeGesture(delta); + } } -void GlobalShortcutsManager::processSwipeCancel() +void GlobalShortcutsManager::processSwipeCancel(DeviceType device) { - m_gestureRecognizer->cancelSwipeGesture(); + if (device == DeviceType::Touchpad) { + m_touchpadGestureRecognizer->cancelSwipeGesture(); + } else { + m_touchscreenGestureRecognizer->cancelSwipeGesture(); + } } -void GlobalShortcutsManager::processSwipeEnd() +void GlobalShortcutsManager::processSwipeEnd(DeviceType device) { - m_gestureRecognizer->endSwipeGesture(); + if (device == DeviceType::Touchpad) { + m_touchpadGestureRecognizer->endSwipeGesture(); + } else { + m_touchscreenGestureRecognizer->endSwipeGesture(); + } // TODO: cancel on Wayland Seat if one triggered } void GlobalShortcutsManager::processPinchStart(uint fingerCount) { - m_gestureRecognizer->startPinchGesture(fingerCount); + m_touchpadGestureRecognizer->startPinchGesture(fingerCount); } void GlobalShortcutsManager::processPinchUpdate(qreal scale, qreal angleDelta, const QSizeF &delta) { - m_gestureRecognizer->updatePinchGesture(scale, angleDelta, delta); + m_touchpadGestureRecognizer->updatePinchGesture(scale, angleDelta, delta); } void GlobalShortcutsManager::processPinchCancel() { - m_gestureRecognizer->cancelPinchGesture(); + m_touchpadGestureRecognizer->cancelPinchGesture(); } void GlobalShortcutsManager::processPinchEnd() { - m_gestureRecognizer->endPinchGesture(); + m_touchpadGestureRecognizer->endPinchGesture(); } } // namespace diff --git a/src/globalshortcuts.h b/src/globalshortcuts.h index 1ebe10c21a..53669a9eab 100644 --- a/src/globalshortcuts.h +++ b/src/globalshortcuts.h @@ -25,6 +25,11 @@ class SwipeGesture; class PinchGesture; class GestureRecognizer; +enum class DeviceType { + Touchpad, + Touchscreen +}; + /** * @brief Manager for the global shortcut system inside KWin. * @@ -67,6 +72,8 @@ public: void registerRealtimeTouchpadPinch(QAction *onUp, std::function progressCallback, PinchDirection direction, uint fingerCount = 4); + void registerTouchscreenSwipe(QAction *action, SwipeDirection direction, uint fingerCount); + /** * @brief Processes a key event to decide whether a shortcut needs to be triggered. * @@ -93,10 +100,10 @@ public: */ bool processAxis(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis); - void processSwipeStart(uint fingerCount); - void processSwipeUpdate(const QSizeF &delta); - void processSwipeCancel(); - void processSwipeEnd(); + void processSwipeStart(DeviceType device, uint fingerCount); + void processSwipeUpdate(DeviceType device, const QSizeF &delta); + void processSwipeCancel(DeviceType device); + void processSwipeEnd(DeviceType device); void processPinchStart(uint fingerCount); void processPinchUpdate(qreal scale, qreal angleDelta, const QSizeF &delta); @@ -110,13 +117,14 @@ public: private: void objectDeleted(QObject *object); - bool addIfNotExists(GlobalShortcut sc); + bool addIfNotExists(GlobalShortcut sc, DeviceType device = DeviceType::Touchpad); QVector m_shortcuts; KGlobalAccelD *m_kglobalAccel = nullptr; KGlobalAccelInterface *m_kglobalAccelInterface = nullptr; - GestureRecognizer *m_gestureRecognizer; + QScopedPointer m_touchpadGestureRecognizer; + QScopedPointer m_touchscreenGestureRecognizer; }; struct KeyboardShortcut @@ -147,15 +155,17 @@ struct PointerAxisShortcut }; struct SwipeShortcut { + DeviceType device; SwipeDirection direction; uint fingerCount; bool operator==(const SwipeShortcut &rhs) const { - return direction == rhs.direction && fingerCount == rhs.fingerCount; + return direction == rhs.direction && fingerCount == rhs.fingerCount && device == rhs.device; } }; struct RealtimeFeedbackSwipeShortcut { + DeviceType device; SwipeDirection direction; std::function progressCallback; uint fingerCount; @@ -163,7 +173,7 @@ struct RealtimeFeedbackSwipeShortcut template bool operator==(const T &rhs) const { - return direction == rhs.direction && fingerCount == rhs.fingerCount; + return direction == rhs.direction && fingerCount == rhs.fingerCount && device == rhs.device; } }; diff --git a/src/input.cpp b/src/input.cpp index 80f67adefd..cb14054dac 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -42,6 +42,8 @@ #include "wayland_server.h" #include "workspace.h" #include "xwl/xwayland_interface.h" +#include "abstract_output.h" + #include #include #include @@ -138,6 +140,16 @@ bool InputEventFilter::touchUp(qint32 id, quint32 time) return false; } +bool InputEventFilter::touchCancel() +{ + return false; +} + +bool InputEventFilter::touchFrame() +{ + return false; +} + bool InputEventFilter::pinchGestureBegin(int fingerCount, quint32 time) { Q_UNUSED(fingerCount) @@ -995,25 +1007,25 @@ public: bool swipeGestureBegin(int fingerCount, quint32 time) override { Q_UNUSED(time) - input()->shortcuts()->processSwipeStart(fingerCount); + input()->shortcuts()->processSwipeStart(DeviceType::Touchpad, fingerCount); return false; } bool swipeGestureUpdate(const QSizeF &delta, quint32 time) override { Q_UNUSED(time) - input()->shortcuts()->processSwipeUpdate(delta); + input()->shortcuts()->processSwipeUpdate(DeviceType::Touchpad, delta); return false; } bool swipeGestureCancelled(quint32 time) override { Q_UNUSED(time) - input()->shortcuts()->processSwipeCancel(); + input()->shortcuts()->processSwipeCancel(DeviceType::Touchpad); return false; } bool swipeGestureEnd(quint32 time) override { Q_UNUSED(time) - input()->shortcuts()->processSwipeEnd(); + input()->shortcuts()->processSwipeEnd(DeviceType::Touchpad); return false; } bool pinchGestureBegin(int fingerCount, quint32 time) override @@ -1042,8 +1054,99 @@ public: input()->shortcuts()->processPinchCancel(); return false; } + bool touchDown(qint32 id, const QPointF &pos, quint32 time) override { + if (m_gestureTaken) { + input()->shortcuts()->processSwipeCancel(DeviceType::Touchscreen); + m_gestureCancelled = true; + return true; + } else { + m_touchPoints.insert(id, {pos, QSize()}); + if (m_touchPoints.count() == 1) { + m_lastTouchDownTime = time; + } else { + if (time - m_lastTouchDownTime > 250) { + m_gestureCancelled = true; + return false; + } + m_lastTouchDownTime = time; + auto output = kwinApp()->platform()->outputAt(pos.toPoint()); + float xfactor = output->physicalSize().width() / (float)output->geometry().width(); + float yfactor = output->physicalSize().height() / (float)output->geometry().height(); + bool distanceMatch = std::any_of(m_touchPoints.constBegin(), m_touchPoints.constEnd(), [pos, xfactor, yfactor](const auto &point) { + QPointF p = pos - point.pos; + return std::abs(xfactor * p.x()) + std::abs(yfactor * p.y()) < 50; + }); + if (!distanceMatch) { + m_gestureCancelled = true; + return false; + } + } + if (m_touchPoints.count() >= 3 && !m_gestureCancelled) { + m_gestureTaken = true; + input()->processFilters(std::bind(&InputEventFilter::touchCancel, std::placeholders::_1)); + input()->shortcuts()->processSwipeStart(DeviceType::Touchscreen, m_touchPoints.count()); + return true; + } + } + return false; + } + + bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override { + Q_UNUSED(time); + if (m_gestureTaken) { + if (m_gestureCancelled) { + return true; + } + // only track one finger for simplicity + if (id != m_touchPoints.begin().key()) { + return true; + } + auto &point = m_touchPoints[id]; + QPointF dist = pos - point.pos; + auto output = kwinApp()->platform()->outputAt(pos.toPoint()); + float xfactor = output->physicalSize().width() / (float)output->geometry().width(); + float yfactor = output->physicalSize().height() / (float)output->geometry().height(); + QSize delta = QSize(xfactor * dist.x(), yfactor * dist.y()) * 10; + point.distance += delta; + point.pos = pos; + input()->shortcuts()->processSwipeUpdate(DeviceType::Touchscreen, delta); + return true; + } + return false; + } + + bool touchUp(qint32 id, quint32 time) override { + Q_UNUSED(time); + m_touchPoints.remove(id); + if (m_gestureTaken) { + if (!m_gestureCancelled) { + input()->shortcuts()->processSwipeEnd(DeviceType::Touchscreen); + m_gestureCancelled = true; + } + m_gestureTaken &= m_touchPoints.count() > 0; + m_gestureCancelled &= m_gestureTaken; + m_touchGestureCancelSent &= m_gestureTaken; + return true; + } + return false; + } + + bool touchFrame() override { + return m_gestureTaken; + } private: + struct TouchPoint { + QPointF pos; + QSize distance; + }; + bool m_gestureTaken = false; + bool m_gestureCancelled = false; + bool m_touchGestureCancelSent = false; + uint32_t m_lastTouchDownTime = 0; + QPointF m_lastAverageDistance; + QMap m_touchPoints; + QTimer *m_powerDown = nullptr; }; @@ -1753,6 +1856,16 @@ public: seat->notifyTouchUp(id); return true; } + bool touchCancel() override + { + waylandServer()->seat()->notifyTouchCancel(); + return true; + } + bool touchFrame() override + { + waylandServer()->seat()->notifyTouchFrame(); + return true; + } bool pinchGestureBegin(int fingerCount, quint32 time) override { auto seat = waylandServer()->seat(); @@ -2681,7 +2794,6 @@ void InputRedirection::setupInputFilters() if (hasGlobalShortcutSupport) { installInputEventFilter(new ScreenEdgeInputFilter); } - installInputEventFilter(new EffectsFilter); installInputEventFilter(new MoveResizeFilter); #if KWIN_BUILD_TABBOX installInputEventFilter(new TabBoxInputFilter); @@ -2689,6 +2801,7 @@ void InputRedirection::setupInputFilters() if (hasGlobalShortcutSupport) { installInputEventFilter(new GlobalShortcutFilter); } + installInputEventFilter(new EffectsFilter); installInputEventFilter(new PopupInputFilter); installInputEventFilter(new DecorationEventFilter); installInputEventFilter(new WindowActionInputFilter); @@ -3068,6 +3181,11 @@ void InputRedirection::registerGlobalAccel(KGlobalAccelInterface *interface) m_shortcuts->setKGlobalAccelInterface(interface); } +void InputRedirection::registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action) +{ + m_shortcuts->registerTouchscreenSwipe(action, direction, fingerCount); +} + void InputRedirection::warpPointer(const QPointF &pos) { m_pointer->warp(pos); diff --git a/src/input.h b/src/input.h index 65de17a0ba..f7c3081319 100644 --- a/src/input.h +++ b/src/input.h @@ -139,6 +139,7 @@ public: void registerRealtimeTouchpadSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *onUp, std::function progressCallback); void registerTouchpadPinchShortcut(PinchDirection direction, uint fingerCount, QAction *action); void registerRealtimeTouchpadPinchShortcut(PinchDirection direction, uint fingerCount, QAction *onUp, std::function progressCallback); + void registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action); void registerGlobalAccel(KGlobalAccelInterface *interface); bool supportsPointerWarping() const; @@ -398,6 +399,8 @@ public: virtual bool touchDown(qint32 id, const QPointF &pos, quint32 time); virtual bool touchMotion(qint32 id, const QPointF &pos, quint32 time); virtual bool touchUp(qint32 id, quint32 time); + virtual bool touchCancel(); + virtual bool touchFrame(); virtual bool pinchGestureBegin(int fingerCount, quint32 time); virtual bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time); diff --git a/src/libkwineffects/kwineffects.h b/src/libkwineffects/kwineffects.h index 401987ce88..4448b64bba 100644 --- a/src/libkwineffects/kwineffects.h +++ b/src/libkwineffects/kwineffects.h @@ -956,6 +956,16 @@ public: virtual void registerRealtimeTouchpadPinchShortcut(PinchDirection dir, uint fingerCount, QAction *onUp, std::function progressCallback) = 0; virtual void registerTouchpadPinchShortcut(PinchDirection direction, uint fingerCount, QAction *action) = 0; + + /** + * @brief Registers a global touchscreen swipe gesture shortcut with the provided @p action. + * + * @param direction The direction for the swipe + * @param action The action which gets triggered when the gesture triggers + * @since 5.25 + */ + virtual void registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action) = 0; + /** * Retrieve the proxy class for an effect if it has one. Will return NULL if * the effect isn't loaded or doesn't have a proxy class. diff --git a/src/touch_input.cpp b/src/touch_input.cpp index 0b69fd50a9..0cf02ad449 100644 --- a/src/touch_input.cpp +++ b/src/touch_input.cpp @@ -207,7 +207,7 @@ void TouchInputRedirection::frame() if (!inited() || !waylandServer()->seat()->hasTouch()) { return; } - waylandServer()->seat()->notifyTouchFrame(); + input()->processFilters(std::bind(&InputEventFilter::touchFrame, std::placeholders::_1)); } } diff --git a/src/virtualdesktops.cpp b/src/virtualdesktops.cpp index 377a29bdba..fcde5588ea 100644 --- a/src/virtualdesktops.cpp +++ b/src/virtualdesktops.cpp @@ -798,6 +798,9 @@ void VirtualDesktopManager::initShortcuts() findChild(QStringLiteral("Switch to Next Desktop"))); input()->registerAxisShortcut(Qt::ControlModifier | Qt::AltModifier, PointerAxisUp, findChild(QStringLiteral("Switch to Previous Desktop"))); + + input()->registerTouchscreenSwipeShortcut(SwipeDirection::Left, 3, nextAction); + input()->registerTouchscreenSwipeShortcut(SwipeDirection::Right, 3, previousAction); } void VirtualDesktopManager::initSwitchToShortcuts()