From 317b350d2b0b7ef07b5594f0247a654bb4b10143 Mon Sep 17 00:00:00 2001 From: csf Date: Tue, 31 May 2022 22:09:36 +0800 Subject: [PATCH] multi remote instances 0.5 --- Cargo.lock | 2 +- flutter/lib/desktop/pages/remote_page.dart | 112 +++++++------- flutter/lib/mobile/widgets/dialog.dart | 2 +- flutter/lib/models/model.dart | 170 +++++++++++---------- flutter/lib/models/native_model.dart | 24 +-- src/client/file_trait.rs | 14 +- src/flutter.rs | 30 ++-- src/flutter_ffi.rs | 107 +++++++------ 8 files changed, 238 insertions(+), 223 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 441b49c58..b999cb585 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1477,7 +1477,7 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_codegen" version = "1.32.0" -source = "git+https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge#827fc60143988dfc3759f7e8ce16a20d80edd710" +source = "git+https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge#3cc3818d19b731d5f9893c48699182bed4d4c15e" dependencies = [ "anyhow", "cargo_metadata", diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 49beb7819..76ab4e8ab 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -143,7 +143,7 @@ class _RemotePageState extends State with WindowListener { if (newValue.length > common) { var s = newValue.substring(common); if (s.length > 1) { - FFI.setByName('input_string', s); + FFI.bind.sessionInputString(id: widget.id, value: s); } else { inputChar(s); } @@ -177,11 +177,11 @@ class _RemotePageState extends State with WindowListener { content == '()' || content == '【】')) { // can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input - FFI.setByName('input_string', content); + FFI.bind.sessionInputString(id: widget.id, value: content); openKeyboard(); return; } - FFI.setByName('input_string', content); + FFI.bind.sessionInputString(id: widget.id, value: content); } else { inputChar(content); } @@ -328,8 +328,8 @@ class _RemotePageState extends State with WindowListener { if (dy > 0) dy = -1; else if (dy < 0) dy = 1; - FFI.setByName( - 'send_mouse', '{"type": "wheel", "x": "$dx", "y": "$dy"}'); + FFI.setByName('send_mouse', + '{"id": "${widget.id}", "type": "wheel", "x": "$dx", "y": "$dy"}'); } }, child: MouseRegion( @@ -456,7 +456,7 @@ class _RemotePageState extends State with WindowListener { icon: Icon(Icons.more_vert), onPressed: () { setState(() => _showEdit = false); - showActions(); + showActions(widget.id); }, ), ]), @@ -553,7 +553,8 @@ class _RemotePageState extends State with WindowListener { }, onTwoFingerScaleEnd: (d) { _scale = 1; - FFI.setByName('peer_option', '{"name": "view-style", "value": ""}'); + FFI.bind + .sessionPeerOption(id: widget.id, name: "view-style", value: ""); }, onThreeFingerVerticalDragUpdate: FFI.ffiModel.isPeerAndroid ? null @@ -599,18 +600,12 @@ class _RemotePageState extends State with WindowListener { Widget getBodyForDesktopWithListener(bool keyboard) { var paints = [ImagePaint()]; - final cursor = await; - if (keyboard || - FFI.getByName('toggle_option', 'show-remote-cursor') == 'true') { + final cursor = FFI.bind.getSessionToggleOptionSync( + id: widget.id, arg: 'show-remote-cursor')[0] == + 1; + if (keyboard || cursor) { paints.add(CursorPaint()); } - return FutureBuilder( - future: FFI.rustdeskImpl - .getSessionToggleOption(id: widget.id, arg: 'show-remote-cursor'), - builder: (ctx, snapshot) { - if(snapshot) - }, - ); return Container( color: MyTheme.canvasColor, child: Stack(children: paints)); } @@ -636,7 +631,7 @@ class _RemotePageState extends State with WindowListener { return out; } - void showActions() { + void showActions(String id) async { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; @@ -655,7 +650,7 @@ class _RemotePageState extends State with WindowListener { style: flatButtonStyle, onPressed: () { Navigator.pop(context); - showSetOSPassword(false); + showSetOSPassword(widget.id, false); }, child: Icon(Icons.edit, color: MyTheme.accent), ) @@ -678,7 +673,8 @@ class _RemotePageState extends State with WindowListener { more.add(PopupMenuItem( child: Text(translate('Insert Lock')), value: 'lock')); if (pi.platform == 'Windows' && - FFI.getByName('toggle_option', 'privacy-mode') != 'true') { + await FFI.bind.getSessionToggleOption(id: id, arg: 'privacy-mode') != + true) { more.add(PopupMenuItem( child: Text(translate( (FFI.ffiModel.inputBlocked ? 'Unb' : 'B') + 'lock user input')), @@ -693,28 +689,30 @@ class _RemotePageState extends State with WindowListener { elevation: 8, ); if (value == 'cad') { - FFI.setByName('ctrl_alt_del'); + FFI.bind.sessionCtrlAltDel(id: widget.id); } else if (value == 'lock') { - FFI.setByName('lock_screen'); + FFI.bind.sessionLockScreen(id: widget.id); } else if (value == 'block-input') { - FFI.setByName('toggle_option', - (FFI.ffiModel.inputBlocked ? 'un' : '') + 'block-input'); + FFI.bind.sessionToggleOption( + id: widget.id, + value: (FFI.ffiModel.inputBlocked ? 'un' : '') + 'block-input'); FFI.ffiModel.inputBlocked = !FFI.ffiModel.inputBlocked; } else if (value == 'refresh') { - FFI.setByName('refresh'); + FFI.bind.sessionRefresh(id: widget.id); } else if (value == 'paste') { () async { ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain); if (data != null && data.text != null) { - FFI.setByName('input_string', '${data.text}'); + FFI.bind.sessionInputString(id: widget.id, value: data.text ?? ""); } }(); } else if (value == 'enter_os_password') { - var password = FFI.getByName('peer_option', "os-password"); - if (password != "") { - FFI.setByName('input_os_password', password); + var password = + await FFI.bind.getSessionOption(id: id, arg: "os-password"); + if (password != null) { + FFI.bind.sessionInputOsPassword(id: widget.id, value: password); } else { - showSetOSPassword(true); + showSetOSPassword(widget.id, true); } } else if (value == 'reset_canvas') { FFI.cursorModel.reset(); @@ -740,8 +738,8 @@ class _RemotePageState extends State with WindowListener { onTouchModeChange: (t) { FFI.ffiModel.toggleTouchMode(); final v = FFI.ffiModel.touchMode ? 'Y' : ''; - FFI.setByName('peer_option', - '{"name": "touch-mode", "value": "$v"}'); + FFI.bind.sessionPeerOption( + id: widget.id, name: "touch-mode", value: v); })); })); } @@ -956,12 +954,13 @@ class ImagePainter extends CustomPainter { } CheckboxListTile getToggle( - void Function(void Function()) setState, option, name) { + String id, void Function(void Function()) setState, option, name) { + final opt = FFI.bind.getSessionToggleOptionSync(id: id, arg: option)[0] == 1; return CheckboxListTile( - value: FFI.getByName('toggle_option', option) == 'true', + value: opt, onChanged: (v) { setState(() { - FFI.setByName('toggle_option', option); + FFI.bind.sessionToggleOption(id: id, value: option); }); }, dense: true, @@ -981,12 +980,10 @@ RadioListTile getRadio(String name, String toValue, String curValue, } void showOptions(String id) async { - // String quality = FFI.getByName('image_quality'); - String quality = - await FFI.rustdeskImpl.getSessionImageQuality(id: id) ?? 'balanced'; + String quality = await FFI.bind.getSessionImageQuality(id: id) ?? 'balanced'; if (quality == '') quality = 'balanced'; String viewStyle = - await FFI.rustdeskImpl.getSessionOption(id: id, arg: 'view-style') ?? ''; + await FFI.bind.getSessionOption(id: id, arg: 'view-style') ?? ''; var displays = []; final pi = FFI.ffiModel.pi; final image = FFI.ffiModel.getConnectionImage(); @@ -999,7 +996,7 @@ void showOptions(String id) async { children.add(InkWell( onTap: () { if (i == cur) return; - FFI.setByName('switch_display', i.toString()); + FFI.bind.sessionSwitchDisplay(id: id, value: i); SmartDialog.dismiss(); }, child: Ink( @@ -1028,30 +1025,30 @@ void showOptions(String id) async { DialogManager.show((setState, close) { final more = []; if (perms['audio'] != false) { - more.add(getToggle(setState, 'disable-audio', 'Mute')); + more.add(getToggle(id, setState, 'disable-audio', 'Mute')); } if (perms['keyboard'] != false) { if (perms['clipboard'] != false) - more.add(getToggle(setState, 'disable-clipboard', 'Disable clipboard')); + more.add( + getToggle(id, setState, 'disable-clipboard', 'Disable clipboard')); more.add(getToggle( - setState, 'lock-after-session-end', 'Lock after session end')); + id, setState, 'lock-after-session-end', 'Lock after session end')); if (pi.platform == 'Windows') { - more.add(getToggle(setState, 'privacy-mode', 'Privacy mode')); + more.add(getToggle(id, setState, 'privacy-mode', 'Privacy mode')); } } var setQuality = (String? value) { if (value == null) return; setState(() { quality = value; - FFI.setByName('image_quality', value); + FFI.bind.sessionSetImageQuality(id: id, value: value); }); }; var setViewStyle = (String? value) { if (value == null) return; setState(() { viewStyle = value; - FFI.setByName( - 'peer_option', '{"name": "view-style", "value": "$value"}'); + FFI.bind.sessionPeerOption(id: id, name: "view-style", value: value); FFI.canvasModel.updateViewStyle(); }); }; @@ -1069,7 +1066,8 @@ void showOptions(String id) async { getRadio('Balanced', 'balanced', quality, setQuality), getRadio('Optimize reaction time', 'low', quality, setQuality), Divider(color: MyTheme.border), - getToggle(setState, 'show-remote-cursor', 'Show remote cursor'), + getToggle( + id, setState, 'show-remote-cursor', 'Show remote cursor'), ] + more), actions: [], @@ -1078,10 +1076,12 @@ void showOptions(String id) async { }, clickMaskDismiss: true, backDismiss: true); } -void showSetOSPassword(bool login) { +void showSetOSPassword(String id, bool login) async { final controller = TextEditingController(); - var password = FFI.getByName('peer_option', "os-password"); - var autoLogin = FFI.getByName('peer_option', "auto-login") != ""; + var password = + await FFI.bind.getSessionOption(id: id, arg: "os-password") ?? ""; + var autoLogin = + await FFI.bind.getSessionOption(id: id, arg: "auto-login") != ""; controller.text = password; DialogManager.show((setState, close) { return CustomAlertDialog( @@ -1114,12 +1114,12 @@ void showSetOSPassword(bool login) { style: flatButtonStyle, onPressed: () { var text = controller.text.trim(); - FFI.setByName( - 'peer_option', '{"name": "os-password", "value": "$text"}'); - FFI.setByName('peer_option', - '{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}'); + FFI.bind + .sessionPeerOption(id: id, name: "os-password", value: text); + FFI.bind.sessionPeerOption( + id: id, name: "auto-login", value: autoLogin ? 'Y' : ''); if (text != "" && login) { - FFI.setByName('input_os_password', text); + FFI.bind.sessionInputOsPassword(id: id, value: text); } close(); }, diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart index 57d44e2aa..54f034627 100644 --- a/flutter/lib/mobile/widgets/dialog.dart +++ b/flutter/lib/mobile/widgets/dialog.dart @@ -137,7 +137,7 @@ void enterPasswordDialog(String id) { onPressed: () { var text = controller.text.trim(); if (text == '') return; - FFI.login(text, remember); + FFI.login(id, text, remember); close(); showLoading(translate('Logging in...')); }, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 4ced438db..b47e06c22 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -128,7 +128,7 @@ class FfiModel with ChangeNotifier { if (name == 'msgbox') { handleMsgBox(evt, peerId); } else if (name == 'peer_info') { - handlePeerInfo(evt); + handlePeerInfo(evt, peerId); } else if (name == 'connection_ready') { FFI.ffiModel.setConnectionType( evt['secure'] == 'true', evt['direct'] == 'true'); @@ -176,7 +176,7 @@ class FfiModel with ChangeNotifier { if (name == 'msgbox') { handleMsgBox(evt, peerId); } else if (name == 'peer_info') { - handlePeerInfo(evt); + handlePeerInfo(evt, peerId); } else if (name == 'connection_ready') { FFI.ffiModel.setConnectionType( evt['secure'] == 'true', evt['direct'] == 'true'); @@ -241,17 +241,19 @@ class FfiModel with ChangeNotifier { enterPasswordDialog(id); } else { var hasRetry = evt['hasRetry'] == 'true'; - showMsgBox(type, title, text, hasRetry); + showMsgBox(id, type, title, text, hasRetry); } } /// Show a message box with [type], [title] and [text]. - void showMsgBox(String type, String title, String text, bool hasRetry) { + void showMsgBox( + String id, String type, String title, String text, bool hasRetry) { msgBox(type, title, text); _timer?.cancel(); if (hasRetry) { _timer = Timer(Duration(seconds: _reconnects), () { - FFI.reconnect(); + FFI.bind.sessionReconnect(id: id); + clearPermissions(); showLoading(translate('Connecting...')); }); _reconnects *= 2; @@ -261,7 +263,7 @@ class FfiModel with ChangeNotifier { } /// Handle the peer info event based on [evt]. - void handlePeerInfo(Map evt) { + void handlePeerInfo(Map evt, String peerId) async { SmartDialog.dismiss(); _pi.version = evt['version']; _pi.username = evt['username']; @@ -276,7 +278,8 @@ class FfiModel with ChangeNotifier { Timer(Duration(milliseconds: 100), showMobileActionsOverlay); } } else { - _touchMode = FFI.getByName('peer_option', "touch-mode") != ''; + _touchMode = + await FFI.bind.getSessionOption(id: peerId, arg: "touch-mode") != ''; } if (evt['is_file_transfer'] == "true") { @@ -311,26 +314,26 @@ class ImageModel with ChangeNotifier { ui.Image? get image => _image; - ImageModel() { - PlatformFFI.setRgbaCallback((rgba) { - if (_waitForImage) { - _waitForImage = false; - SmartDialog.dismiss(); + String id = ""; // TODO multi image model + + void onRgba(Uint8List rgba) { + if (_waitForImage) { + _waitForImage = false; + SmartDialog.dismiss(); + } + final pid = FFI.id; + ui.decodeImageFromPixels( + rgba, + FFI.ffiModel.display.width, + FFI.ffiModel.display.height, + isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888, (image) { + if (FFI.id != pid) return; + try { + // my throw exception, because the listener maybe already dispose + update(image); + } catch (e) { + print('update image: $e'); } - final pid = FFI.id; - ui.decodeImageFromPixels( - rgba, - FFI.ffiModel.display.width, - FFI.ffiModel.display.height, - isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888, (image) { - if (FFI.id != pid) return; - try { - // my throw exception, because the listener maybe already dispose - FFI.imageModel.update(image); - } catch (e) { - print('update image: $e'); - } - }); }); } @@ -347,8 +350,8 @@ class ImageModel with ChangeNotifier { initializeCursorAndCanvas(); Future.delayed(Duration(milliseconds: 1), () { if (FFI.ffiModel.isPeerAndroid) { - FFI.setByName( - 'peer_option', '{"name": "view-style", "value": "shrink"}'); + FFI.bind + .sessionPeerOption(id: id, name: "view-style", value: "shrink"); FFI.canvasModel.updateViewStyle(); } }); @@ -378,6 +381,7 @@ class CanvasModel with ChangeNotifier { double _x = 0; double _y = 0; double _scale = 1.0; + String id = ""; // TODO multi canvas model CanvasModel(); @@ -387,8 +391,11 @@ class CanvasModel with ChangeNotifier { double get scale => _scale; - void updateViewStyle() { - final s = FFI.getByName('peer_option', 'view-style'); + void updateViewStyle() async { + final s = await FFI.bind.getSessionOption(id: id, arg: 'view-style'); + if (s == null) { + return; + } final size = MediaQueryData.fromWindow(ui.window).size; final s1 = size.width / FFI.ffiModel.display.width; final s2 = size.height / FFI.ffiModel.display.height; @@ -498,6 +505,7 @@ class CursorModel with ChangeNotifier { double _hoty = 0; double _displayOriginX = 0; double _displayOriginY = 0; + String id = ""; // TODO multi cursor model ui.Image? get image => _image; @@ -737,7 +745,7 @@ class FFI { /// Get the remote id for current client. static String getId() { - return getByName('remote_id'); + return getByName('remote_id'); // TODO } /// Send a mouse tap event(down and up). @@ -749,14 +757,14 @@ class FFI { /// Send scroll event with scroll distance [y]. static void scroll(int y) { setByName('send_mouse', - json.encode(modify({'type': 'wheel', 'y': y.toString()}))); + json.encode(modify({'id': id, 'type': 'wheel', 'y': y.toString()}))); } /// Reconnect to the remote peer. - static void reconnect() { - setByName('reconnect'); - FFI.ffiModel.clearPermissions(); - } + // static void reconnect() { + // setByName('reconnect'); + // FFI.ffiModel.clearPermissions(); + // } /// Reset key modifiers to false, including [shift], [ctrl], [alt] and [command]. static void resetModifiers() { @@ -776,7 +784,7 @@ class FFI { static void sendMouse(String type, MouseButtons button) { if (!ffiModel.keyboard()) return; setByName('send_mouse', - json.encode(modify({'type': type, 'buttons': button.value}))); + json.encode(modify({'id': id, 'type': type, 'buttons': button.value}))); } /// Send key stroke event. @@ -784,17 +792,27 @@ class FFI { /// [press] indicates a click event(down and up). static void inputKey(String name, {bool? down, bool? press}) { if (!ffiModel.keyboard()) return; - final Map out = Map(); - out['name'] = name; - // default: down = false - if (down == true) { - out['down'] = "true"; - } - // default: press = true - if (press != false) { - out['press'] = "true"; - } - setByName('input_key', json.encode(modify(out))); + // final Map out = Map(); + // out['name'] = name; + // // default: down = false + // if (down == true) { + // out['down'] = "true"; + // } + // // default: press = true + // if (press != false) { + // out['press'] = "true"; + // } + // setByName('input_key', json.encode(modify(out))); + // TODO id + FFI.bind.sessionInputKey( + id: id, + name: name, + down: down ?? false, + press: press ?? true, + alt: alt, + ctrl: ctrl, + shift: shift, + command: command); } /// Send mouse movement event with distance in [x] and [y]. @@ -802,13 +820,14 @@ class FFI { if (!ffiModel.keyboard()) return; var x2 = x.toInt(); var y2 = y.toInt(); - setByName('send_mouse', json.encode(modify({'x': '$x2', 'y': '$y2'}))); + setByName( + 'send_mouse', json.encode(modify({'id': id, 'x': '$x2', 'y': '$y2'}))); } /// List the saved peers. static List peers() { try { - var str = getByName('peers'); + var str = getByName('peers'); // TODO if (str == "") return []; List peers = json.decode(str); return peers @@ -829,16 +848,25 @@ class FFI { } else { FFI.chatModel.resetClientMode(); // setByName('connect', id); - final event_stream = FFI.rustdeskImpl - .sessionConnect(id: id, isFileTransfer: isFileTransfer); + // TODO multi model instances + FFI.canvasModel.id = id; + FFI.imageModel.id = id; + FFI.cursorModel.id = id; + final stream = + FFI.bind.sessionConnect(id: id, isFileTransfer: isFileTransfer); final cb = FFI.ffiModel.startEventListener(id); () async { - await for (final message in event_stream) { - try { - Map event = json.decode(message); - cb(event); - } catch (e) { - print('json.decode fail(): $e'); + await for (final message in stream) { + if (message is Event) { + try { + debugPrint("event:${message.field0}"); + Map event = json.decode(message.field0); + cb(event); + } catch (e) { + print('json.decode fail(): $e'); + } + } else if (message is Rgba) { + FFI.imageModel.onRgba(message.field0); } } }(); @@ -847,26 +875,9 @@ class FFI { FFI.id = id; } - static Map? popEvent() { - var s = getByName('event'); - if (s == '') return null; - try { - Map event = json.decode(s); - return event; - } catch (e) { - print('popEvent(): $e'); - } - return null; - } - /// Login with [password], choose if the client should [remember] it. - static void login(String password, bool remember) { - setByName( - 'login', - json.encode({ - 'password': password, - 'remember': remember ? 'true' : 'false', - })); + static void login(String id, String password, bool remember) { + FFI.bind.sessionLogin(id: id, password: password, remember: remember); } /// Close the remote session. @@ -876,8 +887,8 @@ class FFI { savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x, canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); } + FFI.bind.sessionClose(id: id); id = ""; - setByName('close', ''); imageModel.update(null); cursorModel.clear(); ffiModel.clear(); @@ -896,7 +907,7 @@ class FFI { PlatformFFI.setByName(name, value); } - static RustdeskImpl get rustdeskImpl => PlatformFFI.rustdeskImpl; + static RustdeskImpl get bind => PlatformFFI.ffiBind; static handleMouse(Map evt) { var type = ''; @@ -949,6 +960,7 @@ class FFI { break; } evt['buttons'] = buttons; + evt['id'] = id; setByName('send_mouse', json.encode(evt)); } diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index e1b9137b6..a425ea810 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -30,11 +30,10 @@ class PlatformFFI { static String _homeDir = ''; static F2? _getByName; static F3? _setByName; - static late RustdeskImpl _rustdeskImpl; + static late RustdeskImpl _ffiBind; static void Function(Map)? _eventCallback; - static void Function(Uint8List)? _rgbaCallback; - static RustdeskImpl get rustdeskImpl => _rustdeskImpl; + static RustdeskImpl get ffiBind => _ffiBind; static Future getVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); @@ -91,8 +90,8 @@ class PlatformFFI { dylib.lookupFunction, Pointer), F3>( 'set_by_name'); _dir = (await getApplicationDocumentsDirectory()).path; - _rustdeskImpl = RustdeskImpl(dylib); - _startListenEvent(_rustdeskImpl); // global event + _ffiBind = RustdeskImpl(dylib); + _startListenEvent(_ffiBind); // global event try { _homeDir = (await ExternalPath.getExternalStorageDirectories())[0]; } catch (e) { @@ -137,7 +136,7 @@ class PlatformFFI { /// Start listening to the Rust core's events and frames. static void _startListenEvent(RustdeskImpl rustdeskImpl) { () async { - await for (final message in rustdeskImpl.startEventStream()) { + await for (final message in rustdeskImpl.startGlobalEventStream()) { if (_eventCallback != null) { try { Map event = json.decode(message); @@ -148,24 +147,13 @@ class PlatformFFI { } } }(); - () async { - await for (final rgba in rustdeskImpl.startRgbaStream()) { - if (_rgbaCallback != null) { - _rgbaCallback!(rgba); - } else { - rgba.clear(); - } - } - }(); } static void setEventCallback(void Function(Map) fun) async { _eventCallback = fun; } - static void setRgbaCallback(void Function(Uint8List) fun) async { - _rgbaCallback = fun; - } + static void setRgbaCallback(void Function(Uint8List) fun) async {} static void startDesktopWebListener() {} diff --git a/src/client/file_trait.rs b/src/client/file_trait.rs index 7b1da3e0a..5dbc614e6 100644 --- a/src/client/file_trait.rs +++ b/src/client/file_trait.rs @@ -28,7 +28,7 @@ pub trait FileManager: Interface { } } - fn cancel_job(&mut self, id: i32) { + fn cancel_job(&self, id: i32) { self.send(Data::CancelJob(id)); } @@ -44,23 +44,23 @@ pub trait FileManager: Interface { self.send(Data::Message(msg_out)); } - fn remove_file(&mut self, id: i32, path: String, file_num: i32, is_remote: bool) { + fn remove_file(&self, id: i32, path: String, file_num: i32, is_remote: bool) { self.send(Data::RemoveFile((id, path, file_num, is_remote))); } - fn remove_dir_all(&mut self, id: i32, path: String, is_remote: bool) { + fn remove_dir_all(&self, id: i32, path: String, is_remote: bool) { self.send(Data::RemoveDirAll((id, path, is_remote))); } - fn confirm_delete_files(&mut self, id: i32, file_num: i32) { + fn confirm_delete_files(&self, id: i32, file_num: i32) { self.send(Data::ConfirmDeleteFiles((id, file_num))); } - fn set_no_confirm(&mut self, id: i32) { + fn set_no_confirm(&self, id: i32) { self.send(Data::SetNoConfirm(id)); } - fn remove_dir(&mut self, id: i32, path: String, is_remote: bool) { + fn remove_dir(&self, id: i32, path: String, is_remote: bool) { if is_remote { self.send(Data::RemoveDir((id, path))); } else { @@ -68,7 +68,7 @@ pub trait FileManager: Interface { } } - fn create_dir(&mut self, id: i32, path: String, is_remote: bool) { + fn create_dir(&self, id: i32, path: String, is_remote: bool) { self.send(Data::CreateDir((id, path, is_remote))); } diff --git a/src/flutter.rs b/src/flutter.rs index 3872710c0..7a0f378f9 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,4 +1,4 @@ -use crate::client::*; +use crate::{client::*, flutter_ffi::EventToUI}; use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; use hbb_common::{ allow_err, @@ -28,8 +28,7 @@ use std::{ lazy_static::lazy_static! { // static ref SESSION: Arc>> = Default::default(); pub static ref SESSIONS: RwLock> = Default::default(); - pub static ref EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel - pub static ref RGBA_STREAM: RwLock>>>> = Default::default(); // rust to dart rgba (big u8 list) channel + pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } // pub fn get_session<'a>(id: &str) -> Option<&'a Session> { @@ -41,7 +40,7 @@ pub struct Session { id: String, sender: Arc>>>, // UI to rust lc: Arc>, - events2ui: Arc>>, + events2ui: Arc>>, } impl Session { @@ -51,7 +50,7 @@ impl Session { /// /// * `id` - The id of the remote session. /// * `is_file_transfer` - If the session is used for file transfer. - pub fn start(id: &str, is_file_transfer: bool, events2ui: StreamSink) { + pub fn start(id: &str, is_file_transfer: bool, events2ui: StreamSink) { LocalConfig::set_remote_id(&id); // TODO check same id // TODO close @@ -284,11 +283,8 @@ impl Session { let mut h: HashMap<&str, &str> = event.iter().cloned().collect(); assert!(h.get("name").is_none()); h.insert("name", name); - - self.events2ui - .read() - .unwrap() - .add(serde_json::ser::to_string(&h).unwrap_or("".to_owned())); + let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned()); + self.events2ui.read().unwrap().add(EventToUI::Event(out)); } /// Get platform of peer. @@ -676,11 +672,11 @@ impl Connection { if !self.first_frame { self.first_frame = true; } - if let (Ok(true), Some(s)) = ( - self.video_handler.handle_frame(vf), - RGBA_STREAM.read().unwrap().as_ref(), - ) { - s.add(ZeroCopyBuffer(self.video_handler.rgb.clone())); + if let Ok(true) = self.video_handler.handle_frame(vf) { + let stream = self.session.events2ui.read().unwrap(); + stream.add(EventToUI::Rgba(ZeroCopyBuffer( + self.video_handler.rgb.clone(), + ))); } } Some(message::Union::hash(hash)) => { @@ -1274,7 +1270,7 @@ pub mod connection_manager { use scrap::android::call_main_service_set_by_name; use serde_derive::Serialize; - use super::EVENT_STREAM; + use super::GLOBAL_EVENT_STREAM; #[derive(Debug, Serialize, Clone)] struct Client { @@ -1382,7 +1378,7 @@ pub mod connection_manager { assert!(h.get("name").is_none()); h.insert("name", name); - if let Some(s) = EVENT_STREAM.read().unwrap().as_ref() { + if let Some(s) = GLOBAL_EVENT_STREAM.read().unwrap().as_ref() { s.add(serde_json::ser::to_string(&h).unwrap_or("".to_owned())); }; } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 10d1257db..4e6e63595 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -59,18 +59,18 @@ pub extern "C" fn rustdesk_core_main() -> bool { crate::core_main::core_main() } -pub fn start_event_stream(s: StreamSink) -> ResultType<()> { - let _ = flutter::EVENT_STREAM.write().unwrap().insert(s); - Ok(()) +pub enum EventToUI { + Event(String), + Rgba(ZeroCopyBuffer>), } -pub fn start_rgba_stream(s: StreamSink>>) -> ResultType<()> { - let _ = flutter::RGBA_STREAM.write().unwrap().insert(s); +pub fn start_global_event_stream(s: StreamSink) -> ResultType<()> { + let _ = flutter::GLOBAL_EVENT_STREAM.write().unwrap().insert(s); Ok(()) } pub fn session_connect( - events2ui: StreamSink, + events2ui: StreamSink, id: String, is_file_transfer: bool, ) -> ResultType<()> { @@ -86,6 +86,7 @@ pub fn get_session_remember(id: String) -> Option { } } +// TODO sync pub fn get_session_toggle_option(id: String, arg: String) -> Option { if let Some(session) = SESSIONS.read().unwrap().get(&id) { Some(session.get_toggle_option(&arg)) @@ -94,11 +95,20 @@ pub fn get_session_toggle_option(id: String, arg: String) -> Option { } } -pub fn get_session_image_quality(id: String) -> SyncReturn> { - if let Some(session) = SESSIONS.read().unwrap().get(&id) { - SyncReturn(Some(session.get_image_quality())) +pub fn get_session_toggle_option_sync(id: String, arg: String) -> SyncReturn> { + let res = if get_session_toggle_option(id, arg) == Some(true) { + 1 } else { - SyncReturn(None) + 0 + }; + SyncReturn(vec![res]) +} + +pub fn get_session_image_quality(id: String) -> Option { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + Some(session.get_image_quality()) + } else { + None } } @@ -517,40 +527,49 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) { // "chat_client_mode" => { // Session::send_chat(value.to_owned()); // } - // "send_mouse" => { - // if let Ok(m) = serde_json::from_str::>(value) { - // let alt = m.get("alt").is_some(); - // let ctrl = m.get("ctrl").is_some(); - // let shift = m.get("shift").is_some(); - // let command = m.get("command").is_some(); - // let x = m - // .get("x") - // .map(|x| x.parse::().unwrap_or(0)) - // .unwrap_or(0); - // let y = m - // .get("y") - // .map(|x| x.parse::().unwrap_or(0)) - // .unwrap_or(0); - // let mut mask = 0; - // if let Some(_type) = m.get("type") { - // mask = match _type.as_str() { - // "down" => 1, - // "up" => 2, - // "wheel" => 3, - // _ => 0, - // }; - // } - // if let Some(buttons) = m.get("buttons") { - // mask |= match buttons.as_str() { - // "left" => 1, - // "right" => 2, - // "wheel" => 4, - // _ => 0, - // } << 3; - // } - // Session::send_mouse(mask, x, y, alt, ctrl, shift, command); - // } - // } + + // TODO + "send_mouse" => { + if let Ok(m) = serde_json::from_str::>(value) { + let id = m.get("id"); + if id.is_none() { + return; + } + let id = id.unwrap(); + let alt = m.get("alt").is_some(); + let ctrl = m.get("ctrl").is_some(); + let shift = m.get("shift").is_some(); + let command = m.get("command").is_some(); + let x = m + .get("x") + .map(|x| x.parse::().unwrap_or(0)) + .unwrap_or(0); + let y = m + .get("y") + .map(|x| x.parse::().unwrap_or(0)) + .unwrap_or(0); + let mut mask = 0; + if let Some(_type) = m.get("type") { + mask = match _type.as_str() { + "down" => 1, + "up" => 2, + "wheel" => 3, + _ => 0, + }; + } + if let Some(buttons) = m.get("buttons") { + mask |= match buttons.as_str() { + "left" => 1, + "right" => 2, + "wheel" => 4, + _ => 0, + } << 3; + } + if let Some(session) = SESSIONS.read().unwrap().get(id) { + session.send_mouse(mask, x, y, alt, ctrl, shift, command); + } + } + } "option" => { if let Ok(m) = serde_json::from_str::>(value) { if let Some(name) = m.get("name") {