diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2025-08-28 11:20:29 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-28 11:20:29 +0200 |
| commit | 81bf4eccba449bfe2b6adfb51260108aec710d4f (patch) | |
| tree | 215c5b536cbcf1df9888081aadd3de16eb4b3ebe /src/managers/input/trackpad/TrackpadGestures.cpp | |
| parent | 378e130f1426648d8d734049800128f9882805bf (diff) | |
input: Add fully configurable trackpad gestures (#11490)
Adds configurable trackpad gestures
Diffstat (limited to 'src/managers/input/trackpad/TrackpadGestures.cpp')
| -rw-r--r-- | src/managers/input/trackpad/TrackpadGestures.cpp | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/src/managers/input/trackpad/TrackpadGestures.cpp b/src/managers/input/trackpad/TrackpadGestures.cpp new file mode 100644 index 00000000..1d628da4 --- /dev/null +++ b/src/managers/input/trackpad/TrackpadGestures.cpp @@ -0,0 +1,221 @@ +#include "TrackpadGestures.hpp" + +#include "../InputManager.hpp" + +#include <ranges> + +void CTrackpadGestures::clearGestures() { + m_gestures.clear(); +} + +eTrackpadGestureDirection CTrackpadGestures::dirForString(const std::string_view& s) { + std::string lc = std::string{s}; + std::ranges::transform(lc, lc.begin(), ::tolower); + + if (lc == "swipe") + return TRACKPAD_GESTURE_DIR_SWIPE; + if (lc == "left" || lc == "l") + return TRACKPAD_GESTURE_DIR_LEFT; + if (lc == "right" || lc == "r") + return TRACKPAD_GESTURE_DIR_RIGHT; + if (lc == "up" || lc == "u" || lc == "top" || lc == "t") + return TRACKPAD_GESTURE_DIR_UP; + if (lc == "down" || lc == "d" || lc == "bottom" || lc == "b") + return TRACKPAD_GESTURE_DIR_DOWN; + if (lc == "horizontal" || lc == "horiz") + return TRACKPAD_GESTURE_DIR_HORIZONTAL; + if (lc == "vertical" || lc == "vert") + return TRACKPAD_GESTURE_DIR_VERTICAL; + if (lc == "pinch") + return TRACKPAD_GESTURE_DIR_PINCH; + if (lc == "pinchin" || lc == "zoomin") + return TRACKPAD_GESTURE_DIR_PINCH_IN; + if (lc == "pinchout" || lc == "zoomout") + return TRACKPAD_GESTURE_DIR_PINCH_OUT; + + return TRACKPAD_GESTURE_DIR_NONE; +} + +const char* CTrackpadGestures::stringForDir(eTrackpadGestureDirection dir) { + switch (dir) { + case TRACKPAD_GESTURE_DIR_HORIZONTAL: return "HORIZONTAL"; + case TRACKPAD_GESTURE_DIR_VERTICAL: return "VERTICAL"; + case TRACKPAD_GESTURE_DIR_LEFT: return "LEFT"; + case TRACKPAD_GESTURE_DIR_RIGHT: return "RIGHT"; + case TRACKPAD_GESTURE_DIR_UP: return "UP"; + case TRACKPAD_GESTURE_DIR_DOWN: return "DOWN"; + case TRACKPAD_GESTURE_DIR_SWIPE: return "SWIPE"; + case TRACKPAD_GESTURE_DIR_PINCH: return "PINCH"; + case TRACKPAD_GESTURE_DIR_PINCH_IN: return "PINCH_IN"; + case TRACKPAD_GESTURE_DIR_PINCH_OUT: return "PINCH_OUT"; + default: return "ERROR"; + } + return "ERROR"; +} + +std::expected<void, std::string> CTrackpadGestures::addGesture(UP<ITrackpadGesture>&& gesture, size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, + float deltaScale) { + for (const auto& g : m_gestures) { + if (g->fingerCount != fingerCount) + continue; + + if (g->modMask != modMask) + continue; + + eTrackpadGestureDirection axis = TRACKPAD_GESTURE_DIR_NONE; + switch (direction) { + case TRACKPAD_GESTURE_DIR_UP: + case TRACKPAD_GESTURE_DIR_DOWN: + case TRACKPAD_GESTURE_DIR_VERTICAL: axis = TRACKPAD_GESTURE_DIR_VERTICAL; break; + case TRACKPAD_GESTURE_DIR_LEFT: + case TRACKPAD_GESTURE_DIR_RIGHT: + case TRACKPAD_GESTURE_DIR_HORIZONTAL: axis = TRACKPAD_GESTURE_DIR_HORIZONTAL; break; + case TRACKPAD_GESTURE_DIR_SWIPE: axis = TRACKPAD_GESTURE_DIR_SWIPE; break; + case TRACKPAD_GESTURE_DIR_PINCH: + case TRACKPAD_GESTURE_DIR_PINCH_IN: + case TRACKPAD_GESTURE_DIR_PINCH_OUT: axis = TRACKPAD_GESTURE_DIR_PINCH; break; + default: TRACKPAD_GESTURE_DIR_NONE; break; + } + + if (g->direction == axis || g->direction == direction || + ((axis == TRACKPAD_GESTURE_DIR_VERTICAL || axis == TRACKPAD_GESTURE_DIR_HORIZONTAL) && g->direction == TRACKPAD_GESTURE_DIR_SWIPE)) { + return std::unexpected( + std::format("Gesture will be overshadowed by a previous gesture. Previous {} shadows new {}", stringForDir(g->direction), stringForDir(direction))); + } + } + + m_gestures.emplace_back(makeShared<CTrackpadGestures::SGestureData>(std::move(gesture), fingerCount, modMask, direction, deltaScale)); + + return {}; +} + +void CTrackpadGestures::gestureBegin(const IPointer::SSwipeBeginEvent& e) { + if (m_activeGesture) { + Debug::log(ERR, "CTrackpadGestures::gestureBegin (swipe) but m_activeGesture is already present"); + return; + } + + m_gestureFindFailed = false; + + // nothing here. We need to wait for the first update to determine the delta. +} + +void CTrackpadGestures::gestureUpdate(const IPointer::SSwipeUpdateEvent& e) { + if (m_gestureFindFailed) + return; + + // 5 was chosen because I felt like that's a good number. + if (!m_activeGesture && (std::abs(e.delta.x) < 5 && std::abs(e.delta.y) < 5)) { + Debug::log(TRACE, "CTrackpadGestures::gestureUpdate (swipe): gesture delta too small to start considering, waiting"); + return; + } + + if (!m_activeGesture) { + // try to find a gesture that matches our current state + + auto direction = TRACKPAD_GESTURE_DIR_NONE; + auto axis = std::abs(e.delta.x) > std::abs(e.delta.y) ? TRACKPAD_GESTURE_DIR_HORIZONTAL : TRACKPAD_GESTURE_DIR_VERTICAL; + + if (axis == TRACKPAD_GESTURE_DIR_HORIZONTAL) + direction = e.delta.x < 0 ? TRACKPAD_GESTURE_DIR_LEFT : TRACKPAD_GESTURE_DIR_RIGHT; + else + direction = e.delta.y < 0 ? TRACKPAD_GESTURE_DIR_UP : TRACKPAD_GESTURE_DIR_DOWN; + + const auto MODS = g_pInputManager->getModsFromAllKBs(); + + for (const auto& g : m_gestures) { + if (g->direction != axis && g->direction != direction && g->direction != TRACKPAD_GESTURE_DIR_SWIPE) + continue; + + if (g->fingerCount != e.fingers) + continue; + + if (g->modMask != MODS) + continue; + + m_activeGesture = g; + g->currentDirection = g->gesture->isDirectionSensitive() ? g->direction : direction; + m_activeGesture->gesture->begin({.swipe = &e, .direction = direction, .scale = g->deltaScale}); + break; + } + + if (!m_activeGesture) { + m_gestureFindFailed = true; + return; + } + } + + m_activeGesture->gesture->update({.swipe = &e, .direction = m_activeGesture->currentDirection, .scale = m_activeGesture->deltaScale}); +} + +void CTrackpadGestures::gestureEnd(const IPointer::SSwipeEndEvent& e) { + if (!m_activeGesture) + return; + + m_activeGesture->gesture->end({.swipe = &e, .direction = m_activeGesture->direction, .scale = m_activeGesture->deltaScale}); + + m_activeGesture.reset(); +} + +void CTrackpadGestures::gestureBegin(const IPointer::SPinchBeginEvent& e) { + if (m_activeGesture) { + Debug::log(ERR, "CTrackpadGestures::gestureBegin (pinch) but m_activeGesture is already present"); + return; + } + + m_gestureFindFailed = false; + + // nothing here. We need to wait for the first update to determine the delta. +} + +void CTrackpadGestures::gestureUpdate(const IPointer::SPinchUpdateEvent& e) { + if (m_gestureFindFailed) + return; + + // 0.1 was chosen because I felt like that's a good number. + if (!m_activeGesture && std::abs(e.scale - 1.F) < 0.1) { + Debug::log(TRACE, "CTrackpadGestures::gestureUpdate (pinch): gesture delta too small to start considering, waiting"); + return; + } + + if (!m_activeGesture) { + // try to find a gesture that matches our current state + + auto direction = e.scale < 1.F ? TRACKPAD_GESTURE_DIR_PINCH_OUT : TRACKPAD_GESTURE_DIR_PINCH_IN; + auto axis = TRACKPAD_GESTURE_DIR_PINCH; + + const auto MODS = g_pInputManager->getModsFromAllKBs(); + + for (const auto& g : m_gestures) { + if (g->direction != axis && g->direction != direction) + continue; + + if (g->fingerCount != e.fingers) + continue; + + if (g->modMask != MODS) + continue; + + m_activeGesture = g; + g->currentDirection = g->gesture->isDirectionSensitive() ? g->direction : direction; + m_activeGesture->gesture->begin({.pinch = &e, .direction = direction}); + break; + } + + if (!m_activeGesture) { + m_gestureFindFailed = true; + return; + } + } + + m_activeGesture->gesture->update({.pinch = &e, .direction = m_activeGesture->currentDirection}); +} + +void CTrackpadGestures::gestureEnd(const IPointer::SPinchEndEvent& e) { + if (!m_activeGesture) + return; + + m_activeGesture->gesture->end({.pinch = &e, .direction = m_activeGesture->direction}); + + m_activeGesture.reset(); +} |
