diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index e58c361f8..328d53c38 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2935,6 +2935,16 @@ openMonitorInTheSameTab(int i, FFI ffi, PeerInfo pi, final displays = i == kAllDisplayValue ? List.generate(pi.displays.length, (index) => index) : [i]; + // Try clear image model before switching from all displays + // 1. The remote side has multiple displays. + // 2. Do not use texture render. + // 3. Connect to Display 1. + // 4. Switch to multi-displays `kAllDisplayValue` + // 5. Switch to Display 2. + // Then the remote page will display last picture of Display 1 at the beginning. + if (pi.forceTextureRender && i != kAllDisplayValue) { + ffi.imageModel.clearImage(); + } bind.sessionSwitchDisplay( isDesktop: isDesktop, sessionId: ffi.sessionId, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 51578055d..2ad9fdb9b 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1173,6 +1173,8 @@ class ImageModel with ChangeNotifier { addCallbackOnFirstImage(Function(String) cb) => callbacksOnFirstImage.add(cb); + clearImage() => _image = null; + onRgba(int display, Uint8List rgba) { final pid = parent.target?.id; final rect = parent.target?.ffiModel.pi.getDisplayRect(display); diff --git a/src/flutter.rs b/src/flutter.rs index 3da754506..1e7f1f283 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1889,6 +1889,8 @@ pub mod sessions { let mut write_lock = s.ui_handler.session_handlers.write().unwrap(); if let Some(h) = write_lock.get_mut(&session_id) { h.displays = value.iter().map(|x| *x as usize).collect::<_>(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] + let displays_refresh = value.clone(); if value.len() == 1 { // Switch display. // This operation will also cause the peer to send a switch display message. @@ -1912,6 +1914,17 @@ pub mod sessions { // Try capture all displays. s.capture_displays(vec![], vec![], value); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + let is_support_multi_ui_session = crate::common::is_support_multi_ui_session( + &s.ui_handler.peer_info.read().unwrap().version, + ); + if is_support_multi_ui_session { + for display in displays_refresh.iter() { + s.refresh_video(*display); + } + } + } break; } } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 7fd5dee1f..54108515d 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -68,6 +68,8 @@ use std::{ pub const NAME: &'static str = "video"; pub const OPTION_REFRESH: &'static str = "refresh"; +const REFRESH_MIN_INTERVAL_MILLIS: u128 = 300; + lazy_static::lazy_static! { static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option)>, Arc)>>>) = { let (tx, rx) = unbounded_channel(); @@ -76,6 +78,8 @@ lazy_static::lazy_static! { pub static ref VIDEO_QOS: Arc> = Default::default(); pub static ref IS_UAC_RUNNING: Arc> = Default::default(); pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc> = Default::default(); + // Avoid refreshing too frequently + static ref LAST_REFRESH_TIME: Arc>> = Default::default(); } #[inline] @@ -513,9 +517,17 @@ fn run(vs: VideoService) -> ResultType<()> { drop(video_qos); if sp.is_option_true(OPTION_REFRESH) { - let _ = try_broadcast_display_changed(&sp, display_idx, &c, true); - log::info!("switch to refresh"); - bail!("SWITCH"); + let mut last_refresh_lock = LAST_REFRESH_TIME.lock().unwrap(); + if last_refresh_lock + .get(&vs.idx) + .map(|x| x.elapsed().as_millis() > REFRESH_MIN_INTERVAL_MILLIS) + .unwrap_or(true) + { + let _ = try_broadcast_display_changed(&sp, display_idx, &c, true); + last_refresh_lock.insert(vs.idx, Instant::now()); + log::info!("switch to refresh"); + bail!("SWITCH"); + } } if codec_format != Encoder::negotiated_codec() { log::info!(