From c91cb9f0613443d6f1274ae0cbafaea126f9fda9 Mon Sep 17 00:00:00 2001 From: alex-ds13 <145657253+alex-ds13@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:02:21 +0000 Subject: [PATCH] fix(wm): improve display_index_preferences selection This commit reworks the way the `postload` and the `reload` functions apply the monitor configs to the monitors. Previously it was looping through the monitor configs and applying them to the monitor with the index corresponding to the config's index. However this isn't correct, since the user might set the preferred indices for 3 monitors (like monitor A, B and C), with the preferred index set to 0 for A, 1 for B and 2 for C, but if only monitors A and C are connected then komorebi would apply config 0 to A and config 1 to C, which is wrong it should be 2 for C. This commit changes the way the configs are applied on those functions. Now it loops through the existing monitors (already in order), then checks if the monitor has a preferred config index, if it does it uses that one, if it doesn't then it uses the first monitor config that isn't a preferred index for some other monitor and that hasn't been used yet. For the situation above it means that it would still apply config 2 to monitor C. And in case there aren't any display_index_preferences set it will still apply the configs in order. --- komorebi/src/static_config.rs | 159 ++++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 36 deletions(-) diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 751b54e4..a6bb9cd6 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -1238,32 +1238,73 @@ impl StaticConfig { let value = Self::read(path)?; let mut wm = wm.lock(); - if let Some(monitors) = value.monitors { - for (i, monitor) in monitors.iter().enumerate() { - { - let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); - if let Some(device_id) = display_index_preferences.get(&i) { - monitor_reconciliator::insert_in_monitor_cache(device_id, monitor.clone()); - } + let configs_with_preference: Vec<_> = + DISPLAY_INDEX_PREFERENCES.lock().keys().copied().collect(); + let mut configs_used = Vec::new(); + + let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); + workspace_matching_rules.clear(); + drop(workspace_matching_rules); + + for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() { + let 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)); + drop(display_index_preferences); + c_idx + }; + let idx = config_idx.or({ + // Monitor without preferred config idx. + // Get index of first config that is not a preferred config of some other monitor + // and that has not been used yet. This might return `None` as well, in that case + // this monitor won't have a config tied to it and will use the default values. + let m_config_count = value + .monitors + .as_ref() + .map(|ms| ms.len()) + .unwrap_or_default(); + (0..m_config_count) + .find(|i| !configs_with_preference.contains(i) && !configs_used.contains(i)) + }); + if let Some(monitor_config) = value + .monitors + .as_ref() + .and_then(|ms| idx.and_then(|i| ms.get(i))) + { + // 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 == config_idx { + monitor_reconciliator::insert_in_monitor_cache( + monitor.device_id(), + monitor_config.clone(), + ); } - if let Some(m) = wm.monitors_mut().get_mut(i) { - m.ensure_workspace_count(monitor.workspaces.len()); - m.set_work_area_offset(monitor.work_area_offset); - m.set_window_based_work_area_offset(monitor.window_based_work_area_offset); - m.set_window_based_work_area_offset_limit( - monitor.window_based_work_area_offset_limit.unwrap_or(1), - ); + if let Some(used_config_idx) = idx { + configs_used.push(used_config_idx); + } - for (j, ws) in m.workspaces_mut().iter_mut().enumerate() { - if let Some(workspace_config) = monitor.workspaces.get(j) { - ws.load_static_config(workspace_config)?; - } + monitor.ensure_workspace_count(monitor_config.workspaces.len()); + monitor.set_work_area_offset(monitor_config.work_area_offset); + monitor.set_window_based_work_area_offset( + monitor_config.window_based_work_area_offset, + ); + monitor.set_window_based_work_area_offset_limit( + monitor_config + .window_based_work_area_offset_limit + .unwrap_or(1), + ); + + for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() { + if let Some(workspace_config) = monitor_config.workspaces.get(j) { + ws.load_static_config(workspace_config)?; } } let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); - for (j, ws) in monitor.workspaces.iter().enumerate() { + for (j, ws) in monitor_config.workspaces.iter().enumerate() { if let Some(rules) = &ws.workspace_rules { for r in rules { workspace_matching_rules.push(WorkspaceMatchingRule { @@ -1303,29 +1344,75 @@ impl StaticConfig { value.apply_globals()?; - if let Some(monitors) = value.monitors { - let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); - workspace_matching_rules.clear(); + let configs_with_preference: Vec<_> = + DISPLAY_INDEX_PREFERENCES.lock().keys().copied().collect(); + let mut configs_used = Vec::new(); - for (i, monitor) in monitors.iter().enumerate() { - if let Some(m) = wm.monitors_mut().get_mut(i) { - m.ensure_workspace_count(monitor.workspaces.len()); - if m.work_area_offset().is_none() { - m.set_work_area_offset(monitor.work_area_offset); - } - m.set_window_based_work_area_offset(monitor.window_based_work_area_offset); - m.set_window_based_work_area_offset_limit( - monitor.window_based_work_area_offset_limit.unwrap_or(1), + let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); + workspace_matching_rules.clear(); + drop(workspace_matching_rules); + + for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() { + let 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)); + drop(display_index_preferences); + c_idx + }; + let idx = config_idx.or({ + // Monitor without preferred config idx. + // Get index of first config that is not a preferred config of some other monitor + // and that has not been used yet. This might return `None` as well, in that case + // this monitor won't have a config tied to it and will use the default values. + let m_config_count = value + .monitors + .as_ref() + .map(|ms| ms.len()) + .unwrap_or_default(); + (0..m_config_count) + .find(|i| !configs_with_preference.contains(i) && !configs_used.contains(i)) + }); + if let Some(monitor_config) = value + .monitors + .as_ref() + .and_then(|ms| idx.and_then(|i| ms.get(i))) + { + // 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 == config_idx { + monitor_reconciliator::insert_in_monitor_cache( + monitor.device_id(), + monitor_config.clone(), ); + } - for (j, ws) in m.workspaces_mut().iter_mut().enumerate() { - if let Some(workspace_config) = monitor.workspaces.get(j) { - ws.load_static_config(workspace_config)?; - } + if let Some(used_config_idx) = idx { + configs_used.push(used_config_idx); + } + + monitor.ensure_workspace_count(monitor_config.workspaces.len()); + if monitor.work_area_offset().is_none() { + monitor.set_work_area_offset(monitor_config.work_area_offset); + } + monitor.set_window_based_work_area_offset( + monitor_config.window_based_work_area_offset, + ); + monitor.set_window_based_work_area_offset_limit( + monitor_config + .window_based_work_area_offset_limit + .unwrap_or(1), + ); + + for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() { + if let Some(workspace_config) = monitor_config.workspaces.get(j) { + ws.load_static_config(workspace_config)?; } } - for (j, ws) in monitor.workspaces.iter().enumerate() { + let mut workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock(); + for (j, ws) in monitor_config.workspaces.iter().enumerate() { if let Some(rules) = &ws.workspace_rules { for r in rules { workspace_matching_rules.push(WorkspaceMatchingRule {