Hyprland/src/protocols/OutputManagement.cpp

650 lines
23 KiB
C++
Raw Normal View History

2024-05-03 17:58:40 +01:00
#include "OutputManagement.hpp"
#include <algorithm>
#include "../Compositor.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../config/ConfigManager.hpp"
2024-05-03 17:58:40 +01:00
using namespace Aquamarine;
COutputManager::COutputManager(SP<CZwlrOutputManagerV1> resource_) : m_resource(resource_) {
if UNLIKELY (!good())
2024-05-03 17:58:40 +01:00
return;
LOGM(LOG, "New OutputManager registered");
m_resource->setOnDestroy([this](CZwlrOutputManagerV1* r) { PROTO::outputManagement->destroyResource(this); });
2024-05-03 17:58:40 +01:00
m_resource->setStop([this](CZwlrOutputManagerV1* r) { m_stopped = true; });
2024-05-03 17:58:40 +01:00
m_resource->setCreateConfiguration([this](CZwlrOutputManagerV1* r, uint32_t id, uint32_t serial) {
2024-05-03 17:58:40 +01:00
LOGM(LOG, "Creating new configuration");
const auto RESOURCE = PROTO::outputManagement->m_configurations.emplace_back(
makeShared<COutputConfiguration>(makeShared<CZwlrOutputConfigurationV1>(m_resource->client(), m_resource->version(), id), m_self.lock()));
2024-05-03 17:58:40 +01:00
if UNLIKELY (!RESOURCE->good()) {
m_resource->noMemory();
PROTO::outputManagement->m_configurations.pop_back();
2024-05-03 17:58:40 +01:00
return;
}
});
// send all heads at start
for (auto const& m : g_pCompositor->m_realMonitors) {
if (m == g_pCompositor->m_unsafeOutput)
2024-05-03 17:58:40 +01:00
continue;
LOGM(LOG, " | sending output head for {}", m->m_name);
2024-05-03 17:58:40 +01:00
makeAndSendNewHead(m);
2024-05-03 17:58:40 +01:00
}
sendDone();
}
bool COutputManager::good() {
return m_resource->resource();
2024-05-03 17:58:40 +01:00
}
void COutputManager::makeAndSendNewHead(PHLMONITOR pMonitor) {
if UNLIKELY (m_stopped)
2024-05-03 17:58:40 +01:00
return;
const auto RESOURCE =
PROTO::outputManagement->m_heads.emplace_back(makeShared<COutputHead>(makeShared<CZwlrOutputHeadV1>(m_resource->client(), m_resource->version(), 0), pMonitor));
2024-05-03 17:58:40 +01:00
if UNLIKELY (!RESOURCE->good()) {
m_resource->noMemory();
PROTO::outputManagement->m_heads.pop_back();
2024-05-03 17:58:40 +01:00
return;
}
m_heads.emplace_back(RESOURCE);
2024-05-03 17:58:40 +01:00
m_resource->sendHead(RESOURCE->m_resource.get());
2024-05-03 17:58:40 +01:00
RESOURCE->sendAllData();
}
void COutputManager::ensureMonitorSent(PHLMONITOR pMonitor) {
if (pMonitor == g_pCompositor->m_unsafeOutput)
2024-05-03 17:58:40 +01:00
return;
for (auto const& hw : m_heads) {
2024-05-03 17:58:40 +01:00
auto h = hw.lock();
if (!h)
continue;
if (h->m_monitor == pMonitor)
2024-05-03 17:58:40 +01:00
return;
}
makeAndSendNewHead(pMonitor);
sendDone();
}
void COutputManager::sendDone() {
m_resource->sendDone(wl_display_next_serial(g_pCompositor->m_wlDisplay));
2024-05-03 17:58:40 +01:00
}
COutputHead::COutputHead(SP<CZwlrOutputHeadV1> resource_, PHLMONITOR pMonitor_) : m_resource(resource_), m_monitor(pMonitor_) {
if UNLIKELY (!good())
2024-05-03 17:58:40 +01:00
return;
m_resource->setRelease([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); });
2024-05-03 17:58:40 +01:00
m_listeners.monitorDestroy = m_monitor->m_events.destroy.listen([this] {
m_resource->sendFinished();
2024-05-03 17:58:40 +01:00
for (auto const& mw : m_modes) {
2024-05-03 17:58:40 +01:00
auto m = mw.lock();
if (!m)
continue;
m->m_resource->sendFinished();
2024-05-03 17:58:40 +01:00
}
m_monitor.reset();
for (auto const& m : PROTO::outputManagement->m_managers) {
m->sendDone();
}
2024-05-03 17:58:40 +01:00
});
m_listeners.monitorModeChange = m_monitor->m_events.modeChanged.listen([this] { updateMode(); });
2024-05-03 17:58:40 +01:00
}
bool COutputHead::good() {
return m_resource->resource();
2024-05-03 17:58:40 +01:00
}
void COutputHead::sendAllData() {
const auto VERSION = m_resource->version();
m_resource->sendName(m_monitor->m_name.c_str());
m_resource->sendDescription(m_monitor->m_description.c_str());
if (m_monitor->m_output->physicalSize.x > 0 && m_monitor->m_output->physicalSize.y > 0)
m_resource->sendPhysicalSize(m_monitor->m_output->physicalSize.x, m_monitor->m_output->physicalSize.y);
m_resource->sendEnabled(m_monitor->m_enabled);
if (m_monitor->m_enabled) {
m_resource->sendPosition(m_monitor->m_position.x, m_monitor->m_position.y);
m_resource->sendTransform(m_monitor->m_transform);
m_resource->sendScale(wl_fixed_from_double(m_monitor->m_scale));
2024-05-03 17:58:40 +01:00
}
if (!m_monitor->m_output->make.empty() && VERSION >= 2)
m_resource->sendMake(m_monitor->m_output->make.c_str());
if (!m_monitor->m_output->model.empty() && VERSION >= 2)
m_resource->sendModel(m_monitor->m_output->model.c_str());
if (!m_monitor->m_output->serial.empty() && VERSION >= 2)
m_resource->sendSerialNumber(m_monitor->m_output->serial.c_str());
2024-05-03 17:58:40 +01:00
if (VERSION >= 4)
m_resource->sendAdaptiveSync(m_monitor->m_vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED);
2024-05-03 17:58:40 +01:00
// send all available modes
if (m_modes.empty()) {
if (!m_monitor->m_output->modes.empty()) {
for (auto const& m : m_monitor->m_output->modes) {
makeAndSendNewMode(m);
2024-05-03 17:58:40 +01:00
}
} else if (m_monitor->m_output->state->state().customMode) {
makeAndSendNewMode(m_monitor->m_output->state->state().customMode);
2024-05-03 17:58:40 +01:00
} else
makeAndSendNewMode(nullptr);
}
// send current mode
if (m_monitor->m_enabled) {
for (auto const& mw : m_modes) {
2024-05-03 17:58:40 +01:00
auto m = mw.lock();
if (!m)
continue;
if (m->m_mode == m_monitor->m_output->state->state().mode) {
if (m->m_mode)
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", m_monitor->m_name, m->m_mode->pixelSize.x, m->m_mode->pixelSize.y, m->m_mode->refreshRate);
2024-05-03 17:58:40 +01:00
else
LOGM(LOG, " | sending current mode for {}: null (fake)", m_monitor->m_name);
m_resource->sendCurrentMode(m->m_resource.get());
2024-05-03 17:58:40 +01:00
break;
}
}
}
}
void COutputHead::updateMode() {
m_resource->sendEnabled(m_monitor->m_enabled);
2024-05-03 17:58:40 +01:00
if (m_monitor->m_enabled) {
m_resource->sendPosition(m_monitor->m_position.x, m_monitor->m_position.y);
m_resource->sendTransform(m_monitor->m_transform);
m_resource->sendScale(wl_fixed_from_double(m_monitor->m_scale));
2024-05-03 17:58:40 +01:00
}
if (m_resource->version() >= 4)
m_resource->sendAdaptiveSync(m_monitor->m_vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED);
2024-05-03 17:58:40 +01:00
if (m_monitor->m_enabled) {
for (auto const& mw : m_modes) {
2024-05-03 17:58:40 +01:00
auto m = mw.lock();
if (!m)
continue;
if (m->m_mode == m_monitor->m_currentMode) {
if (m->m_mode)
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", m_monitor->m_name, m->m_mode->pixelSize.x, m->m_mode->pixelSize.y, m->m_mode->refreshRate);
2024-05-03 17:58:40 +01:00
else
LOGM(LOG, " | sending current mode for {}: null (fake)", m_monitor->m_name);
m_resource->sendCurrentMode(m->m_resource.get());
2024-05-03 17:58:40 +01:00
break;
}
}
}
}
void COutputHead::makeAndSendNewMode(SP<Aquamarine::SOutputMode> mode) {
const auto RESOURCE =
PROTO::outputManagement->m_modes.emplace_back(makeShared<COutputMode>(makeShared<CZwlrOutputModeV1>(m_resource->client(), m_resource->version(), 0), mode));
2024-05-03 17:58:40 +01:00
if UNLIKELY (!RESOURCE->good()) {
m_resource->noMemory();
PROTO::outputManagement->m_modes.pop_back();
2024-05-03 17:58:40 +01:00
return;
}
m_modes.emplace_back(RESOURCE);
m_resource->sendMode(RESOURCE->m_resource.get());
2024-05-03 17:58:40 +01:00
RESOURCE->sendAllData();
}
PHLMONITOR COutputHead::monitor() {
return m_monitor.lock();
2024-05-03 17:58:40 +01:00
}
COutputMode::COutputMode(SP<CZwlrOutputModeV1> resource_, SP<Aquamarine::SOutputMode> mode_) : m_resource(resource_), m_mode(mode_) {
if UNLIKELY (!good())
2024-05-03 17:58:40 +01:00
return;
m_resource->setRelease([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); });
2024-05-03 17:58:40 +01:00
}
void COutputMode::sendAllData() {
if (!m_mode)
2024-05-03 17:58:40 +01:00
return;
LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", m_mode->pixelSize.x, m_mode->pixelSize.y, m_mode->refreshRate, m_mode->preferred);
2024-05-03 17:58:40 +01:00
m_resource->sendSize(m_mode->pixelSize.x, m_mode->pixelSize.y);
if (m_mode->refreshRate > 0)
m_resource->sendRefresh(m_mode->refreshRate);
if (m_mode->preferred)
m_resource->sendPreferred();
2024-05-03 17:58:40 +01:00
}
bool COutputMode::good() {
return m_resource->resource();
2024-05-03 17:58:40 +01:00
}
SP<Aquamarine::SOutputMode> COutputMode::getMode() {
return m_mode.lock();
2024-05-03 17:58:40 +01:00
}
COutputConfiguration::COutputConfiguration(SP<CZwlrOutputConfigurationV1> resource_, SP<COutputManager> owner_) : m_resource(resource_), m_owner(owner_) {
if UNLIKELY (!good())
2024-05-03 17:58:40 +01:00
return;
m_resource->setDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); });
2024-05-03 17:58:40 +01:00
m_resource->setEnableHead([this](CZwlrOutputConfigurationV1* r, uint32_t id, wl_resource* outputHead) {
2024-05-03 17:58:40 +01:00
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_configurationHeads.emplace_back(
makeShared<COutputConfigurationHead>(makeShared<CZwlrOutputConfigurationHeadV1>(m_resource->client(), m_resource->version(), id), PMONITOR));
2024-05-03 17:58:40 +01:00
if UNLIKELY (!RESOURCE->good()) {
m_resource->noMemory();
PROTO::outputManagement->m_configurationHeads.pop_back();
2024-05-03 17:58:40 +01:00
return;
}
m_heads.emplace_back(RESOURCE);
2024-05-03 17:58:40 +01:00
LOGM(LOG, "enableHead on {}. For now, doing nothing. Waiting for apply().", PMONITOR->m_name);
2024-05-03 17:58:40 +01:00
});
m_resource->setDisableHead([this](CZwlrOutputConfigurationV1* r, wl_resource* outputHead) {
2024-05-03 17:58:40 +01:00
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->m_name);
2024-05-03 17:58:40 +01:00
SWlrManagerSavedOutputState newState;
if (m_owner->m_monitorStates.contains(PMONITOR->m_name))
newState = m_owner->m_monitorStates.at(PMONITOR->m_name);
newState.enabled = false;
g_pConfigManager->m_wantsMonitorReload = true;
m_owner->m_monitorStates[PMONITOR->m_name] = newState;
2024-05-03 17:58:40 +01:00
});
m_resource->setTest([this](CZwlrOutputConfigurationV1* r) {
2024-05-03 17:58:40 +01:00
const auto SUCCESS = applyTestConfiguration(true);
if (SUCCESS)
m_resource->sendSucceeded();
2024-05-03 17:58:40 +01:00
else
m_resource->sendFailed();
2024-05-03 17:58:40 +01:00
});
m_resource->setApply([this](CZwlrOutputConfigurationV1* r) {
2024-05-03 17:58:40 +01:00
const auto SUCCESS = applyTestConfiguration(false);
if (SUCCESS)
m_resource->sendSucceeded();
2024-05-03 17:58:40 +01:00
else
m_resource->sendFailed();
2024-05-03 17:58:40 +01:00
m_owner->sendDone();
2024-05-03 17:58:40 +01:00
});
}
bool COutputConfiguration::good() {
return m_resource->resource();
2024-05-03 17:58:40 +01:00
}
bool COutputConfiguration::applyTestConfiguration(bool test) {
if (test) {
LOGM(WARN, "TODO: STUB: applyTestConfiguration for test not implemented, returning true.");
return true;
}
LOGM(LOG, "Applying configuration");
if (!m_owner) {
LOGM(ERR, "applyTestConfiguration: no owner?!");
return false;
}
for (auto const& headw : m_heads) {
2024-05-03 17:58:40 +01:00
auto head = headw.lock();
if (!head)
continue;
const auto PMONITOR = head->m_monitor;
2024-05-03 17:58:40 +01:00
if (!PMONITOR)
continue;
LOGM(LOG, "Saving config for monitor {}", PMONITOR->m_name);
SWlrManagerSavedOutputState newState;
if (m_owner->m_monitorStates.contains(PMONITOR->m_name))
newState = m_owner->m_monitorStates.at(PMONITOR->m_name);
2024-05-03 17:58:40 +01:00
newState.enabled = true;
2024-05-03 17:58:40 +01:00
if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) {
newState.resolution = head->m_state.mode->getMode()->pixelSize;
newState.refresh = head->m_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->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) {
newState.resolution = head->m_state.customMode.size;
newState.refresh = head->m_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);
2024-05-03 17:58:40 +01:00
}
if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION) {
newState.position = head->m_state.position;
newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION;
LOGM(LOG, " > Position: {:.0f}, {:.0f}", head->m_state.position.x, head->m_state.position.y);
}
2024-05-03 17:58:40 +01:00
if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) {
newState.adaptiveSync = head->m_state.adaptiveSync;
newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC;
LOGM(LOG, " > vrr: {}", newState.adaptiveSync);
}
2024-05-03 17:58:40 +01:00
if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE) {
newState.scale = head->m_state.scale;
newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE;
LOGM(LOG, " > scale: {:.2f}", newState.scale);
}
2024-05-03 17:58:40 +01:00
if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM) {
newState.transform = head->m_state.transform;
newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM;
LOGM(LOG, " > transform: {}", (uint8_t)newState.transform);
}
2024-05-03 17:58:40 +01:00
// reset properties for next set.
head->m_state.committedProperties = 0;
2024-05-03 17:58:40 +01:00
g_pConfigManager->m_wantsMonitorReload = true;
m_owner->m_monitorStates[PMONITOR->m_name] = newState;
2024-05-03 17:58:40 +01:00
}
LOGM(LOG, "Saved configuration");
2024-05-03 17:58:40 +01:00
return true;
}
COutputConfigurationHead::COutputConfigurationHead(SP<CZwlrOutputConfigurationHeadV1> resource_, PHLMONITOR pMonitor_) : m_resource(resource_), m_monitor(pMonitor_) {
if UNLIKELY (!good())
2024-05-03 17:58:40 +01:00
return;
m_resource->setOnDestroy([this](CZwlrOutputConfigurationHeadV1* r) { PROTO::outputManagement->destroyResource(this); });
2024-05-03 17:58:40 +01:00
m_resource->setSetMode([this](CZwlrOutputConfigurationHeadV1* r, wl_resource* outputMode) {
2024-05-03 17:58:40 +01:00
const auto MODE = PROTO::outputManagement->modeFromResource(outputMode);
if (!MODE || !MODE->getMode()) {
LOGM(ERR, "No mode in setMode??");
return;
}
if (!m_monitor) {
2024-05-03 17:58:40 +01:00
LOGM(ERR, "setMode on inert resource");
return;
}
if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_MODE) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
2024-05-03 17:58:40 +01:00
return;
}
m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_MODE;
m_state.mode = MODE;
2024-05-03 17:58:40 +01:00
LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", m_monitor->m_name, MODE->getMode()->pixelSize.x, MODE->getMode()->pixelSize.y, MODE->getMode()->refreshRate);
2024-05-03 17:58:40 +01:00
});
m_resource->setSetCustomMode([this](CZwlrOutputConfigurationHeadV1* r, int32_t w, int32_t h, int32_t refresh) {
if (!m_monitor) {
2024-05-03 17:58:40 +01:00
LOGM(ERR, "setCustomMode on inert resource");
return;
}
if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
2024-05-03 17:58:40 +01:00
return;
}
if (w <= 0 || h <= 0 || refresh < 0) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE, "Invalid mode");
2024-05-03 17:58:40 +01:00
return;
}
if (refresh == 0) {
LOGM(LOG, " | configHead for {}: refreshRate 0, using old refresh rate of {:.2f}Hz", m_monitor->m_name, m_monitor->m_refreshRate);
refresh = std::round(m_monitor->m_refreshRate * 1000.F);
}
m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_CUSTOM_MODE;
m_state.customMode = {{w, h}, sc<uint32_t>(refresh)};
2024-05-03 17:58:40 +01:00
LOGM(LOG, " | configHead for {}: set custom mode to {}x{}@{}", m_monitor->m_name, w, h, refresh);
2024-05-03 17:58:40 +01:00
});
m_resource->setSetPosition([this](CZwlrOutputConfigurationHeadV1* r, int32_t x, int32_t y) {
if (!m_monitor) {
2024-05-03 17:58:40 +01:00
LOGM(ERR, "setMode on inert resource");
return;
}
if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
2024-05-03 17:58:40 +01:00
return;
}
m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_POSITION;
m_state.position = {x, y};
2024-05-03 17:58:40 +01:00
LOGM(LOG, " | configHead for {}: set pos to {}, {}", m_monitor->m_name, x, y);
2024-05-03 17:58:40 +01:00
});
m_resource->setSetTransform([this](CZwlrOutputConfigurationHeadV1* r, int32_t transform) {
if (!m_monitor) {
2024-05-03 17:58:40 +01:00
LOGM(ERR, "setMode on inert resource");
return;
}
if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
2024-05-03 17:58:40 +01:00
return;
}
if (transform < 0 || transform > 7) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM, "Invalid transform");
2024-05-03 17:58:40 +01:00
return;
}
m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_TRANSFORM;
m_state.transform = sc<wl_output_transform>(transform);
2024-05-03 17:58:40 +01:00
LOGM(LOG, " | configHead for {}: set transform to {}", m_monitor->m_name, transform);
2024-05-03 17:58:40 +01:00
});
m_resource->setSetScale([this](CZwlrOutputConfigurationHeadV1* r, wl_fixed_t scale_) {
if (!m_monitor) {
2024-05-03 17:58:40 +01:00
LOGM(ERR, "setMode on inert resource");
return;
}
if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
2024-05-03 17:58:40 +01:00
return;
}
double scale = wl_fixed_to_double(scale_);
if (scale < 0.1 || scale > 10.0) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE, "Invalid scale");
2024-05-03 17:58:40 +01:00
return;
}
m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_SCALE;
m_state.scale = scale;
2024-05-03 17:58:40 +01:00
LOGM(LOG, " | configHead for {}: set scale to {:.2f}", m_monitor->m_name, scale);
2024-05-03 17:58:40 +01:00
});
m_resource->setSetAdaptiveSync([this](CZwlrOutputConfigurationHeadV1* r, uint32_t as) {
if (!m_monitor) {
2024-05-03 17:58:40 +01:00
LOGM(ERR, "setMode on inert resource");
return;
}
if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
2024-05-03 17:58:40 +01:00
return;
}
if (as > 1) {
m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE, "Invalid adaptive sync state");
2024-05-03 17:58:40 +01:00
return;
}
m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC;
m_state.adaptiveSync = as;
2024-05-03 17:58:40 +01:00
LOGM(LOG, " | configHead for {}: set adaptiveSync to {}", m_monitor->m_name, as);
2024-05-03 17:58:40 +01:00
});
}
bool COutputConfigurationHead::good() {
return m_resource->resource();
2024-05-03 17:58:40 +01:00
}
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_managers.emplace_back(makeShared<COutputManager>(makeShared<CZwlrOutputManagerV1>(client, ver, id)));
2024-05-03 17:58:40 +01:00
if UNLIKELY (!RESOURCE->good()) {
2024-05-03 17:58:40 +01:00
wl_client_post_no_memory(client);
m_managers.pop_back();
2024-05-03 17:58:40 +01:00
return;
}
RESOURCE->m_self = RESOURCE;
2024-05-03 17:58:40 +01:00
}
void COutputManagementProtocol::destroyResource(COutputManager* resource) {
std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; });
2024-05-03 17:58:40 +01:00
}
void COutputManagementProtocol::destroyResource(COutputHead* resource) {
std::erase_if(m_heads, [&](const auto& other) { return other.get() == resource; });
2024-05-03 17:58:40 +01:00
}
void COutputManagementProtocol::destroyResource(COutputMode* resource) {
std::erase_if(m_modes, [&](const auto& other) { return other.get() == resource; });
2024-05-03 17:58:40 +01:00
}
void COutputManagementProtocol::destroyResource(COutputConfiguration* resource) {
std::erase_if(m_configurations, [&](const auto& other) { return other.get() == resource; });
2024-05-03 17:58:40 +01:00
}
void COutputManagementProtocol::destroyResource(COutputConfigurationHead* resource) {
std::erase_if(m_configurationHeads, [&](const auto& other) { return other.get() == resource; });
2024-05-03 17:58:40 +01:00
}
void COutputManagementProtocol::updateAllOutputs() {
for (auto const& m : g_pCompositor->m_realMonitors) {
for (auto const& mgr : m_managers) {
mgr->ensureMonitorSent(m);
2024-05-03 17:58:40 +01:00
}
}
}
SP<COutputHead> COutputManagementProtocol::headFromResource(wl_resource* r) {
for (auto const& h : m_heads) {
if (h->m_resource->resource() == r)
2024-05-03 17:58:40 +01:00
return h;
}
return nullptr;
}
SP<COutputMode> COutputManagementProtocol::modeFromResource(wl_resource* r) {
for (auto const& h : m_modes) {
if (h->m_resource->resource() == r)
2024-05-03 17:58:40 +01:00
return h;
}
return nullptr;
}
SP<SWlrManagerSavedOutputState> COutputManagementProtocol::getOutputStateFor(PHLMONITOR pMonitor) {
for (auto const& m : m_managers) {
if (!m->m_monitorStates.contains(pMonitor->m_name))
continue;
return makeShared<SWlrManagerSavedOutputState>(m->m_monitorStates.at(pMonitor->m_name));
}
return nullptr;
}