config: add a new monitor v2 config syntax (#9761)

This commit is contained in:
UjinT34
2025-06-05 17:56:46 +03:00
committed by GitHub
parent 59c886d855
commit abdfc5ea40
3 changed files with 322 additions and 157 deletions

View File

@@ -791,6 +791,20 @@ CConfigManager::CConfigManager() {
m_config->addSpecialConfigValue("device", "flip_y", Hyprlang::INT{0}); // only for touchpads
m_config->addSpecialConfigValue("device", "keybinds", Hyprlang::INT{1}); // enable/disable keybinds
m_config->addSpecialCategory("monitorv2", {.key = "output"});
m_config->addSpecialConfigValue("monitorv2", "disabled", Hyprlang::INT{0});
m_config->addSpecialConfigValue("monitorv2", "mode", {"preferred"});
m_config->addSpecialConfigValue("monitorv2", "position", {"auto"});
m_config->addSpecialConfigValue("monitorv2", "scale", Hyprlang::FLOAT{1.0});
m_config->addSpecialConfigValue("monitorv2", "addreserved", {STRVAL_EMPTY});
m_config->addSpecialConfigValue("monitorv2", "mirror", {STRVAL_EMPTY});
m_config->addSpecialConfigValue("monitorv2", "bitdepth", {STRVAL_EMPTY}); // TODO use correct type
m_config->addSpecialConfigValue("monitorv2", "cm", {"auto"});
m_config->addSpecialConfigValue("monitorv2", "sdrbrightness", Hyprlang::FLOAT{1.0});
m_config->addSpecialConfigValue("monitorv2", "sdrsaturation", Hyprlang::FLOAT{1.0});
m_config->addSpecialConfigValue("monitorv2", "vrr", Hyprlang::INT{0});
m_config->addSpecialConfigValue("monitorv2", "transform", {STRVAL_EMPTY}); // TODO use correct type
// keywords
m_config->registerHandler(&::handleExec, "exec", {false});
m_config->registerHandler(&::handleRawExec, "execr", {false});
@@ -918,8 +932,9 @@ void CConfigManager::reload() {
resetHLConfig();
m_configCurrentPath = getMainConfigPath();
const auto ERR = m_config->parse();
m_lastConfigVerificationWasSuccessful = !ERR.error;
postConfigReload(ERR);
const auto monitorError = handleMonitorv2();
m_lastConfigVerificationWasSuccessful = !ERR.error && !monitorError.error;
postConfigReload(ERR.error || !monitorError.error ? ERR : monitorError);
}
std::string CConfigManager::verify() {
@@ -1009,6 +1024,68 @@ void CConfigManager::updateWatcher() {
g_pConfigWatcher->setWatchList(*PDISABLEAUTORELOAD ? std::vector<std::string>{} : m_configPaths);
}
std::optional<std::string> CConfigManager::handleMonitorv2(const std::string& output) {
auto parser = CMonitorRuleParser(output);
auto VAL = m_config->getSpecialConfigValuePtr("monitorv2", "disabled", output.c_str());
if (VAL && VAL->m_bSetByUser && std::any_cast<Hyprlang::INT>(VAL->getValue()))
parser.setDisabled();
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "mode", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.parseMode(std::any_cast<Hyprlang::STRING>(VAL->getValue()));
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "position", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.parsePosition(std::any_cast<Hyprlang::STRING>(VAL->getValue()));
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "scale", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().scale = std::any_cast<Hyprlang::FLOAT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "addreserved", output.c_str());
if (VAL && VAL->m_bSetByUser) {
const auto ARGS = CVarList(std::any_cast<Hyprlang::STRING>(VAL->getValue()));
parser.setReserved({.top = std::stoi(ARGS[0]), .bottom = std::stoi(ARGS[1]), .left = std::stoi(ARGS[2]), .right = std::stoi(ARGS[3])});
}
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "mirror", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.setMirror(std::any_cast<Hyprlang::STRING>(VAL->getValue()));
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "bitdepth", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.parseBitdepth(std::any_cast<Hyprlang::STRING>(VAL->getValue()));
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "cm", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.parseCM(std::any_cast<Hyprlang::STRING>(VAL->getValue()));
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdrbrightness", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().sdrBrightness = std::any_cast<Hyprlang::FLOAT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdrsaturation", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().sdrSaturation = std::any_cast<Hyprlang::FLOAT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "vrr", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().vrr = std::any_cast<Hyprlang::INT>(VAL->getValue());
VAL = m_config->getSpecialConfigValuePtr("monitorv2", "transform", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.parseTransform(std::any_cast<Hyprlang::STRING>(VAL->getValue()));
auto newrule = parser.rule();
std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; });
m_monitorRules.push_back(newrule);
return parser.getError();
}
Hyprlang::CParseResult CConfigManager::handleMonitorv2() {
Hyprlang::CParseResult result;
for (const auto& output : m_config->listKeysForSpecialCategory("monitorv2")) {
const auto error = handleMonitorv2(output);
if (error.has_value()) {
result.setError(error.value().c_str());
return result;
}
}
return result;
}
void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
static int prevEnabledExplicit = *PENABLEEXPLICIT;
@@ -1607,6 +1684,7 @@ void CConfigManager::dispatchExecShutdown() {
}
void CConfigManager::performMonitorReload() {
handleMonitorv2();
bool overAgain = false;
@@ -1956,30 +2034,207 @@ static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) {
return true;
}
CMonitorRuleParser::CMonitorRuleParser(const std::string& name) {
m_rule.name = name;
}
const std::string& CMonitorRuleParser::name() {
return m_rule.name;
}
SMonitorRule& 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.resolution = Vector2D();
else if (value.starts_with("highrr"))
m_rule.resolution = Vector2D(-1, -1);
else if (value.starts_with("highres"))
m_rule.resolution = Vector2D(-1, -2);
else if (parseModeLine(value, m_rule.drmMode)) {
m_rule.resolution = Vector2D(m_rule.drmMode.hdisplay, m_rule.drmMode.vdisplay);
m_rule.refreshRate = float(m_rule.drmMode.vrefresh) / 1000;
} else {
if (!value.contains("x")) {
m_error += "invalid resolution ";
m_rule.resolution = Vector2D();
return false;
} else {
try {
m_rule.resolution.x = stoi(value.substr(0, value.find_first_of('x')));
m_rule.resolution.y = stoi(value.substr(value.find_first_of('x') + 1, value.find_first_of('@')));
if (value.contains("@"))
m_rule.refreshRate = stof(value.substr(value.find_first_of('@') + 1));
} catch (...) {
m_error += "invalid resolution ";
m_rule.resolution = Vector2D();
return false;
}
}
}
return true;
}
bool CMonitorRuleParser::parsePosition(const std::string& value, bool isFirst) {
if (value.starts_with("auto")) {
m_rule.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.autoDir = eAutoDirs::DIR_AUTO_RIGHT;
else if (value == "auto-left")
m_rule.autoDir = eAutoDirs::DIR_AUTO_LEFT;
else if (value == "auto-up")
m_rule.autoDir = eAutoDirs::DIR_AUTO_UP;
else if (value == "auto-down")
m_rule.autoDir = eAutoDirs::DIR_AUTO_DOWN;
else {
Debug::log(WARN,
"Invalid auto direction. Valid options are 'auto',"
"'auto-up', 'auto-down', 'auto-left', and 'auto-right'.");
m_error += "invalid auto direction ";
return false;
}
} else {
if (!value.contains("x")) {
m_error += "invalid offset ";
m_rule.offset = Vector2D(-INT32_MAX, -INT32_MAX);
return false;
} else {
m_rule.offset.x = stoi(value.substr(0, value.find_first_of('x')));
m_rule.offset.y = stoi(value.substr(value.find_first_of('x') + 1));
}
}
return true;
}
bool CMonitorRuleParser::parseScale(const std::string& value) {
if (value.starts_with("auto"))
m_rule.scale = -1;
else {
if (!isNumber(value, true)) {
m_error += "invalid scale ";
return false;
} else {
m_rule.scale = stof(value);
if (m_rule.scale < 0.25f) {
m_error += "invalid scale ";
m_rule.scale = 1;
return false;
}
}
}
return true;
}
bool CMonitorRuleParser::parseTransform(const std::string& value) {
const auto TSF = std::stoi(value);
if (std::clamp(TSF, 0, 7) != TSF) {
Debug::log(ERR, "Invalid transform {} in monitor", TSF);
m_error += "invalid transform ";
return false;
}
m_rule.transform = (wl_output_transform)TSF;
return true;
}
bool CMonitorRuleParser::parseBitdepth(const std::string& value) {
m_rule.enable10bit = value == "10";
return true;
}
bool CMonitorRuleParser::parseCM(const std::string& value) {
if (value == "auto")
m_rule.cmType = CM_AUTO;
else if (value == "srgb")
m_rule.cmType = CM_SRGB;
else if (value == "wide")
m_rule.cmType = CM_WIDE;
else if (value == "edid")
m_rule.cmType = CM_EDID;
else if (value == "hdr")
m_rule.cmType = CM_HDR;
else if (value == "hdredid")
m_rule.cmType = CM_HDR_EDID;
else {
m_error += "invalid cm ";
return false;
}
return true;
}
bool CMonitorRuleParser::parseSDRBrightness(const std::string& value) {
try {
m_rule.sdrBrightness = stof(value);
} catch (...) {
m_error += "invalid sdrbrightness ";
return false;
}
return true;
}
bool CMonitorRuleParser::parseSDRSaturation(const std::string& value) {
try {
m_rule.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.vrr = std::stoi(value);
return true;
}
void CMonitorRuleParser::setDisabled() {
m_rule.disabled = true;
}
void CMonitorRuleParser::setMirror(const std::string& value) {
m_rule.mirrorOf = value;
}
bool CMonitorRuleParser::setReserved(const SMonitorAdditionalReservedArea& value) {
g_pConfigManager->m_mAdditionalReservedAreas[name()] = value;
return true;
}
std::optional<std::string> CConfigManager::handleMonitor(const std::string& command, const std::string& args) {
// get the monitor config
SMonitorRule newrule;
const auto ARGS = CVarList(args);
const auto ARGS = CVarList(args);
newrule.name = ARGS[0];
auto parser = CMonitorRuleParser(ARGS[0]);
if (ARGS[1] == "disable" || ARGS[1] == "disabled" || ARGS[1] == "addreserved" || ARGS[1] == "transform") {
if (ARGS[1] == "disable" || ARGS[1] == "disabled")
newrule.disabled = true;
parser.setDisabled();
else if (ARGS[1] == "transform") {
const auto TSF = std::stoi(ARGS[2]);
if (std::clamp(TSF, 0, 7) != TSF) {
Debug::log(ERR, "Invalid transform {} in monitor", TSF);
return "invalid transform";
}
if (!parser.parseTransform(ARGS[2]))
return parser.getError();
const auto TRANSFORM = (wl_output_transform)TSF;
const auto TRANSFORM = parser.rule().transform;
// overwrite if exists
for (auto& r : m_monitorRules) {
if (r.name == newrule.name) {
if (r.name == parser.name()) {
r.transform = TRANSFORM;
return {};
}
@@ -1987,179 +2242,53 @@ std::optional<std::string> CConfigManager::handleMonitor(const std::string& comm
return {};
} else if (ARGS[1] == "addreserved") {
int top = std::stoi(ARGS[2]);
int bottom = std::stoi(ARGS[3]);
int left = std::stoi(ARGS[4]);
int right = std::stoi(ARGS[5]);
m_mAdditionalReservedAreas[newrule.name] = {top, bottom, left, right};
parser.setReserved({.top = std::stoi(ARGS[2]), .bottom = std::stoi(ARGS[3]), .left = std::stoi(ARGS[4]), .right = std::stoi(ARGS[5])});
return {};
} else {
Debug::log(ERR, "ConfigManager parseMonitor, curitem bogus???");
return "parse error: curitem bogus";
}
std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; });
std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == parser.name(); });
m_monitorRules.push_back(newrule);
m_monitorRules.push_back(parser.rule());
return {};
}
std::string error = "";
if (ARGS[1].starts_with("pref")) {
newrule.resolution = Vector2D();
} else if (ARGS[1].starts_with("highrr")) {
newrule.resolution = Vector2D(-1, -1);
} else if (ARGS[1].starts_with("highres")) {
newrule.resolution = Vector2D(-1, -2);
} else if (ARGS[1].starts_with("maxwidth")) {
newrule.resolution = Vector2D(-1, -3);
} else if (parseModeLine(ARGS[1], newrule.drmMode)) {
newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay);
newrule.refreshRate = float(newrule.drmMode.vrefresh) / 1000;
} else {
if (!ARGS[1].contains("x")) {
error += "invalid resolution ";
newrule.resolution = Vector2D();
} else {
try {
newrule.resolution.x = stoi(ARGS[1].substr(0, ARGS[1].find_first_of('x')));
newrule.resolution.y = stoi(ARGS[1].substr(ARGS[1].find_first_of('x') + 1, ARGS[1].find_first_of('@')));
if (ARGS[1].contains("@"))
newrule.refreshRate = stof(ARGS[1].substr(ARGS[1].find_first_of('@') + 1));
} catch (...) {
error += "invalid resolution ";
newrule.resolution = Vector2D();
}
}
}
if (ARGS[2].starts_with("auto")) {
newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX);
// If this is the first monitor rule needs to be on the right.
if (ARGS[2] == "auto-right" || ARGS[2] == "auto" || m_monitorRules.empty())
newrule.autoDir = eAutoDirs::DIR_AUTO_RIGHT;
else if (ARGS[2] == "auto-left")
newrule.autoDir = eAutoDirs::DIR_AUTO_LEFT;
else if (ARGS[2] == "auto-up")
newrule.autoDir = eAutoDirs::DIR_AUTO_UP;
else if (ARGS[2] == "auto-down")
newrule.autoDir = eAutoDirs::DIR_AUTO_DOWN;
else if (ARGS[2] == "auto-center-right")
newrule.autoDir = eAutoDirs::DIR_AUTO_CENTER_RIGHT;
else if (ARGS[2] == "auto-center-left")
newrule.autoDir = eAutoDirs::DIR_AUTO_CENTER_LEFT;
else if (ARGS[2] == "auto-center-up")
newrule.autoDir = eAutoDirs::DIR_AUTO_CENTER_UP;
else if (ARGS[2] == "auto-center-down")
newrule.autoDir = eAutoDirs::DIR_AUTO_CENTER_DOWN;
else {
Debug::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'.");
error += "invalid auto direction ";
}
} else {
if (!ARGS[2].contains("x")) {
error += "invalid offset ";
newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX);
} else {
newrule.offset.x = stoi(ARGS[2].substr(0, ARGS[2].find_first_of('x')));
newrule.offset.y = stoi(ARGS[2].substr(ARGS[2].find_first_of('x') + 1));
}
}
if (ARGS[3].starts_with("auto")) {
newrule.scale = -1;
} else {
if (!isNumber(ARGS[3], true))
error += "invalid scale ";
else {
newrule.scale = stof(ARGS[3]);
if (newrule.scale < 0.25f) {
error += "invalid scale ";
newrule.scale = 1;
}
}
}
parser.parseMode(ARGS[1]);
parser.parsePosition(ARGS[2]);
parser.parseScale(ARGS[3]);
int argno = 4;
while (!ARGS[argno].empty()) {
if (ARGS[argno] == "mirror") {
newrule.mirrorOf = ARGS[argno + 1];
parser.setMirror(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "bitdepth") {
newrule.enable10bit = ARGS[argno + 1] == "10";
parser.parseBitdepth(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "cm") {
if (ARGS[argno + 1] == "auto")
newrule.cmType = CM_AUTO;
else if (ARGS[argno + 1] == "srgb")
newrule.cmType = CM_SRGB;
else if (ARGS[argno + 1] == "wide")
newrule.cmType = CM_WIDE;
else if (ARGS[argno + 1] == "edid")
newrule.cmType = CM_EDID;
else if (ARGS[argno + 1] == "hdr")
newrule.cmType = CM_HDR;
else if (ARGS[argno + 1] == "hdredid")
newrule.cmType = CM_HDR_EDID;
else
error = "invalid cm ";
parser.parseCM(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "sdrsaturation") {
try {
newrule.sdrSaturation = stof(ARGS[argno + 1]);
} catch (...) { error = "invalid sdrsaturation "; }
parser.parseSDRSaturation(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "sdrbrightness") {
try {
newrule.sdrBrightness = stof(ARGS[argno + 1]);
} catch (...) { error = "invalid sdrbrightness "; }
parser.parseSDRBrightness(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "transform") {
if (!isNumber(ARGS[argno + 1])) {
error = "invalid transform ";
argno++;
continue;
}
const auto NUM = std::stoi(ARGS[argno + 1]);
if (NUM < 0 || NUM > 7) {
error = "invalid transform ";
argno++;
continue;
}
newrule.transform = (wl_output_transform)std::stoi(ARGS[argno + 1]);
parser.parseTransform(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "vrr") {
if (!isNumber(ARGS[argno + 1])) {
error = "invalid vrr ";
argno++;
continue;
}
newrule.vrr = std::stoi(ARGS[argno + 1]);
parser.parseVRR(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "workspace") {
const auto& [id, name] = getWorkspaceIDNameFromString(ARGS[argno + 1]);
SWorkspaceRule wsRule;
wsRule.monitor = newrule.name;
wsRule.monitor = parser.name();
wsRule.workspaceString = ARGS[argno + 1];
wsRule.workspaceId = id;
wsRule.workspaceName = name;
@@ -2174,13 +2303,13 @@ std::optional<std::string> CConfigManager::handleMonitor(const std::string& comm
argno++;
}
auto newrule = parser.rule();
std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; });
m_monitorRules.push_back(newrule);
if (error.empty())
return {};
return error;
return parser.getError();
}
std::optional<std::string> CConfigManager::handleBezier(const std::string& command, const std::string& args) {

View File

@@ -27,6 +27,8 @@
#define HANDLE void*
class CConfigManager;
struct SWorkspaceRule {
std::string monitor = "";
std::string workspaceString = "";
@@ -170,6 +172,32 @@ namespace std {
};
}
class CMonitorRuleParser {
public:
CMonitorRuleParser(const std::string& name);
const std::string& name();
SMonitorRule& rule();
std::optional<std::string> getError();
bool parseMode(const std::string& value);
bool parsePosition(const std::string& value, bool isFirst = false);
bool parseScale(const std::string& value);
bool parseTransform(const std::string& value);
bool parseBitdepth(const std::string& value);
bool parseCM(const std::string& value);
bool parseSDRBrightness(const std::string& value);
bool parseSDRSaturation(const std::string& value);
bool parseVRR(const std::string& value);
void setDisabled();
void setMirror(const std::string& value);
bool setReserved(const SMonitorAdditionalReservedArea& value);
private:
SMonitorRule m_rule;
std::string m_error = "";
};
class CConfigManager {
public:
CConfigManager();
@@ -257,6 +285,9 @@ class CConfigManager {
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::optional<std::string> handlePermission(const std::string&, const std::string&);
std::optional<std::string> handleMonitorv2(const std::string& output);
Hyprlang::CParseResult handleMonitorv2();
std::string m_configCurrentPath;
bool m_wantsMonitorReload = false;
@@ -307,6 +338,7 @@ class CConfigManager {
std::optional<std::string> resetHLConfig();
std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> verifyConfigExists();
void postConfigReload(const Hyprlang::CParseResult& result);
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
@@ -319,6 +351,7 @@ class CConfigManager {
std::unordered_map<SFloatCache, Vector2D> m_mStoredFloatingSizes;
friend struct SConfigOptionDescription;
friend class CMonitorRuleParser;
};
inline UP<CConfigManager> g_pConfigManager;

View File

@@ -1109,6 +1109,9 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
if (COMMAND == "monitor" || COMMAND == "source")
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
if (COMMAND.contains("monitorv2"))
g_pEventLoopManager->doLater([] { g_pConfigManager->m_wantsMonitorReload = true; });
if (COMMAND.contains("input") || COMMAND.contains("device") || COMMAND == "source") {
g_pInputManager->setKeyboardLayout(); // update kb layout
g_pInputManager->setPointerConfigs(); // update mouse cfgs