[PATCH v2 0/6] MR10364: dinput: Add support for generating consistent guidInstance values for devices.
This MR adds support for creating `guidInstance` values for joystick devices in a way that matches native behavior. Our current method of creating a `guidInstance` value uses the device's rawinput handle, which changes each time a controller is plugged in. Fixes a bug in Rocket League where a controller plug/unplug cycle results in a new controller being detected each time. The process is like this: - Controller is connected, the game gets its `guidInstance` value from `EnumDevices()`, stores it and associates it with player 1. - Controller is disconnected. - The same controller is reconnected, the game calls `EnumDevices()`, and finds a new `guidInstance` value. - It then assumes this is a new controller, creates a new player, and behaves as though player 1's controller is unplugged. This MR implements `guidInstance` in a way that matches native, and ensures that the `guidInstance` value remains consistent. Doing things this way also makes it easier to add support for `DIPROP_JOYSTICKID` and `GUID_Joystick` in a future MR. I've implemented those in a branch [here](https://gitlab.winehq.org/cmcadams/wine/-/tree/WIP/dinput-instance-guid-v08) using this system. -- v2: dinput: Return E_FAIL when joystick device is disconnected. dinput: Properly implement guidInstance for joystick devices. dinput/tests: Add tests for dinput joystick guidInstance values. dinput/tests: Fix occasional GameInput test crash. include: Fix HID_STRING_ID_* definitions. dinput/tests: Add support for having multiple devices to the test driver. https://gitlab.winehq.org/wine/wine/-/merge_requests/10364
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/tests/driver_bus.c | 17 ++++++++++++++--- dlls/dinput/tests/driver_hid.c | 33 +++++++++++++++++++++++++++++++-- dlls/dinput/tests/driver_hid.h | 1 + 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/dlls/dinput/tests/driver_bus.c b/dlls/dinput/tests/driver_bus.c index e8338e49711..f7bfd23dfaa 100644 --- a/dlls/dinput/tests/driver_bus.c +++ b/dlls/dinput/tests/driver_bus.c @@ -563,7 +563,7 @@ static NTSTATUS remove_child_device( struct func_device *impl, DEVICE_OBJECT *de for (i = 0; i < impl->devices->Count; ++i) if (impl->devices->Objects[i] == device) break; if (i == impl->devices->Count) status = STATUS_NOT_FOUND; - else impl->devices->Objects[i] = impl->devices->Objects[impl->devices->Count--]; + else impl->devices->Objects[i] = impl->devices->Objects[--impl->devices->Count]; KeReleaseSpinLock( &impl->base.lock, irql ); return status; @@ -587,8 +587,16 @@ static NTSTATUS append_child_device( struct func_device *impl, DEVICE_OBJECT *de return status; } +static BOOL check_device_match(struct phys_device *phys, const WCHAR *device_id, const WCHAR *instance_id) +{ + if (instance_id && wcscmp( phys->instance_id, instance_id )) + return FALSE; + return !wcscmp( phys->device_id, device_id ); +} + static DEVICE_OBJECT *find_child_device( struct func_device *impl, struct hid_device_desc *desc ) { + const WCHAR *instance_id = desc->instance_id[0] ? desc->instance_id : NULL; DEVICE_OBJECT *device = NULL, **devices; WCHAR device_id[MAX_PATH]; KIRQL irql; @@ -603,7 +611,7 @@ static DEVICE_OBJECT *find_child_device( struct func_device *impl, struct hid_de for (i = 0; i < impl->devices->Count; ++i) { struct phys_device *phys = pdo_from_DEVICE_OBJECT( (device = devices[i]) ); - if (!wcscmp( phys->device_id, device_id )) break; + if (check_device_match(phys, device_id, instance_id)) break; else device = NULL; } KeReleaseSpinLock( &impl->base.lock, irql ); @@ -907,7 +915,10 @@ static NTSTATUS create_child_pdo( DEVICE_OBJECT *device, struct hid_device_desc desc->attributes.ProductID ); /* use a different device ID so that driver cache select the polled driver */ if (desc->is_polled) wcscat( impl->device_id, L"&POLL" ); - swprintf( impl->instance_id, MAX_PATH, L"0&0000&0" ); + if (desc->instance_id[0]) + swprintf( impl->instance_id, MAX_PATH, L"%s", desc->instance_id ); + else + swprintf( impl->instance_id, MAX_PATH, L"0&0000&0" ); impl->base.is_phys = TRUE; impl->fdo = fdo; diff --git a/dlls/dinput/tests/driver_hid.c b/dlls/dinput/tests/driver_hid.c index 7b20af07493..f98ec2edb7b 100644 --- a/dlls/dinput/tests/driver_hid.c +++ b/dlls/dinput/tests/driver_hid.c @@ -42,8 +42,12 @@ static DRIVER_OBJECT *expect_driver; +static KSPIN_LOCK driver_data_lock; +static struct list device_list = LIST_INIT(device_list); + struct hid_device { + struct list entry; DEVICE_OBJECT *expect_bus_pdo; DEVICE_OBJECT *expect_hid_fdo; struct hid_device *expect_hid_ext; @@ -54,11 +58,27 @@ static void check_device( DEVICE_OBJECT *device ) { HID_DEVICE_EXTENSION *ext = device->DeviceExtension; struct hid_device *impl = ext->MiniDeviceExtension; + KIRQL irql; ok( device == impl->expect_hid_fdo, "got device %p\n", device ); ok( device->DriverObject == expect_driver, "got DriverObject %p\n", device->DriverObject ); - if (!device->NextDevice) ok( device == impl->expect_hid_fdo, "got device %p\n", device ); - else ok( device->NextDevice == impl->expect_hid_fdo, "got NextDevice %p\n", device->NextDevice ); + + KeAcquireSpinLock( &driver_data_lock, &irql ); + if (!device->NextDevice) + { + struct hid_device *impl_tail = LIST_ENTRY( list_tail(&device_list), struct hid_device, entry ); + + ok( device == impl_tail->expect_hid_fdo, "got device %p\n", device ); + } + else + { + struct hid_device *next_impl = LIST_ENTRY( list_next(&device_list, &impl->entry), struct hid_device, entry ); + HID_DEVICE_EXTENSION *next_ext = device->NextDevice->DeviceExtension; + struct hid_device *next_ext_impl = next_ext->MiniDeviceExtension; + + ok( next_impl == next_ext_impl, "got NextDevice %p.\n", device->NextDevice ); + } + KeReleaseSpinLock( &driver_data_lock, irql ); ok( !device->AttachedDevice, "got AttachedDevice %p\n", device->AttachedDevice ); ok( ext->MiniDeviceExtension == impl->expect_hid_ext, "got MiniDeviceExtension %p\n", ext->MiniDeviceExtension ); @@ -113,6 +133,7 @@ static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp ) HID_DEVICE_EXTENSION *ext = device->DeviceExtension; struct hid_device *impl = ext->MiniDeviceExtension; ULONG code = stack->MinorFunction; + KIRQL irql; if (winetest_debug > 1) trace( "%s: device %p, code %#lx %s\n", __func__, device, code, debugstr_pnp(code) ); @@ -126,6 +147,9 @@ static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp ) IoSetDeviceInterfaceState( &impl->control_symlink, FALSE ); RtlFreeUnicodeString( &impl->control_symlink ); irp->IoStatus.Status = STATUS_SUCCESS; + KeAcquireSpinLock( &driver_data_lock, &irql ); + list_remove( &impl->entry ); + KeReleaseSpinLock( &driver_data_lock, irql ); break; case IRP_MN_STOP_DEVICE: case IRP_MN_SURPRISE_REMOVAL: @@ -229,12 +253,16 @@ static NTSTATUS WINAPI driver_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT * struct hid_device *impl = ext->MiniDeviceExtension; DEVICE_OBJECT *bus_pdo = ext->PhysicalDeviceObject; NTSTATUS status; + KIRQL irql; if (winetest_debug > 1) trace( "%s: driver %p, device %p\n", __func__, driver, device ); impl->expect_hid_fdo = device; impl->expect_bus_pdo = ext->PhysicalDeviceObject; impl->expect_hid_ext = ext->MiniDeviceExtension; + KeAcquireSpinLock( &driver_data_lock, &irql ); + list_add_head( &device_list, &impl->entry ); + KeReleaseSpinLock( &driver_data_lock, irql ); todo_wine ok( impl->expect_bus_pdo->AttachedDevice == device, "got AttachedDevice %p\n", bus_pdo->AttachedDevice ); @@ -286,6 +314,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *registry ) NTSTATUS status; expect_driver = driver; + KeInitializeSpinLock( &driver_data_lock ); if ((status = winetest_init())) return status; if (winetest_debug > 1) trace( "%s: driver %p\n", __func__, driver ); diff --git a/dlls/dinput/tests/driver_hid.h b/dlls/dinput/tests/driver_hid.h index 8ba01d14b1e..e4f8f00a07a 100644 --- a/dlls/dinput/tests/driver_hid.h +++ b/dlls/dinput/tests/driver_hid.h @@ -92,6 +92,7 @@ struct hid_device_desc struct hid_expect expect[64]; ULONG context_size; char context[64]; + WCHAR instance_id[MAX_PATH]; }; /* kernel/user shared data */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10364
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- include/ddk/hidport.h | 8 ++++++++ include/ddk/hidtypes.h | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/ddk/hidport.h b/include/ddk/hidport.h index bd3ddb3f2a5..6574aba75bb 100644 --- a/include/ddk/hidport.h +++ b/include/ddk/hidport.h @@ -68,6 +68,14 @@ typedef struct _HID_DESCRIPTOR #define HID_HID_DESCRIPTOR_TYPE 0x21 #define HID_REPORT_DESCRIPTOR_TYPE 0x22 +/* + * String IDs used with IOCTL_HID_GET_STRING. + * These values match the string field offsets in chapter 9 of the USB spec. + */ +#define HID_STRING_ID_IMANUFACTURER 14 +#define HID_STRING_ID_IPRODUCT 15 +#define HID_STRING_ID_ISERIALNUMBER 16 + #define IOCTL_HID_GET_DEVICE_DESCRIPTOR HID_CTL_CODE(0) #define IOCTL_HID_GET_REPORT_DESCRIPTOR HID_CTL_CODE(1) #define IOCTL_HID_READ_REPORT HID_CTL_CODE(2) diff --git a/include/ddk/hidtypes.h b/include/ddk/hidtypes.h index dfd1061de18..8e6f76955e9 100644 --- a/include/ddk/hidtypes.h +++ b/include/ddk/hidtypes.h @@ -20,9 +20,6 @@ typedef enum _HID_STRING_TYPE { HID_STRING_INDEXED = 0, - HID_STRING_ID_IMANUFACTURER, - HID_STRING_ID_IPRODUCT, - HID_STRING_ID_ISERIALNUMBER, HID_STRING_MAX } HID_STRING_TYPE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10364
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/tests/joystick8.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index eab4a7ff7e8..d267aff724f 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -5319,6 +5319,7 @@ static void test_game_input(void) HMODULE gameinput = LoadLibraryW( L"gameinput.dll" ); HRESULT (WINAPI *pGameInputCreate)( v0_IGameInput **out ); v0_IGameInput *gi0; + HRESULT hr; if (!gameinput || !(pGameInputCreate = (void *)GetProcAddress( gameinput, "GameInputCreate" ))) { @@ -5327,8 +5328,9 @@ static void test_game_input(void) } gi0 = (void *)0xdeadbeef; - todo_wine ok_hr( S_OK, pGameInputCreate( &gi0 ) ); - if (gi0 != (void *)0xdeadbeef) ok_ret( 0, v0_IGameInput_Release( gi0 ) ); + hr = pGameInputCreate( &gi0 ); + todo_wine ok( hr == S_OK || broken(hr == E_NOTIMPL), "Unexpected hr %#lx.\n", hr ); + if (gi0 && gi0 != (void *)0xdeadbeef) ok_ret( 0, v0_IGameInput_Release( gi0 ) ); } static HANDLE rawinput_device_added, rawinput_device_removed, rawinput_event; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10364
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/tests/dinput_test.h | 6 + dlls/dinput/tests/driver_bus.c | 32 ++- dlls/dinput/tests/hid.c | 115 +++++++-- dlls/dinput/tests/hotplug.c | 19 +- dlls/dinput/tests/joystick8.c | 442 ++++++++++++++++++++++++++++++++ 5 files changed, 594 insertions(+), 20 deletions(-) diff --git a/dlls/dinput/tests/dinput_test.h b/dlls/dinput/tests/dinput_test.h index d19e1868d76..58030710310 100644 --- a/dlls/dinput/tests/dinput_test.h +++ b/dlls/dinput/tests/dinput_test.h @@ -64,9 +64,15 @@ void cleanup_registry_keys(void); void dinput_test_init_( const char *file, int line ); void dinput_test_exit(void); +HRESULT dinput_test_create_device_instance( DWORD version, const GUID *guid_inst, IDirectInputDevice8W **device ); HRESULT dinput_test_create_device( DWORD version, DIDEVICEINSTANCEW *devinst, IDirectInputDevice8W **device ); DWORD WINAPI dinput_test_device_thread( void *stop_event ); +#define dinput_test_get_device_hid_serial_string( a, b ) \ + dinput_test_get_device_hid_serial_string_( __FILE__, __LINE__, a, b ) +void dinput_test_get_device_hid_serial_string_( const char *file, int line, IDirectInputDevice8W *device, + WCHAR *serial_out ); + #define fill_context( a, b ) fill_context_( __FILE__, __LINE__, a, b ) void fill_context_( const char *file, int line, char *buffer, SIZE_T size ); diff --git a/dlls/dinput/tests/driver_bus.c b/dlls/dinput/tests/driver_bus.c index f7bfd23dfaa..7303d9f9247 100644 --- a/dlls/dinput/tests/driver_bus.c +++ b/dlls/dinput/tests/driver_bus.c @@ -1249,10 +1249,36 @@ static NTSTATUS pdo_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) } case IOCTL_HID_GET_STRING: - memcpy( irp->UserBuffer, L"Wine Test", sizeof(L"Wine Test") ); - irp->IoStatus.Information = sizeof(L"Wine Test"); - status = STATUS_SUCCESS; + { + UINT index = (UINT_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer & 0xffff; + UINT lcid = ((UINT_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer) >> 16; + + todo_wine ok( lcid, "Unexpected LCID %#x.\n", lcid ); + if (winetest_debug > 1) + trace( "%s: device %p, code %#lx %s, lcid %#x, idx %#x.\n", __func__, device, code, debugstr_ioctl(code), + lcid, index ); + switch (index) + { + case HID_STRING_ID_IMANUFACTURER: + case HID_STRING_ID_IPRODUCT: + memcpy( irp->UserBuffer, L"Wine Test", sizeof(L"Wine Test") ); + irp->IoStatus.Information = sizeof(L"Wine Test"); + status = STATUS_SUCCESS; + break; + + case HID_STRING_ID_ISERIALNUMBER: + wcscpy( irp->UserBuffer, impl->instance_id ); + irp->IoStatus.Information = (wcslen(impl->instance_id) + 1) * sizeof(WCHAR); + status = STATUS_SUCCESS; + break; + + default: + ok(0, "Unknown string type %#x.\n", index); + status = STATUS_NOT_IMPLEMENTED; + break; + } break; + } case IOCTL_GET_PHYSICAL_DESCRIPTOR: irp->IoStatus.Information = 0; diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c index c9cdb649f26..966fa29c8fd 100644 --- a/dlls/dinput/tests/hid.c +++ b/dlls/dinput/tests/hid.c @@ -3587,13 +3587,36 @@ done: hid_device_stop( &desc, 1 ); } +static BOOL cleanup_test_device_keys(HKEY key, const WCHAR *path) +{ + WCHAR buf[MAX_PATH]; + BOOL ret = FALSE; + unsigned int i; + HKEY root_key; + DWORD size; + + RegCreateKeyExW( key, path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); + size = ARRAY_SIZE(buf); + for (i = 0; !RegEnumKeyExW( root_key, i, buf, &size, NULL, NULL, NULL, NULL ); i++) + { + if (!wcsnicmp( buf, L"VID_1209", 8 )) + { + RegDeleteTreeW( root_key, buf ); + ret = TRUE; + break; + } + size = ARRAY_SIZE(buf); + } + RegCloseKey( root_key ); + return ret; +} + void cleanup_registry_keys(void) { static const WCHAR joystick_oem_path[] = L"System\\CurrentControlSet\\Control\\MediaProperties\\" "PrivateProperties\\Joystick\\OEM"; static const WCHAR dinput_path[] = L"System\\CurrentControlSet\\Control\\MediaProperties\\" "PrivateProperties\\DirectInput"; - HKEY root_key; /* These keys are automatically created by DInput and they store the list of supported force-feedback effects. OEM drivers are supposed @@ -3603,21 +3626,10 @@ void cleanup_registry_keys(void) We need to clean them up, or DInput will not refresh the list of effects from the PID report changes. */ - RegCreateKeyExW( HKEY_CURRENT_USER, joystick_oem_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); - - RegCreateKeyExW( HKEY_CURRENT_USER, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); - - RegCreateKeyExW( HKEY_LOCAL_MACHINE, joystick_oem_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); - - RegCreateKeyExW( HKEY_LOCAL_MACHINE, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); - RegDeleteTreeW( root_key, expect_vidpid_str ); - RegCloseKey( root_key ); + while (cleanup_test_device_keys( HKEY_CURRENT_USER, joystick_oem_path )) {} + while (cleanup_test_device_keys( HKEY_CURRENT_USER, dinput_path )) {} + while (cleanup_test_device_keys( HKEY_LOCAL_MACHINE, joystick_oem_path )) {} + while (cleanup_test_device_keys( HKEY_LOCAL_MACHINE, dinput_path )) {} } static LRESULT CALLBACK monitor_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) @@ -3750,6 +3762,44 @@ void dinput_test_exit(void) CoUninitialize(); } +HRESULT dinput_test_create_device_instance( DWORD version, const GUID *guid_inst, IDirectInputDevice8W **device ) +{ + IDirectInput8W *di8; + IDirectInputW *di; + HRESULT hr; + ULONG ref; + + *device = NULL; + if (version >= 0x800) + { + hr = DirectInput8Create( instance, version, &IID_IDirectInput8W, (void **)&di8, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInput8Create returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput8_CreateDevice( di8, guid_inst, device, NULL ); + ref = IDirectInput8_Release( di8 ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + else + { + hr = DirectInputCreateEx( instance, version, &IID_IDirectInput2W, (void **)&di, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInputCreateEx returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput_CreateDevice( di, guid_inst, (IDirectInputDeviceW **)device, NULL ); + ref = IDirectInput_Release( di ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + + return hr; +} + BOOL CALLBACK find_test_device( const DIDEVICEINSTANCEW *devinst, void *context ) { if (IsEqualGUID( &devinst->guidProduct, &expect_guid_product )) @@ -3819,6 +3869,39 @@ HRESULT dinput_test_create_device( DWORD version, DIDEVICEINSTANCEW *devinst, ID return DI_OK; } +void dinput_test_get_device_hid_serial_string_( const char *file, int line, IDirectInputDevice8W *device, + WCHAR *serial_out) +{ + DIPROPGUIDANDPATH prop_guid_path = + { + .diph = + { + .dwSize = sizeof(DIPROPGUIDANDPATH), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + WCHAR device_id[MAX_PATH] = { 0 }; + HANDLE file_handle; + HRESULT hr; + BOOL ret; + + serial_out[0] = 0; + hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph ); + ok_(file, line)( hr == S_OK, "Unexpected hr %#lx.\n", hr ); + if (FAILED(hr)) return; + + file_handle = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + ok_(file, line)( file_handle != INVALID_HANDLE_VALUE, "Got error %lu.\n", GetLastError() ); + if (file_handle == INVALID_HANDLE_VALUE) return; + + ret = HidD_GetSerialNumberString( file_handle, device_id, ARRAY_SIZE(device_id) ); + ok_(file, line)( ret, "Failed to get HID serial number string.\n" ); + if (ret) wcscpy( serial_out, device_id ); + CloseHandle( file_handle ); +} + DWORD WINAPI dinput_test_device_thread( void *stop_event ) { #include "psh_hid_macros.h" diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index ce44bae754c..350050c0a42 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -156,8 +156,9 @@ static BOOL test_input_lost( DWORD version ) DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; DIDEVICEOBJECTDATA objdata[32] = {{0}}; - IDirectInputDevice8W *device = NULL; + IDirectInputDevice8W *device, *device2; ULONG ref, count, size; + GUID guid_instance; DIJOYSTATE2 state; HRESULT hr; @@ -169,6 +170,7 @@ static BOOL test_input_lost( DWORD version ) memcpy( desc.report_descriptor_buf, report_desc, sizeof(report_desc) ); fill_context( desc.context, ARRAY_SIZE(desc.context) ); + device = device2 = NULL; if (!hid_device_start( &desc, 1 )) goto done; if (FAILED(hr = dinput_test_create_device( version, &devinst, &device ))) goto done; @@ -220,6 +222,21 @@ static BOOL test_input_lost( DWORD version ) ref = IDirectInputDevice8_Release( device ); ok( ref == 0, "Release returned %ld\n", ref ); + /* Test guidInstance across hotplugs. It should remain the same. */ + guid_instance = devinst.guidInstance; + memset( &devinst, 0, sizeof(devinst) ); + devinst.dwSize = sizeof(DIDEVICEINSTANCEW); + hr = dinput_test_create_device( version, &devinst, &device2 ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( !!device2, "device2 is NULL.\n" ); + if (device2) + { + todo_wine ok( !memcmp( &guid_instance, &devinst.guidInstance, sizeof(guid_instance) ), + "Unexpected guidInstance.\n" ); + ref = IDirectInputDevice8_Release( device2 ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + done: hid_device_stop( &desc, 1 ); cleanup_registry_keys(); diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index d267aff724f..c5dc5c066bf 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -5981,6 +5981,444 @@ static void test_rawinput_desktop( const char *path, BOOL input ) DestroyWindow( hwnd ); } +static void init_hid_device_desc(struct hid_device_desc *desc, const WCHAR *instance_id, WORD pid) +{ +#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 init_desc = + { + .use_report_id = TRUE, + .caps = { .InputReportByteLength = 1 }, + .attributes = default_attributes, + }; + + *desc = init_desc; + desc->report_descriptor_len = sizeof(report_desc); + memcpy( desc->report_descriptor_buf, report_desc, sizeof(report_desc) ); + fill_context( desc->context, ARRAY_SIZE(desc->context) ); + if (instance_id) wcscpy(desc->instance_id, instance_id); + desc->attributes.ProductID = pid; +} + +struct joystick_test_device +{ + struct hid_device_desc desc; + BOOL started; +}; + +static BOOL start_joystick_test_device(struct joystick_test_device *device) +{ + device->started = hid_device_start( &device->desc, 1 ); + return device->started; +} + +static void stop_joystick_test_device(struct joystick_test_device *device) +{ + if (device->started) hid_device_stop( &device->desc, 1 ); + device->started = FALSE; +} + +struct dinput_test_devices +{ + DIDEVICEINSTANCEW *instances; + unsigned int instances_size; + unsigned int instances_count; +}; + +static void dinput_test_devices_struct_init(struct dinput_test_devices *devices, DIDEVICEINSTANCEW *insts, + unsigned int insts_size) +{ + const DIDEVICEINSTANCEW devinst_init = { .dwSize = sizeof(DIDEVICEINSTANCEW) }; + unsigned int i; + + for (i = 0; i < insts_size; i++) insts[i] = devinst_init; + devices->instances = insts; + devices->instances_size = insts_size; + devices->instances_count = 0; +} + +BOOL CALLBACK find_test_devices( const DIDEVICEINSTANCEW *devinst, void *context ) +{ + struct dinput_test_devices *devices = (struct dinput_test_devices *)context; + + if (!memcmp(devinst->guidProduct.Data4, expect_guid_product.Data4, sizeof(expect_guid_product.Data4)) + && (LOWORD(devinst->guidProduct.Data1) == LOWORD(EXPECT_VIDPID))) + { + devices->instances[devices->instances_count++] = *devinst; + if (devices->instances_count >= devices->instances_size) return DIENUM_STOP; + } + return DIENUM_CONTINUE; +} + +static HRESULT dinput_test_enum_devices( DWORD version, struct dinput_test_devices *context ) +{ + IDirectInput8W *di8; + IDirectInputW *di; + HRESULT hr; + ULONG ref; + + context->instances_count = 0; + if (version >= 0x800) + { + hr = DirectInput8Create( instance, version, &IID_IDirectInput8W, (void **)&di8, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInput8Create returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput8_EnumDevices( di8, DI8DEVCLASS_ALL, find_test_devices, context, DIEDFL_ALLDEVICES ); + ok( hr == DI_OK, "EnumDevices returned: %#lx\n", hr ); + + ref = IDirectInput8_Release( di8 ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + else + { + hr = DirectInputCreateEx( instance, version, &IID_IDirectInput2W, (void **)&di, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInputCreateEx returned %#lx\n", hr ); + return hr; + } + + hr = IDirectInput_EnumDevices( di, 0, find_test_devices, context, DIEDFL_ALLDEVICES ); + ok( hr == DI_OK, "EnumDevices returned: %#lx\n", hr ); + + ref = IDirectInput_Release( di ); + ok( ref == 0, "Release returned %ld\n", ref ); + } + + return DI_OK; +} + +static void test_joystick_instance_guid( DWORD version ) +{ + struct dinput_test_devices dinput_test_devs; + struct joystick_test_device test_devs[4]; + WCHAR device_serial[MAX_PATH]; + DIDEVICEINSTANCEW devinsts[4]; + IDirectInputDevice8W *device; + GUID guid_instances[4]; + unsigned int i; + HRESULT hr; + + winetest_push_context( "%#lx", version ); + cleanup_registry_keys(); + + memset( test_devs, 0, sizeof(test_devs) ); + init_hid_device_desc( &test_devs[0].desc, L"1&2345&6", 0x0001 ); + init_hid_device_desc( &test_devs[1].desc, L"2&7812&1", 0x0001 ); + init_hid_device_desc( &test_devs[2].desc, L"3&4580&7", 0x0002 ); + init_hid_device_desc( &test_devs[3].desc, L"4&5880&3", 0x0002 ); + + for (i = 0; i < ARRAY_SIZE(test_devs); i++) + { + if (!start_joystick_test_device( &test_devs[i] )) + goto done; + } + + /* Enumerate all devices, each with a unique guidInstance. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 4, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + guid_instances[i] = devinsts[i].guidInstance; + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Delete the existing registry keys, this will result in a new set of + * guidInstance values being generated on the next call to EnumDevices(). + */ + cleanup_registry_keys(); + + /* + * EnumDevices() hasn't been called yet, guidInstance values are still + * valid. + */ + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Call EnumDevices(), a new set of guidInstance values and registry entries + * will be created. + */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 4, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + todo_wine ok( !IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", + debugstr_guid(&devinsts[i].guidInstance) ); + + /* Old guidInstance no longer works. */ + device = NULL; + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + todo_wine ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); + if (device) IDirectInputDevice8_Release(device); + guid_instances[i] = devinsts[i].guidInstance; + + /* New guidInstance, same device. */ + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Stop devices 0 and 2. After enumeration, their guidInstance values will + * be assigned to devices 1 and 3. + */ + stop_joystick_test_device( &test_devs[0] ); + stop_joystick_test_device( &test_devs[2] ); + + /* Stopped devices should return E_FAIL. */ + hr = dinput_test_create_device_instance( version, &devinsts[0].guidInstance, &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + hr = dinput_test_create_device_instance( version, &devinsts[2].guidInstance, &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + /* After calling EnumDevices(), guidInstance values will be reassigned. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 2, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + const unsigned int expected_joystick = !i ? 1 : 3; + + winetest_push_context( "device %d", i ); + + todo_wine ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), + "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[expected_joystick].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* + * Restart devices 0 and 2, they should get their old guidInstance values + * back after another call to EnumDevices(). + */ + if (!start_joystick_test_device( &test_devs[0] )) goto done; + if (!start_joystick_test_device( &test_devs[2] )) goto done; + + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + const unsigned int expected_joystick = !i ? 1 : 3; + + winetest_push_context( "device %d", i ); + + todo_wine ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), + "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[expected_joystick].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* guidInstance values are all back to what they were. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( dinput_test_devs.instances_count == 4, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < dinput_test_devs.instances_count; i++) + { + winetest_push_context( "device %d", i ); + + todo_wine_if(!(i & 0x1)) ok( IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", + debugstr_guid(&devinsts[i].guidInstance) ); + + hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + winetest_pop_context(); + } + + /* Stop all devices. */ + for (i = 0; i < ARRAY_SIZE(test_devs); i++) stop_joystick_test_device( &test_devs[i] ); + + /* + * Call EnumDevices() with all devices stopped. Test behavior of device + * creation without calling EnumDevices() with the devices present + * beforehand. + */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( !dinput_test_devs.instances_count, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + for (i = 0; i < ARRAY_SIZE(test_devs); i++) + { + winetest_push_context( "device %d", i ); + + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); + + /* + * Start the joystick device. This will succeed, even without having + * called EnumDevices() beforehand. + */ + if (!start_joystick_test_device( &test_devs[i] )) goto done; + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + if (device) + { + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[i].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + } + stop_joystick_test_device( &test_devs[i] ); + + hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + if (!start_joystick_test_device( &test_devs[i] )) goto done; + winetest_pop_context(); + } + + for (i = 0; i < ARRAY_SIZE(test_devs); i++) stop_joystick_test_device( &test_devs[i] ); + + /* Reset device list. */ + dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); + hr = dinput_test_enum_devices( version, &dinput_test_devs ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( !dinput_test_devs.instances_count, "Unexpected count %u.\n", dinput_test_devs.instances_count ); + + /* + * Start device 0, create a device with guidInstance 0. + * Stop device 0. + * Start device 1, attempt to create a device with guidInstance 1. + * Fails. + * Try to create a device with guidInstance 0, which is now associated + * with device 1. + */ + if (!start_joystick_test_device( &test_devs[0] )) goto done; + + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + if (device) + { + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[0].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + } + stop_joystick_test_device( &test_devs[0] ); + + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + /* Start device 1. */ + if (!start_joystick_test_device( &test_devs[1] )) goto done; + + /* + * This still fails with E_FAIL, as guid_instances[0] is still associated + * with device 0. + */ + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + + /* + * guid_instances[1] is not currently associated with a device, which + * means attempting to use this GUID results in what seems to be an + * internal call to EnumDevices(). With device 0 currently stopped, + * guid_instances[0] gets reassigned to device 1. + */ + hr = dinput_test_create_device_instance( version, &guid_instances[1], &device ); + ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); + + hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); + todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + if (device) + { + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[1].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + + IDirectInputDevice8_Release(device); + } + stop_joystick_test_device( &test_devs[1] ); + +done: + for (i = 0; i < ARRAY_SIZE(test_devs); i++) + stop_joystick_test_device( &test_devs[i] ); + cleanup_registry_keys(); + winetest_pop_context(); +} + START_TEST( joystick8 ) { char **argv; @@ -6008,6 +6446,10 @@ START_TEST( joystick8 ) test_simple_joystick( 0x700 ); test_simple_joystick( 0x800 ); + test_joystick_instance_guid( 0x500 ); + test_joystick_instance_guid( 0x700 ); + test_joystick_instance_guid( 0x800 ); + test_many_axes_joystick(); test_driving_wheel_axes(); test_rawinput( argv ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10364
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/dinput_main.c | 1 + dlls/dinput/dinput_private.h | 1 + dlls/dinput/joystick_hid.c | 444 +++++++++++++++++++++++++++++----- dlls/dinput/tests/hotplug.c | 11 +- dlls/dinput/tests/joystick8.c | 40 ++- 5 files changed, 412 insertions(+), 85 deletions(-) diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index e4bc1f7961d..aabad4ff97a 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -522,6 +522,7 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) break; case DLL_PROCESS_DETACH: if (reserved) break; + hid_joystick_cleanup(); unregister_di_em_win_class(); break; } diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h index 0609b816b3b..7e7b1e490f5 100644 --- a/dlls/dinput/dinput_private.h +++ b/dlls/dinput/dinput_private.h @@ -56,6 +56,7 @@ extern HRESULT keyboard_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW extern HRESULT keyboard_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out ); extern HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, DWORD version, int index ); extern HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out ); +extern void hid_joystick_cleanup( void ); struct DevicePlayer { GUID instance_guid; diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 22709c4bfd9..32ef69eb899 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -49,9 +49,320 @@ WINE_DEFAULT_DEBUG_CHANNEL(dinput); DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 ); -DEFINE_GUID( hid_joystick_guid, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 ); DEFINE_GUID( device_path_guid, 0x00000000, 0x0000, 0x0000, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf8 ); +/* + * Version 1 UUID timestamps are a count of the number of 100 nanosecond + * intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian + * reform to the Christian calendar). FILETIME is a count of the number of 100 + * nanosecond intervals since 00:00:00.00, 1 January 1601. In order to convert + * a FILETIME value to a UUID timestamp, we need to add: + * - 17 days in October 1582. + * - 30 days in November 1582. + * - 31 days in December 1582. + * - 18 years between January 1583 and January 1601. + * - 5 leap days in those 18 years. + */ +#define UUID_TIME_TO_SYSTEM_TIME_DAYS ((ULONG64)((365 * 18) + 5 + 17 + 30 + 31)) +#define UUID_TIME_TO_SYSTEM_TIME_NS_DIFFERENCE ((UUID_TIME_TO_SYSTEM_TIME_DAYS) * 24 * 60 * 60 * 10000000) +DEFINE_GUID( dinput_joystick_uuid_init, 0x00000000, 0x0000, 0x1000, 0x80, 0x00, 0x00, 0x00, 'D', 'E', 'S', 'T' ); +static void create_v1_uuid(GUID *guid) +{ + GUID tmp = dinput_joystick_uuid_init; + static LONG clock_seq; + ULARGE_INTEGER time; + ULONG cur_seq; + + GetSystemTimeAsFileTime( (FILETIME *)&time ); + time.QuadPart += UUID_TIME_TO_SYSTEM_TIME_NS_DIFFERENCE; + tmp.Data1 = (time.QuadPart & 0xffffffff); + tmp.Data2 = ((time.QuadPart >> 32) & 0xffff); + tmp.Data3 |= ((time.QuadPart >> 48) & 0x0fff); + cur_seq = InterlockedIncrement( &clock_seq ); + tmp.Data4[1] |= (cur_seq & 0xff); + tmp.Data4[0] |= ((cur_seq & 0x3f00) >> 8); + *guid = tmp; +} + +static HANDLE dinput_reg_mutex; +static BOOL WINAPI dinput_init_reg_mutex(INIT_ONCE *once, void *param, void **ctx) +{ + HANDLE mutex = CreateMutexW( NULL, FALSE, L"__wine_dinput_reg_mutex" ); + + if (mutex) dinput_reg_mutex = mutex; + + return !!mutex; +} + +static void get_dinput_reg_mutex( void ) +{ + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; + + if (!dinput_reg_mutex) InitOnceExecuteOnce( &once, dinput_init_reg_mutex, NULL, NULL ); + WaitForSingleObject( dinput_reg_mutex, INFINITE ); +} + +static void release_dinput_reg_mutex( void ) +{ + assert( dinput_reg_mutex ); + ReleaseMutex( dinput_reg_mutex ); +} + +/* Need to enter the mutex prior to accessing these registry entries. */ +static const WCHAR *dinput_path = L"System\\CurrentControlSet\\Control\\MediaProperties\\" + "PrivateProperties\\DirectInput"; +static void reset_joystick_instances_in_registry(void) +{ + WCHAR buf[MAX_PATH]; + unsigned int i, j; + HKEY root_key; + DWORD size; + + if (RegCreateKeyExW( HKEY_CURRENT_USER, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL )) return; + + size = ARRAY_SIZE(buf); + for (i = 0; !RegEnumKeyExW( root_key, i, buf, &size, NULL, NULL, NULL, NULL ); i++) + { + HKEY dev_key; + + size = ARRAY_SIZE(buf); + wcscat( buf, L"\\Calibration" ); + if (RegOpenKeyExW( root_key, buf, 0, KEY_ALL_ACCESS, &dev_key )) continue; + + for (j = 0; !RegEnumKeyExW( dev_key, j, buf, &size, NULL, NULL, NULL, NULL ); j++) + { + size = ARRAY_SIZE(buf); + RegDeleteKeyValueW( dev_key, buf, L"DevicePath" ); + } + + size = ARRAY_SIZE(buf); + RegCloseKey( dev_key ); + } + RegCloseKey( root_key ); +} + +static HKEY get_empty_joystick_instance_in_registry(WORD vid, WORD pid) +{ + HKEY root_key, dev_key = NULL; + WCHAR buf[MAX_PATH]; + DWORD len, size; + unsigned int i; + LSTATUS ret; + + swprintf( buf, ARRAY_SIZE(buf), L"%s\\VID_%04X&PID_%04X\\Calibration", dinput_path, vid, pid ); + if (RegCreateKeyExW( HKEY_CURRENT_USER, buf, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL )) + { + ERR( "Failed to create key.\n" ); + return NULL; + } + + size = ARRAY_SIZE(buf); + for (i = 0; !RegEnumKeyExW( root_key, i, buf, &size, NULL, NULL, NULL, NULL ); i++) + { + size = ARRAY_SIZE(buf); + if ((ret = RegGetValueW( root_key, buf, L"DevicePath", RRF_RT_REG_SZ, NULL, NULL, &len ))) + { + if (ret != ERROR_FILE_NOT_FOUND) WARN( "Got unexpected error %#lx.\n", ret ); + if (!RegOpenKeyExW( root_key, buf, 0, KEY_ALL_ACCESS, &dev_key )) break; + } + } + + if (!dev_key) + { + swprintf( buf, ARRAY_SIZE(buf), L"%s\\VID_%04X&PID_%04X\\Calibration\\%d", dinput_path, vid, pid, i ); + if ((ret = RegCreateKeyExW( HKEY_CURRENT_USER, buf, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &dev_key, NULL ))) + WARN( "Got unexpected error %#lx when creating key.\n", ret ); + } + RegCloseKey( root_key ); + return dev_key; +} + +static void set_joystick_instance_in_registry(WORD vid, WORD pid, const WCHAR *dev_path) +{ + HKEY dev_key = get_empty_joystick_instance_in_registry( vid, pid ); + LSTATUS ret; + DWORD len; + + if (!dev_key) + { + ERR( "Failed to get device instance in registry.\n" ); + return; + } + + /* If the instance GUID doesn't exist already, create it. */ + if ((ret = RegGetValueW( dev_key, NULL, L"GUID", RRF_RT_REG_BINARY, NULL, NULL, &len ))) + { + GUID tmp; + + if (ret != ERROR_FILE_NOT_FOUND) WARN( "Got unexpected error %#lx.\n", ret ); + + create_v1_uuid( &tmp ); + if ((ret = RegSetValueExW( dev_key, L"GUID", 0, REG_BINARY, (const BYTE *)&tmp, + sizeof(tmp) ))) + WARN( "Failed to set instance GUID value in registry with error %#lx.\n", ret ); + } + + if ((ret = RegSetValueExW( dev_key, L"DevicePath", 0, REG_SZ, (const BYTE *)dev_path, + (wcslen(dev_path) + 1) * sizeof(WCHAR) ))) + WARN( "Failed to set device path in registry with error %#lx.\n", ret ); + + RegCloseKey( dev_key ); +} + +static CRITICAL_SECTION joystick_crit; +static CRITICAL_SECTION_DEBUG joystick_critsect_debug = +{ + 0, 0, &joystick_crit, + { &joystick_critsect_debug.ProcessLocksList, &joystick_critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": joystick_crit") } +}; +static CRITICAL_SECTION joystick_crit = { &joystick_critsect_debug, -1, 0, 0, 0, 0 }; + +/* Need to enter the CS to access this list. */ +static struct list joystick_list = LIST_INIT(joystick_list); + +struct joystick_instance +{ + struct list entry; + WORD vid, pid; + GUID guid; + + WCHAR dev_path[MAX_PATH]; +}; + +static void dinput_update_joystick_instances_from_registry(void) +{ + struct joystick_instance *cur, *cur2; + WCHAR buf[MAX_PATH]; + unsigned int i, j; + HKEY root_key; + DWORD size; + + EnterCriticalSection( &joystick_crit ); + LIST_FOR_EACH_ENTRY_SAFE( cur, cur2, &joystick_list, struct joystick_instance, entry ) + { + list_remove( &cur->entry ); + free( cur ); + } + + get_dinput_reg_mutex(); + if (RegCreateKeyExW( HKEY_CURRENT_USER, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL )) goto exit; + + size = ARRAY_SIZE(buf); + for (i = 0; !RegEnumKeyExW( root_key, i, buf, &size, NULL, NULL, NULL, NULL ); i++) + { + WORD vid, pid; + HKEY dev_key; + + size = ARRAY_SIZE(buf); + if (swscanf( buf, L"VID_%04X&PID_%04X", &vid, &pid ) != 2) continue; + + wcscat( buf, L"\\Calibration" ); + if (RegOpenKeyExW( root_key, buf, 0, KEY_ALL_ACCESS, &dev_key )) continue; + + for (j = 0; !RegEnumKeyExW( dev_key, j, buf, &size, NULL, NULL, NULL, NULL ); j++) + { + struct joystick_instance *inst; + DWORD len; + + size = ARRAY_SIZE(buf); + if (RegGetValueW( dev_key, buf, L"DevicePath", RRF_RT_REG_SZ, NULL, NULL, &len )) continue; + + if (!(inst = calloc( 1, sizeof(*inst) ))) + { + ERR( "Failed to allocate memory for device instance.\n" ); + continue; + } + + inst->vid = vid; + inst->pid = pid; + len = sizeof(inst->guid); + if (!RegGetValueW( dev_key, buf, L"GUID", RRF_RT_REG_BINARY, NULL, &inst->guid, &len )) + { + len = sizeof(inst->dev_path); + if (RegGetValueW( dev_key, buf, L"DevicePath", RRF_RT_REG_SZ, NULL, inst->dev_path, &len )) + { + ERR( "Failed to get device path from key.\n" ); + free(inst); + continue; + } + } + else + { + ERR( "Failed to get guidInstance from key.\n" ); + free(inst); + continue; + } + list_add_tail( &joystick_list, &inst->entry ); + } + RegCloseKey( dev_key ); + } + +exit: + RegCloseKey( root_key ); + release_dinput_reg_mutex(); + LeaveCriticalSection( &joystick_crit ); +} + +static BOOL dinput_get_joystick_instance_from_guid(const GUID *guid, WCHAR *dev_path, GUID *out_guid) +{ + struct joystick_instance *inst; + BOOL ret_val = FALSE; + + EnterCriticalSection( &joystick_crit ); + LIST_FOR_EACH_ENTRY( inst, &joystick_list, struct joystick_instance, entry ) + { + GUID guid_product = dinput_pidvid_guid; + + guid_product.Data1 = MAKELONG( inst->vid, inst->pid); + if (IsEqualGUID( &inst->guid, guid ) || IsEqualGUID( &guid_product, guid )) + { + wcscpy( dev_path, inst->dev_path ); + *out_guid = inst->guid; + ret_val = TRUE; + break; + } + } + LeaveCriticalSection( &joystick_crit ); + + return ret_val; +} + +static BOOL dinput_get_joystick_instance_from_idx(unsigned int idx, WCHAR *dev_path, GUID *out_guid) +{ + struct joystick_instance *inst; + BOOL ret_val = FALSE; + unsigned int i = 0; + + EnterCriticalSection( &joystick_crit ); + LIST_FOR_EACH_ENTRY( inst, &joystick_list, struct joystick_instance, entry ) + { + if (i == idx) + { + wcscpy( dev_path, inst->dev_path ); + *out_guid = inst->guid; + ret_val = TRUE; + break; + } + i++; + } + LeaveCriticalSection( &joystick_crit ); + + return ret_val; +} + +void hid_joystick_cleanup( void ) +{ + struct joystick_instance *cur, *cur2; + + if (dinput_reg_mutex) CloseHandle( dinput_reg_mutex ); + LIST_FOR_EACH_ENTRY_SAFE( cur, cur2, &joystick_list, struct joystick_instance, entry ) + { + list_remove( &cur->entry ); + free( cur ); + } +} + struct pid_control_report { BYTE id; @@ -1421,13 +1732,12 @@ static HRESULT hid_joystick_device_try_open( const WCHAR *path, HANDLE *device, BOOL has_accelerator, has_brake, has_clutch, has_z, has_pov; PHIDP_PREPARSED_DATA preparsed_data = NULL; HIDP_LINK_COLLECTION_NODE nodes[256]; - DWORD type, size, button_count = 0; + DWORD type, button_count = 0; HIDP_BUTTON_CAPS buttons[10]; HIDP_VALUE_CAPS value; HANDLE device_file; ULONG node_count; NTSTATUS status; - UINT32 handle; USHORT count; device_file = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -1450,14 +1760,7 @@ static HRESULT hid_joystick_device_try_open( const WCHAR *path, HANDLE *device, if (!HidD_GetProductString( device_file, instance->tszInstanceName, MAX_PATH * sizeof(WCHAR) )) goto failed; if (!HidD_GetProductString( device_file, instance->tszProductName, MAX_PATH * sizeof(WCHAR) )) goto failed; - if (!DeviceIoControl( device_file, IOCTL_HID_GET_WINE_RAWINPUT_HANDLE, NULL, 0, &handle, sizeof(handle), &size, NULL )) - { - ERR( "failed to get raw input handle, error %lu\n", GetLastError() ); - goto failed; - } - - instance->guidInstance = hid_joystick_guid; - instance->guidInstance.Data1 ^= handle; + instance->guidInstance = GUID_NULL; instance->guidProduct = dinput_pidvid_guid; instance->guidProduct.Data1 = MAKELONG( attrs->VendorID, attrs->ProductID ); instance->guidFFDriver = GUID_NULL; @@ -1572,21 +1875,23 @@ failed: return DIERR_DEVICENOTREG; } -static HRESULT hid_joystick_device_open( int index, const GUID *guid, DIDEVICEINSTANCEW *instance, - WCHAR *device_path, HANDLE *device, PHIDP_PREPARSED_DATA *preparsed, - HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DWORD version ) +static HRESULT hid_joystick_refresh_devices( void ) { char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof(WCHAR)]; SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer; SP_DEVICE_INTERFACE_DATA iface = {.cbSize = sizeof(iface)}; SP_DEVINFO_DATA devinfo = {.cbSize = sizeof(devinfo)}; WCHAR device_id[MAX_PATH], *tmp; + PHIDP_PREPARSED_DATA preparsed; + HIDD_ATTRIBUTES attrs = { 0 }; HDEVINFO set, xi_set; + HIDP_CAPS caps; + HANDLE device; BOOL override; UINT32 i = 0; GUID hid; - TRACE( "index %d, guid %s\n", index, debugstr_guid( guid ) ); + TRACE( "\n" ); HidD_GetHidGuid( &hid ); @@ -1594,18 +1899,23 @@ static HRESULT hid_joystick_device_open( int index, const GUID *guid, DIDEVICEIN if (set == INVALID_HANDLE_VALUE) return DIERR_DEVICENOTREG; xi_set = SetupDiGetClassDevsW( &GUID_DEVINTERFACE_WINEXINPUT, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT ); - *device = NULL; - *preparsed = NULL; + device = NULL; + preparsed = NULL; + + get_dinput_reg_mutex(); + reset_joystick_instances_in_registry(); while (SetupDiEnumDeviceInterfaces( set, NULL, &hid, i++, &iface )) { + DIDEVICEINSTANCEW cur_instance = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, &devinfo )) continue; - if (FAILED(hid_joystick_device_try_open( detail->DevicePath, device, preparsed, - attrs, caps, instance, version ))) + if (FAILED(hid_joystick_device_try_open( detail->DevicePath, &device, &preparsed, + &attrs, &caps, &cur_instance, 0x0800 ))) continue; - if (device_instance_is_disabled( instance, &override )) + if (device_instance_is_disabled( &cur_instance, &override )) goto next; if (override && SetupDiGetDeviceInstanceIdW( set, &devinfo, device_id, MAX_PATH, NULL ) && @@ -1619,58 +1929,67 @@ static HRESULT hid_joystick_device_open( int index, const GUID *guid, DIDEVICEIN if (!SetupDiGetDeviceInterfaceDetailW( xi_set, &iface, detail, sizeof(buffer), NULL, &devinfo )) goto next; - CloseHandle( *device ); - HidD_FreePreparsedData( *preparsed ); - if (FAILED(hid_joystick_device_try_open( detail->DevicePath, device, preparsed, - attrs, caps, instance, version ))) + CloseHandle( device ); + HidD_FreePreparsedData( preparsed ); + if (FAILED(hid_joystick_device_try_open( detail->DevicePath, &device, &preparsed, + &attrs, &caps, &cur_instance, 0x0800 ))) continue; } - /* enumerate device by GUID */ - if (IsEqualGUID( guid, &instance->guidProduct ) || IsEqualGUID( guid, &instance->guidInstance )) break; - - /* enumerate all devices */ - if (index >= 0 && !index--) break; - + set_joystick_instance_in_registry(attrs.VendorID, attrs.ProductID, detail->DevicePath); next: - CloseHandle( *device ); - HidD_FreePreparsedData( *preparsed ); - *device = NULL; - *preparsed = NULL; + CloseHandle( device ); + HidD_FreePreparsedData( preparsed ); + device = NULL; + preparsed = NULL; } + release_dinput_reg_mutex(); if (xi_set != INVALID_HANDLE_VALUE) SetupDiDestroyDeviceInfoList( xi_set ); SetupDiDestroyDeviceInfoList( set ); - if (!*device || !*preparsed) return DIERR_DEVICENOTREG; - - lstrcpynW( device_path, detail->DevicePath, MAX_PATH ); + dinput_update_joystick_instances_from_registry(); return DI_OK; } HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, DWORD version, int index ) { - HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)}; - PHIDP_PREPARSED_DATA preparsed; - WCHAR device_path[MAX_PATH]; - GUID guid = GUID_NULL; - HIDP_CAPS caps; - HANDLE device; - HRESULT hr; + WCHAR device_path[MAX_PATH] = { 0 }; + HRESULT hr = DI_OK; + BOOL found = FALSE; + GUID guid; TRACE( "type %#lx, flags %#lx, instance %p, version %#lx, index %d\n", type, flags, instance, version, index ); - hr = hid_joystick_device_open( index, &guid, instance, device_path, &device, &preparsed, - &attrs, &caps, version ); + if (!index) hr = hid_joystick_refresh_devices(); if (hr != DI_OK) return hr; - HidD_FreePreparsedData( preparsed ); - CloseHandle( device ); + if (dinput_get_joystick_instance_from_idx( index, device_path, &guid )) + { + DIDEVICEINSTANCEW dev_inst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + PHIDP_PREPARSED_DATA preparsed = NULL; + HIDD_ATTRIBUTES attrs = { 0 }; + HIDP_CAPS caps; + HANDLE device; - TRACE( "found device %s, usage %04x:%04x, product %s, instance %s, name %s\n", debugstr_w(device_path), - instance->wUsagePage, instance->wUsage, debugstr_guid( &instance->guidProduct ), - debugstr_guid( &instance->guidInstance ), debugstr_w(instance->tszInstanceName) ); + if (SUCCEEDED(hid_joystick_device_try_open( device_path, &device, &preparsed, + &attrs, &caps, &dev_inst, version ))) + { + HidD_FreePreparsedData( preparsed ); + CloseHandle( device ); - return DI_OK; + dev_inst.guidInstance = guid; + *instance = dev_inst; + found = TRUE; + } + } + + if (!found) hr = DIERR_DEVICENOTREG; + if (hr == DI_OK) + TRACE( "found device %s, usage %04x:%04x, product %s, instance %s, name %s\n", debugstr_w(device_path), + instance->wUsagePage, instance->wUsage, debugstr_guid( &instance->guidProduct ), + debugstr_guid( &instance->guidInstance ), debugstr_w(instance->tszInstanceName) ); + + return hr; } static BOOL init_object_properties( struct dinput_device *device, UINT index, struct hid_value_caps *caps, @@ -2040,15 +2359,28 @@ HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDi impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND; impl->base.read_event = CreateEventW( NULL, TRUE, FALSE, NULL ); - if (memcmp( device_path_guid.Data4, guid->Data4, sizeof(device_path_guid.Data4) )) - hr = hid_joystick_device_open( -1, guid, &impl->base.instance, impl->device_path, &impl->device, &impl->preparsed, - &attrs, &impl->caps, dinput->dwVersion ); - else + if (!memcmp( device_path_guid.Data4, guid->Data4, sizeof(device_path_guid.Data4) )) { wcscpy( impl->device_path, *(const WCHAR **)guid ); hr = hid_joystick_device_try_open( impl->device_path, &impl->device, &impl->preparsed, &attrs, &impl->caps, &impl->base.instance, dinput->dwVersion ); } + else + { + GUID guid_inst; + + /* If we don't get a device for this GUID initially, try refreshing. */ + if (!dinput_get_joystick_instance_from_guid( guid, impl->device_path, &guid_inst )) + hid_joystick_refresh_devices(); + if (dinput_get_joystick_instance_from_guid( guid, impl->device_path, &guid_inst )) + { + hr = hid_joystick_device_try_open( impl->device_path, &impl->device, &impl->preparsed, &attrs, + &impl->caps, &impl->base.instance, dinput->dwVersion ); + if (hr == DI_OK) impl->base.instance.guidInstance = guid_inst; + } + else + hr = DIERR_DEVICENOTREG; + } if (hr != DI_OK) goto failed; impl->base.caps.dwDevType = impl->base.instance.dwDevType; diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index 350050c0a42..e47fd768c18 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -229,13 +229,10 @@ static BOOL test_input_lost( DWORD version ) hr = dinput_test_create_device( version, &devinst, &device2 ); ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); ok( !!device2, "device2 is NULL.\n" ); - if (device2) - { - todo_wine ok( !memcmp( &guid_instance, &devinst.guidInstance, sizeof(guid_instance) ), - "Unexpected guidInstance.\n" ); - ref = IDirectInputDevice8_Release( device2 ); - ok( ref == 0, "Release returned %ld\n", ref ); - } + ok( !memcmp( &guid_instance, &devinst.guidInstance, sizeof(guid_instance) ), + "Unexpected guidInstance.\n" ); + ref = IDirectInputDevice8_Release( device2 ); + ok( ref == 0, "Release returned %ld\n", ref ); done: hid_device_stop( &desc, 1 ); diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index c5dc5c066bf..ee2e6357344 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6197,13 +6197,13 @@ static void test_joystick_instance_guid( DWORD version ) { winetest_push_context( "device %d", i ); - todo_wine ok( !IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", + ok( !IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); /* Old guidInstance no longer works. */ device = NULL; hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); - todo_wine ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); + ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); if (device) IDirectInputDevice8_Release(device); guid_instances[i] = devinsts[i].guidInstance; @@ -6243,7 +6243,7 @@ static void test_joystick_instance_guid( DWORD version ) winetest_push_context( "device %d", i ); - todo_wine ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), + ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); @@ -6269,7 +6269,7 @@ static void test_joystick_instance_guid( DWORD version ) winetest_push_context( "device %d", i ); - todo_wine ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), + ok( IsEqualGUID( &guid_instances[expected_joystick - 1], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); @@ -6291,7 +6291,7 @@ static void test_joystick_instance_guid( DWORD version ) { winetest_push_context( "device %d", i ); - todo_wine_if(!(i & 0x1)) ok( IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", + ok( IsEqualGUID( &guid_instances[i], &devinsts[i].guidInstance ), "Unexpected guidInstance %s.\n", debugstr_guid(&devinsts[i].guidInstance) ); hr = dinput_test_create_device_instance( version, &devinsts[i].guidInstance, &device ); @@ -6330,7 +6330,7 @@ static void test_joystick_instance_guid( DWORD version ) */ if (!start_joystick_test_device( &test_devs[i] )) goto done; hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); - todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); if (device) { dinput_test_get_device_hid_serial_string(device, device_serial); @@ -6367,15 +6367,13 @@ static void test_joystick_instance_guid( DWORD version ) if (!start_joystick_test_device( &test_devs[0] )) goto done; hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); - todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); - if (device) - { - dinput_test_get_device_hid_serial_string(device, device_serial); - ok( !wcscmp(test_devs[0].desc.instance_id, device_serial), "Unexpected device serial %s.\n", - debugstr_w(device_serial) ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[0].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + IDirectInputDevice8_Release(device); - IDirectInputDevice8_Release(device); - } stop_joystick_test_device( &test_devs[0] ); hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); @@ -6401,15 +6399,13 @@ static void test_joystick_instance_guid( DWORD version ) ok( hr == DIERR_DEVICENOTREG, "Unexpected hr %#lx.\n", hr ); hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); - todo_wine ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); - if (device) - { - dinput_test_get_device_hid_serial_string(device, device_serial); - ok( !wcscmp(test_devs[1].desc.instance_id, device_serial), "Unexpected device serial %s.\n", - debugstr_w(device_serial) ); + ok( hr == DI_OK, "Unexpected hr %#lx.\n", hr ); + + dinput_test_get_device_hid_serial_string(device, device_serial); + ok( !wcscmp(test_devs[1].desc.instance_id, device_serial), "Unexpected device serial %s.\n", + debugstr_w(device_serial) ); + IDirectInputDevice8_Release(device); - IDirectInputDevice8_Release(device); - } stop_joystick_test_device( &test_devs[1] ); done: -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10364
From: Connor McAdams <cmcadams@codeweavers.com> Signed-off-by: Connor McAdams <cmcadams@codeweavers.com> --- dlls/dinput/joystick_hid.c | 2 +- dlls/dinput/tests/joystick8.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 32ef69eb899..0b01be90fde 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -1742,7 +1742,7 @@ static HRESULT hid_joystick_device_try_open( const WCHAR *path, HANDLE *device, device_file = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 0 ); - if (device_file == INVALID_HANDLE_VALUE) return DIERR_DEVICENOTREG; + if (device_file == INVALID_HANDLE_VALUE) return E_FAIL; if (!HidD_GetPreparsedData( device_file, &preparsed_data )) goto failed; if (!HidD_GetAttributes( device_file, attrs )) goto failed; diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index ee2e6357344..7dfd2b61527 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -6228,9 +6228,9 @@ static void test_joystick_instance_guid( DWORD version ) /* Stopped devices should return E_FAIL. */ hr = dinput_test_create_device_instance( version, &devinsts[0].guidInstance, &device ); - todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); hr = dinput_test_create_device_instance( version, &devinsts[2].guidInstance, &device ); - todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); /* After calling EnumDevices(), guidInstance values will be reassigned. */ dinput_test_devices_struct_init( &dinput_test_devs, devinsts, ARRAY_SIZE(devinsts) ); @@ -6342,7 +6342,7 @@ static void test_joystick_instance_guid( DWORD version ) stop_joystick_test_device( &test_devs[i] ); hr = dinput_test_create_device_instance( version, &guid_instances[i], &device ); - todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); if (!start_joystick_test_device( &test_devs[i] )) goto done; winetest_pop_context(); @@ -6377,7 +6377,7 @@ static void test_joystick_instance_guid( DWORD version ) stop_joystick_test_device( &test_devs[0] ); hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); - todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); /* Start device 1. */ if (!start_joystick_test_device( &test_devs[1] )) goto done; @@ -6387,7 +6387,7 @@ static void test_joystick_instance_guid( DWORD version ) * with device 0. */ hr = dinput_test_create_device_instance( version, &guid_instances[0], &device ); - todo_wine ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); + ok( hr == E_FAIL, "Unexpected hr %#lx.\n", hr ); /* * guid_instances[1] is not currently associated with a device, which -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10364
On Fri Mar 27 12:59:12 2026 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/10364/diffs?diff_id=254963&start_sha=a35e037db6c5858eafa4caaacff9241bbac7658b#4ad1ac26b8a52443a124ce8c55def4aa2a4f5075_215_226) I've changed how this works internally to be in two separate steps, one for populating the registry entries, and then a second step that populates a local list based cache from the registry entries. Hopefully this is more in line with what you suggested.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10364#note_134039
I've send some of the straightforward changes to a separate MR, splitting some of the commits into related changes. I'm still looking at the registry code. It seems a bit complicated still, and would be nice to have something simpler but I'm not yet sure how. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10364#note_134367
participants (3)
-
Connor McAdams -
Connor McAdams (@cmcadams) -
Rémi Bernon (@rbernon)