diff --git a/README.md b/README.md index 2ce0c40..7325378 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,9 @@ where (skip to [examples](#examples) if this is confusing): - `finger_count` must be >= 3 - `direction` is one of `l`, `r`, `u`, `d`, or `ld`, `rd`, `lu`, `ru` for diagonal directions. (l, r, u, d stand for left, right, up, down) - 2. `edge::` + 2. `tap:` + - `finger_count` must be >= 3 + 3. `edge::` - `` is from which edge to start from (l/r/u/d) - `` is in which direction to swipe (l/r/u/d/lu/ld/ru/rd) @@ -158,6 +160,10 @@ bind = , swipe:4:d, killactive # swipe diagonally left and down with 3 fingers # l (or r) must come before d and u bind = , swipe:3:ld, exec, foot + +# tap with 3 fingers +# NOTE: tap events only trigger for finger count of >= 3 +bind = , tap:3, exec, foot ``` # Acknowledgements diff --git a/src/GestureManager.cpp b/src/GestureManager.cpp index 4452a6a..1f89971 100644 --- a/src/GestureManager.cpp +++ b/src/GestureManager.cpp @@ -14,6 +14,7 @@ GestureManager::GestureManager() { &HyprlandAPI::getConfigValue(PHANDLE, "plugin:touch_gestures:sensitivity")->floatValue; this->addMultiFingerGesture(PSENSITIVITY); + this->addMultiFingerTap(PSENSITIVITY); this->addEdgeSwipeGesture(PSENSITIVITY); } diff --git a/src/gestures/Gestures.cpp b/src/gestures/Gestures.cpp index 0f13bef..60eca6b 100644 --- a/src/gestures/Gestures.cpp +++ b/src/gestures/Gestures.cpp @@ -28,31 +28,18 @@ std::string stringifyDirection(gestureDirection direction) { } std::string CompletedGesture::to_string() const { - std::string bind = ""; switch (type) { case TouchGestureType::EDGE_SWIPE: - bind += "edge"; - break; + return "edge:" + stringifyDirection(this->edge_origin) + ":" + stringifyDirection(this->direction); case TouchGestureType::SWIPE: - bind += "swipe"; + return "swipe:" + std::to_string(finger_count) + ":" + stringifyDirection(this->direction); break; case TouchGestureType::SWIPE_HOLD: // this gesture is only used internally for workspace swipe return "workspace_swipe"; + case TouchGestureType::TAP: + return "tap:" + std::to_string(finger_count); } - - bind += ":"; - - if (type == TouchGestureType::EDGE_SWIPE) { - bind += stringifyDirection(this->edge_origin); - } else { - bind += std::to_string(finger_count); - } - - bind += ":"; - - bind += stringifyDirection(this->direction); - return bind; } wf::touch::action_status_t CMultiAction::update_state(const wf::touch::gesture_state_t& state, @@ -116,6 +103,28 @@ wf::touch::action_status_t MultiFingerDownAction::update_state(const wf::touch:: return wf::touch::ACTION_STATUS_RUNNING; } +wf::touch::action_status_t MultiFingerTap::update_state(const wf::touch::gesture_state_t& state, + const wf::touch::gesture_event_t& event) { + if (event.time - this->start_time > this->get_duration()) { + return wf::touch::ACTION_STATUS_CANCELLED; + } + + if (event.type == wf::touch::EVENT_TYPE_TOUCH_UP) { + return wf::touch::ACTION_STATUS_COMPLETED; + } + + if (event.type == wf::touch::EVENT_TYPE_MOTION) { + for (const auto& finger : state.fingers) { + const auto delta = finger.second.delta(); + if (delta.x * delta.x + delta.y + delta.y > this->base_threshold * *this->sensitivity) { + return wf::touch::ACTION_STATUS_CANCELLED; + } + } + } + + return wf::touch::ACTION_STATUS_RUNNING; +} + wf::touch::action_status_t LiftoffAction::update_state(const wf::touch::gesture_state_t& state, const wf::touch::gesture_event_t& event) { @@ -226,10 +235,9 @@ void IGestureManager::addMultiFingerGesture(const float* sensitivity) { auto swipe = std::make_unique(SWIPE_INCORRECT_DRAG_TOLERANCE, sensitivity); swipe->set_duration(GESTURE_BASE_DURATION); - // FIXME memory management be damned auto swipe_ptr = swipe.get(); - auto swipe_begin_callback = [swipe_ptr, this]() { + auto trigger_swipe = std::make_unique([=, this]() { if (this->dragGestureActive) { return; } @@ -237,8 +245,7 @@ void IGestureManager::addMultiFingerGesture(const float* sensitivity) { static_cast(this->m_sGestureState.fingers.size())}; this->dragGestureActive = this->handleGesture(gesture); - }; - auto swipe_begin = std::make_unique(swipe_begin_callback); + }); auto swipe_liftoff = std::make_unique(); // swipe_liftoff->set_duration(GESTURE_BASE_DURATION / 2); @@ -246,7 +253,7 @@ void IGestureManager::addMultiFingerGesture(const float* sensitivity) { std::vector> swipe_actions; swipe_actions.emplace_back(std::move(multi_down)); swipe_actions.emplace_back(std::move(swipe)); - swipe_actions.emplace_back(std::move(swipe_begin)); + swipe_actions.emplace_back(std::move(trigger_swipe)); swipe_actions.emplace_back(std::move(swipe_liftoff)); auto ack = [swipe_ptr, this]() { @@ -259,6 +266,24 @@ void IGestureManager::addMultiFingerGesture(const float* sensitivity) { this->addTouchGesture(std::make_unique(std::move(swipe_actions), ack, cancel)); } +void IGestureManager::addMultiFingerTap(const float* sensitivity) { + auto tap = std::make_unique(SWIPE_INCORRECT_DRAG_TOLERANCE, sensitivity); + tap->set_duration(GESTURE_BASE_DURATION); + // swipe_liftoff->set_duration(GESTURE_BASE_DURATION / 2); + + std::vector> tap_actions; + tap_actions.emplace_back(std::move(tap)); + + auto ack = [this]() { + const auto gesture = + CompletedGesture{TouchGestureType::TAP, 0, static_cast(this->m_sGestureState.fingers.size())}; + this->handleGesture(gesture); + }; + auto cancel = [this]() { this->handleCancelledGesture(); }; + + this->addTouchGesture(std::make_unique(std::move(tap_actions), ack, cancel)); +} + // TODO: fix duration, do not depend on sensitivity void IGestureManager::addEdgeSwipeGesture(const float* sensitivity) { // Edge swipe needs a quick release to be considered edge swipe @@ -275,7 +300,6 @@ void IGestureManager::addEdgeSwipeGesture(const float* sensitivity) { // TODO make this adjustable: edge_release->set_duration(GESTURE_BASE_DURATION * 1.5 * *sensitivity); - // FIXME proper memory management pls auto edge_ptr = edge.get(); std::vector> edge_swipe_actions; diff --git a/src/gestures/Gestures.hpp b/src/gestures/Gestures.hpp index 1bf62dc..434afd7 100644 --- a/src/gestures/Gestures.hpp +++ b/src/gestures/Gestures.hpp @@ -29,6 +29,7 @@ enum class TouchGestureType { SWIPE, SWIPE_HOLD, // same as SWIPE but fingers were not lifted EDGE_SWIPE, + TAP, // PINCH, }; @@ -96,6 +97,23 @@ class CMultiAction : public wf::touch::gesture_action_t { }; }; +class MultiFingerTap : public wf::touch::gesture_action_t { + private: + double base_threshold; + const float* sensitivity; + + public: + MultiFingerTap(double base_threshold, const float* sensitivity) + : base_threshold(base_threshold), sensitivity(sensitivity){}; + + wf::touch::action_status_t update_state(const wf::touch::gesture_state_t& state, + const wf::touch::gesture_event_t& event) override; + + void reset(uint32_t time) override { + gesture_action_t::reset(time); + }; +}; + // Completes upon receiving enough touch down events within a short duration class MultiFingerDownAction : public wf::touch::gesture_action_t { // upon completion, calls the given callback. @@ -159,6 +177,7 @@ class IGestureManager { void addTouchGesture(std::unique_ptr gesture); void addMultiFingerGesture(const float* sensitivity); + void addMultiFingerTap(const float* sensitivity); void addEdgeSwipeGesture(const float* sensitivity); bool dragGestureIsActive() const { diff --git a/src/gestures/test/test.cpp b/src/gestures/test/test.cpp index 5eca958..d2ec588 100644 --- a/src/gestures/test/test.cpp +++ b/src/gestures/test/test.cpp @@ -36,8 +36,7 @@ void Tester::testFindSwipeEdges() { } } -enum class ExpectResultType -{ +enum class ExpectResultType { COMPLETED, DRAG_TRIGGERED, CANCELLED, @@ -167,6 +166,52 @@ TEST_CASE("Swipe: Complete upon moving more than the threshold then lifting a " ProcessEvents(gm, {.type = ExpectResultType::COMPLETED}, events); } +TEST_CASE("Multi-finger Tap") { + std::cout << " ==== stdout:" << std::endl; + CMockGestureManager gm; + gm.addMultiFingerTap(&SENSITIVITY); + + const std::vector events{ + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 100, 0, {450, 290}}, + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 105, 1, {500, 300}}, + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 110, 2, {550, 290}}, + {wf::touch::EVENT_TYPE_TOUCH_UP, 120, 2, {550, 290}}, + }; + + ProcessEvents(gm, {.type = ExpectResultType::COMPLETED}, events); +} + +TEST_CASE("Multi-finger Tap: Timeout") { + std::cout << " ==== stdout:" << std::endl; + CMockGestureManager gm; + gm.addMultiFingerTap(&SENSITIVITY); + + const std::vector events{ + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 100, 0, {450, 290}}, + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 105, 1, {500, 300}}, + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 110, 2, {550, 290}}, + {wf::touch::EVENT_TYPE_TOUCH_UP, 510, 2, {550, 290}}, + }; + + ProcessEvents(gm, {.type = ExpectResultType::CANCELLED}, events); +} + +TEST_CASE("Multi-finger Tap: finger moved too much") { + std::cout << " ==== stdout:" << std::endl; + CMockGestureManager gm; + gm.addMultiFingerTap(&SENSITIVITY); + + const std::vector events{ + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 100, 0, {450, 290}}, + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 105, 1, {500, 300}}, + {wf::touch::EVENT_TYPE_TOUCH_DOWN, 110, 2, {550, 290}}, + {wf::touch::EVENT_TYPE_MOTION, 120, 1, {650, 290}}, + // {wf::touch::EVENT_TYPE_TOUCH_UP, 130, 2, {550, 290}}, + }; + + ProcessEvents(gm, {.type = ExpectResultType::CANCELLED}, events); +} + TEST_CASE("Edge Swipe: Complete upon: \n" "1. touch down on edge of screen\n" "2. swiping more than the threshold, within the time limit, then\n"