From c4f09b5598af48bf9682e0c5e29253a61a1360cc Mon Sep 17 00:00:00 2001 From: dignow Date: Tue, 17 Oct 2023 23:31:50 +0800 Subject: [PATCH 1/7] fix, change display resolution Signed-off-by: dignow --- flutter/lib/models/model.dart | 2 +- src/server/connection.rs | 13 +-- src/server/display_service.rs | 155 ++++++++++++++++++++++++---------- src/server/video_service.rs | 121 ++++++++------------------ 4 files changed, 148 insertions(+), 143 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index fb2b96d62..f3d9899e1 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -2282,7 +2282,7 @@ class PeerInfo with ChangeNotifier { if (currentDisplay == kAllDisplayValue) { return null; } - if (currentDisplay > 0 && currentDisplay < displays.length) { + if (currentDisplay >= 0 && currentDisplay < displays.length) { return displays[currentDisplay]; } else { return null; diff --git a/src/server/connection.rs b/src/server/connection.rs index 5f9502397..db594fbcd 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -611,7 +611,7 @@ impl Connection { _ => {}, } } - Some(message::Union::PeerInfo(_)) => { + Some(message::Union::PeerInfo(..)) => { conn.refresh_video_display(None); } _ => {} @@ -1132,11 +1132,13 @@ impl Connection { self.send(msg_out).await; } - match super::display_service::get_displays().await { + match super::display_service::update_get_sync_displays().await { Err(err) => { res.set_error(format!("{}", err)); } Ok(displays) => { + // 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. pi.displays = displays.clone(); pi.current_display = self.display_idx as _; res.set_peer_info(pi); @@ -2139,13 +2141,6 @@ impl Connection { ..Default::default() }); } - - // send display changed message - if let Some(msg_out) = - video_service::make_display_changed_msg(self.display_idx, None) - { - self.send(msg_out).await; - } } } } diff --git a/src/server/display_service.rs b/src/server/display_service.rs index dd4410312..a6d744694 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -5,6 +5,12 @@ use crate::virtual_display_manager; use hbb_common::get_version_number; use hbb_common::protobuf::MessageField; use scrap::Display; +#[cfg(target_os = "linux")] +use std::sync::atomic::{AtomicBool, Ordering}; + +// https://github.com/rustdesk/rustdesk/discussions/6042, avoiding dbus call +#[cfg(target_os = "linux")] +pub(super) static IS_X11: AtomicBool = AtomicBool::new(false); pub const NAME: &'static str = "display"; @@ -19,6 +25,59 @@ lazy_static::lazy_static! { // Initial primary display index. // It should only be updated when the rustdesk server is started, and should not be updated when displays changed. pub static ref PRIMARY_DISPLAY_IDX: usize = get_primary(); + static ref SYNC_DISPLAYS: Arc> = Default::default(); +} + +#[derive(Default)] +struct SyncDisplaysInfo { + displays: Vec, + is_synced: bool, +} + +impl SyncDisplaysInfo { + fn check_changed(&mut self, displays: Vec) { + if self.displays.len() != displays.len() { + self.displays = displays; + self.is_synced = false; + return; + } + for (i, d) in displays.iter().enumerate() { + if d != &self.displays[i] { + self.displays = displays; + self.is_synced = false; + return; + } + } + } + + fn get_update_sync_displays(&mut self) -> Option> { + if self.is_synced { + return None; + } + self.is_synced = true; + Some(self.displays.clone()) + } +} + +pub(super) fn check_display_changed( + idx: usize, + (x, y, w, h): (i32, i32, usize, usize), +) -> Option { + #[cfg(target_os = "linux")] + { + // wayland do not support changing display for now + if !IS_X11.load(Ordering::SeqCst) { + return None; + } + } + + let lock = SYNC_DISPLAYS.lock().unwrap(); + let d = lock.displays.get(idx)?; + if !(d.x == x && d.y == y && d.width == w as i32 && d.height == h as i32) { + Some(d.clone()) + } else { + None + } } #[inline] @@ -74,45 +133,27 @@ pub fn is_privacy_mode_supported() -> bool { return false; } -#[derive(Default)] -struct StateDisplay { - synced_displays: Vec, -} - -impl super::service::Reset for StateDisplay { - fn reset(&mut self) { - self.synced_displays.clear(); - } -} - pub fn new() -> GenericService { - let svc = EmptyExtraFieldService::new(NAME.to_owned(), false); - GenericService::repeat::(&svc.clone(), 300, run); + let svc = EmptyExtraFieldService::new(NAME.to_owned(), true); + GenericService::run(&svc.clone(), run); svc.sp } -fn check_get_displays_changed_msg(last_synced_displays: &mut Vec) -> Option { - let displays = try_get_displays().ok()?; - if displays.len() == last_synced_displays.len() { - return None; - } +fn displays_to_msg(displays: Vec) -> Message { + let mut pi = PeerInfo { + ..Default::default() + }; + pi.displays = displays.clone(); + pi.current_display = 0; + let mut msg_out = Message::new(); + msg_out.set_peer_info(pi); + msg_out +} - // Display to DisplayInfo - let displays = to_display_info(&displays); - if last_synced_displays.len() == 0 { - *last_synced_displays = displays; - None - } else { - let mut pi = PeerInfo { - ..Default::default() - }; - pi.displays = displays.clone(); - pi.current_display = 0; - let mut msg_out = Message::new(); - msg_out.set_peer_info(pi); - *last_synced_displays = displays; - Some(msg_out) - } +fn check_get_displays_changed_msg() -> Option { + check_update_displays(try_get_displays().ok()?); + let displays = SYNC_DISPLAYS.lock().unwrap().get_update_sync_displays()?; + Some(displays_to_msg(displays)) } #[cfg(all(windows, feature = "virtual_display_driver"))] @@ -120,11 +161,28 @@ pub fn try_plug_out_virtual_display() { let _res = virtual_display_manager::plug_out_headless(); } -fn run(sp: EmptyExtraFieldService, state: &mut StateDisplay) -> ResultType<()> { - if let Some(msg_out) = check_get_displays_changed_msg(&mut state.synced_displays) { - sp.send(msg_out); - log::info!("Displays changed"); +fn run(sp: EmptyExtraFieldService) -> ResultType<()> { + #[cfg(target_os = "linux")] + { + IS_X11.store(scrap::is_x11(), Ordering::SeqCst); } + + while sp.ok() { + sp.snapshot(|sps| { + if sps.has_subscribes() { + SYNC_DISPLAYS.lock().unwrap().is_synced = false; + bail!("new subscriber"); + } + Ok(()) + })?; + + if let Some(msg_out) = check_get_displays_changed_msg() { + sp.send(msg_out); + log::info!("Displays changed"); + } + std::thread::sleep(Duration::from_millis(300)); + } + Ok(()) } @@ -167,8 +225,11 @@ pub(super) fn get_original_resolution( .into() } -pub fn to_display_info(all: &Vec) -> Vec { - all.iter() +// Display to DisplayInfo +// The DisplayInfo is be sent to the peer. +fn check_update_displays(all: Vec) { + let displays = all + .iter() .map(|d| { let display_name = d.name(); let original_resolution = get_original_resolution(&display_name, d.width(), d.height()); @@ -184,32 +245,34 @@ pub fn to_display_info(all: &Vec) -> Vec { ..Default::default() } }) - .collect::>() + .collect::>(); + SYNC_DISPLAYS.lock().unwrap().check_changed(displays); } pub fn is_inited_msg() -> Option { #[cfg(target_os = "linux")] - if !scrap::is_x11() { + if !IS_X11.load(Ordering::SeqCst) { return super::wayland::is_inited(); } None } -pub async fn get_displays() -> ResultType> { +pub async fn update_get_sync_displays() -> ResultType> { #[cfg(target_os = "linux")] { - if !scrap::is_x11() { + if !IS_X11.load(Ordering::SeqCst) { return super::wayland::get_displays().await; } } - Ok(to_display_info(&try_get_displays()?)) + check_update_displays(try_get_displays()?); + Ok(SYNC_DISPLAYS.lock().unwrap().displays.clone()) } #[inline] pub fn get_primary() -> usize { #[cfg(target_os = "linux")] { - if !scrap::is_x11() { + if !IS_X11.load(Ordering::SeqCst) { return match super::wayland::get_primary() { Ok(n) => n, Err(_) => 0, diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 1dd793fc3..ca5204c78 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -18,7 +18,11 @@ // to-do: // https://slhck.info/video/2017/03/01/rate-control.html -use super::{service::ServiceTmpl, video_qos::VideoQoS, *}; +#[cfg(target_os = "linux")] +use super::display_service::IS_X11; +use super::{display_service::check_display_changed, service::ServiceTmpl, video_qos::VideoQoS, *}; +#[cfg(target_os = "linux")] +use crate::common::SimpleCallOnReturn; #[cfg(windows)] use crate::{platform::windows::is_process_consent_running, privacy_win_mag}; use hbb_common::{ @@ -38,7 +42,7 @@ use scrap::{ CodecName, Display, TraitCapturer, }; #[cfg(target_os = "linux")] -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::Ordering; #[cfg(windows)] use std::sync::Once; use std::{ @@ -49,7 +53,6 @@ use std::{ }; pub const NAME: &'static str = "video"; -pub const OPTION_DISPLAY_CHANGED: &'static str = "changed"; pub const OPTION_REFRESH: &'static str = "refresh"; lazy_static::lazy_static! { @@ -63,10 +66,6 @@ lazy_static::lazy_static! { pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc> = Default::default(); } -// https://github.com/rustdesk/rustdesk/discussions/6042, avoiding dbus call -#[cfg(target_os = "linux")] -static IS_X11: AtomicBool = AtomicBool::new(false); - #[inline] pub fn notify_video_frame_fetched(conn_id: i32, frame_tm: Option) { FRAME_FETCHED_NOTIFIER.0.send((conn_id, frame_tm)).ok(); @@ -165,35 +164,6 @@ pub fn new(idx: usize) -> GenericService { vs.sp } -fn check_display_changed( - last_n: usize, - last_current: usize, - last_width: usize, - last_height: usize, -) -> bool { - #[cfg(target_os = "linux")] - { - // wayland do not support changing display for now - if !IS_X11.load(Ordering::SeqCst) { - return false; - } - } - - let displays = match try_get_displays() { - Ok(d) => d, - _ => return false, - }; - - let n = displays.len(); - if n != last_n { - return true; - }; - match displays.get(last_current) { - Some(d) => d.width() != last_width || d.height() != last_height, - None => true, - } -} - // Capturer object is expensive, avoiding to create it frequently. fn create_capturer( privacy_mode_id: i32, @@ -327,7 +297,6 @@ pub(super) struct CapturerInfo { pub origin: (i32, i32), pub width: usize, pub height: usize, - pub ndisplay: usize, pub current: usize, pub privacy_mode_id: i32, pub _capturer_privacy_mode_id: i32, @@ -419,7 +388,6 @@ fn get_capturer( origin, width, height, - ndisplay, current, privacy_mode_id, _capturer_privacy_mode_id: capturer_privacy_mode_id, @@ -431,16 +399,21 @@ fn run(vs: VideoService) -> ResultType<()> { #[cfg(not(any(target_os = "android", target_os = "ios")))] let _wake_lock = get_wake_lock(); - #[cfg(target_os = "linux")] - { - IS_X11.store(scrap::is_x11(), Ordering::SeqCst); - } - + // Wayland only support one video capturer for now. It is ok to call ensure_inited() here. + // // ensure_inited() is needed because clear() may be called. // to-do: wayland ensure_inited should pass current display index. // But for now, we do not support multi-screen capture on wayland. #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; + #[cfg(target_os = "linux")] + let wayland_call_on_ret = SimpleCallOnReturn { + b: true, + f: Box::new(|| { + super::wayland::clear(); + }), + }; + #[cfg(windows)] let last_portable_service_running = crate::portable_service::client::running(); #[cfg(not(windows))] @@ -471,16 +444,6 @@ fn run(vs: VideoService) -> ResultType<()> { c.set_use_yuv(encoder.use_yuv()); VIDEO_QOS.lock().unwrap().store_bitrate(encoder.bitrate()); - if sp.is_option_true(OPTION_DISPLAY_CHANGED) { - log::debug!("Broadcasting display changed"); - broadcast_display_changed( - display_idx, - &sp, - Some((c.name.clone(), c.origin.clone(), c.width, c.height)), - ); - sp.set_option_bool(OPTION_DISPLAY_CHANGED, false); - } - if sp.is_option_true(OPTION_REFRESH) { sp.set_option_bool(OPTION_REFRESH, false); } @@ -518,7 +481,7 @@ fn run(vs: VideoService) -> ResultType<()> { } drop(video_qos); - if sp.is_option_true(OPTION_DISPLAY_CHANGED) || sp.is_option_true(OPTION_REFRESH) { + if sp.is_option_true(OPTION_REFRESH) { bail!("SWITCH"); } if codec_name != Encoder::negotiated_codec() { @@ -540,12 +503,11 @@ fn run(vs: VideoService) -> ResultType<()> { let now = time::Instant::now(); if last_check_displays.elapsed().as_millis() > 1000 { last_check_displays = now; - - // Capturer on macos does not return Err event the solution is changed. - #[cfg(target_os = "macos")] - if check_display_changed(c.ndisplay, c.current, c.width, c.height) { - sp.set_option_bool(OPTION_DISPLAY_CHANGED, true); - log::info!("Displays changed"); + if let Some(display) = + check_display_changed(c.current, (c.origin.0, c.origin.1, c.width, c.height)) + { + log::info!("Display {} changed", display); + broadcast_display_changed(display_idx, &sp, &c.name, display); bail!("SWITCH"); } } @@ -624,21 +586,12 @@ fn run(vs: VideoService) -> ResultType<()> { } } Err(err) => { - if check_display_changed(c.ndisplay, c.current, c.width, c.height) { - log::info!("Displays changed"); - #[cfg(target_os = "linux")] - super::wayland::clear(); - sp.set_option_bool(OPTION_DISPLAY_CHANGED, true); - bail!("SWITCH"); - } - #[cfg(windows)] if !c.is_gdi() { c.set_gdi(); log::info!("dxgi error, fall back to gdi: {:?}", err); continue; } - return Err(err.into()); } _ => { @@ -671,9 +624,6 @@ fn run(vs: VideoService) -> ResultType<()> { } } - #[cfg(target_os = "linux")] - super::wayland::clear(); - Ok(()) } @@ -892,9 +842,10 @@ fn get_wake_lock() -> crate::platform::WakeLock { fn broadcast_display_changed( display_idx: usize, sp: &GenericService, - display_meta: Option<(String, (i32, i32), usize, usize)>, + name: &str, + display: DisplayInfo, ) { - if let Some(msg_out) = make_display_changed_msg(display_idx, display_meta) { + if let Some(msg_out) = make_display_changed_msg(display_idx, name, display) { sp.send(msg_out); } } @@ -913,34 +864,30 @@ fn get_display_info_simple_meta(display_idx: usize) -> Option<(String, (i32, i32 } } -pub fn make_display_changed_msg( +fn make_display_changed_msg( display_idx: usize, - display_meta: Option<(String, (i32, i32), usize, usize)>, + name: &str, + display: DisplayInfo, ) -> Option { let mut misc = Misc::new(); - let (name, origin, width, height) = match display_meta { - Some(d) => d, - None => get_display_info_simple_meta(display_idx)?, - }; - let original_resolution = display_service::get_original_resolution(&name, width, height); misc.set_switch_display(SwitchDisplay { display: display_idx as _, - x: origin.0, - y: origin.1, - width: width as _, - height: height as _, + x: display.x, + y: display.y, + width: display.width, + height: display.height, cursor_embedded: display_service::capture_cursor_embedded(), #[cfg(not(any(target_os = "android", target_os = "ios")))] resolutions: Some(SupportedResolutions { resolutions: if name.is_empty() { vec![] } else { - crate::platform::resolutions(&name) + crate::platform::resolutions(name) }, ..SupportedResolutions::default() }) .into(), - original_resolution, + original_resolution: display.original_resolution, ..Default::default() }); let mut msg_out = Message::new(); From a32e740242339a078d1e30f68d28a828c0a9ba24 Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 18 Oct 2023 08:07:00 +0800 Subject: [PATCH 2/7] fix build linux Signed-off-by: dignow --- src/core_main.rs | 2 +- src/server/display_service.rs | 11 ++++++++--- src/server/video_service.rs | 2 +- src/server/wayland.rs | 15 ++++++++------- src/tray.rs | 15 +++++++++------ 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/core_main.rs b/src/core_main.rs index e7bb4d0f6..f1669d36b 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,4 +1,4 @@ -#[cfg(not(any(target_os = "android", target_os = "ios")))] +#[cfg(windows)] use crate::client::translate; #[cfg(not(debug_assertions))] #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/server/display_service.rs b/src/server/display_service.rs index a6d744694..878526872 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -151,7 +151,7 @@ fn displays_to_msg(displays: Vec) -> Message { } fn check_get_displays_changed_msg() -> Option { - check_update_displays(try_get_displays().ok()?); + check_update_displays(&try_get_displays().ok()?); let displays = SYNC_DISPLAYS.lock().unwrap().get_update_sync_displays()?; Some(displays_to_msg(displays)) } @@ -225,9 +225,14 @@ pub(super) fn get_original_resolution( .into() } +#[cfg(target_os = "linux")] +pub(super) fn get_sync_displays() -> Vec { + SYNC_DISPLAYS.lock().unwrap().displays.clone() +} + // Display to DisplayInfo // The DisplayInfo is be sent to the peer. -fn check_update_displays(all: Vec) { +pub(super) fn check_update_displays(all: &Vec) { let displays = all .iter() .map(|d| { @@ -264,7 +269,7 @@ pub async fn update_get_sync_displays() -> ResultType> { return super::wayland::get_displays().await; } } - check_update_displays(try_get_displays()?); + check_update_displays(&try_get_displays()?); Ok(SYNC_DISPLAYS.lock().unwrap().displays.clone()) } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index ca5204c78..25b08bb3e 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -407,7 +407,7 @@ fn run(vs: VideoService) -> ResultType<()> { #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; #[cfg(target_os = "linux")] - let wayland_call_on_ret = SimpleCallOnReturn { + let _wayland_call_on_ret = SimpleCallOnReturn { b: true, f: Box::new(|| { super::wayland::clear(); diff --git a/src/server/wayland.rs b/src/server/wayland.rs index f26b27b20..2be67bc32 100644 --- a/src/server/wayland.rs +++ b/src/server/wayland.rs @@ -84,7 +84,6 @@ impl TraitCapturer for CapturerPtr { struct CapDisplayInfo { rects: Vec<((i32, i32), usize, usize)>, displays: Vec, - num: usize, primary: usize, current: usize, capturer: CapturerPtr, @@ -146,7 +145,8 @@ pub(super) async fn check_init() -> ResultType<()> { let num = all.len(); let primary = super::display_service::get_primary_2(&all); let current = primary; - let mut displays = super::display_service::to_display_info(&all); + super::display_service::check_update_displays(&all); + let mut displays = super::display_service::get_sync_displays(); for display in displays.iter_mut() { display.cursor_embedded = is_cursor_embedded(); } @@ -173,12 +173,15 @@ pub(super) async fn check_init() -> ResultType<()> { Some(result) if !result.is_empty() => { let resolution: Vec<&str> = result.split(" ").collect(); let w: i32 = resolution[0].parse().unwrap_or(origin.0 + width as i32); - let h: i32 = resolution[2].trim_end_matches(",").parse().unwrap_or(origin.1 + height as i32); + let h: i32 = resolution[2] + .trim_end_matches(",") + .parse() + .unwrap_or(origin.1 + height as i32); (w, h) } - _ => (origin.0 + width as i32, origin.1 + height as i32) + _ => (origin.0 + width as i32, origin.1 + height as i32), }; - + minx = 0; maxx = max_width; miny = 0; @@ -191,7 +194,6 @@ pub(super) async fn check_init() -> ResultType<()> { let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo { rects, displays, - num, primary, current, capturer, @@ -273,7 +275,6 @@ pub(super) fn get_capturer() -> ResultType { origin: rect.0, width: rect.1, height: rect.2, - ndisplay: cap_display_info.num, current: cap_display_info.current, privacy_mode_id: 0, _capturer_privacy_mode_id: 0, diff --git a/src/tray.rs b/src/tray.rs index 7b05be101..221a8a7fe 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -1,9 +1,12 @@ -use crate::{client::translate, ipc::Data}; -use hbb_common::{allow_err, log, tokio}; -use std::{ - sync::{Arc, Mutex}, - time::Duration, -}; +use crate::client::translate; +#[cfg(windows)] +use crate::ipc::Data; +#[cfg(windows)] +use hbb_common::tokio; +use hbb_common::{allow_err, log}; +use std::sync::{Arc, Mutex}; +#[cfg(windows)] +use std::time::Duration; pub fn start_tray() { allow_err!(make_tray()); From c1b865d00e95bb63921b42d914b5a8307373785b Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 18 Oct 2023 09:59:02 +0800 Subject: [PATCH 3/7] fix, change_display_resolution, add comments Signed-off-by: dignow --- flutter/lib/models/model.dart | 13 ++++++------- src/server/display_service.rs | 12 ++++++++++++ src/server/video_service.rs | 34 +++++++++++++++++----------------- src/server/wayland.rs | 3 +++ 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index f3d9899e1..ce4cbe523 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -430,14 +430,12 @@ class FfiModel with ChangeNotifier { Map evt, SessionID sessionId, String peerId) { final curDisplay = int.parse(evt['display']); - // The message should be handled by the another UI session. - if (isChooseDisplayToOpenInNewWindow(_pi, sessionId)) { - if (curDisplay != _pi.currentDisplay) { - return; - } - } - if (_pi.currentDisplay != kAllDisplayValue) { + if (bind.peerGetDefaultSessionsCount(id: peerId) > 1) { + if (curDisplay != _pi.currentDisplay) { + return; + } + } _pi.currentDisplay = curDisplay; } @@ -825,6 +823,7 @@ class FfiModel with ChangeNotifier { } _pi.displays = newDisplays; _pi.displaysCount.value = _pi.displays.length; + if (_pi.currentDisplay == kAllDisplayValue) { updateCurDisplay(sessionId); // to-do: What if the displays are changed? diff --git a/src/server/display_service.rs b/src/server/display_service.rs index 878526872..eb9b4272b 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -59,7 +59,10 @@ impl SyncDisplaysInfo { } } +// This function is really useful, though a duplicate check if display changed. +// Because the video server will send the supported resolutions of the {idx} display to the subscribers. pub(super) fn check_display_changed( + ndisplay: usize, idx: usize, (x, y, w, h): (i32, i32, usize, usize), ) -> Option { @@ -72,7 +75,14 @@ pub(super) fn check_display_changed( } let lock = SYNC_DISPLAYS.lock().unwrap(); + // If plugging out a monitor && lock.displays.get(idx) is None. + // 1. The client version < 1.2.4. The client side has to reconnect. + // 2. The client version > 1.2.4, The client side can handle the case becuase sync peer info message will be sent. + // But it is acceptable to for the user to reconnect manually, becuase the monitor is unplugged. let d = lock.displays.get(idx)?; + if ndisplay != lock.displays.len() { + return Some(d.clone()); + } if !(d.x == x && d.y == y && d.width == w as i32 && d.height == h as i32) { Some(d.clone()) } else { @@ -144,6 +154,8 @@ fn displays_to_msg(displays: Vec) -> Message { ..Default::default() }; pi.displays = displays.clone(); + // current_display should not be used in server. + // It is set to 0 for compatibility with old clients. pi.current_display = 0; let mut msg_out = Message::new(); msg_out.set_peer_info(pi); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 25b08bb3e..664ca6628 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -297,6 +297,7 @@ pub(super) struct CapturerInfo { pub origin: (i32, i32), pub width: usize, pub height: usize, + pub ndisplay: usize, pub current: usize, pub privacy_mode_id: i32, pub _capturer_privacy_mode_id: i32, @@ -388,6 +389,7 @@ fn get_capturer( origin, width, height, + ndisplay, current, privacy_mode_id, _capturer_privacy_mode_id: capturer_privacy_mode_id, @@ -503,9 +505,11 @@ fn run(vs: VideoService) -> ResultType<()> { let now = time::Instant::now(); if last_check_displays.elapsed().as_millis() > 1000 { last_check_displays = now; - if let Some(display) = - check_display_changed(c.current, (c.origin.0, c.origin.1, c.width, c.height)) - { + if let Some(display) = check_display_changed( + c.ndisplay, + c.current, + (c.origin.0, c.origin.1, c.width, c.height), + ) { log::info!("Display {} changed", display); broadcast_display_changed(display_idx, &sp, &c.name, display); bail!("SWITCH"); @@ -586,6 +590,16 @@ fn run(vs: VideoService) -> ResultType<()> { } } Err(err) => { + if let Some(display) = check_display_changed( + c.ndisplay, + c.current, + (c.origin.0, c.origin.1, c.width, c.height), + ) { + log::info!("Display {} changed", display); + broadcast_display_changed(display_idx, &sp, &c.name, display); + bail!("SWITCH"); + } + #[cfg(windows)] if !c.is_gdi() { c.set_gdi(); @@ -850,20 +864,6 @@ fn broadcast_display_changed( } } -fn get_display_info_simple_meta(display_idx: usize) -> Option<(String, (i32, i32), usize, usize)> { - let displays = display_service::try_get_displays().ok()?; - if let Some(display) = displays.get(display_idx) { - Some(( - display.name(), - display.origin(), - display.width(), - display.height(), - )) - } else { - None - } -} - fn make_display_changed_msg( display_idx: usize, name: &str, diff --git a/src/server/wayland.rs b/src/server/wayland.rs index 2be67bc32..02e83fdc2 100644 --- a/src/server/wayland.rs +++ b/src/server/wayland.rs @@ -84,6 +84,7 @@ impl TraitCapturer for CapturerPtr { struct CapDisplayInfo { rects: Vec<((i32, i32), usize, usize)>, displays: Vec, + num: usize, primary: usize, current: usize, capturer: CapturerPtr, @@ -194,6 +195,7 @@ pub(super) async fn check_init() -> ResultType<()> { let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo { rects, displays, + num, primary, current, capturer, @@ -275,6 +277,7 @@ pub(super) fn get_capturer() -> ResultType { origin: rect.0, width: rect.1, height: rect.2, + ndisplay: cap_display_info.num, current: cap_display_info.current, privacy_mode_id: 0, _capturer_privacy_mode_id: 0, From 21f7d6c9b9c6be0810f18d0592e632fcb9c7f400 Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 18 Oct 2023 10:45:46 +0800 Subject: [PATCH 4/7] fix/change_display_resolution, send switch display msg Signed-off-by: dignow --- src/server/connection.rs | 6 ++++++ src/server/display_service.rs | 4 ++++ src/server/video_service.rs | 33 ++++++++++++++++++--------------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index db594fbcd..1b005ea5e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2142,6 +2142,12 @@ impl Connection { }); } } + + // Send display changed message. + // For compatibility with old versions ( < 1.2.4 ). + if let Some(msg_out) = video_service::make_display_changed_msg(self.display_idx, None) { + self.send(msg_out).await; + } } } diff --git a/src/server/display_service.rs b/src/server/display_service.rs index eb9b4272b..d1d14f0f9 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -242,6 +242,10 @@ pub(super) fn get_sync_displays() -> Vec { SYNC_DISPLAYS.lock().unwrap().displays.clone() } +pub(super) fn get_display_info(idx: usize) -> Option { + SYNC_DISPLAYS.lock().unwrap().displays.get(idx).cloned() +} + // Display to DisplayInfo // The DisplayInfo is be sent to the peer. pub(super) fn check_update_displays(all: &Vec) { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 664ca6628..0e1c3cb23 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -20,7 +20,12 @@ #[cfg(target_os = "linux")] use super::display_service::IS_X11; -use super::{display_service::check_display_changed, service::ServiceTmpl, video_qos::VideoQoS, *}; +use super::{ + display_service::{check_display_changed, get_display_info}, + service::ServiceTmpl, + video_qos::VideoQoS, + *, +}; #[cfg(target_os = "linux")] use crate::common::SimpleCallOnReturn; #[cfg(windows)] @@ -511,7 +516,7 @@ fn run(vs: VideoService) -> ResultType<()> { (c.origin.0, c.origin.1, c.width, c.height), ) { log::info!("Display {} changed", display); - broadcast_display_changed(display_idx, &sp, &c.name, display); + broadcast_display_changed(display_idx, &sp, display); bail!("SWITCH"); } } @@ -596,7 +601,7 @@ fn run(vs: VideoService) -> ResultType<()> { (c.origin.0, c.origin.1, c.width, c.height), ) { log::info!("Display {} changed", display); - broadcast_display_changed(display_idx, &sp, &c.name, display); + broadcast_display_changed(display_idx, &sp, display); bail!("SWITCH"); } @@ -853,22 +858,20 @@ fn get_wake_lock() -> crate::platform::WakeLock { } #[inline] -fn broadcast_display_changed( - display_idx: usize, - sp: &GenericService, - name: &str, - display: DisplayInfo, -) { - if let Some(msg_out) = make_display_changed_msg(display_idx, name, display) { +fn broadcast_display_changed(display_idx: usize, sp: &GenericService, display: DisplayInfo) { + if let Some(msg_out) = make_display_changed_msg(display_idx, Some(display)) { sp.send(msg_out); } } -fn make_display_changed_msg( +pub fn make_display_changed_msg( display_idx: usize, - name: &str, - display: DisplayInfo, + opt_display: Option, ) -> Option { + let display = match opt_display { + Some(d) => d, + None => get_display_info(display_idx)?, + }; let mut misc = Misc::new(); misc.set_switch_display(SwitchDisplay { display: display_idx as _, @@ -879,10 +882,10 @@ fn make_display_changed_msg( cursor_embedded: display_service::capture_cursor_embedded(), #[cfg(not(any(target_os = "android", target_os = "ios")))] resolutions: Some(SupportedResolutions { - resolutions: if name.is_empty() { + resolutions: if display.name.is_empty() { vec![] } else { - crate::platform::resolutions(name) + crate::platform::resolutions(&display.name) }, ..SupportedResolutions::default() }) From 4d537b2a9ad8d57b670cdb7f9af6df4052a7a346 Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 18 Oct 2023 11:35:28 +0800 Subject: [PATCH 5/7] fix/change_display_resolution, send change resolution message Signed-off-by: dignow --- src/server/display_service.rs | 4 +++- src/server/video_service.rs | 42 ++++++++++++++++------------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/server/display_service.rs b/src/server/display_service.rs index d1d14f0f9..780833f78 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -60,7 +60,9 @@ impl SyncDisplaysInfo { } // This function is really useful, though a duplicate check if display changed. -// Because the video server will send the supported resolutions of the {idx} display to the subscribers. +// The video server will then send the following messages to the client: +// 1. the supported resolutions of the {idx} display +// 2. the switch resolution message, so that the client can record the custom resolution. pub(super) fn check_display_changed( ndisplay: usize, idx: usize, diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 0e1c3cb23..5b7496015 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -298,7 +298,6 @@ fn check_uac_switch(privacy_mode_id: i32, capturer_privacy_mode_id: i32) -> Resu } pub(super) struct CapturerInfo { - pub name: String, pub origin: (i32, i32), pub width: usize, pub height: usize, @@ -390,7 +389,6 @@ fn get_capturer( portable_service_running, )?; Ok(CapturerInfo { - name, origin, width, height, @@ -489,6 +487,7 @@ fn run(vs: VideoService) -> ResultType<()> { drop(video_qos); if sp.is_option_true(OPTION_REFRESH) { + let _ = try_broadcast_display_changed(&sp, display_idx, &c); bail!("SWITCH"); } if codec_name != Encoder::negotiated_codec() { @@ -510,15 +509,7 @@ fn run(vs: VideoService) -> ResultType<()> { let now = time::Instant::now(); if last_check_displays.elapsed().as_millis() > 1000 { last_check_displays = now; - if let Some(display) = check_display_changed( - c.ndisplay, - c.current, - (c.origin.0, c.origin.1, c.width, c.height), - ) { - log::info!("Display {} changed", display); - broadcast_display_changed(display_idx, &sp, display); - bail!("SWITCH"); - } + try_broadcast_display_changed(&sp, display_idx, &c)?; } frame_controller.reset(); @@ -595,15 +586,7 @@ fn run(vs: VideoService) -> ResultType<()> { } } Err(err) => { - if let Some(display) = check_display_changed( - c.ndisplay, - c.current, - (c.origin.0, c.origin.1, c.width, c.height), - ) { - log::info!("Display {} changed", display); - broadcast_display_changed(display_idx, &sp, display); - bail!("SWITCH"); - } + try_broadcast_display_changed(&sp, display_idx, &c)?; #[cfg(windows)] if !c.is_gdi() { @@ -858,10 +841,23 @@ fn get_wake_lock() -> crate::platform::WakeLock { } #[inline] -fn broadcast_display_changed(display_idx: usize, sp: &GenericService, display: DisplayInfo) { - if let Some(msg_out) = make_display_changed_msg(display_idx, Some(display)) { - sp.send(msg_out); +fn try_broadcast_display_changed( + sp: &GenericService, + display_idx: usize, + cap: &CapturerInfo, +) -> ResultType<()> { + if let Some(display) = check_display_changed( + cap.ndisplay, + cap.current, + (cap.origin.0, cap.origin.1, cap.width, cap.height), + ) { + log::info!("Display {} changed", display); + if let Some(msg_out) = make_display_changed_msg(display_idx, Some(display)) { + sp.send(msg_out); + bail!("SWITCH"); + } } + Ok(()) } pub fn make_display_changed_msg( From b55c916e7719e88ba05d344218951d09d1844a6e Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 18 Oct 2023 11:39:05 +0800 Subject: [PATCH 6/7] add comments Signed-off-by: dignow --- src/server/video_service.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 5b7496015..074b041c9 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -509,6 +509,8 @@ fn run(vs: VideoService) -> ResultType<()> { let now = time::Instant::now(); if last_check_displays.elapsed().as_millis() > 1000 { last_check_displays = now; + // This check may be redundant, but it is better to be safe. + // The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough. try_broadcast_display_changed(&sp, display_idx, &c)?; } @@ -586,6 +588,8 @@ fn run(vs: VideoService) -> ResultType<()> { } } Err(err) => { + // This check may be redundant, but it is better to be safe. + // The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough. try_broadcast_display_changed(&sp, display_idx, &c)?; #[cfg(windows)] From ed28928c8437284fa5300851bdb5096cd93f61e2 Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 18 Oct 2023 12:06:55 +0800 Subject: [PATCH 7/7] fix build linux Signed-off-by: dignow --- src/server/wayland.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/server/wayland.rs b/src/server/wayland.rs index 02e83fdc2..38edc4472 100644 --- a/src/server/wayland.rs +++ b/src/server/wayland.rs @@ -271,9 +271,6 @@ pub(super) fn get_capturer() -> ResultType { let cap_display_info = &*cap_display_info; let rect = cap_display_info.rects[cap_display_info.current]; Ok(super::video_service::CapturerInfo { - name: cap_display_info.displays[cap_display_info.current] - .name - .clone(), origin: rect.0, width: rect.1, height: rect.2,