From ffba1d4f7a8a626c7bdcb875531e1d587fd7266e Mon Sep 17 00:00:00 2001 From: asur4s Date: Thu, 15 Dec 2022 03:51:50 -0800 Subject: [PATCH 1/6] refactor: release key of server --- src/server/input_service.rs | 168 +++++++++++++++++++++++------------- 1 file changed, 107 insertions(+), 61 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 021d85732..7a1ae6592 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -69,6 +69,7 @@ struct Input { y: i32, } +const KEY_RDEV_START: u64 = 999; const KEY_CHAR_START: u64 = 9999; #[derive(Clone, Default)] @@ -339,7 +340,7 @@ pub fn handle_mouse(evt: &MouseEvent, conn: i32) { pub fn fix_key_down_timeout_loop() { std::thread::spawn(move || loop { - std::thread::sleep(std::time::Duration::from_millis(1_000)); + std::thread::sleep(std::time::Duration::from_millis(10_000)); fix_key_down_timeout(false); }); if let Err(err) = ctrlc::set_handler(move || { @@ -360,38 +361,61 @@ pub fn fix_key_down_timeout_at_exit() { } #[inline] -fn get_layout(key: u32) -> Key { - Key::Layout(std::char::from_u32(key).unwrap_or('\0')) +fn record_key_is_control_key(record_key: u64) -> bool { + record_key < KEY_CHAR_START +} + +#[inline] +fn record_key_is_chr(record_key: u64) -> bool { + KEY_RDEV_START <= record_key && record_key < KEY_CHAR_START +} + +#[inline] +fn record_key_is_rdev_layout(record_key: u64) -> bool { + KEY_CHAR_START <= record_key +} + +#[inline] +fn record_key_to_key(record_key: u64) -> Option { + if record_key_is_control_key(record_key) { + control_key_value_to_key(record_key as _) + } else if record_key_is_chr(record_key) { + let chr: u32 = (record_key - KEY_CHAR_START) as _; + Some(char_value_to_key(chr)) + } else { + None + } +} + +#[inline] +fn release_record_key(record_key: u64) { + let func = move || { + if record_key_is_rdev_layout(record_key) { + rdev_key_down_or_up(RdevKey::Unknown((record_key - KEY_RDEV_START) as _), false); + } else if let Some(key) = record_key_to_key(record_key) { + ENIGO.lock().unwrap().key_up(key); + log::debug!("Fixed {:?} timeout", key); + } + }; + + #[cfg(target_os = "macos")] + QUEUE.exec_async(func); + #[cfg(not(target_os = "macos"))] + func(); } fn fix_key_down_timeout(force: bool) { - if KEYS_DOWN.lock().unwrap().is_empty() { + let key_down = KEYS_DOWN.lock().unwrap(); + if key_down.is_empty() { return; } - let cloned = (*KEYS_DOWN.lock().unwrap()).clone(); - for (key, value) in cloned.into_iter() { - if force || value.elapsed().as_millis() >= 360_000 { - KEYS_DOWN.lock().unwrap().remove(&key); - let key = if key < KEY_CHAR_START { - if let Some(key) = KEY_MAP.get(&(key as _)) { - Some(*key) - } else { - None - } - } else { - Some(get_layout((key - KEY_CHAR_START) as _)) - }; - if let Some(key) = key { - let func = move || { - let mut en = ENIGO.lock().unwrap(); - en.key_up(key); - log::debug!("Fixed {:?} timeout", key); - }; - #[cfg(target_os = "macos")] - QUEUE.exec_async(func); - #[cfg(not(target_os = "macos"))] - func(); - } + let cloned = (*key_down).clone(); + drop(key_down); + + for (record_key, time) in cloned.into_iter() { + if force || time.elapsed().as_millis() >= 360_000 { + record_pressed_key(record_key, false); + release_record_key(record_key); } } } @@ -685,8 +709,14 @@ fn is_modifier_in_key_event(control_key: ControlKey, key_event: &KeyEvent) -> bo .is_some() } -fn control_key_to_key(control_key: &EnumOrUnknown) -> Option<&Key> { - KEY_MAP.get(&control_key.value()) +#[inline] +fn control_key_value_to_key(value: i32) -> Option { + KEY_MAP.get(&value).and_then(|k| Some(*k)) +} + +#[inline] +fn char_value_to_key(value: u32) -> Key { + Key::Layout(std::char::from_u32(value).unwrap_or('\0')) } fn is_not_same_status(client_locking: bool, remote_locking: bool) -> bool { @@ -772,6 +802,8 @@ fn sync_numlock_capslock_status(key_event: &KeyEvent) { fn map_keyboard_mode(evt: &KeyEvent) { // map mode(1): Send keycode according to the peer platform. + record_pressed_key(evt.chr() as u64 + KEY_CHAR_START, evt.down); + #[cfg(windows)] crate::platform::windows::try_change_desktop(); @@ -818,7 +850,7 @@ fn release_unpressed_modifiers(en: &mut Enigo, key_event: &KeyEvent) { fix_modifiers(&key_event.modifiers[..], en, ck_value); } -fn is_altgr_pressed(en: &mut Enigo) -> bool { +fn is_altgr_pressed() -> bool { KEYS_DOWN .lock() .unwrap() @@ -828,10 +860,10 @@ fn is_altgr_pressed(en: &mut Enigo) -> bool { fn press_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec) { for ref ck in key_event.modifiers.iter() { - if let Some(key) = control_key_to_key(ck) { - if !is_pressed(key, en) { + if let Some(key) = control_key_value_to_key(ck.value()) { + if !is_pressed(&key, en) { #[cfg(target_os = "linux")] - if key == &Key::Alt && is_altgr_pressed(en) { + if key == Key::Alt && is_altgr_pressed() { continue; } en.key_down(key.clone()).ok(); @@ -855,44 +887,25 @@ fn sync_modifiers(en: &mut Enigo, key_event: &KeyEvent, to_release: &mut Vec, down: bool) { - let mut key_down = KEYS_DOWN.lock().unwrap(); - - if ck.value() == ControlKey::CtrlAltDel.value() { - // have to spawn new thread because send_sas is tokio_main, the caller can not be tokio_main. - std::thread::spawn(|| { - allow_err!(send_sas()); - }); - } else if ck.value() == ControlKey::LockScreen.value() { - lock_screen_2(); - } else if let Some(key) = control_key_to_key(ck) { + if let Some(key) = control_key_value_to_key(ck.value()) { if down { - en.key_down(key.clone()).ok(); - key_down.insert(ck.value() as _, Instant::now()); + en.key_down(key).ok(); } else { - en.key_up(key.clone()); - key_down.remove(&(ck.value() as _)); + en.key_up(key); } } } -#[inline] -fn chr_to_record_chr(chr: u32) -> u64 { - chr as u64 + KEY_CHAR_START -} - #[inline] fn need_to_uppercase(en: &mut Enigo) -> bool { get_modifier_state(Key::Shift, en) || get_modifier_state(Key::CapsLock, en) } fn process_chr(en: &mut Enigo, chr: u32, down: bool) { - let mut key_down = KEYS_DOWN.lock().unwrap(); - let key = get_layout(chr); - let record_chr = chr_to_record_chr(chr); + let key = char_value_to_key(chr); if down { if en.key_down(key).is_ok() { - key_down.insert(record_chr, Instant::now()); } else { if let Ok(chr) = char::try_from(chr) { let mut s = chr.to_string(); @@ -902,10 +915,8 @@ fn process_chr(en: &mut Enigo, chr: u32, down: bool) { en.key_sequence(&s); }; } - key_down.insert(record_chr, Instant::now()); } else { en.key_up(key); - key_down.remove(&record_chr); } } @@ -925,6 +936,30 @@ fn release_keys(en: &mut Enigo, to_release: &Vec) { } } +fn record_pressed_key(record_key: u64, down: bool) { + let mut key_down = KEYS_DOWN.lock().unwrap(); + if down { + key_down.insert(record_key, Instant::now()); + } else { + key_down.remove(&record_key); + } +} + +fn is_function_key(ck: &EnumOrUnknown) -> bool { + let mut res = false; + if ck.value() == ControlKey::CtrlAltDel.value() { + // have to spawn new thread because send_sas is tokio_main, the caller can not be tokio_main. + std::thread::spawn(|| { + allow_err!(send_sas()); + }); + res = true; + } else if ck.value() == ControlKey::LockScreen.value() { + lock_screen_2(); + res = true; + } + return res; +} + fn legacy_keyboard_mode(evt: &KeyEvent) { #[cfg(windows)] crate::platform::windows::try_change_desktop(); @@ -936,8 +971,19 @@ fn legacy_keyboard_mode(evt: &KeyEvent) { let down = evt.down; match evt.union { - Some(key_event::Union::ControlKey(ck)) => process_control_key(&mut en, &ck, down), - Some(key_event::Union::Chr(chr)) => process_chr(&mut en, chr, down), + Some(key_event::Union::ControlKey(ck)) => { + if is_function_key(&ck) { + return; + } + let record_key = ck.value() as u64; + record_pressed_key(record_key, down); + process_control_key(&mut en, &ck, down) + } + Some(key_event::Union::Chr(chr)) => { + let record_key = chr as u64 + KEY_CHAR_START; + record_pressed_key(record_key, down); + process_chr(&mut en, chr, down) + } Some(key_event::Union::Unicode(chr)) => process_unicode(&mut en, chr), Some(key_event::Union::Seq(ref seq)) => process_seq(&mut en, seq), _ => {} From 4837d84209ee0de8bfd2ffa8938ec3ae029e3ab9 Mon Sep 17 00:00:00 2001 From: asur4s Date: Tue, 20 Dec 2022 01:09:35 -0800 Subject: [PATCH 2/6] opt: enum KeyboardMode --- libs/hbb_common/src/keyboard.rs | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 libs/hbb_common/src/keyboard.rs diff --git a/libs/hbb_common/src/keyboard.rs b/libs/hbb_common/src/keyboard.rs new file mode 100644 index 000000000..10979f520 --- /dev/null +++ b/libs/hbb_common/src/keyboard.rs @@ -0,0 +1,39 @@ +use std::{fmt, slice::Iter, str::FromStr}; + +use crate::protos::message::KeyboardMode; + +impl fmt::Display for KeyboardMode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + KeyboardMode::Legacy => write!(f, "legacy"), + KeyboardMode::Map => write!(f, "map"), + KeyboardMode::Translate => write!(f, "translate"), + KeyboardMode::Auto => write!(f, "auto"), + } + } +} + +impl FromStr for KeyboardMode { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "legacy" => Ok(KeyboardMode::Legacy), + "map" => Ok(KeyboardMode::Map), + "translate" => Ok(KeyboardMode::Translate), + "auto" => Ok(KeyboardMode::Auto), + _ => Err(()), + } + } +} + +impl KeyboardMode { + pub fn iter() -> Iter<'static, KeyboardMode> { + static KEYBOARD_MODES: [KeyboardMode; 4] = [ + KeyboardMode::Legacy, + KeyboardMode::Map, + KeyboardMode::Translate, + KeyboardMode::Auto, + ]; + KEYBOARD_MODES.iter() + } +} From c267fc9d9bfa4f6ff7a155fd9c3abd4ae3b67c0e Mon Sep 17 00:00:00 2001 From: asur4s Date: Tue, 20 Dec 2022 01:11:52 -0800 Subject: [PATCH 3/6] refactor: set default keyboard mode --- src/client.rs | 16 +++++++++------- src/common.rs | 9 +++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/client.rs b/src/client.rs index 03bbf5918..1bb2ff861 100644 --- a/src/client.rs +++ b/src/client.rs @@ -23,7 +23,7 @@ use hbb_common::{ Config, PeerConfig, PeerInfoSerde, CONNECT_TIMEOUT, READ_TIMEOUT, RELAY_PORT, RENDEZVOUS_TIMEOUT, }, - log, + get_version_number, log, message_proto::{option_message::BoolOption, *}, protobuf::Message as _, rand, @@ -47,7 +47,10 @@ pub use super::lang::*; pub mod file_trait; pub mod helper; pub mod io_loop; -use crate::server::video_service::{SCRAP_X11_REF_URL, SCRAP_X11_REQUIRED}; +use crate::{ + common::is_keyboard_mode_supported, + server::video_service::{SCRAP_X11_REF_URL, SCRAP_X11_REQUIRED}, +}; 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); @@ -1410,12 +1413,11 @@ impl LoginConfigHandler { log::debug!("remove password of {}", self.id); } } - if config.keyboard_mode == "" { - if hbb_common::get_version_number(&pi.version) < hbb_common::get_version_number("1.2.0") - { - config.keyboard_mode = "legacy".to_string(); + if config.keyboard_mode.is_empty() { + if is_keyboard_mode_supported(&KeyboardMode::Map, get_version_number(&pi.version)) { + config.keyboard_mode = KeyboardMode::Map.to_string(); } else { - config.keyboard_mode = "map".to_string(); + config.keyboard_mode = KeyboardMode::Legacy.to_string(); } } self.conn_id = pi.conn_id; diff --git a/src/common.rs b/src/common.rs index 9023780f4..0f3794261 100644 --- a/src/common.rs +++ b/src/common.rs @@ -689,6 +689,15 @@ pub fn make_privacy_mode_msg(state: back_notification::PrivacyModeState) -> Mess msg_out } +pub fn is_keyboard_mode_supported(keyboard_mode: &KeyboardMode, version_number: i64) -> bool { + match keyboard_mode { + KeyboardMode::Legacy => true, + KeyboardMode::Map => version_number >= hbb_common::get_version_number("1.2.0"), + KeyboardMode::Translate => true, + KeyboardMode::Auto => true, + } +} + #[cfg(not(target_os = "linux"))] lazy_static::lazy_static! { pub static ref IS_X11: Mutex = Mutex::new(false); From 85620b73a74780b510f2fbbca624baa9726303e3 Mon Sep 17 00:00:00 2001 From: asur4s Date: Wed, 21 Dec 2022 00:03:15 -0800 Subject: [PATCH 4/6] opt: get supported keyboard modes --- libs/hbb_common/src/lib.rs | 1 + src/common.rs | 4 ++-- src/ui_session_interface.rs | 14 +++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index ae564685f..4245b1d73 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -40,6 +40,7 @@ pub use tokio_socks::TargetAddr; pub mod password_security; pub use chrono; pub use directories_next; +pub mod keyboard; #[cfg(feature = "quic")] pub type Stream = quic::Connection; diff --git a/src/common.rs b/src/common.rs index 0f3794261..02b4a0c10 100644 --- a/src/common.rs +++ b/src/common.rs @@ -693,8 +693,8 @@ pub fn is_keyboard_mode_supported(keyboard_mode: &KeyboardMode, version_number: match keyboard_mode { KeyboardMode::Legacy => true, KeyboardMode::Map => version_number >= hbb_common::get_version_number("1.2.0"), - KeyboardMode::Translate => true, - KeyboardMode::Auto => true, + KeyboardMode::Translate => false, + KeyboardMode::Auto => false, } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 0cf2f2e2d..e7ac620ee 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -4,7 +4,7 @@ use crate::client::{ load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, }; -use crate::common::GrabState; +use crate::common::{is_keyboard_mode_supported, GrabState}; use crate::keyboard; use crate::{client::Data, client::Interface}; use async_trait::async_trait; @@ -48,6 +48,10 @@ impl Session { self.lc.read().unwrap().custom_image_quality.clone() } + pub fn get_peer_version(&self) -> i64 { + self.lc.read().unwrap().version.clone() + } + pub fn get_keyboard_mode(&self) -> String { self.lc.read().unwrap().keyboard_mode.clone() } @@ -198,6 +202,14 @@ impl Session { crate::platform::is_xfce() } + pub fn get_supported_keyboard_modes(&self) -> Vec { + let version = self.get_peer_version(); + KeyboardMode::iter() + .filter(|&mode| is_keyboard_mode_supported(mode, version)) + .map(|&mode| mode) + .collect::>() + } + pub fn remove_port_forward(&self, port: i32) { let mut config = self.load_config(); config.port_forwards = config From a3769ca8e937116faaaaf1a4a64235e26fd3b332 Mon Sep 17 00:00:00 2001 From: asur4s Date: Mon, 26 Dec 2022 02:30:25 -0800 Subject: [PATCH 5/6] opt: map mode hide when unsupported --- .../lib/desktop/widgets/remote_menubar.dart | 23 +++++++++++++++---- src/flutter_ffi.rs | 18 +++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index c1a7bdce2..fc35fb47e 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -1185,10 +1185,25 @@ class _RemoteMenubarState extends State { final keyboardMenu = [ MenuEntryRadios( text: translate('Ratio'), - optionsGetter: () => [ - MenuEntryRadioOption(text: translate('Legacy mode'), value: 'legacy'), - MenuEntryRadioOption(text: translate('Map mode'), value: 'map'), - ], + optionsGetter: () { + List list = []; + List modes = ["legacy"]; + + if (bind.sessionIsKeyboardModeSupported(id: widget.id, mode: "map")) { + modes.add("map"); + } + + for (String mode in modes) { + if (mode == "legacy") { + list.add(MenuEntryRadioOption( + text: translate('Legacy mode'), value: 'legacy')); + } else if (mode == "map") { + list.add(MenuEntryRadioOption( + text: translate('Map mode'), value: 'map')); + } + } + return list; + }, curOptionGetter: () async { return await bind.sessionGetKeyboardMode(id: widget.id) ?? "legacy"; }, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index ddfaad06d..61b01f50c 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -7,11 +7,14 @@ use std::{ use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer}; use serde_json::json; +use crate::common::is_keyboard_mode_supported; +use hbb_common::message_proto::KeyboardMode; use hbb_common::ResultType; use hbb_common::{ config::{self, LocalConfig, PeerConfig, ONLINE}, fs, log, }; +use std::str::FromStr; // use crate::hbbs_http::account::AuthResult; @@ -245,6 +248,21 @@ pub fn session_get_custom_image_quality(id: String) -> Option> { } } +pub fn session_is_keyboard_mode_supported(id: String, mode: String) -> SyncReturn { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + if let Ok(mode) = KeyboardMode::from_str(&mode[..]) { + SyncReturn(is_keyboard_mode_supported( + &mode, + session.get_peer_version(), + )) + } else { + SyncReturn(false) + } + } else { + SyncReturn(false) + } +} + pub fn session_set_custom_image_quality(id: String, value: i32) { if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) { session.save_custom_image_quality(value); From 3102a241667c4cf4345f2896b5cb0654550d6b30 Mon Sep 17 00:00:00 2001 From: Asur4s Date: Wed, 11 Jan 2023 23:38:05 +0800 Subject: [PATCH 6/6] fix default keyboard mode when changing version --- src/client.rs | 10 +++++++++- src/common.rs | 7 +++++++ src/ui_session_interface.rs | 7 ++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/client.rs b/src/client.rs index 1bb2ff861..fc92c3674 100644 --- a/src/client.rs +++ b/src/client.rs @@ -7,6 +7,7 @@ use cpal::{ use magnum_opus::{Channels::*, Decoder as AudioDecoder}; use sha2::{Digest, Sha256}; use std::{ + str::FromStr, collections::HashMap, net::SocketAddr, ops::{Deref, Not}, @@ -48,7 +49,7 @@ pub mod file_trait; pub mod helper; pub mod io_loop; use crate::{ - common::is_keyboard_mode_supported, + common::{self, is_keyboard_mode_supported}, server::video_service::{SCRAP_X11_REF_URL, SCRAP_X11_REQUIRED}, }; pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); @@ -1419,6 +1420,13 @@ impl LoginConfigHandler { } else { config.keyboard_mode = KeyboardMode::Legacy.to_string(); } + } else { + let keyboard_modes = + common::get_supported_keyboard_modes(get_version_number(&pi.version)); + let current_mode = &KeyboardMode::from_str(&config.keyboard_mode).unwrap_or_default(); + if !keyboard_modes.contains(current_mode) { + config.keyboard_mode = KeyboardMode::Legacy.to_string(); + } } self.conn_id = pi.conn_id; // no matter if change, for update file time diff --git a/src/common.rs b/src/common.rs index 02b4a0c10..906ea2240 100644 --- a/src/common.rs +++ b/src/common.rs @@ -698,6 +698,13 @@ pub fn is_keyboard_mode_supported(keyboard_mode: &KeyboardMode, version_number: } } +pub fn get_supported_keyboard_modes(version: i64) -> Vec { + KeyboardMode::iter() + .filter(|&mode| is_keyboard_mode_supported(mode, version)) + .map(|&mode| mode) + .collect::>() +} + #[cfg(not(target_os = "linux"))] lazy_static::lazy_static! { pub static ref IS_X11: Mutex = Mutex::new(false); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index e7ac620ee..33193bd9e 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -4,7 +4,7 @@ use crate::client::{ load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, }; -use crate::common::{is_keyboard_mode_supported, GrabState}; +use crate::common::{self, is_keyboard_mode_supported, GrabState}; use crate::keyboard; use crate::{client::Data, client::Interface}; use async_trait::async_trait; @@ -204,10 +204,7 @@ impl Session { pub fn get_supported_keyboard_modes(&self) -> Vec { let version = self.get_peer_version(); - KeyboardMode::iter() - .filter(|&mode| is_keyboard_mode_supported(mode, version)) - .map(|&mode| mode) - .collect::>() + common::get_supported_keyboard_modes(version) } pub fn remove_port_forward(&self, port: i32) {