From eb76f7cecc6c580328f0c863379a3483082dfcad Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 4 Aug 2021 03:44:00 +0800 Subject: [PATCH] fix mac key mapping, https://github.com/rustdesk/rustdesk/issues/186, https://github.com/rustdesk/rustdesk/issues/86 --- libs/enigo/src/macos/keycodes.rs | 47 ++++ libs/enigo/src/macos/macos_impl.rs | 334 +++++------------------------ 2 files changed, 98 insertions(+), 283 deletions(-) diff --git a/libs/enigo/src/macos/keycodes.rs b/libs/enigo/src/macos/keycodes.rs index 8dcd2191b..540d0e185 100644 --- a/libs/enigo/src/macos/keycodes.rs +++ b/libs/enigo/src/macos/keycodes.rs @@ -71,3 +71,50 @@ pub const kVK_ANSI_KeypadEnter: u16 = 0x4C; pub const kVK_ANSI_KeypadMinus: u16 = 0x4E; pub const kVK_ANSI_KeypadEquals: u16 = 0x51; pub const kVK_RIGHT_COMMAND: u16 = 0x36; +pub const kVK_ANSI_A : u16 = 0x00; +pub const kVK_ANSI_S : u16 = 0x01; +pub const kVK_ANSI_D : u16 = 0x02; +pub const kVK_ANSI_F : u16 = 0x03; +pub const kVK_ANSI_H : u16 = 0x04; +pub const kVK_ANSI_G : u16 = 0x05; +pub const kVK_ANSI_Z : u16 = 0x06; +pub const kVK_ANSI_X : u16 = 0x07; +pub const kVK_ANSI_C : u16 = 0x08; +pub const kVK_ANSI_V : u16 = 0x09; +pub const kVK_ANSI_B : u16 = 0x0B; +pub const kVK_ANSI_Q : u16 = 0x0C; +pub const kVK_ANSI_W : u16 = 0x0D; +pub const kVK_ANSI_E : u16 = 0x0E; +pub const kVK_ANSI_R : u16 = 0x0F; +pub const kVK_ANSI_Y : u16 = 0x10; +pub const kVK_ANSI_T : u16 = 0x11; +pub const kVK_ANSI_1 : u16 = 0x12; +pub const kVK_ANSI_2 : u16 = 0x13; +pub const kVK_ANSI_3 : u16 = 0x14; +pub const kVK_ANSI_4 : u16 = 0x15; +pub const kVK_ANSI_6 : u16 = 0x16; +pub const kVK_ANSI_5 : u16 = 0x17; +pub const kVK_ANSI_Equal : u16 = 0x18; +pub const kVK_ANSI_9 : u16 = 0x19; +pub const kVK_ANSI_7 : u16 = 0x1A; +pub const kVK_ANSI_Minus : u16 = 0x1B; +pub const kVK_ANSI_8 : u16 = 0x1C; +pub const kVK_ANSI_0 : u16 = 0x1D; +pub const kVK_ANSI_RightBracket : u16 = 0x1E; +pub const kVK_ANSI_O : u16 = 0x1F; +pub const kVK_ANSI_U : u16 = 0x20; +pub const kVK_ANSI_LeftBracket : u16 = 0x21; +pub const kVK_ANSI_I : u16 = 0x22; +pub const kVK_ANSI_P : u16 = 0x23; +pub const kVK_ANSI_L : u16 = 0x25; +pub const kVK_ANSI_J : u16 = 0x26; +pub const kVK_ANSI_Quote : u16 = 0x27; +pub const kVK_ANSI_K : u16 = 0x28; +pub const kVK_ANSI_Semicolon : u16 = 0x29; +pub const kVK_ANSI_Backslash : u16 = 0x2A; +pub const kVK_ANSI_Comma : u16 = 0x2B; +pub const kVK_ANSI_Slash : u16 = 0x2C; +pub const kVK_ANSI_N : u16 = 0x2D; +pub const kVK_ANSI_M : u16 = 0x2E; +pub const kVK_ANSI_Period : u16 = 0x2F; +pub const kVK_ANSI_Grave : u16 = 0x32; diff --git a/libs/enigo/src/macos/macos_impl.rs b/libs/enigo/src/macos/macos_impl.rs index ac0bb3687..33c7d7ad2 100644 --- a/libs/enigo/src/macos/macos_impl.rs +++ b/libs/enigo/src/macos/macos_impl.rs @@ -9,12 +9,6 @@ use self::core_graphics::event_source::*; use crate::macos::keycodes::*; use crate::{Key, KeyboardControllable, MouseButton, MouseControllable}; use objc::runtime::Class; -use std::ffi::CStr; -use std::os::raw::*; - -// required for pressedMouseButtons on NSEvent -#[link(name = "AppKit", kind = "framework")] -extern "C" {} struct MyCGEvent; @@ -34,8 +28,6 @@ extern "C" { fn CGEventSourceKeyState(stateID: i32, key: u16) -> bool; } -pub type CFDataRef = *const c_void; - #[repr(C)] #[derive(Clone, Copy)] struct NSPoint { @@ -43,162 +35,6 @@ struct NSPoint { y: f64, } -#[repr(C)] -pub struct __TISInputSource; -pub type TISInputSourceRef = *const __TISInputSource; - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct __CFString([u8; 0]); -pub type CFStringRef = *const __CFString; -pub type Boolean = c_uchar; -pub type UInt8 = c_uchar; -pub type SInt32 = c_int; -pub type UInt16 = c_ushort; -pub type UInt32 = c_uint; -pub type UniChar = UInt16; -pub type UniCharCount = c_ulong; - -pub type OptionBits = UInt32; -pub type OSStatus = SInt32; - -pub type CFStringEncoding = UInt32; - -#[allow(non_upper_case_globals)] -pub const kUCKeyActionDisplay: _bindgen_ty_702 = _bindgen_ty_702::kUCKeyActionDisplay; - -#[allow(non_camel_case_types)] -#[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum _bindgen_ty_702 { - // kUCKeyActionDown = 0, - // kUCKeyActionUp = 1, - // kUCKeyActionAutoKey = 2, - kUCKeyActionDisplay = 3, -} - -#[allow(non_snake_case)] -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct UCKeyboardTypeHeader { - pub keyboardTypeFirst: UInt32, - pub keyboardTypeLast: UInt32, - pub keyModifiersToTableNumOffset: UInt32, - pub keyToCharTableIndexOffset: UInt32, - pub keyStateRecordsIndexOffset: UInt32, - pub keyStateTerminatorsOffset: UInt32, - pub keySequenceDataIndexOffset: UInt32, -} - -#[allow(non_snake_case)] -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct UCKeyboardLayout { - pub keyLayoutHeaderFormat: UInt16, - pub keyLayoutDataVersion: UInt16, - pub keyLayoutFeatureInfoOffset: UInt32, - pub keyboardTypeCount: UInt32, - pub keyboardTypeList: [UCKeyboardTypeHeader; 1usize], -} - -#[allow(non_upper_case_globals)] -pub const kUCKeyTranslateNoDeadKeysBit: _bindgen_ty_703 = - _bindgen_ty_703::kUCKeyTranslateNoDeadKeysBit; - -#[allow(non_camel_case_types)] -#[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum _bindgen_ty_703 { - kUCKeyTranslateNoDeadKeysBit = 0, -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct __CFAllocator([u8; 0]); -pub type CFAllocatorRef = *const __CFAllocator; - -// #[repr(u32)] -// #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -// pub enum _bindgen_ty_15 { -// kCFStringEncodingMacRoman = 0, -// kCFStringEncodingWindowsLatin1 = 1280, -// kCFStringEncodingISOLatin1 = 513, -// kCFStringEncodingNextStepLatin = 2817, -// kCFStringEncodingASCII = 1536, -// kCFStringEncodingUnicode = 256, -// kCFStringEncodingUTF8 = 134217984, -// kCFStringEncodingNonLossyASCII = 3071, -// kCFStringEncodingUTF16BE = 268435712, -// kCFStringEncodingUTF16LE = 335544576, -// kCFStringEncodingUTF32 = 201326848, -// kCFStringEncodingUTF32BE = 402653440, -// kCFStringEncodingUTF32LE = 469762304, -// } - -#[allow(non_upper_case_globals)] -pub const kCFStringEncodingUTF8: u32 = 134_217_984; - -#[allow(improper_ctypes)] -#[link(name = "Carbon", kind = "framework")] -extern "C" { - fn TISCopyCurrentKeyboardInputSource() -> TISInputSourceRef; - fn TISCopyCurrentKeyboardLayoutInputSource() -> TISInputSourceRef; - fn TISCopyCurrentASCIICapableKeyboardLayoutInputSource() -> TISInputSourceRef; - - // extern void * - // TISGetInputSourceProperty( - // TISInputSourceRef inputSource, - // CFStringRef propertyKey) - - #[allow(non_upper_case_globals)] - #[link_name = "kTISPropertyUnicodeKeyLayoutData"] - pub static kTISPropertyUnicodeKeyLayoutData: CFStringRef; - - #[allow(non_snake_case)] - pub fn TISGetInputSourceProperty( - inputSource: TISInputSourceRef, - propertyKey: CFStringRef, - ) -> *mut c_void; - - #[allow(non_snake_case)] - pub fn CFDataGetBytePtr(theData: CFDataRef) -> *const UInt8; - - #[allow(non_snake_case)] - pub fn UCKeyTranslate( - keyLayoutPtr: *const UInt8, //*const UCKeyboardLayout, - virtualKeyCode: UInt16, - keyAction: UInt16, - modifierKeyState: UInt32, - keyboardType: UInt32, - keyTranslateOptions: OptionBits, - deadKeyState: *mut UInt32, - maxStringLength: UniCharCount, - actualStringLength: *mut UniCharCount, - unicodeString: *mut UniChar, - ) -> OSStatus; - - pub fn LMGetKbdType() -> UInt8; - - #[allow(non_snake_case)] - pub fn CFStringCreateWithCharacters( - alloc: CFAllocatorRef, - chars: *const UniChar, - numChars: CFIndex, - ) -> CFStringRef; - - #[allow(non_upper_case_globals)] - #[link_name = "kCFAllocatorDefault"] - pub static kCFAllocatorDefault: CFAllocatorRef; - - #[allow(non_snake_case)] - pub fn CFStringGetCString( - theString: CFStringRef, - buffer: *mut c_char, - bufferSize: CFIndex, - encoding: CFStringEncoding, - ) -> Boolean; -} - // not present in servo/core-graphics #[allow(dead_code)] #[derive(Debug)] @@ -211,7 +47,6 @@ enum ScrollUnit { /// The main struct for handling the event emitting pub struct Enigo { event_source: Option, - keycode_to_string_map: std::collections::HashMap, double_click_interval: u32, last_click_time: Option, multiple_click: i64, @@ -263,7 +98,6 @@ impl Default for Enigo { } else { None }, - keycode_to_string_map: Default::default(), double_click_interval, multiple_click: 1, last_click_time: None, @@ -591,129 +425,63 @@ impl Enigo { Key::RightAlt => kVK_RightOption, Key::Raw(raw_keycode) => raw_keycode, - Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()), + Key::Layout(c) => self.map_key_board(c), Key::Super | Key::Command | Key::Windows | Key::Meta => kVK_Command, _ => 0, } } - - fn get_layoutdependent_keycode(&mut self, string: String) -> CGKeyCode { - if self.keycode_to_string_map.is_empty() { - self.init_map(); + fn map_key_board(&self, ch: char) -> CGKeyCode { + match ch { + 'a' => kVK_ANSI_A, + 'b' => kVK_ANSI_B, + 'c' => kVK_ANSI_C, + 'd' => kVK_ANSI_D, + 'e' => kVK_ANSI_E, + 'f' => kVK_ANSI_F, + 'g' => kVK_ANSI_G, + 'h' => kVK_ANSI_H, + 'i' => kVK_ANSI_I, + 'j' => kVK_ANSI_J, + 'k' => kVK_ANSI_K, + 'l' => kVK_ANSI_L, + 'm' => kVK_ANSI_M, + 'n' => kVK_ANSI_N, + 'o' => kVK_ANSI_O, + 'p' => kVK_ANSI_P, + 'q' => kVK_ANSI_Q, + 'r' => kVK_ANSI_R, + 's' => kVK_ANSI_S, + 't' => kVK_ANSI_T, + 'u' => kVK_ANSI_U, + 'v' => kVK_ANSI_V, + 'w' => kVK_ANSI_W, + 'x' => kVK_ANSI_X, + 'y' => kVK_ANSI_Y, + 'z' => kVK_ANSI_Z, + '0' => kVK_ANSI_0, + '1' => kVK_ANSI_1, + '2' => kVK_ANSI_2, + '3' => kVK_ANSI_3, + '4' => kVK_ANSI_4, + '5' => kVK_ANSI_5, + '6' => kVK_ANSI_6, + '7' => kVK_ANSI_7, + '8' => kVK_ANSI_8, + '9' => kVK_ANSI_9, + '-' => kVK_ANSI_Minus, + '=' => kVK_ANSI_Equal, + '[' => kVK_ANSI_LeftBracket, + ']' => kVK_ANSI_RightBracket, + '\\' => kVK_ANSI_Backslash, + ';' => kVK_ANSI_Semicolon, + '\'' => kVK_ANSI_Quote, + ',' => kVK_ANSI_Comma, + '.' => kVK_ANSI_Period, + '/' => kVK_ANSI_Slash, + '`' => kVK_ANSI_Grave, + _ => 0, } - *self.keycode_to_string_map.get(&string).unwrap_or(&0) - } - - fn init_map(&mut self) { - println!("init_map"); - self.keycode_to_string_map.insert("".to_owned(), 0); - // loop through every keycode (0 - 127) - for keycode in 0..128 { - // no modifier - if let Some(key_string) = self.keycode_to_string(keycode, 0x100) { - self.keycode_to_string_map.insert(key_string, keycode); - } - - // shift modifier - if let Some(key_string) = self.keycode_to_string(keycode, 0x20102) { - self.keycode_to_string_map.insert(key_string, keycode); - } - - // alt modifier - // if let Some(string) = self.keycode_to_string(keycode, 0x80120) { - // println!("{:?}", string); - // } - // alt + shift modifier - // if let Some(string) = self.keycode_to_string(keycode, 0xa0122) { - // println!("{:?}", string); - // } - } - } - - fn keycode_to_string(&self, keycode: u16, modifier: u32) -> Option { - let cf_string = self.create_string_for_key(keycode, modifier); - unsafe { - if !cf_string.is_null() { - let mut buf: [i8; 255] = [0; 255]; - let success = CFStringGetCString( - cf_string, - buf.as_mut_ptr(), - buf.len() as _, - kCFStringEncodingUTF8, - ); - if success != 0 { - let name: &CStr = CStr::from_ptr(buf.as_ptr()); - if let Ok(name) = name.to_str() { - return Some(name.to_owned()); - } - } - } - } - - None - } - - fn create_string_for_key(&self, keycode: u16, modifier: u32) -> CFStringRef { - let current_keyboard = unsafe { TISCopyCurrentKeyboardInputSource() }; - let mut layout_data = std::ptr::null_mut(); - if !current_keyboard.is_null() { - layout_data = unsafe { - TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData) - }; - } - if layout_data.is_null() { - // https://github.com/microsoft/vscode/issues/23833 - let current_keyboard = unsafe { TISCopyCurrentKeyboardLayoutInputSource() }; - if !current_keyboard.is_null() { - layout_data = unsafe { - TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData) - }; - } - } - if layout_data.is_null() { - let current_keyboard = unsafe { TISCopyCurrentASCIICapableKeyboardLayoutInputSource() }; - if !current_keyboard.is_null() { - layout_data = unsafe { - TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData) - }; - } - } - if layout_data.is_null() { - // to-do: try out manual mapping in https://github.com/stweil/OSXvnc - // we do see crash like this, also not easy to reproduce, no sure if it related -/* -0 rustdesk 0x000000010f921bc9 std::collections::hash::map::HashMap$LT$K$C$V$C$S$GT$::insert::h84e28c51a3292e7a + 473 -1 rustdesk 0x000000010f921884 enigo::macos::macos_impl::Enigo::key_to_keycode::h85ead82e9b1075ae + 1428 -2 rustdesk 0x000000010f922a8c _$LT$enigo..macos..macos_impl..Enigo$u20$as$u20$enigo..KeyboardControllable$GT$::key_down::h54f24da6d274b948 + 44 -*/ - return std::ptr::null() as _; - } - let keyboard_layout = unsafe { CFDataGetBytePtr(layout_data) }; - if keyboard_layout.is_null() { - return std::ptr::null() as _; - } - - let mut keys_down: UInt32 = 0; - let mut chars: u16 = 0; - // let mut chars: *mut c_void;//[UniChar; 4]; - let mut real_length: UniCharCount = 0; - unsafe { - UCKeyTranslate( - keyboard_layout, - keycode, - kUCKeyActionDisplay as u16, - modifier, - LMGetKbdType() as u32, - kUCKeyTranslateNoDeadKeysBit as u32, - &mut keys_down, - 8, // sizeof(chars) / sizeof(chars[0]), - &mut real_length, - &mut chars, - ); - } - - unsafe { CFStringCreateWithCharacters(kCFAllocatorDefault, &chars, 1) } } }