diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2026-05-08 15:31:02 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-08 15:31:02 +0100 |
| commit | b94f1498542be0e83fe8060a50bce5ff2d5a1c39 (patch) | |
| tree | 8bddcfc9f8504817d5778136ec8c7c05d881c852 /src/config | |
| parent | a6a0cc308c827557d04cb3e4c3b5d258de0411e6 (diff) | |
config: allow hashes for parsing colors (#14337)
Diffstat (limited to 'src/config')
| -rw-r--r-- | src/config/legacy/ConfigManager.cpp | 17 | ||||
| -rw-r--r-- | src/config/lua/bindings/LuaBindingsNotification.cpp | 3 | ||||
| -rw-r--r-- | src/config/lua/objects/LuaNotification.cpp | 5 | ||||
| -rw-r--r-- | src/config/lua/types/LuaConfigColor.cpp | 4 | ||||
| -rw-r--r-- | src/config/lua/types/LuaConfigGradient.cpp | 3 | ||||
| -rw-r--r-- | src/config/shared/actions/ConfigActions.cpp | 15 | ||||
| -rw-r--r-- | src/config/shared/parserUtils/ParserUtils.cpp | 150 | ||||
| -rw-r--r-- | src/config/shared/parserUtils/ParserUtils.hpp | 10 |
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 |
