config: allow hashes for parsing colors (#14337)

This commit is contained in:
Vaxry 2026-05-08 15:31:02 +01:00 committed by GitHub
parent a6a0cc308c
commit b94f149854
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 277 additions and 195 deletions

View file

@ -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));

View file

@ -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;

View file

@ -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;

View file

@ -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));

View file

@ -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));

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
};

View file

@ -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;

View file

@ -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;

View file

@ -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()));

View file

@ -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 {

View file

@ -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)

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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"));
}

View file

@ -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);
}