diff --git a/src/privacy_mode/win_virtual_display.rs b/src/privacy_mode/win_virtual_display.rs index de85e7ba8..25997f036 100644 --- a/src/privacy_mode/win_virtual_display.rs +++ b/src/privacy_mode/win_virtual_display.rs @@ -34,7 +34,7 @@ const CONFIG_KEY_REG_RECOVERY: &str = "reg_recovery"; struct Display { dm: DEVMODEW, name: [WCHAR; 32], - _primary: bool, + primary: bool, } pub struct PrivacyModeImpl { @@ -135,7 +135,7 @@ impl PrivacyModeImpl { let display = Display { dm, name: dd.DeviceName, - _primary: primary, + primary, }; let ds = virtual_display_manager::get_cur_device_string(); @@ -357,6 +357,35 @@ impl PrivacyModeImpl { } Ok(()) } + + fn restore(&mut self) { + Self::restore_displays(&self.displays); + Self::restore_displays(&self.virtual_displays); + allow_err!(Self::commit_change_display(0)); + self.restore_plug_out_monitor(); + self.displays.clear(); + self.virtual_displays.clear(); + } + + fn restore_displays(displays: &[Display]) { + for display in displays { + unsafe { + let mut dm = display.dm.clone(); + let flags = if display.primary { + CDS_NORESET | CDS_UPDATEREGISTRY | CDS_SET_PRIMARY + } else { + CDS_NORESET | CDS_UPDATEREGISTRY + }; + ChangeDisplaySettingsExW( + display.name.as_ptr(), + &mut dm, + std::ptr::null_mut(), + flags, + std::ptr::null_mut(), + ); + } + } + } } impl PrivacyMode for PrivacyModeImpl { @@ -431,14 +460,9 @@ impl PrivacyMode for PrivacyModeImpl { ) -> ResultType<()> { self.check_off_conn_id(conn_id)?; super::win_input::unhook()?; - let virtual_display_added = self.virtual_displays_added.len() > 0; - if virtual_display_added { - self.restore_plug_out_monitor(); - } + let _tmp_ignore_changed_holder = crate::display_service::temp_ignore_displays_changed(); + self.restore(); restore_reg_connectivity(false); - if !virtual_display_added { - Self::commit_change_display(CDS_RESET)?; - } if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID { if let Some(state) = state { diff --git a/src/server/display_service.rs b/src/server/display_service.rs index 7260b9c7a..e099e25a0 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -1,4 +1,5 @@ use super::*; +use crate::common::SimpleCallOnReturn; #[cfg(target_os = "linux")] use crate::platform::linux::is_x11; #[cfg(windows)] @@ -7,6 +8,7 @@ use crate::virtual_display_manager; use hbb_common::get_version_number; use hbb_common::protobuf::MessageField; use scrap::Display; +use std::sync::atomic::{AtomicBool, Ordering}; // https://github.com/rustdesk/rustdesk/discussions/6042, avoiding dbus call @@ -29,6 +31,9 @@ lazy_static::lazy_static! { static ref SYNC_DISPLAYS: Arc> = Default::default(); } +// https://github.com/rustdesk/rustdesk/pull/8537 +static TEMP_IGNORE_DISPLAYS_CHANGED: AtomicBool = AtomicBool::new(false); + #[derive(Default)] struct SyncDisplaysInfo { displays: Vec, @@ -39,13 +44,17 @@ impl SyncDisplaysInfo { fn check_changed(&mut self, displays: Vec) { if self.displays.len() != displays.len() { self.displays = displays; - self.is_synced = false; + if !TEMP_IGNORE_DISPLAYS_CHANGED.load(Ordering::Relaxed) { + self.is_synced = false; + } return; } for (i, d) in displays.iter().enumerate() { if d != &self.displays[i] { self.displays = displays; - self.is_synced = false; + if !TEMP_IGNORE_DISPLAYS_CHANGED.load(Ordering::Relaxed) { + self.is_synced = false; + } return; } } @@ -60,6 +69,21 @@ impl SyncDisplaysInfo { } } +pub fn temp_ignore_displays_changed() -> SimpleCallOnReturn { + TEMP_IGNORE_DISPLAYS_CHANGED.store(true, std::sync::atomic::Ordering::Relaxed); + SimpleCallOnReturn { + b: true, + f: Box::new(move || { + // Wait for a while to make sure check_display_changed() is called + // after video service has sending its `SwitchDisplay` message(`try_broadcast_display_changed()`). + std::thread::sleep(Duration::from_millis(1000)); + TEMP_IGNORE_DISPLAYS_CHANGED.store(false, Ordering::Relaxed); + // Trigger the display changed message. + SYNC_DISPLAYS.lock().unwrap().is_synced = false; + }), + } +} + // This function is really useful, though a duplicate check if display changed. // The video server will then send the following messages to the client: // 1. the supported resolutions of the {idx} display @@ -204,9 +228,11 @@ fn get_displays_msg() -> Option { fn run(sp: EmptyExtraFieldService) -> ResultType<()> { while sp.ok() { sp.snapshot(|sps| { - if sps.has_subscribes() { - SYNC_DISPLAYS.lock().unwrap().is_synced = false; - bail!("new subscriber"); + if !TEMP_IGNORE_DISPLAYS_CHANGED.load(Ordering::Relaxed) { + if sps.has_subscribes() { + SYNC_DISPLAYS.lock().unwrap().is_synced = false; + bail!("new subscriber"); + } } Ok(()) })?;