From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/dinput_main.c | 64 ++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 31 deletions(-)
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index 48aa53c63cb..c9bbb34e554 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -52,6 +52,8 @@ struct input_thread_state { UINT events_count; UINT devices_count; + HHOOK mouse_ll_hook; + HHOOK keyboard_ll_hook; struct dinput_device *devices[INPUT_THREAD_MAX_DEVICES]; HANDLE events[INPUT_THREAD_MAX_DEVICES]; }; @@ -167,7 +169,7 @@ static void unregister_di_em_win_class(void) WARN( "Unable to unregister message window class\n" ); }
-static LRESULT CALLBACK LL_hook_proc( int code, WPARAM wparam, LPARAM lparam ) +static LRESULT CALLBACK input_thread_ll_hook_proc( int code, WPARAM wparam, LPARAM lparam ) { struct dinput_device *impl; int skip = 0; @@ -243,8 +245,8 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam
static void input_thread_update_device_list( struct input_thread_state *state ) { + UINT count = 0, keyboard_count, mouse_count; struct dinput_device *device; - UINT count = 0;
EnterCriticalSection( &dinput_hook_crit ); LIST_FOR_EACH_ENTRY( device, &acquired_device_list, struct dinput_device, entry ) @@ -256,14 +258,32 @@ static void input_thread_update_device_list( struct input_thread_state *state ) } state->events_count = count; state->devices_count = count; + + keyboard_count = list_count( &acquired_keyboard_list ); + mouse_count = list_count( &acquired_mouse_list ); LeaveCriticalSection( &dinput_hook_crit ); + + if (keyboard_count && !state->keyboard_ll_hook) + state->keyboard_ll_hook = SetWindowsHookExW( WH_KEYBOARD_LL, input_thread_ll_hook_proc, DINPUT_instance, 0 ); + else if (!keyboard_count && state->keyboard_ll_hook) + { + UnhookWindowsHookEx( state->keyboard_ll_hook ); + state->keyboard_ll_hook = NULL; + } + + if (mouse_count && !state->mouse_ll_hook) + state->mouse_ll_hook = SetWindowsHookExW( WH_MOUSE_LL, input_thread_ll_hook_proc, DINPUT_instance, 0 ); + else if (!mouse_count && state->mouse_ll_hook) + { + UnhookWindowsHookEx( state->mouse_ll_hook ); + state->mouse_ll_hook = NULL; + } }
static DWORD WINAPI dinput_thread_proc( void *params ) { HANDLE finished_event, start_event = params; struct input_thread_state state = {0}; - static HHOOK kbd_hook, mouse_hook; struct dinput_device *device; DWORD ret; MSG msg; @@ -276,8 +296,6 @@ static DWORD WINAPI dinput_thread_proc( void *params )
while ((ret = MsgWaitForMultipleObjectsEx( state.events_count, state.events, INFINITE, QS_ALLINPUT, 0 )) <= state.events_count) { - UINT kbd_cnt = 0, mice_cnt = 0; - if (ret < state.events_count) { if ((device = state.devices[ret]) && FAILED( device->vtbl->read( &device->IDirectInputDevice8W_iface ) )) @@ -286,6 +304,11 @@ static DWORD WINAPI dinput_thread_proc( void *params ) dinput_device_internal_unacquire( &device->IDirectInputDevice8W_iface ); LeaveCriticalSection( &dinput_hook_crit ); device->status = STATUS_UNPLUGGED; + + state.events[ret] = state.events[--state.events_count]; + state.devices[ret] = state.devices[state.events_count]; + state.devices[state.events_count] = state.devices[--state.devices_count]; + dinput_device_internal_release( device ); } }
@@ -304,38 +327,17 @@ static DWORD WINAPI dinput_thread_proc( void *params )
if (!msg.wParam) { - if (kbd_hook) UnhookWindowsHookEx( kbd_hook ); - if (mouse_hook) UnhookWindowsHookEx( mouse_hook ); - kbd_hook = mouse_hook = NULL; + if (state.keyboard_ll_hook) UnhookWindowsHookEx( state.keyboard_ll_hook ); + if (state.mouse_ll_hook) UnhookWindowsHookEx( state.mouse_ll_hook ); + state.keyboard_ll_hook = state.mouse_ll_hook = NULL; goto done; }
- EnterCriticalSection( &dinput_hook_crit ); - kbd_cnt = list_count( &acquired_keyboard_list ); - mice_cnt = list_count( &acquired_mouse_list ); - LeaveCriticalSection( &dinput_hook_crit ); - - if (kbd_cnt && !kbd_hook) - kbd_hook = SetWindowsHookExW( WH_KEYBOARD_LL, LL_hook_proc, DINPUT_instance, 0 ); - else if (!kbd_cnt && kbd_hook) - { - UnhookWindowsHookEx( kbd_hook ); - kbd_hook = NULL; - } - - if (mice_cnt && !mouse_hook) - mouse_hook = SetWindowsHookExW( WH_MOUSE_LL, LL_hook_proc, DINPUT_instance, 0 ); - else if (!mice_cnt && mouse_hook) - { - UnhookWindowsHookEx( mouse_hook ); - mouse_hook = NULL; - } + while (state.devices_count--) dinput_device_internal_release( state.devices[state.devices_count] ); + input_thread_update_device_list( &state );
SetEvent(finished_event); } - - while (state.devices_count--) dinput_device_internal_release( state.devices[state.devices_count] ); - input_thread_update_device_list( &state ); }
if (ret != state.events_count) ERR("Unexpected termination, ret %#lx\n", ret);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/dinput_main.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index c9bbb34e554..2b7528a20ea 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -107,7 +107,7 @@ void dinput_hooks_unacquire_device( IDirectInputDevice8W *iface ) LeaveCriticalSection( &dinput_hook_crit ); }
-static void dinput_device_internal_unacquire( IDirectInputDevice8W *iface ) +static void dinput_device_internal_unacquire( IDirectInputDevice8W *iface, DWORD status ) { struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface );
@@ -117,7 +117,7 @@ static void dinput_device_internal_unacquire( IDirectInputDevice8W *iface ) if (impl->status == STATUS_ACQUIRED) { impl->vtbl->unacquire( iface ); - impl->status = STATUS_UNACQUIRED; + impl->status = status; list_remove( &impl->entry ); } LeaveCriticalSection( &impl->crit ); @@ -211,7 +211,7 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam if (msg->hwnd == impl->win && msg->hwnd != foreground) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); - dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface ); + dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); } } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_mouse_list, struct dinput_device, entry ) @@ -219,7 +219,7 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam if (msg->hwnd == impl->win && msg->hwnd != foreground) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); - dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface ); + dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); } } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_rawmouse_list, struct dinput_device, entry ) @@ -227,7 +227,7 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam if (msg->hwnd == impl->win && msg->hwnd != foreground) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); - dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface ); + dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); } } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_keyboard_list, struct dinput_device, entry ) @@ -235,7 +235,7 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam if (msg->hwnd == impl->win && msg->hwnd != foreground) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); - dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface ); + dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); } } LeaveCriticalSection( &dinput_hook_crit ); @@ -301,9 +301,8 @@ static DWORD WINAPI dinput_thread_proc( void *params ) if ((device = state.devices[ret]) && FAILED( device->vtbl->read( &device->IDirectInputDevice8W_iface ) )) { EnterCriticalSection( &dinput_hook_crit ); - dinput_device_internal_unacquire( &device->IDirectInputDevice8W_iface ); + dinput_device_internal_unacquire( &device->IDirectInputDevice8W_iface, STATUS_UNPLUGGED ); LeaveCriticalSection( &dinput_hook_crit ); - device->status = STATUS_UNPLUGGED;
state.events[ret] = state.events[--state.events_count]; state.devices[ret] = state.devices[state.events_count];
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/dinput_main.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-)
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index 2b7528a20ea..1334bf28431 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -282,9 +282,9 @@ static void input_thread_update_device_list( struct input_thread_state *state )
static DWORD WINAPI dinput_thread_proc( void *params ) { - HANDLE finished_event, start_event = params; struct input_thread_state state = {0}; struct dinput_device *device; + HANDLE start_event = params; DWORD ret; MSG msg;
@@ -320,22 +320,13 @@ static DWORD WINAPI dinput_thread_proc( void *params ) continue; }
- finished_event = (HANDLE)msg.lParam; - TRACE( "Processing hook change notification wparam %#Ix, lparam %#Ix.\n", msg.wParam, msg.lParam );
- if (!msg.wParam) - { - if (state.keyboard_ll_hook) UnhookWindowsHookEx( state.keyboard_ll_hook ); - if (state.mouse_ll_hook) UnhookWindowsHookEx( state.mouse_ll_hook ); - state.keyboard_ll_hook = state.mouse_ll_hook = NULL; - goto done; - } + if (!msg.wParam) goto done;
while (state.devices_count--) dinput_device_internal_release( state.devices[state.devices_count] ); input_thread_update_device_list( &state ); - - SetEvent(finished_event); + SetEvent( (HANDLE)msg.lParam ); } }
@@ -343,6 +334,8 @@ static DWORD WINAPI dinput_thread_proc( void *params )
done: while (state.devices_count--) dinput_device_internal_release( state.devices[state.devices_count] ); + if (state.keyboard_ll_hook) UnhookWindowsHookEx( state.keyboard_ll_hook ); + if (state.mouse_ll_hook) UnhookWindowsHookEx( state.mouse_ll_hook ); DestroyWindow( di_em_win ); di_em_win = NULL; return 0;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/dinput_main.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index 1334bf28431..21b77b32cac 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -193,22 +193,15 @@ static LRESULT CALLBACK input_thread_ll_hook_proc( int code, WPARAM wparam, LPAR return skip ? 1 : CallNextHookEx( 0, code, wparam, lparam ); }
-static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam ) +static void dinput_unacquire_window_devices( HWND window ) { struct dinput_device *impl, *next; - CWPSTRUCT *msg = (CWPSTRUCT *)lparam; - HWND foreground; - - if (code != HC_ACTION || (msg->message != WM_KILLFOCUS && - msg->message != WM_ACTIVATEAPP && msg->message != WM_ACTIVATE)) - return CallNextHookEx( 0, code, wparam, lparam ); - - foreground = GetForegroundWindow();
EnterCriticalSection( &dinput_hook_crit ); + LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_device_list, struct dinput_device, entry ) { - if (msg->hwnd == impl->win && msg->hwnd != foreground) + if (window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); @@ -216,7 +209,7 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_mouse_list, struct dinput_device, entry ) { - if (msg->hwnd == impl->win && msg->hwnd != foreground) + if (window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); @@ -224,7 +217,7 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_rawmouse_list, struct dinput_device, entry ) { - if (msg->hwnd == impl->win && msg->hwnd != foreground) + if (window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); @@ -232,13 +225,25 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_keyboard_list, struct dinput_device, entry ) { - if (msg->hwnd == impl->win && msg->hwnd != foreground) + if (window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); } } + LeaveCriticalSection( &dinput_hook_crit ); +} + +static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam ) +{ + CWPSTRUCT *msg = (CWPSTRUCT *)lparam; + + if (code != HC_ACTION || (msg->message != WM_KILLFOCUS && + msg->message != WM_ACTIVATEAPP && msg->message != WM_ACTIVATE)) + return CallNextHookEx( 0, code, wparam, lparam ); + + if (msg->hwnd != GetForegroundWindow()) dinput_unacquire_window_devices( msg->hwnd );
return CallNextHookEx( 0, code, wparam, lparam ); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dinput/dinput_main.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index 21b77b32cac..aceed841df5 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -201,7 +201,7 @@ static void dinput_unacquire_window_devices( HWND window )
LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_device_list, struct dinput_device, entry ) { - if (window == impl->win) + if (!window || window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); @@ -209,7 +209,7 @@ static void dinput_unacquire_window_devices( HWND window ) } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_mouse_list, struct dinput_device, entry ) { - if (window == impl->win) + if (!window || window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); @@ -217,7 +217,7 @@ static void dinput_unacquire_window_devices( HWND window ) } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_rawmouse_list, struct dinput_device, entry ) { - if (window == impl->win) + if (!window || window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); @@ -225,7 +225,7 @@ static void dinput_unacquire_window_devices( HWND window ) } LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_keyboard_list, struct dinput_device, entry ) { - if (window == impl->win) + if (!window || window == impl->win) { TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); @@ -335,7 +335,8 @@ static DWORD WINAPI dinput_thread_proc( void *params ) } }
- if (ret != state.events_count) ERR("Unexpected termination, ret %#lx\n", ret); + ERR( "Unexpected termination, ret %#lx\n", ret ); + dinput_unacquire_window_devices( 0 );
done: while (state.devices_count--) dinput_device_internal_release( state.devices[state.devices_count] );
From: Rémi Bernon rbernon@codeweavers.com
And start the input thread when first user is created, then stop it when last user is destroyed.
The thread will not need to enter the hook critical section on stop, as no public reference are held and devices are already unacquired. --- dlls/dinput/device.c | 3 +++ dlls/dinput/dinput.c | 3 +++ dlls/dinput/dinput_main.c | 49 +++++++++++++++++++----------------- dlls/dinput/dinput_private.h | 3 +++ 4 files changed, 35 insertions(+), 23 deletions(-)
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index 7f97067bd81..04788b22f9a 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -734,6 +734,7 @@ static ULONG WINAPI dinput_device_Release( IDirectInputDevice8W *iface ) if (!ref) { IDirectInputDevice_Unacquire( iface ); + input_thread_remove_user(); dinput_device_internal_release( impl ); }
@@ -2127,6 +2128,8 @@ void dinput_device_init( struct dinput_device *device, const struct dinput_devic InitializeCriticalSection( &device->crit ); dinput_internal_addref( (device->dinput = dinput) ); device->vtbl = vtbl; + + input_thread_add_user(); }
static const GUID *object_instance_guid( const DIDEVICEOBJECTINSTANCEW *instance ) diff --git a/dlls/dinput/dinput.c b/dlls/dinput/dinput.c index 7cf06364dc3..db367e1f1ba 100644 --- a/dlls/dinput/dinput.c +++ b/dlls/dinput/dinput.c @@ -146,6 +146,7 @@ static ULONG WINAPI dinput7_Release( IDirectInput7W *iface )
if (!ref) { + input_thread_remove_user(); dinput_internal_release( impl ); }
@@ -819,6 +820,8 @@ static HRESULT dinput_create( IUnknown **out ) #else *out = (IUnknown *)&impl->IDirectInput8W_iface; #endif + + input_thread_add_user(); return DI_OK; }
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index aceed841df5..f264c367305 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -69,6 +69,7 @@ static HWND di_em_win;
static HANDLE dinput_thread; static DWORD dinput_thread_id; +static UINT input_thread_user_count;
static CRITICAL_SECTION dinput_hook_crit; static CRITICAL_SECTION_DEBUG dinput_critsect_debug = @@ -347,34 +348,39 @@ done: return 0; }
-static BOOL WINAPI dinput_thread_start_once( INIT_ONCE *once, void *param, void **context ) +void input_thread_add_user(void) { - HANDLE start_event; - - start_event = CreateEventW( NULL, FALSE, FALSE, NULL ); - if (!start_event) ERR( "failed to create start event, error %lu\n", GetLastError() ); + EnterCriticalSection( &dinput_hook_crit ); + if (!input_thread_user_count++) + { + HANDLE start_event;
- dinput_thread = CreateThread( NULL, 0, dinput_thread_proc, start_event, 0, &dinput_thread_id ); - if (!dinput_thread) ERR( "failed to create internal thread, error %lu\n", GetLastError() ); + TRACE( "Starting input thread.\n" );
- WaitForSingleObject( start_event, INFINITE ); - CloseHandle( start_event ); + if (!(start_event = CreateEventW( NULL, FALSE, FALSE, NULL ))) + ERR( "Failed to create start event, error %lu\n", GetLastError() ); + else if (!(dinput_thread = CreateThread( NULL, 0, dinput_thread_proc, start_event, 0, &dinput_thread_id ))) + ERR( "Failed to create internal thread, error %lu\n", GetLastError() ); + else + WaitForSingleObject( start_event, INFINITE );
- return TRUE; + CloseHandle( start_event ); + } + LeaveCriticalSection( &dinput_hook_crit ); }
-static void dinput_thread_start(void) +void input_thread_remove_user(void) { - static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; - InitOnceExecuteOnce( &init_once, dinput_thread_start_once, NULL, NULL ); -} + EnterCriticalSection( &dinput_hook_crit ); + if (!--input_thread_user_count) + { + TRACE( "Stopping input thread.\n" );
-static void dinput_thread_stop(void) -{ - PostThreadMessageW( dinput_thread_id, WM_USER + 0x10, 0, 0 ); - if (WaitForSingleObject( dinput_thread, 500 ) == WAIT_TIMEOUT) - WARN("Timeout while waiting for internal thread\n"); - CloseHandle( dinput_thread ); + PostThreadMessageW( dinput_thread_id, WM_USER + 0x10, 0, 0 ); + WaitForSingleObject( dinput_thread, INFINITE ); + CloseHandle( dinput_thread ); + } + LeaveCriticalSection( &dinput_hook_crit ); }
void check_dinput_hooks( IDirectInputDevice8W *iface, BOOL acquired ) @@ -384,8 +390,6 @@ void check_dinput_hooks( IDirectInputDevice8W *iface, BOOL acquired ) struct dinput_device *impl = impl_from_IDirectInputDevice8W( iface ); HANDLE hook_change_finished_event = NULL;
- dinput_thread_start(); - EnterCriticalSection(&dinput_hook_crit);
if (impl->dwCoopLevel & DISCL_FOREGROUND) @@ -468,7 +472,6 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) break; case DLL_PROCESS_DETACH: if (reserved) break; - dinput_thread_stop(); unregister_di_em_win_class(); break; } diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h index fb9558716a3..f10d5120c89 100644 --- a/dlls/dinput/dinput_private.h +++ b/dlls/dinput/dinput_private.h @@ -63,6 +63,9 @@ struct DevicePlayer { struct list entry; };
+extern void input_thread_add_user(void); +extern void input_thread_remove_user(void); + extern void dinput_hooks_acquire_device( IDirectInputDevice8W *iface ); extern void dinput_hooks_unacquire_device( IDirectInputDevice8W *iface ); extern int dinput_mouse_hook( IDirectInputDevice8W *iface, WPARAM wparam, LPARAM lparam );