diff --git a/src/Window.cpp b/src/Window.cpp index 1cda063e..9666b2dc 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -260,6 +260,8 @@ void CWindow::onUnmap() { m_fAlpha.setCallbackOnEnd(unregisterVar); m_cRealShadowColor.setCallbackOnEnd(unregisterVar); m_fDimPercent.setCallbackOnEnd(unregisterVar); + + m_vRealSize.setCallbackOnBegin(nullptr); } void CWindow::onMap() { @@ -270,6 +272,13 @@ void CWindow::onMap() { m_fAlpha.registerVar(); m_cRealShadowColor.registerVar(); m_fDimPercent.registerVar(); + + m_vRealSize.setCallbackOnEnd([&] (void* ptr) { + g_pHyprOpenGL->onWindowResizeEnd(this); + }, false); + m_vRealSize.setCallbackOnBegin([&] (void* ptr) { + g_pHyprOpenGL->onWindowResizeStart(this); + }, false); } void CWindow::setHidden(bool hidden) { diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 303ba340..40653a80 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -105,6 +105,7 @@ void CConfigManager::setDefaultVars() { configValues["master:no_gaps_when_only"].intValue = 0; configValues["animations:enabled"].intValue = 1; + configValues["animations:use_resize_transitions"].intValue = 1; configValues["animations:speed"].floatValue = 7.f; configValues["animations:curve"].strValue = "default"; configValues["animations:windows_style"].strValue = STRVAL_EMPTY; diff --git a/src/helpers/AnimatedVariable.cpp b/src/helpers/AnimatedVariable.cpp index 77707349..18162c99 100644 --- a/src/helpers/AnimatedVariable.cpp +++ b/src/helpers/AnimatedVariable.cpp @@ -66,3 +66,8 @@ void CAnimatedVariable::registerVar() { int CAnimatedVariable::getDurationLeftMs() { return std::max((int)(m_pConfig->pValues->internalSpeed * 100) - (int)std::chrono::duration_cast(std::chrono::system_clock::now() - animationBegin).count(), 0); } + +float CAnimatedVariable::getPercent() { + const auto DURATIONPASSED = std::chrono::duration_cast(std::chrono::system_clock::now() - animationBegin).count(); + return std::clamp((DURATIONPASSED / 100.f) / m_pConfig->pValues->internalSpeed, 0.f, 1.f); +} \ No newline at end of file diff --git a/src/helpers/AnimatedVariable.hpp b/src/helpers/AnimatedVariable.hpp index 7b5a6895..2c2c3ccf 100644 --- a/src/helpers/AnimatedVariable.hpp +++ b/src/helpers/AnimatedVariable.hpp @@ -21,6 +21,7 @@ class CAnimationManager; class CWorkspace; struct SLayerSurface; struct SAnimationPropertyConfig; +class CHyprRenderer; class CAnimatedVariable { public: @@ -68,18 +69,24 @@ public: m_vGoal = v; animationBegin = std::chrono::system_clock::now(); m_vBegun = m_vValue; + + onAnimationBegin(); } void operator=(const float& v) { m_fGoal = v; animationBegin = std::chrono::system_clock::now(); m_fBegun = m_fValue; + + onAnimationBegin(); } void operator=(const CColor& v) { m_cGoal = v; animationBegin = std::chrono::system_clock::now(); m_cBegun = m_cValue; + + onAnimationBegin(); } // Sets the actual stored value, without affecting the goal, but resets the timer @@ -87,6 +94,8 @@ public: m_vValue = v; animationBegin = std::chrono::system_clock::now(); m_vBegun = m_vValue; + + onAnimationBegin(); } // Sets the actual stored value, without affecting the goal, but resets the timer @@ -94,6 +103,8 @@ public: m_fValue = v; animationBegin = std::chrono::system_clock::now(); m_vBegun = m_vValue; + + onAnimationBegin(); } // Sets the actual stored value, without affecting the goal, but resets the timer @@ -101,6 +112,8 @@ public: m_cValue = v; animationBegin = std::chrono::system_clock::now(); m_vBegun = m_vValue; + + onAnimationBegin(); } // Sets the actual value and goal @@ -139,7 +152,7 @@ public: return false; // just so that the warning is suppressed } - void warp() { + void warp(bool endCallback = true) { switch (m_eVarType) { case AVARTYPE_FLOAT: { m_fValue = m_fGoal; @@ -156,6 +169,9 @@ public: default: UNREACHABLE(); } + + if (endCallback) + onAnimationEnd(); } void setConfig(SAnimationPropertyConfig* pConfig) { @@ -168,16 +184,27 @@ public: int getDurationLeftMs(); + /* returns the spent (completion) % */ + float getPercent(); + /* sets a function to be ran when the animation finishes. if an animation is not running, runs instantly. - will remove the callback when ran. */ - void setCallbackOnEnd(std::function func) { + if "remove" is set to true, will remove the callback when ran. */ + void setCallbackOnEnd(std::function func, bool remove = true) { m_fEndCallback = func; + m_bRemoveEndAfterRan = remove; if (!isBeingAnimated()) onAnimationEnd(); } + /* sets a function to be ran when an animation is started. + if "remove" is set to true, will remove the callback when ran. */ + void setCallbackOnBegin(std::function func, bool remove = true) { + m_fBeginCallback = func; + m_bRemoveBeginAfterRan = remove; + } + private: Vector2D m_vValue = Vector2D(0,0); @@ -207,17 +234,30 @@ private: ANIMATEDVARTYPE m_eVarType = AVARTYPE_INVALID; AVARDAMAGEPOLICY m_eDamagePolicy = AVARDAMAGE_INVALID; + bool m_bRemoveEndAfterRan = true; + bool m_bRemoveBeginAfterRan = true; std::function m_fEndCallback; + std::function m_fBeginCallback; // methods void onAnimationEnd() { if (m_fEndCallback) { m_fEndCallback(this); - m_fEndCallback = nullptr; // reset + if (m_bRemoveEndAfterRan) + m_fEndCallback = nullptr; // reset + } + } + + void onAnimationBegin() { + if (m_fBeginCallback) { + m_fBeginCallback(this); + if (m_bRemoveBeginAfterRan) + m_fBeginCallback = nullptr; // reset } } friend class CAnimationManager; friend class CWorkspace; friend struct SLayerSurface; + friend class CHyprRenderer; }; diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp index 06fa60da..550278a5 100644 --- a/src/managers/AnimationManager.cpp +++ b/src/managers/AnimationManager.cpp @@ -42,16 +42,12 @@ void CAnimationManager::tick() { continue; if (av->m_eDamagePolicy == AVARDAMAGE_SHADOW && !*PSHADOWSENABLED) { - av->warp(); + av->warp(false); continue; } - // get speed - const auto SPEED = av->m_pConfig->pValues->internalSpeed; - // get the spent % (0 - 1) - const auto DURATIONPASSED = std::chrono::duration_cast(std::chrono::system_clock::now() - av->animationBegin).count(); - const float SPENT = std::clamp((DURATIONPASSED / 100.f) / SPEED, 0.f, 1.f); + const float SPENT = av->getPercent(); // window stuff const auto PWINDOW = (CWindow*)av->m_pWindow; @@ -80,12 +76,12 @@ void CAnimationManager::tick() { case AVARTYPE_FLOAT: { // for disabled anims just warp if (av->m_pConfig->pValues->internalEnabled == 0 || animationsDisabled) { - av->warp(); + av->warp(false); break; } if (SPENT >= 1.f) { - av->warp(); + av->warp(false); break; } @@ -101,12 +97,12 @@ void CAnimationManager::tick() { case AVARTYPE_VECTOR: { // for disabled anims just warp if (av->m_pConfig->pValues->internalEnabled == 0 || animationsDisabled) { - av->warp(); + av->warp(false); break; } if (SPENT >= 1.f) { - av->warp(); + av->warp(false); break; } @@ -122,12 +118,12 @@ void CAnimationManager::tick() { case AVARTYPE_COLOR: { // for disabled anims just warp if (av->m_pConfig->pValues->internalEnabled == 0 || animationsDisabled) { - av->warp(); + av->warp(false); break; } if (SPENT >= 1.f) { - av->warp(); + av->warp(false); break; } @@ -297,7 +293,7 @@ void CAnimationManager::animationPopin(CWindow* pWindow, bool close, float minPe } void CAnimationManager::animationSlide(CWindow* pWindow, std::string force, bool close) { - pWindow->m_vRealSize.warp(); // size we preserve in slide + pWindow->m_vRealSize.warp(false); // size we preserve in slide const auto GOALPOS = pWindow->m_vRealPosition.goalv(); const auto GOALSIZE = pWindow->m_vRealSize.goalv(); diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp index 33bf321b..a1b45fc8 100644 --- a/src/render/Framebuffer.cpp +++ b/src/render/Framebuffer.cpp @@ -27,7 +27,6 @@ bool CFramebuffer::alloc(int w, int h) { glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_cTex.m_iTexID, 0); - // TODO: Allow this with gles2 #ifndef GLES2 if (m_pStencilTex) { @@ -80,3 +79,7 @@ void CFramebuffer::release() { CFramebuffer::~CFramebuffer() { release(); } + +bool CFramebuffer::isAllocated() { + return m_iFb != (GLuint)-1; +} \ No newline at end of file diff --git a/src/render/Framebuffer.hpp b/src/render/Framebuffer.hpp index 6af59ca2..2d1e621c 100644 --- a/src/render/Framebuffer.hpp +++ b/src/render/Framebuffer.hpp @@ -12,6 +12,7 @@ public: void bind(); void release(); void reset(); + bool isAllocated(); Vector2D m_Position; Vector2D m_Size; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 7d3ac39e..bcf91258 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -905,6 +905,61 @@ void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CColor& col, int round) { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } +void CHyprOpenGLImpl::makeRawWindowSnapshot(CWindow* pWindow, CFramebuffer* pFramebuffer) { + // we trust the window is valid. + const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); + wlr_output_attach_render(PMONITOR->output, nullptr); + + // we need to "damage" the entire monitor + // so that we render the entire window + // this is temporary, doesnt mess with the actual wlr damage + pixman_region32_t fakeDamage; + pixman_region32_init(&fakeDamage); + pixman_region32_union_rect(&fakeDamage, &fakeDamage, 0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y); + + begin(PMONITOR, &fakeDamage, true); + + clear(CColor(0, 0, 0, 0)); // JIC + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + // this is a hack but it works :P + // we need to disable blur or else we will get a black background, as the shader + // will try to copy the bg to apply blur. + // this isn't entirely correct, but like, oh well. + // small todo: maybe make this correct? :P + const auto BLURVAL = g_pConfigManager->getInt("decoration:blur"); + g_pConfigManager->setInt("decoration:blur", 0); + + // TODO: how can we make this the size of the window? setting it to window's size makes the entire screen render with the wrong res forever more. odd. + glViewport(0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); + + pFramebuffer->m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; + + pFramebuffer->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); + + pFramebuffer->bind(); + + clear(CColor(0, 0, 0, 0)); // JIC + + g_pHyprRenderer->renderWindow(pWindow, PMONITOR, &now, false, RENDER_PASS_ALL, true); + + g_pConfigManager->setInt("decoration:blur", BLURVAL); + +// restore original fb +#ifndef GLES2 + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iCurrentOutputFb); +#else + glBindFramebuffer(GL_FRAMEBUFFER, m_iCurrentOutputFb); +#endif + end(); + + pixman_region32_fini(&fakeDamage); + + wlr_output_rollback(PMONITOR->output); +} + void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) { // we trust the window is valid. const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); @@ -919,7 +974,7 @@ void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) { begin(PMONITOR, &fakeDamage, true); - clear(CColor(0,0,0,0)); // JIC + clear(CColor(0, 0, 0, 0)); // JIC timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -932,10 +987,9 @@ void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) { const auto BLURVAL = g_pConfigManager->getInt("decoration:blur"); g_pConfigManager->setInt("decoration:blur", 0); - // render onto the window fb - const auto PFRAMEBUFFER = &m_mWindowFramebuffers[pWindow]; + glViewport(0, 0, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y); - glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y); + const auto PFRAMEBUFFER = &m_mWindowFramebuffers[pWindow]; PFRAMEBUFFER->m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; @@ -949,12 +1003,12 @@ void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) { g_pConfigManager->setInt("decoration:blur", BLURVAL); - // restore original fb - #ifndef GLES2 +// restore original fb +#ifndef GLES2 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iCurrentOutputFb); - #else +#else glBindFramebuffer(GL_FRAMEBUFFER, m_iCurrentOutputFb); - #endif +#endif end(); pixman_region32_fini(&fakeDamage); @@ -1014,6 +1068,32 @@ void CHyprOpenGLImpl::makeLayerSnapshot(SLayerSurface* pLayer) { wlr_output_rollback(PMONITOR->output); } +void CHyprOpenGLImpl::onWindowResizeStart(CWindow* pWindow) { + static auto *const PTRANSITIONS = &g_pConfigManager->getConfigValuePtr("animations:use_resize_transitions")->intValue; + static auto *const PENABLED = &g_pConfigManager->getConfigValuePtr("animations:enabled")->intValue; + + if (!*PTRANSITIONS || !*PENABLED) + return; + + if (pWindow->m_vRealSize.vec().x < 5 || pWindow->m_vRealSize.vec().y < 5) + return; + + // make a fb and render a snapshot + const auto PFRAMEBUFFER = &m_mWindowResizeFramebuffers[pWindow]; + makeRawWindowSnapshot(pWindow, PFRAMEBUFFER); +} + +void CHyprOpenGLImpl::onWindowResizeEnd(CWindow* pWindow) { + static auto *const PTRANSITIONS = &g_pConfigManager->getConfigValuePtr("animations:use_resize_transitions")->intValue; + static auto *const PENABLED = &g_pConfigManager->getConfigValuePtr("animations:enabled")->intValue; + + if (!*PTRANSITIONS || !*PENABLED) + return; + + // remove the fb + m_mWindowResizeFramebuffers.erase(pWindow); +} + void CHyprOpenGLImpl::renderSnapshot(CWindow** pWindow) { RASSERT(m_RenderData.pMonitor, "Tried to render snapshot rect without begin()!"); const auto PWINDOW = *pWindow; diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index b62732f6..dbc2977c 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -83,6 +83,7 @@ public: void renderBorder(wlr_box*, const CColor&, int round); void makeWindowSnapshot(CWindow*); + void makeRawWindowSnapshot(CWindow*, CFramebuffer*); void makeLayerSnapshot(SLayerSurface*); void renderSnapshot(CWindow**); void renderSnapshot(SLayerSurface**); @@ -103,6 +104,9 @@ public: void saveBufferForMirror(); void renderMirrored(); + void onWindowResizeStart(CWindow*); + void onWindowResizeEnd(CWindow*); + SCurrentRenderData m_RenderData; GLint m_iCurrentOutputFb = 0; @@ -113,6 +117,7 @@ public: pixman_region32_t m_rOriginalDamageRegion; // used for storing the pre-expanded region std::unordered_map m_mWindowFramebuffers; + std::unordered_map m_mWindowResizeFramebuffers; std::unordered_map m_mLayerFramebuffers; std::unordered_map m_mMonitorRenderResources; std::unordered_map m_mMonitorBGTextures; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 420b45be..60238144 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -215,7 +215,7 @@ void CHyprRenderer::renderWorkspaceWithFullscreenWindow(CMonitor* pMonitor, CWor g_pHyprError->draw(); } -void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* time, bool decorate, eRenderPassMode mode) { +void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* time, bool decorate, eRenderPassMode mode, bool ignorePosition) { if (pWindow->isHidden()) return; @@ -227,9 +227,15 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); const auto REALPOS = pWindow->m_vRealPosition.vec() + (pWindow->m_bPinned ? Vector2D{} : PWORKSPACE->m_vRenderOffset.vec()); - static const auto PNOFLOATINGBORDERS = &g_pConfigManager->getConfigValuePtr("general:no_border_on_floating")->intValue; + static auto *const PNOFLOATINGBORDERS = &g_pConfigManager->getConfigValuePtr("general:no_border_on_floating")->intValue; + static auto *const PTRANSITIONS = &g_pConfigManager->getConfigValuePtr("animations:use_resize_transitions")->intValue; SRenderData renderdata = {pMonitor->output, time, REALPOS.x, REALPOS.y}; + if (ignorePosition) { + renderdata.x = 0; + renderdata.y = 0; + } + renderdata.surface = g_pXWaylandManager->getWindowSurface(pWindow); renderdata.w = std::max(pWindow->m_vRealSize.vec().x, 5.0); // clamp the size to min 5, renderdata.h = std::max(pWindow->m_vRealSize.vec().y, 5.0); // otherwise we'll have issues later with invalid boxes @@ -288,6 +294,23 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* wlr_surface_for_each_surface(g_pXWaylandManager->getWindowSurface(pWindow), renderSurface, &renderdata); + if (*PTRANSITIONS && !ignorePosition /* ignorePosition probably means we are rendering the snapshot rn */) { + const auto PFB = g_pHyprOpenGL->m_mWindowResizeFramebuffers.find(pWindow); + + if (PFB != g_pHyprOpenGL->m_mWindowResizeFramebuffers.end() && PFB->second.isAllocated()) { + wlr_box box = {renderdata.x, renderdata.y, renderdata.w, renderdata.h}; + + // adjust UV (remove when I figure out how to change the size of the fb) + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = {0, 0}; + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = { pWindow->m_vRealSize.m_vBegun.x / pMonitor->vecPixelSize.x, pWindow->m_vRealSize.m_vBegun.y / pMonitor->vecPixelSize.y}; + + g_pHyprOpenGL->renderTexture(PFB->second.m_cTex, &box, (1.f - pWindow->m_vRealSize.getPercent()) * 84.f, 0, false, true); + + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + } + } + if (renderdata.decorate && pWindow->m_sSpecialRenderData.border) { static auto *const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue; diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 74fc0a9a..8bd65418 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -54,7 +54,7 @@ public: private: void arrangeLayerArray(CMonitor*, const std::vector>&, bool, wlr_box*); void renderWorkspaceWithFullscreenWindow(CMonitor*, CWorkspace*, timespec*); - void renderWindow(CWindow*, CMonitor*, timespec*, bool, eRenderPassMode); + void renderWindow(CWindow*, CMonitor*, timespec*, bool, eRenderPassMode, bool ignorePosition = false); void renderLayer(SLayerSurface*, CMonitor*, timespec*); void renderDragIcon(CMonitor*, timespec*); void renderIMEPopup(SIMEPopup*, CMonitor*, timespec*);