From 18ad23435b4124ccdd8948d7d3cd3900a2852281 Mon Sep 17 00:00:00 2001 From: csf Date: Tue, 31 May 2022 14:44:06 +0800 Subject: [PATCH] multi remote instances --- flutter/lib/desktop/pages/remote_page.dart | 7 +- flutter/lib/models/model.dart | 36 ++- flutter/lib/models/native_model.dart | 6 +- src/flutter.rs | 311 ++++++++++----------- src/flutter_ffi.rs | 16 +- 5 files changed, 182 insertions(+), 194 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 6827bde60..b7d567482 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -404,7 +404,7 @@ class _RemotePageState extends State with WindowListener { icon: Icon(Icons.tv), onPressed: () { setState(() => _showEdit = false); - showOptions(); + showOptions(widget.id); }, ) ] + @@ -972,8 +972,9 @@ RadioListTile getRadio(String name, String toValue, String curValue, ); } -void showOptions() { - String quality = FFI.getByName('image_quality'); +void showOptions(String id) async { + // String quality = FFI.getByName('image_quality'); + String quality = await FFI.rustdeskImpl.getImageQuality(id: id) ?? 'balanced'; if (quality == '') quality = 'balanced'; String viewStyle = FFI.getByName('peer_option', 'view-style'); var displays = []; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index d94e69341..6590dc41a 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'; @@ -598,17 +599,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 +638,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; @@ -765,7 +767,7 @@ class FFI { 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,7 +781,11 @@ class FFI { setByName('connect_file_transfer', id); } else { FFI.chatModel.resetClientMode(); - setByName('connect', id); + // setByName('connect', id); + final stream = + FFI.rustdeskImpl.connect(id: id, isFileTransfer: isFileTransfer); + // listen stream ... + // every instance will bind a stream } FFI.id = id; } @@ -833,6 +839,8 @@ class FFI { PlatformFFI.setByName(name, value); } + static RustdeskImpl get rustdeskImpl => PlatformFFI.rustdeskImpl; + static handleMouse(Map evt) { var type = ''; var isMove = false; diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 9527555d0..e1b9137b6 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -30,9 +30,12 @@ class PlatformFFI { static String _homeDir = ''; static F2? _getByName; static F3? _setByName; + static late RustdeskImpl _rustdeskImpl; static void Function(Map)? _eventCallback; static void Function(Uint8List)? _rgbaCallback; + static RustdeskImpl get rustdeskImpl => _rustdeskImpl; + static Future getVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); return packageInfo.version; @@ -88,7 +91,8 @@ class PlatformFFI { dylib.lookupFunction, Pointer), F3>( 'set_by_name'); _dir = (await getApplicationDocumentsDirectory()).path; - _startListenEvent(RustdeskImpl(dylib)); + _rustdeskImpl = RustdeskImpl(dylib); + _startListenEvent(_rustdeskImpl); // global event try { _homeDir = (await ExternalPath.getExternalStorageDirectories())[0]; } catch (e) { diff --git a/src/flutter.rs b/src/flutter.rs index e40084450..c24923c72 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -26,17 +26,22 @@ use std::{ }; lazy_static::lazy_static! { - static ref SESSION: Arc>> = Default::default(); + // static ref SESSION: Arc>> = Default::default(); + static ref SESSIONS: RwLock> = Default::default(); pub static ref EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel pub static ref RGBA_STREAM: RwLock>>>> = Default::default(); // rust to dart rgba (big u8 list) channel } -#[derive(Clone, Default)] +pub fn get_session(id: &str) -> Option<&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 +51,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 +100,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 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 +162,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 +172,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 +203,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 +211,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 +219,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 +266,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. @@ -309,9 +285,10 @@ impl Session { 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())); - }; + self.events2ui + .read() + .unwrap() + .add(serde_json::ser::to_string(&h).unwrap_or("".to_owned())); } /// Get platform of peer. @@ -321,15 +298,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 +313,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 +321,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 +342,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 +351,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); } } } @@ -396,12 +368,12 @@ impl Session { /// # Arguments /// /// * `value` - The text to input. - pub fn input_string(value: &str) { + 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 +397,7 @@ impl Session { } pub fn send_mouse( + &self, mask: i32, x: i32, y: i32, @@ -433,9 +406,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( diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index a1b9b1e7b..5d1ca2368 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1,6 +1,6 @@ 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, get_session, make_fd_to_json, Session}; use crate::start_server; use crate::ui_interface; use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; @@ -69,6 +69,15 @@ pub fn start_rgba_stream(s: StreamSink>>) -> ResultType<( Ok(()) } +pub fn connect(id: String, is_file_transfer: bool, events2ui: StreamSink) { + Session::start(&id, is_file_transfer, events2ui); +} + +pub fn get_image_quality(id: String) -> Option { + let session = get_session(&id)?; + Some(session.get_image_quality()) +} + /// FFI for **get** commands which are idempotent. /// Return result in c string. /// @@ -100,11 +109,6 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co "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) {