From bdcb848a7562ec899b7ea1c0656056ef5f68ce06 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 31 Aug 2022 16:31:31 +0800 Subject: [PATCH 1/7] refactor remote interface --- src/client.rs | 6 + src/client/file_trait.rs | 24 +- src/flutter.rs | 74 +-- src/lib.rs | 1 + src/ui.rs | 2 +- src/ui/remote.rs | 870 ++++++++++++++++++++++-------------- src/ui_session_interface.rs | 257 +++++++++++ 7 files changed, 826 insertions(+), 408 deletions(-) create mode 100644 src/ui_session_interface.rs diff --git a/src/client.rs b/src/client.rs index 0bc69a7c1..64c7daf4d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1651,6 +1651,12 @@ pub trait Interface: Send + Clone + 'static + Sized { fn handle_login_error(&mut self, err: &str) -> bool; fn handle_peer_info(&mut self, pi: PeerInfo); fn set_force_relay(&mut self, direct: bool, received: bool); + fn is_file_transfer(&self) -> bool; + fn is_port_forward(&self) -> bool; + fn is_rdp(&self) -> bool; + fn on_error(&self, err: &str) { + self.msgbox("error", "Error", err); + } fn is_force_relay(&self) -> bool; async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream); async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream); diff --git a/src/client/file_trait.rs b/src/client/file_trait.rs index cc149c53f..d2f7b1648 100644 --- a/src/client/file_trait.rs +++ b/src/client/file_trait.rs @@ -1,4 +1,4 @@ -use hbb_common::{fs, message_proto::*}; +use hbb_common::{fs, message_proto::*, log}; use super::{Data, Interface}; @@ -114,4 +114,26 @@ pub trait FileManager: Interface { fn resume_job(&self, id: i32, is_remote: bool) { self.send(Data::ResumeJob((id, is_remote))); } + + fn set_confirm_override_file( + &self, + id: i32, + file_num: i32, + need_override: bool, + remember: bool, + is_upload: bool, + ) { + log::info!( + "confirm file transfer, job: {}, need_override: {}", + id, + need_override + ); + self.send(Data::SetConfirmOverrideFile(( + id, + file_num, + need_override, + remember, + is_upload, + ))); + } } diff --git a/src/flutter.rs b/src/flutter.rs index 60650aa9b..392f0f733 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -39,8 +39,9 @@ pub(super) const APP_TYPE_MAIN: &str = "main"; pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; +const MILLI1: Duration = Duration::from_millis(1); + lazy_static::lazy_static! { - // 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 } @@ -48,9 +49,6 @@ lazy_static::lazy_static! { static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -// pub fn get_session<'a>(id: &str) -> Option<&'a Session> { -// SESSIONS.read().unwrap().get(id) -// } #[derive(Clone)] pub struct Session { @@ -113,10 +111,6 @@ impl Session { } } - /// Get the current session instance. - // pub fn get() -> Arc>> { - // SESSION.clone() - // } /// Get the option of the current session. /// @@ -252,57 +246,6 @@ impl Session { 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); - // } - // } - - // 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, - ) { - 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. - /// - /// # 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); - // } - // } - /// Push an event to the event queue. /// An event is stored as json in the event queue. /// @@ -595,6 +538,18 @@ impl Interface for Session { } } + fn is_file_transfer(&self) -> bool { + todo!() + } + + fn is_port_forward(&self) -> bool { + todo!() + } + + fn is_rdp(&self) -> bool { + todo!() + } + fn msgbox(&self, msgtype: &str, title: &str, text: &str) { let has_retry = if check_if_retry(msgtype, title, text) { "true" @@ -706,7 +661,6 @@ impl Interface for Session { } } -const MILLI1: Duration = Duration::from_millis(1); struct Connection { video_handler: VideoHandler, diff --git a/src/lib.rs b/src/lib.rs index b7d1883c8..f554d447e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod port_forward; mod tray; mod ui_interface; +mod ui_session_interface; #[cfg(windows)] pub mod clipboard_file; diff --git a/src/ui.rs b/src/ui.rs index 78654e9ec..b66d1453b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -146,7 +146,7 @@ pub fn start(args: &mut [String]) { let args: Vec = iter.map(|x| x.clone()).collect(); frame.set_title(&id); frame.register_behavior("native-remote", move || { - Box::new(remote::Handler::new( + Box::new(remote::SciterSession::new( cmd.clone(), id.clone(), pass.clone(), diff --git a/src/ui/remote.rs b/src/ui/remote.rs index a0245c28a..f5abb3d73 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - ops::Deref, + ops::{Deref, DerefMut}, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, Mutex, RwLock, @@ -48,6 +48,7 @@ use crate::clipboard_file::*; use crate::{ client::*, common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, + ui_session_interface::{InvokeUi, Session}, }; use errno; @@ -74,46 +75,200 @@ static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); #[cfg(windows)] static mut IS_ALT_GR: bool = false; -#[derive(Default)] -pub struct HandlerInner { - element: Option, - sender: Option>, - thread: Option>, +/// SciterHandler +/// * element +/// * thread TODO check if flutter need +/// * close_state for file path when close +#[derive(Clone, Default)] +pub struct SciterHandler { + element: Arc>>, + thread: Arc>>>, close_state: HashMap, } -#[derive(Clone, Default)] -pub struct Handler { - inner: Arc>, - cmd: String, - id: String, - password: String, - args: Vec, - lc: Arc>, -} +impl SciterHandler { + #[inline] + fn call(&self, func: &str, args: &[Value]) { + if let Some(ref e) = self.element.lock().unwrap().as_ref() { + allow_err!(e.call_method(func, args)); + } + } -impl Deref for Handler { - type Target = Arc>; - - fn deref(&self) -> &Self::Target { - &self.inner + #[inline] + fn call2(&self, func: &str, args: &[Value]) { + if let Some(ref e) = self.element.lock().unwrap().as_ref() { + allow_err!(e.call_method(func, &super::value_crash_workaround(args)[..])); + } } } -impl FileManager for Handler {} +impl InvokeUi for SciterHandler { + fn set_cursor_data(&self, cd: CursorData) { + let mut colors = hbb_common::compress::decompress(&cd.colors); + if colors.iter().filter(|x| **x != 0).next().is_none() { + log::info!("Fix transparent"); + // somehow all 0 images shows black rect, here is a workaround + colors[3] = 1; + } + let mut png = Vec::new(); + if let Ok(()) = repng::encode(&mut png, cd.width as _, cd.height as _, &colors) { + self.call( + "setCursorData", + &make_args!( + cd.id.to_string(), + cd.hotx, + cd.hoty, + cd.width, + cd.height, + &png[..] + ), + ); + } + } -impl sciter::EventHandler for Handler { + fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { + self.call("setDisplay", &make_args!(x, y, w, h)); + } + + fn update_privacy_mode(&self) { + self.call("updatePrivacyMode", &[]); + } + + fn set_permission(&self, name: &str, value: bool) { + self.call2("setPermission", &make_args!(name, value)); + } + + fn update_pi(&self, pi: PeerInfo) {} + + fn close_success(&self) { + self.call2("closeSuccess", &make_args!()); + } + + fn update_quality_status(&self, status: QualityStatus) { + self.call2( + "updateQualityStatus", + &make_args!( + status.speed.map_or(Value::null(), |it| it.into()), + status.fps.map_or(Value::null(), |it| it.into()), + status.delay.map_or(Value::null(), |it| it.into()), + status.target_bitrate.map_or(Value::null(), |it| it.into()), + status + .codec_format + .map_or(Value::null(), |it| it.to_string().into()) + ), + ); + } + + fn set_cursor_id(&self, id: String) { + self.call("setCursorId", &make_args!(id)); + } + + fn set_cursor_position(&self, cp: CursorPosition) { + self.call("setCursorPosition", &make_args!(cp.x, cp.y)); + } + + fn set_connection_type(&self, is_secured: bool, direct: bool) { + self.call("setConnectionType", &make_args!(is_secured, direct)); + } + + fn job_error(&self, id: i32, err: String, file_num: i32) { + todo!() + } + + fn job_done(&self, id: i32, file_num: i32) { + todo!() + } + + fn clear_all_jobs(&self) { + todo!() + } + + fn add_job( + &self, + id: i32, + path: String, + to: String, + file_num: i32, + show_hidden: bool, + is_remote: bool, + ) { + todo!() + } + + fn update_transfer_list(&self) { + todo!() + } + + fn confirm_delete_files(&self, id: i32, i: i32, name: String) { + todo!() + } + + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { + todo!() + } + + fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) { + todo!() + } + + fn adapt_size(&self) { + self.call("adaptSize", &make_args!()); + } +} + +pub struct SciterSession(Session); + +impl Deref for SciterSession { + type Target = Session; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for SciterSession { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +// #[derive(Default)] +// pub struct HandlerInner { +// element: Option, +// sender: Option>, +// thread: Option>, +// close_state: HashMap, +// } + +// #[derive(Clone, Default)] +// pub struct Handler { +// inner: Arc>, +// cmd: String, +// id: String, +// password: String, +// args: Vec, +// lc: Arc>, +// } + +// impl Deref for Handler { +// type Target = Arc>; + +// fn deref(&self) -> &Self::Target { +// &self.inner +// } +// } + +impl sciter::EventHandler for SciterSession { fn get_subscription(&mut self) -> Option { Some(EVENT_GROUPS::HANDLE_BEHAVIOR_EVENT) } fn attached(&mut self, root: HELEMENT) { - self.write().unwrap().element = Some(Element::from(root)); + *self.element.lock().unwrap() = Some(Element::from(root)); } fn detached(&mut self, _root: HELEMENT) { - self.write().unwrap().element = None; - self.write().unwrap().sender.take().map(|sender| { + *self.element.lock().unwrap() = None; + self.sender.write().unwrap().take().map(|sender| { sender.send(Data::Close).ok(); }); } @@ -239,38 +394,40 @@ impl sciter::EventHandler for Handler { } } -impl Handler { +impl SciterSession { pub fn new(cmd: String, id: String, password: String, args: Vec) -> Self { - let me = Self { + let session: Session = Session { cmd, id: id.clone(), password: password.clone(), args, ..Default::default() }; - me.lc - .write() - .unwrap() - .initialize(id, me.is_file_transfer(), me.is_port_forward()); - me - } - - fn update_quality_status(&self, status: QualityStatus) { - self.call2( - "updateQualityStatus", - &make_args!( - status.speed.map_or(Value::null(), |it| it.into()), - status.fps.map_or(Value::null(), |it| it.into()), - status.delay.map_or(Value::null(), |it| it.into()), - status.target_bitrate.map_or(Value::null(), |it| it.into()), - status - .codec_format - .map_or(Value::null(), |it| it.to_string().into()) - ), + session.lc.write().unwrap().initialize( + id, + session.is_file_transfer(), + session.is_port_forward(), ); + + Self(session) } - fn start_keyboard_hook(&self) { + // fn update_quality_status(&self, status: QualityStatus) { + // self.call2( + // "updateQualityStatus", + // &make_args!( + // status.speed.map_or(Value::null(), |it| it.into()), + // status.fps.map_or(Value::null(), |it| it.into()), + // status.delay.map_or(Value::null(), |it| it.into()), + // status.target_bitrate.map_or(Value::null(), |it| it.into()), + // status + // .codec_format + // .map_or(Value::null(), |it| it.to_string().into()) + // ), + // ); + // } + + fn start_keyboard_hook(&'static self) { if self.is_port_forward() || self.is_file_transfer() { return; } @@ -278,7 +435,7 @@ impl Handler { return; } log::info!("keyboard hooked"); - let mut me = self.clone(); + let me = self.clone(); let peer = self.peer_platform(); let is_win = peer == "Windows"; #[cfg(windows)] @@ -364,7 +521,7 @@ impl Handler { Key::UpArrow => Some(ControlKey::UpArrow), Key::Delete => { if is_win && ctrl && alt { - me.ctrl_alt_del(); + // me.ctrl_alt_del(); // TODO return; } Some(ControlKey::Delete) @@ -485,7 +642,7 @@ impl Handler { } if chr != '\0' { if chr == 'l' && is_win && command { - me.lock_screen(); + // me.lock_screen(); // TODO return; } key_event.set_chr(chr as _); @@ -494,7 +651,7 @@ impl Handler { return; } } - me.key_down_or_up(down, key_event, alt, ctrl, shift, command); + // me.key_down_or_up(down, key_event, alt, ctrl, shift, command); // TODO }; if let Err(error) = rdev::listen(func) { log::error!("rdev: {:?}", error); @@ -518,19 +675,19 @@ impl Handler { v } - #[inline] - pub(super) fn save_config(&self, config: PeerConfig) { - self.lc.write().unwrap().save_config(config); - } + // #[inline] + // pub(super) fn save_config(&self, config: PeerConfig) { + // self.lc.write().unwrap().save_config(config); + // } fn save_view_style(&mut self, value: String) { self.lc.write().unwrap().save_view_style(value); } - #[inline] - pub(super) fn load_config(&self) -> PeerConfig { - load_config(&self.id) - } + // #[inline] + // pub(super) fn load_config(&self) -> PeerConfig { + // load_config(&self.id) + // } fn toggle_option(&mut self, name: String) { let msg = self.lc.write().unwrap().toggle_option(name.clone()); @@ -635,9 +792,9 @@ impl Handler { self.send(Data::Message(msg)); } - pub fn is_restarting_remote_device(&self) -> bool { - self.lc.read().unwrap().restarting_remote_device - } + // pub fn is_restarting_remote_device(&self) -> bool { + // self.lc.read().unwrap().restarting_remote_device + // } fn t(&self, name: String) -> String { crate::client::translate(name) @@ -672,7 +829,7 @@ impl Handler { let size = (x, y, w, h); let mut config = self.load_config(); if self.is_file_transfer() { - let close_state = self.read().unwrap().close_state.clone(); + let close_state = self.close_state.clone(); let mut has_change = false; for (k, mut v) in close_state { if k == "remote_dir" { @@ -785,20 +942,20 @@ impl Handler { pi } - fn get_option(&self, k: String) -> String { - self.lc.read().unwrap().get_option(&k) - } + // fn get_option(&self, k: String) -> String { + // self.lc.read().unwrap().get_option(&k) + // } - fn set_option(&self, k: String, v: String) { - self.lc.write().unwrap().set_option(k, v); - } + // fn set_option(&self, k: String, v: String) { + // self.lc.write().unwrap().set_option(k, v); + // } fn input_os_password(&mut self, pass: String, activate: bool) { input_os_password(pass, activate, self.clone()); } - fn save_close_state(&self, k: String, v: String) { - self.write().unwrap().close_state.insert(k, v); + fn save_close_state(&mut self, k: String, v: String) { + self.close_state.insert(k, v); } fn get_chatbox(&mut self) -> String { @@ -834,49 +991,49 @@ impl Handler { self.send(Data::Message(msg_out)); } - fn is_file_transfer(&self) -> bool { - self.cmd == "--file-transfer" - } + // fn is_file_transfer(&self) -> bool { + // self.cmd == "--file-transfer" + // } - fn is_port_forward(&self) -> bool { - self.cmd == "--port-forward" || self.is_rdp() - } + // fn is_port_forward(&self) -> bool { + // self.cmd == "--port-forward" || self.is_rdp() + // } - fn is_rdp(&self) -> bool { - self.cmd == "--rdp" - } + // fn is_rdp(&self) -> bool { + // self.cmd == "--rdp" + // } fn reconnect(&mut self) { println!("reconnecting"); let cloned = self.clone(); - let mut lock = self.write().unwrap(); - lock.thread.take().map(|t| t.join()); - lock.thread = Some(std::thread::spawn(move || { + let mut lock = self.thread.lock().unwrap(); + lock.take().map(|t| t.join()); + *lock = Some(std::thread::spawn(move || { io_loop(cloned); })); } - #[inline] - fn peer_platform(&self) -> String { - self.lc.read().unwrap().info.platform.clone() - } + // #[inline] + // fn peer_platform(&self) -> String { + // self.lc.read().unwrap().info.platform.clone() + // } - fn get_platform(&mut self, is_remote: bool) -> String { - if is_remote { - self.peer_platform() - } else { - whoami::platform().to_string() - } - } + // fn get_platform(&mut self, is_remote: bool) -> String { + // if is_remote { + // self.peer_platform() + // } else { + // whoami::platform().to_string() + // } + // } - fn get_path_sep(&mut self, is_remote: bool) -> &'static str { - let p = self.get_platform(is_remote); - if &p == "Windows" { - return "\\"; - } else { - return "/"; - } - } + // fn get_path_sep(&mut self, is_remote: bool) -> &'static str { + // let p = self.get_platform(is_remote); + // if &p == "Windows" { + // return "\\"; + // } else { + // return "/"; + // } + // } fn get_icon_path(&mut self, file_type: i32, ext: String) -> String { let mut path = Config::icon_path(); @@ -967,7 +1124,7 @@ impl Handler { } } - send_mouse(mask, x, y, alt, ctrl, shift, command, self); + send_mouse(mask, x, y, alt, ctrl, shift, command, &self.0); // on macos, ctrl + left button down = right button down, up won't emit, so we need to // emit up myself if peer is not macos // to-do: how about ctrl + left from win to macos @@ -1210,42 +1367,26 @@ impl Handler { self.send(Data::Message(msg_out)); } - #[inline] - fn set_cursor_id(&mut self, id: String) { - self.call("setCursorId", &make_args!(id)); - } + // #[inline] + // fn set_cursor_id(&mut self, id: String) { + // self.call("setCursorId", &make_args!(id)); + // } - #[inline] - fn set_cursor_position(&mut self, cd: CursorPosition) { - self.call("setCursorPosition", &make_args!(cd.x, cd.y)); - } + // #[inline] + // fn set_cursor_position(&mut self, cd: CursorPosition) { + // self.call("setCursorPosition", &make_args!(cd.x, cd.y)); + // } - #[inline] - fn call(&self, func: &str, args: &[Value]) { - let r = self.read().unwrap(); - if let Some(ref e) = r.element { - allow_err!(e.call_method(func, args)); - } - } - - #[inline] - fn call2(&self, func: &str, args: &[Value]) { - let r = self.read().unwrap(); - if let Some(ref e) = r.element { - allow_err!(e.call_method(func, &super::value_crash_workaround(args)[..])); - } - } - - #[inline] - fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { - self.call("setDisplay", &make_args!(x, y, w, h)); - } + // #[inline] + // fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { + // self.call("setDisplay", &make_args!(x, y, w, h)); + // } } const MILLI1: Duration = Duration::from_millis(1); -async fn start_one_port_forward( - handler: Handler, +async fn start_one_port_forward( + handler: Session, port: i32, remote_host: String, remote_port: i32, @@ -1273,9 +1414,9 @@ async fn start_one_port_forward( } #[tokio::main(flavor = "current_thread")] -async fn io_loop(handler: Handler) { +async fn io_loop(handler: Session) { let (sender, mut receiver) = mpsc::unbounded_channel::(); - handler.write().unwrap().sender = Some(sender.clone()); + *handler.sender.write().unwrap() = Some(sender.clone()); let mut options = crate::ipc::get_options_async().await; let mut key = options.remove("key").unwrap_or("".to_owned()); let token = LocalConfig::get_option("access_token"); @@ -1431,8 +1572,8 @@ impl RemoveJob { } } -struct Remote { - handler: Handler, +struct Remote { + handler: Session, video_sender: MediaSender, audio_sender: MediaSender, receiver: mpsc::UnboundedReceiver, @@ -1451,7 +1592,7 @@ struct Remote { video_format: CodecFormat, } -impl Remote { +impl Remote { async fn io_loop(&mut self, key: &str, token: &str) { let stop_clipboard = self.start_clipboard(); let mut last_recv_time = Instant::now(); @@ -1474,8 +1615,9 @@ impl Remote { SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst); - self.handler - .call("setConnectionType", &make_args!(peer.is_secured(), direct)); + // self.handler + // .call("setConnectionType", &make_args!(peer.is_secured(), direct)); + self.handler.set_connection_type(peer.is_secured(), direct); // just build for now #[cfg(not(windows))] @@ -1597,10 +1739,12 @@ impl Remote { } } if let Some(err) = err { - self.handler - .call("jobError", &make_args!(id, err, file_num)); + // self.handler + // .call("jobError", &make_args!(id, err, file_num)); + self.handler.job_error(id, err, file_num); } else { - self.handler.call("jobDone", &make_args!(id, file_num)); + // self.handler.call("jobDone", &make_args!(id, file_num)); + self.handler.job_done(id, file_num); } } @@ -1645,7 +1789,8 @@ impl Remote { async fn load_last_jobs(&mut self) { log::info!("start load last jobs"); - self.handler.call("clearAllJobs", &make_args!()); + // self.handler.call("clearAllJobs", &make_args!()); + self.handler.clear_all_jobs(); let pc = self.handler.load_config(); if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { // no last jobs @@ -1656,16 +1801,24 @@ impl Remote { for job_str in pc.transfer.read_jobs.iter() { let job: Result = serde_json::from_str(&job_str); if let Ok(job) = job { - self.handler.call( - "addJob", - &make_args!( - cnt, - job.to.clone(), - job.remote.clone(), - job.file_num, - job.show_hidden, - false - ), + // self.handler.call( + // "addJob", + // &make_args!( + // cnt, + // job.to.clone(), + // job.remote.clone(), + // job.file_num, + // job.show_hidden, + // false + // ), + // ); + self.handler.add_job( + cnt, + job.to.clone(), + job.remote.clone(), + job.file_num, + job.show_hidden, + false, ); cnt += 1; println!("restore read_job: {:?}", job); @@ -1674,22 +1827,31 @@ impl Remote { for job_str in pc.transfer.write_jobs.iter() { let job: Result = serde_json::from_str(&job_str); if let Ok(job) = job { - self.handler.call( - "addJob", - &make_args!( - cnt, - job.remote.clone(), - job.to.clone(), - job.file_num, - job.show_hidden, - true - ), + // self.handler.call( + // "addJob", + // &make_args!( + // cnt, + // job.remote.clone(), + // job.to.clone(), + // job.file_num, + // job.show_hidden, + // true + // ), + // ); + self.handler.add_job( + cnt, + job.remote.clone(), + job.to.clone(), + job.file_num, + job.show_hidden, + true, ); cnt += 1; println!("restore write_job: {:?}", job); } } - self.handler.call("updateTransferList", &make_args!()); + // self.handler.call("updateTransferList", &make_args!()); + self.handler.update_transfer_list(); } async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { @@ -1753,8 +1915,8 @@ impl Remote { to, job.files().len() ); - let m = make_fd(job.id(), job.files(), true); - self.handler.call("updateFolderFiles", &make_args!(m)); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); // TODO #[cfg(not(windows))] let files = job.files().clone(); #[cfg(windows)] @@ -1813,8 +1975,8 @@ impl Remote { to, job.files().len() ); - let m = make_fd(job.id(), job.files(), true); - self.handler.call("updateFolderFiles", &make_args!(m)); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); job.is_last_job = true; self.read_jobs.push(job); self.timer = time::interval(MILLI1); @@ -1860,10 +2022,11 @@ impl Remote { if let Some(job) = self.remove_jobs.get_mut(&id) { let i = file_num as usize; if i < job.files.len() { - self.handler.call( - "confirmDeleteFiles", - &make_args!(id, file_num, job.files[i].name.clone()), - ); + // self.handler.call( + // "confirmDeleteFiles", + // &make_args!(id, file_num, job.files[i].name.clone()), + // ); + self.handler.confirm_delete_files(id, file_num); } } } @@ -1924,8 +2087,8 @@ impl Remote { } else { match fs::get_recursive_files(&path, include_hidden) { Ok(entries) => { - let m = make_fd(id, &entries, true); - self.handler.call("updateFolderFiles", &make_args!(m)); + // let m = make_fd(id, &entries, true); + // self.handler.call("updateFolderFiles", &make_args!(m)); self.remove_jobs .insert(id, RemoveJob::new(entries, path, sep, is_remote)); } @@ -2018,7 +2181,7 @@ impl Remote { job: &fs::TransferJob, elapsed: i32, last_update_jobs_status: &mut (Instant, HashMap), - handler: &mut Handler, + handler: &mut Session, ) { if elapsed <= 0 { return; @@ -2034,10 +2197,11 @@ impl Remote { last_update_jobs_status.1.insert(job.id(), transferred); let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); let file_num = job.file_num() - 1; - handler.call( - "jobProgress", - &make_args!(job.id(), file_num, speed, job.finished_size() as f64), - ); + // handler.call( + // "jobProgress", + // &make_args!(job.id(), file_num, speed, job.finished_size() as f64), + // ); + handler.job_progress(job.id(), file_num, speed, job.finished_size() as f64); } fn update_jobs_status(&mut self) { @@ -2103,8 +2267,10 @@ impl Remote { Some(message::Union::VideoFrame(vf)) => { if !self.first_frame { self.first_frame = true; - self.handler.call2("closeSuccess", &make_args!()); - self.handler.call("adaptSize", &make_args!()); + // self.handler.call2("closeSuccess", &make_args!()); + self.handler.close_success(); + // self.handler.call("adaptSize", &make_args!()); + self.handler.adapt_size(); self.send_opts_after_login(peer).await; } let incomming_format = CodecFormat::from(&vf); @@ -2192,11 +2358,11 @@ impl Remote { fs::transform_windows_path(&mut entries); } } - let mut m = make_fd(fd.id, &entries, fd.id > 0); - if fd.id <= 0 { - m.set_item("path", fd.path); - } - self.handler.call("updateFolderFiles", &make_args!(m)); + // let mut m = make_fd(fd.id, &entries, fd.id > 0); + // if fd.id <= 0 { + // m.set_item("path", fd.path); + // } + // self.handler.call("updateFolderFiles", &make_args!(m)); if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) { log::info!("job set_files: {:?}", entries); job.set_files(entries); @@ -2227,14 +2393,20 @@ impl Remote { let msg = new_send_confirm(req); allow_err!(peer.send(&msg).await); } else { - self.handler.call( - "overrideFileConfirm", - &make_args!( - digest.id, - digest.file_num, - read_path, - true - ), + // self.handler.call( + // "overrideFileConfirm", + // &make_args!( + // digest.id, + // digest.file_num, + // read_path, + // true + // ), + // ); + self.handler.override_file_confirm( + digest.id, + digest.file_num, + read_path, + true, ); } } @@ -2271,14 +2443,20 @@ impl Remote { ); allow_err!(peer.send(&msg).await); } else { - self.handler.call( - "overrideFileConfirm", - &make_args!( - digest.id, - digest.file_num, - write_path, - false - ), + // self.handler.call( + // "overrideFileConfirm", + // &make_args!( + // digest.id, + // digest.file_num, + // write_path, + // false + // ), + // ); + self.handler.override_file_confirm( + digest.id, + digest.file_num, + write_path, + false, ); } } @@ -2333,24 +2511,27 @@ impl Remote { self.audio_sender.send(MediaData::AudioFormat(f)).ok(); } Some(misc::Union::ChatMessage(c)) => { - self.handler.call("newMessage", &make_args!(c.text)); + // self.handler.call("newMessage", &make_args!(c.text)); // TODO } Some(misc::Union::PermissionInfo(p)) => { log::info!("Change permission {:?} -> {}", p.permission, p.enabled); match p.permission.enum_value_or_default() { Permission::Keyboard => { SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - self.handler - .call2("setPermission", &make_args!("keyboard", p.enabled)); + // self.handler + // .call2("setPermission", &make_args!("keyboard", p.enabled)); + self.handler.set_permission("keyboard", p.enabled); } Permission::Clipboard => { SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - self.handler - .call2("setPermission", &make_args!("clipboard", p.enabled)); + // self.handler + // .call2("setPermission", &make_args!("clipboard", p.enabled)); + self.handler.set_permission("clipboard", p.enabled); } Permission::Audio => { - self.handler - .call2("setPermission", &make_args!("audio", p.enabled)); + // self.handler + // .call2("setPermission", &make_args!("audio", p.enabled)); + self.handler.set_permission("audio", p.enabled); } Permission::File => { SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst); @@ -2358,17 +2539,19 @@ impl Remote { return true; } self.check_clipboard_file_context(); - self.handler - .call2("setPermission", &make_args!("file", p.enabled)); + // self.handler + // .call2("setPermission", &make_args!("file", p.enabled)); + self.handler.set_permission("file", p.enabled); } Permission::Restart => { - self.handler - .call2("setPermission", &make_args!("restart", p.enabled)); + // self.handler + // .call2("setPermission", &make_args!("restart", p.enabled)); + self.handler.set_permission("restart", p.enabled); } } } Some(misc::Union::SwitchDisplay(s)) => { - self.handler.call("switchDisplay", &make_args!(s.display)); + // self.handler.call("switchDisplay", &make_args!(s.display)); // TODO self.video_sender.send(MediaData::Reset).ok(); if s.width > 0 && s.height > 0 { VIDEO.lock().unwrap().as_mut().map(|v| { @@ -2441,7 +2624,7 @@ impl Remote { #[inline(always)] fn update_block_input_state(&mut self, on: bool) { - self.handler.call("updateBlockInputState", &make_args!(on)); + // self.handler.call("updateBlockInputState", &make_args!(on)); // TODO } async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { @@ -2471,7 +2654,8 @@ impl Remote { config.privacy_mode = on; self.handler.save_config(config); - self.handler.call("updatePrivacyMode", &[]); + // self.handler.call("updatePrivacyMode", &[]); + self.handler.update_privacy_mode(); } async fn handle_back_msg_privacy_mode( @@ -2593,143 +2777,137 @@ pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value { m } -#[async_trait] -impl Interface for Handler { - fn send(&self, data: Data) { - if let Some(ref sender) = self.read().unwrap().sender { - sender.send(data).ok(); - } - } +// #[async_trait] +// impl Interface for Handler { +// fn send(&self, data: Data) { +// if let Some(ref sender) = self.read().unwrap().sender { +// sender.send(data).ok(); +// } +// } - fn msgbox(&self, msgtype: &str, title: &str, text: &str) { - let retry = check_if_retry(msgtype, title, text); - self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); - } +// fn msgbox(&self, msgtype: &str, title: &str, text: &str) { +// let retry = check_if_retry(msgtype, title, text); +// self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); +// } - fn handle_login_error(&mut self, err: &str) -> bool { - self.lc.write().unwrap().handle_login_error(err, self) - } +// fn handle_login_error(&mut self, err: &str) -> bool { +// self.lc.write().unwrap().handle_login_error(err, self) +// } - fn handle_peer_info(&mut self, pi: PeerInfo) { - let mut pi_sciter = Value::map(); - let username = self.lc.read().unwrap().get_username(&pi); - pi_sciter.set_item("username", username.clone()); - pi_sciter.set_item("hostname", pi.hostname.clone()); - pi_sciter.set_item("platform", pi.platform.clone()); - pi_sciter.set_item("sas_enabled", pi.sas_enabled); - if get_version_number(&pi.version) < get_version_number("1.1.10") { - self.call2("setPermission", &make_args!("restart", false)); - } - if self.is_file_transfer() { - if pi.username.is_empty() { - self.on_error("No active console user logged on, please connect and logon first."); - return; - } - } else if !self.is_port_forward() { - if pi.displays.is_empty() { - self.lc.write().unwrap().handle_peer_info(username, pi); - self.call("updatePrivacyMode", &[]); - self.msgbox("error", "Remote Error", "No Display"); - return; - } - let mut displays = Value::array(0); - for ref d in pi.displays.iter() { - let mut display = Value::map(); - display.set_item("x", d.x); - display.set_item("y", d.y); - display.set_item("width", d.width); - display.set_item("height", d.height); - displays.push(display); - } - pi_sciter.set_item("displays", displays); - let mut current = pi.current_display as usize; - if current >= pi.displays.len() { - current = 0; - } - pi_sciter.set_item("current_display", current as i32); - let current = &pi.displays[current]; - self.set_display(current.x, current.y, current.width, current.height); - // https://sciter.com/forums/topic/color_spaceiyuv-crash - // Nothing spectacular in decoder – done on CPU side. - // So if you can do BGRA translation on your side – the better. - // BGRA is used as internal image format so it will not require additional transformations. - VIDEO.lock().unwrap().as_mut().map(|v| { - let ok = v.start_streaming( - (current.width as _, current.height as _), - COLOR_SPACE::Rgb32, - None, - ); - log::info!("[video] initialized: {:?}", ok); - }); - let p = self.lc.read().unwrap().should_auto_login(); - if !p.is_empty() { - input_os_password(p, true, self.clone()); - } - } - self.lc.write().unwrap().handle_peer_info(username, pi); - self.call("updatePrivacyMode", &[]); - self.call("updatePi", &make_args!(pi_sciter)); - if self.is_file_transfer() { - self.call2("closeSuccess", &make_args!()); - } else if !self.is_port_forward() { - self.msgbox("success", "Successful", "Connected, waiting for image..."); - } - #[cfg(windows)] - { - let mut path = std::env::temp_dir(); - path.push(&self.id); - let path = path.with_extension(crate::get_app_name().to_lowercase()); - std::fs::File::create(&path).ok(); - if let Some(path) = path.to_str() { - crate::platform::windows::add_recent_document(&path); - } - } - self.start_keyboard_hook(); - } +// fn handle_peer_info(&mut self, pi: PeerInfo) { +// let mut pi_sciter = Value::map(); +// let username = self.lc.read().unwrap().get_username(&pi); +// pi_sciter.set_item("username", username.clone()); +// pi_sciter.set_item("hostname", pi.hostname.clone()); +// pi_sciter.set_item("platform", pi.platform.clone()); +// pi_sciter.set_item("sas_enabled", pi.sas_enabled); +// if get_version_number(&pi.version) < get_version_number("1.1.10") { +// self.call2("setPermission", &make_args!("restart", false)); +// } +// if self.is_file_transfer() { +// if pi.username.is_empty() { +// self.on_error("No active console user logged on, please connect and logon first."); +// return; +// } +// } else if !self.is_port_forward() { +// if pi.displays.is_empty() { +// self.lc.write().unwrap().handle_peer_info(username, pi); +// self.call("updatePrivacyMode", &[]); +// self.msgbox("error", "Remote Error", "No Display"); +// return; +// } +// let mut displays = Value::array(0); +// for ref d in pi.displays.iter() { +// let mut display = Value::map(); +// display.set_item("x", d.x); +// display.set_item("y", d.y); +// display.set_item("width", d.width); +// display.set_item("height", d.height); +// displays.push(display); +// } +// pi_sciter.set_item("displays", displays); +// let mut current = pi.current_display as usize; +// if current >= pi.displays.len() { +// current = 0; +// } +// pi_sciter.set_item("current_display", current as i32); +// let current = &pi.displays[current]; +// self.set_display(current.x, current.y, current.width, current.height); +// // https://sciter.com/forums/topic/color_spaceiyuv-crash +// // Nothing spectacular in decoder – done on CPU side. +// // So if you can do BGRA translation on your side – the better. +// // BGRA is used as internal image format so it will not require additional transformations. +// VIDEO.lock().unwrap().as_mut().map(|v| { +// let ok = v.start_streaming( +// (current.width as _, current.height as _), +// COLOR_SPACE::Rgb32, +// None, +// ); +// log::info!("[video] initialized: {:?}", ok); +// }); +// let p = self.lc.read().unwrap().should_auto_login(); +// if !p.is_empty() { +// input_os_password(p, true, self.clone()); +// } +// } +// self.lc.write().unwrap().handle_peer_info(username, pi); +// self.call("updatePrivacyMode", &[]); +// self.call("updatePi", &make_args!(pi_sciter)); +// if self.is_file_transfer() { +// self.call2("closeSuccess", &make_args!()); +// } else if !self.is_port_forward() { +// self.msgbox("success", "Successful", "Connected, waiting for image..."); +// } +// #[cfg(windows)] +// { +// let mut path = std::env::temp_dir(); +// path.push(&self.id); +// let path = path.with_extension(crate::get_app_name().to_lowercase()); +// std::fs::File::create(&path).ok(); +// if let Some(path) = path.to_str() { +// crate::platform::windows::add_recent_document(&path); +// } +// } +// self.start_keyboard_hook(); +// } - async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { - handle_hash(self.lc.clone(), pass, hash, self, peer).await; - } +// async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { +// handle_hash(self.lc.clone(), pass, hash, self, peer).await; +// } - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { - handle_login_from_ui(self.lc.clone(), password, remember, peer).await; - } +// async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { +// handle_login_from_ui(self.lc.clone(), password, remember, peer).await; +// } - async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { - if !t.from_client { - self.update_quality_status(QualityStatus { - delay: Some(t.last_delay as _), - target_bitrate: Some(t.target_bitrate as _), - ..Default::default() - }); - handle_test_delay(t, peer).await; - } - } +// async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { +// if !t.from_client { +// self.update_quality_status(QualityStatus { +// delay: Some(t.last_delay as _), +// target_bitrate: Some(t.target_bitrate as _), +// ..Default::default() +// }); +// handle_test_delay(t, peer).await; +// } +// } - fn set_force_relay(&mut self, direct: bool, received: bool) { - let mut lc = self.lc.write().unwrap(); - lc.force_relay = false; - if direct && !received { - let errno = errno::errno().0; - log::info!("errno is {}", errno); - // TODO: check mac and ios - if cfg!(windows) && errno == 10054 || !cfg!(windows) && errno == 104 { - lc.force_relay = true; - lc.set_option("force-always-relay".to_owned(), "Y".to_owned()); - } - } - } +// fn set_force_relay(&mut self, direct: bool, received: bool) { +// let mut lc = self.lc.write().unwrap(); +// lc.force_relay = false; +// if direct && !received { +// let errno = errno::errno().0; +// log::info!("errno is {}", errno); +// // TODO: check mac and ios +// if cfg!(windows) && errno == 10054 || !cfg!(windows) && errno == 104 { +// lc.force_relay = true; +// lc.set_option("force-always-relay".to_owned(), "Y".to_owned()); +// } +// } +// } - fn is_force_relay(&self) -> bool { - self.lc.read().unwrap().force_relay - } -} - -impl Handler { - fn on_error(&self, err: &str) { - self.msgbox("error", "Error", err); - } -} +// fn is_force_relay(&self) -> bool { +// self.lc.read().unwrap().force_relay +// } +// } #[tokio::main(flavor = "current_thread")] async fn send_note(url: String, id: String, conn_id: i32, note: String) { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs new file mode 100644 index 000000000..8f7a0b904 --- /dev/null +++ b/src/ui_session_interface.rs @@ -0,0 +1,257 @@ +use crate::client::{ + self, check_if_retry, handle_hash, handle_login_from_ui, handle_test_delay, input_os_password, + FileManager, LoginConfigHandler, QualityStatus, load_config, +}; +use crate::{client::Data, client::Interface}; +use async_trait::async_trait; +use hbb_common::config::PeerConfig; +use hbb_common::message_proto::{CursorData, Hash, PeerInfo, TestDelay, CursorPosition}; +use hbb_common::tokio::{ + self, + sync::mpsc, + time::{self, Duration, Instant, Interval}, +}; +use hbb_common::{get_version_number, log, Stream}; +use std::ops::{Deref, DerefMut}; +use std::sync::{Arc, RwLock}; + +#[derive(Clone, Default)] +pub struct Session { + pub cmd: String, + pub id: String, + pub password: String, + pub args: Vec, + pub lc: Arc>, + pub sender: Arc>>>, + pub ui_handler: T, +} + +impl Session { + pub fn get_option(&self, k: String) -> String { + self.lc.read().unwrap().get_option(&k) + } + + pub fn set_option(&self, k: String, v: String) { + self.lc.write().unwrap().set_option(k, v); + } + + #[inline] + pub fn load_config(&self) -> PeerConfig { + load_config(&self.id) + } + + #[inline] + pub(super) fn save_config(&self, config: PeerConfig) { + self.lc.write().unwrap().save_config(config); + } + + pub fn is_restarting_remote_device(&self) -> bool { + self.lc.read().unwrap().restarting_remote_device + } + + #[inline] + pub fn peer_platform(&self) -> String { + self.lc.read().unwrap().info.platform.clone() + } + + pub fn get_platform(&mut self, is_remote: bool) -> String { + if is_remote { + self.peer_platform() + } else { + whoami::platform().to_string() + } + } + + pub fn get_path_sep(&mut self, is_remote: bool) -> &'static str { + let p = self.get_platform(is_remote); + if &p == "Windows" { + return "\\"; + } else { + return "/"; + } + } +} + +pub trait InvokeUi: Send + Sync + Clone + 'static + Sized + Default { + fn set_cursor_data(&self, cd: CursorData); + fn set_cursor_id(&self, id: String); + fn set_cursor_position(&self, cp:CursorPosition); + fn set_display(&self, x: i32, y: i32, w: i32, h: i32); + fn update_privacy_mode(&self); + fn set_permission(&self, name: &str, value: bool); + fn update_pi(&self, pi: PeerInfo); + fn close_success(&self); + fn update_quality_status(&self, qs: QualityStatus); + fn set_connection_type(&self,is_secured: bool, direct: bool); + fn job_error(&self,id:i32, err:String, file_num:i32); + fn job_done(&self,id:i32, file_num:i32); + fn clear_all_jobs(&self); + fn add_job(&self, id:i32, path:String, to:String, file_num:i32, show_hidden:bool, is_remote:bool); + fn update_transfer_list(&self); + // fn update_folder_files(&self); // TODO + fn confirm_delete_files(&self,id:i32, i:i32, name:String); + fn override_file_confirm(&self, id:i32, file_num:i32, to:String, is_upload:bool); + fn job_progress(&self, id:i32, file_num:i32, speed:f64, finished_size:f64); + fn adapt_size(&self); +} + + +impl Deref for Session { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.ui_handler + } +} + +impl DerefMut for Session { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.ui_handler + } +} + +impl FileManager for Session {} + +#[async_trait] +impl Interface for Session { + fn send(&self, data: Data) { + if let Some(sender) = self.sender.read().unwrap().as_ref() { + sender.send(data).ok(); + } + } + + fn is_file_transfer(&self) -> bool { + self.cmd == "--file-transfer" + } + + fn is_port_forward(&self) -> bool { + self.cmd == "--port-forward" || self.is_rdp() + } + + fn is_rdp(&self) -> bool { + self.cmd == "--rdp" + } + + fn msgbox(&self, msgtype: &str, title: &str, text: &str) { + let retry = check_if_retry(msgtype, title, text); + // self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); + } + + fn handle_login_error(&mut self, err: &str) -> bool { + self.lc.write().unwrap().handle_login_error(err, self) + } + + fn handle_peer_info(&mut self, pi: PeerInfo) { + // let mut pi_sciter = Value::map(); + let username = self.lc.read().unwrap().get_username(&pi); + // pi_sciter.set_item("username", username.clone()); + // pi_sciter.set_item("hostname", pi.hostname.clone()); + // pi_sciter.set_item("platform", pi.platform.clone()); + // pi_sciter.set_item("sas_enabled", pi.sas_enabled); + if get_version_number(&pi.version) < get_version_number("1.1.10") { + self.set_permission("restart", false); + } + if self.is_file_transfer() { + if pi.username.is_empty() { + self.on_error("No active console user logged on, please connect and logon first."); + return; + } + } else if !self.is_port_forward() { + if pi.displays.is_empty() { + self.lc.write().unwrap().handle_peer_info(username, pi); + self.update_privacy_mode(); + self.msgbox("error", "Remote Error", "No Display"); + return; + } + // let mut displays = Value::array(0); + // for ref d in pi.displays.iter() { + // let mut display = Value::map(); + // display.set_item("x", d.x); + // display.set_item("y", d.y); + // display.set_item("width", d.width); + // display.set_item("height", d.height); + // displays.push(display); + // } + // pi_sciter.set_item("displays", displays); + let mut current = pi.current_display as usize; + if current >= pi.displays.len() { + current = 0; + } + // pi_sciter.set_item("current_display", current as i32); + let current = &pi.displays[current]; + self.set_display(current.x, current.y, current.width, current.height); + // https://sciter.com/forums/topic/color_spaceiyuv-crash + // Nothing spectacular in decoder – done on CPU side. + // So if you can do BGRA translation on your side – the better. + // BGRA is used as internal image format so it will not require additional transformations. + // VIDEO.lock().unwrap().as_mut().map(|v| { + // let ok = v.start_streaming( + // (current.width as _, current.height as _), + // COLOR_SPACE::Rgb32, + // None, + // ); + // log::info!("[video] initialized: {:?}", ok); + // }); + let p = self.lc.read().unwrap().should_auto_login(); + if !p.is_empty() { + input_os_password(p, true, self.clone()); + } + } + self.lc.write().unwrap().handle_peer_info(username, pi); + self.update_privacy_mode(); + // self.update_pi(pi); + if self.is_file_transfer() { + self.close_success(); + } else if !self.is_port_forward() { + self.msgbox("success", "Successful", "Connected, waiting for image..."); + } + #[cfg(windows)] + { + let mut path = std::env::temp_dir(); + path.push(&self.id); + let path = path.with_extension(crate::get_app_name().to_lowercase()); + std::fs::File::create(&path).ok(); + if let Some(path) = path.to_str() { + crate::platform::windows::add_recent_document(&path); + } + } + // self.start_keyboard_hook(); // TODO + } + + async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { + handle_hash(self.lc.clone(), pass, hash, self, peer).await; + } + + async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { + handle_login_from_ui(self.lc.clone(), password, remember, peer).await; + } + + async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { + if !t.from_client { + self.update_quality_status(QualityStatus { + delay: Some(t.last_delay as _), + target_bitrate: Some(t.target_bitrate as _), + ..Default::default() + }); + handle_test_delay(t, peer).await; + } + } + + fn set_force_relay(&mut self, direct: bool, received: bool) { + let mut lc = self.lc.write().unwrap(); + lc.force_relay = false; + if direct && !received { + let errno = errno::errno().0; + log::info!("errno is {}", errno); + // TODO: check mac and ios + if cfg!(windows) && errno == 10054 || !cfg!(windows) && errno == 104 { + lc.force_relay = true; + lc.set_option("force-always-relay".to_owned(), "Y".to_owned()); + } + } + } + + fn is_force_relay(&self) -> bool { + self.lc.read().unwrap().force_relay + } +} From e5c45542218daa8280e5054f317b3ab18093a7d1 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 31 Aug 2022 20:46:30 +0800 Subject: [PATCH 2/7] refactor remote, sciter / flutter run success --- src/flutter.rs | 3483 +++++++++++++++++++---------------- src/flutter_ffi.rs | 36 +- src/ui/remote.rs | 2027 +++----------------- src/ui_session_interface.rs | 1767 +++++++++++++++++- 4 files changed, 3888 insertions(+), 3425 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index 392f0f733..88c6f1961 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -28,7 +28,10 @@ use hbb_common::{ ResultType, Stream, }; -use crate::common::{self, make_fd_to_json, CLIPBOARD_INTERVAL}; +use crate::{ + common::{self, make_fd_to_json, CLIPBOARD_INTERVAL}, + ui_session_interface::{io_loop, InvokeUi, Session}, +}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{check_clipboard, update_clipboard, ClipboardContext}; @@ -42,210 +45,19 @@ pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; const MILLI1: Duration = Duration::from_millis(1); lazy_static::lazy_static! { - pub static ref SESSIONS: RwLock> = Default::default(); + pub static ref SESSIONS: RwLock>> = Default::default(); pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); - -#[derive(Clone)] -pub struct Session { - id: String, - sender: Arc>>>, // UI to rust - lc: Arc>, - events2ui: Arc>>>, +#[derive(Default, Clone)] +pub struct FlutterHandler { + pub event_stream: Arc>>>, } -impl Session { - /// Create a new remote session with the given id. - /// - /// # Arguments - /// - /// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ - /// * `is_file_transfer` - If the session is used for file transfer. - /// * `is_port_forward` - If the session is used for port forward. - pub fn add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> ResultType<()> { - // TODO check same id - let session_id = get_session_id(id.to_owned()); - LocalConfig::set_remote_id(&session_id); - // TODO close - // Self::close(); - let session = Session { - id: session_id.clone(), - sender: Default::default(), - lc: Default::default(), - events2ui: Arc::new(RwLock::new(None)), - }; - session.lc.write().unwrap().initialize( - session_id.clone(), - is_file_transfer, - is_port_forward, - ); - SESSIONS - .write() - .unwrap() - .insert(id.to_owned(), session.clone()); - Ok(()) - } - - /// Create a new remote session with the given id. - /// - /// # Arguments - /// - /// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ - /// * `events2ui` - The events channel to ui. - pub fn start(id: &str, events2ui: StreamSink) -> ResultType<()> { - if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { - *session.events2ui.write().unwrap() = Some(events2ui); - let session = session.clone(); - std::thread::spawn(move || { - let is_file_transfer = session.lc.read().unwrap().is_file_transfer; - let is_port_forward = session.lc.read().unwrap().is_port_forward; - Connection::start(session, is_file_transfer, is_port_forward); - }); - Ok(()) - } else { - bail!("No session with peer id {}", id) - } - } - - - /// 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(&self, name: &str) -> String { - if name == "remote_dir" { - return self.lc.read().unwrap().get_remote_dir(); - } - self.lc.read().unwrap().get_option(name) - } - - /// Set the option of the current session. - /// - /// # Arguments - /// - /// * `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(&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(&self, pass: String, activate: bool) { - input_os_password(pass, activate, self.clone()); - } - - pub fn restart_remote_device(&self) { - let mut lc = self.lc.write().unwrap(); - lc.restarting_remote_device = true; - let msg = lc.restart_remote_device(); - self.send_msg(msg); - } - - /// Toggle an option. - 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) { - self.send(Data::Message(LoginConfigHandler::refresh())); - } - - /// Get image quality. - pub fn get_image_quality(&self) -> String { - self.lc.read().unwrap().image_quality.clone() - } - - /// Set image quality. - 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); - } - } - - /// Get the status of a toggle option. - /// Return `None` if the option is not found. - /// - /// # Arguments - /// - /// * `name` - The name of the option to get. - pub fn get_toggle_option(&self, name: &str) -> bool { - self.lc.write().unwrap().get_toggle_option(name) - } - - /// Login. - /// - /// # Arguments - /// - /// * `password` - The password to login. - /// * `remember` - If the password should be remembered. - pub fn login(&self, password: &str, remember: bool) { - self.send(Data::Login((password.to_owned(), remember))); - } - - /// Close the session. - pub fn close(&self) { - self.send(Data::Close); - } - - /// Reconnect to the current session. - pub fn reconnect(&self) { - self.send(Data::Close); - let session = self.clone(); - std::thread::spawn(move || { - Connection::start(session, false, false); - }); - } - - /// Get `remember` flag in [`LoginConfigHandler`]. - pub fn get_remember(&self) -> bool { - self.lc.read().unwrap().remember - } - - /// Send message over the current session. - /// - /// # Arguments - /// - /// * `msg` - The message to send. - #[inline] - pub fn send_msg(&self, msg: Message) { - self.send(Data::Message(msg)); - } - - /// Send chat message over the current session. - /// - /// # Arguments - /// - /// * `text` - The message to send. - pub fn send_chat(&self, text: String) { - let mut misc = Misc::new(); - misc.set_chat_message(ChatMessage { - text, - ..Default::default() - }); - let mut msg_out = Message::new(); - msg_out.set_misc(misc); - self.send_msg(msg_out); - } - +impl FlutterHandler { /// Push an event to the event queue. /// An event is stored as json in the event queue. /// @@ -258,221 +70,60 @@ impl Session { assert!(h.get("name").is_none()); h.insert("name", name); let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned()); - if let Some(stream) = &*self.events2ui.read().unwrap() { + if let Some(stream) = &*self.event_stream.read().unwrap() { stream.add(EventToUI::Event(out)); } } +} - /// Get platform of peer. - #[inline] - fn peer_platform(&self) -> String { - self.lc.read().unwrap().info.platform.clone() +impl InvokeUi for FlutterHandler { + fn set_cursor_data(&self, cd: CursorData) { + let colors = hbb_common::compress::decompress(&cd.colors); + self.push_event( + "cursor_data", + vec![ + ("id", &cd.id.to_string()), + ("hotx", &cd.hotx.to_string()), + ("hoty", &cd.hoty.to_string()), + ("width", &cd.width.to_string()), + ("height", &cd.height.to_string()), + ( + "colors", + &serde_json::ser::to_string(&colors).unwrap_or("".to_owned()), + ), + ], + ); } - /// Quick method for sending a ctrl_alt_del command. - 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); - } + fn set_cursor_id(&self, id: String) { + self.push_event("cursor_id", vec![("id", &id.to_string())]); } - /// Switch the display. - /// - /// # Arguments - /// - /// * `display` - The display to switch to. - pub fn switch_display(&self, display: i32) { - let mut misc = Misc::new(); - misc.set_switch_display(SwitchDisplay { - display, - ..Default::default() - }); - let mut msg_out = Message::new(); - msg_out.set_misc(misc); - self.send_msg(msg_out); + fn set_cursor_position(&self, cp: CursorPosition) { + self.push_event( + "cursor_position", + vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())], + ); } - /// Send lock screen command. - pub fn lock_screen(&self) { - let k = Key::ControlKey(ControlKey::LockScreen); - self.key_down_or_up(1, k, false, false, false, false); + fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { + // todo!() } - /// Send key input command. - /// - /// # Arguments - /// - /// * `name` - The name of the key. - /// * `down` - Whether the key is down or up. - /// * `press` - If the key is simply being pressed(Down+Up). - /// * `alt` - If the alt key is also pressed. - /// * `ctrl` - If the ctrl key is also pressed. - /// * `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, - alt: bool, - ctrl: bool, - shift: bool, - command: bool, - ) { - 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); - } - } + fn update_privacy_mode(&self) { + self.push_event("update_privacy_mode", [].into()); } - /// Input a string of text. - /// String is parsed into individual key presses. - /// - /// # Arguments - /// - /// * `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(msg_out); + fn set_permission(&self, name: &str, value: bool) { + // todo!() } - fn _input_key( - &self, - key: Key, - down: bool, - press: bool, - alt: bool, - ctrl: bool, - shift: bool, - command: bool, - ) { - let v = if press { - 3 - } else if down { - 1 - } else { - 0 - }; - self.key_down_or_up(v, key, alt, ctrl, shift, command); + fn update_pi(&self, pi: PeerInfo) { + // todo!() } - pub fn send_mouse( - &self, - mask: i32, - x: i32, - y: i32, - alt: bool, - ctrl: bool, - shift: bool, - command: bool, - ) { - send_mouse(mask, x, y, alt, ctrl, shift, command, self); - } - - fn key_down_or_up( - &self, - down_or_up: i32, - key: Key, - alt: bool, - ctrl: bool, - shift: bool, - command: bool, - ) { - let mut down_or_up = down_or_up; - let mut key_event = KeyEvent::new(); - match key { - Key::Chr(chr) => { - key_event.set_chr(chr); - } - Key::ControlKey(key) => { - key_event.set_control_key(key.clone()); - } - Key::_Raw(raw) => { - if raw > 'z' as u32 || raw < 'a' as u32 { - key_event.set_unicode(raw); - if down_or_up == 0 { - // ignore up, avoiding trigger twice - return; - } - down_or_up = 1; // if press, turn into down for avoiding trigger twice on server side - } else { - // to make ctrl+c works on windows - key_event.set_chr(raw); - } - } - } - if alt { - key_event.modifiers.push(ControlKey::Alt.into()); - } - if shift { - key_event.modifiers.push(ControlKey::Shift.into()); - } - if ctrl { - key_event.modifiers.push(ControlKey::Control.into()); - } - if command { - key_event.modifiers.push(ControlKey::Meta.into()); - } - if down_or_up == 1 { - key_event.down = true; - } else if down_or_up == 3 { - key_event.press = true; - } - let mut msg_out = Message::new(); - msg_out.set_key_event(key_event); - log::debug!("{:?}", msg_out); - self.send_msg(msg_out); - } - - pub fn load_config(&self) -> PeerConfig { - load_config(&self.id) - } - - pub fn save_config(&self, config: &PeerConfig) { - config.store(&self.id); - } - - pub fn get_platform(&self, is_remote: bool) -> String { - if is_remote { - self.lc.read().unwrap().info.platform.clone() - } else { - whoami::platform().to_string() - } - } - - pub fn load_last_jobs(&self) { - let pc = self.load_config(); - if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { - // no last jobs - return; - } - let mut cnt = 1; - for job_str in pc.transfer.read_jobs.iter() { - if !job_str.is_empty() { - self.push_event("load_last_job", vec![("value", job_str)]); - cnt += 1; - println!("restore read_job: {:?}", job_str); - } - } - for job_str in pc.transfer.write_jobs.iter() { - if !job_str.is_empty() { - self.push_event("load_last_job", vec![("value", job_str)]); - cnt += 1; - println!("restore write_job: {:?}", job_str); - } - } + fn close_success(&self) { + // todo!() } fn update_quality_status(&self, status: QualityStatus) { @@ -495,67 +146,95 @@ impl Session { ); } - pub fn remove_port_forward(&mut self, port: i32) { - let mut config = self.load_config(); - config.port_forwards = config - .port_forwards - .drain(..) - .filter(|x| x.0 != port) - .collect(); - self.save_config(&config); - self.send(Data::RemovePortForward(port)); + fn set_connection_type(&self, is_secured: bool, direct: bool) { + self.push_event( + "connection_ready", + vec![ + ("secure", &is_secured.to_string()), + ("direct", &direct.to_string()), + ], + ); } - pub fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { - let mut config = self.load_config(); - if config - .port_forwards - .iter() - .filter(|x| x.0 == port) - .next() - .is_some() - { - return; - } - let pf = (port, remote_host, remote_port); - config.port_forwards.push(pf.clone()); - self.save_config(&config); - self.send(Data::AddPortForward(pf)); + fn job_error(&self, id: i32, err: String, file_num: i32) { + // todo!() } - fn on_error(&self, err: &str) { - self.msgbox("error", "Error", err); + fn job_done(&self, id: i32, file_num: i32) { + // todo!() } -} -impl FileManager for Session {} + fn clear_all_jobs(&self) { + // todo!() + } -#[async_trait] -impl Interface for Session { - fn send(&self, data: Data) { - if let Some(sender) = self.sender.read().unwrap().as_ref() { - sender.send(data).ok(); + fn add_job( + &self, + id: i32, + path: String, + to: String, + file_num: i32, + show_hidden: bool, + is_remote: bool, + ) { + // todo!() + } + + fn update_transfer_list(&self) { + // todo!() + } + + fn confirm_delete_files(&self, id: i32, i: i32, name: String) { + // todo!() + } + + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { + // todo!() + } + + fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) { + // todo!() + } + + fn adapt_size(&self) { + // todo!() + } + + fn on_rgba(&self, data: &[u8]) { + if let Some(stream) = &*self.event_stream.read().unwrap() { + stream.add(EventToUI::Rgba(ZeroCopyBuffer(data.to_owned()))); } } - fn is_file_transfer(&self) -> bool { - todo!() + fn set_peer_info( + &self, + username: &str, + hostname: &str, + platform: &str, + sas_enabled: bool, + displays: &Vec>, + version: &str, + current_display: usize, + is_file_transfer: bool, + ) { + let displays = serde_json::ser::to_string(displays).unwrap_or("".to_owned()); + self.push_event( + "peer_info", + vec![ + ("username", username), + ("hostname", hostname), + ("platform", platform), + ("sas_enabled", &sas_enabled.to_string()), + ("displays", &displays), + ("version", &version), + ("current_display", ¤t_display.to_string()), + ("is_file_transfer", &is_file_transfer.to_string()), + ], + ); } - fn is_port_forward(&self) -> bool { - todo!() - } - - fn is_rdp(&self) -> bool { - todo!() - } - - fn msgbox(&self, msgtype: &str, title: &str, text: &str) { - let has_retry = if check_if_retry(msgtype, title, text) { - "true" - } else { - "" - }; + fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool) { + let has_retry = if retry { "true" } else { "" }; self.push_event( "msgbox", vec![ @@ -566,1184 +245,1754 @@ impl Interface for Session { ], ); } +} - fn handle_login_error(&mut self, err: &str) -> bool { - self.lc.write().unwrap().handle_login_error(err, self) - } +/// Create a new remote session with the given id. +/// +/// # Arguments +/// +/// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ +/// * `is_file_transfer` - If the session is used for file transfer. +/// * `is_port_forward` - If the session is used for port forward. +pub fn session_add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> ResultType<()> { + // TODO check same id + let session_id = get_session_id(id.to_owned()); + LocalConfig::set_remote_id(&session_id); + // TODO close + // Self::close(); - fn handle_peer_info(&mut self, pi: PeerInfo) { - let mut lc = self.lc.write().unwrap(); - let username = lc.get_username(&pi); - let mut displays = Vec::new(); - let mut current = pi.current_display as usize; + // TODO cmd passwd args + let session: Session = Session { + id: session_id.clone(), + ..Default::default() + }; - if lc.is_file_transfer { - if pi.username.is_empty() { - self.msgbox( - "error", - "Error", - "No active console user logged on, please connect and logon first.", - ); - return; - } - } else { - if pi.displays.is_empty() { - self.msgbox("error", "Remote Error", "No Display"); - } - for ref d in pi.displays.iter() { - let mut h: HashMap<&str, i32> = Default::default(); - h.insert("x", d.x); - h.insert("y", d.y); - h.insert("width", d.width); - h.insert("height", d.height); - displays.push(h); - } - if current >= pi.displays.len() { - current = 0; - } - } - let displays = serde_json::ser::to_string(&displays).unwrap_or("".to_owned()); - self.push_event( - "peer_info", - vec![ - ("username", &username), - ("hostname", &pi.hostname), - ("platform", &pi.platform), - ("sas_enabled", &pi.sas_enabled.to_string()), - ("displays", &displays), - ("version", &pi.version), - ("current_display", ¤t.to_string()), - ("is_file_transfer", &lc.is_file_transfer.to_string()), - ], - ); - lc.handle_peer_info(username, pi); - let p = lc.should_auto_login(); - if !p.is_empty() { - input_os_password(p, true, self.clone()); - } - } + session + .lc + .write() + .unwrap() + .initialize(session_id.clone(), is_file_transfer, is_port_forward); + SESSIONS + .write() + .unwrap() + .insert(id.to_owned(), session.clone()); + Ok(()) +} - fn set_force_relay(&mut self, direct: bool, received: bool) { - let mut lc = self.lc.write().unwrap(); - lc.force_relay = false; - if direct && !received { - let errno = errno::errno().0; - log::info!("errno is {}", errno); - // TODO: check mac and ios - if cfg!(windows) && errno == 10054 || !cfg!(windows) && errno == 104 { - lc.force_relay = true; - lc.set_option("force-always-relay".to_owned(), "Y".to_owned()); - } - } - } - - fn is_force_relay(&self) -> bool { - self.lc.read().unwrap().force_relay - } - - async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { - handle_hash(self.lc.clone(), pass, hash, self, peer).await; - } - - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { - handle_login_from_ui(self.lc.clone(), password, remember, peer).await; - } - - async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { - if !t.from_client { - self.update_quality_status(QualityStatus { - delay: Some(t.last_delay as _), - target_bitrate: Some(t.target_bitrate as _), - ..Default::default() - }); - handle_test_delay(t, peer).await; - } +/// start a session with the given id. +/// +/// # Arguments +/// +/// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ +/// * `events2ui` - The events channel to ui. +pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultType<()> { + if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { + *session.event_stream.write().unwrap() = Some(event_stream); + let session = session.clone(); + std::thread::spawn(move || { + // let is_file_transfer = session.lc.read().unwrap().is_file_transfer; + // let is_port_forward = session.lc.read().unwrap().is_port_forward; + // Connection::start(session, is_file_transfer, is_port_forward); + io_loop(session); + }); + Ok(()) + } else { + bail!("No session with peer id {}", id) } } +// #[derive(Clone)] +// pub struct Session { +// id: String, +// sender: Arc>>>, // UI to rust +// lc: Arc>, +// events2ui: Arc>>>, +// } -struct Connection { - video_handler: VideoHandler, - audio_handler: AudioHandler, - session: Session, - first_frame: bool, - read_jobs: Vec, - write_jobs: Vec, - timer: Interval, - last_update_jobs_status: (Instant, HashMap), - data_count: Arc, - frame_count: Arc, - video_format: CodecFormat, -} +// impl Session1 { +// /// Create a new remote session with the given id. +// /// +// /// # Arguments +// /// +// /// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ +// /// * `is_file_transfer` - If the session is used for file transfer. +// /// * `is_port_forward` - If the session is used for port forward. +// pub fn add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> ResultType<()> { +// // TODO check same id +// let session_id = get_session_id(id.to_owned()); +// LocalConfig::set_remote_id(&session_id); +// // TODO close +// // Self::close(); +// let session = Session { +// id: session_id.clone(), +// sender: Default::default(), +// lc: Default::default(), +// events2ui: Arc::new(RwLock::new(None)), +// }; +// session.lc.write().unwrap().initialize( +// session_id.clone(), +// is_file_transfer, +// is_port_forward, +// ); +// SESSIONS +// .write() +// .unwrap() +// .insert(id.to_owned(), session.clone()); +// Ok(()) +// } -impl Connection { - // TODO: Similar to remote::start_clipboard - // merge the code - fn start_clipboard( - tx_protobuf: mpsc::UnboundedSender, - lc: Arc>, - ) -> Option> { - let (tx, rx) = std::sync::mpsc::channel(); - #[cfg(not(any(target_os = "android", target_os = "ios")))] - match ClipboardContext::new() { - Ok(mut ctx) => { - let old_clipboard: Arc> = Default::default(); - // ignore clipboard update before service start - check_clipboard(&mut ctx, Some(&old_clipboard)); - std::thread::spawn(move || loop { - std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); - match rx.try_recv() { - Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { - log::debug!("Exit clipboard service of client"); - break; - } - _ => {} - } - if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) - || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - || lc.read().unwrap().disable_clipboard - { - continue; - } - if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { - tx_protobuf.send(Data::Message(msg)).ok(); - } - }); - } - Err(err) => { - log::error!("Failed to start clipboard service of client: {}", err); - } - } - Some(tx) - } +// /// Create a new remote session with the given id. +// /// +// /// # Arguments +// /// +// /// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ +// /// * `events2ui` - The events channel to ui. +// pub fn start(id: &str, events2ui: StreamSink) -> ResultType<()> { +// if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { +// *session.events2ui.write().unwrap() = Some(events2ui); +// let session = session.clone(); +// std::thread::spawn(move || { +// let is_file_transfer = session.lc.read().unwrap().is_file_transfer; +// let is_port_forward = session.lc.read().unwrap().is_port_forward; +// Connection::start(session, is_file_transfer, is_port_forward); +// }); +// Ok(()) +// } else { +// bail!("No session with peer id {}", id) +// } +// } - /// Create a new connection. - /// - /// # Arguments - /// - /// * `session` - The session to create a new connection for. - /// * `is_file_transfer` - Whether the connection is for file transfer. - /// * `is_port_forward` - Whether the connection is for port forward. - #[tokio::main(flavor = "current_thread")] - async fn start(session: Session, is_file_transfer: bool, is_port_forward: bool) { - let mut last_recv_time = Instant::now(); - let (sender, mut receiver) = mpsc::unbounded_channel::(); - let mut stop_clipboard = None; - if !is_file_transfer && !is_port_forward { - stop_clipboard = Self::start_clipboard(sender.clone(), session.lc.clone()); - } - *session.sender.write().unwrap() = Some(sender.clone()); - let conn_type = if is_file_transfer { - session.lc.write().unwrap().is_file_transfer = true; - ConnType::FILE_TRANSFER - } else if is_port_forward { - ConnType::PORT_FORWARD // TODO: RDP - } else { - ConnType::DEFAULT_CONN - }; - let key = Config::get_option("key"); - let token = Config::get_option("access_token"); +// /// 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(&self, name: &str) -> String { +// if name == "remote_dir" { +// return self.lc.read().unwrap().get_remote_dir(); +// } +// self.lc.read().unwrap().get_option(name) +// } - // TODO rdp & cli args - let is_rdp = false; - let args: Vec = Vec::new(); +// /// Set the option of the current session. +// /// +// /// # Arguments +// /// +// /// * `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(&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); +// } - if is_port_forward { - if is_rdp { - // let port = handler - // .get_option("rdp_port".to_owned()) - // .parse::() - // .unwrap_or(3389); - // std::env::set_var( - // "rdp_username", - // handler.get_option("rdp_username".to_owned()), - // ); - // std::env::set_var( - // "rdp_password", - // handler.get_option("rdp_password".to_owned()), - // ); - // log::info!("Remote rdp port: {}", port); - // start_one_port_forward(handler, 0, "".to_owned(), port, receiver, &key, &token).await; - } else if args.len() == 0 { - let pfs = session.lc.read().unwrap().port_forwards.clone(); - let mut queues = HashMap::>::new(); - for d in pfs { - sender.send(Data::AddPortForward(d)).ok(); - } - loop { - match receiver.recv().await { - Some(Data::AddPortForward((port, remote_host, remote_port))) => { - if port <= 0 || remote_port <= 0 { - continue; - } - let (sender, receiver) = mpsc::unbounded_channel::(); - queues.insert(port, sender); - let handler = session.clone(); - let key = key.clone(); - let token = token.clone(); - tokio::spawn(async move { - start_one_port_forward( - handler, - port, - remote_host, - remote_port, - receiver, - &key, - &token, - ) - .await; - }); - } - Some(Data::RemovePortForward(port)) => { - if let Some(s) = queues.remove(&port) { - s.send(Data::Close).ok(); - } - } - Some(Data::Close) => { - break; - } - Some(d) => { - for (_, s) in queues.iter() { - s.send(d.clone()).ok(); - } - } - _ => {} - } - } - } else { - // let port = handler.args[0].parse::().unwrap_or(0); - // if handler.args.len() != 3 - // || handler.args[2].parse::().unwrap_or(0) <= 0 - // || port <= 0 - // { - // handler.on_error("Invalid arguments, usage:

rustdesk --port-forward remote-id listen-port remote-host remote-port"); - // } - // let remote_host = handler.args[1].clone(); - // let remote_port = handler.args[2].parse::().unwrap_or(0); - // start_one_port_forward( - // handler, - // port, - // remote_host, - // remote_port, - // receiver, - // &key, - // &token, - // ) - // .await; - } - return; - } +// /// Input the OS password. +// pub fn input_os_password(&self, pass: String, activate: bool) { +// input_os_password(pass, activate, self.clone()); +// } - let latency_controller = LatencyController::new(); - let latency_controller_cl = latency_controller.clone(); +// pub fn restart_remote_device(&self) { +// let mut lc = self.lc.write().unwrap(); +// lc.restarting_remote_device = true; +// let msg = lc.restart_remote_device(); +// self.send_msg(msg); +// } - let mut conn = Connection { - video_handler: VideoHandler::new(latency_controller), - audio_handler: AudioHandler::new(latency_controller_cl), - session: session.clone(), - first_frame: false, - read_jobs: Vec::new(), - write_jobs: Vec::new(), - timer: time::interval(SEC30), - last_update_jobs_status: (Instant::now(), Default::default()), - data_count: Arc::new(AtomicUsize::new(0)), - frame_count: Arc::new(AtomicUsize::new(0)), - video_format: CodecFormat::Unknown, - }; +// /// Toggle an option. +// 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); +// } +// } - match Client::start(&session.id, &key, &token, conn_type, session.clone()).await { - Ok((mut peer, direct)) => { - SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); +// /// Send a refresh command. +// pub fn refresh(&self) { +// self.send(Data::Message(LoginConfigHandler::refresh())); +// } - session.push_event( - "connection_ready", - vec![ - ("secure", &peer.is_secured().to_string()), - ("direct", &direct.to_string()), - ], - ); +// /// Get image quality. +// pub fn get_image_quality(&self) -> String { +// self.lc.read().unwrap().image_quality.clone() +// } - let mut status_timer = time::interval(Duration::new(1, 0)); +// /// Set image quality. +// 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); +// } +// } - loop { - tokio::select! { - res = peer.next() => { - if let Some(res) = res { - match res { - Err(err) => { - log::error!("Connection closed: {}", err); - session.msgbox("error", "Connection Error", &err.to_string()); - break; - } - Ok(ref bytes) => { - last_recv_time = Instant::now(); - conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed); - if !conn.handle_msg_from_peer(bytes, &mut peer).await { - break - } - } - } - } else { - if session.lc.read().unwrap().restarting_remote_device { - log::info!("Restart remote device"); - session.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); - } else { - log::info!("Reset by the peer"); - session.msgbox("error", "Connection Error", "Reset by the peer"); - } - break; - } - } - d = receiver.recv() => { - if let Some(d) = d { - if !conn.handle_msg_from_ui(d, &mut peer).await { - break; - } - } - } - _ = conn.timer.tick() => { - if last_recv_time.elapsed() >= SEC30 { - session.msgbox("error", "Connection Error", "Timeout"); - break; - } - if !conn.read_jobs.is_empty() { - if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut peer).await { - log::debug!("Connection Error: {}", err); - break; - } - conn.update_jobs_status(); - } else { - conn.timer = time::interval_at(Instant::now() + SEC30, SEC30); - } - } - _ = status_timer.tick() => { - let speed = conn.data_count.swap(0, Ordering::Relaxed); - let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); - let fps = conn.frame_count.swap(0, Ordering::Relaxed) as _; - conn.session.update_quality_status(QualityStatus { - speed:Some(speed), - fps:Some(fps), - ..Default::default() - }); - } - } - } - log::debug!("Exit io_loop of id={}", session.id); - } - Err(err) => { - session.msgbox("error", "Connection Error", &err.to_string()); - } - } +// /// Get the status of a toggle option. +// /// Return `None` if the option is not found. +// /// +// /// # Arguments +// /// +// /// * `name` - The name of the option to get. +// pub fn get_toggle_option(&self, name: &str) -> bool { +// self.lc.write().unwrap().get_toggle_option(name) +// } - if let Some(stop) = stop_clipboard { - stop.send(()).ok(); - } - SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); - } +// /// Login. +// /// +// /// # Arguments +// /// +// /// * `password` - The password to login. +// /// * `remember` - If the password should be remembered. +// pub fn login(&self, password: &str, remember: bool) { +// self.send(Data::Login((password.to_owned(), remember))); +// } - /// Handle message from peer. - /// Return false if the connection should be closed. - /// - /// The message is handled by [`Message`], see [`message::Union`] for possible types. - async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { - if let Ok(msg_in) = Message::parse_from_bytes(&data) { - match msg_in.union { - Some(message::Union::VideoFrame(vf)) => { - if !self.first_frame { - self.first_frame = true; - common::send_opts_after_login(&self.session.lc.read().unwrap(), peer).await; - } - let incomming_format = CodecFormat::from(&vf); - if self.video_format != incomming_format { - self.video_format = incomming_format.clone(); - self.session.update_quality_status(QualityStatus { - codec_format: Some(incomming_format), - ..Default::default() - }) - }; - if let Ok(true) = self.video_handler.handle_frame(vf) { - if let Some(stream) = &*self.session.events2ui.read().unwrap() { - self.frame_count.fetch_add(1, Ordering::Relaxed); - stream.add(EventToUI::Rgba(ZeroCopyBuffer( - self.video_handler.rgb.clone(), - ))); - } - } - } - Some(message::Union::Hash(hash)) => { - self.session.handle_hash("", hash, peer).await; - } - Some(message::Union::LoginResponse(lr)) => match lr.union { - Some(login_response::Union::Error(err)) => { - if !self.session.handle_login_error(&err) { - return false; - } - } - Some(login_response::Union::PeerInfo(pi)) => { - self.session.handle_peer_info(pi); - } - _ => {} - }, - Some(message::Union::Clipboard(cb)) => { - if !self.session.lc.read().unwrap().disable_clipboard { - let content = if cb.compress { - decompress(&cb.content) - } else { - cb.content.into() - }; - if let Ok(content) = String::from_utf8(content) { - self.session - .push_event("clipboard", vec![("content", &content)]); - } - } - } - Some(message::Union::CursorData(cd)) => { - let colors = hbb_common::compress::decompress(&cd.colors); - self.session.push_event( - "cursor_data", - vec![ - ("id", &cd.id.to_string()), - ("hotx", &cd.hotx.to_string()), - ("hoty", &cd.hoty.to_string()), - ("width", &cd.width.to_string()), - ("height", &cd.height.to_string()), - ( - "colors", - &serde_json::ser::to_string(&colors).unwrap_or("".to_owned()), - ), - ], - ); - } - Some(message::Union::CursorId(id)) => { - self.session - .push_event("cursor_id", vec![("id", &id.to_string())]); - } - Some(message::Union::CursorPosition(cp)) => { - self.session.push_event( - "cursor_position", - vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())], - ); - } - Some(message::Union::FileResponse(fr)) => { - match fr.union { - Some(file_response::Union::Dir(fd)) => { - let mut entries = fd.entries.to_vec(); - if self.session.peer_platform() == "Windows" { - transform_windows_path(&mut entries); - } - let id = fd.id; - self.session.push_event( - "file_dir", - vec![("value", &make_fd_to_json(fd)), ("is_local", "false")], - ); - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - job.set_files(entries); - } - } - Some(file_response::Union::Block(block)) => { - if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { - if let Err(_err) = job.write(block, None).await { - // to-do: add "skip" for writing job - } - self.update_jobs_status(); - } - } - Some(file_response::Union::Done(d)) => { - if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { - job.modify_time(); - fs::remove_job(d.id, &mut self.write_jobs); - } - self.handle_job_status(d.id, d.file_num, None); - } - Some(file_response::Union::Error(e)) => { - self.handle_job_status(e.id, e.file_num, Some(e.error)); - } - Some(file_response::Union::Digest(digest)) => { - if digest.is_upload { - if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let read_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - if let Some(overwrite) = overwrite_strategy { - let req = FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip( - true, - ) - }), - ..Default::default() - }; - job.confirm(&req); - let msg = new_send_confirm(req); - allow_err!(peer.send(&msg).await); - } else { - self.handle_override_file_confirm( - digest.id, - digest.file_num, - read_path, - true, - ); - } - } - } - } else { - if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let write_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - match fs::is_write_need_confirmation(&write_path, &digest) { - Ok(res) => match res { - DigestCheckResult::IsSame => { - let msg= new_send_confirm(FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::Skip(true)), - ..Default::default() - }); - self.session.send_msg(msg); - } - DigestCheckResult::NeedConfirm(digest) => { - if let Some(overwrite) = overwrite_strategy { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip(true) - }), - ..Default::default() - }, - ); - self.session.send_msg(msg); - } else { - self.handle_override_file_confirm( - digest.id, - digest.file_num, - write_path.to_string(), - false, - ); - } - } - DigestCheckResult::NoSuchFile => { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), - ..Default::default() - }, - ); - self.session.send_msg(msg); - } - }, - Err(err) => { - println!("error recving digest: {}", err); - } - } - } - } - } - } - _ => {} - } - } - Some(message::Union::Misc(misc)) => match misc.union { - Some(misc::Union::AudioFormat(f)) => { - self.audio_handler.handle_format(f); // - } - Some(misc::Union::ChatMessage(c)) => { - self.session - .push_event("chat_client_mode", vec![("text", &c.text)]); - } - Some(misc::Union::PermissionInfo(p)) => { - log::info!("Change permission {:?} -> {}", p.permission, p.enabled); - use permission_info::Permission; - self.session.push_event( - "permission", - vec![( - match p.permission.enum_value_or_default() { - Permission::Keyboard => "keyboard", - Permission::Clipboard => "clipboard", - Permission::Audio => "audio", - Permission::Restart => "restart", - _ => "", - }, - &p.enabled.to_string(), - )], - ); - } - Some(misc::Union::SwitchDisplay(s)) => { - self.video_handler.reset(); - self.session.push_event( - "switch_display", - vec![ - ("display", &s.display.to_string()), - ("x", &s.x.to_string()), - ("y", &s.y.to_string()), - ("width", &s.width.to_string()), - ("height", &s.height.to_string()), - ], - ); - } - Some(misc::Union::CloseReason(c)) => { - self.session.msgbox("error", "Connection Error", &c); - return false; - } - Some(misc::Union::BackNotification(notification)) => { - if !self.handle_back_notification(notification).await { - return false; - } - } - _ => {} - }, - Some(message::Union::TestDelay(t)) => { - self.session.handle_test_delay(t, peer).await; - } - Some(message::Union::AudioFrame(frame)) => { - if !self.session.lc.read().unwrap().disable_audio { - self.audio_handler.handle_frame(frame); - } - } - Some(message::Union::FileAction(action)) => match action.union { - Some(file_action::Union::SendConfirm(c)) => { - if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { - job.confirm(&c); - } - } - _ => {} - }, - _ => {} - } - } - true - } +// /// Close the session. +// pub fn close(&self) { +// self.send(Data::Close); +// } - async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { - match notification.union { - Some(back_notification::Union::BlockInputState(state)) => { - self.handle_back_msg_block_input( - state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), - ) - .await; - } - Some(back_notification::Union::PrivacyModeState(state)) => { - if !self - .handle_back_msg_privacy_mode( - state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), - ) - .await - { - return false; - } - } - _ => {} - } - true - } +// /// Reconnect to the current session. +// pub fn reconnect(&self) { +// self.send(Data::Close); +// let session = self.clone(); +// std::thread::spawn(move || { +// Connection::start(session, false, false); +// }); +// } - #[inline(always)] - fn update_block_input_state(&mut self, on: bool) { - self.session.push_event( - "update_block_input_state", - [("input_state", if on { "on" } else { "off" })].into(), - ); - } +// /// Get `remember` flag in [`LoginConfigHandler`]. +// pub fn get_remember(&self) -> bool { +// self.lc.read().unwrap().remember +// } - async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { - match state { - back_notification::BlockInputState::BlkOnSucceeded => { - self.update_block_input_state(true); - } - back_notification::BlockInputState::BlkOnFailed => { - self.session - .msgbox("custom-error", "Block user input", "Failed"); - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffSucceeded => { - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffFailed => { - self.session - .msgbox("custom-error", "Unblock user input", "Failed"); - } - _ => {} - } - } +// /// Send message over the current session. +// /// +// /// # Arguments +// /// +// /// * `msg` - The message to send. +// #[inline] +// pub fn send_msg(&self, msg: Message) { +// self.send(Data::Message(msg)); +// } - #[inline(always)] - fn update_privacy_mode(&mut self, on: bool) { - let mut config = self.session.load_config(); - config.privacy_mode = on; - self.session.save_config(&config); - self.session.lc.write().unwrap().get_config().privacy_mode = on; - self.session.push_event("update_privacy_mode", [].into()); - } +// /// Send chat message over the current session. +// /// +// /// # Arguments +// /// +// /// * `text` - The message to send. +// pub fn send_chat(&self, text: String) { +// let mut misc = Misc::new(); +// misc.set_chat_message(ChatMessage { +// text, +// ..Default::default() +// }); +// let mut msg_out = Message::new(); +// msg_out.set_misc(misc); +// self.send_msg(msg_out); +// } - async fn handle_back_msg_privacy_mode( - &mut self, - state: back_notification::PrivacyModeState, - ) -> bool { - match state { - back_notification::PrivacyModeState::PrvOnByOther => { - self.session.msgbox( - "error", - "Connecting...", - "Someone turns on privacy mode, exit", - ); - return false; - } - back_notification::PrivacyModeState::PrvNotSupported => { - self.session - .msgbox("custom-error", "Privacy mode", "Unsupported"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnSucceeded => { - self.session - .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); - self.update_privacy_mode(true); - } - back_notification::PrivacyModeState::PrvOnFailedDenied => { - self.session - .msgbox("custom-error", "Privacy mode", "Peer denied"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailedPlugin => { - self.session - .msgbox("custom-error", "Privacy mode", "Please install plugins"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailed => { - self.session - .msgbox("custom-error", "Privacy mode", "Failed"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffSucceeded => { - self.session - .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffByPeer => { - self.session - .msgbox("custom-error", "Privacy mode", "Peer exit"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffFailed => { - self.session - .msgbox("custom-error", "Privacy mode", "Failed to turn off"); - } - back_notification::PrivacyModeState::PrvOffUnknown => { - self.session - .msgbox("custom-error", "Privacy mode", "Turned off"); - // log::error!("Privacy mode is turned off with unknown reason"); - self.update_privacy_mode(false); - } - _ => {} - } - true - } +// /// Push an event to the event queue. +// /// An event is stored as json in the event queue. +// /// +// /// # Arguments +// /// +// /// * `name` - The name of the event. +// /// * `event` - Fields of the event content. +// fn push_event(&self, name: &str, event: Vec<(&str, &str)>) { +// let mut h: HashMap<&str, &str> = event.iter().cloned().collect(); +// assert!(h.get("name").is_none()); +// h.insert("name", name); +// let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned()); +// if let Some(stream) = &*self.events2ui.read().unwrap() { +// stream.add(EventToUI::Event(out)); +// } +// } - async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { - match data { - Data::Close => { - self.sync_jobs_status_to_local().await; - return false; - } - Data::Login((password, remember)) => { - self.session - .handle_login_from_ui(password, remember, peer) - .await; - } - Data::Message(msg) => { - allow_err!(peer.send(&msg).await); - } - Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { - let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); - if is_remote { - log::debug!("New job {}, write to {} from remote {}", id, to, path); - self.write_jobs.push(fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - )); - allow_err!( - peer.send(&fs::new_send(id, path, file_num, include_hidden)) - .await - ); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(job) => { - log::debug!( - "New job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - let m = make_fd_flutter(id, job.files(), true); - self.session - .push_event("update_folder_files", vec![("info", &m)]); - let files = job.files().clone(); - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); - } - } - } - } - Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_all_files(ReadAllFiles { - id, - path: path.clone(), - include_hidden, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::get_recursive_files(&path, include_hidden) { - Ok(entries) => { - let mut fd = FileDirectory::new(); - fd.id = id; - fd.path = path; - fd.entries = entries; - self.session.push_event( - "file_dir", - vec![("value", &make_fd_to_json(fd)), ("is_local", "true")], - ); - } - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - } - } - } - Data::CancelJob(id) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_cancel(FileTransferCancel { - id: id, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - job.remove_download_file(); - fs::remove_job(id, &mut self.write_jobs); - } - fs::remove_job(id, &mut self.read_jobs); - } - Data::RemoveDir((id, path)) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_dir(FileRemoveDir { - id, - path, - recursive: true, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } - Data::RemoveFile((id, path, file_num, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_file(FileRemoveFile { - id, - path, - file_num, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::remove_file(&path) { - Err(err) => { - self.handle_job_status(id, file_num, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, file_num, None); - } - } - } - } - Data::CreateDir((id, path, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_create(FileDirCreate { - id, - path, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::create_dir(&path) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, -1, None); - } - } - } - } - Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { - if is_upload { - if let Some(job) = fs::get_job(id, &mut self.read_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - job.confirm(&FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - } - } else { - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - let mut msg = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_send_confirm(FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - msg.set_file_action(file_action); - self.session.send_msg(msg); - } - } - } - Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { - let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); - if is_remote { - log::debug!( - "new write waiting job {}, write to {} from remote {}", - id, - to, - path - ); - let mut job = fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - ); - job.is_last_job = true; - self.write_jobs.push(job); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(mut job) => { - log::debug!( - "new read waiting job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - let m = make_fd_flutter(job.id(), job.files(), true); - self.session - .push_event("update_folder_files", vec![("info", &m)]); - job.is_last_job = true; - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - } - } - } - } - Data::ResumeJob((id, is_remote)) => { - if is_remote { - if let Some(job) = get_job(id, &mut self.write_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_send( - id, - job.remote.clone(), - job.file_num, - job.show_hidden - )) - .await - ); - } - } else { - if let Some(job) = get_job(id, &mut self.read_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_receive( - id, - job.path.to_string_lossy().to_string(), - job.file_num, - job.files.clone() - )) - .await - ); - } - } - } - _ => {} - } - true - } +// /// Get platform of peer. +// #[inline] +// fn peer_platform(&self) -> String { +// self.lc.read().unwrap().info.platform.clone() +// } - #[inline] - fn update_job_status( - job: &fs::TransferJob, - elapsed: i32, - last_update_jobs_status: &mut (Instant, HashMap), - session: &Session, - ) { - if elapsed <= 0 { - return; - } - let transferred = job.transferred(); - let last_transferred = { - if let Some(v) = last_update_jobs_status.1.get(&job.id()) { - v.to_owned() - } else { - 0 - } - }; - last_update_jobs_status.1.insert(job.id(), transferred); - let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); - let file_num = job.file_num() - 1; - session.push_event( - "job_progress", - vec![ - ("id", &job.id().to_string()), - ("file_num", &file_num.to_string()), - ("speed", &speed.to_string()), - ("finished_size", &job.finished_size().to_string()), - ], - ); - } +// /// Quick method for sending a ctrl_alt_del command. +// 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); +// } +// } - fn update_jobs_status(&mut self) { - let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; - if elapsed >= 1000 { - for job in self.read_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &self.session, - ); - } - for job in self.write_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &self.session, - ); - } - self.last_update_jobs_status.0 = Instant::now(); - } - } +// /// Switch the display. +// /// +// /// # Arguments +// /// +// /// * `display` - The display to switch to. +// pub fn switch_display(&self, display: i32) { +// let mut misc = Misc::new(); +// misc.set_switch_display(SwitchDisplay { +// display, +// ..Default::default() +// }); +// let mut msg_out = Message::new(); +// msg_out.set_misc(misc); +// self.send_msg(msg_out); +// } - fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { - if let Some(err) = err { - self.session - .push_event("job_error", vec![("id", &id.to_string()), ("err", &err)]); - } else { - self.session.push_event( - "job_done", - vec![("id", &id.to_string()), ("file_num", &file_num.to_string())], - ); - } - } +// /// Send lock screen command. +// pub fn lock_screen(&self) { +// let k = Key::ControlKey(ControlKey::LockScreen); +// self.key_down_or_up(1, k, false, false, false, false); +// } - fn handle_override_file_confirm( - &mut self, - id: i32, - file_num: i32, - read_path: String, - is_upload: bool, - ) { - self.session.push_event( - "override_file_confirm", - vec![ - ("id", &id.to_string()), - ("file_num", &file_num.to_string()), - ("read_path", &read_path), - ("is_upload", &is_upload.to_string()), - ], - ); - } +// /// Send key input command. +// /// +// /// # Arguments +// /// +// /// * `name` - The name of the key. +// /// * `down` - Whether the key is down or up. +// /// * `press` - If the key is simply being pressed(Down+Up). +// /// * `alt` - If the alt key is also pressed. +// /// * `ctrl` - If the ctrl key is also pressed. +// /// * `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, +// alt: bool, +// ctrl: bool, +// shift: bool, +// command: bool, +// ) { +// 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); +// } +// } +// } - async fn sync_jobs_status_to_local(&mut self) -> bool { - log::info!("sync transfer job status"); - let mut config: PeerConfig = self.session.load_config(); - let mut transfer_metas = TransferSerde::default(); - for job in self.read_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); - transfer_metas.read_jobs.push(json_str); - } - for job in self.write_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); - transfer_metas.write_jobs.push(json_str); - } - log::info!("meta: {:?}", transfer_metas); - config.transfer = transfer_metas; - self.session.save_config(&config); - true - } -} +// /// Input a string of text. +// /// String is parsed into individual key presses. +// /// +// /// # Arguments +// /// +// /// * `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(msg_out); +// } + +// fn _input_key( +// &self, +// key: Key, +// down: bool, +// press: bool, +// alt: bool, +// ctrl: bool, +// shift: bool, +// command: bool, +// ) { +// let v = if press { +// 3 +// } else if down { +// 1 +// } else { +// 0 +// }; +// self.key_down_or_up(v, key, alt, ctrl, shift, command); +// } + +// pub fn send_mouse( +// &self, +// mask: i32, +// x: i32, +// y: i32, +// alt: bool, +// ctrl: bool, +// shift: bool, +// command: bool, +// ) { +// send_mouse(mask, x, y, alt, ctrl, shift, command, self); +// } + +// fn key_down_or_up( +// &self, +// down_or_up: i32, +// key: Key, +// alt: bool, +// ctrl: bool, +// shift: bool, +// command: bool, +// ) { +// let mut down_or_up = down_or_up; +// let mut key_event = KeyEvent::new(); +// match key { +// Key::Chr(chr) => { +// key_event.set_chr(chr); +// } +// Key::ControlKey(key) => { +// key_event.set_control_key(key.clone()); +// } +// Key::_Raw(raw) => { +// if raw > 'z' as u32 || raw < 'a' as u32 { +// key_event.set_unicode(raw); +// if down_or_up == 0 { +// // ignore up, avoiding trigger twice +// return; +// } +// down_or_up = 1; // if press, turn into down for avoiding trigger twice on server side +// } else { +// // to make ctrl+c works on windows +// key_event.set_chr(raw); +// } +// } +// } +// if alt { +// key_event.modifiers.push(ControlKey::Alt.into()); +// } +// if shift { +// key_event.modifiers.push(ControlKey::Shift.into()); +// } +// if ctrl { +// key_event.modifiers.push(ControlKey::Control.into()); +// } +// if command { +// key_event.modifiers.push(ControlKey::Meta.into()); +// } +// if down_or_up == 1 { +// key_event.down = true; +// } else if down_or_up == 3 { +// key_event.press = true; +// } +// let mut msg_out = Message::new(); +// msg_out.set_key_event(key_event); +// log::debug!("{:?}", msg_out); +// self.send_msg(msg_out); +// } + +// pub fn load_config(&self) -> PeerConfig { +// load_config(&self.id) +// } + +// pub fn save_config(&self, config: &PeerConfig) { +// config.store(&self.id); +// } + +// pub fn get_platform(&self, is_remote: bool) -> String { +// if is_remote { +// self.lc.read().unwrap().info.platform.clone() +// } else { +// whoami::platform().to_string() +// } +// } + +// pub fn load_last_jobs(&self) { +// let pc = self.load_config(); +// if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { +// // no last jobs +// return; +// } +// let mut cnt = 1; +// for job_str in pc.transfer.read_jobs.iter() { +// if !job_str.is_empty() { +// self.push_event("load_last_job", vec![("value", job_str)]); +// cnt += 1; +// println!("restore read_job: {:?}", job_str); +// } +// } +// for job_str in pc.transfer.write_jobs.iter() { +// if !job_str.is_empty() { +// self.push_event("load_last_job", vec![("value", job_str)]); +// cnt += 1; +// println!("restore write_job: {:?}", job_str); +// } +// } +// } + +// fn update_quality_status(&self, status: QualityStatus) { +// const NULL: String = String::new(); +// self.push_event( +// "update_quality_status", +// vec![ +// ("speed", &status.speed.map_or(NULL, |it| it)), +// ("fps", &status.fps.map_or(NULL, |it| it.to_string())), +// ("delay", &status.delay.map_or(NULL, |it| it.to_string())), +// ( +// "target_bitrate", +// &status.target_bitrate.map_or(NULL, |it| it.to_string()), +// ), +// ( +// "codec_format", +// &status.codec_format.map_or(NULL, |it| it.to_string()), +// ), +// ], +// ); +// } + +// pub fn remove_port_forward(&mut self, port: i32) { +// let mut config = self.load_config(); +// config.port_forwards = config +// .port_forwards +// .drain(..) +// .filter(|x| x.0 != port) +// .collect(); +// self.save_config(&config); +// self.send(Data::RemovePortForward(port)); +// } + +// pub fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { +// let mut config = self.load_config(); +// if config +// .port_forwards +// .iter() +// .filter(|x| x.0 == port) +// .next() +// .is_some() +// { +// return; +// } +// let pf = (port, remote_host, remote_port); +// config.port_forwards.push(pf.clone()); +// self.save_config(&config); +// self.send(Data::AddPortForward(pf)); +// } + +// fn on_error(&self, err: &str) { +// self.msgbox("error", "Error", err); +// } +// } + +// impl FileManager for Session {} + +// #[async_trait] +// impl Interface for Session { +// fn send(&self, data: Data) { +// if let Some(sender) = self.sender.read().unwrap().as_ref() { +// sender.send(data).ok(); +// } +// } + +// fn is_file_transfer(&self) -> bool { +// todo!() +// } + +// fn is_port_forward(&self) -> bool { +// todo!() +// } + +// fn is_rdp(&self) -> bool { +// todo!() +// } + +// fn msgbox(&self, msgtype: &str, title: &str, text: &str) { +// let has_retry = if check_if_retry(msgtype, title, text) { +// "true" +// } else { +// "" +// }; +// self.push_event( +// "msgbox", +// vec![ +// ("type", msgtype), +// ("title", title), +// ("text", text), +// ("hasRetry", has_retry), +// ], +// ); +// } + +// fn handle_login_error(&mut self, err: &str) -> bool { +// self.lc.write().unwrap().handle_login_error(err, self) +// } + +// fn handle_peer_info(&mut self, pi: PeerInfo) { +// let mut lc = self.lc.write().unwrap(); +// let username = lc.get_username(&pi); +// let mut displays = Vec::new(); +// let mut current = pi.current_display as usize; + +// if lc.is_file_transfer { +// if pi.username.is_empty() { +// self.msgbox( +// "error", +// "Error", +// "No active console user logged on, please connect and logon first.", +// ); +// return; +// } +// } else { +// if pi.displays.is_empty() { +// self.msgbox("error", "Remote Error", "No Display"); +// } +// for ref d in pi.displays.iter() { +// let mut h: HashMap<&str, i32> = Default::default(); +// h.insert("x", d.x); +// h.insert("y", d.y); +// h.insert("width", d.width); +// h.insert("height", d.height); +// displays.push(h); +// } +// if current >= pi.displays.len() { +// current = 0; +// } +// } +// let displays = serde_json::ser::to_string(&displays).unwrap_or("".to_owned()); +// self.push_event( +// "peer_info", +// vec![ +// ("username", &username), +// ("hostname", &pi.hostname), +// ("platform", &pi.platform), +// ("sas_enabled", &pi.sas_enabled.to_string()), +// ("displays", &displays), +// ("version", &pi.version), +// ("current_display", ¤t.to_string()), +// ("is_file_transfer", &lc.is_file_transfer.to_string()), +// ], +// ); +// lc.handle_peer_info(username, pi); +// let p = lc.should_auto_login(); +// if !p.is_empty() { +// input_os_password(p, true, self.clone()); +// } +// } + +// fn set_force_relay(&mut self, direct: bool, received: bool) { +// let mut lc = self.lc.write().unwrap(); +// lc.force_relay = false; +// if direct && !received { +// let errno = errno::errno().0; +// log::info!("errno is {}", errno); +// // TODO: check mac and ios +// if cfg!(windows) && errno == 10054 || !cfg!(windows) && errno == 104 { +// lc.force_relay = true; +// lc.set_option("force-always-relay".to_owned(), "Y".to_owned()); +// } +// } +// } + +// fn is_force_relay(&self) -> bool { +// self.lc.read().unwrap().force_relay +// } + +// async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { +// handle_hash(self.lc.clone(), pass, hash, self, peer).await; +// } + +// async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { +// handle_login_from_ui(self.lc.clone(), password, remember, peer).await; +// } + +// async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { +// if !t.from_client { +// self.update_quality_status(QualityStatus { +// delay: Some(t.last_delay as _), +// target_bitrate: Some(t.target_bitrate as _), +// ..Default::default() +// }); +// handle_test_delay(t, peer).await; +// } +// } +// } + +// struct Connection { +// video_handler: VideoHandler, +// audio_handler: AudioHandler, +// session: Session, +// first_frame: bool, +// read_jobs: Vec, +// write_jobs: Vec, +// timer: Interval, +// last_update_jobs_status: (Instant, HashMap), +// data_count: Arc, +// frame_count: Arc, +// video_format: CodecFormat, +// } + +// impl Connection { +// // TODO: Similar to remote::start_clipboard +// // merge the code +// fn start_clipboard( +// tx_protobuf: mpsc::UnboundedSender, +// lc: Arc>, +// ) -> Option> { +// let (tx, rx) = std::sync::mpsc::channel(); +// #[cfg(not(any(target_os = "android", target_os = "ios")))] +// match ClipboardContext::new() { +// Ok(mut ctx) => { +// let old_clipboard: Arc> = Default::default(); +// // ignore clipboard update before service start +// check_clipboard(&mut ctx, Some(&old_clipboard)); +// std::thread::spawn(move || loop { +// std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); +// match rx.try_recv() { +// Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { +// log::debug!("Exit clipboard service of client"); +// break; +// } +// _ => {} +// } +// if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) +// || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) +// || lc.read().unwrap().disable_clipboard +// { +// continue; +// } +// if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { +// tx_protobuf.send(Data::Message(msg)).ok(); +// } +// }); +// } +// Err(err) => { +// log::error!("Failed to start clipboard service of client: {}", err); +// } +// } +// Some(tx) +// } + +// /// Create a new connection. +// /// +// /// # Arguments +// /// +// /// * `session` - The session to create a new connection for. +// /// * `is_file_transfer` - Whether the connection is for file transfer. +// /// * `is_port_forward` - Whether the connection is for port forward. +// #[tokio::main(flavor = "current_thread")] +// async fn start(session: Session, is_file_transfer: bool, is_port_forward: bool) { +// let mut last_recv_time = Instant::now(); +// let (sender, mut receiver) = mpsc::unbounded_channel::(); +// let mut stop_clipboard = None; +// if !is_file_transfer && !is_port_forward { +// stop_clipboard = Self::start_clipboard(sender.clone(), session.lc.clone()); +// } +// *session.sender.write().unwrap() = Some(sender.clone()); +// let conn_type = if is_file_transfer { +// session.lc.write().unwrap().is_file_transfer = true; +// ConnType::FILE_TRANSFER +// } else if is_port_forward { +// ConnType::PORT_FORWARD // TODO: RDP +// } else { +// ConnType::DEFAULT_CONN +// }; +// let key = Config::get_option("key"); +// let token = Config::get_option("access_token"); + +// // TODO rdp & cli args +// let is_rdp = false; +// let args: Vec = Vec::new(); + +// if is_port_forward { +// if is_rdp { +// // let port = handler +// // .get_option("rdp_port".to_owned()) +// // .parse::() +// // .unwrap_or(3389); +// // std::env::set_var( +// // "rdp_username", +// // handler.get_option("rdp_username".to_owned()), +// // ); +// // std::env::set_var( +// // "rdp_password", +// // handler.get_option("rdp_password".to_owned()), +// // ); +// // log::info!("Remote rdp port: {}", port); +// // start_one_port_forward(handler, 0, "".to_owned(), port, receiver, &key, &token).await; +// } else if args.len() == 0 { +// let pfs = session.lc.read().unwrap().port_forwards.clone(); +// let mut queues = HashMap::>::new(); +// for d in pfs { +// sender.send(Data::AddPortForward(d)).ok(); +// } +// loop { +// match receiver.recv().await { +// Some(Data::AddPortForward((port, remote_host, remote_port))) => { +// if port <= 0 || remote_port <= 0 { +// continue; +// } +// let (sender, receiver) = mpsc::unbounded_channel::(); +// queues.insert(port, sender); +// let handler = session.clone(); +// let key = key.clone(); +// let token = token.clone(); +// tokio::spawn(async move { +// start_one_port_forward( +// handler, +// port, +// remote_host, +// remote_port, +// receiver, +// &key, +// &token, +// ) +// .await; +// }); +// } +// Some(Data::RemovePortForward(port)) => { +// if let Some(s) = queues.remove(&port) { +// s.send(Data::Close).ok(); +// } +// } +// Some(Data::Close) => { +// break; +// } +// Some(d) => { +// for (_, s) in queues.iter() { +// s.send(d.clone()).ok(); +// } +// } +// _ => {} +// } +// } +// } else { +// // let port = handler.args[0].parse::().unwrap_or(0); +// // if handler.args.len() != 3 +// // || handler.args[2].parse::().unwrap_or(0) <= 0 +// // || port <= 0 +// // { +// // handler.on_error("Invalid arguments, usage:

rustdesk --port-forward remote-id listen-port remote-host remote-port"); +// // } +// // let remote_host = handler.args[1].clone(); +// // let remote_port = handler.args[2].parse::().unwrap_or(0); +// // start_one_port_forward( +// // handler, +// // port, +// // remote_host, +// // remote_port, +// // receiver, +// // &key, +// // &token, +// // ) +// // .await; +// } +// return; +// } + +// let latency_controller = LatencyController::new(); +// let latency_controller_cl = latency_controller.clone(); + +// let mut conn = Connection { +// video_handler: VideoHandler::new(latency_controller), +// audio_handler: AudioHandler::new(latency_controller_cl), +// session: session.clone(), +// first_frame: false, +// read_jobs: Vec::new(), +// write_jobs: Vec::new(), +// timer: time::interval(SEC30), +// last_update_jobs_status: (Instant::now(), Default::default()), +// data_count: Arc::new(AtomicUsize::new(0)), +// frame_count: Arc::new(AtomicUsize::new(0)), +// video_format: CodecFormat::Unknown, +// }; + +// match Client::start(&session.id, &key, &token, conn_type, session.clone()).await { +// Ok((mut peer, direct)) => { +// SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); +// SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); + +// session.push_event( +// "connection_ready", +// vec![ +// ("secure", &peer.is_secured().to_string()), +// ("direct", &direct.to_string()), +// ], +// ); + +// let mut status_timer = time::interval(Duration::new(1, 0)); + +// loop { +// tokio::select! { +// res = peer.next() => { +// if let Some(res) = res { +// match res { +// Err(err) => { +// log::error!("Connection closed: {}", err); +// session.msgbox("error", "Connection Error", &err.to_string()); +// break; +// } +// Ok(ref bytes) => { +// last_recv_time = Instant::now(); +// conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed); +// if !conn.handle_msg_from_peer(bytes, &mut peer).await { +// break +// } +// } +// } +// } else { +// if session.lc.read().unwrap().restarting_remote_device { +// log::info!("Restart remote device"); +// session.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); +// } else { +// log::info!("Reset by the peer"); +// session.msgbox("error", "Connection Error", "Reset by the peer"); +// } +// break; +// } +// } +// d = receiver.recv() => { +// if let Some(d) = d { +// if !conn.handle_msg_from_ui(d, &mut peer).await { +// break; +// } +// } +// } +// _ = conn.timer.tick() => { +// if last_recv_time.elapsed() >= SEC30 { +// session.msgbox("error", "Connection Error", "Timeout"); +// break; +// } +// if !conn.read_jobs.is_empty() { +// if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut peer).await { +// log::debug!("Connection Error: {}", err); +// break; +// } +// conn.update_jobs_status(); +// } else { +// conn.timer = time::interval_at(Instant::now() + SEC30, SEC30); +// } +// } +// _ = status_timer.tick() => { +// let speed = conn.data_count.swap(0, Ordering::Relaxed); +// let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); +// let fps = conn.frame_count.swap(0, Ordering::Relaxed) as _; +// conn.session.update_quality_status(QualityStatus { +// speed:Some(speed), +// fps:Some(fps), +// ..Default::default() +// }); +// } +// } +// } +// log::debug!("Exit io_loop of id={}", session.id); +// } +// Err(err) => { +// session.msgbox("error", "Connection Error", &err.to_string()); +// } +// } + +// if let Some(stop) = stop_clipboard { +// stop.send(()).ok(); +// } +// SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); +// SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); +// } + +// /// Handle message from peer. +// /// Return false if the connection should be closed. +// /// +// /// The message is handled by [`Message`], see [`message::Union`] for possible types. +// async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { +// if let Ok(msg_in) = Message::parse_from_bytes(&data) { +// match msg_in.union { +// Some(message::Union::VideoFrame(vf)) => { +// if !self.first_frame { +// self.first_frame = true; +// common::send_opts_after_login(&self.session.lc.read().unwrap(), peer).await; +// } +// let incomming_format = CodecFormat::from(&vf); +// if self.video_format != incomming_format { +// self.video_format = incomming_format.clone(); +// self.session.update_quality_status(QualityStatus { +// codec_format: Some(incomming_format), +// ..Default::default() +// }) +// }; +// if let Ok(true) = self.video_handler.handle_frame(vf) { +// if let Some(stream) = &*self.session.events2ui.read().unwrap() { +// self.frame_count.fetch_add(1, Ordering::Relaxed); +// stream.add(EventToUI::Rgba(ZeroCopyBuffer( +// self.video_handler.rgb.clone(), +// ))); +// } +// } +// } +// Some(message::Union::Hash(hash)) => { +// self.session.handle_hash("", hash, peer).await; +// } +// Some(message::Union::LoginResponse(lr)) => match lr.union { +// Some(login_response::Union::Error(err)) => { +// if !self.session.handle_login_error(&err) { +// return false; +// } +// } +// Some(login_response::Union::PeerInfo(pi)) => { +// self.session.handle_peer_info(pi); +// } +// _ => {} +// }, +// Some(message::Union::Clipboard(cb)) => { +// if !self.session.lc.read().unwrap().disable_clipboard { +// let content = if cb.compress { +// decompress(&cb.content) +// } else { +// cb.content.into() +// }; +// if let Ok(content) = String::from_utf8(content) { +// self.session +// .push_event("clipboard", vec![("content", &content)]); +// } +// } +// } +// Some(message::Union::CursorData(cd)) => { +// let colors = hbb_common::compress::decompress(&cd.colors); +// self.session.push_event( +// "cursor_data", +// vec![ +// ("id", &cd.id.to_string()), +// ("hotx", &cd.hotx.to_string()), +// ("hoty", &cd.hoty.to_string()), +// ("width", &cd.width.to_string()), +// ("height", &cd.height.to_string()), +// ( +// "colors", +// &serde_json::ser::to_string(&colors).unwrap_or("".to_owned()), +// ), +// ], +// ); +// } +// Some(message::Union::CursorId(id)) => { +// self.session +// .push_event("cursor_id", vec![("id", &id.to_string())]); +// } +// Some(message::Union::CursorPosition(cp)) => { +// self.session.push_event( +// "cursor_position", +// vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())], +// ); +// } +// Some(message::Union::FileResponse(fr)) => { +// match fr.union { +// Some(file_response::Union::Dir(fd)) => { +// let mut entries = fd.entries.to_vec(); +// if self.session.peer_platform() == "Windows" { +// transform_windows_path(&mut entries); +// } +// let id = fd.id; +// self.session.push_event( +// "file_dir", +// vec![("value", &make_fd_to_json(fd)), ("is_local", "false")], +// ); +// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { +// job.set_files(entries); +// } +// } +// Some(file_response::Union::Block(block)) => { +// if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { +// if let Err(_err) = job.write(block, None).await { +// // to-do: add "skip" for writing job +// } +// self.update_jobs_status(); +// } +// } +// Some(file_response::Union::Done(d)) => { +// if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { +// job.modify_time(); +// fs::remove_job(d.id, &mut self.write_jobs); +// } +// self.handle_job_status(d.id, d.file_num, None); +// } +// Some(file_response::Union::Error(e)) => { +// self.handle_job_status(e.id, e.file_num, Some(e.error)); +// } +// Some(file_response::Union::Digest(digest)) => { +// if digest.is_upload { +// if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { +// if let Some(file) = job.files().get(digest.file_num as usize) { +// let read_path = get_string(&job.join(&file.name)); +// let overwrite_strategy = job.default_overwrite_strategy(); +// if let Some(overwrite) = overwrite_strategy { +// let req = FileTransferSendConfirmRequest { +// id: digest.id, +// file_num: digest.file_num, +// union: Some(if overwrite { +// file_transfer_send_confirm_request::Union::OffsetBlk(0) +// } else { +// file_transfer_send_confirm_request::Union::Skip( +// true, +// ) +// }), +// ..Default::default() +// }; +// job.confirm(&req); +// let msg = new_send_confirm(req); +// allow_err!(peer.send(&msg).await); +// } else { +// self.handle_override_file_confirm( +// digest.id, +// digest.file_num, +// read_path, +// true, +// ); +// } +// } +// } +// } else { +// if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { +// if let Some(file) = job.files().get(digest.file_num as usize) { +// let write_path = get_string(&job.join(&file.name)); +// let overwrite_strategy = job.default_overwrite_strategy(); +// match fs::is_write_need_confirmation(&write_path, &digest) { +// Ok(res) => match res { +// DigestCheckResult::IsSame => { +// let msg= new_send_confirm(FileTransferSendConfirmRequest { +// id: digest.id, +// file_num: digest.file_num, +// union: Some(file_transfer_send_confirm_request::Union::Skip(true)), +// ..Default::default() +// }); +// self.session.send_msg(msg); +// } +// DigestCheckResult::NeedConfirm(digest) => { +// if let Some(overwrite) = overwrite_strategy { +// let msg = new_send_confirm( +// FileTransferSendConfirmRequest { +// id: digest.id, +// file_num: digest.file_num, +// union: Some(if overwrite { +// file_transfer_send_confirm_request::Union::OffsetBlk(0) +// } else { +// file_transfer_send_confirm_request::Union::Skip(true) +// }), +// ..Default::default() +// }, +// ); +// self.session.send_msg(msg); +// } else { +// self.handle_override_file_confirm( +// digest.id, +// digest.file_num, +// write_path.to_string(), +// false, +// ); +// } +// } +// DigestCheckResult::NoSuchFile => { +// let msg = new_send_confirm( +// FileTransferSendConfirmRequest { +// id: digest.id, +// file_num: digest.file_num, +// union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), +// ..Default::default() +// }, +// ); +// self.session.send_msg(msg); +// } +// }, +// Err(err) => { +// println!("error recving digest: {}", err); +// } +// } +// } +// } +// } +// } +// _ => {} +// } +// } +// Some(message::Union::Misc(misc)) => match misc.union { +// Some(misc::Union::AudioFormat(f)) => { +// self.audio_handler.handle_format(f); // +// } +// Some(misc::Union::ChatMessage(c)) => { +// self.session +// .push_event("chat_client_mode", vec![("text", &c.text)]); +// } +// Some(misc::Union::PermissionInfo(p)) => { +// log::info!("Change permission {:?} -> {}", p.permission, p.enabled); +// use permission_info::Permission; +// self.session.push_event( +// "permission", +// vec![( +// match p.permission.enum_value_or_default() { +// Permission::Keyboard => "keyboard", +// Permission::Clipboard => "clipboard", +// Permission::Audio => "audio", +// Permission::Restart => "restart", +// _ => "", +// }, +// &p.enabled.to_string(), +// )], +// ); +// } +// Some(misc::Union::SwitchDisplay(s)) => { +// self.video_handler.reset(); +// self.session.push_event( +// "switch_display", +// vec![ +// ("display", &s.display.to_string()), +// ("x", &s.x.to_string()), +// ("y", &s.y.to_string()), +// ("width", &s.width.to_string()), +// ("height", &s.height.to_string()), +// ], +// ); +// } +// Some(misc::Union::CloseReason(c)) => { +// self.session.msgbox("error", "Connection Error", &c); +// return false; +// } +// Some(misc::Union::BackNotification(notification)) => { +// if !self.handle_back_notification(notification).await { +// return false; +// } +// } +// _ => {} +// }, +// Some(message::Union::TestDelay(t)) => { +// self.session.handle_test_delay(t, peer).await; +// } +// Some(message::Union::AudioFrame(frame)) => { +// if !self.session.lc.read().unwrap().disable_audio { +// self.audio_handler.handle_frame(frame); +// } +// } +// Some(message::Union::FileAction(action)) => match action.union { +// Some(file_action::Union::SendConfirm(c)) => { +// if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { +// job.confirm(&c); +// } +// } +// _ => {} +// }, +// _ => {} +// } +// } +// true +// } + +// async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { +// match notification.union { +// Some(back_notification::Union::BlockInputState(state)) => { +// self.handle_back_msg_block_input( +// state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), +// ) +// .await; +// } +// Some(back_notification::Union::PrivacyModeState(state)) => { +// if !self +// .handle_back_msg_privacy_mode( +// state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), +// ) +// .await +// { +// return false; +// } +// } +// _ => {} +// } +// true +// } + +// #[inline(always)] +// fn update_block_input_state(&mut self, on: bool) { +// self.session.push_event( +// "update_block_input_state", +// [("input_state", if on { "on" } else { "off" })].into(), +// ); +// } + +// async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { +// match state { +// back_notification::BlockInputState::BlkOnSucceeded => { +// self.update_block_input_state(true); +// } +// back_notification::BlockInputState::BlkOnFailed => { +// self.session +// .msgbox("custom-error", "Block user input", "Failed"); +// self.update_block_input_state(false); +// } +// back_notification::BlockInputState::BlkOffSucceeded => { +// self.update_block_input_state(false); +// } +// back_notification::BlockInputState::BlkOffFailed => { +// self.session +// .msgbox("custom-error", "Unblock user input", "Failed"); +// } +// _ => {} +// } +// } + +// #[inline(always)] +// fn update_privacy_mode(&mut self, on: bool) { +// let mut config = self.session.load_config(); +// config.privacy_mode = on; +// self.session.save_config(&config); +// self.session.lc.write().unwrap().get_config().privacy_mode = on; +// self.session.push_event("update_privacy_mode", [].into()); +// } + +// async fn handle_back_msg_privacy_mode( +// &mut self, +// state: back_notification::PrivacyModeState, +// ) -> bool { +// match state { +// back_notification::PrivacyModeState::PrvOnByOther => { +// self.session.msgbox( +// "error", +// "Connecting...", +// "Someone turns on privacy mode, exit", +// ); +// return false; +// } +// back_notification::PrivacyModeState::PrvNotSupported => { +// self.session +// .msgbox("custom-error", "Privacy mode", "Unsupported"); +// self.update_privacy_mode(false); +// } +// back_notification::PrivacyModeState::PrvOnSucceeded => { +// self.session +// .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); +// self.update_privacy_mode(true); +// } +// back_notification::PrivacyModeState::PrvOnFailedDenied => { +// self.session +// .msgbox("custom-error", "Privacy mode", "Peer denied"); +// self.update_privacy_mode(false); +// } +// back_notification::PrivacyModeState::PrvOnFailedPlugin => { +// self.session +// .msgbox("custom-error", "Privacy mode", "Please install plugins"); +// self.update_privacy_mode(false); +// } +// back_notification::PrivacyModeState::PrvOnFailed => { +// self.session +// .msgbox("custom-error", "Privacy mode", "Failed"); +// self.update_privacy_mode(false); +// } +// back_notification::PrivacyModeState::PrvOffSucceeded => { +// self.session +// .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); +// self.update_privacy_mode(false); +// } +// back_notification::PrivacyModeState::PrvOffByPeer => { +// self.session +// .msgbox("custom-error", "Privacy mode", "Peer exit"); +// self.update_privacy_mode(false); +// } +// back_notification::PrivacyModeState::PrvOffFailed => { +// self.session +// .msgbox("custom-error", "Privacy mode", "Failed to turn off"); +// } +// back_notification::PrivacyModeState::PrvOffUnknown => { +// self.session +// .msgbox("custom-error", "Privacy mode", "Turned off"); +// // log::error!("Privacy mode is turned off with unknown reason"); +// self.update_privacy_mode(false); +// } +// _ => {} +// } +// true +// } + +// async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { +// match data { +// Data::Close => { +// self.sync_jobs_status_to_local().await; +// return false; +// } +// Data::Login((password, remember)) => { +// self.session +// .handle_login_from_ui(password, remember, peer) +// .await; +// } +// Data::Message(msg) => { +// allow_err!(peer.send(&msg).await); +// } +// Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { +// let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); +// if is_remote { +// log::debug!("New job {}, write to {} from remote {}", id, to, path); +// self.write_jobs.push(fs::TransferJob::new_write( +// id, +// path.clone(), +// to, +// file_num, +// include_hidden, +// is_remote, +// Vec::new(), +// od, +// )); +// allow_err!( +// peer.send(&fs::new_send(id, path, file_num, include_hidden)) +// .await +// ); +// } else { +// match fs::TransferJob::new_read( +// id, +// to.clone(), +// path.clone(), +// file_num, +// include_hidden, +// is_remote, +// od, +// ) { +// Err(err) => { +// self.handle_job_status(id, -1, Some(err.to_string())); +// } +// Ok(job) => { +// log::debug!( +// "New job {}, read {} to remote {}, {} files", +// id, +// path, +// to, +// job.files().len() +// ); +// let m = make_fd_flutter(id, job.files(), true); +// self.session +// .push_event("update_folder_files", vec![("info", &m)]); +// let files = job.files().clone(); +// self.read_jobs.push(job); +// self.timer = time::interval(MILLI1); +// allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); +// } +// } +// } +// } +// Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { +// if is_remote { +// let mut msg_out = Message::new(); +// let mut file_action = FileAction::new(); +// file_action.set_all_files(ReadAllFiles { +// id, +// path: path.clone(), +// include_hidden, +// ..Default::default() +// }); +// msg_out.set_file_action(file_action); +// allow_err!(peer.send(&msg_out).await); +// } else { +// match fs::get_recursive_files(&path, include_hidden) { +// Ok(entries) => { +// let mut fd = FileDirectory::new(); +// fd.id = id; +// fd.path = path; +// fd.entries = entries; +// self.session.push_event( +// "file_dir", +// vec![("value", &make_fd_to_json(fd)), ("is_local", "true")], +// ); +// } +// Err(err) => { +// self.handle_job_status(id, -1, Some(err.to_string())); +// } +// } +// } +// } +// Data::CancelJob(id) => { +// let mut msg_out = Message::new(); +// let mut file_action = FileAction::new(); +// file_action.set_cancel(FileTransferCancel { +// id: id, +// ..Default::default() +// }); +// msg_out.set_file_action(file_action); +// allow_err!(peer.send(&msg_out).await); +// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { +// job.remove_download_file(); +// fs::remove_job(id, &mut self.write_jobs); +// } +// fs::remove_job(id, &mut self.read_jobs); +// } +// Data::RemoveDir((id, path)) => { +// let mut msg_out = Message::new(); +// let mut file_action = FileAction::new(); +// file_action.set_remove_dir(FileRemoveDir { +// id, +// path, +// recursive: true, +// ..Default::default() +// }); +// msg_out.set_file_action(file_action); +// allow_err!(peer.send(&msg_out).await); +// } +// Data::RemoveFile((id, path, file_num, is_remote)) => { +// if is_remote { +// let mut msg_out = Message::new(); +// let mut file_action = FileAction::new(); +// file_action.set_remove_file(FileRemoveFile { +// id, +// path, +// file_num, +// ..Default::default() +// }); +// msg_out.set_file_action(file_action); +// allow_err!(peer.send(&msg_out).await); +// } else { +// match fs::remove_file(&path) { +// Err(err) => { +// self.handle_job_status(id, file_num, Some(err.to_string())); +// } +// Ok(()) => { +// self.handle_job_status(id, file_num, None); +// } +// } +// } +// } +// Data::CreateDir((id, path, is_remote)) => { +// if is_remote { +// let mut msg_out = Message::new(); +// let mut file_action = FileAction::new(); +// file_action.set_create(FileDirCreate { +// id, +// path, +// ..Default::default() +// }); +// msg_out.set_file_action(file_action); +// allow_err!(peer.send(&msg_out).await); +// } else { +// match fs::create_dir(&path) { +// Err(err) => { +// self.handle_job_status(id, -1, Some(err.to_string())); +// } +// Ok(()) => { +// self.handle_job_status(id, -1, None); +// } +// } +// } +// } +// Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { +// if is_upload { +// if let Some(job) = fs::get_job(id, &mut self.read_jobs) { +// if remember { +// job.set_overwrite_strategy(Some(need_override)); +// } +// job.confirm(&FileTransferSendConfirmRequest { +// id, +// file_num, +// union: if need_override { +// Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) +// } else { +// Some(file_transfer_send_confirm_request::Union::Skip(true)) +// }, +// ..Default::default() +// }); +// } +// } else { +// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { +// if remember { +// job.set_overwrite_strategy(Some(need_override)); +// } +// let mut msg = Message::new(); +// let mut file_action = FileAction::new(); +// file_action.set_send_confirm(FileTransferSendConfirmRequest { +// id, +// file_num, +// union: if need_override { +// Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) +// } else { +// Some(file_transfer_send_confirm_request::Union::Skip(true)) +// }, +// ..Default::default() +// }); +// msg.set_file_action(file_action); +// self.session.send_msg(msg); +// } +// } +// } +// Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { +// let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); +// if is_remote { +// log::debug!( +// "new write waiting job {}, write to {} from remote {}", +// id, +// to, +// path +// ); +// let mut job = fs::TransferJob::new_write( +// id, +// path.clone(), +// to, +// file_num, +// include_hidden, +// is_remote, +// Vec::new(), +// od, +// ); +// job.is_last_job = true; +// self.write_jobs.push(job); +// } else { +// match fs::TransferJob::new_read( +// id, +// to.clone(), +// path.clone(), +// file_num, +// include_hidden, +// is_remote, +// od, +// ) { +// Err(err) => { +// self.handle_job_status(id, -1, Some(err.to_string())); +// } +// Ok(mut job) => { +// log::debug!( +// "new read waiting job {}, read {} to remote {}, {} files", +// id, +// path, +// to, +// job.files().len() +// ); +// let m = make_fd_flutter(job.id(), job.files(), true); +// self.session +// .push_event("update_folder_files", vec![("info", &m)]); +// job.is_last_job = true; +// self.read_jobs.push(job); +// self.timer = time::interval(MILLI1); +// } +// } +// } +// } +// Data::ResumeJob((id, is_remote)) => { +// if is_remote { +// if let Some(job) = get_job(id, &mut self.write_jobs) { +// job.is_last_job = false; +// allow_err!( +// peer.send(&fs::new_send( +// id, +// job.remote.clone(), +// job.file_num, +// job.show_hidden +// )) +// .await +// ); +// } +// } else { +// if let Some(job) = get_job(id, &mut self.read_jobs) { +// job.is_last_job = false; +// allow_err!( +// peer.send(&fs::new_receive( +// id, +// job.path.to_string_lossy().to_string(), +// job.file_num, +// job.files.clone() +// )) +// .await +// ); +// } +// } +// } +// _ => {} +// } +// true +// } + +// #[inline] +// fn update_job_status( +// job: &fs::TransferJob, +// elapsed: i32, +// last_update_jobs_status: &mut (Instant, HashMap), +// session: &Session, +// ) { +// if elapsed <= 0 { +// return; +// } +// let transferred = job.transferred(); +// let last_transferred = { +// if let Some(v) = last_update_jobs_status.1.get(&job.id()) { +// v.to_owned() +// } else { +// 0 +// } +// }; +// last_update_jobs_status.1.insert(job.id(), transferred); +// let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); +// let file_num = job.file_num() - 1; +// session.push_event( +// "job_progress", +// vec![ +// ("id", &job.id().to_string()), +// ("file_num", &file_num.to_string()), +// ("speed", &speed.to_string()), +// ("finished_size", &job.finished_size().to_string()), +// ], +// ); +// } + +// fn update_jobs_status(&mut self) { +// let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; +// if elapsed >= 1000 { +// for job in self.read_jobs.iter() { +// Self::update_job_status( +// job, +// elapsed, +// &mut self.last_update_jobs_status, +// &self.session, +// ); +// } +// for job in self.write_jobs.iter() { +// Self::update_job_status( +// job, +// elapsed, +// &mut self.last_update_jobs_status, +// &self.session, +// ); +// } +// self.last_update_jobs_status.0 = Instant::now(); +// } +// } + +// fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { +// if let Some(err) = err { +// self.session +// .push_event("job_error", vec![("id", &id.to_string()), ("err", &err)]); +// } else { +// self.session.push_event( +// "job_done", +// vec![("id", &id.to_string()), ("file_num", &file_num.to_string())], +// ); +// } +// } + +// fn handle_override_file_confirm( +// &mut self, +// id: i32, +// file_num: i32, +// read_path: String, +// is_upload: bool, +// ) { +// self.session.push_event( +// "override_file_confirm", +// vec![ +// ("id", &id.to_string()), +// ("file_num", &file_num.to_string()), +// ("read_path", &read_path), +// ("is_upload", &is_upload.to_string()), +// ], +// ); +// } + +// async fn sync_jobs_status_to_local(&mut self) -> bool { +// log::info!("sync transfer job status"); +// let mut config: PeerConfig = self.session.load_config(); +// let mut transfer_metas = TransferSerde::default(); +// for job in self.read_jobs.iter() { +// let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); +// transfer_metas.read_jobs.push(json_str); +// } +// for job in self.write_jobs.iter() { +// let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); +// transfer_metas.write_jobs.push(json_str); +// } +// log::info!("meta: {:?}", transfer_metas); +// config.transfer = transfer_metas; +// self.session.save_config(&config); +// true +// } +// } // Server Side // TODO connection_manager need use struct and trait,impl default method @@ -2379,30 +2628,30 @@ pub fn get_session_id(id: String) -> String { }; } -async fn start_one_port_forward( - handler: Session, - port: i32, - remote_host: String, - remote_port: i32, - receiver: mpsc::UnboundedReceiver, - key: &str, - token: &str, -) { - if let Err(err) = crate::port_forward::listen( - handler.id.clone(), - String::new(), // TODO - port, - handler.clone(), - receiver, - key, - token, - handler.lc.clone(), - remote_host, - remote_port, - ) - .await - { - handler.on_error(&format!("Failed to listen on {}: {}", port, err)); - } - log::info!("port forward (:{}) exit", port); -} +// async fn start_one_port_forward( +// handler: Session, +// port: i32, +// remote_host: String, +// remote_port: i32, +// receiver: mpsc::UnboundedReceiver, +// key: &str, +// token: &str, +// ) { +// if let Err(err) = crate::port_forward::listen( +// handler.id.clone(), +// String::new(), // TODO +// port, +// handler.clone(), +// receiver, +// key, +// token, +// handler.lc.clone(), +// remote_host, +// remote_port, +// ) +// .await +// { +// handler.on_error(&format!("Failed to listen on {}: {}", port, err)); +// } +// log::info!("port forward (:{}) exit", port); +// } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index db8030782..5226416b2 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -13,10 +13,10 @@ use hbb_common::{ }; use hbb_common::{password_security, ResultType}; -use crate::client::file_trait::FileManager; +use crate::{client::file_trait::FileManager, flutter::{session_add, session_start_}}; use crate::common::make_fd_to_json; use crate::flutter::connection_manager::{self, get_clients_length, get_clients_state}; -use crate::flutter::{self, Session, SESSIONS}; +use crate::flutter::{self, SESSIONS}; use crate::start_server; use crate::ui_interface; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -110,7 +110,7 @@ pub fn host_stop_system_key_propagate(stopped: bool) { // FIXME: -> ResultType<()> cannot be parsed by frb_codegen // thread 'main' panicked at 'Failed to parse function output type `ResultType<()>`', $HOME\.cargo\git\checkouts\flutter_rust_bridge-ddba876d3ebb2a1e\e5adce5\frb_codegen\src\parser\mod.rs:151:25 pub fn session_add_sync(id: String, is_file_transfer: bool, is_port_forward: bool) -> SyncReturn { - if let Err(e) = Session::add(&id, is_file_transfer, is_port_forward) { + if let Err(e) = session_add(&id, is_file_transfer, is_port_forward) { SyncReturn(format!("Failed to add session with id {}, {}", &id, e)) } else { SyncReturn("".to_owned()) @@ -118,7 +118,7 @@ pub fn session_add_sync(id: String, is_file_transfer: bool, is_port_forward: boo } pub fn session_start(events2ui: StreamSink, id: String) -> ResultType<()> { - Session::start(&id, events2ui) + session_start_(&id, events2ui) } pub fn session_get_remember(id: String) -> Option { @@ -131,7 +131,7 @@ pub fn session_get_remember(id: String) -> Option { pub fn session_get_toggle_option(id: String, arg: String) -> Option { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - Some(session.get_toggle_option(&arg)) + Some(session.get_toggle_option(arg)) } else { None } @@ -152,7 +152,7 @@ pub fn session_get_image_quality(id: String) -> Option { pub fn session_get_option(id: String, arg: String) -> Option { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - Some(session.get_option(&arg)) + Some(session.get_option(arg)) } else { None } @@ -160,7 +160,7 @@ pub fn session_get_option(id: String, arg: String) -> Option { pub fn session_login(id: String, password: String, remember: bool) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.login(&password, remember); + session.login(password, remember); } } @@ -173,7 +173,7 @@ pub fn session_close(id: String) { pub fn session_refresh(id: String) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.refresh(); + session.refresh_video(); } } @@ -184,26 +184,26 @@ pub fn session_reconnect(id: String) { } pub fn session_toggle_option(id: String, value: String) { - if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.toggle_option(&value); + if let Some(session) = SESSIONS.write().unwrap().get_mut(&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); + // session.set_image_quality(value); } } pub fn session_lock_screen(id: String) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.lock_screen(); + // session.lock_screen(); } } pub fn session_ctrl_alt_del(id: String) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.ctrl_alt_del(); + // session.ctrl_alt_del(); } } @@ -224,13 +224,13 @@ pub fn session_input_key( command: bool, ) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.input_key(&name, down, press, alt, ctrl, shift, command); + // 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); + // session.input_string(&value); } } @@ -249,7 +249,7 @@ pub fn session_peer_option(id: String, name: String, value: String) { pub fn session_get_peer_option(id: String, name: String) -> String { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - return session.get_option(&name); + return session.get_option(name); } "".to_string() } @@ -348,7 +348,7 @@ pub fn session_get_platform(id: String, is_remote: bool) -> String { pub fn session_load_last_transfer_jobs(id: String) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - return session.load_last_jobs(); + // return session.load_last_jobs(); } else { // a tip for flutter dev eprintln!( @@ -719,7 +719,7 @@ pub fn session_send_mouse(id: String, msg: String) { } << 3; } if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.send_mouse(mask, x, y, alt, ctrl, shift, command); + // session.send_mouse(mask, x, y, alt, ctrl, shift, command); } } } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index f5abb3d73..49812e09a 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -48,7 +48,7 @@ use crate::clipboard_file::*; use crate::{ client::*, common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, - ui_session_interface::{InvokeUi, Session}, + ui_session_interface::{io_loop, InvokeUi, Remote, Session, SERVER_KEYBOARD_ENABLED}, }; use errno; @@ -69,9 +69,7 @@ fn get_key_state(key: enigo::Key) -> bool { static IS_IN: AtomicBool = AtomicBool::new(false); static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false); -static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true); -static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); + #[cfg(windows)] static mut IS_ALT_GR: bool = false; @@ -82,7 +80,6 @@ static mut IS_ALT_GR: bool = false; #[derive(Clone, Default)] pub struct SciterHandler { element: Arc>>, - thread: Arc>>>, close_state: HashMap, } @@ -128,6 +125,11 @@ impl InvokeUi for SciterHandler { fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { self.call("setDisplay", &make_args!(x, y, w, h)); + VIDEO.lock().unwrap().as_mut().map(|v| { + v.stop_streaming().ok(); + let ok = v.start_streaming((w, h), COLOR_SPACE::Rgb32, None); + log::info!("[video] reinitialized: {:?}", ok); + }); } fn update_privacy_mode(&self) { @@ -214,6 +216,32 @@ impl InvokeUi for SciterHandler { fn adapt_size(&self) { self.call("adaptSize", &make_args!()); } + + fn on_rgba(&self, data: &[u8]) { + VIDEO + .lock() + .unwrap() + .as_mut() + .map(|v| v.render_frame(data).ok()); + } + + fn set_peer_info( + &self, + username: &str, + hostname: &str, + platform: &str, + sas_enabled: bool, + displays: &Vec>, + version: &str, + current_display: usize, + is_file_transfer: bool, + ) { + todo!() + } + + fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool) { + todo!() + } } pub struct SciterSession(Session); @@ -231,32 +259,6 @@ impl DerefMut for SciterSession { } } -// #[derive(Default)] -// pub struct HandlerInner { -// element: Option, -// sender: Option>, -// thread: Option>, -// close_state: HashMap, -// } - -// #[derive(Clone, Default)] -// pub struct Handler { -// inner: Arc>, -// cmd: String, -// id: String, -// password: String, -// args: Vec, -// lc: Arc>, -// } - -// impl Deref for Handler { -// type Target = Arc>; - -// fn deref(&self) -> &Self::Target { -// &self.inner -// } -// } - impl sciter::EventHandler for SciterSession { fn get_subscription(&mut self) -> Option { Some(EVENT_GROUPS::HANDLE_BEHAVIOR_EVENT) @@ -659,14 +661,15 @@ impl SciterSession { }); } - fn get_view_style(&mut self) -> String { - return self.lc.read().unwrap().view_style.clone(); - } + // fn get_view_style(&mut self) -> String { + // return self.lc.read().unwrap().view_style.clone(); + // } - fn get_image_quality(&mut self) -> String { - return self.lc.read().unwrap().image_quality.clone(); - } + // fn get_image_quality(&mut self) -> String { + // return self.lc.read().unwrap().image_quality.clone(); + // } + // TODO fn get_custom_image_quality(&mut self) -> Value { let mut v = Value::array(0); for x in self.lc.read().unwrap().custom_image_quality.iter() { @@ -680,82 +683,83 @@ impl SciterSession { // self.lc.write().unwrap().save_config(config); // } - fn save_view_style(&mut self, value: String) { - self.lc.write().unwrap().save_view_style(value); - } + // fn save_view_style(&mut self, value: String) { + // self.lc.write().unwrap().save_view_style(value); + // } // #[inline] // pub(super) fn load_config(&self) -> PeerConfig { // load_config(&self.id) // } - fn toggle_option(&mut self, name: String) { - let msg = self.lc.write().unwrap().toggle_option(name.clone()); - if name == "enable-file-transfer" { - self.send(Data::ToggleClipboardFile); - } - if let Some(msg) = msg { - self.send(Data::Message(msg)); - } - } + // fn toggle_option(&mut self, name: String) { + // let msg = self.lc.write().unwrap().toggle_option(name.clone()); + // if name == "enable-file-transfer" { + // self.send(Data::ToggleClipboardFile); + // } + // if let Some(msg) = msg { + // self.send(Data::Message(msg)); + // } + // } - fn get_toggle_option(&mut self, name: String) -> bool { - self.lc.read().unwrap().get_toggle_option(&name) - } + // fn get_toggle_option(&mut self, name: String) -> bool { + // self.lc.read().unwrap().get_toggle_option(&name) + // } - fn is_privacy_mode_supported(&self) -> bool { - self.lc.read().unwrap().is_privacy_mode_supported() - } + // fn is_privacy_mode_supported(&self) -> bool { + // self.lc.read().unwrap().is_privacy_mode_supported() + // } - fn refresh_video(&mut self) { - self.send(Data::Message(LoginConfigHandler::refresh())); - } + // fn refresh_video(&mut self) { + // self.send(Data::Message(LoginConfigHandler::refresh())); + // } - fn save_custom_image_quality(&mut self, custom_image_quality: i32) { - let msg = self - .lc - .write() - .unwrap() - .save_custom_image_quality(custom_image_quality); - self.send(Data::Message(msg)); - } + // fn save_custom_image_quality(&mut self, custom_image_quality: i32) { + // let msg = self + // .lc + // .write() + // .unwrap() + // .save_custom_image_quality(custom_image_quality); + // self.send(Data::Message(msg)); + // } - fn save_image_quality(&mut self, value: String) { - let msg = self.lc.write().unwrap().save_image_quality(value); - if let Some(msg) = msg { - self.send(Data::Message(msg)); - } - } + // fn save_image_quality(&mut self, value: String) { + // let msg = self.lc.write().unwrap().save_image_quality(value); + // if let Some(msg) = msg { + // self.send(Data::Message(msg)); + // } + // } - fn get_remember(&mut self) -> bool { - self.lc.read().unwrap().remember - } + // fn get_remember(&mut self) -> bool { + // self.lc.read().unwrap().remember + // } - fn set_write_override( - &mut self, - job_id: i32, - file_num: i32, - is_override: bool, - remember: bool, - is_upload: bool, - ) -> bool { - self.send(Data::SetConfirmOverrideFile(( - job_id, - file_num, - is_override, - remember, - is_upload, - ))); - true - } + // fn set_write_override( + // &mut self, + // job_id: i32, + // file_num: i32, + // is_override: bool, + // remember: bool, + // is_upload: bool, + // ) -> bool { + // self.send(Data::SetConfirmOverrideFile(( + // job_id, + // file_num, + // is_override, + // remember, + // is_upload, + // ))); + // true + // } - fn has_hwcodec(&self) -> bool { - #[cfg(not(feature = "hwcodec"))] - return false; - #[cfg(feature = "hwcodec")] - return true; - } + // fn has_hwcodec(&self) -> bool { + // #[cfg(not(feature = "hwcodec"))] + // return false; + // #[cfg(feature = "hwcodec")] + // return true; + // } + // TODO fn supported_hwcodec(&self) -> Value { #[cfg(feature = "hwcodec")] { @@ -780,51 +784,52 @@ impl SciterSession { } } - fn change_prefer_codec(&self) { - let msg = self.lc.write().unwrap().change_prefer_codec(); - self.send(Data::Message(msg)); - } + // fn change_prefer_codec(&self) { + // let msg = self.lc.write().unwrap().change_prefer_codec(); + // self.send(Data::Message(msg)); + // } - fn restart_remote_device(&mut self) { - let mut lc = self.lc.write().unwrap(); - lc.restarting_remote_device = true; - let msg = lc.restart_remote_device(); - self.send(Data::Message(msg)); - } + // fn restart_remote_device(&mut self) { + // let mut lc = self.lc.write().unwrap(); + // lc.restarting_remote_device = true; + // let msg = lc.restart_remote_device(); + // self.send(Data::Message(msg)); + // } // pub fn is_restarting_remote_device(&self) -> bool { // self.lc.read().unwrap().restarting_remote_device // } - fn t(&self, name: String) -> String { - crate::client::translate(name) - } + // fn t(&self, name: String) -> String { + // crate::client::translate(name) + // } - fn get_audit_server(&self) -> String { - if self.lc.read().unwrap().conn_id <= 0 - || LocalConfig::get_option("access_token").is_empty() - { - return "".to_owned(); - } - crate::get_audit_server( - Config::get_option("api-server"), - Config::get_option("custom-rendezvous-server"), - ) - } + // fn get_audit_server(&self) -> String { + // if self.lc.read().unwrap().conn_id <= 0 + // || LocalConfig::get_option("access_token").is_empty() + // { + // return "".to_owned(); + // } + // crate::get_audit_server( + // Config::get_option("api-server"), + // Config::get_option("custom-rendezvous-server"), + // ) + // } - fn send_note(&self, note: String) { - let url = self.get_audit_server(); - let id = self.id.clone(); - let conn_id = self.lc.read().unwrap().conn_id; - std::thread::spawn(move || { - send_note(url, id, conn_id, note); - }); - } + // fn send_note(&self, note: String) { + // let url = self.get_audit_server(); + // let id = self.id.clone(); + // let conn_id = self.lc.read().unwrap().conn_id; + // std::thread::spawn(move || { + // send_note(url, id, conn_id, note); + // }); + // } - fn is_xfce(&self) -> bool { - crate::platform::is_xfce() - } + // fn is_xfce(&self) -> bool { + // crate::platform::is_xfce() + // } + // TODO fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) { let size = (x, y, w, h); let mut config = self.load_config(); @@ -885,33 +890,33 @@ impl SciterSession { v } - fn remove_port_forward(&mut self, port: i32) { - let mut config = self.load_config(); - config.port_forwards = config - .port_forwards - .drain(..) - .filter(|x| x.0 != port) - .collect(); - self.save_config(config); - self.send(Data::RemovePortForward(port)); - } + // fn remove_port_forward(&mut self, port: i32) { + // let mut config = self.load_config(); + // config.port_forwards = config + // .port_forwards + // .drain(..) + // .filter(|x| x.0 != port) + // .collect(); + // self.save_config(config); + // self.send(Data::RemovePortForward(port)); + // } - fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { - let mut config = self.load_config(); - if config - .port_forwards - .iter() - .filter(|x| x.0 == port) - .next() - .is_some() - { - return; - } - let pf = (port, remote_host, remote_port); - config.port_forwards.push(pf.clone()); - self.save_config(config); - self.send(Data::AddPortForward(pf)); - } + // fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { + // let mut config = self.load_config(); + // if config + // .port_forwards + // .iter() + // .filter(|x| x.0 == port) + // .next() + // .is_some() + // { + // return; + // } + // let pf = (port, remote_host, remote_port); + // config.port_forwards.push(pf.clone()); + // self.save_config(config); + // self.send(Data::AddPortForward(pf)); + // } fn get_size(&mut self) -> Value { let s = if self.is_file_transfer() { @@ -929,9 +934,9 @@ impl SciterSession { v } - fn get_id(&mut self) -> String { - self.id.clone() - } + // fn get_id(&mut self) -> String { + // self.id.clone() + // } fn get_default_pi(&mut self) -> Value { let mut pi = Value::map(); @@ -950,46 +955,47 @@ impl SciterSession { // self.lc.write().unwrap().set_option(k, v); // } - fn input_os_password(&mut self, pass: String, activate: bool) { - input_os_password(pass, activate, self.clone()); - } + // fn input_os_password(&mut self, pass: String, activate: bool) { + // input_os_password(pass, activate, self.clone()); + // } + // close_state sciter only fn save_close_state(&mut self, k: String, v: String) { self.close_state.insert(k, v); } - fn get_chatbox(&mut self) -> String { - #[cfg(feature = "inline")] - return super::inline::get_chatbox(); - #[cfg(not(feature = "inline"))] - return "".to_owned(); - } + // fn get_chatbox(&mut self) -> String { + // #[cfg(feature = "inline")] + // return super::inline::get_chatbox(); + // #[cfg(not(feature = "inline"))] + // return "".to_owned(); + // } - fn get_icon(&mut self) -> String { - crate::get_icon() - } + // fn get_icon(&mut self) -> String { + // crate::get_icon() + // } - fn send_chat(&mut self, text: String) { - let mut misc = Misc::new(); - misc.set_chat_message(ChatMessage { - text, - ..Default::default() - }); - let mut msg_out = Message::new(); - msg_out.set_misc(misc); - self.send(Data::Message(msg_out)); - } + // fn send_chat(&mut self, text: String) { + // let mut misc = Misc::new(); + // misc.set_chat_message(ChatMessage { + // text, + // ..Default::default() + // }); + // let mut msg_out = Message::new(); + // msg_out.set_misc(misc); + // self.send(Data::Message(msg_out)); + // } - fn switch_display(&mut self, display: i32) { - let mut misc = Misc::new(); - misc.set_switch_display(SwitchDisplay { - display, - ..Default::default() - }); - let mut msg_out = Message::new(); - msg_out.set_misc(misc); - self.send(Data::Message(msg_out)); - } + // fn switch_display(&mut self, display: i32) { + // let mut misc = Misc::new(); + // misc.set_switch_display(SwitchDisplay { + // display, + // ..Default::default() + // }); + // let mut msg_out = Message::new(); + // msg_out.set_misc(misc); + // self.send(Data::Message(msg_out)); + // } // fn is_file_transfer(&self) -> bool { // self.cmd == "--file-transfer" @@ -1003,15 +1009,15 @@ impl SciterSession { // self.cmd == "--rdp" // } - fn reconnect(&mut self) { - println!("reconnecting"); - let cloned = self.clone(); - let mut lock = self.thread.lock().unwrap(); - lock.take().map(|t| t.join()); - *lock = Some(std::thread::spawn(move || { - io_loop(cloned); - })); - } + // fn reconnect(&mut self) { + // println!("reconnecting"); + // let cloned = self.clone(); + // let mut lock = self.thread.lock().unwrap(); + // lock.take().map(|t| t.join()); + // *lock = Some(std::thread::spawn(move || { + // io_loop(cloned); + // })); + // } // #[inline] // fn peer_platform(&self) -> String { @@ -1035,63 +1041,63 @@ impl SciterSession { // } // } - fn get_icon_path(&mut self, file_type: i32, ext: String) -> String { - let mut path = Config::icon_path(); - if file_type == FileType::DirLink as i32 { - let new_path = path.join("dir_link"); - if !std::fs::metadata(&new_path).is_ok() { - #[cfg(windows)] - allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); - #[cfg(not(windows))] - allow_err!(std::os::unix::fs::symlink(&path, &new_path)); - } - path = new_path; - } else if file_type == FileType::File as i32 { - if !ext.is_empty() { - path = path.join(format!("file.{}", ext)); - } else { - path = path.join("file"); - } - if !std::fs::metadata(&path).is_ok() { - allow_err!(std::fs::File::create(&path)); - } - } else if file_type == FileType::FileLink as i32 { - let new_path = path.join("file_link"); - if !std::fs::metadata(&new_path).is_ok() { - path = path.join("file"); - if !std::fs::metadata(&path).is_ok() { - allow_err!(std::fs::File::create(&path)); - } - #[cfg(windows)] - allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); - #[cfg(not(windows))] - allow_err!(std::os::unix::fs::symlink(&path, &new_path)); - } - path = new_path; - } else if file_type == FileType::DirDrive as i32 { - if cfg!(windows) { - path = fs::get_path("C:"); - } else if cfg!(target_os = "macos") { - if let Ok(entries) = fs::get_path("/Volumes/").read_dir() { - for entry in entries { - if let Ok(entry) = entry { - path = entry.path(); - break; - } - } - } - } - } - fs::get_string(&path) - } + // fn get_icon_path(&mut self, file_type: i32, ext: String) -> String { + // let mut path = Config::icon_path(); + // if file_type == FileType::DirLink as i32 { + // let new_path = path.join("dir_link"); + // if !std::fs::metadata(&new_path).is_ok() { + // #[cfg(windows)] + // allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); + // #[cfg(not(windows))] + // allow_err!(std::os::unix::fs::symlink(&path, &new_path)); + // } + // path = new_path; + // } else if file_type == FileType::File as i32 { + // if !ext.is_empty() { + // path = path.join(format!("file.{}", ext)); + // } else { + // path = path.join("file"); + // } + // if !std::fs::metadata(&path).is_ok() { + // allow_err!(std::fs::File::create(&path)); + // } + // } else if file_type == FileType::FileLink as i32 { + // let new_path = path.join("file_link"); + // if !std::fs::metadata(&new_path).is_ok() { + // path = path.join("file"); + // if !std::fs::metadata(&path).is_ok() { + // allow_err!(std::fs::File::create(&path)); + // } + // #[cfg(windows)] + // allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); + // #[cfg(not(windows))] + // allow_err!(std::os::unix::fs::symlink(&path, &new_path)); + // } + // path = new_path; + // } else if file_type == FileType::DirDrive as i32 { + // if cfg!(windows) { + // path = fs::get_path("C:"); + // } else if cfg!(target_os = "macos") { + // if let Ok(entries) = fs::get_path("/Volumes/").read_dir() { + // for entry in entries { + // if let Ok(entry) = entry { + // path = entry.path(); + // break; + // } + // } + // } + // } + // } + // fs::get_string(&path) + // } - fn login(&mut self, password: String, remember: bool) { - self.send(Data::Login((password, remember))); - } + // fn login(&mut self, password: String, remember: bool) { + // self.send(Data::Login((password, remember))); + // } - fn new_rdp(&mut self) { - self.send(Data::NewRDP); - } + // fn new_rdp(&mut self) { + // self.send(Data::NewRDP); + // } fn enter(&mut self) { #[cfg(windows)] @@ -1383,1373 +1389,6 @@ impl SciterSession { // } } -const MILLI1: Duration = Duration::from_millis(1); - -async fn start_one_port_forward( - handler: Session, - port: i32, - remote_host: String, - remote_port: i32, - receiver: mpsc::UnboundedReceiver, - key: &str, - token: &str, -) { - if let Err(err) = crate::port_forward::listen( - handler.id.clone(), - handler.password.clone(), - port, - handler.clone(), - receiver, - key, - token, - handler.lc.clone(), - remote_host, - remote_port, - ) - .await - { - handler.on_error(&format!("Failed to listen on {}: {}", port, err)); - } - log::info!("port forward (:{}) exit", port); -} - -#[tokio::main(flavor = "current_thread")] -async fn io_loop(handler: Session) { - let (sender, mut receiver) = mpsc::unbounded_channel::(); - *handler.sender.write().unwrap() = Some(sender.clone()); - let mut options = crate::ipc::get_options_async().await; - let mut key = options.remove("key").unwrap_or("".to_owned()); - let token = LocalConfig::get_option("access_token"); - if key.is_empty() { - key = crate::platform::get_license_key(); - } - if handler.is_port_forward() { - if handler.is_rdp() { - let port = handler - .get_option("rdp_port".to_owned()) - .parse::() - .unwrap_or(3389); - std::env::set_var( - "rdp_username", - handler.get_option("rdp_username".to_owned()), - ); - std::env::set_var( - "rdp_password", - handler.get_option("rdp_password".to_owned()), - ); - log::info!("Remote rdp port: {}", port); - start_one_port_forward(handler, 0, "".to_owned(), port, receiver, &key, &token).await; - } else if handler.args.len() == 0 { - let pfs = handler.lc.read().unwrap().port_forwards.clone(); - let mut queues = HashMap::>::new(); - for d in pfs { - sender.send(Data::AddPortForward(d)).ok(); - } - loop { - match receiver.recv().await { - Some(Data::AddPortForward((port, remote_host, remote_port))) => { - if port <= 0 || remote_port <= 0 { - continue; - } - let (sender, receiver) = mpsc::unbounded_channel::(); - queues.insert(port, sender); - let handler = handler.clone(); - let key = key.clone(); - let token = token.clone(); - tokio::spawn(async move { - start_one_port_forward( - handler, - port, - remote_host, - remote_port, - receiver, - &key, - &token, - ) - .await; - }); - } - Some(Data::RemovePortForward(port)) => { - if let Some(s) = queues.remove(&port) { - s.send(Data::Close).ok(); - } - } - Some(Data::Close) => { - break; - } - Some(d) => { - for (_, s) in queues.iter() { - s.send(d.clone()).ok(); - } - } - _ => {} - } - } - } else { - let port = handler.args[0].parse::().unwrap_or(0); - if handler.args.len() != 3 - || handler.args[2].parse::().unwrap_or(0) <= 0 - || port <= 0 - { - handler.on_error("Invalid arguments, usage:

rustdesk --port-forward remote-id listen-port remote-host remote-port"); - } - let remote_host = handler.args[1].clone(); - let remote_port = handler.args[2].parse::().unwrap_or(0); - start_one_port_forward( - handler, - port, - remote_host, - remote_port, - receiver, - &key, - &token, - ) - .await; - } - return; - } - let frame_count = Arc::new(AtomicUsize::new(0)); - let frame_count_cl = frame_count.clone(); - let (video_sender, audio_sender) = start_video_audio_threads(move |data: &[u8]| { - frame_count_cl.fetch_add(1, Ordering::Relaxed); - VIDEO - .lock() - .unwrap() - .as_mut() - .map(|v| v.render_frame(data).ok()); - }); - - let mut remote = Remote { - handler, - video_sender, - audio_sender, - receiver, - sender, - old_clipboard: Default::default(), - read_jobs: Vec::new(), - write_jobs: Vec::new(), - remove_jobs: Default::default(), - timer: time::interval(SEC30), - last_update_jobs_status: (Instant::now(), Default::default()), - first_frame: false, - #[cfg(windows)] - clipboard_file_context: None, - data_count: Arc::new(AtomicUsize::new(0)), - frame_count, - video_format: CodecFormat::Unknown, - }; - remote.io_loop(&key, &token).await; - remote.sync_jobs_status_to_local().await; -} - -struct RemoveJob { - files: Vec, - path: String, - sep: &'static str, - is_remote: bool, - no_confirm: bool, - last_update_job_status: Instant, -} - -impl RemoveJob { - fn new(files: Vec, path: String, sep: &'static str, is_remote: bool) -> Self { - Self { - files, - path, - sep, - is_remote, - no_confirm: false, - last_update_job_status: Instant::now(), - } - } - - pub fn _gen_meta(&self) -> RemoveJobMeta { - RemoveJobMeta { - path: self.path.clone(), - is_remote: self.is_remote, - no_confirm: self.no_confirm, - } - } -} - -struct Remote { - handler: Session, - video_sender: MediaSender, - audio_sender: MediaSender, - receiver: mpsc::UnboundedReceiver, - sender: mpsc::UnboundedSender, - old_clipboard: Arc>, - read_jobs: Vec, - write_jobs: Vec, - remove_jobs: HashMap, - timer: Interval, - last_update_jobs_status: (Instant, HashMap), - first_frame: bool, - #[cfg(windows)] - clipboard_file_context: Option>, - data_count: Arc, - frame_count: Arc, - video_format: CodecFormat, -} - -impl Remote { - async fn io_loop(&mut self, key: &str, token: &str) { - let stop_clipboard = self.start_clipboard(); - let mut last_recv_time = Instant::now(); - let mut received = false; - let conn_type = if self.handler.is_file_transfer() { - ConnType::FILE_TRANSFER - } else { - ConnType::default() - }; - match Client::start( - &self.handler.id, - key, - token, - conn_type, - self.handler.clone(), - ) - .await - { - Ok((mut peer, direct)) => { - SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); - SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst); - // self.handler - // .call("setConnectionType", &make_args!(peer.is_secured(), direct)); - self.handler.set_connection_type(peer.is_secured(), direct); - - // just build for now - #[cfg(not(windows))] - let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::(); - #[cfg(windows)] - let mut rx_clip_client = get_rx_clip_client().lock().await; - - let mut status_timer = time::interval(Duration::new(1, 0)); - - loop { - tokio::select! { - res = peer.next() => { - if let Some(res) = res { - match res { - Err(err) => { - log::error!("Connection closed: {}", err); - self.handler.set_force_relay(direct, received); - self.handler.msgbox("error", "Connection Error", &err.to_string()); - break; - } - Ok(ref bytes) => { - last_recv_time = Instant::now(); - received = true; - self.data_count.fetch_add(bytes.len(), Ordering::Relaxed); - if !self.handle_msg_from_peer(bytes, &mut peer).await { - break - } - } - } - } else { - if self.handler.is_restarting_remote_device() { - log::info!("Restart remote device"); - self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); - } else { - log::info!("Reset by the peer"); - self.handler.msgbox("error", "Connection Error", "Reset by the peer"); - } - break; - } - } - d = self.receiver.recv() => { - if let Some(d) = d { - if !self.handle_msg_from_ui(d, &mut peer).await { - break; - } - } - } - _msg = rx_clip_client.recv() => { - #[cfg(windows)] - match _msg { - Some((_, clip)) => { - allow_err!(peer.send(&clip_2_msg(clip)).await); - } - None => { - // unreachable!() - } - } - } - _ = self.timer.tick() => { - if last_recv_time.elapsed() >= SEC30 { - self.handler.msgbox("error", "Connection Error", "Timeout"); - break; - } - if !self.read_jobs.is_empty() { - if let Err(err) = fs::handle_read_jobs(&mut self.read_jobs, &mut peer).await { - self.handler.msgbox("error", "Connection Error", &err.to_string()); - break; - } - self.update_jobs_status(); - } else { - self.timer = time::interval_at(Instant::now() + SEC30, SEC30); - } - } - _ = status_timer.tick() => { - let speed = self.data_count.swap(0, Ordering::Relaxed); - let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); - let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; - self.handler.update_quality_status(QualityStatus { - speed:Some(speed), - fps:Some(fps), - ..Default::default() - }); - } - } - } - log::debug!("Exit io_loop of id={}", self.handler.id); - } - Err(err) => { - self.handler - .msgbox("error", "Connection Error", &err.to_string()); - } - } - if let Some(stop) = stop_clipboard { - stop.send(()).ok(); - } - SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); - SERVER_FILE_TRANSFER_ENABLED.store(false, Ordering::SeqCst); - } - - fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { - if let Some(job) = self.remove_jobs.get_mut(&id) { - if job.no_confirm { - let file_num = (file_num + 1) as usize; - if file_num < job.files.len() { - let path = format!("{}{}{}", job.path, job.sep, job.files[file_num].name); - self.sender - .send(Data::RemoveFile((id, path, file_num as i32, job.is_remote))) - .ok(); - let elapsed = job.last_update_job_status.elapsed().as_millis() as i32; - if elapsed >= 1000 { - job.last_update_job_status = Instant::now(); - } else { - return; - } - } else { - self.remove_jobs.remove(&id); - } - } - } - if let Some(err) = err { - // self.handler - // .call("jobError", &make_args!(id, err, file_num)); - self.handler.job_error(id, err, file_num); - } else { - // self.handler.call("jobDone", &make_args!(id, file_num)); - self.handler.job_done(id, file_num); - } - } - - fn start_clipboard(&mut self) -> Option> { - if self.handler.is_file_transfer() || self.handler.is_port_forward() { - return None; - } - let (tx, rx) = std::sync::mpsc::channel(); - let old_clipboard = self.old_clipboard.clone(); - let tx_protobuf = self.sender.clone(); - let lc = self.handler.lc.clone(); - match ClipboardContext::new() { - Ok(mut ctx) => { - // ignore clipboard update before service start - check_clipboard(&mut ctx, Some(&old_clipboard)); - std::thread::spawn(move || loop { - std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); - match rx.try_recv() { - Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { - log::debug!("Exit clipboard service of client"); - break; - } - _ => {} - } - if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) - || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - || lc.read().unwrap().disable_clipboard - { - continue; - } - if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { - tx_protobuf.send(Data::Message(msg)).ok(); - } - }); - } - Err(err) => { - log::error!("Failed to start clipboard service of client: {}", err); - } - } - Some(tx) - } - - async fn load_last_jobs(&mut self) { - log::info!("start load last jobs"); - // self.handler.call("clearAllJobs", &make_args!()); - self.handler.clear_all_jobs(); - let pc = self.handler.load_config(); - if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { - // no last jobs - return; - } - // TODO: can add a confirm dialog - let mut cnt = 1; - for job_str in pc.transfer.read_jobs.iter() { - let job: Result = serde_json::from_str(&job_str); - if let Ok(job) = job { - // self.handler.call( - // "addJob", - // &make_args!( - // cnt, - // job.to.clone(), - // job.remote.clone(), - // job.file_num, - // job.show_hidden, - // false - // ), - // ); - self.handler.add_job( - cnt, - job.to.clone(), - job.remote.clone(), - job.file_num, - job.show_hidden, - false, - ); - cnt += 1; - println!("restore read_job: {:?}", job); - } - } - for job_str in pc.transfer.write_jobs.iter() { - let job: Result = serde_json::from_str(&job_str); - if let Ok(job) = job { - // self.handler.call( - // "addJob", - // &make_args!( - // cnt, - // job.remote.clone(), - // job.to.clone(), - // job.file_num, - // job.show_hidden, - // true - // ), - // ); - self.handler.add_job( - cnt, - job.remote.clone(), - job.to.clone(), - job.file_num, - job.show_hidden, - true, - ); - cnt += 1; - println!("restore write_job: {:?}", job); - } - } - // self.handler.call("updateTransferList", &make_args!()); - self.handler.update_transfer_list(); - } - - async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { - match data { - Data::Close => { - let mut misc = Misc::new(); - misc.set_close_reason("".to_owned()); - let mut msg = Message::new(); - msg.set_misc(misc); - allow_err!(peer.send(&msg).await); - return false; - } - Data::Login((password, remember)) => { - self.handler - .handle_login_from_ui(password, remember, peer) - .await; - } - Data::ToggleClipboardFile => { - self.check_clipboard_file_context(); - } - Data::Message(msg) => { - allow_err!(peer.send(&msg).await); - } - Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { - log::info!("send files, is remote {}", is_remote); - let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); - if is_remote { - log::debug!("New job {}, write to {} from remote {}", id, to, path); - self.write_jobs.push(fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - )); - allow_err!( - peer.send(&fs::new_send(id, path, file_num, include_hidden)) - .await - ); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(job) => { - log::debug!( - "New job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - // let m = make_fd(job.id(), job.files(), true); - // self.handler.call("updateFolderFiles", &make_args!(m)); // TODO - #[cfg(not(windows))] - let files = job.files().clone(); - #[cfg(windows)] - let mut files = job.files().clone(); - #[cfg(windows)] - if self.handler.peer_platform() != "Windows" { - // peer is not windows, need transform \ to / - fs::transform_windows_path(&mut files); - } - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); - } - } - } - } - Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { - let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); - if is_remote { - log::debug!( - "new write waiting job {}, write to {} from remote {}", - id, - to, - path - ); - let mut job = fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - ); - job.is_last_job = true; - self.write_jobs.push(job); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(mut job) => { - log::debug!( - "new read waiting job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - // let m = make_fd(job.id(), job.files(), true); - // self.handler.call("updateFolderFiles", &make_args!(m)); - job.is_last_job = true; - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - } - } - } - } - Data::ResumeJob((id, is_remote)) => { - if is_remote { - if let Some(job) = get_job(id, &mut self.write_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_send( - id, - job.remote.clone(), - job.file_num, - job.show_hidden - )) - .await - ); - } - } else { - if let Some(job) = get_job(id, &mut self.read_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_receive( - id, - job.path.to_string_lossy().to_string(), - job.file_num, - job.files.clone() - )) - .await - ); - } - } - } - Data::SetNoConfirm(id) => { - if let Some(job) = self.remove_jobs.get_mut(&id) { - job.no_confirm = true; - } - } - Data::ConfirmDeleteFiles((id, file_num)) => { - if let Some(job) = self.remove_jobs.get_mut(&id) { - let i = file_num as usize; - if i < job.files.len() { - // self.handler.call( - // "confirmDeleteFiles", - // &make_args!(id, file_num, job.files[i].name.clone()), - // ); - self.handler.confirm_delete_files(id, file_num); - } - } - } - Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { - if is_upload { - if let Some(job) = fs::get_job(id, &mut self.read_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - job.confirm(&FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - } - } else { - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - let mut msg = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_send_confirm(FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - msg.set_file_action(file_action); - allow_err!(peer.send(&msg).await); - } - } - } - Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { - let sep = self.handler.get_path_sep(is_remote); - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_all_files(ReadAllFiles { - id, - path: path.clone(), - include_hidden, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - self.remove_jobs - .insert(id, RemoveJob::new(Vec::new(), path, sep, is_remote)); - } else { - match fs::get_recursive_files(&path, include_hidden) { - Ok(entries) => { - // let m = make_fd(id, &entries, true); - // self.handler.call("updateFolderFiles", &make_args!(m)); - self.remove_jobs - .insert(id, RemoveJob::new(entries, path, sep, is_remote)); - } - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - } - } - } - Data::CancelJob(id) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_cancel(FileTransferCancel { - id: id, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - job.remove_download_file(); - fs::remove_job(id, &mut self.write_jobs); - } - fs::remove_job(id, &mut self.read_jobs); - self.remove_jobs.remove(&id); - } - Data::RemoveDir((id, path)) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_dir(FileRemoveDir { - id, - path, - recursive: true, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } - Data::RemoveFile((id, path, file_num, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_file(FileRemoveFile { - id, - path, - file_num, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::remove_file(&path) { - Err(err) => { - self.handle_job_status(id, file_num, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, file_num, None); - } - } - } - } - Data::CreateDir((id, path, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_create(FileDirCreate { - id, - path, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::create_dir(&path) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, -1, None); - } - } - } - } - _ => {} - } - true - } - - #[inline] - fn update_job_status( - job: &fs::TransferJob, - elapsed: i32, - last_update_jobs_status: &mut (Instant, HashMap), - handler: &mut Session, - ) { - if elapsed <= 0 { - return; - } - let transferred = job.transferred(); - let last_transferred = { - if let Some(v) = last_update_jobs_status.1.get(&job.id()) { - v.to_owned() - } else { - 0 - } - }; - last_update_jobs_status.1.insert(job.id(), transferred); - let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); - let file_num = job.file_num() - 1; - // handler.call( - // "jobProgress", - // &make_args!(job.id(), file_num, speed, job.finished_size() as f64), - // ); - handler.job_progress(job.id(), file_num, speed, job.finished_size() as f64); - } - - fn update_jobs_status(&mut self) { - let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; - if elapsed >= 1000 { - for job in self.read_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &mut self.handler, - ); - } - for job in self.write_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &mut self.handler, - ); - } - self.last_update_jobs_status.0 = Instant::now(); - } - } - - async fn sync_jobs_status_to_local(&mut self) -> bool { - log::info!("sync transfer job status"); - let mut config: PeerConfig = self.handler.load_config(); - let mut transfer_metas = TransferSerde::default(); - for job in self.read_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); - transfer_metas.read_jobs.push(json_str); - } - for job in self.write_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); - transfer_metas.write_jobs.push(json_str); - } - log::info!("meta: {:?}", transfer_metas); - config.transfer = transfer_metas; - self.handler.save_config(config); - true - } - - async fn send_opts_after_login(&self, peer: &mut Stream) { - if let Some(opts) = self - .handler - .lc - .read() - .unwrap() - .get_option_message_after_login() - { - let mut misc = Misc::new(); - misc.set_option(opts); - let mut msg_out = Message::new(); - msg_out.set_misc(misc); - allow_err!(peer.send(&msg_out).await); - } - } - - async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { - if let Ok(msg_in) = Message::parse_from_bytes(&data) { - match msg_in.union { - Some(message::Union::VideoFrame(vf)) => { - if !self.first_frame { - self.first_frame = true; - // self.handler.call2("closeSuccess", &make_args!()); - self.handler.close_success(); - // self.handler.call("adaptSize", &make_args!()); - self.handler.adapt_size(); - self.send_opts_after_login(peer).await; - } - let incomming_format = CodecFormat::from(&vf); - if self.video_format != incomming_format { - self.video_format = incomming_format.clone(); - self.handler.update_quality_status(QualityStatus { - codec_format: Some(incomming_format), - ..Default::default() - }) - }; - self.video_sender.send(MediaData::VideoFrame(vf)).ok(); - } - Some(message::Union::Hash(hash)) => { - self.handler - .handle_hash(&self.handler.password.clone(), hash, peer) - .await; - } - Some(message::Union::LoginResponse(lr)) => match lr.union { - Some(login_response::Union::Error(err)) => { - if !self.handler.handle_login_error(&err) { - return false; - } - } - Some(login_response::Union::PeerInfo(pi)) => { - self.handler.handle_peer_info(pi); - self.check_clipboard_file_context(); - if !(self.handler.is_file_transfer() - || self.handler.is_port_forward() - || !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) - || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - || self.handler.lc.read().unwrap().disable_clipboard) - { - let txt = self.old_clipboard.lock().unwrap().clone(); - if !txt.is_empty() { - let msg_out = crate::create_clipboard_msg(txt); - let sender = self.sender.clone(); - tokio::spawn(async move { - // due to clipboard service interval time - sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; - sender.send(Data::Message(msg_out)).ok(); - }); - } - } - - if self.handler.is_file_transfer() { - self.load_last_jobs().await; - } - } - _ => {} - }, - Some(message::Union::CursorData(cd)) => { - self.handler.set_cursor_data(cd); - } - Some(message::Union::CursorId(id)) => { - self.handler.set_cursor_id(id.to_string()); - } - Some(message::Union::CursorPosition(cp)) => { - self.handler.set_cursor_position(cp); - } - Some(message::Union::Clipboard(cb)) => { - if !self.handler.lc.read().unwrap().disable_clipboard { - update_clipboard(cb, Some(&self.old_clipboard)); - } - } - #[cfg(windows)] - Some(message::Union::Cliprdr(clip)) => { - if !self.handler.lc.read().unwrap().disable_clipboard { - if let Some(context) = &mut self.clipboard_file_context { - if let Some(clip) = msg_2_clip(clip) { - server_clip_file(context, 0, clip); - } - } - } - } - Some(message::Union::FileResponse(fr)) => { - match fr.union { - Some(file_response::Union::Dir(fd)) => { - #[cfg(windows)] - let entries = fd.entries.to_vec(); - #[cfg(not(windows))] - let mut entries = fd.entries.to_vec(); - #[cfg(not(windows))] - { - if self.handler.peer_platform() == "Windows" { - fs::transform_windows_path(&mut entries); - } - } - // let mut m = make_fd(fd.id, &entries, fd.id > 0); - // if fd.id <= 0 { - // m.set_item("path", fd.path); - // } - // self.handler.call("updateFolderFiles", &make_args!(m)); - if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) { - log::info!("job set_files: {:?}", entries); - job.set_files(entries); - } else if let Some(job) = self.remove_jobs.get_mut(&fd.id) { - job.files = entries; - } - } - Some(file_response::Union::Digest(digest)) => { - if digest.is_upload { - if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let read_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - if let Some(overwrite) = overwrite_strategy { - let req = FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip( - true, - ) - }), - ..Default::default() - }; - job.confirm(&req); - let msg = new_send_confirm(req); - allow_err!(peer.send(&msg).await); - } else { - // self.handler.call( - // "overrideFileConfirm", - // &make_args!( - // digest.id, - // digest.file_num, - // read_path, - // true - // ), - // ); - self.handler.override_file_confirm( - digest.id, - digest.file_num, - read_path, - true, - ); - } - } - } - } else { - if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let write_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - match fs::is_write_need_confirmation(&write_path, &digest) { - Ok(res) => match res { - DigestCheckResult::IsSame => { - let msg= new_send_confirm(FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::Skip(true)), - ..Default::default() - }); - allow_err!(peer.send(&msg).await); - } - DigestCheckResult::NeedConfirm(digest) => { - if let Some(overwrite) = overwrite_strategy { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip(true) - }), - ..Default::default() - }, - ); - allow_err!(peer.send(&msg).await); - } else { - // self.handler.call( - // "overrideFileConfirm", - // &make_args!( - // digest.id, - // digest.file_num, - // write_path, - // false - // ), - // ); - self.handler.override_file_confirm( - digest.id, - digest.file_num, - write_path, - false, - ); - } - } - DigestCheckResult::NoSuchFile => { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), - ..Default::default() - }, - ); - allow_err!(peer.send(&msg).await); - } - }, - Err(err) => { - println!("error recving digest: {}", err); - } - } - } - } - } - } - Some(file_response::Union::Block(block)) => { - log::info!( - "file response block, file id:{}, file num: {}", - block.id, - block.file_num - ); - if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { - if let Err(_err) = job.write(block, None).await { - // to-do: add "skip" for writing job - } - self.update_jobs_status(); - } - } - Some(file_response::Union::Done(d)) => { - if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { - job.modify_time(); - fs::remove_job(d.id, &mut self.write_jobs); - } - self.handle_job_status(d.id, d.file_num, None); - } - Some(file_response::Union::Error(e)) => { - self.handle_job_status(e.id, e.file_num, Some(e.error)); - } - _ => {} - } - } - Some(message::Union::Misc(misc)) => match misc.union { - Some(misc::Union::AudioFormat(f)) => { - self.audio_sender.send(MediaData::AudioFormat(f)).ok(); - } - Some(misc::Union::ChatMessage(c)) => { - // self.handler.call("newMessage", &make_args!(c.text)); // TODO - } - Some(misc::Union::PermissionInfo(p)) => { - log::info!("Change permission {:?} -> {}", p.permission, p.enabled); - match p.permission.enum_value_or_default() { - Permission::Keyboard => { - SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - // self.handler - // .call2("setPermission", &make_args!("keyboard", p.enabled)); - self.handler.set_permission("keyboard", p.enabled); - } - Permission::Clipboard => { - SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - // self.handler - // .call2("setPermission", &make_args!("clipboard", p.enabled)); - self.handler.set_permission("clipboard", p.enabled); - } - Permission::Audio => { - // self.handler - // .call2("setPermission", &make_args!("audio", p.enabled)); - self.handler.set_permission("audio", p.enabled); - } - Permission::File => { - SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst); - if !p.enabled && self.handler.is_file_transfer() { - return true; - } - self.check_clipboard_file_context(); - // self.handler - // .call2("setPermission", &make_args!("file", p.enabled)); - self.handler.set_permission("file", p.enabled); - } - Permission::Restart => { - // self.handler - // .call2("setPermission", &make_args!("restart", p.enabled)); - self.handler.set_permission("restart", p.enabled); - } - } - } - Some(misc::Union::SwitchDisplay(s)) => { - // self.handler.call("switchDisplay", &make_args!(s.display)); // TODO - self.video_sender.send(MediaData::Reset).ok(); - if s.width > 0 && s.height > 0 { - VIDEO.lock().unwrap().as_mut().map(|v| { - v.stop_streaming().ok(); - let ok = v.start_streaming( - (s.width, s.height), - COLOR_SPACE::Rgb32, - None, - ); - log::info!("[video] reinitialized: {:?}", ok); - }); - self.handler.set_display(s.x, s.y, s.width, s.height); - } - } - Some(misc::Union::CloseReason(c)) => { - self.handler.msgbox("error", "Connection Error", &c); - return false; - } - Some(misc::Union::BackNotification(notification)) => { - if !self.handle_back_notification(notification).await { - return false; - } - } - _ => {} - }, - Some(message::Union::TestDelay(t)) => { - self.handler.handle_test_delay(t, peer).await; - } - Some(message::Union::AudioFrame(frame)) => { - if !self.handler.lc.read().unwrap().disable_audio { - self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); - } - } - Some(message::Union::FileAction(action)) => match action.union { - Some(file_action::Union::SendConfirm(c)) => { - if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { - job.confirm(&c); - } - } - _ => {} - }, - _ => {} - } - } - true - } - - async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { - match notification.union { - Some(back_notification::Union::BlockInputState(state)) => { - self.handle_back_msg_block_input( - state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), - ) - .await; - } - Some(back_notification::Union::PrivacyModeState(state)) => { - if !self - .handle_back_msg_privacy_mode( - state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), - ) - .await - { - return false; - } - } - _ => {} - } - true - } - - #[inline(always)] - fn update_block_input_state(&mut self, on: bool) { - // self.handler.call("updateBlockInputState", &make_args!(on)); // TODO - } - - async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { - match state { - back_notification::BlockInputState::BlkOnSucceeded => { - self.update_block_input_state(true); - } - back_notification::BlockInputState::BlkOnFailed => { - self.handler - .msgbox("custom-error", "Block user input", "Failed"); - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffSucceeded => { - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffFailed => { - self.handler - .msgbox("custom-error", "Unblock user input", "Failed"); - } - _ => {} - } - } - - #[inline(always)] - fn update_privacy_mode(&mut self, on: bool) { - let mut config = self.handler.load_config(); - config.privacy_mode = on; - self.handler.save_config(config); - - // self.handler.call("updatePrivacyMode", &[]); - self.handler.update_privacy_mode(); - } - - async fn handle_back_msg_privacy_mode( - &mut self, - state: back_notification::PrivacyModeState, - ) -> bool { - match state { - back_notification::PrivacyModeState::PrvOnByOther => { - self.handler.msgbox( - "error", - "Connecting...", - "Someone turns on privacy mode, exit", - ); - return false; - } - back_notification::PrivacyModeState::PrvNotSupported => { - self.handler - .msgbox("custom-error", "Privacy mode", "Unsupported"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnSucceeded => { - self.handler - .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); - self.update_privacy_mode(true); - } - back_notification::PrivacyModeState::PrvOnFailedDenied => { - self.handler - .msgbox("custom-error", "Privacy mode", "Peer denied"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailedPlugin => { - self.handler - .msgbox("custom-error", "Privacy mode", "Please install plugins"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailed => { - self.handler - .msgbox("custom-error", "Privacy mode", "Failed"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffSucceeded => { - self.handler - .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffByPeer => { - self.handler - .msgbox("custom-error", "Privacy mode", "Peer exit"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffFailed => { - self.handler - .msgbox("custom-error", "Privacy mode", "Failed to turn off"); - } - back_notification::PrivacyModeState::PrvOffUnknown => { - self.handler - .msgbox("custom-error", "Privacy mode", "Turned off"); - // log::error!("Privacy mode is turned off with unknown reason"); - self.update_privacy_mode(false); - } - _ => {} - } - true - } - - fn check_clipboard_file_context(&mut self) { - #[cfg(windows)] - { - let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) - && self.handler.lc.read().unwrap().enable_file_transfer; - if enabled == self.clipboard_file_context.is_none() { - self.clipboard_file_context = if enabled { - match create_clipboard_file_context(true, false) { - Ok(context) => { - log::info!("clipboard context for file transfer created."); - Some(context) - } - Err(err) => { - log::error!( - "Create clipboard context for file transfer: {}", - err.to_string() - ); - None - } - } - } else { - log::info!("clipboard context for file transfer destroyed."); - None - }; - } - } - } -} - pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value { let mut m = Value::map(); m.set_item("id", id); @@ -2776,141 +1415,3 @@ pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value { m.set_item("total_size", n as f64); m } - -// #[async_trait] -// impl Interface for Handler { -// fn send(&self, data: Data) { -// if let Some(ref sender) = self.read().unwrap().sender { -// sender.send(data).ok(); -// } -// } - -// fn msgbox(&self, msgtype: &str, title: &str, text: &str) { -// let retry = check_if_retry(msgtype, title, text); -// self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); -// } - -// fn handle_login_error(&mut self, err: &str) -> bool { -// self.lc.write().unwrap().handle_login_error(err, self) -// } - -// fn handle_peer_info(&mut self, pi: PeerInfo) { -// let mut pi_sciter = Value::map(); -// let username = self.lc.read().unwrap().get_username(&pi); -// pi_sciter.set_item("username", username.clone()); -// pi_sciter.set_item("hostname", pi.hostname.clone()); -// pi_sciter.set_item("platform", pi.platform.clone()); -// pi_sciter.set_item("sas_enabled", pi.sas_enabled); -// if get_version_number(&pi.version) < get_version_number("1.1.10") { -// self.call2("setPermission", &make_args!("restart", false)); -// } -// if self.is_file_transfer() { -// if pi.username.is_empty() { -// self.on_error("No active console user logged on, please connect and logon first."); -// return; -// } -// } else if !self.is_port_forward() { -// if pi.displays.is_empty() { -// self.lc.write().unwrap().handle_peer_info(username, pi); -// self.call("updatePrivacyMode", &[]); -// self.msgbox("error", "Remote Error", "No Display"); -// return; -// } -// let mut displays = Value::array(0); -// for ref d in pi.displays.iter() { -// let mut display = Value::map(); -// display.set_item("x", d.x); -// display.set_item("y", d.y); -// display.set_item("width", d.width); -// display.set_item("height", d.height); -// displays.push(display); -// } -// pi_sciter.set_item("displays", displays); -// let mut current = pi.current_display as usize; -// if current >= pi.displays.len() { -// current = 0; -// } -// pi_sciter.set_item("current_display", current as i32); -// let current = &pi.displays[current]; -// self.set_display(current.x, current.y, current.width, current.height); -// // https://sciter.com/forums/topic/color_spaceiyuv-crash -// // Nothing spectacular in decoder – done on CPU side. -// // So if you can do BGRA translation on your side – the better. -// // BGRA is used as internal image format so it will not require additional transformations. -// VIDEO.lock().unwrap().as_mut().map(|v| { -// let ok = v.start_streaming( -// (current.width as _, current.height as _), -// COLOR_SPACE::Rgb32, -// None, -// ); -// log::info!("[video] initialized: {:?}", ok); -// }); -// let p = self.lc.read().unwrap().should_auto_login(); -// if !p.is_empty() { -// input_os_password(p, true, self.clone()); -// } -// } -// self.lc.write().unwrap().handle_peer_info(username, pi); -// self.call("updatePrivacyMode", &[]); -// self.call("updatePi", &make_args!(pi_sciter)); -// if self.is_file_transfer() { -// self.call2("closeSuccess", &make_args!()); -// } else if !self.is_port_forward() { -// self.msgbox("success", "Successful", "Connected, waiting for image..."); -// } -// #[cfg(windows)] -// { -// let mut path = std::env::temp_dir(); -// path.push(&self.id); -// let path = path.with_extension(crate::get_app_name().to_lowercase()); -// std::fs::File::create(&path).ok(); -// if let Some(path) = path.to_str() { -// crate::platform::windows::add_recent_document(&path); -// } -// } -// self.start_keyboard_hook(); -// } - -// async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { -// handle_hash(self.lc.clone(), pass, hash, self, peer).await; -// } - -// async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { -// handle_login_from_ui(self.lc.clone(), password, remember, peer).await; -// } - -// async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { -// if !t.from_client { -// self.update_quality_status(QualityStatus { -// delay: Some(t.last_delay as _), -// target_bitrate: Some(t.target_bitrate as _), -// ..Default::default() -// }); -// handle_test_delay(t, peer).await; -// } -// } - -// fn set_force_relay(&mut self, direct: bool, received: bool) { -// let mut lc = self.lc.write().unwrap(); -// lc.force_relay = false; -// if direct && !received { -// let errno = errno::errno().0; -// log::info!("errno is {}", errno); -// // TODO: check mac and ios -// if cfg!(windows) && errno == 10054 || !cfg!(windows) && errno == 104 { -// lc.force_relay = true; -// lc.set_option("force-always-relay".to_owned(), "Y".to_owned()); -// } -// } -// } - -// fn is_force_relay(&self) -> bool { -// self.lc.read().unwrap().force_relay -// } -// } - -#[tokio::main(flavor = "current_thread")] -async fn send_note(url: String, id: String, conn_id: i32, note: String) { - let body = serde_json::json!({ "id": id, "Id": conn_id, "note": note }); - allow_err!(crate::post_request(url, body.to_string(), "").await); -} diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 8f7a0b904..a8871fac1 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1,19 +1,33 @@ use crate::client::{ self, check_if_retry, handle_hash, handle_login_from_ui, handle_test_delay, input_os_password, - FileManager, LoginConfigHandler, QualityStatus, load_config, + load_config, start_video_audio_threads, Client, CodecFormat, FileManager, LoginConfigHandler, + MediaData, MediaSender, QualityStatus, SEC30, }; +use crate::common::{ + self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL, +}; +use crate::platform; use crate::{client::Data, client::Interface}; use async_trait::async_trait; -use hbb_common::config::PeerConfig; -use hbb_common::message_proto::{CursorData, Hash, PeerInfo, TestDelay, CursorPosition}; +use hbb_common::config::{Config, LocalConfig, PeerConfig, TransferSerde}; +use hbb_common::fs::{ + can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, + RemoveJobMeta, TransferJobMeta, +}; +use hbb_common::message_proto::permission_info::Permission; +use hbb_common::protobuf::Message as _; +use hbb_common::rendezvous_proto::ConnType; use hbb_common::tokio::{ self, sync::mpsc, time::{self, Duration, Instant, Interval}, }; -use hbb_common::{get_version_number, log, Stream}; +use hbb_common::{allow_err, message_proto::*, sleep}; +use hbb_common::{fs, get_version_number, log, Stream}; +use std::collections::HashMap; use std::ops::{Deref, DerefMut}; -use std::sync::{Arc, RwLock}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex, RwLock}; #[derive(Clone, Default)] pub struct Session { @@ -23,18 +37,173 @@ pub struct Session { pub args: Vec, pub lc: Arc>, pub sender: Arc>>>, + pub thread: Arc>>>, pub ui_handler: T, } impl Session { + pub fn get_view_style(&self) -> String { + return self.lc.read().unwrap().view_style.clone(); + } + + pub fn get_image_quality(&self) -> String { + return self.lc.read().unwrap().image_quality.clone(); + } + + pub fn save_view_style(&mut self, value: String) { + self.lc.write().unwrap().save_view_style(value); + } + + pub fn toggle_option(&mut self, name: String) { + let msg = self.lc.write().unwrap().toggle_option(name.clone()); + if name == "enable-file-transfer" { + self.send(Data::ToggleClipboardFile); + } + if let Some(msg) = msg { + self.send(Data::Message(msg)); + } + } + + pub fn get_toggle_option(&self, name: String) -> bool { + let res = self.lc.read().unwrap().get_toggle_option(&name); + return res; + } + + pub fn is_privacy_mode_supported(&self) -> bool { + self.lc.read().unwrap().is_privacy_mode_supported() + } + + pub fn refresh_video(&self) { + self.send(Data::Message(LoginConfigHandler::refresh())); + } + + pub fn save_custom_image_quality(&mut self, custom_image_quality: i32) { + let msg = self + .lc + .write() + .unwrap() + .save_custom_image_quality(custom_image_quality); + self.send(Data::Message(msg)); + } + + pub fn save_image_quality(&mut self, value: String) { + let msg = self.lc.write().unwrap().save_image_quality(value); + if let Some(msg) = msg { + self.send(Data::Message(msg)); + } + } + + pub fn get_remember(&self) -> bool { + self.lc.read().unwrap().remember + } + + pub fn set_write_override( + &mut self, + job_id: i32, + file_num: i32, + is_override: bool, + remember: bool, + is_upload: bool, + ) -> bool { + self.send(Data::SetConfirmOverrideFile(( + job_id, + file_num, + is_override, + remember, + is_upload, + ))); + true + } + + pub fn has_hwcodec(&self) -> bool { + #[cfg(not(feature = "hwcodec"))] + return false; + #[cfg(feature = "hwcodec")] + return true; + } + + pub fn change_prefer_codec(&self) { + let msg = self.lc.write().unwrap().change_prefer_codec(); + self.send(Data::Message(msg)); + } + + pub fn restart_remote_device(&self) { + let mut lc = self.lc.write().unwrap(); + lc.restarting_remote_device = true; + let msg = lc.restart_remote_device(); + self.send(Data::Message(msg)); + } + + pub fn t(&self, name: String) -> String { + crate::client::translate(name) + } + + pub fn get_audit_server(&self) -> String { + if self.lc.read().unwrap().conn_id <= 0 + || LocalConfig::get_option("access_token").is_empty() + { + return "".to_owned(); + } + crate::get_audit_server( + Config::get_option("api-server"), + Config::get_option("custom-rendezvous-server"), + ) + } + + pub fn send_note(&self, note: String) { + let url = self.get_audit_server(); + let id = self.id.clone(); + let conn_id = self.lc.read().unwrap().conn_id; + std::thread::spawn(move || { + send_note(url, id, conn_id, note); + }); + } + + pub fn is_xfce(&self) -> bool { + crate::platform::is_xfce() + } + + pub fn remove_port_forward(&self, port: i32) { + let mut config = self.load_config(); + config.port_forwards = config + .port_forwards + .drain(..) + .filter(|x| x.0 != port) + .collect(); + self.save_config(config); + self.send(Data::RemovePortForward(port)); + } + + pub fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { + let mut config = self.load_config(); + if config + .port_forwards + .iter() + .filter(|x| x.0 == port) + .next() + .is_some() + { + return; + } + let pf = (port, remote_host, remote_port); + config.port_forwards.push(pf.clone()); + self.save_config(config); + self.send(Data::AddPortForward(pf)); + } + + pub fn get_id(&self) -> String { + self.id.clone() + } + pub fn get_option(&self, k: String) -> String { - self.lc.read().unwrap().get_option(&k) + let res = self.lc.read().unwrap().get_option(&k); + return res; } pub fn set_option(&self, k: String, v: String) { - self.lc.write().unwrap().set_option(k, v); + self.lc.write().unwrap().set_option(k.clone(), v); } - + #[inline] pub fn load_config(&self) -> PeerConfig { load_config(&self.id) @@ -54,7 +223,7 @@ impl Session { self.lc.read().unwrap().info.platform.clone() } - pub fn get_platform(&mut self, is_remote: bool) -> String { + pub fn get_platform(&self, is_remote: bool) -> String { if is_remote { self.peer_platform() } else { @@ -62,7 +231,7 @@ impl Session { } } - pub fn get_path_sep(&mut self, is_remote: bool) -> &'static str { + pub fn get_path_sep(&self, is_remote: bool) -> &'static str { let p = self.get_platform(is_remote); if &p == "Windows" { return "\\"; @@ -70,32 +239,161 @@ impl Session { return "/"; } } + + pub fn input_os_password(&self, pass: String, activate: bool) { + input_os_password(pass, activate, self.clone()); + } + + pub fn get_chatbox(&self) -> String { + #[cfg(feature = "inline")] + return super::inline::get_chatbox(); + #[cfg(not(feature = "inline"))] + return "".to_owned(); + } + + pub fn get_icon(&self) -> String { + crate::get_icon() + } + + pub fn send_chat(&self, text: String) { + let mut misc = Misc::new(); + misc.set_chat_message(ChatMessage { + text, + ..Default::default() + }); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + self.send(Data::Message(msg_out)); + } + + pub fn switch_display(&self, display: i32) { + let mut misc = Misc::new(); + misc.set_switch_display(SwitchDisplay { + display, + ..Default::default() + }); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + self.send(Data::Message(msg_out)); + } + + pub fn reconnect(&self) { + println!("reconnecting"); + let cloned = self.clone(); + let mut lock = self.thread.lock().unwrap(); + lock.take().map(|t| t.join()); + *lock = Some(std::thread::spawn(move || { + io_loop(cloned); + })); + } + + pub fn get_icon_path(&self, file_type: i32, ext: String) -> String { + let mut path = Config::icon_path(); + if file_type == FileType::DirLink as i32 { + let new_path = path.join("dir_link"); + if !std::fs::metadata(&new_path).is_ok() { + #[cfg(windows)] + allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); + #[cfg(not(windows))] + allow_err!(std::os::unix::fs::symlink(&path, &new_path)); + } + path = new_path; + } else if file_type == FileType::File as i32 { + if !ext.is_empty() { + path = path.join(format!("file.{}", ext)); + } else { + path = path.join("file"); + } + if !std::fs::metadata(&path).is_ok() { + allow_err!(std::fs::File::create(&path)); + } + } else if file_type == FileType::FileLink as i32 { + let new_path = path.join("file_link"); + if !std::fs::metadata(&new_path).is_ok() { + path = path.join("file"); + if !std::fs::metadata(&path).is_ok() { + allow_err!(std::fs::File::create(&path)); + } + #[cfg(windows)] + allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); + #[cfg(not(windows))] + allow_err!(std::os::unix::fs::symlink(&path, &new_path)); + } + path = new_path; + } else if file_type == FileType::DirDrive as i32 { + if cfg!(windows) { + path = fs::get_path("C:"); + } else if cfg!(target_os = "macos") { + if let Ok(entries) = fs::get_path("/Volumes/").read_dir() { + for entry in entries { + if let Ok(entry) = entry { + path = entry.path(); + break; + } + } + } + } + } + fs::get_string(&path) + } + + pub fn login(&self, password: String, remember: bool) { + self.send(Data::Login((password, remember))); + } + + pub fn new_rdp(&self) { + self.send(Data::NewRDP); + } + + pub fn close(&self) { + self.send(Data::Close); + } } pub trait InvokeUi: Send + Sync + Clone + 'static + Sized + Default { fn set_cursor_data(&self, cd: CursorData); fn set_cursor_id(&self, id: String); - fn set_cursor_position(&self, cp:CursorPosition); + fn set_cursor_position(&self, cp: CursorPosition); fn set_display(&self, x: i32, y: i32, w: i32, h: i32); + fn set_peer_info( + &self, + username: &str, + hostname: &str, + platform: &str, + sas_enabled: bool, + displays: &Vec>, + version: &str, + current_display: usize, + is_file_transfer: bool, + ); // flutter fn update_privacy_mode(&self); fn set_permission(&self, name: &str, value: bool); fn update_pi(&self, pi: PeerInfo); fn close_success(&self); fn update_quality_status(&self, qs: QualityStatus); - fn set_connection_type(&self,is_secured: bool, direct: bool); - fn job_error(&self,id:i32, err:String, file_num:i32); - fn job_done(&self,id:i32, file_num:i32); + fn set_connection_type(&self, is_secured: bool, direct: bool); + fn job_error(&self, id: i32, err: String, file_num: i32); + fn job_done(&self, id: i32, file_num: i32); fn clear_all_jobs(&self); - fn add_job(&self, id:i32, path:String, to:String, file_num:i32, show_hidden:bool, is_remote:bool); + fn add_job( + &self, + id: i32, + path: String, + to: String, + file_num: i32, + show_hidden: bool, + is_remote: bool, + ); fn update_transfer_list(&self); // fn update_folder_files(&self); // TODO - fn confirm_delete_files(&self,id:i32, i:i32, name:String); - fn override_file_confirm(&self, id:i32, file_num:i32, to:String, is_upload:bool); - fn job_progress(&self, id:i32, file_num:i32, speed:f64, finished_size:f64); + fn confirm_delete_files(&self, id: i32, i: i32, name: String); + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool); + fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64); fn adapt_size(&self); + fn on_rgba(&self, data: &[u8]); + fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool); } - impl Deref for Session { type Target = T; @@ -135,6 +433,7 @@ impl Interface for Session { fn msgbox(&self, msgtype: &str, title: &str, text: &str) { let retry = check_if_retry(msgtype, title, text); // self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); + self.ui_handler.msgbox(msgtype, title, text, retry); } fn handle_login_error(&mut self, err: &str) -> bool { @@ -142,8 +441,15 @@ impl Interface for Session { } fn handle_peer_info(&mut self, pi: PeerInfo) { + let mut lc = self.lc.write().unwrap(); + // let mut pi_sciter = Value::map(); - let username = self.lc.read().unwrap().get_username(&pi); + let username = lc.get_username(&pi); + + // flutter + let mut displays = Vec::new(); + let mut current_index = pi.current_display as usize; + // pi_sciter.set_item("username", username.clone()); // pi_sciter.set_item("hostname", pi.hostname.clone()); // pi_sciter.set_item("platform", pi.platform.clone()); @@ -158,7 +464,7 @@ impl Interface for Session { } } else if !self.is_port_forward() { if pi.displays.is_empty() { - self.lc.write().unwrap().handle_peer_info(username, pi); + lc.handle_peer_info(username, pi); self.update_privacy_mode(); self.msgbox("error", "Remote Error", "No Display"); return; @@ -173,13 +479,38 @@ impl Interface for Session { // displays.push(display); // } // pi_sciter.set_item("displays", displays); - let mut current = pi.current_display as usize; - if current >= pi.displays.len() { - current = 0; + + // flutter + for ref d in pi.displays.iter() { + let mut h: HashMap<&str, i32> = Default::default(); + h.insert("x", d.x); + h.insert("y", d.y); + h.insert("width", d.width); + h.insert("height", d.height); + displays.push(h); + } + if current_index >= pi.displays.len() { + current_index = 0; + } + + if current_index >= pi.displays.len() { + current_index = 0; } // pi_sciter.set_item("current_display", current as i32); - let current = &pi.displays[current]; + let current = &pi.displays[current_index]; self.set_display(current.x, current.y, current.width, current.height); + + self.set_peer_info( + &username, + &pi.hostname, + &pi.platform, + pi.sas_enabled, + &displays, + &pi.version, + current_index, + lc.is_file_transfer, + ); + // https://sciter.com/forums/topic/color_spaceiyuv-crash // Nothing spectacular in decoder – done on CPU side. // So if you can do BGRA translation on your side – the better. @@ -192,12 +523,12 @@ impl Interface for Session { // ); // log::info!("[video] initialized: {:?}", ok); // }); - let p = self.lc.read().unwrap().should_auto_login(); + let p = lc.should_auto_login(); if !p.is_empty() { input_os_password(p, true, self.clone()); } } - self.lc.write().unwrap().handle_peer_info(username, pi); + lc.handle_peer_info(username, pi); self.update_privacy_mode(); // self.update_pi(pi); if self.is_file_transfer() { @@ -255,3 +586,1385 @@ impl Interface for Session { self.lc.read().unwrap().force_relay } } + +#[tokio::main(flavor = "current_thread")] +pub async fn io_loop(handler: Session) { + let (sender, mut receiver) = mpsc::unbounded_channel::(); + *handler.sender.write().unwrap() = Some(sender.clone()); + let mut options = crate::ipc::get_options_async().await; + let mut key = options.remove("key").unwrap_or("".to_owned()); + let token = LocalConfig::get_option("access_token"); + if key.is_empty() { + key = crate::platform::get_license_key(); + } + if handler.is_port_forward() { + if handler.is_rdp() { + let port = handler + .get_option("rdp_port".to_owned()) + .parse::() + .unwrap_or(3389); + std::env::set_var( + "rdp_username", + handler.get_option("rdp_username".to_owned()), + ); + std::env::set_var( + "rdp_password", + handler.get_option("rdp_password".to_owned()), + ); + log::info!("Remote rdp port: {}", port); + start_one_port_forward(handler, 0, "".to_owned(), port, receiver, &key, &token).await; + } else if handler.args.len() == 0 { + let pfs = handler.lc.read().unwrap().port_forwards.clone(); + let mut queues = HashMap::>::new(); + for d in pfs { + sender.send(Data::AddPortForward(d)).ok(); + } + loop { + match receiver.recv().await { + Some(Data::AddPortForward((port, remote_host, remote_port))) => { + if port <= 0 || remote_port <= 0 { + continue; + } + let (sender, receiver) = mpsc::unbounded_channel::(); + queues.insert(port, sender); + let handler = handler.clone(); + let key = key.clone(); + let token = token.clone(); + tokio::spawn(async move { + start_one_port_forward( + handler, + port, + remote_host, + remote_port, + receiver, + &key, + &token, + ) + .await; + }); + } + Some(Data::RemovePortForward(port)) => { + if let Some(s) = queues.remove(&port) { + s.send(Data::Close).ok(); + } + } + Some(Data::Close) => { + break; + } + Some(d) => { + for (_, s) in queues.iter() { + s.send(d.clone()).ok(); + } + } + _ => {} + } + } + } else { + let port = handler.args[0].parse::().unwrap_or(0); + if handler.args.len() != 3 + || handler.args[2].parse::().unwrap_or(0) <= 0 + || port <= 0 + { + handler.on_error("Invalid arguments, usage:

rustdesk --port-forward remote-id listen-port remote-host remote-port"); + } + let remote_host = handler.args[1].clone(); + let remote_port = handler.args[2].parse::().unwrap_or(0); + start_one_port_forward( + handler, + port, + remote_host, + remote_port, + receiver, + &key, + &token, + ) + .await; + } + return; + } + let frame_count = Arc::new(AtomicUsize::new(0)); + let frame_count_cl = frame_count.clone(); + let ui_handler = handler.ui_handler.clone(); + let (video_sender, audio_sender) = start_video_audio_threads(move |data: &[u8]| { + frame_count_cl.fetch_add(1, Ordering::Relaxed); + ui_handler.on_rgba(data); + }); + + let mut remote = Remote::new( + handler, + video_sender, + audio_sender, + receiver, + sender, + frame_count, + ); + remote.io_loop(&key, &token).await; + remote.sync_jobs_status_to_local().await; +} + +async fn start_one_port_forward( + handler: Session, + port: i32, + remote_host: String, + remote_port: i32, + receiver: mpsc::UnboundedReceiver, + key: &str, + token: &str, +) { + if let Err(err) = crate::port_forward::listen( + handler.id.clone(), + handler.password.clone(), + port, + handler.clone(), + receiver, + key, + token, + handler.lc.clone(), + remote_host, + remote_port, + ) + .await + { + handler.on_error(&format!("Failed to listen on {}: {}", port, err)); + } + log::info!("port forward (:{}) exit", port); +} + +pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); +pub static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true); +pub static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); +const MILLI1: Duration = Duration::from_millis(1); + +pub struct Remote { + handler: Session, + video_sender: MediaSender, + audio_sender: MediaSender, + receiver: mpsc::UnboundedReceiver, + sender: mpsc::UnboundedSender, + old_clipboard: Arc>, + read_jobs: Vec, + write_jobs: Vec, + remove_jobs: HashMap, + timer: Interval, + last_update_jobs_status: (Instant, HashMap), + first_frame: bool, + #[cfg(windows)] + clipboard_file_context: Option>, + data_count: Arc, + frame_count: Arc, + video_format: CodecFormat, +} + +impl Remote { + pub fn new( + handler: Session, + video_sender: MediaSender, + audio_sender: MediaSender, + receiver: mpsc::UnboundedReceiver, + sender: mpsc::UnboundedSender, + frame_count: Arc, + ) -> Self { + Self { + handler, + video_sender, + audio_sender, + receiver, + sender, + old_clipboard: Default::default(), + read_jobs: Vec::new(), + write_jobs: Vec::new(), + remove_jobs: Default::default(), + timer: time::interval(SEC30), + last_update_jobs_status: (Instant::now(), Default::default()), + first_frame: false, + #[cfg(windows)] + clipboard_file_context: None, + data_count: Arc::new(AtomicUsize::new(0)), + frame_count, + video_format: CodecFormat::Unknown, + } + } + + pub async fn io_loop(&mut self, key: &str, token: &str) { + let stop_clipboard = self.start_clipboard(); + let mut last_recv_time = Instant::now(); + let mut received = false; + let conn_type = if self.handler.is_file_transfer() { + ConnType::FILE_TRANSFER + } else { + ConnType::default() + }; + match Client::start( + &self.handler.id, + key, + token, + conn_type, + self.handler.clone(), + ) + .await + { + Ok((mut peer, direct)) => { + SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); + SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); + SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst); + // self.handler + // .call("setConnectionType", &make_args!(peer.is_secured(), direct)); + self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready + + // just build for now + #[cfg(not(windows))] + let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::(); + #[cfg(windows)] + let mut rx_clip_client = get_rx_clip_client().lock().await; + + let mut status_timer = time::interval(Duration::new(1, 0)); + + loop { + tokio::select! { + res = peer.next() => { + if let Some(res) = res { + match res { + Err(err) => { + log::error!("Connection closed: {}", err); + self.handler.set_force_relay(direct, received); + self.handler.msgbox("error", "Connection Error", &err.to_string()); + break; + } + Ok(ref bytes) => { + last_recv_time = Instant::now(); + received = true; + self.data_count.fetch_add(bytes.len(), Ordering::Relaxed); + if !self.handle_msg_from_peer(bytes, &mut peer).await { + break + } + } + } + } else { + if self.handler.is_restarting_remote_device() { + log::info!("Restart remote device"); + self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); + } else { + log::info!("Reset by the peer"); + self.handler.msgbox("error", "Connection Error", "Reset by the peer"); + } + break; + } + } + d = self.receiver.recv() => { + if let Some(d) = d { + if !self.handle_msg_from_ui(d, &mut peer).await { + break; + } + } + } + _msg = rx_clip_client.recv() => { + #[cfg(windows)] + match _msg { + Some((_, clip)) => { + allow_err!(peer.send(&clip_2_msg(clip)).await); + } + None => { + // unreachable!() + } + } + } + _ = self.timer.tick() => { + if last_recv_time.elapsed() >= SEC30 { + self.handler.msgbox("error", "Connection Error", "Timeout"); + break; + } + if !self.read_jobs.is_empty() { + if let Err(err) = fs::handle_read_jobs(&mut self.read_jobs, &mut peer).await { + self.handler.msgbox("error", "Connection Error", &err.to_string()); + break; + } + self.update_jobs_status(); + } else { + self.timer = time::interval_at(Instant::now() + SEC30, SEC30); + } + } + _ = status_timer.tick() => { + let speed = self.data_count.swap(0, Ordering::Relaxed); + let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); + let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; + self.handler.update_quality_status(QualityStatus { + speed:Some(speed), + fps:Some(fps), + ..Default::default() + }); + } + } + } + log::debug!("Exit io_loop of id={}", self.handler.id); + } + Err(err) => { + self.handler + .msgbox("error", "Connection Error", &err.to_string()); + } + } + if let Some(stop) = stop_clipboard { + stop.send(()).ok(); + } + SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); + SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); + SERVER_FILE_TRANSFER_ENABLED.store(false, Ordering::SeqCst); + } + + fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { + if let Some(job) = self.remove_jobs.get_mut(&id) { + if job.no_confirm { + let file_num = (file_num + 1) as usize; + if file_num < job.files.len() { + let path = format!("{}{}{}", job.path, job.sep, job.files[file_num].name); + self.sender + .send(Data::RemoveFile((id, path, file_num as i32, job.is_remote))) + .ok(); + let elapsed = job.last_update_job_status.elapsed().as_millis() as i32; + if elapsed >= 1000 { + job.last_update_job_status = Instant::now(); + } else { + return; + } + } else { + self.remove_jobs.remove(&id); + } + } + } + if let Some(err) = err { + // self.handler + // .call("jobError", &make_args!(id, err, file_num)); + self.handler.job_error(id, err, file_num); + } else { + // self.handler.call("jobDone", &make_args!(id, file_num)); + self.handler.job_done(id, file_num); + } + } + + fn start_clipboard(&mut self) -> Option> { + if self.handler.is_file_transfer() || self.handler.is_port_forward() { + return None; + } + let (tx, rx) = std::sync::mpsc::channel(); + let old_clipboard = self.old_clipboard.clone(); + let tx_protobuf = self.sender.clone(); + let lc = self.handler.lc.clone(); + match ClipboardContext::new() { + Ok(mut ctx) => { + // ignore clipboard update before service start + check_clipboard(&mut ctx, Some(&old_clipboard)); + std::thread::spawn(move || loop { + std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); + match rx.try_recv() { + Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { + log::debug!("Exit clipboard service of client"); + break; + } + _ => {} + } + if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) + || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + || lc.read().unwrap().disable_clipboard + { + continue; + } + if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { + tx_protobuf.send(Data::Message(msg)).ok(); + } + }); + } + Err(err) => { + log::error!("Failed to start clipboard service of client: {}", err); + } + } + Some(tx) + } + + async fn load_last_jobs(&mut self) { + log::info!("start load last jobs"); + // self.handler.call("clearAllJobs", &make_args!()); + self.handler.clear_all_jobs(); + let pc = self.handler.load_config(); + if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { + // no last jobs + return; + } + // TODO: can add a confirm dialog + let mut cnt = 1; + for job_str in pc.transfer.read_jobs.iter() { + let job: Result = serde_json::from_str(&job_str); + if let Ok(job) = job { + // self.handler.call( + // "addJob", + // &make_args!( + // cnt, + // job.to.clone(), + // job.remote.clone(), + // job.file_num, + // job.show_hidden, + // false + // ), + // ); + self.handler.add_job( + cnt, + job.to.clone(), + job.remote.clone(), + job.file_num, + job.show_hidden, + false, + ); + cnt += 1; + println!("restore read_job: {:?}", job); + } + } + for job_str in pc.transfer.write_jobs.iter() { + let job: Result = serde_json::from_str(&job_str); + if let Ok(job) = job { + // self.handler.call( + // "addJob", + // &make_args!( + // cnt, + // job.remote.clone(), + // job.to.clone(), + // job.file_num, + // job.show_hidden, + // true + // ), + // ); + self.handler.add_job( + cnt, + job.remote.clone(), + job.to.clone(), + job.file_num, + job.show_hidden, + true, + ); + cnt += 1; + println!("restore write_job: {:?}", job); + } + } + // self.handler.call("updateTransferList", &make_args!()); + self.handler.update_transfer_list(); + } + + async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { + match data { + Data::Close => { + let mut misc = Misc::new(); + misc.set_close_reason("".to_owned()); + let mut msg = Message::new(); + msg.set_misc(misc); + allow_err!(peer.send(&msg).await); + return false; + } + Data::Login((password, remember)) => { + self.handler + .handle_login_from_ui(password, remember, peer) + .await; + } + Data::ToggleClipboardFile => { + self.check_clipboard_file_context(); + } + Data::Message(msg) => { + allow_err!(peer.send(&msg).await); + } + Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { + log::info!("send files, is remote {}", is_remote); + let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); + if is_remote { + log::debug!("New job {}, write to {} from remote {}", id, to, path); + self.write_jobs.push(fs::TransferJob::new_write( + id, + path.clone(), + to, + file_num, + include_hidden, + is_remote, + Vec::new(), + od, + )); + allow_err!( + peer.send(&fs::new_send(id, path, file_num, include_hidden)) + .await + ); + } else { + match fs::TransferJob::new_read( + id, + to.clone(), + path.clone(), + file_num, + include_hidden, + is_remote, + od, + ) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(job) => { + log::debug!( + "New job {}, read {} to remote {}, {} files", + id, + path, + to, + job.files().len() + ); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); // TODO + #[cfg(not(windows))] + let files = job.files().clone(); + #[cfg(windows)] + let mut files = job.files().clone(); + #[cfg(windows)] + if self.handler.peer_platform() != "Windows" { + // peer is not windows, need transform \ to / + fs::transform_windows_path(&mut files); + } + self.read_jobs.push(job); + self.timer = time::interval(MILLI1); + allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); + } + } + } + } + Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { + let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); + if is_remote { + log::debug!( + "new write waiting job {}, write to {} from remote {}", + id, + to, + path + ); + let mut job = fs::TransferJob::new_write( + id, + path.clone(), + to, + file_num, + include_hidden, + is_remote, + Vec::new(), + od, + ); + job.is_last_job = true; + self.write_jobs.push(job); + } else { + match fs::TransferJob::new_read( + id, + to.clone(), + path.clone(), + file_num, + include_hidden, + is_remote, + od, + ) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(mut job) => { + log::debug!( + "new read waiting job {}, read {} to remote {}, {} files", + id, + path, + to, + job.files().len() + ); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); + job.is_last_job = true; + self.read_jobs.push(job); + self.timer = time::interval(MILLI1); + } + } + } + } + Data::ResumeJob((id, is_remote)) => { + if is_remote { + if let Some(job) = get_job(id, &mut self.write_jobs) { + job.is_last_job = false; + allow_err!( + peer.send(&fs::new_send( + id, + job.remote.clone(), + job.file_num, + job.show_hidden + )) + .await + ); + } + } else { + if let Some(job) = get_job(id, &mut self.read_jobs) { + job.is_last_job = false; + allow_err!( + peer.send(&fs::new_receive( + id, + job.path.to_string_lossy().to_string(), + job.file_num, + job.files.clone() + )) + .await + ); + } + } + } + Data::SetNoConfirm(id) => { + if let Some(job) = self.remove_jobs.get_mut(&id) { + job.no_confirm = true; + } + } + Data::ConfirmDeleteFiles((id, file_num)) => { + if let Some(job) = self.remove_jobs.get_mut(&id) { + let i = file_num as usize; + if i < job.files.len() { + // self.handler.call( + // "confirmDeleteFiles", + // &make_args!(id, file_num, job.files[i].name.clone()), + // ); + self.handler.confirm_delete_files(id, file_num); + } + } + } + Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { + if is_upload { + if let Some(job) = fs::get_job(id, &mut self.read_jobs) { + if remember { + job.set_overwrite_strategy(Some(need_override)); + } + job.confirm(&FileTransferSendConfirmRequest { + id, + file_num, + union: if need_override { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) + } else { + Some(file_transfer_send_confirm_request::Union::Skip(true)) + }, + ..Default::default() + }); + } + } else { + if let Some(job) = fs::get_job(id, &mut self.write_jobs) { + if remember { + job.set_overwrite_strategy(Some(need_override)); + } + let mut msg = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_send_confirm(FileTransferSendConfirmRequest { + id, + file_num, + union: if need_override { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) + } else { + Some(file_transfer_send_confirm_request::Union::Skip(true)) + }, + ..Default::default() + }); + msg.set_file_action(file_action); + allow_err!(peer.send(&msg).await); + } + } + } + Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { + let sep = self.handler.get_path_sep(is_remote); + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_all_files(ReadAllFiles { + id, + path: path.clone(), + include_hidden, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + self.remove_jobs + .insert(id, RemoveJob::new(Vec::new(), path, sep, is_remote)); + } else { + match fs::get_recursive_files(&path, include_hidden) { + Ok(entries) => { + // let m = make_fd(id, &entries, true); + // self.handler.call("updateFolderFiles", &make_args!(m)); + self.remove_jobs + .insert(id, RemoveJob::new(entries, path, sep, is_remote)); + } + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + } + } + } + Data::CancelJob(id) => { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_cancel(FileTransferCancel { + id: id, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + if let Some(job) = fs::get_job(id, &mut self.write_jobs) { + job.remove_download_file(); + fs::remove_job(id, &mut self.write_jobs); + } + fs::remove_job(id, &mut self.read_jobs); + self.remove_jobs.remove(&id); + } + Data::RemoveDir((id, path)) => { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_remove_dir(FileRemoveDir { + id, + path, + recursive: true, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } + Data::RemoveFile((id, path, file_num, is_remote)) => { + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_remove_file(FileRemoveFile { + id, + path, + file_num, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } else { + match fs::remove_file(&path) { + Err(err) => { + self.handle_job_status(id, file_num, Some(err.to_string())); + } + Ok(()) => { + self.handle_job_status(id, file_num, None); + } + } + } + } + Data::CreateDir((id, path, is_remote)) => { + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_create(FileDirCreate { + id, + path, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } else { + match fs::create_dir(&path) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(()) => { + self.handle_job_status(id, -1, None); + } + } + } + } + _ => {} + } + true + } + + #[inline] + fn update_job_status( + job: &fs::TransferJob, + elapsed: i32, + last_update_jobs_status: &mut (Instant, HashMap), + handler: &mut Session, + ) { + if elapsed <= 0 { + return; + } + let transferred = job.transferred(); + let last_transferred = { + if let Some(v) = last_update_jobs_status.1.get(&job.id()) { + v.to_owned() + } else { + 0 + } + }; + last_update_jobs_status.1.insert(job.id(), transferred); + let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); + let file_num = job.file_num() - 1; + // handler.call( + // "jobProgress", + // &make_args!(job.id(), file_num, speed, job.finished_size() as f64), + // ); + handler.job_progress(job.id(), file_num, speed, job.finished_size() as f64); + } + + fn update_jobs_status(&mut self) { + let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; + if elapsed >= 1000 { + for job in self.read_jobs.iter() { + Self::update_job_status( + job, + elapsed, + &mut self.last_update_jobs_status, + &mut self.handler, + ); + } + for job in self.write_jobs.iter() { + Self::update_job_status( + job, + elapsed, + &mut self.last_update_jobs_status, + &mut self.handler, + ); + } + self.last_update_jobs_status.0 = Instant::now(); + } + } + + pub async fn sync_jobs_status_to_local(&mut self) -> bool { + log::info!("sync transfer job status"); + let mut config: PeerConfig = self.handler.load_config(); + let mut transfer_metas = TransferSerde::default(); + for job in self.read_jobs.iter() { + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); + transfer_metas.read_jobs.push(json_str); + } + for job in self.write_jobs.iter() { + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); + transfer_metas.write_jobs.push(json_str); + } + log::info!("meta: {:?}", transfer_metas); + config.transfer = transfer_metas; + self.handler.save_config(config); + true + } + + async fn send_opts_after_login(&self, peer: &mut Stream) { + if let Some(opts) = self + .handler + .lc + .read() + .unwrap() + .get_option_message_after_login() + { + let mut misc = Misc::new(); + misc.set_option(opts); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + allow_err!(peer.send(&msg_out).await); + } + } + + async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { + if let Ok(msg_in) = Message::parse_from_bytes(&data) { + match msg_in.union { + Some(message::Union::VideoFrame(vf)) => { + if !self.first_frame { + self.first_frame = true; + // self.handler.call2("closeSuccess", &make_args!()); + self.handler.close_success(); + // self.handler.call("adaptSize", &make_args!()); + self.handler.adapt_size(); + self.send_opts_after_login(peer).await; + } + let incomming_format = CodecFormat::from(&vf); + if self.video_format != incomming_format { + self.video_format = incomming_format.clone(); + self.handler.update_quality_status(QualityStatus { + codec_format: Some(incomming_format), + ..Default::default() + }) + }; + self.video_sender.send(MediaData::VideoFrame(vf)).ok(); + } + Some(message::Union::Hash(hash)) => { + self.handler + .handle_hash(&self.handler.password.clone(), hash, peer) + .await; + } + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { + if !self.handler.handle_login_error(&err) { + return false; + } + } + Some(login_response::Union::PeerInfo(pi)) => { + self.handler.handle_peer_info(pi); + // self.check_clipboard_file_context(); + // if !(self.handler.is_file_transfer() + // || self.handler.is_port_forward() + // || !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) + // || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + // || self.handler.lc.read().unwrap().disable_clipboard) + // { + // let txt = self.old_clipboard.lock().unwrap().clone(); + // if !txt.is_empty() { + // let msg_out = crate::create_clipboard_msg(txt); + // let sender = self.sender.clone(); + // tokio::spawn(async move { + // // due to clipboard service interval time + // sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; + // sender.send(Data::Message(msg_out)).ok(); + // }); + // } + // } + + // if self.handler.is_file_transfer() { + // self.load_last_jobs().await; + // } + } + _ => {} + }, + Some(message::Union::CursorData(cd)) => { + self.handler.set_cursor_data(cd); + } + Some(message::Union::CursorId(id)) => { + self.handler.set_cursor_id(id.to_string()); + } + Some(message::Union::CursorPosition(cp)) => { + self.handler.set_cursor_position(cp); + } + Some(message::Union::Clipboard(cb)) => { + if !self.handler.lc.read().unwrap().disable_clipboard { + update_clipboard(cb, Some(&self.old_clipboard)); + } + } + #[cfg(windows)] + Some(message::Union::Cliprdr(clip)) => { + if !self.handler.lc.read().unwrap().disable_clipboard { + if let Some(context) = &mut self.clipboard_file_context { + if let Some(clip) = msg_2_clip(clip) { + server_clip_file(context, 0, clip); + } + } + } + } + Some(message::Union::FileResponse(fr)) => { + match fr.union { + Some(file_response::Union::Dir(fd)) => { + #[cfg(windows)] + let entries = fd.entries.to_vec(); + #[cfg(not(windows))] + let mut entries = fd.entries.to_vec(); + #[cfg(not(windows))] + { + if self.handler.peer_platform() == "Windows" { + fs::transform_windows_path(&mut entries); + } + } + // let mut m = make_fd(fd.id, &entries, fd.id > 0); + // if fd.id <= 0 { + // m.set_item("path", fd.path); + // } + // self.handler.call("updateFolderFiles", &make_args!(m)); + if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) { + log::info!("job set_files: {:?}", entries); + job.set_files(entries); + } else if let Some(job) = self.remove_jobs.get_mut(&fd.id) { + job.files = entries; + } + } + Some(file_response::Union::Digest(digest)) => { + if digest.is_upload { + if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { + if let Some(file) = job.files().get(digest.file_num as usize) { + let read_path = get_string(&job.join(&file.name)); + let overwrite_strategy = job.default_overwrite_strategy(); + if let Some(overwrite) = overwrite_strategy { + let req = FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(if overwrite { + file_transfer_send_confirm_request::Union::OffsetBlk(0) + } else { + file_transfer_send_confirm_request::Union::Skip( + true, + ) + }), + ..Default::default() + }; + job.confirm(&req); + let msg = new_send_confirm(req); + allow_err!(peer.send(&msg).await); + } else { + // self.handler.call( + // "overrideFileConfirm", + // &make_args!( + // digest.id, + // digest.file_num, + // read_path, + // true + // ), + // ); + self.handler.override_file_confirm( + digest.id, + digest.file_num, + read_path, + true, + ); + } + } + } + } else { + if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { + if let Some(file) = job.files().get(digest.file_num as usize) { + let write_path = get_string(&job.join(&file.name)); + let overwrite_strategy = job.default_overwrite_strategy(); + match fs::is_write_need_confirmation(&write_path, &digest) { + Ok(res) => match res { + DigestCheckResult::IsSame => { + let msg= new_send_confirm(FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(file_transfer_send_confirm_request::Union::Skip(true)), + ..Default::default() + }); + allow_err!(peer.send(&msg).await); + } + DigestCheckResult::NeedConfirm(digest) => { + if let Some(overwrite) = overwrite_strategy { + let msg = new_send_confirm( + FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(if overwrite { + file_transfer_send_confirm_request::Union::OffsetBlk(0) + } else { + file_transfer_send_confirm_request::Union::Skip(true) + }), + ..Default::default() + }, + ); + allow_err!(peer.send(&msg).await); + } else { + // self.handler.call( + // "overrideFileConfirm", + // &make_args!( + // digest.id, + // digest.file_num, + // write_path, + // false + // ), + // ); + self.handler.override_file_confirm( + digest.id, + digest.file_num, + write_path, + false, + ); + } + } + DigestCheckResult::NoSuchFile => { + let msg = new_send_confirm( + FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), + ..Default::default() + }, + ); + allow_err!(peer.send(&msg).await); + } + }, + Err(err) => { + println!("error recving digest: {}", err); + } + } + } + } + } + } + Some(file_response::Union::Block(block)) => { + log::info!( + "file response block, file id:{}, file num: {}", + block.id, + block.file_num + ); + if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { + if let Err(_err) = job.write(block, None).await { + // to-do: add "skip" for writing job + } + self.update_jobs_status(); + } + } + Some(file_response::Union::Done(d)) => { + if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { + job.modify_time(); + fs::remove_job(d.id, &mut self.write_jobs); + } + self.handle_job_status(d.id, d.file_num, None); + } + Some(file_response::Union::Error(e)) => { + self.handle_job_status(e.id, e.file_num, Some(e.error)); + } + _ => {} + } + } + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::AudioFormat(f)) => { + self.audio_sender.send(MediaData::AudioFormat(f)).ok(); + } + Some(misc::Union::ChatMessage(c)) => { + // self.handler.call("newMessage", &make_args!(c.text)); // TODO + } + Some(misc::Union::PermissionInfo(p)) => { + log::info!("Change permission {:?} -> {}", p.permission, p.enabled); + match p.permission.enum_value_or_default() { + Permission::Keyboard => { + SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); + // self.handler + // .call2("setPermission", &make_args!("keyboard", p.enabled)); + self.handler.set_permission("keyboard", p.enabled); + } + Permission::Clipboard => { + SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); + // self.handler + // .call2("setPermission", &make_args!("clipboard", p.enabled)); + self.handler.set_permission("clipboard", p.enabled); + } + Permission::Audio => { + // self.handler + // .call2("setPermission", &make_args!("audio", p.enabled)); + self.handler.set_permission("audio", p.enabled); + } + Permission::File => { + SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst); + if !p.enabled && self.handler.is_file_transfer() { + return true; + } + self.check_clipboard_file_context(); + // self.handler + // .call2("setPermission", &make_args!("file", p.enabled)); + self.handler.set_permission("file", p.enabled); + } + Permission::Restart => { + // self.handler + // .call2("setPermission", &make_args!("restart", p.enabled)); + self.handler.set_permission("restart", p.enabled); + } + } + } + Some(misc::Union::SwitchDisplay(s)) => { + // self.handler.call("switchDisplay", &make_args!(s.display)); // TODO + self.video_sender.send(MediaData::Reset).ok(); + if s.width > 0 && s.height > 0 { + self.handler.set_display(s.x, s.y, s.width, s.height); + } + } + Some(misc::Union::CloseReason(c)) => { + self.handler.msgbox("error", "Connection Error", &c); + return false; + } + Some(misc::Union::BackNotification(notification)) => { + if !self.handle_back_notification(notification).await { + return false; + } + } + _ => {} + }, + Some(message::Union::TestDelay(t)) => { + self.handler.handle_test_delay(t, peer).await; + } + Some(message::Union::AudioFrame(frame)) => { + if !self.handler.lc.read().unwrap().disable_audio { + self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); + } + } + Some(message::Union::FileAction(action)) => match action.union { + Some(file_action::Union::SendConfirm(c)) => { + if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { + job.confirm(&c); + } + } + _ => {} + }, + _ => {} + } + } + true + } + + async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { + match notification.union { + Some(back_notification::Union::BlockInputState(state)) => { + self.handle_back_msg_block_input( + state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), + ) + .await; + } + Some(back_notification::Union::PrivacyModeState(state)) => { + if !self + .handle_back_msg_privacy_mode( + state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), + ) + .await + { + return false; + } + } + _ => {} + } + true + } + + #[inline(always)] + fn update_block_input_state(&mut self, on: bool) { + // self.handler.call("updateBlockInputState", &make_args!(on)); // TODO + } + + async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { + match state { + back_notification::BlockInputState::BlkOnSucceeded => { + self.update_block_input_state(true); + } + back_notification::BlockInputState::BlkOnFailed => { + self.handler + .msgbox("custom-error", "Block user input", "Failed"); + self.update_block_input_state(false); + } + back_notification::BlockInputState::BlkOffSucceeded => { + self.update_block_input_state(false); + } + back_notification::BlockInputState::BlkOffFailed => { + self.handler + .msgbox("custom-error", "Unblock user input", "Failed"); + } + _ => {} + } + } + + #[inline(always)] + fn update_privacy_mode(&mut self, on: bool) { + let mut config = self.handler.load_config(); + config.privacy_mode = on; + self.handler.save_config(config); + + // self.handler.call("updatePrivacyMode", &[]); + self.handler.update_privacy_mode(); + } + + async fn handle_back_msg_privacy_mode( + &mut self, + state: back_notification::PrivacyModeState, + ) -> bool { + match state { + back_notification::PrivacyModeState::PrvOnByOther => { + self.handler.msgbox( + "error", + "Connecting...", + "Someone turns on privacy mode, exit", + ); + return false; + } + back_notification::PrivacyModeState::PrvNotSupported => { + self.handler + .msgbox("custom-error", "Privacy mode", "Unsupported"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnSucceeded => { + self.handler + .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); + self.update_privacy_mode(true); + } + back_notification::PrivacyModeState::PrvOnFailedDenied => { + self.handler + .msgbox("custom-error", "Privacy mode", "Peer denied"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnFailedPlugin => { + self.handler + .msgbox("custom-error", "Privacy mode", "Please install plugins"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnFailed => { + self.handler + .msgbox("custom-error", "Privacy mode", "Failed"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffSucceeded => { + self.handler + .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffByPeer => { + self.handler + .msgbox("custom-error", "Privacy mode", "Peer exit"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffFailed => { + self.handler + .msgbox("custom-error", "Privacy mode", "Failed to turn off"); + } + back_notification::PrivacyModeState::PrvOffUnknown => { + self.handler + .msgbox("custom-error", "Privacy mode", "Turned off"); + // log::error!("Privacy mode is turned off with unknown reason"); + self.update_privacy_mode(false); + } + _ => {} + } + true + } + + fn check_clipboard_file_context(&mut self) { + #[cfg(windows)] + { + let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) + && self.handler.lc.read().unwrap().enable_file_transfer; + if enabled == self.clipboard_file_context.is_none() { + self.clipboard_file_context = if enabled { + match create_clipboard_file_context(true, false) { + Ok(context) => { + log::info!("clipboard context for file transfer created."); + Some(context) + } + Err(err) => { + log::error!( + "Create clipboard context for file transfer: {}", + err.to_string() + ); + None + } + } + } else { + log::info!("clipboard context for file transfer destroyed."); + None + }; + } + } + } +} + +struct RemoveJob { + files: Vec, + path: String, + sep: &'static str, + is_remote: bool, + no_confirm: bool, + last_update_job_status: Instant, +} + +impl RemoveJob { + fn new(files: Vec, path: String, sep: &'static str, is_remote: bool) -> Self { + Self { + files, + path, + sep, + is_remote, + no_confirm: false, + last_update_job_status: Instant::now(), + } + } + + pub fn _gen_meta(&self) -> RemoveJobMeta { + RemoveJobMeta { + path: self.path.clone(), + is_remote: self.is_remote, + no_confirm: self.no_confirm, + } + } +} + +#[tokio::main(flavor = "current_thread")] +async fn send_note(url: String, id: String, conn_id: i32, note: String) { + let body = serde_json::json!({ "id": id, "Id": conn_id, "note": note }); + allow_err!(crate::post_request(url, body.to_string(), "").await); +} From ae265ca83619b15261690eeb5aa60bb3b519cd4f Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 31 Aug 2022 22:24:57 +0800 Subject: [PATCH 3/7] flutter.rs Session -> ui_session_interface.rs --- src/client.rs | 13 + src/flutter.rs | 610 ------------------------------------ src/ui/remote.rs | 114 +------ src/ui_session_interface.rs | 234 +++++++++++--- 4 files changed, 213 insertions(+), 758 deletions(-) diff --git a/src/client.rs b/src/client.rs index 64c7daf4d..ddb093b08 100644 --- a/src/client.rs +++ b/src/client.rs @@ -11,6 +11,7 @@ use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, Device, Host, StreamConfig, }; +use enigo::{Enigo, KeyboardControllable}; use magnum_opus::{Channels::*, Decoder as AudioDecoder}; use sha2::{Digest, Sha256}; use uuid::Uuid; @@ -58,6 +59,18 @@ lazy_static::lazy_static! { static ref AUDIO_HOST: Host = cpal::default_host(); } +lazy_static::lazy_static! { + static ref ENIGO: Arc> = Arc::new(Mutex::new(Enigo::new())); +} + +pub fn get_key_state(key: enigo::Key) -> bool { + #[cfg(target_os = "macos")] + if key == enigo::Key::NumLock { + return true; + } + ENIGO.lock().unwrap().get_key_state(key) +} + cfg_if::cfg_if! { if #[cfg(target_os = "android")] { diff --git a/src/flutter.rs b/src/flutter.rs index 88c6f1961..514048e31 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -301,616 +301,6 @@ pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultTy } } -// #[derive(Clone)] -// pub struct Session { -// id: String, -// sender: Arc>>>, // UI to rust -// lc: Arc>, -// events2ui: Arc>>>, -// } - -// impl Session1 { -// /// Create a new remote session with the given id. -// /// -// /// # Arguments -// /// -// /// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ -// /// * `is_file_transfer` - If the session is used for file transfer. -// /// * `is_port_forward` - If the session is used for port forward. -// pub fn add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> ResultType<()> { -// // TODO check same id -// let session_id = get_session_id(id.to_owned()); -// LocalConfig::set_remote_id(&session_id); -// // TODO close -// // Self::close(); -// let session = Session { -// id: session_id.clone(), -// sender: Default::default(), -// lc: Default::default(), -// events2ui: Arc::new(RwLock::new(None)), -// }; -// session.lc.write().unwrap().initialize( -// session_id.clone(), -// is_file_transfer, -// is_port_forward, -// ); -// SESSIONS -// .write() -// .unwrap() -// .insert(id.to_owned(), session.clone()); -// Ok(()) -// } - -// /// Create a new remote session with the given id. -// /// -// /// # Arguments -// /// -// /// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+ -// /// * `events2ui` - The events channel to ui. -// pub fn start(id: &str, events2ui: StreamSink) -> ResultType<()> { -// if let Some(session) = SESSIONS.write().unwrap().get_mut(id) { -// *session.events2ui.write().unwrap() = Some(events2ui); -// let session = session.clone(); -// std::thread::spawn(move || { -// let is_file_transfer = session.lc.read().unwrap().is_file_transfer; -// let is_port_forward = session.lc.read().unwrap().is_port_forward; -// Connection::start(session, is_file_transfer, is_port_forward); -// }); -// Ok(()) -// } else { -// bail!("No session with peer id {}", id) -// } -// } - -// /// 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(&self, name: &str) -> String { -// if name == "remote_dir" { -// return self.lc.read().unwrap().get_remote_dir(); -// } -// self.lc.read().unwrap().get_option(name) -// } - -// /// Set the option of the current session. -// /// -// /// # Arguments -// /// -// /// * `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(&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(&self, pass: String, activate: bool) { -// input_os_password(pass, activate, self.clone()); -// } - -// pub fn restart_remote_device(&self) { -// let mut lc = self.lc.write().unwrap(); -// lc.restarting_remote_device = true; -// let msg = lc.restart_remote_device(); -// self.send_msg(msg); -// } - -// /// Toggle an option. -// 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) { -// self.send(Data::Message(LoginConfigHandler::refresh())); -// } - -// /// Get image quality. -// pub fn get_image_quality(&self) -> String { -// self.lc.read().unwrap().image_quality.clone() -// } - -// /// Set image quality. -// 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); -// } -// } - -// /// Get the status of a toggle option. -// /// Return `None` if the option is not found. -// /// -// /// # Arguments -// /// -// /// * `name` - The name of the option to get. -// pub fn get_toggle_option(&self, name: &str) -> bool { -// self.lc.write().unwrap().get_toggle_option(name) -// } - -// /// Login. -// /// -// /// # Arguments -// /// -// /// * `password` - The password to login. -// /// * `remember` - If the password should be remembered. -// pub fn login(&self, password: &str, remember: bool) { -// self.send(Data::Login((password.to_owned(), remember))); -// } - -// /// Close the session. -// pub fn close(&self) { -// self.send(Data::Close); -// } - -// /// Reconnect to the current session. -// pub fn reconnect(&self) { -// self.send(Data::Close); -// let session = self.clone(); -// std::thread::spawn(move || { -// Connection::start(session, false, false); -// }); -// } - -// /// Get `remember` flag in [`LoginConfigHandler`]. -// pub fn get_remember(&self) -> bool { -// self.lc.read().unwrap().remember -// } - -// /// Send message over the current session. -// /// -// /// # Arguments -// /// -// /// * `msg` - The message to send. -// #[inline] -// pub fn send_msg(&self, msg: Message) { -// self.send(Data::Message(msg)); -// } - -// /// Send chat message over the current session. -// /// -// /// # Arguments -// /// -// /// * `text` - The message to send. -// pub fn send_chat(&self, text: String) { -// let mut misc = Misc::new(); -// misc.set_chat_message(ChatMessage { -// text, -// ..Default::default() -// }); -// let mut msg_out = Message::new(); -// msg_out.set_misc(misc); -// self.send_msg(msg_out); -// } - -// /// Push an event to the event queue. -// /// An event is stored as json in the event queue. -// /// -// /// # Arguments -// /// -// /// * `name` - The name of the event. -// /// * `event` - Fields of the event content. -// fn push_event(&self, name: &str, event: Vec<(&str, &str)>) { -// let mut h: HashMap<&str, &str> = event.iter().cloned().collect(); -// assert!(h.get("name").is_none()); -// h.insert("name", name); -// let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned()); -// if let Some(stream) = &*self.events2ui.read().unwrap() { -// stream.add(EventToUI::Event(out)); -// } -// } - -// /// Get platform of peer. -// #[inline] -// fn peer_platform(&self) -> String { -// self.lc.read().unwrap().info.platform.clone() -// } - -// /// Quick method for sending a ctrl_alt_del command. -// 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); -// } -// } - -// /// Switch the display. -// /// -// /// # Arguments -// /// -// /// * `display` - The display to switch to. -// pub fn switch_display(&self, display: i32) { -// let mut misc = Misc::new(); -// misc.set_switch_display(SwitchDisplay { -// display, -// ..Default::default() -// }); -// let mut msg_out = Message::new(); -// msg_out.set_misc(misc); -// self.send_msg(msg_out); -// } - -// /// Send lock screen command. -// 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. -// /// -// /// # Arguments -// /// -// /// * `name` - The name of the key. -// /// * `down` - Whether the key is down or up. -// /// * `press` - If the key is simply being pressed(Down+Up). -// /// * `alt` - If the alt key is also pressed. -// /// * `ctrl` - If the ctrl key is also pressed. -// /// * `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, -// alt: bool, -// ctrl: bool, -// shift: bool, -// command: bool, -// ) { -// 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); -// } -// } -// } - -// /// Input a string of text. -// /// String is parsed into individual key presses. -// /// -// /// # Arguments -// /// -// /// * `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(msg_out); -// } - -// fn _input_key( -// &self, -// key: Key, -// down: bool, -// press: bool, -// alt: bool, -// ctrl: bool, -// shift: bool, -// command: bool, -// ) { -// let v = if press { -// 3 -// } else if down { -// 1 -// } else { -// 0 -// }; -// self.key_down_or_up(v, key, alt, ctrl, shift, command); -// } - -// pub fn send_mouse( -// &self, -// mask: i32, -// x: i32, -// y: i32, -// alt: bool, -// ctrl: bool, -// shift: bool, -// command: bool, -// ) { -// send_mouse(mask, x, y, alt, ctrl, shift, command, self); -// } - -// fn key_down_or_up( -// &self, -// down_or_up: i32, -// key: Key, -// alt: bool, -// ctrl: bool, -// shift: bool, -// command: bool, -// ) { -// let mut down_or_up = down_or_up; -// let mut key_event = KeyEvent::new(); -// match key { -// Key::Chr(chr) => { -// key_event.set_chr(chr); -// } -// Key::ControlKey(key) => { -// key_event.set_control_key(key.clone()); -// } -// Key::_Raw(raw) => { -// if raw > 'z' as u32 || raw < 'a' as u32 { -// key_event.set_unicode(raw); -// if down_or_up == 0 { -// // ignore up, avoiding trigger twice -// return; -// } -// down_or_up = 1; // if press, turn into down for avoiding trigger twice on server side -// } else { -// // to make ctrl+c works on windows -// key_event.set_chr(raw); -// } -// } -// } -// if alt { -// key_event.modifiers.push(ControlKey::Alt.into()); -// } -// if shift { -// key_event.modifiers.push(ControlKey::Shift.into()); -// } -// if ctrl { -// key_event.modifiers.push(ControlKey::Control.into()); -// } -// if command { -// key_event.modifiers.push(ControlKey::Meta.into()); -// } -// if down_or_up == 1 { -// key_event.down = true; -// } else if down_or_up == 3 { -// key_event.press = true; -// } -// let mut msg_out = Message::new(); -// msg_out.set_key_event(key_event); -// log::debug!("{:?}", msg_out); -// self.send_msg(msg_out); -// } - -// pub fn load_config(&self) -> PeerConfig { -// load_config(&self.id) -// } - -// pub fn save_config(&self, config: &PeerConfig) { -// config.store(&self.id); -// } - -// pub fn get_platform(&self, is_remote: bool) -> String { -// if is_remote { -// self.lc.read().unwrap().info.platform.clone() -// } else { -// whoami::platform().to_string() -// } -// } - -// pub fn load_last_jobs(&self) { -// let pc = self.load_config(); -// if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { -// // no last jobs -// return; -// } -// let mut cnt = 1; -// for job_str in pc.transfer.read_jobs.iter() { -// if !job_str.is_empty() { -// self.push_event("load_last_job", vec![("value", job_str)]); -// cnt += 1; -// println!("restore read_job: {:?}", job_str); -// } -// } -// for job_str in pc.transfer.write_jobs.iter() { -// if !job_str.is_empty() { -// self.push_event("load_last_job", vec![("value", job_str)]); -// cnt += 1; -// println!("restore write_job: {:?}", job_str); -// } -// } -// } - -// fn update_quality_status(&self, status: QualityStatus) { -// const NULL: String = String::new(); -// self.push_event( -// "update_quality_status", -// vec![ -// ("speed", &status.speed.map_or(NULL, |it| it)), -// ("fps", &status.fps.map_or(NULL, |it| it.to_string())), -// ("delay", &status.delay.map_or(NULL, |it| it.to_string())), -// ( -// "target_bitrate", -// &status.target_bitrate.map_or(NULL, |it| it.to_string()), -// ), -// ( -// "codec_format", -// &status.codec_format.map_or(NULL, |it| it.to_string()), -// ), -// ], -// ); -// } - -// pub fn remove_port_forward(&mut self, port: i32) { -// let mut config = self.load_config(); -// config.port_forwards = config -// .port_forwards -// .drain(..) -// .filter(|x| x.0 != port) -// .collect(); -// self.save_config(&config); -// self.send(Data::RemovePortForward(port)); -// } - -// pub fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { -// let mut config = self.load_config(); -// if config -// .port_forwards -// .iter() -// .filter(|x| x.0 == port) -// .next() -// .is_some() -// { -// return; -// } -// let pf = (port, remote_host, remote_port); -// config.port_forwards.push(pf.clone()); -// self.save_config(&config); -// self.send(Data::AddPortForward(pf)); -// } - -// fn on_error(&self, err: &str) { -// self.msgbox("error", "Error", err); -// } -// } - -// impl FileManager for Session {} - -// #[async_trait] -// impl Interface for Session { -// fn send(&self, data: Data) { -// if let Some(sender) = self.sender.read().unwrap().as_ref() { -// sender.send(data).ok(); -// } -// } - -// fn is_file_transfer(&self) -> bool { -// todo!() -// } - -// fn is_port_forward(&self) -> bool { -// todo!() -// } - -// fn is_rdp(&self) -> bool { -// todo!() -// } - -// fn msgbox(&self, msgtype: &str, title: &str, text: &str) { -// let has_retry = if check_if_retry(msgtype, title, text) { -// "true" -// } else { -// "" -// }; -// self.push_event( -// "msgbox", -// vec![ -// ("type", msgtype), -// ("title", title), -// ("text", text), -// ("hasRetry", has_retry), -// ], -// ); -// } - -// fn handle_login_error(&mut self, err: &str) -> bool { -// self.lc.write().unwrap().handle_login_error(err, self) -// } - -// fn handle_peer_info(&mut self, pi: PeerInfo) { -// let mut lc = self.lc.write().unwrap(); -// let username = lc.get_username(&pi); -// let mut displays = Vec::new(); -// let mut current = pi.current_display as usize; - -// if lc.is_file_transfer { -// if pi.username.is_empty() { -// self.msgbox( -// "error", -// "Error", -// "No active console user logged on, please connect and logon first.", -// ); -// return; -// } -// } else { -// if pi.displays.is_empty() { -// self.msgbox("error", "Remote Error", "No Display"); -// } -// for ref d in pi.displays.iter() { -// let mut h: HashMap<&str, i32> = Default::default(); -// h.insert("x", d.x); -// h.insert("y", d.y); -// h.insert("width", d.width); -// h.insert("height", d.height); -// displays.push(h); -// } -// if current >= pi.displays.len() { -// current = 0; -// } -// } -// let displays = serde_json::ser::to_string(&displays).unwrap_or("".to_owned()); -// self.push_event( -// "peer_info", -// vec![ -// ("username", &username), -// ("hostname", &pi.hostname), -// ("platform", &pi.platform), -// ("sas_enabled", &pi.sas_enabled.to_string()), -// ("displays", &displays), -// ("version", &pi.version), -// ("current_display", ¤t.to_string()), -// ("is_file_transfer", &lc.is_file_transfer.to_string()), -// ], -// ); -// lc.handle_peer_info(username, pi); -// let p = lc.should_auto_login(); -// if !p.is_empty() { -// input_os_password(p, true, self.clone()); -// } -// } - -// fn set_force_relay(&mut self, direct: bool, received: bool) { -// let mut lc = self.lc.write().unwrap(); -// lc.force_relay = false; -// if direct && !received { -// let errno = errno::errno().0; -// log::info!("errno is {}", errno); -// // TODO: check mac and ios -// if cfg!(windows) && errno == 10054 || !cfg!(windows) && errno == 104 { -// lc.force_relay = true; -// lc.set_option("force-always-relay".to_owned(), "Y".to_owned()); -// } -// } -// } - -// fn is_force_relay(&self) -> bool { -// self.lc.read().unwrap().force_relay -// } - -// async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { -// handle_hash(self.lc.clone(), pass, hash, self, peer).await; -// } - -// async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { -// handle_login_from_ui(self.lc.clone(), password, remember, peer).await; -// } - -// async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { -// if !t.from_client { -// self.update_quality_status(QualityStatus { -// delay: Some(t.last_delay as _), -// target_bitrate: Some(t.target_bitrate as _), -// ..Default::default() -// }); -// handle_test_delay(t, peer).await; -// } -// } -// } - // struct Connection { // video_handler: VideoHandler, // audio_handler: AudioHandler, diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 49812e09a..9e8c8fc51 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -55,18 +55,9 @@ use errno; type Video = AssetPtr; lazy_static::lazy_static! { - static ref ENIGO: Arc> = Arc::new(Mutex::new(Enigo::new())); static ref VIDEO: Arc>> = Default::default(); } -fn get_key_state(key: enigo::Key) -> bool { - #[cfg(target_os = "macos")] - if key == enigo::Key::NumLock { - return true; - } - ENIGO.lock().unwrap().get_key_state(key) -} - static IS_IN: AtomicBool = AtomicBool::new(false); static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false); @@ -1111,38 +1102,7 @@ impl SciterSession { IS_IN.store(false, Ordering::SeqCst); } - fn send_mouse( - &mut self, - mask: i32, - x: i32, - y: i32, - alt: bool, - ctrl: bool, - shift: bool, - command: bool, - ) { - #[allow(unused_mut)] - let mut command = command; - #[cfg(windows)] - { - if !command && crate::platform::windows::get_win_key_state() { - command = true; - } - } - - send_mouse(mask, x, y, alt, ctrl, shift, command, &self.0); - // on macos, ctrl + left button down = right button down, up won't emit, so we need to - // emit up myself if peer is not macos - // to-do: how about ctrl + left from win to macos - if cfg!(target_os = "macos") { - let buttons = mask >> 3; - let evt_type = mask & 0x7; - if buttons == 1 && evt_type == 1 && ctrl && self.peer_platform() != "Mac OS" { - self.send_mouse((1 << 3 | 2) as _, x, y, alt, ctrl, shift, command); - } - } - } - + // TODO fn set_cursor_data(&mut self, cd: CursorData) { let mut colors = hbb_common::compress::decompress(&cd.colors); if colors.iter().filter(|x| **x != 0).next().is_none() { @@ -1285,24 +1245,6 @@ impl SciterSession { "".to_owned() } - fn ctrl_alt_del(&mut self) { - if self.peer_platform() == "Windows" { - let mut key_event = KeyEvent::new(); - key_event.set_control_key(ControlKey::CtrlAltDel); - self.key_down_or_up(1, key_event, false, false, false, false); - } else { - let mut key_event = KeyEvent::new(); - key_event.set_control_key(ControlKey::Delete); - self.key_down_or_up(3, key_event, true, true, false, false); - } - } - - fn lock_screen(&mut self) { - let mut key_event = KeyEvent::new(); - key_event.set_control_key(ControlKey::LockScreen); - self.key_down_or_up(1, key_event, false, false, false, false); - } - fn transfer_file(&mut self) { let id = self.get_id(); let args = vec!["--file-transfer", &id, &self.password]; @@ -1319,60 +1261,6 @@ impl SciterSession { } } - fn key_down_or_up( - &mut self, - down_or_up: i32, - evt: KeyEvent, - alt: bool, - ctrl: bool, - shift: bool, - command: bool, - ) { - let mut key_event = evt; - - if alt - && !crate::is_control_key(&key_event, &ControlKey::Alt) - && !crate::is_control_key(&key_event, &ControlKey::RAlt) - { - key_event.modifiers.push(ControlKey::Alt.into()); - } - if shift - && !crate::is_control_key(&key_event, &ControlKey::Shift) - && !crate::is_control_key(&key_event, &ControlKey::RShift) - { - key_event.modifiers.push(ControlKey::Shift.into()); - } - if ctrl - && !crate::is_control_key(&key_event, &ControlKey::Control) - && !crate::is_control_key(&key_event, &ControlKey::RControl) - { - key_event.modifiers.push(ControlKey::Control.into()); - } - if command - && !crate::is_control_key(&key_event, &ControlKey::Meta) - && !crate::is_control_key(&key_event, &ControlKey::RWin) - { - key_event.modifiers.push(ControlKey::Meta.into()); - } - if get_key_state(enigo::Key::CapsLock) { - key_event.modifiers.push(ControlKey::CapsLock.into()); - } - if self.peer_platform() != "Mac OS" { - if get_key_state(enigo::Key::NumLock) && common::valid_for_numlock(&key_event) { - key_event.modifiers.push(ControlKey::NumLock.into()); - } - } - if down_or_up == 1 { - key_event.down = true; - } else if down_or_up == 3 { - key_event.press = true; - } - let mut msg_out = Message::new(); - msg_out.set_key_event(key_event); - log::debug!("{:?}", msg_out); - self.send(Data::Message(msg_out)); - } - // #[inline] // fn set_cursor_id(&mut self, id: String) { // self.call("setCursorId", &make_args!(id)); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index a8871fac1..03666ed92 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1,7 +1,7 @@ use crate::client::{ - self, check_if_retry, handle_hash, handle_login_from_ui, handle_test_delay, input_os_password, - load_config, start_video_audio_threads, Client, CodecFormat, FileManager, LoginConfigHandler, - MediaData, MediaSender, QualityStatus, SEC30, + self, check_if_retry, get_key_state, handle_hash, handle_login_from_ui, handle_test_delay, + input_os_password, load_config, send_mouse, start_video_audio_threads, Client, CodecFormat, + FileManager, Key, LoginConfigHandler, MediaData, MediaSender, QualityStatus, KEY_MAP, SEC30, }; use crate::common::{ self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL, @@ -9,6 +9,7 @@ use crate::common::{ use crate::platform; use crate::{client::Data, client::Interface}; use async_trait::async_trait; +use enigo::{Enigo, KeyboardControllable}; use hbb_common::config::{Config, LocalConfig, PeerConfig, TransferSerde}; use hbb_common::fs::{ can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, @@ -43,11 +44,11 @@ pub struct Session { impl Session { pub fn get_view_style(&self) -> String { - return self.lc.read().unwrap().view_style.clone(); + self.lc.read().unwrap().view_style.clone() } pub fn get_image_quality(&self) -> String { - return self.lc.read().unwrap().image_quality.clone(); + self.lc.read().unwrap().image_quality.clone() } pub fn save_view_style(&mut self, value: String) { @@ -65,8 +66,7 @@ impl Session { } pub fn get_toggle_option(&self, name: String) -> bool { - let res = self.lc.read().unwrap().get_toggle_option(&name); - return res; + self.lc.read().unwrap().get_toggle_option(&name) } pub fn is_privacy_mode_supported(&self) -> bool { @@ -196,12 +196,18 @@ impl Session { } pub fn get_option(&self, k: String) -> String { - let res = self.lc.read().unwrap().get_option(&k); - return res; + if k.eq("remote_dir") { + return self.lc.read().unwrap().get_remote_dir(); + } + self.lc.read().unwrap().get_option(&k) } - pub fn set_option(&self, k: String, v: String) { - self.lc.write().unwrap().set_option(k.clone(), v); + pub fn set_option(&self, k: String, mut v: String) { + let mut lc = self.lc.write().unwrap(); + if k.eq("remote_dir") { + v = lc.get_all_remote_dir(v); + } + lc.set_option(k, v); } #[inline] @@ -223,6 +229,72 @@ impl Session { self.lc.read().unwrap().info.platform.clone() } + pub fn ctrl_alt_del(&mut self) { + if self.peer_platform() == "Windows" { + let mut key_event = KeyEvent::new(); + key_event.set_control_key(ControlKey::CtrlAltDel); + self.key_down_or_up(1, key_event, false, false, false, false); + } else { + let mut key_event = KeyEvent::new(); + key_event.set_control_key(ControlKey::Delete); + self.key_down_or_up(3, key_event, true, true, false, false); + } + } + + pub fn key_down_or_up( + &self, + down_or_up: i32, + evt: KeyEvent, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, + ) { + let mut key_event = evt; + + if alt + && !crate::is_control_key(&key_event, &ControlKey::Alt) + && !crate::is_control_key(&key_event, &ControlKey::RAlt) + { + key_event.modifiers.push(ControlKey::Alt.into()); + } + if shift + && !crate::is_control_key(&key_event, &ControlKey::Shift) + && !crate::is_control_key(&key_event, &ControlKey::RShift) + { + key_event.modifiers.push(ControlKey::Shift.into()); + } + if ctrl + && !crate::is_control_key(&key_event, &ControlKey::Control) + && !crate::is_control_key(&key_event, &ControlKey::RControl) + { + key_event.modifiers.push(ControlKey::Control.into()); + } + if command + && !crate::is_control_key(&key_event, &ControlKey::Meta) + && !crate::is_control_key(&key_event, &ControlKey::RWin) + { + key_event.modifiers.push(ControlKey::Meta.into()); + } + if get_key_state(enigo::Key::CapsLock) { + key_event.modifiers.push(ControlKey::CapsLock.into()); + } + if self.peer_platform() != "Mac OS" { + if get_key_state(enigo::Key::NumLock) && common::valid_for_numlock(&key_event) { + key_event.modifiers.push(ControlKey::NumLock.into()); + } + } + if down_or_up == 1 { + key_event.down = true; + } else if down_or_up == 3 { + key_event.press = true; + } + let mut msg_out = Message::new(); + msg_out.set_key_event(key_event); + log::debug!("{:?}", msg_out); + self.send(Data::Message(msg_out)); + } + pub fn get_platform(&self, is_remote: bool) -> String { if is_remote { self.peer_platform() @@ -277,8 +349,122 @@ impl Session { self.send(Data::Message(msg_out)); } + pub fn lock_screen(&mut self) { + let mut key_event = KeyEvent::new(); + key_event.set_control_key(ControlKey::LockScreen); + self.key_down_or_up(1, key_event, false, false, false, false); + } + + // flutter only TODO new input + pub fn input_key( + &self, + name: &str, + down: bool, + press: bool, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, + ) { + 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); + } + } + } + + // flutter only TODO new input + 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(Data::Message(msg_out)); + } + + // flutter only TODO new input + fn _input_key( + &self, + key: Key, + down: bool, + press: bool, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, + ) { + let v = if press { + 3 + } else if down { + 1 + } else { + 0 + }; + let mut key_event = KeyEvent::new(); + match key { + Key::Chr(chr) => { + key_event.set_chr(chr); + } + Key::ControlKey(key) => { + key_event.set_control_key(key.clone()); + } + Key::_Raw(raw) => { + if raw > 'z' as u32 || raw < 'a' as u32 { + key_event.set_unicode(raw); + // TODO + // if down_or_up == 0 { + // // ignore up, avoiding trigger twice + // return; + // } + // down_or_up = 1; // if press, turn into down for avoiding trigger twice on server side + } else { + // to make ctrl+c works on windows + key_event.set_chr(raw); + } + } + } + + self.key_down_or_up(v, key_event, alt, ctrl, shift, command); + } + + pub fn send_mouse( + &mut self, + mask: i32, + x: i32, + y: i32, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, + ) { + #[allow(unused_mut)] + let mut command = command; + #[cfg(windows)] + { + if !command && crate::platform::windows::get_win_key_state() { + command = true; + } + } + + send_mouse(mask, x, y, alt, ctrl, shift, command, self); + // on macos, ctrl + left button down = right button down, up won't emit, so we need to + // emit up myself if peer is not macos + // to-do: how about ctrl + left from win to macos + if cfg!(target_os = "macos") { + let buttons = mask >> 3; + let evt_type = mask & 0x7; + if buttons == 1 && evt_type == 1 && ctrl && self.peer_platform() != "Mac OS" { + self.send_mouse((1 << 3 | 2) as _, x, y, alt, ctrl, shift, command); + } + } + } + pub fn reconnect(&self) { - println!("reconnecting"); + self.send(Data::Close); let cloned = self.clone(); let mut lock = self.thread.lock().unwrap(); lock.take().map(|t| t.join()); @@ -979,7 +1165,7 @@ impl Remote { Some(tx) } - async fn load_last_jobs(&mut self) { + fn load_last_jobs(&mut self) { log::info!("start load last jobs"); // self.handler.call("clearAllJobs", &make_args!()); self.handler.clear_all_jobs(); @@ -993,17 +1179,6 @@ impl Remote { for job_str in pc.transfer.read_jobs.iter() { let job: Result = serde_json::from_str(&job_str); if let Ok(job) = job { - // self.handler.call( - // "addJob", - // &make_args!( - // cnt, - // job.to.clone(), - // job.remote.clone(), - // job.file_num, - // job.show_hidden, - // false - // ), - // ); self.handler.add_job( cnt, job.to.clone(), @@ -1019,17 +1194,6 @@ impl Remote { for job_str in pc.transfer.write_jobs.iter() { let job: Result = serde_json::from_str(&job_str); if let Ok(job) = job { - // self.handler.call( - // "addJob", - // &make_args!( - // cnt, - // job.remote.clone(), - // job.to.clone(), - // job.file_num, - // job.show_hidden, - // true - // ), - // ); self.handler.add_job( cnt, job.remote.clone(), From 41a53e4983fe681442e3d8aee2cdfec91c91f3ec Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 1 Sep 2022 09:48:53 +0800 Subject: [PATCH 4/7] refactor io_loop --- src/client.rs | 7 +- src/client/io_loop.rs | 1205 +++++++++++++++++++++++++++++++++ src/flutter.rs | 1176 ++------------------------------ src/ui/remote.rs | 435 +----------- src/ui_session_interface.rs | 1258 +---------------------------------- 5 files changed, 1326 insertions(+), 2755 deletions(-) create mode 100644 src/client/io_loop.rs diff --git a/src/client.rs b/src/client.rs index ddb093b08..6346af6d0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, net::SocketAddr, ops::{Deref, Not}, - sync::{mpsc, Arc, Mutex, RwLock}, + sync::{mpsc, Arc, Mutex, RwLock, atomic::AtomicBool}, }; pub use async_trait::async_trait; @@ -48,7 +48,12 @@ pub use super::lang::*; pub mod file_trait; pub mod helper; +pub mod io_loop; +pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); +pub static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true); +pub static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); +pub const MILLI1: Duration = Duration::from_millis(1); pub const SEC30: Duration = Duration::from_secs(30); /// Client of the remote desktop. diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs new file mode 100644 index 000000000..f7f8f4f18 --- /dev/null +++ b/src/client/io_loop.rs @@ -0,0 +1,1205 @@ +use crate::client::{ + Client, CodecFormat, FileManager, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, + SERVER_CLIPBOARD_ENABLED, SERVER_FILE_TRANSFER_ENABLED, SERVER_KEYBOARD_ENABLED, +}; +use crate::common::{check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}; + +use crate::ui_session_interface::{InvokeUi, Session}; +use crate::{client::Data, client::Interface}; + +use hbb_common::config::{PeerConfig, TransferSerde}; +use hbb_common::fs::{ + can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, + RemoveJobMeta, TransferJobMeta, +}; +use hbb_common::message_proto::permission_info::Permission; +use hbb_common::protobuf::Message as _; +use hbb_common::rendezvous_proto::ConnType; +use hbb_common::tokio::{ + self, + sync::mpsc, + time::{self, Duration, Instant, Interval}, +}; +use hbb_common::{allow_err, message_proto::*}; +use hbb_common::{fs, log, Stream}; +use std::collections::HashMap; + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; + +pub struct Remote { + handler: Session, + video_sender: MediaSender, + audio_sender: MediaSender, + receiver: mpsc::UnboundedReceiver, + sender: mpsc::UnboundedSender, + old_clipboard: Arc>, + read_jobs: Vec, + write_jobs: Vec, + remove_jobs: HashMap, + timer: Interval, + last_update_jobs_status: (Instant, HashMap), + first_frame: bool, + #[cfg(windows)] + clipboard_file_context: Option>, + data_count: Arc, + frame_count: Arc, + video_format: CodecFormat, +} + +impl Remote { + pub fn new( + handler: Session, + video_sender: MediaSender, + audio_sender: MediaSender, + receiver: mpsc::UnboundedReceiver, + sender: mpsc::UnboundedSender, + frame_count: Arc, + ) -> Self { + Self { + handler, + video_sender, + audio_sender, + receiver, + sender, + old_clipboard: Default::default(), + read_jobs: Vec::new(), + write_jobs: Vec::new(), + remove_jobs: Default::default(), + timer: time::interval(SEC30), + last_update_jobs_status: (Instant::now(), Default::default()), + first_frame: false, + #[cfg(windows)] + clipboard_file_context: None, + data_count: Arc::new(AtomicUsize::new(0)), + frame_count, + video_format: CodecFormat::Unknown, + } + } + + pub async fn io_loop(&mut self, key: &str, token: &str) { + let stop_clipboard = self.start_clipboard(); + let mut last_recv_time = Instant::now(); + let mut received = false; + let conn_type = if self.handler.is_file_transfer() { + ConnType::FILE_TRANSFER + } else { + ConnType::default() + }; + match Client::start( + &self.handler.id, + key, + token, + conn_type, + self.handler.clone(), + ) + .await + { + Ok((mut peer, direct)) => { + SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); + SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); + SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst); + self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready + + // just build for now + #[cfg(not(windows))] + let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::(); + #[cfg(windows)] + let mut rx_clip_client = get_rx_clip_client().lock().await; + + let mut status_timer = time::interval(Duration::new(1, 0)); + + loop { + tokio::select! { + res = peer.next() => { + if let Some(res) = res { + match res { + Err(err) => { + log::error!("Connection closed: {}", err); + self.handler.set_force_relay(direct, received); + self.handler.msgbox("error", "Connection Error", &err.to_string()); + break; + } + Ok(ref bytes) => { + last_recv_time = Instant::now(); + received = true; + self.data_count.fetch_add(bytes.len(), Ordering::Relaxed); + if !self.handle_msg_from_peer(bytes, &mut peer).await { + break + } + } + } + } else { + if self.handler.is_restarting_remote_device() { + log::info!("Restart remote device"); + self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); + } else { + log::info!("Reset by the peer"); + self.handler.msgbox("error", "Connection Error", "Reset by the peer"); + } + break; + } + } + d = self.receiver.recv() => { + if let Some(d) = d { + if !self.handle_msg_from_ui(d, &mut peer).await { + break; + } + } + } + _msg = rx_clip_client.recv() => { + #[cfg(windows)] + match _msg { + Some((_, clip)) => { + allow_err!(peer.send(&clip_2_msg(clip)).await); + } + None => { + // unreachable!() + } + } + } + _ = self.timer.tick() => { + if last_recv_time.elapsed() >= SEC30 { + self.handler.msgbox("error", "Connection Error", "Timeout"); + break; + } + if !self.read_jobs.is_empty() { + if let Err(err) = fs::handle_read_jobs(&mut self.read_jobs, &mut peer).await { + self.handler.msgbox("error", "Connection Error", &err.to_string()); + break; + } + self.update_jobs_status(); + } else { + self.timer = time::interval_at(Instant::now() + SEC30, SEC30); + } + } + _ = status_timer.tick() => { + let speed = self.data_count.swap(0, Ordering::Relaxed); + let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); + let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; + self.handler.update_quality_status(QualityStatus { + speed:Some(speed), + fps:Some(fps), + ..Default::default() + }); + } + } + } + log::debug!("Exit io_loop of id={}", self.handler.id); + } + Err(err) => { + self.handler + .msgbox("error", "Connection Error", &err.to_string()); + } + } + if let Some(stop) = stop_clipboard { + stop.send(()).ok(); + } + SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); + SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); + SERVER_FILE_TRANSFER_ENABLED.store(false, Ordering::SeqCst); + } + + fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { + if let Some(job) = self.remove_jobs.get_mut(&id) { + if job.no_confirm { + let file_num = (file_num + 1) as usize; + if file_num < job.files.len() { + let path = format!("{}{}{}", job.path, job.sep, job.files[file_num].name); + self.sender + .send(Data::RemoveFile((id, path, file_num as i32, job.is_remote))) + .ok(); + let elapsed = job.last_update_job_status.elapsed().as_millis() as i32; + if elapsed >= 1000 { + job.last_update_job_status = Instant::now(); + } else { + return; + } + } else { + self.remove_jobs.remove(&id); + } + } + } + if let Some(err) = err { + self.handler.job_error(id, err, file_num); + } else { + self.handler.job_done(id, file_num); + } + } + + fn start_clipboard(&mut self) -> Option> { + if self.handler.is_file_transfer() || self.handler.is_port_forward() { + return None; + } + let (tx, rx) = std::sync::mpsc::channel(); + let old_clipboard = self.old_clipboard.clone(); + let tx_protobuf = self.sender.clone(); + let lc = self.handler.lc.clone(); + match ClipboardContext::new() { + Ok(mut ctx) => { + // ignore clipboard update before service start + check_clipboard(&mut ctx, Some(&old_clipboard)); + std::thread::spawn(move || loop { + std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); + match rx.try_recv() { + Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { + log::debug!("Exit clipboard service of client"); + break; + } + _ => {} + } + if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) + || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + || lc.read().unwrap().disable_clipboard + { + continue; + } + if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { + tx_protobuf.send(Data::Message(msg)).ok(); + } + }); + } + Err(err) => { + log::error!("Failed to start clipboard service of client: {}", err); + } + } + Some(tx) + } + + fn load_last_jobs(&mut self) { + log::info!("start load last jobs"); + self.handler.clear_all_jobs(); + let pc = self.handler.load_config(); + if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { + // no last jobs + return; + } + // TODO: can add a confirm dialog + let mut cnt = 1; + for job_str in pc.transfer.read_jobs.iter() { + let job: Result = serde_json::from_str(&job_str); + if let Ok(job) = job { + self.handler.add_job( + cnt, + job.to.clone(), + job.remote.clone(), + job.file_num, + job.show_hidden, + false, + ); + cnt += 1; + println!("restore read_job: {:?}", job); + } + } + for job_str in pc.transfer.write_jobs.iter() { + let job: Result = serde_json::from_str(&job_str); + if let Ok(job) = job { + self.handler.add_job( + cnt, + job.remote.clone(), + job.to.clone(), + job.file_num, + job.show_hidden, + true, + ); + cnt += 1; + println!("restore write_job: {:?}", job); + } + } + self.handler.update_transfer_list(); + } + + async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { + match data { + Data::Close => { + let mut misc = Misc::new(); + misc.set_close_reason("".to_owned()); + let mut msg = Message::new(); + msg.set_misc(misc); + allow_err!(peer.send(&msg).await); + return false; + } + Data::Login((password, remember)) => { + self.handler + .handle_login_from_ui(password, remember, peer) + .await; + } + Data::ToggleClipboardFile => { + self.check_clipboard_file_context(); + } + Data::Message(msg) => { + allow_err!(peer.send(&msg).await); + } + Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { + log::info!("send files, is remote {}", is_remote); + let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); + if is_remote { + log::debug!("New job {}, write to {} from remote {}", id, to, path); + self.write_jobs.push(fs::TransferJob::new_write( + id, + path.clone(), + to, + file_num, + include_hidden, + is_remote, + Vec::new(), + od, + )); + allow_err!( + peer.send(&fs::new_send(id, path, file_num, include_hidden)) + .await + ); + } else { + match fs::TransferJob::new_read( + id, + to.clone(), + path.clone(), + file_num, + include_hidden, + is_remote, + od, + ) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(job) => { + log::debug!( + "New job {}, read {} to remote {}, {} files", + id, + path, + to, + job.files().len() + ); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); // TODO + #[cfg(not(windows))] + let files = job.files().clone(); + #[cfg(windows)] + let mut files = job.files().clone(); + #[cfg(windows)] + if self.handler.peer_platform() != "Windows" { + // peer is not windows, need transform \ to / + fs::transform_windows_path(&mut files); + } + self.read_jobs.push(job); + self.timer = time::interval(MILLI1); + allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); + } + } + } + } + Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { + let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); + if is_remote { + log::debug!( + "new write waiting job {}, write to {} from remote {}", + id, + to, + path + ); + let mut job = fs::TransferJob::new_write( + id, + path.clone(), + to, + file_num, + include_hidden, + is_remote, + Vec::new(), + od, + ); + job.is_last_job = true; + self.write_jobs.push(job); + } else { + match fs::TransferJob::new_read( + id, + to.clone(), + path.clone(), + file_num, + include_hidden, + is_remote, + od, + ) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(mut job) => { + log::debug!( + "new read waiting job {}, read {} to remote {}, {} files", + id, + path, + to, + job.files().len() + ); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); + job.is_last_job = true; + self.read_jobs.push(job); + self.timer = time::interval(MILLI1); + } + } + } + } + Data::ResumeJob((id, is_remote)) => { + if is_remote { + if let Some(job) = get_job(id, &mut self.write_jobs) { + job.is_last_job = false; + allow_err!( + peer.send(&fs::new_send( + id, + job.remote.clone(), + job.file_num, + job.show_hidden + )) + .await + ); + } + } else { + if let Some(job) = get_job(id, &mut self.read_jobs) { + job.is_last_job = false; + allow_err!( + peer.send(&fs::new_receive( + id, + job.path.to_string_lossy().to_string(), + job.file_num, + job.files.clone() + )) + .await + ); + } + } + } + Data::SetNoConfirm(id) => { + if let Some(job) = self.remove_jobs.get_mut(&id) { + job.no_confirm = true; + } + } + Data::ConfirmDeleteFiles((id, file_num)) => { + if let Some(job) = self.remove_jobs.get_mut(&id) { + let i = file_num as usize; + if i < job.files.len() { + self.handler.ui_handler.confirm_delete_files( + id, + file_num, + job.files[i].name.clone(), + ); + self.handler.confirm_delete_files(id, file_num); + } + } + } + Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { + if is_upload { + if let Some(job) = fs::get_job(id, &mut self.read_jobs) { + if remember { + job.set_overwrite_strategy(Some(need_override)); + } + job.confirm(&FileTransferSendConfirmRequest { + id, + file_num, + union: if need_override { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) + } else { + Some(file_transfer_send_confirm_request::Union::Skip(true)) + }, + ..Default::default() + }); + } + } else { + if let Some(job) = fs::get_job(id, &mut self.write_jobs) { + if remember { + job.set_overwrite_strategy(Some(need_override)); + } + let mut msg = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_send_confirm(FileTransferSendConfirmRequest { + id, + file_num, + union: if need_override { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) + } else { + Some(file_transfer_send_confirm_request::Union::Skip(true)) + }, + ..Default::default() + }); + msg.set_file_action(file_action); + allow_err!(peer.send(&msg).await); + } + } + } + Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { + let sep = self.handler.get_path_sep(is_remote); + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_all_files(ReadAllFiles { + id, + path: path.clone(), + include_hidden, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + self.remove_jobs + .insert(id, RemoveJob::new(Vec::new(), path, sep, is_remote)); + } else { + match fs::get_recursive_files(&path, include_hidden) { + Ok(entries) => { + // let m = make_fd(id, &entries, true); + // self.handler.call("updateFolderFiles", &make_args!(m)); + self.remove_jobs + .insert(id, RemoveJob::new(entries, path, sep, is_remote)); + } + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + } + } + } + Data::CancelJob(id) => { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_cancel(FileTransferCancel { + id: id, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + if let Some(job) = fs::get_job(id, &mut self.write_jobs) { + job.remove_download_file(); + fs::remove_job(id, &mut self.write_jobs); + } + fs::remove_job(id, &mut self.read_jobs); + self.remove_jobs.remove(&id); + } + Data::RemoveDir((id, path)) => { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_remove_dir(FileRemoveDir { + id, + path, + recursive: true, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } + Data::RemoveFile((id, path, file_num, is_remote)) => { + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_remove_file(FileRemoveFile { + id, + path, + file_num, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } else { + match fs::remove_file(&path) { + Err(err) => { + self.handle_job_status(id, file_num, Some(err.to_string())); + } + Ok(()) => { + self.handle_job_status(id, file_num, None); + } + } + } + } + Data::CreateDir((id, path, is_remote)) => { + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_create(FileDirCreate { + id, + path, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } else { + match fs::create_dir(&path) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(()) => { + self.handle_job_status(id, -1, None); + } + } + } + } + _ => {} + } + true + } + + #[inline] + fn update_job_status( + job: &fs::TransferJob, + elapsed: i32, + last_update_jobs_status: &mut (Instant, HashMap), + handler: &mut Session, + ) { + if elapsed <= 0 { + return; + } + let transferred = job.transferred(); + let last_transferred = { + if let Some(v) = last_update_jobs_status.1.get(&job.id()) { + v.to_owned() + } else { + 0 + } + }; + last_update_jobs_status.1.insert(job.id(), transferred); + let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); + let file_num = job.file_num() - 1; + handler.job_progress(job.id(), file_num, speed, job.finished_size() as f64); + } + + fn update_jobs_status(&mut self) { + let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; + if elapsed >= 1000 { + for job in self.read_jobs.iter() { + Self::update_job_status( + job, + elapsed, + &mut self.last_update_jobs_status, + &mut self.handler, + ); + } + for job in self.write_jobs.iter() { + Self::update_job_status( + job, + elapsed, + &mut self.last_update_jobs_status, + &mut self.handler, + ); + } + self.last_update_jobs_status.0 = Instant::now(); + } + } + + pub async fn sync_jobs_status_to_local(&mut self) -> bool { + log::info!("sync transfer job status"); + let mut config: PeerConfig = self.handler.load_config(); + let mut transfer_metas = TransferSerde::default(); + for job in self.read_jobs.iter() { + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); + transfer_metas.read_jobs.push(json_str); + } + for job in self.write_jobs.iter() { + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); + transfer_metas.write_jobs.push(json_str); + } + log::info!("meta: {:?}", transfer_metas); + config.transfer = transfer_metas; + self.handler.save_config(config); + true + } + + async fn send_opts_after_login(&self, peer: &mut Stream) { + if let Some(opts) = self + .handler + .lc + .read() + .unwrap() + .get_option_message_after_login() + { + let mut misc = Misc::new(); + misc.set_option(opts); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + allow_err!(peer.send(&msg_out).await); + } + } + + async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { + if let Ok(msg_in) = Message::parse_from_bytes(&data) { + match msg_in.union { + Some(message::Union::VideoFrame(vf)) => { + if !self.first_frame { + self.first_frame = true; + self.handler.close_success(); + self.handler.adapt_size(); + self.send_opts_after_login(peer).await; + } + let incomming_format = CodecFormat::from(&vf); + if self.video_format != incomming_format { + self.video_format = incomming_format.clone(); + self.handler.update_quality_status(QualityStatus { + codec_format: Some(incomming_format), + ..Default::default() + }) + }; + self.video_sender.send(MediaData::VideoFrame(vf)).ok(); + } + Some(message::Union::Hash(hash)) => { + self.handler + .handle_hash(&self.handler.password.clone(), hash, peer) + .await; + } + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { + if !self.handler.handle_login_error(&err) { + return false; + } + } + Some(login_response::Union::PeerInfo(pi)) => { + self.handler.handle_peer_info(pi); + // self.check_clipboard_file_context(); + // if !(self.handler.is_file_transfer() + // || self.handler.is_port_forward() + // || !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) + // || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + // || self.handler.lc.read().unwrap().disable_clipboard) + // { + // let txt = self.old_clipboard.lock().unwrap().clone(); + // if !txt.is_empty() { + // let msg_out = crate::create_clipboard_msg(txt); + // let sender = self.sender.clone(); + // tokio::spawn(async move { + // // due to clipboard service interval time + // sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; + // sender.send(Data::Message(msg_out)).ok(); + // }); + // } + // } + + // if self.handler.is_file_transfer() { + // self.load_last_jobs().await; + // } + } + _ => {} + }, + Some(message::Union::CursorData(cd)) => { + self.handler.set_cursor_data(cd); + } + Some(message::Union::CursorId(id)) => { + self.handler.set_cursor_id(id.to_string()); + } + Some(message::Union::CursorPosition(cp)) => { + self.handler.set_cursor_position(cp); + } + Some(message::Union::Clipboard(cb)) => { + if !self.handler.lc.read().unwrap().disable_clipboard { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + update_clipboard(cb, Some(&self.old_clipboard)); + #[cfg(any(target_os = "android", target_os = "ios"))] + { + let content = if cb.compress { + hbb_common::compress::decompress(&cb.content) + } else { + cb.content.into() + }; + if let Ok(content) = String::from_utf8(content) { + self.handler.clipboard(content); + } + } + } + } + #[cfg(windows)] + Some(message::Union::Cliprdr(clip)) => { + if !self.handler.lc.read().unwrap().disable_clipboard { + if let Some(context) = &mut self.clipboard_file_context { + if let Some(clip) = msg_2_clip(clip) { + server_clip_file(context, 0, clip); + } + } + } + } + Some(message::Union::FileResponse(fr)) => { + match fr.union { + Some(file_response::Union::Dir(fd)) => { + #[cfg(windows)] + let entries = fd.entries.to_vec(); + #[cfg(not(windows))] + let mut entries = fd.entries.to_vec(); + #[cfg(not(windows))] + { + if self.handler.peer_platform() == "Windows" { + fs::transform_windows_path(&mut entries); + } + } + // let mut m = make_fd(fd.id, &entries, fd.id > 0); + // if fd.id <= 0 { + // m.set_item("path", fd.path); + // } + // self.handler.call("updateFolderFiles", &make_args!(m)); + if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) { + log::info!("job set_files: {:?}", entries); + job.set_files(entries); + } else if let Some(job) = self.remove_jobs.get_mut(&fd.id) { + job.files = entries; + } + } + Some(file_response::Union::Digest(digest)) => { + if digest.is_upload { + if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { + if let Some(file) = job.files().get(digest.file_num as usize) { + let read_path = get_string(&job.join(&file.name)); + let overwrite_strategy = job.default_overwrite_strategy(); + if let Some(overwrite) = overwrite_strategy { + let req = FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(if overwrite { + file_transfer_send_confirm_request::Union::OffsetBlk(0) + } else { + file_transfer_send_confirm_request::Union::Skip( + true, + ) + }), + ..Default::default() + }; + job.confirm(&req); + let msg = new_send_confirm(req); + allow_err!(peer.send(&msg).await); + } else { + self.handler.override_file_confirm( + digest.id, + digest.file_num, + read_path, + true, + ); + } + } + } + } else { + if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { + if let Some(file) = job.files().get(digest.file_num as usize) { + let write_path = get_string(&job.join(&file.name)); + let overwrite_strategy = job.default_overwrite_strategy(); + match fs::is_write_need_confirmation(&write_path, &digest) { + Ok(res) => match res { + DigestCheckResult::IsSame => { + let msg= new_send_confirm(FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(file_transfer_send_confirm_request::Union::Skip(true)), + ..Default::default() + }); + allow_err!(peer.send(&msg).await); + } + DigestCheckResult::NeedConfirm(digest) => { + if let Some(overwrite) = overwrite_strategy { + let msg = new_send_confirm( + FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(if overwrite { + file_transfer_send_confirm_request::Union::OffsetBlk(0) + } else { + file_transfer_send_confirm_request::Union::Skip(true) + }), + ..Default::default() + }, + ); + allow_err!(peer.send(&msg).await); + } else { + self.handler.override_file_confirm( + digest.id, + digest.file_num, + write_path, + false, + ); + } + } + DigestCheckResult::NoSuchFile => { + let msg = new_send_confirm( + FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), + ..Default::default() + }, + ); + allow_err!(peer.send(&msg).await); + } + }, + Err(err) => { + println!("error recving digest: {}", err); + } + } + } + } + } + } + Some(file_response::Union::Block(block)) => { + log::info!( + "file response block, file id:{}, file num: {}", + block.id, + block.file_num + ); + if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { + if let Err(_err) = job.write(block, None).await { + // to-do: add "skip" for writing job + } + self.update_jobs_status(); + } + } + Some(file_response::Union::Done(d)) => { + if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { + job.modify_time(); + fs::remove_job(d.id, &mut self.write_jobs); + } + self.handle_job_status(d.id, d.file_num, None); + } + Some(file_response::Union::Error(e)) => { + self.handle_job_status(e.id, e.file_num, Some(e.error)); + } + _ => {} + } + } + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::AudioFormat(f)) => { + self.audio_sender.send(MediaData::AudioFormat(f)).ok(); + } + Some(misc::Union::ChatMessage(c)) => { + self.handler.new_message(c.text); + } + Some(misc::Union::PermissionInfo(p)) => { + log::info!("Change permission {:?} -> {}", p.permission, p.enabled); + match p.permission.enum_value_or_default() { + Permission::Keyboard => { + SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); + self.handler.set_permission("keyboard", p.enabled); + } + Permission::Clipboard => { + SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); + self.handler.set_permission("clipboard", p.enabled); + } + Permission::Audio => { + self.handler.set_permission("audio", p.enabled); + } + Permission::File => { + SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst); + if !p.enabled && self.handler.is_file_transfer() { + return true; + } + self.check_clipboard_file_context(); + self.handler.set_permission("file", p.enabled); + } + Permission::Restart => { + self.handler.set_permission("restart", p.enabled); + } + } + } + Some(misc::Union::SwitchDisplay(s)) => { + self.handler.ui_handler.switch_display(&s); + self.video_sender.send(MediaData::Reset).ok(); + if s.width > 0 && s.height > 0 { + self.handler.set_display(s.x, s.y, s.width, s.height); + } + } + Some(misc::Union::CloseReason(c)) => { + self.handler.msgbox("error", "Connection Error", &c); + return false; + } + Some(misc::Union::BackNotification(notification)) => { + if !self.handle_back_notification(notification).await { + return false; + } + } + _ => {} + }, + Some(message::Union::TestDelay(t)) => { + self.handler.handle_test_delay(t, peer).await; + } + Some(message::Union::AudioFrame(frame)) => { + if !self.handler.lc.read().unwrap().disable_audio { + self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); + } + } + Some(message::Union::FileAction(action)) => match action.union { + Some(file_action::Union::SendConfirm(c)) => { + if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { + job.confirm(&c); + } + } + _ => {} + }, + _ => {} + } + } + true + } + + async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { + match notification.union { + Some(back_notification::Union::BlockInputState(state)) => { + self.handle_back_msg_block_input( + state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), + ) + .await; + } + Some(back_notification::Union::PrivacyModeState(state)) => { + if !self + .handle_back_msg_privacy_mode( + state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), + ) + .await + { + return false; + } + } + _ => {} + } + true + } + + #[inline(always)] + fn update_block_input_state(&mut self, on: bool) { + self.handler.update_block_input_state(on); + } + + async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { + match state { + back_notification::BlockInputState::BlkOnSucceeded => { + self.update_block_input_state(true); + } + back_notification::BlockInputState::BlkOnFailed => { + self.handler + .msgbox("custom-error", "Block user input", "Failed"); + self.update_block_input_state(false); + } + back_notification::BlockInputState::BlkOffSucceeded => { + self.update_block_input_state(false); + } + back_notification::BlockInputState::BlkOffFailed => { + self.handler + .msgbox("custom-error", "Unblock user input", "Failed"); + } + _ => {} + } + } + + #[inline(always)] + fn update_privacy_mode(&mut self, on: bool) { + let mut config = self.handler.load_config(); + config.privacy_mode = on; + self.handler.save_config(config); + + self.handler.update_privacy_mode(); + } + + async fn handle_back_msg_privacy_mode( + &mut self, + state: back_notification::PrivacyModeState, + ) -> bool { + match state { + back_notification::PrivacyModeState::PrvOnByOther => { + self.handler.msgbox( + "error", + "Connecting...", + "Someone turns on privacy mode, exit", + ); + return false; + } + back_notification::PrivacyModeState::PrvNotSupported => { + self.handler + .msgbox("custom-error", "Privacy mode", "Unsupported"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnSucceeded => { + self.handler + .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); + self.update_privacy_mode(true); + } + back_notification::PrivacyModeState::PrvOnFailedDenied => { + self.handler + .msgbox("custom-error", "Privacy mode", "Peer denied"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnFailedPlugin => { + self.handler + .msgbox("custom-error", "Privacy mode", "Please install plugins"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnFailed => { + self.handler + .msgbox("custom-error", "Privacy mode", "Failed"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffSucceeded => { + self.handler + .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffByPeer => { + self.handler + .msgbox("custom-error", "Privacy mode", "Peer exit"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffFailed => { + self.handler + .msgbox("custom-error", "Privacy mode", "Failed to turn off"); + } + back_notification::PrivacyModeState::PrvOffUnknown => { + self.handler + .msgbox("custom-error", "Privacy mode", "Turned off"); + // log::error!("Privacy mode is turned off with unknown reason"); + self.update_privacy_mode(false); + } + _ => {} + } + true + } + + fn check_clipboard_file_context(&mut self) { + #[cfg(windows)] + { + let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) + && self.handler.lc.read().unwrap().enable_file_transfer; + if enabled == self.clipboard_file_context.is_none() { + self.clipboard_file_context = if enabled { + match create_clipboard_file_context(true, false) { + Ok(context) => { + log::info!("clipboard context for file transfer created."); + Some(context) + } + Err(err) => { + log::error!( + "Create clipboard context for file transfer: {}", + err.to_string() + ); + None + } + } + } else { + log::info!("clipboard context for file transfer destroyed."); + None + }; + } + } + } +} + +struct RemoveJob { + files: Vec, + path: String, + sep: &'static str, + is_remote: bool, + no_confirm: bool, + last_update_job_status: Instant, +} + +impl RemoveJob { + fn new(files: Vec, path: String, sep: &'static str, is_remote: bool) -> Self { + Self { + files, + path, + sep, + is_remote, + no_confirm: false, + last_update_job_status: Instant::now(), + } + } + + pub fn _gen_meta(&self) -> RemoveJobMeta { + RemoveJobMeta { + path: self.path.clone(), + is_remote: self.is_remote, + no_confirm: self.no_confirm, + } + } +} diff --git a/src/flutter.rs b/src/flutter.rs index 514048e31..0b8c3626f 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,57 +1,36 @@ use std::{ collections::HashMap, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Mutex, RwLock, + Arc, RwLock, }, }; use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; use hbb_common::{ - allow_err, bail, - compress::decompress, - config::{Config, LocalConfig, PeerConfig, TransferSerde}, - fs::{ - self, can_enable_overwrite_detection, get_job, get_string, new_send_confirm, - transform_windows_path, DigestCheckResult, - }, - log, + bail, + config::{LocalConfig}, message_proto::*, - protobuf::Message as _, - rendezvous_proto::ConnType, - tokio::{ - self, - sync::mpsc, - time::{self, Duration, Instant, Interval}, - }, - ResultType, Stream, + ResultType, }; use crate::{ - common::{self, make_fd_to_json, CLIPBOARD_INTERVAL}, ui_session_interface::{io_loop, InvokeUi, Session}, }; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::common::{check_clipboard, update_clipboard, ClipboardContext}; -use crate::{client::*, flutter_ffi::EventToUI, make_fd_flutter}; + +use crate::{client::*, flutter_ffi::EventToUI}; pub(super) const APP_TYPE_MAIN: &str = "main"; pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; -const MILLI1: Duration = Duration::from_millis(1); - lazy_static::lazy_static! { pub static ref SESSIONS: RwLock>> = Default::default(); pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } -static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); - #[derive(Default, Clone)] pub struct FlutterHandler { pub event_stream: Arc>>>, @@ -115,7 +94,7 @@ impl InvokeUi for FlutterHandler { } fn set_permission(&self, name: &str, value: bool) { - // todo!() + self.push_event("permission", vec![(name, &value.to_string())]); } fn update_pi(&self, pi: PeerInfo) { @@ -157,11 +136,14 @@ impl InvokeUi for FlutterHandler { } fn job_error(&self, id: i32, err: String, file_num: i32) { - // todo!() + self.push_event("job_error", vec![("id", &id.to_string()), ("err", &err)]); } fn job_done(&self, id: i32, file_num: i32) { - // todo!() + self.push_event( + "job_done", + vec![("id", &id.to_string()), ("file_num", &file_num.to_string())], + ); } fn clear_all_jobs(&self) { @@ -189,11 +171,27 @@ impl InvokeUi for FlutterHandler { } fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { - // todo!() + self.push_event( + "override_file_confirm", + vec![ + ("id", &id.to_string()), + ("file_num", &file_num.to_string()), + ("read_path", &to), + ("is_upload", &is_upload.to_string()), + ], + ); } fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) { - // todo!() + self.push_event( + "job_progress", + vec![ + ("id", &id.to_string()), + ("file_num", &file_num.to_string()), + ("speed", &speed.to_string()), + ("finished_size", &finished_size.to_string()), + ], + ); } fn adapt_size(&self) { @@ -245,6 +243,35 @@ impl InvokeUi for FlutterHandler { ], ); } + + fn new_message(&self, msg: String) { + self.push_event("chat_client_mode", vec![("text", &msg)]); + } + + fn switch_display(&self, display: &SwitchDisplay) { + self.push_event( + "switch_display", + vec![ + ("display", &display.to_string()), + ("x", &display.x.to_string()), + ("y", &display.y.to_string()), + ("width", &display.width.to_string()), + ("height", &display.height.to_string()), + ], + ); + } + + fn update_block_input_state(&self, on: bool) { + self.push_event( + "update_block_input_state", + [("input_state", if on { "on" } else { "off" })].into(), + ); + } + + #[cfg(any(target_os = "android", target_os = "ios"))] + fn clipboard(&self, content: String) { + self.push_event("clipboard", vec![("content", &content)]); + } } /// Create a new remote session with the given id. @@ -290,6 +317,7 @@ pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultTy *session.event_stream.write().unwrap() = Some(event_stream); let session = session.clone(); std::thread::spawn(move || { + // TODO is_file_transfer is_port_forward // let is_file_transfer = session.lc.read().unwrap().is_file_transfer; // let is_port_forward = session.lc.read().unwrap().is_port_forward; // Connection::start(session, is_file_transfer, is_port_forward); @@ -301,1091 +329,7 @@ pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultTy } } -// struct Connection { -// video_handler: VideoHandler, -// audio_handler: AudioHandler, -// session: Session, -// first_frame: bool, -// read_jobs: Vec, -// write_jobs: Vec, -// timer: Interval, -// last_update_jobs_status: (Instant, HashMap), -// data_count: Arc, -// frame_count: Arc, -// video_format: CodecFormat, -// } - -// impl Connection { -// // TODO: Similar to remote::start_clipboard -// // merge the code -// fn start_clipboard( -// tx_protobuf: mpsc::UnboundedSender, -// lc: Arc>, -// ) -> Option> { -// let (tx, rx) = std::sync::mpsc::channel(); -// #[cfg(not(any(target_os = "android", target_os = "ios")))] -// match ClipboardContext::new() { -// Ok(mut ctx) => { -// let old_clipboard: Arc> = Default::default(); -// // ignore clipboard update before service start -// check_clipboard(&mut ctx, Some(&old_clipboard)); -// std::thread::spawn(move || loop { -// std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); -// match rx.try_recv() { -// Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { -// log::debug!("Exit clipboard service of client"); -// break; -// } -// _ => {} -// } -// if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) -// || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) -// || lc.read().unwrap().disable_clipboard -// { -// continue; -// } -// if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { -// tx_protobuf.send(Data::Message(msg)).ok(); -// } -// }); -// } -// Err(err) => { -// log::error!("Failed to start clipboard service of client: {}", err); -// } -// } -// Some(tx) -// } - -// /// Create a new connection. -// /// -// /// # Arguments -// /// -// /// * `session` - The session to create a new connection for. -// /// * `is_file_transfer` - Whether the connection is for file transfer. -// /// * `is_port_forward` - Whether the connection is for port forward. -// #[tokio::main(flavor = "current_thread")] -// async fn start(session: Session, is_file_transfer: bool, is_port_forward: bool) { -// let mut last_recv_time = Instant::now(); -// let (sender, mut receiver) = mpsc::unbounded_channel::(); -// let mut stop_clipboard = None; -// if !is_file_transfer && !is_port_forward { -// stop_clipboard = Self::start_clipboard(sender.clone(), session.lc.clone()); -// } -// *session.sender.write().unwrap() = Some(sender.clone()); -// let conn_type = if is_file_transfer { -// session.lc.write().unwrap().is_file_transfer = true; -// ConnType::FILE_TRANSFER -// } else if is_port_forward { -// ConnType::PORT_FORWARD // TODO: RDP -// } else { -// ConnType::DEFAULT_CONN -// }; -// let key = Config::get_option("key"); -// let token = Config::get_option("access_token"); - -// // TODO rdp & cli args -// let is_rdp = false; -// let args: Vec = Vec::new(); - -// if is_port_forward { -// if is_rdp { -// // let port = handler -// // .get_option("rdp_port".to_owned()) -// // .parse::() -// // .unwrap_or(3389); -// // std::env::set_var( -// // "rdp_username", -// // handler.get_option("rdp_username".to_owned()), -// // ); -// // std::env::set_var( -// // "rdp_password", -// // handler.get_option("rdp_password".to_owned()), -// // ); -// // log::info!("Remote rdp port: {}", port); -// // start_one_port_forward(handler, 0, "".to_owned(), port, receiver, &key, &token).await; -// } else if args.len() == 0 { -// let pfs = session.lc.read().unwrap().port_forwards.clone(); -// let mut queues = HashMap::>::new(); -// for d in pfs { -// sender.send(Data::AddPortForward(d)).ok(); -// } -// loop { -// match receiver.recv().await { -// Some(Data::AddPortForward((port, remote_host, remote_port))) => { -// if port <= 0 || remote_port <= 0 { -// continue; -// } -// let (sender, receiver) = mpsc::unbounded_channel::(); -// queues.insert(port, sender); -// let handler = session.clone(); -// let key = key.clone(); -// let token = token.clone(); -// tokio::spawn(async move { -// start_one_port_forward( -// handler, -// port, -// remote_host, -// remote_port, -// receiver, -// &key, -// &token, -// ) -// .await; -// }); -// } -// Some(Data::RemovePortForward(port)) => { -// if let Some(s) = queues.remove(&port) { -// s.send(Data::Close).ok(); -// } -// } -// Some(Data::Close) => { -// break; -// } -// Some(d) => { -// for (_, s) in queues.iter() { -// s.send(d.clone()).ok(); -// } -// } -// _ => {} -// } -// } -// } else { -// // let port = handler.args[0].parse::().unwrap_or(0); -// // if handler.args.len() != 3 -// // || handler.args[2].parse::().unwrap_or(0) <= 0 -// // || port <= 0 -// // { -// // handler.on_error("Invalid arguments, usage:

rustdesk --port-forward remote-id listen-port remote-host remote-port"); -// // } -// // let remote_host = handler.args[1].clone(); -// // let remote_port = handler.args[2].parse::().unwrap_or(0); -// // start_one_port_forward( -// // handler, -// // port, -// // remote_host, -// // remote_port, -// // receiver, -// // &key, -// // &token, -// // ) -// // .await; -// } -// return; -// } - -// let latency_controller = LatencyController::new(); -// let latency_controller_cl = latency_controller.clone(); - -// let mut conn = Connection { -// video_handler: VideoHandler::new(latency_controller), -// audio_handler: AudioHandler::new(latency_controller_cl), -// session: session.clone(), -// first_frame: false, -// read_jobs: Vec::new(), -// write_jobs: Vec::new(), -// timer: time::interval(SEC30), -// last_update_jobs_status: (Instant::now(), Default::default()), -// data_count: Arc::new(AtomicUsize::new(0)), -// frame_count: Arc::new(AtomicUsize::new(0)), -// video_format: CodecFormat::Unknown, -// }; - -// match Client::start(&session.id, &key, &token, conn_type, session.clone()).await { -// Ok((mut peer, direct)) => { -// SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); -// SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); - -// session.push_event( -// "connection_ready", -// vec![ -// ("secure", &peer.is_secured().to_string()), -// ("direct", &direct.to_string()), -// ], -// ); - -// let mut status_timer = time::interval(Duration::new(1, 0)); - -// loop { -// tokio::select! { -// res = peer.next() => { -// if let Some(res) = res { -// match res { -// Err(err) => { -// log::error!("Connection closed: {}", err); -// session.msgbox("error", "Connection Error", &err.to_string()); -// break; -// } -// Ok(ref bytes) => { -// last_recv_time = Instant::now(); -// conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed); -// if !conn.handle_msg_from_peer(bytes, &mut peer).await { -// break -// } -// } -// } -// } else { -// if session.lc.read().unwrap().restarting_remote_device { -// log::info!("Restart remote device"); -// session.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); -// } else { -// log::info!("Reset by the peer"); -// session.msgbox("error", "Connection Error", "Reset by the peer"); -// } -// break; -// } -// } -// d = receiver.recv() => { -// if let Some(d) = d { -// if !conn.handle_msg_from_ui(d, &mut peer).await { -// break; -// } -// } -// } -// _ = conn.timer.tick() => { -// if last_recv_time.elapsed() >= SEC30 { -// session.msgbox("error", "Connection Error", "Timeout"); -// break; -// } -// if !conn.read_jobs.is_empty() { -// if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut peer).await { -// log::debug!("Connection Error: {}", err); -// break; -// } -// conn.update_jobs_status(); -// } else { -// conn.timer = time::interval_at(Instant::now() + SEC30, SEC30); -// } -// } -// _ = status_timer.tick() => { -// let speed = conn.data_count.swap(0, Ordering::Relaxed); -// let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); -// let fps = conn.frame_count.swap(0, Ordering::Relaxed) as _; -// conn.session.update_quality_status(QualityStatus { -// speed:Some(speed), -// fps:Some(fps), -// ..Default::default() -// }); -// } -// } -// } -// log::debug!("Exit io_loop of id={}", session.id); -// } -// Err(err) => { -// session.msgbox("error", "Connection Error", &err.to_string()); -// } -// } - -// if let Some(stop) = stop_clipboard { -// stop.send(()).ok(); -// } -// SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); -// SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); -// } - -// /// Handle message from peer. -// /// Return false if the connection should be closed. -// /// -// /// The message is handled by [`Message`], see [`message::Union`] for possible types. -// async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { -// if let Ok(msg_in) = Message::parse_from_bytes(&data) { -// match msg_in.union { -// Some(message::Union::VideoFrame(vf)) => { -// if !self.first_frame { -// self.first_frame = true; -// common::send_opts_after_login(&self.session.lc.read().unwrap(), peer).await; -// } -// let incomming_format = CodecFormat::from(&vf); -// if self.video_format != incomming_format { -// self.video_format = incomming_format.clone(); -// self.session.update_quality_status(QualityStatus { -// codec_format: Some(incomming_format), -// ..Default::default() -// }) -// }; -// if let Ok(true) = self.video_handler.handle_frame(vf) { -// if let Some(stream) = &*self.session.events2ui.read().unwrap() { -// self.frame_count.fetch_add(1, Ordering::Relaxed); -// stream.add(EventToUI::Rgba(ZeroCopyBuffer( -// self.video_handler.rgb.clone(), -// ))); -// } -// } -// } -// Some(message::Union::Hash(hash)) => { -// self.session.handle_hash("", hash, peer).await; -// } -// Some(message::Union::LoginResponse(lr)) => match lr.union { -// Some(login_response::Union::Error(err)) => { -// if !self.session.handle_login_error(&err) { -// return false; -// } -// } -// Some(login_response::Union::PeerInfo(pi)) => { -// self.session.handle_peer_info(pi); -// } -// _ => {} -// }, -// Some(message::Union::Clipboard(cb)) => { -// if !self.session.lc.read().unwrap().disable_clipboard { -// let content = if cb.compress { -// decompress(&cb.content) -// } else { -// cb.content.into() -// }; -// if let Ok(content) = String::from_utf8(content) { -// self.session -// .push_event("clipboard", vec![("content", &content)]); -// } -// } -// } -// Some(message::Union::CursorData(cd)) => { -// let colors = hbb_common::compress::decompress(&cd.colors); -// self.session.push_event( -// "cursor_data", -// vec![ -// ("id", &cd.id.to_string()), -// ("hotx", &cd.hotx.to_string()), -// ("hoty", &cd.hoty.to_string()), -// ("width", &cd.width.to_string()), -// ("height", &cd.height.to_string()), -// ( -// "colors", -// &serde_json::ser::to_string(&colors).unwrap_or("".to_owned()), -// ), -// ], -// ); -// } -// Some(message::Union::CursorId(id)) => { -// self.session -// .push_event("cursor_id", vec![("id", &id.to_string())]); -// } -// Some(message::Union::CursorPosition(cp)) => { -// self.session.push_event( -// "cursor_position", -// vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())], -// ); -// } -// Some(message::Union::FileResponse(fr)) => { -// match fr.union { -// Some(file_response::Union::Dir(fd)) => { -// let mut entries = fd.entries.to_vec(); -// if self.session.peer_platform() == "Windows" { -// transform_windows_path(&mut entries); -// } -// let id = fd.id; -// self.session.push_event( -// "file_dir", -// vec![("value", &make_fd_to_json(fd)), ("is_local", "false")], -// ); -// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { -// job.set_files(entries); -// } -// } -// Some(file_response::Union::Block(block)) => { -// if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { -// if let Err(_err) = job.write(block, None).await { -// // to-do: add "skip" for writing job -// } -// self.update_jobs_status(); -// } -// } -// Some(file_response::Union::Done(d)) => { -// if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { -// job.modify_time(); -// fs::remove_job(d.id, &mut self.write_jobs); -// } -// self.handle_job_status(d.id, d.file_num, None); -// } -// Some(file_response::Union::Error(e)) => { -// self.handle_job_status(e.id, e.file_num, Some(e.error)); -// } -// Some(file_response::Union::Digest(digest)) => { -// if digest.is_upload { -// if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { -// if let Some(file) = job.files().get(digest.file_num as usize) { -// let read_path = get_string(&job.join(&file.name)); -// let overwrite_strategy = job.default_overwrite_strategy(); -// if let Some(overwrite) = overwrite_strategy { -// let req = FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(if overwrite { -// file_transfer_send_confirm_request::Union::OffsetBlk(0) -// } else { -// file_transfer_send_confirm_request::Union::Skip( -// true, -// ) -// }), -// ..Default::default() -// }; -// job.confirm(&req); -// let msg = new_send_confirm(req); -// allow_err!(peer.send(&msg).await); -// } else { -// self.handle_override_file_confirm( -// digest.id, -// digest.file_num, -// read_path, -// true, -// ); -// } -// } -// } -// } else { -// if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { -// if let Some(file) = job.files().get(digest.file_num as usize) { -// let write_path = get_string(&job.join(&file.name)); -// let overwrite_strategy = job.default_overwrite_strategy(); -// match fs::is_write_need_confirmation(&write_path, &digest) { -// Ok(res) => match res { -// DigestCheckResult::IsSame => { -// let msg= new_send_confirm(FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(file_transfer_send_confirm_request::Union::Skip(true)), -// ..Default::default() -// }); -// self.session.send_msg(msg); -// } -// DigestCheckResult::NeedConfirm(digest) => { -// if let Some(overwrite) = overwrite_strategy { -// let msg = new_send_confirm( -// FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(if overwrite { -// file_transfer_send_confirm_request::Union::OffsetBlk(0) -// } else { -// file_transfer_send_confirm_request::Union::Skip(true) -// }), -// ..Default::default() -// }, -// ); -// self.session.send_msg(msg); -// } else { -// self.handle_override_file_confirm( -// digest.id, -// digest.file_num, -// write_path.to_string(), -// false, -// ); -// } -// } -// DigestCheckResult::NoSuchFile => { -// let msg = new_send_confirm( -// FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), -// ..Default::default() -// }, -// ); -// self.session.send_msg(msg); -// } -// }, -// Err(err) => { -// println!("error recving digest: {}", err); -// } -// } -// } -// } -// } -// } -// _ => {} -// } -// } -// Some(message::Union::Misc(misc)) => match misc.union { -// Some(misc::Union::AudioFormat(f)) => { -// self.audio_handler.handle_format(f); // -// } -// Some(misc::Union::ChatMessage(c)) => { -// self.session -// .push_event("chat_client_mode", vec![("text", &c.text)]); -// } -// Some(misc::Union::PermissionInfo(p)) => { -// log::info!("Change permission {:?} -> {}", p.permission, p.enabled); -// use permission_info::Permission; -// self.session.push_event( -// "permission", -// vec![( -// match p.permission.enum_value_or_default() { -// Permission::Keyboard => "keyboard", -// Permission::Clipboard => "clipboard", -// Permission::Audio => "audio", -// Permission::Restart => "restart", -// _ => "", -// }, -// &p.enabled.to_string(), -// )], -// ); -// } -// Some(misc::Union::SwitchDisplay(s)) => { -// self.video_handler.reset(); -// self.session.push_event( -// "switch_display", -// vec![ -// ("display", &s.display.to_string()), -// ("x", &s.x.to_string()), -// ("y", &s.y.to_string()), -// ("width", &s.width.to_string()), -// ("height", &s.height.to_string()), -// ], -// ); -// } -// Some(misc::Union::CloseReason(c)) => { -// self.session.msgbox("error", "Connection Error", &c); -// return false; -// } -// Some(misc::Union::BackNotification(notification)) => { -// if !self.handle_back_notification(notification).await { -// return false; -// } -// } -// _ => {} -// }, -// Some(message::Union::TestDelay(t)) => { -// self.session.handle_test_delay(t, peer).await; -// } -// Some(message::Union::AudioFrame(frame)) => { -// if !self.session.lc.read().unwrap().disable_audio { -// self.audio_handler.handle_frame(frame); -// } -// } -// Some(message::Union::FileAction(action)) => match action.union { -// Some(file_action::Union::SendConfirm(c)) => { -// if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { -// job.confirm(&c); -// } -// } -// _ => {} -// }, -// _ => {} -// } -// } -// true -// } - -// async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { -// match notification.union { -// Some(back_notification::Union::BlockInputState(state)) => { -// self.handle_back_msg_block_input( -// state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), -// ) -// .await; -// } -// Some(back_notification::Union::PrivacyModeState(state)) => { -// if !self -// .handle_back_msg_privacy_mode( -// state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), -// ) -// .await -// { -// return false; -// } -// } -// _ => {} -// } -// true -// } - -// #[inline(always)] -// fn update_block_input_state(&mut self, on: bool) { -// self.session.push_event( -// "update_block_input_state", -// [("input_state", if on { "on" } else { "off" })].into(), -// ); -// } - -// async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { -// match state { -// back_notification::BlockInputState::BlkOnSucceeded => { -// self.update_block_input_state(true); -// } -// back_notification::BlockInputState::BlkOnFailed => { -// self.session -// .msgbox("custom-error", "Block user input", "Failed"); -// self.update_block_input_state(false); -// } -// back_notification::BlockInputState::BlkOffSucceeded => { -// self.update_block_input_state(false); -// } -// back_notification::BlockInputState::BlkOffFailed => { -// self.session -// .msgbox("custom-error", "Unblock user input", "Failed"); -// } -// _ => {} -// } -// } - -// #[inline(always)] -// fn update_privacy_mode(&mut self, on: bool) { -// let mut config = self.session.load_config(); -// config.privacy_mode = on; -// self.session.save_config(&config); -// self.session.lc.write().unwrap().get_config().privacy_mode = on; -// self.session.push_event("update_privacy_mode", [].into()); -// } - -// async fn handle_back_msg_privacy_mode( -// &mut self, -// state: back_notification::PrivacyModeState, -// ) -> bool { -// match state { -// back_notification::PrivacyModeState::PrvOnByOther => { -// self.session.msgbox( -// "error", -// "Connecting...", -// "Someone turns on privacy mode, exit", -// ); -// return false; -// } -// back_notification::PrivacyModeState::PrvNotSupported => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Unsupported"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOnSucceeded => { -// self.session -// .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); -// self.update_privacy_mode(true); -// } -// back_notification::PrivacyModeState::PrvOnFailedDenied => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Peer denied"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOnFailedPlugin => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Please install plugins"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOnFailed => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Failed"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOffSucceeded => { -// self.session -// .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOffByPeer => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Peer exit"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOffFailed => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Failed to turn off"); -// } -// back_notification::PrivacyModeState::PrvOffUnknown => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Turned off"); -// // log::error!("Privacy mode is turned off with unknown reason"); -// self.update_privacy_mode(false); -// } -// _ => {} -// } -// true -// } - -// async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { -// match data { -// Data::Close => { -// self.sync_jobs_status_to_local().await; -// return false; -// } -// Data::Login((password, remember)) => { -// self.session -// .handle_login_from_ui(password, remember, peer) -// .await; -// } -// Data::Message(msg) => { -// allow_err!(peer.send(&msg).await); -// } -// Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { -// let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); -// if is_remote { -// log::debug!("New job {}, write to {} from remote {}", id, to, path); -// self.write_jobs.push(fs::TransferJob::new_write( -// id, -// path.clone(), -// to, -// file_num, -// include_hidden, -// is_remote, -// Vec::new(), -// od, -// )); -// allow_err!( -// peer.send(&fs::new_send(id, path, file_num, include_hidden)) -// .await -// ); -// } else { -// match fs::TransferJob::new_read( -// id, -// to.clone(), -// path.clone(), -// file_num, -// include_hidden, -// is_remote, -// od, -// ) { -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// Ok(job) => { -// log::debug!( -// "New job {}, read {} to remote {}, {} files", -// id, -// path, -// to, -// job.files().len() -// ); -// let m = make_fd_flutter(id, job.files(), true); -// self.session -// .push_event("update_folder_files", vec![("info", &m)]); -// let files = job.files().clone(); -// self.read_jobs.push(job); -// self.timer = time::interval(MILLI1); -// allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); -// } -// } -// } -// } -// Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { -// if is_remote { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_all_files(ReadAllFiles { -// id, -// path: path.clone(), -// include_hidden, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } else { -// match fs::get_recursive_files(&path, include_hidden) { -// Ok(entries) => { -// let mut fd = FileDirectory::new(); -// fd.id = id; -// fd.path = path; -// fd.entries = entries; -// self.session.push_event( -// "file_dir", -// vec![("value", &make_fd_to_json(fd)), ("is_local", "true")], -// ); -// } -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// } -// } -// } -// Data::CancelJob(id) => { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_cancel(FileTransferCancel { -// id: id, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { -// job.remove_download_file(); -// fs::remove_job(id, &mut self.write_jobs); -// } -// fs::remove_job(id, &mut self.read_jobs); -// } -// Data::RemoveDir((id, path)) => { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_remove_dir(FileRemoveDir { -// id, -// path, -// recursive: true, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } -// Data::RemoveFile((id, path, file_num, is_remote)) => { -// if is_remote { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_remove_file(FileRemoveFile { -// id, -// path, -// file_num, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } else { -// match fs::remove_file(&path) { -// Err(err) => { -// self.handle_job_status(id, file_num, Some(err.to_string())); -// } -// Ok(()) => { -// self.handle_job_status(id, file_num, None); -// } -// } -// } -// } -// Data::CreateDir((id, path, is_remote)) => { -// if is_remote { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_create(FileDirCreate { -// id, -// path, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } else { -// match fs::create_dir(&path) { -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// Ok(()) => { -// self.handle_job_status(id, -1, None); -// } -// } -// } -// } -// Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { -// if is_upload { -// if let Some(job) = fs::get_job(id, &mut self.read_jobs) { -// if remember { -// job.set_overwrite_strategy(Some(need_override)); -// } -// job.confirm(&FileTransferSendConfirmRequest { -// id, -// file_num, -// union: if need_override { -// Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) -// } else { -// Some(file_transfer_send_confirm_request::Union::Skip(true)) -// }, -// ..Default::default() -// }); -// } -// } else { -// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { -// if remember { -// job.set_overwrite_strategy(Some(need_override)); -// } -// let mut msg = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_send_confirm(FileTransferSendConfirmRequest { -// id, -// file_num, -// union: if need_override { -// Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) -// } else { -// Some(file_transfer_send_confirm_request::Union::Skip(true)) -// }, -// ..Default::default() -// }); -// msg.set_file_action(file_action); -// self.session.send_msg(msg); -// } -// } -// } -// Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { -// let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); -// if is_remote { -// log::debug!( -// "new write waiting job {}, write to {} from remote {}", -// id, -// to, -// path -// ); -// let mut job = fs::TransferJob::new_write( -// id, -// path.clone(), -// to, -// file_num, -// include_hidden, -// is_remote, -// Vec::new(), -// od, -// ); -// job.is_last_job = true; -// self.write_jobs.push(job); -// } else { -// match fs::TransferJob::new_read( -// id, -// to.clone(), -// path.clone(), -// file_num, -// include_hidden, -// is_remote, -// od, -// ) { -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// Ok(mut job) => { -// log::debug!( -// "new read waiting job {}, read {} to remote {}, {} files", -// id, -// path, -// to, -// job.files().len() -// ); -// let m = make_fd_flutter(job.id(), job.files(), true); -// self.session -// .push_event("update_folder_files", vec![("info", &m)]); -// job.is_last_job = true; -// self.read_jobs.push(job); -// self.timer = time::interval(MILLI1); -// } -// } -// } -// } -// Data::ResumeJob((id, is_remote)) => { -// if is_remote { -// if let Some(job) = get_job(id, &mut self.write_jobs) { -// job.is_last_job = false; -// allow_err!( -// peer.send(&fs::new_send( -// id, -// job.remote.clone(), -// job.file_num, -// job.show_hidden -// )) -// .await -// ); -// } -// } else { -// if let Some(job) = get_job(id, &mut self.read_jobs) { -// job.is_last_job = false; -// allow_err!( -// peer.send(&fs::new_receive( -// id, -// job.path.to_string_lossy().to_string(), -// job.file_num, -// job.files.clone() -// )) -// .await -// ); -// } -// } -// } -// _ => {} -// } -// true -// } - -// #[inline] -// fn update_job_status( -// job: &fs::TransferJob, -// elapsed: i32, -// last_update_jobs_status: &mut (Instant, HashMap), -// session: &Session, -// ) { -// if elapsed <= 0 { -// return; -// } -// let transferred = job.transferred(); -// let last_transferred = { -// if let Some(v) = last_update_jobs_status.1.get(&job.id()) { -// v.to_owned() -// } else { -// 0 -// } -// }; -// last_update_jobs_status.1.insert(job.id(), transferred); -// let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); -// let file_num = job.file_num() - 1; -// session.push_event( -// "job_progress", -// vec![ -// ("id", &job.id().to_string()), -// ("file_num", &file_num.to_string()), -// ("speed", &speed.to_string()), -// ("finished_size", &job.finished_size().to_string()), -// ], -// ); -// } - -// fn update_jobs_status(&mut self) { -// let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; -// if elapsed >= 1000 { -// for job in self.read_jobs.iter() { -// Self::update_job_status( -// job, -// elapsed, -// &mut self.last_update_jobs_status, -// &self.session, -// ); -// } -// for job in self.write_jobs.iter() { -// Self::update_job_status( -// job, -// elapsed, -// &mut self.last_update_jobs_status, -// &self.session, -// ); -// } -// self.last_update_jobs_status.0 = Instant::now(); -// } -// } - -// fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { -// if let Some(err) = err { -// self.session -// .push_event("job_error", vec![("id", &id.to_string()), ("err", &err)]); -// } else { -// self.session.push_event( -// "job_done", -// vec![("id", &id.to_string()), ("file_num", &file_num.to_string())], -// ); -// } -// } - -// fn handle_override_file_confirm( -// &mut self, -// id: i32, -// file_num: i32, -// read_path: String, -// is_upload: bool, -// ) { -// self.session.push_event( -// "override_file_confirm", -// vec![ -// ("id", &id.to_string()), -// ("file_num", &file_num.to_string()), -// ("read_path", &read_path), -// ("is_upload", &is_upload.to_string()), -// ], -// ); -// } - -// async fn sync_jobs_status_to_local(&mut self) -> bool { -// log::info!("sync transfer job status"); -// let mut config: PeerConfig = self.session.load_config(); -// let mut transfer_metas = TransferSerde::default(); -// for job in self.read_jobs.iter() { -// let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); -// transfer_metas.read_jobs.push(json_str); -// } -// for job in self.write_jobs.iter() { -// let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); -// transfer_metas.write_jobs.push(json_str); -// } -// log::info!("meta: {:?}", transfer_metas); -// config.transfer = transfer_metas; -// self.session.save_config(&config); -// true -// } -// } - // Server Side -// TODO connection_manager need use struct and trait,impl default method #[cfg(not(any(target_os = "ios")))] pub mod connection_manager { use std::{ diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 9e8c8fc51..2d412ea9b 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -2,8 +2,8 @@ use std::{ collections::HashMap, ops::{Deref, DerefMut}, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Mutex, RwLock, + atomic::{AtomicBool, Ordering}, + Arc, Mutex, }, }; @@ -22,35 +22,15 @@ use clipboard::{ cliprdr::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context, get_rx_clip_client, server_clip_file, }; -use enigo::{self, Enigo, KeyboardControllable}; -use hbb_common::{ - allow_err, - config::{Config, LocalConfig, PeerConfig, TransferSerde}, - fs::{ - self, can_enable_overwrite_detection, get_job, get_string, new_send_confirm, - DigestCheckResult, RemoveJobMeta, TransferJobMeta, - }, - get_version_number, log, - message_proto::{permission_info::Permission, *}, - protobuf::Message as _, - rendezvous_proto::ConnType, - sleep, - tokio::{ - self, - sync::mpsc, - time::{self, Duration, Instant, Interval}, - }, - Stream, -}; +use enigo::{self}; +use hbb_common::{allow_err, log, message_proto::*}; #[cfg(windows)] use crate::clipboard_file::*; use crate::{ client::*, - common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, - ui_session_interface::{io_loop, InvokeUi, Remote, Session, SERVER_KEYBOARD_ENABLED}, + ui_session_interface::{InvokeUi, Session}, }; -use errno; type Video = AssetPtr; @@ -131,7 +111,7 @@ impl InvokeUi for SciterHandler { self.call2("setPermission", &make_args!(name, value)); } - fn update_pi(&self, pi: PeerInfo) {} + fn update_pi(&self, pi: PeerInfo) {} // TODO dup flutter fn close_success(&self) { self.call2("closeSuccess", &make_args!()); @@ -165,15 +145,15 @@ impl InvokeUi for SciterHandler { } fn job_error(&self, id: i32, err: String, file_num: i32) { - todo!() + self.call("jobError", &make_args!(id, err, file_num)); } fn job_done(&self, id: i32, file_num: i32) { - todo!() + self.call("jobDone", &make_args!(id, file_num)); } fn clear_all_jobs(&self) { - todo!() + self.call("clearAllJobs", &make_args!()); } fn add_job( @@ -189,19 +169,25 @@ impl InvokeUi for SciterHandler { } fn update_transfer_list(&self) { - todo!() + self.call("updateTransferList", &make_args!()); } fn confirm_delete_files(&self, id: i32, i: i32, name: String) { - todo!() + self.call("confirmDeleteFiles", &make_args!(id, i, name)); } fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { - todo!() + self.call( + "overrideFileConfirm", + &make_args!(id, file_num, to, is_upload), + ); } fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) { - todo!() + self.call( + "jobProgress", + &make_args!(id, file_num, speed, finished_size), + ); } fn adapt_size(&self) { @@ -227,11 +213,22 @@ impl InvokeUi for SciterHandler { current_display: usize, is_file_transfer: bool, ) { - todo!() } fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool) { - todo!() + self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); + } + + fn new_message(&self, msg: String) { + self.call("newMessage", &make_args!(msg)); + } + + fn switch_display(&self, display: &SwitchDisplay) { + self.call("switchDisplay", &make_args!(display.display)); + } + + fn update_block_input_state(&self, on: bool) { + self.call("updateBlockInputState", &make_args!(on)); } } @@ -405,21 +402,7 @@ impl SciterSession { Self(session) } - // fn update_quality_status(&self, status: QualityStatus) { - // self.call2( - // "updateQualityStatus", - // &make_args!( - // status.speed.map_or(Value::null(), |it| it.into()), - // status.fps.map_or(Value::null(), |it| it.into()), - // status.delay.map_or(Value::null(), |it| it.into()), - // status.target_bitrate.map_or(Value::null(), |it| it.into()), - // status - // .codec_format - // .map_or(Value::null(), |it| it.to_string().into()) - // ), - // ); - // } - + // TODO fn start_keyboard_hook(&'static self) { if self.is_port_forward() || self.is_file_transfer() { return; @@ -652,14 +635,6 @@ impl SciterSession { }); } - // fn get_view_style(&mut self) -> String { - // return self.lc.read().unwrap().view_style.clone(); - // } - - // fn get_image_quality(&mut self) -> String { - // return self.lc.read().unwrap().image_quality.clone(); - // } - // TODO fn get_custom_image_quality(&mut self) -> Value { let mut v = Value::array(0); @@ -669,87 +644,6 @@ impl SciterSession { v } - // #[inline] - // pub(super) fn save_config(&self, config: PeerConfig) { - // self.lc.write().unwrap().save_config(config); - // } - - // fn save_view_style(&mut self, value: String) { - // self.lc.write().unwrap().save_view_style(value); - // } - - // #[inline] - // pub(super) fn load_config(&self) -> PeerConfig { - // load_config(&self.id) - // } - - // fn toggle_option(&mut self, name: String) { - // let msg = self.lc.write().unwrap().toggle_option(name.clone()); - // if name == "enable-file-transfer" { - // self.send(Data::ToggleClipboardFile); - // } - // if let Some(msg) = msg { - // self.send(Data::Message(msg)); - // } - // } - - // fn get_toggle_option(&mut self, name: String) -> bool { - // self.lc.read().unwrap().get_toggle_option(&name) - // } - - // fn is_privacy_mode_supported(&self) -> bool { - // self.lc.read().unwrap().is_privacy_mode_supported() - // } - - // fn refresh_video(&mut self) { - // self.send(Data::Message(LoginConfigHandler::refresh())); - // } - - // fn save_custom_image_quality(&mut self, custom_image_quality: i32) { - // let msg = self - // .lc - // .write() - // .unwrap() - // .save_custom_image_quality(custom_image_quality); - // self.send(Data::Message(msg)); - // } - - // fn save_image_quality(&mut self, value: String) { - // let msg = self.lc.write().unwrap().save_image_quality(value); - // if let Some(msg) = msg { - // self.send(Data::Message(msg)); - // } - // } - - // fn get_remember(&mut self) -> bool { - // self.lc.read().unwrap().remember - // } - - // fn set_write_override( - // &mut self, - // job_id: i32, - // file_num: i32, - // is_override: bool, - // remember: bool, - // is_upload: bool, - // ) -> bool { - // self.send(Data::SetConfirmOverrideFile(( - // job_id, - // file_num, - // is_override, - // remember, - // is_upload, - // ))); - // true - // } - - // fn has_hwcodec(&self) -> bool { - // #[cfg(not(feature = "hwcodec"))] - // return false; - // #[cfg(feature = "hwcodec")] - // return true; - // } - // TODO fn supported_hwcodec(&self) -> Value { #[cfg(feature = "hwcodec")] @@ -775,51 +669,6 @@ impl SciterSession { } } - // fn change_prefer_codec(&self) { - // let msg = self.lc.write().unwrap().change_prefer_codec(); - // self.send(Data::Message(msg)); - // } - - // fn restart_remote_device(&mut self) { - // let mut lc = self.lc.write().unwrap(); - // lc.restarting_remote_device = true; - // let msg = lc.restart_remote_device(); - // self.send(Data::Message(msg)); - // } - - // pub fn is_restarting_remote_device(&self) -> bool { - // self.lc.read().unwrap().restarting_remote_device - // } - - // fn t(&self, name: String) -> String { - // crate::client::translate(name) - // } - - // fn get_audit_server(&self) -> String { - // if self.lc.read().unwrap().conn_id <= 0 - // || LocalConfig::get_option("access_token").is_empty() - // { - // return "".to_owned(); - // } - // crate::get_audit_server( - // Config::get_option("api-server"), - // Config::get_option("custom-rendezvous-server"), - // ) - // } - - // fn send_note(&self, note: String) { - // let url = self.get_audit_server(); - // let id = self.id.clone(); - // let conn_id = self.lc.read().unwrap().conn_id; - // std::thread::spawn(move || { - // send_note(url, id, conn_id, note); - // }); - // } - - // fn is_xfce(&self) -> bool { - // crate::platform::is_xfce() - // } - // TODO fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) { let size = (x, y, w, h); @@ -881,34 +730,6 @@ impl SciterSession { v } - // fn remove_port_forward(&mut self, port: i32) { - // let mut config = self.load_config(); - // config.port_forwards = config - // .port_forwards - // .drain(..) - // .filter(|x| x.0 != port) - // .collect(); - // self.save_config(config); - // self.send(Data::RemovePortForward(port)); - // } - - // fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { - // let mut config = self.load_config(); - // if config - // .port_forwards - // .iter() - // .filter(|x| x.0 == port) - // .next() - // .is_some() - // { - // return; - // } - // let pf = (port, remote_host, remote_port); - // config.port_forwards.push(pf.clone()); - // self.save_config(config); - // self.send(Data::AddPortForward(pf)); - // } - fn get_size(&mut self) -> Value { let s = if self.is_file_transfer() { self.lc.read().unwrap().size_ft @@ -925,10 +746,6 @@ impl SciterSession { v } - // fn get_id(&mut self) -> String { - // self.id.clone() - // } - fn get_default_pi(&mut self) -> Value { let mut pi = Value::map(); let info = self.lc.read().unwrap().info.clone(); @@ -938,158 +755,11 @@ impl SciterSession { pi } - // fn get_option(&self, k: String) -> String { - // self.lc.read().unwrap().get_option(&k) - // } - - // fn set_option(&self, k: String, v: String) { - // self.lc.write().unwrap().set_option(k, v); - // } - - // fn input_os_password(&mut self, pass: String, activate: bool) { - // input_os_password(pass, activate, self.clone()); - // } - // close_state sciter only fn save_close_state(&mut self, k: String, v: String) { self.close_state.insert(k, v); } - // fn get_chatbox(&mut self) -> String { - // #[cfg(feature = "inline")] - // return super::inline::get_chatbox(); - // #[cfg(not(feature = "inline"))] - // return "".to_owned(); - // } - - // fn get_icon(&mut self) -> String { - // crate::get_icon() - // } - - // fn send_chat(&mut self, text: String) { - // let mut misc = Misc::new(); - // misc.set_chat_message(ChatMessage { - // text, - // ..Default::default() - // }); - // let mut msg_out = Message::new(); - // msg_out.set_misc(misc); - // self.send(Data::Message(msg_out)); - // } - - // fn switch_display(&mut self, display: i32) { - // let mut misc = Misc::new(); - // misc.set_switch_display(SwitchDisplay { - // display, - // ..Default::default() - // }); - // let mut msg_out = Message::new(); - // msg_out.set_misc(misc); - // self.send(Data::Message(msg_out)); - // } - - // fn is_file_transfer(&self) -> bool { - // self.cmd == "--file-transfer" - // } - - // fn is_port_forward(&self) -> bool { - // self.cmd == "--port-forward" || self.is_rdp() - // } - - // fn is_rdp(&self) -> bool { - // self.cmd == "--rdp" - // } - - // fn reconnect(&mut self) { - // println!("reconnecting"); - // let cloned = self.clone(); - // let mut lock = self.thread.lock().unwrap(); - // lock.take().map(|t| t.join()); - // *lock = Some(std::thread::spawn(move || { - // io_loop(cloned); - // })); - // } - - // #[inline] - // fn peer_platform(&self) -> String { - // self.lc.read().unwrap().info.platform.clone() - // } - - // fn get_platform(&mut self, is_remote: bool) -> String { - // if is_remote { - // self.peer_platform() - // } else { - // whoami::platform().to_string() - // } - // } - - // fn get_path_sep(&mut self, is_remote: bool) -> &'static str { - // let p = self.get_platform(is_remote); - // if &p == "Windows" { - // return "\\"; - // } else { - // return "/"; - // } - // } - - // fn get_icon_path(&mut self, file_type: i32, ext: String) -> String { - // let mut path = Config::icon_path(); - // if file_type == FileType::DirLink as i32 { - // let new_path = path.join("dir_link"); - // if !std::fs::metadata(&new_path).is_ok() { - // #[cfg(windows)] - // allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); - // #[cfg(not(windows))] - // allow_err!(std::os::unix::fs::symlink(&path, &new_path)); - // } - // path = new_path; - // } else if file_type == FileType::File as i32 { - // if !ext.is_empty() { - // path = path.join(format!("file.{}", ext)); - // } else { - // path = path.join("file"); - // } - // if !std::fs::metadata(&path).is_ok() { - // allow_err!(std::fs::File::create(&path)); - // } - // } else if file_type == FileType::FileLink as i32 { - // let new_path = path.join("file_link"); - // if !std::fs::metadata(&new_path).is_ok() { - // path = path.join("file"); - // if !std::fs::metadata(&path).is_ok() { - // allow_err!(std::fs::File::create(&path)); - // } - // #[cfg(windows)] - // allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); - // #[cfg(not(windows))] - // allow_err!(std::os::unix::fs::symlink(&path, &new_path)); - // } - // path = new_path; - // } else if file_type == FileType::DirDrive as i32 { - // if cfg!(windows) { - // path = fs::get_path("C:"); - // } else if cfg!(target_os = "macos") { - // if let Ok(entries) = fs::get_path("/Volumes/").read_dir() { - // for entry in entries { - // if let Ok(entry) = entry { - // path = entry.path(); - // break; - // } - // } - // } - // } - // } - // fs::get_string(&path) - // } - - // fn login(&mut self, password: String, remember: bool) { - // self.send(Data::Login((password, remember))); - // } - - // fn new_rdp(&mut self) { - // self.send(Data::NewRDP); - // } - fn enter(&mut self) { #[cfg(windows)] crate::platform::windows::stop_system_key_propagate(true); @@ -1102,30 +772,6 @@ impl SciterSession { IS_IN.store(false, Ordering::SeqCst); } - // TODO - fn set_cursor_data(&mut self, cd: CursorData) { - let mut colors = hbb_common::compress::decompress(&cd.colors); - if colors.iter().filter(|x| **x != 0).next().is_none() { - log::info!("Fix transparent"); - // somehow all 0 images shows black rect, here is a workaround - colors[3] = 1; - } - let mut png = Vec::new(); - if let Ok(()) = repng::encode(&mut png, cd.width as _, cd.height as _, &colors) { - self.call( - "setCursorData", - &make_args!( - cd.id.to_string(), - cd.hotx, - cd.hoty, - cd.width, - cd.height, - &png[..] - ), - ); - } - } - fn get_key_event(&self, down_or_up: i32, name: &str, code: i32) -> Option { let mut key_event = KeyEvent::new(); if down_or_up == 2 { @@ -1260,21 +906,6 @@ impl SciterSession { log::error!("Failed to spawn IP tunneling: {}", err); } } - - // #[inline] - // fn set_cursor_id(&mut self, id: String) { - // self.call("setCursorId", &make_args!(id)); - // } - - // #[inline] - // fn set_cursor_position(&mut self, cd: CursorPosition) { - // self.call("setCursorPosition", &make_args!(cd.x, cd.y)); - // } - - // #[inline] - // fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { - // self.call("setDisplay", &make_args!(x, y, w, h)); - // } } pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 03666ed92..a164a2d94 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1,33 +1,23 @@ +use crate::client::io_loop::Remote; use crate::client::{ - self, check_if_retry, get_key_state, handle_hash, handle_login_from_ui, handle_test_delay, - input_os_password, load_config, send_mouse, start_video_audio_threads, Client, CodecFormat, - FileManager, Key, LoginConfigHandler, MediaData, MediaSender, QualityStatus, KEY_MAP, SEC30, + check_if_retry, get_key_state, handle_hash, handle_login_from_ui, handle_test_delay, + input_os_password, load_config, send_mouse, start_video_audio_threads, FileManager, Key, + LoginConfigHandler, QualityStatus, KEY_MAP, }; -use crate::common::{ - self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL, -}; -use crate::platform; +use crate::common; + use crate::{client::Data, client::Interface}; use async_trait::async_trait; -use enigo::{Enigo, KeyboardControllable}; -use hbb_common::config::{Config, LocalConfig, PeerConfig, TransferSerde}; -use hbb_common::fs::{ - can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, - RemoveJobMeta, TransferJobMeta, -}; -use hbb_common::message_proto::permission_info::Permission; -use hbb_common::protobuf::Message as _; -use hbb_common::rendezvous_proto::ConnType; -use hbb_common::tokio::{ - self, - sync::mpsc, - time::{self, Duration, Instant, Interval}, -}; -use hbb_common::{allow_err, message_proto::*, sleep}; + +use hbb_common::config::{Config, LocalConfig, PeerConfig}; + +use hbb_common::tokio::{self, sync::mpsc}; + +use hbb_common::{allow_err, message_proto::*}; use hbb_common::{fs, get_version_number, log, Stream}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock}; #[derive(Clone, Default)] @@ -541,6 +531,7 @@ pub trait InvokeUi: Send + Sync + Clone + 'static + Sized + Default { fn set_cursor_id(&self, id: String); fn set_cursor_position(&self, cp: CursorPosition); fn set_display(&self, x: i32, y: i32, w: i32, h: i32); + fn switch_display(&self, display: &SwitchDisplay); fn set_peer_info( &self, username: &str, @@ -570,14 +561,18 @@ pub trait InvokeUi: Send + Sync + Clone + 'static + Sized + Default { show_hidden: bool, is_remote: bool, ); + fn new_message(&self, msg: String); fn update_transfer_list(&self); - // fn update_folder_files(&self); // TODO + // fn update_folder_files(&self); // TODO flutter with file_dir and update_folder_files fn confirm_delete_files(&self, id: i32, i: i32, name: String); fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool); + fn update_block_input_state(&self, on: bool); fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64); fn adapt_size(&self); fn on_rgba(&self, data: &[u8]); fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool); + #[cfg(any(target_os = "android", target_os = "ios"))] + fn clipboard(&self, content: String); } impl Deref for Session { @@ -604,21 +599,23 @@ impl Interface for Session { } } + // TODO flutter fn is_file_transfer(&self) -> bool { self.cmd == "--file-transfer" } + // TODO flutter fn is_port_forward(&self) -> bool { self.cmd == "--port-forward" || self.is_rdp() } + // TODO flutter fn is_rdp(&self) -> bool { self.cmd == "--rdp" } fn msgbox(&self, msgtype: &str, title: &str, text: &str) { let retry = check_if_retry(msgtype, title, text); - // self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); self.ui_handler.msgbox(msgtype, title, text, retry); } @@ -916,1217 +913,6 @@ async fn start_one_port_forward( log::info!("port forward (:{}) exit", port); } -pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -pub static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true); -pub static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -const MILLI1: Duration = Duration::from_millis(1); - -pub struct Remote { - handler: Session, - video_sender: MediaSender, - audio_sender: MediaSender, - receiver: mpsc::UnboundedReceiver, - sender: mpsc::UnboundedSender, - old_clipboard: Arc>, - read_jobs: Vec, - write_jobs: Vec, - remove_jobs: HashMap, - timer: Interval, - last_update_jobs_status: (Instant, HashMap), - first_frame: bool, - #[cfg(windows)] - clipboard_file_context: Option>, - data_count: Arc, - frame_count: Arc, - video_format: CodecFormat, -} - -impl Remote { - pub fn new( - handler: Session, - video_sender: MediaSender, - audio_sender: MediaSender, - receiver: mpsc::UnboundedReceiver, - sender: mpsc::UnboundedSender, - frame_count: Arc, - ) -> Self { - Self { - handler, - video_sender, - audio_sender, - receiver, - sender, - old_clipboard: Default::default(), - read_jobs: Vec::new(), - write_jobs: Vec::new(), - remove_jobs: Default::default(), - timer: time::interval(SEC30), - last_update_jobs_status: (Instant::now(), Default::default()), - first_frame: false, - #[cfg(windows)] - clipboard_file_context: None, - data_count: Arc::new(AtomicUsize::new(0)), - frame_count, - video_format: CodecFormat::Unknown, - } - } - - pub async fn io_loop(&mut self, key: &str, token: &str) { - let stop_clipboard = self.start_clipboard(); - let mut last_recv_time = Instant::now(); - let mut received = false; - let conn_type = if self.handler.is_file_transfer() { - ConnType::FILE_TRANSFER - } else { - ConnType::default() - }; - match Client::start( - &self.handler.id, - key, - token, - conn_type, - self.handler.clone(), - ) - .await - { - Ok((mut peer, direct)) => { - SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); - SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst); - // self.handler - // .call("setConnectionType", &make_args!(peer.is_secured(), direct)); - self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready - - // just build for now - #[cfg(not(windows))] - let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::(); - #[cfg(windows)] - let mut rx_clip_client = get_rx_clip_client().lock().await; - - let mut status_timer = time::interval(Duration::new(1, 0)); - - loop { - tokio::select! { - res = peer.next() => { - if let Some(res) = res { - match res { - Err(err) => { - log::error!("Connection closed: {}", err); - self.handler.set_force_relay(direct, received); - self.handler.msgbox("error", "Connection Error", &err.to_string()); - break; - } - Ok(ref bytes) => { - last_recv_time = Instant::now(); - received = true; - self.data_count.fetch_add(bytes.len(), Ordering::Relaxed); - if !self.handle_msg_from_peer(bytes, &mut peer).await { - break - } - } - } - } else { - if self.handler.is_restarting_remote_device() { - log::info!("Restart remote device"); - self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); - } else { - log::info!("Reset by the peer"); - self.handler.msgbox("error", "Connection Error", "Reset by the peer"); - } - break; - } - } - d = self.receiver.recv() => { - if let Some(d) = d { - if !self.handle_msg_from_ui(d, &mut peer).await { - break; - } - } - } - _msg = rx_clip_client.recv() => { - #[cfg(windows)] - match _msg { - Some((_, clip)) => { - allow_err!(peer.send(&clip_2_msg(clip)).await); - } - None => { - // unreachable!() - } - } - } - _ = self.timer.tick() => { - if last_recv_time.elapsed() >= SEC30 { - self.handler.msgbox("error", "Connection Error", "Timeout"); - break; - } - if !self.read_jobs.is_empty() { - if let Err(err) = fs::handle_read_jobs(&mut self.read_jobs, &mut peer).await { - self.handler.msgbox("error", "Connection Error", &err.to_string()); - break; - } - self.update_jobs_status(); - } else { - self.timer = time::interval_at(Instant::now() + SEC30, SEC30); - } - } - _ = status_timer.tick() => { - let speed = self.data_count.swap(0, Ordering::Relaxed); - let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); - let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; - self.handler.update_quality_status(QualityStatus { - speed:Some(speed), - fps:Some(fps), - ..Default::default() - }); - } - } - } - log::debug!("Exit io_loop of id={}", self.handler.id); - } - Err(err) => { - self.handler - .msgbox("error", "Connection Error", &err.to_string()); - } - } - if let Some(stop) = stop_clipboard { - stop.send(()).ok(); - } - SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); - SERVER_FILE_TRANSFER_ENABLED.store(false, Ordering::SeqCst); - } - - fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { - if let Some(job) = self.remove_jobs.get_mut(&id) { - if job.no_confirm { - let file_num = (file_num + 1) as usize; - if file_num < job.files.len() { - let path = format!("{}{}{}", job.path, job.sep, job.files[file_num].name); - self.sender - .send(Data::RemoveFile((id, path, file_num as i32, job.is_remote))) - .ok(); - let elapsed = job.last_update_job_status.elapsed().as_millis() as i32; - if elapsed >= 1000 { - job.last_update_job_status = Instant::now(); - } else { - return; - } - } else { - self.remove_jobs.remove(&id); - } - } - } - if let Some(err) = err { - // self.handler - // .call("jobError", &make_args!(id, err, file_num)); - self.handler.job_error(id, err, file_num); - } else { - // self.handler.call("jobDone", &make_args!(id, file_num)); - self.handler.job_done(id, file_num); - } - } - - fn start_clipboard(&mut self) -> Option> { - if self.handler.is_file_transfer() || self.handler.is_port_forward() { - return None; - } - let (tx, rx) = std::sync::mpsc::channel(); - let old_clipboard = self.old_clipboard.clone(); - let tx_protobuf = self.sender.clone(); - let lc = self.handler.lc.clone(); - match ClipboardContext::new() { - Ok(mut ctx) => { - // ignore clipboard update before service start - check_clipboard(&mut ctx, Some(&old_clipboard)); - std::thread::spawn(move || loop { - std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); - match rx.try_recv() { - Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { - log::debug!("Exit clipboard service of client"); - break; - } - _ => {} - } - if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) - || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - || lc.read().unwrap().disable_clipboard - { - continue; - } - if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { - tx_protobuf.send(Data::Message(msg)).ok(); - } - }); - } - Err(err) => { - log::error!("Failed to start clipboard service of client: {}", err); - } - } - Some(tx) - } - - fn load_last_jobs(&mut self) { - log::info!("start load last jobs"); - // self.handler.call("clearAllJobs", &make_args!()); - self.handler.clear_all_jobs(); - let pc = self.handler.load_config(); - if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { - // no last jobs - return; - } - // TODO: can add a confirm dialog - let mut cnt = 1; - for job_str in pc.transfer.read_jobs.iter() { - let job: Result = serde_json::from_str(&job_str); - if let Ok(job) = job { - self.handler.add_job( - cnt, - job.to.clone(), - job.remote.clone(), - job.file_num, - job.show_hidden, - false, - ); - cnt += 1; - println!("restore read_job: {:?}", job); - } - } - for job_str in pc.transfer.write_jobs.iter() { - let job: Result = serde_json::from_str(&job_str); - if let Ok(job) = job { - self.handler.add_job( - cnt, - job.remote.clone(), - job.to.clone(), - job.file_num, - job.show_hidden, - true, - ); - cnt += 1; - println!("restore write_job: {:?}", job); - } - } - // self.handler.call("updateTransferList", &make_args!()); - self.handler.update_transfer_list(); - } - - async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { - match data { - Data::Close => { - let mut misc = Misc::new(); - misc.set_close_reason("".to_owned()); - let mut msg = Message::new(); - msg.set_misc(misc); - allow_err!(peer.send(&msg).await); - return false; - } - Data::Login((password, remember)) => { - self.handler - .handle_login_from_ui(password, remember, peer) - .await; - } - Data::ToggleClipboardFile => { - self.check_clipboard_file_context(); - } - Data::Message(msg) => { - allow_err!(peer.send(&msg).await); - } - Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { - log::info!("send files, is remote {}", is_remote); - let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); - if is_remote { - log::debug!("New job {}, write to {} from remote {}", id, to, path); - self.write_jobs.push(fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - )); - allow_err!( - peer.send(&fs::new_send(id, path, file_num, include_hidden)) - .await - ); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(job) => { - log::debug!( - "New job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - // let m = make_fd(job.id(), job.files(), true); - // self.handler.call("updateFolderFiles", &make_args!(m)); // TODO - #[cfg(not(windows))] - let files = job.files().clone(); - #[cfg(windows)] - let mut files = job.files().clone(); - #[cfg(windows)] - if self.handler.peer_platform() != "Windows" { - // peer is not windows, need transform \ to / - fs::transform_windows_path(&mut files); - } - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); - } - } - } - } - Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { - let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); - if is_remote { - log::debug!( - "new write waiting job {}, write to {} from remote {}", - id, - to, - path - ); - let mut job = fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - ); - job.is_last_job = true; - self.write_jobs.push(job); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(mut job) => { - log::debug!( - "new read waiting job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - // let m = make_fd(job.id(), job.files(), true); - // self.handler.call("updateFolderFiles", &make_args!(m)); - job.is_last_job = true; - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - } - } - } - } - Data::ResumeJob((id, is_remote)) => { - if is_remote { - if let Some(job) = get_job(id, &mut self.write_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_send( - id, - job.remote.clone(), - job.file_num, - job.show_hidden - )) - .await - ); - } - } else { - if let Some(job) = get_job(id, &mut self.read_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_receive( - id, - job.path.to_string_lossy().to_string(), - job.file_num, - job.files.clone() - )) - .await - ); - } - } - } - Data::SetNoConfirm(id) => { - if let Some(job) = self.remove_jobs.get_mut(&id) { - job.no_confirm = true; - } - } - Data::ConfirmDeleteFiles((id, file_num)) => { - if let Some(job) = self.remove_jobs.get_mut(&id) { - let i = file_num as usize; - if i < job.files.len() { - // self.handler.call( - // "confirmDeleteFiles", - // &make_args!(id, file_num, job.files[i].name.clone()), - // ); - self.handler.confirm_delete_files(id, file_num); - } - } - } - Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { - if is_upload { - if let Some(job) = fs::get_job(id, &mut self.read_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - job.confirm(&FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - } - } else { - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - let mut msg = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_send_confirm(FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - msg.set_file_action(file_action); - allow_err!(peer.send(&msg).await); - } - } - } - Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { - let sep = self.handler.get_path_sep(is_remote); - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_all_files(ReadAllFiles { - id, - path: path.clone(), - include_hidden, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - self.remove_jobs - .insert(id, RemoveJob::new(Vec::new(), path, sep, is_remote)); - } else { - match fs::get_recursive_files(&path, include_hidden) { - Ok(entries) => { - // let m = make_fd(id, &entries, true); - // self.handler.call("updateFolderFiles", &make_args!(m)); - self.remove_jobs - .insert(id, RemoveJob::new(entries, path, sep, is_remote)); - } - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - } - } - } - Data::CancelJob(id) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_cancel(FileTransferCancel { - id: id, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - job.remove_download_file(); - fs::remove_job(id, &mut self.write_jobs); - } - fs::remove_job(id, &mut self.read_jobs); - self.remove_jobs.remove(&id); - } - Data::RemoveDir((id, path)) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_dir(FileRemoveDir { - id, - path, - recursive: true, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } - Data::RemoveFile((id, path, file_num, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_file(FileRemoveFile { - id, - path, - file_num, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::remove_file(&path) { - Err(err) => { - self.handle_job_status(id, file_num, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, file_num, None); - } - } - } - } - Data::CreateDir((id, path, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_create(FileDirCreate { - id, - path, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::create_dir(&path) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, -1, None); - } - } - } - } - _ => {} - } - true - } - - #[inline] - fn update_job_status( - job: &fs::TransferJob, - elapsed: i32, - last_update_jobs_status: &mut (Instant, HashMap), - handler: &mut Session, - ) { - if elapsed <= 0 { - return; - } - let transferred = job.transferred(); - let last_transferred = { - if let Some(v) = last_update_jobs_status.1.get(&job.id()) { - v.to_owned() - } else { - 0 - } - }; - last_update_jobs_status.1.insert(job.id(), transferred); - let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); - let file_num = job.file_num() - 1; - // handler.call( - // "jobProgress", - // &make_args!(job.id(), file_num, speed, job.finished_size() as f64), - // ); - handler.job_progress(job.id(), file_num, speed, job.finished_size() as f64); - } - - fn update_jobs_status(&mut self) { - let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; - if elapsed >= 1000 { - for job in self.read_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &mut self.handler, - ); - } - for job in self.write_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &mut self.handler, - ); - } - self.last_update_jobs_status.0 = Instant::now(); - } - } - - pub async fn sync_jobs_status_to_local(&mut self) -> bool { - log::info!("sync transfer job status"); - let mut config: PeerConfig = self.handler.load_config(); - let mut transfer_metas = TransferSerde::default(); - for job in self.read_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); - transfer_metas.read_jobs.push(json_str); - } - for job in self.write_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); - transfer_metas.write_jobs.push(json_str); - } - log::info!("meta: {:?}", transfer_metas); - config.transfer = transfer_metas; - self.handler.save_config(config); - true - } - - async fn send_opts_after_login(&self, peer: &mut Stream) { - if let Some(opts) = self - .handler - .lc - .read() - .unwrap() - .get_option_message_after_login() - { - let mut misc = Misc::new(); - misc.set_option(opts); - let mut msg_out = Message::new(); - msg_out.set_misc(misc); - allow_err!(peer.send(&msg_out).await); - } - } - - async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { - if let Ok(msg_in) = Message::parse_from_bytes(&data) { - match msg_in.union { - Some(message::Union::VideoFrame(vf)) => { - if !self.first_frame { - self.first_frame = true; - // self.handler.call2("closeSuccess", &make_args!()); - self.handler.close_success(); - // self.handler.call("adaptSize", &make_args!()); - self.handler.adapt_size(); - self.send_opts_after_login(peer).await; - } - let incomming_format = CodecFormat::from(&vf); - if self.video_format != incomming_format { - self.video_format = incomming_format.clone(); - self.handler.update_quality_status(QualityStatus { - codec_format: Some(incomming_format), - ..Default::default() - }) - }; - self.video_sender.send(MediaData::VideoFrame(vf)).ok(); - } - Some(message::Union::Hash(hash)) => { - self.handler - .handle_hash(&self.handler.password.clone(), hash, peer) - .await; - } - Some(message::Union::LoginResponse(lr)) => match lr.union { - Some(login_response::Union::Error(err)) => { - if !self.handler.handle_login_error(&err) { - return false; - } - } - Some(login_response::Union::PeerInfo(pi)) => { - self.handler.handle_peer_info(pi); - // self.check_clipboard_file_context(); - // if !(self.handler.is_file_transfer() - // || self.handler.is_port_forward() - // || !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) - // || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - // || self.handler.lc.read().unwrap().disable_clipboard) - // { - // let txt = self.old_clipboard.lock().unwrap().clone(); - // if !txt.is_empty() { - // let msg_out = crate::create_clipboard_msg(txt); - // let sender = self.sender.clone(); - // tokio::spawn(async move { - // // due to clipboard service interval time - // sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; - // sender.send(Data::Message(msg_out)).ok(); - // }); - // } - // } - - // if self.handler.is_file_transfer() { - // self.load_last_jobs().await; - // } - } - _ => {} - }, - Some(message::Union::CursorData(cd)) => { - self.handler.set_cursor_data(cd); - } - Some(message::Union::CursorId(id)) => { - self.handler.set_cursor_id(id.to_string()); - } - Some(message::Union::CursorPosition(cp)) => { - self.handler.set_cursor_position(cp); - } - Some(message::Union::Clipboard(cb)) => { - if !self.handler.lc.read().unwrap().disable_clipboard { - update_clipboard(cb, Some(&self.old_clipboard)); - } - } - #[cfg(windows)] - Some(message::Union::Cliprdr(clip)) => { - if !self.handler.lc.read().unwrap().disable_clipboard { - if let Some(context) = &mut self.clipboard_file_context { - if let Some(clip) = msg_2_clip(clip) { - server_clip_file(context, 0, clip); - } - } - } - } - Some(message::Union::FileResponse(fr)) => { - match fr.union { - Some(file_response::Union::Dir(fd)) => { - #[cfg(windows)] - let entries = fd.entries.to_vec(); - #[cfg(not(windows))] - let mut entries = fd.entries.to_vec(); - #[cfg(not(windows))] - { - if self.handler.peer_platform() == "Windows" { - fs::transform_windows_path(&mut entries); - } - } - // let mut m = make_fd(fd.id, &entries, fd.id > 0); - // if fd.id <= 0 { - // m.set_item("path", fd.path); - // } - // self.handler.call("updateFolderFiles", &make_args!(m)); - if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) { - log::info!("job set_files: {:?}", entries); - job.set_files(entries); - } else if let Some(job) = self.remove_jobs.get_mut(&fd.id) { - job.files = entries; - } - } - Some(file_response::Union::Digest(digest)) => { - if digest.is_upload { - if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let read_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - if let Some(overwrite) = overwrite_strategy { - let req = FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip( - true, - ) - }), - ..Default::default() - }; - job.confirm(&req); - let msg = new_send_confirm(req); - allow_err!(peer.send(&msg).await); - } else { - // self.handler.call( - // "overrideFileConfirm", - // &make_args!( - // digest.id, - // digest.file_num, - // read_path, - // true - // ), - // ); - self.handler.override_file_confirm( - digest.id, - digest.file_num, - read_path, - true, - ); - } - } - } - } else { - if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let write_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - match fs::is_write_need_confirmation(&write_path, &digest) { - Ok(res) => match res { - DigestCheckResult::IsSame => { - let msg= new_send_confirm(FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::Skip(true)), - ..Default::default() - }); - allow_err!(peer.send(&msg).await); - } - DigestCheckResult::NeedConfirm(digest) => { - if let Some(overwrite) = overwrite_strategy { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip(true) - }), - ..Default::default() - }, - ); - allow_err!(peer.send(&msg).await); - } else { - // self.handler.call( - // "overrideFileConfirm", - // &make_args!( - // digest.id, - // digest.file_num, - // write_path, - // false - // ), - // ); - self.handler.override_file_confirm( - digest.id, - digest.file_num, - write_path, - false, - ); - } - } - DigestCheckResult::NoSuchFile => { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), - ..Default::default() - }, - ); - allow_err!(peer.send(&msg).await); - } - }, - Err(err) => { - println!("error recving digest: {}", err); - } - } - } - } - } - } - Some(file_response::Union::Block(block)) => { - log::info!( - "file response block, file id:{}, file num: {}", - block.id, - block.file_num - ); - if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { - if let Err(_err) = job.write(block, None).await { - // to-do: add "skip" for writing job - } - self.update_jobs_status(); - } - } - Some(file_response::Union::Done(d)) => { - if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { - job.modify_time(); - fs::remove_job(d.id, &mut self.write_jobs); - } - self.handle_job_status(d.id, d.file_num, None); - } - Some(file_response::Union::Error(e)) => { - self.handle_job_status(e.id, e.file_num, Some(e.error)); - } - _ => {} - } - } - Some(message::Union::Misc(misc)) => match misc.union { - Some(misc::Union::AudioFormat(f)) => { - self.audio_sender.send(MediaData::AudioFormat(f)).ok(); - } - Some(misc::Union::ChatMessage(c)) => { - // self.handler.call("newMessage", &make_args!(c.text)); // TODO - } - Some(misc::Union::PermissionInfo(p)) => { - log::info!("Change permission {:?} -> {}", p.permission, p.enabled); - match p.permission.enum_value_or_default() { - Permission::Keyboard => { - SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - // self.handler - // .call2("setPermission", &make_args!("keyboard", p.enabled)); - self.handler.set_permission("keyboard", p.enabled); - } - Permission::Clipboard => { - SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - // self.handler - // .call2("setPermission", &make_args!("clipboard", p.enabled)); - self.handler.set_permission("clipboard", p.enabled); - } - Permission::Audio => { - // self.handler - // .call2("setPermission", &make_args!("audio", p.enabled)); - self.handler.set_permission("audio", p.enabled); - } - Permission::File => { - SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst); - if !p.enabled && self.handler.is_file_transfer() { - return true; - } - self.check_clipboard_file_context(); - // self.handler - // .call2("setPermission", &make_args!("file", p.enabled)); - self.handler.set_permission("file", p.enabled); - } - Permission::Restart => { - // self.handler - // .call2("setPermission", &make_args!("restart", p.enabled)); - self.handler.set_permission("restart", p.enabled); - } - } - } - Some(misc::Union::SwitchDisplay(s)) => { - // self.handler.call("switchDisplay", &make_args!(s.display)); // TODO - self.video_sender.send(MediaData::Reset).ok(); - if s.width > 0 && s.height > 0 { - self.handler.set_display(s.x, s.y, s.width, s.height); - } - } - Some(misc::Union::CloseReason(c)) => { - self.handler.msgbox("error", "Connection Error", &c); - return false; - } - Some(misc::Union::BackNotification(notification)) => { - if !self.handle_back_notification(notification).await { - return false; - } - } - _ => {} - }, - Some(message::Union::TestDelay(t)) => { - self.handler.handle_test_delay(t, peer).await; - } - Some(message::Union::AudioFrame(frame)) => { - if !self.handler.lc.read().unwrap().disable_audio { - self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); - } - } - Some(message::Union::FileAction(action)) => match action.union { - Some(file_action::Union::SendConfirm(c)) => { - if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { - job.confirm(&c); - } - } - _ => {} - }, - _ => {} - } - } - true - } - - async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { - match notification.union { - Some(back_notification::Union::BlockInputState(state)) => { - self.handle_back_msg_block_input( - state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), - ) - .await; - } - Some(back_notification::Union::PrivacyModeState(state)) => { - if !self - .handle_back_msg_privacy_mode( - state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), - ) - .await - { - return false; - } - } - _ => {} - } - true - } - - #[inline(always)] - fn update_block_input_state(&mut self, on: bool) { - // self.handler.call("updateBlockInputState", &make_args!(on)); // TODO - } - - async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { - match state { - back_notification::BlockInputState::BlkOnSucceeded => { - self.update_block_input_state(true); - } - back_notification::BlockInputState::BlkOnFailed => { - self.handler - .msgbox("custom-error", "Block user input", "Failed"); - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffSucceeded => { - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffFailed => { - self.handler - .msgbox("custom-error", "Unblock user input", "Failed"); - } - _ => {} - } - } - - #[inline(always)] - fn update_privacy_mode(&mut self, on: bool) { - let mut config = self.handler.load_config(); - config.privacy_mode = on; - self.handler.save_config(config); - - // self.handler.call("updatePrivacyMode", &[]); - self.handler.update_privacy_mode(); - } - - async fn handle_back_msg_privacy_mode( - &mut self, - state: back_notification::PrivacyModeState, - ) -> bool { - match state { - back_notification::PrivacyModeState::PrvOnByOther => { - self.handler.msgbox( - "error", - "Connecting...", - "Someone turns on privacy mode, exit", - ); - return false; - } - back_notification::PrivacyModeState::PrvNotSupported => { - self.handler - .msgbox("custom-error", "Privacy mode", "Unsupported"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnSucceeded => { - self.handler - .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); - self.update_privacy_mode(true); - } - back_notification::PrivacyModeState::PrvOnFailedDenied => { - self.handler - .msgbox("custom-error", "Privacy mode", "Peer denied"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailedPlugin => { - self.handler - .msgbox("custom-error", "Privacy mode", "Please install plugins"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailed => { - self.handler - .msgbox("custom-error", "Privacy mode", "Failed"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffSucceeded => { - self.handler - .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffByPeer => { - self.handler - .msgbox("custom-error", "Privacy mode", "Peer exit"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffFailed => { - self.handler - .msgbox("custom-error", "Privacy mode", "Failed to turn off"); - } - back_notification::PrivacyModeState::PrvOffUnknown => { - self.handler - .msgbox("custom-error", "Privacy mode", "Turned off"); - // log::error!("Privacy mode is turned off with unknown reason"); - self.update_privacy_mode(false); - } - _ => {} - } - true - } - - fn check_clipboard_file_context(&mut self) { - #[cfg(windows)] - { - let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) - && self.handler.lc.read().unwrap().enable_file_transfer; - if enabled == self.clipboard_file_context.is_none() { - self.clipboard_file_context = if enabled { - match create_clipboard_file_context(true, false) { - Ok(context) => { - log::info!("clipboard context for file transfer created."); - Some(context) - } - Err(err) => { - log::error!( - "Create clipboard context for file transfer: {}", - err.to_string() - ); - None - } - } - } else { - log::info!("clipboard context for file transfer destroyed."); - None - }; - } - } - } -} - -struct RemoveJob { - files: Vec, - path: String, - sep: &'static str, - is_remote: bool, - no_confirm: bool, - last_update_job_status: Instant, -} - -impl RemoveJob { - fn new(files: Vec, path: String, sep: &'static str, is_remote: bool) -> Self { - Self { - files, - path, - sep, - is_remote, - no_confirm: false, - last_update_job_status: Instant::now(), - } - } - - pub fn _gen_meta(&self) -> RemoveJobMeta { - RemoveJobMeta { - path: self.path.clone(), - is_remote: self.is_remote, - no_confirm: self.no_confirm, - } - } -} - #[tokio::main(flavor = "current_thread")] async fn send_note(url: String, id: String, conn_id: i32, note: String) { let body = serde_json::json!({ "id": id, "Id": conn_id, "note": note }); From 2891c1b148e38a639f97207ffdcc0526e3463a0d Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 1 Sep 2022 16:21:41 +0800 Subject: [PATCH 5/7] refactor set_peer_info --- flutter/lib/models/model.dart | 1 + src/client.rs | 4 +- src/flutter.rs | 57 ++++++++------------ src/ui/remote.rs | 36 ++++++++----- src/ui_session_interface.rs | 97 +++++------------------------------ 5 files changed, 59 insertions(+), 136 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 171a41dfa..c01451280 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -325,6 +325,7 @@ class FfiModel with ChangeNotifier { } if (evt['is_file_transfer'] == "true") { + // TODO is file transfer parent.target?.fileModel.onReady(); } else { _pi.displays = []; diff --git a/src/client.rs b/src/client.rs index 6346af6d0..184170e4a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1278,13 +1278,13 @@ impl LoginConfigHandler { /// /// * `username` - The name of the peer. /// * `pi` - The peer info. - pub fn handle_peer_info(&mut self, username: String, pi: PeerInfo) { + pub fn handle_peer_info(&mut self, pi: PeerInfo) { if !pi.version.is_empty() { self.version = hbb_common::get_version_number(&pi.version); } self.features = pi.features.into_option(); let serde = PeerInfoSerde { - username, + username: pi.username.clone(), hostname: pi.hostname.clone(), platform: pi.platform.clone(), }; diff --git a/src/flutter.rs b/src/flutter.rs index 0b8c3626f..1244e521a 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,24 +1,13 @@ use std::{ collections::HashMap, - sync::{ - Arc, RwLock, - }, + sync::{Arc, RwLock}, }; use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; -use hbb_common::{ - bail, - config::{LocalConfig}, - message_proto::*, - ResultType, -}; - -use crate::{ - ui_session_interface::{io_loop, InvokeUi, Session}, -}; - +use hbb_common::{bail, config::LocalConfig, message_proto::*, ResultType}; +use crate::ui_session_interface::{io_loop, InvokeUi, Session}; use crate::{client::*, flutter_ffi::EventToUI}; @@ -97,10 +86,6 @@ impl InvokeUi for FlutterHandler { self.push_event("permission", vec![(name, &value.to_string())]); } - fn update_pi(&self, pi: PeerInfo) { - // todo!() - } - fn close_success(&self) { // todo!() } @@ -204,29 +189,27 @@ impl InvokeUi for FlutterHandler { } } - fn set_peer_info( - &self, - username: &str, - hostname: &str, - platform: &str, - sas_enabled: bool, - displays: &Vec>, - version: &str, - current_display: usize, - is_file_transfer: bool, - ) { - let displays = serde_json::ser::to_string(displays).unwrap_or("".to_owned()); + fn set_peer_info(&self, pi: &PeerInfo) { + let mut displays = Vec::new(); + for ref d in pi.displays.iter() { + let mut h: HashMap<&str, i32> = Default::default(); + h.insert("x", d.x); + h.insert("y", d.y); + h.insert("width", d.width); + h.insert("height", d.height); + displays.push(h); + } + let displays = serde_json::ser::to_string(&displays).unwrap_or("".to_owned()); self.push_event( "peer_info", vec![ - ("username", username), - ("hostname", hostname), - ("platform", platform), - ("sas_enabled", &sas_enabled.to_string()), + ("username", &pi.username), + ("hostname", &pi.hostname), + ("platform", &pi.platform), + ("sas_enabled", &pi.sas_enabled.to_string()), ("displays", &displays), - ("version", &version), - ("current_display", ¤t_display.to_string()), - ("is_file_transfer", &is_file_transfer.to_string()), + ("version", &pi.version), + ("current_display", &pi.current_display.to_string()), ], ); } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 2d412ea9b..4310f64bf 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -96,6 +96,10 @@ impl InvokeUi for SciterHandler { fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { self.call("setDisplay", &make_args!(x, y, w, h)); + // https://sciter.com/forums/topic/color_spaceiyuv-crash + // Nothing spectacular in decoder – done on CPU side. + // So if you can do BGRA translation on your side – the better. + // BGRA is used as internal image format so it will not require additional transformations. VIDEO.lock().unwrap().as_mut().map(|v| { v.stop_streaming().ok(); let ok = v.start_streaming((w, h), COLOR_SPACE::Rgb32, None); @@ -111,8 +115,6 @@ impl InvokeUi for SciterHandler { self.call2("setPermission", &make_args!(name, value)); } - fn update_pi(&self, pi: PeerInfo) {} // TODO dup flutter - fn close_success(&self) { self.call2("closeSuccess", &make_args!()); } @@ -202,17 +204,25 @@ impl InvokeUi for SciterHandler { .map(|v| v.render_frame(data).ok()); } - fn set_peer_info( - &self, - username: &str, - hostname: &str, - platform: &str, - sas_enabled: bool, - displays: &Vec>, - version: &str, - current_display: usize, - is_file_transfer: bool, - ) { + fn set_peer_info(&self, pi: &PeerInfo) { + let mut pi_sciter = Value::map(); + pi_sciter.set_item("username", pi.username.clone()); + pi_sciter.set_item("hostname", pi.hostname.clone()); + pi_sciter.set_item("platform", pi.platform.clone()); + pi_sciter.set_item("sas_enabled", pi.sas_enabled); + + let mut displays = Value::array(0); + for ref d in pi.displays.iter() { + let mut display = Value::map(); + display.set_item("x", d.x); + display.set_item("y", d.y); + display.set_item("width", d.width); + display.set_item("height", d.height); + displays.push(display); + } + pi_sciter.set_item("displays", displays); + pi_sciter.set_item("current_display", pi.current_display); + self.call("updatePi", &make_args!(pi_sciter)); } fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool) { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index a164a2d94..2f401e26f 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -532,20 +532,9 @@ pub trait InvokeUi: Send + Sync + Clone + 'static + Sized + Default { fn set_cursor_position(&self, cp: CursorPosition); fn set_display(&self, x: i32, y: i32, w: i32, h: i32); fn switch_display(&self, display: &SwitchDisplay); - fn set_peer_info( - &self, - username: &str, - hostname: &str, - platform: &str, - sas_enabled: bool, - displays: &Vec>, - version: &str, - current_display: usize, - is_file_transfer: bool, - ); // flutter + fn set_peer_info(&self, peer_info: &PeerInfo); // flutter fn update_privacy_mode(&self); fn set_permission(&self, name: &str, value: bool); - fn update_pi(&self, pi: PeerInfo); fn close_success(&self); fn update_quality_status(&self, qs: QualityStatus); fn set_connection_type(&self, is_secured: bool, direct: bool); @@ -623,20 +612,11 @@ impl Interface for Session { self.lc.write().unwrap().handle_login_error(err, self) } - fn handle_peer_info(&mut self, pi: PeerInfo) { - let mut lc = self.lc.write().unwrap(); - - // let mut pi_sciter = Value::map(); - let username = lc.get_username(&pi); - - // flutter - let mut displays = Vec::new(); - let mut current_index = pi.current_display as usize; - - // pi_sciter.set_item("username", username.clone()); - // pi_sciter.set_item("hostname", pi.hostname.clone()); - // pi_sciter.set_item("platform", pi.platform.clone()); - // pi_sciter.set_item("sas_enabled", pi.sas_enabled); + fn handle_peer_info(&mut self, mut pi: PeerInfo) { + pi.username = self.lc.read().unwrap().get_username(&pi); + if pi.current_display as usize >= pi.displays.len() { + pi.current_display = 0; + } if get_version_number(&pi.version) < get_version_number("1.1.10") { self.set_permission("restart", false); } @@ -647,73 +627,22 @@ impl Interface for Session { } } else if !self.is_port_forward() { if pi.displays.is_empty() { - lc.handle_peer_info(username, pi); + self.lc.write().unwrap().handle_peer_info(pi); self.update_privacy_mode(); self.msgbox("error", "Remote Error", "No Display"); return; } - // let mut displays = Value::array(0); - // for ref d in pi.displays.iter() { - // let mut display = Value::map(); - // display.set_item("x", d.x); - // display.set_item("y", d.y); - // display.set_item("width", d.width); - // display.set_item("height", d.height); - // displays.push(display); - // } - // pi_sciter.set_item("displays", displays); - - // flutter - for ref d in pi.displays.iter() { - let mut h: HashMap<&str, i32> = Default::default(); - h.insert("x", d.x); - h.insert("y", d.y); - h.insert("width", d.width); - h.insert("height", d.height); - displays.push(h); - } - if current_index >= pi.displays.len() { - current_index = 0; - } - - if current_index >= pi.displays.len() { - current_index = 0; - } - // pi_sciter.set_item("current_display", current as i32); - let current = &pi.displays[current_index]; - self.set_display(current.x, current.y, current.width, current.height); - - self.set_peer_info( - &username, - &pi.hostname, - &pi.platform, - pi.sas_enabled, - &displays, - &pi.version, - current_index, - lc.is_file_transfer, - ); - - // https://sciter.com/forums/topic/color_spaceiyuv-crash - // Nothing spectacular in decoder – done on CPU side. - // So if you can do BGRA translation on your side – the better. - // BGRA is used as internal image format so it will not require additional transformations. - // VIDEO.lock().unwrap().as_mut().map(|v| { - // let ok = v.start_streaming( - // (current.width as _, current.height as _), - // COLOR_SPACE::Rgb32, - // None, - // ); - // log::info!("[video] initialized: {:?}", ok); - // }); - let p = lc.should_auto_login(); + let p = self.lc.read().unwrap().should_auto_login(); if !p.is_empty() { input_os_password(p, true, self.clone()); } + let current = &pi.displays[pi.current_display as usize]; + self.set_display(current.x, current.y, current.width, current.height); } - lc.handle_peer_info(username, pi); self.update_privacy_mode(); - // self.update_pi(pi); + self.set_peer_info(&pi); + self.lc.write().unwrap().handle_peer_info(pi); + if self.is_file_transfer() { self.close_success(); } else if !self.is_port_forward() { From ee839875234505ce29197d1eeee4825886dd4fad Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 1 Sep 2022 17:36:37 +0800 Subject: [PATCH 6/7] sciter input & conn_type and other InvokeUi impl --- src/client.rs | 32 ++--- src/flutter.rs | 43 +++--- src/flutter_ffi.rs | 15 +- src/ui/remote.rs | 265 +++--------------------------------- src/ui_session_interface.rs | 263 +++++++++++++++++++++++++++++++++-- 5 files changed, 312 insertions(+), 306 deletions(-) diff --git a/src/client.rs b/src/client.rs index 184170e4a..8f6cb12bb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, net::SocketAddr, ops::{Deref, Not}, - sync::{mpsc, Arc, Mutex, RwLock, atomic::AtomicBool}, + sync::{atomic::AtomicBool, mpsc, Arc, Mutex, RwLock}, }; pub use async_trait::async_trait; @@ -864,8 +864,7 @@ impl VideoHandler { #[derive(Default)] pub struct LoginConfigHandler { id: String, - pub is_file_transfer: bool, - pub is_port_forward: bool, + pub conn_type: ConnType, hash: Hash, password: Vec, // remember password for reconnect pub remember: bool, @@ -904,12 +903,10 @@ impl LoginConfigHandler { /// # Arguments /// /// * `id` - id of peer - /// * `is_file_transfer` - Whether the connection is file transfer. - /// * `is_port_forward` - Whether the connection is port forward. - pub fn initialize(&mut self, id: String, is_file_transfer: bool, is_port_forward: bool) { + /// * `conn_type` - Connection type enum. + pub fn initialize(&mut self, id: String, conn_type: ConnType) { self.id = id; - self.is_file_transfer = is_file_transfer; - self.is_port_forward = is_port_forward; + self.conn_type = conn_type; let config = self.load_config(); self.remember = !config.password.is_empty(); self.config = config; @@ -1066,7 +1063,8 @@ impl LoginConfigHandler { /// /// * `ignore_default` - If `true`, ignore the default value of the option. fn get_option_message(&self, ignore_default: bool) -> Option { - if self.is_port_forward || self.is_file_transfer { + if self.conn_type.eq(&ConnType::FILE_TRANSFER) || self.conn_type.eq(&ConnType::PORT_FORWARD) + { return None; } let mut n = 0; @@ -1112,7 +1110,8 @@ impl LoginConfigHandler { } pub fn get_option_message_after_login(&self) -> Option { - if self.is_port_forward || self.is_file_transfer { + if self.conn_type.eq(&ConnType::FILE_TRANSFER) || self.conn_type.eq(&ConnType::PORT_FORWARD) + { return None; } let mut n = 0; @@ -1348,19 +1347,20 @@ impl LoginConfigHandler { version: crate::VERSION.to_string(), ..Default::default() }; - if self.is_file_transfer { - lr.set_file_transfer(FileTransfer { + match self.conn_type { + ConnType::FILE_TRANSFER => lr.set_file_transfer(FileTransfer { dir: self.get_remote_dir(), show_hidden: !self.get_option("remote_show_hidden").is_empty(), ..Default::default() - }); - } else if self.is_port_forward { - lr.set_port_forward(PortForward { + }), + ConnType::PORT_FORWARD => lr.set_port_forward(PortForward { host: self.port_forward.0.clone(), port: self.port_forward.1, ..Default::default() - }); + }), + _ => {} } + let mut msg_out = Message::new(); msg_out.set_login_request(lr); msg_out diff --git a/src/flutter.rs b/src/flutter.rs index 1244e521a..b84e91ce8 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -5,7 +5,7 @@ use std::{ use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; -use hbb_common::{bail, config::LocalConfig, message_proto::*, ResultType}; +use hbb_common::{bail, config::LocalConfig, message_proto::*, ResultType, rendezvous_proto::ConnType}; use crate::ui_session_interface::{io_loop, InvokeUi, Session}; @@ -74,9 +74,8 @@ impl InvokeUi for FlutterHandler { ); } - fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { - // todo!() - } + /// unused in flutter, use switch_display or set_peer_info + fn set_display(&self, _x: i32, _y: i32, _w: i32, _h: i32) {} fn update_privacy_mode(&self) { self.push_event("update_privacy_mode", [].into()); @@ -86,9 +85,7 @@ impl InvokeUi for FlutterHandler { self.push_event("permission", vec![(name, &value.to_string())]); } - fn close_success(&self) { - // todo!() - } + fn close_success(&self) {} fn update_quality_status(&self, status: QualityStatus) { const NULL: String = String::new(); @@ -179,9 +176,7 @@ impl InvokeUi for FlutterHandler { ); } - fn adapt_size(&self) { - // todo!() - } + fn adapt_size(&self) {} fn on_rgba(&self, data: &[u8]) { if let Some(stream) = &*self.event_stream.read().unwrap() { @@ -265,27 +260,37 @@ impl InvokeUi for FlutterHandler { /// * `is_file_transfer` - If the session is used for file transfer. /// * `is_port_forward` - If the session is used for port forward. pub fn session_add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> ResultType<()> { - // TODO check same id let session_id = get_session_id(id.to_owned()); LocalConfig::set_remote_id(&session_id); - // TODO close - // Self::close(); - // TODO cmd passwd args let session: Session = Session { id: session_id.clone(), ..Default::default() }; + // TODO rdp + let conn_type = if is_file_transfer { + ConnType::FILE_TRANSFER + } else if is_port_forward { + ConnType::PORT_FORWARD + } else { + ConnType::DEFAULT_CONN + }; + session .lc .write() .unwrap() - .initialize(session_id.clone(), is_file_transfer, is_port_forward); - SESSIONS + .initialize(session_id, conn_type); + + if let Some(same_id_session) = SESSIONS .write() .unwrap() - .insert(id.to_owned(), session.clone()); + .insert(id.to_owned(), session) + { + same_id_session.close(); + } + Ok(()) } @@ -300,10 +305,6 @@ pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultTy *session.event_stream.write().unwrap() = Some(event_stream); let session = session.clone(); std::thread::spawn(move || { - // TODO is_file_transfer is_port_forward - // let is_file_transfer = session.lc.read().unwrap().is_file_transfer; - // let is_port_forward = session.lc.read().unwrap().is_port_forward; - // Connection::start(session, is_file_transfer, is_port_forward); io_loop(session); }); Ok(()) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 5226416b2..69da5f540 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -190,20 +190,20 @@ pub fn session_toggle_option(id: String, value: String) { } pub fn session_set_image_quality(id: String, value: String) { - if let Some(session) = SESSIONS.read().unwrap().get(&id) { - // session.set_image_quality(value); + if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) { + session.save_image_quality(value); } } pub fn session_lock_screen(id: String) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - // session.lock_screen(); + session.lock_screen(); } } pub fn session_ctrl_alt_del(id: String) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - // session.ctrl_alt_del(); + session.ctrl_alt_del(); } } @@ -224,13 +224,13 @@ pub fn session_input_key( command: bool, ) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - // session.input_key(&name, down, press, alt, ctrl, shift, command); + 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); + session.input_string(&value); } } @@ -686,7 +686,6 @@ pub fn main_has_hwcodec() -> bool { has_hwcodec() } -// TODO pub fn session_send_mouse(id: String, msg: String) { if let Ok(m) = serde_json::from_str::>(&msg) { let alt = m.get("alt").is_some(); @@ -719,7 +718,7 @@ pub fn session_send_mouse(id: String, msg: String) { } << 3; } if let Some(session) = SESSIONS.read().unwrap().get(&id) { - // session.send_mouse(mask, x, y, alt, ctrl, shift, command); + session.send_mouse(mask, x, y, alt, ctrl, shift, command); } } } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 4310f64bf..7e2c5cd9c 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -22,14 +22,14 @@ use clipboard::{ cliprdr::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context, get_rx_clip_client, server_clip_file, }; -use enigo::{self}; -use hbb_common::{allow_err, log, message_proto::*}; + +use hbb_common::{allow_err, log, message_proto::*, rendezvous_proto::ConnType}; #[cfg(windows)] use crate::clipboard_file::*; use crate::{ client::*, - ui_session_interface::{InvokeUi, Session}, + ui_session_interface::{InvokeUi, Session, IS_IN}, }; type Video = AssetPtr; @@ -38,9 +38,6 @@ lazy_static::lazy_static! { static ref VIDEO: Arc>> = Default::default(); } -static IS_IN: AtomicBool = AtomicBool::new(false); -static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false); - #[cfg(windows)] static mut IS_ALT_GR: bool = false; @@ -397,255 +394,28 @@ impl sciter::EventHandler for SciterSession { impl SciterSession { pub fn new(cmd: String, id: String, password: String, args: Vec) -> Self { let session: Session = Session { - cmd, + cmd: cmd.clone(), id: id.clone(), password: password.clone(), args, ..Default::default() }; - session.lc.write().unwrap().initialize( - id, - session.is_file_transfer(), - session.is_port_forward(), - ); + + let conn_type = if cmd.eq("--file-transfer") { + ConnType::FILE_TRANSFER + } else if cmd.eq("--port-forward") { + ConnType::PORT_FORWARD + } else if cmd.eq("--rdp") { + ConnType::RDP + } else { + ConnType::DEFAULT_CONN + }; + + session.lc.write().unwrap().initialize(id, conn_type); Self(session) } - // TODO - fn start_keyboard_hook(&'static self) { - if self.is_port_forward() || self.is_file_transfer() { - return; - } - if KEYBOARD_HOOKED.swap(true, Ordering::SeqCst) { - return; - } - log::info!("keyboard hooked"); - let me = self.clone(); - let peer = self.peer_platform(); - let is_win = peer == "Windows"; - #[cfg(windows)] - crate::platform::windows::enable_lowlevel_keyboard(std::ptr::null_mut() as _); - std::thread::spawn(move || { - // This will block. - std::env::set_var("KEYBOARD_ONLY", "y"); // pass to rdev - use rdev::{EventType::*, *}; - let func = move |evt: Event| { - if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - { - return; - } - let (key, down) = match evt.event_type { - KeyPress(k) => (k, 1), - KeyRelease(k) => (k, 0), - _ => return, - }; - let alt = get_key_state(enigo::Key::Alt); - #[cfg(windows)] - let ctrl = { - let mut tmp = get_key_state(enigo::Key::Control); - unsafe { - if IS_ALT_GR { - if alt || key == Key::AltGr { - if tmp { - tmp = false; - } - } else { - IS_ALT_GR = false; - } - } - } - tmp - }; - #[cfg(not(windows))] - let ctrl = get_key_state(enigo::Key::Control); - let shift = get_key_state(enigo::Key::Shift); - #[cfg(windows)] - let command = crate::platform::windows::get_win_key_state(); - #[cfg(not(windows))] - let command = get_key_state(enigo::Key::Meta); - let control_key = match key { - Key::Alt => Some(ControlKey::Alt), - Key::AltGr => Some(ControlKey::RAlt), - Key::Backspace => Some(ControlKey::Backspace), - Key::ControlLeft => { - // when pressing AltGr, an extra VK_LCONTROL with a special - // scancode with bit 9 set is sent, let's ignore this. - #[cfg(windows)] - if evt.scan_code & 0x200 != 0 { - unsafe { - IS_ALT_GR = true; - } - return; - } - Some(ControlKey::Control) - } - Key::ControlRight => Some(ControlKey::RControl), - Key::DownArrow => Some(ControlKey::DownArrow), - Key::Escape => Some(ControlKey::Escape), - Key::F1 => Some(ControlKey::F1), - Key::F10 => Some(ControlKey::F10), - Key::F11 => Some(ControlKey::F11), - Key::F12 => Some(ControlKey::F12), - Key::F2 => Some(ControlKey::F2), - Key::F3 => Some(ControlKey::F3), - Key::F4 => Some(ControlKey::F4), - Key::F5 => Some(ControlKey::F5), - Key::F6 => Some(ControlKey::F6), - Key::F7 => Some(ControlKey::F7), - Key::F8 => Some(ControlKey::F8), - Key::F9 => Some(ControlKey::F9), - Key::LeftArrow => Some(ControlKey::LeftArrow), - Key::MetaLeft => Some(ControlKey::Meta), - Key::MetaRight => Some(ControlKey::RWin), - Key::Return => Some(ControlKey::Return), - Key::RightArrow => Some(ControlKey::RightArrow), - Key::ShiftLeft => Some(ControlKey::Shift), - Key::ShiftRight => Some(ControlKey::RShift), - Key::Space => Some(ControlKey::Space), - Key::Tab => Some(ControlKey::Tab), - Key::UpArrow => Some(ControlKey::UpArrow), - Key::Delete => { - if is_win && ctrl && alt { - // me.ctrl_alt_del(); // TODO - return; - } - Some(ControlKey::Delete) - } - Key::Apps => Some(ControlKey::Apps), - Key::Cancel => Some(ControlKey::Cancel), - Key::Clear => Some(ControlKey::Clear), - Key::Kana => Some(ControlKey::Kana), - Key::Hangul => Some(ControlKey::Hangul), - Key::Junja => Some(ControlKey::Junja), - Key::Final => Some(ControlKey::Final), - Key::Hanja => Some(ControlKey::Hanja), - Key::Hanji => Some(ControlKey::Hanja), - Key::Convert => Some(ControlKey::Convert), - Key::Print => Some(ControlKey::Print), - Key::Select => Some(ControlKey::Select), - Key::Execute => Some(ControlKey::Execute), - Key::PrintScreen => Some(ControlKey::Snapshot), - Key::Help => Some(ControlKey::Help), - Key::Sleep => Some(ControlKey::Sleep), - Key::Separator => Some(ControlKey::Separator), - Key::KpReturn => Some(ControlKey::NumpadEnter), - Key::Kp0 => Some(ControlKey::Numpad0), - Key::Kp1 => Some(ControlKey::Numpad1), - Key::Kp2 => Some(ControlKey::Numpad2), - Key::Kp3 => Some(ControlKey::Numpad3), - Key::Kp4 => Some(ControlKey::Numpad4), - Key::Kp5 => Some(ControlKey::Numpad5), - Key::Kp6 => Some(ControlKey::Numpad6), - Key::Kp7 => Some(ControlKey::Numpad7), - Key::Kp8 => Some(ControlKey::Numpad8), - Key::Kp9 => Some(ControlKey::Numpad9), - Key::KpDivide => Some(ControlKey::Divide), - Key::KpMultiply => Some(ControlKey::Multiply), - Key::KpDecimal => Some(ControlKey::Decimal), - Key::KpMinus => Some(ControlKey::Subtract), - Key::KpPlus => Some(ControlKey::Add), - Key::CapsLock | Key::NumLock | Key::ScrollLock => { - return; - } - Key::Home => Some(ControlKey::Home), - Key::End => Some(ControlKey::End), - Key::Insert => Some(ControlKey::Insert), - Key::PageUp => Some(ControlKey::PageUp), - Key::PageDown => Some(ControlKey::PageDown), - Key::Pause => Some(ControlKey::Pause), - _ => None, - }; - let mut key_event = KeyEvent::new(); - if let Some(k) = control_key { - key_event.set_control_key(k); - } else { - let mut chr = match evt.name { - Some(ref s) => { - if s.len() <= 2 { - // exclude chinese characters - s.chars().next().unwrap_or('\0') - } else { - '\0' - } - } - _ => '\0', - }; - if chr == '·' { - // special for Chinese - chr = '`'; - } - if chr == '\0' { - chr = match key { - Key::Num1 => '1', - Key::Num2 => '2', - Key::Num3 => '3', - Key::Num4 => '4', - Key::Num5 => '5', - Key::Num6 => '6', - Key::Num7 => '7', - Key::Num8 => '8', - Key::Num9 => '9', - Key::Num0 => '0', - Key::KeyA => 'a', - Key::KeyB => 'b', - Key::KeyC => 'c', - Key::KeyD => 'd', - Key::KeyE => 'e', - Key::KeyF => 'f', - Key::KeyG => 'g', - Key::KeyH => 'h', - Key::KeyI => 'i', - Key::KeyJ => 'j', - Key::KeyK => 'k', - Key::KeyL => 'l', - Key::KeyM => 'm', - Key::KeyN => 'n', - Key::KeyO => 'o', - Key::KeyP => 'p', - Key::KeyQ => 'q', - Key::KeyR => 'r', - Key::KeyS => 's', - Key::KeyT => 't', - Key::KeyU => 'u', - Key::KeyV => 'v', - Key::KeyW => 'w', - Key::KeyX => 'x', - Key::KeyY => 'y', - Key::KeyZ => 'z', - Key::Comma => ',', - Key::Dot => '.', - Key::SemiColon => ';', - Key::Quote => '\'', - Key::LeftBracket => '[', - Key::RightBracket => ']', - Key::BackSlash => '\\', - Key::Minus => '-', - Key::Equal => '=', - Key::BackQuote => '`', - _ => '\0', - } - } - if chr != '\0' { - if chr == 'l' && is_win && command { - // me.lock_screen(); // TODO - return; - } - key_event.set_chr(chr as _); - } else { - log::error!("Unknown key {:?}", evt); - return; - } - } - // me.key_down_or_up(down, key_event, alt, ctrl, shift, command); // TODO - }; - if let Err(error) = rdev::listen(func) { - log::error!("rdev: {:?}", error); - } - }); - } - - // TODO fn get_custom_image_quality(&mut self) -> Value { let mut v = Value::array(0); for x in self.lc.read().unwrap().custom_image_quality.iter() { @@ -654,7 +424,6 @@ impl SciterSession { v } - // TODO fn supported_hwcodec(&self) -> Value { #[cfg(feature = "hwcodec")] { @@ -679,7 +448,6 @@ impl SciterSession { } } - // TODO fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) { let size = (x, y, w, h); let mut config = self.load_config(); @@ -765,7 +533,6 @@ impl SciterSession { pi } - // close_state sciter only fn save_close_state(&mut self, k: String, v: String) { self.close_state.insert(k, v); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 2f401e26f..c08cc09ce 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -2,7 +2,7 @@ use crate::client::io_loop::Remote; use crate::client::{ check_if_retry, get_key_state, handle_hash, handle_login_from_ui, handle_test_delay, input_os_password, load_config, send_mouse, start_video_audio_threads, FileManager, Key, - LoginConfigHandler, QualityStatus, KEY_MAP, + LoginConfigHandler, QualityStatus, KEY_MAP, SERVER_KEYBOARD_ENABLED, }; use crate::common; @@ -11,15 +11,20 @@ use async_trait::async_trait; use hbb_common::config::{Config, LocalConfig, PeerConfig}; +use hbb_common::rendezvous_proto::ConnType; use hbb_common::tokio::{self, sync::mpsc}; use hbb_common::{allow_err, message_proto::*}; use hbb_common::{fs, get_version_number, log, Stream}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering, AtomicBool}; use std::sync::{Arc, Mutex, RwLock}; +/// IS_IN KEYBOARD_HOOKED sciter only +pub static IS_IN: AtomicBool = AtomicBool::new(false); +static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false); + #[derive(Clone, Default)] pub struct Session { pub cmd: String, @@ -219,7 +224,7 @@ impl Session { self.lc.read().unwrap().info.platform.clone() } - pub fn ctrl_alt_del(&mut self) { + pub fn ctrl_alt_del(&self) { if self.peer_platform() == "Windows" { let mut key_event = KeyEvent::new(); key_event.set_control_key(ControlKey::CtrlAltDel); @@ -339,7 +344,7 @@ impl Session { self.send(Data::Message(msg_out)); } - pub fn lock_screen(&mut self) { + pub fn lock_screen(&self) { let mut key_event = KeyEvent::new(); key_event.set_control_key(ControlKey::LockScreen); self.key_down_or_up(1, key_event, false, false, false, false); @@ -422,7 +427,7 @@ impl Session { } pub fn send_mouse( - &mut self, + &self, mask: i32, x: i32, y: i32, @@ -588,19 +593,16 @@ impl Interface for Session { } } - // TODO flutter fn is_file_transfer(&self) -> bool { - self.cmd == "--file-transfer" + self.lc.read().unwrap().conn_type.eq(&ConnType::FILE_TRANSFER) } - // TODO flutter fn is_port_forward(&self) -> bool { - self.cmd == "--port-forward" || self.is_rdp() + self.lc.read().unwrap().conn_type.eq(&ConnType::PORT_FORWARD) } - // TODO flutter fn is_rdp(&self) -> bool { - self.cmd == "--rdp" + self.lc.read().unwrap().conn_type.eq(&ConnType::RDP) } fn msgbox(&self, msgtype: &str, title: &str, text: &str) { @@ -658,7 +660,8 @@ impl Interface for Session { crate::platform::windows::add_recent_document(&path); } } - // self.start_keyboard_hook(); // TODO + // TODO use event callbcak + self.start_keyboard_hook(); } async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) { @@ -699,6 +702,242 @@ impl Interface for Session { } } +// TODO use event callbcak +// sciter only +impl Session { + fn start_keyboard_hook(&self) { + if self.is_port_forward() || self.is_file_transfer() { + return; + } + if KEYBOARD_HOOKED.swap(true, Ordering::SeqCst) { + return; + } + log::info!("keyboard hooked"); + let me = self.clone(); + let peer = self.peer_platform(); + let is_win = peer == "Windows"; + #[cfg(windows)] + crate::platform::windows::enable_lowlevel_keyboard(std::ptr::null_mut() as _); + std::thread::spawn(move || { + // This will block. + std::env::set_var("KEYBOARD_ONLY", "y"); // pass to rdev + use rdev::{EventType::*, *}; + let func = move |evt: Event| { + if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + { + return; + } + let (key, down) = match evt.event_type { + KeyPress(k) => (k, 1), + KeyRelease(k) => (k, 0), + _ => return, + }; + let alt = get_key_state(enigo::Key::Alt); + #[cfg(windows)] + let ctrl = { + let mut tmp = get_key_state(enigo::Key::Control); + unsafe { + if IS_ALT_GR { + if alt || key == Key::AltGr { + if tmp { + tmp = false; + } + } else { + IS_ALT_GR = false; + } + } + } + tmp + }; + #[cfg(not(windows))] + let ctrl = get_key_state(enigo::Key::Control); + let shift = get_key_state(enigo::Key::Shift); + #[cfg(windows)] + let command = crate::platform::windows::get_win_key_state(); + #[cfg(not(windows))] + let command = get_key_state(enigo::Key::Meta); + let control_key = match key { + Key::Alt => Some(ControlKey::Alt), + Key::AltGr => Some(ControlKey::RAlt), + Key::Backspace => Some(ControlKey::Backspace), + Key::ControlLeft => { + // when pressing AltGr, an extra VK_LCONTROL with a special + // scancode with bit 9 set is sent, let's ignore this. + #[cfg(windows)] + if evt.scan_code & 0x200 != 0 { + unsafe { + IS_ALT_GR = true; + } + return; + } + Some(ControlKey::Control) + } + Key::ControlRight => Some(ControlKey::RControl), + Key::DownArrow => Some(ControlKey::DownArrow), + Key::Escape => Some(ControlKey::Escape), + Key::F1 => Some(ControlKey::F1), + Key::F10 => Some(ControlKey::F10), + Key::F11 => Some(ControlKey::F11), + Key::F12 => Some(ControlKey::F12), + Key::F2 => Some(ControlKey::F2), + Key::F3 => Some(ControlKey::F3), + Key::F4 => Some(ControlKey::F4), + Key::F5 => Some(ControlKey::F5), + Key::F6 => Some(ControlKey::F6), + Key::F7 => Some(ControlKey::F7), + Key::F8 => Some(ControlKey::F8), + Key::F9 => Some(ControlKey::F9), + Key::LeftArrow => Some(ControlKey::LeftArrow), + Key::MetaLeft => Some(ControlKey::Meta), + Key::MetaRight => Some(ControlKey::RWin), + Key::Return => Some(ControlKey::Return), + Key::RightArrow => Some(ControlKey::RightArrow), + Key::ShiftLeft => Some(ControlKey::Shift), + Key::ShiftRight => Some(ControlKey::RShift), + Key::Space => Some(ControlKey::Space), + Key::Tab => Some(ControlKey::Tab), + Key::UpArrow => Some(ControlKey::UpArrow), + Key::Delete => { + if is_win && ctrl && alt { + me.ctrl_alt_del(); + return; + } + Some(ControlKey::Delete) + } + Key::Apps => Some(ControlKey::Apps), + Key::Cancel => Some(ControlKey::Cancel), + Key::Clear => Some(ControlKey::Clear), + Key::Kana => Some(ControlKey::Kana), + Key::Hangul => Some(ControlKey::Hangul), + Key::Junja => Some(ControlKey::Junja), + Key::Final => Some(ControlKey::Final), + Key::Hanja => Some(ControlKey::Hanja), + Key::Hanji => Some(ControlKey::Hanja), + Key::Convert => Some(ControlKey::Convert), + Key::Print => Some(ControlKey::Print), + Key::Select => Some(ControlKey::Select), + Key::Execute => Some(ControlKey::Execute), + Key::PrintScreen => Some(ControlKey::Snapshot), + Key::Help => Some(ControlKey::Help), + Key::Sleep => Some(ControlKey::Sleep), + Key::Separator => Some(ControlKey::Separator), + Key::KpReturn => Some(ControlKey::NumpadEnter), + Key::Kp0 => Some(ControlKey::Numpad0), + Key::Kp1 => Some(ControlKey::Numpad1), + Key::Kp2 => Some(ControlKey::Numpad2), + Key::Kp3 => Some(ControlKey::Numpad3), + Key::Kp4 => Some(ControlKey::Numpad4), + Key::Kp5 => Some(ControlKey::Numpad5), + Key::Kp6 => Some(ControlKey::Numpad6), + Key::Kp7 => Some(ControlKey::Numpad7), + Key::Kp8 => Some(ControlKey::Numpad8), + Key::Kp9 => Some(ControlKey::Numpad9), + Key::KpDivide => Some(ControlKey::Divide), + Key::KpMultiply => Some(ControlKey::Multiply), + Key::KpDecimal => Some(ControlKey::Decimal), + Key::KpMinus => Some(ControlKey::Subtract), + Key::KpPlus => Some(ControlKey::Add), + Key::CapsLock | Key::NumLock | Key::ScrollLock => { + return; + } + Key::Home => Some(ControlKey::Home), + Key::End => Some(ControlKey::End), + Key::Insert => Some(ControlKey::Insert), + Key::PageUp => Some(ControlKey::PageUp), + Key::PageDown => Some(ControlKey::PageDown), + Key::Pause => Some(ControlKey::Pause), + _ => None, + }; + let mut key_event = KeyEvent::new(); + if let Some(k) = control_key { + key_event.set_control_key(k); + } else { + let mut chr = match evt.name { + Some(ref s) => { + if s.len() <= 2 { + // exclude chinese characters + s.chars().next().unwrap_or('\0') + } else { + '\0' + } + } + _ => '\0', + }; + if chr == '·' { + // special for Chinese + chr = '`'; + } + if chr == '\0' { + chr = match key { + Key::Num1 => '1', + Key::Num2 => '2', + Key::Num3 => '3', + Key::Num4 => '4', + Key::Num5 => '5', + Key::Num6 => '6', + Key::Num7 => '7', + Key::Num8 => '8', + Key::Num9 => '9', + Key::Num0 => '0', + Key::KeyA => 'a', + Key::KeyB => 'b', + Key::KeyC => 'c', + Key::KeyD => 'd', + Key::KeyE => 'e', + Key::KeyF => 'f', + Key::KeyG => 'g', + Key::KeyH => 'h', + Key::KeyI => 'i', + Key::KeyJ => 'j', + Key::KeyK => 'k', + Key::KeyL => 'l', + Key::KeyM => 'm', + Key::KeyN => 'n', + Key::KeyO => 'o', + Key::KeyP => 'p', + Key::KeyQ => 'q', + Key::KeyR => 'r', + Key::KeyS => 's', + Key::KeyT => 't', + Key::KeyU => 'u', + Key::KeyV => 'v', + Key::KeyW => 'w', + Key::KeyX => 'x', + Key::KeyY => 'y', + Key::KeyZ => 'z', + Key::Comma => ',', + Key::Dot => '.', + Key::SemiColon => ';', + Key::Quote => '\'', + Key::LeftBracket => '[', + Key::RightBracket => ']', + Key::BackSlash => '\\', + Key::Minus => '-', + Key::Equal => '=', + Key::BackQuote => '`', + _ => '\0', + } + } + if chr != '\0' { + if chr == 'l' && is_win && command { + me.lock_screen(); + return; + } + key_event.set_chr(chr as _); + } else { + log::error!("Unknown key {:?}", evt); + return; + } + } + me.key_down_or_up(down, key_event, alt, ctrl, shift, command); // TODO + }; + if let Err(error) = rdev::listen(func) { + log::error!("rdev: {:?}", error); + } + }); + } +} + #[tokio::main(flavor = "current_thread")] pub async fn io_loop(handler: Session) { let (sender, mut receiver) = mpsc::unbounded_channel::(); From a435fc999a49ee99085f3607fa7c1db2b5eb4617 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 1 Sep 2022 18:23:06 +0800 Subject: [PATCH 7/7] mobile build --- src/client.rs | 9 +++++---- src/client/io_loop.rs | 3 +++ src/ui_session_interface.rs | 13 +++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/client.rs b/src/client.rs index 8f6cb12bb..25061bcfe 100644 --- a/src/client.rs +++ b/src/client.rs @@ -11,7 +11,6 @@ use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, Device, Host, StreamConfig, }; -use enigo::{Enigo, KeyboardControllable}; use magnum_opus::{Channels::*, Decoder as AudioDecoder}; use sha2::{Digest, Sha256}; use uuid::Uuid; @@ -38,7 +37,6 @@ use hbb_common::{ }; pub use helper::LatencyController; pub use helper::*; -use scrap::Image; use scrap::{ codec::{Decoder, DecoderCfg}, VpxDecoderConfig, VpxVideoCodecId, @@ -61,14 +59,17 @@ pub struct Client; #[cfg(not(any(target_os = "android", target_os = "linux")))] lazy_static::lazy_static! { -static ref AUDIO_HOST: Host = cpal::default_host(); + static ref AUDIO_HOST: Host = cpal::default_host(); } +#[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { - static ref ENIGO: Arc> = Arc::new(Mutex::new(Enigo::new())); + static ref ENIGO: Arc> = Arc::new(Mutex::new(enigo::Enigo::new())); } +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_key_state(key: enigo::Key) -> bool { + use enigo::KeyboardControllable; #[cfg(target_os = "macos")] if key == enigo::Key::NumLock { return true; diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index f7f8f4f18..e61690c32 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -2,6 +2,7 @@ use crate::client::{ Client, CodecFormat, FileManager, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, SERVER_CLIPBOARD_ENABLED, SERVER_FILE_TRANSFER_ENABLED, SERVER_KEYBOARD_ENABLED, }; +#[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}; use crate::ui_session_interface::{InvokeUi, Session}; @@ -235,6 +236,7 @@ impl Remote { let old_clipboard = self.old_clipboard.clone(); let tx_protobuf = self.sender.clone(); let lc = self.handler.lc.clone(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] match ClipboardContext::new() { Ok(mut ctx) => { // ignore clipboard update before service start @@ -266,6 +268,7 @@ impl Remote { Some(tx) } + // TODO fn load_last_jobs(&mut self) { log::info!("start load last jobs"); self.handler.clear_all_jobs(); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index c08cc09ce..d89ce2d3b 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1,11 +1,12 @@ use crate::client::io_loop::Remote; use crate::client::{ - check_if_retry, get_key_state, handle_hash, handle_login_from_ui, handle_test_delay, + check_if_retry, handle_hash, handle_login_from_ui, handle_test_delay, input_os_password, load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, SERVER_KEYBOARD_ENABLED, }; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::client::get_key_state; use crate::common; - use crate::{client::Data, client::Interface}; use async_trait::async_trait; @@ -129,6 +130,7 @@ impl Session { self.send(Data::Message(msg)); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn t(&self, name: String) -> String { crate::client::translate(name) } @@ -271,9 +273,11 @@ impl Session { { key_event.modifiers.push(ControlKey::Meta.into()); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] if get_key_state(enigo::Key::CapsLock) { key_event.modifiers.push(ControlKey::CapsLock.into()); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.peer_platform() != "Mac OS" { if get_key_state(enigo::Key::NumLock) && common::valid_for_numlock(&key_event) { key_event.modifiers.push(ControlKey::NumLock.into()); @@ -318,6 +322,7 @@ impl Session { return "".to_owned(); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_icon(&self) -> String { crate::get_icon() } @@ -661,6 +666,7 @@ impl Interface for Session { } } // TODO use event callbcak + #[cfg(not(any(target_os = "android", target_os = "ios")))] self.start_keyboard_hook(); } @@ -704,6 +710,7 @@ impl Interface for Session { // TODO use event callbcak // sciter only +#[cfg(not(any(target_os = "android", target_os = "ios")))] impl Session { fn start_keyboard_hook(&self) { if self.is_port_forward() || self.is_file_transfer() { @@ -948,6 +955,7 @@ pub async fn io_loop(handler: Session) { if key.is_empty() { key = crate::platform::get_license_key(); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] if handler.is_port_forward() { if handler.is_rdp() { let port = handler @@ -1053,6 +1061,7 @@ pub async fn io_loop(handler: Session) { remote.sync_jobs_status_to_local().await; } +#[cfg(not(any(target_os = "android", target_os = "ios")))] async fn start_one_port_forward( handler: Session, port: i32,