diff --git a/README.md b/README.md index 55672549..1c1721f1 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Nevertheless, REPORT any you find! Make an issue! - Config reloaded instantly upon saving - Easily expandable and readable codebase - Rounded corners + - Fade in/out - Support for docks/whatever - Window rules - Monitor rules @@ -33,10 +34,8 @@ Nevertheless, REPORT any you find! Make an issue! # Major to-dos - Damage tracking - Animations (better) - - Fix XWayland focus - Fix GDK popups on multimon - Blur - - Fadein/out - Fix electron rendering issues - Optimization - Fix weird scroll on XWayland diff --git a/example/hyprland.conf b/example/hyprland.conf index 962707ff..8503bf42 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -26,13 +26,19 @@ general { col.inactive_border=0x66333333 } +decoration { + rounding=10 +} + animations { enabled=1 speed=7 windows_speed=6 # specific speeds for components can be made with name_speed=float. 0 means use global (speed=float). If not set, will use the global value. windows=1 borders=1 - fadein=1 # not yet implemented + borders_speed=20 + fadein=1 # fade in AND out + fadein_speed=20 } dwindle { diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 7fdd65bc..601cf114 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -559,6 +559,8 @@ void CCompositor::cleanupWindows() { if (!w->m_bFadingOut || w->m_fAlpha == 0.f) { m_lWindows.remove(*w); m_lWindowsFadingOut.remove(w); + g_pHyprOpenGL->m_mWindowFramebuffers[w].release(); + g_pHyprOpenGL->m_mWindowFramebuffers.erase(w); Debug::log(LOG, "Cleanup: cleaned up a window"); return; } diff --git a/src/defines.hpp b/src/defines.hpp index 7eb1b56a..4bd886e7 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -32,7 +32,7 @@ #ifndef __INTELLISENSE__ #define RASSERT(expr, reason, ...) \ - if (!expr) { \ + if (!(expr)) { \ Debug::log(CRIT, "\n==========================================================================================\nASSERTION FAILED! \n\n%s\n\nat: line %d in %s", getFormat(reason, ##__VA_ARGS__).c_str(), __LINE__, ([]() constexpr->std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })().c_str()); \ RIP("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info."); \ } diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 3a00041d..4ce53697 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -171,6 +171,9 @@ void Events::listener_unmapWindow(void* owner, void* data) { g_pCompositor->m_pLastFocus = nullptr; } + // Allow the renderer to catch the last frame. + g_pHyprOpenGL->makeWindowSnapshot(PWINDOW); + PWINDOW->m_bMappedX11 = false; // remove the fullscreen window status from workspace if we closed it diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 23b76073..86bca5de 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -261,8 +261,8 @@ void CHyprDwindleLayout::onWindowRemoved(CWindow* pWindow) { m_lDwindleNodesData.remove(*PNODE); // jump back like it jumps in - pWindow->m_vEffectivePosition = pWindow->m_vEffectivePosition + ((pWindow->m_vEffectiveSize - Vector2D(5, 5)) * 0.5f); - pWindow->m_vEffectiveSize = Vector2D(5, 5); + //pWindow->m_vEffectivePosition = pWindow->m_vEffectivePosition + ((pWindow->m_vEffectiveSize - Vector2D(5, 5)) * 0.5f); + // pWindow->m_vEffectiveSize = Vector2D(5, 5); } void CHyprDwindleLayout::recalculateMonitor(const int& monid) { diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp new file mode 100644 index 00000000..8ab03776 --- /dev/null +++ b/src/render/Framebuffer.cpp @@ -0,0 +1,62 @@ +#include "Framebuffer.hpp" +#include "OpenGL.hpp" + +bool CFramebuffer::alloc(int w, int h) { + bool firstAlloc = false; + if (m_iFb == (uint32_t)-1) + { + firstAlloc = true; + glGenFramebuffers(1, &m_iFb); + } + + if (m_cTex.m_iTexID == 0) + { + firstAlloc = true; + glGenTextures(1, &m_cTex.m_iTexID); + glBindTexture(GL_TEXTURE_2D, m_cTex.m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + if (firstAlloc) + { + glBindTexture(GL_TEXTURE_2D, m_cTex.m_iTexID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); + glBindTexture(GL_TEXTURE_2D, m_cTex.m_iTexID); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_cTex.m_iTexID, 0); + + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: %i)", status); + + Debug::log(LOG, "Framebuffer created, status %i", status); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb); + + m_Size = Vector2D(w, h); + + return true; +} + +void CFramebuffer::bind() { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iFb); + glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.y); +} + +void CFramebuffer::release() { + if (m_iFb != (uint32_t)-1 && m_iFb) { + glDeleteFramebuffers(1, &m_iFb); + } + + if (m_cTex.m_iTexID) { + glDeleteTextures(1, &m_cTex.m_iTexID); + } + + m_cTex.m_iTexID = 0; + m_iFb = -1; +} diff --git a/src/render/Framebuffer.hpp b/src/render/Framebuffer.hpp new file mode 100644 index 00000000..5c76b9a6 --- /dev/null +++ b/src/render/Framebuffer.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "../defines.hpp" +#include "Texture.hpp" + +class CFramebuffer { +public: + + bool alloc(int w, int h); + void bind(); + void release(); + void reset(); + + Vector2D m_Position; + Vector2D m_Size; + float m_fScale = 1; + + CTexture m_cTex; + GLuint m_iFb = -1; + + wl_output_transform m_tTransform; // for saving state +}; \ No newline at end of file diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 1e5ec07d..f46302ab 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -106,6 +106,8 @@ void CHyprOpenGLImpl::begin(SMonitor* pMonitor) { wlr_matrix_projection(m_RenderData.projection, pMonitor->vecSize.x, pMonitor->vecSize.y, WL_OUTPUT_TRANSFORM_NORMAL); // TODO: this is deprecated glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_iCurrentOutputFb); } void CHyprOpenGLImpl::end() { @@ -323,4 +325,58 @@ void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CColor& col, int thick, i glDrawArrays(GL_LINE_STRIP, 0, 41); glDisableVertexAttribArray(m_shQUAD.posAttrib); +} + +void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) { + // we trust the window is valid. + const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); + wlr_output_attach_render(PMONITOR->output, nullptr); + + begin(PMONITOR); + + const auto PFRAMEBUFFER = &m_mWindowFramebuffers[pWindow]; + + PFRAMEBUFFER->m_tTransform = g_pXWaylandManager->getWindowSurface(pWindow)->current.transform; + + PFRAMEBUFFER->alloc(PMONITOR->vecSize.x, PMONITOR->vecSize.y); + + PFRAMEBUFFER->bind(); + + clear(CColor(0,0,0,0)); // JIC + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + g_pHyprRenderer->renderWindow(pWindow, PMONITOR, &now, !pWindow->m_bX11DoesntWantBorders); + + // restore original fb + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iCurrentOutputFb); + glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.y); + + end(); + + wlr_output_rollback(PMONITOR->output); +} + +void CHyprOpenGLImpl::renderSnapshot(CWindow** pWindow) { + const auto PWINDOW = *pWindow; + + auto it = m_mWindowFramebuffers.begin(); + for (;it != m_mWindowFramebuffers.end(); it++) { + if (it->first == PWINDOW) { + break; + } + } + + if (it == m_mWindowFramebuffers.end() || !it->second.m_cTex.m_iTexID) + return; + + const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); + + const auto TRANSFORM = wlr_output_transform_invert(it->second.m_tTransform); + float matrix[9]; + wlr_box windowBox = {0, 0, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + wlr_matrix_project_box(matrix, &windowBox, TRANSFORM, 0, PMONITOR->output->transform_matrix); + + renderTexture(it->second.m_cTex, matrix, PWINDOW->m_fAlpha, 0); } \ No newline at end of file diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 43fa6c6c..7d938c15 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -5,10 +5,12 @@ #include "../helpers/Color.hpp" #include #include +#include #include "Shaders.hpp" #include "Shader.hpp" #include "Texture.hpp" +#include "Framebuffer.hpp" inline const float matrixFlip180[] = { 1.0f, 0.0f, 0.0f, @@ -40,11 +42,18 @@ public: void renderTexture(const CTexture&, float matrix[9], float a, int round = 0); void renderBorder(wlr_box*, const CColor&, int thick = 1, int round = 0); + void makeWindowSnapshot(CWindow*); + void renderSnapshot(CWindow**); + void clear(const CColor&); void scissor(const wlr_box*); SCurrentRenderData m_RenderData; + GLint m_iCurrentOutputFb = 0; + + std::unordered_map m_mWindowFramebuffers; + private: std::list m_lBuffers; std::list m_lTextures; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index d34d6897..fa62b5fd 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -82,6 +82,11 @@ void CHyprRenderer::renderWorkspaceWithFullscreenWindow(SMonitor* pMonitor, SWor } void CHyprRenderer::renderWindow(CWindow* pWindow, SMonitor* pMonitor, timespec* time, bool decorate) { + if (pWindow->m_bFadingOut) { + g_pHyprOpenGL->renderSnapshot(&pWindow); + return; + } + const auto REALPOS = pWindow->m_vRealPosition; SRenderData renderdata = {pMonitor->output, time, REALPOS.x, REALPOS.y}; renderdata.surface = g_pXWaylandManager->getWindowSurface(pWindow); @@ -134,7 +139,7 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { // Non-floating for (auto& w : g_pCompositor->m_lWindows) { - if (!g_pCompositor->windowValidMapped(&w)) + if (!g_pCompositor->windowValidMapped(&w) && !w.m_bFadingOut) continue; if (w.m_bIsFloating) @@ -149,7 +154,7 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { // floating on top for (auto& w : g_pCompositor->m_lWindows) { - if (!g_pCompositor->windowValidMapped(&w)) + if (!g_pCompositor->windowValidMapped(&w) && !w.m_bFadingOut) continue; if (!w.m_bIsFloating) diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index cd0b5d01..ec1597f8 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -5,6 +5,7 @@ #include "../helpers/Monitor.hpp" #include "../helpers/Workspace.hpp" #include "../Window.hpp" +#include "OpenGL.hpp" class CHyprRenderer { public: @@ -20,6 +21,9 @@ private: void renderWorkspaceWithFullscreenWindow(SMonitor*, SWorkspace*, timespec*); void renderWindow(CWindow*, SMonitor*, timespec*, bool); void renderDragIcon(SMonitor*, timespec*); + + + friend class CHyprOpenGLImpl; }; inline std::unique_ptr g_pHyprRenderer;