diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 59c473c..3f69749 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -35,6 +35,13 @@ CConfigManager::CConfigManager() : m_config(getMainConfigPath().c_str(), Hyprlan
}
void CConfigManager::init() {
+
+#define SHADOWABLE(name) \
+ m_config.addSpecialConfigValue(name, "shadow_size", Hyprlang::INT{3}); \
+ m_config.addSpecialConfigValue(name, "shadow_passes", Hyprlang::INT{2}); \
+ m_config.addSpecialConfigValue(name, "shadow_color", Hyprlang::INT{0xFF000000}); \
+ m_config.addSpecialConfigValue(name, "shadow_boost", Hyprlang::FLOAT{1.2});
+
m_config.addConfigValue("general:disable_loading_bar", Hyprlang::INT{0});
m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0});
m_config.addConfigValue("general:grace", Hyprlang::INT{0});
@@ -70,6 +77,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"Input Password"});
m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0});
m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1});
+ SHADOWABLE("input-field");
m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("label", "monitor", Hyprlang::STRING{""});
@@ -80,6 +88,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"});
m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"none"});
m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"none"});
+ SHADOWABLE("label");
m_config.registerHandler(&::handleSource, "source", {false});
@@ -89,6 +98,8 @@ void CConfigManager::init() {
if (result.error)
Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError());
+
+#undef SHADOWABLE
}
std::mutex configMtx;
@@ -101,6 +112,12 @@ void* const* CConfigManager::getValuePtr(const std::string& name) {
std::vector CConfigManager::getWidgetConfigs() {
std::vector result;
+#define SHADOWABLE(name) \
+ {"shadow_size", m_config.getSpecialConfigValue(name, "shadow_size", k.c_str())}, {"shadow_passes", m_config.getSpecialConfigValue(name, "shadow_passes", k.c_str())}, \
+ {"shadow_color", m_config.getSpecialConfigValue(name, "shadow_color", k.c_str())}, { \
+ "shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str()) \
+ }
+
//
auto keys = m_config.listKeysForSpecialCategory("background");
for (auto& k : keys) {
@@ -146,6 +163,7 @@ std::vector CConfigManager::getWidgetConfigs() {
{"placeholder_text", m_config.getSpecialConfigValue("input-field", "placeholder_text", k.c_str())},
{"hide_input", m_config.getSpecialConfigValue("input-field", "hide_input", k.c_str())},
{"rounding", m_config.getSpecialConfigValue("input-field", "rounding", k.c_str())},
+ SHADOWABLE("input-field"),
}
});
// clang-format on
@@ -165,6 +183,7 @@ std::vector CConfigManager::getWidgetConfigs() {
{"text", m_config.getSpecialConfigValue("label", "text", k.c_str())},
{"halign", m_config.getSpecialConfigValue("label", "halign", k.c_str())},
{"valign", m_config.getSpecialConfigValue("label", "valign", k.c_str())},
+ SHADOWABLE("label"),
}
});
// clang-format on
diff --git a/src/renderer/Framebuffer.cpp b/src/renderer/Framebuffer.cpp
index 0b3cb62..75de303 100644
--- a/src/renderer/Framebuffer.cpp
+++ b/src/renderer/Framebuffer.cpp
@@ -17,11 +17,11 @@ static uint32_t glFormatToType(uint32_t gl) {
return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE;
}
-bool CFramebuffer::alloc(int w, int h) {
+bool CFramebuffer::alloc(int w, int h, bool highres) {
bool firstAlloc = false;
- uint32_t glFormat = drmFormatToGL(DRM_FORMAT_XRGB2101010); // TODO: revise only 10b when I find a way to figure out without sc whether display is 10b
- uint32_t glType = glFormatToType(glFormat);
+ uint32_t glFormat = highres ? GL_RGBA16F : drmFormatToGL(DRM_FORMAT_XRGB2101010); // TODO: revise only 10b when I find a way to figure out without sc whether display is 10b
+ uint32_t glType = highres ? GL_FLOAT : glFormatToType(glFormat);
if (m_iFb == (uint32_t)-1) {
firstAlloc = true;
diff --git a/src/renderer/Framebuffer.hpp b/src/renderer/Framebuffer.hpp
index 76858fc..07fd772 100644
--- a/src/renderer/Framebuffer.hpp
+++ b/src/renderer/Framebuffer.hpp
@@ -8,7 +8,7 @@ class CFramebuffer {
public:
~CFramebuffer();
- bool alloc(int w, int h);
+ bool alloc(int w, int h, bool highres = false);
void addStencil();
void bind() const;
void release();
diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp
index 56765c4..af8c042 100644
--- a/src/renderer/Renderer.cpp
+++ b/src/renderer/Renderer.cpp
@@ -138,14 +138,17 @@ CRenderer::CRenderer() {
blurPrepareShader.contrast = glGetUniformLocation(prog, "contrast");
blurPrepareShader.brightness = glGetUniformLocation(prog, "brightness");
- prog = createProgram(TEXVERTSRC, FRAGBLURFINISH);
- blurFinishShader.program = prog;
- blurFinishShader.tex = glGetUniformLocation(prog, "tex");
- blurFinishShader.proj = glGetUniformLocation(prog, "proj");
- blurFinishShader.posAttrib = glGetAttribLocation(prog, "pos");
- blurFinishShader.texAttrib = glGetAttribLocation(prog, "texcoord");
- blurFinishShader.brightness = glGetUniformLocation(prog, "brightness");
- blurFinishShader.noise = glGetUniformLocation(prog, "noise");
+ prog = createProgram(TEXVERTSRC, FRAGBLURFINISH);
+ blurFinishShader.program = prog;
+ blurFinishShader.tex = glGetUniformLocation(prog, "tex");
+ blurFinishShader.proj = glGetUniformLocation(prog, "proj");
+ blurFinishShader.posAttrib = glGetAttribLocation(prog, "pos");
+ blurFinishShader.texAttrib = glGetAttribLocation(prog, "texcoord");
+ blurFinishShader.brightness = glGetUniformLocation(prog, "brightness");
+ blurFinishShader.noise = glGetUniformLocation(prog, "noise");
+ blurFinishShader.colorize = glGetUniformLocation(prog, "colorize");
+ blurFinishShader.colorizeTint = glGetUniformLocation(prog, "colorizeTint");
+ blurFinishShader.boostA = glGetUniformLocation(prog, "boostA");
wlr_matrix_identity(projMatrix.data());
@@ -164,6 +167,10 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
g_pEGL->makeCurrent(surf.eglSurface);
glViewport(0, 0, surf.size.x, surf.size.y);
+ GLint fb = 0;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fb);
+ pushFb(fb);
+
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
@@ -330,8 +337,8 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
wlr_matrix_multiply(glMatrix, projection.data(), matrix);
CFramebuffer mirrors[2];
- mirrors[0].alloc(outfb.m_vSize.x, outfb.m_vSize.y);
- mirrors[1].alloc(outfb.m_vSize.x, outfb.m_vSize.y);
+ mirrors[0].alloc(outfb.m_vSize.x, outfb.m_vSize.y, true);
+ mirrors[1].alloc(outfb.m_vSize.x, outfb.m_vSize.y, true);
CFramebuffer* currentRenderToFB = &mirrors[0];
@@ -442,6 +449,10 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
glUniformMatrix3fv(blurFinishShader.proj, 1, GL_TRUE, glMatrix);
glUniform1f(blurFinishShader.noise, params.noise);
glUniform1f(blurFinishShader.brightness, params.brightness);
+ glUniform1i(blurFinishShader.colorize, params.colorize.has_value());
+ if (params.colorize.has_value())
+ glUniform3f(blurFinishShader.colorizeTint, params.colorize->r, params.colorize->g, params.colorize->b);
+ glUniform1f(blurFinishShader.boostA, params.boostA);
glUniform1i(blurFinishShader.tex, 0);
@@ -467,4 +478,14 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
renderTexture(box, currentRenderToFB->m_cTex, 1.0, 0, WL_OUTPUT_TRANSFORM_NORMAL);
glEnable(GL_BLEND);
-}
\ No newline at end of file
+}
+
+void CRenderer::pushFb(GLint fb) {
+ boundFBs.push_back(fb);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
+}
+
+void CRenderer::popFb() {
+ boundFBs.pop_back();
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundFBs.empty() ? 0 : boundFBs.back());
+}
diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp
index 30fb931..6599740 100644
--- a/src/renderer/Renderer.hpp
+++ b/src/renderer/Renderer.hpp
@@ -2,6 +2,7 @@
#include
#include
+#include
#include "../core/LockSurface.hpp"
#include "Shader.hpp"
@@ -22,8 +23,10 @@ class CRenderer {
};
struct SBlurParams {
- int size = 0, passes = 0;
- float noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0;
+ int size = 0, passes = 0;
+ float noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0;
+ std::optional colorize;
+ float boostA = 1.0;
};
SRenderFeedback renderLock(const CSessionLockSurface& surface);
@@ -35,6 +38,9 @@ class CRenderer {
std::unique_ptr asyncResourceGatherer;
std::chrono::system_clock::time_point gatheredAt;
+ void pushFb(GLint fb);
+ void popFb();
+
private:
widgetMap_t widgets;
@@ -49,6 +55,8 @@ class CRenderer {
std::array projMatrix;
std::array projection;
+
+ std::vector boundFBs;
};
inline std::unique_ptr g_pRenderer;
\ No newline at end of file
diff --git a/src/renderer/Shader.hpp b/src/renderer/Shader.hpp
index e123632..bb1d5e8 100644
--- a/src/renderer/Shader.hpp
+++ b/src/renderer/Shader.hpp
@@ -59,6 +59,11 @@ class CShader {
GLint brightness = -1;
GLint noise = -1;
+ // colorize
+ GLint colorize = -1;
+ GLint colorizeTint = -1;
+ GLint boostA = -1;
+
GLint getUniformLocation(const std::string&);
void destroy();
diff --git a/src/renderer/Shaders.hpp b/src/renderer/Shaders.hpp
index de41f3a..2710051 100644
--- a/src/renderer/Shaders.hpp
+++ b/src/renderer/Shaders.hpp
@@ -331,6 +331,10 @@ uniform sampler2D tex;
uniform float noise;
uniform float brightness;
+uniform int colorize;
+uniform vec3 colorizeTint;
+uniform float boostA;
+
float hash(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
@@ -348,6 +352,13 @@ void main() {
pixColor.rgb *= brightness;
}
+ pixColor.a *= boostA;
+
+ if (colorize == 1) {
+ gl_FragColor = vec4(colorizeTint.r, colorizeTint.g, colorizeTint.b, pixColor.a);
+ return;
+ }
+
gl_FragColor = pixColor;
}
)#";
\ No newline at end of file
diff --git a/src/renderer/widgets/Label.cpp b/src/renderer/widgets/Label.cpp
index 3e6d54c..5c2a05a 100644
--- a/src/renderer/widgets/Label.cpp
+++ b/src/renderer/widgets/Label.cpp
@@ -52,7 +52,8 @@ void CLabel::plantTimer() {
labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), onTimer, this);
}
-CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map& props, CSessionLockSurface* surface_) : surface(surface_) {
+CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map& props, CSessionLockSurface* surface_) :
+ surface(surface_), shadow(this, props, viewport_) {
labelPreFormat = std::any_cast(props.at("text"));
std::string fontFamily = std::any_cast(props.at("font_family"));
CColor labelColor = std::any_cast(props.at("color"));
@@ -92,6 +93,7 @@ bool CLabel::draw(const SRenderData& data) {
// calc pos
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
+ shadow.markShadowDirty();
}
if (!pendingResourceID.empty()) {
@@ -104,11 +106,12 @@ bool CLabel::draw(const SRenderData& data) {
resourceID = pendingResourceID;
pendingResourceID = "";
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
+ shadow.markShadowDirty();
}
}
CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y};
-
+ shadow.draw(data);
g_pRenderer->renderTexture(box, asset->texture, data.opacity);
return false;
diff --git a/src/renderer/widgets/Label.hpp b/src/renderer/widgets/Label.hpp
index 890390e..b3dd6f0 100644
--- a/src/renderer/widgets/Label.hpp
+++ b/src/renderer/widgets/Label.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "IWidget.hpp"
+#include "Shadowable.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../../core/Timer.hpp"
#include "../AsyncResourceGatherer.hpp"
@@ -38,4 +39,6 @@ class CLabel : public IWidget {
CAsyncResourceGatherer::SPreloadRequest request;
std::shared_ptr labelTimer;
+
+ CShadowable shadow;
};
\ No newline at end of file
diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp
index 41d08c4..fd09a7e 100644
--- a/src/renderer/widgets/PasswordInputField.cpp
+++ b/src/renderer/widgets/PasswordInputField.cpp
@@ -3,7 +3,7 @@
#include "../../core/hyprlock.hpp"
#include
-CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map& props) {
+CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map& props) : shadow(this, props, viewport_) {
size = std::any_cast(props.at("size"));
inner = std::any_cast(props.at("inner_color"));
outer = std::any_cast(props.at("outer_color"));
@@ -105,6 +105,11 @@ bool CPasswordInputField::draw(const SRenderData& data) {
CBox inputFieldBox = {pos, size};
CBox outerBox = {pos - Vector2D{outThick, outThick}, size + Vector2D{outThick * 2, outThick * 2}};
+ if (firstRender) {
+ firstRender = false;
+ shadow.markShadowDirty();
+ }
+
bool forceReload = false;
updateFade();
@@ -112,6 +117,10 @@ bool CPasswordInputField::draw(const SRenderData& data) {
updateFailTex();
updateHiddenInputState();
+ SRenderData shadowData = data;
+ shadowData.opacity *= fade.a;
+ shadow.draw(shadowData);
+
float passAlpha = g_pHyprlock->passwordCheckWaiting() ? 0.5 : 1.0;
CColor outerCol = outer;
diff --git a/src/renderer/widgets/PasswordInputField.hpp b/src/renderer/widgets/PasswordInputField.hpp
index 49c51d3..433e114 100644
--- a/src/renderer/widgets/PasswordInputField.hpp
+++ b/src/renderer/widgets/PasswordInputField.hpp
@@ -3,6 +3,7 @@
#include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../../helpers/Color.hpp"
+#include "Shadowable.hpp"
#include
#include
#include
@@ -22,6 +23,8 @@ class CPasswordInputField : public IWidget {
void updateFailTex();
void updateHiddenInputState();
+ bool firstRender = true;
+
Vector2D size;
Vector2D pos;
Vector2D viewport;
@@ -63,5 +66,7 @@ class CPasswordInputField : public IWidget {
bool enabled = false;
} hiddenInputState;
- bool fadeOnEmpty;
+ bool fadeOnEmpty;
+
+ CShadowable shadow;
};
diff --git a/src/renderer/widgets/Shadowable.cpp b/src/renderer/widgets/Shadowable.cpp
new file mode 100644
index 0000000..c2fbcd0
--- /dev/null
+++ b/src/renderer/widgets/Shadowable.cpp
@@ -0,0 +1,36 @@
+#include "Shadowable.hpp"
+#include "../Renderer.hpp"
+
+CShadowable::CShadowable(IWidget* widget_, const std::unordered_map& props, const Vector2D& viewport_) : widget(widget_), viewport(viewport_) {
+ size = std::any_cast(props.at("shadow_size"));
+ passes = std::any_cast(props.at("shadow_passes"));
+ color = std::any_cast(props.at("shadow_color"));
+ boostA = std::any_cast(props.at("shadow_boost"));
+}
+
+void CShadowable::markShadowDirty() {
+
+ if (!shadowFB.isAllocated())
+ shadowFB.alloc(viewport.x, viewport.y, true);
+
+ g_pRenderer->pushFb(shadowFB.m_iFb);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ ignoreDraw = true;
+ widget->draw(IWidget::SRenderData{.opacity = 1.0});
+ ignoreDraw = false;
+
+ g_pRenderer->blurFB(shadowFB, CRenderer::SBlurParams{.size = size, .passes = passes, .colorize = color, .boostA = boostA});
+
+ g_pRenderer->popFb();
+}
+
+bool CShadowable::draw(const IWidget::SRenderData& data) {
+ if (!shadowFB.isAllocated() || ignoreDraw)
+ return true;
+
+ CBox box = {0, 0, viewport.x, viewport.y};
+ g_pRenderer->renderTexture(box, shadowFB.m_cTex, 1.0, 0, WL_OUTPUT_TRANSFORM_NORMAL);
+ return true;
+}
\ No newline at end of file
diff --git a/src/renderer/widgets/Shadowable.hpp b/src/renderer/widgets/Shadowable.hpp
new file mode 100644
index 0000000..5dafb68
--- /dev/null
+++ b/src/renderer/widgets/Shadowable.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "../Framebuffer.hpp"
+#include "../Texture.hpp"
+#include "../../helpers/Color.hpp"
+#include "IWidget.hpp"
+
+#include
+#include
+#include
+
+class CShadowable {
+ public:
+ CShadowable(IWidget* widget_, const std::unordered_map& props, const Vector2D& viewport_ /* TODO: make this not the entire viewport */);
+
+ // instantly re-renders the shadow using the widget's draw() method
+ void markShadowDirty();
+ virtual bool draw(const IWidget::SRenderData& data);
+
+ private:
+ IWidget* widget = nullptr;
+ int size = 10;
+ int passes = 4;
+ float boostA = 1.0;
+ CColor color{0, 0, 0, 1.0};
+ Vector2D viewport;
+
+ // to avoid recursive shadows
+ bool ignoreDraw = false;
+
+ CFramebuffer shadowFB;
+};
\ No newline at end of file