[PATCH v3] dinput: Introduce new HID joystick backend.

Rémi Bernon rbernon at codeweavers.com
Wed Jun 30 08:16:13 CDT 2021


On 6/30/21 2:30 PM, Rémi Bernon wrote:
> On 6/30/21 1:08 PM, Arkadiusz Hiler wrote:
>> On Wed, Jun 30, 2021 at 10:54:19AM +0200, Rémi Bernon wrote:
>>> +
>>> +static HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, 
>>> DIDEVICEINSTANCEW *instance,
>>> +                                         DWORD version, int index )
>>> +{
>>> +    HIDD_ATTRIBUTES attrs = {sizeof(attrs)};
>>> +    PHIDP_PREPARSED_DATA preparsed;
>>> +    WCHAR device_path[MAX_PATH];
>>> +    HIDP_CAPS caps;
>>> +    HANDLE device;
>>> +    HRESULT hr;
>>> +
>>> +    TRACE( "type %x, flags %#x, instance %p, version %04x, index 
>>> %d\n", type, flags, instance, version, index );
>>> +
>>> +    hr = hid_joystick_device_open( index, 0, 0, device_path, 
>>> &device, version,
>>> +                                   &preparsed, &attrs, &caps, 
>>> instance );
>>
>> hid_joystick_device_open() is getting a bit unwieldy with all the
>> possible parameter combinations. I don't think it needs the enumeration
>> / vidpid lookup logic.
>>
>> IMO it would read better if hid_joystick_device_open() would take the
>> path or RI handle and enum logic would be moved either here or to
>> a helper.
>>
> 
> I don't think we need yet another device list cache, and I would rather 
> avoid it if possible. It's really just two call sites.
> 
> Instead of the two additional params we could use the DIDEVICEINSTANCEW 
> parameter as an in/out filter, providing the desired GUIDs to match (or 
> zeroed when enumerating all devices).
> 
>>> +    if (hr != DI_OK) return hr;
>>> +
>>> +    HidD_FreePreparsedData( preparsed );
>>> +    CloseHandle( device );
>>> +
>>> +    if (instance->dwSize != sizeof(DIDEVICEINSTANCEW))
>>> +        return S_FALSE;
>>> +    if (version < 0x0800 && type != DIDEVTYPE_JOYSTICK)
>>> +        return S_FALSE;
>>> +    if (version >= 0x0800 && type != DI8DEVCLASS_ALL && type != 
>>> DI8DEVCLASS_GAMECTRL)
>>> +        return S_FALSE;
>>> +
>>> +    if (device_disabled_registry( "HID", TRUE ))
>>> +        return DIERR_DEVICENOTREG;
>>> +
>>> +    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 DI_OK;
>>> +}
>>> +
>>> +static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, 
>>> REFGUID guid, IDirectInputDevice8W **out )
>>> +{
>>> +    DIDEVICEINSTANCEW instance = {sizeof(instance)};
>>> +    GUID inst_guid = *guid, prod_guid = *guid;
>>> +    DWORD size = sizeof(struct hid_joystick);
>>> +    HIDD_ATTRIBUTES attrs = {sizeof(attrs)};
>>> +    struct hid_joystick *impl = NULL;
>>> +    PHIDP_PREPARSED_DATA preparsed;
>>> +    UINT32 handle = 0, vid_pid = 0;
>>> +    DIDATAFORMAT *format = NULL;
>>> +    WCHAR device_path[MAX_PATH];
>>> +    HIDP_CAPS caps;
>>> +    HANDLE device;
>>> +    HRESULT hr;
>>> +
>>> +    TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( 
>>> guid ), out );
>>> +
>>> +    *out = NULL;
>>> +
>>> +    inst_guid.Data3 = hid_joystick_guid.Data3;
>>> +    prod_guid.Data1 = DInput_PIDVID_Product_GUID.Data1;
>>> +    if (IsEqualGUID( &hid_joystick_guid, &inst_guid )) handle = 
>>> guid->Data3;
>>
>> Data3 is only a short. I think we should use 64 bits for our magic and
>> another 64 bits for the RawInput handle.
>>
>> Or 32 if we go with our internal knowledge about how those handles are
>> implemented in Wine.
>>
> 
> Should we really worry about overflowing the short capacity for the HID 
> handle? If we do we could combine Data2 and Data3, or XOR it in Data1 
> (just to keep a bit of randomness), but as the property is also checked 
> to be 32bit I don't think we need to use more than that.

For instance I think something along those lines would actually be nice, 
with the "instance" filled in try_open, and then returned into "filter" 
as output when device matches:

> 
>         /* enumerate device by GUID */
>         if (IsEqualGUID( &filter->guidProduct, &instance.guidProduct )) break;
>         if (IsEqualGUID( &filter->guidInstance, &instance.guidInstance )) break;
> 
>         /* enumerate all devices */
>         if (IsEqualGUID( &filter->guidProduct, &DInput_PIDVID_Product_GUID ) &&
>             IsEqualGUID( &filter->guidInstance, &hid_joystick_guid ) &&
>             !index--)
>             break;
> 

And the two call sites would look like:

> 
>     instance->guidProduct = DInput_PIDVID_Product_GUID;
>     instance->guidInstance = hid_joystick_guid;
>     hr = hid_joystick_device_open( index, instance, device_path, &device, &preparsed,
>                                    &attrs, &caps, version );
>     if (hr != DI_OK) return hr;


>     DIDEVICEINSTANCEW instance = {.dwSize = sizeof(instance), .guidProduct = *guid, .guidInstance = *guid};
>     instance.guidProduct.Data1 = DInput_PIDVID_Product_GUID.Data1;
>     instance.guidInstance.Data1 = hid_joystick_guid.Data1;
>     if (IsEqualGUID( &DInput_PIDVID_Product_GUID, &instance.guidProduct ))
>     {
>         instance.guidProduct = *guid;
>         instance.guidInstance = hid_joystick_guid;
>     }
>     else if (IsEqualGUID( &hid_joystick_guid, &instance.guidInstance ))
>     {
>         instance.guidProduct = DInput_PIDVID_Product_GUID;
>         instance.guidInstance = *guid;
>     }
>     else
>     {
>         return DIERR_DEVICENOTREG;
>     }
> 
>     hr = hid_joystick_device_open( 0, &instance, device_path, &device, &preparsed,
>                                    &attrs, &caps, dinput->dwVersion );
>     if (hr != DI_OK) return hr;

It also fulfill the vid_pid matching nicely.
-- 
Rémi Bernon <rbernon at codeweavers.com>



More information about the wine-devel mailing list