From 8726a7363eb9213235e6e23657668e0daac39a5b Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:52:37 -0400 Subject: config: cleanup the entire config infrastructure (#13785) Massively refactors the config infrastructure, sorely needed and will be req'd for the lua stuff --- src/config/shared/monitor/Parser.cpp | 294 +++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 src/config/shared/monitor/Parser.cpp (limited to 'src/config/shared/monitor/Parser.cpp') diff --git a/src/config/shared/monitor/Parser.cpp b/src/config/shared/monitor/Parser.cpp new file mode 100644 index 00000000..05faa0c7 --- /dev/null +++ b/src/config/shared/monitor/Parser.cpp @@ -0,0 +1,294 @@ +#include "Parser.hpp" + +#include "../../../debug/log/Logger.hpp" + +#include +#include +#include +#include +#include + +using namespace Config; +using namespace Hyprutils::String; + +static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { + auto args = CVarList2(std::string{modeline}, 0, 's'); + + auto keyword = std::string{args[0]}; + std::ranges::transform(keyword, keyword.begin(), ::tolower); + + if (keyword != "modeline") + return false; + + if (args.size() < 10) { + Log::logger->log(Log::ERR, "modeline parse error: expected at least 9 arguments, got {}", args.size() - 1); + return false; + } + + int argno = 1; + + auto huErrStr = [](Hyprutils::String::eNumericParseResult r) -> const char* { + switch (r) { + case Hyprutils::String::NUMERIC_PARSE_BAD: return "bad input"; + case Hyprutils::String::NUMERIC_PARSE_GARBAGE: return "garbage input"; + case Hyprutils::String::NUMERIC_PARSE_OUT_OF_RANGE: return "out of range"; + case Hyprutils::String::NUMERIC_PARSE_OK: return "ok"; + default: return "error"; + } + }; + +#define ASSIGN_OR_FAIL(prop, type) \ + if (auto n = strToNumber(args[argno++]); n) \ + prop = n.value(); \ + else { \ + Log::logger->log(Log::ERR, "modeline parse error: invalid input at \"{}\": {}", args[argno - 1], huErrStr(n.error())); \ + return false; \ + } + + mode.type = DRM_MODE_TYPE_USERDEF; + + ASSIGN_OR_FAIL(mode.clock, float); + ASSIGN_OR_FAIL(mode.hdisplay, int); + ASSIGN_OR_FAIL(mode.hsync_start, int); + ASSIGN_OR_FAIL(mode.hsync_end, int); + ASSIGN_OR_FAIL(mode.htotal, int); + ASSIGN_OR_FAIL(mode.vdisplay, int); + ASSIGN_OR_FAIL(mode.vsync_start, int); + ASSIGN_OR_FAIL(mode.vsync_end, int); + ASSIGN_OR_FAIL(mode.vtotal, int); + + mode.clock *= 1000; + mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal; + +#undef ASSIGN_OR_FAIL + + // clang-format off + static std::unordered_map flagsmap = { + {"+hsync", DRM_MODE_FLAG_PHSYNC}, + {"-hsync", DRM_MODE_FLAG_NHSYNC}, + {"+vsync", DRM_MODE_FLAG_PVSYNC}, + {"-vsync", DRM_MODE_FLAG_NVSYNC}, + {"Interlace", DRM_MODE_FLAG_INTERLACE}, + }; + // clang-format on + + for (; argno < sc(args.size()); argno++) { + auto key = std::string{args[argno]}; + std::ranges::transform(key, key.begin(), ::tolower); + + auto it = flagsmap.find(key); + + if (it != flagsmap.end()) + mode.flags |= it->second; + else + Log::logger->log(Log::ERR, "Invalid flag {} in modeline", key); + } + + snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000); + + return true; +} + +CMonitorRuleParser::CMonitorRuleParser(const std::string& name) { + m_rule.m_name = name; +} + +const std::string& CMonitorRuleParser::name() { + return m_rule.m_name; +} + +Config::CMonitorRule& CMonitorRuleParser::rule() { + return m_rule; +} + +std::optional CMonitorRuleParser::getError() { + if (m_error.empty()) + return {}; + return m_error; +} + +bool CMonitorRuleParser::parseMode(const std::string& value) { + if (value.starts_with("pref")) + m_rule.m_resolution = Vector2D(); + else if (value.starts_with("highrr")) + m_rule.m_resolution = Vector2D(-1, -1); + else if (value.starts_with("highres")) + m_rule.m_resolution = Vector2D(-1, -2); + else if (value.starts_with("maxwidth")) + m_rule.m_resolution = Vector2D(-1, -3); + else if (parseModeLine(value, m_rule.m_drmMode)) { + m_rule.m_resolution = Vector2D(m_rule.m_drmMode.hdisplay, m_rule.m_drmMode.vdisplay); + m_rule.m_refreshRate = sc(m_rule.m_drmMode.vrefresh) / 1000; + } else { + + if (!value.contains("x")) { + m_error += "invalid resolution "; + m_rule.m_resolution = Vector2D(); + return false; + } else { + try { + m_rule.m_resolution.x = stoi(value.substr(0, value.find_first_of('x'))); + m_rule.m_resolution.y = stoi(value.substr(value.find_first_of('x') + 1, value.find_first_of('@'))); + + if (value.contains("@")) + m_rule.m_refreshRate = stof(value.substr(value.find_first_of('@') + 1)); + } catch (...) { + m_error += "invalid resolution "; + m_rule.m_resolution = Vector2D(); + return false; + } + } + } + return true; +} + +bool CMonitorRuleParser::parsePosition(const std::string& value, bool isFirst) { + if (value.starts_with("auto")) { + m_rule.m_offset = Vector2D(-INT32_MAX, -INT32_MAX); + // If this is the first monitor rule needs to be on the right. + if (value == "auto-right" || value == "auto" || isFirst) + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_RIGHT; + else if (value == "auto-left") + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_LEFT; + else if (value == "auto-up") + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_UP; + else if (value == "auto-down") + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_DOWN; + else if (value == "auto-center-right") + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_CENTER_RIGHT; + else if (value == "auto-center-left") + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_CENTER_LEFT; + else if (value == "auto-center-up") + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_CENTER_UP; + else if (value == "auto-center-down") + m_rule.m_autoDir = eAutoDirs::DIR_AUTO_CENTER_DOWN; + else { + Log::logger->log(Log::WARN, + "Invalid auto direction. Valid options are 'auto'," + "'auto-up', 'auto-down', 'auto-left', 'auto-right'," + "'auto-center-up', 'auto-center-down'," + "'auto-center-left', and 'auto-center-right'."); + m_error += "invalid auto direction "; + return false; + } + } else { + if (!value.contains("x")) { + m_error += "invalid offset "; + m_rule.m_offset = Vector2D(-INT32_MAX, -INT32_MAX); + return false; + } else { + try { + m_rule.m_offset.x = stoi(value.substr(0, value.find_first_of('x'))); + m_rule.m_offset.y = stoi(value.substr(value.find_first_of('x') + 1)); + } catch (...) { + m_error += "invalid offset "; + m_rule.m_offset = Vector2D(-INT32_MAX, -INT32_MAX); + return false; + } + } + } + return true; +} + +bool CMonitorRuleParser::parseScale(const std::string& value) { + if (value.starts_with("auto")) + m_rule.m_scale = -1; + else { + if (!isNumber(value, true)) { + m_error += "invalid scale "; + return false; + } else { + m_rule.m_scale = stof(value); + + if (m_rule.m_scale < 0.25F) { + m_error += "invalid scale "; + m_rule.m_scale = 1; + return false; + } + } + } + return true; +} + +bool CMonitorRuleParser::parseTransform(const std::string& value) { + if (!isNumber(value)) { + m_error += "invalid transform "; + return false; + } + + const auto TSF = std::stoi(value); + if (std::clamp(TSF, 0, 7) != TSF) { + Log::logger->log(Log::ERR, "Invalid transform {} in monitor", TSF); + m_error += "invalid transform "; + return false; + } + m_rule.m_transform = sc(TSF); + return true; +} + +bool CMonitorRuleParser::parseBitdepth(const std::string& value) { + m_rule.m_enable10bit = value == "10"; + return true; +} + +bool CMonitorRuleParser::parseCM(const std::string& value) { + auto parsedCM = NCMType::fromString(value); + if (!parsedCM.has_value()) { + m_error += "invalid cm "; + return false; + } + m_rule.m_cmType = parsedCM.value(); + return true; +} + +bool CMonitorRuleParser::parseSDRBrightness(const std::string& value) { + try { + m_rule.m_sdrBrightness = stof(value); + } catch (...) { + m_error += "invalid sdrbrightness "; + return false; + } + return true; +} + +bool CMonitorRuleParser::parseSDRSaturation(const std::string& value) { + try { + m_rule.m_sdrSaturation = stof(value); + } catch (...) { + m_error += "invalid sdrsaturation "; + return false; + } + return true; +} + +bool CMonitorRuleParser::parseVRR(const std::string& value) { + if (!isNumber(value)) { + m_error += "invalid vrr "; + return false; + } + + m_rule.m_vrr = std::stoi(value); + return true; +} + +bool CMonitorRuleParser::parseICC(const std::string& val) { + if (val.empty()) { + m_error += "invalid icc "; + return false; + } + m_rule.m_iccFile = val; + return true; +} + +void CMonitorRuleParser::setDisabled() { + m_rule.m_disabled = true; +} + +void CMonitorRuleParser::setMirror(const std::string& value) { + m_rule.m_mirrorOf = value; +} + +bool CMonitorRuleParser::setReserved(const Desktop::CReservedArea& value) { + m_rule.m_reservedArea = value; + return true; +} \ No newline at end of file -- cgit v1.2.3