diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index 738b57c4d..4e98f311d 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -70,12 +70,7 @@ jobs: - name: Install flutter rust bridge deps run: | - dart pub global activate ffigen --version 5.0.1 - $exists = Test-Path ~/.cargo/bin/flutter_rust_bridge_codegen.exe - Push-Location .. - git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1 - Push-Location flutter_rust_bridge/frb_codegen ; cargo install --path . ; Pop-Location - Pop-Location + cargo install flutter_rust_bridge_codegen Push-Location flutter ; flutter pub get ; Pop-Location ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart @@ -134,14 +129,7 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | - dart pub global activate ffigen --version 5.0.1 - # flutter_rust_bridge - pushd /tmp - wget https://github.com/Kingtous/flutter_rust_bridge/releases/download/1.32.0-rustdesk/flutter_rust_bridge_codegen-x86_64-darwin.tgz - tar -zxvf flutter_rust_bridge_codegen-x86_64-darwin.tgz - mkdir -p ~/.cargo/bin - mv flutter_rust_bridge_codegen ~/.cargo/bin; chmod +x ~/.cargo/bin/flutter_rust_bridge_codegen - popd + cargo install flutter_rust_bridge_codegen pushd flutter && flutter pub get && popd ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart @@ -307,15 +295,10 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true - - name: Install ffigen - run: | - dart pub global activate ffigen --version 5.0.1 - - name: Install flutter rust bridge deps shell: bash run: | - pushd /tmp && git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1 || true && popd - pushd /tmp/flutter_rust_bridge/frb_codegen && cargo install --path . && popd + cargo install flutter_rust_bridge_codegen pushd flutter && flutter pub get && popd - name: Run flutter rust bridge @@ -328,6 +311,7 @@ jobs: name: bridge-artifact path: | ./src/bridge_generated.rs + ./src/bridge_generated.io.rs ./flutter/lib/generated_bridge.dart ./flutter/lib/generated_bridge.freezed.dart diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index efa085ea8..145eee55d 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -72,12 +72,7 @@ jobs: - name: Install flutter rust bridge deps run: | - dart pub global activate ffigen --version 5.0.1 - $exists = Test-Path ~/.cargo/bin/flutter_rust_bridge_codegen.exe - Push-Location .. - git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1 - Push-Location flutter_rust_bridge/frb_codegen ; cargo install --path . ; Pop-Location - Pop-Location + cargo install flutter_rust_bridge_codegen Push-Location flutter ; flutter pub get ; Pop-Location ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart @@ -213,14 +208,7 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | - dart pub global activate ffigen --version 5.0.1 - # flutter_rust_bridge - pushd /tmp - wget https://github.com/Kingtous/flutter_rust_bridge/releases/download/1.32.0-rustdesk/flutter_rust_bridge_codegen-x86_64-darwin.tgz - tar -zxvf flutter_rust_bridge_codegen-x86_64-darwin.tgz - mkdir -p ~/.cargo/bin - mv flutter_rust_bridge_codegen ~/.cargo/bin; chmod +x ~/.cargo/bin/flutter_rust_bridge_codegen - popd + cargo install flutter_rust_bridge_codegen pushd flutter && flutter pub get && popd ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart @@ -414,15 +402,10 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true - - name: Install ffigen - run: | - dart pub global activate ffigen --version 5.0.1 - - name: Install flutter rust bridge deps shell: bash run: | - pushd /tmp && git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1 || true && popd - pushd /tmp/flutter_rust_bridge/frb_codegen && cargo install --path . && popd + cargo install flutter_rust_bridge_codegen pushd flutter && flutter pub get && popd - name: Run flutter rust bridge @@ -435,6 +418,7 @@ jobs: name: bridge-artifact path: | ./src/bridge_generated.rs + ./src/bridge_generated.io.rs ./flutter/lib/generated_bridge.dart ./flutter/lib/generated_bridge.freezed.dart diff --git a/.gitignore b/.gitignore index 1ecea7af8..fd5b5955e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ cert.pfx sciter.dll **pdb src/bridge_generated.rs +src/bridge_generated.io.rs *deb rustdesk *.cache diff --git a/Cargo.lock b/Cargo.lock index 8dfde0335..693ae7d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,11 +45,13 @@ dependencies = [ [[package]] name = "allo-isolate" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb993621e6bf1b67591005b0adad126159a0ab31af379743906158aed5330d0" +checksum = "8ed55848be9f41d44c79df6045b680a74a78bc579e0813f7f196cd7928e22fb1" dependencies = [ + "anyhow", "atomic", + "chrono", ] [[package]] @@ -471,6 +473,12 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "build-target" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" + [[package]] name = "bumpalo" version = "3.11.1" @@ -565,9 +573,9 @@ dependencies = [ [[package]] name = "cbindgen" -version = "0.23.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6d248e3ca02f3fbfabcb9284464c596baec223a26d91bbf44a5a62ddb0d900" +checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" dependencies = [ "clap 3.2.23", "heck 0.4.0", @@ -838,6 +846,16 @@ dependencies = [ "toml", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + [[package]] name = "convert_case" version = "0.5.0" @@ -1335,6 +1353,17 @@ dependencies = [ "byteorder", ] +[[package]] +name = "delegate" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1672,6 +1701,18 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "extend" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5216e387a76eebaaf11f6d871ec8a4aae0b25f05456ee21f228e024b1b3610" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "failure" version = "0.1.8" @@ -1744,28 +1785,44 @@ dependencies = [ [[package]] name = "flutter_rust_bridge" -version = "1.32.0" -source = "git+https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge#e5adce55eea0b74d3680e66a2c5252edf17b07e1" +version = "1.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8079119bbe8fb63d7ebb731fa2aa68c6c8375f4ac95ca26d5868e64c0f4b9244" dependencies = [ "allo-isolate", "anyhow", + "build-target", + "bytemuck", + "cc", + "chrono", + "console_error_panic_hook", "flutter_rust_bridge_macros", + "js-sys", "lazy_static", + "libc", + "log", "parking_lot 0.12.1", "threadpool", + "wasm-bindgen", + "web-sys", ] [[package]] name = "flutter_rust_bridge_codegen" -version = "1.32.0" -source = "git+https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge#e5adce55eea0b74d3680e66a2c5252edf17b07e1" +version = "1.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd7396bc479eae8aa24243e4c0e3d3dbda1909134f8de6bde4f080d262c9a0d" dependencies = [ "anyhow", "cargo_metadata", "cbindgen", + "clap 3.2.23", "convert_case", + "delegate", "enum_dispatch", "env_logger 0.9.3", + "extend", + "itertools 0.10.5", "lazy_static", "log", "pathdiff", @@ -1773,17 +1830,18 @@ dependencies = [ "regex", "serde 1.0.149", "serde_yaml", - "structopt", "syn", "tempfile", "thiserror", "toml", + "topological-sort", ] [[package]] name = "flutter_rust_bridge_macros" -version = "1.32.0" -source = "git+https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge#e5adce55eea0b74d3680e66a2c5252edf17b07e1" +version = "1.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5cd827645690ef378be57a890d0581e17c28d07b712872af7d744f454fd27d" [[package]] name = "fnv" @@ -2164,7 +2222,7 @@ checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" dependencies = [ "anyhow", "heck 0.3.3", - "itertools", + "itertools 0.9.0", "proc-macro-crate 0.1.5", "proc-macro-error", "proc-macro2", @@ -2798,6 +2856,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.3.4" @@ -5150,30 +5217,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "strum" version = "0.18.0" @@ -5564,6 +5607,12 @@ dependencies = [ "serde 1.0.149", ] +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + [[package]] name = "tower-service" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 427fcd4e7..1e9af30e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ num_cpus = "1.13" bytes = { version = "1.2", features = ["serde"] } default-net = { git = "https://github.com/Kingtous/default-net" } wol-rs = "0.9.1" -flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge", optional = true } +flutter_rust_bridge = { version = "1.61.1", optional = true } errno = "0.2.8" rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.1", features = ["serde"] } @@ -126,7 +126,7 @@ android_logger = "0.11" jni = "0.19" [target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] -flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge" } +flutter_rust_bridge = "1.61.1" [workspace] members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/simple_rc", "libs/portable"] @@ -144,7 +144,7 @@ winapi = { version = "0.3", features = [ "winnt" ] } cc = "1.0" hbb_common = { path = "libs/hbb_common" } simple_rc = { path = "libs/simple_rc", optional = true } -flutter_rust_bridge_codegen = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge" } +flutter_rust_bridge_codegen = "1.61.1" [dev-dependencies] hound = "3.5" diff --git a/build.rs b/build.rs index ade63f0bc..d15f27424 100644 --- a/build.rs +++ b/build.rs @@ -85,26 +85,35 @@ fn install_oboe() { #[cfg(feature = "flutter")] fn gen_flutter_rust_bridge() { + use lib_flutter_rust_bridge_codegen::{ + config_parse, frb_codegen, get_symbols_if_no_duplicates, RawOpts, + }; let llvm_path = match std::env::var("LLVM_HOME") { Ok(path) => Some(vec![path]), Err(_) => None, }; // Tell Cargo that if the given file changes, to rerun this build script. println!("cargo:rerun-if-changed=src/flutter_ffi.rs"); - // settings for fbr_codegen - let opts = lib_flutter_rust_bridge_codegen::Opts { + // Options for frb_codegen + let raw_opts = RawOpts { // Path of input Rust code - rust_input: "src/flutter_ffi.rs".to_string(), + rust_input: vec!["src/flutter_ffi.rs".to_string()], // Path of output generated Dart code - dart_output: "flutter/lib/generated_bridge.dart".to_string(), + dart_output: vec!["flutter/lib/generated_bridge.dart".to_string()], // Path of output generated C header c_output: Some(vec!["flutter/macos/Runner/bridge_generated.h".to_string()]), - // for other options lets use default + /// Path to the installed LLVM llvm_path, + // for other options use defaults ..Default::default() }; - // run fbr_codegen - lib_flutter_rust_bridge_codegen::frb_codegen(opts).unwrap(); + // get opts from raw opts + let configs = config_parse(raw_opts); + // generation of rust api for ffi + let all_symbols = get_symbols_if_no_duplicates(&configs).unwrap(); + for config in configs.iter() { + frb_codegen(config, &all_symbols).unwrap(); + } } fn main() { diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index fde039017..1aead0fd4 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1261,23 +1261,28 @@ StreamSubscription? listenUniLinks() { /// Returns true if we successfully handle the startup arguments. bool checkArguments() { + // bootArgs:[--connect, 362587269, --switch_uuid, e3d531cc-5dce-41e0-bd06-5d4a2b1eec05] // check connect args final connectIndex = bootArgs.indexOf("--connect"); if (connectIndex == -1) { return false; } - String? arg = + String? id = bootArgs.length < connectIndex + 1 ? null : bootArgs[connectIndex + 1]; - if (arg != null) { - if (arg.startsWith(kUniLinksPrefix)) { - return parseRustdeskUri(arg); + final switchUuidIndex = bootArgs.indexOf("--switch_uuid"); + String? switchUuid = bootArgs.length < switchUuidIndex + 1 + ? null + : bootArgs[switchUuidIndex + 1]; + if (id != null) { + if (id.startsWith(kUniLinksPrefix)) { + return parseRustdeskUri(id); } else { // remove "--connect xxx" in the `bootArgs` array bootArgs.removeAt(connectIndex); bootArgs.removeAt(connectIndex); // fallback to peer id Future.delayed(Duration.zero, () { - rustDeskWinManager.newRemoteDesktop(arg); + rustDeskWinManager.newRemoteDesktop(id, switch_uuid: switchUuid); }); return true; } @@ -1307,8 +1312,10 @@ bool callUniLinksUriHandler(Uri uri) { // new connection if (uri.authority == "connection" && uri.path.startsWith("/new/")) { final peerId = uri.path.substring("/new/".length); + var param = uri.queryParameters; + String? switch_uuid = param["switch_uuid"]; Future.delayed(Duration.zero, () { - rustDeskWinManager.newRemoteDesktop(peerId); + rustDeskWinManager.newRemoteDesktop(peerId, switch_uuid: switch_uuid); }); return false; } @@ -1606,3 +1613,7 @@ Widget dialogButton(String text, )); } } + +int version_cmp(String v1, String v2) { + return bind.versionToNumber(v: v1) - bind.versionToNumber(v: v2); +} diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 2773a3049..0501c298a 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -544,7 +544,7 @@ void setPasswordDialog() async { final p1 = TextEditingController(text: pw); var errMsg0 = ""; var errMsg1 = ""; - final RxString rxPass = p0.text.obs; + final RxString rxPass = pw.trim().obs; final rules = [ DigitValidationRule(), UppercaseValidationRule(), @@ -603,7 +603,7 @@ void setPasswordDialog() async { controller: p0, focusNode: FocusNode()..requestFocus(), onChanged: (value) { - rxPass.value = value; + rxPass.value = value.trim(); }, ), ), 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..62289d5f0 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,19 @@ class _RemoteMenubarState extends State { dismissOnClicked: true, )); } + if (pi.platform != kPeerPlatformAndroid && + 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 +735,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 +895,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 +1404,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..986d93fe8 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -184,13 +184,9 @@ class FfiModel with ChangeNotifier { } else if (name == 'update_privacy_mode') { updatePrivacyMode(evt, peerId); } else if (name == 'new_connection') { - var arg = evt['peer_id'].toString(); - if (arg.startsWith(kUniLinksPrefix)) { - parseRustdeskUri(arg); - } else { - Future.delayed(Duration.zero, () { - rustDeskWinManager.newRemoteDesktop(arg); - }); + var uni_links = evt['uni_links'].toString(); + if (uni_links.startsWith(kUniLinksPrefix)) { + parseRustdeskUri(uni_links); } } else if (name == 'alias') { handleAliasChanged(evt); @@ -199,6 +195,10 @@ class FfiModel with ChangeNotifier { parent.target?.serverModel.setShowElevation(show); } else if (name == 'cancel_msgbox') { cancelMsgBox(evt, peerId); + } else if (name == 'switch_back') { + final peer_id = evt['peer_id'].toString(); + await bind.sessionSwitchSides(id: peer_id); + closeConnection(id: peer_id); } }; } @@ -1289,7 +1289,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,19 +1307,23 @@ 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 { await for (final message in stream) { - if (message is Event) { + if (message is EventToUI_Event) { try { Map event = json.decode(message.field0); await cb(event); } catch (e) { debugPrint('json.decode fail1(): $e, ${message.field0}'); } - } else if (message is Rgba) { + } else if (message is EventToUI_Rgba) { imageModel.onRgba(message.field0); } } 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 13e9d36bf..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(); diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index f096218b0..a63f49ba8 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -51,11 +51,7 @@ dependencies: image_picker: ^0.8.5 image: ^3.1.3 back_button_interceptor: ^6.0.1 - flutter_rust_bridge: - git: - url: https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge - ref: master - path: frb_dart + flutter_rust_bridge: ^1.61.1 window_manager: git: url: https://github.com/Kingtous/rustdesk_window_manager @@ -103,6 +99,7 @@ dev_dependencies: build_runner: ^2.1.11 freezed: ^2.0.3 flutter_lints: ^2.0.0 + ffigen: ^7.2.4 # rerun: flutter pub run flutter_launcher_icons:main icons_launcher: 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/core_main.rs b/src/core_main.rs index 7707a41c8..8b99f6131 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -298,11 +298,27 @@ fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option { return None; } @@ -316,7 +332,6 @@ fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option 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 +424,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(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index d92cfba23..992fff853 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()) @@ -503,6 +504,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(); @@ -1065,6 +1072,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(); @@ -1107,8 +1118,8 @@ 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 version_to_number(v: String) -> SyncReturn { + SyncReturn(hbb_common::get_version_number(&v)) } pub fn option_synced() -> bool { diff --git a/src/ipc.rs b/src/ipc.rs index 9048db766..d4d803aec 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,8 @@ pub enum Data { Empty, Disconnected, DataPortableService(DataPortableService), + SwitchSidesRequest(String), + SwitchSidesBack, } #[tokio::main(flavor = "current_thread")] @@ -427,6 +430,15 @@ async fn handle(data: Data, stream: &mut Connection) { Data::TestRendezvousServer => { crate::test_rendezvous_server(); } + Data::SwitchSidesRequest(id) => { + let uuid = uuid::Uuid::new_v4(); + crate::server::insert_switch_sides_uuid(id, uuid.clone()); + allow_err!( + stream + .send(&Data::SwitchSidesRequest(uuid.to_string())) + .await + ); + } _ => {} } } 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 6d94f8816..5161f9846 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/platform/linux.rs b/src/platform/linux.rs index 0e4f98d52..34276426d 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -784,12 +784,14 @@ extern "C" fn breakdown_signal_handler(sig: i32) { sig, stack.join("\n").to_string() ); - system_message( - "RustDesk", - &format!("Got signal {} and exit.{}", sig, info), - true, - ) - .ok(); + if !info.is_empty() { + system_message( + "RustDesk", + &format!("Got signal {} and exit.{}", sig, info), + true, + ) + .ok(); + } std::process::exit(0); } diff --git a/src/server/connection.rs b/src/server/connection.rs index a7526c8b4..cd5bd8cfa 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(); + 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); @@ -101,7 +102,9 @@ pub struct Connection { last_recv_time: Arc>, chat_unanswered: bool, close_manually: bool, + #[allow(unused)] elevation_requested: bool, + from_switch: bool, } impl Subscriber for ConnInner { @@ -134,6 +137,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(10); impl Connection { pub async fn start( @@ -198,6 +202,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 +367,13 @@ impl Connection { log::error!("Failed to start portable service from cm:{:?}", e); } } + ipc::Data::SwitchSidesBack => { + 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 +966,7 @@ impl Connection { file_transfer_enabled: self.file_transfer_enabled(), restart: self.restart, recording: self.recording, + from_switch: self.from_switch, }); } @@ -1078,29 +1091,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 +1264,25 @@ 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; + SWITCH_SIDES_UUID + .lock() + .unwrap() + .retain(|_, v| v.0.elapsed() < SWITCH_SIDES_TIMEOUT); + 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 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)) => { @@ -1512,7 +1548,7 @@ impl Connection { self.send(msg).await; } } - Some(elevation_request::Union::Logon(r)) => { + Some(elevation_request::Union::Logon(_r)) => { #[cfg(windows)] { let mut err = "No need to elevate".to_string(); @@ -1521,7 +1557,8 @@ impl Connection { { use crate::portable_service::client; err = client::start_portable_service(client::StartPara::Logon( - r.username, r.password, + _r.username, + _r.password, )) .err() .map_or("".to_string(), |e| e.to_string()); @@ -1536,6 +1573,19 @@ impl Connection { } _ => {} }, + #[cfg(feature = "flutter")] + Some(misc::Union::SwitchSidesRequest(s)) => { + if let Ok(uuid) = uuid::Uuid::from_slice(&s.uuid.to_vec()[..]) { + crate::run_me(vec![ + "--connect", + &self.lr.my_id, + "--switch_uuid", + uuid.to_string().as_ref(), + ]) + .ok(); + return false; + } + } _ => {} }, _ => {} @@ -1756,6 +1806,13 @@ impl Connection { } } +pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) { + SWITCH_SIDES_UUID + .lock() + .unwrap() + .insert(id, (tokio::time::Instant::now(), uuid)); +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] async fn start_ipc( mut rx_to_cm: mpsc::UnboundedReceiver, diff --git a/src/server/dbus.rs b/src/server/dbus.rs index 5a38fe7cb..081db3e8f 100644 --- a/src/server/dbus.rs +++ b/src/server/dbus.rs @@ -5,10 +5,10 @@ /// [Flutter]: handle uni links for linux use dbus::blocking::Connection; use dbus_crossroads::{Crossroads, IfaceBuilder}; -use hbb_common::{log}; -use std::{error::Error, fmt, time::Duration}; +use hbb_common::log; #[cfg(feature = "flutter")] use std::collections::HashMap; +use std::{error::Error, fmt, time::Duration}; const DBUS_NAME: &str = "org.rustdesk.rustdesk"; const DBUS_PREFIX: &str = "/dbus"; @@ -30,15 +30,16 @@ impl fmt::Display for DbusError { impl Error for DbusError {} /// invoke new connection from dbus -/// +/// /// [Tips]: /// How to test by CLI: /// - use dbus-send command: /// `dbus-send --session --print-reply --dest=org.rustdesk.rustdesk /dbus org.rustdesk.rustdesk.NewConnection string:'PEER_ID'` -pub fn invoke_new_connection(peer_id: String) -> Result<(), Box> { +pub fn invoke_new_connection(uni_links: String) -> Result<(), Box> { let conn = Connection::new_session()?; let proxy = conn.with_proxy(DBUS_NAME, DBUS_PREFIX, DBUS_TIMEOUT); - let (ret,): (String,) = proxy.method_call(DBUS_NAME, DBUS_METHOD_NEW_CONNECTION, (peer_id,))?; + let (ret,): (String,) = + proxy.method_call(DBUS_NAME, DBUS_METHOD_NEW_CONNECTION, (uni_links,))?; if ret != DBUS_METHOD_RETURN_SUCCESS { log::error!("error on call new connection to dbus server"); return Err(Box::new(DbusError("not success".to_string()))); @@ -67,7 +68,7 @@ fn handle_client_message(builder: &mut IfaceBuilder<()>) { DBUS_METHOD_NEW_CONNECTION, (DBUS_METHOD_NEW_CONNECTION_ID,), (DBUS_METHOD_RETURN,), - move |_, _, (_peer_id,): (String,)| { + move |_, _, (_uni_links,): (String,)| { #[cfg(feature = "flutter")] { use crate::flutter::{self, APP_TYPE_MAIN}; @@ -79,7 +80,7 @@ fn handle_client_message(builder: &mut IfaceBuilder<()>) { { let data = HashMap::from([ ("name", "new_connection"), - ("peer_id", _peer_id.as_str()) + ("uni_links", _uni_links.as_str()), ]); if !stream.add(serde_json::ser::to_string(&data).unwrap_or("".to_string())) { log::error!("failed to add dbus message to flutter global dbus stream."); 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..ea3553c8a 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::SwitchSidesBack)); + }; +} + 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 00d046882..3fb3f2621 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -8,6 +8,7 @@ use crate::common::{self, 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}; @@ -16,8 +17,10 @@ use hbb_common::{fs, get_version_number, log, Stream}; use rdev::{Event, EventType::*}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; +use std::str::FromStr; 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 +619,40 @@ impl Session { pub fn elevate_with_logon(&self, username: String, password: String) { self.send(Data::ElevateWithLogon(username, password)); } + + #[tokio::main(flavor = "current_thread")] + pub async fn switch_sides(&self) { + match crate::ipc::connect(1000, "").await { + Ok(mut conn) => { + if conn + .send(&crate::ipc::Data::SwitchSidesRequest(self.id.to_string())) + .await + .is_ok() + { + if let Ok(Some(data)) = conn.next_timeout(1000).await { + match data { + crate::ipc::Data::SwitchSidesRequest(str_uuid) => { + if let Ok(uuid) = Uuid::from_str(&str_uuid) { + 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)); + } + } + _ => {} + } + } + } + } + Err(err) => { + log::info!("server not started (will try to start): {}", err); + } + } + } } pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { @@ -655,6 +692,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 {