diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2026-03-27 10:08:56 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-27 14:08:56 +0000 |
| commit | 2c4852e31f1cc974164fdd1bf383a67c26cafe44 (patch) | |
| tree | c248ef256b6ff1d8de9fe031ff2bf33b67e85dde /src | |
| parent | 2fde538e0fa5663ac99f631fcfee6742e09368f6 (diff) | |
renderer/deco: add glow decoration (#13862)
Diffstat (limited to 'src')
| -rw-r--r-- | src/config/legacy/ConfigManager.cpp | 5 | ||||
| -rw-r--r-- | src/config/shared/animation/AnimationTree.cpp | 1 | ||||
| -rw-r--r-- | src/config/supplementary/ConfigDescriptions.hpp | 30 | ||||
| -rw-r--r-- | src/desktop/view/Window.cpp | 15 | ||||
| -rw-r--r-- | src/desktop/view/Window.hpp | 3 | ||||
| -rw-r--r-- | src/render/GLRenderer.cpp | 6 | ||||
| -rw-r--r-- | src/render/GLRenderer.hpp | 1 | ||||
| -rw-r--r-- | src/render/OpenGL.cpp | 71 | ||||
| -rw-r--r-- | src/render/OpenGL.hpp | 1 | ||||
| -rw-r--r-- | src/render/Renderer.cpp | 1 | ||||
| -rw-r--r-- | src/render/Renderer.hpp | 18 | ||||
| -rw-r--r-- | src/render/ShaderLoader.hpp | 1 | ||||
| -rw-r--r-- | src/render/decorations/CHyprInnerGlowDecoration.cpp | 104 | ||||
| -rw-r--r-- | src/render/decorations/CHyprInnerGlowDecoration.hpp | 35 | ||||
| -rw-r--r-- | src/render/decorations/IHyprWindowDecoration.hpp | 1 | ||||
| -rw-r--r-- | src/render/pass/InnerGlowPassElement.cpp | 13 | ||||
| -rw-r--r-- | src/render/pass/InnerGlowPassElement.hpp | 28 | ||||
| -rw-r--r-- | src/render/pass/PassElement.hpp | 3 | ||||
| -rw-r--r-- | src/render/shaders/glsl/inner_glow.frag | 57 | ||||
| -rw-r--r-- | src/render/shaders/glsl/inner_glow.glsl | 106 |
20 files changed, 487 insertions, 13 deletions
diff --git a/src/config/legacy/ConfigManager.cpp b/src/config/legacy/ConfigManager.cpp index 31da257c..70a15754 100644 --- a/src/config/legacy/ConfigManager.cpp +++ b/src/config/legacy/ConfigManager.cpp @@ -626,6 +626,11 @@ CConfigManager::CConfigManager() { registerConfigVar("decoration:shadow:sharp", Hyprlang::INT{0}); registerConfigVar("decoration:shadow:color", Hyprlang::INT{0xee1a1a1a}); registerConfigVar("decoration:shadow:color_inactive", Hyprlang::INT{-1}); + registerConfigVar("decoration:glow:enabled", Hyprlang::INT{0}); + registerConfigVar("decoration:glow:range", Hyprlang::INT{10}); + registerConfigVar("decoration:glow:render_power", Hyprlang::INT{3}); + registerConfigVar("decoration:glow:color", Hyprlang::INT{0xee33ccff}); + registerConfigVar("decoration:glow:color_inactive", Hyprlang::INT{0x0033ccff}); registerConfigVar("decoration:dim_inactive", Hyprlang::INT{0}); registerConfigVar("decoration:dim_modal", Hyprlang::INT{1}); registerConfigVar("decoration:dim_strength", {0.5f}); diff --git a/src/config/shared/animation/AnimationTree.cpp b/src/config/shared/animation/AnimationTree.cpp index 39f7620a..bd763a70 100644 --- a/src/config/shared/animation/AnimationTree.cpp +++ b/src/config/shared/animation/AnimationTree.cpp @@ -39,6 +39,7 @@ void CAnimationTreeController::reset() { m_animationTree.createNode("fadeOut", "fade"); m_animationTree.createNode("fadeSwitch", "fade"); m_animationTree.createNode("fadeShadow", "fade"); + m_animationTree.createNode("fadeGlow", "fade"); m_animationTree.createNode("fadeDim", "fade"); m_animationTree.createNode("fadeLayers", "fade"); m_animationTree.createNode("fadeLayersIn", "fadeLayers"); diff --git a/src/config/supplementary/ConfigDescriptions.hpp b/src/config/supplementary/ConfigDescriptions.hpp index 90ac0031..6a287d43 100644 --- a/src/config/supplementary/ConfigDescriptions.hpp +++ b/src/config/supplementary/ConfigDescriptions.hpp @@ -310,6 +310,36 @@ namespace Config::Supplementary { .data = SConfigOptionDescription::SFloatData{1, 0, 1}, }, SConfigOptionDescription{ + .value = "decoration:glow:enabled", + .description = "enable inner glow on windows", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, + SConfigOptionDescription{ + .value = "decoration:glow:range", + .description = "glow range (size) in layout px", + .type = CONFIG_OPTION_INT, + .data = SConfigOptionDescription::SRangeData{10, 0, 100}, + }, + SConfigOptionDescription{ + .value = "decoration:glow:render_power", + .description = "in what power to render the falloff (more power, the faster the falloff) [1 - 4]", + .type = CONFIG_OPTION_INT, + .data = SConfigOptionDescription::SRangeData{3, 1, 4}, + }, + SConfigOptionDescription{ + .value = "decoration:glow:color", + .description = "glow's color. Alpha dictates glow's opacity.", + .type = CONFIG_OPTION_COLOR, + .data = SConfigOptionDescription::SColorData{0xee33ccff}, + }, + SConfigOptionDescription{ + .value = "decoration:glow:color_inactive", + .description = "inactive glow color. (if not set, will fall back to decoration:glow:color)", + .type = CONFIG_OPTION_COLOR, + .data = SConfigOptionDescription::SColorData{0x0033ccff}, + }, + SConfigOptionDescription{ .value = "decoration:dim_modal", .description = "enables dimming of parents of modal windows", .type = CONFIG_OPTION_BOOL, diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index edab2135..9656bf28 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -21,6 +21,7 @@ #include "../history/WindowHistoryTracker.hpp" #include "../../Compositor.hpp" #include "../../render/decorations/CHyprDropShadowDecoration.hpp" +#include "../../render/decorations/CHyprInnerGlowDecoration.hpp" #include "../../render/decorations/CHyprGroupBarDecoration.hpp" #include "../../render/decorations/CHyprBorderDecoration.hpp" #include "../../config/ConfigValue.hpp" @@ -85,6 +86,7 @@ PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) { g_pAnimationManager->createAnimation(1.f, pWindow->m_alpha, Config::animationTree()->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(1.f, pWindow->m_activeInactiveAlpha, Config::animationTree()->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_realShadowColor, Config::animationTree()->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); + g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_realGlowColor, Config::animationTree()->getAnimationPropertyConfig("fadeGlow"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(0.f, pWindow->m_dimPercent, Config::animationTree()->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(0.f, pWindow->m_movingToWorkspaceAlpha, Config::animationTree()->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(0.f, pWindow->m_movingFromWorkspaceAlpha, Config::animationTree()->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); @@ -92,6 +94,7 @@ PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) { pWindow->addWindowDeco(makeUnique<CHyprDropShadowDecoration>(pWindow)); pWindow->addWindowDeco(makeUnique<CHyprBorderDecoration>(pWindow)); + pWindow->addWindowDeco(makeUnique<CHyprInnerGlowDecoration>(pWindow)); pWindow->m_target = Layout::CWindowTarget::create(pWindow); @@ -113,6 +116,7 @@ PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) { g_pAnimationManager->createAnimation(1.f, pWindow->m_alpha, Config::animationTree()->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(1.f, pWindow->m_activeInactiveAlpha, Config::animationTree()->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_realShadowColor, Config::animationTree()->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); + g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_realGlowColor, Config::animationTree()->getAnimationPropertyConfig("fadeGlow"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(0.f, pWindow->m_dimPercent, Config::animationTree()->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(0.f, pWindow->m_movingToWorkspaceAlpha, Config::animationTree()->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(0.f, pWindow->m_movingFromWorkspaceAlpha, Config::animationTree()->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); @@ -120,6 +124,7 @@ PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) { pWindow->addWindowDeco(makeUnique<CHyprDropShadowDecoration>(pWindow)); pWindow->addWindowDeco(makeUnique<CHyprBorderDecoration>(pWindow)); + pWindow->addWindowDeco(makeUnique<CHyprInnerGlowDecoration>(pWindow)); pWindow->m_target = Layout::CWindowTarget::create(pWindow); @@ -622,6 +627,7 @@ void CWindow::onMap() { m_activeInactiveAlpha->resetAllCallbacks(); m_alpha->resetAllCallbacks(); m_realShadowColor->resetAllCallbacks(); + m_realGlowColor->resetAllCallbacks(); m_dimPercent->resetAllCallbacks(); m_movingToWorkspaceAlpha->resetAllCallbacks(); m_movingFromWorkspaceAlpha->resetAllCallbacks(); @@ -1528,6 +1534,9 @@ void CWindow::updateDecorationValues() { static auto PFULLSCREENALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:fullscreen_opacity"); static auto PSHADOWCOL = CConfigValue<Hyprlang::INT>("decoration:shadow:color"); static auto PSHADOWCOLINACTIVE = CConfigValue<Hyprlang::INT>("decoration:shadow:color_inactive"); + static auto PGLOW = CConfigValue<Hyprlang::INT>("decoration:glow:enabled"); + static auto PGLOWCOL = CConfigValue<Hyprlang::INT>("decoration:glow:color"); + static auto PGLOWCOLINACTIVE = CConfigValue<Hyprlang::INT>("decoration:glow:color_inactive"); static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength"); static auto PDIMENABLED = CConfigValue<Hyprlang::INT>("decoration:dim_inactive"); static auto PDIMMODAL = CConfigValue<Hyprlang::INT>("decoration:dim_modal"); @@ -1557,9 +1566,15 @@ void CWindow::updateDecorationValues() { if (m_self == Desktop::focusState()->window()) { const auto* const ACTIVECOLOR = !m_group ? (!(m_groupRules & GROUP_DENY) ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); setBorderColor(m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR)); + + if (*PGLOW) + *m_realGlowColor = *PGLOWCOL; } else { const auto* const INACTIVECOLOR = !m_group ? (!(m_groupRules & GROUP_DENY) ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); setBorderColor(m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR)); + + if (*PGLOW) + *m_realGlowColor = *PGLOWCOLINACTIVE; } // opacity diff --git a/src/desktop/view/Window.hpp b/src/desktop/view/Window.hpp index 23dd0117..57dc2697 100644 --- a/src/desktop/view/Window.hpp +++ b/src/desktop/view/Window.hpp @@ -227,6 +227,9 @@ namespace Desktop::View { // animated shadow color PHLANIMVAR<CHyprColor> m_realShadowColor; + // animated glow color + PHLANIMVAR<CHyprColor> m_realGlowColor; + // animated tint PHLANIMVAR<float> m_dimPercent; diff --git a/src/render/GLRenderer.cpp b/src/render/GLRenderer.cpp index 48825a4b..afef2ba0 100644 --- a/src/render/GLRenderer.cpp +++ b/src/render/GLRenderer.cpp @@ -1,4 +1,5 @@ #include "GLRenderer.hpp" +#include "decorations/CHyprInnerGlowDecoration.hpp" #include <aquamarine/output/Output.hpp> #include "../config/ConfigValue.hpp" #include "../managers/CursorManager.hpp" @@ -354,6 +355,11 @@ void CHyprGLRenderer::draw(CShadowPassElement* element, const CRegion& damage) { m_data.deco->render(g_pHyprRenderer->m_renderData.pMonitor.lock(), m_data.a); }; +void CHyprGLRenderer::draw(CInnerGlowPassElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + m_data.deco->render(g_pHyprRenderer->m_renderData.pMonitor.lock(), m_data.a); +}; + void CHyprGLRenderer::draw(CTexPassElement* element, const CRegion& damage) { const auto m_data = element->m_data; diff --git a/src/render/GLRenderer.hpp b/src/render/GLRenderer.hpp index 1446e9b2..6f511fc3 100644 --- a/src/render/GLRenderer.hpp +++ b/src/render/GLRenderer.hpp @@ -41,6 +41,7 @@ class CHyprGLRenderer : public IHyprRenderer { void draw(CPreBlurElement* element, const CRegion& damage) override; void draw(CRectPassElement* element, const CRegion& damage) override; void draw(CShadowPassElement* element, const CRegion& damage) override; + void draw(CInnerGlowPassElement* element, const CRegion& damage) override; void draw(CTexPassElement* element, const CRegion& damage) override; void draw(CTextureMatteElement* element, const CRegion& damage) override; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 16e892ca..9aaa99d5 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -853,14 +853,14 @@ void CHyprOpenGLImpl::end() { } static const std::vector<std::string> SHADER_INCLUDES = { - "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl", - "border.glsl", "shadow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl", + "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl", + "border.glsl", "shadow.glsl", "inner_glow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl", }; // order matters, see ePreparedFragmentShader const std::array<std::string, SH_FRAG_LAST> FRAG_SHADERS = { - "quad.frag", "passthru.frag", "rgbamatte.frag", "ext.frag", "blur1.frag", "blur2.frag", - "blurprepare.frag", "blurfinish.frag", "shadow.frag", "surface.frag", "border.frag", "glitch.frag", + "quad.frag", "passthru.frag", "rgbamatte.frag", "ext.frag", "blur1.frag", "blur2.frag", "blurprepare.frag", + "blurfinish.frag", "shadow.frag", "inner_glow.frag", "surface.frag", "border.frag", "glitch.frag", }; bool CHyprOpenGLImpl::initShaders(const std::string& path) { @@ -2283,6 +2283,69 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun glBindVertexArray(0); } +void CHyprOpenGLImpl::renderInnerGlow(const CBox& box, int round, float roundingPower, int range, const CHyprColor& color, int glowPower, float a) { + auto& m_renderData = g_pHyprRenderer->m_renderData; + RASSERT(m_renderData.pMonitor, "Tried to render inner glow without begin()!"); + RASSERT((box.width > 0 && box.height > 0), "Tried to render inner glow with width/height < 0!"); + + if (g_pHyprRenderer->m_renderData.damage.empty()) + return; + + TRACY_GPU_ZONE("RenderInnerGlow"); + + CBox newBox = box; + g_pHyprRenderer->m_renderData.renderModif.applyToBox(newBox); + + const auto col = color; + + const auto& glMatrix = g_pHyprRenderer->projectBoxToTarget(newBox); + + blend(true); + + const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + auto shader = useShader(getShaderVariant(SH_FRAG_INNER_GLOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + if (!skipCM) + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); + + shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); + shader->setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a); + + const auto TOPLEFT = Vector2D(round, round); + const auto BOTTOMRIGHT = Vector2D(newBox.width - round, newBox.height - round); + const auto FULLSIZE = Vector2D(newBox.width, newBox.height); + + shader->setUniformFloat2(SHADER_TOP_LEFT, sc<float>(TOPLEFT.x), sc<float>(TOPLEFT.y)); + shader->setUniformFloat2(SHADER_BOTTOM_RIGHT, sc<float>(BOTTOMRIGHT.x), sc<float>(BOTTOMRIGHT.y)); + shader->setUniformFloat2(SHADER_FULL_SIZE, sc<float>(FULLSIZE.x), sc<float>(FULLSIZE.y)); + shader->setUniformFloat(SHADER_RADIUS, round); + shader->setUniformFloat(SHADER_ROUNDING_POWER, roundingPower); + shader->setUniformFloat(SHADER_RANGE, range); + shader->setUniformFloat(SHADER_SHADOW_POWER, glowPower); + + glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + + if (g_pHyprRenderer->m_renderData.clipBox.width != 0 && g_pHyprRenderer->m_renderData.clipBox.height != 0) { + CRegion damageClip{g_pHyprRenderer->m_renderData.clipBox.x, g_pHyprRenderer->m_renderData.clipBox.y, g_pHyprRenderer->m_renderData.clipBox.width, + g_pHyprRenderer->m_renderData.clipBox.height}; + damageClip.intersect(g_pHyprRenderer->m_renderData.damage); + + if (!damageClip.empty()) { + damageClip.forEachRect([this](const auto& RECT) { + scissor(&RECT, g_pHyprRenderer->m_renderData.transformDamage); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + }); + } + } else { + g_pHyprRenderer->m_renderData.damage.forEachRect([this](const auto& RECT) { + scissor(&RECT, g_pHyprRenderer->m_renderData.transformDamage); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + }); + } + + glBindVertexArray(0); +} + void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { auto& m_renderData = g_pHyprRenderer->m_renderData; if (!g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->isAllocated()) diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index e92d4822..bbca01c0 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -228,6 +228,7 @@ class CHyprOpenGLImpl { void renderRect(const CBox&, const CHyprColor&, SRectRenderData data); void renderTexture(SP<ITexture>, const CBox&, STextureRenderData data); void renderRoundedShadow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, float a = 1.0); + void renderInnerGlow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, int glowPower, float a = 1.0); void renderBorder(const CBox&, const Config::CGradientValueData&, SBorderRenderData data); void renderBorder(const CBox&, const Config::CGradientValueData&, const Config::CGradientValueData&, float lerp, SBorderRenderData data); void renderTextureMatte(SP<ITexture> tex, const CBox& pBox, SP<IFramebuffer> matte); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 66d48247..0c184238 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1105,6 +1105,7 @@ void IHyprRenderer::draw(WP<IPassElement> element, const CRegion& damage) { case EK_RECT: drawRect(dc<CRectPassElement*>(element.get()), damage); break; case EK_HINTS: drawHints(dc<CRendererHintsPassElement*>(element.get()), damage); break; case EK_SHADOW: draw(dc<CShadowPassElement*>(element.get()), damage); break; + case EK_INNER_GLOW: draw(dc<CInnerGlowPassElement*>(element.get()), damage); break; case EK_SURFACE: preDrawSurface(dc<CSurfacePassElement*>(element.get()), damage); break; case EK_TEXTURE: drawTex(dc<CTexPassElement*>(element.get()), damage); break; case EK_TEXTURE_MATTE: drawTexMatte(dc<CTextureMatteElement*>(element.get()), damage); break; diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 4b65141a..d8056d82 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -23,6 +23,7 @@ #include "pass/RectPassElement.hpp" #include "pass/RendererHintsPassElement.hpp" #include "pass/ShadowPassElement.hpp" +#include "pass/InnerGlowPassElement.hpp" #include "pass/SurfacePassElement.hpp" #include "pass/TexPassElement.hpp" #include "pass/TextureMatteElement.hpp" @@ -288,14 +289,15 @@ class IHyprRenderer { }; SP<ITexture> getBackground(PHLMONITOR pMonitor); - virtual void draw(CBorderPassElement* element, const CRegion& damage) = 0; - virtual void draw(CClearPassElement* element, const CRegion& damage) = 0; - virtual void draw(CFramebufferElement* element, const CRegion& damage) = 0; - virtual void draw(CPreBlurElement* element, const CRegion& damage) = 0; - virtual void draw(CRectPassElement* element, const CRegion& damage) = 0; - virtual void draw(CShadowPassElement* element, const CRegion& damage) = 0; - virtual void draw(CTexPassElement* element, const CRegion& damage) = 0; - virtual void draw(CTextureMatteElement* element, const CRegion& damage) = 0; + virtual void draw(CBorderPassElement* element, const CRegion& damage) = 0; + virtual void draw(CClearPassElement* element, const CRegion& damage) = 0; + virtual void draw(CFramebufferElement* element, const CRegion& damage) = 0; + virtual void draw(CPreBlurElement* element, const CRegion& damage) = 0; + virtual void draw(CRectPassElement* element, const CRegion& damage) = 0; + virtual void draw(CShadowPassElement* element, const CRegion& damage) = 0; + virtual void draw(CInnerGlowPassElement* element, const CRegion& damage) = 0; + virtual void draw(CTexPassElement* element, const CRegion& damage) = 0; + virtual void draw(CTextureMatteElement* element, const CRegion& damage) = 0; virtual SP<ITexture> getBlurTexture(PHLMONITORREF pMonitor); SP<ITexture> m_lockDeadTexture; SP<ITexture> m_lockDead2Texture; diff --git a/src/render/ShaderLoader.hpp b/src/render/ShaderLoader.hpp index e522e9fa..9f3f787d 100644 --- a/src/render/ShaderLoader.hpp +++ b/src/render/ShaderLoader.hpp @@ -36,6 +36,7 @@ namespace Render { SH_FRAG_BLURPREPARE, SH_FRAG_BLURFINISH, SH_FRAG_SHADOW, + SH_FRAG_INNER_GLOW, SH_FRAG_SURFACE, SH_FRAG_BORDER1, SH_FRAG_GLITCH, diff --git a/src/render/decorations/CHyprInnerGlowDecoration.cpp b/src/render/decorations/CHyprInnerGlowDecoration.cpp new file mode 100644 index 00000000..a60deccb --- /dev/null +++ b/src/render/decorations/CHyprInnerGlowDecoration.cpp @@ -0,0 +1,104 @@ +#include "CHyprInnerGlowDecoration.hpp" + +#include "../../Compositor.hpp" +#include "../pass/InnerGlowPassElement.hpp" +#include "../Renderer.hpp" +#include "../OpenGL.hpp" + +CHyprInnerGlowDecoration::CHyprInnerGlowDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_window(pWindow) { + ; +} + +eDecorationType CHyprInnerGlowDecoration::getDecorationType() { + return DECORATION_INNER_GLOW; +} + +SDecorationPositioningInfo CHyprInnerGlowDecoration::getPositioningInfo() { + SDecorationPositioningInfo info; + info.policy = DECORATION_POSITION_ABSOLUTE; + info.edges = DECORATION_EDGE_BOTTOM | DECORATION_EDGE_LEFT | DECORATION_EDGE_RIGHT | DECORATION_EDGE_TOP; + return info; +} + +void CHyprInnerGlowDecoration::onPositioningReply(const SDecorationPositioningReply& reply) { + updateWindow(m_window.lock()); +} + +uint64_t CHyprInnerGlowDecoration::getDecorationFlags() { + return DECORATION_NON_SOLID; +} + +std::string CHyprInnerGlowDecoration::getDisplayName() { + return "Inner Glow"; +} + +void CHyprInnerGlowDecoration::damageEntire() { + const auto PWINDOW = m_window.lock(); + if (!validMapped(PWINDOW)) + return; + + CBox windowBox = PWINDOW->getWindowMainSurfaceBox(); + + const auto PWORKSPACE = PWINDOW->m_workspace; + if (PWORKSPACE && PWORKSPACE->m_renderOffset->isBeingAnimated() && !PWINDOW->m_pinned) + windowBox.translate(PWORKSPACE->m_renderOffset->value()); + windowBox.translate(PWINDOW->m_floatingOffset); + + g_pHyprRenderer->damageRegion(CRegion(windowBox)); +} + +void CHyprInnerGlowDecoration::updateWindow(PHLWINDOW pWindow) { + const auto PWINDOW = m_window.lock(); + m_lastWindowPos = PWINDOW->m_realPosition->value(); + m_lastWindowSize = PWINDOW->m_realSize->value(); +} + +void CHyprInnerGlowDecoration::draw(PHLMONITOR pMonitor, float const& a) { + CInnerGlowPassElement::SInnerGlowData data; + data.deco = this; + data.a = a; + g_pHyprRenderer->m_renderPass.add(makeUnique<CInnerGlowPassElement>(data)); +} + +void CHyprInnerGlowDecoration::render(PHLMONITOR pMonitor, float const& a) { + + static auto PGLOW = CConfigValue<Hyprlang::INT>("decoration:glow:enabled"); + static auto PGLOWRANGE = CConfigValue<Hyprlang::INT>("decoration:glow:range"); + static auto PGLOWPOWER = CConfigValue<Hyprlang::INT>("decoration:glow:render_power"); + + if (!*PGLOW) + return; + + const auto PWINDOW = m_window.lock(); + + if (!validMapped(PWINDOW)) + return; + + const auto ROUNDING = PWINDOW->rounding() > 0 ? PWINDOW->rounding() - 1 : PWINDOW->rounding(); + const auto ROUNDINGPOWER = PWINDOW->roundingPower(); + const auto PWORKSPACE = PWINDOW->m_workspace; + const auto WORKSPACEOFF = PWORKSPACE && !PWINDOW->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D(); + + CBox windowBox = {m_lastWindowPos.x, m_lastWindowPos.y, m_lastWindowSize.x, m_lastWindowSize.y}; + windowBox.translate(-pMonitor->m_position + WORKSPACEOFF + PWINDOW->m_floatingOffset); + windowBox.scale(pMonitor->m_scale).round(); + + if (windowBox.width < 1 || windowBox.height < 1) + return; + + const int GLOWSIZE = *PGLOWRANGE; + const float GLOWPOWER = *PGLOWPOWER; + const auto GLOWCOLOR = m_window->m_realGlowColor->value(); + + g_pHyprRenderer->m_renderData.currentWindow = m_window; + + g_pHyprRenderer->blend(true); + + g_pHyprOpenGL->renderInnerGlow(windowBox, ROUNDING * pMonitor->m_scale, ROUNDINGPOWER, GLOWSIZE * pMonitor->m_scale, GLOWCOLOR, GLOWPOWER, a); + + g_pHyprRenderer->m_renderData.currentWindow.reset(); +} + +eDecorationLayer CHyprInnerGlowDecoration::getDecorationLayer() { + return DECORATION_LAYER_OVER; +} diff --git a/src/render/decorations/CHyprInnerGlowDecoration.hpp b/src/render/decorations/CHyprInnerGlowDecoration.hpp new file mode 100644 index 00000000..a2e4d79a --- /dev/null +++ b/src/render/decorations/CHyprInnerGlowDecoration.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "IHyprWindowDecoration.hpp" + +class CHyprInnerGlowDecoration : public IHyprWindowDecoration { + public: + CHyprInnerGlowDecoration(PHLWINDOW); + virtual ~CHyprInnerGlowDecoration() = default; + + virtual SDecorationPositioningInfo getPositioningInfo(); + + virtual void onPositioningReply(const SDecorationPositioningReply& reply); + + virtual void draw(PHLMONITOR, float const& a); + + virtual eDecorationType getDecorationType(); + + virtual void updateWindow(PHLWINDOW); + + virtual void damageEntire(); + + virtual eDecorationLayer getDecorationLayer(); + + virtual uint64_t getDecorationFlags(); + + virtual std::string getDisplayName(); + + void render(PHLMONITOR, float const& a); + + private: + PHLWINDOWREF m_window; + + Vector2D m_lastWindowPos; + Vector2D m_lastWindowSize; +}; diff --git a/src/render/decorations/IHyprWindowDecoration.hpp b/src/render/decorations/IHyprWindowDecoration.hpp index 9916d392..5f77ea42 100644 --- a/src/render/decorations/IHyprWindowDecoration.hpp +++ b/src/render/decorations/IHyprWindowDecoration.hpp @@ -9,6 +9,7 @@ enum eDecorationType : int8_t { DECORATION_NONE = -1, DECORATION_GROUPBAR, DECORATION_SHADOW, + DECORATION_INNER_GLOW, DECORATION_BORDER, DECORATION_CUSTOM }; diff --git a/src/render/pass/InnerGlowPassElement.cpp b/src/render/pass/InnerGlowPassElement.cpp new file mode 100644 index 00000000..04ccebd7 --- /dev/null +++ b/src/render/pass/InnerGlowPassElement.cpp @@ -0,0 +1,13 @@ +#include "InnerGlowPassElement.hpp" + +CInnerGlowPassElement::CInnerGlowPassElement(const CInnerGlowPassElement::SInnerGlowData& data_) : m_data(data_) { + ; +} + +bool CInnerGlowPassElement::needsLiveBlur() { + return false; +} + +bool CInnerGlowPassElement::needsPrecomputeBlur() { + return false; +} diff --git a/src/render/pass/InnerGlowPassElement.hpp b/src/render/pass/InnerGlowPassElement.hpp new file mode 100644 index 00000000..62db7791 --- /dev/null +++ b/src/render/pass/InnerGlowPassElement.hpp @@ -0,0 +1,28 @@ +#pragma once +#include "PassElement.hpp" + +class CHyprInnerGlowDecoration; + +class CInnerGlowPassElement : public IPassElement { + public: + struct SInnerGlowData { + CHyprInnerGlowDecoration* deco = nullptr; + float a = 1.F; + }; + + CInnerGlowPassElement(const SInnerGlowData& data_); + virtual ~CInnerGlowPassElement() = default; + + virtual bool needsLiveBlur(); + virtual bool needsPrecomputeBlur(); + + virtual const char* passName() { + return "CInnerGlowPassElement"; + } + + virtual ePassElementType type() { + return EK_INNER_GLOW; + }; + + SInnerGlowData m_data; +}; diff --git a/src/render/pass/PassElement.hpp b/src/render/pass/PassElement.hpp index 0dc5f581..c262b36c 100644 --- a/src/render/pass/PassElement.hpp +++ b/src/render/pass/PassElement.hpp @@ -13,7 +13,8 @@ enum ePassElementType : uint8_t { EK_SHADOW, EK_SURFACE, EK_TEXTURE, - EK_TEXTURE_MATTE + EK_TEXTURE_MATTE, + EK_INNER_GLOW }; class IPassElement { diff --git a/src/render/shaders/glsl/inner_glow.frag b/src/render/shaders/glsl/inner_glow.frag new file mode 100644 index 00000000..2fc405c0 --- /dev/null +++ b/src/render/shaders/glsl/inner_glow.frag @@ -0,0 +1,57 @@ +#version 300 es +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable + +#include "defines.h" + +precision highp float; +in vec4 v_color; +in vec2 v_texcoord; + +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +uniform mat3 targetPrimariesXYZ; + +uniform vec2 topLeft; +uniform vec2 bottomRight; +uniform vec2 fullSize; +uniform float radius; +uniform float roundingPower; +uniform float range; +uniform float shadowPower; + +#if USE_CM +#include "cm_helpers.glsl" +#include "CM.glsl" +#endif + +#include "inner_glow.glsl" + +layout(location = 0) out vec4 fragColor; +void main() { + vec4 pixColor = v_color; + + fragColor = getInnerGlow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight +#if USE_CM + , + sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_ICC + , + iccLut3D, iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance +#endif +#if USE_SDR_MOD + , + sdrSaturation, sdrBrightnessMultiplier +#endif +#endif +#endif + ); +} diff --git a/src/render/shaders/glsl/inner_glow.glsl b/src/render/shaders/glsl/inner_glow.glsl new file mode 100644 index 00000000..b0d55b19 --- /dev/null +++ b/src/render/shaders/glsl/inner_glow.glsl @@ -0,0 +1,106 @@ +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif +#ifndef INNER_GLOW_GLSL +#define INNER_GLOW_GLSL + +#include "cm_helpers.glsl" + +float innerGlowAlpha(float distFromEdge, float range, float glowPower) { + if (distFromEdge >= range) + return 0.0; + + if (distFromEdge <= 0.0) + return 1.0; + + return pow(1.0 - distFromEdge / range, glowPower); +} + +float innerGlowModifiedLength(vec2 a, float roundingPower) { + return pow(pow(abs(a.x), roundingPower) + pow(abs(a.y), roundingPower), 1.0 / roundingPower); +} + +float innerGlowSmin(float a, float b, float k) { + float h = max(k - abs(a - b), 0.0) / k; + return min(a, b) - h * h * h * k * (1.0 / 6.0); +} + +vec4 getInnerGlow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float glowPower, vec2 bottomRight +#if USE_CM + , + int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange +#if USE_ICC + , + highp sampler3D iccLut3D, float iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + mat3 targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance +#endif +#if USE_SDR_MOD + , + float sdrSaturation, float sdrBrightnessMultiplier +#endif +#endif +#endif +) { + vec2 pixCoord = fullSize * v_texcoord; + + // clip to the rounded rectangle shape using actual SDF + vec2 center = fullSize * 0.5; + vec2 p = abs(pixCoord - center); + vec2 q = p - (center - vec2(radius)); + vec2 qc = max(q, vec2(0.0)); + float cornerD = (qc.x > 0.0 || qc.y > 0.0) ? innerGlowModifiedLength(qc, roundingPower) : 0.0; + float sdfDist = cornerD + min(max(q.x, q.y), 0.0) - radius; + + if (sdfDist > 0.0) + discard; + + // smooth-min of edge distances for rounded glow contours + float distT = pixCoord.y; + float distB = fullSize.y - pixCoord.y; + float distL = pixCoord.x; + float distR = fullSize.x - pixCoord.x; + + float k = range; + float distFromEdge = innerGlowSmin(innerGlowSmin(distT, distB, k), innerGlowSmin(distL, distR, k), k); + + pixColor[3] = pixColor[3] * innerGlowAlpha(distFromEdge, range, glowPower); + + if (pixColor[3] == 0.0) + discard; + + // premultiply + pixColor.rgb *= pixColor[3]; + +#if USE_CM + pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_ICC + , + iccLut3D, iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance +#endif +#if USE_SDR_MOD + , + sdrSaturation, sdrBrightnessMultiplier +#endif +#endif + ); +#endif + + return pixColor; +} +#endif |
