From ae53ec877b7da292725eff8d891621bd0c8d9ebb Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 18:09:53 +0800 Subject: [PATCH 1/4] translate mode, win2win, Send both unicode and virtual keycode to remote side Signed-off-by: fufesou --- libs/hbb_common/protos/message.proto | 6 ++- src/keyboard.rs | 57 +++++++++++++++++----------- src/platform/windows.rs | 42 +++++++++++--------- src/server/input_service.rs | 43 +++++++++++++++++++++ 4 files changed, 105 insertions(+), 43 deletions(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 6295c160b..a481ae6c4 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -203,11 +203,13 @@ message KeyEvent { bool press = 2; oneof union { ControlKey control_key = 3; - // high word, sym key code. win: virtual-key code, linux: keysym ?, macos: - // low word, position key code. win: scancode, linux: key code, macos: key code + // position key code. win: scancode, linux: key code, macos: key code uint32 chr = 4; uint32 unicode = 5; string seq = 6; + // high word. virtual keycode + // low word. unicode + uint32 win2win_hotkey = 7; } repeated ControlKey modifiers = 8; KeyboardMode mode = 9; diff --git a/src/keyboard.rs b/src/keyboard.rs index 86735b8f6..a2d17bc14 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -4,7 +4,7 @@ use crate::common::GrabState; #[cfg(feature = "flutter")] use crate::flutter::{CUR_SESSION_ID, SESSIONS}; #[cfg(target_os = "windows")] -use crate::platform::windows::get_char_by_vk; +use crate::platform::windows::{get_char_from_vk, get_unicode_from_vk}; #[cfg(not(any(feature = "flutter", feature = "cli")))] use crate::ui::CUR_SESSION; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -874,7 +874,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m #[cfg(target_os = "windows")] if _peer == OS_LOWER_LINUX { if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { - if let Some(chr) = get_char_by_vk(event.platform_code as u32) { + if let Some(chr) = get_char_from_vk(event.platform_code as u32) { let mut evt = key_event.clone(); evt.set_seq(chr.to_string()); events.push(evt); @@ -885,6 +885,33 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m } } +#[cfg(target_os = "windows")] +fn try_file_win2win_hotkey( + peer: &str, + event: &Event, + key_event: &KeyEvent, + events: &mut Vec, +) { + if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { + let win2win_hotkey = match event.event_type { + EventType::KeyPress(..) => { + if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) { + Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16)) + } else { + None + } + } + EventType::KeyRelease(..) => Some(event.platform_code << 16), + _ => None, + }; + if let Some(code) = win2win_hotkey { + let mut evt = key_event.clone(); + evt.set_win2win_hotkey(code); + events.push(evt); + } + } +} + #[cfg(target_os = "windows")] fn is_hot_key_modifiers_down() -> bool { if rdev::get_modifier(Key::ControlLeft) || rdev::get_modifier(Key::ControlRight) { @@ -899,25 +926,6 @@ fn is_hot_key_modifiers_down() -> bool { return false; } -#[inline] -#[cfg(target_os = "windows")] -pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { - let mut key_event = map_keyboard_mode(peer, event, key_event)?; - let chr = if peer == OS_LOWER_WINDOWS { - (key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16) - } else { - key_event.chr() - }; - key_event.set_chr(chr); - Some(key_event) -} - -#[inline] -#[cfg(not(target_os = "windows"))] -pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { - map_keyboard_mode(peer, event, key_event) -} - #[inline] #[cfg(any(target_os = "linux", target_os = "windows"))] fn is_altgr(event: &Event) -> bool { @@ -961,7 +969,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(not(any(target_os = "android", target_os = "ios")))] if is_numpad_key(&event) { - if let Some(evt) = translate_key_code(peer, event, key_event) { + if let Some(evt) = map_keyboard_mode(peer, event, key_event) { events.push(evt); } return events; @@ -995,13 +1003,16 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } + #[cfg(target_os = "windows")] + try_file_win2win_hotkey(peer, event, &key_event, &mut events); + #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { try_fill_unicode(peer, event, &key_event, &mut events); } if events.is_empty() { - if let Some(evt) = translate_key_code(peer, event, key_event) { + if let Some(evt) = map_keyboard_mode(peer, event, key_event) { events.push(evt); } } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 83f5af2dd..76b06c490 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1229,7 +1229,7 @@ sc delete {app_name} } pub fn run_after_install() -> ResultType<()> { - let (_, _, _, exe,_) = get_install_info(); + let (_, _, _, exe, _) = get_install_info(); run_cmds(get_after_install(&exe), true, "after_install") } @@ -2130,7 +2130,25 @@ mod cert { } } -pub fn get_char_by_vk(vk: u32) -> Option { +#[inline] +pub fn get_char_from_vk(vk: u32) -> Option { + get_char_from_unicode(get_unicode_from_vk(vk)?) +} + +pub fn get_char_from_unicode(unicode: u16) -> Option { + let buff = [unicode]; + if let Some(chr) = String::from_utf16(&buff[..1]).ok()?.chars().next() { + if chr.is_control() { + return None; + } else { + Some(chr) + } + } else { + None + } +} + +pub fn get_unicode_from_vk(vk: u32) -> Option { const BUF_LEN: i32 = 32; let mut buff = [0_u16; BUF_LEN as usize]; let buff_ptr = buff.as_mut_ptr(); @@ -2155,19 +2173,7 @@ pub fn get_char_by_vk(vk: u32) -> Option { ToUnicodeEx(vk, 0x00, &state as _, buff_ptr, BUF_LEN, 0, layout) }; if len == 1 { - if let Some(chr) = String::from_utf16(&buff[..len as usize]) - .ok()? - .chars() - .next() - { - if chr.is_control() { - return None; - } else { - Some(chr) - } - } else { - None - } + Some(buff[0]) } else { None } @@ -2190,10 +2196,10 @@ mod tests { } #[test] - fn test_get_char_by_vk() { - let chr = get_char_by_vk(0x41); // VK_A + fn test_get_unicode_char_by_vk() { + let chr = get_char_from_vk(0x41); // VK_A assert_eq!(chr, Some('a')); - let chr = get_char_by_vk(VK_ESCAPE as u32); // VK_ESC + let chr = get_char_from_vk(VK_ESCAPE as u32); // VK_ESC assert_eq!(chr, None) } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 5bc486516..38467fa32 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1,6 +1,8 @@ use super::*; #[cfg(target_os = "linux")] use crate::common::IS_X11; +#[cfg(target_os = "windows")] +use crate::platform::windows::get_char_from_unicode; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; @@ -1280,12 +1282,53 @@ fn translate_keyboard_mode(evt: &KeyEvent) { Some(key_event::Union::Unicode(..)) => { // Do not handle unicode for now. } + #[cfg(target_os = "windows")] + Some(key_event::Union::Win2winHotkey(code)) => { + simulate_win2win_hotkey(*code, evt.down); + } _ => { log::debug!("Unreachable. Unexpected key event {:?}", &evt); } } } +#[cfg(target_os = "windows")] +fn simulate_win2win_hotkey(code: u32, down: bool) { + let mut simulated = false; + + let unicode: u16 = (code & 0x0000FFFF) as u16; + if unicode != 0 { + // Try convert unicode to virtual keycode first. + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw + let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + if res as u16 != 0xFFFF { + let vk = res & 0x00FF; + let flag = res >> 8; + let modifiers = [rdev::Key::ShiftLeft, rdev::Key::ControlLeft, rdev::Key::Alt]; + let mod_len = modifiers.len(); + for pos in 0..mod_len { + if flag & (0x0001 << pos) != 0 { + allow_err!(rdev::simulate(&EventType::KeyPress(modifiers[pos]))); + } + } + allow_err!(rdev::simulate_code(Some(vk as _), None, true)); + allow_err!(rdev::simulate_code(Some(vk as _), None, false)); + for pos in 0..mod_len { + let rpos = mod_len - 1 - pos; + if flag & (0x0001 << rpos) != 0 { + allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos]))); + } + } + simulated = true; + } + } + + if simulated { + let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; + allow_err!(rdev::simulate_code(Some(keycode), None, down)); + } +} + pub fn handle_key_(evt: &KeyEvent) { if EXITING.load(Ordering::SeqCst) { return; From 021939a6a6c16b2ec62aefb5bf2d6eddb4dd22bd Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 19:30:22 +0800 Subject: [PATCH 2/4] tmp commit, for debug Signed-off-by: fufesou --- src/keyboard.rs | 8 ++++++++ src/server/input_service.rs | 16 ++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index a2d17bc14..e8186ad45 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -517,6 +517,11 @@ pub fn event_to_key_events( } }; + println!( + "REMOVE ME ==================================== key_events {:?}", + &key_events + ); + #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate { let is_numpad_key = is_numpad_key(&event); @@ -893,8 +898,10 @@ fn try_file_win2win_hotkey( events: &mut Vec, ) { if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { + let mut down = false; let win2win_hotkey = match event.event_type { EventType::KeyPress(..) => { + down = true; if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) { Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16)) } else { @@ -907,6 +914,7 @@ fn try_file_win2win_hotkey( if let Some(code) = win2win_hotkey { let mut evt = key_event.clone(); evt.set_win2win_hotkey(code); + evt.down = down; events.push(evt); } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 38467fa32..d85848314 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1,8 +1,6 @@ use super::*; #[cfg(target_os = "linux")] use crate::common::IS_X11; -#[cfg(target_os = "windows")] -use crate::platform::windows::get_char_from_unicode; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; @@ -1294,13 +1292,12 @@ fn translate_keyboard_mode(evt: &KeyEvent) { #[cfg(target_os = "windows")] fn simulate_win2win_hotkey(code: u32, down: bool) { - let mut simulated = false; - let unicode: u16 = (code & 0x0000FFFF) as u16; - if unicode != 0 { + if down { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + println!("REMOVE ME =============================== VkKeyScanW {} {}", unicode, res); if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; @@ -1319,14 +1316,13 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos]))); } } - simulated = true; + return; } } - if simulated { - let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; - allow_err!(rdev::simulate_code(Some(keycode), None, down)); - } + let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; + println!("REMOVE ME =============================== simulate_win2win_hotkey down {} {},{}", down, unicode, keycode); + allow_err!(rdev::simulate_code(Some(keycode), None, down)); } pub fn handle_key_(evt: &KeyEvent) { From ed4016a77aa62fb6bb41730ae8a28906b0b35d2b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 20:56:03 +0800 Subject: [PATCH 3/4] debug done Signed-off-by: fufesou --- src/keyboard.rs | 5 ----- src/server/input_service.rs | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index e8186ad45..b0eb68aa5 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -517,11 +517,6 @@ pub fn event_to_key_events( } }; - println!( - "REMOVE ME ==================================== key_events {:?}", - &key_events - ); - #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate { let is_numpad_key = is_numpad_key(&event); diff --git a/src/server/input_service.rs b/src/server/input_service.rs index d85848314..0def3aa90 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1297,7 +1297,6 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; - println!("REMOVE ME =============================== VkKeyScanW {} {}", unicode, res); if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; @@ -1321,7 +1320,6 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; - println!("REMOVE ME =============================== simulate_win2win_hotkey down {} {},{}", down, unicode, keycode); allow_err!(rdev::simulate_code(Some(keycode), None, down)); } From 83249f0f957770c5757386aefe0c9f132be7542a Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 21:35:59 +0800 Subject: [PATCH 4/4] debug done Signed-off-by: fufesou --- src/keyboard.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index b0eb68aa5..7f1f7e5f8 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -865,6 +865,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m if name.len() > 0 { let mut evt = key_event.clone(); evt.set_seq(name.to_string()); + evt.down = true; events.push(evt); } } @@ -877,6 +878,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m if let Some(chr) = get_char_from_vk(event.platform_code as u32) { let mut evt = key_event.clone(); evt.set_seq(chr.to_string()); + evt.down = true; events.push(evt); } } @@ -956,6 +958,7 @@ fn is_press(event: &Event) -> bool { // https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); + if let Some(unicode_info) = &event.unicode { if unicode_info.is_dead { #[cfg(target_os = "macos")] @@ -994,11 +997,15 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - return events; } + #[cfg(target_os = "windows")] + try_file_win2win_hotkey(peer, event, &key_event, &mut events); + #[cfg(any(target_os = "linux", target_os = "windows"))] - if is_press(event) { + if events.is_empty() && is_press(event) { try_fill_unicode(peer, event, &key_event, &mut events); } + // If AltGr is down, no need to send events other than unicode. #[cfg(target_os = "windows")] unsafe { if IS_0X021D_DOWN { @@ -1006,9 +1013,6 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } - #[cfg(target_os = "windows")] - try_file_win2win_hotkey(peer, event, &key_event, &mut events); - #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { try_fill_unicode(peer, event, &key_event, &mut events);