Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 10 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 29 additions & 1 deletion cosmic-comp-config/src/output/comp.rs
Original file line number Diff line number Diff line change
@@ -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<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let value = Option::<String>::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<String>) -> bool {
match value {
None => true,
Some(v) => v.trim().is_empty(),
}
}

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum OutputState {
Expand Down Expand Up @@ -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<String>,
}

pub fn load_outputs(path: Option<impl AsRef<Path>>) -> OutputsConfig {
Expand Down
1 change: 1 addition & 0 deletions cosmic-comp-config/src/output/randr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ impl From<CompList> 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 {
Expand Down
46 changes: 40 additions & 6 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,11 +417,28 @@ impl Config {
.collect::<Vec<_>>();
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;
}
}
Comment on lines +422 to +439
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional backwards compatibility. Can be removed and a default display configuration will be created.


if let Some(configs) = configs_ref_opt
.filter(|configs| {
if configs
.iter()
Expand Down Expand Up @@ -453,7 +470,10 @@ impl Config {
.collect::<Vec<_>>();

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();
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Comment on lines +549 to +555
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional backwards compatibility. Can be removed and a default display configuration will be created.

} else {
if outputs
.iter()
Expand Down Expand Up @@ -963,10 +992,15 @@ pub struct CompOutputInfo(OutputInfo);
impl From<Output> 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,
})
}
}