win_hotkeys/manager.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
//! Defines the `HotkeyManager`, which manages the registration,
//! unregistration, and execution of hotkeys. It also handles the main event
//! loop that listens for keyboard events and invokes associated callbacks.
use crate::error::WHKError;
use crate::error::WHKError::RegistrationFailed;
use crate::hook;
use crate::hook::{KeyAction, KeyboardEvent};
use crate::hotkey::Hotkey;
use crate::state::KeyboardState;
use crate::VKey;
use crossbeam_channel::Sender;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
/// Manages the lifecycle of hotkeys, including their registration, unregistration, and execution.
///
/// The `HotkeyManager` listens for keyboard events and triggers the corresponding
/// hotkey callbacks when events match registered
/// hotkeys.
/// # Type Parameters
/// - `T`: The return type of the hotkey callbacks.
pub struct HotkeyManager<T> {
hotkeys: HashMap<u16, Vec<Hotkey<T>>>,
interrupt: Arc<AtomicBool>,
callback_results_channel: Option<Sender<T>>,
}
impl<T> Default for HotkeyManager<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> HotkeyManager<T> {
pub fn new() -> HotkeyManager<T> {
Self {
hotkeys: HashMap::new(),
interrupt: Arc::new(AtomicBool::new(false)),
callback_results_channel: None,
}
}
/// Registers a new hotkey.
pub fn register_hotkey(
&mut self,
trigger_key: VKey,
mod_keys: &[VKey],
callback: impl Fn() -> T + Send + 'static,
) -> Result<i32, WHKError> {
let hotkey = Hotkey::new(trigger_key, mod_keys, callback);
// Check if already exists
let state = hotkey.generate_keyboard_state();
if self.hotkeys.values().any(|vec| {
vec.iter()
.any(|hotkey| hotkey.generate_keyboard_state() == state)
}) {
return Err(RegistrationFailed);
}
// Add hotkey and return id
let id = hotkey.generate_id();
let entry = self.hotkeys.entry(trigger_key.to_vk_code()).or_default();
entry.push(hotkey);
Ok(id)
}
/// Unregisters a hotkey by its unique id
pub fn unregister_hotkey(&mut self, hotkey_id: i32) {
for vec in self.hotkeys.values_mut() {
vec.retain(|hotkey| hotkey.generate_id() != hotkey_id);
}
}
/// Unregisters all hotkeys
pub fn unregister_all(&mut self) {
self.hotkeys.clear();
}
/// Registers a channel for callback results to be sent into
pub fn register_channel(&mut self, channel: Sender<T>) {
self.callback_results_channel = Some(channel);
}
/// Runs the main event loop to listen for keyboard events.
///
/// This method blocks and processes keyboard events until interrupted.
/// It matches events against registered hotkeys and executes the corresponding callbacks.
pub fn event_loop(&mut self) {
let hook = hook::start();
while !self.interrupt.load(Ordering::Relaxed) {
if let Ok(event) = hook.recv() {
let (key_code, state) = match event {
KeyboardEvent::KeyDown {
vk_code: key_code,
keyboard_state: state,
} => (key_code, state),
_ => continue,
};
let mut found = false;
if let Some(hotkeys) = self.hotkeys.get_mut(&key_code) {
for hotkey in hotkeys {
if hotkey.is_trigger_state(state) {
if state.is_down(VKey::LWin.to_vk_code()) {
hook.key_action(KeyAction::Replace);
} else {
hook.key_action(KeyAction::Block);
}
let result = hotkey.callback();
if let Some(callback_result_channel) = &self.callback_results_channel {
callback_result_channel.send(result).unwrap();
}
found = true;
break;
}
}
}
if !found {
hook.key_action(KeyAction::Allow);
}
}
}
hook.exit();
}
/// Signals the `HotkeyManager` to interrupt its event loop.
pub fn interrupt_handle(&self) -> InterruptHandle {
InterruptHandle {
interrupt_handle: Arc::clone(&self.interrupt),
}
}
}
/// A handle for signaling the `HotkeyManager` to stop its event loop.
///
/// The `InterruptHandle` is used to gracefully interrupt the event loop by sending
/// a control signal. This allows the `HotkeyManager` to clean up resources and stop
/// processing keyboard events.
pub struct InterruptHandle {
interrupt_handle: Arc<AtomicBool>,
}
unsafe impl Sync for InterruptHandle {}
unsafe impl Send for InterruptHandle {}
impl InterruptHandle {
/// Interrupts the `HotkeyManager`'s event loop.
///
/// This method sets an internal flag to indicate that the interruption has been requested.
/// then sends a dummy keyboard event to the event loop to force it to check the flag.
pub fn interrupt(&self) {
use crate::hook::{KeyboardEvent, HOOK_EVENT_TX};
let dummy_event = KeyboardEvent::KeyDown {
vk_code: 0,
keyboard_state: KeyboardState::new(),
};
self.interrupt_handle.store(true, Ordering::Relaxed);
let event_tx = HOOK_EVENT_TX.read().unwrap();
if let Some(ke_tx) = &*event_tx {
ke_tx.send(dummy_event).unwrap();
}
}
}