11use std:: {
2+ collections:: HashSet ,
23 ffi:: c_int,
34 ptr,
4- sync:: { Mutex , Once } ,
5+ sync:: { LazyLock , Mutex , RwLock } ,
56} ;
67
7- use dtor:: dtor;
88use winapi:: {
99 shared:: {
1010 minwindef:: { LPARAM , WPARAM } ,
11- windef:: { HHOOK , POINT } ,
11+ windef:: { HHOOK , HWND , POINT } ,
1212 } ,
1313 um:: {
1414 libloaderapi:: GetModuleHandleA ,
1515 processthreadsapi:: GetCurrentThreadId ,
1616 winuser:: {
17- CallNextHookEx , GetClassNameA , GetWindowLongPtrW , SetWindowsHookExA ,
18- UnhookWindowsHookEx , GWLP_USERDATA , HC_ACTION , MSG , PM_REMOVE , WH_GETMESSAGE , WM_CHAR ,
19- WM_KEYDOWN , WM_KEYUP , WM_SYSCHAR , WM_SYSKEYDOWN , WM_SYSKEYUP , WM_USER ,
17+ CallNextHookEx , SetWindowsHookExA , UnhookWindowsHookEx , HC_ACTION , MSG , PM_REMOVE ,
18+ WH_GETMESSAGE , WM_CHAR , WM_KEYDOWN , WM_KEYUP , WM_SYSCHAR , WM_SYSKEYDOWN , WM_SYSKEYUP ,
19+ WM_USER ,
2020 } ,
2121 } ,
2222} ;
2323
24- use crate :: win:: { wnd_proc, WindowState } ;
24+ use crate :: win:: wnd_proc;
2525
26- static HOOK : Mutex < WinKeyboardHook > = Mutex :: new ( WinKeyboardHook :: new ( ) ) ;
27- static ONCE : Once = Once :: new ( ) ;
26+ static HOOK : Mutex < Option < KeyboardHook > > = Mutex :: new ( None ) ;
27+
28+ // track all windows opened by this instance of baseview
29+ // we use an RwLock here since the vast majority of uses (event interceptions)
30+ // will only need to read from the HashSet
31+ static OPEN_WINDOWS : LazyLock < RwLock < HashSet < HWNDWrapper > > > = LazyLock :: new ( || RwLock :: default ( ) ) ;
32+
33+ pub ( crate ) struct KeyboardHookHandle ( HWNDWrapper ) ;
34+
35+ struct KeyboardHook ( HHOOK ) ;
36+
37+ #[ derive( Hash , PartialEq , Eq , Clone , Copy ) ]
38+ struct HWNDWrapper ( HWND ) ;
39+
40+ // SAFETY: it's a pointer behind a mutex. we'll live
41+ unsafe impl Send for KeyboardHook { }
42+ unsafe impl Sync for KeyboardHook { }
43+
44+ // SAFETY: ditto
45+ unsafe impl Send for HWNDWrapper { }
46+ unsafe impl Sync for HWNDWrapper { }
47+
48+ impl Drop for KeyboardHookHandle {
49+ fn drop ( & mut self ) {
50+ deinit_keyboard_hook ( self . 0 ) ;
51+ }
52+ }
2853
2954// initialize keyboard hook
3055// some DAWs (particularly Ableton) intercept incoming keyboard messages,
3156// but we're naughty so we intercept them right back
32- //
33- // this is invoked by Window::open() since Rust doesn't have runtime static ctors
34- pub ( crate ) fn init_keyboard_hook ( ) {
35- ONCE . call_once ( || {
36- HOOK . lock ( ) . unwrap ( ) . hook = unsafe {
57+ pub ( crate ) fn init_keyboard_hook ( hwnd : HWND ) -> KeyboardHookHandle {
58+ // register hwnd to global window set
59+ OPEN_WINDOWS . write ( ) . unwrap ( ) . insert ( HWNDWrapper ( hwnd) ) ;
60+
61+ let hook = & mut * HOOK . lock ( ) . unwrap ( ) ;
62+
63+ if hook. is_some ( ) {
64+ // keyboard hook already exists, just return handle
65+ KeyboardHookHandle ( HWNDWrapper ( hwnd) )
66+ } else {
67+ // keyboard hook doesn't exist (no windows open before this), create it
68+ let new_hook = KeyboardHook ( unsafe {
3769 SetWindowsHookExA (
3870 WH_GETMESSAGE ,
3971 Some ( keyboard_hook_callback) ,
4072 GetModuleHandleA ( ptr:: null ( ) ) ,
4173 GetCurrentThreadId ( ) ,
4274 )
43- } ;
44- } ) ;
45- }
75+ } ) ;
4676
47- #[ dtor]
48- fn deinit_keyboard_hook ( ) {
49- let hook = HOOK . lock ( ) . unwrap ( ) ;
77+ * hook = Some ( new_hook) ;
5078
51- if !hook. hook . is_null ( ) {
52- unsafe {
53- UnhookWindowsHookEx ( hook. hook ) ;
54- }
79+ KeyboardHookHandle ( HWNDWrapper ( hwnd) )
5580 }
5681}
5782
58- struct WinKeyboardHook {
59- hook : HHOOK ,
60- }
83+ fn deinit_keyboard_hook ( hwnd : HWNDWrapper ) {
84+ let windows = & mut * OPEN_WINDOWS . write ( ) . unwrap ( ) ;
85+
86+ windows. remove ( & hwnd) ;
6187
62- impl WinKeyboardHook {
63- const fn new ( ) -> Self {
64- Self { hook : ptr:: null_mut ( ) }
88+ if windows. is_empty ( ) {
89+ if let Ok ( Some ( hook) ) = HOOK . lock ( ) . as_deref ( ) {
90+ unsafe {
91+ UnhookWindowsHookEx ( hook. 0 ) ;
92+ }
93+ }
6594 }
6695}
6796
68- // SAFETY: it's a pointer behind a mutex. we'll live
69- unsafe impl Send for WinKeyboardHook { }
70- unsafe impl Sync for WinKeyboardHook { }
71-
7297unsafe extern "system" fn keyboard_hook_callback (
7398 n_code : c_int , wparam : WPARAM , lparam : LPARAM ,
7499) -> isize {
@@ -91,7 +116,7 @@ unsafe extern "system" fn keyboard_hook_callback(
91116}
92117
93118// check if `msg` is a keyboard message addressed
94- // to a baseview window, and intercept it if so
119+ // to a window in OPEN_WINDOWS , and intercept it if so
95120unsafe fn offer_message_to_baseview ( msg : * mut MSG ) -> bool {
96121 let msg = & * msg;
97122
@@ -102,21 +127,11 @@ unsafe fn offer_message_to_baseview(msg: *mut MSG) -> bool {
102127 _ => return false ,
103128 }
104129
105- // check if this is a baseview window (gross)
106- let mut classname = [ 0u8 ; 9 ] ;
130+ // check if this is one of our windows. if so, intercept it
131+ if OPEN_WINDOWS . read ( ) . unwrap ( ) . contains ( & HWNDWrapper ( msg. hwnd ) ) {
132+ let _ = wnd_proc ( msg. hwnd , msg. message , msg. wParam , msg. lParam ) ;
107133
108- // SAFETY: It's Probably ASCII Lmao
109- if GetClassNameA ( msg. hwnd , & mut classname as * mut u8 as * mut i8 , 9 ) != 0 {
110- if & classname[ 0 ..8 ] == "Baseview" . as_bytes ( ) {
111- let _ = wnd_proc (
112- msg. hwnd ,
113- msg. message ,
114- msg. wParam ,
115- msg. lParam ,
116- ) ;
117-
118- return true ;
119- }
134+ return true ;
120135 }
121136
122137 false
0 commit comments