diff --git a/Cargo.lock b/Cargo.lock index acc4bf0f1..214969a13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -973,12 +973,11 @@ dependencies = [ [[package]] name = "cosmic-randr-shell" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-randr/#bce9cdf2d447508d4e2d54a2be4fcd738ab51df5" +source = "git+https://github.com/pop-os/cosmic-randr/#741089cf5e3aa7d5e48042101c1d4cc813b13637" dependencies = [ "kdl", "slotmap", "thiserror 2.0.17", - "tokio", ] [[package]] @@ -1268,7 +1267,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1578,7 +1577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2875,7 +2874,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d463f34ca3c400fde3a054da0e0b8c6ffa21e4590922f3e18281bb5eeef4cbdc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3499,7 +3498,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4672,7 +4671,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4685,7 +4684,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5331,7 +5330,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5489,20 +5488,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tokio" -version = "1.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "windows-sys 0.61.2", -] - [[package]] name = "toml" version = "0.5.11" @@ -5654,7 +5639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "319c70195101a93f56db4c74733e272d720768e13471f400c78406a326b172b0" dependencies = [ "cc", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -6352,7 +6337,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/cosmic-comp-config/src/output/comp.rs b/cosmic-comp-config/src/output/comp.rs index 1810b9d43..6f0627352 100644 --- a/cosmic-comp-config/src/output/comp.rs +++ b/cosmic-comp-config/src/output/comp.rs @@ -1,9 +1,31 @@ // SPDX-License-Identifier: GPL-3.0-only -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use std::{collections::HashMap, fs::OpenOptions, path::Path}; use tracing::{error, warn}; +fn deserialize_optional_trimmed_string<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let value = Option::::deserialize(deserializer)?; + Ok(value.and_then(|s| { + let trimmed = s.trim(); + if trimmed.is_empty() { + None + } else { + Some(trimmed.to_string()) + } + })) +} + +fn is_none_or_whitespace(value: &Option) -> bool { + match value { + None => true, + Some(v) => v.trim().is_empty(), + } +} + #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[serde(rename_all = "lowercase")] pub enum OutputState { @@ -73,6 +95,12 @@ pub struct OutputInfo { pub connector: String, pub make: String, pub model: String, + #[serde( + default, + deserialize_with = "deserialize_optional_trimmed_string", + skip_serializing_if = "is_none_or_whitespace" + )] + pub serial_number: Option, } pub fn load_outputs(path: Option>) -> OutputsConfig { diff --git a/cosmic-comp-config/src/output/randr.rs b/cosmic-comp-config/src/output/randr.rs index dd6cb6c60..4fb58cdfc 100644 --- a/cosmic-comp-config/src/output/randr.rs +++ b/cosmic-comp-config/src/output/randr.rs @@ -35,6 +35,7 @@ impl From for cosmic_randr_shell::List { } else { info.model }, + serial_number: info.serial_number.unwrap_or_default(), position: (output.position.0 as i32, output.position.1 as i32), scale: output.scale, transform: Some(match output.transform { diff --git a/src/config/mod.rs b/src/config/mod.rs index ac8043d36..bbc956178 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -417,11 +417,28 @@ impl Config { .collect::>(); infos.sort(); - if let Some(configs) = self - .dynamic_conf - .outputs() - .config - .get(&infos) + let outputs_config = self.dynamic_conf.outputs(); + + // Backward-compatible lookup: older `outputs.ron` files won't have `serial_number`. + // If a strict match fails, retry with serial numbers ignored. + // This prevents bricking a users config and resetting to default. + let mut used_fallback_key = false; + let mut infos_key = infos.clone(); + let mut configs_ref_opt = outputs_config.config.get(&infos); + if configs_ref_opt.is_none() { + let mut infos_no_serial = infos.clone(); + for info in &mut infos_no_serial { + info.serial_number = None; + } + infos_no_serial.sort(); + if let Some(cfgs) = outputs_config.config.get(&infos_no_serial) { + infos_key = infos_no_serial; + configs_ref_opt = Some(cfgs); + used_fallback_key = true; + } + } + + if let Some(configs) = configs_ref_opt .filter(|configs| { if configs .iter() @@ -453,7 +470,10 @@ impl Config { .collect::>(); let mut found_outputs = Vec::new(); - for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter()) + for (name, output_config) in infos_key + .iter() + .map(|o| &o.connector) + .zip(configs.into_iter()) { let output = outputs.iter().find(|o| &o.name() == name).unwrap().clone(); let enabled = output_config.enabled.clone(); @@ -466,6 +486,7 @@ impl Config { } let mut backend = backend.lock(); + let mut applied_config_ok = false; if let Err(err) = backend.apply_config_for_outputs( false, loop_handle, @@ -513,6 +534,7 @@ impl Config { } } } else { + applied_config_ok = true; for (output, enabled) in found_outputs { if enabled == OutputState::Enabled { output_state.enable_head(&output); @@ -524,6 +546,13 @@ impl Config { output_state.update(); self.write_outputs(output_state.outputs()); + + // One-time migration: if we only found a config by ignoring serial numbers, + // persist the now-known serial-aware key and remove the old key. + if used_fallback_key && applied_config_ok { + let mut outputs_mut = self.dynamic_conf.outputs_mut(); + outputs_mut.config.remove(&infos_key); + } } else { if outputs .iter() @@ -963,10 +992,15 @@ pub struct CompOutputInfo(OutputInfo); impl From for CompOutputInfo { fn from(o: Output) -> CompOutputInfo { let physical = o.physical_properties(); + let serial_number = match physical.serial_number.as_str().trim() { + "" | "Unknown" => None, + s => Some(s.to_string()), + }; CompOutputInfo(OutputInfo { connector: o.name(), make: physical.make, model: physical.model, + serial_number, }) } }