multi remote instances 0.5
This commit is contained in:
parent
5fb89c98e4
commit
317b350d2b
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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",
|
||||
|
@ -143,7 +143,7 @@ class _RemotePageState extends State<RemotePage> 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<RemotePage> 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<RemotePage> 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<RemotePage> with WindowListener {
|
||||
icon: Icon(Icons.more_vert),
|
||||
onPressed: () {
|
||||
setState(() => _showEdit = false);
|
||||
showActions();
|
||||
showActions(widget.id);
|
||||
},
|
||||
),
|
||||
]),
|
||||
@ -553,7 +553,8 @@ class _RemotePageState extends State<RemotePage> 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<RemotePage> with WindowListener {
|
||||
|
||||
Widget getBodyForDesktopWithListener(bool keyboard) {
|
||||
var paints = <Widget>[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<RemotePage> 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<RemotePage> 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<RemotePage> with WindowListener {
|
||||
more.add(PopupMenuItem<String>(
|
||||
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<String>(
|
||||
child: Text(translate(
|
||||
(FFI.ffiModel.inputBlocked ? 'Unb' : 'B') + 'lock user input')),
|
||||
@ -693,28 +689,30 @@ class _RemotePageState extends State<RemotePage> 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<RemotePage> 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<String> 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 = <Widget>[];
|
||||
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 = <Widget>[];
|
||||
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();
|
||||
},
|
||||
|
@ -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...'));
|
||||
},
|
||||
|
@ -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<String, dynamic> evt) {
|
||||
void handlePeerInfo(Map<String, dynamic> 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<String, String> 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<String, String> 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<Peer> peers() {
|
||||
try {
|
||||
var str = getByName('peers');
|
||||
var str = getByName('peers'); // TODO
|
||||
if (str == "") return [];
|
||||
List<dynamic> 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<String, dynamic> 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<String, dynamic> 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<String, dynamic>? popEvent() {
|
||||
var s = getByName('event');
|
||||
if (s == '') return null;
|
||||
try {
|
||||
Map<String, dynamic> 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<String, dynamic> evt) {
|
||||
var type = '';
|
||||
@ -949,6 +960,7 @@ class FFI {
|
||||
break;
|
||||
}
|
||||
evt['buttons'] = buttons;
|
||||
evt['id'] = id;
|
||||
setByName('send_mouse', json.encode(evt));
|
||||
}
|
||||
|
||||
|
@ -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<String, dynamic>)? _eventCallback;
|
||||
static void Function(Uint8List)? _rgbaCallback;
|
||||
|
||||
static RustdeskImpl get rustdeskImpl => _rustdeskImpl;
|
||||
static RustdeskImpl get ffiBind => _ffiBind;
|
||||
|
||||
static Future<String> getVersion() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
@ -91,8 +90,8 @@ class PlatformFFI {
|
||||
dylib.lookupFunction<Void Function(Pointer<Utf8>, Pointer<Utf8>), 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<String, dynamic> 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<String, dynamic>) 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() {}
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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<RwLock<Option<Session>>> = Default::default();
|
||||
pub static ref SESSIONS: RwLock<HashMap<String,Session>> = Default::default();
|
||||
pub static ref EVENT_STREAM: RwLock<Option<StreamSink<String>>> = Default::default(); // rust to dart event channel
|
||||
pub static ref RGBA_STREAM: RwLock<Option<StreamSink<ZeroCopyBuffer<Vec<u8>>>>> = Default::default(); // rust to dart rgba (big u8 list) channel
|
||||
pub static ref GLOBAL_EVENT_STREAM: RwLock<Option<StreamSink<String>>> = 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<RwLock<Option<mpsc::UnboundedSender<Data>>>>, // UI to rust
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
events2ui: Arc<RwLock<StreamSink<String>>>,
|
||||
events2ui: Arc<RwLock<StreamSink<EventToUI>>>,
|
||||
}
|
||||
|
||||
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<String>) {
|
||||
pub fn start(id: &str, is_file_transfer: bool, events2ui: StreamSink<EventToUI>) {
|
||||
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()));
|
||||
};
|
||||
}
|
||||
|
@ -59,18 +59,18 @@ pub extern "C" fn rustdesk_core_main() -> bool {
|
||||
crate::core_main::core_main()
|
||||
}
|
||||
|
||||
pub fn start_event_stream(s: StreamSink<String>) -> ResultType<()> {
|
||||
let _ = flutter::EVENT_STREAM.write().unwrap().insert(s);
|
||||
Ok(())
|
||||
pub enum EventToUI {
|
||||
Event(String),
|
||||
Rgba(ZeroCopyBuffer<Vec<u8>>),
|
||||
}
|
||||
|
||||
pub fn start_rgba_stream(s: StreamSink<ZeroCopyBuffer<Vec<u8>>>) -> ResultType<()> {
|
||||
let _ = flutter::RGBA_STREAM.write().unwrap().insert(s);
|
||||
pub fn start_global_event_stream(s: StreamSink<String>) -> ResultType<()> {
|
||||
let _ = flutter::GLOBAL_EVENT_STREAM.write().unwrap().insert(s);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn session_connect(
|
||||
events2ui: StreamSink<String>,
|
||||
events2ui: StreamSink<EventToUI>,
|
||||
id: String,
|
||||
is_file_transfer: bool,
|
||||
) -> ResultType<()> {
|
||||
@ -86,6 +86,7 @@ pub fn get_session_remember(id: String) -> Option<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO sync
|
||||
pub fn get_session_toggle_option(id: String, arg: String) -> Option<bool> {
|
||||
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<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_session_image_quality(id: String) -> SyncReturn<Option<String>> {
|
||||
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<Vec<u8>> {
|
||||
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<String> {
|
||||
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::<HashMap<String, String>>(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::<i32>().unwrap_or(0))
|
||||
// .unwrap_or(0);
|
||||
// let y = m
|
||||
// .get("y")
|
||||
// .map(|x| x.parse::<i32>().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::<HashMap<String, String>>(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::<i32>().unwrap_or(0))
|
||||
.unwrap_or(0);
|
||||
let y = m
|
||||
.get("y")
|
||||
.map(|x| x.parse::<i32>().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::<HashMap<String, String>>(value) {
|
||||
if let Some(name) = m.get("name") {
|
||||
|
Loading…
Reference in New Issue
Block a user