diff --git a/src/keyboard.rs b/src/keyboard.rs index ce68657ea..4c2a0a428 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -46,6 +46,35 @@ lazy_static::lazy_static! { m.insert(Key::MetaRight, false); Mutex::new(m) }; + static ref NUMPAD_POSITION_CODES: Arc> = { + let numpad_keys = [ + rdev::Key::KpMinus, + rdev::Key::KpPlus, + rdev::Key::KpMultiply, + rdev::Key::KpDivide, + rdev::Key::KpDecimal, + rdev::Key::KpReturn, + rdev::Key::KpEqual, + rdev::Key::KpComma, + rdev::Key::Kp0, + rdev::Key::Kp1, + rdev::Key::Kp2, + rdev::Key::Kp3, + rdev::Key::Kp4, + rdev::Key::Kp5, + rdev::Key::Kp6, + rdev::Key::Kp7, + rdev::Key::Kp8, + rdev::Key::Kp9, + ]; + #[cfg(target_os = "windows")] + let codes = numpad_keys.iter().filter_map(|k| rdev::win_scancode_from_key(*k)).collect(); + #[cfg(target_os = "linux")] + let codes = numpad_keys.iter().filter_map(|k| rdev::linux_code_from_keycode(*k)).collect(); + #[cfg(target_os = "macos")] + let codes = numpad_keys.iter().filter_map(|k| rdev::macos_code_from_keycode(*k)).collect(); + Arc::new(codes) + }; } pub mod client { @@ -333,19 +362,43 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { match client::get_keyboard_mode().as_str() { "map" => KeyboardMode::Map, "translate" => KeyboardMode::Translate, - _ => KeyboardMode::Legacy, + "legacy" => KeyboardMode::Legacy, + _ => { + // Set "map" as default mode if version > 1.2.0. + let mut is_peer_version_gt_1_2_0 = false; + + #[cfg(not(any(feature = "flutter", feature = "cli")))] + if let Some(session) = CUR_SESSION.lock().unwrap().as_ref() { + is_peer_version_gt_1_2_0 = + session.get_peer_version() > hbb_common::get_version_number("1.2.0"); + } + #[cfg(feature = "flutter")] + if let Some(session) = SESSIONS + .read() + .unwrap() + .get(&*CUR_SESSION_ID.read().unwrap()) + { + is_peer_version_gt_1_2_0 = + session.get_peer_version() > hbb_common::get_version_number("1.2.0"); + } + if is_peer_version_gt_1_2_0 { + KeyboardMode::Map + } else { + KeyboardMode::Legacy + } + } } } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn add_numlock_capslock_with_lock_modes(key_event: &mut KeyEvent, lock_modes: i32) { +fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32, is_numpad_key: bool) { const CAPS_LOCK: i32 = 1; const NUM_LOCK: i32 = 2; // const SCROLL_LOCK: i32 = 3; - if lock_modes & (1 << CAPS_LOCK) != 0 { + if !is_numpad_key && (lock_modes & (1 << CAPS_LOCK) != 0) { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if lock_modes & (1 << NUM_LOCK) != 0 { + if is_numpad_key && (lock_modes & (1 << NUM_LOCK) != 0) { key_event.modifiers.push(ControlKey::NumLock.into()); } // if lock_modes & (1 << SCROLL_LOCK) != 0 { @@ -354,11 +407,11 @@ fn add_numlock_capslock_with_lock_modes(key_event: &mut KeyEvent, lock_modes: i3 } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn add_numlock_capslock_status(key_event: &mut KeyEvent) { - if get_key_state(enigo::Key::CapsLock) { +fn add_lock_modes_modifiers(key_event: &mut KeyEvent, is_numpad_key: bool) { + if !is_numpad_key && get_key_state(enigo::Key::CapsLock) { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if get_key_state(enigo::Key::NumLock) { + if is_numpad_key && get_key_state(enigo::Key::NumLock) { key_event.modifiers.push(ControlKey::NumLock.into()); } } @@ -443,12 +496,13 @@ pub fn event_to_key_events( }; if keyboard_mode != KeyboardMode::Translate { + let is_numpad_key = NUMPAD_POSITION_CODES.contains(&event.position_code); for key_event in &mut key_events { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lock_modes) = lock_modes { - add_numlock_capslock_with_lock_modes(key_event, lock_modes); + parse_add_lock_modes_modifiers(key_event, lock_modes, is_numpad_key); } else { - add_numlock_capslock_status(key_event); + add_lock_modes_modifiers(key_event, is_numpad_key); } } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index bb9ba167c..178b26de2 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -113,6 +113,52 @@ impl Subscriber for MouseCursorSub { } } +struct LockModesHandler { + caps_lock_changed: bool, + num_lock_changed: bool, +} + +impl LockModesHandler { + fn new(key_event: &KeyEvent) -> Self { + let mut en = ENIGO.lock().unwrap(); + + let event_caps_enabled = key_event.modifiers.contains(&ControlKey::CapsLock.into()); + let local_caps_enabled = en.get_key_state(enigo::Key::CapsLock); + let caps_lock_changed = event_caps_enabled != local_caps_enabled; + if caps_lock_changed { + click_capslock(&mut en); + } + + let event_num_enabled = key_event.modifiers.contains(&ControlKey::NumLock.into()); + let local_num_enabled = en.get_key_state(enigo::Key::NumLock); + #[cfg(not(target_os = "windows"))] + let disable_numlock = false; + #[cfg(target_os = "windows")] + let disable_numlock = is_numlock_disabled(key_event); + let num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock; + if num_lock_changed { + click_numlock(&mut en); + } + + Self { + caps_lock_changed, + num_lock_changed, + } + } +} + +impl Drop for LockModesHandler { + fn drop(&mut self) { + let mut en = ENIGO.lock().unwrap(); + if self.caps_lock_changed { + click_capslock(&mut en); + } + if self.num_lock_changed { + click_numlock(&mut en); + } + } +} + pub const NAME_CURSOR: &'static str = "mouse_cursor"; pub const NAME_POS: &'static str = "mouse_pos"; pub type MouseCursorService = ServiceTmpl; @@ -850,10 +896,6 @@ 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 { - client_locking != remote_locking -} - #[cfg(target_os = "windows")] fn has_numpad_key(key_event: &KeyEvent) -> bool { key_event @@ -900,37 +942,13 @@ fn click_capslock(en: &mut Enigo) { let _ = en.key_down(enigo::Key::CapsLock); } +#[inline] fn click_numlock(_en: &mut Enigo) { // without numlock in macos #[cfg(not(target_os = "macos"))] _en.key_click(enigo::Key::NumLock); } -fn sync_numlock_capslock_status(key_event: &KeyEvent) { - let mut en = ENIGO.lock().unwrap(); - - let client_caps_locking = is_modifier_in_key_event(ControlKey::CapsLock, key_event); - let client_num_locking = is_modifier_in_key_event(ControlKey::NumLock, key_event); - let remote_caps_locking = en.get_key_state(enigo::Key::CapsLock); - let remote_num_locking = en.get_key_state(enigo::Key::NumLock); - - let need_click_capslock = is_not_same_status(client_caps_locking, remote_caps_locking); - let need_click_numlock = is_not_same_status(client_num_locking, remote_num_locking); - - #[cfg(not(target_os = "windows"))] - let disable_numlock = false; - #[cfg(target_os = "windows")] - let disable_numlock = is_numlock_disabled(key_event); - - if need_click_capslock { - click_capslock(&mut en); - } - - if need_click_numlock && !disable_numlock { - click_numlock(&mut en); - } -} - fn map_keyboard_mode(evt: &KeyEvent) { #[cfg(windows)] crate::platform::windows::try_change_desktop(); @@ -1174,9 +1192,12 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - if evt.down { - sync_numlock_capslock_status(evt) - } + let lock_mode_handler = if evt.down { + Some(LockModesHandler::new(&evt)) + } else { + None + }; + match evt.mode.unwrap() { KeyboardMode::Map => { map_keyboard_mode(evt);