outputconfigurationstore: split user configured and automatically set modes

If the mode the user has previously set through KScreen isn't available, this falls
back to a different mode without overwriting the user preference. If on the next
hotplug, the desired mode is available again, it then gets chosen instead of the
fallback

BUG: 484037
This commit is contained in:
Xaver Hugl 2024-05-22 21:19:20 +02:00
parent c9627dc60f
commit 8c25784070
6 changed files with 58 additions and 11 deletions

View file

@ -405,6 +405,8 @@ void DrmOutput::applyQueuedChanges(const std::shared_ptr<OutputChangeSet> &props
next.vrrPolicy = props->vrrPolicy.value_or(m_state.vrrPolicy);
next.colorProfileSource = props->colorProfileSource.value_or(m_state.colorProfileSource);
next.brightness = props->brightness.value_or(m_state.brightness);
next.desiredModeSize = props->desiredModeSize.value_or(m_state.desiredModeSize);
next.desiredModeRefreshRate = props->desiredModeRefreshRate.value_or(m_state.desiredModeRefreshRate);
setState(next);
if (!isEnabled() && m_pipeline->needsModeset()) {

View file

@ -487,6 +487,16 @@ std::shared_ptr<OutputMode> Output::currentMode() const
return m_state.currentMode;
}
QSize Output::desiredModeSize() const
{
return m_state.desiredModeSize;
}
uint32_t Output::desiredModeRefreshRate() const
{
return m_state.desiredModeRefreshRate;
}
Output::SubPixel Output::subPixel() const
{
return m_information.subPixel;
@ -512,6 +522,8 @@ void Output::applyChanges(const OutputConfiguration &config)
next.iccProfile = IccProfile::load(*props->iccProfilePath);
}
next.vrrPolicy = props->vrrPolicy.value_or(m_state.vrrPolicy);
next.desiredModeSize = props->desiredModeSize.value_or(m_state.desiredModeSize);
next.desiredModeRefreshRate = props->desiredModeRefreshRate.value_or(m_state.desiredModeRefreshRate);
setState(next);

View file

@ -321,6 +321,8 @@ public:
const Edid &edid() const;
QList<std::shared_ptr<OutputMode>> modes() const;
std::shared_ptr<OutputMode> currentMode() const;
QSize desiredModeSize() const;
uint32_t desiredModeRefreshRate() const;
DpmsMode dpmsMode() const;
virtual void setDpmsMode(DpmsMode mode);
@ -457,6 +459,8 @@ protected:
OutputTransform manualTransform = OutputTransform::Normal;
QList<std::shared_ptr<OutputMode>> modes;
std::shared_ptr<OutputMode> currentMode;
QSize desiredModeSize;
uint32_t desiredModeRefreshRate = 0;
DpmsMode dpmsMode = DpmsMode::On;
SubPixel subPixel = SubPixel::Unknown;
bool enabled = false;

View file

@ -24,6 +24,8 @@ class KWIN_EXPORT OutputChangeSet
{
public:
std::optional<std::weak_ptr<OutputMode>> mode;
std::optional<QSize> desiredModeSize;
std::optional<uint32_t> desiredModeRefreshRate;
std::optional<bool> enabled;
std::optional<QPoint> pos;
std::optional<double> scale;

View file

@ -200,9 +200,13 @@ void OutputConfigurationStore::storeConfig(const QList<Output *> &allOutputs, bo
outputIt = setup->outputs.end() - 1;
}
if (const auto changeSet = config.constChangeSet(output)) {
std::shared_ptr<OutputMode> mode = changeSet->mode.value_or(output->currentMode()).lock();
if (!mode) {
mode = output->currentMode();
QSize modeSize = changeSet->desiredModeSize.value_or(output->desiredModeSize());
if (modeSize.isEmpty()) {
modeSize = output->currentMode()->size();
}
uint32_t refreshRate = changeSet->desiredModeRefreshRate.value_or(output->desiredModeRefreshRate());
if (refreshRate == 0) {
refreshRate = output->currentMode()->refreshRate();
}
m_outputs[*outputIndex] = OutputState{
.edidIdentifier = output->edid().identifier(),
@ -210,8 +214,8 @@ void OutputConfigurationStore::storeConfig(const QList<Output *> &allOutputs, bo
.edidHash = output->edid().isValid() ? output->edid().hash() : QString{},
.mstPath = output->mstPath(),
.mode = ModeData{
.size = mode->size(),
.refreshRate = mode->refreshRate(),
.size = modeSize,
.refreshRate = refreshRate,
},
.scale = changeSet->scale.value_or(output->scale()),
.transform = changeSet->transform.value_or(output->transform()),
@ -238,15 +242,22 @@ void OutputConfigurationStore::storeConfig(const QList<Output *> &allOutputs, bo
.priority = int(outputOrder.indexOf(output)),
};
} else {
const auto mode = output->currentMode();
QSize modeSize = output->desiredModeSize();
if (modeSize.isEmpty()) {
modeSize = output->currentMode()->size();
}
uint32_t refreshRate = output->desiredModeRefreshRate();
if (refreshRate == 0) {
refreshRate = output->currentMode()->refreshRate();
}
m_outputs[*outputIndex] = OutputState{
.edidIdentifier = output->edid().identifier(),
.connectorName = output->name(),
.edidHash = output->edid().isValid() ? output->edid().hash() : QString{},
.mstPath = output->mstPath(),
.mode = ModeData{
.size = mode->size(),
.refreshRate = mode->refreshRate(),
.size = modeSize,
.refreshRate = refreshRate,
},
.scale = output->scale(),
.transform = output->transform(),
@ -287,13 +298,19 @@ std::pair<OutputConfiguration, QList<Output *>> OutputConfigurationStore::setupT
return state.outputIndex == outputIndex;
});
const auto modes = output->modes();
const auto mode = std::find_if(modes.begin(), modes.end(), [&state](const auto &mode) {
const auto modeIt = std::find_if(modes.begin(), modes.end(), [&state](const auto &mode) {
return state.mode
&& mode->size() == state.mode->size
&& mode->refreshRate() == state.mode->refreshRate;
});
std::optional<std::shared_ptr<OutputMode>> mode = modeIt == modes.end() ? std::nullopt : std::optional(*modeIt);
if (!mode.has_value()) {
mode = chooseMode(output);
}
*ret.changeSet(output) = OutputChangeSet{
.mode = mode == modes.end() ? std::nullopt : std::optional(*mode),
.mode = mode,
.desiredModeSize = state.mode.has_value() ? std::make_optional(state.mode->size) : std::nullopt,
.desiredModeRefreshRate = state.mode.has_value() ? std::make_optional(state.mode->refreshRate) : std::nullopt,
.enabled = setupState.enabled,
.pos = setupState.position,
.scale = state.scale,
@ -420,6 +437,8 @@ std::pair<OutputConfiguration, QList<Output *>> OutputConfigurationStore::genera
const auto changeset = ret.changeSet(output);
*changeset = {
.mode = mode,
.desiredModeSize = mode->size(),
.desiredModeRefreshRate = mode->refreshRate(),
.enabled = kscreenChangeSet.enabled.value_or(enable),
.pos = pos,
// kscreen scale is unreliable because it gets overwritten with the value 1 on Xorg,

View file

@ -121,7 +121,15 @@ void OutputConfigurationV2Interface::kde_output_configuration_v2_mode(Resource *
OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice);
OutputDeviceModeV2Interface *mode = OutputDeviceModeV2Interface::get(modeResource);
if (output && mode) {
config.changeSet(output->handle())->mode = mode->handle().lock();
const auto change = config.changeSet(output->handle());
const auto modePtr = mode->handle().lock();
if (!modePtr) {
invalid = true;
return;
}
change->mode = modePtr;
change->desiredModeSize = modePtr->size();
change->desiredModeRefreshRate = modePtr->refreshRate();
} else {
invalid = true;
}