#include "TrackpadGestures.hpp" #include "../InputManager.hpp" #include "../../../config/ConfigValue.hpp" #include "../../../protocols/ShortcutsInhibit.hpp" #include 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 CTrackpadGestures::addGesture(UP&& gesture, size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, float deltaScale, bool disableInhibit) { 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(std::move(gesture), fingerCount, modMask, direction, deltaScale, disableInhibit)); return {}; } std::expected CTrackpadGestures::removeGesture(size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, float deltaScale, bool disableInhibit) { const auto IT = std::ranges::find_if(m_gestures, [&](const auto& g) { return g->fingerCount == fingerCount && g->direction == direction && g->modMask == modMask && g->deltaScale == deltaScale && g->disableInhibit == disableInhibit; }); if (IT == m_gestures.end()) return std::unexpected("Can't remove a non-existent gesture"); std::erase(m_gestures, *IT); return {}; } void CTrackpadGestures::gestureBegin(const IPointer::SSwipeBeginEvent& e) { if (m_activeGesture) { Log::logger->log(Log::ERR, "CTrackpadGestures::gestureBegin (swipe) but m_activeGesture is already present"); return; } m_gestureFindFailed = false; m_currentTotalDelta = {}; // nothing here. We need to wait for the first update to determine the delta. } void CTrackpadGestures::gestureUpdate(const IPointer::SSwipeUpdateEvent& e) { static auto PDISABLEINHIBIT = CConfigValue("binds:disable_keybind_grabbing"); if (m_gestureFindFailed) return; m_currentTotalDelta += e.delta; // 5 was chosen because I felt like that's a good number. if (!m_activeGesture && (std::abs(m_currentTotalDelta.x) < 5 && std::abs(m_currentTotalDelta.y) < 5)) { Log::logger->log(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(m_currentTotalDelta.x) > std::abs(m_currentTotalDelta.y) ? TRACKPAD_GESTURE_DIR_HORIZONTAL : TRACKPAD_GESTURE_DIR_VERTICAL; if (axis == TRACKPAD_GESTURE_DIR_HORIZONTAL) direction = m_currentTotalDelta.x < 0 ? TRACKPAD_GESTURE_DIR_LEFT : TRACKPAD_GESTURE_DIR_RIGHT; else direction = m_currentTotalDelta.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; if (PROTO::shortcutsInhibit->isInhibited() && !*PDISABLEINHIBIT && !g->disableInhibit) 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) { Log::logger->log(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) { static auto PDISABLEINHIBIT = CConfigValue("binds:disable_keybind_grabbing"); 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) { Log::logger->log(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; if (PROTO::shortcutsInhibit->isInhibited() && !*PDISABLEINHIBIT && !g->disableInhibit) 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(); }