mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-08 13:31:03 +03:00
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:
@@ -503,7 +503,9 @@ impl Connection {
|
||||
tx_video: Some(tx_video),
|
||||
},
|
||||
require_2fa: crate::auth_2fa::get_2fa(None),
|
||||
display_idx: *display_service::PRIMARY_DISPLAY_IDX,
|
||||
// Defer display enumeration until login succeeds. Monitor login replaces this
|
||||
// with the primary index returned with the refreshed display snapshot.
|
||||
display_idx: 0,
|
||||
stream,
|
||||
server,
|
||||
hash,
|
||||
@@ -1891,13 +1893,15 @@ impl Connection {
|
||||
Err(err) => {
|
||||
res.set_error(format!("{}", err));
|
||||
}
|
||||
Ok(displays) => {
|
||||
Ok((displays, primary_display_idx)) => {
|
||||
// For compatibility with old versions, we need to send the displays to the peer.
|
||||
// But the displays may be updated later, before creating the video capturer.
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
self.retina.set_displays(&displays);
|
||||
}
|
||||
// A separate primary lookup here could race with display hot-plug.
|
||||
self.display_idx = primary_display_idx;
|
||||
pi.displays = displays;
|
||||
pi.current_display = self.display_idx as _;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
@@ -2006,8 +2010,8 @@ impl Connection {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let _h = try_start_record_cursor_pos();
|
||||
self.auto_disconnect_timer = Self::get_auto_disconenct_timer();
|
||||
s.try_add_primay_video_service();
|
||||
s.add_connection(self.inner.clone(), &noperms);
|
||||
s.try_add_monitor_service(self.display_idx);
|
||||
s.add_monitor_connection(self.inner.clone(), &noperms, self.display_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4150,7 +4154,9 @@ impl Connection {
|
||||
let display_idx = s.display as usize;
|
||||
if self.display_idx != display_idx {
|
||||
if let Some(server) = self.server.upgrade() {
|
||||
self.switch_display_to(display_idx, server.clone());
|
||||
if !self.switch_display_to(display_idx, server.clone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if !self.view_camera && s.width != 0 && s.height != 0 {
|
||||
@@ -4177,6 +4183,13 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
fn video_source_count(video_source: VideoSource) -> usize {
|
||||
match video_source {
|
||||
VideoSource::Monitor => display_service::get_sync_displays().len(),
|
||||
VideoSource::Camera => camera::Cameras::get_sync_cameras().len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn video_source(&self) -> VideoSource {
|
||||
if self.view_camera {
|
||||
VideoSource::Camera
|
||||
@@ -4185,18 +4198,28 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_display_to(&mut self, display_idx: usize, server: Arc<RwLock<Server>>) {
|
||||
fn switch_display_to(&mut self, display_idx: usize, server: Arc<RwLock<Server>>) -> bool {
|
||||
let source_count = Self::video_source_count(self.video_source());
|
||||
if display_idx >= source_count {
|
||||
// Do not remap an explicit switch: its resolution belongs to the requested source.
|
||||
log::warn!(
|
||||
"Ignore switch to invalid {:?} index {}, available source count: {}",
|
||||
self.video_source(),
|
||||
display_idx,
|
||||
source_count
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let new_service_name = video_service::get_service_name(self.video_source(), display_idx);
|
||||
let old_service_name =
|
||||
video_service::get_service_name(self.video_source(), self.display_idx);
|
||||
let mut lock = server.write().unwrap();
|
||||
if display_idx != *display_service::PRIMARY_DISPLAY_IDX {
|
||||
if !lock.contains(&new_service_name) {
|
||||
lock.add_service(Box::new(video_service::new(
|
||||
self.video_source(),
|
||||
display_idx,
|
||||
)));
|
||||
}
|
||||
if !lock.contains(&new_service_name) {
|
||||
lock.add_service(Box::new(video_service::new(
|
||||
self.video_source(),
|
||||
display_idx,
|
||||
)));
|
||||
}
|
||||
// For versions greater than 1.2.4, a `CaptureDisplays` message will be sent immediately.
|
||||
// Unnecessary capturers will be removed then.
|
||||
@@ -4205,6 +4228,7 @@ impl Connection {
|
||||
}
|
||||
lock.subscribe(&new_service_name, self.inner.clone(), true);
|
||||
self.display_idx = display_idx;
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -4231,26 +4255,61 @@ impl Connection {
|
||||
|
||||
async fn capture_displays(&mut self, add: &[usize], sub: &[usize], set: &[usize]) {
|
||||
let video_source = self.video_source();
|
||||
if let Some(sever) = self.server.upgrade() {
|
||||
let mut lock = sever.write().unwrap();
|
||||
for display in add.iter() {
|
||||
let source_count = Self::video_source_count(video_source);
|
||||
// Only add/set can create services; sub only narrows existing subscriptions.
|
||||
let valid_add = add
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|display| *display < source_count)
|
||||
.collect::<Vec<_>>();
|
||||
let valid_sub = sub
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|display| *display < source_count)
|
||||
.collect::<Vec<_>>();
|
||||
let valid_set = set
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|display| *display < source_count)
|
||||
.collect::<Vec<_>>();
|
||||
let invalid_count =
|
||||
add.len() + sub.len() + set.len() - valid_add.len() - valid_sub.len() - valid_set.len();
|
||||
if invalid_count != 0 {
|
||||
log::warn!(
|
||||
"Ignore {} invalid {:?} indices, available source count: {}",
|
||||
invalid_count,
|
||||
video_source,
|
||||
source_count
|
||||
);
|
||||
}
|
||||
// Passing an invalid sub request as an empty exclude list would unsubscribe all services.
|
||||
if (!add.is_empty() && valid_add.is_empty())
|
||||
|| (add.is_empty() && !sub.is_empty() && valid_sub.is_empty())
|
||||
|| (add.is_empty() && sub.is_empty() && !set.is_empty() && valid_set.is_empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(server) = self.server.upgrade() {
|
||||
let mut lock = server.write().unwrap();
|
||||
for display in valid_add.iter() {
|
||||
let service_name = video_service::get_service_name(video_source, *display);
|
||||
if !lock.contains(&service_name) {
|
||||
lock.add_service(Box::new(video_service::new(video_source, *display)));
|
||||
}
|
||||
}
|
||||
for display in set.iter() {
|
||||
for display in valid_set.iter() {
|
||||
let service_name = video_service::get_service_name(video_source, *display);
|
||||
if !lock.contains(&service_name) {
|
||||
lock.add_service(Box::new(video_service::new(video_source, *display)));
|
||||
}
|
||||
}
|
||||
if !add.is_empty() {
|
||||
lock.capture_displays(self.inner.clone(), video_source, add, true, false);
|
||||
lock.capture_displays(self.inner.clone(), video_source, &valid_add, true, false);
|
||||
} else if !sub.is_empty() {
|
||||
lock.capture_displays(self.inner.clone(), video_source, sub, false, true);
|
||||
lock.capture_displays(self.inner.clone(), video_source, &valid_sub, false, true);
|
||||
} else {
|
||||
lock.capture_displays(self.inner.clone(), video_source, set, true, true);
|
||||
lock.capture_displays(self.inner.clone(), video_source, &valid_set, true, true);
|
||||
}
|
||||
self.multi_ui_session = lock.get_subbed_displays_count(self.inner.id()) > 1;
|
||||
if self.follow_remote_window {
|
||||
|
||||
Reference in New Issue
Block a user