Skip to content

Commit 48259cf

Browse files
Combine OPEN_WINDOWS and HOOK into HOOK_STATE RwLock
1 parent ed97533 commit 48259cf

1 file changed

Lines changed: 31 additions & 32 deletions

File tree

src/win/hook.rs

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{
22
collections::HashSet,
33
ffi::c_int,
44
ptr,
5-
sync::{LazyLock, Mutex, RwLock},
5+
sync::{LazyLock, RwLock},
66
};
77

88
use winapi::{
@@ -15,33 +15,34 @@ use winapi::{
1515
processthreadsapi::GetCurrentThreadId,
1616
winuser::{
1717
CallNextHookEx, SetWindowsHookExW, UnhookWindowsHookEx, HC_ACTION, MSG, PM_REMOVE,
18-
WH_GETMESSAGE, WM_CHAR, WM_KEYDOWN, WM_KEYUP, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP,
19-
WM_USER,
18+
WH_GETMESSAGE, WM_CHAR, WM_KEYDOWN, WM_KEYUP, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_USER,
2019
},
2120
},
2221
};
2322

2423
use crate::win::wnd_proc;
2524

26-
static HOOK: Mutex<Option<KeyboardHook>> = Mutex::new(None);
27-
2825
// track all windows opened by this instance of baseview
2926
// we use an RwLock here since the vast majority of uses (event interceptions)
3027
// will only need to read from the HashSet
31-
static OPEN_WINDOWS: LazyLock<RwLock<HashSet<HWNDWrapper>>> = LazyLock::new(|| RwLock::default());
28+
static HOOK_STATE: LazyLock<RwLock<KeyboardHookState>> = LazyLock::new(|| RwLock::default());
3229

3330
pub(crate) struct KeyboardHookHandle(HWNDWrapper);
3431

35-
struct KeyboardHook(HHOOK);
32+
#[derive(Default)]
33+
struct KeyboardHookState {
34+
hook: Option<HHOOK>,
35+
open_windows: HashSet<HWNDWrapper>,
36+
}
3637

3738
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
3839
struct HWNDWrapper(HWND);
3940

40-
// SAFETY: it's a pointer behind a mutex. we'll live
41-
unsafe impl Send for KeyboardHook {}
42-
unsafe impl Sync for KeyboardHook {}
41+
// SAFETY: it's a pointer behind an RwLock. we'll live
42+
unsafe impl Send for KeyboardHookState {}
43+
unsafe impl Sync for KeyboardHookState {}
4344

44-
// SAFETY: ditto
45+
// SAFETY: we never access the underlying HWND ourselves, just use it as a HashSet
4546
unsafe impl Send for HWNDWrapper {}
4647
unsafe impl Sync for HWNDWrapper {}
4748

@@ -55,45 +56,43 @@ impl Drop for KeyboardHookHandle {
5556
// some DAWs (particularly Ableton) intercept incoming keyboard messages,
5657
// but we're naughty so we intercept them right back
5758
pub(crate) fn init_keyboard_hook(hwnd: HWND) -> KeyboardHookHandle {
58-
// register hwnd to global window set
59-
OPEN_WINDOWS.write().unwrap().insert(HWNDWrapper(hwnd));
59+
let state = &mut *HOOK_STATE.write().unwrap();
6060

61-
let hook = &mut *HOOK.lock().unwrap();
61+
// register hwnd to global window set
62+
state.open_windows.insert(HWNDWrapper(hwnd));
6263

63-
if hook.is_some() {
64+
if state.hook.is_some() {
6465
// keyboard hook already exists, just return handle
6566
KeyboardHookHandle(HWNDWrapper(hwnd))
6667
} else {
6768
// keyboard hook doesn't exist (no windows open before this), create it
68-
let new_hook = KeyboardHook(unsafe {
69+
let new_hook = unsafe {
6970
SetWindowsHookExW(
7071
WH_GETMESSAGE,
7172
Some(keyboard_hook_callback),
7273
GetModuleHandleW(ptr::null()),
7374
GetCurrentThreadId(),
7475
)
75-
});
76+
};
7677

77-
*hook = Some(new_hook);
78+
state.hook = Some(new_hook);
7879

7980
KeyboardHookHandle(HWNDWrapper(hwnd))
8081
}
8182
}
8283

8384
fn deinit_keyboard_hook(hwnd: HWNDWrapper) {
84-
let windows = &mut *OPEN_WINDOWS.write().unwrap();
85-
86-
windows.remove(&hwnd);
85+
let state = &mut *HOOK_STATE.write().unwrap();
8786

88-
if windows.is_empty() {
89-
if let Ok(mut hook) = HOOK.lock() {
90-
if let Some(KeyboardHook(hhook)) = &mut *hook {
91-
unsafe {
92-
UnhookWindowsHookEx(*hhook);
93-
}
87+
state.open_windows.remove(&hwnd);
9488

95-
*hook = None;
89+
if state.open_windows.is_empty() {
90+
if let Some(hhook) = state.hook {
91+
unsafe {
92+
UnhookWindowsHookEx(hhook);
9693
}
94+
95+
state.hook = None;
9796
}
9897
}
9998
}
@@ -119,8 +118,8 @@ unsafe extern "system" fn keyboard_hook_callback(
119118
}
120119
}
121120

122-
// check if `msg` is a keyboard message addressed
123-
// to a window in OPEN_WINDOWS, and intercept it if so
121+
// check if `msg` is a keyboard message addressed to a window
122+
// in KeyboardHookState::open_windows, and intercept it if so
124123
unsafe fn offer_message_to_baseview(msg: *mut MSG) -> bool {
125124
let msg = &*msg;
126125

@@ -132,9 +131,9 @@ unsafe fn offer_message_to_baseview(msg: *mut MSG) -> bool {
132131
}
133132

134133
// check if this is one of our windows. if so, intercept it
135-
let Ok(windows) = OPEN_WINDOWS.read() else { return false };
134+
let state = HOOK_STATE.read().unwrap();
136135

137-
if windows.contains(&HWNDWrapper(msg.hwnd)) {
136+
if state.open_windows.contains(&HWNDWrapper(msg.hwnd)) {
138137
let _ = wnd_proc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
139138

140139
return true;

0 commit comments

Comments
 (0)