From b94f1498542be0e83fe8060a50bce5ff2d5a1c39 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Fri, 8 May 2026 15:31:02 +0100 Subject: config: allow hashes for parsing colors (#14337) --- src/config/shared/parserUtils/ParserUtils.cpp | 150 ++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 src/config/shared/parserUtils/ParserUtils.cpp (limited to 'src/config/shared/parserUtils/ParserUtils.cpp') 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 +#include + +#include +#include +#include + +#include "../../../helpers/memory/Memory.hpp" + +using namespace Config; +using namespace Config::ParserUtils; +using namespace Hyprutils::String; + +static std::expected parseHex(std::string_view value) { + auto res = value.starts_with("0x") ? strToNumber(value) : strToNumber(std::string{"0x"} + value); + if (!res) + return std::unexpected(std::format("invalid hex \"{}\"", value)); + return *res; +} + +std::expected 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(trim(rolling.substr(0, rolling.find(',')))); + rolling = rolling.substr(rolling.find(',') + 1); + auto g = strToNumber(trim(rolling.substr(0, rolling.find(',')))); + rolling = rolling.substr(rolling.find(',') + 1); + auto b = strToNumber(trim(rolling.substr(0, rolling.find(',')))); + rolling = rolling.substr(rolling.find(',') + 1); + auto a = strToNumber(trim(rolling.substr(0, rolling.find(',')))); + + if (!r || !g || !b || !a) + return std::unexpected(std::format("failed parsing \"{}\" as a color", val)); + + return (sc(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(trim(rolling.substr(0, rolling.find(',')))); + rolling = rolling.substr(rolling.find(',') + 1); + auto g = strToNumber(trim(rolling.substr(0, rolling.find(',')))); + rolling = rolling.substr(rolling.find(',') + 1); + auto b = strToNumber(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(val); v) + return *v; + } + + return std::unexpected(std::format("cannot parse \"{}\" as a color", val)); +} + +std::expected 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(val); + + if (!res) + return std::unexpected(std::format("Failed to parse \"{}\" as an integer", val)); + + return *res; +} -- cgit v1.2.3