Merge pull request #3249 from fufesou/fix/monitor_count_sync

fix sync displays info && select monitor menu
This commit is contained in:
RustDesk 2023-02-17 13:41:43 +08:00 committed by GitHub
commit 0f414822bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 234 additions and 108 deletions

View File

@ -1400,7 +1400,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
} }
return MenuButton( return MenuButton(
icon: widget.icon ?? Icon(Icons.adaptive.more), child: widget.icon ?? Icon(Icons.adaptive.more),
tooltip: tooltip:
widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip, widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip,
onPressed: widget.enabled ? showButtonMenu : null, onPressed: widget.enabled ? showButtonMenu : null,

View File

@ -5,7 +5,7 @@ class MenuButton extends StatefulWidget {
final Color color; final Color color;
final Color hoverColor; final Color hoverColor;
final Color? splashColor; final Color? splashColor;
final Widget icon; final Widget child;
final String? tooltip; final String? tooltip;
final EdgeInsetsGeometry padding; final EdgeInsetsGeometry padding;
final bool enableFeedback; final bool enableFeedback;
@ -14,7 +14,7 @@ class MenuButton extends StatefulWidget {
required this.onPressed, required this.onPressed,
required this.color, required this.color,
required this.hoverColor, required this.hoverColor,
required this.icon, required this.child,
this.splashColor, this.splashColor,
this.tooltip = "", this.tooltip = "",
this.padding = const EdgeInsets.symmetric(horizontal: 3, vertical: 6), this.padding = const EdgeInsets.symmetric(horizontal: 3, vertical: 6),
@ -51,7 +51,7 @@ class _MenuButtonState extends State<MenuButton> {
splashColor: widget.splashColor, splashColor: widget.splashColor,
enableFeedback: widget.enableFeedback, enableFeedback: widget.enableFeedback,
onTap: widget.onPressed, onTap: widget.onPressed,
child: widget.icon, child: widget.child,
), ),
), ),
), ),

View File

@ -476,7 +476,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
onPressed: () { onPressed: () {
widget.state.switchPin(); widget.state.switchPin();
}, },
icon: SvgPicture.asset( child: SvgPicture.asset(
pin ? "assets/pinned.svg" : "assets/unpinned.svg", pin ? "assets/pinned.svg" : "assets/unpinned.svg",
color: Colors.white, color: Colors.white,
), ),
@ -492,7 +492,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
onPressed: () { onPressed: () {
_setFullscreen(!isFullscreen); _setFullscreen(!isFullscreen);
}, },
icon: SvgPicture.asset( child: SvgPicture.asset(
isFullscreen ? "assets/fullscreen_exit.svg" : "assets/fullscreen.svg", isFullscreen ? "assets/fullscreen_exit.svg" : "assets/fullscreen.svg",
color: Colors.white, color: Colors.white,
), ),
@ -503,7 +503,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
Widget _buildMonitor(BuildContext context) { Widget _buildMonitor(BuildContext context) {
final pi = widget.ffi.ffiModel.pi; final pi = widget.ffi.ffiModel.pi;
return mod_menu.PopupMenuButton( final monitor = mod_menu.PopupMenuButton(
tooltip: translate('Select Monitor'), tooltip: translate('Select Monitor'),
position: mod_menu.PopupMenuPosition.under, position: mod_menu.PopupMenuPosition.under,
icon: Stack( icon: Stack(
@ -528,43 +528,44 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
itemBuilder: (BuildContext context) { itemBuilder: (BuildContext context) {
final List<Widget> rowChildren = []; final List<Widget> rowChildren = [];
for (int i = 0; i < pi.displays.length; i++) { for (int i = 0; i < pi.displays.length; i++) {
rowChildren.add( rowChildren.add(MenuButton(
Stack( color: _MenubarTheme.blueColor,
alignment: Alignment.center, hoverColor: _MenubarTheme.hoverBlueColor,
children: [ child: Container(
SvgPicture.asset( alignment: AlignmentDirectional.center,
"assets/display.svg", constraints:
color: Colors.white, const BoxConstraints(minHeight: _MenubarTheme.height),
), child: Stack(
TextButton( alignment: Alignment.center,
child: Container( children: [
alignment: AlignmentDirectional.center, SvgPicture.asset(
constraints: "assets/display.svg",
const BoxConstraints(minHeight: _MenubarTheme.height), color: Colors.white,
child: Padding( ),
padding: const EdgeInsets.only(bottom: 2.5), Padding(
child: Text( padding: const EdgeInsets.only(bottom: 2.5),
(i + 1).toString(), child: Text(
style: TextStyle( (i + 1).toString(),
color: Colors.white, style: TextStyle(
), color: Colors.white,
fontSize: 12,
), ),
), ),
), )
onPressed: () { ],
if (Navigator.canPop(context)) { ),
Navigator.pop(context);
_menuDismissCallback();
}
RxInt display = CurrentDisplayState.find(widget.id);
if (display.value != i) {
bind.sessionSwitchDisplay(id: widget.id, value: i);
}
},
)
],
), ),
); onPressed: () {
if (Navigator.canPop(context)) {
Navigator.pop(context);
_menuDismissCallback();
}
RxInt display = CurrentDisplayState.find(widget.id);
if (display.value != i) {
bind.sessionSwitchDisplay(id: widget.id, value: i);
}
},
));
} }
return <mod_menu.PopupMenuEntry<String>>[ return <mod_menu.PopupMenuEntry<String>>[
mod_menu.PopupMenuItem<String>( mod_menu.PopupMenuItem<String>(
@ -580,6 +581,11 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
]; ];
}, },
); );
return Obx(() => Offstage(
offstage: stateGlobal.displaysCount.value < 2,
child: monitor,
));
} }
Widget _buildControl(BuildContext context) { Widget _buildControl(BuildContext context) {
@ -678,7 +684,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
? translate('Stop session recording') ? translate('Stop session recording')
: translate('Start session recording'), : translate('Start session recording'),
onPressed: () => value.toggle(), onPressed: () => value.toggle(),
icon: SvgPicture.asset( child: SvgPicture.asset(
"assets/rec.svg", "assets/rec.svg",
color: Colors.white, color: Colors.white,
), ),
@ -701,7 +707,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
onPressed: () { onPressed: () {
clientClose(widget.id, widget.ffi.dialogManager); clientClose(widget.id, widget.ffi.dialogManager);
}, },
icon: SvgPicture.asset( child: SvgPicture.asset(
"assets/close.svg", "assets/close.svg",
color: Colors.white, color: Colors.white,
), ),
@ -771,7 +777,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
return tooltipText == null return tooltipText == null
? const Offstage() ? const Offstage()
: MenuButton( : MenuButton(
icon: _getVoiceCallIcon(), child: _getVoiceCallIcon(),
tooltip: translate(tooltipText), tooltip: translate(tooltipText),
onPressed: () => bind.sessionCloseVoiceCall(id: widget.id), onPressed: () => bind.sessionCloseVoiceCall(id: widget.id),
color: _MenubarTheme.redColor, color: _MenubarTheme.redColor,

View File

@ -140,6 +140,8 @@ class FfiModel with ChangeNotifier {
handleMsgBox(evt, peerId); handleMsgBox(evt, peerId);
} else if (name == 'peer_info') { } else if (name == 'peer_info') {
handlePeerInfo(evt, peerId); handlePeerInfo(evt, peerId);
} else if (name == 'sync_peer_info') {
handleSyncPeerInfo(evt, peerId);
} else if (name == 'connection_ready') { } else if (name == 'connection_ready') {
setConnectionType( setConnectionType(
peerId, evt['secure'] == 'true', evt['direct'] == 'true'); peerId, evt['secure'] == 'true', evt['direct'] == 'true');
@ -415,6 +417,7 @@ class FfiModel with ChangeNotifier {
d.cursorEmbedded = d0['cursor_embedded'] == 1; d.cursorEmbedded = d0['cursor_embedded'] == 1;
_pi.displays.add(d); _pi.displays.add(d);
} }
stateGlobal.displaysCount.value = _pi.displays.length;
if (_pi.currentDisplay < _pi.displays.length) { if (_pi.currentDisplay < _pi.displays.length) {
_display = _pi.displays[_pi.currentDisplay]; _display = _pi.displays[_pi.currentDisplay];
} }
@ -431,6 +434,27 @@ class FfiModel with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
/// Handle the peer info synchronization event based on [evt].
handleSyncPeerInfo(Map<String, dynamic> evt, String peerId) async {
if (evt['displays'] != null) {
List<dynamic> displays = json.decode(evt['displays']);
List<Display> newDisplays = [];
for (int i = 0; i < displays.length; ++i) {
Map<String, dynamic> d0 = displays[i];
var d = Display();
d.x = d0['x'].toDouble();
d.y = d0['y'].toDouble();
d.width = d0['width'];
d.height = d0['height'];
d.cursorEmbedded = d0['cursor_embedded'] == 1;
newDisplays.add(d);
}
_pi.displays = newDisplays;
stateGlobal.displaysCount.value = _pi.displays.length;
}
notifyListeners();
}
updateBlockInputState(Map<String, dynamic> evt, String peerId) { updateBlockInputState(Map<String, dynamic> evt, String peerId) {
_inputBlocked = evt['input_state'] == 'on'; _inputBlocked = evt['input_state'] == 'on';
notifyListeners(); notifyListeners();

View File

@ -14,6 +14,7 @@ class StateGlobal {
final RxDouble _resizeEdgeSize = RxDouble(kWindowEdgeSize); final RxDouble _resizeEdgeSize = RxDouble(kWindowEdgeSize);
final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth); final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth);
final RxBool showRemoteMenuBar = false.obs; final RxBool showRemoteMenuBar = false.obs;
final RxInt displaysCount = 0.obs;
int get windowId => _windowId; int get windowId => _windowId;
bool get fullscreen => _fullscreen; bool get fullscreen => _fullscreen;

View File

@ -636,5 +636,6 @@ message Message {
SwitchSidesResponse switch_sides_response = 22; SwitchSidesResponse switch_sides_response = 22;
VoiceCallRequest voice_call_request = 23; VoiceCallRequest voice_call_request = 23;
VoiceCallResponse voice_call_response = 24; VoiceCallResponse voice_call_response = 24;
PeerInfo peer_info = 25;
} }
} }

View File

@ -1253,6 +1253,14 @@ impl<T: InvokeUiSession> Remote<T> {
} }
} }
} }
Some(message::Union::PeerInfo(pi)) => {
match pi.conn_id {
crate::SYNC_PEER_INFO_DISPLAYS => {
self.handler.set_displays(&pi.displays);
}
_ => {}
}
}
_ => {} _ => {}
} }
} }

View File

@ -37,6 +37,8 @@ pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future<Out
pub const CLIPBOARD_NAME: &'static str = "clipboard"; pub const CLIPBOARD_NAME: &'static str = "clipboard";
pub const CLIPBOARD_INTERVAL: u64 = 333; pub const CLIPBOARD_INTERVAL: u64 = 333;
pub const SYNC_PEER_INFO_DISPLAYS: i32 = 1;
// the executable name of the portable version // the executable name of the portable version
pub const PORTABLE_APPNAME_RUNTIME_ENV_KEY: &str = "RUSTDESK_APPNAME"; pub const PORTABLE_APPNAME_RUNTIME_ENV_KEY: &str = "RUSTDESK_APPNAME";

View File

@ -142,6 +142,20 @@ impl FlutterHandler {
} }
*stream_lock = None; *stream_lock = None;
} }
fn make_displays_msg(displays: &Vec<DisplayInfo>) -> String {
let mut msg_vec = Vec::new();
for ref d in displays.iter() {
let mut h: HashMap<&str, i32> = Default::default();
h.insert("x", d.x);
h.insert("y", d.y);
h.insert("width", d.width);
h.insert("height", d.height);
h.insert("cursor_embedded", if d.cursor_embedded { 1 } else { 0 });
msg_vec.push(h);
}
serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned())
}
} }
impl InvokeUiSession for FlutterHandler { impl InvokeUiSession for FlutterHandler {
@ -316,17 +330,7 @@ impl InvokeUiSession for FlutterHandler {
} }
fn set_peer_info(&self, pi: &PeerInfo) { fn set_peer_info(&self, pi: &PeerInfo) {
let mut displays = Vec::new(); let displays = Self::make_displays_msg(&pi.displays);
for ref d in pi.displays.iter() {
let mut h: HashMap<&str, i32> = Default::default();
h.insert("x", d.x);
h.insert("y", d.y);
h.insert("width", d.width);
h.insert("height", d.height);
h.insert("cursor_embedded", if d.cursor_embedded { 1 } else { 0 });
displays.push(h);
}
let displays = serde_json::ser::to_string(&displays).unwrap_or("".to_owned());
let mut features: HashMap<&str, i32> = Default::default(); let mut features: HashMap<&str, i32> = Default::default();
for ref f in pi.features.iter() { for ref f in pi.features.iter() {
features.insert("privacy_mode", if f.privacy_mode { 1 } else { 0 }); features.insert("privacy_mode", if f.privacy_mode { 1 } else { 0 });
@ -351,6 +355,13 @@ impl InvokeUiSession for FlutterHandler {
); );
} }
fn set_displays(&self, displays: &Vec<DisplayInfo>) {
self.push_event(
"sync_peer_info",
vec![("displays", &Self::make_displays_msg(displays))],
);
}
fn on_connected(&self, _conn_type: ConnType) {} fn on_connected(&self, _conn_type: ConnType) {}
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) { fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) {

View File

@ -6,7 +6,10 @@ use crate::common::update_clipboard;
#[cfg(windows)] #[cfg(windows)]
use crate::portable_service::client as portable_client; use crate::portable_service::client as portable_client;
use crate::{ use crate::{
client::{start_audio_thread, LatencyController, MediaData, MediaSender, new_voice_call_request, new_voice_call_response}, client::{
new_voice_call_request, new_voice_call_response, start_audio_thread, LatencyController,
MediaData, MediaSender,
},
common::{get_default_sound_input, set_sound_input}, common::{get_default_sound_input, set_sound_input},
video_service, video_service,
}; };
@ -672,15 +675,15 @@ impl Connection {
.collect(); .collect();
if !whitelist.is_empty() if !whitelist.is_empty()
&& whitelist && whitelist
.iter() .iter()
.filter(|x| x == &"0.0.0.0") .filter(|x| x == &"0.0.0.0")
.next() .next()
.is_none() .is_none()
&& whitelist && whitelist
.iter() .iter()
.filter(|x| IpCidr::from_str(x).map_or(false, |y| y.contains(addr.ip()))) .filter(|x| IpCidr::from_str(x).map_or(false, |y| y.contains(addr.ip())))
.next() .next()
.is_none() .is_none()
{ {
self.send_login_error("Your ip is blocked by the peer") self.send_login_error("Your ip is blocked by the peer")
.await; .await;
@ -806,7 +809,7 @@ impl Connection {
}; };
self.post_conn_audit(json!({"peer": self.peer_info, "type": conn_type})); self.post_conn_audit(json!({"peer": self.peer_info, "type": conn_type}));
#[allow(unused_mut)] #[allow(unused_mut)]
let mut username = crate::platform::get_active_username(); let mut username = crate::platform::get_active_username();
let mut res = LoginResponse::new(); let mut res = LoginResponse::new();
let mut pi = PeerInfo { let mut pi = PeerInfo {
username: username.clone(), username: username.clone(),
@ -833,7 +836,7 @@ impl Connection {
h265, h265,
..Default::default() ..Default::default()
}) })
.into(); .into();
} }
if self.port_forward_socket.is_some() { if self.port_forward_socket.is_some() {
@ -877,7 +880,7 @@ impl Connection {
privacy_mode: video_service::is_privacy_mode_supported(), privacy_mode: video_service::is_privacy_mode_supported(),
..Default::default() ..Default::default()
}) })
.into(); .into();
let mut sub_service = false; let mut sub_service = false;
if self.file_transfer.is_some() { if self.file_transfer.is_some() {
@ -893,10 +896,11 @@ impl Connection {
res.set_error(format!("{}", err)); res.set_error(format!("{}", err));
} }
Ok((current, displays)) => { Ok((current, displays)) => {
pi.displays = displays.into(); pi.displays = displays.clone();
pi.current_display = current as _; pi.current_display = current as _;
res.set_peer_info(pi); res.set_peer_info(pi);
sub_service = true; sub_service = true;
*super::video_service::LAST_SYNC_DISPLAYS.write().unwrap() = displays;
} }
} }
} }
@ -1160,7 +1164,7 @@ impl Connection {
"Failed to access remote {}, please make sure if it is open", "Failed to access remote {}, please make sure if it is open",
addr addr
)) ))
.await; .await;
return false; return false;
} }
} }
@ -1324,12 +1328,12 @@ impl Connection {
} }
} }
Some(message::Union::Clipboard(cb)) => Some(message::Union::Clipboard(cb)) =>
{ {
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.clipboard { if self.clipboard {
update_clipboard(cb, None); update_clipboard(cb, None);
}
} }
}
Some(message::Union::Cliprdr(_clip)) => { Some(message::Union::Cliprdr(_clip)) => {
if self.file_transfer_enabled() { if self.file_transfer_enabled() {
#[cfg(windows)] #[cfg(windows)]
@ -1512,15 +1516,15 @@ impl Connection {
} }
Some(misc::Union::RestartRemoteDevice(_)) => Some(misc::Union::RestartRemoteDevice(_)) =>
{ {
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.restart { if self.restart {
match system_shutdown::reboot() { match system_shutdown::reboot() {
Ok(_) => log::info!("Restart by the peer"), Ok(_) => log::info!("Restart by the peer"),
Err(e) => log::error!("Failed to restart:{}", e), Err(e) => log::error!("Failed to restart:{}", e),
}
} }
} }
}
Some(misc::Union::ElevationRequest(r)) => match r.union { Some(misc::Union::ElevationRequest(r)) => match r.union {
Some(elevation_request::Union::Direct(_)) => { Some(elevation_request::Union::Direct(_)) => {
#[cfg(windows)] #[cfg(windows)]
@ -1530,8 +1534,8 @@ impl Connection {
err = portable_client::start_portable_service( err = portable_client::start_portable_service(
portable_client::StartPara::Direct, portable_client::StartPara::Direct,
) )
.err() .err()
.map_or("".to_string(), |e| e.to_string()); .map_or("".to_string(), |e| e.to_string());
} }
self.portable.elevation_requested = err.is_empty(); self.portable.elevation_requested = err.is_empty();
let mut misc = Misc::new(); let mut misc = Misc::new();
@ -1549,8 +1553,8 @@ impl Connection {
err = portable_client::start_portable_service( err = portable_client::start_portable_service(
portable_client::StartPara::Logon(_r.username, _r.password), portable_client::StartPara::Logon(_r.username, _r.password),
) )
.err() .err()
.map_or("".to_string(), |e| e.to_string()); .map_or("".to_string(), |e| e.to_string());
} }
self.portable.elevation_requested = err.is_empty(); self.portable.elevation_requested = err.is_empty();
let mut misc = Misc::new(); let mut misc = Misc::new();
@ -1571,7 +1575,11 @@ impl Connection {
// No video frame will be sent here, so we need to disable latency controller, or audio check may fail. // No video frame will be sent here, so we need to disable latency controller, or audio check may fail.
latency_controller.lock().unwrap().set_audio_only(true); latency_controller.lock().unwrap().set_audio_only(true);
self.audio_sender = Some(start_audio_thread(Some(latency_controller))); self.audio_sender = Some(start_audio_thread(Some(latency_controller)));
allow_err!(self.audio_sender.as_ref().unwrap().send(MediaData::AudioFormat(format))); allow_err!(self
.audio_sender
.as_ref()
.unwrap()
.send(MediaData::AudioFormat(format)));
} }
} }
#[cfg(feature = "flutter")] #[cfg(feature = "flutter")]
@ -1583,7 +1591,7 @@ impl Connection {
"--switch_uuid", "--switch_uuid",
uuid.to_string().as_ref(), uuid.to_string().as_ref(),
]) ])
.ok(); .ok();
self.send_close_reason_no_retry("Closed as expected").await; self.send_close_reason_no_retry("Closed as expected").await;
self.on_close("switch sides", false).await; self.on_close("switch sides", false).await;
return false; return false;
@ -1596,7 +1604,9 @@ impl Connection {
if let Some(sender) = &self.audio_sender { if let Some(sender) = &self.audio_sender {
allow_err!(sender.send(MediaData::AudioFrame(frame))); allow_err!(sender.send(MediaData::AudioFrame(frame)));
} else { } else {
log::warn!("Processing audio frame without the voice call audio sender."); log::warn!(
"Processing audio frame without the voice call audio sender."
);
} }
} }
} }
@ -1646,7 +1656,9 @@ impl Connection {
pub async fn close_voice_call(&mut self) { pub async fn close_voice_call(&mut self) {
// Restore to the prior audio device. // Restore to the prior audio device.
if let Some(sound_input) = std::mem::replace(&mut self.audio_input_device_before_voice_call, None) { if let Some(sound_input) =
std::mem::replace(&mut self.audio_input_device_before_voice_call, None)
{
set_sound_input(sound_input); set_sound_input(sound_input);
} }
// Notify the connection manager that the voice call has been closed. // Notify the connection manager that the voice call has been closed.
@ -1821,13 +1833,13 @@ impl Connection {
lock_screen().await; lock_screen().await;
} }
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
let data = if self.chat_unanswered { let data = if self.chat_unanswered {
ipc::Data::Disconnected ipc::Data::Disconnected
} else { } else {
ipc::Data::Close ipc::Data::Close
}; };
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(any(target_os = "android", target_os = "ios"))]
let data = ipc::Data::Close; let data = ipc::Data::Close;
self.tx_to_cm.send(data).ok(); self.tx_to_cm.send(data).ok();
self.port_forward_socket.take(); self.port_forward_socket.take();
} }

View File

@ -65,6 +65,7 @@ lazy_static::lazy_static! {
pub static ref VIDEO_QOS: Arc<Mutex<VideoQoS>> = Default::default(); pub static ref VIDEO_QOS: Arc<Mutex<VideoQoS>> = Default::default();
pub static ref IS_UAC_RUNNING: Arc<Mutex<bool>> = Default::default(); pub static ref IS_UAC_RUNNING: Arc<Mutex<bool>> = Default::default();
pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc<Mutex<bool>> = Default::default(); pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc<Mutex<bool>> = Default::default();
pub static ref LAST_SYNC_DISPLAYS: Arc<RwLock<Vec<DisplayInfo>>> = Default::default();
} }
fn is_capturer_mag_supported() -> bool { fn is_capturer_mag_supported() -> bool {
@ -407,6 +408,43 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType<Cap
}) })
} }
fn check_displays_new() -> Option<Vec<Display>> {
let displays = try_get_displays().ok()?;
let last_sync_displays = &*LAST_SYNC_DISPLAYS.read().unwrap();
if displays.len() != last_sync_displays.len() {
Some(displays)
} else {
for i in 0..displays.len() {
if displays[i].height() != (last_sync_displays[i].height as usize) {
return Some(displays);
}
if displays[i].width() != (last_sync_displays[i].width as usize) {
return Some(displays);
}
if displays[i].origin() != (last_sync_displays[i].x, last_sync_displays[i].y) {
return Some(displays);
}
}
None
}
}
fn check_displays_changed() -> Option<Message> {
let displays = check_displays_new()?;
let (current, displays) = get_displays_2(&displays);
let mut pi = PeerInfo {
conn_id: crate::SYNC_PEER_INFO_DISPLAYS,
..Default::default()
};
pi.displays = displays.clone();
pi.current_display = current as _;
let mut msg_out = Message::new();
msg_out.set_peer_info(pi);
*LAST_SYNC_DISPLAYS.write().unwrap() = displays;
Some(msg_out)
}
fn run(sp: GenericService) -> ResultType<()> { fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)] #[cfg(windows)]
ensure_close_virtual_device()?; ensure_close_virtual_device()?;
@ -529,6 +567,11 @@ fn run(sp: GenericService) -> ResultType<()> {
let now = time::Instant::now(); let now = time::Instant::now();
if last_check_displays.elapsed().as_millis() > 1000 { if last_check_displays.elapsed().as_millis() > 1000 {
last_check_displays = now; last_check_displays = now;
if let Some(msg_out) = check_displays_changed() {
sp.send(msg_out);
}
if c.ndisplay != get_display_num() { if c.ndisplay != get_display_num() {
log::info!("Displays changed"); log::info!("Displays changed");
*SWITCH.lock().unwrap() = true; *SWITCH.lock().unwrap() = true;
@ -798,11 +841,7 @@ fn get_display_num() -> usize {
} }
} }
if let Ok(d) = try_get_displays() { LAST_SYNC_DISPLAYS.read().unwrap().len()
d.len()
} else {
0
}
} }
pub(super) fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) { pub(super) fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) {
@ -861,6 +900,7 @@ pub async fn switch_display(i: i32) {
} }
} }
#[inline]
pub fn refresh() { pub fn refresh() {
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
Display::refresh_size(); Display::refresh_size();
@ -888,10 +928,12 @@ fn get_primary() -> usize {
0 0
} }
#[inline]
pub async fn switch_to_primary() { pub async fn switch_to_primary() {
switch_display(get_primary() as _).await; switch_display(get_primary() as _).await;
} }
#[inline]
#[cfg(not(windows))] #[cfg(not(windows))]
fn try_get_displays() -> ResultType<Vec<Display>> { fn try_get_displays() -> ResultType<Vec<Display>> {
Ok(Display::all()?) Ok(Display::all()?)

View File

@ -480,6 +480,14 @@ handler.updatePi = function(v) {
} }
} }
handler.updateDisplays = function(v) {
pi.displays = v;
header.update();
if (is_port_forward) {
view.windowState = View.WINDOW_MINIMIZED;
}
}
function updatePrivacyMode() { function updatePrivacyMode() {
var el = $(li#privacy-mode); var el = $(li#privacy-mode);
if (el) { if (el) {

View File

@ -53,6 +53,20 @@ impl SciterHandler {
allow_err!(e.call_method(func, &super::value_crash_workaround(args)[..])); allow_err!(e.call_method(func, &super::value_crash_workaround(args)[..]));
} }
} }
fn make_displays_array(displays: &Vec<DisplayInfo>) -> Value {
let mut displays_value = Value::array(0);
for d in displays.iter() {
let mut display = Value::map();
display.set_item("x", d.x);
display.set_item("y", d.y);
display.set_item("width", d.width);
display.set_item("height", d.height);
display.set_item("cursor_embedded", d.cursor_embedded);
displays_value.push(display);
}
displays_value
}
} }
impl InvokeUiSession for SciterHandler { impl InvokeUiSession for SciterHandler {
@ -215,22 +229,18 @@ impl InvokeUiSession for SciterHandler {
pi_sciter.set_item("hostname", pi.hostname.clone()); pi_sciter.set_item("hostname", pi.hostname.clone());
pi_sciter.set_item("platform", pi.platform.clone()); pi_sciter.set_item("platform", pi.platform.clone());
pi_sciter.set_item("sas_enabled", pi.sas_enabled); pi_sciter.set_item("sas_enabled", pi.sas_enabled);
pi_sciter.set_item("displays", Self::make_displays_array(&pi.displays));
let mut displays = Value::array(0);
for ref d in pi.displays.iter() {
let mut display = Value::map();
display.set_item("x", d.x);
display.set_item("y", d.y);
display.set_item("width", d.width);
display.set_item("height", d.height);
display.set_item("cursor_embedded", d.cursor_embedded);
displays.push(display);
}
pi_sciter.set_item("displays", displays);
pi_sciter.set_item("current_display", pi.current_display); pi_sciter.set_item("current_display", pi.current_display);
self.call("updatePi", &make_args!(pi_sciter)); self.call("updatePi", &make_args!(pi_sciter));
} }
fn set_displays(&self, displays: &Vec<DisplayInfo>) {
self.call(
"updateDisplays",
&make_args!(Self::make_displays_array(displays)),
);
}
fn on_connected(&self, conn_type: ConnType) { fn on_connected(&self, conn_type: ConnType) {
match conn_type { match conn_type {
ConnType::RDP => {} ConnType::RDP => {}

View File

@ -761,6 +761,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
fn set_display(&self, x: i32, y: i32, w: i32, h: i32, cursor_embedded: bool); fn set_display(&self, x: i32, y: i32, w: i32, h: i32, cursor_embedded: bool);
fn switch_display(&self, display: &SwitchDisplay); fn switch_display(&self, display: &SwitchDisplay);
fn set_peer_info(&self, peer_info: &PeerInfo); // flutter fn set_peer_info(&self, peer_info: &PeerInfo); // flutter
fn set_displays(&self, displays: &Vec<DisplayInfo>);
fn on_connected(&self, conn_type: ConnType); fn on_connected(&self, conn_type: ConnType);
fn update_privacy_mode(&self); fn update_privacy_mode(&self);
fn set_permission(&self, name: &str, value: bool); fn set_permission(&self, name: &str, value: bool);