mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-11-16 23:05:58 +01:00
renderer: add shadowable props
This commit is contained in:
parent
2fa0da47ab
commit
a4dc596cb8
13 changed files with 173 additions and 21 deletions
|
@ -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{"<i>Input Password</i>"});
|
||||
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::SWidgetConfig> CConfigManager::getWidgetConfigs() {
|
||||
std::vector<CConfigManager::SWidgetConfig> 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::SWidgetConfig> 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::SWidgetConfig> 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -146,6 +146,9 @@ CRenderer::CRenderer() {
|
|||
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);
|
||||
|
||||
|
@ -468,3 +479,13 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
|
|||
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
#include "../core/LockSurface.hpp"
|
||||
#include "Shader.hpp"
|
||||
|
@ -24,6 +25,8 @@ class CRenderer {
|
|||
struct SBlurParams {
|
||||
int size = 0, passes = 0;
|
||||
float noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0;
|
||||
std::optional<CColor> colorize;
|
||||
float boostA = 1.0;
|
||||
};
|
||||
|
||||
SRenderFeedback renderLock(const CSessionLockSurface& surface);
|
||||
|
@ -35,6 +38,9 @@ class CRenderer {
|
|||
std::unique_ptr<CAsyncResourceGatherer> 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<float, 9> projMatrix;
|
||||
std::array<float, 9> projection;
|
||||
|
||||
std::vector<GLint> boundFBs;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CRenderer> g_pRenderer;
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
)#";
|
|
@ -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<std::string, std::any>& props, CSessionLockSurface* surface_) : surface(surface_) {
|
||||
CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, CSessionLockSurface* surface_) :
|
||||
surface(surface_), shadow(this, props, viewport_) {
|
||||
labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
|
||||
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
|
||||
CColor labelColor = std::any_cast<Hyprlang::INT>(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;
|
||||
|
|
|
@ -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<CTimer> labelTimer;
|
||||
|
||||
CShadowable shadow;
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
#include "../../core/hyprlock.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) {
|
||||
CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) {
|
||||
size = std::any_cast<Hyprlang::VEC2>(props.at("size"));
|
||||
inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
|
||||
outer = std::any_cast<Hyprlang::INT>(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;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "IWidget.hpp"
|
||||
#include "../../helpers/Vector2D.hpp"
|
||||
#include "../../helpers/Color.hpp"
|
||||
#include "Shadowable.hpp"
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <any>
|
||||
|
@ -22,6 +23,8 @@ class CPasswordInputField : public IWidget {
|
|||
void updateFailTex();
|
||||
void updateHiddenInputState();
|
||||
|
||||
bool firstRender = true;
|
||||
|
||||
Vector2D size;
|
||||
Vector2D pos;
|
||||
Vector2D viewport;
|
||||
|
@ -64,4 +67,6 @@ class CPasswordInputField : public IWidget {
|
|||
} hiddenInputState;
|
||||
|
||||
bool fadeOnEmpty;
|
||||
|
||||
CShadowable shadow;
|
||||
};
|
||||
|
|
36
src/renderer/widgets/Shadowable.cpp
Normal file
36
src/renderer/widgets/Shadowable.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "Shadowable.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
CShadowable::CShadowable(IWidget* widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) : widget(widget_), viewport(viewport_) {
|
||||
size = std::any_cast<Hyprlang::INT>(props.at("shadow_size"));
|
||||
passes = std::any_cast<Hyprlang::INT>(props.at("shadow_passes"));
|
||||
color = std::any_cast<Hyprlang::INT>(props.at("shadow_color"));
|
||||
boostA = std::any_cast<Hyprlang::FLOAT>(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;
|
||||
}
|
32
src/renderer/widgets/Shadowable.hpp
Normal file
32
src/renderer/widgets/Shadowable.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Framebuffer.hpp"
|
||||
#include "../Texture.hpp"
|
||||
#include "../../helpers/Color.hpp"
|
||||
#include "IWidget.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <any>
|
||||
|
||||
class CShadowable {
|
||||
public:
|
||||
CShadowable(IWidget* widget_, const std::unordered_map<std::string, std::any>& 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;
|
||||
};
|
Loading…
Reference in a new issue