From 81bf4eccba449bfe2b6adfb51260108aec710d4f Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:20:29 +0200 Subject: input: Add fully configurable trackpad gestures (#11490) Adds configurable trackpad gestures --- src/managers/AnimationManager.cpp | 518 --------------------- src/managers/AnimationManager.hpp | 65 --- src/managers/KeybindManager.cpp | 4 +- src/managers/PointerManager.cpp | 22 +- src/managers/animation/AnimationManager.cpp | 351 ++++++++++++++ src/managers/animation/AnimationManager.hpp | 58 +++ src/managers/animation/DesktopAnimationManager.cpp | 491 +++++++++++++++++++ src/managers/animation/DesktopAnimationManager.hpp | 26 ++ src/managers/input/InputManager.cpp | 51 ++ src/managers/input/InputManager.hpp | 16 +- src/managers/input/Swipe.cpp | 348 -------------- src/managers/input/Touch.cpp | 35 +- .../input/UnifiedWorkspaceSwipeGesture.cpp | 313 +++++++++++++ .../input/UnifiedWorkspaceSwipeGesture.hpp | 28 ++ src/managers/input/trackpad/GestureTypes.hpp | 17 + src/managers/input/trackpad/TrackpadGestures.cpp | 221 +++++++++ src/managers/input/trackpad/TrackpadGestures.hpp | 43 ++ .../input/trackpad/gestures/CloseGesture.cpp | 145 ++++++ .../input/trackpad/gestures/CloseGesture.hpp | 23 + .../input/trackpad/gestures/DispatcherGesture.cpp | 22 + .../input/trackpad/gestures/DispatcherGesture.hpp | 16 + .../input/trackpad/gestures/FloatGesture.cpp | 88 ++++ .../input/trackpad/gestures/FloatGesture.hpp | 30 ++ .../input/trackpad/gestures/FullscreenGesture.cpp | 97 ++++ .../input/trackpad/gestures/FullscreenGesture.hpp | 33 ++ .../input/trackpad/gestures/ITrackpadGesture.cpp | 40 ++ .../input/trackpad/gestures/ITrackpadGesture.hpp | 43 ++ .../input/trackpad/gestures/MoveGesture.cpp | 66 +++ .../input/trackpad/gestures/MoveGesture.hpp | 19 + .../input/trackpad/gestures/ResizeGesture.cpp | 29 ++ .../input/trackpad/gestures/ResizeGesture.hpp | 18 + .../trackpad/gestures/SpecialWorkspaceGesture.cpp | 126 +++++ .../trackpad/gestures/SpecialWorkspaceGesture.hpp | 27 ++ .../trackpad/gestures/WorkspaceSwipeGesture.cpp | 50 ++ .../trackpad/gestures/WorkspaceSwipeGesture.hpp | 16 + 35 files changed, 2527 insertions(+), 968 deletions(-) delete mode 100644 src/managers/AnimationManager.cpp delete mode 100644 src/managers/AnimationManager.hpp create mode 100644 src/managers/animation/AnimationManager.cpp create mode 100644 src/managers/animation/AnimationManager.hpp create mode 100644 src/managers/animation/DesktopAnimationManager.cpp create mode 100644 src/managers/animation/DesktopAnimationManager.hpp delete mode 100644 src/managers/input/Swipe.cpp create mode 100644 src/managers/input/UnifiedWorkspaceSwipeGesture.cpp create mode 100644 src/managers/input/UnifiedWorkspaceSwipeGesture.hpp create mode 100644 src/managers/input/trackpad/GestureTypes.hpp create mode 100644 src/managers/input/trackpad/TrackpadGestures.cpp create mode 100644 src/managers/input/trackpad/TrackpadGestures.hpp create mode 100644 src/managers/input/trackpad/gestures/CloseGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/CloseGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/DispatcherGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/DispatcherGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/FloatGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/FloatGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/FullscreenGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/FullscreenGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/ITrackpadGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/ITrackpadGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/MoveGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/MoveGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/ResizeGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/ResizeGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp create mode 100644 src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp create mode 100644 src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp (limited to 'src/managers') diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp deleted file mode 100644 index aac4a869..00000000 --- a/src/managers/AnimationManager.cpp +++ /dev/null @@ -1,518 +0,0 @@ -#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 -#include -#include - -static int wlTick(SP self, void* data) { - if (g_pAnimationManager) - g_pAnimationManager->onTicked(); - - if (g_pCompositor->m_sessionActive && g_pAnimationManager && g_pHookSystem && !g_pCompositor->m_unsafeState && - std::ranges::any_of(g_pCompositor->m_monitors, [](const auto& mon) { return mon->m_enabled && mon->m_output; })) { - g_pAnimationManager->tick(); - EMIT_HOOK_EVENT("tick", nullptr); - } - - if (g_pAnimationManager && g_pAnimationManager->shouldTickForNext()) - g_pAnimationManager->scheduleTick(); - - return 0; -} - -CHyprAnimationManager::CHyprAnimationManager() { - m_animationTimer = makeShared(std::chrono::microseconds(500), wlTick, nullptr); - if (g_pEventLoopManager) // null in --verify-config mode - g_pEventLoopManager->addTimer(m_animationTimer); - - addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); -} - -template -static void updateVariable(CAnimatedVariable& av, const float POINTY, bool warp = false) { - if (warp || av.value() == av.goal()) { - av.warp(true, false); - return; - } - - const auto DELTA = av.goal() - av.begun(); - av.value() = av.begun() + DELTA * POINTY; -} - -static void updateColorVariable(CAnimatedVariable& av, const float POINTY, bool warp) { - if (warp || av.value() == av.goal()) { - av.warp(true, false); - return; - } - - // convert both to OkLab, then lerp that, and convert back. - // This is not as fast as just lerping rgb, but it's WAY more precise... - // Use the CHyprColor cache for OkLab - - const auto& L1 = av.begun().asOkLab(); - const auto& L2 = av.goal().asOkLab(); - - static const auto lerp = [](const float one, const float two, const float progress) -> float { return one + ((two - one) * progress); }; - - const Hyprgraphics::CColor lerped = Hyprgraphics::CColor::SOkLab{ - .l = lerp(L1.l, L2.l, POINTY), - .a = lerp(L1.a, L2.a, POINTY), - .b = lerp(L1.b, L2.b, POINTY), - }; - - av.value() = {lerped, lerp(av.begun().a, av.goal().a, POINTY)}; -} - -template -static void handleUpdate(CAnimatedVariable& av, bool warp) { - PHLWINDOW PWINDOW = av.m_Context.pWindow.lock(); - PHLWORKSPACE PWORKSPACE = av.m_Context.pWorkspace.lock(); - PHLLS PLAYER = av.m_Context.pLayer.lock(); - PHLMONITOR PMONITOR = nullptr; - bool animationsDisabled = warp; - - if (PWINDOW) { - if (av.m_Context.eDamagePolicy == AVARDAMAGE_ENTIRE) - g_pHyprRenderer->damageWindow(PWINDOW); - else if (av.m_Context.eDamagePolicy == AVARDAMAGE_BORDER) { - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); - PDECO->damageEntire(); - } else if (av.m_Context.eDamagePolicy == AVARDAMAGE_SHADOW) { - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); - PDECO->damageEntire(); - } - - PMONITOR = PWINDOW->m_monitor.lock(); - if (!PMONITOR) - return; - - animationsDisabled = PWINDOW->m_windowData.noAnim.valueOr(animationsDisabled); - } else if (PWORKSPACE) { - PMONITOR = PWORKSPACE->m_monitor.lock(); - if (!PMONITOR) - return; - - // don't damage the whole monitor on workspace change, unless it's a special workspace, because dim/blur etc - if (PWORKSPACE->m_isSpecialWorkspace) - g_pHyprRenderer->damageMonitor(PMONITOR); - - // TODO: just make this into a damn callback already vax... - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped || w->isHidden() || w->m_workspace != PWORKSPACE) - continue; - - if (w->m_isFloating && !w->m_pinned) { - // still doing the full damage hack for floating because sometimes when the window - // goes through multiple monitors the last rendered frame is missing damage somehow?? - const CBox windowBoxNoOffset = w->getFullWindowBoundingBox(); - const CBox monitorBox = {PMONITOR->m_position, PMONITOR->m_size}; - if (windowBoxNoOffset.intersection(monitorBox) != windowBoxNoOffset) // on edges between multiple monitors - g_pHyprRenderer->damageWindow(w, true); - } - - if (PWORKSPACE->m_isSpecialWorkspace) - g_pHyprRenderer->damageWindow(w, true); // hack for special too because it can cross multiple monitors - } - - // damage any workspace window that is on any monitor - for (auto const& w : g_pCompositor->m_windows) { - if (!validMapped(w) || w->m_workspace != PWORKSPACE || w->m_pinned) - continue; - - g_pHyprRenderer->damageWindow(w); - } - } else if (PLAYER) { - // "some fucking layers miss 1 pixel???" -- vaxry - CBox expandBox = CBox{PLAYER->m_realPosition->value(), PLAYER->m_realSize->value()}; - expandBox.expand(5); - g_pHyprRenderer->damageBox(expandBox); - - PMONITOR = g_pCompositor->getMonitorFromVector(PLAYER->m_realPosition->goal() + PLAYER->m_realSize->goal() / 2.F); - if (!PMONITOR) - return; - animationsDisabled = animationsDisabled || PLAYER->m_noAnimations; - } - - const auto SPENT = av.getPercent(); - const auto PBEZIER = g_pAnimationManager->getBezier(av.getBezierName()); - const auto POINTY = PBEZIER->getYForPoint(SPENT); - const bool WARP = animationsDisabled || SPENT >= 1.f; - - if constexpr (std::same_as) - updateColorVariable(av, POINTY, WARP); - else - updateVariable(av, POINTY, WARP); - - av.onUpdate(); - - switch (av.m_Context.eDamagePolicy) { - case AVARDAMAGE_ENTIRE: { - if (PWINDOW) { - PWINDOW->updateWindowDecos(); - g_pHyprRenderer->damageWindow(PWINDOW); - } else if (PWORKSPACE) { - for (auto const& w : g_pCompositor->m_windows) { - if (!validMapped(w) || w->m_workspace != PWORKSPACE) - continue; - - w->updateWindowDecos(); - - // damage any workspace window that is on any monitor - if (!w->m_pinned) - g_pHyprRenderer->damageWindow(w); - } - } else if (PLAYER) { - if (PLAYER->m_layer <= 1) - g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); - - // some fucking layers miss 1 pixel??? - CBox expandBox = CBox{PLAYER->m_realPosition->value(), PLAYER->m_realSize->value()}; - expandBox.expand(5); - g_pHyprRenderer->damageBox(expandBox); - } - break; - } - case AVARDAMAGE_BORDER: { - RASSERT(PWINDOW, "Tried to AVARDAMAGE_BORDER a non-window AVAR!"); - - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); - PDECO->damageEntire(); - - break; - } - case AVARDAMAGE_SHADOW: { - RASSERT(PWINDOW, "Tried to AVARDAMAGE_SHADOW a non-window AVAR!"); - - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); - - PDECO->damageEntire(); - - break; - } - default: { - break; - } - } - - // manually schedule a frame - if (PMONITOR) - g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_ANIMATION); -} - -void CHyprAnimationManager::tick() { - static std::chrono::time_point lastTick = std::chrono::high_resolution_clock::now(); - m_lastTickTimeMs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastTick).count() / 1000.0; - lastTick = std::chrono::high_resolution_clock::now(); - - static auto PANIMENABLED = CConfigValue("animations:enabled"); - - for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) { - const auto PAV = m_vActiveAnimatedVariables[i].lock(); - if (!PAV) - continue; - - // for disabled anims just warp - bool warp = !*PANIMENABLED || !PAV->enabled(); - - switch (PAV->m_Type) { - case AVARTYPE_FLOAT: { - auto pTypedAV = dc*>(PAV.get()); - RASSERT(pTypedAV, "Failed to upcast animated float"); - handleUpdate(*pTypedAV, warp); - } break; - case AVARTYPE_VECTOR: { - auto pTypedAV = dc*>(PAV.get()); - RASSERT(pTypedAV, "Failed to upcast animated Vector2D"); - handleUpdate(*pTypedAV, warp); - } break; - case AVARTYPE_COLOR: { - auto pTypedAV = dc*>(PAV.get()); - RASSERT(pTypedAV, "Failed to upcast animated CHyprColor"); - handleUpdate(*pTypedAV, warp); - } break; - default: UNREACHABLE(); - } - } - - tickDone(); -} - -void CHyprAnimationManager::scheduleTick() { - if (m_tickScheduled) - return; - - m_tickScheduled = true; - - const auto PMOSTHZ = g_pHyprRenderer->m_mostHzMonitor; - - if (!PMOSTHZ) { - m_animationTimer->updateTimeout(std::chrono::milliseconds(16)); - return; - } - - float refreshDelayMs = std::floor(1000.f / PMOSTHZ->m_refreshRate); - - const float SINCEPRES = std::chrono::duration_cast(Time::steadyNow() - PMOSTHZ->m_lastPresentationTimer.chrono()).count() / 1000.F; - - const auto TOPRES = std::clamp(refreshDelayMs - SINCEPRES, 1.1f, 1000.f); // we can't send 0, that will disarm it - - m_animationTimer->updateTimeout(std::chrono::milliseconds(sc(std::floor(TOPRES)))); -} - -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") - return ""; - else if (style.starts_with("popin")) { - // try parsing - 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) { return "invalid minperc"; } - - return ""; - } - - minPerc; // fix warning - - return ""; - } - - return "unknown style"; - } else if (config.starts_with("workspaces") || config.starts_with("specialWorkspace")) { - if (style == "slide" || style == "slidevert" || style == "fade") - return ""; - else if (style.starts_with("slidefade")) { - // try parsing - float movePerc = 0.f; - if (style.find('%') != std::string::npos) { - try { - auto percstr = style.substr(style.find_last_of(' ') + 1); - movePerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { return "invalid movePerc"; } - - return ""; - } - - movePerc; // fix warning - - return ""; - } - - return "unknown style"; - } else if (config == "borderangle") { - if (style == "loop" || style == "once") - return ""; - return "unknown style"; - } else if (config.starts_with("layers")) { - if (style.empty() || style == "fade" || style == "slide") - return ""; - else if (style.starts_with("popin")) { - // try parsing - 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) { return "invalid minperc"; } - - return ""; - } - - minPerc; // fix warning - - return ""; - } - return ""; - return "unknown style"; - } else { - return "animation has no styles"; - } - - return ""; -} diff --git a/src/managers/AnimationManager.hpp b/src/managers/AnimationManager.hpp deleted file mode 100644 index b5e42036..00000000 --- a/src/managers/AnimationManager.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include - -#include "../defines.hpp" -#include "../helpers/AnimatedVariable.hpp" -#include "../desktop/DesktopTypes.hpp" -#include "eventLoop/EventLoopTimer.hpp" - -class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager { - public: - CHyprAnimationManager(); - - void tick(); - virtual void scheduleTick(); - virtual void onTicked(); - - using SAnimationPropertyConfig = Hyprutils::Animation::SAnimationPropertyConfig; - template - void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, eAVarDamagePolicy policy) { - constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType; - const auto PAV = makeShared>(); - - PAV->create(EAVTYPE, sc(this), PAV, v); - PAV->setConfig(pConfig); - PAV->m_Context.eDamagePolicy = policy; - - pav = std::move(PAV); - } - - template - void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, PHLWINDOW pWindow, eAVarDamagePolicy policy) { - createAnimation(v, pav, pConfig, policy); - pav->m_Context.pWindow = pWindow; - } - template - void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, PHLWORKSPACE pWorkspace, eAVarDamagePolicy policy) { - createAnimation(v, pav, pConfig, policy); - pav->m_Context.pWorkspace = pWorkspace; - } - template - void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, PHLLS pLayer, eAVarDamagePolicy policy) { - createAnimation(v, pav, pConfig, policy); - pav->m_Context.pLayer = pLayer; - } - - void onWindowPostCreateClose(PHLWINDOW, bool close = false); - - std::string styleValidInConfigVar(const std::string&, const std::string&); - - SP m_animationTimer; - - float m_lastTickTimeMs; - - 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 g_pAnimationManager; 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 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 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 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 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 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 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 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/animation/AnimationManager.cpp b/src/managers/animation/AnimationManager.cpp new file mode 100644 index 00000000..6675216a --- /dev/null +++ b/src/managers/animation/AnimationManager.cpp @@ -0,0 +1,351 @@ +#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 +#include +#include + +static int wlTick(SP self, void* data) { + if (g_pAnimationManager) + g_pAnimationManager->onTicked(); + + if (g_pCompositor->m_sessionActive && g_pAnimationManager && g_pHookSystem && !g_pCompositor->m_unsafeState && + std::ranges::any_of(g_pCompositor->m_monitors, [](const auto& mon) { return mon->m_enabled && mon->m_output; })) { + g_pAnimationManager->tick(); + EMIT_HOOK_EVENT("tick", nullptr); + } + + if (g_pAnimationManager && g_pAnimationManager->shouldTickForNext()) + g_pAnimationManager->scheduleTick(); + + return 0; +} + +CHyprAnimationManager::CHyprAnimationManager() { + m_animationTimer = makeShared(std::chrono::microseconds(500), wlTick, nullptr); + if (g_pEventLoopManager) // null in --verify-config mode + g_pEventLoopManager->addTimer(m_animationTimer); + + addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); +} + +template +static void updateVariable(CAnimatedVariable& av, const float POINTY, bool warp = false) { + if (warp || av.value() == av.goal()) { + av.warp(true, false); + return; + } + + const auto DELTA = av.goal() - av.begun(); + av.value() = av.begun() + DELTA * POINTY; +} + +static void updateColorVariable(CAnimatedVariable& av, const float POINTY, bool warp) { + if (warp || av.value() == av.goal()) { + av.warp(true, false); + return; + } + + // convert both to OkLab, then lerp that, and convert back. + // This is not as fast as just lerping rgb, but it's WAY more precise... + // Use the CHyprColor cache for OkLab + + const auto& L1 = av.begun().asOkLab(); + const auto& L2 = av.goal().asOkLab(); + + static const auto lerp = [](const float one, const float two, const float progress) -> float { return one + ((two - one) * progress); }; + + const Hyprgraphics::CColor lerped = Hyprgraphics::CColor::SOkLab{ + .l = lerp(L1.l, L2.l, POINTY), + .a = lerp(L1.a, L2.a, POINTY), + .b = lerp(L1.b, L2.b, POINTY), + }; + + av.value() = {lerped, lerp(av.begun().a, av.goal().a, POINTY)}; +} + +template +static void handleUpdate(CAnimatedVariable& av, bool warp) { + PHLWINDOW PWINDOW = av.m_Context.pWindow.lock(); + PHLWORKSPACE PWORKSPACE = av.m_Context.pWorkspace.lock(); + PHLLS PLAYER = av.m_Context.pLayer.lock(); + PHLMONITOR PMONITOR = nullptr; + bool animationsDisabled = warp; + + if (PWINDOW) { + if (av.m_Context.eDamagePolicy == AVARDAMAGE_ENTIRE) + g_pHyprRenderer->damageWindow(PWINDOW); + else if (av.m_Context.eDamagePolicy == AVARDAMAGE_BORDER) { + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); + PDECO->damageEntire(); + } else if (av.m_Context.eDamagePolicy == AVARDAMAGE_SHADOW) { + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); + PDECO->damageEntire(); + } + + PMONITOR = PWINDOW->m_monitor.lock(); + if (!PMONITOR) + return; + + animationsDisabled = PWINDOW->m_windowData.noAnim.valueOr(animationsDisabled); + } else if (PWORKSPACE) { + PMONITOR = PWORKSPACE->m_monitor.lock(); + if (!PMONITOR) + return; + + // don't damage the whole monitor on workspace change, unless it's a special workspace, because dim/blur etc + if (PWORKSPACE->m_isSpecialWorkspace) + g_pHyprRenderer->damageMonitor(PMONITOR); + + // TODO: just make this into a damn callback already vax... + for (auto const& w : g_pCompositor->m_windows) { + if (!w->m_isMapped || w->isHidden() || w->m_workspace != PWORKSPACE) + continue; + + if (w->m_isFloating && !w->m_pinned) { + // still doing the full damage hack for floating because sometimes when the window + // goes through multiple monitors the last rendered frame is missing damage somehow?? + const CBox windowBoxNoOffset = w->getFullWindowBoundingBox(); + const CBox monitorBox = {PMONITOR->m_position, PMONITOR->m_size}; + if (windowBoxNoOffset.intersection(monitorBox) != windowBoxNoOffset) // on edges between multiple monitors + g_pHyprRenderer->damageWindow(w, true); + } + + if (PWORKSPACE->m_isSpecialWorkspace) + g_pHyprRenderer->damageWindow(w, true); // hack for special too because it can cross multiple monitors + } + + // damage any workspace window that is on any monitor + for (auto const& w : g_pCompositor->m_windows) { + if (!validMapped(w) || w->m_workspace != PWORKSPACE || w->m_pinned) + continue; + + g_pHyprRenderer->damageWindow(w); + } + } else if (PLAYER) { + // "some fucking layers miss 1 pixel???" -- vaxry + CBox expandBox = CBox{PLAYER->m_realPosition->value(), PLAYER->m_realSize->value()}; + expandBox.expand(5); + g_pHyprRenderer->damageBox(expandBox); + + PMONITOR = g_pCompositor->getMonitorFromVector(PLAYER->m_realPosition->goal() + PLAYER->m_realSize->goal() / 2.F); + if (!PMONITOR) + return; + animationsDisabled = animationsDisabled || PLAYER->m_noAnimations; + } + + const auto SPENT = av.getPercent(); + const auto PBEZIER = g_pAnimationManager->getBezier(av.getBezierName()); + const auto POINTY = PBEZIER->getYForPoint(SPENT); + const bool WARP = animationsDisabled || SPENT >= 1.f; + + if constexpr (std::same_as) + updateColorVariable(av, POINTY, WARP); + else + updateVariable(av, POINTY, WARP); + + av.onUpdate(); + + switch (av.m_Context.eDamagePolicy) { + case AVARDAMAGE_ENTIRE: { + if (PWINDOW) { + PWINDOW->updateWindowDecos(); + g_pHyprRenderer->damageWindow(PWINDOW); + } else if (PWORKSPACE) { + for (auto const& w : g_pCompositor->m_windows) { + if (!validMapped(w) || w->m_workspace != PWORKSPACE) + continue; + + w->updateWindowDecos(); + + // damage any workspace window that is on any monitor + if (!w->m_pinned) + g_pHyprRenderer->damageWindow(w); + } + } else if (PLAYER) { + if (PLAYER->m_layer <= 1) + g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); + + // some fucking layers miss 1 pixel??? + CBox expandBox = CBox{PLAYER->m_realPosition->value(), PLAYER->m_realSize->value()}; + expandBox.expand(5); + g_pHyprRenderer->damageBox(expandBox); + } + break; + } + case AVARDAMAGE_BORDER: { + RASSERT(PWINDOW, "Tried to AVARDAMAGE_BORDER a non-window AVAR!"); + + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); + PDECO->damageEntire(); + + break; + } + case AVARDAMAGE_SHADOW: { + RASSERT(PWINDOW, "Tried to AVARDAMAGE_SHADOW a non-window AVAR!"); + + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); + + PDECO->damageEntire(); + + break; + } + default: { + break; + } + } + + // manually schedule a frame + if (PMONITOR) + g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_ANIMATION); +} + +void CHyprAnimationManager::tick() { + static std::chrono::time_point lastTick = std::chrono::high_resolution_clock::now(); + m_lastTickTimeMs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastTick).count() / 1000.0; + lastTick = std::chrono::high_resolution_clock::now(); + + static auto PANIMENABLED = CConfigValue("animations:enabled"); + + for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) { + const auto PAV = m_vActiveAnimatedVariables[i].lock(); + if (!PAV) + continue; + + // for disabled anims just warp + bool warp = !*PANIMENABLED || !PAV->enabled(); + + switch (PAV->m_Type) { + case AVARTYPE_FLOAT: { + auto pTypedAV = dc*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated float"); + handleUpdate(*pTypedAV, warp); + } break; + case AVARTYPE_VECTOR: { + auto pTypedAV = dc*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated Vector2D"); + handleUpdate(*pTypedAV, warp); + } break; + case AVARTYPE_COLOR: { + auto pTypedAV = dc*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated CHyprColor"); + handleUpdate(*pTypedAV, warp); + } break; + default: UNREACHABLE(); + } + } + + tickDone(); +} + +void CHyprAnimationManager::scheduleTick() { + if (m_tickScheduled) + return; + + m_tickScheduled = true; + + const auto PMOSTHZ = g_pHyprRenderer->m_mostHzMonitor; + + if (!PMOSTHZ) { + m_animationTimer->updateTimeout(std::chrono::milliseconds(16)); + return; + } + + float refreshDelayMs = std::floor(1000.f / PMOSTHZ->m_refreshRate); + + const float SINCEPRES = std::chrono::duration_cast(Time::steadyNow() - PMOSTHZ->m_lastPresentationTimer.chrono()).count() / 1000.F; + + const auto TOPRES = std::clamp(refreshDelayMs - SINCEPRES, 1.1f, 1000.f); // we can't send 0, that will disarm it + + m_animationTimer->updateTimeout(std::chrono::milliseconds(sc(std::floor(TOPRES)))); +} + +void CHyprAnimationManager::onTicked() { + m_tickScheduled = false; +} + +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") + return ""; + else if (style.starts_with("popin")) { + // try parsing + 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) { return "invalid minperc"; } + + return ""; + } + + minPerc; // fix warning + + return ""; + } + + return "unknown style"; + } else if (config.starts_with("workspaces") || config.starts_with("specialWorkspace")) { + if (style == "slide" || style == "slidevert" || style == "fade") + return ""; + else if (style.starts_with("slidefade")) { + // try parsing + float movePerc = 0.f; + if (style.find('%') != std::string::npos) { + try { + auto percstr = style.substr(style.find_last_of(' ') + 1); + movePerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { return "invalid movePerc"; } + + return ""; + } + + movePerc; // fix warning + + return ""; + } + + return "unknown style"; + } else if (config == "borderangle") { + if (style == "loop" || style == "once") + return ""; + return "unknown style"; + } else if (config.starts_with("layers")) { + if (style.empty() || style == "fade" || style == "slide") + return ""; + else if (style.starts_with("popin")) { + // try parsing + 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) { return "invalid minperc"; } + + return ""; + } + + minPerc; // fix warning + + return ""; + } + return ""; + return "unknown style"; + } else { + return "animation has no styles"; + } + + return ""; +} diff --git a/src/managers/animation/AnimationManager.hpp b/src/managers/animation/AnimationManager.hpp new file mode 100644 index 00000000..0631e7c7 --- /dev/null +++ b/src/managers/animation/AnimationManager.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#include "../../defines.hpp" +#include "../../helpers/AnimatedVariable.hpp" +#include "../../desktop/DesktopTypes.hpp" +#include "../eventLoop/EventLoopTimer.hpp" + +class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager { + public: + CHyprAnimationManager(); + + void tick(); + virtual void scheduleTick(); + virtual void onTicked(); + + using SAnimationPropertyConfig = Hyprutils::Animation::SAnimationPropertyConfig; + template + void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, eAVarDamagePolicy policy) { + constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType; + const auto PAV = makeShared>(); + + PAV->create(EAVTYPE, sc(this), PAV, v); + PAV->setConfig(pConfig); + PAV->m_Context.eDamagePolicy = policy; + + pav = std::move(PAV); + } + + template + void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, PHLWINDOW pWindow, eAVarDamagePolicy policy) { + createAnimation(v, pav, pConfig, policy); + pav->m_Context.pWindow = pWindow; + } + template + void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, PHLWORKSPACE pWorkspace, eAVarDamagePolicy policy) { + createAnimation(v, pav, pConfig, policy); + pav->m_Context.pWorkspace = pWorkspace; + } + template + void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, PHLLS pLayer, eAVarDamagePolicy policy) { + createAnimation(v, pav, pConfig, policy); + pav->m_Context.pLayer = pLayer; + } + + std::string styleValidInConfigVar(const std::string&, const std::string&); + + SP m_animationTimer; + + float m_lastTickTimeMs; + + private: + bool m_tickScheduled = false; +}; + +inline UP 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 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::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("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 g_pDesktopAnimationManager = makeUnique(); 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 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> 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 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("gestures:workspace_swipe"); - static auto PSWIPEFINGERS = CConfigValue("gestures:workspace_swipe_fingers"); - static auto PSWIPEMINFINGERS = CConfigValue("gestures:workspace_swipe_min_fingers"); - static auto PSWIPENEW = CConfigValue("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("gestures:workspace_swipe_cancel_ratio"); - static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); - static auto PSWIPEFORC = CConfigValue("gestures:workspace_swipe_min_speed_to_force"); - static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); - static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); - static auto PWORKSPACEGAP = CConfigValue("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(1LL), sc(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("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("gestures:workspace_swipe_distance"); - static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); - static auto PSWIPEDIRLOCK = CConfigValue("gestures:workspace_swipe_direction_lock"); - static auto PSWIPEDIRLOCKTHRESHOLD = CConfigValue("gestures:workspace_swipe_direction_lock_threshold"); - static auto PSWIPEFOREVER = CConfigValue("gestures:workspace_swipe_forever"); - static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); - static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); - - const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc(1LL), sc(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(-SWIPEDISTANCE), sc(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("gestures:workspace_swipe_touch_invert"); static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc(1LL), sc(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("gestures:workspace_swipe_distance"); + static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); + static auto PSWIPEDIRLOCK = CConfigValue("gestures:workspace_swipe_direction_lock"); + static auto PSWIPEDIRLOCKTHRESHOLD = CConfigValue("gestures:workspace_swipe_direction_lock_threshold"); + static auto PSWIPEFOREVER = CConfigValue("gestures:workspace_swipe_forever"); + static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); + static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); + + const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc(1LL), sc(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(-SWIPEDISTANCE), sc(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("gestures:workspace_swipe_cancel_ratio"); + static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); + static auto PSWIPEFORC = CConfigValue("gestures:workspace_swipe_min_speed_to_force"); + static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); + static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); + static auto PWORKSPACEGAP = CConfigValue("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(1LL), sc(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 g_pUnifiedWorkspaceSwipe = makeUnique(); 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 + +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 + +void CTrackpadGestures::clearGestures() { + m_gestures.clear(); +} + +eTrackpadGestureDirection CTrackpadGestures::dirForString(const std::string_view& s) { + std::string lc = std::string{s}; + std::ranges::transform(lc, lc.begin(), ::tolower); + + if (lc == "swipe") + return TRACKPAD_GESTURE_DIR_SWIPE; + if (lc == "left" || lc == "l") + return TRACKPAD_GESTURE_DIR_LEFT; + if (lc == "right" || lc == "r") + return TRACKPAD_GESTURE_DIR_RIGHT; + if (lc == "up" || lc == "u" || lc == "top" || lc == "t") + return TRACKPAD_GESTURE_DIR_UP; + if (lc == "down" || lc == "d" || lc == "bottom" || lc == "b") + return TRACKPAD_GESTURE_DIR_DOWN; + if (lc == "horizontal" || lc == "horiz") + return TRACKPAD_GESTURE_DIR_HORIZONTAL; + if (lc == "vertical" || lc == "vert") + return TRACKPAD_GESTURE_DIR_VERTICAL; + if (lc == "pinch") + return TRACKPAD_GESTURE_DIR_PINCH; + if (lc == "pinchin" || lc == "zoomin") + return TRACKPAD_GESTURE_DIR_PINCH_IN; + if (lc == "pinchout" || lc == "zoomout") + return TRACKPAD_GESTURE_DIR_PINCH_OUT; + + return TRACKPAD_GESTURE_DIR_NONE; +} + +const char* CTrackpadGestures::stringForDir(eTrackpadGestureDirection dir) { + switch (dir) { + case TRACKPAD_GESTURE_DIR_HORIZONTAL: return "HORIZONTAL"; + case TRACKPAD_GESTURE_DIR_VERTICAL: return "VERTICAL"; + case TRACKPAD_GESTURE_DIR_LEFT: return "LEFT"; + case TRACKPAD_GESTURE_DIR_RIGHT: return "RIGHT"; + case TRACKPAD_GESTURE_DIR_UP: return "UP"; + case TRACKPAD_GESTURE_DIR_DOWN: return "DOWN"; + case TRACKPAD_GESTURE_DIR_SWIPE: return "SWIPE"; + case TRACKPAD_GESTURE_DIR_PINCH: return "PINCH"; + case TRACKPAD_GESTURE_DIR_PINCH_IN: return "PINCH_IN"; + case TRACKPAD_GESTURE_DIR_PINCH_OUT: return "PINCH_OUT"; + default: return "ERROR"; + } + return "ERROR"; +} + +std::expected CTrackpadGestures::addGesture(UP&& gesture, size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, + float deltaScale) { + for (const auto& g : m_gestures) { + if (g->fingerCount != fingerCount) + continue; + + if (g->modMask != modMask) + continue; + + eTrackpadGestureDirection axis = TRACKPAD_GESTURE_DIR_NONE; + switch (direction) { + case TRACKPAD_GESTURE_DIR_UP: + case TRACKPAD_GESTURE_DIR_DOWN: + case TRACKPAD_GESTURE_DIR_VERTICAL: axis = TRACKPAD_GESTURE_DIR_VERTICAL; break; + case TRACKPAD_GESTURE_DIR_LEFT: + case TRACKPAD_GESTURE_DIR_RIGHT: + case TRACKPAD_GESTURE_DIR_HORIZONTAL: axis = TRACKPAD_GESTURE_DIR_HORIZONTAL; break; + case TRACKPAD_GESTURE_DIR_SWIPE: axis = TRACKPAD_GESTURE_DIR_SWIPE; break; + case TRACKPAD_GESTURE_DIR_PINCH: + case TRACKPAD_GESTURE_DIR_PINCH_IN: + case TRACKPAD_GESTURE_DIR_PINCH_OUT: axis = TRACKPAD_GESTURE_DIR_PINCH; break; + default: TRACKPAD_GESTURE_DIR_NONE; break; + } + + if (g->direction == axis || g->direction == direction || + ((axis == TRACKPAD_GESTURE_DIR_VERTICAL || axis == TRACKPAD_GESTURE_DIR_HORIZONTAL) && g->direction == TRACKPAD_GESTURE_DIR_SWIPE)) { + return std::unexpected( + std::format("Gesture will be overshadowed by a previous gesture. Previous {} shadows new {}", stringForDir(g->direction), stringForDir(direction))); + } + } + + m_gestures.emplace_back(makeShared(std::move(gesture), fingerCount, modMask, direction, deltaScale)); + + 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 +#include + +class CTrackpadGestures { + public: + void clearGestures(); + std::expected addGesture(UP&& 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 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> m_gestures; + + SP m_activeGesture = nullptr; + bool m_gestureFindFailed = false; +}; + +inline UP g_pTrackpadGestures = makeUnique(); 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> 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("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( + std::chrono::milliseconds(*PTIMEOUT), + [window = m_window](SP 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 +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("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("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(); +}; -- cgit v1.2.3