@@ -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
88use 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
2423use 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
3330pub ( 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 ) ]
3839struct 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
4546unsafe impl Send for HWNDWrapper { }
4647unsafe 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
5758pub ( 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
8384fn 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
124123unsafe 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