From 333092f983c3f9c5009dd924ea9de1fecf3caa8b Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 17 Jan 2023 13:28:33 +0800 Subject: [PATCH] switch sides Signed-off-by: 21pages --- flutter/lib/common.dart | 13 ++++ flutter/lib/desktop/pages/remote_page.dart | 7 +- .../lib/desktop/pages/remote_tab_page.dart | 3 + flutter/lib/desktop/pages/server_page.dart | 13 ++++ .../lib/desktop/widgets/remote_menubar.dart | 55 +++++++++++---- flutter/lib/models/model.dart | 20 +++++- flutter/lib/models/server_model.dart | 3 + flutter/lib/utils/multi_window_manager.dart | 12 ++-- libs/hbb_common/protos/message.proto | 14 ++++ src/client.rs | 33 ++++++++- src/client/io_loop.rs | 4 ++ src/flutter.rs | 66 +++++++++++++---- src/flutter_ffi.rs | 17 +++-- src/ipc.rs | 2 + src/lang/ca.rs | 2 + src/lang/cn.rs | 2 + src/lang/cs.rs | 2 + src/lang/da.rs | 2 + src/lang/de.rs | 2 + src/lang/eo.rs | 2 + src/lang/es.rs | 2 + src/lang/fa.rs | 2 + src/lang/fr.rs | 2 + src/lang/gr.rs | 2 + src/lang/hu.rs | 2 + src/lang/id.rs | 2 + src/lang/it.rs | 2 + src/lang/ja.rs | 2 + src/lang/ko.rs | 2 + src/lang/kz.rs | 2 + src/lang/pl.rs | 2 + src/lang/pt_PT.rs | 2 + src/lang/ptbr.rs | 2 + src/lang/ru.rs | 2 + src/lang/sk.rs | 2 + src/lang/sl.rs | 2 + src/lang/sq.rs | 2 + src/lang/sr.rs | 2 + src/lang/sv.rs | 2 + src/lang/template.rs | 2 + src/lang/th.rs | 2 + src/lang/tr.rs | 2 + src/lang/tw.rs | 2 + src/lang/ua.rs | 2 + src/lang/vn.rs | 2 + src/server/connection.rs | 70 ++++++++++++++----- src/ui/remote.rs | 4 +- src/ui_cm_interface.rs | 17 ++++- src/ui_session_interface.rs | 19 +++++ 49 files changed, 373 insertions(+), 61 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 23aa9535d..accbdf8df 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1606,3 +1606,16 @@ Widget dialogButton(String text, )); } } + +int get_version_num(String version) { + final list = version.split('.'); + var n = 0; + for (var i = 0; i < list.length; i++) { + n = n * 1000 + (int.tryParse(list[i]) ?? 0); + } + return n; +} + +int version_cmp(String v1, String v2) { + return get_version_num(v1) - get_version_num(v2); +} diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 55a5bbaef..fb67154bc 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -33,10 +33,12 @@ class RemotePage extends StatefulWidget { Key? key, required this.id, required this.menubarState, + this.switchUuid, }) : super(key: key); final String id; final MenubarState menubarState; + final String? switchUuid; final SimpleWrapper?> _lastState = SimpleWrapper(null); FFI get ffi => (_lastState.value! as _RemotePageState)._ffi; @@ -100,7 +102,10 @@ class _RemotePageState extends State showKBLayoutTypeChooserIfNeeded( _ffi.ffiModel.pi.platform, _ffi.dialogManager); }); - _ffi.start(widget.id); + _ffi.start( + widget.id, + switchUuid: widget.switchUuid, + ); WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); _ffi.dialogManager diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 198b2aea7..a3532d49a 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -64,6 +64,7 @@ class _ConnectionTabPageState extends State { key: ValueKey(peerId), id: peerId, menubarState: _menubarState, + switchUuid: params['switch_uuid'], ), )); _update_remote_count(); @@ -84,6 +85,7 @@ class _ConnectionTabPageState extends State { if (call.method == "new_remote_desktop") { final args = jsonDecode(call.arguments); final id = args['id']; + final switchUuid = args['switch_uuid']; window_on_top(windowId()); ConnectionTypeState.init(id); tabController.add(TabInfo( @@ -96,6 +98,7 @@ class _ConnectionTabPageState extends State { key: ValueKey(id), id: id, menubarState: _menubarState, + switchUuid: switchUuid, ), )); } else if (call.method == "onDestroy") { diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index fa367f488..8c8679e96 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -516,6 +516,15 @@ class _CmControlPanel extends StatelessWidget { return Column( mainAxisAlignment: MainAxisAlignment.end, children: [ + Offstage( + offstage: !client.fromSwitch, + child: buildButton(context, + color: Colors.purple, + onClick: () => handleSwitchBack(context), + icon: Icon(Icons.reply, color: Colors.white), + text: "Switch Sides", + textColor: Colors.white), + ), Offstage( offstage: !showElevation, child: buildButton(context, color: Colors.green[700], onClick: () { @@ -674,6 +683,10 @@ class _CmControlPanel extends StatelessWidget { windowManager.close(); } } + + void handleSwitchBack(BuildContext context) { + bind.cmSwitchBack(connId: client.id); + } } void checkClickTime(int id, Function() callback) async { diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 6a0fa9104..6ea372b1f 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -509,6 +509,7 @@ class _RemoteMenubarState extends State { List> _getControlMenu(BuildContext context) { final pi = widget.ffi.ffiModel.pi; final perms = widget.ffi.ffiModel.permissions; + final peer_version = widget.ffi.ffiModel.pi.version; const EdgeInsets padding = EdgeInsets.only(left: 14.0, right: 5.0); final List> displayMenu = []; displayMenu.addAll([ @@ -651,6 +652,18 @@ class _RemoteMenubarState extends State { dismissOnClicked: true, )); } + if (version_cmp(peer_version, '1.2.0') >= 0) { + displayMenu.add(MenuEntryButton( + childBuilder: (TextStyle? style) => Text( + translate('Switch Sides'), + style: style, + ), + proc: () => + showConfirmSwitchSidesDialog(widget.id, widget.ffi.dialogManager), + padding: padding, + dismissOnClicked: true, + )); + } } if (pi.version.isNotEmpty) { @@ -721,6 +734,7 @@ class _RemoteMenubarState extends State { List> _getDisplayMenu( dynamic futureData, int remoteCount) { const EdgeInsets padding = EdgeInsets.only(left: 18.0, right: 8.0); + final peer_version = widget.ffi.ffiModel.pi.version; final displayMenu = [ MenuEntryRadios( text: translate('Ratio'), @@ -880,9 +894,7 @@ class _RemoteMenubarState extends State { final fpsSlider = Offstage( offstage: (await bind.mainIsUsingPublicServer() && direct != true) || - (await bind.versionToNumber( - v: widget.ffi.ffiModel.pi.version) < - await bind.versionToNumber(v: '1.2.0')), + version_cmp(peer_version, '1.2.0') < 0, child: Row( children: [ Obx((() => Slider( @@ -1391,16 +1403,33 @@ void showAuditDialog(String id, dialogManager) async { focusNode: focusNode, )), actions: [ - TextButton( - style: flatButtonStyle, - onPressed: close, - child: Text(translate('Cancel')), - ), - TextButton( - style: flatButtonStyle, - onPressed: submit, - child: Text(translate('OK')), - ), + dialogButton('Cancel', onPressed: close, isOutline: true), + dialogButton('OK', onPressed: submit) + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + +void showConfirmSwitchSidesDialog( + String id, OverlayDialogManager dialogManager) async { + dialogManager.show((setState, close) { + submit() async { + await bind.sessionSwitchSides(id: id); + closeConnection(id: id); + } + + return CustomAlertDialog( + title: Text(translate('Switch Sides')), + content: Column( + children: [ + Text(translate('Please confirm if you want to share your desktop?')), + ], + ), + actions: [ + dialogButton('Cancel', onPressed: close, isOutline: true), + dialogButton('OK', onPressed: submit), ], onSubmit: submit, onCancel: close, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 641165e67..061c3293f 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -199,6 +199,16 @@ class FfiModel with ChangeNotifier { parent.target?.serverModel.setShowElevation(show); } else if (name == 'cancel_msgbox') { cancelMsgBox(evt, peerId); + } else if (name == 'switch_sides') { + final peer_id = evt['peer_id'].toString(); + final uuid = evt['uuid'].toString(); + Future.delayed(Duration.zero, () { + rustDeskWinManager.newRemoteDesktop(peer_id, switch_uuid: uuid); + }); + } else if (name == 'switch_back') { + final peer_id = evt['peer_id'].toString(); + await bind.sessionSwitchSides(id: peer_id); + closeConnection(id: peer_id); } }; } @@ -1289,7 +1299,9 @@ class FFI { /// Start with the given [id]. Only transfer file if [isFileTransfer], only port forward if [isPortForward]. void start(String id, - {bool isFileTransfer = false, bool isPortForward = false}) { + {bool isFileTransfer = false, + bool isPortForward = false, + String? switchUuid}) { assert(!(isFileTransfer && isPortForward), 'more than one connect type'); if (isFileTransfer) { connType = ConnType.fileTransfer; @@ -1305,7 +1317,11 @@ class FFI { } // ignore: unused_local_variable final addRes = bind.sessionAddSync( - id: id, isFileTransfer: isFileTransfer, isPortForward: isPortForward); + id: id, + isFileTransfer: isFileTransfer, + isPortForward: isPortForward, + switchUuid: switchUuid ?? "", + ); final stream = bind.sessionStart(id: id); final cb = ffiModel.startEventListener(id); () async { diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index c36a54db6..176b1ba2d 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -601,6 +601,7 @@ class Client { bool restart = false; bool recording = false; bool disconnected = false; + bool fromSwitch = false; RxBool hasUnreadChatMessage = false.obs; @@ -621,6 +622,7 @@ class Client { restart = json['restart']; recording = json['recording']; disconnected = json['disconnected']; + fromSwitch = json['from_switch']; } Map toJson() { @@ -638,6 +640,7 @@ class Client { data['restart'] = restart; data['recording'] = recording; data['disconnected'] = disconnected; + data['from_switch'] = fromSwitch; return data; } diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index cf6d78cd2..5087538c5 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -40,9 +40,13 @@ class RustDeskMultiWindowManager { int? _fileTransferWindowId; int? _portForwardWindowId; - Future newRemoteDesktop(String remoteId) async { - final msg = - jsonEncode({"type": WindowType.RemoteDesktop.index, "id": remoteId}); + Future newRemoteDesktop(String remoteId, + {String? switch_uuid}) async { + final msg = jsonEncode({ + "type": WindowType.RemoteDesktop.index, + "id": remoteId, + "switch_uuid": switch_uuid ?? "" + }); try { final ids = await DesktopMultiWindow.getAllSubWindowIds(); @@ -208,7 +212,7 @@ class RustDeskMultiWindowManager { } /// Remove active window which has [`windowId`] - /// + /// /// [Availability] /// This function should only be called from main window. /// For other windows, please post a unregister(hide) event to main window handler: diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index f5910d963..12d698045 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -564,6 +564,17 @@ message ElevationRequest { } } +message SwitchSidesRequest { + bytes uuid = 1; +} + +message SwitchSidesResponse { + bytes uuid = 1; + LoginRequest lr = 2; +} + +message SwitchBack {} + message Misc { oneof union { ChatMessage chat_message = 4; @@ -582,6 +593,8 @@ message Misc { ElevationRequest elevation_request = 18; string elevation_response = 19; bool portable_service_running = 20; + SwitchSidesRequest switch_sides_request = 21; + SwitchBack switch_back = 22; } } @@ -606,5 +619,6 @@ message Message { Misc misc = 19; Cliprdr cliprdr = 20; MessageBox message_box = 21; + SwitchSidesResponse switch_sides_response = 22; } } diff --git a/src/client.rs b/src/client.rs index 493448c3b..e9b8edf39 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,5 @@ pub use async_trait::async_trait; +use bytes::Bytes; #[cfg(not(any(target_os = "android", target_os = "linux")))] use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, @@ -909,6 +910,7 @@ pub struct LoginConfigHandler { pub force_relay: bool, pub direct: Option, pub received: bool, + switch_uuid: Option, } impl Deref for LoginConfigHandler { @@ -936,7 +938,7 @@ impl LoginConfigHandler { /// /// * `id` - id of peer /// * `conn_type` - Connection type enum. - pub fn initialize(&mut self, id: String, conn_type: ConnType) { + pub fn initialize(&mut self, id: String, conn_type: ConnType, switch_uuid: Option) { self.id = id; self.conn_type = conn_type; let config = self.load_config(); @@ -948,6 +950,7 @@ impl LoginConfigHandler { self.force_relay = !self.get_option("force-always-relay").is_empty(); self.direct = None; self.received = false; + self.switch_uuid = switch_uuid; } /// Check if the client should auto login. @@ -1784,6 +1787,14 @@ pub async fn handle_hash( interface: &impl Interface, peer: &mut Stream, ) { + lc.write().unwrap().hash = hash.clone(); + let uuid = lc.read().unwrap().switch_uuid.clone(); + if let Some(uuid) = uuid { + if let Ok(uuid) = uuid::Uuid::from_str(&uuid) { + send_switch_login_request(lc.clone(), peer, uuid).await; + return; + } + } let mut password = lc.read().unwrap().password.clone(); if password.is_empty() { if !password_preset.is_empty() { @@ -1848,6 +1859,26 @@ pub async fn handle_login_from_ui( send_login(lc.clone(), hasher2.finalize()[..].into(), peer).await; } +async fn send_switch_login_request( + lc: Arc>, + peer: &mut Stream, + uuid: Uuid, +) { + let mut msg_out = Message::new(); + msg_out.set_switch_sides_response(SwitchSidesResponse { + uuid: Bytes::from(uuid.as_bytes().to_vec()), + lr: hbb_common::protobuf::MessageField::some( + lc.read() + .unwrap() + .create_login_msg(vec![]) + .login_request() + .to_owned(), + ), + ..Default::default() + }); + allow_err!(peer.send(&msg_out).await); +} + /// Interface for client to send data and commands. #[async_trait] pub trait Interface: Send + Clone + 'static + Sized { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b15949041..ff6d6c004 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1111,6 +1111,10 @@ impl Remote { ); } } + Some(misc::Union::SwitchBack(_)) => { + #[cfg(feature = "flutter")] + self.handler.switch_back(&self.handler.id); + } _ => {} }, Some(message::Union::TestDelay(t)) => { diff --git a/src/flutter.rs b/src/flutter.rs index 1369b5646..0b8cef704 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,3 +1,12 @@ +use crate::ui_session_interface::{io_loop, InvokeUiSession, Session}; +use crate::{client::*, flutter_ffi::EventToUI}; +use bytes::Bytes; +use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; +use hbb_common::{ + bail, config::LocalConfig, get_version_number, message_proto::*, rendezvous_proto::ConnType, + ResultType, +}; +use serde_json::json; use std::{ collections::HashMap, ffi::CString, @@ -5,18 +14,6 @@ use std::{ sync::{Arc, RwLock}, }; -use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; - -use hbb_common::{ - bail, config::LocalConfig, get_version_number, message_proto::*, rendezvous_proto::ConnType, - ResultType, -}; -use serde_json::json; - -use crate::ui_session_interface::{io_loop, InvokeUiSession, Session}; - -use crate::{client::*, flutter_ffi::EventToUI}; - pub(super) const APP_TYPE_MAIN: &str = "main"; pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; @@ -366,7 +363,17 @@ impl InvokeUiSession for FlutterHandler { ("y", &display.y.to_string()), ("width", &display.width.to_string()), ("height", &display.height.to_string()), - ("cursor_embedded", &{if display.cursor_embedded {1} else {0}}.to_string()), + ( + "cursor_embedded", + &{ + if display.cursor_embedded { + 1 + } else { + 0 + } + } + .to_string(), + ), ], ); } @@ -382,6 +389,10 @@ impl InvokeUiSession for FlutterHandler { fn clipboard(&self, content: String) { self.push_event("clipboard", vec![("content", &content)]); } + + fn switch_back(&self, peer_id: &str) { + self.push_event("switch_back", [("peer_id", peer_id)].into()); + } } /// Create a new remote session with the given id. @@ -391,7 +402,12 @@ impl InvokeUiSession for FlutterHandler { /// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ /// * `is_file_transfer` - If the session is used for file transfer. /// * `is_port_forward` - If the session is used for port forward. -pub fn session_add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> ResultType<()> { +pub fn session_add( + id: &str, + is_file_transfer: bool, + is_port_forward: bool, + switch_uuid: &str, +) -> ResultType<()> { let session_id = get_session_id(id.to_owned()); LocalConfig::set_remote_id(&session_id); @@ -409,11 +425,17 @@ pub fn session_add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> R ConnType::DEFAULT_CONN }; + let switch_uuid = if switch_uuid.is_empty() { + None + } else { + Some(switch_uuid.to_string()) + }; + session .lc .write() .unwrap() - .initialize(session_id, conn_type); + .initialize(session_id, conn_type, switch_uuid); if let Some(same_id_session) = SESSIONS.write().unwrap().insert(id.to_owned(), session) { same_id_session.close(); @@ -590,3 +612,17 @@ pub fn set_cur_session_id(id: String) { *CUR_SESSION_ID.write().unwrap() = id; } } + +pub fn switch_sides(peer_id: &str, uuid: &Bytes) { + if let Some(stream) = GLOBAL_EVENT_STREAM.read().unwrap().get(APP_TYPE_MAIN) { + if let Ok(uuid) = uuid::Uuid::from_slice(uuid.to_vec().as_ref()) { + let uuid = uuid.to_string(); + let data = HashMap::from([ + ("name", "switch_sides"), + ("peer_id", peer_id), + ("uuid", &uuid), + ]); + stream.add(serde_json::ser::to_string(&data).unwrap_or("".into())); + } + } +} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index ca6823aa5..874cb4d4d 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -84,8 +84,9 @@ pub fn session_add_sync( id: String, is_file_transfer: bool, is_port_forward: bool, + switch_uuid: String, ) -> SyncReturn { - if let Err(e) = session_add(&id, is_file_transfer, is_port_forward) { + if let Err(e) = session_add(&id, is_file_transfer, is_port_forward, &switch_uuid) { SyncReturn(format!("Failed to add session with id {}, {}", &id, e)) } else { SyncReturn("".to_owned()) @@ -504,6 +505,12 @@ pub fn session_elevate_with_logon(id: String, username: String, password: String } } +pub fn session_switch_sides(id: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.switch_sides(); + } +} + pub fn main_get_sound_inputs() -> Vec { #[cfg(not(any(target_os = "android", target_os = "ios")))] return get_sound_inputs(); @@ -1066,6 +1073,10 @@ pub fn cm_elevate_portable(conn_id: i32) { crate::ui_cm_interface::elevate_portable(conn_id); } +pub fn cm_switch_back(conn_id: i32) { + crate::ui_cm_interface::switch_back(conn_id); +} + pub fn main_get_icon() -> String { #[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))] return ui_interface::get_icon(); @@ -1108,10 +1119,6 @@ pub fn query_onlines(ids: Vec) { crate::rendezvous_mediator::query_online_states(ids, handle_query_onlines) } -pub fn version_to_number(v: String) -> i64 { - hbb_common::get_version_number(&v) -} - pub fn option_synced() -> bool { crate::ui_interface::option_synced() } diff --git a/src/ipc.rs b/src/ipc.rs index 9048db766..d74842d64 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -166,6 +166,7 @@ pub enum Data { file_transfer_enabled: bool, restart: bool, recording: bool, + from_switch: bool, }, ChatMessage { text: String, @@ -207,6 +208,7 @@ pub enum Data { Empty, Disconnected, DataPortableService(DataPortableService), + SwitchBack, } #[tokio::main(flavor = "current_thread")] diff --git a/src/lang/ca.rs b/src/lang/ca.rs index bbcea1347..72f55b44b 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 2f56b6da0..14e8a463d 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", "弱"), ("Medium", "中"), ("Strong", "强"), + ("Switch Sides", "反转访问方向"), + ("Please confirm if you want to share your desktop?", "请确认要让对方访问你的桌面?"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 8852d602c..e2935770c 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 53ae46bd4..937990ea8 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index dd05dcdd5..7394a4628 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", "Schwach"), ("Medium", "Mittel"), ("Strong", "Stark"), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 955a3287d..839c69bbb 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 5ab59b946..88b0ba8e9 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", "Débil"), ("Medium", "Media"), ("Strong", "Fuerte"), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index a257425f1..dfd76405e 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 6edec8477..9c9860fb2 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/gr.rs b/src/lang/gr.rs index 4b54ba8ad..6ec1152cd 100644 --- a/src/lang/gr.rs +++ b/src/lang/gr.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", "Αδύναμο"), ("Medium", "Μέτριο"), ("Strong", "Δυνατό"), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 9e1a4d982..295104a67 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 65c30ec6d..5604a0c52 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index e3c1a1f0f..2e313e101 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", "Debole"), ("Medium", "Media"), ("Strong", "Forte"), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 6ebb11ef5..a280940c7 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index a6825b523..1cdf529ce 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 816eb370d..59d26135f 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index df985cccf..ee4b45334 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index dba37b5da..66373a5e9 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 31c9153f2..5a137f391 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index abe642d9c..a7e42e0e4 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", "Слабый"), ("Medium", "Средний"), ("Strong", "Стойкий"), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 56d14652d..c735cb28c 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 3d2ad3be8..6a17cc906 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 165597e7e..ebb43f6b7 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 739d53570..d9463318d 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 498131d0c..146e60f9a 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index adb05c943..729932973 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 2b062c3f7..a78509e59 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index a4a179c86..483ee67e3 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index cd9f270ec..459c517ff 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", "弱"), ("Medium", "中"), ("Strong", "強"), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index ff24baab7..ca99be12e 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 6988efba7..53de4e67c 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -431,5 +431,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Weak", ""), ("Medium", ""), ("Strong", ""), + ("Switch Sides", ""), + ("Please confirm if you want to share your desktop?", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index a7526c8b4..83ec5db55 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -40,6 +40,7 @@ lazy_static::lazy_static! { static ref LOGIN_FAILURES: Arc::>> = Default::default(); static ref SESSIONS: Arc::>> = Default::default(); static ref ALIVE_CONNS: Arc::>> = Default::default(); + pub static ref SWITCH_SIDES_UUID: Arc::>> = Default::default(); } pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); @@ -102,6 +103,7 @@ pub struct Connection { chat_unanswered: bool, close_manually: bool, elevation_requested: bool, + from_switch: bool, } impl Subscriber for ConnInner { @@ -134,6 +136,7 @@ const MILLI1: Duration = Duration::from_millis(1); const SEND_TIMEOUT_VIDEO: u64 = 12_000; const SEND_TIMEOUT_OTHER: u64 = SEND_TIMEOUT_VIDEO * 10; const SESSION_TIMEOUT: Duration = Duration::from_secs(30); +const SWITCH_SIDES_TIMEOUT: Duration = Duration::from_secs(30); impl Connection { pub async fn start( @@ -198,6 +201,7 @@ impl Connection { chat_unanswered: false, close_manually: false, elevation_requested: false, + from_switch: false, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -362,6 +366,13 @@ impl Connection { log::error!("Failed to start portable service from cm:{:?}", e); } } + ipc::Data::SwitchBack => { + let mut misc = Misc::new(); + misc.set_switch_back(SwitchBack::default()); + let mut msg = Message::new(); + msg.set_misc(misc); + conn.send(msg).await; + } _ => {} } }, @@ -954,6 +965,7 @@ impl Connection { file_transfer_enabled: self.file_transfer_enabled(), restart: self.restart, recording: self.recording, + from_switch: self.from_switch, }); } @@ -1078,29 +1090,33 @@ impl Connection { return Config::get_option(enable_prefix_option).is_empty(); } - async fn on_message(&mut self, msg: Message) -> bool { - if let Some(message::Union::LoginRequest(lr)) = msg.union { - self.lr = lr.clone(); - if let Some(o) = lr.option.as_ref() { - self.update_option(o).await; - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( - self.inner.id(), - scrap::codec::EncoderUpdate::State(q), - ); - } else { - scrap::codec::Encoder::update_video_encoder( - self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, - ); - } + async fn handle_login_request_without_validation(&mut self, lr: &LoginRequest) { + self.lr = lr.clone(); + if let Some(o) = lr.option.as_ref() { + self.update_option(o).await; + if let Some(q) = o.video_codec_state.clone().take() { + scrap::codec::Encoder::update_video_encoder( + self.inner.id(), + scrap::codec::EncoderUpdate::State(q), + ); } else { scrap::codec::Encoder::update_video_encoder( self.inner.id(), scrap::codec::EncoderUpdate::DisableHwIfNotExist, ); } - self.video_ack_required = lr.video_ack_required; + } else { + scrap::codec::Encoder::update_video_encoder( + self.inner.id(), + scrap::codec::EncoderUpdate::DisableHwIfNotExist, + ); + } + self.video_ack_required = lr.video_ack_required; + } + + async fn on_message(&mut self, msg: Message) -> bool { + if let Some(message::Union::LoginRequest(lr)) = msg.union { + self.handle_login_request_without_validation(&lr).await; if self.authorized { return true; } @@ -1247,6 +1263,21 @@ impl Connection { .unwrap() .update_network_delay(new_delay); } + } else if let Some(message::Union::SwitchSidesResponse(_s)) = msg.union { + #[cfg(feature = "flutter")] + if let Some(lr) = _s.lr.clone().take() { + self.handle_login_request_without_validation(&lr).await; + let uuid_old = SWITCH_SIDES_UUID.lock().unwrap().remove(&lr.my_id); + if let Ok(uuid) = uuid::Uuid::from_slice(_s.uuid.to_vec().as_ref()) { + if let Some((instant, uuid_old)) = uuid_old { + if instant.elapsed() < SWITCH_SIDES_TIMEOUT && uuid == uuid_old { + self.from_switch = true; + self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), true); + self.send_logon_response().await; + } + } + } + } } else if self.authorized { match msg.union { Some(message::Union::MouseEvent(me)) => { @@ -1536,6 +1567,11 @@ impl Connection { } _ => {} }, + #[cfg(feature = "flutter")] + Some(misc::Union::SwitchSidesRequest(s)) => { + crate::flutter::switch_sides(&self.lr.my_id, &s.uuid); + return false; + } _ => {} }, _ => {} diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 1f3d5f7ec..21504d20d 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -264,6 +264,8 @@ impl InvokeUiSession for SciterHandler { fn update_block_input_state(&self, on: bool) { self.call("updateBlockInputState", &make_args!(on)); } + + fn switch_back(&self, _id: &str) {} } pub struct SciterSession(Session); @@ -440,7 +442,7 @@ impl SciterSession { ConnType::DEFAULT_CONN }; - session.lc.write().unwrap().initialize(id, conn_type); + session.lc.write().unwrap().initialize(id, conn_type, None); Self(session) } diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 551352ff7..dd0ce2b24 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -48,6 +48,7 @@ pub struct Client { pub file: bool, pub restart: bool, pub recording: bool, + pub from_switch: bool, #[serde(skip)] tx: UnboundedSender, } @@ -118,6 +119,7 @@ impl ConnectionManager { file: bool, restart: bool, recording: bool, + from_switch: bool, tx: mpsc::UnboundedSender, ) { let client = Client { @@ -134,6 +136,7 @@ impl ConnectionManager { file, restart, recording, + from_switch, tx, }; CLIENTS @@ -241,6 +244,14 @@ pub fn get_clients_length() -> usize { clients.len() } +#[inline] +#[cfg(feature = "flutter")] +pub fn switch_back(id: i32) { + if let Some(client) = CLIENTS.read().unwrap().get(&id) { + allow_err!(client.tx.send(Data::SwitchBack)); + }; +} + impl IpcTaskRunner { #[cfg(windows)] async fn enable_cliprdr_file_context(&mut self, conn_id: i32, enabled: bool) { @@ -308,9 +319,9 @@ impl IpcTaskRunner { } Ok(Some(data)) => { match data { - Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording} => { + Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, from_switch} => { log::debug!("conn_id: {}", id); - self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, self.tx.clone()); + self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, from_switch,self.tx.clone()); self.authorized = authorized; self.conn_id = id; #[cfg(windows)] @@ -498,6 +509,7 @@ pub async fn start_listen( file, restart, recording, + from_switch, .. }) => { current_id = id; @@ -514,6 +526,7 @@ pub async fn start_listen( file, restart, recording, + from_switch, tx.clone(), ); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 00f1f90cf..800ca35c6 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -8,6 +8,7 @@ use crate::common::{self, is_keyboard_mode_supported, GrabState}; use crate::keyboard; use crate::{client::Data, client::Interface}; use async_trait::async_trait; +use bytes::Bytes; use hbb_common::config::{Config, LocalConfig, PeerConfig}; use hbb_common::rendezvous_proto::ConnType; use hbb_common::tokio::{self, sync::mpsc}; @@ -18,6 +19,7 @@ use std::collections::HashMap; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock}; +use uuid::Uuid; pub static IS_IN: AtomicBool = AtomicBool::new(false); #[derive(Clone, Default)] @@ -616,6 +618,22 @@ impl Session { pub fn elevate_with_logon(&self, username: String, password: String) { self.send(Data::ElevateWithLogon(username, password)); } + + pub fn switch_sides(&self) { + let uuid = Uuid::new_v4(); + crate::server::SWITCH_SIDES_UUID + .lock() + .unwrap() + .insert(self.id.clone(), (tokio::time::Instant::now(), uuid.clone())); + let mut misc = Misc::new(); + misc.set_switch_sides_request(SwitchSidesRequest { + uuid: Bytes::from(uuid.as_bytes().to_vec()), + ..Default::default() + }); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + self.send(Data::Message(msg_out)); + } } pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { @@ -655,6 +673,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { #[cfg(any(target_os = "android", target_os = "ios"))] fn clipboard(&self, content: String); fn cancel_msgbox(&self, tag: &str); + fn switch_back(&self, id: &str); } impl Deref for Session {