Merge pull request #3249 from fufesou/fix/monitor_count_sync
fix sync displays info && select monitor menu
This commit is contained in:
commit
0f414822bb
@ -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,
|
||||||
|
@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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()?)
|
||||||
|
@ -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) {
|
||||||
|
@ -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 => {}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user