From ae265ca83619b15261690eeb5aa60bb3b519cd4f Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 31 Aug 2022 22:24:57 +0800 Subject: [PATCH] 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(),