Cache CGEventSource and avoid leaking CGEvent when handling events

This commit is contained in:
Antonio Scandurra 2022-09-30 09:51:03 +02:00
parent a977593f3d
commit 25bba396ef

View file

@ -14,8 +14,10 @@ use core_graphics::{
event::{CGEvent, CGEventFlags, CGKeyCode}, event::{CGEvent, CGEventFlags, CGKeyCode},
event_source::{CGEventSource, CGEventSourceStateID}, event_source::{CGEventSource, CGEventSourceStateID},
}; };
use ctor::ctor;
use foreign_types::ForeignType;
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use std::{borrow::Cow, ffi::CStr, os::raw::c_char}; use std::{borrow::Cow, ffi::CStr, mem, os::raw::c_char, ptr};
const BACKSPACE_KEY: u16 = 0x7f; const BACKSPACE_KEY: u16 = 0x7f;
const SPACE_KEY: u16 = b' ' as u16; const SPACE_KEY: u16 = b' ' as u16;
@ -25,6 +27,15 @@ const ESCAPE_KEY: u16 = 0x1b;
const TAB_KEY: u16 = 0x09; const TAB_KEY: u16 = 0x09;
const SHIFT_TAB_KEY: u16 = 0x19; const SHIFT_TAB_KEY: u16 = 0x19;
static mut EVENT_SOURCE: core_graphics::sys::CGEventSourceRef = ptr::null_mut();
#[ctor]
unsafe fn build_event_source() {
let source = CGEventSource::new(CGEventSourceStateID::Private).unwrap();
EVENT_SOURCE = source.as_ptr();
mem::forget(source);
}
pub fn key_to_native(key: &str) -> Cow<str> { pub fn key_to_native(key: &str) -> Cow<str> {
use cocoa::appkit::*; use cocoa::appkit::*;
let code = match key { let code = match key {
@ -228,7 +239,8 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke {
let mut chars_ignoring_modifiers = let mut chars_ignoring_modifiers =
CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char) CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char)
.to_str() .to_str()
.unwrap(); .unwrap()
.to_string();
let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16); let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16);
let modifiers = native_event.modifierFlags(); let modifiers = native_event.modifierFlags();
@ -243,31 +255,31 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke {
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
let key = match first_char { let key = match first_char {
Some(SPACE_KEY) => "space", Some(SPACE_KEY) => "space".to_string(),
Some(BACKSPACE_KEY) => "backspace", Some(BACKSPACE_KEY) => "backspace".to_string(),
Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter", Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter".to_string(),
Some(ESCAPE_KEY) => "escape", Some(ESCAPE_KEY) => "escape".to_string(),
Some(TAB_KEY) => "tab", Some(TAB_KEY) => "tab".to_string(),
Some(SHIFT_TAB_KEY) => "tab", Some(SHIFT_TAB_KEY) => "tab".to_string(),
Some(NSUpArrowFunctionKey) => "up", Some(NSUpArrowFunctionKey) => "up".to_string(),
Some(NSDownArrowFunctionKey) => "down", Some(NSDownArrowFunctionKey) => "down".to_string(),
Some(NSLeftArrowFunctionKey) => "left", Some(NSLeftArrowFunctionKey) => "left".to_string(),
Some(NSRightArrowFunctionKey) => "right", Some(NSRightArrowFunctionKey) => "right".to_string(),
Some(NSPageUpFunctionKey) => "pageup", Some(NSPageUpFunctionKey) => "pageup".to_string(),
Some(NSPageDownFunctionKey) => "pagedown", Some(NSPageDownFunctionKey) => "pagedown".to_string(),
Some(NSDeleteFunctionKey) => "delete", Some(NSDeleteFunctionKey) => "delete".to_string(),
Some(NSF1FunctionKey) => "f1", Some(NSF1FunctionKey) => "f1".to_string(),
Some(NSF2FunctionKey) => "f2", Some(NSF2FunctionKey) => "f2".to_string(),
Some(NSF3FunctionKey) => "f3", Some(NSF3FunctionKey) => "f3".to_string(),
Some(NSF4FunctionKey) => "f4", Some(NSF4FunctionKey) => "f4".to_string(),
Some(NSF5FunctionKey) => "f5", Some(NSF5FunctionKey) => "f5".to_string(),
Some(NSF6FunctionKey) => "f6", Some(NSF6FunctionKey) => "f6".to_string(),
Some(NSF7FunctionKey) => "f7", Some(NSF7FunctionKey) => "f7".to_string(),
Some(NSF8FunctionKey) => "f8", Some(NSF8FunctionKey) => "f8".to_string(),
Some(NSF9FunctionKey) => "f9", Some(NSF9FunctionKey) => "f9".to_string(),
Some(NSF10FunctionKey) => "f10", Some(NSF10FunctionKey) => "f10".to_string(),
Some(NSF11FunctionKey) => "f11", Some(NSF11FunctionKey) => "f11".to_string(),
Some(NSF12FunctionKey) => "f12", Some(NSF12FunctionKey) => "f12".to_string(),
_ => { _ => {
let mut chars_ignoring_modifiers_and_shift = let mut chars_ignoring_modifiers_and_shift =
chars_for_modified_key(native_event.keyCode(), false, false); chars_for_modified_key(native_event.keyCode(), false, false);
@ -303,21 +315,19 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke {
shift, shift,
cmd, cmd,
function, function,
key: key.into(), key,
} }
} }
fn chars_for_modified_key<'a>(code: CGKeyCode, cmd: bool, shift: bool) -> &'a str { fn chars_for_modified_key(code: CGKeyCode, cmd: bool, shift: bool) -> String {
// Ideally, we would use `[NSEvent charactersByApplyingModifiers]` but that // Ideally, we would use `[NSEvent charactersByApplyingModifiers]` but that
// always returns an empty string with certain keyboards, e.g. Japanese. Synthesizing // always returns an empty string with certain keyboards, e.g. Japanese. Synthesizing
// an event with the given flags instead lets us access `characters`, which always // an event with the given flags instead lets us access `characters`, which always
// returns a valid string. // returns a valid string.
let event = CGEvent::new_keyboard_event( let source = unsafe { core_graphics::event_source::CGEventSource::from_ptr(EVENT_SOURCE) };
CGEventSource::new(CGEventSourceStateID::Private).unwrap(), let event = CGEvent::new_keyboard_event(source.clone(), code, true).unwrap();
code, mem::forget(source);
true,
)
.unwrap();
let mut flags = CGEventFlags::empty(); let mut flags = CGEventFlags::empty();
if cmd { if cmd {
flags |= CGEventFlags::CGEventFlagCommand; flags |= CGEventFlags::CGEventFlagCommand;
@ -327,10 +337,11 @@ fn chars_for_modified_key<'a>(code: CGKeyCode, cmd: bool, shift: bool) -> &'a st
} }
event.set_flags(flags); event.set_flags(flags);
let event: id = unsafe { msg_send![class!(NSEvent), eventWithCGEvent: event] };
unsafe { unsafe {
let event: id = msg_send![class!(NSEvent), eventWithCGEvent: &*event];
CStr::from_ptr(event.characters().UTF8String()) CStr::from_ptr(event.characters().UTF8String())
.to_str() .to_str()
.unwrap() .unwrap()
.to_string()
} }
} }