Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: add border shader and border gradients #548

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions src/config/ConfigDataValues.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
#pragma once
#include "../helpers/Log.hpp"
#include "../helpers/Color.hpp"
#include <hyprutils/math/Vector2D.hpp>
#include <hyprutils/string/VarList.hpp>
#include <any>
#include <string>
#include <vector>
#include <cmath>

using namespace Hyprutils::String;

enum eConfigValueDataTypes {
CVD_TYPE_INVALID = -1,
CVD_TYPE_LAYOUT = 0,
CVD_TYPE_INVALID = -1,
CVD_TYPE_LAYOUT = 0,
CVD_TYPE_GRADIENT = 1,
};

class ICustomConfigValueData {
Expand Down Expand Up @@ -51,3 +58,57 @@ class CLayoutValueData : public ICustomConfigValueData {
bool y = false;
} m_sIsRelative;
};

class CGradientValueData : public ICustomConfigValueData {
public:
CGradientValueData() {};
CGradientValueData(CColor col) {
m_vColors.push_back(col);
};
virtual ~CGradientValueData() {};

virtual eConfigValueDataTypes getDataType() {
return CVD_TYPE_GRADIENT;
}

void reset(CColor col) {
m_vColors.clear();
m_vColors.emplace_back(col);
m_fAngle = 0;
}

/* Vector containing the colors */
std::vector<CColor> m_vColors;

/* Float corresponding to the angle (rad) */
float m_fAngle = 0;

//
bool operator==(const CGradientValueData& other) const {
if (other.m_vColors.size() != m_vColors.size() || m_fAngle != other.m_fAngle)
return false;

for (size_t i = 0; i < m_vColors.size(); ++i)
if (m_vColors[i] != other.m_vColors[i])
return false;

return true;
}

virtual std::string toString() {
std::string result;
for (auto& c : m_vColors) {
result += std::format("{:x} ", c.getAsHex());
}

result += std::format("{}deg", (int)(m_fAngle * 180.0 / M_PI));
return result;
}

static CGradientValueData* fromAnyPv(const std::any& v) {
RASSERT(v.type() == typeid(void*), "Invalid config value type");
const auto P = (CGradientValueData*)std::any_cast<void*>(v);
RASSERT(P, "Empty config value");
return P;
}
};
96 changes: 82 additions & 14 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "../helpers/MiscFunctions.hpp"
#include "../helpers/Log.hpp"
#include "../config/ConfigDataValues.hpp"
#include <hyprlang.hpp>
#include <hyprutils/path/Path.hpp>
#include <filesystem>
#include <glob.h>
Expand Down Expand Up @@ -69,6 +70,65 @@ static void configHandleLayoutOptionDestroy(void** data) {
delete reinterpret_cast<CLayoutValueData*>(*data);
}

static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) {
std::string V = VALUE;

if (!*data)
*data = new CGradientValueData();

const auto DATA = reinterpret_cast<CGradientValueData*>(*data);

CVarList varlist(V, 0, ' ');
DATA->m_vColors.clear();

std::string parseError = "";

for (auto const& var : varlist) {
if (var.find("deg") != std::string::npos) {
// last arg
try {
DATA->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (M_PI / 180.0); // radians
} catch (...) {
Debug::log(WARN, "Error parsing gradient {}", V);
parseError = "Error parsing gradient " + V;
}

break;
}

if (DATA->m_vColors.size() >= 10) {
Debug::log(WARN, "Error parsing gradient {}: max colors is 10.", V);
parseError = "Error parsing gradient " + V + ": max colors is 10.";
break;
}

try {
DATA->m_vColors.push_back(CColor(configStringToInt(var)));
} catch (std::exception& e) {
Debug::log(WARN, "Error parsing gradient {}", V);
parseError = "Error parsing gradient " + V + ": " + e.what();
}
}

if (DATA->m_vColors.size() == 0) {
Debug::log(WARN, "Error parsing gradient {}", V);
parseError = "Error parsing gradient " + V + ": No colors?";

DATA->m_vColors.push_back(0); // transparent
}

Hyprlang::CParseResult result;
if (!parseError.empty())
result.setError(parseError.c_str());

return result;
}

static void configHandleGradientDestroy(void** data) {
if (*data)
delete reinterpret_cast<CGradientValueData*>(*data);
}

static std::string getMainConfigPath() {
static const auto paths = Hyprutils::Path::findConfig("hyprlock");
if (paths.first.has_value())
Expand All @@ -82,6 +142,14 @@ CConfigManager::CConfigManager(std::string configPath) :
configCurrentPath = configPath.empty() ? getMainConfigPath() : configPath;
}

inline static constexpr auto GRADIENTCONFIG = [](const char* default_value) -> Hyprlang::CUSTOMTYPE {
return Hyprlang::CUSTOMTYPE{&configHandleGradientSet, configHandleGradientDestroy, default_value};
};

inline static constexpr auto LAYOUTCONFIG = [](const char* default_value) -> Hyprlang::CUSTOMTYPE {
return Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, default_value};
};

void CConfigManager::init() {

#define SHADOWABLE(name) \
Expand Down Expand Up @@ -118,12 +186,12 @@ void CConfigManager::init() {

m_config.addSpecialCategory("shape", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("shape", "monitor", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("shape", "size", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "100,100"});
m_config.addSpecialConfigValue("shape", "size", LAYOUTCONFIG("100,100"));
m_config.addSpecialConfigValue("shape", "rounding", Hyprlang::INT{0});
m_config.addSpecialConfigValue("shape", "border_size", Hyprlang::INT{0});
m_config.addSpecialConfigValue("shape", "border_color", Hyprlang::INT{0xFF00CFE6});
m_config.addSpecialConfigValue("shape", "border_color", GRADIENTCONFIG("0xFF00CFE6"));
m_config.addSpecialConfigValue("shape", "color", Hyprlang::INT{0xFF111111});
m_config.addSpecialConfigValue("shape", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("shape", "position", LAYOUTCONFIG("0,0"));
m_config.addSpecialConfigValue("shape", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("shape", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("shape", "rotate", Hyprlang::FLOAT{0});
Expand All @@ -137,8 +205,8 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("image", "size", Hyprlang::INT{150});
m_config.addSpecialConfigValue("image", "rounding", Hyprlang::INT{-1});
m_config.addSpecialConfigValue("image", "border_size", Hyprlang::INT{4});
m_config.addSpecialConfigValue("image", "border_color", Hyprlang::INT{0xFFDDDDDD});
m_config.addSpecialConfigValue("image", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("image", "border_color", GRADIENTCONFIG("0xFFDDDDDD"));
m_config.addSpecialConfigValue("image", "position", LAYOUTCONFIG("0,0"));
m_config.addSpecialConfigValue("image", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("image", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("image", "rotate", Hyprlang::FLOAT{0});
Expand All @@ -149,9 +217,9 @@ void CConfigManager::init() {

m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("input-field", "monitor", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("input-field", "size", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "400,90"});
m_config.addSpecialConfigValue("input-field", "size", LAYOUTCONFIG("400,90"));
m_config.addSpecialConfigValue("input-field", "inner_color", Hyprlang::INT{0xFFDDDDDD});
m_config.addSpecialConfigValue("input-field", "outer_color", Hyprlang::INT{0xFF111111});
m_config.addSpecialConfigValue("input-field", "outer_color", GRADIENTCONFIG("0xFF111111"));
m_config.addSpecialConfigValue("input-field", "outline_thickness", Hyprlang::INT{4});
m_config.addSpecialConfigValue("input-field", "dots_size", Hyprlang::FLOAT{0.25});
m_config.addSpecialConfigValue("input-field", "dots_center", Hyprlang::INT{1});
Expand All @@ -165,26 +233,26 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("input-field", "font_family", Hyprlang::STRING{"Sans"});
m_config.addSpecialConfigValue("input-field", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("input-field", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("input-field", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("input-field", "position", LAYOUTCONFIG("0,0"));
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});
m_config.addSpecialConfigValue("input-field", "check_color", Hyprlang::INT{0xFFCC8822});
m_config.addSpecialConfigValue("input-field", "fail_color", Hyprlang::INT{0xFFCC2222});
m_config.addSpecialConfigValue("input-field", "check_color", GRADIENTCONFIG("0xFF22CC88"));
m_config.addSpecialConfigValue("input-field", "fail_color", GRADIENTCONFIG("0xFFCC2222"));
m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"<i>$FAIL</i>"});
m_config.addSpecialConfigValue("input-field", "fail_timeout", Hyprlang::INT{2000});
m_config.addSpecialConfigValue("input-field", "fail_transition", Hyprlang::INT{300});
m_config.addSpecialConfigValue("input-field", "capslock_color", Hyprlang::INT{-1});
m_config.addSpecialConfigValue("input-field", "numlock_color", Hyprlang::INT{-1});
m_config.addSpecialConfigValue("input-field", "bothlock_color", Hyprlang::INT{-1});
m_config.addSpecialConfigValue("input-field", "capslock_color", GRADIENTCONFIG(""));
m_config.addSpecialConfigValue("input-field", "numlock_color", GRADIENTCONFIG(""));
m_config.addSpecialConfigValue("input-field", "bothlock_color", GRADIENTCONFIG(""));
m_config.addSpecialConfigValue("input-field", "invert_numlock", Hyprlang::INT{0});
m_config.addSpecialConfigValue("input-field", "swap_font_color", Hyprlang::INT{0});
m_config.addSpecialConfigValue("input-field", "zindex", Hyprlang::INT{0});
SHADOWABLE("input-field");

m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("label", "monitor", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("label", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("label", "position", LAYOUTCONFIG("0,0"));
m_config.addSpecialConfigValue("label", "color", Hyprlang::INT{0xFFFFFFFF});
m_config.addSpecialConfigValue("label", "font_size", Hyprlang::INT{16});
m_config.addSpecialConfigValue("label", "text", Hyprlang::STRING{"Sample Text"});
Expand Down
44 changes: 44 additions & 0 deletions src/helpers/MiscFunctions.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include <filesystem>
#include "MiscFunctions.hpp"
#include "../helpers/Log.hpp"
#include <hyprutils/string/String.hpp>

using namespace Hyprutils::String;

std::string absolutePath(const std::string& rawpath, const std::string& currentDir) {
std::filesystem::path path(rawpath);
Expand All @@ -15,4 +19,44 @@ std::string absolutePath(const std::string& rawpath, const std::string& currentD
} else {
return std::filesystem::weakly_canonical(path);
}
}

int64_t configStringToInt(const std::string& VALUE) {
if (VALUE.starts_with("0x")) {
// Values with 0x are hex
const auto VALUEWITHOUTHEX = VALUE.substr(2);
return stol(VALUEWITHOUTHEX, nullptr, 16);
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6);

if (trim(VALUEWITHOUTFUNC).length() != 8) {
Debug::log(WARN, "invalid length {} for rgba", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes)");
}

const auto RGBA = std::stol(VALUEWITHOUTFUNC, nullptr, 16);

// now we need to RGBA -> ARGB. The config holds ARGB only.
return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF);
} else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5);

if (trim(VALUEWITHOUTFUNC).length() != 6) {
Debug::log(WARN, "invalid length {} for rgb", VALUEWITHOUTFUNC.length());
throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes)");
}

const auto RGB = std::stol(VALUEWITHOUTFUNC, nullptr, 16);

return RGB + 0xFF000000; // 0xFF for opaque
} else if (VALUE.starts_with("true") || VALUE.starts_with("on") || VALUE.starts_with("yes")) {
return 1;
} else if (VALUE.starts_with("false") || VALUE.starts_with("off") || VALUE.starts_with("no")) {
return 0;
}

if (VALUE.empty() || !isNumber(VALUE))
return 0;

return std::stoll(VALUE);
}
1 change: 1 addition & 0 deletions src/helpers/MiscFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
#include <hyprutils/math/Vector2D.hpp>

std::string absolutePath(const std::string&, const std::string&);
int64_t configStringToInt(const std::string& VALUE);
54 changes: 54 additions & 0 deletions src/renderer/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,23 @@ CRenderer::CRenderer() {
blurFinishShader.colorizeTint = glGetUniformLocation(prog, "colorizeTint");
blurFinishShader.boostA = glGetUniformLocation(prog, "boostA");

prog = createProgram(QUADVERTSRC, FRAGBORDER);
borderShader.program = prog;
borderShader.proj = glGetUniformLocation(prog, "proj");
borderShader.thick = glGetUniformLocation(prog, "thick");
borderShader.posAttrib = glGetAttribLocation(prog, "pos");
borderShader.texAttrib = glGetAttribLocation(prog, "texcoord");
borderShader.topLeft = glGetUniformLocation(prog, "topLeft");
borderShader.bottomRight = glGetUniformLocation(prog, "bottomRight");
borderShader.fullSize = glGetUniformLocation(prog, "fullSize");
borderShader.fullSizeUntransformed = glGetUniformLocation(prog, "fullSizeUntransformed");
borderShader.radius = glGetUniformLocation(prog, "radius");
borderShader.radiusOuter = glGetUniformLocation(prog, "radiusOuter");
borderShader.gradient = glGetUniformLocation(prog, "gradient");
borderShader.gradientLength = glGetUniformLocation(prog, "gradientLength");
borderShader.angle = glGetUniformLocation(prog, "angle");
borderShader.alpha = glGetUniformLocation(prog, "alpha");

asyncResourceGatherer = std::make_unique<CAsyncResourceGatherer>();
}

Expand Down Expand Up @@ -250,6 +267,43 @@ void CRenderer::renderRect(const CBox& box, const CColor& col, int rounding) {
glDisableVertexAttribArray(rectShader.posAttrib);
}

void CRenderer::renderBorder(const CBox& box, const CGradientValueData& gradient, int thickness, int rounding, float alpha) {
Mat3x3 matrix = projMatrix.projectBox(box, HYPRUTILS_TRANSFORM_NORMAL, box.rot);
Mat3x3 glMatrix = projection.copy().multiply(matrix);

glUseProgram(borderShader.program);

glUniformMatrix3fv(borderShader.proj, 1, GL_TRUE, glMatrix.getMatrix().data());

static_assert(sizeof(CColor) == 4 * sizeof(float)); // otherwise the line below this will fail

glUniform4fv(borderShader.gradient, gradient.m_vColors.size(), (float*)gradient.m_vColors.data());
glUniform1i(borderShader.gradientLength, gradient.m_vColors.size());
glUniform1f(borderShader.angle, (int)(gradient.m_fAngle / (M_PI / 180.0)) % 360 * (M_PI / 180.0));
glUniform1f(borderShader.alpha, alpha);

const auto TOPLEFT = Vector2D(box.x, box.y);
const auto FULLSIZE = Vector2D(box.width, box.height);

glUniform2f(borderShader.topLeft, (float)TOPLEFT.x, (float)TOPLEFT.y);
glUniform2f(borderShader.fullSize, (float)FULLSIZE.x, (float)FULLSIZE.y);
glUniform2f(borderShader.fullSizeUntransformed, (float)box.width, (float)box.height);
glUniform1f(borderShader.radius, rounding);
glUniform1f(borderShader.radiusOuter, rounding);
glUniform1f(borderShader.thick, thickness);

glVertexAttribPointer(borderShader.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);
glVertexAttribPointer(borderShader.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);

glEnableVertexAttribArray(borderShader.posAttrib);
glEnableVertexAttribArray(borderShader.texAttrib);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glDisableVertexAttribArray(borderShader.posAttrib);
glDisableVertexAttribArray(borderShader.texAttrib);
}

void CRenderer::renderTexture(const CBox& box, const CTexture& tex, float a, int rounding, std::optional<eTransform> tr) {
Mat3x3 matrix = projMatrix.projectBox(box, tr.value_or(HYPRUTILS_TRANSFORM_FLIPPED_180), box.rot);
Mat3x3 glMatrix = projection.copy().multiply(matrix);
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/Renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
#include "Shader.hpp"
#include "../core/LockSurface.hpp"
#include "../helpers/Color.hpp"
#include "../helpers/Math.hpp"
#include "AsyncResourceGatherer.hpp"
#include "../config/ConfigDataValues.hpp"
#include "widgets/IWidget.hpp"
#include "Framebuffer.hpp"

Expand All @@ -31,6 +31,7 @@ class CRenderer {
SRenderFeedback renderLock(const CSessionLockSurface& surface);

void renderRect(const CBox& box, const CColor& col, int rounding = 0);
void renderBorder(const CBox& box, const CGradientValueData& gradient, int thickness, int rounding = 0, float alpha = 1.0);
void renderTexture(const CBox& box, const CTexture& tex, float a = 1.0, int rounding = 0, std::optional<eTransform> tr = {});
void blurFB(const CFramebuffer& outfb, SBlurParams params);

Expand All @@ -53,6 +54,7 @@ class CRenderer {
CShader blurShader2;
CShader blurPrepareShader;
CShader blurFinishShader;
CShader borderShader;

Mat3x3 projMatrix = Mat3x3::identity();
Mat3x3 projection;
Expand Down
Loading
Loading