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.
This commit is contained in:
alex-ds13
2025-01-28 18:40:18 +00:00
committed by LGUG2Z
parent c9e98c3cdb
commit 3ade81444a
2 changed files with 79 additions and 49 deletions

View File

@@ -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 let mut monitor_cache = MONITOR_CACHE
.get_or_init(|| Mutex::new(HashMap::new())) .get_or_init(|| Mutex::new(HashMap::new()))
.lock(); .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<Vec<Monitor>> { pub fn attached_display_devices() -> color_eyre::Result<Vec<Monitor>> {
@@ -260,8 +260,12 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
// Make sure that in our state any attached displays have the latest Win32 data // Make sure that in our state any attached displays have the latest Win32 data
for monitor in wm.monitors_mut() { for monitor in wm.monitors_mut() {
for attached in &attached_devices { 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_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_name(attached.name().clone());
monitor.set_size(*attached.size()); monitor.set_size(*attached.size());
monitor.set_work_area_size(*attached.work_area_size()); monitor.set_work_area_size(*attached.work_area_size());
@@ -295,11 +299,17 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let mut newly_removed_displays = vec![]; let mut newly_removed_displays = vec![];
for m in wm.monitors().iter() { for m in wm.monitors().iter() {
if !attached_devices if !attached_devices.iter().any(|attached| {
.iter() attached.serial_number_id().eq(m.serial_number_id())
.any(|attached| attached.device_id().eq(m.device_id())) || attached.device_id().eq(m.device_id())
{ }) {
newly_removed_displays.push(m.device_id().clone()); 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 workspace in m.workspaces() {
for container in workspace.containers() { for container in workspace.containers() {
// Save the orphaned containers from the removed monitor // Save the orphaned containers from the removed monitor
@@ -308,7 +318,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
} }
// Let's add their state to the cache for later // 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<Mutex<WindowManager>>) -> color_eyre::Result
if !newly_removed_displays.is_empty() { if !newly_removed_displays.is_empty() {
// After we have cached them, remove them from our state // After we have cached them, remove them from our state
wm.monitors_mut() wm.monitors_mut().retain(|m| {
.retain(|m| !newly_removed_displays.contains(m.device_id())); !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(); let post_removal_monitor_count = wm.monitors().len();
@@ -361,7 +375,11 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let post_removal_monitor_count = wm.monitors().len(); 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 let post_removal_device_ids = wm
.monitors() .monitors()
.iter() .iter()
@@ -382,15 +400,21 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
// Look in the updated state for new monitors // Look in the updated state for new monitors
for m in wm.monitors_mut() { 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 // 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 cache_hit = false;
let mut cached_id = String::new();
// Check if that device id exists in the cache for this session // 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; 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 // If it does, load all the monitor settings from the cache entry
m.ensure_workspace_count(cached.workspaces.len()); m.ensure_workspace_count(cached.workspaces.len());
@@ -411,8 +435,8 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
} }
// Entries in the cache should only be used once; remove the entry there was a cache hit // Entries in the cache should only be used once; remove the entry there was a cache hit
if cache_hit { if cache_hit && !cached_id.is_empty() {
monitor_cache.remove(&device_id); monitor_cache.remove(&cached_id);
} }
} }
} }

View File

@@ -1250,9 +1250,14 @@ impl StaticConfig {
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() { for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
let preferred_config_idx = { let preferred_config_idx = {
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock();
let c_idx = display_index_preferences let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| {
.iter() (monitor
.find_map(|(c_idx, m_id)| (monitor.device_id() == m_id).then_some(*c_idx)); .serial_number_id()
.as_ref()
.is_some_and(|sn| sn == id)
|| monitor.device_id() == id)
.then_some(*c_idx)
});
c_idx c_idx
}; };
let idx = preferred_config_idx.or({ 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 // 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. // a copy of the config on the monitor cache if it is.
if idx == preferred_config_idx { if idx == preferred_config_idx {
monitor_reconciliator::insert_in_monitor_cache( let id = monitor
monitor.device_id(), .serial_number_id()
monitor_config.clone(), .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 { 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 // 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` or `device_id` so that when
// connected later we can use the correct config from the cache. // those devices are connected later we can use the correct config from the cache.
if configs_with_preference.len() > configs_used.len() { if configs_with_preference.len() > configs_used.len() {
for i in configs_with_preference for i in configs_with_preference
.iter() .iter()
.filter(|i| !configs_used.contains(i)) .filter(|i| !configs_used.contains(i))
{ {
let device_id = { let id = {
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock();
display_index_preferences.get(i).cloned() display_index_preferences.get(i).cloned()
}; };
if let (Some(device_id), Some(monitor_config)) = if let (Some(id), Some(monitor_config)) =
(device_id, value.monitors.as_ref().and_then(|ms| ms.get(*i))) (id, value.monitors.as_ref().and_then(|ms| ms.get(*i)))
{ {
monitor_reconciliator::insert_in_monitor_cache( monitor_reconciliator::insert_in_monitor_cache(&id, monitor_config.clone());
&device_id,
monitor_config.clone(),
);
} }
} }
} }
@@ -1378,9 +1381,14 @@ impl StaticConfig {
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() { for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
let preferred_config_idx = { let preferred_config_idx = {
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock();
let c_idx = display_index_preferences let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| {
.iter() (monitor
.find_map(|(c_idx, m_id)| (monitor.device_id() == m_id).then_some(*c_idx)); .serial_number_id()
.as_ref()
.is_some_and(|sn| sn == id)
|| monitor.device_id() == id)
.then_some(*c_idx)
});
c_idx c_idx
}; };
let idx = preferred_config_idx.or({ 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 // 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. // a copy of the config on the monitor cache if it is.
if idx == preferred_config_idx { if idx == preferred_config_idx {
monitor_reconciliator::insert_in_monitor_cache( let id = monitor
monitor.device_id(), .serial_number_id()
monitor_config.clone(), .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 { 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 // 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. // connected later we can use the correct config from the cache.
if configs_with_preference.len() > configs_used.len() { if configs_with_preference.len() > configs_used.len() {
for i in configs_with_preference for i in configs_with_preference
.iter() .iter()
.filter(|i| !configs_used.contains(i)) .filter(|i| !configs_used.contains(i))
{ {
let device_id = { let id = {
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock(); let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock();
display_index_preferences.get(i).cloned() display_index_preferences.get(i).cloned()
}; };
if let (Some(device_id), Some(monitor_config)) = if let (Some(id), Some(monitor_config)) =
(device_id, value.monitors.as_ref().and_then(|ms| ms.get(*i))) (id, value.monitors.as_ref().and_then(|ms| ms.get(*i)))
{ {
monitor_reconciliator::insert_in_monitor_cache( monitor_reconciliator::insert_in_monitor_cache(&id, monitor_config.clone());
&device_id,
monitor_config.clone(),
);
} }
} }
} }