hookmap_core/sys/
windows.rs

1mod hook;
2mod input;
3mod vkcode;
4
5use hook::HookHandler;
6use input::Input;
7use windows::Win32::Foundation::{LPARAM, LRESULT, WPARAM};
8use windows::Win32::UI::WindowsAndMessaging::HHOOK;
9
10use crate::button::{Button, ButtonAction};
11use crate::event::{self, EventReceiver, NativeEventOperation};
12
13use std::sync::atomic::{AtomicBool, Ordering};
14
15use once_cell::sync::Lazy;
16use windows::Win32::UI::{HiDpi, WindowsAndMessaging};
17
18const SHOULD_BE_IGNORED_FLAG: usize = 0x1;
19const INJECTED_FLAG: usize = 0x2;
20
21#[derive(Debug)]
22struct ButtonState([AtomicBool; Button::VARIANT_COUNT]);
23
24impl ButtonState {
25    const fn new() -> Self {
26        let inner = unsafe {
27            // AtomicBool has the same in-memory representation as a bool.
28            // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html
29            std::mem::transmute([false; Button::VARIANT_COUNT])
30        };
31        ButtonState(inner)
32    }
33
34    #[inline]
35    fn press(&self, button: Button, order: Ordering) {
36        self.0[button as usize].store(true, order);
37    }
38
39    #[inline]
40    fn release(&self, button: Button, order: Ordering) {
41        self.0[button as usize].store(false, order)
42    }
43
44    #[inline]
45    fn is_pressed(&self, button: Button, order: Ordering) -> bool {
46        self.0[button as usize].load(order)
47    }
48
49    #[inline]
50    fn is_released(&self, button: Button, order: Ordering) -> bool {
51        !self.0[button as usize].load(order)
52    }
53}
54
55static BUTTON_STATE: ButtonState = ButtonState::new();
56
57static INPUT: Lazy<Input> = Lazy::new(Input::new);
58
59#[inline]
60fn send_input(button: Button, action: ButtonAction, recursive: bool, assume: fn(Button)) {
61    let left_and_right_modifier = match button {
62        Button::Shift => Some((Button::LShift, Button::RShift)),
63        Button::Ctrl => Some((Button::LCtrl, Button::RCtrl)),
64        Button::Alt => Some((Button::LAlt, Button::RAlt)),
65        Button::Super => Some((Button::LSuper, Button::RSuper)),
66        _ => None,
67    };
68    if let Some((left, right)) = left_and_right_modifier {
69        assume(left);
70        assume(right);
71        assume(button);
72        INPUT.button_input(left, action, recursive);
73        INPUT.button_input(right, action, recursive);
74    } else {
75        assume(button);
76        INPUT.button_input(button, action, recursive);
77    }
78}
79
80impl Button {
81    /// Simulates a button presses.
82    #[inline]
83    pub fn press(self) {
84        send_input(self, ButtonAction::Press, false, Button::assume_pressed);
85    }
86
87    /// Simulates a button presses.
88    /// Events generated by this method can be hooked.
89    #[inline]
90    pub fn press_recursive(self) {
91        send_input(self, ButtonAction::Press, true, Button::assume_pressed);
92    }
93
94    /// Simulates a button releases.
95    #[inline]
96    pub fn release(self) {
97        send_input(self, ButtonAction::Release, false, Button::assume_released);
98    }
99
100    /// Simulates a button releases.
101    /// Events generated by this method can be hooked.
102    #[inline]
103    pub fn release_recursive(self) {
104        send_input(self, ButtonAction::Release, true, Button::assume_released);
105    }
106
107    /// Simulates a button click.
108    #[inline]
109    pub fn click(self) {
110        self.press();
111        self.release();
112    }
113
114    /// Simulates a button click.
115    /// Events generated by this method can be hooked.
116    #[inline]
117    pub fn click_recursive(self) {
118        self.press_recursive();
119        self.release_recursive();
120    }
121
122    /// Returns `true` if the button is pressed.
123    #[inline]
124    pub fn is_pressed(self) -> bool {
125        BUTTON_STATE.is_pressed(self, Ordering::SeqCst)
126    }
127
128    /// Returns `true` if the button is released.
129    #[inline]
130    pub fn is_released(self) -> bool {
131        BUTTON_STATE.is_released(self, Ordering::SeqCst)
132    }
133
134    #[inline]
135    fn assume_pressed(self) {
136        BUTTON_STATE.press(self, Ordering::SeqCst);
137    }
138
139    #[inline]
140    fn assume_released(self) {
141        BUTTON_STATE.release(self, Ordering::SeqCst);
142    }
143}
144
145pub mod mouse {
146    //! Functions for mouse operations
147
148    use super::INPUT;
149
150    /// Gets the position of the mouse cursor. `(x, y)`
151    #[inline]
152    pub fn get_position() -> (i32, i32) {
153        INPUT.cursor_position()
154    }
155
156    /// Moves the mouse cursor to the specified coordinates.
157    #[inline]
158    pub fn move_absolute(x: i32, y: i32) {
159        INPUT.move_absolute(x, y, false);
160    }
161
162    /// Moves the mouse cursor to the specified coordinates.
163    /// Events generated by this method can be hooked.
164    #[inline]
165    pub fn move_absolute_recursive(x: i32, y: i32) {
166        INPUT.move_absolute(x, y, true);
167    }
168
169    /// Moves the mouse cursor a specified distance.
170    #[inline]
171    pub fn move_relative(dx: i32, dy: i32) {
172        INPUT.move_relative(dx, dy, false);
173    }
174
175    /// Moves the mouse cursor a specified distance.
176    /// Events generated by this method can be hooked.
177    #[inline]
178    pub fn move_relative_recursive(dx: i32, dy: i32) {
179        INPUT.move_relative(dx, dy, true);
180    }
181
182    /// Rotates the mouse wheel.
183    #[inline]
184    pub fn rotate(speed: i32) {
185        INPUT.rotate_wheel(speed, false);
186    }
187
188    /// Rotates the mouse wheel.
189    /// Events generated by this method can be hooked.
190    #[inline]
191    pub fn rotate_recursive(speed: i32) {
192        INPUT.rotate_wheel(speed, true);
193    }
194}
195
196static HOOK_HANDLER: Lazy<HookHandler> = Lazy::new(HookHandler::new);
197
198extern "system" fn keyboard_hook_proc(n_code: i32, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
199    match hook::keyboard_hook_proc_inner(&HOOK_HANDLER, n_code, l_param) {
200        NativeEventOperation::Block => LRESULT(1),
201        NativeEventOperation::Dispatch => unsafe {
202            WindowsAndMessaging::CallNextHookEx(HHOOK(0), n_code, w_param, l_param)
203        },
204    }
205}
206
207extern "system" fn mouse_hook_proc(n_code: i32, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
208    match hook::mouse_hook_proc_inner(&HOOK_HANDLER, &INPUT, n_code, w_param, l_param) {
209        NativeEventOperation::Block => LRESULT(1),
210        NativeEventOperation::Dispatch => unsafe {
211            WindowsAndMessaging::CallNextHookEx(HHOOK(0), n_code, w_param, l_param)
212        },
213    }
214}
215
216/// Installs a hook and returns a receiver to receive the generated event.
217///
218/// # Panics
219///
220/// Panics if other hooks are already installed.
221///
222/// # Example
223///
224/// ```no_run
225/// let rx = hookmap_core::install_hook();
226/// ```
227///
228pub fn install_hook() -> EventReceiver {
229    unsafe {
230        // If this is not executed, the GetCursorPos function returns an invalid cursor position.
231        HiDpi::SetProcessDpiAwarenessContext(HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
232    }
233
234    INPUT.update_cursor_position();
235
236    let (tx, rx) = event::channel();
237    HOOK_HANDLER.install(tx, keyboard_hook_proc, mouse_hook_proc);
238
239    rx
240}
241
242/// Uninstalls a hook.
243/// After this call, [`install_hook`] can be called again.
244///
245/// # Panics
246///
247/// Panics if the hook is not installed.
248///
249/// # Example
250///
251/// ```no_run
252/// let rx = hookmap_core::install_hook();
253/// hookmap_core::uninstall_hook();
254///
255/// assert!(rx.recv().is_err());
256///
257/// let rx = hookmap_core::install_hook();
258/// ```
259///
260pub fn uninstall_hook() {
261    HOOK_HANDLER.uninstall();
262}