diff options
| author | Vaxry <vaxry@vaxry.net> | 2024-05-03 17:58:40 +0100 |
|---|---|---|
| committer | Vaxry <vaxry@vaxry.net> | 2024-05-03 18:08:04 +0100 |
| commit | 8a2269272b6a9752cd48097e19c0bc0f3bdab191 (patch) | |
| tree | e5381ccec7c7c09f946debc0ce5445cdca7a6fad /src/protocols/OutputManagement.cpp | |
| parent | d5bf15387ad3b4ee36cae7a3cd9f4a9c28790f2e (diff) | |
output-management: move to new impl
Diffstat (limited to 'src/protocols/OutputManagement.cpp')
| -rw-r--r-- | src/protocols/OutputManagement.cpp | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/src/protocols/OutputManagement.cpp b/src/protocols/OutputManagement.cpp new file mode 100644 index 00000000..18be636d --- /dev/null +++ b/src/protocols/OutputManagement.cpp @@ -0,0 +1,597 @@ +#include "OutputManagement.hpp" +#include <algorithm> +#include "../Compositor.hpp" + +#define LOGM PROTO::outputManagement->protoLog + +COutputManager::COutputManager(SP<CZwlrOutputManagerV1> resource_) : resource(resource_) { + if (!good()) + return; + + LOGM(LOG, "New OutputManager registered"); + + resource->setOnDestroy([this](CZwlrOutputManagerV1* r) { PROTO::outputManagement->destroyResource(this); }); + + resource->setStop([this](CZwlrOutputManagerV1* r) { stopped = true; }); + + resource->setCreateConfiguration([this](CZwlrOutputManagerV1* r, uint32_t id, uint32_t serial) { + LOGM(LOG, "Creating new configuration"); + + const auto RESOURCE = PROTO::outputManagement->m_vConfigurations.emplace_back( + std::make_shared<COutputConfiguration>(std::make_shared<CZwlrOutputConfigurationV1>(resource->client(), resource->version(), id), self.lock())); + + if (!RESOURCE->good()) { + resource->noMemory(); + PROTO::outputManagement->m_vConfigurations.pop_back(); + return; + } + }); + + // send all heads at start + for (auto& m : g_pCompositor->m_vRealMonitors) { + if (m.get() == g_pCompositor->m_pUnsafeOutput) + continue; + + LOGM(LOG, " | sending output head for {}", m->szName); + + makeAndSendNewHead(m.get()); + } + + sendDone(); +} + +bool COutputManager::good() { + return resource->resource(); +} + +void COutputManager::makeAndSendNewHead(CMonitor* pMonitor) { + if (stopped) + return; + + const auto RESOURCE = + PROTO::outputManagement->m_vHeads.emplace_back(std::make_shared<COutputHead>(std::make_shared<CZwlrOutputHeadV1>(resource->client(), resource->version(), 0), pMonitor)); + + if (!RESOURCE->good()) { + resource->noMemory(); + PROTO::outputManagement->m_vHeads.pop_back(); + return; + } + + heads.push_back(RESOURCE); + + resource->sendHead(RESOURCE->resource.get()); + RESOURCE->sendAllData(); +} + +void COutputManager::ensureMonitorSent(CMonitor* pMonitor) { + if (pMonitor == g_pCompositor->m_pUnsafeOutput) + return; + + for (auto& hw : heads) { + auto h = hw.lock(); + + if (!h) + continue; + + if (h->pMonitor == pMonitor) + return; + } + + makeAndSendNewHead(pMonitor); + + sendDone(); +} + +void COutputManager::sendDone() { + resource->sendDone(wl_display_next_serial(g_pCompositor->m_sWLDisplay)); +} + +COutputHead::COutputHead(SP<CZwlrOutputHeadV1> resource_, CMonitor* pMonitor_) : resource(resource_), pMonitor(pMonitor_) { + if (!good()) + return; + + resource->setRelease([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); + + listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any d) { + resource->sendFinished(); + + for (auto& mw : modes) { + auto m = mw.lock(); + + if (!m) + continue; + + m->resource->sendFinished(); + } + + pMonitor = nullptr; + }); + + listeners.monitorModeChange = pMonitor->events.modeChanged.registerListener([this](std::any d) { updateMode(); }); +} + +bool COutputHead::good() { + return resource->resource(); +} + +void COutputHead::sendAllData() { + const auto VERSION = resource->version(); + + resource->sendName(pMonitor->szName.c_str()); + resource->sendDescription(pMonitor->szDescription.c_str()); + if (pMonitor->output->phys_width > 0 && pMonitor->output->phys_height > 0) + resource->sendPhysicalSize(pMonitor->output->phys_width, pMonitor->output->phys_height); + resource->sendEnabled(pMonitor->m_bEnabled); + + if (pMonitor->m_bEnabled) { + resource->sendPosition(pMonitor->vecPosition.x, pMonitor->vecPosition.y); + resource->sendTransform(pMonitor->transform); + resource->sendScale(wl_fixed_from_double(pMonitor->scale)); + } + + if (pMonitor->output->make && VERSION >= 2) + resource->sendMake(pMonitor->output->make); + if (pMonitor->output->model && VERSION >= 2) + resource->sendModel(pMonitor->output->model); + if (pMonitor->output->serial && VERSION >= 2) + resource->sendSerialNumber(pMonitor->output->serial); + + if (VERSION >= 4) + resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); + + // send all available modes + + if (modes.empty()) { + if (!wl_list_empty(&pMonitor->output->modes)) { + wlr_output_mode* mode; + + wl_list_for_each(mode, &pMonitor->output->modes, link) { + makeAndSendNewMode(mode); + } + } else + makeAndSendNewMode(nullptr); + } + + // send current mode + if (pMonitor->m_bEnabled) { + for (auto& mw : modes) { + auto m = mw.lock(); + + if (!m) + continue; + + if (m->mode == pMonitor->currentMode) { + if (m->mode) + LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh); + else + LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName); + resource->sendCurrentMode(m->resource->resource()); + break; + } + } + } +} + +void COutputHead::updateMode() { + resource->sendEnabled(pMonitor->m_bEnabled); + + if (pMonitor->m_bEnabled) { + resource->sendPosition(pMonitor->vecPosition.x, pMonitor->vecPosition.y); + resource->sendTransform(pMonitor->transform); + resource->sendScale(wl_fixed_from_double(pMonitor->scale)); + } + + if (resource->version() >= 4) + resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); + + if (pMonitor->m_bEnabled) { + for (auto& mw : modes) { + auto m = mw.lock(); + + if (!m) + continue; + + if (m->mode == pMonitor->currentMode) { + if (m->mode) + LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh); + else + LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName); + resource->sendCurrentMode(m->resource->resource()); + break; + } + } + } +} + +void COutputHead::makeAndSendNewMode(wlr_output_mode* mode) { + const auto RESOURCE = + PROTO::outputManagement->m_vModes.emplace_back(std::make_shared<COutputMode>(std::make_shared<CZwlrOutputModeV1>(resource->client(), resource->version(), 0), mode)); + + if (!RESOURCE->good()) { + resource->noMemory(); + PROTO::outputManagement->m_vModes.pop_back(); + return; + } + + modes.push_back(RESOURCE); + resource->sendMode(RESOURCE->resource.get()); + RESOURCE->sendAllData(); +} + +CMonitor* COutputHead::monitor() { + return pMonitor; +} + +COutputMode::COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_) : resource(resource_), mode(mode_) { + if (!good()) + return; + + resource->setRelease([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); }); +} + +void COutputMode::sendAllData() { + if (!mode) + return; + + LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", mode->width, mode->height, mode->refresh, mode->preferred); + + resource->sendSize(mode->width, mode->height); + if (mode->refresh > 0) + resource->sendRefresh(mode->refresh); + if (mode->preferred) + resource->sendPreferred(); +} + +bool COutputMode::good() { + return resource->resource(); +} + +wlr_output_mode* COutputMode::getMode() { + return mode; +} + +COutputConfiguration::COutputConfiguration(SP<CZwlrOutputConfigurationV1> resource_, SP<COutputManager> owner_) : resource(resource_), owner(owner_) { + if (!good()) + return; + + resource->setDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); }); + + resource->setEnableHead([this](CZwlrOutputConfigurationV1* r, uint32_t id, wl_resource* outputHead) { + const auto HEAD = PROTO::outputManagement->headFromResource(outputHead); + + if (!HEAD) { + LOGM(ERR, "No head in setEnableHead??"); + return; + } + + const auto PMONITOR = HEAD->monitor(); + + if (!PMONITOR) { + LOGM(ERR, "No monitor in setEnableHead??"); + return; + } + + const auto RESOURCE = PROTO::outputManagement->m_vConfigurationHeads.emplace_back( + std::make_shared<COutputConfigurationHead>(std::make_shared<CZwlrOutputConfigurationHeadV1>(resource->client(), resource->version(), id), PMONITOR)); + + if (!RESOURCE->good()) { + resource->noMemory(); + PROTO::outputManagement->m_vConfigurationHeads.pop_back(); + return; + } + + heads.push_back(RESOURCE); + + LOGM(LOG, "enableHead on {}. For now, doing nothing. Waiting for apply().", PMONITOR->szName); + }); + + resource->setDisableHead([this](CZwlrOutputConfigurationV1* r, wl_resource* outputHead) { + const auto HEAD = PROTO::outputManagement->headFromResource(outputHead); + + if (!HEAD) { + LOGM(ERR, "No head in setDisableHead??"); + return; + } + + const auto PMONITOR = HEAD->monitor(); + + if (!PMONITOR) { + LOGM(ERR, "No monitor in setDisableHead??"); + return; + } + + LOGM(LOG, "disableHead on {}", PMONITOR->szName); + + PMONITOR->activeMonitorRule.disabled = true; + g_pHyprRenderer->applyMonitorRule(PMONITOR, &PMONITOR->activeMonitorRule, false); + }); + + resource->setTest([this](CZwlrOutputConfigurationV1* r) { + const auto SUCCESS = applyTestConfiguration(true); + + if (SUCCESS) + resource->sendSucceeded(); + else + resource->sendFailed(); + }); + + resource->setApply([this](CZwlrOutputConfigurationV1* r) { + const auto SUCCESS = applyTestConfiguration(false); + + if (SUCCESS) + resource->sendSucceeded(); + else + resource->sendFailed(); + + owner.lock()->sendDone(); + }); +} + +bool COutputConfiguration::good() { + return resource->resource(); +} + +bool COutputConfiguration::applyTestConfiguration(bool test) { + if (test) { + LOGM(WARN, "TODO: STUB: applyTestConfiguration for test not implemented, returning true."); + return true; + } + + LOGM(LOG, "Applying configuration"); + + for (auto& headw : heads) { + auto head = headw.lock(); + + if (!head) + continue; + + const auto PMONITOR = head->pMonitor; + + if (!PMONITOR) + continue; + + LOGM(LOG, "Applying config for monitor {}", PMONITOR->szName); + + SMonitorRule newRule = PMONITOR->activeMonitorRule; + newRule.name = PMONITOR->szName; + + if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) { + newRule.resolution = {head->state.mode.lock()->getMode()->width, head->state.mode.lock()->getMode()->height}; + newRule.refreshRate = head->state.mode.lock()->getMode()->refresh / 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; + } + + if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION) + newRule.offset = head->state.position; + + if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) + newRule.vrr = head->state.adaptiveSync; + + if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE) + newRule.scale = head->state.scale; + + if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM) + newRule.transform = head->state.transform; + + // reset properties for next set. + head->committedProperties = 0; + + g_pConfigManager->appendMonitorRule(newRule); + g_pConfigManager->m_bWantsMonitorReload = true; + } + + LOGM(LOG, "Applied configuration"); + + return true; +} + +COutputConfigurationHead::COutputConfigurationHead(SP<CZwlrOutputConfigurationHeadV1> resource_, CMonitor* pMonitor_) : resource(resource_), pMonitor(pMonitor_) { + if (!good()) + return; + + resource->setOnDestroy([this](CZwlrOutputConfigurationHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); + + listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any d) { pMonitor = nullptr; }); + + resource->setSetMode([this](CZwlrOutputConfigurationHeadV1* r, wl_resource* outputMode) { + const auto MODE = PROTO::outputManagement->modeFromResource(outputMode); + + if (!MODE || !MODE->getMode()) { + LOGM(ERR, "No mode in setMode??"); + return; + } + + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); + return; + } + + if (committedProperties & OUTPUT_HEAD_COMMITTED_MODE) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + return; + } + + committedProperties |= OUTPUT_HEAD_COMMITTED_MODE; + state.mode = MODE; + + LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->width, MODE->getMode()->height, MODE->getMode()->refresh); + }); + + resource->setSetCustomMode([this](CZwlrOutputConfigurationHeadV1* r, int32_t w, int32_t h, int32_t refresh) { + if (!pMonitor) { + LOGM(ERR, "setCustomMode on inert resource"); + return; + } + + if (committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + return; + } + + if (w <= 0 || h <= 0 || refresh <= 100) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE, "Invalid mode"); + return; + } + + committedProperties |= OUTPUT_HEAD_COMMITTED_CUSTOM_MODE; + state.customMode = {{w, h}, (uint32_t)refresh}; + + LOGM(LOG, " | configHead for {}: set custom mode to {}x{}@{}", pMonitor->szName, w, h, refresh); + }); + + resource->setSetPosition([this](CZwlrOutputConfigurationHeadV1* r, int32_t x, int32_t y) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); + return; + } + + if (committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + return; + } + + committedProperties |= OUTPUT_HEAD_COMMITTED_POSITION; + state.position = {x, y}; + + LOGM(LOG, " | configHead for {}: set pos to {}, {}", pMonitor->szName, x, y); + }); + + resource->setSetTransform([this](CZwlrOutputConfigurationHeadV1* r, int32_t transform) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); + return; + } + + if (committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + return; + } + + if (transform < 0 || transform > 7) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM, "Invalid transform"); + return; + } + + committedProperties |= OUTPUT_HEAD_COMMITTED_TRANSFORM; + state.transform = (wl_output_transform)transform; + + LOGM(LOG, " | configHead for {}: set transform to {}", pMonitor->szName, transform); + }); + + resource->setSetScale([this](CZwlrOutputConfigurationHeadV1* r, wl_fixed_t scale_) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); + return; + } + + if (committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + return; + } + + double scale = wl_fixed_to_double(scale_); + + if (scale < 0.1 || scale > 10.0) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE, "Invalid scale"); + return; + } + + committedProperties |= OUTPUT_HEAD_COMMITTED_SCALE; + state.scale = scale; + + LOGM(LOG, " | configHead for {}: set scale to {:.2f}", pMonitor->szName, scale); + }); + + resource->setSetAdaptiveSync([this](CZwlrOutputConfigurationHeadV1* r, uint32_t as) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); + return; + } + + if (committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + return; + } + + if (as > 1) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE, "Invalid adaptive sync state"); + return; + } + + committedProperties |= OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC; + state.adaptiveSync = as; + + LOGM(LOG, " | configHead for {}: set adaptiveSync to {}", pMonitor->szName, as); + }); +} + +bool COutputConfigurationHead::good() { + return resource->resource(); +} + +COutputManagementProtocol::COutputManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); }); +} + +void COutputManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(std::make_shared<COutputManager>(std::make_shared<CZwlrOutputManagerV1>(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; +} + +void COutputManagementProtocol::destroyResource(COutputManager* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void COutputManagementProtocol::destroyResource(COutputHead* resource) { + std::erase_if(m_vHeads, [&](const auto& other) { return other.get() == resource; }); +} + +void COutputManagementProtocol::destroyResource(COutputMode* resource) { + std::erase_if(m_vModes, [&](const auto& other) { return other.get() == resource; }); +} + +void COutputManagementProtocol::destroyResource(COutputConfiguration* resource) { + std::erase_if(m_vConfigurations, [&](const auto& other) { return other.get() == resource; }); +} + +void COutputManagementProtocol::destroyResource(COutputConfigurationHead* resource) { + std::erase_if(m_vConfigurationHeads, [&](const auto& other) { return other.get() == resource; }); +} + +void COutputManagementProtocol::updateAllOutputs() { + for (auto& m : g_pCompositor->m_vRealMonitors) { + for (auto& mgr : m_vManagers) { + mgr->ensureMonitorSent(m.get()); + } + } +} + +SP<COutputHead> COutputManagementProtocol::headFromResource(wl_resource* r) { + for (auto& h : m_vHeads) { + if (h->resource->resource() == r) + return h; + } + + return nullptr; +} + +SP<COutputMode> COutputManagementProtocol::modeFromResource(wl_resource* r) { + for (auto& h : m_vModes) { + if (h->resource->resource() == r) + return h; + } + + return nullptr; +} |
