summaryrefslogtreecommitdiff
path: root/src/managers
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2025-08-28 11:20:29 +0200
committerGitHub <noreply@github.com>2025-08-28 11:20:29 +0200
commit81bf4eccba449bfe2b6adfb51260108aec710d4f (patch)
tree215c5b536cbcf1df9888081aadd3de16eb4b3ebe /src/managers
parent378e130f1426648d8d734049800128f9882805bf (diff)
input: Add fully configurable trackpad gestures (#11490)
Adds configurable trackpad gestures
Diffstat (limited to 'src/managers')
-rw-r--r--src/managers/KeybindManager.cpp4
-rw-r--r--src/managers/PointerManager.cpp22
-rw-r--r--src/managers/animation/AnimationManager.cpp (renamed from src/managers/AnimationManager.cpp)191
-rw-r--r--src/managers/animation/AnimationManager.hpp (renamed from src/managers/AnimationManager.hpp)15
-rw-r--r--src/managers/animation/DesktopAnimationManager.cpp491
-rw-r--r--src/managers/animation/DesktopAnimationManager.hpp26
-rw-r--r--src/managers/input/InputManager.cpp51
-rw-r--r--src/managers/input/InputManager.hpp16
-rw-r--r--src/managers/input/Swipe.cpp348
-rw-r--r--src/managers/input/Touch.cpp35
-rw-r--r--src/managers/input/UnifiedWorkspaceSwipeGesture.cpp313
-rw-r--r--src/managers/input/UnifiedWorkspaceSwipeGesture.hpp28
-rw-r--r--src/managers/input/trackpad/GestureTypes.hpp17
-rw-r--r--src/managers/input/trackpad/TrackpadGestures.cpp221
-rw-r--r--src/managers/input/trackpad/TrackpadGestures.hpp43
-rw-r--r--src/managers/input/trackpad/gestures/CloseGesture.cpp145
-rw-r--r--src/managers/input/trackpad/gestures/CloseGesture.hpp23
-rw-r--r--src/managers/input/trackpad/gestures/DispatcherGesture.cpp22
-rw-r--r--src/managers/input/trackpad/gestures/DispatcherGesture.hpp16
-rw-r--r--src/managers/input/trackpad/gestures/FloatGesture.cpp88
-rw-r--r--src/managers/input/trackpad/gestures/FloatGesture.hpp30
-rw-r--r--src/managers/input/trackpad/gestures/FullscreenGesture.cpp97
-rw-r--r--src/managers/input/trackpad/gestures/FullscreenGesture.hpp33
-rw-r--r--src/managers/input/trackpad/gestures/ITrackpadGesture.cpp40
-rw-r--r--src/managers/input/trackpad/gestures/ITrackpadGesture.hpp43
-rw-r--r--src/managers/input/trackpad/gestures/MoveGesture.cpp66
-rw-r--r--src/managers/input/trackpad/gestures/MoveGesture.hpp19
-rw-r--r--src/managers/input/trackpad/gestures/ResizeGesture.cpp29
-rw-r--r--src/managers/input/trackpad/gestures/ResizeGesture.hpp18
-rw-r--r--src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp126
-rw-r--r--src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp27
-rw-r--r--src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp50
-rw-r--r--src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp16
33 files changed, 2134 insertions, 575 deletions
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index 1e4e5710..0eab9689 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -14,6 +14,7 @@
#include "debug/Log.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../managers/input/InputManager.hpp"
+#include "../managers/animation/DesktopAnimationManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/EventManager.hpp"
#include "../render/Renderer.hpp"
@@ -2327,7 +2328,8 @@ SDispatchResult CKeybindManager::focusWindow(std::string regexp) {
// don't make floating implicitly fs
if (!PWINDOW->m_createdOverFullscreen) {
g_pCompositor->changeWindowZOrder(PWINDOW, true);
- g_pCompositor->updateFullscreenFadeOnWorkspace(PWORKSPACE);
+ g_pDesktopAnimationManager->setFullscreenFadeAnimation(
+ PWORKSPACE, PWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
}
g_pCompositor->focusWindow(PWINDOW);
diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp
index f5635810..093c751d 100644
--- a/src/managers/PointerManager.cpp
+++ b/src/managers/PointerManager.cpp
@@ -902,7 +902,7 @@ void CPointerManager::attachPointer(SP<IPointer> pointer) {
PROTO::idle->onActivity();
if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS)
- g_pKeybindManager->dpms("on");
+ CKeybindManager::dpms("on");
});
listener->motionAbsolute = pointer->m_pointerEvents.motionAbsolute.listen([](const IPointer::SMotionAbsoluteEvent& event) {
@@ -911,7 +911,7 @@ void CPointerManager::attachPointer(SP<IPointer> pointer) {
PROTO::idle->onActivity();
if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS)
- g_pKeybindManager->dpms("on");
+ CKeybindManager::dpms("on");
});
listener->button = pointer->m_pointerEvents.button.listen([](const IPointer::SButtonEvent& event) {
@@ -941,7 +941,7 @@ void CPointerManager::attachPointer(SP<IPointer> pointer) {
PROTO::idle->onActivity();
if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS)
- g_pKeybindManager->dpms("on");
+ CKeybindManager::dpms("on");
});
listener->swipeEnd = pointer->m_pointerEvents.swipeEnd.listen([](const IPointer::SSwipeEndEvent& event) {
@@ -955,21 +955,23 @@ void CPointerManager::attachPointer(SP<IPointer> pointer) {
});
listener->pinchBegin = pointer->m_pointerEvents.pinchBegin.listen([](const IPointer::SPinchBeginEvent& event) {
- PROTO::pointerGestures->pinchBegin(event.timeMs, event.fingers);
+ g_pInputManager->onPinchBegin(event);
PROTO::idle->onActivity();
if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS)
- g_pKeybindManager->dpms("on");
+ CKeybindManager::dpms("on");
});
listener->pinchEnd = pointer->m_pointerEvents.pinchEnd.listen([](const IPointer::SPinchEndEvent& event) {
- PROTO::pointerGestures->pinchEnd(event.timeMs, event.cancelled);
+ g_pInputManager->onPinchEnd(event);
+
PROTO::idle->onActivity();
});
listener->pinchUpdate = pointer->m_pointerEvents.pinchUpdate.listen([](const IPointer::SPinchUpdateEvent& event) {
- PROTO::pointerGestures->pinchUpdate(event.timeMs, event.delta, event.scale, event.rotation);
+ g_pInputManager->onPinchUpdate(event);
+
PROTO::idle->onActivity();
});
@@ -1005,7 +1007,7 @@ void CPointerManager::attachTouch(SP<ITouch> touch) {
PROTO::idle->onActivity();
if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS)
- g_pKeybindManager->dpms("on");
+ CKeybindManager::dpms("on");
});
listener->up = touch->m_touchEvents.up.listen([](const ITouch::SUpEvent& event) {
@@ -1046,7 +1048,7 @@ void CPointerManager::attachTablet(SP<CTablet> tablet) {
PROTO::idle->onActivity();
if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS)
- g_pKeybindManager->dpms("on");
+ CKeybindManager::dpms("on");
});
listener->proximity = tablet->m_tabletEvents.proximity.listen([](const CTablet::SProximityEvent& event) {
@@ -1060,7 +1062,7 @@ void CPointerManager::attachTablet(SP<CTablet> tablet) {
PROTO::idle->onActivity();
if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS)
- g_pKeybindManager->dpms("on");
+ CKeybindManager::dpms("on");
});
listener->button = tablet->m_tabletEvents.button.listen([](const CTablet::SButtonEvent& event) {
diff --git a/src/managers/AnimationManager.cpp b/src/managers/animation/AnimationManager.cpp
index aac4a869..6675216a 100644
--- a/src/managers/AnimationManager.cpp
+++ b/src/managers/animation/AnimationManager.cpp
@@ -1,16 +1,16 @@
#include "AnimationManager.hpp"
-#include "../Compositor.hpp"
-#include "HookSystemManager.hpp"
-#include "../config/ConfigManager.hpp"
-#include "../desktop/DesktopTypes.hpp"
-#include "../helpers/AnimatedVariable.hpp"
-#include "../macros.hpp"
-#include "../config/ConfigValue.hpp"
-#include "../desktop/Window.hpp"
-#include "../desktop/LayerSurface.hpp"
-#include "eventLoop/EventLoopManager.hpp"
-#include "../helpers/varlist/VarList.hpp"
-#include "../render/Renderer.hpp"
+#include "../../Compositor.hpp"
+#include "../HookSystemManager.hpp"
+#include "../../config/ConfigManager.hpp"
+#include "../../desktop/DesktopTypes.hpp"
+#include "../../helpers/AnimatedVariable.hpp"
+#include "../../macros.hpp"
+#include "../../config/ConfigValue.hpp"
+#include "../../desktop/Window.hpp"
+#include "../../desktop/LayerSurface.hpp"
+#include "../eventLoop/EventLoopManager.hpp"
+#include "../../helpers/varlist/VarList.hpp"
+#include "../../render/Renderer.hpp"
#include <hyprgraphics/color/Color.hpp>
#include <hyprutils/animation/AnimatedVariable.hpp>
@@ -275,173 +275,6 @@ void CHyprAnimationManager::onTicked() {
m_tickScheduled = false;
}
-//
-// Anims
-//
-//
-
-void CHyprAnimationManager::animationPopin(PHLWINDOW pWindow, bool close, float minPerc) {
- const auto GOALPOS = pWindow->m_realPosition->goal();
- const auto GOALSIZE = pWindow->m_realSize->goal();
-
- if (!close) {
- pWindow->m_realSize->setValue((GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y}));
- pWindow->m_realPosition->setValue(GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->value() / 2.f);
- } else {
- *pWindow->m_realSize = (GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y});
- *pWindow->m_realPosition = GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->goal() / 2.f;
- }
-}
-
-void CHyprAnimationManager::animationSlide(PHLWINDOW pWindow, std::string force, bool close) {
- pWindow->m_realSize->warp(false); // size we preserve in slide
-
- const auto GOALPOS = pWindow->m_realPosition->goal();
- const auto GOALSIZE = pWindow->m_realSize->goal();
-
- const auto PMONITOR = pWindow->m_monitor.lock();
-
- if (!PMONITOR)
- return; // unsafe state most likely
-
- Vector2D posOffset;
-
- if (!force.empty()) {
- if (force == "bottom")
- posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y);
- else if (force == "left")
- posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0);
- else if (force == "right")
- posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0);
- else
- posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y);
-
- if (!close)
- pWindow->m_realPosition->setValue(posOffset);
- else
- *pWindow->m_realPosition = posOffset;
-
- return;
- }
-
- const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f;
-
- // check sides it touches
- const bool DISPLAYLEFT = STICKS(pWindow->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x);
- const bool DISPLAYRIGHT = STICKS(pWindow->m_position.x + pWindow->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x);
- const bool DISPLAYTOP = STICKS(pWindow->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y);
- const bool DISPLAYBOTTOM = STICKS(pWindow->m_position.y + pWindow->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y);
-
- if (DISPLAYBOTTOM && DISPLAYTOP) {
- if (DISPLAYLEFT && DISPLAYRIGHT) {
- posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y);
- } else if (DISPLAYLEFT) {
- posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0);
- } else {
- posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0);
- }
- } else if (DISPLAYTOP) {
- posOffset = GOALPOS - Vector2D(0.0, GOALSIZE.y);
- } else if (DISPLAYBOTTOM) {
- posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y);
- } else {
- if (MIDPOINT.y > PMONITOR->m_position.y + PMONITOR->m_size.y / 2.f)
- posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y);
- else
- posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y);
- }
-
- if (!close)
- pWindow->m_realPosition->setValue(posOffset);
- else
- *pWindow->m_realPosition = posOffset;
-}
-
-void CHyprAnimationManager::animationGnomed(PHLWINDOW pWindow, bool close) {
- const auto GOALPOS = pWindow->m_realPosition->goal();
- const auto GOALSIZE = pWindow->m_realSize->goal();
-
- if (close) {
- *pWindow->m_realPosition = GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F};
- *pWindow->m_realSize = Vector2D{GOALSIZE.x, 0.F};
- } else {
- pWindow->m_realPosition->setValueAndWarp(GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F});
- pWindow->m_realSize->setValueAndWarp(Vector2D{GOALSIZE.x, 0.F});
- *pWindow->m_realPosition = GOALPOS;
- *pWindow->m_realSize = GOALSIZE;
- }
-}
-
-void CHyprAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool close) {
- if (!close) {
- pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn"));
- pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn"));
- pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn"));
- } else {
- pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut"));
- pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut"));
- pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeOut"));
- }
-
- std::string ANIMSTYLE = pWindow->m_realPosition->getStyle();
- std::ranges::transform(ANIMSTYLE, ANIMSTYLE.begin(), ::tolower);
-
- CVarList animList(ANIMSTYLE, 0, 's');
-
- // if the window is not being animated, that means the layout set a fixed size for it, don't animate.
- if (!pWindow->m_realPosition->isBeingAnimated() && !pWindow->m_realSize->isBeingAnimated())
- return;
-
- // if the animation is disabled and we are leaving, ignore the anim to prevent the snapshot being fucked
- if (!pWindow->m_realPosition->enabled())
- return;
-
- if (pWindow->m_windowData.animationStyle.hasValue()) {
- const auto STYLE = pWindow->m_windowData.animationStyle.value();
- // the window has config'd special anim
- if (STYLE.starts_with("slide")) {
- CVarList animList2(STYLE, 0, 's');
- animationSlide(pWindow, animList2[1], close);
- } else if (STYLE == "gnomed" || STYLE == "gnome")
- animationGnomed(pWindow, close);
- else {
- // anim popin, fallback
-
- float minPerc = 0.f;
- if (STYLE.find("%") != std::string::npos) {
- try {
- auto percstr = STYLE.substr(STYLE.find_last_of(' '));
- minPerc = std::stoi(percstr.substr(0, percstr.length() - 1));
- } catch (std::exception& e) {
- ; // oops
- }
- }
-
- animationPopin(pWindow, close, minPerc / 100.f);
- }
- } else {
- if (animList[0] == "slide")
- animationSlide(pWindow, animList[1], close);
- else if (animList[0] == "gnomed" || animList[0] == "gnome")
- animationGnomed(pWindow, close);
- else {
- // anim popin, fallback
-
- float minPerc = 0.f;
- if (!ANIMSTYLE.starts_with("%")) {
- try {
- auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' '));
- minPerc = std::stoi(percstr.substr(0, percstr.length() - 1));
- } catch (std::exception& e) {
- ; // oops
- }
- }
-
- animationPopin(pWindow, close, minPerc / 100.f);
- }
- }
-}
-
std::string CHyprAnimationManager::styleValidInConfigVar(const std::string& config, const std::string& style) {
if (config.starts_with("window")) {
if (style.starts_with("slide") || style == "gnome" || style == "gnomed")
diff --git a/src/managers/AnimationManager.hpp b/src/managers/animation/AnimationManager.hpp
index b5e42036..0631e7c7 100644
--- a/src/managers/AnimationManager.hpp
+++ b/src/managers/animation/AnimationManager.hpp
@@ -3,10 +3,10 @@
#include <hyprutils/animation/AnimationManager.hpp>
#include <hyprutils/animation/AnimatedVariable.hpp>
-#include "../defines.hpp"
-#include "../helpers/AnimatedVariable.hpp"
-#include "../desktop/DesktopTypes.hpp"
-#include "eventLoop/EventLoopTimer.hpp"
+#include "../../defines.hpp"
+#include "../../helpers/AnimatedVariable.hpp"
+#include "../../desktop/DesktopTypes.hpp"
+#include "../eventLoop/EventLoopTimer.hpp"
class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager {
public:
@@ -45,8 +45,6 @@ class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager {
pav->m_Context.pLayer = pLayer;
}
- void onWindowPostCreateClose(PHLWINDOW, bool close = false);
-
std::string styleValidInConfigVar(const std::string&, const std::string&);
SP<CEventLoopTimer> m_animationTimer;
@@ -55,11 +53,6 @@ class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager {
private:
bool m_tickScheduled = false;
-
- // Anim stuff
- void animationPopin(PHLWINDOW, bool close = false, float minPerc = 0.f);
- void animationSlide(PHLWINDOW, std::string force = "", bool close = false);
- void animationGnomed(PHLWINDOW, bool close = false);
};
inline UP<CHyprAnimationManager> g_pAnimationManager;
diff --git a/src/managers/animation/DesktopAnimationManager.cpp b/src/managers/animation/DesktopAnimationManager.cpp
new file mode 100644
index 00000000..e55a0439
--- /dev/null
+++ b/src/managers/animation/DesktopAnimationManager.cpp
@@ -0,0 +1,491 @@
+#include "DesktopAnimationManager.hpp"
+
+#include "../../desktop/LayerSurface.hpp"
+#include "../../desktop/Window.hpp"
+#include "../../desktop/Workspace.hpp"
+
+#include "../../config/ConfigManager.hpp"
+#include "../../Compositor.hpp"
+#include "wlr-layer-shell-unstable-v1.hpp"
+
+void CDesktopAnimationManager::startAnimation(PHLWINDOW pWindow, eAnimationType type, bool force) {
+ const bool CLOSE = type == ANIMATION_TYPE_OUT;
+
+ if (CLOSE)
+ *pWindow->m_alpha = 0.F;
+ else {
+ pWindow->m_alpha->setValueAndWarp(0.F);
+ *pWindow->m_alpha = 1.F;
+ }
+
+ if (!CLOSE) {
+ pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn"));
+ pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn"));
+ pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn"));
+ } else {
+ pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut"));
+ pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut"));
+ pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeOut"));
+ }
+
+ std::string ANIMSTYLE = pWindow->m_realPosition->getStyle();
+ std::ranges::transform(ANIMSTYLE, ANIMSTYLE.begin(), ::tolower);
+
+ CVarList animList(ANIMSTYLE, 0, 's');
+
+ // if the window is not being animated, that means the layout set a fixed size for it, don't animate.
+ if (!pWindow->m_realPosition->isBeingAnimated() && !pWindow->m_realSize->isBeingAnimated() && !force)
+ return;
+
+ // if the animation is disabled and we are leaving, ignore the anim to prevent the snapshot being fucked
+ if (!pWindow->m_realPosition->enabled() && !force)
+ return;
+
+ if (pWindow->m_windowData.animationStyle.hasValue()) {
+ const auto STYLE = pWindow->m_windowData.animationStyle.value();
+ // the window has config'd special anim
+ if (STYLE.starts_with("slide")) {
+ CVarList animList2(STYLE, 0, 's');
+ animationSlide(pWindow, animList2[1], CLOSE);
+ } else if (STYLE == "gnomed" || STYLE == "gnome")
+ animationGnomed(pWindow, CLOSE);
+ else {
+ // anim popin, fallback
+
+ float minPerc = 0.f;
+ if (STYLE.find("%") != std::string::npos) {
+ try {
+ auto percstr = STYLE.substr(STYLE.find_last_of(' '));
+ minPerc = std::stoi(percstr.substr(0, percstr.length() - 1));
+ } catch (std::exception& e) {
+ ; // oops
+ }
+ }
+
+ animationPopin(pWindow, CLOSE, minPerc / 100.f);
+ }
+ } else {
+ if (animList[0] == "slide")
+ animationSlide(pWindow, animList[1], CLOSE);
+ else if (animList[0] == "gnomed" || animList[0] == "gnome")
+ animationGnomed(pWindow, CLOSE);
+ else {
+ // anim popin, fallback
+
+ float minPerc = 0.f;
+ if (!ANIMSTYLE.starts_with("%")) {
+ try {
+ auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' '));
+ minPerc = std::stoi(percstr.substr(0, percstr.length() - 1));
+ } catch (std::exception& e) {
+ ; // oops
+ }
+ }
+
+ animationPopin(pWindow, CLOSE, minPerc / 100.f);
+ }
+ }
+}
+
+void CDesktopAnimationManager::startAnimation(PHLLS ls, eAnimationType type, bool instant) {
+ const bool IN = type == ANIMATION_TYPE_IN;
+
+ if (IN) {
+ ls->m_alpha->setValueAndWarp(0.F);
+ *ls->m_alpha = 1.F;
+ } else
+ *ls->m_alpha = 0.F;
+
+ if (IN) {
+ ls->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
+ ls->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
+ ls->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"));
+ } else {
+ ls->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
+ ls->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
+ ls->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut"));
+ }
+
+ const auto ANIMSTYLE = ls->m_animationStyle.value_or(ls->m_realPosition->getStyle());
+ if (ANIMSTYLE.starts_with("slide")) {
+ // get closest edge
+ const auto MIDDLE = ls->m_geometry.middle();
+
+ const auto PMONITOR = g_pCompositor->getMonitorFromVector(MIDDLE);
+
+ if (!PMONITOR) { // can rarely happen on exit
+ ls->m_alpha->setValueAndWarp(IN ? 1.F : 0.F);
+ return;
+ }
+
+ int force = -1;
+
+ CVarList args(ANIMSTYLE, 0, 's');
+ if (args.size() > 1) {
+ const auto ARG2 = args[1];
+ if (ARG2 == "top")
+ force = 0;
+ else if (ARG2 == "bottom")
+ force = 1;
+ else if (ARG2 == "left")
+ force = 2;
+ else if (ARG2 == "right")
+ force = 3;
+ }
+
+ const std::array<Vector2D, 4> edgePoints = {
+ PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, 0.0},
+ PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, PMONITOR->m_size.y},
+ PMONITOR->m_position + Vector2D{0.0, PMONITOR->m_size.y},
+ PMONITOR->m_position + Vector2D{PMONITOR->m_size.x, PMONITOR->m_size.y / 2},
+ };
+
+ float closest = std::numeric_limits<float>::max();
+ int leader = force;
+ if (leader == -1) {
+ for (size_t i = 0; i < 4; ++i) {
+ float dist = MIDDLE.distance(edgePoints[i]);
+ if (dist < closest) {
+ leader = i;
+ closest = dist;
+ }
+ }
+ }
+
+ ls->m_realSize->setValueAndWarp(ls->m_geometry.size());
+
+ Vector2D prePos;
+
+ switch (leader) {
+ case 0:
+ // TOP
+ prePos = {ls->m_geometry.x, PMONITOR->m_position.y - ls->m_geometry.h};
+ break;
+ case 1:
+ // BOTTOM
+ prePos = {ls->m_geometry.x, PMONITOR->m_position.y + PMONITOR->m_size.y};
+ break;
+ case 2:
+ // LEFT
+ prePos = {PMONITOR->m_position.x - ls->m_geometry.w, ls->m_geometry.y};
+ break;
+ case 3:
+ // RIGHT
+ prePos = {PMONITOR->m_position.x + PMONITOR->m_size.x, ls->m_geometry.y};
+ break;
+ default: UNREACHABLE();
+ }
+
+ if (IN) {
+ ls->m_realPosition->setValueAndWarp(prePos);
+ *ls->m_realPosition = ls->m_geometry.pos();
+ } else {
+ ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos());
+ *ls->m_realPosition = prePos;
+ }
+
+ } else if (ANIMSTYLE.starts_with("popin")) {
+ float minPerc = 0.f;
+ if (ANIMSTYLE.find("%") != std::string::npos) {
+ try {
+ auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' '));
+ minPerc = std::stoi(percstr.substr(0, percstr.length() - 1));
+ } catch (std::exception& e) {
+ ; // oops
+ }
+ }
+
+ minPerc *= 0.01;
+
+ const auto GOALSIZE = (ls->m_geometry.size() * minPerc).clamp({5, 5});
+ const auto GOALPOS = ls->m_geometry.pos() + (ls->m_geometry.size() - GOALSIZE) / 2.f;
+
+ ls->m_alpha->setValueAndWarp(IN ? 0.f : 1.f);
+ *ls->m_alpha = IN ? 1.f : 0.f;
+
+ if (IN) {
+ ls->m_realSize->setValueAndWarp(GOALSIZE);
+ ls->m_realPosition->setValueAndWarp(GOALPOS);
+ *ls->m_realSize = ls->m_geometry.size();
+ *ls->m_realPosition = ls->m_geometry.pos();
+ } else {
+ ls->m_realSize->setValueAndWarp(ls->m_geometry.size());
+ ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos());
+ *ls->m_realSize = GOALSIZE;
+ *ls->m_realPosition = GOALPOS;
+ }
+ } else {
+ // fade
+ ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos());
+ ls->m_realSize->setValueAndWarp(ls->m_geometry.size());
+ *ls->m_alpha = IN ? 1.f : 0.f;
+ }
+
+ if (instant) {
+ ls->m_realPosition->warp();
+ ls->m_realSize->warp();
+ ls->m_alpha->warp();
+ }
+}
+
+void CDesktopAnimationManager::startAnimation(PHLWORKSPACE ws, eAnimationType type, bool left, bool instant) {
+ const bool IN = type == ANIMATION_TYPE_IN;
+
+ if (!instant) {
+ const std::string ANIMNAME = std::format("{}{}", ws->m_isSpecialWorkspace ? "specialWorkspace" : "workspaces", IN ? "In" : "Out");
+
+ ws->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME));
+ ws->m_renderOffset->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME));
+ }
+
+ const auto ANIMSTYLE = ws->m_alpha->getStyle();
+ static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
+
+ // set floating windows offset callbacks
+ ws->m_renderOffset->setUpdateCallback([weak = PHLWORKSPACEREF{ws}](auto) {
+ if (!weak)
+ return;
+
+ for (auto const& w : g_pCompositor->m_windows) {
+ if (!validMapped(w) || w->workspaceID() != weak->m_id)
+ continue;
+
+ w->onWorkspaceAnimUpdate();
+ };
+ });
+
+ if (ANIMSTYLE.starts_with("slidefade")) {
+ const auto PMONITOR = ws->m_monitor.lock();
+ float movePerc = 100.f;
+
+ if (ANIMSTYLE.find('%') != std::string::npos) {
+ try {
+ auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ') + 1);
+ movePerc = std::stoi(percstr.substr(0, percstr.length() - 1));
+ } catch (std::exception& e) { Debug::log(ERR, "Error in startAnim: invalid percentage"); }
+ }
+
+ ws->m_alpha->setValueAndWarp(1.f);
+ ws->m_renderOffset->setValueAndWarp(Vector2D(0, 0));
+
+ if (ANIMSTYLE.starts_with("slidefadevert")) {
+ if (IN) {
+ ws->m_alpha->setValueAndWarp(0.f);
+ ws->m_renderOffset->setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->m_size.y : -PMONITOR->m_size.y) * (movePerc / 100.f)));
+ *ws->m_alpha = 1.f;
+ *ws->m_renderOffset = Vector2D(0, 0);
+ } else {
+ ws->m_alpha->setValueAndWarp(1.f);
+ *ws->m_alpha = 0.f;
+ *ws->m_renderOffset = Vector2D(0.0, (left ? -PMONITOR->m_size.y : PMONITOR->m_size.y) * (movePerc / 100.f));
+ }
+ } else {
+ if (IN) {
+ ws->m_alpha->setValueAndWarp(0.f);
+ ws->m_renderOffset->setValueAndWarp(Vector2D((left ? PMONITOR->m_size.x : -PMONITOR->m_size.x) * (movePerc / 100.f), 0.0));
+ *ws->m_alpha = 1.f;
+ *ws->m_renderOffset = Vector2D(0, 0);
+ } else {
+ ws->m_alpha->setValueAndWarp(1.f);
+ *ws->m_alpha = 0.f;
+ *ws->m_renderOffset = Vector2D((left ? -PMONITOR->m_size.x : PMONITOR->m_size.x) * (movePerc / 100.f), 0.0);
+ }
+ }
+ } else if (ANIMSTYLE == "fade") {
+ ws->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade.
+
+ if (IN) {
+ ws->m_alpha->setValueAndWarp(0.f);
+ *ws->m_alpha = 1.f;
+ } else {
+ ws->m_alpha->setValueAndWarp(1.f);
+ *ws->m_alpha = 0.f;
+ }
+ } else if (ANIMSTYLE == "slidevert") {
+ // fallback is slide
+ const auto PMONITOR = ws->m_monitor.lock();
+ const auto YDISTANCE = PMONITOR->m_size.y + *PWORKSPACEGAP;
+
+ ws->m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
+
+ if (IN) {
+ ws->m_renderOffset->setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE));
+ *ws->m_renderOffset = Vector2D(0, 0);
+ } else {
+ *ws->m_renderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE);
+ }
+ } else {
+ // fallback is slide
+ const auto PMONITOR = ws->m_monitor.lock();
+ const auto XDISTANCE = PMONITOR->m_size.x + *PWORKSPACEGAP;
+
+ ws->m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
+
+ if (IN) {
+ ws->m_renderOffset->setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0));
+ *ws->m_renderOffset = Vector2D(0, 0);
+ } else {
+ *ws->m_renderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0);
+ }
+ }
+
+ if (ws->m_isSpecialWorkspace) {
+ // required for open/close animations
+ if (IN) {
+ ws->m_alpha->setValueAndWarp(0.f);
+ *ws->m_alpha = 1.f;
+ } else {
+ ws->m_alpha->setValueAndWarp(1.f);
+ *ws->m_alpha = 0.f;
+ }
+ }
+
+ if (instant) {
+ ws->m_renderOffset->warp();
+ ws->m_alpha->warp();
+ }
+}
+
+void CDesktopAnimationManager::animationPopin(PHLWINDOW pWindow, bool close, float minPerc) {
+ const auto GOALPOS = pWindow->m_realPosition->goal();
+ const auto GOALSIZE = pWindow->m_realSize->goal();
+
+ if (!close) {
+ pWindow->m_realSize->setValue((GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y}));
+ pWindow->m_realPosition->setValue(GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->value() / 2.f);
+ } else {
+ *pWindow->m_realSize = (GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y});
+ *pWindow->m_realPosition = GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->goal() / 2.f;
+ }
+}
+
+void CDesktopAnimationManager::animationSlide(PHLWINDOW pWindow, std::string force, bool close) {
+ pWindow->m_realSize->warp(false); // size we preserve in slide
+
+ const auto GOALPOS = pWindow->m_realPosition->goal();
+ const auto GOALSIZE = pWindow->m_realSize->goal();
+
+ const auto PMONITOR = pWindow->m_monitor.lock();
+
+ if (!PMONITOR)
+ return; // unsafe state most likely
+
+ Vector2D posOffset;
+
+ if (!force.empty()) {
+ if (force == "bottom")
+ posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y);
+ else if (force == "left")
+ posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0);
+ else if (force == "right")
+ posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0);
+ else
+ posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y);
+
+ if (!close)
+ pWindow->m_realPosition->setValue(posOffset);
+ else
+ *pWindow->m_realPosition = posOffset;
+
+ return;
+ }
+
+ const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f;
+
+ // check sides it touches
+ const bool DISPLAYLEFT = STICKS(pWindow->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x);
+ const bool DISPLAYRIGHT = STICKS(pWindow->m_position.x + pWindow->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x);
+ const bool DISPLAYTOP = STICKS(pWindow->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y);
+ const bool DISPLAYBOTTOM = STICKS(pWindow->m_position.y + pWindow->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y);
+
+ if (DISPLAYBOTTOM && DISPLAYTOP) {
+ if (DISPLAYLEFT && DISPLAYRIGHT) {
+ posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y);
+ } else if (DISPLAYLEFT) {
+ posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0);
+ } else {
+ posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0);
+ }
+ } else if (DISPLAYTOP) {
+ posOffset = GOALPOS - Vector2D(0.0, GOALSIZE.y);
+ } else if (DISPLAYBOTTOM) {
+ posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y);
+ } else {
+ if (MIDPOINT.y > PMONITOR->m_position.y + PMONITOR->m_size.y / 2.f)
+ posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y);
+ else
+ posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y);
+ }
+
+ if (!close)
+ pWindow->m_realPosition->setValue(posOffset);
+ else
+ *pWindow->m_realPosition = posOffset;
+}
+
+void CDesktopAnimationManager::animationGnomed(PHLWINDOW pWindow, bool close) {
+ const auto GOALPOS = pWindow->m_realPosition->goal();
+ const auto GOALSIZE = pWindow->m_realSize->goal();
+
+ if (close) {
+ *pWindow->m_realPosition = GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F};
+ *pWindow->m_realSize = Vector2D{GOALSIZE.x, 0.F};
+ } else {
+ pWindow->m_realPosition->setValueAndWarp(GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F});
+ pWindow->m_realSize->setValueAndWarp(Vector2D{GOALSIZE.x, 0.F});
+ *pWindow->m_realPosition = GOALPOS;
+ *pWindow->m_realSize = GOALSIZE;
+ }
+}
+
+void CDesktopAnimationManager::setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnimationType type) {
+ if (!ws)
+ return;
+
+ const auto FULLSCREEN = type == ANIMATION_TYPE_IN;
+
+ for (auto const& w : g_pCompositor->m_windows) {
+ if (w->m_workspace == ws) {
+
+ if (w->m_fadingOut || w->m_pinned || w->isFullscreen())
+ continue;
+
+ if (!FULLSCREEN)
+ *w->m_alpha = 1.F;
+ else if (!w->isFullscreen())
+ *w->m_alpha = !w->m_createdOverFullscreen ? 0.f : 1.f;
+ }
+ }
+
+ const auto PMONITOR = ws->m_monitor.lock();
+
+ if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) {
+ for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
+ if (!ls->m_fadingOut)
+ *ls->m_alpha = FULLSCREEN && ws->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
+ }
+ }
+}
+
+void CDesktopAnimationManager::overrideFullscreenFadeAmount(PHLWORKSPACE ws, float fade, PHLWINDOW exclude) {
+ for (auto const& w : g_pCompositor->m_windows) {
+ if (w == exclude)
+ continue;
+
+ if (w->m_workspace == ws) {
+ if (w->m_fadingOut || w->m_pinned || w->isFullscreen())
+ continue;
+
+ *w->m_alpha = fade;
+ }
+ }
+
+ const auto PMONITOR = ws->m_monitor.lock();
+
+ if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) {
+ for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
+ if (!ls->m_fadingOut)
+ *ls->m_alpha = fade;
+ }
+ }
+}
diff --git a/src/managers/animation/DesktopAnimationManager.hpp b/src/managers/animation/DesktopAnimationManager.hpp
new file mode 100644
index 00000000..623fc5e9
--- /dev/null
+++ b/src/managers/animation/DesktopAnimationManager.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "../../helpers/memory/Memory.hpp"
+#include "../../desktop/DesktopTypes.hpp"
+
+class CDesktopAnimationManager {
+ public:
+ enum eAnimationType : uint8_t {
+ ANIMATION_TYPE_IN = 0,
+ ANIMATION_TYPE_OUT,
+ };
+
+ void startAnimation(PHLWINDOW w, eAnimationType type, bool force = false);
+ void startAnimation(PHLLS ls, eAnimationType type, bool instant = false);
+ void startAnimation(PHLWORKSPACE ws, eAnimationType type, bool left = true, bool instant = false);
+
+ void setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnimationType type);
+ void overrideFullscreenFadeAmount(PHLWORKSPACE ws, float fade, PHLWINDOW exclude = nullptr);
+
+ private:
+ void animationPopin(PHLWINDOW w, bool close = false, float minPerc = 0.f);
+ void animationSlide(PHLWINDOW w, std::string force = "", bool close = false);
+ void animationGnomed(PHLWINDOW w, bool close = false);
+};
+
+inline UP<CDesktopAnimationManager> g_pDesktopAnimationManager = makeUnique<CDesktopAnimationManager>();
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index dff97166..5224f940 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -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);
+}
diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp
index 7a0a308f..56751cf5 100644
--- a/src/managers/input/InputManager.hpp
+++ b/src/managers/input/InputManager.hpp
@@ -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;
diff --git a/src/managers/input/Swipe.cpp b/src/managers/input/Swipe.cpp
deleted file mode 100644
index 1d9b80e2..00000000
--- a/src/managers/input/Swipe.cpp
+++ /dev/null
@@ -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();
- }
- }
-}
diff --git a/src/managers/input/Touch.cpp b/src/managers/input/Touch.cpp
index c01b8b41..2d3842f9 100644
--- a/src/managers/input/Touch.cpp
+++ b/src/managers/input/Touch.cpp
@@ -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) {
diff --git a/src/managers/input/UnifiedWorkspaceSwipeGesture.cpp b/src/managers/input/UnifiedWorkspaceSwipeGesture.cpp
new file mode 100644
index 00000000..01b92b48
--- /dev/null
+++ b/src/managers/input/UnifiedWorkspaceSwipeGesture.cpp
@@ -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;
+ }
+} \ No newline at end of file
diff --git a/src/managers/input/UnifiedWorkspaceSwipeGesture.hpp b/src/managers/input/UnifiedWorkspaceSwipeGesture.hpp
new file mode 100644
index 00000000..4dbb6c5d
--- /dev/null
+++ b/src/managers/input/UnifiedWorkspaceSwipeGesture.hpp
@@ -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>();
diff --git a/src/managers/input/trackpad/GestureTypes.hpp b/src/managers/input/trackpad/GestureTypes.hpp
new file mode 100644
index 00000000..89f69638
--- /dev/null
+++ b/src/managers/input/trackpad/GestureTypes.hpp
@@ -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,
+}; \ No newline at end of file
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();
+}
diff --git a/src/managers/input/trackpad/TrackpadGestures.hpp b/src/managers/input/trackpad/TrackpadGestures.hpp
new file mode 100644
index 00000000..3d0745df
--- /dev/null
+++ b/src/managers/input/trackpad/TrackpadGestures.hpp
@@ -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>();
diff --git a/src/managers/input/trackpad/gestures/CloseGesture.cpp b/src/managers/input/trackpad/gestures/CloseGesture.cpp
new file mode 100644
index 00000000..59beb712
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/CloseGesture.cpp
@@ -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();
+}
diff --git a/src/managers/input/trackpad/gestures/CloseGesture.hpp b/src/managers/input/trackpad/gestures/CloseGesture.hpp
new file mode 100644
index 00000000..a8e18fd2
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/CloseGesture.hpp
@@ -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;
+};
diff --git a/src/managers/input/trackpad/gestures/DispatcherGesture.cpp b/src/managers/input/trackpad/gestures/DispatcherGesture.cpp
new file mode 100644
index 00000000..4d76a671
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/DispatcherGesture.cpp
@@ -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);
+}
diff --git a/src/managers/input/trackpad/gestures/DispatcherGesture.hpp b/src/managers/input/trackpad/gestures/DispatcherGesture.hpp
new file mode 100644
index 00000000..b15abbed
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/DispatcherGesture.hpp
@@ -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;
+};
diff --git a/src/managers/input/trackpad/gestures/FloatGesture.cpp b/src/managers/input/trackpad/gestures/FloatGesture.cpp
new file mode 100644
index 00000000..97e4c98f
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/FloatGesture.cpp
@@ -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;
+}
diff --git a/src/managers/input/trackpad/gestures/FloatGesture.hpp b/src/managers/input/trackpad/gestures/FloatGesture.hpp
new file mode 100644
index 00000000..4132a1ac
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/FloatGesture.hpp
@@ -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;
+};
diff --git a/src/managers/input/trackpad/gestures/FullscreenGesture.cpp b/src/managers/input/trackpad/gestures/FullscreenGesture.cpp
new file mode 100644
index 00000000..14e99238
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/FullscreenGesture.cpp
@@ -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;
+}
diff --git a/src/managers/input/trackpad/gestures/FullscreenGesture.hpp b/src/managers/input/trackpad/gestures/FullscreenGesture.hpp
new file mode 100644
index 00000000..dd125e5b
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/FullscreenGesture.hpp
@@ -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);
+};
diff --git a/src/managers/input/trackpad/gestures/ITrackpadGesture.cpp b/src/managers/input/trackpad/gestures/ITrackpadGesture.cpp
new file mode 100644
index 00000000..afc96627
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/ITrackpadGesture.cpp
@@ -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;
+}
diff --git a/src/managers/input/trackpad/gestures/ITrackpadGesture.hpp b/src/managers/input/trackpad/gestures/ITrackpadGesture.hpp
new file mode 100644
index 00000000..a5108a2c
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/ITrackpadGesture.hpp
@@ -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;
+}; \ No newline at end of file
diff --git a/src/managers/input/trackpad/gestures/MoveGesture.cpp b/src/managers/input/trackpad/gestures/MoveGesture.cpp
new file mode 100644
index 00000000..6ceb02ec
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/MoveGesture.cpp
@@ -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();
+}
diff --git a/src/managers/input/trackpad/gestures/MoveGesture.hpp b/src/managers/input/trackpad/gestures/MoveGesture.hpp
new file mode 100644
index 00000000..3a045561
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/MoveGesture.hpp
@@ -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;
+};
diff --git a/src/managers/input/trackpad/gestures/ResizeGesture.cpp b/src/managers/input/trackpad/gestures/ResizeGesture.cpp
new file mode 100644
index 00000000..d788257a
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/ResizeGesture.cpp
@@ -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();
+}
diff --git a/src/managers/input/trackpad/gestures/ResizeGesture.hpp b/src/managers/input/trackpad/gestures/ResizeGesture.hpp
new file mode 100644
index 00000000..0b47a224
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/ResizeGesture.hpp
@@ -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;
+};
diff --git a/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp b/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp
new file mode 100644
index 00000000..146d3b50
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp
@@ -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;
+ }
+}
diff --git a/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp b/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp
new file mode 100644
index 00000000..0cc62529
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp
@@ -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 = {};
+};
diff --git a/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp b/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp
new file mode 100644
index 00000000..793bfd77
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp
@@ -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;
+}
diff --git a/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp b/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp
new file mode 100644
index 00000000..203fc329
--- /dev/null
+++ b/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp
@@ -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();
+};