Skip to content

Commit

Permalink
feat multi tap (#68)
Browse files Browse the repository at this point in the history
* cleanup

* feat: add tap gestures

* tests: add tests for tap gesture

* feat: enable tap gesture

* docs: add description for tap events
  • Loading branch information
horriblename committed Dec 17, 2023
1 parent f600c18 commit 1c7c454
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 26 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:<from_edge>:<direction>`
2. `tap:<finger_count>`
- `finger_count` must be >= 3
3. `edge:<from_edge>:<direction>`
- `<from_edge>` is from which edge to start from (l/r/u/d)
- `<direction>` is in which direction to swipe (l/r/u/d/lu/ld/ru/rd)
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/GestureManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GestureManager::GestureManager() {
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:touch_gestures:sensitivity")->floatValue;

this->addMultiFingerGesture(PSENSITIVITY);
this->addMultiFingerTap(PSENSITIVITY);
this->addEdgeSwipeGesture(PSENSITIVITY);
}

Expand Down
70 changes: 47 additions & 23 deletions src/gestures/Gestures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {

Expand Down Expand Up @@ -226,27 +235,25 @@ void IGestureManager::addMultiFingerGesture(const float* sensitivity) {
auto swipe = std::make_unique<CMultiAction>(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<CallbackAction>([=, this]() {
if (this->dragGestureActive) {
return;
}
const auto gesture = CompletedGesture{TouchGestureType::SWIPE_HOLD, swipe_ptr->target_direction,
static_cast<int>(this->m_sGestureState.fingers.size())};

this->dragGestureActive = this->handleGesture(gesture);
};
auto swipe_begin = std::make_unique<CallbackAction>(swipe_begin_callback);
});

auto swipe_liftoff = std::make_unique<LiftoffAction>();
// swipe_liftoff->set_duration(GESTURE_BASE_DURATION / 2);

std::vector<std::unique_ptr<wf::touch::gesture_action_t>> 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]() {
Expand All @@ -259,6 +266,24 @@ void IGestureManager::addMultiFingerGesture(const float* sensitivity) {
this->addTouchGesture(std::make_unique<wf::touch::gesture_t>(std::move(swipe_actions), ack, cancel));
}

void IGestureManager::addMultiFingerTap(const float* sensitivity) {
auto tap = std::make_unique<MultiFingerTap>(SWIPE_INCORRECT_DRAG_TOLERANCE, sensitivity);
tap->set_duration(GESTURE_BASE_DURATION);
// swipe_liftoff->set_duration(GESTURE_BASE_DURATION / 2);

std::vector<std::unique_ptr<wf::touch::gesture_action_t>> tap_actions;
tap_actions.emplace_back(std::move(tap));

auto ack = [this]() {
const auto gesture =
CompletedGesture{TouchGestureType::TAP, 0, static_cast<int>(this->m_sGestureState.fingers.size())};
this->handleGesture(gesture);
};
auto cancel = [this]() { this->handleCancelledGesture(); };

this->addTouchGesture(std::make_unique<wf::touch::gesture_t>(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
Expand All @@ -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<std::unique_ptr<wf::touch::gesture_action_t>> edge_swipe_actions;
Expand Down
19 changes: 19 additions & 0 deletions src/gestures/Gestures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum class TouchGestureType {
SWIPE,
SWIPE_HOLD, // same as SWIPE but fingers were not lifted
EDGE_SWIPE,
TAP,
// PINCH,
};

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -159,6 +177,7 @@ class IGestureManager {

void addTouchGesture(std::unique_ptr<wf::touch::gesture_t> gesture);
void addMultiFingerGesture(const float* sensitivity);
void addMultiFingerTap(const float* sensitivity);
void addEdgeSwipeGesture(const float* sensitivity);

bool dragGestureIsActive() const {
Expand Down
49 changes: 47 additions & 2 deletions src/gestures/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ void Tester::testFindSwipeEdges() {
}
}

enum class ExpectResultType
{
enum class ExpectResultType {
COMPLETED,
DRAG_TRIGGERED,
CANCELLED,
Expand Down Expand Up @@ -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<TouchEvent> 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<TouchEvent> 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<TouchEvent> 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"
Expand Down

0 comments on commit 1c7c454

Please sign in to comment.