Merge pull request #3208 from fufesou/feat/macos_translate_mode

feat/macos translate mode
This commit is contained in:
RustDesk 2023-02-15 10:48:30 +08:00 committed by GitHub
commit b2827303e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 88 deletions

3
Cargo.lock generated
View File

@ -4554,12 +4554,13 @@ dependencies = [
[[package]] [[package]]
name = "rdev" name = "rdev"
version = "0.5.0-2" version = "0.5.0-2"
source = "git+https://github.com/fufesou/rdev#cedc4e62744566775026af4b434ef799804c1130" source = "git+https://github.com/fufesou/rdev#5b9fb5e42117f44e0ce0fe7cf2bddf270c75f1dc"
dependencies = [ dependencies = [
"cocoa", "cocoa",
"core-foundation 0.9.3", "core-foundation 0.9.3",
"core-foundation-sys 0.8.3", "core-foundation-sys 0.8.3",
"core-graphics 0.22.3", "core-graphics 0.22.3",
"dispatch",
"enum-map", "enum-map",
"epoll", "epoll",
"inotify", "inotify",

View File

@ -1510,13 +1510,16 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
if (bind.sessionIsKeyboardModeSupported( if (bind.sessionIsKeyboardModeSupported(
id: widget.id, mode: mode.key)) { id: widget.id, mode: mode.key)) {
if (mode.key == 'translate') { if (mode.key == 'translate') {
if (!Platform.isWindows || if (Platform.isLinux ||
widget.ffi.ffiModel.pi.platform != kPeerPlatformWindows) { widget.ffi.ffiModel.pi.platform == kPeerPlatformLinux) {
continue; continue;
} }
} }
list.add(MenuEntryRadioOption( var text = translate(mode.menu);
text: translate(mode.menu), value: mode.key)); if (mode.key == 'translate') {
text = '$text beta';
}
list.add(MenuEntryRadioOption(text: text, value: mode.key));
} }
} }
return list; return list;

View File

@ -39,7 +39,7 @@ fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) } unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
} }
fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD { fn keybd_event(mut flags: u32, vk: u16, scan: u16) -> DWORD {
let mut scan = scan; let mut scan = scan;
unsafe { unsafe {
// https://github.com/rustdesk/rustdesk/issues/366 // https://github.com/rustdesk/rustdesk/issues/366
@ -52,35 +52,33 @@ fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
scan = MapVirtualKeyExW(vk as _, 0, LAYOUT) as _; scan = MapVirtualKeyExW(vk as _, 0, LAYOUT) as _;
} }
} }
let mut input: INPUT = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
input.type_ = INPUT_KEYBOARD;
unsafe {
let dst_ptr = (&mut input.u as *mut _) as *mut u8;
let flags = match vk as _ {
winapi::um::winuser::VK_HOME |
winapi::um::winuser::VK_UP |
winapi::um::winuser::VK_PRIOR |
winapi::um::winuser::VK_LEFT |
winapi::um::winuser::VK_RIGHT |
winapi::um::winuser::VK_END |
winapi::um::winuser::VK_DOWN |
winapi::um::winuser::VK_NEXT |
winapi::um::winuser::VK_INSERT |
winapi::um::winuser::VK_DELETE => flags | winapi::um::winuser::KEYEVENTF_EXTENDEDKEY,
_ => flags,
};
let k = KEYBDINPUT { if flags & KEYEVENTF_UNICODE == 0 {
if scan >> 8 == 0xE0 || scan >> 8 == 0xE1 {
flags |= winapi::um::winuser::KEYEVENTF_EXTENDEDKEY;
}
}
let mut union: INPUT_u = unsafe { std::mem::zeroed() };
unsafe {
*union.ki_mut() = KEYBDINPUT {
wVk: vk, wVk: vk,
wScan: scan, wScan: scan,
dwFlags: flags, dwFlags: flags,
time: 0, time: 0,
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE, dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
}; };
let src_ptr = (&k as *const _) as *const u8;
std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, size_of::<KEYBDINPUT>());
} }
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) } let mut inputs = [INPUT {
type_: INPUT_KEYBOARD,
u: union,
}; 1];
unsafe {
SendInput(
inputs.len() as UINT,
inputs.as_mut_ptr(),
size_of::<INPUT>() as c_int,
)
}
} }
fn get_error() -> String { fn get_error() -> String {

View File

@ -201,6 +201,8 @@ message KeyEvent {
bool press = 2; bool press = 2;
oneof union { oneof union {
ControlKey control_key = 3; 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
uint32 chr = 4; uint32 chr = 4;
uint32 unicode = 5; uint32 unicode = 5;
string seq = 6; string seq = 6;

View File

@ -20,7 +20,6 @@ use std::{
os::raw::c_char, os::raw::c_char,
str::FromStr, str::FromStr,
}; };
use crate::ui_session_interface::InvokeUiSession;
// use crate::hbbs_http::account::AuthResult; // use crate::hbbs_http::account::AuthResult;

View File

@ -18,6 +18,13 @@ use std::{
#[cfg(windows)] #[cfg(windows)]
static mut IS_ALT_GR: bool = false; static mut IS_ALT_GR: bool = false;
#[allow(dead_code)]
const OS_LOWER_WINDOWS: &str = "windows";
#[allow(dead_code)]
const OS_LOWER_LINUX: &str = "linux";
#[allow(dead_code)]
const OS_LOWER_MACOS: &str = "macos";
#[cfg(any(target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "windows", target_os = "macos"))]
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false); static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
@ -202,6 +209,9 @@ pub fn update_grab_get_key_name() {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
static mut IS_0X021D_DOWN: bool = false; static mut IS_0X021D_DOWN: bool = false;
#[cfg(target_os = "macos")]
static mut IS_LEFT_OPTION_DOWN: bool = false;
pub fn start_grab_loop() { pub fn start_grab_loop() {
#[cfg(any(target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "windows", target_os = "macos"))]
std::thread::spawn(move || { std::thread::spawn(move || {
@ -213,6 +223,7 @@ pub fn start_grab_loop() {
let mut _keyboard_mode = KeyboardMode::Map; let mut _keyboard_mode = KeyboardMode::Map;
let _scan_code = event.scan_code; let _scan_code = event.scan_code;
let _code = event.code;
let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) { let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) {
_keyboard_mode = client::process_event(&event, None); _keyboard_mode = client::process_event(&event, None);
if is_press { if is_press {
@ -246,6 +257,13 @@ pub fn start_grab_loop() {
} }
} }
#[cfg(target_os = "macos")]
unsafe {
if _code as u32 == rdev::kVK_Option {
IS_LEFT_OPTION_DOWN = is_press;
}
}
return res; return res;
}; };
let func = move |event: Event| match event.event_type { let func = move |event: Event| match event.event_type {
@ -253,11 +271,13 @@ pub fn start_grab_loop() {
EventType::KeyRelease(key) => try_handle_keyboard(event, key, false), EventType::KeyRelease(key) => try_handle_keyboard(event, key, false),
_ => Some(event), _ => Some(event),
}; };
#[cfg(target_os = "macos")]
rdev::set_is_main_thread(false);
#[cfg(target_os = "windows")]
rdev::set_event_popup(false);
if let Err(error) = rdev::grab(func) { if let Err(error) = rdev::grab(func) {
log::error!("rdev Error: {:?}", error) log::error!("rdev Error: {:?}", error)
} }
#[cfg(target_os = "windows")]
rdev::set_event_popup(false);
}); });
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -395,13 +415,16 @@ pub fn event_to_key_events(
_ => {} _ => {}
} }
let mut peer = get_peer_platform().to_lowercase();
peer.retain(|c| !c.is_whitespace());
key_event.mode = keyboard_mode.into(); key_event.mode = keyboard_mode.into();
let mut key_events = match keyboard_mode { let mut key_events = match keyboard_mode {
KeyboardMode::Map => match map_keyboard_mode(event, key_event) { KeyboardMode::Map => match map_keyboard_mode(peer.as_str(), event, key_event) {
Some(event) => [event].to_vec(), Some(event) => [event].to_vec(),
None => Vec::new(), None => Vec::new(),
}, },
KeyboardMode::Translate => translate_keyboard_mode(event, key_event), KeyboardMode::Translate => translate_keyboard_mode(peer.as_str(), event, key_event),
_ => { _ => {
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
{ {
@ -424,7 +447,6 @@ pub fn event_to_key_events(
} }
} }
} }
key_events key_events
} }
@ -698,7 +720,7 @@ pub fn legacy_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Vec<KeyEv
events events
} }
pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEvent> { pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> Option<KeyEvent> {
match event.event_type { match event.event_type {
EventType::KeyPress(..) => { EventType::KeyPress(..) => {
key_event.down = true; key_event.down = true;
@ -709,12 +731,9 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
_ => return None, _ => return None,
}; };
let mut peer = get_peer_platform().to_lowercase();
peer.retain(|c| !c.is_whitespace());
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let keycode = match peer.as_str() { let keycode = match peer {
"windows" => { OS_LOWER_WINDOWS => {
// https://github.com/rustdesk/rustdesk/issues/1371 // https://github.com/rustdesk/rustdesk/issues/1371
// Filter scancodes that are greater than 255 and the hight word is not 0xE0. // Filter scancodes that are greater than 255 and the hight word is not 0xE0.
if event.scan_code > 255 && (event.scan_code >> 8) != 0xE0 { if event.scan_code > 255 && (event.scan_code >> 8) != 0xE0 {
@ -722,7 +741,7 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
} }
event.scan_code event.scan_code
} }
"macos" => { OS_LOWER_MACOS => {
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" { if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
rdev::win_scancode_to_macos_iso_code(event.scan_code)? rdev::win_scancode_to_macos_iso_code(event.scan_code)?
} else { } else {
@ -732,15 +751,15 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
_ => rdev::win_scancode_to_linux_code(event.scan_code)?, _ => rdev::win_scancode_to_linux_code(event.scan_code)?,
}; };
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let keycode = match peer.as_str() { let keycode = match peer {
"windows" => rdev::macos_code_to_win_scancode(event.code as _)?, OS_LOWER_WINDOWS => rdev::macos_code_to_win_scancode(event.code as _)?,
"macos" => event.code as _, OS_LOWER_MACOS => event.code as _,
_ => rdev::macos_code_to_linux_code(event.code as _)?, _ => rdev::macos_code_to_linux_code(event.code as _)?,
}; };
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
let keycode = match peer.as_str() { let keycode = match peer {
"windows" => rdev::linux_code_to_win_scancode(event.code as _)?, OS_LOWER_WINDOWS => rdev::linux_code_to_win_scancode(event.code as _)?,
"macos" => { OS_LOWER_MACOS => {
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" { if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
rdev::linux_code_to_macos_iso_code(event.code as _)? rdev::linux_code_to_macos_iso_code(event.code as _)?
} else { } else {
@ -759,10 +778,12 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) { fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
match &event.unicode { match &event.unicode {
Some(unicode_info) => { Some(unicode_info) => {
for code in &unicode_info.unicode { if let Some(name) = &unicode_info.name {
let mut evt = key_event.clone(); if name.len() > 0 {
evt.set_unicode(*code as _); let mut evt = key_event.clone();
events.push(evt); evt.set_seq(name.to_string());
events.push(evt);
}
} }
} }
None => {} None => {}
@ -783,45 +804,42 @@ fn is_hot_key_modifiers_down() -> bool {
return false; return false;
} }
pub fn translate_virtual_keycode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEvent> { #[inline]
match event.event_type { #[cfg(target_os = "windows")]
EventType::KeyPress(..) => { pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
key_event.down = true; let mut key_event = map_keyboard_mode(peer, event, key_event)?;
} key_event.set_chr((key_event.chr() & 0x0000FFFF) | ((event.code as u32) << 16));
EventType::KeyRelease(..) => {
key_event.down = false;
}
_ => return None,
};
let mut peer = get_peer_platform().to_lowercase();
peer.retain(|c| !c.is_whitespace());
// #[cfg(target_os = "windows")]
// let keycode = match peer.as_str() {
// "windows" => event.code,
// "macos" => {
// if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
// rdev::win_scancode_to_macos_iso_code(event.scan_code)?
// } else {
// rdev::win_scancode_to_macos_code(event.scan_code)?
// }
// }
// _ => rdev::win_scancode_to_linux_code(event.scan_code)?,
// };
key_event.set_chr(event.code as _);
Some(key_event) Some(key_event)
} }
pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> { #[inline]
#[cfg(not(target_os = "windows"))]
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
map_keyboard_mode(peer, event, key_event)
}
pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> {
let mut events: Vec<KeyEvent> = Vec::new(); let mut events: Vec<KeyEvent> = Vec::new();
if let Some(unicode_info) = &event.unicode { if let Some(unicode_info) = &event.unicode {
if unicode_info.is_dead { if unicode_info.is_dead {
#[cfg(target_os = "macos")]
if peer != OS_LOWER_MACOS && unsafe { IS_LEFT_OPTION_DOWN } {
// try clear dead key state
// rdev::clear_dead_key_state();
} else {
return events;
}
#[cfg(not(target_os = "macos"))]
return events; return events;
} }
} }
#[cfg(target_os = "macos")]
// ignore right option key
if event.code as u32 == rdev::kVK_RightOption {
return events;
}
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
unsafe { unsafe {
if event.scan_code == 0x021D { if event.scan_code == 0x021D {
@ -847,11 +865,16 @@ pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEve
} }
} }
#[cfg(not(target_os = "windows"))] #[cfg(target_os = "linux")]
try_fill_unicode(event, &key_event, &mut events); try_fill_unicode(event, &key_event, &mut events);
#[cfg(target_os = "macos")]
if !unsafe { IS_LEFT_OPTION_DOWN } {
try_fill_unicode(event, &key_event, &mut events);
}
if events.is_empty() { if events.is_empty() {
if let Some(evt) = translate_virtual_keycode(event, key_event) { if let Some(evt) = translate_key_code(peer, event, key_event) {
events.push(evt); events.push(evt);
} }
} }

View File

@ -1082,21 +1082,28 @@ fn legacy_keyboard_mode(evt: &KeyEvent) {
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn translate_process_virtual_keycode(vk: u32, down: bool) { fn translate_process_code(code: u32, down: bool) {
crate::platform::windows::try_change_desktop(); crate::platform::windows::try_change_desktop();
sim_rdev_rawkey_virtual(vk, down); match code >> 16 {
0 => sim_rdev_rawkey_position(code, down),
vk_code => sim_rdev_rawkey_virtual(vk_code, down),
};
} }
fn translate_keyboard_mode(evt: &KeyEvent) { fn translate_keyboard_mode(evt: &KeyEvent) {
match evt.union { match &evt.union {
Some(key_event::Union::Unicode(_unicode)) => { Some(key_event::Union::Seq(seq)) => {
#[cfg(target_os = "windows")] ENIGO.lock().unwrap().key_sequence(seq);
allow_err!(rdev::simulate_unicode(_unicode as _));
} }
Some(key_event::Union::Chr(..)) => Some(key_event::Union::Chr(..)) =>
{ {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
translate_process_virtual_keycode(evt.chr(), evt.down) translate_process_code(evt.chr(), evt.down);
#[cfg(not(target_os = "windows"))]
sim_rdev_rawkey_position(evt.chr(), evt.down);
}
Some(key_event::Union::Unicode(..)) => {
// Do not handle unicode for now.
} }
_ => { _ => {
log::debug!("Unreachable. Unexpected key event {:?}", &evt); log::debug!("Unreachable. Unexpected key event {:?}", &evt);