summaryrefslogtreecommitdiff
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
parenta6a0cc308c827557d04cb3e4c3b5d258de0411e6 (diff)
config: allow hashes for parsing colors (#14337)
-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
-rw-r--r--src/debug/HyprCtl.cpp5
-rw-r--r--src/desktop/Workspace.cpp5
-rw-r--r--src/desktop/rule/windowRule/WindowRule.cpp3
-rw-r--r--src/errorOverlay/Overlay.hpp7
-rw-r--r--src/helpers/MiscFunctions.cpp113
-rw-r--r--src/helpers/MiscFunctions.hpp2
-rw-r--r--src/managers/VersionKeeperManager.cpp9
-rw-r--r--src/plugins/HookSystem.cpp6
-rw-r--r--tests/config/shared/ParserUtils.cpp69
-rw-r--r--tests/helpers/MiscFunctions.cpp46
18 files changed, 277 insertions, 195 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
diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp
index b6777de3..fdb9c89c 100644
--- a/src/debug/HyprCtl.cpp
+++ b/src/debug/HyprCtl.cpp
@@ -36,6 +36,7 @@ using namespace Hyprutils::OS;
#include "../config/legacy/ConfigManager.hpp"
#include "../config/lua/ConfigManager.hpp"
#include "../config/ConfigValue.hpp"
+#include "../config/shared/parserUtils/ParserUtils.hpp"
#include "../config/shared/complex/ComplexDataTypes.hpp"
#include "../config/shared/inotify/ConfigWatcher.hpp"
#include "../config/shared/workspace/WorkspaceRuleManager.hpp"
@@ -1404,7 +1405,7 @@ static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string req
return "ok";
}
- const CHyprColor COLOR = configStringToInt(vars[1]).value_or(0);
+ const CHyprColor COLOR = Config::ParserUtils::parseColor(vars[1]).value_or(0);
for (size_t i = 2; i < vars.size(); ++i)
errorMessage += vars[i] + ' ';
@@ -1871,7 +1872,7 @@ static std::string dispatchNotify(eHyprCtlOutputFormat format, std::string reque
time = std::stoi(TIME);
} catch (std::exception& e) { return "invalid arg 2"; }
- const auto COLOR_RESULT = configStringToInt(vars[3]);
+ const auto COLOR_RESULT = Config::ParserUtils::parseColor(vars[3]);
if (!COLOR_RESULT)
return "invalid arg 3";
CHyprColor color = *COLOR_RESULT;
diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp
index 18f77a63..293c57b5 100644
--- a/src/desktop/Workspace.cpp
+++ b/src/desktop/Workspace.cpp
@@ -2,6 +2,7 @@
#include "view/Group.hpp"
#include "view/LayerSurface.hpp"
#include "../Compositor.hpp"
+#include "../config/shared/parserUtils/ParserUtils.hpp"
#include "../config/shared/animation/AnimationTree.hpp"
#include "../config/shared/workspace/WorkspaceRuleManager.hpp"
#include "../config/supplementary/executor/Executor.hpp"
@@ -199,7 +200,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
prop = prop.substr(2, prop.length() - 3);
- const auto SHOULDBESPECIAL = configStringToInt(prop);
+ const auto SHOULDBESPECIAL = Config::ParserUtils::parseInt(prop);
if (SHOULDBESPECIAL && sc<bool>(*SHOULDBESPECIAL) != m_isSpecialWorkspace)
return false;
@@ -234,7 +235,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
if (prop.starts_with("e:") && !m_name.ends_with(prop.substr(2)))
return false;
- const auto WANTSNAMED = configStringToInt(prop);
+ const auto WANTSNAMED = Config::ParserUtils::parseInt(prop);
if (WANTSNAMED && *WANTSNAMED != (m_id <= -1337))
return false;
diff --git a/src/desktop/rule/windowRule/WindowRule.cpp b/src/desktop/rule/windowRule/WindowRule.cpp
index 5bbf2abe..0262697a 100644
--- a/src/desktop/rule/windowRule/WindowRule.cpp
+++ b/src/desktop/rule/windowRule/WindowRule.cpp
@@ -6,6 +6,7 @@
#include "../../../managers/TokenManager.hpp"
#include "../../../desktop/state/FocusState.hpp"
#include "../../../protocols/types/ContentType.hpp"
+#include "../../../config/shared/parserUtils/ParserUtils.hpp"
#include <hyprutils/string/Numeric.hpp>
#include <hyprutils/string/String.hpp>
@@ -45,7 +46,7 @@ static std::expected<float, std::string> parseFloat(std::string_view effectName,
}
static std::expected<CHyprColor, std::string> parseBorderColorToken(const std::string& raw, const std::string& token) {
- auto parsed = configStringToInt(token);
+ auto parsed = Config::ParserUtils::parseColor(token);
if (!parsed)
return std::unexpected(std::format(R"(border_color rule "{}" has invalid color "{}": {})", raw, token, parsed.error()));
diff --git a/src/errorOverlay/Overlay.hpp b/src/errorOverlay/Overlay.hpp
index 68f0e5dc..08c04336 100644
--- a/src/errorOverlay/Overlay.hpp
+++ b/src/errorOverlay/Overlay.hpp
@@ -5,7 +5,6 @@
#include "../defines.hpp"
#include "../render/Texture.hpp"
#include "../helpers/AnimatedVariable.hpp"
-#include "../helpers/MiscFunctions.hpp"
#include "../config/shared/complex/ComplexDataTypes.hpp"
namespace ErrorOverlay {
@@ -13,10 +12,8 @@ namespace ErrorOverlay {
namespace Colors {
constexpr const float ANGLE_30 = 0.52359877;
- static const Config::CGradientValueData ERROR =
- Config::CGradientValueData{std::vector<CHyprColor>{*configStringToInt("0xffff6666"), *configStringToInt("0xff800000")}, ANGLE_30};
- static const Config::CGradientValueData WARNING =
- Config::CGradientValueData{std::vector<CHyprColor>{*configStringToInt("0xffffdb4d"), *configStringToInt("0xff665200")}, ANGLE_30};
+ static const Config::CGradientValueData ERROR = Config::CGradientValueData{std::vector<CHyprColor>{0xffff6666, 0xff800000}, ANGLE_30};
+ static const Config::CGradientValueData WARNING = Config::CGradientValueData{std::vector<CHyprColor>{0xffffdb4d, 0xff665200}, ANGLE_30};
};
class COverlay {
diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp
index 5aa58a2c..e0903069 100644
--- a/src/helpers/MiscFunctions.cpp
+++ b/src/helpers/MiscFunctions.cpp
@@ -572,119 +572,6 @@ int64_t getPPIDof(int64_t pid) {
#endif
}
-std::expected<int64_t, std::string> configStringToInt(const std::string& VALUE) {
- auto parseHex = [](const std::string& value) -> std::expected<int64_t, std::string> {
- try {
- size_t position;
- auto result = stoll(value, &position, 16);
- if (position == value.size())
- return result;
- } catch (const std::exception&) {}
- return std::unexpected("invalid hex " + value);
- };
- if (VALUE.starts_with("0x")) {
- // Values with 0x are hex
- return parseHex(VALUE);
- } else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
- const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6));
-
- // try doing it the comma way first
- if (std::ranges::count(VALUEWITHOUTFUNC, ',') == 3) {
- // cool
- std::string rolling = VALUEWITHOUTFUNC;
- auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
- rolling = rolling.substr(rolling.find(',') + 1);
- auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
- rolling = rolling.substr(rolling.find(',') + 1);
- auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
- rolling = rolling.substr(rolling.find(',') + 1);
- uint8_t a = 0;
-
- if (!r || !g || !b)
- return std::unexpected("failed parsing " + VALUEWITHOUTFUNC);
-
- try {
- a = std::round(std::stof(trim(rolling.substr(0, rolling.find(',')))) * 255.f);
- } catch (std::exception& e) { return std::unexpected("failed parsing " + VALUEWITHOUTFUNC); }
-
- return a * sc<Config::INTEGER>(0x1000000) + *r * sc<Config::INTEGER>(0x10000) + *g * sc<Config::INTEGER>(0x100) + *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");
-
- } else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
- const auto VALUEWITHOUTFUNC = trim(VALUE.substr(4, VALUE.length() - 5));
-
- // try doing it the comma way first
- if (std::ranges::count(VALUEWITHOUTFUNC, ',') == 2) {
- // cool
- std::string rolling = VALUEWITHOUTFUNC;
- auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
- rolling = rolling.substr(rolling.find(',') + 1);
- auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
- rolling = rolling.substr(rolling.find(',') + 1);
- auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
-
- if (!r || !g || !b)
- return std::unexpected("failed parsing " + VALUEWITHOUTFUNC);
-
- return sc<Config::INTEGER>(0xFF000000) + *r * sc<Config::INTEGER>(0x10000) + *g * sc<Config::INTEGER>(0x100) + *b;
- } else if (VALUEWITHOUTFUNC.length() == 6) {
- 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");
- } 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, false))
- return std::unexpected("cannot parse \"" + VALUE + "\" as an int.");
-
- try {
- const auto RES = std::stoll(VALUE);
- return RES;
- } catch (std::exception& e) { return std::unexpected(std::string{"stoll threw: "} + e.what()); }
-
- return std::unexpected("parse error");
-}
-
-Vector2D configStringToVector2D(const std::string& VALUE) {
- std::istringstream iss(VALUE);
- std::string token;
-
- if (!std::getline(iss, token, ' ') && !std::getline(iss, token, ','))
- throw std::invalid_argument("Invalid string format");
-
- if (!isNumber(token))
- throw std::invalid_argument("Invalid x value");
-
- long long x = std::stoll(token);
-
- if (!std::getline(iss, token))
- throw std::invalid_argument("Invalid string format");
-
- if (!isNumber(token))
- throw std::invalid_argument("Invalid y value");
-
- long long y = std::stoll(token);
-
- if (std::getline(iss, token))
- throw std::invalid_argument("Invalid string format");
-
- return Vector2D(sc<double>(x), sc<double>(y));
-}
-
double normalizeAngleRad(double ang) {
if (ang > M_PI * 2) {
while (ang > M_PI * 2)
diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp
index 0528be58..3cba5d57 100644
--- a/src/helpers/MiscFunctions.hpp
+++ b/src/helpers/MiscFunctions.hpp
@@ -29,8 +29,6 @@ std::optional<std::string> cleanCmdForWorkspace(const std::string&,
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2);
std::string execAndGet(const char*);
int64_t getPPIDof(int64_t pid);
-std::expected<int64_t, std::string> configStringToInt(const std::string&);
-Vector2D configStringToVector2D(const std::string&);
std::optional<float> getPlusMinusKeywordResult(std::string in, float relative);
double normalizeAngleRad(double ang);
std::vector<SCallstackFrameInfo> getBacktrace();
diff --git a/src/managers/VersionKeeperManager.cpp b/src/managers/VersionKeeperManager.cpp
index 9a12adac..12fde80d 100644
--- a/src/managers/VersionKeeperManager.cpp
+++ b/src/managers/VersionKeeperManager.cpp
@@ -11,6 +11,7 @@
#include <filesystem>
#include <fstream>
#include <hyprutils/string/String.hpp>
+#include <hyprutils/string/Numeric.hpp>
#include <hyprutils/os/Process.hpp>
using namespace Hyprutils::String;
@@ -62,13 +63,13 @@ CVersionKeeperManager::CVersionKeeperManager() {
bool CVersionKeeperManager::isMajorVersionOlderThanRunning(const std::string& ver) {
const CVarList verStrings(ver, 0, '.', true);
- const int V1 = configStringToInt(verStrings[0]).value_or(0);
- const int V2 = configStringToInt(verStrings[1]).value_or(0);
+ const int V1 = strToNumber<uint32_t>(verStrings[0]).value_or(0);
+ const int V2 = strToNumber<uint32_t>(verStrings[1]).value_or(0);
static const CVarList runningStrings(HYPRLAND_VERSION, 0, '.', true);
- static const int R1 = configStringToInt(runningStrings[0]).value_or(0);
- static const int R2 = configStringToInt(runningStrings[1]).value_or(0);
+ static const int R1 = strToNumber<uint32_t>(runningStrings[0]).value_or(0);
+ static const int R2 = strToNumber<uint32_t>(runningStrings[1]).value_or(0);
if (R1 > V1)
return true;
diff --git a/src/plugins/HookSystem.cpp b/src/plugins/HookSystem.cpp
index f17a4556..15ae0f50 100644
--- a/src/plugins/HookSystem.cpp
+++ b/src/plugins/HookSystem.cpp
@@ -14,6 +14,10 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <hyprutils/string/Numeric.hpp>
+
+using namespace Hyprutils::String;
+
CFunctionHook::CFunctionHook(HANDLE owner, void* source, void* destination) : m_source(source), m_destination(destination), m_owner(owner) {
;
}
@@ -92,7 +96,7 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr
size_t plusPresent = tokens[1][0] == '+' ? 1 : 0;
size_t minusPresent = tokens[1][0] == '-' ? 1 : 0;
std::string addr = tokens[1].substr((plusPresent || minusPresent), tokens[1].find("(%rip)") - (plusPresent || minusPresent));
- auto addrResult = configStringToInt(addr);
+ auto addrResult = strToNumber<int64_t>(addr);
if (!addrResult)
return {};
const int32_t OFFSET = (minusPresent ? -1 : 1) * *addrResult;
diff --git a/tests/config/shared/ParserUtils.cpp b/tests/config/shared/ParserUtils.cpp
new file mode 100644
index 00000000..76b516b4
--- /dev/null
+++ b/tests/config/shared/ParserUtils.cpp
@@ -0,0 +1,69 @@
+
+#include <config/shared/parserUtils/ParserUtils.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace Config;
+
+TEST(ParserUtils, parseIntDecimal) {
+ EXPECT_EQ(ParserUtils::parseInt("42").value(), 42);
+ EXPECT_EQ(ParserUtils::parseInt("0").value(), 0);
+ EXPECT_EQ(ParserUtils::parseInt("-1").value(), -1);
+}
+
+TEST(ParserUtils, parseIntHex) {
+ EXPECT_EQ(ParserUtils::parseInt("0xFF").value(), 255);
+ EXPECT_EQ(ParserUtils::parseInt("0x00").value(), 0);
+ EXPECT_EQ(ParserUtils::parseInt("0x10").value(), 16);
+}
+
+TEST(ParserUtils, parseIntBooleanStrings) {
+ // "true", "yes", "on" -> 1; "false", "no", "off" -> 0
+ EXPECT_EQ(ParserUtils::parseInt("true").value(), 1);
+ EXPECT_EQ(ParserUtils::parseInt("yes").value(), 1);
+ EXPECT_EQ(ParserUtils::parseInt("on").value(), 1);
+ EXPECT_EQ(ParserUtils::parseInt("false").value(), 0);
+ EXPECT_EQ(ParserUtils::parseInt("no").value(), 0);
+ EXPECT_EQ(ParserUtils::parseInt("off").value(), 0);
+}
+
+TEST(ParserUtils, parseIntInvalid) {
+ EXPECT_FALSE(ParserUtils::parseInt("").has_value());
+ EXPECT_FALSE(ParserUtils::parseInt("abc").has_value());
+ EXPECT_FALSE(ParserUtils::parseInt("rgba(20,20,20,5)").has_value());
+ EXPECT_FALSE(ParserUtils::parseInt("#fff").has_value());
+}
+
+TEST(ParserUtils, parseColor) {
+ EXPECT_EQ(ParserUtils::parseColor("0xDEADBEEF").value_or(0), 0xDEADBEEF);
+ EXPECT_EQ(ParserUtils::parseColor("rgba(20, 21, 22, 0.5)").value_or(0), 0x7F141516);
+ EXPECT_EQ(ParserUtils::parseColor("rgb(20, 21, 22)").value_or(0), 0xFF141516);
+ EXPECT_EQ(ParserUtils::parseColor("rgb(141516)").value_or(0), 0xFF141516);
+ EXPECT_EQ(ParserUtils::parseColor("rgba(1415167f)").value_or(0), 0x7F141516);
+ EXPECT_EQ(ParserUtils::parseColor("4279506198").value_or(0), 0xFF141516);
+ EXPECT_EQ(ParserUtils::parseColor("1").value_or(0), 0x1);
+ EXPECT_EQ(ParserUtils::parseColor("#fed").value_or(0), 0xFFFFEEDD);
+ EXPECT_EQ(ParserUtils::parseColor("#FED").value_or(0), 0xFFFFEEDD);
+ EXPECT_EQ(ParserUtils::parseColor("#deffad").value_or(0), 0xFFDEFFAD);
+ EXPECT_EQ(ParserUtils::parseColor("#DEFFaD").value_or(0), 0xFFDEFFAD);
+ EXPECT_EQ(ParserUtils::parseColor("#DEFFaDAA").value_or(0), 0xAADEFFAD);
+ EXPECT_EQ(ParserUtils::parseColor("#DEFFaDaa").value_or(0), 0xAADEFFAD);
+}
+
+TEST(ParserUtils, parseColorBad) {
+ EXPECT_FALSE(!!ParserUtils::parseColor("mak"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("true"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("on"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("fucker"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("I sniff glue"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("0DEADBEEF"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("6270000000"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("rgba(20, 21, 22)"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("rgb(20, 21, 22, 0.2)"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("#afed"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("#FE"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("#defd"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("#DEFFD"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("#DEFFaDAAe"));
+ EXPECT_FALSE(!!ParserUtils::parseColor("#DEFFaDa"));
+} \ No newline at end of file
diff --git a/tests/helpers/MiscFunctions.cpp b/tests/helpers/MiscFunctions.cpp
index e0857987..4eefb5d3 100644
--- a/tests/helpers/MiscFunctions.cpp
+++ b/tests/helpers/MiscFunctions.cpp
@@ -97,49 +97,3 @@ TEST(Helpers, truthyFalse) {
EXPECT_FALSE(truthy(""));
EXPECT_FALSE(truthy("random"));
}
-
-// configStringToInt
-
-TEST(Helpers, configStringToIntDecimal) {
- EXPECT_EQ(configStringToInt("42").value(), 42);
- EXPECT_EQ(configStringToInt("0").value(), 0);
- EXPECT_EQ(configStringToInt("-1").value(), -1);
-}
-
-TEST(Helpers, configStringToIntHex) {
- EXPECT_EQ(configStringToInt("0xFF").value(), 255);
- EXPECT_EQ(configStringToInt("0x00").value(), 0);
- EXPECT_EQ(configStringToInt("0x10").value(), 16);
-}
-
-TEST(Helpers, configStringToIntRgba) {
- auto result = configStringToInt("rgba(255, 0, 0, 1.0)");
- EXPECT_TRUE(result.has_value());
-}
-
-TEST(Helpers, configStringToIntBooleanStrings) {
- // "true", "yes", "on" -> 1; "false", "no", "off" -> 0
- EXPECT_EQ(configStringToInt("true").value(), 1);
- EXPECT_EQ(configStringToInt("yes").value(), 1);
- EXPECT_EQ(configStringToInt("on").value(), 1);
- EXPECT_EQ(configStringToInt("false").value(), 0);
- EXPECT_EQ(configStringToInt("no").value(), 0);
- EXPECT_EQ(configStringToInt("off").value(), 0);
-}
-
-TEST(Helpers, configStringToIntInvalid) {
- EXPECT_FALSE(configStringToInt("").has_value());
- EXPECT_FALSE(configStringToInt("abc").has_value());
-}
-
-// configStringToVector2D
-
-TEST(Helpers, configStringToVector2DValid) {
- EXPECT_EQ(configStringToVector2D("1920 1080"), Vector2D(1920, 1080));
- EXPECT_EQ(configStringToVector2D("0 0"), Vector2D(0, 0));
-}
-
-TEST(Helpers, configStringToVector2DInvalid) {
- EXPECT_THROW(configStringToVector2D("notvalid"), std::invalid_argument);
- EXPECT_THROW(configStringToVector2D(""), std::invalid_argument);
-}