multi remote instances 0.5

This commit is contained in:
csf
2022-05-31 22:09:36 +08:00
parent 5fb89c98e4
commit 317b350d2b
8 changed files with 238 additions and 223 deletions

View File

@@ -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();
},

View File

@@ -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...'));
},

View File

@@ -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));
}

View File

@@ -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() {}