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();
        }
    }
}