diff --git a/.changes/use-objc2.md b/.changes/use-objc2.md new file mode 100644 index 0000000..5c5596a --- /dev/null +++ b/.changes/use-objc2.md @@ -0,0 +1,5 @@ +--- +"global-hotkey": patch +--- + +Use `objc2` internally, leading to slightly better memory- and type-safety. diff --git a/Cargo.toml b/Cargo.toml index d9c0291..ad4b1e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,29 +3,28 @@ name = "global-hotkey" version = "0.6.0" description = "Global hotkeys for Desktop Applications" edition = "2021" -keywords = [ "windowing", "global", "global-hotkey", "hotkey" ] +keywords = ["windowing", "global", "global-hotkey", "hotkey"] license = "Apache-2.0 OR MIT" readme = "README.md" repository = "https://github.com/amrbashir/global-hotkey" documentation = "https://docs.rs/global-hotkey" -categories = [ "gui" ] +categories = ["gui"] [features] -serde = [ "dep:serde" ] +serde = ["dep:serde"] [dependencies] crossbeam-channel = "0.5" keyboard-types = "0.7" once_cell = "1" thiserror = "1" -serde = { version = "1", optional = true, features = [ "derive" ] } +serde = { version = "1", optional = true, features = ["derive"] } -[target."cfg(target_os = \"macos\")".dependencies] -bitflags = "2" -cocoa = "0.26" -objc = "0.2" +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = "0.5.2" +objc2-app-kit = { version = "0.2.2", features = ["NSEvent"] } -[target."cfg(target_os = \"windows\")".dependencies.windows-sys] +[target.'cfg(target_os = "windows")'.dependencies.windows-sys] version = "0.59" features = [ "Win32_UI_WindowsAndMessaging", @@ -33,10 +32,10 @@ features = [ "Win32_System_SystemServices", "Win32_Graphics_Gdi", "Win32_UI_Shell", - "Win32_UI_Input_KeyboardAndMouse" + "Win32_UI_Input_KeyboardAndMouse", ] -[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] +[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] x11-dl = "2.21" [dev-dependencies] diff --git a/src/platform_impl/macos/ffi.rs b/src/platform_impl/macos/ffi.rs index 7683ac4..1e1f27b 100644 --- a/src/platform_impl/macos/ffi.rs +++ b/src/platform_impl/macos/ffi.rs @@ -7,6 +7,8 @@ use std::ffi::{c_long, c_void}; +use objc2::encode::{Encode, Encoding, RefEncode}; + pub type UInt32 = ::std::os::raw::c_uint; pub type SInt32 = ::std::os::raw::c_int; pub type OSStatus = SInt32; @@ -199,6 +201,10 @@ macro_rules! CGEventMaskBit { pub enum CGEvent {} pub type CGEventRef = *const CGEvent; +unsafe impl RefEncode for CGEvent { + const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("__CGEvent", &[])); +} + pub type CGEventTapProxy = *const c_void; type CGEventTapCallBack = unsafe extern "C" fn( proxy: CGEventTapProxy, diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index d7d4205..13cc207 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -1,11 +1,6 @@ -use bitflags::bitflags; -use cocoa::{ - appkit::NSEventType, - base::id, - foundation::{NSInteger, NSUInteger}, -}; use keyboard_types::{Code, Modifiers}; -use objc::{class, msg_send, sel, sel_impl}; +use objc2::{msg_send_id, rc::Retained, ClassType}; +use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType}; use std::{ collections::{BTreeMap, HashSet}, ffi::c_void, @@ -255,35 +250,6 @@ impl GlobalHotKeyManager { } } -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - struct NSEventModifierFlags: NSUInteger { - const Shift = 1 << 17; - const Control = 1 << 18; - const Option = 1 << 19; - const Command = 1 << 20; - } -} - -impl From for Modifiers { - fn from(mod_flags: NSEventModifierFlags) -> Self { - let mut mods = Modifiers::empty(); - if mod_flags.contains(NSEventModifierFlags::Shift) { - mods |= Modifiers::SHIFT; - } - if mod_flags.contains(NSEventModifierFlags::Control) { - mods |= Modifiers::CONTROL; - } - if mod_flags.contains(NSEventModifierFlags::Option) { - mods |= Modifiers::ALT; - } - if mod_flags.contains(NSEventModifierFlags::Command) { - mods |= Modifiers::META; - } - mods - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[allow(non_camel_case_types)] enum NX_KEYTYPE { @@ -294,10 +260,10 @@ enum NX_KEYTYPE { Rewind = 20, } -impl TryFrom for NX_KEYTYPE { +impl TryFrom for NX_KEYTYPE { type Error = String; - fn try_from(value: i64) -> Result { + fn try_from(value: isize) -> Result { match value { 16 => Ok(NX_KEYTYPE::Play), 17 => Ok(NX_KEYTYPE::Next), @@ -381,13 +347,13 @@ unsafe extern "C" fn media_key_event_callback( return event; } - let ns_event: id = msg_send![class!(NSEvent), eventWithCGEvent:event]; - let event_type: NSEventType = msg_send![ns_event, type]; - let event_subtype: u64 = msg_send![ns_event, subtype]; + let ns_event: Retained = msg_send_id![NSEvent::class(), eventWithCGEvent: event]; + let event_type = ns_event.r#type(); + let event_subtype = ns_event.subtype(); - if event_type == NSEventType::NSSystemDefined && event_subtype == 8 { + if event_type == NSEventType::SystemDefined && event_subtype == NSEventSubtype::ScreenChanged { // Key - let data_1: NSInteger = msg_send![ns_event, data1]; + let data_1 = ns_event.data1(); let nx_keytype = NX_KEYTYPE::try_from((data_1 & 0xFFFF0000) >> 16); if nx_keytype.is_err() { return event; @@ -395,11 +361,23 @@ unsafe extern "C" fn media_key_event_callback( let nx_keytype = nx_keytype.unwrap(); // Modifiers - let mods: NSUInteger = msg_send![ns_event, modifierFlags]; - let mods = NSEventModifierFlags::from_bits_truncate(mods); + let flags = ns_event.modifierFlags(); + let mut mods = Modifiers::empty(); + if flags.contains(NSEventModifierFlags::NSEventModifierFlagShift) { + mods |= Modifiers::SHIFT; + } + if flags.contains(NSEventModifierFlags::NSEventModifierFlagControl) { + mods |= Modifiers::CONTROL; + } + if flags.contains(NSEventModifierFlags::NSEventModifierFlagOption) { + mods |= Modifiers::ALT; + } + if flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand) { + mods |= Modifiers::META; + } // Generate hotkey for matching - let hotkey = HotKey::new(Some(mods.into()), nx_keytype.into()); + let hotkey = HotKey::new(Some(mods), nx_keytype.into()); // Prevent Arc been releaded after callback returned let media_hotkeys = &*(user_info as *const Mutex>);