Skip to content

Commit

Permalink
feat(macos): register media keys
Browse files Browse the repository at this point in the history
  • Loading branch information
pewsheen committed Apr 18, 2024
1 parent 6c6feb3 commit e0f8102
Show file tree
Hide file tree
Showing 9 changed files with 400 additions and 18 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ keyboard-types = "0.7"
once_cell = "1"
thiserror = "1"

[target."cfg(target_os = \"macos\")".dependencies]
bitflags = "2"
cocoa = "0.25"
objc = "0.2"

[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
version = "0.52"
features = [
Expand Down
3 changes: 2 additions & 1 deletion examples/egui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use global_hotkey::{hotkey::HotKey, GlobalHotKeyEvent, GlobalHotKeyManager};
use keyboard_types::{Code, Modifiers};

fn main() -> Result<(), eframe::Error> {
let manager = GlobalHotKeyManager::new().unwrap();
let mut manager = GlobalHotKeyManager::new().unwrap();
let hotkey = HotKey::new(Some(Modifiers::SHIFT), Code::KeyD);

manager.register(hotkey).unwrap();
let receiver = GlobalHotKeyEvent::receiver();
std::thread::spawn(|| loop {
Expand Down
5 changes: 3 additions & 2 deletions examples/iced.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ impl Application for Example {
type Flags = ();

fn new(_flags: Self::Flags) -> (Example, iced::Command<Self::Message>) {
let manager = GlobalHotKeyManager::new().unwrap();
let mut manager = GlobalHotKeyManager::new().unwrap();
let hotkey_1 = HotKey::new(Some(Modifiers::CONTROL), Code::ArrowRight);
let hotkey_2 = HotKey::new(None, Code::ArrowUp);
manager.register(hotkey_2).unwrap();

manager.register(hotkey_1).unwrap();
manager.register(hotkey_2).unwrap();
(
Example {
last_pressed: "".to_string(),
Expand Down
16 changes: 15 additions & 1 deletion examples/tao.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,29 @@ use tao::event_loop::{ControlFlow, EventLoopBuilder};
fn main() {
let event_loop = EventLoopBuilder::new().build();

let hotkeys_manager = GlobalHotKeyManager::new().unwrap();
let mut hotkeys_manager = GlobalHotKeyManager::new().unwrap();

let hotkey = HotKey::new(Some(Modifiers::SHIFT), Code::KeyD);
let hotkey2 = HotKey::new(Some(Modifiers::SHIFT | Modifiers::ALT), Code::KeyD);
let hotkey3 = HotKey::new(None, Code::KeyF);
let hotkey4 = {
#[cfg(target_os = "macos")]
{
HotKey::new(
Some(Modifiers::SHIFT | Modifiers::ALT),
Code::MediaPlayPause,
)
}
#[cfg(not(target_os = "macos"))]
{
HotKey::new(Some(Modifiers::SHIFT | Modifiers::ALT), Code::MediaPlay)
}
};

hotkeys_manager.register(hotkey).unwrap();
hotkeys_manager.register(hotkey2).unwrap();
hotkeys_manager.register(hotkey3).unwrap();
hotkeys_manager.register(hotkey4).unwrap();

let global_hotkey_channel = GlobalHotKeyEvent::receiver();

Expand Down
2 changes: 1 addition & 1 deletion examples/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use winit::event_loop::{ControlFlow, EventLoopBuilder};
fn main() {
let event_loop = EventLoopBuilder::new().build().unwrap();

let hotkeys_manager = GlobalHotKeyManager::new().unwrap();
let mut hotkeys_manager = GlobalHotKeyManager::new().unwrap();

let hotkey = HotKey::new(Some(Modifiers::SHIFT), Code::KeyD);
let hotkey2 = HotKey::new(Some(Modifiers::SHIFT | Modifiers::ALT), Code::KeyD);
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub enum Error {
FailedToUnRegister(HotKey),
#[error("HotKey already registerd: {0:?}")]
AlreadyRegistered(HotKey),
#[error("Failed to watch media key event")]
FailedToWatchMediaKeyEvent,
}

/// Convenient type alias of Result type for tray-icon.
Expand Down
9 changes: 5 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,24 +141,25 @@ impl GlobalHotKeyManager {
})
}

pub fn register(&self, hotkey: HotKey) -> crate::Result<()> {
pub fn register(&mut self, hotkey: HotKey) -> crate::Result<()> {
self.platform_impl.register(hotkey)
}

pub fn unregister(&self, hotkey: HotKey) -> crate::Result<()> {
pub fn unregister(&mut self, hotkey: HotKey) -> crate::Result<()> {
self.platform_impl.unregister(hotkey)
}

pub fn register_all(&self, hotkeys: &[HotKey]) -> crate::Result<()> {
pub fn register_all(&mut self, hotkeys: &[HotKey]) -> crate::Result<()> {
self.platform_impl.register_all(hotkeys)?;
Ok(())
}

pub fn unregister_all(&self, hotkeys: &[HotKey]) -> crate::Result<()> {
pub fn unregister_all(&mut self, hotkeys: &[HotKey]) -> crate::Result<()> {
self.platform_impl.unregister_all(hotkeys)?;
Ok(())
}
}

#[cfg(test)]
mod tests {
fn assert_send<T: Send>() {}
Expand Down
143 changes: 143 additions & 0 deletions src/platform_impl/macos/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

/* taken from https://github.com/wusyong/carbon-bindgen/blob/467fca5d71047050b632fbdfb41b1f14575a8499/bindings.rs */

use std::ffi::{c_long, c_void};

pub type UInt32 = ::std::os::raw::c_uint;
pub type SInt32 = ::std::os::raw::c_int;
pub type OSStatus = SInt32;
Expand Down Expand Up @@ -115,3 +117,144 @@ extern "C" {
) -> OSStatus;
pub fn UnregisterEventHotKey(inHotKey: EventHotKeyRef) -> OSStatus;
}

/* Core Graphics */

/// Possible tapping points for events.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapLocation {
Hid,
Session,
AnnotatedSession,
}

// The next three enums are taken from:
// [Ref](https://github.com/phracker/MacOSX-SDKs/blob/ef9fe35d5691b6dd383c8c46d867a499817a01b6/MacOSX10.15.sdk/System/Library/Frameworks/CoreGraphics.framework/Versions/A/Headers/CGEventTypes.h)
/* Constants that specify where a new event tap is inserted into the list of active event taps. */
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapPlacement {
HeadInsertEventTap = 0,
TailAppendEventTap,
}

/* Constants that specify whether a new event tap is an active filter or a passive listener. */
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapOptions {
Default = 0x00000000,
ListenOnly = 0x00000001,
}

/// Constants that specify the different types of input events.
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventType {
Null = 0,

// Mouse events.
LeftMouseDown = 1,
LeftMouseUp = 2,
RightMouseDown = 3,
RightMouseUp = 4,
MouseMoved = 5,
LeftMouseDragged = 6,
RightMouseDragged = 7,

// Keyboard events.
KeyDown = 10,
KeyUp = 11,
FlagsChanged = 12,

// Composite events.
AppKitDefined = 13,
SystemDefined = 14,
ApplicationDefined = 15,

// Specialized control devices.
ScrollWheel = 22,
TabletPointer = 23,
TabletProximity = 24,
OtherMouseDown = 25,
OtherMouseUp = 26,
OtherMouseDragged = 27,

// Out of band event types. These are delivered to the event tap callback
// to notify it of unusual conditions that disable the event tap.
TapDisabledByTimeout = 0xFFFFFFFE,
TapDisabledByUserInput = 0xFFFFFFFF,
}

pub type CGEventMask = u64;
#[macro_export]
macro_rules! CGEventMaskBit {
($eventType:expr) => {
1 << $eventType as CGEventMask
};
}

pub enum CGEvent {}
pub type CGEventRef = *const CGEvent;

pub type CGEventTapProxy = *const c_void;
type CGEventTapCallBack = unsafe extern "C" fn(
proxy: CGEventTapProxy,
etype: CGEventType,
event: CGEventRef,
user_info: *const c_void,
) -> CGEventRef;

#[link(name = "CoreGraphics", kind = "framework")]
extern "C" {
pub fn CGEventTapCreate(
tap: CGEventTapLocation,
place: CGEventTapPlacement,
options: CGEventTapOptions,
events_of_interest: CGEventMask,
callback: CGEventTapCallBack,
user_info: *const c_void,
) -> CFMachPortRef;
pub fn CGEventTapEnable(tap: CFMachPortRef, enable: bool);
}

/* Core Foundation */

pub enum CFAllocator {}
pub type CFAllocatorRef = *mut CFAllocator;
pub enum CFRunLoop {}
pub type CFRunLoopRef = *mut CFRunLoop;
pub type CFRunLoopMode = CFStringRef;
pub enum CFRunLoopObserver {}
pub type CFRunLoopObserverRef = *mut CFRunLoopObserver;
pub enum CFRunLoopTimer {}
pub type CFRunLoopTimerRef = *mut CFRunLoopTimer;
pub enum CFRunLoopSource {}
pub type CFRunLoopSourceRef = *mut CFRunLoopSource;
pub enum CFString {}
pub type CFStringRef = *const CFString;

pub enum CFMachPort {}
pub type CFMachPortRef = *mut CFMachPort;

pub type CFIndex = c_long;

#[link(name = "CoreFoundation", kind = "framework")]
extern "C" {
pub static kCFRunLoopCommonModes: CFRunLoopMode;
pub static kCFAllocatorDefault: CFAllocatorRef;

pub fn CFRunLoopGetMain() -> CFRunLoopRef;

pub fn CFMachPortCreateRunLoopSource(
allocator: CFAllocatorRef,
port: CFMachPortRef,
order: CFIndex,
) -> CFRunLoopSourceRef;
pub fn CFMachPortInvalidate(port: CFMachPortRef);
pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
pub fn CFRunLoopRemoveSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
pub fn CFRelease(cftype: *const c_void);
}
Loading

0 comments on commit e0f8102

Please sign in to comment.