diff --git a/Cargo.lock b/Cargo.lock index 441b49c58..9346e75b4 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#e5adce55eea0b74d3680e66a2c5252edf17b07e1" dependencies = [ "anyhow", "cargo_metadata", diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 6827bde60..c40a7cb47 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -59,7 +59,7 @@ class _RemotePageState extends State with WindowListener { Wakelock.enable(); } _physicalFocusNode.requestFocus(); - FFI.ffiModel.updateEventListener(widget.id); + // FFI.ffiModel.updateEventListener(widget.id); FFI.listenToMouse(true); WindowManager.instance.addListener(this); } @@ -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( @@ -404,7 +404,7 @@ class _RemotePageState extends State with WindowListener { icon: Icon(Icons.tv), onPressed: () { setState(() => _showEdit = false); - showOptions(); + showOptions(widget.id); }, ) ] + @@ -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,8 +600,9 @@ class _RemotePageState extends State with WindowListener { Widget getBodyForDesktopWithListener(bool keyboard) { var paints = [ImagePaint()]; - if (keyboard || - FFI.getByName('toggle_option', 'show-remote-cursor') == 'true') { + final cursor = FFI.bind + .getSessionToggleOptionSync(id: widget.id, arg: 'show-remote-cursor'); + if (keyboard || cursor) { paints.add(CursorPaint()); } return Container( @@ -628,7 +630,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; @@ -647,7 +649,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), ) @@ -670,7 +672,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')), @@ -685,28 +688,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(); @@ -732,8 +737,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); })); })); } @@ -948,12 +953,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); 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, @@ -972,10 +978,11 @@ RadioListTile getRadio(String name, String toValue, String curValue, ); } -void showOptions() { - String quality = FFI.getByName('image_quality'); +void showOptions(String id) async { + String quality = await FFI.bind.getSessionImageQuality(id: id) ?? 'balanced'; if (quality == '') quality = 'balanced'; - String viewStyle = FFI.getByName('peer_option', 'view-style'); + String viewStyle = + await FFI.bind.getSessionOption(id: id, arg: 'view-style') ?? ''; var displays = []; final pi = FFI.ffiModel.pi; final image = FFI.ffiModel.getConnectionImage(); @@ -988,7 +995,7 @@ void showOptions() { 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( @@ -1017,30 +1024,30 @@ void showOptions() { 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(); }); }; @@ -1058,7 +1065,8 @@ void showOptions() { 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: [], @@ -1067,10 +1075,12 @@ void showOptions() { }, 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( @@ -1103,12 +1113,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 d94e69341..3659a85df 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -6,6 +6,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/generated_bridge.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/file_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; @@ -121,6 +122,53 @@ class FfiModel with ChangeNotifier { _permissions.clear(); } + void Function(Map) startEventListener(String peerId) { + return (evt) { + var name = evt['name']; + if (name == 'msgbox') { + handleMsgBox(evt, peerId); + } else if (name == 'peer_info') { + handlePeerInfo(evt, peerId); + } else if (name == 'connection_ready') { + FFI.ffiModel.setConnectionType( + evt['secure'] == 'true', evt['direct'] == 'true'); + } else if (name == 'switch_display') { + handleSwitchDisplay(evt); + } else if (name == 'cursor_data') { + FFI.cursorModel.updateCursorData(evt); + } else if (name == 'cursor_id') { + FFI.cursorModel.updateCursorId(evt); + } else if (name == 'cursor_position') { + FFI.cursorModel.updateCursorPosition(evt); + } else if (name == 'clipboard') { + Clipboard.setData(ClipboardData(text: evt['content'])); + } else if (name == 'permission') { + FFI.ffiModel.updatePermission(evt); + } else if (name == 'chat_client_mode') { + FFI.chatModel.receive(ChatModel.clientModeID, evt['text'] ?? ""); + } else if (name == 'chat_server_mode') { + FFI.chatModel + .receive(int.parse(evt['id'] as String), evt['text'] ?? ""); + } else if (name == 'file_dir') { + FFI.fileModel.receiveFileDir(evt); + } else if (name == 'job_progress') { + FFI.fileModel.tryUpdateJobProgress(evt); + } else if (name == 'job_done') { + FFI.fileModel.jobDone(evt); + } else if (name == 'job_error') { + FFI.fileModel.jobError(evt); + } else if (name == 'override_file_confirm') { + FFI.fileModel.overrideFileConfirm(evt); + } else if (name == 'try_start_without_auth') { + FFI.serverModel.loginRequest(evt); + } else if (name == 'on_client_authorized') { + FFI.serverModel.onClientAuthorized(evt); + } else if (name == 'on_client_remove') { + FFI.serverModel.onClientRemove(evt); + } + }; + } + /// Bind the event listener to receive events from the Rust core. void updateEventListener(String peerId) { final void Function(Map) cb = (evt) { @@ -128,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'); @@ -193,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; @@ -213,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']; @@ -228,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") { @@ -263,26 +314,26 @@ class ImageModel with ChangeNotifier { ui.Image? get image => _image; - ImageModel() { - PlatformFFI.setRgbaCallback((rgba) { - if (_waitForImage) { - _waitForImage = false; - SmartDialog.dismiss(); + String _id = ""; + + 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'); - } - }); }); } @@ -299,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(); } }); @@ -330,6 +381,7 @@ class CanvasModel with ChangeNotifier { double _x = 0; double _y = 0; double _scale = 1.0; + String id = ""; // TODO multi canvas model CanvasModel(); @@ -339,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; @@ -450,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; @@ -598,17 +654,17 @@ class CursorModel with ChangeNotifier { final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); var pid = FFI.id; ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, - (image) { - if (FFI.id != pid) return; - _image = image; - _images[id] = Tuple3(image, _hotx, _hoty); - try { - // my throw exception, because the listener maybe already dispose - notifyListeners(); - } catch (e) { - print('notify cursor: $e'); - } - }); + (image) { + if (FFI.id != pid) return; + _image = image; + _images[id] = Tuple3(image, _hotx, _hoty); + try { + // my throw exception, because the listener maybe already dispose + notifyListeners(); + } catch (e) { + print('notify cursor: $e'); + } + }); } void updateCursorId(Map evt) { @@ -637,7 +693,8 @@ class CursorModel with ChangeNotifier { notifyListeners(); } - void updateDisplayOriginWithCursor(double x, double y, double xCursor, double yCursor) { + void updateDisplayOriginWithCursor( + double x, double y, double xCursor, double yCursor) { _displayOriginX = x; _displayOriginY = y; _x = xCursor; @@ -688,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). @@ -700,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() { @@ -727,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. @@ -735,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]. @@ -753,19 +820,20 @@ 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 .map((s) => s as List) .map((s) => - Peer.fromJson(s[0] as String, s[1] as Map)) + Peer.fromJson(s[0] as String, s[1] as Map)) .toList(); } catch (e) { print('peers(): $e'); @@ -779,31 +847,37 @@ class FFI { setByName('connect_file_transfer', id); } else { FFI.chatModel.resetClientMode(); - setByName('connect', id); + // setByName('connect', id); + // 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 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); + } + } + }(); + // every instance will bind a stream } 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. @@ -813,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(); @@ -833,6 +907,8 @@ class FFI { PlatformFFI.setByName(name, value); } + static RustdeskImpl get bind => PlatformFFI.ffiBind; + static handleMouse(Map evt) { var type = ''; var isMove = false; @@ -884,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 9527555d0..a425ea810 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -30,8 +30,10 @@ class PlatformFFI { static String _homeDir = ''; static F2? _getByName; static F3? _setByName; + static late RustdeskImpl _ffiBind; static void Function(Map)? _eventCallback; - static void Function(Uint8List)? _rgbaCallback; + + static RustdeskImpl get ffiBind => _ffiBind; static Future getVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); @@ -88,7 +90,8 @@ class PlatformFFI { dylib.lookupFunction, Pointer), F3>( 'set_by_name'); _dir = (await getApplicationDocumentsDirectory()).path; - _startListenEvent(RustdeskImpl(dylib)); + _ffiBind = RustdeskImpl(dylib); + _startListenEvent(_ffiBind); // global event try { _homeDir = (await ExternalPath.getExternalStorageDirectories())[0]; } catch (e) { @@ -133,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); @@ -144,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/flutter/pubspec.lock b/flutter/pubspec.lock index 1ba610f19..c5ad4c8ae 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -396,9 +396,11 @@ packages: flutter_rust_bridge: dependency: "direct main" description: - name: flutter_rust_bridge - url: "https://pub.dartlang.org" - source: hosted + path: frb_dart + ref: master + resolved-ref: e5adce55eea0b74d3680e66a2c5252edf17b07e1 + url: "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge" + source: git version: "1.32.0" flutter_smart_dialog: dependency: "direct main" diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 21b1857eb..ab9a7d7eb 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -53,7 +53,11 @@ dependencies: image_picker: ^0.8.5 image: ^3.1.3 flutter_smart_dialog: ^4.3.1 - flutter_rust_bridge: ^1.30.0 + flutter_rust_bridge: + git: + url: https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge + ref: master + path: frb_dart window_manager: ^0.2.3 desktop_multi_window: git: diff --git a/src/client/file_trait.rs b/src/client/file_trait.rs index 5dc4cd786..5dbc614e6 100644 --- a/src/client/file_trait.rs +++ b/src/client/file_trait.rs @@ -1,16 +1,13 @@ use super::{Data, Interface}; -use hbb_common::{ - fs, - message_proto::*, -}; +use hbb_common::{fs, message_proto::*}; pub trait FileManager: Interface { - fn get_home_dir(&self) -> String{ + fn get_home_dir(&self) -> String { fs::get_home_as_string() } #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn read_dir(&self,path: String, include_hidden: bool) -> sciter::Value { + fn read_dir(&self, path: String, include_hidden: bool) -> sciter::Value { match fs::read_dir(&fs::get_path(&path), include_hidden) { Err(_) => sciter::Value::null(), Ok(fd) => { @@ -23,15 +20,15 @@ pub trait FileManager: Interface { } #[cfg(any(target_os = "android", target_os = "ios"))] - fn read_dir(&self,path: &str, include_hidden: bool) -> String { + fn read_dir(&self, path: &str, include_hidden: bool) -> String { use crate::flutter::make_fd_to_json; - match fs::read_dir(&fs::get_path(path), include_hidden){ + match fs::read_dir(&fs::get_path(path), include_hidden) { Ok(fd) => make_fd_to_json(fd), - Err(_)=>"".into() + Err(_) => "".into(), } } - fn cancel_job(&mut self, id: i32) { + fn cancel_job(&self, id: i32) { self.send(Data::CancelJob(id)); } @@ -47,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 { @@ -71,12 +68,12 @@ 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))); } fn send_files( - &mut self, + &self, id: i32, path: String, to: String, @@ -84,7 +81,14 @@ pub trait FileManager: Interface { include_hidden: bool, is_remote: bool, ) { - self.send(Data::SendFiles((id, path, to, file_num, include_hidden, is_remote))); + self.send(Data::SendFiles(( + id, + path, + to, + file_num, + include_hidden, + is_remote, + ))); } fn add_job( @@ -96,10 +100,17 @@ pub trait FileManager: Interface { include_hidden: bool, is_remote: bool, ) { - self.send(Data::AddJob((id, path, to, file_num, include_hidden, is_remote))); + self.send(Data::AddJob(( + id, + path, + to, + file_num, + include_hidden, + is_remote, + ))); } - fn resume_job(&mut self, id: i32, is_remote: bool){ - self.send(Data::ResumeJob((id,is_remote))); + fn resume_job(&mut self, id: i32, is_remote: bool) { + self.send(Data::ResumeJob((id, is_remote))); } } diff --git a/src/flutter.rs b/src/flutter.rs index e40084450..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, @@ -26,17 +26,21 @@ use std::{ }; lazy_static::lazy_static! { - static ref SESSION: Arc>> = 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 + // static ref SESSION: Arc>> = Default::default(); + pub static ref SESSIONS: RwLock> = Default::default(); + pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } -#[derive(Clone, Default)] +// pub fn get_session<'a>(id: &str) -> Option<&'a Session> { +// SESSIONS.read().unwrap().get(id) +// } + +#[derive(Clone)] pub struct Session { id: String, - sender: Arc>>>, + sender: Arc>>>, // UI to rust lc: Arc>, - events2ui: Arc>>, + events2ui: Arc>>, } impl Session { @@ -46,40 +50,47 @@ 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) { - LocalConfig::set_remote_id(id); - Self::close(); - let mut session = Session::default(); + pub fn start(id: &str, is_file_transfer: bool, events2ui: StreamSink) { + LocalConfig::set_remote_id(&id); + // TODO check same id + // TODO close + // Self::close(); + let events2ui = Arc::new(RwLock::new(events2ui)); + let mut session = Session { + id: id.to_owned(), + sender: Default::default(), + lc: Default::default(), + events2ui, + }; session .lc .write() .unwrap() .initialize(id.to_owned(), false, false); - session.id = id.to_owned(); - *SESSION.write().unwrap() = Some(session.clone()); + SESSIONS + .write() + .unwrap() + .insert(id.to_owned(), session.clone()); std::thread::spawn(move || { Connection::start(session, is_file_transfer); }); } /// Get the current session instance. - pub fn get() -> Arc>> { - SESSION.clone() - } + // pub fn get() -> Arc>> { + // SESSION.clone() + // } /// Get the option of the current session. /// /// # Arguments /// /// * `name` - The name of the option to get. Currently only `remote_dir` is supported. - pub fn get_option(name: &str) -> String { - if let Some(session) = SESSION.read().unwrap().as_ref() { - if name == "remote_dir" { - return session.lc.read().unwrap().get_remote_dir(); - } - return session.lc.read().unwrap().get_option(name); + pub fn get_option(&self, name: &str) -> String { + if name == "remote_dir" { + return self.lc.read().unwrap().get_remote_dir(); } - "".to_owned() + self.lc.read().unwrap().get_option(name) } /// Set the option of the current session. @@ -88,78 +99,59 @@ impl Session { /// /// * `name` - The name of the option to set. Currently only `remote_dir` is supported. /// * `value` - The value of the option to set. - pub fn set_option(name: String, value: String) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - let mut value = value; - if name == "remote_dir" { - value = session.lc.write().unwrap().get_all_remote_dir(value); - } - return session.lc.write().unwrap().set_option(name, value); + pub fn set_option(&self, name: String, value: String) { + let mut value = value; + let mut lc = self.lc.write().unwrap(); + if name == "remote_dir" { + value = lc.get_all_remote_dir(value); } + lc.set_option(name, value); } /// Input the OS password. - pub fn input_os_password(pass: String, activate: bool) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - input_os_password(pass, activate, session.clone()); - } + pub fn input_os_password(&self, pass: String, activate: bool) { + input_os_password(pass, activate, self.clone()); } + // impl Interface /// Send message to the remote session. /// /// # Arguments /// /// * `data` - The data to send. See [`Data`] for more details. - fn send(data: Data) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - session.send(data); - } - } - - /// Pop a event from the event queue. - pub fn pop_event() -> Option { - if let Some(session) = SESSION.read().unwrap().as_ref() { - session.events2ui.write().unwrap().pop_front() - } else { - None - } - } + // fn send(data: Data) { + // if let Some(session) = SESSION.read().unwrap().as_ref() { + // session.send(data); + // } + // } /// Toggle an option. - pub fn toggle_option(name: &str) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - let msg = session.lc.write().unwrap().toggle_option(name.to_owned()); - if let Some(msg) = msg { - session.send_msg(msg); - } + pub fn toggle_option(&self, name: &str) { + let msg = self.lc.write().unwrap().toggle_option(name.to_owned()); + if let Some(msg) = msg { + self.send_msg(msg); } } /// Send a refresh command. - pub fn refresh() { - Self::send(Data::Message(LoginConfigHandler::refresh())); + pub fn refresh(&self) { + self.send(Data::Message(LoginConfigHandler::refresh())); } /// Get image quality. - pub fn get_image_quality() -> String { - if let Some(session) = SESSION.read().unwrap().as_ref() { - session.lc.read().unwrap().image_quality.clone() - } else { - "".to_owned() - } + pub fn get_image_quality(&self) -> String { + self.lc.read().unwrap().image_quality.clone() } /// Set image quality. - pub fn set_image_quality(value: &str) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - let msg = session - .lc - .write() - .unwrap() - .save_image_quality(value.to_owned()); - if let Some(msg) = msg { - session.send_msg(msg); - } + pub fn set_image_quality(&self, value: &str) { + let msg = self + .lc + .write() + .unwrap() + .save_image_quality(value.to_owned()); + if let Some(msg) = msg { + self.send_msg(msg); } } @@ -169,12 +161,8 @@ impl Session { /// # Arguments /// /// * `name` - The name of the option to get. - pub fn get_toggle_option(name: &str) -> Option { - if let Some(session) = SESSION.read().unwrap().as_ref() { - Some(session.lc.write().unwrap().get_toggle_option(name)) - } else { - None - } + pub fn get_toggle_option(&self, name: &str) -> bool { + self.lc.write().unwrap().get_toggle_option(name) } /// Login. @@ -183,36 +171,28 @@ impl Session { /// /// * `password` - The password to login. /// * `remember` - If the password should be remembered. - pub fn login(password: &str, remember: bool) { - Session::send(Data::Login((password.to_owned(), remember))); + pub fn login(&self, password: &str, remember: bool) { + self.send(Data::Login((password.to_owned(), remember))); } /// Close the session. - pub fn close() { - Session::send(Data::Close); - SESSION.write().unwrap().take(); + pub fn close(&self) { + self.send(Data::Close); + let _ = SESSIONS.write().unwrap().remove(&self.id); } /// Reconnect to the current session. - pub fn reconnect() { - if let Some(session) = SESSION.read().unwrap().as_ref() { - if let Some(sender) = session.sender.read().unwrap().as_ref() { - sender.send(Data::Close).ok(); - } - let session = session.clone(); - std::thread::spawn(move || { - Connection::start(session, false); - }); - } + pub fn reconnect(&self) { + self.send(Data::Close); + let session = self.clone(); + std::thread::spawn(move || { + Connection::start(session, false); + }); } /// Get `remember` flag in [`LoginConfigHandler`]. - pub fn get_remember() -> bool { - if let Some(session) = SESSION.read().unwrap().as_ref() { - session.lc.read().unwrap().remember - } else { - false - } + pub fn get_remember(&self) -> bool { + self.lc.read().unwrap().remember } /// Send message over the current session. @@ -222,9 +202,7 @@ impl Session { /// * `msg` - The message to send. #[inline] pub fn send_msg(&self, msg: Message) { - if let Some(sender) = self.sender.read().unwrap().as_ref() { - sender.send(Data::Message(msg)).ok(); - } + self.send(Data::Message(msg)); } /// Send chat message over the current session. @@ -232,7 +210,7 @@ impl Session { /// # Arguments /// /// * `text` - The message to send. - pub fn send_chat(text: String) { + pub fn send_chat(&self, text: String) { let mut misc = Misc::new(); misc.set_chat_message(ChatMessage { text, @@ -240,49 +218,46 @@ impl Session { }); let mut msg_out = Message::new(); msg_out.set_misc(misc); - Self::send_msg_static(msg_out); + self.send_msg(msg_out); } + // file trait /// Send file over the current session. - pub fn send_files( - id: i32, - path: String, - to: String, - file_num: i32, - include_hidden: bool, - is_remote: bool, - ) { - if let Some(session) = SESSION.write().unwrap().as_mut() { - session.send_files(id, path, to, file_num, include_hidden, is_remote); - } - } + // pub fn send_files( + // id: i32, + // path: String, + // to: String, + // file_num: i32, + // include_hidden: bool, + // is_remote: bool, + // ) { + // if let Some(session) = SESSION.write().unwrap().as_mut() { + // session.send_files(id, path, to, file_num, include_hidden, is_remote); + // } + // } + // TODO into file trait /// Confirm file override. pub fn set_confirm_override_file( + &self, id: i32, file_num: i32, need_override: bool, remember: bool, is_upload: bool, ) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - if let Some(sender) = session.sender.read().unwrap().as_ref() { - log::info!( - "confirm file transfer, job: {}, need_override: {}", - id, - need_override - ); - sender - .send(Data::SetConfirmOverrideFile(( - id, - file_num, - need_override, - remember, - is_upload, - ))) - .ok(); - } - } + log::info!( + "confirm file transfer, job: {}, need_override: {}", + id, + need_override + ); + self.send(Data::SetConfirmOverrideFile(( + id, + file_num, + need_override, + remember, + is_upload, + ))); } /// Static method to send message over the current session. @@ -290,12 +265,12 @@ impl Session { /// # Arguments /// /// * `msg` - The message to send. - #[inline] - pub fn send_msg_static(msg: Message) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - session.send_msg(msg); - } - } + // #[inline] + // pub fn send_msg_static(msg: Message) { + // if let Some(session) = SESSION.read().unwrap().as_ref() { + // session.send_msg(msg); + // } + // } /// Push an event to the event queue. /// An event is stored as json in the event queue. @@ -308,10 +283,8 @@ impl Session { let mut h: HashMap<&str, &str> = event.iter().cloned().collect(); assert!(h.get("name").is_none()); h.insert("name", name); - - if let Some(s) = EVENT_STREAM.read().unwrap().as_ref() { - s.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. @@ -321,15 +294,13 @@ impl Session { } /// Quick method for sending a ctrl_alt_del command. - pub fn ctrl_alt_del() { - if let Some(session) = SESSION.read().unwrap().as_ref() { - if session.peer_platform() == "Windows" { - let k = Key::ControlKey(ControlKey::CtrlAltDel); - session.key_down_or_up(1, k, false, false, false, false); - } else { - let k = Key::ControlKey(ControlKey::Delete); - session.key_down_or_up(3, k, true, true, false, false); - } + pub fn ctrl_alt_del(&self) { + if self.peer_platform() == "Windows" { + let k = Key::ControlKey(ControlKey::CtrlAltDel); + self.key_down_or_up(1, k, false, false, false, false); + } else { + let k = Key::ControlKey(ControlKey::Delete); + self.key_down_or_up(3, k, true, true, false, false); } } @@ -338,7 +309,7 @@ impl Session { /// # Arguments /// /// * `display` - The display to switch to. - pub fn switch_display(display: i32) { + pub fn switch_display(&self, display: i32) { let mut misc = Misc::new(); misc.set_switch_display(SwitchDisplay { display, @@ -346,15 +317,13 @@ impl Session { }); let mut msg_out = Message::new(); msg_out.set_misc(misc); - Self::send_msg_static(msg_out); + self.send_msg(msg_out); } /// Send lock screen command. - pub fn lock_screen() { - if let Some(session) = SESSION.read().unwrap().as_ref() { - let k = Key::ControlKey(ControlKey::LockScreen); - session.key_down_or_up(1, k, false, false, false, false); - } + pub fn lock_screen(&self) { + let k = Key::ControlKey(ControlKey::LockScreen); + self.key_down_or_up(1, k, false, false, false, false); } /// Send key input command. @@ -369,6 +338,7 @@ impl Session { /// * `shift` - If the shift key is also pressed. /// * `command` - If the command key is also pressed. pub fn input_key( + &self, name: &str, down: bool, press: bool, @@ -377,15 +347,13 @@ impl Session { shift: bool, command: bool, ) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - let chars: Vec = name.chars().collect(); - if chars.len() == 1 { - let key = Key::_Raw(chars[0] as _); - session._input_key(key, down, press, alt, ctrl, shift, command); - } else { - if let Some(key) = KEY_MAP.get(name) { - session._input_key(key.clone(), down, press, alt, ctrl, shift, command); - } + let chars: Vec = name.chars().collect(); + if chars.len() == 1 { + let key = Key::_Raw(chars[0] as _); + self._input_key(key, down, press, alt, ctrl, shift, command); + } else { + if let Some(key) = KEY_MAP.get(name) { + self._input_key(key.clone(), down, press, alt, ctrl, shift, command); } } } @@ -395,13 +363,13 @@ impl Session { /// /// # Arguments /// - /// * `value` - The text to input. - pub fn input_string(value: &str) { + /// * `value` - The text to input. TODO &str -> String + pub fn input_string(&self, value: &str) { let mut key_event = KeyEvent::new(); key_event.set_seq(value.to_owned()); let mut msg_out = Message::new(); msg_out.set_key_event(key_event); - Self::send_msg_static(msg_out); + self.send_msg(msg_out); } fn _input_key( @@ -425,6 +393,7 @@ impl Session { } pub fn send_mouse( + &self, mask: i32, x: i32, y: i32, @@ -433,9 +402,7 @@ impl Session { shift: bool, command: bool, ) { - if let Some(session) = SESSION.read().unwrap().as_ref() { - send_mouse(mask, x, y, alt, ctrl, shift, command, session); - } + send_mouse(mask, x, y, alt, ctrl, shift, command, self); } fn key_down_or_up( @@ -705,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)) => { @@ -1303,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 { @@ -1411,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 a1b9b1e7b..25c37d418 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1,9 +1,9 @@ use crate::client::file_trait::FileManager; use crate::flutter::connection_manager::{self, get_clients_length, get_clients_state}; -use crate::flutter::{self, make_fd_to_json, Session}; +use crate::flutter::{self, make_fd_to_json, Session, SESSIONS}; use crate::start_server; use crate::ui_interface; -use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; +use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer}; use hbb_common::ResultType; use hbb_common::{ config::{self, Config, LocalConfig, PeerConfig, ONLINE}, @@ -59,16 +59,254 @@ 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); +pub enum EventToUI { + Event(String), + Rgba(ZeroCopyBuffer>), +} + +pub fn start_global_event_stream(s: StreamSink) -> ResultType<()> { + let _ = flutter::GLOBAL_EVENT_STREAM.write().unwrap().insert(s); Ok(()) } -pub fn start_rgba_stream(s: StreamSink>>) -> ResultType<()> { - let _ = flutter::RGBA_STREAM.write().unwrap().insert(s); +pub fn session_connect( + events2ui: StreamSink, + id: String, + is_file_transfer: bool, +) -> ResultType<()> { + Session::start(&id, is_file_transfer, events2ui); Ok(()) } +pub fn get_session_remember(id: String) -> Option { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + Some(session.get_remember()) + } else { + None + } +} + +// 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)) + } else { + None + } +} + +pub fn get_session_toggle_option_sync(id: String, arg: String) -> SyncReturn { + let res = get_session_toggle_option(id, arg) == Some(true); + SyncReturn(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 + } +} + +pub fn get_session_option(id: String, arg: String) -> Option { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + Some(session.get_option(&arg)) + } else { + None + } +} + +// void +pub fn session_login(id: String, password: String, remember: bool) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.login(&password, remember); + } +} + +pub fn session_close(id: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.close(); + } +} + +pub fn session_refresh(id: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.refresh(); + } +} + +pub fn session_reconnect(id: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.reconnect(); + } +} + +pub fn session_toggle_option(id: String, value: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.toggle_option(&value); + } +} + +pub fn session_set_image_quality(id: String, value: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.set_image_quality(&value); + } +} + +pub fn session_lock_screen(id: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.lock_screen(); + } +} + +pub fn session_ctrl_alt_del(id: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.ctrl_alt_del(); + } +} + +pub fn session_switch_display(id: String, value: i32) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.switch_display(value); + } +} + +pub fn session_input_key( + id: String, + name: String, + down: bool, + press: bool, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, +) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.input_key(&name, down, press, alt, ctrl, shift, command); + } +} + +pub fn session_input_string(id: String, value: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.input_string(&value); + } +} + +// chat_client_mode +pub fn session_send_chat(id: String, text: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.send_chat(text); + } +} + +// 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; +// } +// TODO +pub fn session_send_mouse( + id: String, + mask: i32, + x: i32, + y: i32, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, +) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.send_mouse(mask, x, y, alt, ctrl, shift, command); + } +} + +pub fn session_peer_option(id: String, name: String, value: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.set_option(name, value); + } +} + +pub fn session_input_os_password(id: String, value: String) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.input_os_password(value, true); + } +} + +// File Action +pub fn session_read_remote_dir(id: String, path: String, include_hidden: bool) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.read_remote_dir(path, include_hidden); + } +} + +pub fn session_send_files( + id: String, + act_id: i32, + path: String, + to: String, + file_num: i32, + include_hidden: bool, + is_remote: bool, +) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.send_files(act_id, path, to, file_num, include_hidden, is_remote); + } +} + +pub fn session_set_confirm_override_file( + id: String, + act_id: i32, + file_num: i32, + need_override: bool, + remember: bool, + is_upload: bool, +) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.set_confirm_override_file(act_id, file_num, need_override, remember, is_upload); + } +} + +pub fn session_remove_file(id: String, act_id: i32, path: String, file_num: i32, is_remote: bool) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.remove_file(act_id, path, file_num, is_remote); + } +} + +pub fn session_read_dir_recursive(id: String, act_id: i32, path: String, is_remote: bool) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.remove_dir_all(act_id, path, is_remote); + } +} + +pub fn session_remove_all_empty_dirs(id: String, act_id: i32, path: String, is_remote: bool) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.remove_dir(act_id, path, is_remote); + } +} + +pub fn session_cancel_job(id: String, act_id: i32) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.cancel_job(act_id); + } +} + +pub fn session_create_dir(id: String, act_id: i32, path: String, is_remote: bool) { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + session.create_dir(act_id, path, is_remote); + } +} + /// FFI for **get** commands which are idempotent. /// Return result in c string. /// @@ -97,21 +335,16 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co res = LocalConfig::get_remote_id(); } } - "remember" => { - res = Session::get_remember().to_string(); - } - "event" => { - if let Some(e) = Session::pop_event() { - res = e; - } - } - "toggle_option" => { - if let Ok(arg) = arg.to_str() { - if let Some(v) = Session::get_toggle_option(arg) { - res = v.to_string(); - } - } - } + // "remember" => { + // res = Session::get_remember().to_string(); + // } + // "toggle_option" => { + // if let Ok(arg) = arg.to_str() { + // if let Some(v) = Session::get_toggle_option(arg) { + // res = v.to_string(); + // } + // } + // } "test_if_valid_server" => { if let Ok(arg) = arg.to_str() { res = hbb_common::socket_client::test_if_valid_server(arg); @@ -122,9 +355,9 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co res = Config::get_option(arg); } } - "image_quality" => { - res = Session::get_image_quality(); - } + // "image_quality" => { + // res = Session::get_image_quality(); + // } "software_update_url" => { res = crate::common::SOFTWARE_UPDATE_URL.lock().unwrap().clone() } @@ -139,11 +372,11 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co } } } - "peer_option" => { - if let Ok(arg) = arg.to_str() { - res = Session::get_option(arg); - } - } + // "peer_option" => { + // if let Ok(arg) = arg.to_str() { + // res = Session::get_option(arg); + // } + // } "server_id" => { res = ui_interface::get_id(); } @@ -227,71 +460,78 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) { "info2" => { *crate::common::MOBILE_INFO2.lock().unwrap() = value.to_owned(); } - "connect" => { - Session::start(value, false); - } - "connect_file_transfer" => { - Session::start(value, true); - } - "login" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let Some(password) = m.get("password") { - if let Some(remember) = m.get("remember") { - Session::login(password, remember == "true"); - } - } - } - } - "close" => { - Session::close(); - } - "refresh" => { - Session::refresh(); - } - "reconnect" => { - Session::reconnect(); - } - "toggle_option" => { - Session::toggle_option(value); - } - "image_quality" => { - Session::set_image_quality(value); - } - "lock_screen" => { - Session::lock_screen(); - } - "ctrl_alt_del" => { - Session::ctrl_alt_del(); - } - "switch_display" => { - if let Ok(v) = value.parse::() { - Session::switch_display(v); - } - } + // "connect" => { + // Session::start(value, false); + // } + // "connect_file_transfer" => { + // Session::start(value, true); + // } + // "login" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let Some(password) = m.get("password") { + // if let Some(remember) = m.get("remember") { + // Session::login(password, remember == "true"); + // } + // } + // } + // } + // "close" => { + // Session::close(); + // } + // "refresh" => { + // Session::refresh(); + // } + // "reconnect" => { + // Session::reconnect(); + // } + // "toggle_option" => { + // Session::toggle_option(value); + // } + // "image_quality" => { + // Session::set_image_quality(value); + // } + // "lock_screen" => { + // Session::lock_screen(); + // } + // "ctrl_alt_del" => { + // Session::ctrl_alt_del(); + // } + // "switch_display" => { + // if let Ok(v) = value.parse::() { + // Session::switch_display(v); + // } + // } "remove" => { PeerConfig::remove(value); } - "input_key" => { - 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 down = m.get("down").is_some(); - let press = m.get("press").is_some(); - if let Some(name) = m.get("name") { - Session::input_key(name, down, press, alt, ctrl, shift, command); - } - } - } - "input_string" => { - Session::input_string(value); - } - "chat_client_mode" => { - Session::send_chat(value.to_owned()); - } + // "input_key" => { + // 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 down = m.get("down").is_some(); + // let press = m.get("press").is_some(); + // if let Some(name) = m.get("name") { + // Session::input_key(name, down, press, alt, ctrl, shift, command); + // } + // } + // } + // "input_string" => { + // Session::input_string(value); + // } + // "chat_client_mode" => { + // Session::send_chat(value.to_owned()); + // } + + // 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(); @@ -321,7 +561,9 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) { _ => 0, } << 3; } - Session::send_mouse(mask, x, y, alt, ctrl, shift, command); + if let Some(session) = SESSIONS.read().unwrap().get(id) { + session.send_mouse(mask, x, y, alt, ctrl, shift, command); + } } } "option" => { @@ -343,162 +585,163 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) { } } } - "peer_option" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let Some(name) = m.get("name") { - if let Some(value) = m.get("value") { - Session::set_option(name.to_owned(), value.to_owned()); - } - } - } - } - "input_os_password" => { - Session::input_os_password(value.to_owned(), true); - } - // File Action - "read_remote_dir" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let (Some(path), Some(show_hidden), Some(session)) = ( - m.get("path"), - m.get("show_hidden"), - Session::get().read().unwrap().as_ref(), - ) { - session.read_remote_dir(path.to_owned(), show_hidden.eq("true")); - } - } - } - "send_files" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let ( - Some(id), - Some(path), - Some(to), - Some(file_num), - Some(show_hidden), - Some(is_remote), - ) = ( - m.get("id"), - m.get("path"), - m.get("to"), - m.get("file_num"), - m.get("show_hidden"), - m.get("is_remote"), - ) { - Session::send_files( - id.parse().unwrap_or(0), - path.to_owned(), - to.to_owned(), - file_num.parse().unwrap_or(0), - show_hidden.eq("true"), - is_remote.eq("true"), - ); - } - } - } - "set_confirm_override_file" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let ( - Some(id), - Some(file_num), - Some(need_override), - Some(remember), - Some(is_upload), - ) = ( - m.get("id"), - m.get("file_num"), - m.get("need_override"), - m.get("remember"), - m.get("is_upload"), - ) { - Session::set_confirm_override_file( - id.parse().unwrap_or(0), - file_num.parse().unwrap_or(0), - need_override.eq("true"), - remember.eq("true"), - is_upload.eq("true"), - ); - } - } - } - "remove_file" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let ( - Some(id), - Some(path), - Some(file_num), - Some(is_remote), - Some(session), - ) = ( - m.get("id"), - m.get("path"), - m.get("file_num"), - m.get("is_remote"), - Session::get().write().unwrap().as_mut(), - ) { - session.remove_file( - id.parse().unwrap_or(0), - path.to_owned(), - file_num.parse().unwrap_or(0), - is_remote.eq("true"), - ); - } - } - } - "read_dir_recursive" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let (Some(id), Some(path), Some(is_remote), Some(session)) = ( - m.get("id"), - m.get("path"), - m.get("is_remote"), - Session::get().write().unwrap().as_mut(), - ) { - session.remove_dir_all( - id.parse().unwrap_or(0), - path.to_owned(), - is_remote.eq("true"), - ); - } - } - } - "remove_all_empty_dirs" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let (Some(id), Some(path), Some(is_remote), Some(session)) = ( - m.get("id"), - m.get("path"), - m.get("is_remote"), - Session::get().write().unwrap().as_mut(), - ) { - session.remove_dir( - id.parse().unwrap_or(0), - path.to_owned(), - is_remote.eq("true"), - ); - } - } - } - "cancel_job" => { - if let (Ok(id), Some(session)) = - (value.parse(), Session::get().write().unwrap().as_mut()) - { - session.cancel_job(id); - } - } - "create_dir" => { - if let Ok(m) = serde_json::from_str::>(value) { - if let (Some(id), Some(path), Some(is_remote), Some(session)) = ( - m.get("id"), - m.get("path"), - m.get("is_remote"), - Session::get().write().unwrap().as_mut(), - ) { - session.create_dir( - id.parse().unwrap_or(0), - path.to_owned(), - is_remote.eq("true"), - ); - } - } - } + // "peer_option" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let Some(name) = m.get("name") { + // if let Some(value) = m.get("value") { + // Session::set_option(name.to_owned(), value.to_owned()); + // } + // } + // } + // } + // "input_os_password" => { + // Session::input_os_password(value.to_owned(), true); + // } + // // File Action + // "read_remote_dir" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let (Some(path), Some(show_hidden), Some(session)) = ( + // m.get("path"), + // m.get("show_hidden"), + // Session::get().read().unwrap().as_ref(), + // ) { + // session.read_remote_dir(path.to_owned(), show_hidden.eq("true")); + // } + // } + // } + // "send_files" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let ( + // Some(id), + // Some(path), + // Some(to), + // Some(file_num), + // Some(show_hidden), + // Some(is_remote), + // ) = ( + // m.get("id"), + // m.get("path"), + // m.get("to"), + // m.get("file_num"), + // m.get("show_hidden"), + // m.get("is_remote"), + // ) { + // Session::send_files( + // id.parse().unwrap_or(0), + // path.to_owned(), + // to.to_owned(), + // file_num.parse().unwrap_or(0), + // show_hidden.eq("true"), + // is_remote.eq("true"), + // ); + // } + // } + // } + // "set_confirm_override_file" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let ( + // Some(id), + // Some(file_num), + // Some(need_override), + // Some(remember), + // Some(is_upload), + // ) = ( + // m.get("id"), + // m.get("file_num"), + // m.get("need_override"), + // m.get("remember"), + // m.get("is_upload"), + // ) { + // Session::set_confirm_override_file( + // id.parse().unwrap_or(0), + // file_num.parse().unwrap_or(0), + // need_override.eq("true"), + // remember.eq("true"), + // is_upload.eq("true"), + // ); + // } + // } + // } + // ** TODO ** continue + // "remove_file" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let ( + // Some(id), + // Some(path), + // Some(file_num), + // Some(is_remote), + // Some(session), + // ) = ( + // m.get("id"), + // m.get("path"), + // m.get("file_num"), + // m.get("is_remote"), + // Session::get().write().unwrap().as_mut(), + // ) { + // session.remove_file( + // id.parse().unwrap_or(0), + // path.to_owned(), + // file_num.parse().unwrap_or(0), + // is_remote.eq("true"), + // ); + // } + // } + // } + // "read_dir_recursive" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let (Some(id), Some(path), Some(is_remote), Some(session)) = ( + // m.get("id"), + // m.get("path"), + // m.get("is_remote"), + // Session::get().write().unwrap().as_mut(), + // ) { + // session.remove_dir_all( + // id.parse().unwrap_or(0), + // path.to_owned(), + // is_remote.eq("true"), + // ); + // } + // } + // } + // "remove_all_empty_dirs" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let (Some(id), Some(path), Some(is_remote), Some(session)) = ( + // m.get("id"), + // m.get("path"), + // m.get("is_remote"), + // Session::get().write().unwrap().as_mut(), + // ) { + // session.remove_dir( + // id.parse().unwrap_or(0), + // path.to_owned(), + // is_remote.eq("true"), + // ); + // } + // } + // } + // "cancel_job" => { + // if let (Ok(id), Some(session)) = + // (value.parse(), Session::get().write().unwrap().as_mut()) + // { + // session.cancel_job(id); + // } + // } + // "create_dir" => { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let (Some(id), Some(path), Some(is_remote), Some(session)) = ( + // m.get("id"), + // m.get("path"), + // m.get("is_remote"), + // Session::get().write().unwrap().as_mut(), + // ) { + // session.create_dir( + // id.parse().unwrap_or(0), + // path.to_owned(), + // is_remote.eq("true"), + // ); + // } + // } + // } // Server Side "update_password" => { if value.is_empty() {