From 3ade81444aefb55d1d4cbb0f8a3c8570591f3d8c Mon Sep 17 00:00:00 2001 From: alex-ds13 <145657253+alex-ds13@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:40:18 +0000 Subject: [PATCH] feat(wm): support both serial numbers and device ids This commit makes use of both `serial_number_id` and `device_id` with the first taking priority on all monitor reconciliator code, monitor cache and on postload and reload. This allows using the serial numbers on the `display_index_preferences` config, while keeping compatibility with the use of `device_id`. Using `device_id` should be discouraged since that value can change on restart while serial number doesn't appear to do so. --- komorebi/src/monitor_reconciliator/mod.rs | 60 ++++++++++++++------ komorebi/src/static_config.rs | 68 ++++++++++++----------- 2 files changed, 79 insertions(+), 49 deletions(-) diff --git a/komorebi/src/monitor_reconciliator/mod.rs b/komorebi/src/monitor_reconciliator/mod.rs index 7214fa01..b57fa966 100644 --- a/komorebi/src/monitor_reconciliator/mod.rs +++ b/komorebi/src/monitor_reconciliator/mod.rs @@ -64,12 +64,12 @@ pub fn send_notification(notification: MonitorNotification) { } } -pub fn insert_in_monitor_cache(device_id: &str, config: MonitorConfig) { +pub fn insert_in_monitor_cache(serial_or_device_id: &str, config: MonitorConfig) { let mut monitor_cache = MONITOR_CACHE .get_or_init(|| Mutex::new(HashMap::new())) .lock(); - monitor_cache.insert(device_id.to_string(), config); + monitor_cache.insert(serial_or_device_id.to_string(), config); } pub fn attached_display_devices() -> color_eyre::Result> { @@ -260,8 +260,12 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result // Make sure that in our state any attached displays have the latest Win32 data for monitor in wm.monitors_mut() { for attached in &attached_devices { - if attached.device_id().eq(monitor.device_id()) { + if attached.serial_number_id().eq(monitor.serial_number_id()) + || attached.device_id().eq(monitor.device_id()) + { monitor.set_id(attached.id()); + monitor.set_device_id(attached.device_id().clone()); + monitor.set_serial_number_id(attached.serial_number_id().clone()); monitor.set_name(attached.name().clone()); monitor.set_size(*attached.size()); monitor.set_work_area_size(*attached.work_area_size()); @@ -295,11 +299,17 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result let mut newly_removed_displays = vec![]; for m in wm.monitors().iter() { - if !attached_devices - .iter() - .any(|attached| attached.device_id().eq(m.device_id())) - { - newly_removed_displays.push(m.device_id().clone()); + if !attached_devices.iter().any(|attached| { + attached.serial_number_id().eq(m.serial_number_id()) + || attached.device_id().eq(m.device_id()) + }) { + let id = m + .serial_number_id() + .as_ref() + .map_or(m.device_id().clone(), |sn| sn.clone()); + + newly_removed_displays.push(id.clone()); + for workspace in m.workspaces() { for container in workspace.containers() { // Save the orphaned containers from the removed monitor @@ -308,7 +318,7 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result } // Let's add their state to the cache for later - monitor_cache.insert(m.device_id().clone(), m.into()); + monitor_cache.insert(id, m.into()); } } @@ -320,8 +330,12 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result if !newly_removed_displays.is_empty() { // After we have cached them, remove them from our state - wm.monitors_mut() - .retain(|m| !newly_removed_displays.contains(m.device_id())); + wm.monitors_mut().retain(|m| { + !newly_removed_displays.iter().any(|id| { + m.serial_number_id().as_ref().is_some_and(|sn| sn == id) + || m.device_id() == id + }) + }); } let post_removal_monitor_count = wm.monitors().len(); @@ -361,7 +375,11 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result let post_removal_monitor_count = wm.monitors().len(); - // This is the list of device ids after we have removed detached displays + // This is the list of device ids after we have removed detached displays. We can + // keep this with just the device_ids without the serial numbers since this is used + // only to check which one is the newly added monitor below if there is a new + // monitor. Everything done after with said new monitor will again consider both + // serial number and device ids. let post_removal_device_ids = wm .monitors() .iter() @@ -382,15 +400,21 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result // Look in the updated state for new monitors for m in wm.monitors_mut() { - let device_id = m.device_id().clone(); + let device_id = m.device_id(); // We identify a new monitor when we encounter a new device id - if !post_removal_device_ids.contains(&device_id) { + if !post_removal_device_ids.contains(device_id) { let mut cache_hit = false; + let mut cached_id = String::new(); // Check if that device id exists in the cache for this session - if let Some(cached) = monitor_cache.get(&device_id) { + if let Some((id, cached)) = monitor_cache.get_key_value(device_id).or(m + .serial_number_id() + .as_ref() + .and_then(|sn| monitor_cache.get_key_value(sn))) + { cache_hit = true; + cached_id = id.clone(); - tracing::info!("found monitor and workspace configuration for {device_id} in the monitor cache, applying"); + tracing::info!("found monitor and workspace configuration for {id} in the monitor cache, applying"); // If it does, load all the monitor settings from the cache entry m.ensure_workspace_count(cached.workspaces.len()); @@ -411,8 +435,8 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result } // Entries in the cache should only be used once; remove the entry there was a cache hit - if cache_hit { - monitor_cache.remove(&device_id); + if cache_hit && !cached_id.is_empty() { + monitor_cache.remove(&cached_id); } } } diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 75c46bb8..4bc766d1 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -1250,9 +1250,14 @@ impl StaticConfig { for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() { let preferred_config_idx = { let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); - let c_idx = display_index_preferences - .iter() - .find_map(|(c_idx, m_id)| (monitor.device_id() == m_id).then_some(*c_idx)); + let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| { + (monitor + .serial_number_id() + .as_ref() + .is_some_and(|sn| sn == id) + || monitor.device_id() == id) + .then_some(*c_idx) + }); c_idx }; let idx = preferred_config_idx.or({ @@ -1276,10 +1281,11 @@ impl StaticConfig { // Check if this monitor config is the preferred config for this monitor and store // a copy of the config on the monitor cache if it is. if idx == preferred_config_idx { - monitor_reconciliator::insert_in_monitor_cache( - monitor.device_id(), - monitor_config.clone(), - ); + let id = monitor + .serial_number_id() + .as_ref() + .map_or(monitor.device_id(), |sn| sn); + monitor_reconciliator::insert_in_monitor_cache(id, monitor_config.clone()); } if let Some(used_config_idx) = idx { @@ -1331,24 +1337,21 @@ impl StaticConfig { } // Check for configs that should be tied to a specific display that isn't loaded right now - // and cache those configs with the specific `device_id` so that when those devices are - // connected later we can use the correct config from the cache. + // and cache those configs with the specific `serial_number_id` or `device_id` so that when + // those devices are connected later we can use the correct config from the cache. if configs_with_preference.len() > configs_used.len() { for i in configs_with_preference .iter() .filter(|i| !configs_used.contains(i)) { - let device_id = { + let id = { let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); display_index_preferences.get(i).cloned() }; - if let (Some(device_id), Some(monitor_config)) = - (device_id, value.monitors.as_ref().and_then(|ms| ms.get(*i))) + if let (Some(id), Some(monitor_config)) = + (id, value.monitors.as_ref().and_then(|ms| ms.get(*i))) { - monitor_reconciliator::insert_in_monitor_cache( - &device_id, - monitor_config.clone(), - ); + monitor_reconciliator::insert_in_monitor_cache(&id, monitor_config.clone()); } } } @@ -1378,9 +1381,14 @@ impl StaticConfig { for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() { let preferred_config_idx = { let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); - let c_idx = display_index_preferences - .iter() - .find_map(|(c_idx, m_id)| (monitor.device_id() == m_id).then_some(*c_idx)); + let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| { + (monitor + .serial_number_id() + .as_ref() + .is_some_and(|sn| sn == id) + || monitor.device_id() == id) + .then_some(*c_idx) + }); c_idx }; let idx = preferred_config_idx.or({ @@ -1404,10 +1412,11 @@ impl StaticConfig { // Check if this monitor config is the preferred config for this monitor and store // a copy of the config on the monitor cache if it is. if idx == preferred_config_idx { - monitor_reconciliator::insert_in_monitor_cache( - monitor.device_id(), - monitor_config.clone(), - ); + let id = monitor + .serial_number_id() + .as_ref() + .map_or(monitor.device_id(), |sn| sn); + monitor_reconciliator::insert_in_monitor_cache(id, monitor_config.clone()); } if let Some(used_config_idx) = idx { @@ -1461,24 +1470,21 @@ impl StaticConfig { } // Check for configs that should be tied to a specific display that isn't loaded right now - // and cache those configs with the specific `device_id` so that when those devices are + // and cache those configs with the specific `serial_number_id` so that when those devices are // connected later we can use the correct config from the cache. if configs_with_preference.len() > configs_used.len() { for i in configs_with_preference .iter() .filter(|i| !configs_used.contains(i)) { - let device_id = { + let id = { let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); display_index_preferences.get(i).cloned() }; - if let (Some(device_id), Some(monitor_config)) = - (device_id, value.monitors.as_ref().and_then(|ms| ms.get(*i))) + if let (Some(id), Some(monitor_config)) = + (id, value.monitors.as_ref().and_then(|ms| ms.get(*i))) { - monitor_reconciliator::insert_in_monitor_cache( - &device_id, - monitor_config.clone(), - ); + monitor_reconciliator::insert_in_monitor_cache(&id, monitor_config.clone()); } } }