From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/joystick_hid.c | 60 ++++++++++++++++++++++++++++------- dlls/dinput/tests/joystick8.c | 27 ++++++++-------- 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index fa22d806437..53c055ceb89 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -96,11 +96,30 @@ static CRITICAL_SECTION joystick_cache_cs = { &joystick_cache_cs_debug, -1, 0, 0 static struct list joystick_cache = LIST_INIT( joystick_cache ); +#define JOY_ID_EMPTY 17u +#define JOY_ID_UNASSIGNED ~0u +#define JOY_ID_MASK_INIT 0xffffu +static DWORD joy_ids = JOY_ID_MASK_INIT; +static BOOL is_joy_id_available( DWORD joy_id ) +{ + BOOL available = (joy_id < 16) && !!(joy_ids & (1 << joy_id)); + + if (available) joy_ids &= ~(1 << joy_id); + return available; +} + +static void get_next_available_joy_id( DWORD *joy_id ) +{ + if (BitScanForward( joy_id, joy_ids )) joy_ids &= ~(1 << *joy_id); + else *joy_id = JOY_ID_EMPTY; +} + struct cache_entry { struct list entry; DIDEVICEINSTANCEW instance; WCHAR path[MAX_PATH]; + DWORD joy_id; }; static const char *debugstr_device_instance( const DIDEVICEINSTANCEW *instance ) @@ -109,12 +128,14 @@ static const char *debugstr_device_instance( const DIDEVICEINSTANCEW *instance ) HIWORD(instance->guidProduct.Data1), debugstr_guid( &instance->guidInstance ) ); } -static HRESULT cache_entry_create( const DIDEVICEINSTANCEW *instance, const WCHAR *path, struct cache_entry **out ) +static HRESULT cache_entry_create( const DIDEVICEINSTANCEW *instance, const WCHAR *path, DWORD joy_id, + struct cache_entry **out ) { struct cache_entry *entry; if (!(entry = calloc( 1, sizeof(*entry) ))) return E_OUTOFMEMORY; entry->instance = *instance; + entry->joy_id = joy_id; wcscpy( entry->path, path ); CharLowerW( entry->path ); @@ -122,7 +143,7 @@ static HRESULT cache_entry_create( const DIDEVICEINSTANCEW *instance, const WCHA return S_OK; } -static HRESULT insert_cache_entry( DIDEVICEINSTANCEW *instance, const WCHAR *path ) +static HRESULT insert_cache_entry( DIDEVICEINSTANCEW *instance, DWORD joy_id, const WCHAR *path ) { struct cache_entry *entry, *next; HRESULT hr; @@ -137,13 +158,15 @@ static HRESULT insert_cache_entry( DIDEVICEINSTANCEW *instance, const WCHAR *pat instance->guidInstance = next->instance.guidInstance; TRACE( "Reusing instance %s, path %s\n", debugstr_device_instance( instance ), debugstr_w( path ) ); next->instance = *instance; + if (next->joy_id != JOY_ID_UNASSIGNED && !is_joy_id_available( next->joy_id )) + next->joy_id = JOY_ID_UNASSIGNED; wcscpy( next->path, path ); return S_OK; } } #undef SWAP - if (FAILED(hr = cache_entry_create( instance, path, &entry ))) return hr; + if (FAILED(hr = cache_entry_create( instance, path, joy_id, &entry ))) return hr; TRACE( "Created instance %s, path %s\n", debugstr_device_instance( instance ), debugstr_w( path ) ); list_add_before( &next->entry, &entry->entry ); @@ -167,6 +190,13 @@ static void save_registry_instances( HKEY root ) LOWORD(vidpid), HIWORD(vidpid), index ); RegSetKeyValueW( root, buffer, L"GUID", REG_BINARY, (BYTE *)&entry->instance.guidInstance, sizeof(entry->instance.guidInstance) ); + if (wcslen( entry->path )) + { + if (entry->joy_id == JOY_ID_UNASSIGNED) get_next_available_joy_id( &entry->joy_id ); + if (entry->joy_id == JOY_ID_EMPTY) RegDeleteKeyValueW( root, buffer, L"Joystick Id" ); + else RegSetKeyValueW( root, buffer, L"Joystick Id", REG_BINARY, (BYTE *)&entry->joy_id, + sizeof(entry->joy_id) ); + } TRACE( "Saved %04x:%04x index %lu, guid %s\n", LOWORD(vidpid), HIWORD(vidpid), index, debugstr_guid( &entry->instance.guidInstance ) ); @@ -180,14 +210,16 @@ static void load_registry_product_instances( HKEY root, DWORD vidpid ) for (DWORD i = 0; !RegEnumKeyW( root, i, name, ARRAY_SIZE(name) ); i++) { DIDEVICEINSTANCEW instance = {.guidProduct = dinput_pidvid_guid }; - DWORD len = sizeof(GUID); + DWORD len = sizeof(GUID), joy_id; instance.guidProduct.Data1 = vidpid; if (RegGetValueW( root, name, L"GUID", RRF_RT_REG_BINARY, NULL, &instance.guidInstance, &len )) continue; - insert_cache_entry( &instance, L"" ); + len = sizeof(joy_id); + if (RegGetValueW( root, name, L"Joystick Id", RRF_RT_REG_BINARY, NULL, &joy_id, &len )) joy_id = JOY_ID_UNASSIGNED; + insert_cache_entry( &instance, joy_id, L"" ); - TRACE( "Loaded %04x:%04x index %s, guid %s\n", LOWORD(vidpid), HIWORD(vidpid), debugstr_w( name ), - debugstr_guid( &instance.guidInstance ) ); + TRACE( "Loaded %04x:%04x index %s, guid %s, joy_id %#lx\n", LOWORD(vidpid), HIWORD(vidpid), debugstr_w( name ), + debugstr_guid( &instance.guidInstance ), joy_id ); } } @@ -211,7 +243,7 @@ static void load_registry_instances( HKEY root ) } } -static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *instance, WCHAR *path ) +static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *instance, DWORD *joy_id, WCHAR *path ) { struct cache_entry *entry; HRESULT hr = DI_OK; @@ -235,6 +267,7 @@ static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *inst { *instance = entry->instance; wcscpy( path, entry->path ); + *joy_id = entry->joy_id; } LeaveCriticalSection( &joystick_cache_cs ); @@ -247,6 +280,7 @@ void hid_joystick_cleanup_devices(void) { struct list *ptr; + joy_ids = JOY_ID_MASK_INIT; while ((ptr = list_head( &joystick_cache ))) { struct cache_entry *entry = LIST_ENTRY( ptr, struct cache_entry, entry ); @@ -382,6 +416,7 @@ struct hid_joystick WCHAR device_path[MAX_PATH]; HIDD_ATTRIBUTES attrs; HIDP_CAPS caps; + DWORD joy_id; char *input_report_buf; char *output_report_buf; @@ -1007,7 +1042,7 @@ static HRESULT hid_joystick_get_property( IDirectInputDevice8W *iface, DWORD pro case (DWORD_PTR)DIPROP_JOYSTICKID: { DIPROPDWORD *value = (DIPROPDWORD *)header; - value->dwData = impl->base.instance.guidInstance.Data3; + value->dwData = impl->joy_id; return DI_OK; } case (DWORD_PTR)DIPROP_GUIDANDPATH: @@ -1851,7 +1886,7 @@ HRESULT hid_joystick_refresh_devices(void) HIDP_CAPS caps; if (FAILED(hid_joystick_device_try_open( path, &device, &preparsed, &attrs, &caps, &instance, 0x0800 ))) continue; - hr = insert_cache_entry( &instance, path ); + hr = insert_cache_entry( &instance, JOY_ID_UNASSIGNED, path ); HidD_FreePreparsedData( preparsed ); CloseHandle( device ); } @@ -2244,18 +2279,19 @@ HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDi struct hid_joystick *impl = NULL; WCHAR device_path[MAX_PATH]; USAGE_AND_PAGE *usages; + DWORD size, joy_id = 0; char *buffer; HRESULT hr; - DWORD size; TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out ); *out = NULL; - if (FAILED(hr = get_instance_from_guid( guid, &instance, device_path ))) return hr; + if (FAILED(hr = get_instance_from_guid( guid, &instance, &joy_id, device_path ))) return hr; if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; dinput_device_init( &impl->base, &hid_joystick_vtbl, guid, dinput ); + impl->joy_id = joy_id; impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_joystick.base.crit"); impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND; impl->base.read_event = CreateEventW( NULL, TRUE, FALSE, NULL ); diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index 96109776e52..97ac8c6003d 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -2107,7 +2107,6 @@ static void test_simple_joystick( DWORD version ) prop_dword.dwData = 0xdeadbeef; hr = IDirectInputDevice8_GetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph ); ok( hr == DI_OK, "GetProperty DIPROP_JOYSTICKID returned %#lx\n", hr ); - todo_wine ok( prop_dword.dwData == 0, "got %#lx expected 0\n", prop_dword.dwData ); prop_dword.dwData = 0xdeadbeef; @@ -6587,7 +6586,7 @@ static void test_joystick_id( DWORD version ) ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); check_device_hid_serial( device, descs[i].serial_str ); - check_device_joystick_id( device, i, TRUE ); + check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release( device ); winetest_pop_context(); @@ -6613,7 +6612,7 @@ static void test_joystick_id( DWORD version ) ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); check_device_hid_serial( device, descs[expected_dev_idx].serial_str ); - check_device_joystick_id( device, expected_dev_idx, TRUE ); + check_device_joystick_id( device, expected_dev_idx, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); } @@ -6639,7 +6638,7 @@ static void test_joystick_id( DWORD version ) ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); check_device_hid_serial( device, descs[expected_dev_idx].serial_str ); - check_device_joystick_id( device, i, TRUE ); + check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); @@ -6664,9 +6663,9 @@ static void test_joystick_id( DWORD version ) check_device_hid_serial( device, descs[i].serial_str ); if (i < 2) - check_device_joystick_id( device, i, TRUE ); + check_device_joystick_id( device, i, FALSE ); else - check_device_joystick_id( device, (i == 2) ? 3 : 2, TRUE ); + check_device_joystick_id( device, (i == 2) ? 3 : 2, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); @@ -6686,7 +6685,7 @@ static void test_joystick_id( DWORD version ) ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); check_device_hid_serial( device, descs[i].serial_str ); - check_device_joystick_id( device, i, TRUE ); + check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); @@ -6705,7 +6704,7 @@ static void test_joystick_id( DWORD version ) ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); check_device_hid_serial( device, descs[0].serial_str ); - check_device_joystick_id( device, 0, TRUE ); + check_device_joystick_id( device, 0, FALSE ); IDirectInputDevice8_Release(device); @@ -6722,7 +6721,7 @@ static void test_joystick_id( DWORD version ) ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); check_device_hid_serial( device, descs[i].serial_str ); - check_device_joystick_id( device, !i ? 15 : i, TRUE ); + check_device_joystick_id( device, !i ? 15 : i, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); @@ -6747,7 +6746,7 @@ static void test_joystick_id( DWORD version ) ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); check_device_hid_serial( device, descs[i].serial_str ); - check_device_joystick_id( device, i, TRUE ); + check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); @@ -6774,9 +6773,9 @@ static void test_joystick_id( DWORD version ) check_device_hid_serial( device, descs[i].serial_str ); if (i < 2) - check_device_joystick_id( device, !i ? 1 : 0, TRUE ); + check_device_joystick_id( device, !i ? 1 : 0, FALSE ); else - check_device_joystick_id( device, i, TRUE ); + check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); } @@ -6806,9 +6805,9 @@ static void test_joystick_id( DWORD version ) * Devices 16 and above all get a fixed joystick ID of 17. */ if (i >= 16) - check_device_joystick_id( device, 17, TRUE ); + check_device_joystick_id( device, 17, FALSE ); else - check_device_joystick_id( device, i, TRUE ); + check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release(device); winetest_pop_context(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10755