From caaa9b11e4763ed0367f81bf97ceaad5175806fc Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:10:53 +0100 Subject: [PATCH] wlr-output-configuration: Improve output configuration (#7571) --- src/config/ConfigManager.cpp | 66 ++++++++++++++--- src/config/ConfigManager.hpp | 2 +- src/helpers/Monitor.cpp | 4 +- src/managers/KeybindManager.cpp | 2 +- src/protocols/OutputManagement.cpp | 115 +++++++++++++++++++---------- src/protocols/OutputManagement.hpp | 69 +++++++++++------ 6 files changed, 181 insertions(+), 77 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 21a3a21e..ab5905c2 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -7,6 +7,7 @@ #include "helpers/varlist/VarList.hpp" #include "../protocols/LayerShell.hpp" #include "../xwayland/XWayland.hpp" +#include "../protocols/OutputManagement.hpp" #include #include @@ -1079,28 +1080,69 @@ std::string CConfigManager::getDeviceString(const std::string& dev, const std::s return VAL; } -SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) { +SMonitorRule CConfigManager::getMonitorRuleFor(const SP PMONITOR) { + auto applyWlrOutputConfig = [PMONITOR](SMonitorRule rule) -> SMonitorRule { + const auto CONFIG = PROTO::outputManagement->getOutputStateFor(PMONITOR); + + if (!CONFIG) + return rule; + + Debug::log(LOG, "CConfigManager::getMonitorRuleFor: found a wlr_output_manager override for {}", PMONITOR->szName); + + Debug::log(LOG, " > overriding enabled: {} -> {}", !rule.disabled, !CONFIG->enabled); + rule.disabled = !CONFIG->enabled; + + if ((CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_MODE) || (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE)) { + Debug::log(LOG, " > overriding mode: {:.0f}x{:.0f}@{:.2f}Hz -> {:.0f}x{:.0f}@{:.2f}Hz", rule.resolution.x, rule.resolution.y, rule.refreshRate, CONFIG->resolution.x, + CONFIG->resolution.y, CONFIG->refresh / 1000.F); + rule.resolution = CONFIG->resolution; + rule.refreshRate = CONFIG->refresh / 1000.F; + } + + if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) { + Debug::log(LOG, " > overriding offset: {:.0f}, {:.0f} -> {:.0f}, {:.0f}", rule.offset.x, rule.offset.y, CONFIG->position.x, CONFIG->position.y); + rule.offset = CONFIG->position; + } + + if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) { + Debug::log(LOG, " > overriding transform: {} -> {}", (uint8_t)rule.transform, (uint8_t)CONFIG->transform); + rule.transform = CONFIG->transform; + } + + if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) { + Debug::log(LOG, " > overriding scale: {} -> {}", (uint8_t)rule.scale, (uint8_t)CONFIG->scale); + rule.scale = CONFIG->scale; + } + + if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { + Debug::log(LOG, " > overriding vrr: {} -> {}", rule.vrr.value_or(0), CONFIG->adaptiveSync); + rule.vrr = (int)CONFIG->adaptiveSync; + } + + return rule; + }; + for (auto const& r : m_dMonitorRules | std::views::reverse) { - if (PMONITOR.matchesStaticSelector(r.name)) { - return r; + if (PMONITOR->matchesStaticSelector(r.name)) { + return applyWlrOutputConfig(r); } } - Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR.szName); + Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR->szName); for (auto const& r : m_dMonitorRules) { if (r.name.empty()) { - return r; + return applyWlrOutputConfig(r); } } Debug::log(WARN, "No rules configured. Using the default hardcoded one."); - return SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT, - .name = "", - .resolution = Vector2D(0, 0), - .offset = Vector2D(-INT32_MAX, -INT32_MAX), - .scale = -1}; // 0, 0 is preferred and -1, -1 is auto + return applyWlrOutputConfig(SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT, + .name = "", + .resolution = Vector2D(0, 0), + .offset = Vector2D(-INT32_MAX, -INT32_MAX), + .scale = -1}); // 0, 0 is preferred and -1, -1 is auto } SWorkspaceRule CConfigManager::getWorkspaceRuleFor(PHLWORKSPACE pWorkspace) { @@ -1459,7 +1501,7 @@ void CConfigManager::performMonitorReload() { if (!m->output || m->isUnsafeFallback) continue; - auto rule = getMonitorRuleFor(*m); + auto rule = getMonitorRuleFor(m); if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) { overAgain = true; @@ -1516,7 +1558,7 @@ void CConfigManager::ensureMonitorStatus() { if (!rm->output || rm->isUnsafeFallback) continue; - auto rule = getMonitorRuleFor(*rm); + auto rule = getMonitorRuleFor(rm); if (rule.disabled == rm->m_bEnabled) g_pHyprRenderer->applyMonitorRule(rm.get(), &rule); diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index da450e39..2134acf4 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -169,7 +169,7 @@ class CConfigManager { static std::string getMainConfigPath(); const std::string getConfigString(); - SMonitorRule getMonitorRuleFor(const CMonitor&); + SMonitorRule getMonitorRuleFor(const SP); SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace); std::string getDefaultWorkspaceFor(const std::string&); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index a2d4c504..a9569699 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -114,7 +114,7 @@ void CMonitor::onConnect(bool noRule) { createdByUser = true; // should be true. WL and Headless backends should be addable / removable // get monitor rule that matches - SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(*this); + SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(self.lock()); // if it's disabled, disable and ignore if (monitorRule.disabled) { @@ -489,7 +489,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) { pMirrorOf = nullptr; // set rule - const auto RULE = g_pConfigManager->getMonitorRuleFor(*this); + const auto RULE = g_pConfigManager->getMonitorRuleFor(self.lock()); vecPosition = RULE.offset; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 48c4517a..cd47bf71 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1943,7 +1943,7 @@ SDispatchResult CKeybindManager::forceRendererReload(std::string args) { if (!m->output) continue; - auto rule = g_pConfigManager->getMonitorRuleFor(*m); + auto rule = g_pConfigManager->getMonitorRuleFor(m); if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule, true)) { overAgain = true; break; diff --git a/src/protocols/OutputManagement.cpp b/src/protocols/OutputManagement.cpp index 09552b19..dcc7a62d 100644 --- a/src/protocols/OutputManagement.cpp +++ b/src/protocols/OutputManagement.cpp @@ -307,10 +307,15 @@ COutputConfiguration::COutputConfiguration(SP resour LOGM(LOG, "disableHead on {}", PMONITOR->szName); - PMONITOR->activeMonitorRule.disabled = true; - if (!g_pConfigManager->replaceMonitorRule(PMONITOR->activeMonitorRule)) - g_pConfigManager->appendMonitorRule(PMONITOR->activeMonitorRule); - g_pHyprRenderer->applyMonitorRule(PMONITOR, &PMONITOR->activeMonitorRule, false); + SWlrManagerSavedOutputState newState; + if (owner->monitorStates.contains(PMONITOR->szName)) + newState = owner->monitorStates.at(PMONITOR->szName); + + newState.enabled = false; + + g_pConfigManager->m_bWantsMonitorReload = true; + + owner->monitorStates[PMONITOR->szName] = newState; }); resource->setTest([this](CZwlrOutputConfigurationV1* r) { @@ -346,6 +351,11 @@ bool COutputConfiguration::applyTestConfiguration(bool test) { LOGM(LOG, "Applying configuration"); + if (!owner) { + LOGM(ERR, "applyTestConfiguration: no owner?!"); + return false; + } + for (auto const& headw : heads) { auto head = headw.lock(); @@ -357,41 +367,59 @@ bool COutputConfiguration::applyTestConfiguration(bool test) { if (!PMONITOR) continue; - LOGM(LOG, "Applying config for monitor {}", PMONITOR->szName); + LOGM(LOG, "Saving config for monitor {}", PMONITOR->szName); - SMonitorRule newRule = PMONITOR->activeMonitorRule; - newRule.name = PMONITOR->szName; - newRule.disabled = false; + SWlrManagerSavedOutputState newState; + if (owner->monitorStates.contains(PMONITOR->szName)) + newState = owner->monitorStates.at(PMONITOR->szName); - if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) { - newRule.resolution = head->state.mode->getMode()->pixelSize; - newRule.refreshRate = head->state.mode->getMode()->refreshRate / 1000.F; - } else if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) { - newRule.resolution = head->state.customMode.size; - newRule.refreshRate = head->state.customMode.refresh / 1000.F; + newState.enabled = true; + + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) { + newState.resolution = head->state.mode->getMode()->pixelSize; + newState.refresh = head->state.mode->getMode()->refreshRate; + newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE; + LOGM(LOG, " > Mode: {:.0f}x{:.0f}@{}mHz", newState.resolution.x, newState.resolution.y, newState.refresh); + } else if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) { + newState.resolution = head->state.customMode.size; + newState.refresh = head->state.customMode.refresh; + newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE; + LOGM(LOG, " > Custom mode: {:.0f}x{:.0f}@{}mHz", newState.resolution.x, newState.resolution.y, newState.refresh); } - if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION) - newRule.offset = head->state.position; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION) { + newState.position = head->state.position; + newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION; + LOGM(LOG, " > Position: {:.0f}, {:.0f}", head->state.position.x, head->state.position.y); + } - if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) - newRule.vrr = head->state.adaptiveSync; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { + newState.adaptiveSync = head->state.adaptiveSync; + newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC; + LOGM(LOG, " > vrr: {}", newState.adaptiveSync); + } - if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE) - newRule.scale = head->state.scale; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE) { + newState.scale = head->state.scale; + newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE; + LOGM(LOG, " > scale: {:.2f}", newState.scale); + } - if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM) - newRule.transform = head->state.transform; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM) { + newState.transform = head->state.transform; + newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM; + LOGM(LOG, " > transform: {}", (uint8_t)newState.transform); + } // reset properties for next set. - head->committedProperties = 0; + head->state.committedProperties = 0; - if (!g_pConfigManager->replaceMonitorRule(newRule)) - g_pConfigManager->appendMonitorRule(newRule); g_pConfigManager->m_bWantsMonitorReload = true; + + owner->monitorStates[PMONITOR->szName] = newState; } - LOGM(LOG, "Applied configuration"); + LOGM(LOG, "Saved configuration"); return true; } @@ -417,12 +445,12 @@ COutputConfigurationHead::COutputConfigurationHead(SPerror(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } - committedProperties |= OUTPUT_HEAD_COMMITTED_MODE; + state.committedProperties |= OUTPUT_HEAD_COMMITTED_MODE; state.mode = MODE; LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->pixelSize.x, MODE->getMode()->pixelSize.y, MODE->getMode()->refreshRate); @@ -434,7 +462,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPerror(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } @@ -444,7 +472,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPszName, w, h, refresh); @@ -456,12 +484,12 @@ COutputConfigurationHead::COutputConfigurationHead(SPerror(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } - committedProperties |= OUTPUT_HEAD_COMMITTED_POSITION; + state.committedProperties |= OUTPUT_HEAD_COMMITTED_POSITION; state.position = {x, y}; LOGM(LOG, " | configHead for {}: set pos to {}, {}", pMonitor->szName, x, y); @@ -473,7 +501,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPerror(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } @@ -483,7 +511,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPszName, transform); @@ -495,7 +523,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPerror(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } @@ -507,7 +535,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPszName, scale); @@ -519,7 +547,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPerror(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } @@ -529,7 +557,7 @@ COutputConfigurationHead::COutputConfigurationHead(SPszName, as); @@ -601,3 +629,14 @@ SP COutputManagementProtocol::modeFromResource(wl_resource* r) { return nullptr; } + +SP COutputManagementProtocol::getOutputStateFor(SP pMonitor) { + for (auto const& m : m_vManagers) { + if (!m->monitorStates.contains(pMonitor->szName)) + continue; + + return makeShared(m->monitorStates.at(pMonitor->szName)); + } + + return nullptr; +} diff --git a/src/protocols/OutputManagement.hpp b/src/protocols/OutputManagement.hpp index 36478d0b..f4a84475 100644 --- a/src/protocols/OutputManagement.hpp +++ b/src/protocols/OutputManagement.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "WaylandProtocol.hpp" #include "wlr-output-management-unstable-v1.hpp" #include "../helpers/signal/Signal.hpp" @@ -13,6 +14,43 @@ class CMonitor; class COutputHead; class COutputMode; +struct SMonitorRule; + +enum eWlrOutputCommittedProperties : uint32_t { + OUTPUT_HEAD_COMMITTED_MODE = (1 << 0), + OUTPUT_HEAD_COMMITTED_CUSTOM_MODE = (1 << 1), + OUTPUT_HEAD_COMMITTED_POSITION = (1 << 2), + OUTPUT_HEAD_COMMITTED_TRANSFORM = (1 << 3), + OUTPUT_HEAD_COMMITTED_SCALE = (1 << 4), + OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC = (1 << 5), +}; + +struct SWlrManagerOutputState { + uint32_t committedProperties = 0; + + WP mode; + struct { + Vector2D size; + uint32_t refresh = 0; + } customMode; + Vector2D position; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + float scale = 1.F; + bool adaptiveSync = false; + bool enabled = true; +}; + +struct SWlrManagerSavedOutputState { + uint32_t committedProperties = 0; + Vector2D resolution; + uint32_t refresh = 0; + Vector2D position; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + float scale = 1.F; + bool adaptiveSync = false; + bool enabled = true; +}; + class COutputManager { public: COutputManager(SP resource_); @@ -21,6 +59,9 @@ class COutputManager { void ensureMonitorSent(CMonitor* pMonitor); void sendDone(); + // holds the states for this manager. + std::unordered_map monitorStates; + private: SP resource; bool stopped = false; @@ -80,30 +121,9 @@ class COutputConfigurationHead { public: COutputConfigurationHead(SP resource_, CMonitor* pMonitor_); - bool good(); + bool good(); - enum eCommittedProperties : uint32_t { - OUTPUT_HEAD_COMMITTED_MODE = (1 << 0), - OUTPUT_HEAD_COMMITTED_CUSTOM_MODE = (1 << 1), - OUTPUT_HEAD_COMMITTED_POSITION = (1 << 2), - OUTPUT_HEAD_COMMITTED_TRANSFORM = (1 << 3), - OUTPUT_HEAD_COMMITTED_SCALE = (1 << 4), - OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC = (1 << 5), - }; - - uint32_t committedProperties = 0; - - struct { - WP mode; - struct { - Vector2D size; - uint32_t refresh = 0; - } customMode; - Vector2D position; - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; - float scale = 1.F; - bool adaptiveSync = false; - } state; + SWlrManagerOutputState state; private: SP resource; @@ -136,6 +156,9 @@ class COutputManagementProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + // doesn't have to return one + SP getOutputStateFor(SP pMonitor); + private: void destroyResource(COutputManager* resource); void destroyResource(COutputHead* resource);