[PATCH 0/5] MR10776: dinput: Add proper support for DIPROP_JOYSTICKID and GUID_Joystick.
Supersedes https://gitlab.winehq.org/wine/wine/-/merge_requests/10755 with some style fixes and simplified joystick ID allocation, moved to a dedicated function. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10776
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/dinput/dinput.c | 10 ++++++++-- dlls/dinput/keyboard.c | 1 - dlls/dinput/mouse.c | 1 - dlls/dinput/tests/device8.c | 18 ++++++++++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/dlls/dinput/dinput.c b/dlls/dinput/dinput.c index f3161ca92f3..4a28b4240fd 100644 --- a/dlls/dinput/dinput.c +++ b/dlls/dinput/dinput.c @@ -280,8 +280,14 @@ static HRESULT WINAPI dinput7_CreateDeviceEx( IDirectInput7W *iface, const GUID if (!guid) return E_POINTER; if (!impl->dwVersion) return DIERR_NOTINITIALIZED; - 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 ); + if (IsEqualGUID( &GUID_SysKeyboard, guid ) || + IsEqualGUID( &GUID_SysKeyboardEm, guid ) || + IsEqualGUID( &GUID_SysKeyboardEm2, guid )) + hr = keyboard_create_device( impl, guid, &device ); + else if (IsEqualGUID( &GUID_SysMouse, guid ) || + IsEqualGUID( &GUID_SysMouseEm, guid ) || + IsEqualGUID( &GUID_SysMouseEm2, guid )) + hr = mouse_create_device( impl, guid, &device ); else { hr = hid_joystick_create_device( impl, guid, &device ); diff --git a/dlls/dinput/keyboard.c b/dlls/dinput/keyboard.c index 8ec9dd47150..5f3710ee7c9 100644 --- a/dlls/dinput/keyboard.c +++ b/dlls/dinput/keyboard.c @@ -196,7 +196,6 @@ HRESULT keyboard_create_device( struct dinput *dinput, const GUID *guid, IDirect TRACE( "dinput %p, guid %s, out %p.\n", dinput, debugstr_guid( guid ), out ); *out = NULL; - if (!IsEqualGUID( &GUID_SysKeyboard, guid )) return DIERR_DEVICENOTREG; if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; dinput_device_init( &impl->base, &keyboard_vtbl, guid, dinput ); diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index eec022974b5..5c0bdbcf768 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -519,7 +519,6 @@ HRESULT mouse_create_device( struct dinput *dinput, const GUID *guid, IDirectInp TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out ); *out = NULL; - if (!IsEqualGUID( &GUID_SysMouse, guid )) return DIERR_DEVICENOTREG; if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; dinput_device_init( &impl->base, &mouse_vtbl, guid, dinput ); diff --git a/dlls/dinput/tests/device8.c b/dlls/dinput/tests/device8.c index ceed0573510..37ed9428561 100644 --- a/dlls/dinput/tests/device8.c +++ b/dlls/dinput/tests/device8.c @@ -1171,11 +1171,11 @@ static void test_sys_mouse( DWORD version ) .dwHow = DIPH_DEVICE, }, }; + IDirectInputDevice8W *device, *tmp_device; DIDEVICEOBJECTINSTANCEW objinst = {0}; DIDEVICEOBJECTDATA objdata = {0}; DIDEVICEINSTANCEW devinst = {0}; BOOL old_localized = localized; - IDirectInputDevice8W *device; HWND hwnd, tmp_hwnd, child; DIDEVCAPS caps = {0}; DIMOUSESTATE state; @@ -1201,6 +1201,13 @@ static void test_sys_mouse( DWORD version ) ok( IsEqualGUID( &guid, &GUID_SysMouseEm ), "got %s expected %s\n", debugstr_guid( &guid ), debugstr_guid( &GUID_SysMouseEm ) ); + hr = create_dinput_device( version, &GUID_SysMouseEm, &tmp_device ); + ok( hr == DI_OK, "Initialize returned %#lx\n", hr ); + if (hr == DI_OK) IDirectInputDevice8_Release( tmp_device ); + hr = create_dinput_device( version, &GUID_SysMouseEm2, &tmp_device ); + ok( hr == DI_OK, "Initialize returned %#lx\n", hr ); + if (hr == DI_OK) IDirectInputDevice8_Release( tmp_device ); + hr = IDirectInputDevice8_Initialize( device, instance, version, &GUID_SysMouse ); ok( hr == DI_OK, "Initialize returned %#lx\n", hr ); @@ -2671,10 +2678,10 @@ static void test_sys_keyboard( DWORD version ) sizeof(key_state), ARRAY_SIZE(obj_data_format), obj_data_format, }; + IDirectInputDevice8W *device, *tmp_device; DIDEVICEOBJECTINSTANCEW objinst = {0}; DIDEVICEINSTANCEW devinst = {0}; BOOL old_localized = localized; - IDirectInputDevice8W *device; DIDEVCAPS caps = {0}; BYTE full_state[256]; HKL hkl, old_hkl; @@ -2699,6 +2706,13 @@ static void test_sys_keyboard( DWORD version ) ok( IsEqualGUID( &guid, &GUID_SysKeyboardEm ), "got %s expected %s\n", debugstr_guid( &guid ), debugstr_guid( &GUID_SysKeyboardEm ) ); + hr = create_dinput_device( version, &GUID_SysKeyboardEm, &tmp_device ); + ok( hr == DI_OK, "Initialize returned %#lx\n", hr ); + if (hr == DI_OK) IDirectInputDevice8_Release( tmp_device ); + hr = create_dinput_device( version, &GUID_SysKeyboardEm2, &tmp_device ); + ok( hr == DI_OK, "Initialize returned %#lx\n", hr ); + if (hr == DI_OK) IDirectInputDevice8_Release( tmp_device ); + hr = IDirectInputDevice8_Initialize( device, instance, version, &GUID_SysKeyboard ); ok( hr == DI_OK, "Initialize returned %#lx\n", hr ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10776
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
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/joystick_hid.c | 74 +++++++++++++++++++++++++++-------- dlls/dinput/tests/joystick8.c | 27 ++++++------- 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index fa22d806437..14547245af1 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -96,10 +96,13 @@ static CRITICAL_SECTION joystick_cache_cs = { &joystick_cache_cs_debug, -1, 0, 0 static struct list joystick_cache = LIST_INIT( joystick_cache ); +#define MAX_JOY_ID 16 + struct cache_entry { struct list entry; DIDEVICEINSTANCEW instance; + DWORD joy_id; WCHAR path[MAX_PATH]; }; @@ -109,12 +112,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, DWORD joy_id, + const WCHAR *path, 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 +127,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; @@ -135,7 +140,8 @@ static HRESULT insert_cache_entry( DIDEVICEINSTANCEW *instance, const WCHAR *pat if (*path && !*next->path) { instance->guidInstance = next->instance.guidInstance; - TRACE( "Reusing instance %s, path %s\n", debugstr_device_instance( instance ), debugstr_w( path ) ); + TRACE( "Reusing instance %s, path %s, joy_id %#lx\n", debugstr_device_instance( instance ), + debugstr_w( path ), next->joy_id ); next->instance = *instance; wcscpy( next->path, path ); return S_OK; @@ -143,8 +149,9 @@ static HRESULT insert_cache_entry( DIDEVICEINSTANCEW *instance, const WCHAR *pat } #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 ) ); + if (FAILED(hr = cache_entry_create( instance, joy_id, path, &entry ))) return hr; + TRACE( "Created instance %s, path %s, joy_id %#lx\n", debugstr_device_instance( instance ), + debugstr_w( path ), joy_id ); list_add_before( &next->entry, &entry->entry ); return S_OK; @@ -167,9 +174,12 @@ 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 (entry->joy_id >= MAX_JOY_ID) 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 ) ); + TRACE( "Saved %04x:%04x index %lu, guid %s, joy_id %#lx\n", LOWORD(vidpid), HIWORD(vidpid), index, + debugstr_guid( &entry->instance.guidInstance ), entry->joy_id ); } } @@ -180,14 +190,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, len_id = sizeof(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"" ); + if (RegGetValueW( root, name, L"Joystick Id", RRF_RT_REG_BINARY, NULL, &joy_id, &len_id )) joy_id = MAX_JOY_ID + 1; + + 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 ); - TRACE( "Loaded %04x:%04x index %s, guid %s\n", LOWORD(vidpid), HIWORD(vidpid), debugstr_w( name ), - debugstr_guid( &instance.guidInstance ) ); + insert_cache_entry( &instance, joy_id, L"" ); } } @@ -211,7 +223,33 @@ static void load_registry_instances( HKEY root ) } } -static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *instance, WCHAR *path ) +static void assign_joystick_ids(void) +{ + DWORD ids = ((1 << MAX_JOY_ID) - 1) | (1 << (MAX_JOY_ID + 1)); + struct cache_entry *entry; + + LIST_FOR_EACH_ENTRY( entry, &joystick_cache, struct cache_entry, entry ) + { + if (!*entry->path || entry->joy_id >= MAX_JOY_ID) continue; + if (!(ids & (1 << entry->joy_id))) entry->joy_id = MAX_JOY_ID + 1; + else ids &= ~(1 << entry->joy_id); + + TRACE( "Reusing joy_id %#lx for instance %s, path %s\n", entry->joy_id, + debugstr_device_instance( &entry->instance ), debugstr_w( entry->path ) ); + } + + LIST_FOR_EACH_ENTRY( entry, &joystick_cache, struct cache_entry, entry ) + { + if (!*entry->path || entry->joy_id < MAX_JOY_ID) continue; + BitScanForward( &entry->joy_id, ids ); + if (entry->joy_id < MAX_JOY_ID) ids &= ~(1 << entry->joy_id); + + TRACE( "Assigned joy_id %#lx to instance %s, path %s\n", entry->joy_id, + debugstr_device_instance( &entry->instance ), debugstr_w( entry->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; @@ -234,6 +272,7 @@ static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *inst else { *instance = entry->instance; + *joy_id = entry->joy_id; wcscpy( path, entry->path ); } @@ -382,6 +421,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 +1047,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,12 +1891,13 @@ 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, MAX_JOY_ID + 1, path ); HidD_FreePreparsedData( preparsed ); CloseHandle( device ); } free( paths ); + assign_joystick_ids(); save_registry_instances( root ); ReleaseMutex( mutex ); CloseHandle( mutex ); @@ -2244,18 +2285,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 f6d3d0e2f5a..de9841a2d22 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; @@ -6622,7 +6621,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(); @@ -6648,7 +6647,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(); } @@ -6674,7 +6673,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(); @@ -6698,8 +6697,8 @@ 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 ); - if (i < 2) check_device_joystick_id( device, i, TRUE ); - else check_device_joystick_id( device, (i == 2) ? 3 : 2, TRUE ); + if (i < 2) check_device_joystick_id( device, i, FALSE ); + else check_device_joystick_id( device, (i == 2) ? 3 : 2, FALSE ); IDirectInputDevice8_Release( device ); winetest_pop_context(); @@ -6719,7 +6718,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(); @@ -6738,7 +6737,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 ); @@ -6755,7 +6754,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(); @@ -6780,7 +6779,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(); @@ -6806,8 +6805,8 @@ 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 ); - if (i < 2) check_device_joystick_id( device, !i ? 1 : 0, TRUE ); - else check_device_joystick_id( device, i, TRUE ); + if (i < 2) check_device_joystick_id( device, !i ? 1 : 0, FALSE ); + else check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release( device ); winetest_pop_context(); } @@ -6836,8 +6835,8 @@ 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 ); - else check_device_joystick_id( device, i, TRUE ); + if (i >= 16) check_device_joystick_id( device, 17, FALSE ); + else check_device_joystick_id( device, i, FALSE ); IDirectInputDevice8_Release( device ); winetest_pop_context(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10776
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 de9841a2d22..1733ff33222 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6839,6 +6839,29 @@ static void test_joystick_id( DWORD version ) else 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/10776
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/joystick_hid.c | 3 +++ dlls/dinput/tests/joystick8.c | 13 ++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 14547245af1..9277eea5e96 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -264,7 +264,10 @@ static HRESULT get_instance_from_guid( const GUID *guid, DIDEVICEINSTANCEW *inst LIST_FOR_EACH_ENTRY( entry, &joystick_cache, struct cache_entry, entry ) { + GUID guid_joystick = GUID_Joystick; + guid_joystick.Data1 += entry->joy_id; if (!*entry->path) continue; + if (IsEqualGUID( &guid_joystick, guid )) break; 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 1733ff33222..ddcbddc9880 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6853,13 +6853,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/10776
participants (3)
-
Connor McAdams -
Rémi Bernon -
Rémi Bernon (@rbernon)