From 2aae2ca51387454698c305af2659035cbb2727e2 Mon Sep 17 00:00:00 2001 From: asur4s Date: Sat, 18 Mar 2023 20:55:35 -0700 Subject: [PATCH 1/7] Refector key click && Clear remapped keycodes --- libs/enigo/src/linux/nix_impl.rs | 47 ++++++++++++++++++++++++++++++-- src/server/connection.rs | 2 ++ src/server/input_service.rs | 24 +++++++++++++++- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/libs/enigo/src/linux/nix_impl.rs b/libs/enigo/src/linux/nix_impl.rs index f6e172677..f0e6a2166 100644 --- a/libs/enigo/src/linux/nix_impl.rs +++ b/libs/enigo/src/linux/nix_impl.rs @@ -1,5 +1,5 @@ use super::xdo::EnigoXdo; -use crate::{Key, KeyboardControllable, MouseButton, MouseControllable}; +use crate::{Key, KeyboardControllable, MouseButton, MouseControllable, ResultType}; use std::io::Read; use tfc::{traits::*, Context as TFC_Context, Key as TFC_Key}; @@ -42,6 +42,37 @@ impl Enigo { &mut self.custom_mouse } + /// Clear remapped keycodes + pub fn tfc_clear_remapped(&mut self) { + if let Some(tfc) = &mut self.tfc { + tfc.recover_remapped_keycodes(); + } + } + + fn tfc_key_click(&mut self, key: Key) -> ResultType { + if let Some(tfc) = &mut self.tfc { + let res = match key { + Key::Layout(chr) => tfc.unicode_char(chr), + key => { + let tfc_key: TFC_Key = match convert_to_tfc_key(key) { + Some(key) => key, + None => { + return Err("Failed to convert key to TFC_Key".into()); + } + }; + tfc.key_click(tfc_key) + } + }; + if res.is_err() { + Err("Failed to click char by tfc".to_string().into()) + } else { + Ok(()) + } + } else { + Err("Not Found TFC".into()) + } + } + fn tfc_key_down_or_up(&mut self, key: Key, down: bool, up: bool) -> bool { match &mut self.tfc { None => false, @@ -223,6 +254,7 @@ impl KeyboardControllable for Enigo { } } + /// Warning: Get 6^ in French. fn key_sequence(&mut self, sequence: &str) { if self.is_x11 { self.xdo.key_sequence(sequence) @@ -262,8 +294,10 @@ impl KeyboardControllable for Enigo { } } fn key_click(&mut self, key: Key) { - self.key_down(key).ok(); - self.key_up(key); + if self.tfc_key_click(key).is_err() { + self.key_down(key).ok(); + self.key_up(key); + } } } @@ -335,3 +369,10 @@ fn convert_to_tfc_key(key: Key) -> Option { }; Some(key) } + +#[test] +fn test_key_seq() { + // Get 6^ in French. + let mut en = Enigo::new(); + en.key_sequence("^^"); +} diff --git a/src/server/connection.rs b/src/server/connection.rs index b531a2add..5e6fbe50e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -612,6 +612,8 @@ impl Connection { } } } + #[cfg(target_os = "linux")] + clear_remapped_keycode(); log::info!("Input thread exited"); } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index f205e3f02..bb9ba167c 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -413,6 +413,11 @@ pub fn fix_key_down_timeout_at_exit() { log::info!("fix_key_down_timeout_at_exit"); } +#[cfg(target_os = "linux")] +pub fn clear_remapped_keycode() { + ENIGO.lock().unwrap().tfc_clear_remapped(); +} + #[inline] fn record_key_is_control_key(record_key: u64) -> bool { record_key < KEY_CHAR_START @@ -1130,7 +1135,24 @@ fn translate_process_code(code: u32, down: bool) { fn translate_keyboard_mode(evt: &KeyEvent) { match &evt.union { Some(key_event::Union::Seq(seq)) => { - ENIGO.lock().unwrap().key_sequence(seq); + // Fr -> US + // client: Shift + & => 1(send to remote) + // remote: Shift + 1 => ! + // + // Try to release shift first. + // remote: Shift + 1 => 1 + let mut en = ENIGO.lock().unwrap(); + + #[cfg(target_os = "linux")] + { + simulate_(&EventType::KeyRelease(RdevKey::ShiftLeft)); + simulate_(&EventType::KeyRelease(RdevKey::ShiftRight)); + for chr in seq.chars() { + en.key_click(Key::Layout(chr)); + } + } + #[cfg(not(target_os = "linux"))] + en.key_sequence(seq); } Some(key_event::Union::Chr(..)) => { #[cfg(target_os = "windows")] From ba4945f598a095cf43f587eb98910db0ac09fa9d Mon Sep 17 00:00:00 2001 From: asur4s Date: Sat, 18 Mar 2023 20:57:19 -0700 Subject: [PATCH 2/7] Get char when hot key is pressed --- src/keyboard.rs | 18 ++++++++++--- src/platform/windows.rs | 58 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index bfff9ff86..f613a06ef 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -3,11 +3,13 @@ use crate::client::get_key_state; 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; #[cfg(not(any(feature = "flutter", feature = "cli")))] use crate::ui::CUR_SESSION; -use hbb_common::message_proto::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::log; +use hbb_common::message_proto::*; use rdev::{Event, EventType, Key}; #[cfg(any(target_os = "windows", target_os = "macos"))] use std::sync::atomic::{AtomicBool, Ordering}; @@ -789,8 +791,18 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec {} + None => { + #[cfg(target_os = "windows")] + if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN }{ + if let Some(chr) = get_char_by_vk(event.code as u32) { + let mut evt = key_event.clone(); + evt.set_seq(chr.to_string()); + events.push(evt); + } + } + } } + } #[cfg(target_os = "windows")] @@ -857,7 +869,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } #[cfg(target_os = "windows")] - if unsafe { IS_0X021D_DOWN } || !is_hot_key_modifiers_down() { + if matches!(event.event_type, EventType::KeyPress(_)) { try_fill_unicode(event, &key_event, &mut events); } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 696a18ab9..ac3dc4e1d 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -10,6 +10,7 @@ use hbb_common::{ sleep, timeout, tokio, }; use std::io::prelude::*; +use std::ptr::null_mut; use std::{ ffi::OsString, fs, io, mem, @@ -26,8 +27,8 @@ use winapi::{ handleapi::CloseHandle, minwinbase::STILL_ACTIVE, processthreadsapi::{ - GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess, - OpenProcessToken, PROCESS_INFORMATION, STARTUPINFOW, + GetCurrentProcess, GetCurrentProcessId, GetCurrentThreadId, GetExitCodeProcess, + OpenProcess, OpenProcessToken, PROCESS_INFORMATION, STARTUPINFOW, }, securitybaseapi::GetTokenInformation, shellapi::ShellExecuteW, @@ -2072,6 +2073,51 @@ mod cert { } } +pub fn get_char_by_vk(vk: u32) -> Option { + let mut buff = [0_u16; BUF_LEN as usize]; + let buff_ptr = buff.as_mut_ptr(); + const BUF_LEN: i32 = 32; + let len = unsafe { + let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut()); + let layout = GetKeyboardLayout(current_window_thread_id); + + // refs: https://github.com/fufesou/rdev/blob/25a99ce71ab42843ad253dd51e6a35e83e87a8a4/src/windows/keyboard.rs#L115 + let press_state = 129; + let mut state: [BYTE; 256] = [0; 256]; + let shift_left = rdev::get_modifier(rdev::Key::ShiftLeft); + let shift_right = rdev::get_modifier(rdev::Key::ShiftRight); + if shift_left { + state[VK_LSHIFT as usize] = press_state; + } + if shift_right { + state[VK_RSHIFT as usize] = press_state; + } + if shift_left || shift_right { + state[VK_SHIFT as usize] = press_state; + } + buff = [0; 32]; + let buff_ptr = buff.as_mut_ptr(); + 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 + } + } else { + None + } +} + #[cfg(test)] mod tests { use super::*; @@ -2084,4 +2130,12 @@ mod tests { fn test_uninstall_cert() { println!("uninstall driver certs: {:?}", cert::uninstall_certs()); } + + #[test] + fn test_get_char_by_vk() { + let chr = get_char_by_vk(0x41); // VK_A + assert_eq!(chr, Some('a')); + let chr = get_char_by_vk(VK_ESCAPE as u32); // VK_ESC + assert_eq!(chr, None) + } } From b59f87f82530ca27566854d2f7308de93566ed77 Mon Sep 17 00:00:00 2001 From: asur4s Date: Sat, 18 Mar 2023 20:58:00 -0700 Subject: [PATCH 3/7] Support translate mode in x11 --- flutter/lib/desktop/widgets/remote_toolbar.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 65d763013..a9099dcf9 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1694,7 +1694,7 @@ class _KeyboardMenu extends StatelessWidget { for (KeyboardModeMenu mode in modes) { if (bind.sessionIsKeyboardModeSupported(id: id, mode: mode.key)) { if (mode.key == _kKeyTranslateMode) { - if (Platform.isLinux || pi.platform == kPeerPlatformLinux) { + if (Platform.isLinux) { continue; } } From 6ffa2cacb12b1659acdcf55a70d35eac8e241ee8 Mon Sep 17 00:00:00 2001 From: asur4s Date: Sun, 19 Mar 2023 19:02:59 -0700 Subject: [PATCH 4/7] Clean remapped keycodes when breakdown --- libs/hbb_common/src/platform/mod.rs | 13 ++++++++++++- src/core_main.rs | 3 ++- src/platform/mod.rs | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index aa929ca99..c37c8aaf5 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -7,6 +7,8 @@ pub mod macos; use crate::{config::Config, log}; use std::process::exit; +static mut GLOBAL_CALLBACK: Option> = None; + extern "C" fn breakdown_signal_handler(sig: i32) { let mut stack = vec![]; backtrace::trace(|frame| { @@ -41,11 +43,20 @@ extern "C" fn breakdown_signal_handler(sig: i32) { ) .ok(); } + unsafe { + if let Some(callback) = &GLOBAL_CALLBACK { + callback() + } + } exit(0); } -pub fn register_breakdown_handler() { +pub fn register_breakdown_handler(callback: T) +where + T: Fn() + 'static, +{ unsafe { + GLOBAL_CALLBACK = Some(Box::new(callback)); libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } diff --git a/src/core_main.rs b/src/core_main.rs index 76b630f88..d5e497060 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,3 +1,4 @@ +use crate::platform::breakdown_callback; use hbb_common::log; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::platform::register_breakdown_handler; @@ -38,7 +39,7 @@ pub fn core_main() -> Option> { i += 1; } #[cfg(not(any(target_os = "android", target_os = "ios")))] - register_breakdown_handler(); + register_breakdown_handler(breakdown_callback); #[cfg(target_os = "linux")] #[cfg(feature = "flutter")] { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f2b609d3f..ac5240dae 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -17,6 +17,7 @@ pub mod delegate; #[cfg(target_os = "linux")] pub mod linux; +use crate::input_service::clear_remapped_keycode; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::{message_proto::CursorData, ResultType}; #[cfg(not(target_os = "macos"))] @@ -41,6 +42,11 @@ pub fn is_xfce() -> bool { } } +pub fn breakdown_callback() { + #[cfg(target_os = "linux")] + clear_remapped_keycode() +} + // Android #[cfg(target_os = "android")] pub fn get_active_username() -> String { From 44560b687ee46b8219352c5b2cb8f653a11f3ab9 Mon Sep 17 00:00:00 2001 From: chiehw Date: Mon, 20 Mar 2023 10:13:06 +0800 Subject: [PATCH 5/7] fix format --- src/keyboard.rs | 6 +++--- src/platform/mod.rs | 3 +-- src/platform/windows.rs | 21 +++++++++++++-------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index f613a06ef..42cff9ce0 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -791,9 +791,10 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec { + None => + { #[cfg(target_os = "windows")] - if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN }{ + if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { if let Some(chr) = get_char_by_vk(event.code as u32) { let mut evt = key_event.clone(); evt.set_seq(chr.to_string()); @@ -802,7 +803,6 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec bool { pub fn breakdown_callback() { #[cfg(target_os = "linux")] - clear_remapped_keycode() + crate::input_service::clear_remapped_keycode() } // Android diff --git a/src/platform/windows.rs b/src/platform/windows.rs index ac3dc4e1d..d135bf14f 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -27,8 +27,8 @@ use winapi::{ handleapi::CloseHandle, minwinbase::STILL_ACTIVE, processthreadsapi::{ - GetCurrentProcess, GetCurrentProcessId, GetCurrentThreadId, GetExitCodeProcess, - OpenProcess, OpenProcessToken, PROCESS_INFORMATION, STARTUPINFOW, + GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess, + OpenProcessToken, PROCESS_INFORMATION, STARTUPINFOW, }, securitybaseapi::GetTokenInformation, shellapi::ShellExecuteW, @@ -1948,7 +1948,8 @@ mod cert { RegKey, }; - const ROOT_CERT_STORE_PATH: &str = "SOFTWARE\\Microsoft\\SystemCertificates\\ROOT\\Certificates\\"; + const ROOT_CERT_STORE_PATH: &str = + "SOFTWARE\\Microsoft\\SystemCertificates\\ROOT\\Certificates\\"; const THUMBPRINT_ALG: ALG_ID = CALG_SHA1; const THUMBPRINT_LEN: DWORD = 20; @@ -1966,7 +1967,10 @@ mod cert { &mut size, ) == TRUE { - (thumbprint.to_vec(), hex::encode(thumbprint).to_ascii_uppercase()) + ( + thumbprint.to_vec(), + hex::encode(thumbprint).to_ascii_uppercase(), + ) } else { (thumbprint.to_vec(), "".to_owned()) } @@ -2074,9 +2078,9 @@ mod cert { } pub fn get_char_by_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(); - const BUF_LEN: i32 = 32; let len = unsafe { let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut()); let layout = GetKeyboardLayout(current_window_thread_id); @@ -2095,8 +2099,6 @@ pub fn get_char_by_vk(vk: u32) -> Option { if shift_left || shift_right { state[VK_SHIFT as usize] = press_state; } - buff = [0; 32]; - let buff_ptr = buff.as_mut_ptr(); ToUnicodeEx(vk, 0x00, &state as _, buff_ptr, BUF_LEN, 0, layout) }; if len == 1 { @@ -2123,7 +2125,10 @@ mod tests { use super::*; #[test] fn test_install_cert() { - println!("install driver cert: {:?}", cert::install_cert("RustDeskIddDriver.cer")); + println!( + "install driver cert: {:?}", + cert::install_cert("RustDeskIddDriver.cer") + ); } #[test] From 43b7fd6d62532ad2e4a3a4360d5416ab51d3bc9f Mon Sep 17 00:00:00 2001 From: asur4s Date: Sun, 19 Mar 2023 23:08:43 -0700 Subject: [PATCH 6/7] update tfc --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7652f4430..b80b4d924 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5939,7 +5939,7 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "tfc" version = "0.6.1" -source = "git+https://github.com/fufesou/The-Fat-Controller#102f2ec2cb2bbbd64413d20d28323e5e77e0fe71" +source = "git+https://github.com/fufesou/The-Fat-Controller#1dba9a39c089ac9a7853b9dd5399c1d4aa3157d3" dependencies = [ "anyhow", "core-graphics 0.22.3", From d7dbe41adab1e916636f03e7876e826bbeefbdca Mon Sep 17 00:00:00 2001 From: Chieh Wang <99897242+chiehw@users.noreply.github.com> Date: Mon, 20 Mar 2023 15:28:14 +0800 Subject: [PATCH 7/7] better error info --- libs/enigo/src/linux/nix_impl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/enigo/src/linux/nix_impl.rs b/libs/enigo/src/linux/nix_impl.rs index f0e6a2166..e7dc69ab1 100644 --- a/libs/enigo/src/linux/nix_impl.rs +++ b/libs/enigo/src/linux/nix_impl.rs @@ -57,14 +57,14 @@ impl Enigo { let tfc_key: TFC_Key = match convert_to_tfc_key(key) { Some(key) => key, None => { - return Err("Failed to convert key to TFC_Key".into()); + return Err(format!("Failed to convert {:?} to TFC_Key", key).into()); } }; tfc.key_click(tfc_key) } }; if res.is_err() { - Err("Failed to click char by tfc".to_string().into()) + Err(format!("Failed to click {:?} by tfc", key).into()) } else { Ok(()) }