input: Add fully configurable trackpad gestures (#11490)

Adds configurable trackpad gestures
This commit is contained in:
Vaxry 2025-08-28 11:20:29 +02:00 committed by GitHub
parent 378e130f14
commit 81bf4eccba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 2518 additions and 940 deletions

View file

@ -12,6 +12,7 @@
#include "../../protocols/IdleInhibit.hpp"
#include "../../protocols/RelativePointer.hpp"
#include "../../protocols/PointerConstraints.hpp"
#include "../../protocols/PointerGestures.hpp"
#include "../../protocols/IdleNotify.hpp"
#include "../../protocols/SessionLock.hpp"
#include "../../protocols/InputMethodV2.hpp"
@ -41,6 +42,8 @@
#include "../../helpers/time/Time.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "trackpad/TrackpadGestures.hpp"
#include <aquamarine/input/Input.hpp>
CInputManager::CInputManager() {
@ -1946,3 +1949,51 @@ void CInputManager::recheckMouseWarpOnMouseInput() {
if (!m_lastInputMouse && *PWARPFORNONMOUSE)
g_pPointerManager->warpTo(m_lastMousePos);
}
void CInputManager::onSwipeBegin(IPointer::SSwipeBeginEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("swipeBegin", e);
g_pTrackpadGestures->gestureBegin(e);
PROTO::pointerGestures->swipeBegin(e.timeMs, e.fingers);
}
void CInputManager::onSwipeUpdate(IPointer::SSwipeUpdateEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("swipeUpdate", e);
g_pTrackpadGestures->gestureUpdate(e);
PROTO::pointerGestures->swipeUpdate(e.timeMs, e.delta);
}
void CInputManager::onSwipeEnd(IPointer::SSwipeEndEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("swipeEnd", e);
g_pTrackpadGestures->gestureEnd(e);
PROTO::pointerGestures->swipeEnd(e.timeMs, e.cancelled);
}
void CInputManager::onPinchBegin(IPointer::SPinchBeginEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("pinchBegin", e);
g_pTrackpadGestures->gestureBegin(e);
PROTO::pointerGestures->pinchBegin(e.timeMs, e.fingers);
}
void CInputManager::onPinchUpdate(IPointer::SPinchUpdateEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("pinchUpdate", e);
g_pTrackpadGestures->gestureUpdate(e);
PROTO::pointerGestures->pinchUpdate(e.timeMs, e.delta, e.scale, e.rotation);
}
void CInputManager::onPinchEnd(IPointer::SPinchEndEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("pinchEnd", e);
g_pTrackpadGestures->gestureEnd(e);
PROTO::pointerGestures->pinchEnd(e.timeMs, e.cancelled);
}

View file

@ -141,6 +141,10 @@ class CInputManager {
void onSwipeEnd(IPointer::SSwipeEndEvent);
void onSwipeUpdate(IPointer::SSwipeUpdateEvent);
void onPinchBegin(IPointer::SPinchBeginEvent);
void onPinchUpdate(IPointer::SPinchUpdateEvent);
void onPinchEnd(IPointer::SPinchEndEvent);
void onTabletAxis(CTablet::SAxisEvent);
void onTabletProximity(CTablet::SProximityEvent);
void onTabletTip(CTablet::STipEvent);
@ -179,8 +183,6 @@ class CInputManager {
void recheckIdleInhibitorStatus();
bool isWindowInhibiting(const PHLWINDOW& pWindow, bool onlyHl = true);
SSwipeGesture m_activeSwipe;
CTimer m_lastCursorMovement;
CInputMethodRelay m_relay;
@ -276,13 +278,8 @@ class CInputManager {
};
std::vector<UP<SIdleInhibitor>> m_idleInhibitors;
// swipe
void beginWorkspaceSwipe();
void updateWorkspaceSwipe(double);
void endWorkspaceSwipe();
void setBorderCursorIcon(eBorderIconDirection);
void setCursorIconOnBorder(PHLWINDOW w);
void setBorderCursorIcon(eBorderIconDirection);
void setCursorIconOnBorder(PHLWINDOW w);
// temporary. Obeys setUntilUnset.
void setCursorImageOverride(const std::string& name);
@ -313,6 +310,7 @@ class CInputManager {
friend class CKeybindManager;
friend class CWLSurface;
friend class CWorkspaceSwipeGesture;
};
inline UP<CInputManager> g_pInputManager;

View file

@ -1,348 +0,0 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "../../desktop/LayerSurface.hpp"
#include "../../config/ConfigValue.hpp"
#include "../../managers/HookSystemManager.hpp"
#include "../../render/Renderer.hpp"
void CInputManager::onSwipeBegin(IPointer::SSwipeBeginEvent e) {
static auto PSWIPE = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe");
static auto PSWIPEFINGERS = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_fingers");
static auto PSWIPEMINFINGERS = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_min_fingers");
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
EMIT_HOOK_EVENT_CANCELLABLE("swipeBegin", e);
if ((!*PSWIPEMINFINGERS && e.fingers != *PSWIPEFINGERS) || (*PSWIPEMINFINGERS && e.fingers < *PSWIPEFINGERS) || *PSWIPE == 0 || g_pSessionLockManager->isSessionLocked())
return;
int onMonitor = 0;
for (auto const& w : g_pCompositor->getWorkspaces()) {
if (w->m_monitor == g_pCompositor->m_lastMonitor && !g_pCompositor->isWorkspaceSpecial(w->m_id))
onMonitor++;
}
if (onMonitor < 2 && !*PSWIPENEW)
return; // disallow swiping when there's 1 workspace on a monitor
beginWorkspaceSwipe();
}
void CInputManager::beginWorkspaceSwipe() {
const auto PWORKSPACE = g_pCompositor->m_lastMonitor->m_activeWorkspace;
Debug::log(LOG, "Starting a swipe from {}", PWORKSPACE->m_name);
m_activeSwipe.pWorkspaceBegin = PWORKSPACE;
m_activeSwipe.delta = 0;
m_activeSwipe.pMonitor = g_pCompositor->m_lastMonitor;
m_activeSwipe.avgSpeed = 0;
m_activeSwipe.speedPoints = 0;
if (PWORKSPACE->m_hasFullscreenWindow) {
for (auto const& ls : g_pCompositor->m_lastMonitor->m_layerSurfaceLayers[2]) {
*ls->m_alpha = 1.f;
}
}
}
void CInputManager::onSwipeEnd(IPointer::SSwipeEndEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("swipeEnd", e);
if (!m_activeSwipe.pWorkspaceBegin)
return; // no valid swipe
endWorkspaceSwipe();
}
void CInputManager::endWorkspaceSwipe() {
static auto PSWIPEPERC = CConfigValue<Hyprlang::FLOAT>("gestures:workspace_swipe_cancel_ratio");
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
static auto PSWIPEFORC = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_min_speed_to_force");
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
static auto PSWIPEUSER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_use_r");
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
const auto ANIMSTYLE = m_activeSwipe.pWorkspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
// commit
auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id;
auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id;
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc<int64_t>(1LL), sc<int64_t>(UINT32_MAX));
// If we've been swiping off the right end with PSWIPENEW enabled, there is
// no workspace there yet, and we need to choose an ID for a new one now.
if (workspaceIDRight <= m_activeSwipe.pWorkspaceBegin->m_id && *PSWIPENEW) {
workspaceIDRight = getWorkspaceIDNameFromString("r+1").id;
}
auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); // not guaranteed if PSWIPENEW || PSWIPENUMBER
auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); // not guaranteed if PSWIPENUMBER
const auto RENDEROFFSETMIDDLE = m_activeSwipe.pWorkspaceBegin->m_renderOffset->value();
const auto XDISTANCE = m_activeSwipe.pMonitor->m_size.x + *PWORKSPACEGAP;
const auto YDISTANCE = m_activeSwipe.pMonitor->m_size.y + *PWORKSPACEGAP;
PHLWORKSPACE pSwitchedTo = nullptr;
if ((abs(m_activeSwipe.delta) < SWIPEDISTANCE * *PSWIPEPERC && (*PSWIPEFORC == 0 || (*PSWIPEFORC != 0 && m_activeSwipe.avgSpeed < *PSWIPEFORC))) ||
abs(m_activeSwipe.delta) < 2) {
// revert
if (abs(m_activeSwipe.delta) < 2) {
if (PWORKSPACEL)
PWORKSPACEL->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
if (PWORKSPACER)
PWORKSPACER->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
} else {
if (m_activeSwipe.delta < 0) {
// to left
if (PWORKSPACEL) {
if (VERTANIMS)
*PWORKSPACEL->m_renderOffset = Vector2D{0.0, -YDISTANCE};
else
*PWORKSPACEL->m_renderOffset = Vector2D{-XDISTANCE, 0.0};
}
} else if (PWORKSPACER) {
// to right
if (VERTANIMS)
*PWORKSPACER->m_renderOffset = Vector2D{0.0, YDISTANCE};
else
*PWORKSPACER->m_renderOffset = Vector2D{XDISTANCE, 0.0};
}
*m_activeSwipe.pWorkspaceBegin->m_renderOffset = Vector2D();
}
pSwitchedTo = m_activeSwipe.pWorkspaceBegin;
} else if (m_activeSwipe.delta < 0) {
// switch to left
const auto RENDEROFFSET = PWORKSPACEL ? PWORKSPACEL->m_renderOffset->value() : Vector2D();
if (PWORKSPACEL)
m_activeSwipe.pMonitor->changeWorkspace(workspaceIDLeft);
else {
m_activeSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDLeft, m_activeSwipe.pMonitor->m_id));
PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
PWORKSPACEL->rememberPrevWorkspace(m_activeSwipe.pWorkspaceBegin);
}
PWORKSPACEL->m_renderOffset->setValue(RENDEROFFSET);
PWORKSPACEL->m_alpha->setValueAndWarp(1.f);
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE);
if (VERTANIMS)
*m_activeSwipe.pWorkspaceBegin->m_renderOffset = Vector2D(0.0, YDISTANCE);
else
*m_activeSwipe.pWorkspaceBegin->m_renderOffset = Vector2D(XDISTANCE, 0.0);
m_activeSwipe.pWorkspaceBegin->m_alpha->setValueAndWarp(1.f);
g_pInputManager->unconstrainMouse();
Debug::log(LOG, "Ended swipe to the left");
pSwitchedTo = PWORKSPACEL;
} else {
// switch to right
const auto RENDEROFFSET = PWORKSPACER ? PWORKSPACER->m_renderOffset->value() : Vector2D();
if (PWORKSPACER)
m_activeSwipe.pMonitor->changeWorkspace(workspaceIDRight);
else {
m_activeSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDRight, m_activeSwipe.pMonitor->m_id));
PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
PWORKSPACER->rememberPrevWorkspace(m_activeSwipe.pWorkspaceBegin);
}
PWORKSPACER->m_renderOffset->setValue(RENDEROFFSET);
PWORKSPACER->m_alpha->setValueAndWarp(1.f);
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE);
if (VERTANIMS)
*m_activeSwipe.pWorkspaceBegin->m_renderOffset = Vector2D(0.0, -YDISTANCE);
else
*m_activeSwipe.pWorkspaceBegin->m_renderOffset = Vector2D(-XDISTANCE, 0.0);
m_activeSwipe.pWorkspaceBegin->m_alpha->setValueAndWarp(1.f);
g_pInputManager->unconstrainMouse();
Debug::log(LOG, "Ended swipe to the right");
pSwitchedTo = PWORKSPACER;
}
m_activeSwipe.pWorkspaceBegin->rememberPrevWorkspace(pSwitchedTo);
g_pHyprRenderer->damageMonitor(m_activeSwipe.pMonitor.lock());
if (PWORKSPACEL)
PWORKSPACEL->m_forceRendering = false;
if (PWORKSPACER)
PWORKSPACER->m_forceRendering = false;
m_activeSwipe.pWorkspaceBegin->m_forceRendering = false;
m_activeSwipe.pWorkspaceBegin = nullptr;
m_activeSwipe.initialDirection = 0;
g_pInputManager->refocus();
// apply alpha
for (auto const& ls : g_pCompositor->m_lastMonitor->m_layerSurfaceLayers[2]) {
*ls->m_alpha = pSwitchedTo->m_hasFullscreenWindow && pSwitchedTo->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
}
}
void CInputManager::onSwipeUpdate(IPointer::SSwipeUpdateEvent e) {
EMIT_HOOK_EVENT_CANCELLABLE("swipeUpdate", e);
if (!m_activeSwipe.pWorkspaceBegin)
return;
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_invert");
const auto ANIMSTYLE = m_activeSwipe.pWorkspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
const double delta = m_activeSwipe.delta + (VERTANIMS ? (*PSWIPEINVR ? -e.delta.y : e.delta.y) : (*PSWIPEINVR ? -e.delta.x : e.delta.x));
updateWorkspaceSwipe(delta);
}
void CInputManager::updateWorkspaceSwipe(double delta) {
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
static auto PSWIPEDIRLOCK = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_direction_lock");
static auto PSWIPEDIRLOCKTHRESHOLD = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_direction_lock_threshold");
static auto PSWIPEFOREVER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_forever");
static auto PSWIPEUSER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_use_r");
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc<int64_t>(1LL), sc<int64_t>(UINT32_MAX));
const auto XDISTANCE = m_activeSwipe.pMonitor->m_size.x + *PWORKSPACEGAP;
const auto YDISTANCE = m_activeSwipe.pMonitor->m_size.y + *PWORKSPACEGAP;
const auto ANIMSTYLE = m_activeSwipe.pWorkspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
const double d = m_activeSwipe.delta - delta;
m_activeSwipe.delta = delta;
m_activeSwipe.avgSpeed = (m_activeSwipe.avgSpeed * m_activeSwipe.speedPoints + abs(d)) / (m_activeSwipe.speedPoints + 1);
m_activeSwipe.speedPoints++;
auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id;
auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id;
if ((workspaceIDLeft == WORKSPACE_INVALID || workspaceIDRight == WORKSPACE_INVALID || workspaceIDLeft == m_activeSwipe.pWorkspaceBegin->m_id) && !*PSWIPENEW) {
m_activeSwipe.pWorkspaceBegin = nullptr; // invalidate the swipe
return;
}
m_activeSwipe.pWorkspaceBegin->m_forceRendering = true;
m_activeSwipe.delta = std::clamp(m_activeSwipe.delta, sc<double>(-SWIPEDISTANCE), sc<double>(SWIPEDISTANCE));
if ((m_activeSwipe.pWorkspaceBegin->m_id == workspaceIDLeft && *PSWIPENEW && (m_activeSwipe.delta < 0)) ||
(m_activeSwipe.delta > 0 && m_activeSwipe.pWorkspaceBegin->getWindows() == 0 && workspaceIDRight <= m_activeSwipe.pWorkspaceBegin->m_id) ||
(m_activeSwipe.delta < 0 && m_activeSwipe.pWorkspaceBegin->m_id <= workspaceIDLeft)) {
m_activeSwipe.delta = 0;
g_pHyprRenderer->damageMonitor(m_activeSwipe.pMonitor.lock());
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, 0.0));
return;
}
if (*PSWIPEDIRLOCK) {
if (m_activeSwipe.initialDirection != 0 && m_activeSwipe.initialDirection != (m_activeSwipe.delta < 0 ? -1 : 1))
m_activeSwipe.delta = 0;
else if (m_activeSwipe.initialDirection == 0 && abs(m_activeSwipe.delta) > *PSWIPEDIRLOCKTHRESHOLD)
m_activeSwipe.initialDirection = m_activeSwipe.delta < 0 ? -1 : 1;
}
if (m_activeSwipe.delta < 0) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (workspaceIDLeft > m_activeSwipe.pWorkspaceBegin->m_id || !PWORKSPACE) {
if (*PSWIPENEW) {
g_pHyprRenderer->damageMonitor(m_activeSwipe.pMonitor.lock());
if (VERTANIMS)
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_activeSwipe.delta) / SWIPEDISTANCE) * YDISTANCE));
else
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_activeSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
m_activeSwipe.pWorkspaceBegin->updateWindowDecos();
return;
}
m_activeSwipe.delta = 0;
return;
}
PWORKSPACE->m_forceRendering = true;
PWORKSPACE->m_alpha->setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_activeSwipe.pWorkspaceBegin->m_id) {
const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (PWORKSPACER) {
PWORKSPACER->m_forceRendering = false;
PWORKSPACER->m_alpha->setValueAndWarp(0.f);
}
}
if (VERTANIMS) {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_activeSwipe.delta) / SWIPEDISTANCE) * YDISTANCE - YDISTANCE));
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_activeSwipe.delta) / SWIPEDISTANCE) * YDISTANCE));
} else {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_activeSwipe.delta) / SWIPEDISTANCE) * XDISTANCE - XDISTANCE, 0.0));
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_activeSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
}
PWORKSPACE->updateWindowDecos();
} else {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (workspaceIDRight < m_activeSwipe.pWorkspaceBegin->m_id || !PWORKSPACE) {
if (*PSWIPENEW) {
g_pHyprRenderer->damageMonitor(m_activeSwipe.pMonitor.lock());
if (VERTANIMS)
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_activeSwipe.delta) / SWIPEDISTANCE) * YDISTANCE));
else
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_activeSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
m_activeSwipe.pWorkspaceBegin->updateWindowDecos();
return;
}
m_activeSwipe.delta = 0;
return;
}
PWORKSPACE->m_forceRendering = true;
PWORKSPACE->m_alpha->setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_activeSwipe.pWorkspaceBegin->m_id) {
const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (PWORKSPACEL) {
PWORKSPACEL->m_forceRendering = false;
PWORKSPACEL->m_alpha->setValueAndWarp(0.f);
}
}
if (VERTANIMS) {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_activeSwipe.delta) / SWIPEDISTANCE) * YDISTANCE + YDISTANCE));
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_activeSwipe.delta) / SWIPEDISTANCE) * YDISTANCE));
} else {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_activeSwipe.delta) / SWIPEDISTANCE) * XDISTANCE + XDISTANCE, 0.0));
m_activeSwipe.pWorkspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_activeSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
}
PWORKSPACE->updateWindowDecos();
}
g_pHyprRenderer->damageMonitor(m_activeSwipe.pMonitor.lock());
m_activeSwipe.pWorkspaceBegin->updateWindowDecos();
if (*PSWIPEFOREVER) {
if (abs(m_activeSwipe.delta) >= SWIPEDISTANCE) {
onSwipeEnd({});
beginWorkspaceSwipe();
}
}
}

View file

@ -7,9 +7,10 @@
#include "../../helpers/Monitor.hpp"
#include "../../devices/ITouch.hpp"
#include "../SeatManager.hpp"
#include "managers/AnimationManager.hpp"
#include "managers/animation/AnimationManager.hpp"
#include "../HookSystemManager.hpp"
#include "debug/Log.hpp"
#include "UnifiedWorkspaceSwipeGesture.hpp"
void CInputManager::onTouchDown(ITouch::SDownEvent e) {
m_lastInputTouch = true;
@ -39,7 +40,7 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
}
// Don't propagate new touches when a workspace swipe is in progress.
if (m_activeSwipe.pWorkspaceBegin) {
if (g_pUnifiedWorkspaceSwipe->isGestureInProgress()) {
return;
// TODO: Don't swipe if you touched a floating window.
} else if (*PSWIPETOUCH && (m_foundLSToFocus.expired() || m_foundLSToFocus->m_layer <= 1) && !g_pSessionLockManager->isSessionLocked()) {
@ -50,13 +51,13 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
const double TARGETRIGHT = 1 - (((VERTANIMS ? gapsOut.m_bottom : gapsOut.m_right) + *PBORDERSIZE) / (VERTANIMS ? PMONITOR->m_size.y : PMONITOR->m_size.x));
const double POSITION = (VERTANIMS ? e.pos.y : e.pos.x);
if (POSITION < TARGETLEFT || POSITION > TARGETRIGHT) {
beginWorkspaceSwipe();
m_activeSwipe.touch_id = e.touchID;
g_pUnifiedWorkspaceSwipe->begin();
g_pUnifiedWorkspaceSwipe->m_touchID = e.touchID;
// Set the initial direction based on which edge you started from
if (POSITION > 0.5)
m_activeSwipe.initialDirection = *PSWIPEINVR ? -1 : 1;
g_pUnifiedWorkspaceSwipe->m_initialDirection = *PSWIPEINVR ? -1 : 1;
else
m_activeSwipe.initialDirection = *PSWIPEINVR ? 1 : -1;
g_pUnifiedWorkspaceSwipe->m_initialDirection = *PSWIPEINVR ? 1 : -1;
return;
}
}
@ -109,10 +110,10 @@ void CInputManager::onTouchUp(ITouch::SUpEvent e) {
m_lastInputTouch = true;
EMIT_HOOK_EVENT_CANCELLABLE("touchUp", e);
if (m_activeSwipe.pWorkspaceBegin) {
if (g_pUnifiedWorkspaceSwipe->isGestureInProgress()) {
// If there was a swipe from this finger, end it.
if (e.touchID == m_activeSwipe.touch_id)
endWorkspaceSwipe();
if (e.touchID == g_pUnifiedWorkspaceSwipe->m_touchID)
g_pUnifiedWorkspaceSwipe->end();
return;
}
@ -124,30 +125,30 @@ void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
m_lastInputTouch = true;
EMIT_HOOK_EVENT_CANCELLABLE("touchMove", e);
if (m_activeSwipe.pWorkspaceBegin) {
if (g_pUnifiedWorkspaceSwipe->isGestureInProgress()) {
// Do nothing if this is using a different finger.
if (e.touchID != m_activeSwipe.touch_id)
if (e.touchID != g_pUnifiedWorkspaceSwipe->m_touchID)
return;
const auto ANIMSTYLE = m_activeSwipe.pWorkspaceBegin->m_renderOffset->getStyle();
const auto ANIMSTYLE = g_pUnifiedWorkspaceSwipe->m_workspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_touch_invert");
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc<int64_t>(1LL), sc<int64_t>(UINT32_MAX));
// Handle the workspace swipe if there is one
if (m_activeSwipe.initialDirection == -1) {
if (g_pUnifiedWorkspaceSwipe->m_initialDirection == -1) {
if (*PSWIPEINVR)
// go from 0 to -SWIPEDISTANCE
updateWorkspaceSwipe(SWIPEDISTANCE * ((VERTANIMS ? e.pos.y : e.pos.x) - 1));
g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * ((VERTANIMS ? e.pos.y : e.pos.x) - 1));
else
// go from 0 to -SWIPEDISTANCE
updateWorkspaceSwipe(SWIPEDISTANCE * (-1 * (VERTANIMS ? e.pos.y : e.pos.x)));
g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * (-1 * (VERTANIMS ? e.pos.y : e.pos.x)));
} else if (*PSWIPEINVR)
// go from 0 to SWIPEDISTANCE
updateWorkspaceSwipe(SWIPEDISTANCE * (VERTANIMS ? e.pos.y : e.pos.x));
g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * (VERTANIMS ? e.pos.y : e.pos.x));
else
// go from 0 to SWIPEDISTANCE
updateWorkspaceSwipe(SWIPEDISTANCE * (1 - (VERTANIMS ? e.pos.y : e.pos.x)));
g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * (1 - (VERTANIMS ? e.pos.y : e.pos.x)));
return;
}
if (m_touchData.touchFocusLockSurface) {

View file

@ -0,0 +1,313 @@
#include "UnifiedWorkspaceSwipeGesture.hpp"
#include "../../Compositor.hpp"
#include "../../render/Renderer.hpp"
#include "InputManager.hpp"
bool CUnifiedWorkspaceSwipeGesture::isGestureInProgress() {
return m_workspaceBegin;
}
void CUnifiedWorkspaceSwipeGesture::begin() {
if (isGestureInProgress())
return;
const auto PWORKSPACE = g_pCompositor->m_lastMonitor->m_activeWorkspace;
Debug::log(LOG, "CUnifiedWorkspaceSwipeGesture::begin: Starting a swipe from {}", PWORKSPACE->m_name);
m_workspaceBegin = PWORKSPACE;
m_delta = 0;
m_monitor = g_pCompositor->m_lastMonitor;
m_avgSpeed = 0;
m_speedPoints = 0;
if (PWORKSPACE->m_hasFullscreenWindow) {
for (auto const& ls : g_pCompositor->m_lastMonitor->m_layerSurfaceLayers[2]) {
*ls->m_alpha = 1.f;
}
}
}
void CUnifiedWorkspaceSwipeGesture::update(double delta) {
if (!isGestureInProgress())
return;
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
static auto PSWIPEDIRLOCK = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_direction_lock");
static auto PSWIPEDIRLOCKTHRESHOLD = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_direction_lock_threshold");
static auto PSWIPEFOREVER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_forever");
static auto PSWIPEUSER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_use_r");
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc<int64_t>(1LL), sc<int64_t>(UINT32_MAX));
const auto XDISTANCE = m_monitor->m_size.x + *PWORKSPACEGAP;
const auto YDISTANCE = m_monitor->m_size.y + *PWORKSPACEGAP;
const auto ANIMSTYLE = m_workspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
const double d = m_delta - delta;
m_delta = delta;
m_avgSpeed = (m_avgSpeed * m_speedPoints + abs(d)) / (m_speedPoints + 1);
m_speedPoints++;
auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id;
auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id;
if ((workspaceIDLeft == WORKSPACE_INVALID || workspaceIDRight == WORKSPACE_INVALID || workspaceIDLeft == m_workspaceBegin->m_id) && !*PSWIPENEW) {
m_workspaceBegin = nullptr; // invalidate the swipe
return;
}
m_workspaceBegin->m_forceRendering = true;
m_delta = std::clamp(m_delta, sc<double>(-SWIPEDISTANCE), sc<double>(SWIPEDISTANCE));
if ((m_workspaceBegin->m_id == workspaceIDLeft && *PSWIPENEW && (m_delta < 0)) ||
(m_delta > 0 && m_workspaceBegin->getWindows() == 0 && workspaceIDRight <= m_workspaceBegin->m_id) || (m_delta < 0 && m_workspaceBegin->m_id <= workspaceIDLeft)) {
m_delta = 0;
g_pHyprRenderer->damageMonitor(m_monitor.lock());
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, 0.0));
return;
}
if (*PSWIPEDIRLOCK) {
if (m_initialDirection != 0 && m_initialDirection != (m_delta < 0 ? -1 : 1))
m_delta = 0;
else if (m_initialDirection == 0 && abs(m_delta) > *PSWIPEDIRLOCKTHRESHOLD)
m_initialDirection = m_delta < 0 ? -1 : 1;
}
if (m_delta < 0) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (workspaceIDLeft > m_workspaceBegin->m_id || !PWORKSPACE) {
if (*PSWIPENEW) {
g_pHyprRenderer->damageMonitor(m_monitor.lock());
if (VERTANIMS)
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
else
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
m_workspaceBegin->updateWindowDecos();
return;
}
m_delta = 0;
return;
}
PWORKSPACE->m_forceRendering = true;
PWORKSPACE->m_alpha->setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_workspaceBegin->m_id) {
const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (PWORKSPACER) {
PWORKSPACER->m_forceRendering = false;
PWORKSPACER->m_alpha->setValueAndWarp(0.f);
}
}
if (VERTANIMS) {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE - YDISTANCE));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
} else {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE - XDISTANCE, 0.0));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
}
PWORKSPACE->updateWindowDecos();
} else {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDRight);
if (workspaceIDRight < m_workspaceBegin->m_id || !PWORKSPACE) {
if (*PSWIPENEW) {
g_pHyprRenderer->damageMonitor(m_monitor.lock());
if (VERTANIMS)
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
else
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
m_workspaceBegin->updateWindowDecos();
return;
}
m_delta = 0;
return;
}
PWORKSPACE->m_forceRendering = true;
PWORKSPACE->m_alpha->setValueAndWarp(1.f);
if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_workspaceBegin->m_id) {
const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
if (PWORKSPACEL) {
PWORKSPACEL->m_forceRendering = false;
PWORKSPACEL->m_alpha->setValueAndWarp(0.f);
}
}
if (VERTANIMS) {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE + YDISTANCE));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE));
} else {
PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE + XDISTANCE, 0.0));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0));
}
PWORKSPACE->updateWindowDecos();
}
g_pHyprRenderer->damageMonitor(m_monitor.lock());
m_workspaceBegin->updateWindowDecos();
if (*PSWIPEFOREVER) {
if (abs(m_delta) >= SWIPEDISTANCE) {
end();
begin();
}
}
}
void CUnifiedWorkspaceSwipeGesture::end() {
if (!isGestureInProgress())
return;
static auto PSWIPEPERC = CConfigValue<Hyprlang::FLOAT>("gestures:workspace_swipe_cancel_ratio");
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
static auto PSWIPEFORC = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_min_speed_to_force");
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
static auto PSWIPEUSER = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_use_r");
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
const auto ANIMSTYLE = m_workspaceBegin->m_renderOffset->getStyle();
const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert");
// commit
auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id;
auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id;
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc<int64_t>(1LL), sc<int64_t>(UINT32_MAX));
// If we've been swiping off the right end with PSWIPENEW enabled, there is
// no workspace there yet, and we need to choose an ID for a new one now.
if (workspaceIDRight <= m_workspaceBegin->m_id && *PSWIPENEW)
workspaceIDRight = getWorkspaceIDNameFromString("r+1").id;
auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); // not guaranteed if PSWIPENEW || PSWIPENUMBER
auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); // not guaranteed if PSWIPENUMBER
const auto RENDEROFFSETMIDDLE = m_workspaceBegin->m_renderOffset->value();
const auto XDISTANCE = m_monitor->m_size.x + *PWORKSPACEGAP;
const auto YDISTANCE = m_monitor->m_size.y + *PWORKSPACEGAP;
PHLWORKSPACE pSwitchedTo = nullptr;
if ((abs(m_delta) < SWIPEDISTANCE * *PSWIPEPERC && (*PSWIPEFORC == 0 || (*PSWIPEFORC != 0 && m_avgSpeed < *PSWIPEFORC))) || abs(m_delta) < 2) {
// revert
if (abs(m_delta) < 2) {
if (PWORKSPACEL)
PWORKSPACEL->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
if (PWORKSPACER)
PWORKSPACER->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
} else {
if (m_delta < 0) {
// to left
if (PWORKSPACEL) {
if (VERTANIMS)
*PWORKSPACEL->m_renderOffset = Vector2D{0.0, -YDISTANCE};
else
*PWORKSPACEL->m_renderOffset = Vector2D{-XDISTANCE, 0.0};
}
} else if (PWORKSPACER) {
// to right
if (VERTANIMS)
*PWORKSPACER->m_renderOffset = Vector2D{0.0, YDISTANCE};
else
*PWORKSPACER->m_renderOffset = Vector2D{XDISTANCE, 0.0};
}
*m_workspaceBegin->m_renderOffset = Vector2D();
}
pSwitchedTo = m_workspaceBegin;
} else if (m_delta < 0) {
// switch to left
const auto RENDEROFFSET = PWORKSPACEL ? PWORKSPACEL->m_renderOffset->value() : Vector2D();
if (PWORKSPACEL)
m_monitor->changeWorkspace(workspaceIDLeft);
else {
m_monitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDLeft, m_monitor->m_id));
PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
PWORKSPACEL->rememberPrevWorkspace(m_workspaceBegin);
}
PWORKSPACEL->m_renderOffset->setValue(RENDEROFFSET);
PWORKSPACEL->m_alpha->setValueAndWarp(1.f);
m_workspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE);
if (VERTANIMS)
*m_workspaceBegin->m_renderOffset = Vector2D(0.0, YDISTANCE);
else
*m_workspaceBegin->m_renderOffset = Vector2D(XDISTANCE, 0.0);
m_workspaceBegin->m_alpha->setValueAndWarp(1.f);
g_pInputManager->unconstrainMouse();
Debug::log(LOG, "Ended swipe to the left");
pSwitchedTo = PWORKSPACEL;
} else {
// switch to right
const auto RENDEROFFSET = PWORKSPACER ? PWORKSPACER->m_renderOffset->value() : Vector2D();
if (PWORKSPACER)
m_monitor->changeWorkspace(workspaceIDRight);
else {
m_monitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDRight, m_monitor->m_id));
PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
PWORKSPACER->rememberPrevWorkspace(m_workspaceBegin);
}
PWORKSPACER->m_renderOffset->setValue(RENDEROFFSET);
PWORKSPACER->m_alpha->setValueAndWarp(1.f);
m_workspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE);
if (VERTANIMS)
*m_workspaceBegin->m_renderOffset = Vector2D(0.0, -YDISTANCE);
else
*m_workspaceBegin->m_renderOffset = Vector2D(-XDISTANCE, 0.0);
m_workspaceBegin->m_alpha->setValueAndWarp(1.f);
g_pInputManager->unconstrainMouse();
Debug::log(LOG, "Ended swipe to the right");
pSwitchedTo = PWORKSPACER;
}
m_workspaceBegin->rememberPrevWorkspace(pSwitchedTo);
g_pHyprRenderer->damageMonitor(m_monitor.lock());
if (PWORKSPACEL)
PWORKSPACEL->m_forceRendering = false;
if (PWORKSPACER)
PWORKSPACER->m_forceRendering = false;
m_workspaceBegin->m_forceRendering = false;
m_workspaceBegin = nullptr;
m_initialDirection = 0;
g_pInputManager->refocus();
// apply alpha
for (auto const& ls : g_pCompositor->m_lastMonitor->m_layerSurfaceLayers[2]) {
*ls->m_alpha = pSwitchedTo->m_hasFullscreenWindow && pSwitchedTo->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
}
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "../../helpers/memory/Memory.hpp"
#include "../../desktop/DesktopTypes.hpp"
class CUnifiedWorkspaceSwipeGesture {
public:
void begin();
void update(double delta);
void end();
bool isGestureInProgress();
private:
PHLWORKSPACE m_workspaceBegin = nullptr;
PHLMONITORREF m_monitor;
double m_delta = 0;
int m_initialDirection = 0;
float m_avgSpeed = 0;
int m_speedPoints = 0;
int m_touchID = 0;
friend class CWorkspaceSwipeGesture;
friend class CInputManager;
};
inline UP<CUnifiedWorkspaceSwipeGesture> g_pUnifiedWorkspaceSwipe = makeUnique<CUnifiedWorkspaceSwipeGesture>();

View file

@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
enum eTrackpadGestureDirection : uint8_t {
TRACKPAD_GESTURE_DIR_NONE = 0,
TRACKPAD_GESTURE_DIR_SWIPE,
TRACKPAD_GESTURE_DIR_LEFT,
TRACKPAD_GESTURE_DIR_RIGHT,
TRACKPAD_GESTURE_DIR_UP,
TRACKPAD_GESTURE_DIR_DOWN,
TRACKPAD_GESTURE_DIR_VERTICAL,
TRACKPAD_GESTURE_DIR_HORIZONTAL,
TRACKPAD_GESTURE_DIR_PINCH,
TRACKPAD_GESTURE_DIR_PINCH_OUT,
TRACKPAD_GESTURE_DIR_PINCH_IN,
};

View file

@ -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();
}

View file

@ -0,0 +1,43 @@
#pragma once
#include "../../../devices/IPointer.hpp"
#include "gestures/ITrackpadGesture.hpp"
#include "GestureTypes.hpp"
#include <vector>
#include <expected>
class CTrackpadGestures {
public:
void clearGestures();
std::expected<void, std::string> addGesture(UP<ITrackpadGesture>&& gesture, size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, float deltaScale);
void gestureBegin(const IPointer::SSwipeBeginEvent& e);
void gestureUpdate(const IPointer::SSwipeUpdateEvent& e);
void gestureEnd(const IPointer::SSwipeEndEvent& e);
void gestureBegin(const IPointer::SPinchBeginEvent& e);
void gestureUpdate(const IPointer::SPinchUpdateEvent& e);
void gestureEnd(const IPointer::SPinchEndEvent& e);
eTrackpadGestureDirection dirForString(const std::string_view& s);
const char* stringForDir(eTrackpadGestureDirection dir);
private:
struct SGestureData {
UP<ITrackpadGesture> gesture;
size_t fingerCount = 0;
uint32_t modMask = 0;
eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE; // configured dir
float deltaScale = 1.F;
eTrackpadGestureDirection currentDirection = TRACKPAD_GESTURE_DIR_NONE; // actual dir of that select swipe
};
std::vector<SP<SGestureData>> m_gestures;
SP<SGestureData> m_activeGesture = nullptr;
bool m_gestureFindFailed = false;
};
inline UP<CTrackpadGestures> g_pTrackpadGestures = makeUnique<CTrackpadGestures>();

View file

@ -0,0 +1,145 @@
#include "CloseGesture.hpp"
#include "../../../../Compositor.hpp"
#include "../../../../managers/LayoutManager.hpp"
#include "../../../../managers/animation/DesktopAnimationManager.hpp"
#include "../../../../render/Renderer.hpp"
#include "../../../../managers/eventLoop/EventLoopManager.hpp"
#include "../../../../managers/eventLoop/EventLoopTimer.hpp"
#include "../../../../config/ConfigValue.hpp"
constexpr const float MAX_DISTANCE = 200.F;
static std::vector<SP<CEventLoopTimer>> trackpadCloseTimers;
//
static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) {
return Vector2D{
from.x + ((to.x - from.x) * t),
from.y + ((to.y - from.y) * t),
};
}
static float lerpVal(const float& from, const float& to, const float& t) {
return from + ((to - from) * t);
}
void CCloseTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
ITrackpadGesture::begin(e);
m_window = g_pCompositor->m_lastWindow;
if (!m_window)
return;
m_alphaFrom = m_window->m_alpha->goal();
m_posFrom = m_window->m_realPosition->goal();
m_sizeFrom = m_window->m_realSize->goal();
g_pDesktopAnimationManager->startAnimation(m_window.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT, true);
*m_window->m_alpha = 0.f;
m_alphaTo = m_window->m_alpha->goal();
m_posTo = m_window->m_realPosition->goal();
m_sizeTo = m_window->m_realSize->goal();
m_window->m_alpha->setValueAndWarp(m_alphaFrom);
m_window->m_realPosition->setValueAndWarp(m_posFrom);
m_window->m_realSize->setValueAndWarp(m_sizeFrom);
m_lastDelta = 0.F;
}
void CCloseTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
if (!m_window)
return;
g_pHyprRenderer->damageWindow(m_window.lock());
m_lastDelta += distance(e);
const auto FADEPERCENT = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
m_window->m_alpha->setValueAndWarp(lerpVal(m_alphaFrom, m_alphaTo, FADEPERCENT));
m_window->m_realPosition->setValueAndWarp(lerpVal(m_posFrom, m_posTo, FADEPERCENT));
m_window->m_realSize->setValueAndWarp(lerpVal(m_sizeFrom, m_sizeTo, FADEPERCENT));
g_pDecorationPositioner->onWindowUpdate(m_window.lock());
g_pHyprRenderer->damageWindow(m_window.lock());
}
void CCloseTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
static const auto PTIMEOUT = CConfigValue<Hyprlang::INT>("gestures:close_max_timeout");
if (!m_window)
return;
const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
if (COMPLETION < 0.2F) {
// revert the animation
g_pHyprRenderer->damageWindow(m_window.lock());
*m_window->m_alpha = m_alphaFrom;
*m_window->m_realPosition = m_posFrom;
*m_window->m_realSize = m_sizeFrom;
return;
}
// commence. Close the window and restore our current state to avoid a harsh anim
const auto CURRENT_ALPHA = m_window->m_alpha->value();
const auto CURRENT_POS = m_window->m_realPosition->value();
const auto CURRENT_SIZE = m_window->m_realSize->value();
g_pCompositor->closeWindow(m_window.lock());
m_window->m_alpha->setValueAndWarp(CURRENT_ALPHA);
m_window->m_realPosition->setValueAndWarp(CURRENT_POS);
m_window->m_realSize->setValueAndWarp(CURRENT_SIZE);
// this is a kinda hack, but oh well.
m_window->m_realPosition->setCallbackOnBegin(
[CURRENT_POS, window = m_window](auto) {
if (!window || !window->m_isMapped)
return;
window->m_realPosition->setValueAndWarp(CURRENT_POS);
},
false);
m_window->m_realSize->setCallbackOnBegin(
[CURRENT_SIZE, window = m_window](auto) {
if (!window || !window->m_isMapped)
return;
window->m_realSize->setValueAndWarp(CURRENT_SIZE);
},
false);
// we give windows 2s to close. If they don't, pop them back in.
auto timer = makeShared<CEventLoopTimer>(
std::chrono::milliseconds(*PTIMEOUT),
[window = m_window](SP<CEventLoopTimer> self, void* data) {
std::erase(trackpadCloseTimers, self);
// if after 2 seconds the window is still alive and mapped, we revert our changes.
if (!window)
return;
window->m_realPosition->setCallbackOnBegin(nullptr);
window->m_realSize->setCallbackOnBegin(nullptr);
if (!window->m_isMapped)
return;
g_pLayoutManager->getCurrentLayout()->recalculateWindow(window.lock());
g_pCompositor->updateWindowAnimatedDecorationValues(window.lock());
window->sendWindowSize(true);
*window->m_alpha = 1.F;
},
nullptr);
trackpadCloseTimers.emplace_back(timer);
g_pEventLoopManager->addTimer(timer);
m_window.reset();
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "ITrackpadGesture.hpp"
#include "../../../../desktop/DesktopTypes.hpp"
class CCloseTrackpadGesture : public ITrackpadGesture {
public:
CCloseTrackpadGesture() = default;
virtual ~CCloseTrackpadGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
private:
PHLWINDOWREF m_window;
Vector2D m_posFrom, m_posTo, m_sizeFrom, m_sizeTo;
float m_alphaFrom = 0.F, m_alphaTo = 0.F;
float m_lastDelta = 0.F;
};

View file

@ -0,0 +1,22 @@
#include "DispatcherGesture.hpp"
#include "../../../../managers/KeybindManager.hpp"
CDispatcherTrackpadGesture::CDispatcherTrackpadGesture(const std::string& dispatcher, const std::string& data) : m_dispatcher(dispatcher), m_data(data) {
;
}
void CDispatcherTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
; // intentionally blank
}
void CDispatcherTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
; // intentionally blank
}
void CDispatcherTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
if (!g_pKeybindManager->m_dispatchers.contains(m_dispatcher))
return;
g_pKeybindManager->m_dispatchers.at(m_dispatcher)(m_data);
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "ITrackpadGesture.hpp"
class CDispatcherTrackpadGesture : public ITrackpadGesture {
public:
CDispatcherTrackpadGesture(const std::string& dispatcher, const std::string& data);
virtual ~CDispatcherTrackpadGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
private:
std::string m_dispatcher, m_data;
};

View file

@ -0,0 +1,88 @@
#include "FloatGesture.hpp"
#include "../../../../Compositor.hpp"
#include "../../../../managers/LayoutManager.hpp"
#include "../../../../render/Renderer.hpp"
constexpr const float MAX_DISTANCE = 250.F;
//
static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) {
return Vector2D{
from.x + ((to.x - from.x) * t),
from.y + ((to.y - from.y) * t),
};
}
CFloatTrackpadGesture::CFloatTrackpadGesture(const std::string_view& data) {
std::string lc = std::string{data};
std::ranges::transform(lc, lc.begin(), ::tolower);
if (lc.starts_with("float"))
m_mode = FLOAT_MODE_FLOAT;
else if (lc.starts_with("tile"))
m_mode == FLOAT_MODE_TILE;
else
m_mode = FLOAT_MODE_TOGGLE;
}
void CFloatTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
ITrackpadGesture::begin(e);
m_window = g_pCompositor->m_lastWindow;
if (!m_window)
return;
if ((m_window->m_isFloating && m_mode == FLOAT_MODE_FLOAT) || (!m_window->m_isFloating && m_mode == FLOAT_MODE_TILE)) {
m_window.reset();
return;
}
m_window->m_isFloating = !m_window->m_isFloating;
g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(m_window.lock());
m_posFrom = m_window->m_realPosition->begun();
m_sizeFrom = m_window->m_realSize->begun();
m_posTo = m_window->m_realPosition->goal();
m_sizeTo = m_window->m_realSize->goal();
m_lastDelta = 0.F;
}
void CFloatTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
if (!m_window)
return;
g_pHyprRenderer->damageWindow(m_window.lock());
m_lastDelta += distance(e);
const auto FADEPERCENT = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
m_window->m_realPosition->setValueAndWarp(lerpVal(m_posFrom, m_posTo, FADEPERCENT));
m_window->m_realSize->setValueAndWarp(lerpVal(m_sizeFrom, m_sizeTo, FADEPERCENT));
g_pDecorationPositioner->onWindowUpdate(m_window.lock());
g_pHyprRenderer->damageWindow(m_window.lock());
}
void CFloatTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
if (!m_window)
return;
const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
if (COMPLETION < 0.2F) {
// revert the animation
g_pHyprRenderer->damageWindow(m_window.lock());
m_window->m_isFloating = !m_window->m_isFloating;
g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(m_window.lock());
return;
}
*m_window->m_realPosition = m_posTo;
*m_window->m_realSize = m_sizeTo;
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "ITrackpadGesture.hpp"
#include "../../../../desktop/DesktopTypes.hpp"
class CFloatTrackpadGesture : public ITrackpadGesture {
public:
CFloatTrackpadGesture(const std::string_view& mode);
virtual ~CFloatTrackpadGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
private:
PHLWINDOWREF m_window;
Vector2D m_posFrom, m_posTo, m_sizeFrom, m_sizeTo;
float m_lastDelta = 0;
enum eMode : uint8_t {
FLOAT_MODE_TOGGLE = 0,
FLOAT_MODE_FLOAT,
FLOAT_MODE_TILE,
};
eMode m_mode = FLOAT_MODE_TOGGLE;
};

View file

@ -0,0 +1,97 @@
#include "FullscreenGesture.hpp"
#include "../../../../Compositor.hpp"
#include "../../../../render/Renderer.hpp"
#include "../../../animation/DesktopAnimationManager.hpp"
constexpr const float MAX_DISTANCE = 250.F;
//
static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) {
return Vector2D{
from.x + ((to.x - from.x) * t),
from.y + ((to.y - from.y) * t),
};
}
CFullscreenTrackpadGesture::CFullscreenTrackpadGesture(const std::string_view& mode) {
std::string lc = std::string{mode};
std::ranges::transform(lc, lc.begin(), ::tolower);
if (lc.starts_with("fullscreen"))
m_mode = MODE_FULLSCREEN;
else if (lc.starts_with("maximize"))
m_mode == MODE_MAXIMIZE;
else
m_mode = MODE_FULLSCREEN;
}
void CFullscreenTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
ITrackpadGesture::begin(e);
m_window = g_pCompositor->m_lastWindow;
if (!m_window)
return;
m_posFrom = m_window->m_realPosition->goal();
m_sizeFrom = m_window->m_realSize->goal();
m_originalMode = m_window->m_fullscreenState.internal;
g_pCompositor->setWindowFullscreenInternal(m_window.lock(), m_window->m_fullscreenState.internal == FSMODE_NONE ? fsModeForMode(m_mode) : FSMODE_NONE);
m_posTo = m_window->m_realPosition->goal();
m_sizeTo = m_window->m_realSize->goal();
m_lastDelta = 0.F;
}
void CFullscreenTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
if (!m_window)
return;
g_pHyprRenderer->damageWindow(m_window.lock());
m_lastDelta += distance(e);
const auto FADEPERCENT = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
m_window->m_realPosition->setValueAndWarp(lerpVal(m_posFrom, m_posTo, FADEPERCENT));
m_window->m_realSize->setValueAndWarp(lerpVal(m_sizeFrom, m_sizeTo, FADEPERCENT));
g_pDesktopAnimationManager->overrideFullscreenFadeAmount(m_window->m_workspace, m_originalMode == FSMODE_NONE ? 1.F - FADEPERCENT : FADEPERCENT, m_window.lock());
g_pDecorationPositioner->onWindowUpdate(m_window.lock());
g_pHyprRenderer->damageWindow(m_window.lock());
}
void CFullscreenTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
if (!m_window)
return;
const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
if (COMPLETION < 0.2F) {
// revert the animation
g_pHyprRenderer->damageWindow(m_window.lock());
m_window->m_isFloating = !m_window->m_isFloating;
g_pDesktopAnimationManager->overrideFullscreenFadeAmount(m_window->m_workspace, m_originalMode == FSMODE_NONE ? 1.F : 0.F, m_window.lock());
g_pCompositor->setWindowFullscreenInternal(m_window.lock(), m_window->m_fullscreenState.internal == FSMODE_NONE ? m_originalMode : FSMODE_NONE);
return;
}
*m_window->m_realPosition = m_posTo;
*m_window->m_realSize = m_sizeTo;
g_pDesktopAnimationManager->overrideFullscreenFadeAmount(m_window->m_workspace, m_originalMode == FSMODE_NONE ? 0.F : 1.F);
}
eFullscreenMode CFullscreenTrackpadGesture::fsModeForMode(eMode mode) {
switch (mode) {
case MODE_FULLSCREEN: return FSMODE_FULLSCREEN;
case MODE_MAXIMIZE: return FSMODE_MAXIMIZED;
default: break;
}
return FSMODE_FULLSCREEN;
}

View file

@ -0,0 +1,33 @@
#pragma once
#include "ITrackpadGesture.hpp"
#include "../../../../desktop/DesktopTypes.hpp"
#include "../../../../desktop/Workspace.hpp"
class CFullscreenTrackpadGesture : public ITrackpadGesture {
public:
CFullscreenTrackpadGesture(const std::string_view& mode);
virtual ~CFullscreenTrackpadGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
private:
PHLWINDOWREF m_window;
Vector2D m_posFrom, m_posTo, m_sizeFrom, m_sizeTo;
float m_lastDelta = 0;
enum eMode : uint8_t {
MODE_FULLSCREEN = 0,
MODE_MAXIMIZE,
};
eMode m_mode = MODE_FULLSCREEN;
eFullscreenMode m_originalMode = FSMODE_NONE;
eFullscreenMode fsModeForMode(eMode mode);
};

View file

@ -0,0 +1,40 @@
#include "ITrackpadGesture.hpp"
// scale the pinch "scale" to match our imaginary delta units
constexpr const float PINCH_DELTA_SCALE = 400.F;
constexpr const float PINCH_DELTA_SCALE_OUT_ADD = 1.6F;
//
void ITrackpadGesture::begin(const STrackpadGestureBegin& e) {
m_lastPinchScale = 1.F;
m_scale = e.scale;
}
float ITrackpadGesture::distance(const STrackpadGestureBegin& e) {
if (e.direction == TRACKPAD_GESTURE_DIR_LEFT || e.direction == TRACKPAD_GESTURE_DIR_RIGHT || e.direction == TRACKPAD_GESTURE_DIR_HORIZONTAL)
return m_scale * (e.direction == TRACKPAD_GESTURE_DIR_LEFT ? -e.swipe->delta.x : e.swipe->delta.x);
if (e.direction == TRACKPAD_GESTURE_DIR_UP || e.direction == TRACKPAD_GESTURE_DIR_DOWN || e.direction == TRACKPAD_GESTURE_DIR_VERTICAL)
return m_scale * (e.direction == TRACKPAD_GESTURE_DIR_UP ? -e.swipe->delta.y : e.swipe->delta.y);
if (e.direction == TRACKPAD_GESTURE_DIR_SWIPE)
return m_scale * (e.swipe->delta.size());
if (e.direction == TRACKPAD_GESTURE_DIR_PINCH || e.direction == TRACKPAD_GESTURE_DIR_PINCH_IN || e.direction == TRACKPAD_GESTURE_DIR_PINCH_OUT) {
const auto Δ = m_lastPinchScale - e.pinch->scale;
m_lastPinchScale = e.pinch->scale;
return m_scale * ((e.direction == TRACKPAD_GESTURE_DIR_PINCH_IN ? -Δ : Δ * PINCH_DELTA_SCALE_OUT_ADD) * PINCH_DELTA_SCALE);
}
return m_scale * (e.swipe ? e.swipe->delta.size() : e.pinch->delta.size());
}
float ITrackpadGesture::distance(const STrackpadGestureUpdate& e) {
return ITrackpadGesture::distance(STrackpadGestureBegin{
.swipe = e.swipe,
.pinch = e.pinch,
.direction = e.direction,
.scale = e.scale,
});
}
bool ITrackpadGesture::isDirectionSensitive() {
return false;
}

View file

@ -0,0 +1,43 @@
#pragma once
#include "../../../../devices/IPointer.hpp"
#include "../GestureTypes.hpp"
class ITrackpadGesture {
public:
virtual ~ITrackpadGesture() = default;
struct STrackpadGestureBegin {
// this has update because we wait for the delta
const IPointer::SSwipeUpdateEvent* swipe = nullptr;
const IPointer::SPinchUpdateEvent* pinch = nullptr;
eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE;
float scale = 1.F;
};
struct STrackpadGestureUpdate {
const IPointer::SSwipeUpdateEvent* swipe = nullptr;
const IPointer::SPinchUpdateEvent* pinch = nullptr;
eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE;
float scale = 1.F;
};
struct STrackpadGestureEnd {
const IPointer::SSwipeEndEvent* swipe = nullptr;
const IPointer::SPinchEndEvent* pinch = nullptr;
eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE;
float scale = 1.F;
};
virtual void begin(const STrackpadGestureBegin& e);
virtual void update(const STrackpadGestureUpdate& e) = 0;
virtual void end(const STrackpadGestureEnd& e) = 0;
virtual float distance(const STrackpadGestureBegin& e);
virtual float distance(const STrackpadGestureUpdate& e);
virtual bool isDirectionSensitive();
protected:
float m_lastPinchScale = 1.F, m_scale = 1.F;
};

View file

@ -0,0 +1,66 @@
#include "MoveGesture.hpp"
#include "../../../../Compositor.hpp"
#include "../../../../managers/LayoutManager.hpp"
#include "../../../../render/Renderer.hpp"
void CMoveTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
ITrackpadGesture::begin(e);
m_window = g_pCompositor->m_lastWindow;
m_lastDelta = {};
}
void CMoveTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
if (!m_window)
return;
const auto DELTA = e.swipe ? e.swipe->delta : e.pinch->delta;
if (m_window->m_isFloating) {
g_pLayoutManager->getCurrentLayout()->moveActiveWindow(DELTA, m_window.lock());
m_window->m_realSize->warp();
m_window->m_realPosition->warp();
return;
}
// tiled window -> displace, then execute a move dispatcher on end.
g_pHyprRenderer->damageWindow(m_window.lock());
// funny name but works on tiled too lmao
m_lastDelta += DELTA;
m_window->m_floatingOffset = (m_lastDelta * 0.5F).clamp(Vector2D{-100.F, -100.F}, Vector2D{100.F, 100.F});
g_pHyprRenderer->damageWindow(m_window.lock());
}
void CMoveTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
if (!m_window)
return;
if (m_window->m_isFloating || m_lastDelta.size() < 0.1F)
return;
// tiled: attempt to move window in the given direction
const auto WINDOWPOS = m_window->m_realPosition->goal() + m_window->m_floatingOffset;
m_window->m_floatingOffset = {};
if (std::abs(m_lastDelta.x) > std::abs(m_lastDelta.y)) {
// horizontal
g_pLayoutManager->getCurrentLayout()->moveWindowTo(m_window.lock(), m_lastDelta.x > 0 ? "r" : "l");
} else {
// vertical
g_pLayoutManager->getCurrentLayout()->moveWindowTo(m_window.lock(), m_lastDelta.y > 0 ? "b" : "t");
}
const auto GOAL = m_window->m_realPosition->goal();
m_window->m_realPosition->setValueAndWarp(WINDOWPOS);
*m_window->m_realPosition = GOAL;
m_window.reset();
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "ITrackpadGesture.hpp"
#include "../../../../desktop/DesktopTypes.hpp"
class CMoveTrackpadGesture : public ITrackpadGesture {
public:
CMoveTrackpadGesture() = default;
virtual ~CMoveTrackpadGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
private:
PHLWINDOWREF m_window;
Vector2D m_lastDelta;
};

View file

@ -0,0 +1,29 @@
#include "ResizeGesture.hpp"
#include "../../../../Compositor.hpp"
#include "../../../../managers/LayoutManager.hpp"
#include "../../../../render/Renderer.hpp"
void CResizeTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
ITrackpadGesture::begin(e);
m_window = g_pCompositor->m_lastWindow;
}
void CResizeTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
if (!m_window)
return;
g_pHyprRenderer->damageWindow(m_window.lock());
g_pLayoutManager->getCurrentLayout()->resizeActiveWindow((e.swipe ? e.swipe->delta : e.pinch->delta),
cornerFromBox(m_window->getWindowMainSurfaceBox(), g_pInputManager->getMouseCoordsInternal()), m_window.lock());
m_window->m_realSize->warp();
m_window->m_realPosition->warp();
g_pHyprRenderer->damageWindow(m_window.lock());
}
void CResizeTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
m_window.reset();
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "ITrackpadGesture.hpp"
#include "../../../../desktop/DesktopTypes.hpp"
class CResizeTrackpadGesture : public ITrackpadGesture {
public:
CResizeTrackpadGesture() = default;
virtual ~CResizeTrackpadGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
private:
PHLWINDOWREF m_window;
};

View file

@ -0,0 +1,126 @@
#include "SpecialWorkspaceGesture.hpp"
#include "../../../../Compositor.hpp"
#include "../../../../managers/LayoutManager.hpp"
#include "../../../../render/Renderer.hpp"
#include <hyprutils/memory/Casts.hpp>
using namespace Hyprutils::Memory;
constexpr const float MAX_DISTANCE = 150.F;
//
static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) {
return Vector2D{
from.x + ((to.x - from.x) * t),
from.y + ((to.y - from.y) * t),
};
}
static float lerpVal(const float& from, const float& to, const float& t) {
return from + ((to - from) * t);
}
CSpecialWorkspaceGesture::CSpecialWorkspaceGesture(const std::string& workspaceName) : m_specialWorkspaceName(workspaceName) {
;
}
void CSpecialWorkspaceGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
ITrackpadGesture::begin(e);
m_specialWorkspace.reset();
m_lastDelta = 0.F;
m_monitor.reset();
m_specialWorkspace = g_pCompositor->getWorkspaceByName("special:" + m_specialWorkspaceName);
if (m_specialWorkspace) {
m_animatingOut = m_specialWorkspace->isVisible();
m_monitor = m_animatingOut ? m_specialWorkspace->m_monitor : g_pCompositor->m_lastMonitor;
if (!m_monitor)
return;
if (!m_animatingOut)
m_monitor->setSpecialWorkspace(m_specialWorkspace);
} else {
m_monitor = g_pCompositor->m_lastMonitor;
if (!m_monitor)
return;
m_animatingOut = false;
const auto& [workspaceID, workspaceName] = getWorkspaceIDNameFromString("special:" + m_specialWorkspaceName);
const auto WS = g_pCompositor->createNewWorkspace(workspaceID, m_monitor->m_id, workspaceName);
m_monitor->setSpecialWorkspace(WS);
m_specialWorkspace = WS;
}
if (!m_specialWorkspace)
return;
m_monitorDimFrom = m_monitor->m_specialFade->begun();
m_monitorDimTo = m_monitor->m_specialFade->goal();
m_workspaceAlphaFrom = m_specialWorkspace->m_alpha->begun();
m_workspaceAlphaTo = m_specialWorkspace->m_alpha->goal();
m_workspaceOffsetFrom = m_specialWorkspace->m_renderOffset->begun();
m_workspaceOffsetTo = m_specialWorkspace->m_renderOffset->goal();
}
void CSpecialWorkspaceGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
if (!m_specialWorkspace || !m_monitor)
return;
g_pHyprRenderer->damageMonitor(m_specialWorkspace->m_monitor.lock());
m_lastDelta += distance(e);
const auto FADEPERCENT = m_animatingOut ? 1.F - std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F) : std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
m_monitor->m_specialFade->setValueAndWarp(lerpVal(m_monitorDimFrom, m_monitorDimTo, FADEPERCENT));
m_specialWorkspace->m_alpha->setValueAndWarp(lerpVal(m_workspaceAlphaFrom, m_workspaceAlphaTo, FADEPERCENT));
m_specialWorkspace->m_renderOffset->setValueAndWarp(lerpVal(m_workspaceOffsetFrom, m_workspaceOffsetTo, FADEPERCENT));
}
void CSpecialWorkspaceGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
if (!m_specialWorkspace || !m_monitor)
return;
const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F);
if (COMPLETION < 0.3F) {
// cancel the operation, which effectively means just flip the animation direction
// also flip goals if animating in
m_animatingOut = !m_animatingOut;
if (m_animatingOut) {
m_workspaceOffsetTo = m_workspaceOffsetFrom;
m_workspaceAlphaTo = m_workspaceAlphaFrom;
m_monitorDimTo = m_monitorDimFrom;
}
}
if (m_animatingOut) {
const auto CURR_WS_ALPHA = m_specialWorkspace->m_alpha->value();
const auto CURR_WS_OFFSET = m_specialWorkspace->m_renderOffset->value();
const auto CURR_MON_FADE = m_monitor->m_specialFade->value();
m_monitor->setSpecialWorkspace(nullptr);
const auto GOAL_WS_ALPHA = m_specialWorkspace->m_alpha->goal();
const auto GOAL_WS_OFFSET = m_specialWorkspace->m_renderOffset->goal();
m_monitor->m_specialFade->setValueAndWarp(CURR_MON_FADE);
m_specialWorkspace->m_alpha->setValueAndWarp(CURR_WS_ALPHA);
m_specialWorkspace->m_renderOffset->setValueAndWarp(CURR_WS_OFFSET);
*m_monitor->m_specialFade = 0.F;
*m_specialWorkspace->m_alpha = GOAL_WS_ALPHA;
*m_specialWorkspace->m_renderOffset = GOAL_WS_OFFSET;
} else {
*m_monitor->m_specialFade = m_monitorDimTo;
*m_specialWorkspace->m_renderOffset = m_workspaceOffsetTo;
*m_specialWorkspace->m_alpha = m_workspaceAlphaTo;
}
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "ITrackpadGesture.hpp"
#include "../../../../desktop/DesktopTypes.hpp"
class CSpecialWorkspaceGesture : public ITrackpadGesture {
public:
CSpecialWorkspaceGesture(const std::string& workspaceName);
virtual ~CSpecialWorkspaceGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
private:
std::string m_specialWorkspaceName;
PHLWORKSPACE m_specialWorkspace;
PHLMONITORREF m_monitor;
bool m_animatingOut = false;
float m_lastDelta = 0.F;
// animated properties, kinda sucks
float m_monitorDimFrom = 0.F, m_monitorDimTo = 0.F;
float m_workspaceAlphaFrom = 0.F, m_workspaceAlphaTo = 0.F;
Vector2D m_workspaceOffsetFrom = {}, m_workspaceOffsetTo = {};
};

View file

@ -0,0 +1,50 @@
#include "WorkspaceSwipeGesture.hpp"
#include "../../../../Compositor.hpp"
#include "../../../../managers/input/InputManager.hpp"
#include "../../../../render/Renderer.hpp"
#include "../../UnifiedWorkspaceSwipeGesture.hpp"
void CWorkspaceSwipeGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
ITrackpadGesture::begin(e);
static auto PSWIPENEW = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_create_new");
if (g_pSessionLockManager->isSessionLocked() || g_pUnifiedWorkspaceSwipe->isGestureInProgress())
return;
int onMonitor = 0;
for (auto const& w : g_pCompositor->getWorkspaces()) {
if (w->m_monitor == g_pCompositor->m_lastMonitor && !g_pCompositor->isWorkspaceSpecial(w->m_id))
onMonitor++;
}
if (onMonitor < 2 && !*PSWIPENEW)
return; // disallow swiping when there's 1 workspace on a monitor
g_pUnifiedWorkspaceSwipe->begin();
}
void CWorkspaceSwipeGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
if (!g_pUnifiedWorkspaceSwipe->isGestureInProgress())
return;
const float DELTA = distance(e);
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_invert");
const double D = g_pUnifiedWorkspaceSwipe->m_delta + (*PSWIPEINVR ? -DELTA : DELTA);
g_pUnifiedWorkspaceSwipe->update(D);
}
void CWorkspaceSwipeGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
if (!g_pUnifiedWorkspaceSwipe->isGestureInProgress())
return;
g_pUnifiedWorkspaceSwipe->end();
}
bool CWorkspaceSwipeGesture::isDirectionSensitive() {
return true;
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "ITrackpadGesture.hpp"
#include "../../../../desktop/DesktopTypes.hpp"
class CWorkspaceSwipeGesture : public ITrackpadGesture {
public:
CWorkspaceSwipeGesture() = default;
virtual ~CWorkspaceSwipeGesture() = default;
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
virtual bool isDirectionSensitive();
};