config: Make read_outputs failable

Previously we ignored when we had no output configuration
**and** failed to apply the automatically created one.

This leads to two problems:
- If this happens on startup, we end up with no outputs being added to the shell and we quit.
- If this happens later, we might end up in an inconsistent state, where the shell thinks we have an output, when it didn't light up for similar reasons.

Thus `read_outputs` is failable and handling that very much depends on
the where is was called from, because `read_outputs` doesn't know what
configuration was active before.

Thus make it failable and provide useful mitigations everywhere
possible:
- Try to enable just one output in case we fail on startup.
- Don't enable any additional outputs, when we fail on hotplug.
- Log the error like previously in any other case (and come up with more
  mitigations, once we understand these cases better).
This commit is contained in:
Victoria Brekenfeld 2025-09-10 18:25:33 +02:00 committed by Victoria Brekenfeld
parent cd1117080c
commit b83e9f1d32
8 changed files with 232 additions and 91 deletions

View file

@ -69,7 +69,7 @@ use smithay::{
tablet_manager::{TabletDescriptor, TabletSeatTrait},
},
};
use tracing::{error, trace};
use tracing::{error, trace, warn};
use xkbcommon::xkb::{Keycode, Keysym};
use std::{
@ -1525,17 +1525,38 @@ impl State {
InputEvent::SwitchToggle { event } => {
#[cfg(feature = "systemd")]
if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() {
if event.state() == SwitchState::On {
self.backend
.lock()
let backend = self.backend.lock();
let output = backend
.all_outputs()
.iter()
.find(|o| o.is_internal())
.cloned();
let closed = event.state() == SwitchState::On;
if closed {
backend
.disable_internal_output(&mut self.common.output_configuration_state);
} else {
self.backend
.lock()
.enable_internal_output(&mut self.common.output_configuration_state);
backend.enable_internal_output(&mut self.common.output_configuration_state);
}
std::mem::drop(backend);
self.refresh_output_config();
if let Err(err) = self.refresh_output_config() {
if !closed {
warn!(?err, "Failed to re-enable internal connector");
if let Some(output) = output {
use cosmic_comp_config::output::comp::OutputState;
output.config_mut().enabled = OutputState::Disabled;
if let Err(err) = self.refresh_output_config() {
error!("Unrecoverable output configuration error: {}", err);
}
}
} else {
// Disabling an output should never fail.
error!("Unrecoverable output configuration error: {}", err);
}
}
}
}
}