From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/tests/driver_hid.h | 2 +- dlls/dinput/tests/joystick8.c | 386 +++++++++++++++++++++++++++++++++ 2 files changed, 387 insertions(+), 1 deletion(-) 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/joystick8.c b/dlls/dinput/tests/joystick8.c index 562dc87e62a..f6d3d0e2f5a 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6469,6 +6469,388 @@ 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 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; !dev_key && !RegEnumKeyW( root_key, i, buf, ARRAY_SIZE(buf) ); i++) + { + wcscat( buf, L"\\Calibration" ); + RegCreateKeyExW( root_key, buf, 0, NULL, 0, KEY_READ, NULL, &tmp, NULL ); + for (ULONG j = 0; !dev_key && !RegEnumKeyW( tmp, j, buf, ARRAY_SIZE(buf) ); j++) + { + DWORD len = sizeof(GUID); + GUID guid; + if (RegGetValueW( tmp, buf, L"GUID", RRF_RT_REG_BINARY, NULL, &guid, &len )) continue; + if (memcmp( &guid, guid_instance, sizeof(*guid_instance) )) continue; + RegCreateKeyExW( tmp, buf, 0, NULL, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, NULL, &dev_key, NULL ); + } + RegCloseKey( tmp ); + } + RegCloseKey( root_key ); + return dev_key; +} + +static BOOL set_joystick_id_for_guid_instance( const GUID *guid_instance, DWORD joystick_id ) +{ + HKEY instance_key; + if (!(instance_key = find_registry_key_for_guid_instance( guid_instance ))) return FALSE; + RegSetValueExW( instance_key, L"Joystick Id", 0, REG_BINARY, (const BYTE *)&joystick_id, sizeof(joystick_id) ); + RegCloseKey( instance_key ); + return TRUE; +} + +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 +6882,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/10776