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

Arkadiusz Hiler ahiler at codeweavers.com
Thu Jul 1 05:38:44 CDT 2021


On Wed, Jun 30, 2021 at 03:16:13PM +0200, Rémi Bernon wrote:
> 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.

Oh sorry, this is not what I was suggesting.

I was thinking about factoring out the setupapi enumeration / device
finding logic to a separate function than the one that does all the
Hid?_* handling.

Something like:

HANDLE hid_joystick_find_device( int index, HANDLE ri_handle, int vid_pid, HANDLE *ri_handle_out );

and then you can either:

device = hid_joystick_find_device( index, INVALID_HANDLE, -1, &ri_handle );
device = hid_joystick_find_device( -1, ri_handle, -1, NULL );
device = hid_joystick_find_device( -1, INVALID_HANDLE, vid_pid, NULL );

open would then look more like:

hr = hid_joystick_device_read( device, version, &preparsed, &attrs, &caps );

Those two should give you all the thing you need to fill
DIDEVICEINSTANCEW in _enum_ and to fill in hid_joystick in _create_.

Also I like to use obviously wrong values for parameters that are not used.

> > 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.

as long as we are using the DEVPROPKEY_HID_HANDLE 32 bits should be enough

> 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.

That's even more logic added to _device_open... I am still not a fan of
how hard it is to parse through hid_joystick_device_open() and I would
prefer it to be split.

This seems to be a theme in Wine and Win32 APIs though, so I won't NAK
or nag you on this anymore :-P

-- 
Cheers,
Arek



More information about the wine-devel mailing list