Skip to content

Commit

Permalink
feat(macOS&linux): ad pressed and released states (#34)
Browse files Browse the repository at this point in the history
* feat: add Pressed and Released enum to GlobalHotKeyEvent

* feet: handle macos hotkey release

* fix: examples consider hotkey state

* docs: add comment for pub struct and enum

* Apply suggestions from code review

---------

Co-authored-by: Amr Bashir <[email protected]>
  • Loading branch information
chrisflatley and amrbashir authored Oct 18, 2023
1 parent ac63064 commit 0e35ee3
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 19 deletions.
4 changes: 2 additions & 2 deletions examples/tao.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use global_hotkey::{
hotkey::{Code, HotKey, Modifiers},
GlobalHotKeyEvent, GlobalHotKeyManager,
GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState,
};
use tao::event_loop::{ControlFlow, EventLoopBuilder};

Expand All @@ -29,7 +29,7 @@ fn main() {
if let Ok(event) = global_hotkey_channel.try_recv() {
println!("{event:?}");

if hotkey2.id() == event.id {
if hotkey2.id() == event.id && event.state == HotKeyState::Released {
hotkeys_manager.unregister(hotkey2).unwrap();
}
}
Expand Down
4 changes: 2 additions & 2 deletions examples/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use global_hotkey::{
hotkey::{Code, HotKey, Modifiers},
GlobalHotKeyEvent, GlobalHotKeyManager,
GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState,
};
use winit::event_loop::{ControlFlow, EventLoopBuilder};

Expand All @@ -29,7 +29,7 @@ fn main() {
if let Ok(event) = global_hotkey_channel.try_recv() {
println!("{event:?}");

if hotkey2.id() == event.id {
if hotkey2.id() == event.id && event.state == HotKeyState::Released {
hotkeys_manager.unregister(hotkey2).unwrap();
}
}
Expand Down
14 changes: 12 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,22 @@ mod platform_impl;
pub use self::error::*;
use hotkey::HotKey;

/// Contains the id of the triggered [`HotKey`].
/// Describes a global hotkey event emitted when a [`HotKey`] is pressed.
/// Describes the state of the [`HotKey`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum HotKeyState {
/// The [`HotKey`] is pressed (the key is down).
Pressed,
/// The [`HotKey`] is released (the key is up).
Released,
}

/// Describes a global hotkey event emitted when a [`HotKey`] is pressed or released.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct GlobalHotKeyEvent {
/// Id of the associated [`HotKey`].
pub id: u32,
/// State of the associated [`HotKey`].
pub state: HotKeyState,
}

/// A reciever that could be used to listen to global hotkey events.
Expand Down
6 changes: 5 additions & 1 deletion src/platform_impl/macos/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub type OSType = FourCharCode;
pub type ByteCount = ::std::os::raw::c_ulong;
pub type ItemCount = ::std::os::raw::c_ulong;
pub type OptionBits = UInt32;
pub type EventKind = UInt32;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct OpaqueEventRef {
Expand Down Expand Up @@ -63,6 +64,8 @@ pub type _bindgen_ty_1939 = ::std::os::raw::c_uint;
pub const kEventClassKeyboard: _bindgen_ty_1939 = 1801812322;
pub type _bindgen_ty_1980 = ::std::os::raw::c_uint;
pub const kEventHotKeyPressed: _bindgen_ty_1980 = 5;
pub type _bindgen_ty_1981 = ::std::os::raw::c_uint;
pub const kEventHotKeyReleased: _bindgen_ty_1981 = 6;
pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
pub const noErr: _bindgen_ty_1 = 0;

Expand All @@ -77,7 +80,7 @@ pub struct EventHotKeyID {
#[derive(Debug, Copy, Clone)]
pub struct EventTypeSpec {
pub eventClass: OSType,
pub eventKind: UInt32,
pub eventKind: EventKind,
}

#[link(name = "Carbon", kind = "framework")]
Expand All @@ -91,6 +94,7 @@ extern "C" {
outActualSize: *mut ByteCount,
outData: *mut ::std::os::raw::c_void,
) -> OSStatus;
pub fn GetEventKind(inEvent: EventRef) -> EventKind;
pub fn GetApplicationEventTarget() -> EventTargetRef;
pub fn InstallEventHandler(
inTarget: EventTargetRef,
Expand Down
36 changes: 26 additions & 10 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use keyboard_types::{Code, Modifiers};
use crate::{hotkey::HotKey, GlobalHotKeyEvent};

use self::ffi::{
kEventClassKeyboard, kEventHotKeyPressed, kEventParamDirectObject, noErr, typeEventHotKeyID,
EventHandlerCallRef, EventHandlerRef, EventHotKeyID, EventHotKeyRef, EventRef, EventTypeSpec,
GetApplicationEventTarget, GetEventParameter, InstallEventHandler, OSStatus,
RegisterEventHotKey, RemoveEventHandler, UnregisterEventHotKey,
kEventClassKeyboard, kEventHotKeyPressed, kEventHotKeyReleased, kEventParamDirectObject, noErr,
typeEventHotKeyID, EventHandlerCallRef, EventHandlerRef, EventHotKeyID, EventHotKeyRef,
EventRef, EventTypeSpec, GetApplicationEventTarget, GetEventKind, GetEventParameter,
InstallEventHandler, OSStatus, RegisterEventHotKey, RemoveEventHandler, UnregisterEventHotKey,
};

mod ffi;
Expand All @@ -23,19 +23,24 @@ unsafe impl Sync for GlobalHotKeyManager {}

impl GlobalHotKeyManager {
pub fn new() -> crate::Result<Self> {
let event_type = EventTypeSpec {
let pressed_event_type = EventTypeSpec {
eventClass: kEventClassKeyboard,
eventKind: kEventHotKeyPressed,
};
let released_event_type = EventTypeSpec {
eventClass: kEventClassKeyboard,
eventKind: kEventHotKeyReleased,
};
let event_types = [pressed_event_type, released_event_type];

let ptr = unsafe {
let mut handler_ref: EventHandlerRef = std::mem::zeroed();

let result = InstallEventHandler(
GetApplicationEventTarget(),
Some(hotkey_handler),
1,
&event_type,
2,
event_types.as_ptr(),
std::ptr::null_mut(),
&mut handler_ref,
);
Expand Down Expand Up @@ -174,9 +179,20 @@ unsafe extern "C" fn hotkey_handler(
);

if result == noErr as _ {
let _ = GlobalHotKeyEvent::send(GlobalHotKeyEvent {
id: event_hotkey.id,
});
let event_kind = GetEventKind(event);
match event_kind {
#[allow(non_upper_case_globals)]
kEventHotKeyPressed => GlobalHotKeyEvent::send(GlobalHotKeyEvent {
id: event_hotkey.id,
state: crate::HotKeyState::Pressed,
}),
#[allow(non_upper_case_globals)]
kEventHotKeyReleased => GlobalHotKeyEvent::send(GlobalHotKeyEvent {
id: event_hotkey.id,
state: crate::HotKeyState::Released,
}),
_ => {}
};
}

noErr as _
Expand Down
5 changes: 4 additions & 1 deletion src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ unsafe extern "system" fn global_hotkey_subclass_proc(
_subclass_input_ptr: usize,
) -> LRESULT {
if msg == WM_HOTKEY {
GlobalHotKeyEvent::send(GlobalHotKeyEvent { id: wparam as _ });
GlobalHotKeyEvent::send(GlobalHotKeyEvent {
id: wparam as _,
state: crate::HotKeyState::Pressed,
});
}

DefSubclassProc(hwnd, msg, wparam, lparam)
Expand Down
9 changes: 8 additions & 1 deletion src/platform_impl/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,17 @@ fn events_processor(thread_rx: Receiver<ThreadMessage>) {
if let Some((id, repeating)) = hotkeys.get_mut(&(modifiers, keycode)) {
match (e, *repeating) {
(xlib::KeyPress, false) => {
GlobalHotKeyEvent::send(GlobalHotKeyEvent { id: *id });
GlobalHotKeyEvent::send(GlobalHotKeyEvent {
id: *id,
state: crate::HotKeyState::Pressed,
});
*repeating = true;
}
(xlib::KeyRelease, true) => {
GlobalHotKeyEvent::send(GlobalHotKeyEvent {
id: *id,
state: crate::HotKeyState::Released,
});
*repeating = false;
}
_ => {}
Expand Down

0 comments on commit 0e35ee3

Please sign in to comment.