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

@ -171,9 +171,14 @@ pub fn init_egl(gbm: &GbmDevice<DrmDeviceFd>) -> Result<EGLInternals> {
}
impl State {
pub fn device_added(&mut self, dev: dev_t, path: &Path, dh: &DisplayHandle) -> Result<()> {
pub fn device_added(
&mut self,
dev: dev_t,
path: &Path,
dh: &DisplayHandle,
) -> Result<Vec<Output>> {
if !self.backend.kms().session.is_active() {
return Ok(());
return Ok(Vec::new());
}
if let Some(allowlist) = dev_list_var("COSMIC_DRM_ALLOW_DEVICES") {
@ -195,7 +200,7 @@ impl State {
"Skipping device {} due to COSMIC_DRM_ALLOW_DEVICE list.",
path.display()
);
return Ok(());
return Ok(Vec::new());
}
}
}
@ -212,7 +217,7 @@ impl State {
"Skipping device {} due to COSMIC_DRM_BLOCK_DEVICE list.",
path.display()
);
return Ok(());
return Ok(Vec::new());
}
}
}
@ -384,12 +389,12 @@ impl State {
.add_heads(wl_outputs.iter());
self.backend.kms().refresh_used_devices()?;
Ok(())
Ok(wl_outputs)
}
pub fn device_changed(&mut self, dev: dev_t) -> Result<()> {
pub fn device_changed(&mut self, dev: dev_t) -> Result<Vec<Output>> {
if !self.backend.kms().session.is_active() {
return Ok(());
return Ok(Vec::new());
}
let drm_node = DrmNode::from_dev_id(dev)?;
@ -475,7 +480,7 @@ impl State {
}
self.backend.kms().refresh_used_devices()?;
Ok(())
Ok(outputs_added)
}
pub fn device_removed(&mut self, dev: dev_t, dh: &DisplayHandle) -> Result<()> {
@ -548,7 +553,7 @@ impl State {
Ok(())
}
pub fn refresh_output_config(&mut self) {
pub fn refresh_output_config(&mut self) -> Result<()> {
self.common.config.read_outputs(
&mut self.common.output_configuration_state,
&mut self.backend,
@ -558,8 +563,9 @@ impl State {
&self.common.xdg_activation_state,
self.common.startup_done.clone(),
&self.common.clock,
);
)?;
self.common.refresh();
Ok(())
}
}