From: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/dinput.c | 12 +++-- dlls/dinput/dinput_main.c | 1 + dlls/dinput/dinput_private.h | 2 + dlls/dinput/joystick_hid.c | 99 ++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 3 deletions(-) diff --git a/dlls/dinput/dinput.c b/dlls/dinput/dinput.c index f851e17a244..f3161ca92f3 100644 --- a/dlls/dinput/dinput.c +++ b/dlls/dinput/dinput.c @@ -282,7 +282,12 @@ static HRESULT WINAPI dinput7_CreateDeviceEx( IDirectInput7W *iface, const GUID if (IsEqualGUID( &GUID_SysKeyboard, guid )) hr = keyboard_create_device( impl, guid, &device ); else if (IsEqualGUID( &GUID_SysMouse, guid )) hr = mouse_create_device( impl, guid, &device ); - else hr = hid_joystick_create_device( impl, guid, &device ); + else + { + hr = hid_joystick_create_device( impl, guid, &device ); + if (hr == DIERR_DEVICENOTREG && SUCCEEDED(hr = hid_joystick_refresh_devices())) + hr = hid_joystick_create_device( impl, guid, &device ); + } if (FAILED(hr)) return hr; @@ -372,12 +377,13 @@ static HRESULT WINAPI dinput8_EnumDevices( IDirectInput8W *iface, DWORD type, LP if (device_class == DI8DEVCLASS_ALL || device_class == DI8DEVCLASS_GAMECTRL) { - do + hr = hid_joystick_refresh_devices(); + while (SUCCEEDED(hr)) { hr = hid_joystick_enum_device( type, flags, &instance, impl->dwVersion, i++ ); if (hr == DI_OK && try_enum_device( device_type, callback, &instance, context, flags ) == DIENUM_STOP) return DI_OK; - } while (SUCCEEDED(hr)); + } } return DI_OK; diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index e4bc1f7961d..0c8bcad149c 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -522,6 +522,7 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) break; case DLL_PROCESS_DETACH: if (reserved) break; + hid_joystick_cleanup_devices(); unregister_di_em_win_class(); break; } diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h index 0609b816b3b..0b0caebfaf1 100644 --- a/dlls/dinput/dinput_private.h +++ b/dlls/dinput/dinput_private.h @@ -56,6 +56,8 @@ extern HRESULT keyboard_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW extern HRESULT keyboard_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out ); extern HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, DWORD version, int index ); extern HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out ); +extern HRESULT hid_joystick_refresh_devices(void); +extern void hid_joystick_cleanup_devices(void); struct DevicePlayer { GUID instance_guid; diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index f217e5f3e21..19b15bf471b 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -53,6 +53,72 @@ DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x4 DEFINE_GUID( hid_joystick_guid, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 ); DEFINE_GUID( device_path_guid, 0x00000000, 0x0000, 0x0000, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf8 ); +static CRITICAL_SECTION joystick_cache_cs; +static CRITICAL_SECTION_DEBUG joystick_cache_cs_debug = +{ + 0, 0, &joystick_cache_cs, + { &joystick_cache_cs_debug.ProcessLocksList, &joystick_cache_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": joystick_cache_cs") } +}; +static CRITICAL_SECTION joystick_cache_cs = { &joystick_cache_cs_debug, -1, 0, 0, 0, 0 }; + +static struct list joystick_cache = LIST_INIT( joystick_cache ); + +struct cache_entry +{ + struct list entry; + DIDEVICEINSTANCEW instance; + WCHAR path[MAX_PATH]; +}; + +static const char *debugstr_device_instance( const DIDEVICEINSTANCEW *instance ) +{ + return wine_dbg_sprintf( "product: %04x:%04x, instance: %s", LOWORD(instance->guidProduct.Data1), + HIWORD(instance->guidProduct.Data1), debugstr_guid( &instance->guidInstance ) ); +} + +static HRESULT cache_entry_create( const DIDEVICEINSTANCEW *instance, const WCHAR *path, struct cache_entry **out ) +{ + struct cache_entry *entry; + + if (!(entry = calloc( 1, sizeof(*entry) ))) return E_OUTOFMEMORY; + entry->instance = *instance; + wcscpy( entry->path, path ); + CharLowerW( entry->path ); + + *out = entry; + return S_OK; +} + +static HRESULT insert_cache_entry( DIDEVICEINSTANCEW *instance, const WCHAR *path ) +{ + struct cache_entry *entry, *next; + HRESULT hr; + +#define SWAP(x) MAKELONG( HIWORD(x), LOWORD(x) ) + LIST_FOR_EACH_ENTRY( next, &joystick_cache, struct cache_entry, entry ) + if (SWAP( next->instance.guidProduct.Data1 ) > SWAP( instance->guidProduct.Data1 )) break; +#undef SWAP + + if (FAILED(hr = cache_entry_create( instance, path, &entry ))) return hr; + TRACE( "Created instance %s, path %s\n", debugstr_device_instance( instance ), debugstr_w( path ) ); + list_add_before( &next->entry, &entry->entry ); + + return S_OK; +} + +void hid_joystick_cleanup_devices(void) +{ + struct list *ptr; + + while ((ptr = list_head( &joystick_cache ))) + { + struct cache_entry *entry = LIST_ENTRY( ptr, struct cache_entry, entry ); + list_remove( &entry->entry ); + free( entry ); + } +} + struct pid_control_report { BYTE id; @@ -1620,6 +1686,39 @@ failed: return DIERR_DEVICENOTREG; } +HRESULT hid_joystick_refresh_devices(void) +{ + WCHAR *paths = NULL; + HANDLE device; + HRESULT hr; + GUID hid; + + TRACE( "\n" ); + + HidD_GetHidGuid( &hid ); + + EnterCriticalSection( &joystick_cache_cs ); + hid_joystick_cleanup_devices(); + + hr = get_device_interfaces( hid, NULL, &paths ); + for (WCHAR *path = paths; SUCCEEDED(hr) && *path; path = path + wcslen( path ) + 1) + { + DIDEVICEINSTANCEW instance = {.dwSize = sizeof(instance)}; + HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)}; + PHIDP_PREPARSED_DATA preparsed; + HIDP_CAPS caps; + + if (FAILED(hid_joystick_device_try_open( path, &device, &preparsed, &attrs, &caps, &instance, 0x0800 ))) continue; + hr = insert_cache_entry( &instance, path ); + HidD_FreePreparsedData( preparsed ); + CloseHandle( device ); + } + free( paths ); + + LeaveCriticalSection( &joystick_cache_cs ); + return hr; +} + static HRESULT hid_joystick_device_open( int index, const GUID *guid, DIDEVICEINSTANCEW *instance, WCHAR *device_path, HANDLE *device, PHIDP_PREPARSED_DATA *preparsed, HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DWORD version ) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10719