mirror of
https://github.com/hyprwm/Hyprland
synced 2025-01-10 19:29:49 +01:00
core: refactor/improve monitor mode selection (#8804)
This commit is contained in:
parent
c600e1aaff
commit
8d4c18d723
7 changed files with 384 additions and 425 deletions
|
@ -425,7 +425,7 @@ void CCompositor::initAllSignals() {
|
||||||
|
|
||||||
for (auto const& m : m_vMonitors) {
|
for (auto const& m : m_vMonitors) {
|
||||||
scheduleFrameForMonitor(m);
|
scheduleFrameForMonitor(m);
|
||||||
g_pHyprRenderer->applyMonitorRule(m, &m->activeMonitorRule, true);
|
m->applyMonitorRule(&m->activeMonitorRule, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_pConfigManager->m_bWantsMonitorReload = true;
|
g_pConfigManager->m_bWantsMonitorReload = true;
|
||||||
|
|
|
@ -1495,7 +1495,7 @@ void CConfigManager::performMonitorReload() {
|
||||||
|
|
||||||
auto rule = getMonitorRuleFor(m);
|
auto rule = getMonitorRuleFor(m);
|
||||||
|
|
||||||
if (!g_pHyprRenderer->applyMonitorRule(m, &rule)) {
|
if (!m->applyMonitorRule(&rule)) {
|
||||||
overAgain = true;
|
overAgain = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1553,7 +1553,7 @@ void CConfigManager::ensureMonitorStatus() {
|
||||||
auto rule = getMonitorRuleFor(rm);
|
auto rule = getMonitorRuleFor(rm);
|
||||||
|
|
||||||
if (rule.disabled == rm->m_bEnabled)
|
if (rule.disabled == rm->m_bEnabled)
|
||||||
g_pHyprRenderer->applyMonitorRule(rm, &rule);
|
rm->applyMonitorRule(&rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include "debug/Log.hpp"
|
#include "debug/Log.hpp"
|
||||||
#include <hyprutils/string/String.hpp>
|
#include <hyprutils/string/String.hpp>
|
||||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
#include <ranges>
|
||||||
using namespace Hyprutils::String;
|
using namespace Hyprutils::String;
|
||||||
using namespace Hyprutils::Utils;
|
using namespace Hyprutils::Utils;
|
||||||
|
|
||||||
|
@ -85,7 +87,7 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Debug::log(LOG, "Reapplying monitor rule for {} from a state request", szName);
|
Debug::log(LOG, "Reapplying monitor rule for {} from a state request", szName);
|
||||||
g_pHyprRenderer->applyMonitorRule(self.lock(), &activeMonitorRule, true);
|
applyMonitorRule(&activeMonitorRule, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +101,7 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
SMonitorRule rule = activeMonitorRule;
|
SMonitorRule rule = activeMonitorRule;
|
||||||
rule.resolution = SIZE;
|
rule.resolution = SIZE;
|
||||||
|
|
||||||
g_pHyprRenderer->applyMonitorRule(self.lock(), &rule);
|
applyMonitorRule(&rule);
|
||||||
});
|
});
|
||||||
|
|
||||||
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
|
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
|
||||||
|
@ -172,7 +174,7 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
|
|
||||||
// set mode, also applies
|
// set mode, also applies
|
||||||
if (!noRule)
|
if (!noRule)
|
||||||
g_pHyprRenderer->applyMonitorRule(self.lock(), &monitorRule, true);
|
applyMonitorRule(&monitorRule, true);
|
||||||
|
|
||||||
if (!state.commit())
|
if (!state.commit())
|
||||||
Debug::log(WARN, "state.commit() failed in CMonitor::onCommit");
|
Debug::log(WARN, "state.commit() failed in CMonitor::onCommit");
|
||||||
|
@ -361,6 +363,379 @@ void CMonitor::onDisconnect(bool destroy) {
|
||||||
std::erase_if(g_pCompositor->m_vMonitors, [&](PHLMONITOR& el) { return el.get() == this; });
|
std::erase_if(g_pCompositor->m_vMonitors, [&](PHLMONITOR& el) { return el.get() == this; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
|
||||||
|
|
||||||
|
static auto PDISABLESCALECHECKS = CConfigValue<Hyprlang::INT>("debug:disable_scale_checks");
|
||||||
|
|
||||||
|
Debug::log(LOG, "Applying monitor rule for {}", szName);
|
||||||
|
|
||||||
|
activeMonitorRule = *pMonitorRule;
|
||||||
|
|
||||||
|
if (forceSize.has_value())
|
||||||
|
activeMonitorRule.resolution = forceSize.value();
|
||||||
|
|
||||||
|
const auto RULE = &activeMonitorRule;
|
||||||
|
|
||||||
|
// if it's disabled, disable and ignore
|
||||||
|
if (RULE->disabled) {
|
||||||
|
if (m_bEnabled)
|
||||||
|
onDisconnect();
|
||||||
|
|
||||||
|
events.modeChanged.emit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't touch VR headsets
|
||||||
|
if (output->nonDesktop)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!m_bEnabled) {
|
||||||
|
onConnect(true); // enable it.
|
||||||
|
Debug::log(LOG, "Monitor {} is disabled but is requested to be enabled", szName);
|
||||||
|
force = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the rule isn't already applied
|
||||||
|
// TODO: clean this up lol
|
||||||
|
if (!force && DELTALESSTHAN(vecPixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(vecPixelSize.y, RULE->resolution.y, 1) &&
|
||||||
|
DELTALESSTHAN(refreshRate, RULE->refreshRate, 1) && setScale == RULE->scale &&
|
||||||
|
((DELTALESSTHAN(vecPosition.x, RULE->offset.x, 1) && DELTALESSTHAN(vecPosition.y, RULE->offset.y, 1)) || RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) &&
|
||||||
|
transform == RULE->transform && RULE->enable10bit == enabled10bit && !std::memcmp(&customDrmMode, &RULE->drmMode, sizeof(customDrmMode))) {
|
||||||
|
|
||||||
|
Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", szName);
|
||||||
|
|
||||||
|
setMirror(RULE->mirrorOf);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool autoScale = false;
|
||||||
|
|
||||||
|
if (RULE->scale > 0.1) {
|
||||||
|
scale = RULE->scale;
|
||||||
|
} else {
|
||||||
|
autoScale = true;
|
||||||
|
const auto DEFAULTSCALE = getDefaultScale();
|
||||||
|
scale = DEFAULTSCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
setScale = scale;
|
||||||
|
transform = RULE->transform;
|
||||||
|
|
||||||
|
// accumulate requested modes in reverse order (cause inesrting at front is inefficient)
|
||||||
|
std::vector<SP<Aquamarine::SOutputMode>> requestedModes;
|
||||||
|
std::string requestedStr = "preferred";
|
||||||
|
|
||||||
|
// use sortFunc, add best 3 to requestedModes in reverse, since we test in reverse
|
||||||
|
auto addBest3Modes = [&](auto const& sortFunc) {
|
||||||
|
auto sortedModes = output->modes;
|
||||||
|
std::ranges::sort(sortedModes, sortFunc);
|
||||||
|
if (sortedModes.size() > 3)
|
||||||
|
sortedModes.erase(sortedModes.begin() + 3, sortedModes.end());
|
||||||
|
requestedModes.insert(requestedModes.end(), sortedModes.rbegin(), sortedModes.rend());
|
||||||
|
};
|
||||||
|
|
||||||
|
// last fallback is preferred mode, btw this covers resolution == Vector2D()
|
||||||
|
if (!output->preferredMode())
|
||||||
|
Debug::log(ERR, "Monitor {} has NO PREFERRED MODE", output->name);
|
||||||
|
else
|
||||||
|
requestedModes.push_back(output->preferredMode());
|
||||||
|
|
||||||
|
if (RULE->resolution == Vector2D(-1, -1)) {
|
||||||
|
requestedStr = "highrr";
|
||||||
|
|
||||||
|
// sort prioritizing refresh rate 1st and resolution 2nd, then add best 3
|
||||||
|
addBest3Modes([](auto const& a, auto const& b) {
|
||||||
|
if (a->refreshRate > b->refreshRate)
|
||||||
|
return true;
|
||||||
|
else if (DELTALESSTHAN((float)a->refreshRate, (float)b->refreshRate, 1000) && a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else if (RULE->resolution == Vector2D(-1, -2)) {
|
||||||
|
requestedStr = "highres";
|
||||||
|
|
||||||
|
// sort prioritizing resultion 1st and refresh rate 2nd, then add best 3
|
||||||
|
addBest3Modes([](auto const& a, auto const& b) {
|
||||||
|
if (a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
|
||||||
|
return true;
|
||||||
|
else if (DELTALESSTHAN(a->pixelSize.x, b->pixelSize.x, 1) && DELTALESSTHAN(a->pixelSize.y, b->pixelSize.y, 1) && a->refreshRate > b->refreshRate)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else if (RULE->resolution != Vector2D()) {
|
||||||
|
// user requested mode
|
||||||
|
requestedStr = std::format("{:X0}@{:.2f}Hz", RULE->resolution, RULE->refreshRate);
|
||||||
|
|
||||||
|
// sort by closeness to requested, then add best 3
|
||||||
|
addBest3Modes([&](auto const& a, auto const& b) {
|
||||||
|
if (abs(a->pixelSize.x - RULE->resolution.x) < abs(b->pixelSize.x - RULE->resolution.x))
|
||||||
|
return true;
|
||||||
|
if (a->pixelSize.x == b->pixelSize.x && abs(a->pixelSize.y - RULE->resolution.y) < abs(b->pixelSize.y - RULE->resolution.y))
|
||||||
|
return true;
|
||||||
|
if (a->pixelSize == b->pixelSize && abs((a->refreshRate / 1000.f) - RULE->refreshRate) < abs((b->refreshRate / 1000.f) - RULE->refreshRate))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// then if requested is custom, try custom mode first
|
||||||
|
if (RULE->drmMode.type == DRM_MODE_TYPE_USERDEF) {
|
||||||
|
if (output->getBackend()->type() != Aquamarine::eBackendType::AQ_BACKEND_DRM)
|
||||||
|
Debug::log(ERR, "Tried to set custom modeline on non-DRM output");
|
||||||
|
else
|
||||||
|
requestedModes.push_back(makeShared<Aquamarine::SOutputMode>(
|
||||||
|
Aquamarine::SOutputMode{.pixelSize = {RULE->drmMode.hdisplay, RULE->drmMode.vdisplay}, .refreshRate = RULE->drmMode.vrefresh, .modeInfo = RULE->drmMode}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto WAS10B = enabled10bit;
|
||||||
|
const auto OLDRES = vecPixelSize;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
// Needed in case we are switching from a custom modeline to a standard mode
|
||||||
|
customDrmMode = {};
|
||||||
|
currentMode = nullptr;
|
||||||
|
|
||||||
|
output->state->setFormat(DRM_FORMAT_XRGB8888);
|
||||||
|
prevDrmFormat = drmFormat;
|
||||||
|
drmFormat = DRM_FORMAT_XRGB8888;
|
||||||
|
output->state->resetExplicitFences();
|
||||||
|
|
||||||
|
if (Debug::trace) {
|
||||||
|
Debug::log(TRACE, "Monitor {} requested modes:", szName);
|
||||||
|
for (auto const& mode : requestedModes | std::views::reverse) {
|
||||||
|
Debug::log(TRACE, "| {:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& mode : requestedModes | std::views::reverse) {
|
||||||
|
std::string modeStr = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);
|
||||||
|
|
||||||
|
if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF) {
|
||||||
|
output->state->setCustomMode(mode);
|
||||||
|
|
||||||
|
if (!state.test()) {
|
||||||
|
Debug::log(ERR, "Monitor {}: REJECTED custom mode {}!", szName, modeStr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
customDrmMode = mode->modeInfo.value();
|
||||||
|
} else {
|
||||||
|
output->state->setMode(mode);
|
||||||
|
|
||||||
|
if (!state.test()) {
|
||||||
|
Debug::log(ERR, "Monitor {}: REJECTED available mode {}!", szName, modeStr);
|
||||||
|
if (mode->preferred)
|
||||||
|
Debug::log(ERR, "Monitor {}: REJECTED preferred mode!!!", szName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
customDrmMode = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshRate = mode->refreshRate / 1000.f;
|
||||||
|
vecSize = mode->pixelSize;
|
||||||
|
currentMode = mode;
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
if (mode->preferred)
|
||||||
|
Debug::log(LOG, "Monitor {}: requested {}, using preferred mode {}", szName, requestedStr, modeStr);
|
||||||
|
else if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF)
|
||||||
|
Debug::log(LOG, "Monitor {}: requested {}, using custom mode {}", szName, requestedStr, modeStr);
|
||||||
|
else
|
||||||
|
Debug::log(LOG, "Monitor {}: requested {}, using available mode {}", szName, requestedStr, modeStr);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try requested as custom mode jic it works
|
||||||
|
if (!success && RULE->resolution != Vector2D() && RULE->resolution != Vector2D(-1, -1) && RULE->resolution != Vector2D(-1, -2)) {
|
||||||
|
auto refreshRate = output->getBackend()->type() == Aquamarine::eBackendType::AQ_BACKEND_DRM ? RULE->refreshRate * 1000 : 0;
|
||||||
|
auto mode = makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = RULE->resolution, .refreshRate = refreshRate});
|
||||||
|
std::string modeStr = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);
|
||||||
|
|
||||||
|
output->state->setCustomMode(mode);
|
||||||
|
|
||||||
|
if (state.test()) {
|
||||||
|
Debug::log(LOG, "Monitor {}: requested {}, using custom mode {}", szName, requestedStr, modeStr);
|
||||||
|
|
||||||
|
refreshRate = mode->refreshRate / 1000.f;
|
||||||
|
vecSize = mode->pixelSize;
|
||||||
|
currentMode = mode;
|
||||||
|
customDrmMode = {};
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} else
|
||||||
|
Debug::log(ERR, "Monitor {}: REJECTED custom mode {}!", szName, modeStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try any of the modes if none of the above work
|
||||||
|
if (!success) {
|
||||||
|
for (auto const& mode : output->modes) {
|
||||||
|
output->state->setMode(mode);
|
||||||
|
|
||||||
|
if (!state.test())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto errorMessage =
|
||||||
|
std::format("Monitor {} failed to set any requested modes, falling back to mode {:X0}@{:.2f}Hz", szName, mode->pixelSize, mode->refreshRate / 1000.f);
|
||||||
|
Debug::log(WARN, errorMessage);
|
||||||
|
g_pHyprNotificationOverlay->addNotification(errorMessage, CHyprColor(0xff0000ff), 5000, ICON_WARNING);
|
||||||
|
|
||||||
|
refreshRate = mode->refreshRate / 1000.f;
|
||||||
|
vecSize = mode->pixelSize;
|
||||||
|
currentMode = mode;
|
||||||
|
customDrmMode = {};
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
Debug::log(ERR, "Monitor {} has NO FALLBACK MODES, and an INVALID one was requested: {:X0}@{:.2f}Hz", szName, RULE->resolution, RULE->refreshRate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
vrrActive = output->state->state().adaptiveSync // disabled here, will be tested in CConfigManager::ensureVRR()
|
||||||
|
|| createdByUser; // wayland backend doesn't allow for disabling adaptive_sync
|
||||||
|
|
||||||
|
vecPixelSize = vecSize;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static const std::array<std::vector<std::pair<std::string, uint32_t>>, 2> formats{
|
||||||
|
std::vector<std::pair<std::string, uint32_t>>{ /* 10-bit */
|
||||||
|
{"DRM_FORMAT_XRGB2101010", DRM_FORMAT_XRGB2101010}, {"DRM_FORMAT_XBGR2101010", DRM_FORMAT_XBGR2101010}, {"DRM_FORMAT_XRGB8888", DRM_FORMAT_XRGB8888}, {"DRM_FORMAT_XBGR8888", DRM_FORMAT_XBGR8888}
|
||||||
|
},
|
||||||
|
std::vector<std::pair<std::string, uint32_t>>{ /* 8-bit */
|
||||||
|
{"DRM_FORMAT_XRGB8888", DRM_FORMAT_XRGB8888}, {"DRM_FORMAT_XBGR8888", DRM_FORMAT_XBGR8888}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
bool set10bit = false;
|
||||||
|
|
||||||
|
for (auto const& fmt : formats[(int)!RULE->enable10bit]) {
|
||||||
|
output->state->setFormat(fmt.second);
|
||||||
|
prevDrmFormat = drmFormat;
|
||||||
|
drmFormat = fmt.second;
|
||||||
|
|
||||||
|
if (!state.test()) {
|
||||||
|
Debug::log(ERR, "output {} failed basic test on format {}", szName, fmt.first);
|
||||||
|
} else {
|
||||||
|
Debug::log(LOG, "output {} succeeded basic test on format {}", szName, fmt.first);
|
||||||
|
if (RULE->enable10bit && fmt.first.contains("101010"))
|
||||||
|
set10bit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled10bit = set10bit;
|
||||||
|
|
||||||
|
Vector2D logicalSize = vecPixelSize / scale;
|
||||||
|
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
|
||||||
|
// invalid scale, will produce fractional pixels.
|
||||||
|
// find the nearest valid.
|
||||||
|
|
||||||
|
float searchScale = std::round(scale * 120.0);
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
double scaleZero = searchScale / 120.0;
|
||||||
|
|
||||||
|
Vector2D logicalZero = vecPixelSize / scaleZero;
|
||||||
|
if (logicalZero == logicalZero.round())
|
||||||
|
scale = scaleZero;
|
||||||
|
else {
|
||||||
|
for (size_t i = 1; i < 90; ++i) {
|
||||||
|
double scaleUp = (searchScale + i) / 120.0;
|
||||||
|
double scaleDown = (searchScale - i) / 120.0;
|
||||||
|
|
||||||
|
Vector2D logicalUp = vecPixelSize / scaleUp;
|
||||||
|
Vector2D logicalDown = vecPixelSize / scaleDown;
|
||||||
|
|
||||||
|
if (logicalUp == logicalUp.round()) {
|
||||||
|
found = true;
|
||||||
|
searchScale = scaleUp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (logicalDown == logicalDown.round()) {
|
||||||
|
found = true;
|
||||||
|
searchScale = scaleDown;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
if (autoScale)
|
||||||
|
scale = std::round(scaleZero);
|
||||||
|
else {
|
||||||
|
Debug::log(ERR, "Invalid scale passed to monitor, {} failed to find a clean divisor", scale);
|
||||||
|
g_pConfigManager->addParseError("Invalid scale passed to monitor " + szName + ", failed to find a clean divisor");
|
||||||
|
scale = getDefaultScale();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!autoScale) {
|
||||||
|
Debug::log(ERR, "Invalid scale passed to monitor, {} found suggestion {}", scale, searchScale);
|
||||||
|
g_pConfigManager->addParseError(
|
||||||
|
std::format("Invalid scale passed to monitor {}, failed to find a clean divisor. Suggested nearest scale: {:5f}", szName, searchScale));
|
||||||
|
scale = getDefaultScale();
|
||||||
|
} else
|
||||||
|
scale = searchScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output->scheduleFrame();
|
||||||
|
|
||||||
|
if (!state.commit())
|
||||||
|
Debug::log(ERR, "Couldn't commit output named {}", output->name);
|
||||||
|
|
||||||
|
Vector2D xfmd = transform % 2 == 1 ? Vector2D{vecPixelSize.y, vecPixelSize.x} : vecPixelSize;
|
||||||
|
vecSize = (xfmd / scale).round();
|
||||||
|
vecTransformedSize = xfmd;
|
||||||
|
|
||||||
|
if (createdByUser) {
|
||||||
|
CBox transformedBox = {0, 0, vecTransformedSize.x, vecTransformedSize.y};
|
||||||
|
transformedBox.transform(wlTransformToHyprutils(invertTransform(transform)), vecTransformedSize.x, vecTransformedSize.y);
|
||||||
|
|
||||||
|
vecPixelSize = Vector2D(transformedBox.width, transformedBox.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMatrix();
|
||||||
|
|
||||||
|
if (WAS10B != enabled10bit || OLDRES != vecPixelSize)
|
||||||
|
g_pHyprOpenGL->destroyMonitorResources(self.lock());
|
||||||
|
|
||||||
|
g_pCompositor->arrangeMonitors();
|
||||||
|
|
||||||
|
damage.setSize(vecTransformedSize);
|
||||||
|
|
||||||
|
// Set scale for all surfaces on this monitor, needed for some clients
|
||||||
|
// but not on unsafe state to avoid crashes
|
||||||
|
if (!g_pCompositor->m_bUnsafeState) {
|
||||||
|
for (auto const& w : g_pCompositor->m_vWindows) {
|
||||||
|
w->updateSurfaceScaleTransformDetails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// updato us
|
||||||
|
g_pHyprRenderer->arrangeLayersForMonitor(ID);
|
||||||
|
|
||||||
|
// reload to fix mirrors
|
||||||
|
g_pConfigManager->m_bWantsMonitorReload = true;
|
||||||
|
|
||||||
|
Debug::log(LOG, "Monitor {} data dump: res {:X}@{:.2f}Hz, scale {:.2f}, transform {}, pos {:X}, 10b {}", szName, vecPixelSize, refreshRate, scale, (int)transform, vecPosition,
|
||||||
|
(int)enabled10bit);
|
||||||
|
|
||||||
|
EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr);
|
||||||
|
|
||||||
|
events.modeChanged.emit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CMonitor::addDamage(const pixman_region32_t* rg) {
|
void CMonitor::addDamage(const pixman_region32_t* rg) {
|
||||||
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
||||||
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) {
|
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) {
|
||||||
|
@ -530,7 +905,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
|
||||||
|
|
||||||
setupDefaultWS(RULE);
|
setupDefaultWS(RULE);
|
||||||
|
|
||||||
g_pHyprRenderer->applyMonitorRule(self.lock(), (SMonitorRule*)&RULE, true); // will apply the offset and stuff
|
applyMonitorRule((SMonitorRule*)&RULE, true); // will apply the offset and stuff
|
||||||
} else {
|
} else {
|
||||||
PHLMONITOR BACKUPMON = nullptr;
|
PHLMONITOR BACKUPMON = nullptr;
|
||||||
for (auto const& m : g_pCompositor->m_vMonitors) {
|
for (auto const& m : g_pCompositor->m_vMonitors) {
|
||||||
|
|
|
@ -163,6 +163,7 @@ class CMonitor {
|
||||||
// methods
|
// methods
|
||||||
void onConnect(bool noRule);
|
void onConnect(bool noRule);
|
||||||
void onDisconnect(bool destroy = false);
|
void onDisconnect(bool destroy = false);
|
||||||
|
bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false);
|
||||||
void addDamage(const pixman_region32_t* rg);
|
void addDamage(const pixman_region32_t* rg);
|
||||||
void addDamage(const CRegion* rg);
|
void addDamage(const CRegion* rg);
|
||||||
void addDamage(const CBox* box);
|
void addDamage(const CBox* box);
|
||||||
|
|
|
@ -1992,7 +1992,7 @@ SDispatchResult CKeybindManager::forceRendererReload(std::string args) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto rule = g_pConfigManager->getMonitorRuleFor(m);
|
auto rule = g_pConfigManager->getMonitorRuleFor(m);
|
||||||
if (!g_pHyprRenderer->applyMonitorRule(m, &rule, true)) {
|
if (!m->applyMonitorRule(&rule, true)) {
|
||||||
overAgain = true;
|
overAgain = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "../helpers/sync/SyncReleaser.hpp"
|
#include "../helpers/sync/SyncReleaser.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <aquamarine/output/Output.hpp>
|
#include <aquamarine/output/Output.hpp>
|
||||||
#include <cstring>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "../config/ConfigValue.hpp"
|
#include "../config/ConfigValue.hpp"
|
||||||
#include "../managers/CursorManager.hpp"
|
#include "../managers/CursorManager.hpp"
|
||||||
|
@ -1831,421 +1830,6 @@ void CHyprRenderer::renderDragIcon(PHLMONITOR pMonitor, timespec* time) {
|
||||||
PROTO::data->renderDND(pMonitor, time);
|
PROTO::data->renderDND(pMonitor, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CHyprRenderer::applyMonitorRule(PHLMONITOR pMonitor, SMonitorRule* pMonitorRule, bool force) {
|
|
||||||
|
|
||||||
static auto PDISABLESCALECHECKS = CConfigValue<Hyprlang::INT>("debug:disable_scale_checks");
|
|
||||||
|
|
||||||
Debug::log(LOG, "Applying monitor rule for {}", pMonitor->szName);
|
|
||||||
|
|
||||||
pMonitor->activeMonitorRule = *pMonitorRule;
|
|
||||||
|
|
||||||
if (pMonitor->forceSize.has_value())
|
|
||||||
pMonitor->activeMonitorRule.resolution = pMonitor->forceSize.value();
|
|
||||||
|
|
||||||
const auto RULE = &pMonitor->activeMonitorRule;
|
|
||||||
|
|
||||||
// if it's disabled, disable and ignore
|
|
||||||
if (RULE->disabled) {
|
|
||||||
if (pMonitor->m_bEnabled)
|
|
||||||
pMonitor->onDisconnect();
|
|
||||||
|
|
||||||
pMonitor->events.modeChanged.emit();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't touch VR headsets
|
|
||||||
if (pMonitor->output->nonDesktop)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!pMonitor->m_bEnabled) {
|
|
||||||
pMonitor->onConnect(true); // enable it.
|
|
||||||
Debug::log(LOG, "Monitor {} is disabled but is requested to be enabled", pMonitor->szName);
|
|
||||||
force = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the rule isn't already applied
|
|
||||||
// TODO: clean this up lol
|
|
||||||
if (!force && DELTALESSTHAN(pMonitor->vecPixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(pMonitor->vecPixelSize.y, RULE->resolution.y, 1) &&
|
|
||||||
DELTALESSTHAN(pMonitor->refreshRate, RULE->refreshRate, 1) && pMonitor->setScale == RULE->scale &&
|
|
||||||
((DELTALESSTHAN(pMonitor->vecPosition.x, RULE->offset.x, 1) && DELTALESSTHAN(pMonitor->vecPosition.y, RULE->offset.y, 1)) ||
|
|
||||||
RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) &&
|
|
||||||
pMonitor->transform == RULE->transform && RULE->enable10bit == pMonitor->enabled10bit &&
|
|
||||||
!memcmp(&pMonitor->customDrmMode, &RULE->drmMode, sizeof(pMonitor->customDrmMode))) {
|
|
||||||
|
|
||||||
Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", pMonitor->szName);
|
|
||||||
|
|
||||||
pMonitor->setMirror(RULE->mirrorOf);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto WAS10B = pMonitor->enabled10bit;
|
|
||||||
const auto OLDRES = pMonitor->vecPixelSize;
|
|
||||||
|
|
||||||
// Needed in case we are switching from a custom modeline to a standard mode
|
|
||||||
pMonitor->customDrmMode = {};
|
|
||||||
pMonitor->currentMode = nullptr;
|
|
||||||
|
|
||||||
pMonitor->output->state->setFormat(DRM_FORMAT_XRGB8888);
|
|
||||||
pMonitor->prevDrmFormat = pMonitor->drmFormat;
|
|
||||||
pMonitor->drmFormat = DRM_FORMAT_XRGB8888;
|
|
||||||
pMonitor->output->state->resetExplicitFences();
|
|
||||||
|
|
||||||
bool autoScale = false;
|
|
||||||
|
|
||||||
if (RULE->scale > 0.1) {
|
|
||||||
pMonitor->scale = RULE->scale;
|
|
||||||
} else {
|
|
||||||
autoScale = true;
|
|
||||||
const auto DEFAULTSCALE = pMonitor->getDefaultScale();
|
|
||||||
pMonitor->scale = DEFAULTSCALE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pMonitor->setScale = pMonitor->scale;
|
|
||||||
pMonitor->transform = RULE->transform;
|
|
||||||
|
|
||||||
const auto WLRREFRESHRATE = pMonitor->output->getBackend()->type() == Aquamarine::eBackendType::AQ_BACKEND_DRM ? RULE->refreshRate * 1000 : 0;
|
|
||||||
|
|
||||||
// loop over modes and choose an appropriate one.
|
|
||||||
if (RULE->resolution != Vector2D() && RULE->resolution != Vector2D(-1, -1) && RULE->resolution != Vector2D(-1, -2)) {
|
|
||||||
if (!pMonitor->output->modes.empty() && RULE->drmMode.type != DRM_MODE_TYPE_USERDEF) {
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
for (auto const& mode : pMonitor->output->modes) {
|
|
||||||
// if delta of refresh rate, w and h chosen and mode is < 1 we accept it
|
|
||||||
if (DELTALESSTHAN(mode->pixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(mode->pixelSize.y, RULE->resolution.y, 1) &&
|
|
||||||
DELTALESSTHAN(mode->refreshRate / 1000.f, RULE->refreshRate, 1)) {
|
|
||||||
pMonitor->output->state->setMode(mode);
|
|
||||||
|
|
||||||
if (!pMonitor->state.test()) {
|
|
||||||
Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->pixelSize.x, mode->pixelSize.y,
|
|
||||||
mode->refreshRate / 1000.f);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug::log(LOG, "Monitor {}: requested {:X0}@{:2f}, found available mode: {}x{}@{}mHz, applying.", pMonitor->output->name, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate, mode->pixelSize.x, mode->pixelSize.y, mode->refreshRate);
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
|
|
||||||
pMonitor->refreshRate = mode->refreshRate / 1000.f;
|
|
||||||
pMonitor->vecSize = mode->pixelSize;
|
|
||||||
pMonitor->currentMode = mode;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
pMonitor->output->state->setCustomMode(makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = RULE->resolution, .refreshRate = WLRREFRESHRATE}));
|
|
||||||
pMonitor->vecSize = RULE->resolution;
|
|
||||||
pMonitor->refreshRate = RULE->refreshRate;
|
|
||||||
|
|
||||||
if (!pMonitor->state.test()) {
|
|
||||||
Debug::log(ERR, "Custom resolution FAILED, falling back to preferred");
|
|
||||||
|
|
||||||
const auto PREFERREDMODE = pMonitor->output->preferredMode();
|
|
||||||
|
|
||||||
if (!PREFERREDMODE) {
|
|
||||||
Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->ID, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preferred is valid
|
|
||||||
pMonitor->output->state->setMode(PREFERREDMODE);
|
|
||||||
|
|
||||||
Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate, PREFERREDMODE->pixelSize.x, PREFERREDMODE->pixelSize.y, PREFERREDMODE->refreshRate / 1000.f);
|
|
||||||
|
|
||||||
pMonitor->refreshRate = PREFERREDMODE->refreshRate / 1000.f;
|
|
||||||
pMonitor->vecSize = PREFERREDMODE->pixelSize;
|
|
||||||
pMonitor->currentMode = PREFERREDMODE;
|
|
||||||
} else {
|
|
||||||
Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", RULE->resolution, (float)RULE->refreshRate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// custom resolution
|
|
||||||
bool fail = false;
|
|
||||||
|
|
||||||
if (RULE->drmMode.type == DRM_MODE_TYPE_USERDEF) {
|
|
||||||
if (pMonitor->output->getBackend()->type() != Aquamarine::eBackendType::AQ_BACKEND_DRM) {
|
|
||||||
Debug::log(ERR, "Tried to set custom modeline on non-DRM output");
|
|
||||||
fail = true;
|
|
||||||
} else
|
|
||||||
pMonitor->output->state->setCustomMode(makeShared<Aquamarine::SOutputMode>(
|
|
||||||
Aquamarine::SOutputMode{.pixelSize = {RULE->drmMode.hdisplay, RULE->drmMode.vdisplay}, .refreshRate = RULE->drmMode.vrefresh, .modeInfo = RULE->drmMode}));
|
|
||||||
} else
|
|
||||||
pMonitor->output->state->setCustomMode(makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = RULE->resolution, .refreshRate = WLRREFRESHRATE}));
|
|
||||||
|
|
||||||
pMonitor->vecSize = RULE->resolution;
|
|
||||||
pMonitor->refreshRate = RULE->refreshRate;
|
|
||||||
|
|
||||||
if (fail || !pMonitor->state.test()) {
|
|
||||||
Debug::log(ERR, "Custom resolution FAILED, falling back to preferred");
|
|
||||||
|
|
||||||
const auto PREFERREDMODE = pMonitor->output->preferredMode();
|
|
||||||
|
|
||||||
if (!PREFERREDMODE) {
|
|
||||||
Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->output->name, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preferred is valid
|
|
||||||
pMonitor->output->state->setMode(PREFERREDMODE);
|
|
||||||
|
|
||||||
Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate, PREFERREDMODE->pixelSize.x, PREFERREDMODE->pixelSize.y, PREFERREDMODE->refreshRate / 1000.f);
|
|
||||||
|
|
||||||
pMonitor->refreshRate = PREFERREDMODE->refreshRate / 1000.f;
|
|
||||||
pMonitor->vecSize = PREFERREDMODE->pixelSize;
|
|
||||||
pMonitor->customDrmMode = {};
|
|
||||||
} else
|
|
||||||
Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", RULE->resolution, (float)RULE->refreshRate);
|
|
||||||
}
|
|
||||||
} else if (RULE->resolution != Vector2D()) {
|
|
||||||
if (!pMonitor->output->modes.empty()) {
|
|
||||||
float currentWidth = 0;
|
|
||||||
float currentHeight = 0;
|
|
||||||
float currentRefresh = 0;
|
|
||||||
bool success = false;
|
|
||||||
|
|
||||||
//(-1,-1) indicates a preference to refreshrate over resolution, (-1,-2) preference to resolution
|
|
||||||
if (RULE->resolution == Vector2D(-1, -1)) {
|
|
||||||
for (auto const& mode : pMonitor->output->modes) {
|
|
||||||
if ((mode->pixelSize.x >= currentWidth && mode->pixelSize.y >= currentHeight && mode->refreshRate >= (currentRefresh - 1000.f)) ||
|
|
||||||
mode->refreshRate > (currentRefresh + 3000.f)) {
|
|
||||||
pMonitor->output->state->setMode(mode);
|
|
||||||
if (pMonitor->state.test()) {
|
|
||||||
currentWidth = mode->pixelSize.x;
|
|
||||||
currentHeight = mode->pixelSize.y;
|
|
||||||
currentRefresh = mode->refreshRate;
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto const& mode : pMonitor->output->modes) {
|
|
||||||
if ((mode->pixelSize.x >= currentWidth && mode->pixelSize.y >= currentHeight && mode->refreshRate >= (currentRefresh - 1000.f)) ||
|
|
||||||
(mode->pixelSize.x > currentWidth && mode->pixelSize.y > currentHeight)) {
|
|
||||||
pMonitor->output->state->setMode(mode);
|
|
||||||
if (pMonitor->state.test()) {
|
|
||||||
currentWidth = mode->pixelSize.x;
|
|
||||||
currentHeight = mode->pixelSize.y;
|
|
||||||
currentRefresh = mode->refreshRate;
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
if (pMonitor->output->state->state().mode)
|
|
||||||
Debug::log(LOG, "Monitor {}: REJECTED mode: {:X0}@{:2f}! Falling back to preferred: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate, pMonitor->output->state->state().mode->pixelSize.x, pMonitor->output->state->state().mode->pixelSize.y,
|
|
||||||
pMonitor->output->state->state().mode->refreshRate / 1000.f);
|
|
||||||
|
|
||||||
const auto PREFERREDMODE = pMonitor->output->preferredMode();
|
|
||||||
|
|
||||||
if (!PREFERREDMODE) {
|
|
||||||
Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->ID, RULE->resolution, (float)RULE->refreshRate);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preferred is valid
|
|
||||||
pMonitor->output->state->setMode(PREFERREDMODE);
|
|
||||||
|
|
||||||
Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate, PREFERREDMODE->pixelSize.x, PREFERREDMODE->pixelSize.y, PREFERREDMODE->refreshRate / 1000.f);
|
|
||||||
|
|
||||||
pMonitor->refreshRate = PREFERREDMODE->refreshRate / 1000.f;
|
|
||||||
pMonitor->vecSize = PREFERREDMODE->pixelSize;
|
|
||||||
pMonitor->currentMode = PREFERREDMODE;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
Debug::log(LOG, "Monitor {}: Applying highest mode {}x{}@{:2f}.", pMonitor->output->name, (int)currentWidth, (int)currentHeight, (int)currentRefresh / 1000.f);
|
|
||||||
|
|
||||||
pMonitor->refreshRate = currentRefresh / 1000.f;
|
|
||||||
pMonitor->vecSize = Vector2D(currentWidth, currentHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const auto PREFERREDMODE = pMonitor->output->preferredMode();
|
|
||||||
|
|
||||||
if (!PREFERREDMODE) {
|
|
||||||
Debug::log(ERR, "Monitor {} has NO PREFERRED MODE", pMonitor->output->name);
|
|
||||||
|
|
||||||
if (!pMonitor->output->modes.empty()) {
|
|
||||||
for (auto const& mode : pMonitor->output->modes) {
|
|
||||||
pMonitor->output->state->setMode(mode);
|
|
||||||
|
|
||||||
if (!pMonitor->state.test()) {
|
|
||||||
Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->pixelSize.x, mode->pixelSize.y,
|
|
||||||
mode->refreshRate / 1000.f);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug::log(LOG, "Monitor {}: requested {:X0}@{:2f}, found available mode: {}x{}@{}mHz, applying.", pMonitor->output->name, RULE->resolution,
|
|
||||||
(float)RULE->refreshRate, mode->pixelSize.x, mode->pixelSize.y, mode->refreshRate);
|
|
||||||
|
|
||||||
pMonitor->refreshRate = mode->refreshRate / 1000.f;
|
|
||||||
pMonitor->vecSize = mode->pixelSize;
|
|
||||||
pMonitor->currentMode = mode;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Preferred is valid
|
|
||||||
pMonitor->output->state->setMode(PREFERREDMODE);
|
|
||||||
|
|
||||||
pMonitor->vecSize = PREFERREDMODE->pixelSize;
|
|
||||||
pMonitor->refreshRate = PREFERREDMODE->refreshRate / 1000.f;
|
|
||||||
pMonitor->currentMode = PREFERREDMODE;
|
|
||||||
|
|
||||||
Debug::log(LOG, "Setting preferred mode for {}", pMonitor->output->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pMonitor->vrrActive = pMonitor->output->state->state().adaptiveSync // disabled here, will be tested in CConfigManager::ensureVRR()
|
|
||||||
|| pMonitor->createdByUser; // wayland backend doesn't allow for disabling adaptive_sync
|
|
||||||
|
|
||||||
pMonitor->vecPixelSize = pMonitor->vecSize;
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
static const std::array<std::vector<std::pair<std::string, uint32_t>>, 2> formats{
|
|
||||||
std::vector<std::pair<std::string, uint32_t>>{ /* 10-bit */
|
|
||||||
{"DRM_FORMAT_XRGB2101010", DRM_FORMAT_XRGB2101010}, {"DRM_FORMAT_XBGR2101010", DRM_FORMAT_XBGR2101010}, {"DRM_FORMAT_XRGB8888", DRM_FORMAT_XRGB8888}, {"DRM_FORMAT_XBGR8888", DRM_FORMAT_XBGR8888}, {"DRM_FORMAT_INVALID", DRM_FORMAT_INVALID}
|
|
||||||
},
|
|
||||||
std::vector<std::pair<std::string, uint32_t>>{ /* 8-bit */
|
|
||||||
{"DRM_FORMAT_XRGB8888", DRM_FORMAT_XRGB8888}, {"DRM_FORMAT_XBGR8888", DRM_FORMAT_XBGR8888}, {"DRM_FORMAT_INVALID", DRM_FORMAT_INVALID}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
bool set10bit = false;
|
|
||||||
|
|
||||||
for (auto const& fmt : formats[(int)!RULE->enable10bit]) {
|
|
||||||
pMonitor->output->state->setFormat(fmt.second);
|
|
||||||
pMonitor->prevDrmFormat = pMonitor->drmFormat;
|
|
||||||
pMonitor->drmFormat = fmt.second;
|
|
||||||
|
|
||||||
if (!pMonitor->state.test()) {
|
|
||||||
Debug::log(ERR, "output {} failed basic test on format {}", pMonitor->szName, fmt.first);
|
|
||||||
} else {
|
|
||||||
Debug::log(LOG, "output {} succeeded basic test on format {}", pMonitor->szName, fmt.first);
|
|
||||||
if (RULE->enable10bit && fmt.first.contains("101010"))
|
|
||||||
set10bit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pMonitor->enabled10bit = set10bit;
|
|
||||||
|
|
||||||
Vector2D logicalSize = pMonitor->vecPixelSize / pMonitor->scale;
|
|
||||||
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
|
|
||||||
// invalid scale, will produce fractional pixels.
|
|
||||||
// find the nearest valid.
|
|
||||||
|
|
||||||
float searchScale = std::round(pMonitor->scale * 120.0);
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
double scaleZero = searchScale / 120.0;
|
|
||||||
|
|
||||||
Vector2D logicalZero = pMonitor->vecPixelSize / scaleZero;
|
|
||||||
if (logicalZero == logicalZero.round())
|
|
||||||
pMonitor->scale = scaleZero;
|
|
||||||
else {
|
|
||||||
for (size_t i = 1; i < 90; ++i) {
|
|
||||||
double scaleUp = (searchScale + i) / 120.0;
|
|
||||||
double scaleDown = (searchScale - i) / 120.0;
|
|
||||||
|
|
||||||
Vector2D logicalUp = pMonitor->vecPixelSize / scaleUp;
|
|
||||||
Vector2D logicalDown = pMonitor->vecPixelSize / scaleDown;
|
|
||||||
|
|
||||||
if (logicalUp == logicalUp.round()) {
|
|
||||||
found = true;
|
|
||||||
searchScale = scaleUp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (logicalDown == logicalDown.round()) {
|
|
||||||
found = true;
|
|
||||||
searchScale = scaleDown;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
if (autoScale)
|
|
||||||
pMonitor->scale = std::round(scaleZero);
|
|
||||||
else {
|
|
||||||
Debug::log(ERR, "Invalid scale passed to monitor, {} failed to find a clean divisor", pMonitor->scale);
|
|
||||||
g_pConfigManager->addParseError("Invalid scale passed to monitor " + pMonitor->szName + ", failed to find a clean divisor");
|
|
||||||
pMonitor->scale = pMonitor->getDefaultScale();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!autoScale) {
|
|
||||||
Debug::log(ERR, "Invalid scale passed to monitor, {} found suggestion {}", pMonitor->scale, searchScale);
|
|
||||||
g_pConfigManager->addParseError(
|
|
||||||
std::format("Invalid scale passed to monitor {}, failed to find a clean divisor. Suggested nearest scale: {:5f}", pMonitor->szName, searchScale));
|
|
||||||
pMonitor->scale = pMonitor->getDefaultScale();
|
|
||||||
} else
|
|
||||||
pMonitor->scale = searchScale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pMonitor->output->scheduleFrame();
|
|
||||||
|
|
||||||
if (!pMonitor->state.commit())
|
|
||||||
Debug::log(ERR, "Couldn't commit output named {}", pMonitor->output->name);
|
|
||||||
|
|
||||||
Vector2D xfmd = pMonitor->transform % 2 == 1 ? Vector2D{pMonitor->vecPixelSize.y, pMonitor->vecPixelSize.x} : pMonitor->vecPixelSize;
|
|
||||||
pMonitor->vecSize = (xfmd / pMonitor->scale).round();
|
|
||||||
pMonitor->vecTransformedSize = xfmd;
|
|
||||||
|
|
||||||
if (pMonitor->createdByUser) {
|
|
||||||
CBox transformedBox = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y};
|
|
||||||
transformedBox.transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y);
|
|
||||||
|
|
||||||
pMonitor->vecPixelSize = Vector2D(transformedBox.width, transformedBox.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
pMonitor->updateMatrix();
|
|
||||||
|
|
||||||
if (WAS10B != pMonitor->enabled10bit || OLDRES != pMonitor->vecPixelSize)
|
|
||||||
g_pHyprOpenGL->destroyMonitorResources(pMonitor);
|
|
||||||
|
|
||||||
g_pCompositor->arrangeMonitors();
|
|
||||||
|
|
||||||
pMonitor->damage.setSize(pMonitor->vecTransformedSize);
|
|
||||||
|
|
||||||
// Set scale for all surfaces on this monitor, needed for some clients
|
|
||||||
// but not on unsafe state to avoid crashes
|
|
||||||
if (!g_pCompositor->m_bUnsafeState) {
|
|
||||||
for (auto const& w : g_pCompositor->m_vWindows) {
|
|
||||||
w->updateSurfaceScaleTransformDetails();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// updato us
|
|
||||||
arrangeLayersForMonitor(pMonitor->ID);
|
|
||||||
|
|
||||||
// reload to fix mirrors
|
|
||||||
g_pConfigManager->m_bWantsMonitorReload = true;
|
|
||||||
|
|
||||||
Debug::log(LOG, "Monitor {} data dump: res {:X}@{:.2f}Hz, scale {:.2f}, transform {}, pos {:X}, 10b {}", pMonitor->szName, pMonitor->vecPixelSize, pMonitor->refreshRate,
|
|
||||||
pMonitor->scale, (int)pMonitor->transform, pMonitor->vecPosition, (int)pMonitor->enabled10bit);
|
|
||||||
|
|
||||||
EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr);
|
|
||||||
|
|
||||||
pMonitor->events.modeChanged.emit();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHyprRenderer::setCursorSurface(SP<CWLSurface> surf, int hotspotX, int hotspotY, bool force) {
|
void CHyprRenderer::setCursorSurface(SP<CWLSurface> surf, int hotspotX, int hotspotY, bool force) {
|
||||||
m_bCursorHasSurface = surf;
|
m_bCursorHasSurface = surf;
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@ class CHyprRenderer {
|
||||||
void damageRegion(const CRegion&);
|
void damageRegion(const CRegion&);
|
||||||
void damageMonitor(PHLMONITOR);
|
void damageMonitor(PHLMONITOR);
|
||||||
void damageMirrorsWith(PHLMONITOR, const CRegion&);
|
void damageMirrorsWith(PHLMONITOR, const CRegion&);
|
||||||
bool applyMonitorRule(PHLMONITOR, SMonitorRule*, bool force = false);
|
|
||||||
bool shouldRenderWindow(PHLWINDOW, PHLMONITOR);
|
bool shouldRenderWindow(PHLWINDOW, PHLMONITOR);
|
||||||
bool shouldRenderWindow(PHLWINDOW);
|
bool shouldRenderWindow(PHLWINDOW);
|
||||||
void ensureCursorRenderingMode();
|
void ensureCursorRenderingMode();
|
||||||
|
|
Loading…
Reference in a new issue