summaryrefslogtreecommitdiff
path: root/src/config/shared/monitor/Parser.cpp
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2026-03-20 13:52:37 -0400
committerGitHub <noreply@github.com>2026-03-20 17:52:37 +0000
commit8726a7363eb9213235e6e23657668e0daac39a5b (patch)
tree42eeb40f6f81a9ccec1455f308e56173e9dcce4b /src/config/shared/monitor/Parser.cpp
parentb940b0d2c197841b0f648598ee782dbaf9e0a89b (diff)
config: cleanup the entire config infrastructure (#13785)
Massively refactors the config infrastructure, sorely needed and will be req'd for the lua stuff
Diffstat (limited to 'src/config/shared/monitor/Parser.cpp')
-rw-r--r--src/config/shared/monitor/Parser.cpp294
1 files changed, 294 insertions, 0 deletions
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 <hyprutils/string/VarList2.hpp>
+#include <hyprutils/string/Numeric.hpp>
+#include <hyprutils/string/String.hpp>
+#include <string>
+#include <algorithm>
+
+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<type>(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<std::string, uint32_t> 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<int>(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<std::string> 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<float>(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<wl_output_transform>(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