diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index ecbba0c3..c95781c6 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -1311,12 +1311,7 @@ fn create_connector_display_data( } } } - let output_id = Rc::new(OutputId::new( - connector_id.to_string(), - manufacturer, - name, - serial_number, - )); + let output_id = OutputId::new(connector_id.to_string(), manufacturer, name, serial_number); let first_mode = info .modes .first() diff --git a/src/backends/x.rs b/src/backends/x.rs index 72e1dde5..a843f391 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -591,12 +591,12 @@ impl XBackend { .push(BackendEvent::NewConnector(output.clone())); output.events.push(ConnectorEvent::Connected(MonitorInfo { modes: Some(vec![]), - output_id: Rc::new(OutputId::new( - String::new(), - "X.Org Foundation".to_string(), + output_id: OutputId::new( + "", + "X.Org Foundation", format!("X-Window-{}", output.window), output.window.to_string(), - )), + ), width_mm: output.width.get(), height_mm: output.height.get(), non_desktop: false, diff --git a/src/compositor.rs b/src/compositor.rs index 0a2567a1..611c8bdc 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -686,12 +686,7 @@ fn init_fd_limit() { } fn create_dummy_output(state: &Rc) { - let output_id = Rc::new(OutputId { - connector: Some("jay-dummy-connector".to_string()), - manufacturer: "jay".to_string(), - model: "jay-dummy-output".to_string(), - serial_number: "".to_string(), - }); + let output_id = OutputId::new("jay-dummy-connector", "jay", "jay-dummy-output", ""); let persistent_state = Rc::new(PersistentOutputState::default()); let id = state.connector_ids.next(); let connector = Rc::new(DummyOutput { id }) as Rc; diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 5ff56fa8..1687824d 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -33,6 +33,7 @@ use { std::{ cell::{Cell, RefCell}, collections::hash_map::Entry, + hash::{Hash, Hasher}, rc::Rc, }, thiserror::Error, @@ -164,27 +165,67 @@ impl Default for PersistentOutputState { } } -#[derive(Eq, PartialEq, Hash, Debug)] +#[derive(Eq, Debug)] pub struct OutputId { - pub connector: Option, + pub _connector: Option, pub manufacturer: String, pub model: String, pub serial_number: String, + pub hash: OutputIdHash, +} + +hash_type!(OutputIdHash); + +impl PartialEq for OutputId { + fn eq(&self, other: &Self) -> bool { + self.hash == other.hash + } +} + +impl Hash for OutputId { + fn hash(&self, state: &mut H) { + self.hash.hash(state); + } } impl OutputId { pub fn new( + connector: impl Into, + manufacturer: impl Into, + model: impl Into, + serial_number: impl Into, + ) -> Rc { + let connector = connector.into(); + let manufacturer = manufacturer.into(); + let model = model.into(); + let serial_number = serial_number.into(); + Self::new_(connector, manufacturer, model, serial_number) + } + + fn new_( connector: String, manufacturer: String, model: String, serial_number: String, - ) -> Self { - Self { - connector: serial_number.is_empty().then_some(connector), + ) -> Rc { + let connector = serial_number.is_empty().then_some(connector); + let mut hasher = blake3::Hasher::new(); + hasher.update(&[connector.is_some() as u8]); + let mut hash = |s: &str| { + hasher.update(&(s.len() as u64).to_le_bytes()); + hasher.update(s.as_bytes()); + }; + connector.as_deref().map(&mut hash); + hash(&manufacturer); + hash(&model); + hash(&serial_number); + Rc::new(Self { + _connector: connector, manufacturer, model, serial_number, - } + hash: OutputIdHash(*hasher.finalize().as_bytes()), + }) } } diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 1dc3739a..7c177b7b 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -154,12 +154,7 @@ impl TestBackend { }); let default_monitor_info = MonitorInfo { modes: Some(vec![mode]), - output_id: Rc::new(OutputId { - connector: None, - manufacturer: "jay".to_string(), - model: "TestConnector".to_string(), - serial_number: default_connector.id.to_string(), - }), + output_id: OutputId::new("", "jay", "TestConnector", default_connector.id.to_string()), width_mm: 80, height_mm: 60, non_desktop: false, diff --git a/src/it/tests/t0034_workspace_restoration.rs b/src/it/tests/t0034_workspace_restoration.rs index 03f1f389..4b01cac9 100644 --- a/src/it/tests/t0034_workspace_restoration.rs +++ b/src/it/tests/t0034_workspace_restoration.rs @@ -54,12 +54,7 @@ async fn test(run: Rc) -> TestResult { }); let new_monitor_info = MonitorInfo { modes: Some(vec![]), - output_id: Rc::new(OutputId { - connector: None, - manufacturer: "jay".to_string(), - model: "jay second connector".to_string(), - serial_number: "".to_string(), - }), + output_id: OutputId::new("", "jay", "jay second connector", ""), width_mm: 0, height_mm: 0, non_desktop: false, diff --git a/src/macros.rs b/src/macros.rs index b5088a23..df16289b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -933,3 +933,35 @@ macro_rules! opaque { } }; } + +macro_rules! hash_type { + ($name:ident) => { + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] + pub struct $name(pub [u8; 32]); + + impl $name { + #[allow(clippy::allow_attributes, dead_code)] + pub fn hash(t: impl AsRef<[u8]>) -> Self { + Self(*blake3::hash(t.as_ref()).as_bytes()) + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.serialize(serializer) + } + } + + impl<'de> serde::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + <[u8; 32]>::deserialize(deserializer).map(Self) + } + } + }; +} diff --git a/src/virtual_output.rs b/src/virtual_output.rs index 5ff0fda2..ce153d7e 100644 --- a/src/virtual_output.rs +++ b/src/virtual_output.rs @@ -258,12 +258,7 @@ impl VirtualOutputs { state: state.clone(), id, kernel_id, - output_id: Rc::new(OutputId::new( - kernel_id.to_string(), - "Jay".to_string(), - "VirtualOutput".to_string(), - name.to_string(), - )), + output_id: OutputId::new(kernel_id.to_string(), "Jay", "VirtualOutput", name), name: format!("VO-{}", name), frontend_state: Default::default(), needs_format_update: Default::default(),