[PATCH 0/4] MR10755: dinput: Add proper support for DIPROP_JOYSTICKID and GUID_Joystick.
This was also attempted in MR !6878. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10755
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/tests/dinput_test.h | 1 + dlls/dinput/tests/driver_hid.h | 2 +- dlls/dinput/tests/hid.c | 41 ++++ dlls/dinput/tests/joystick8.c | 357 ++++++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+), 1 deletion(-) diff --git a/dlls/dinput/tests/dinput_test.h b/dlls/dinput/tests/dinput_test.h index ae3416c4ba7..64325ee2b32 100644 --- a/dlls/dinput/tests/dinput_test.h +++ b/dlls/dinput/tests/dinput_test.h @@ -59,6 +59,7 @@ void bus_device_stop(void); BOOL find_hid_device_path( WCHAR *device_path ); void cleanup_registry_keys(void); +BOOL set_joystick_id_for_guid_instance(const GUID *guid_instance, DWORD joystick_id); #define dinput_test_init() dinput_test_init_( __FILE__, __LINE__ ) void dinput_test_init_( const char *file, int line ); diff --git a/dlls/dinput/tests/driver_hid.h b/dlls/dinput/tests/driver_hid.h index 941e7296719..06cbd77f6dc 100644 --- a/dlls/dinput/tests/driver_hid.h +++ b/dlls/dinput/tests/driver_hid.h @@ -213,7 +213,7 @@ int winetest_start_time, winetest_last_time; int winetest_mute_threshold = 42; static KSPIN_LOCK tls_data_lock; -static struct winetest_thread_data tls_data_pool[128]; +static struct winetest_thread_data tls_data_pool[256]; static HANDLE tls_data_thread[ARRAY_SIZE(tls_data_pool)]; static DWORD tls_data_count; diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c index 7c42ac54f74..86b1640baf4 100644 --- a/dlls/dinput/tests/hid.c +++ b/dlls/dinput/tests/hid.c @@ -3650,6 +3650,47 @@ void cleanup_registry_keys(void) cleanup_registry_tree( HKEY_LOCAL_MACHINE, dinput_path ); } +static HKEY find_registry_key_for_guid_instance(const GUID *guid_instance) +{ + static const WCHAR dinput_path[] = L"System\\CurrentControlSet\\Control\\MediaProperties\\" + "PrivateProperties\\DirectInput"; + HKEY root_key, tmp, dev_key = NULL; + WCHAR buf[MAX_PATH]; + + RegCreateKeyExW( HKEY_CURRENT_USER, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); + for (ULONG i = 0, size = ARRAY_SIZE(buf); !RegEnumKeyExW( root_key, i, buf, &size, NULL, NULL, NULL, NULL ); i++, size = ARRAY_SIZE(buf)) + { + wcscat( buf, L"\\Calibration" ); + RegCreateKeyExW( root_key, buf, 0, NULL, 0, KEY_READ, NULL, &tmp, NULL ); + for (ULONG j = 0, size = ARRAY_SIZE(buf); !RegEnumKeyExW( tmp, j, buf, &size, NULL, NULL, NULL, NULL ); j++, size = ARRAY_SIZE(buf)) + { + GUID guid = { 0 }; + DWORD len; + + len = sizeof(guid); + if (!RegGetValueW( tmp, buf, L"GUID", RRF_RT_REG_BINARY, NULL, &guid, &len) + && !memcmp(&guid, guid_instance, sizeof(*guid_instance))) + RegCreateKeyExW( tmp, buf, 0, NULL, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, NULL, &dev_key, NULL ); + if (dev_key) break; + } + RegCloseKey( tmp ); + tmp = NULL; + if (dev_key) break; + } + RegCloseKey( root_key ); + return dev_key; +} + +BOOL set_joystick_id_for_guid_instance(const GUID *guid_instance, DWORD joystick_id) +{ + HKEY instance_key = find_registry_key_for_guid_instance(guid_instance); + + if (!instance_key) return FALSE; + RegSetValueExW(instance_key, L"Joystick Id", 0, REG_BINARY, (const BYTE *)&joystick_id, sizeof(joystick_id)); + RegCloseKey( instance_key ); + return TRUE; +} + static LRESULT CALLBACK monitor_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { if (msg == WM_DEVICECHANGE) diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index 562dc87e62a..96109776e52 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6469,6 +6469,359 @@ done: winetest_pop_context(); } +#define check_device_joystick_id( a, b, c ) check_device_joystick_id_( __LINE__, a, b, c ) +static void check_device_joystick_id_( unsigned int line, IDirectInputDevice8W *device, DWORD joystick_id, BOOL todo ) +{ + DIPROPDWORD prop_dword = + { + .diph = + { + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + HRESULT hr; + + hr = IDirectInputDevice8_GetProperty( device, DIPROP_JOYSTICKID, &prop_dword.diph ); + ok_(__FILE__, line)( hr == S_OK, "Unexpected hr %#lx.\n", hr ); + todo_wine_if(todo) ok_(__FILE__, line)( prop_dword.dwData == joystick_id, + "Unexpected joystick id %#lx.\n", prop_dword.dwData ); +} + +static void test_joystick_id( DWORD version ) +{ +#include "psh_hid_macros.h" + static const unsigned char report_desc[] = + { + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Application), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Physical), + USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON), + USAGE_MINIMUM(1, 1), + USAGE_MAXIMUM(1, 6), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 8), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + END_COLLECTION, + }; + C_ASSERT( sizeof(report_desc) < MAX_HID_DESCRIPTOR_LEN ); +#include "pop_hid_macros.h" + struct hid_device_desc descs[4] = + { +#define MAKE_ATTR( pid) { sizeof(HID_DEVICE_ATTRIBUTES), LOWORD(EXPECT_VIDPID), pid, 0x0100 } +#define MAKE_DESC( pid, serial ) {.use_report_id = TRUE, .caps = {.InputReportByteLength = 1}, .attributes = MAKE_ATTR( pid ), .serial_str = serial} + MAKE_DESC( 0x0001, L"2&2345&6" ), + MAKE_DESC( 0x0001, L"3&7812&1" ), + MAKE_DESC( 0x0001, L"4&4580&7" ), + MAKE_DESC( 0x0002, L"5&5880&3" ), + }; + struct hid_device_desc descs_bulk[18] = + { + MAKE_DESC( 0x0003, L"0&2345&0" ), + MAKE_DESC( 0x0004, L"1&7812&0" ), + MAKE_DESC( 0x0005, L"2&4580&0" ), + MAKE_DESC( 0x0006, L"3&5880&0" ), + MAKE_DESC( 0x0007, L"4&2345&0" ), + MAKE_DESC( 0x0008, L"5&7812&0" ), + MAKE_DESC( 0x0009, L"6&4580&0" ), + MAKE_DESC( 0x000a, L"7&5880&0" ), + MAKE_DESC( 0x000b, L"8&2345&0" ), + MAKE_DESC( 0x000c, L"9&7812&0" ), + MAKE_DESC( 0x000d, L"a&4580&0" ), + MAKE_DESC( 0x000e, L"b&5880&0" ), + MAKE_DESC( 0x000f, L"c&2345&0" ), + MAKE_DESC( 0x0010, L"d&7812&0" ), + MAKE_DESC( 0x0011, L"e&4580&0" ), + MAKE_DESC( 0x0012, L"f&5880&0" ), + MAKE_DESC( 0x0013, L"f&6880&0" ), + MAKE_DESC( 0x0014, L"f&7880&0" ), +#undef MAKE_DESC +#undef MAKE_ATTR + }; + struct dinput di = {.version = version}; + GUID instances[64], *instances_end; + IDirectInputDevice8W *device; + HRESULT hr; + ULONG ref; + + winetest_push_context( "%#lx", version ); + cleanup_registry_keys(); + + if (FAILED(hr = dinput_create( &di ))) + { + win_skip( "DirectInput8Create returned %#lx\n", hr ); + winetest_pop_context(); + return; + } + + for (UINT i = 0; i < ARRAY_SIZE(descs); i++) + { + descs[i].report_descriptor_len = sizeof(report_desc); + memcpy( descs[i].report_descriptor_buf, report_desc, sizeof(report_desc) ); + } + + if (!hid_device_start( descs, ARRAY_SIZE(descs) )) goto done; + + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 4, "Unexpected count %Iu.\n", instances_end - instances ); + + /* + * Enumerate all devices, joystick ID is initially assigned based on + * enumeration order. + */ + for (UINT i = 0; i < instances_end - instances; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + 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 ); + + IDirectInputDevice8_Release( device ); + winetest_pop_context(); + } + + /* + * Joystick ID is associated with a particular guidInstance once created. + * If we stop device 2, device 3 will still have Joystick ID 3, even if + * that no longer matches enumeration order. + */ + hid_device_stop( &descs[2], 1 ); + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 3, "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + const unsigned int expected_dev_idx = (i >= 2) ? i + 1 : i; + + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + 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 ); + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Delete the existing registry keys, this will result in a new set of + * guidInstance values (and joystick IDs) being generated on the next call to + * EnumDevices(). + */ + cleanup_registry_keys(); + + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 3, "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + const unsigned int expected_dev_idx = (i >= 2) ? i + 1 : i; + + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + 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 ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Restart device 2. Due to enumeration order this should in theory get + * joystick ID 2, but because this was already taken by device 3, it will + * get joystick ID 3 instead. + */ + hid_device_start( &descs[2], 1 ); + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 4, "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + check_device_hid_serial( device, descs[i].serial_str ); + if (i < 2) + check_device_joystick_id( device, i, TRUE ); + else + check_device_joystick_id( device, (i == 2) ? 3 : 2, TRUE ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* Reset things back to normal. */ + cleanup_registry_keys(); + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 4, "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + 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 ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Joystick Id values are pulled from the registry by EnumDevices(). + * Set device 0 to have joystick ID 15, which is the highest possible + * value. + */ + if (!set_joystick_id_for_guid_instance(&instances[0], 15)) + trace("Failed to set joystick id for device instance.\n"); + + /* No call to EnumDevices(), no joystick ID change. */ + hr = dinput_create_device( &di, &instances[0], &device ); + 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 ); + + IDirectInputDevice8_Release(device); + + /* Post EnumDevices() call, now device 0 has joystick ID 15. */ + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 4, "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + 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 ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Attempting to set the Joystick Id value to anything above 15 results in it being + * reset to 0. + */ + if (!set_joystick_id_for_guid_instance(&instances[0], 18)) + trace("Failed to set joystick id for device instance.\n"); + + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 4, "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + 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 ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Set device 0 to have joystick ID 1. This will conflict with device 1, + * and as a result device 1 gets Joystick ID 0. + */ + if (!set_joystick_id_for_guid_instance(&instances[0], 1)) + trace("Failed to set joystick id for device instance.\n"); + + /* Post EnumDevices() call, now device 0 has joystick ID 1. */ + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + 4, "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + check_device_hid_serial( device, descs[i].serial_str ); + if (i < 2) + check_device_joystick_id( device, !i ? 1 : 0, TRUE ); + else + check_device_joystick_id( device, i, TRUE ); + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + hid_device_stop( descs, ARRAY_SIZE(descs) ); + + for (UINT i = 0; i < ARRAY_SIZE(descs_bulk); i++) + { + descs_bulk[i].report_descriptor_len = sizeof(report_desc); + memcpy( descs_bulk[i].report_descriptor_buf, report_desc, sizeof(report_desc) ); + } + + if (!hid_device_start( descs_bulk, ARRAY_SIZE(descs_bulk) )) goto done; + instances_end = instances; + hr = dinput_enum_devices( &di, find_test_device_instances, &instances_end ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( instances_end == instances + ARRAY_SIZE(descs_bulk), "Unexpected count %Iu.\n", instances_end - instances ); + for (UINT i = 0; i < instances_end - instances; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_create_device( &di, &instances[i], &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + check_device_hid_serial( device, descs_bulk[i].serial_str ); + /* + * Devices 16 and above all get a fixed joystick ID of 17. + */ + if (i >= 16) + check_device_joystick_id( device, 17, TRUE ); + else + check_device_joystick_id( device, i, TRUE ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + hid_device_stop( descs_bulk, ARRAY_SIZE(descs_bulk) ); +done: + ref = dinput_release( &di ); + ok( ref == 0, "Release returned %ld\n", ref ); + cleanup_registry_keys(); + winetest_pop_context(); +} + START_TEST( joystick8 ) { char **argv; @@ -6500,6 +6853,10 @@ START_TEST( joystick8 ) test_joystick_instance_guid( 0x700 ); test_joystick_instance_guid( 0x800 ); + test_joystick_id( 0x500 ); + test_joystick_id( 0x700 ); + test_joystick_id( 0x800 ); + test_many_axes_joystick(); test_driving_wheel_axes(); test_rawinput( argv ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10755
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
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/tests/joystick8.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index 97ac8c6003d..d52a53792d6 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6810,6 +6810,29 @@ static void test_joystick_id( DWORD version ) check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release(device); + + /* + * Undocumented behavior, guessed at by seeing GUID_SysMouseEm/GUID_SysMouseEm2 + * where their Data1 values are 0x6f1d2b80/0x6f1d2b81 respectively. + * GUID_Joystick has a Data1 value of 0x6f1d2b70, which gives us 16 + * possible values before hitting GUID_SysMouseEm. This aligns with + * the maximum possible unique joystick ID being 15. + */ + if (i <= 15) + { + GUID joystick_id_guid = GUID_Joystick; + + joystick_id_guid.Data1 += i; + hr = dinput_create_device( &di, &joystick_id_guid, &device ); + todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + if (device) + { + check_device_hid_serial( device, descs_bulk[i].serial_str ); + check_device_joystick_id( device, i, TRUE ); + IDirectInputDevice8_Release(device); + } + } + winetest_pop_context(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10755
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/joystick_hid.c | 10 ++++++++++ dlls/dinput/tests/joystick8.c | 13 ++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 53c055ceb89..8eedc9b57b9 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -245,8 +245,10 @@ static void load_registry_instances( HKEY root ) static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *instance, DWORD *joy_id, WCHAR *path ) { + BOOL is_guid_joystick = FALSE; struct cache_entry *entry; HRESULT hr = DI_OK; + GUID tmp = *guid; if (!memcmp( device_path_guid.Data4, guid->Data4, sizeof(device_path_guid.Data4) )) { @@ -254,11 +256,19 @@ static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *inst return S_OK; } + tmp.Data1 &= 0xfffffff0; + is_guid_joystick = IsEqualGUID( &tmp, &GUID_Joystick); + EnterCriticalSection( &joystick_cache_cs ); LIST_FOR_EACH_ENTRY( entry, &joystick_cache, struct cache_entry, entry ) { if (!*entry->path) continue; + if (is_guid_joystick) + { + if (entry->joy_id == (guid->Data1 & 0xf)) break; + continue; + } if (IsEqualGUID( &entry->instance.guidProduct, guid )) break; if (IsEqualGUID( &entry->instance.guidInstance, guid )) break; } diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index d52a53792d6..48a7e6ebc0d 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6824,13 +6824,12 @@ static void test_joystick_id( DWORD version ) joystick_id_guid.Data1 += i; hr = dinput_create_device( &di, &joystick_id_guid, &device ); - todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); - if (device) - { - check_device_hid_serial( device, descs_bulk[i].serial_str ); - check_device_joystick_id( device, i, TRUE ); - IDirectInputDevice8_Release(device); - } + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + check_device_hid_serial( device, descs_bulk[i].serial_str ); + check_device_joystick_id( device, i, FALSE ); + + IDirectInputDevice8_Release(device); } winetest_pop_context(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10755
Rémi Bernon (@rbernon) commented about dlls/dinput/joystick_hid.c:
+ tmp.Data1 &= 0xfffffff0; + is_guid_joystick = IsEqualGUID( &tmp, &GUID_Joystick); + EnterCriticalSection( &joystick_cache_cs );
LIST_FOR_EACH_ENTRY( entry, &joystick_cache, struct cache_entry, entry ) { if (!*entry->path) continue; + if (is_guid_joystick) + { + if (entry->joy_id == (guid->Data1 & 0xf)) break; + continue; + } if (IsEqualGUID( &entry->instance.guidProduct, guid )) break; if (IsEqualGUID( &entry->instance.guidInstance, guid )) break;
Instead of an integer joy_id, we could keep a full GUID guid_joystick, and compare it like the other here. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10755#note_137762
Rémi Bernon (@rbernon) commented about dlls/dinput/joystick_hid.c:
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) ); + }
Seems weird to do that here. Would be nicer to do that in `insert_cache_entry`/`cache_entry_create`? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10755#note_137763
Rémi Bernon (@rbernon) commented about dlls/dinput/tests/joystick8.c:
+ hr = dinput_create_device( &di, &instances[i], &device ); + 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 ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Attempting to set the Joystick Id value to anything above 15 results in it being + * reset to 0. + */ + if (!set_joystick_id_for_guid_instance(&instances[0], 18)) + trace("Failed to set joystick id for device instance.\n"); Is this because 0 is available, or can multiple joystick have ID 0?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10755#note_137764
Rémi Bernon (@rbernon) commented about dlls/dinput/joystick_hid.c:
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; +}
It looks misleading to update the available IDs when the function name seem to only indicate a check. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10755#note_137765
participants (3)
-
Connor McAdams -
Connor McAdams (@cmcadams) -
Rémi Bernon (@rbernon)