summaryrefslogtreecommitdiff
path: root/src/config
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2026-05-08 15:31:02 +0100
committerGitHub <noreply@github.com>2026-05-08 15:31:02 +0100
commitb94f1498542be0e83fe8060a50bce5ff2d5a1c39 (patch)
tree8bddcfc9f8504817d5778136ec8c7c05d881c852 /src/config
parenta6a0cc308c827557d04cb3e4c3b5d258de0411e6 (diff)
config: allow hashes for parsing colors (#14337)
Diffstat (limited to 'src/config')
-rw-r--r--src/config/legacy/ConfigManager.cpp17
-rw-r--r--src/config/lua/bindings/LuaBindingsNotification.cpp3
-rw-r--r--src/config/lua/objects/LuaNotification.cpp5
-rw-r--r--src/config/lua/types/LuaConfigColor.cpp4
-rw-r--r--src/config/lua/types/LuaConfigGradient.cpp3
-rw-r--r--src/config/shared/actions/ConfigActions.cpp15
-rw-r--r--src/config/shared/parserUtils/ParserUtils.cpp150
-rw-r--r--src/config/shared/parserUtils/ParserUtils.hpp10
8 files changed, 188 insertions, 19 deletions
diff --git a/src/config/legacy/ConfigManager.cpp b/src/config/legacy/ConfigManager.cpp
index 7b4cc696..09608fa8 100644
--- a/src/config/legacy/ConfigManager.cpp
+++ b/src/config/legacy/ConfigManager.cpp
@@ -14,6 +14,7 @@
#include "../shared/workspace/WorkspaceRuleManager.hpp"
#include "../shared/animation/AnimationTree.hpp"
#include "../shared/monitor/Parser.hpp"
+#include "../shared/parserUtils/ParserUtils.hpp"
#include "../supplementary/executor/Executor.hpp"
#include "../supplementary/jeremy/Jeremy.hpp"
#include "../../protocols/LayerShell.hpp"
@@ -131,7 +132,7 @@ static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void**
}
try {
- const auto COL = configStringToInt(std::string(var));
+ const auto COL = ParserUtils::parseColor(var);
if (!COL)
throw std::runtime_error(std::format("failed to parse {} as a color", var));
DATA->m_colors.emplace_back(COL.value());
@@ -1424,7 +1425,7 @@ std::optional<std::string> CConfigManager::handleAnimation(const std::string& co
return "no such animation";
// This helper casts strings like "1", "true", "off", "yes"... to int.
- int64_t enabledInt = configStringToInt(ARGS[1]).value_or(0) == 1;
+ int64_t enabledInt = ParserUtils::parseInt(ARGS[1]).value_or(0) == 1;
// Checking that the int is 1 or 0 because the helper can return integers out of range.
if (enabledInt != 0 && enabledInt != 1)
@@ -1715,24 +1716,24 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
wsRule.m_borderSize = std::stoi(rule.substr(delim + 11));
} catch (...) { return "Error parsing workspace rule bordersize: {}", rule.substr(delim + 11); }
else if ((delim = rule.find("border:")) != std::string::npos) {
- CHECK_OR_THROW(configStringToInt(rule.substr(delim + 7)))
+ CHECK_OR_THROW(ParserUtils::parseInt(rule.substr(delim + 7)))
wsRule.m_noBorder = !*X;
} else if ((delim = rule.find("shadow:")) != std::string::npos) {
- CHECK_OR_THROW(configStringToInt(rule.substr(delim + 7)))
+ CHECK_OR_THROW(ParserUtils::parseInt(rule.substr(delim + 7)))
wsRule.m_noShadow = !*X;
} else if ((delim = rule.find("rounding:")) != std::string::npos) {
- CHECK_OR_THROW(configStringToInt(rule.substr(delim + 9)))
+ CHECK_OR_THROW(ParserUtils::parseInt(rule.substr(delim + 9)))
wsRule.m_noRounding = !*X;
} else if ((delim = rule.find("decorate:")) != std::string::npos) {
- CHECK_OR_THROW(configStringToInt(rule.substr(delim + 9)))
+ CHECK_OR_THROW(ParserUtils::parseInt(rule.substr(delim + 9)))
wsRule.m_decorate = *X;
} else if ((delim = rule.find("monitor:")) != std::string::npos)
wsRule.m_monitor = rule.substr(delim + 8);
else if ((delim = rule.find("default:")) != std::string::npos) {
- CHECK_OR_THROW(configStringToInt(rule.substr(delim + 8)))
+ CHECK_OR_THROW(ParserUtils::parseInt(rule.substr(delim + 8)))
wsRule.m_isDefault = *X;
} else if ((delim = rule.find("persistent:")) != std::string::npos) {
- CHECK_OR_THROW(configStringToInt(rule.substr(delim + 11)))
+ CHECK_OR_THROW(ParserUtils::parseInt(rule.substr(delim + 11)))
wsRule.m_isPersistent = *X;
} else if ((delim = rule.find("defaultName:")) != std::string::npos)
wsRule.m_defaultName = trim(rule.substr(delim + 12));
diff --git a/src/config/lua/bindings/LuaBindingsNotification.cpp b/src/config/lua/bindings/LuaBindingsNotification.cpp
index b7a54546..3ba14ce7 100644
--- a/src/config/lua/bindings/LuaBindingsNotification.cpp
+++ b/src/config/lua/bindings/LuaBindingsNotification.cpp
@@ -1,6 +1,7 @@
#include "LuaBindingsInternal.hpp"
#include "../objects/LuaNotification.hpp"
+#include "../../shared/parserUtils/ParserUtils.hpp"
#include "../../../helpers/MiscFunctions.hpp"
#include "../../../notification/NotificationOverlay.hpp"
@@ -52,7 +53,7 @@ static std::optional<CHyprColor> parseColorArg(lua_State* L, int idx) {
return CHyprColor(sc<uint64_t>(lua_tonumber(L, idx)));
if (lua_isstring(L, idx)) {
- auto parsed = configStringToInt(lua_tostring(L, idx));
+ auto parsed = ParserUtils::parseColor(lua_tostring(L, idx));
if (!parsed)
return std::nullopt;
diff --git a/src/config/lua/objects/LuaNotification.cpp b/src/config/lua/objects/LuaNotification.cpp
index f116e2eb..99382bf6 100644
--- a/src/config/lua/objects/LuaNotification.cpp
+++ b/src/config/lua/objects/LuaNotification.cpp
@@ -8,6 +8,9 @@
#include <optional>
#include <string_view>
+#include "../../shared/parserUtils/ParserUtils.hpp"
+
+using namespace Config;
using namespace Config::Lua;
static constexpr const char* MT = "HL.Notification";
@@ -23,7 +26,7 @@ namespace {
return CHyprColor(sc<uint64_t>(lua_tonumber(L, idx)));
if (lua_isstring(L, idx)) {
- auto parsed = configStringToInt(lua_tostring(L, idx));
+ auto parsed = ParserUtils::parseColor(lua_tostring(L, idx));
if (!parsed)
return std::nullopt;
diff --git a/src/config/lua/types/LuaConfigColor.cpp b/src/config/lua/types/LuaConfigColor.cpp
index c58b232b..e8c79088 100644
--- a/src/config/lua/types/LuaConfigColor.cpp
+++ b/src/config/lua/types/LuaConfigColor.cpp
@@ -3,6 +3,8 @@
#include <expected>
#include <format>
+#include "../../shared/parserUtils/ParserUtils.hpp"
+
#include "../../../helpers/Color.hpp"
#include "../../../helpers/MiscFunctions.hpp"
@@ -10,7 +12,7 @@ using namespace Config;
using namespace Config::Lua;
static std::expected<CHyprColor, std::string> parseColorString(const std::string& str) {
- auto result = configStringToInt(str);
+ auto result = ParserUtils::parseColor(str);
if (!result)
return std::unexpected(std::format("invalid color \"{}\"", str));
return CHyprColor(sc<uint64_t>(*result));
diff --git a/src/config/lua/types/LuaConfigGradient.cpp b/src/config/lua/types/LuaConfigGradient.cpp
index 6e5838a9..cc936509 100644
--- a/src/config/lua/types/LuaConfigGradient.cpp
+++ b/src/config/lua/types/LuaConfigGradient.cpp
@@ -1,4 +1,5 @@
#include "LuaConfigGradient.hpp"
+#include "../../shared/parserUtils/ParserUtils.hpp"
#include "../../../helpers/MiscFunctions.hpp"
#include <numbers>
@@ -7,7 +8,7 @@ using namespace Config;
using namespace Config::Lua;
static std::expected<CHyprColor, std::string> parseColorString(const std::string& str) {
- auto result = configStringToInt(str);
+ auto result = ParserUtils::parseColor(str);
if (!result)
return std::unexpected(std::format("invalid color \"{}\"", str));
return CHyprColor(sc<uint64_t>(*result));
diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp
index 3f586b9f..a59b4ab3 100644
--- a/src/config/shared/actions/ConfigActions.cpp
+++ b/src/config/shared/actions/ConfigActions.cpp
@@ -1,4 +1,5 @@
#include "ConfigActions.hpp"
+#include "../parserUtils/ParserUtils.hpp"
#include "../../../desktop/state/FocusState.hpp"
#include "../../../desktop/view/Window.hpp"
#include "../../../desktop/view/Group.hpp"
@@ -726,15 +727,15 @@ ActionResult Actions::setProp(const std::string& PROP, const std::string& VAL, s
if (TOKEN.ends_with("deg"))
colorData.m_angle = std::stoi(std::string(TOKEN.substr(0, TOKEN.size() - 3))) * (PI / 180.0);
else
- configStringToInt(std::string(TOKEN)).and_then([&colorData](const auto& e) {
+ ParserUtils::parseColor(std::string(TOKEN)).and_then([&colorData](const auto& e) {
colorData.m_colors.push_back(e);
- return std::invoke_result_t<decltype(::configStringToInt), const std::string&>(1);
+ return std::invoke_result_t<decltype(ParserUtils::parseColor), const std::string&>(1);
});
}
} else if (VAL != "-1")
- configStringToInt(VAL).and_then([&colorData](const auto& e) {
+ ParserUtils::parseColor(VAL).and_then([&colorData](const auto& e) {
colorData.m_colors.push_back(e);
- return std::invoke_result_t<decltype(::configStringToInt), const std::string&>(1);
+ return std::invoke_result_t<decltype(ParserUtils::parseColor), const std::string&>(1);
});
colorData.updateColorsOk();
@@ -754,15 +755,15 @@ ActionResult Actions::setProp(const std::string& PROP, const std::string& VAL, s
Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP));
} else if (PROP == "opacity_override") {
PWINDOW->m_ruleApplicator->alphaOverride(Desktop::Types::COverridableVar(
- Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alpha().valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))},
+ Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alpha().valueOrDefault().alpha, sc<bool>(ParserUtils::parseInt(VAL).value_or(0))},
Desktop::Types::PRIORITY_SET_PROP));
} else if (PROP == "opacity_inactive_override") {
PWINDOW->m_ruleApplicator->alphaInactiveOverride(Desktop::Types::COverridableVar(
- Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))},
+ Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().alpha, sc<bool>(ParserUtils::parseInt(VAL).value_or(0))},
Desktop::Types::PRIORITY_SET_PROP));
} else if (PROP == "opacity_fullscreen_override") {
PWINDOW->m_ruleApplicator->alphaFullscreenOverride(Desktop::Types::COverridableVar(
- Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().alpha, sc<bool>(configStringToInt(VAL).value_or(0))},
+ Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().alpha, sc<bool>(ParserUtils::parseInt(VAL).value_or(0))},
Desktop::Types::PRIORITY_SET_PROP));
} else if (PROP == "allows_input")
parsePropTrivial(PWINDOW->m_ruleApplicator->allowsInput(), VAL);
diff --git a/src/config/shared/parserUtils/ParserUtils.cpp b/src/config/shared/parserUtils/ParserUtils.cpp
new file mode 100644
index 00000000..dec0bb2e
--- /dev/null
+++ b/src/config/shared/parserUtils/ParserUtils.cpp
@@ -0,0 +1,150 @@
+#include "ParserUtils.hpp"
+
+#include <hyprutils/string/String.hpp>
+#include <hyprutils/string/Numeric.hpp>
+
+#include <format>
+#include <algorithm>
+#include <cmath>
+
+#include "../../../helpers/memory/Memory.hpp"
+
+using namespace Config;
+using namespace Config::ParserUtils;
+using namespace Hyprutils::String;
+
+static std::expected<uint64_t, std::string> parseHex(std::string_view value) {
+ auto res = value.starts_with("0x") ? strToNumber<uint64_t>(value) : strToNumber<uint64_t>(std::string{"0x"} + value);
+ if (!res)
+ return std::unexpected(std::format("invalid hex \"{}\"", value));
+ return *res;
+}
+
+std::expected<int64_t, std::string> ParserUtils::parseColor(std::string_view val) {
+
+ if (val.starts_with("#")) {
+ // parse either rgb or rgba
+ val = val.substr(1);
+
+ if (val.length() != 6 && val.length() != 8 && val.length() != 3)
+ return std::unexpected(std::format("couldn't parse \"{}\" as a color", val));
+
+ if (val.length() == 3) {
+ auto r = parseHex(val.substr(0, 1));
+ auto g = parseHex(val.substr(1, 1));
+ auto b = parseHex(val.substr(2, 1));
+
+ if (!r || !g || !b)
+ return std::unexpected(std::format("couldn't parse \"{}\" as a color (bad hex)", val));
+
+ return 0xFF000000 | ((*r | (*r << 4)) << 16) | ((*g | (*g << 4)) << 8) | (*b | (*b << 4));
+ }
+
+ if (val.length() == 6) {
+ auto r = parseHex(val.substr(0, 2));
+ auto g = parseHex(val.substr(2, 2));
+ auto b = parseHex(val.substr(4, 2));
+
+ if (!r || !g || !b)
+ return std::unexpected(std::format("couldn't parse \"{}\" as a color (bad hex)", val));
+
+ return 0xFF000000 | (*r << 16) | (*g << 8) | *b;
+ }
+
+ if (val.length() == 8) {
+ auto r = parseHex(val.substr(0, 2));
+ auto g = parseHex(val.substr(2, 2));
+ auto b = parseHex(val.substr(4, 2));
+ auto a = parseHex(val.substr(6, 2));
+
+ if (!r || !g || !b || !a)
+ return std::unexpected(std::format("couldn't parse \"{}\" as a color (bad hex)", val));
+
+ return (*a << 24) | (*r << 16) | (*g << 8) | *b;
+ }
+ }
+
+ if (val.starts_with("0x"))
+ return parseHex(val);
+
+ if (val.starts_with("rgba(") && val.ends_with(')')) {
+ const auto VALUEWITHOUTFUNC = trim(val.substr(5, val.length() - 6));
+
+ // try doing it the comma way first
+ if (std::ranges::count(VALUEWITHOUTFUNC, ',') == 3) {
+ // cool
+ std::string_view rolling = VALUEWITHOUTFUNC;
+ auto r = strToNumber<uint8_t>(trim(rolling.substr(0, rolling.find(','))));
+ rolling = rolling.substr(rolling.find(',') + 1);
+ auto g = strToNumber<uint8_t>(trim(rolling.substr(0, rolling.find(','))));
+ rolling = rolling.substr(rolling.find(',') + 1);
+ auto b = strToNumber<uint8_t>(trim(rolling.substr(0, rolling.find(','))));
+ rolling = rolling.substr(rolling.find(',') + 1);
+ auto a = strToNumber<float>(trim(rolling.substr(0, rolling.find(','))));
+
+ if (!r || !g || !b || !a)
+ return std::unexpected(std::format("failed parsing \"{}\" as a color", val));
+
+ return (sc<uint64_t>(std::floor(*a * 255.F)) << 24) | (*r << 16) | (*g << 8) | *b;
+ } else if (VALUEWITHOUTFUNC.length() == 8) {
+ const auto RGBA = parseHex(VALUEWITHOUTFUNC);
+
+ if (!RGBA)
+ return RGBA;
+ // now we need to RGBA -> ARGB. The config holds ARGB only.
+ return (*RGBA >> 8) + (0x1000000 * (*RGBA & 0xFF));
+ }
+
+ return std::unexpected("rgba() expects length of 8 characters (4 bytes) or 4 comma separated values");
+ }
+
+ if (val.starts_with("rgb(") && val.ends_with(')')) {
+ const auto VALUEWITHOUTFUNC = trim(val.substr(4, val.length() - 5));
+
+ // try doing it the comma way first
+ if (std::ranges::count(VALUEWITHOUTFUNC, ',') == 2) {
+ // cool
+ std::string_view rolling = VALUEWITHOUTFUNC;
+ auto r = strToNumber<uint8_t>(trim(rolling.substr(0, rolling.find(','))));
+ rolling = rolling.substr(rolling.find(',') + 1);
+ auto g = strToNumber<uint8_t>(trim(rolling.substr(0, rolling.find(','))));
+ rolling = rolling.substr(rolling.find(',') + 1);
+ auto b = strToNumber<uint8_t>(trim(rolling.substr(0, rolling.find(','))));
+
+ if (!r || !g || !b)
+ return std::unexpected(std::format("failed parsing \"{}\" as a color", val));
+
+ return 0xFF000000 | (*r << 16) | (*g << 8) | *b;
+ } else if (VALUEWITHOUTFUNC.length() == 6) {
+ const auto r = parseHex(VALUEWITHOUTFUNC);
+ return r ? *r + 0xFF000000 : r;
+ }
+
+ return std::unexpected("rgb() expects length of 6 characters (3 bytes) or 3 comma separated values");
+ }
+
+ if (isNumber2(val)) {
+ if (const auto v = strToNumber<uint32_t>(val); v)
+ return *v;
+ }
+
+ return std::unexpected(std::format("cannot parse \"{}\" as a color", val));
+}
+
+std::expected<int64_t, std::string> ParserUtils::parseInt(std::string_view val) {
+ if (val.starts_with("0x"))
+ return parseHex(val);
+
+ if (val.starts_with("true") || val.starts_with("on") || val.starts_with("yes"))
+ return 1;
+
+ if (val.starts_with("false") || val.starts_with("off") || val.starts_with("no"))
+ return 0;
+
+ auto res = strToNumber<int64_t>(val);
+
+ if (!res)
+ return std::unexpected(std::format("Failed to parse \"{}\" as an integer", val));
+
+ return *res;
+}
diff --git a/src/config/shared/parserUtils/ParserUtils.hpp b/src/config/shared/parserUtils/ParserUtils.hpp
new file mode 100644
index 00000000..9dfe37f4
--- /dev/null
+++ b/src/config/shared/parserUtils/ParserUtils.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <expected>
+#include <cstdint>
+#include <string>
+
+namespace Config::ParserUtils {
+ std::expected<int64_t, std::string> parseColor(std::string_view val);
+ std::expected<int64_t, std::string> parseInt(std::string_view val);
+}; \ No newline at end of file