fix stale primary display selection (#15460)

* fix stale primary display selection

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix stale display selection during login and switching

  - resolve the primary display from the refreshed login snapshot
  - defer display enumeration until authentication succeeds
  - read Wayland displays and primary index from the same cache snapshot
  - reject stale monitor and camera indices during display switching

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix inconsistent display snapshots during login

  - return displays from the same enumeration used to select the primary
  - avoid re-reading the shared display cache after updating it
  - use the same converted snapshot during Wayland initialization

Signed-off-by: 21pages <sunboeasy@gmail.com>

* avoid cloning unchanged display snapshots

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix invalid display subset handling

Signed-off-by: 21pages <sunboeasy@gmail.com>

* minimize code churn in switch_display_to

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
21pages
2026-07-23 17:17:01 +08:00
committed by GitHub
parent 929e989f17
commit beaa754299
4 changed files with 143 additions and 85 deletions

View File

@@ -175,8 +175,7 @@ pub(super) async fn check_init() -> ResultType<()> {
*PIPEWIRE_INITIALIZED.write().unwrap() = true;
let num = all.len();
let primary = super::display_service::get_primary_2(&all);
super::display_service::check_update_displays(&all);
let mut displays = super::display_service::get_sync_displays();
let mut displays = super::display_service::update_sync_displays(&all);
for display in displays.iter_mut() {
display.cursor_embedded = is_cursor_embedded();
}
@@ -220,27 +219,15 @@ pub(super) async fn check_init() -> ResultType<()> {
Ok(())
}
pub(super) async fn get_displays() -> ResultType<Vec<DisplayInfo>> {
pub(super) async fn get_displays_and_primary() -> ResultType<(Vec<DisplayInfo>, usize)> {
check_init().await?;
// Keep one read guard so clear/reinitialization cannot split these across cache snapshots.
let cap_map = CAP_DISPLAY_INFO.read().unwrap();
if let Some(addr) = cap_map.values().next() {
let cap_display_info: *const CapDisplayInfo = *addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
Ok(cap_display_info.displays.clone())
}
} else {
bail!("Failed to get capturer display info");
}
}
pub(super) fn get_primary() -> ResultType<usize> {
let cap_map = CAP_DISPLAY_INFO.read().unwrap();
if let Some(addr) = cap_map.values().next() {
let cap_display_info: *const CapDisplayInfo = *addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
Ok(cap_display_info.primary)
Ok((cap_display_info.displays.clone(), cap_display_info.primary))
}
} else {
bail!("Failed to get capturer display info");