output-management: move to new impl

This commit is contained in:
Vaxry 2024-05-03 17:58:40 +01:00
parent d5bf15387a
commit 8a2269272b
17 changed files with 1386 additions and 139 deletions

View file

@ -243,8 +243,6 @@ void CCompositor::initServer() {
m_sWLRServerDecoMgr = wlr_server_decoration_manager_create(m_sWLDisplay);
wlr_server_decoration_manager_set_default_mode(m_sWLRServerDecoMgr, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend);
if (!m_sWRLDRMLeaseMgr) {
Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager");
@ -300,9 +298,6 @@ void CCompositor::initAllSignals() {
addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_set_primary_selection, &Events::listen_requestSetPrimarySel, &m_sSeat, "Seat");
addWLSignal(&m_sWLRLayerShell->events.new_surface, &Events::listen_newLayerSurface, m_sWLRLayerShell, "LayerShell");
addWLSignal(&m_sWLROutputLayout->events.change, &Events::listen_change, m_sWLROutputLayout, "OutputLayout");
addWLSignal(&m_sWLROutputMgr->events.apply, &Events::listen_outputMgrApply, m_sWLROutputMgr, "OutputMgr");
addWLSignal(&m_sWLROutputMgr->events.test, &Events::listen_outputMgrTest, m_sWLROutputMgr, "OutputMgr");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
if (m_sWRLDRMLeaseMgr)
@ -340,9 +335,6 @@ void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_requestSetSel);
removeWLSignal(&Events::listen_requestSetPrimarySel);
removeWLSignal(&Events::listen_newLayerSurface);
removeWLSignal(&Events::listen_change);
removeWLSignal(&Events::listen_outputMgrApply);
removeWLSignal(&Events::listen_outputMgrTest);
removeWLSignal(&Events::listen_RendererDestroy);
if (m_sWRLDRMLeaseMgr)

View file

@ -55,7 +55,6 @@ class CCompositor {
wlr_layer_shell_v1* m_sWLRLayerShell;
wlr_xdg_shell* m_sWLRXDGShell;
wlr_cursor* m_sWLRCursor;
wlr_output_manager_v1* m_sWLROutputMgr;
wlr_presentation* m_sWLRPresentation;
wlr_egl* m_sWLREGL;
int m_iDRMFD;

View file

@ -16,6 +16,7 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <ranges>
extern "C" char** environ;
@ -954,7 +955,7 @@ std::string CConfigManager::getDeviceString(const std::string& dev, const std::s
}
SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) {
for (auto& r : m_dMonitorRules) {
for (auto& r : m_dMonitorRules | std::views::reverse) {
if (PMONITOR.matchesStaticSelector(r.name)) {
return r;
}
@ -1236,6 +1237,10 @@ void CConfigManager::dispatchExecOnce() {
g_pCompositor->performUserChecks();
}
void CConfigManager::appendMonitorRule(const SMonitorRule& r) {
m_dMonitorRules.emplace_back(r);
}
void CConfigManager::performMonitorReload() {
bool overAgain = false;

View file

@ -127,6 +127,7 @@ class CConfigManager {
void dispatchExecOnce();
void performMonitorReload();
void appendMonitorRule(const SMonitorRule&);
bool m_bWantsMonitorReload = false;
bool m_bForceReload = false;
bool m_bNoMonitorReload = false;

View file

@ -65,10 +65,6 @@ namespace Events {
LISTENER(requestSetSel);
LISTENER(requestSetPrimarySel);
// outputMgr
LISTENER(outputMgrApply);
LISTENER(outputMgrTest);
// Monitor part 2 the sequel
DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorDestroy);

View file

@ -16,16 +16,6 @@
// //
// ------------------------------ //
void Events::listener_outputMgrApply(wl_listener* listener, void* data) {
const auto CONFIG = (wlr_output_configuration_v1*)data;
g_pHyprRenderer->outputMgrApplyTest(CONFIG, false);
}
void Events::listener_outputMgrTest(wl_listener* listener, void* data) {
const auto CONFIG = (wlr_output_configuration_v1*)data;
g_pHyprRenderer->outputMgrApplyTest(CONFIG, true);
}
void Events::listener_leaseRequest(wl_listener* listener, void* data) {
const auto REQUEST = (wlr_drm_lease_request_v1*)data;
struct wlr_drm_lease_v1* lease = wlr_drm_lease_request_v1_grant(REQUEST);

View file

@ -16,50 +16,6 @@
// //
// --------------------------------------------------------- //
void Events::listener_change(wl_listener* listener, void* data) {
// layout got changed, let's update monitors.
const auto CONFIG = wlr_output_configuration_v1_create();
if (!CONFIG)
return;
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (!m->output)
continue;
if (g_pCompositor->m_pUnsafeOutput == m.get())
continue;
const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
CBox BOX;
wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, BOX.pWlr());
BOX.applyFromWlr();
//m->vecSize.x = BOX.width;
// m->vecSize.y = BOX.height;
m->vecPosition.x = BOX.x;
m->vecPosition.y = BOX.y;
CONFIGHEAD->state.enabled = m->output->enabled;
CONFIGHEAD->state.mode = m->output->current_mode;
if (!m->output->current_mode) {
CONFIGHEAD->state.custom_mode = {
m->output->width,
m->output->height,
m->output->refresh,
};
}
CONFIGHEAD->state.x = m->vecPosition.x;
CONFIGHEAD->state.y = m->vecPosition.y;
CONFIGHEAD->state.transform = m->transform;
CONFIGHEAD->state.scale = m->scale;
CONFIGHEAD->state.adaptive_sync_enabled = m->vrrActive;
}
wlr_output_manager_v1_set_configuration(g_pCompositor->m_sWLROutputMgr, CONFIG);
}
static void checkDefaultCursorWarp(std::shared_ptr<CMonitor>* PNEWMONITORWRAP, std::string monitorName) {
const auto PNEWMONITOR = PNEWMONITORWRAP->get();

View file

@ -96,6 +96,7 @@ class CMonitor {
float xwaylandScale = 1.f;
std::array<float, 9> projMatrix = {0};
std::optional<Vector2D> forceSize;
wlr_output_mode* currentMode = nullptr;
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.

View file

@ -22,6 +22,7 @@
#include "../protocols/InputMethodV2.hpp"
#include "../protocols/VirtualKeyboard.hpp"
#include "../protocols/VirtualPointer.hpp"
#include "../protocols/OutputManagement.hpp"
CProtocolManager::CProtocolManager() {
@ -47,6 +48,7 @@ CProtocolManager::CProtocolManager() {
PROTO::ime = std::make_unique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
PROTO::virtualKeyboard = std::make_unique<CVirtualKeyboardProtocol>(&zwp_virtual_keyboard_manager_v1_interface, 1, "VirtualKeyboard");
PROTO::virtualPointer = std::make_unique<CVirtualPointerProtocol>(&zwlr_virtual_pointer_manager_v1_interface, 2, "VirtualPointer");
PROTO::outputManagement = std::make_unique<COutputManagementProtocol>(&zwlr_output_manager_v1_interface, 4, "OutputManagement");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.

View file

@ -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;
}

View file

@ -0,0 +1,166 @@
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "wlr-output-management-unstable-v1.hpp"
#include "../helpers/signal/Listener.hpp"
class CMonitor;
class COutputHead;
class COutputMode;
class COutputManager {
public:
COutputManager(SP<CZwlrOutputManagerV1> resource_);
bool good();
void ensureMonitorSent(CMonitor* pMonitor);
void sendDone();
private:
SP<CZwlrOutputManagerV1> resource;
bool stopped = false;
WP<COutputManager> self;
std::vector<WP<COutputHead>> heads;
void makeAndSendNewHead(CMonitor* pMonitor);
friend class COutputManagementProtocol;
};
class COutputMode {
public:
COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_);
bool good();
wlr_output_mode* getMode();
void sendAllData();
private:
SP<CZwlrOutputModeV1> resource;
wlr_output_mode* mode = nullptr;
friend class COutputHead;
friend class COutputManagementProtocol;
};
class COutputHead {
public:
COutputHead(SP<CZwlrOutputHeadV1> resource_, CMonitor* pMonitor_);
bool good();
void sendAllData(); // this has to be separate as we need to send the head first, then set the data
void updateMode();
CMonitor* monitor();
private:
SP<CZwlrOutputHeadV1> resource;
CMonitor* pMonitor = nullptr;
void makeAndSendNewMode(wlr_output_mode* mode);
void sendCurrentMode();
std::vector<WP<COutputMode>> modes;
struct {
CHyprSignalListener monitorDestroy;
CHyprSignalListener monitorModeChange;
} listeners;
friend class COutputManager;
friend class COutputManagementProtocol;
};
class COutputConfigurationHead {
public:
COutputConfigurationHead(SP<CZwlrOutputConfigurationHeadV1> resource_, CMonitor* pMonitor_);
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<COutputMode> 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;
private:
SP<CZwlrOutputConfigurationHeadV1> resource;
CMonitor* pMonitor = nullptr;
struct {
CHyprSignalListener monitorDestroy;
} listeners;
friend class COutputConfiguration;
};
class COutputConfiguration {
public:
COutputConfiguration(SP<CZwlrOutputConfigurationV1> resource_, SP<COutputManager> owner_);
bool good();
private:
SP<CZwlrOutputConfigurationV1> resource;
std::vector<WP<COutputConfigurationHead>> heads;
WP<COutputManager> owner;
bool applyTestConfiguration(bool test);
};
class COutputManagementProtocol : public IWaylandProtocol {
public:
COutputManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
private:
void destroyResource(COutputManager* resource);
void destroyResource(COutputHead* resource);
void destroyResource(COutputMode* resource);
void destroyResource(COutputConfiguration* resource);
void destroyResource(COutputConfigurationHead* resource);
void updateAllOutputs();
//
std::vector<SP<COutputManager>> m_vManagers;
std::vector<SP<COutputHead>> m_vHeads;
std::vector<SP<COutputMode>> m_vModes;
std::vector<SP<COutputConfiguration>> m_vConfigurations;
std::vector<SP<COutputConfigurationHead>> m_vConfigurationHeads;
SP<COutputHead> headFromResource(wl_resource* r);
SP<COutputMode> modeFromResource(wl_resource* r);
friend class COutputManager;
friend class COutputHead;
friend class COutputMode;
friend class COutputConfiguration;
friend class COutputConfigurationHead;
};
namespace PROTO {
inline UP<COutputManagementProtocol> outputManagement;
};

View file

@ -1474,70 +1474,6 @@ void CHyprRenderer::setWindowScanoutMode(PHLWINDOW pWindow) {
Debug::log(LOG, "Scanout mode ON set for {}", pWindow);
}
void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool test) {
wlr_output_configuration_head_v1* head;
bool ok = true;
wl_list_for_each(head, &config->heads, link) {
std::string commandForCfg = "";
const auto OUTPUT = head->state.output;
commandForCfg += std::string(OUTPUT->name) + ",";
if (!head->state.enabled) {
commandForCfg += "disabled";
if (!test)
g_pConfigManager->parseKeyword("monitor", commandForCfg);
continue;
}
const auto PMONITOR = g_pCompositor->getRealMonitorFromOutput(OUTPUT);
RASSERT(PMONITOR, "nullptr monitor in outputMgrApplyTest");
wlr_output_state_set_enabled(PMONITOR->state.wlr(), head->state.enabled);
if (head->state.mode)
commandForCfg +=
std::to_string(head->state.mode->width) + "x" + std::to_string(head->state.mode->height) + "@" + std::to_string(head->state.mode->refresh / 1000.f) + ",";
else
commandForCfg += std::to_string(head->state.custom_mode.width) + "x" + std::to_string(head->state.custom_mode.height) + "@" +
std::to_string(head->state.custom_mode.refresh / 1000.f) + ",";
commandForCfg += std::to_string(head->state.x) + "x" + std::to_string(head->state.y) + "," + std::to_string(head->state.scale) + ",transform," +
std::to_string((int)head->state.transform);
if (!test) {
g_pConfigManager->parseKeyword("monitor", commandForCfg);
wlr_output_state_set_adaptive_sync_enabled(PMONITOR->state.wlr(), head->state.adaptive_sync_enabled);
}
ok = wlr_output_test_state(OUTPUT, PMONITOR->state.wlr());
if (!ok)
break;
}
if (!test) {
g_pConfigManager->m_bWantsMonitorReload = true; // for monitor keywords
// if everything is disabled, performMonitorReload won't be called from renderMonitor
bool allDisabled = std::all_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(),
[](const auto m) { return !m->m_bEnabled || g_pCompositor->m_pUnsafeOutput == m.get(); });
if (allDisabled) {
Debug::log(LOG, "OutputMgr apply: All monitors disabled; performing monitor reload.");
g_pConfigManager->performMonitorReload();
}
}
if (ok)
wlr_output_configuration_v1_send_succeeded(config);
else
wlr_output_configuration_v1_send_failed(config);
wlr_output_configuration_v1_destroy(config);
Debug::log(LOG, "OutputMgr Applied/Tested.");
}
// taken from Sway.
// this is just too much of a spaghetti for me to understand
static void applyExclusive(wlr_box& usableArea, uint32_t anchor, int32_t exclusive, int32_t marginTop, int32_t marginRight, int32_t marginBottom, int32_t marginLeft) {
@ -1939,6 +1875,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
// Needed in case we are switching from a custom modeline to a standard mode
pMonitor->customDrmMode = {};
pMonitor->currentMode = nullptr;
bool autoScale = false;
if (RULE->scale > 0.1) {
@ -1981,6 +1918,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->refreshRate = mode->refresh / 1000.f;
pMonitor->vecSize = Vector2D(mode->width, mode->height);
pMonitor->currentMode = mode;
break;
}
@ -2010,6 +1948,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f;
pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
pMonitor->currentMode = PREFERREDMODE;
} else {
Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", RULE->resolution, (float)RULE->refreshRate);
}
@ -2118,6 +2057,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f;
pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
pMonitor->currentMode = PREFERREDMODE;
} else {
Debug::log(LOG, "Monitor {}: Applying highest mode {}x{}@{:2f}.", pMonitor->output->name, (int)currentWidth, (int)currentHeight, (int)currentRefresh / 1000.f);
@ -2148,6 +2088,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->refreshRate = mode->refresh / 1000.f;
pMonitor->vecSize = Vector2D(mode->width, mode->height);
pMonitor->currentMode = mode;
break;
}
@ -2158,6 +2099,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f;
pMonitor->currentMode = PREFERREDMODE;
Debug::log(LOG, "Setting preferred mode for {}", pMonitor->output->name);
}
@ -2307,8 +2249,6 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->events.modeChanged.emit();
Events::listener_change(nullptr, nullptr);
return true;
}

View file

@ -43,7 +43,6 @@ class CHyprRenderer {
CHyprRenderer();
void renderMonitor(CMonitor* pMonitor);
void outputMgrApplyTest(wlr_output_configuration_v1*, bool);
void arrangeLayersForMonitor(const int&);
void damageSurface(wlr_surface*, double, double, double scale = 1.0);
void damageWindow(PHLWINDOW, bool forceFull = false);