diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2025-08-28 11:20:29 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-28 11:20:29 +0200 |
| commit | 81bf4eccba449bfe2b6adfb51260108aec710d4f (patch) | |
| tree | 215c5b536cbcf1df9888081aadd3de16eb4b3ebe /src/managers/animation | |
| parent | 378e130f1426648d8d734049800128f9882805bf (diff) | |
input: Add fully configurable trackpad gestures (#11490)
Adds configurable trackpad gestures
Diffstat (limited to 'src/managers/animation')
| -rw-r--r-- | src/managers/animation/AnimationManager.cpp | 351 | ||||
| -rw-r--r-- | src/managers/animation/AnimationManager.hpp | 58 | ||||
| -rw-r--r-- | src/managers/animation/DesktopAnimationManager.cpp | 491 | ||||
| -rw-r--r-- | src/managers/animation/DesktopAnimationManager.hpp | 26 |
4 files changed, 926 insertions, 0 deletions
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 <hyprgraphics/color/Color.hpp> +#include <hyprutils/animation/AnimatedVariable.hpp> +#include <hyprutils/animation/AnimationManager.hpp> + +static int wlTick(SP<CEventLoopTimer> 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<CEventLoopTimer>(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 <Animable VarType> +static void updateVariable(CAnimatedVariable<VarType>& 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<CHyprColor>& 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 <Animable VarType> +static void handleUpdate(CAnimatedVariable<VarType>& 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<VarType, CHyprColor>) + updateColorVariable(av, POINTY, WARP); + else + updateVariable<VarType>(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::microseconds>(std::chrono::high_resolution_clock::now() - lastTick).count() / 1000.0; + lastTick = std::chrono::high_resolution_clock::now(); + + static auto PANIMENABLED = CConfigValue<Hyprlang::INT>("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<CAnimatedVariable<float>*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated float"); + handleUpdate(*pTypedAV, warp); + } break; + case AVARTYPE_VECTOR: { + auto pTypedAV = dc<CAnimatedVariable<Vector2D>*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated Vector2D"); + handleUpdate(*pTypedAV, warp); + } break; + case AVARTYPE_COLOR: { + auto pTypedAV = dc<CAnimatedVariable<CHyprColor>*>(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<std::chrono::microseconds>(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<int>(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 <hyprutils/animation/AnimationManager.hpp> +#include <hyprutils/animation/AnimatedVariable.hpp> + +#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 <Animable VarType> + void createAnimation(const VarType& v, PHLANIMVAR<VarType>& pav, SP<SAnimationPropertyConfig> pConfig, eAVarDamagePolicy policy) { + constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType<VarType>; + const auto PAV = makeShared<CAnimatedVariable<VarType>>(); + + PAV->create(EAVTYPE, sc<Hyprutils::Animation::CAnimationManager*>(this), PAV, v); + PAV->setConfig(pConfig); + PAV->m_Context.eDamagePolicy = policy; + + pav = std::move(PAV); + } + + template <Animable VarType> + void createAnimation(const VarType& v, PHLANIMVAR<VarType>& pav, SP<SAnimationPropertyConfig> pConfig, PHLWINDOW pWindow, eAVarDamagePolicy policy) { + createAnimation(v, pav, pConfig, policy); + pav->m_Context.pWindow = pWindow; + } + template <Animable VarType> + void createAnimation(const VarType& v, PHLANIMVAR<VarType>& pav, SP<SAnimationPropertyConfig> pConfig, PHLWORKSPACE pWorkspace, eAVarDamagePolicy policy) { + createAnimation(v, pav, pConfig, policy); + pav->m_Context.pWorkspace = pWorkspace; + } + template <Animable VarType> + void createAnimation(const VarType& v, PHLANIMVAR<VarType>& pav, SP<SAnimationPropertyConfig> 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<CEventLoopTimer> m_animationTimer; + + float m_lastTickTimeMs; + + private: + bool m_tickScheduled = false; +}; + +inline UP<CHyprAnimationManager> g_pAnimationManager; diff --git a/src/managers/animation/DesktopAnimationManager.cpp b/src/managers/animation/DesktopAnimationManager.cpp new file mode 100644 index 00000000..e55a0439 --- /dev/null +++ b/src/managers/animation/DesktopAnimationManager.cpp @@ -0,0 +1,491 @@ +#include "DesktopAnimationManager.hpp" + +#include "../../desktop/LayerSurface.hpp" +#include "../../desktop/Window.hpp" +#include "../../desktop/Workspace.hpp" + +#include "../../config/ConfigManager.hpp" +#include "../../Compositor.hpp" +#include "wlr-layer-shell-unstable-v1.hpp" + +void CDesktopAnimationManager::startAnimation(PHLWINDOW pWindow, eAnimationType type, bool force) { + const bool CLOSE = type == ANIMATION_TYPE_OUT; + + if (CLOSE) + *pWindow->m_alpha = 0.F; + else { + pWindow->m_alpha->setValueAndWarp(0.F); + *pWindow->m_alpha = 1.F; + } + + if (!CLOSE) { + pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn")); + pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn")); + pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn")); + } else { + pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut")); + pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut")); + pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeOut")); + } + + std::string ANIMSTYLE = pWindow->m_realPosition->getStyle(); + std::ranges::transform(ANIMSTYLE, ANIMSTYLE.begin(), ::tolower); + + CVarList animList(ANIMSTYLE, 0, 's'); + + // if the window is not being animated, that means the layout set a fixed size for it, don't animate. + if (!pWindow->m_realPosition->isBeingAnimated() && !pWindow->m_realSize->isBeingAnimated() && !force) + return; + + // if the animation is disabled and we are leaving, ignore the anim to prevent the snapshot being fucked + if (!pWindow->m_realPosition->enabled() && !force) + return; + + if (pWindow->m_windowData.animationStyle.hasValue()) { + const auto STYLE = pWindow->m_windowData.animationStyle.value(); + // the window has config'd special anim + if (STYLE.starts_with("slide")) { + CVarList animList2(STYLE, 0, 's'); + animationSlide(pWindow, animList2[1], CLOSE); + } else if (STYLE == "gnomed" || STYLE == "gnome") + animationGnomed(pWindow, CLOSE); + else { + // anim popin, fallback + + float minPerc = 0.f; + if (STYLE.find("%") != std::string::npos) { + try { + auto percstr = STYLE.substr(STYLE.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { + ; // oops + } + } + + animationPopin(pWindow, CLOSE, minPerc / 100.f); + } + } else { + if (animList[0] == "slide") + animationSlide(pWindow, animList[1], CLOSE); + else if (animList[0] == "gnomed" || animList[0] == "gnome") + animationGnomed(pWindow, CLOSE); + else { + // anim popin, fallback + + float minPerc = 0.f; + if (!ANIMSTYLE.starts_with("%")) { + try { + auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { + ; // oops + } + } + + animationPopin(pWindow, CLOSE, minPerc / 100.f); + } + } +} + +void CDesktopAnimationManager::startAnimation(PHLLS ls, eAnimationType type, bool instant) { + const bool IN = type == ANIMATION_TYPE_IN; + + if (IN) { + ls->m_alpha->setValueAndWarp(0.F); + *ls->m_alpha = 1.F; + } else + *ls->m_alpha = 0.F; + + if (IN) { + ls->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn")); + ls->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn")); + ls->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn")); + } else { + ls->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut")); + ls->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut")); + ls->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut")); + } + + const auto ANIMSTYLE = ls->m_animationStyle.value_or(ls->m_realPosition->getStyle()); + if (ANIMSTYLE.starts_with("slide")) { + // get closest edge + const auto MIDDLE = ls->m_geometry.middle(); + + const auto PMONITOR = g_pCompositor->getMonitorFromVector(MIDDLE); + + if (!PMONITOR) { // can rarely happen on exit + ls->m_alpha->setValueAndWarp(IN ? 1.F : 0.F); + return; + } + + int force = -1; + + CVarList args(ANIMSTYLE, 0, 's'); + if (args.size() > 1) { + const auto ARG2 = args[1]; + if (ARG2 == "top") + force = 0; + else if (ARG2 == "bottom") + force = 1; + else if (ARG2 == "left") + force = 2; + else if (ARG2 == "right") + force = 3; + } + + const std::array<Vector2D, 4> edgePoints = { + PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, 0.0}, + PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, PMONITOR->m_size.y}, + PMONITOR->m_position + Vector2D{0.0, PMONITOR->m_size.y}, + PMONITOR->m_position + Vector2D{PMONITOR->m_size.x, PMONITOR->m_size.y / 2}, + }; + + float closest = std::numeric_limits<float>::max(); + int leader = force; + if (leader == -1) { + for (size_t i = 0; i < 4; ++i) { + float dist = MIDDLE.distance(edgePoints[i]); + if (dist < closest) { + leader = i; + closest = dist; + } + } + } + + ls->m_realSize->setValueAndWarp(ls->m_geometry.size()); + + Vector2D prePos; + + switch (leader) { + case 0: + // TOP + prePos = {ls->m_geometry.x, PMONITOR->m_position.y - ls->m_geometry.h}; + break; + case 1: + // BOTTOM + prePos = {ls->m_geometry.x, PMONITOR->m_position.y + PMONITOR->m_size.y}; + break; + case 2: + // LEFT + prePos = {PMONITOR->m_position.x - ls->m_geometry.w, ls->m_geometry.y}; + break; + case 3: + // RIGHT + prePos = {PMONITOR->m_position.x + PMONITOR->m_size.x, ls->m_geometry.y}; + break; + default: UNREACHABLE(); + } + + if (IN) { + ls->m_realPosition->setValueAndWarp(prePos); + *ls->m_realPosition = ls->m_geometry.pos(); + } else { + ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos()); + *ls->m_realPosition = prePos; + } + + } else if (ANIMSTYLE.starts_with("popin")) { + float minPerc = 0.f; + if (ANIMSTYLE.find("%") != std::string::npos) { + try { + auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { + ; // oops + } + } + + minPerc *= 0.01; + + const auto GOALSIZE = (ls->m_geometry.size() * minPerc).clamp({5, 5}); + const auto GOALPOS = ls->m_geometry.pos() + (ls->m_geometry.size() - GOALSIZE) / 2.f; + + ls->m_alpha->setValueAndWarp(IN ? 0.f : 1.f); + *ls->m_alpha = IN ? 1.f : 0.f; + + if (IN) { + ls->m_realSize->setValueAndWarp(GOALSIZE); + ls->m_realPosition->setValueAndWarp(GOALPOS); + *ls->m_realSize = ls->m_geometry.size(); + *ls->m_realPosition = ls->m_geometry.pos(); + } else { + ls->m_realSize->setValueAndWarp(ls->m_geometry.size()); + ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos()); + *ls->m_realSize = GOALSIZE; + *ls->m_realPosition = GOALPOS; + } + } else { + // fade + ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos()); + ls->m_realSize->setValueAndWarp(ls->m_geometry.size()); + *ls->m_alpha = IN ? 1.f : 0.f; + } + + if (instant) { + ls->m_realPosition->warp(); + ls->m_realSize->warp(); + ls->m_alpha->warp(); + } +} + +void CDesktopAnimationManager::startAnimation(PHLWORKSPACE ws, eAnimationType type, bool left, bool instant) { + const bool IN = type == ANIMATION_TYPE_IN; + + if (!instant) { + const std::string ANIMNAME = std::format("{}{}", ws->m_isSpecialWorkspace ? "specialWorkspace" : "workspaces", IN ? "In" : "Out"); + + ws->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME)); + ws->m_renderOffset->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME)); + } + + const auto ANIMSTYLE = ws->m_alpha->getStyle(); + static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces"); + + // set floating windows offset callbacks + ws->m_renderOffset->setUpdateCallback([weak = PHLWORKSPACEREF{ws}](auto) { + if (!weak) + return; + + for (auto const& w : g_pCompositor->m_windows) { + if (!validMapped(w) || w->workspaceID() != weak->m_id) + continue; + + w->onWorkspaceAnimUpdate(); + }; + }); + + if (ANIMSTYLE.starts_with("slidefade")) { + const auto PMONITOR = ws->m_monitor.lock(); + float movePerc = 100.f; + + if (ANIMSTYLE.find('%') != std::string::npos) { + try { + auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ') + 1); + movePerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { Debug::log(ERR, "Error in startAnim: invalid percentage"); } + } + + ws->m_alpha->setValueAndWarp(1.f); + ws->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); + + if (ANIMSTYLE.starts_with("slidefadevert")) { + if (IN) { + ws->m_alpha->setValueAndWarp(0.f); + ws->m_renderOffset->setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->m_size.y : -PMONITOR->m_size.y) * (movePerc / 100.f))); + *ws->m_alpha = 1.f; + *ws->m_renderOffset = Vector2D(0, 0); + } else { + ws->m_alpha->setValueAndWarp(1.f); + *ws->m_alpha = 0.f; + *ws->m_renderOffset = Vector2D(0.0, (left ? -PMONITOR->m_size.y : PMONITOR->m_size.y) * (movePerc / 100.f)); + } + } else { + if (IN) { + ws->m_alpha->setValueAndWarp(0.f); + ws->m_renderOffset->setValueAndWarp(Vector2D((left ? PMONITOR->m_size.x : -PMONITOR->m_size.x) * (movePerc / 100.f), 0.0)); + *ws->m_alpha = 1.f; + *ws->m_renderOffset = Vector2D(0, 0); + } else { + ws->m_alpha->setValueAndWarp(1.f); + *ws->m_alpha = 0.f; + *ws->m_renderOffset = Vector2D((left ? -PMONITOR->m_size.x : PMONITOR->m_size.x) * (movePerc / 100.f), 0.0); + } + } + } else if (ANIMSTYLE == "fade") { + ws->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade. + + if (IN) { + ws->m_alpha->setValueAndWarp(0.f); + *ws->m_alpha = 1.f; + } else { + ws->m_alpha->setValueAndWarp(1.f); + *ws->m_alpha = 0.f; + } + } else if (ANIMSTYLE == "slidevert") { + // fallback is slide + const auto PMONITOR = ws->m_monitor.lock(); + const auto YDISTANCE = PMONITOR->m_size.y + *PWORKSPACEGAP; + + ws->m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. + + if (IN) { + ws->m_renderOffset->setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE)); + *ws->m_renderOffset = Vector2D(0, 0); + } else { + *ws->m_renderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE); + } + } else { + // fallback is slide + const auto PMONITOR = ws->m_monitor.lock(); + const auto XDISTANCE = PMONITOR->m_size.x + *PWORKSPACEGAP; + + ws->m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. + + if (IN) { + ws->m_renderOffset->setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0)); + *ws->m_renderOffset = Vector2D(0, 0); + } else { + *ws->m_renderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0); + } + } + + if (ws->m_isSpecialWorkspace) { + // required for open/close animations + if (IN) { + ws->m_alpha->setValueAndWarp(0.f); + *ws->m_alpha = 1.f; + } else { + ws->m_alpha->setValueAndWarp(1.f); + *ws->m_alpha = 0.f; + } + } + + if (instant) { + ws->m_renderOffset->warp(); + ws->m_alpha->warp(); + } +} + +void CDesktopAnimationManager::animationPopin(PHLWINDOW pWindow, bool close, float minPerc) { + const auto GOALPOS = pWindow->m_realPosition->goal(); + const auto GOALSIZE = pWindow->m_realSize->goal(); + + if (!close) { + pWindow->m_realSize->setValue((GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y})); + pWindow->m_realPosition->setValue(GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->value() / 2.f); + } else { + *pWindow->m_realSize = (GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y}); + *pWindow->m_realPosition = GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->goal() / 2.f; + } +} + +void CDesktopAnimationManager::animationSlide(PHLWINDOW pWindow, std::string force, bool close) { + pWindow->m_realSize->warp(false); // size we preserve in slide + + const auto GOALPOS = pWindow->m_realPosition->goal(); + const auto GOALSIZE = pWindow->m_realSize->goal(); + + const auto PMONITOR = pWindow->m_monitor.lock(); + + if (!PMONITOR) + return; // unsafe state most likely + + Vector2D posOffset; + + if (!force.empty()) { + if (force == "bottom") + posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y); + else if (force == "left") + posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0); + else if (force == "right") + posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0); + else + posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y); + + if (!close) + pWindow->m_realPosition->setValue(posOffset); + else + *pWindow->m_realPosition = posOffset; + + return; + } + + const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f; + + // check sides it touches + const bool DISPLAYLEFT = STICKS(pWindow->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x); + const bool DISPLAYRIGHT = STICKS(pWindow->m_position.x + pWindow->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x); + const bool DISPLAYTOP = STICKS(pWindow->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y); + const bool DISPLAYBOTTOM = STICKS(pWindow->m_position.y + pWindow->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y); + + if (DISPLAYBOTTOM && DISPLAYTOP) { + if (DISPLAYLEFT && DISPLAYRIGHT) { + posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y); + } else if (DISPLAYLEFT) { + posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0); + } else { + posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0); + } + } else if (DISPLAYTOP) { + posOffset = GOALPOS - Vector2D(0.0, GOALSIZE.y); + } else if (DISPLAYBOTTOM) { + posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y); + } else { + if (MIDPOINT.y > PMONITOR->m_position.y + PMONITOR->m_size.y / 2.f) + posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y); + else + posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y); + } + + if (!close) + pWindow->m_realPosition->setValue(posOffset); + else + *pWindow->m_realPosition = posOffset; +} + +void CDesktopAnimationManager::animationGnomed(PHLWINDOW pWindow, bool close) { + const auto GOALPOS = pWindow->m_realPosition->goal(); + const auto GOALSIZE = pWindow->m_realSize->goal(); + + if (close) { + *pWindow->m_realPosition = GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F}; + *pWindow->m_realSize = Vector2D{GOALSIZE.x, 0.F}; + } else { + pWindow->m_realPosition->setValueAndWarp(GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F}); + pWindow->m_realSize->setValueAndWarp(Vector2D{GOALSIZE.x, 0.F}); + *pWindow->m_realPosition = GOALPOS; + *pWindow->m_realSize = GOALSIZE; + } +} + +void CDesktopAnimationManager::setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnimationType type) { + if (!ws) + return; + + const auto FULLSCREEN = type == ANIMATION_TYPE_IN; + + for (auto const& w : g_pCompositor->m_windows) { + if (w->m_workspace == ws) { + + if (w->m_fadingOut || w->m_pinned || w->isFullscreen()) + continue; + + if (!FULLSCREEN) + *w->m_alpha = 1.F; + else if (!w->isFullscreen()) + *w->m_alpha = !w->m_createdOverFullscreen ? 0.f : 1.f; + } + } + + const auto PMONITOR = ws->m_monitor.lock(); + + if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) { + for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { + if (!ls->m_fadingOut) + *ls->m_alpha = FULLSCREEN && ws->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f; + } + } +} + +void CDesktopAnimationManager::overrideFullscreenFadeAmount(PHLWORKSPACE ws, float fade, PHLWINDOW exclude) { + for (auto const& w : g_pCompositor->m_windows) { + if (w == exclude) + continue; + + if (w->m_workspace == ws) { + if (w->m_fadingOut || w->m_pinned || w->isFullscreen()) + continue; + + *w->m_alpha = fade; + } + } + + const auto PMONITOR = ws->m_monitor.lock(); + + if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) { + for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { + if (!ls->m_fadingOut) + *ls->m_alpha = fade; + } + } +} diff --git a/src/managers/animation/DesktopAnimationManager.hpp b/src/managers/animation/DesktopAnimationManager.hpp new file mode 100644 index 00000000..623fc5e9 --- /dev/null +++ b/src/managers/animation/DesktopAnimationManager.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "../../helpers/memory/Memory.hpp" +#include "../../desktop/DesktopTypes.hpp" + +class CDesktopAnimationManager { + public: + enum eAnimationType : uint8_t { + ANIMATION_TYPE_IN = 0, + ANIMATION_TYPE_OUT, + }; + + void startAnimation(PHLWINDOW w, eAnimationType type, bool force = false); + void startAnimation(PHLLS ls, eAnimationType type, bool instant = false); + void startAnimation(PHLWORKSPACE ws, eAnimationType type, bool left = true, bool instant = false); + + void setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnimationType type); + void overrideFullscreenFadeAmount(PHLWORKSPACE ws, float fade, PHLWINDOW exclude = nullptr); + + private: + void animationPopin(PHLWINDOW w, bool close = false, float minPerc = 0.f); + void animationSlide(PHLWINDOW w, std::string force = "", bool close = false); + void animationGnomed(PHLWINDOW w, bool close = false); +}; + +inline UP<CDesktopAnimationManager> g_pDesktopAnimationManager = makeUnique<CDesktopAnimationManager>(); |
