Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 0d362cf9af3..d7efc44e806 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -54,6 +54,8 @@ struct hid_joystick
HANDLE device; PHIDP_PREPARSED_DATA preparsed; + + DIDEVICEINSTANCEW instance; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -97,14 +99,18 @@ static HRESULT WINAPI hid_joystick_GetDeviceState( IDirectInputDevice8W *iface,
static HRESULT WINAPI hid_joystick_GetDeviceInfo( IDirectInputDevice8W *iface, DIDEVICEINSTANCEW *instance ) { - FIXME( "iface %p, instance %p stub!\n", iface, instance ); + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + + TRACE( "iface %p, instance %p.\n", iface, instance );
if (!instance) return E_POINTER; if (instance->dwSize != sizeof(DIDEVICEINSTANCE_DX3W) && instance->dwSize != sizeof(DIDEVICEINSTANCEW)) return DIERR_INVALIDPARAM;
- return DIERR_UNSUPPORTED; + memcpy( instance, &impl->instance, instance->dwSize ); + + return S_OK; }
static HRESULT WINAPI hid_joystick_BuildActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format, @@ -342,6 +348,8 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID &attrs, &caps, dinput->dwVersion ); if (hr != DI_OK) goto failed;
+ impl->instance = instance; + *out = &impl->base.IDirectInputDevice8W_iface; return DI_OK;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 58 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index d7efc44e806..01864bb91e5 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -56,6 +56,8 @@ struct hid_joystick PHIDP_PREPARSED_DATA preparsed;
DIDEVICEINSTANCEW instance; + WCHAR device_path[MAX_PATH]; + HIDD_ATTRIBUTES attrs; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -88,6 +90,56 @@ static HRESULT WINAPI hid_joystick_GetCapabilities( IDirectInputDevice8W *iface, return DIERR_UNSUPPORTED; }
+static HRESULT WINAPI hid_joystick_GetProperty( IDirectInputDevice8W *iface, const GUID *guid, + DIPROPHEADER *header ) +{ + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + + TRACE( "iface %p, guid %s, header %p\n", iface, debugstr_guid( guid ), header ); + + if (!header) return DIERR_INVALIDPARAM; + if (!IS_DIPROP( guid )) return DI_OK; + + switch (LOWORD( guid )) + { + case (DWORD_PTR)DIPROP_PRODUCTNAME: + { + DIPROPSTRING *value = (DIPROPSTRING *)header; + lstrcpynW( value->wsz, impl->instance.tszProductName, MAX_PATH ); + return DI_OK; + } + case (DWORD_PTR)DIPROP_INSTANCENAME: + { + DIPROPSTRING *value = (DIPROPSTRING *)header; + lstrcpynW( value->wsz, impl->instance.tszInstanceName, MAX_PATH ); + return DI_OK; + } + case (DWORD_PTR)DIPROP_VIDPID: + { + DIPROPDWORD *value = (DIPROPDWORD *)header; + if (!impl->attrs.VendorID || !impl->attrs.ProductID) return DIERR_UNSUPPORTED; + value->dwData = MAKELONG( impl->attrs.VendorID, impl->attrs.ProductID ); + return DI_OK; + } + case (DWORD_PTR)DIPROP_JOYSTICKID: + { + DIPROPDWORD *value = (DIPROPDWORD *)header; + value->dwData = impl->instance.guidInstance.Data3; + return DI_OK; + } + case (DWORD_PTR)DIPROP_GUIDANDPATH: + { + DIPROPGUIDANDPATH *value = (DIPROPGUIDANDPATH *)header; + lstrcpynW( value->wszPath, impl->device_path, MAX_PATH ); + return DI_OK; + } + default: + return IDirectInputDevice2WImpl_GetProperty( iface, guid, header ); + } + + return DI_OK; +} + static HRESULT WINAPI hid_joystick_GetDeviceState( IDirectInputDevice8W *iface, DWORD len, void *ptr ) { FIXME( "iface %p, len %u, ptr %p stub!\n", iface, len, ptr ); @@ -144,7 +196,7 @@ static const IDirectInputDevice8WVtbl hid_joystick_vtbl = /*** IDirectInputDevice methods ***/ hid_joystick_GetCapabilities, IDirectInputDevice2WImpl_EnumObjects, - IDirectInputDevice2WImpl_GetProperty, + hid_joystick_GetProperty, IDirectInputDevice2WImpl_SetProperty, IDirectInputDevice2WImpl_Acquire, IDirectInputDevice2WImpl_Unacquire, @@ -322,7 +374,6 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID }; HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)}; struct hid_joystick *impl = NULL; - WCHAR device_path[MAX_PATH]; HIDP_CAPS caps; HRESULT hr;
@@ -344,11 +395,12 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_joystick.base.crit"); impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
- hr = hid_joystick_device_open( -1, &instance, device_path, &impl->device, &impl->preparsed, + hr = hid_joystick_device_open( -1, &instance, impl->device_path, &impl->device, &impl->preparsed, &attrs, &caps, dinput->dwVersion ); if (hr != DI_OK) goto failed;
impl->instance = instance; + impl->attrs = attrs;
*out = &impl->base.IDirectInputDevice8W_iface; return DI_OK;
Removing the trace as it can get pretty verbose otherwise.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 01864bb91e5..53dc97177a1 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -51,6 +51,7 @@ DEFINE_DEVPROPKEY( DEVPROPKEY_HID_HANDLE, 0xbc62e415, 0xf4fe, 0x405c, 0x8e, 0xda struct hid_joystick { IDirectInputDeviceImpl base; + DIJOYSTATE2 state;
HANDLE device; PHIDP_PREPARSED_DATA preparsed; @@ -142,11 +143,13 @@ static HRESULT WINAPI hid_joystick_GetProperty( IDirectInputDevice8W *iface, con
static HRESULT WINAPI hid_joystick_GetDeviceState( IDirectInputDevice8W *iface, DWORD len, void *ptr ) { - FIXME( "iface %p, len %u, ptr %p stub!\n", iface, len, ptr ); + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
if (!ptr) return DIERR_INVALIDPARAM;
- return DIERR_UNSUPPORTED; + fill_DataFormat( ptr, len, &impl->state, &impl->base.data_format ); + + return DI_OK; }
static HRESULT WINAPI hid_joystick_GetDeviceInfo( IDirectInputDevice8W *iface, DIDEVICEINSTANCEW *instance )
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 53dc97177a1..601de3b00b0 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -59,6 +59,7 @@ struct hid_joystick DIDEVICEINSTANCEW instance; WCHAR device_path[MAX_PATH]; HIDD_ATTRIBUTES attrs; + DIDEVCAPS dev_caps; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -84,11 +85,15 @@ static ULONG WINAPI hid_joystick_Release( IDirectInputDevice8W *iface )
static HRESULT WINAPI hid_joystick_GetCapabilities( IDirectInputDevice8W *iface, DIDEVCAPS *caps ) { - FIXME( "iface %p, caps %p stub!\n", iface, caps ); + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + + TRACE( "iface %p, caps %p.\n", iface, caps );
if (!caps) return E_POINTER;
- return DIERR_UNSUPPORTED; + *caps = impl->dev_caps; + + return DI_OK; }
static HRESULT WINAPI hid_joystick_GetProperty( IDirectInputDevice8W *iface, const GUID *guid, @@ -404,6 +409,9 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID
impl->instance = instance; impl->attrs = attrs; + impl->dev_caps.dwSize = sizeof(impl->dev_caps); + impl->dev_caps.dwFlags = DIDC_ATTACHED | DIDC_EMULATED; + impl->dev_caps.dwDevType = instance.dwDevType;
*out = &impl->base.IDirectInputDevice8W_iface; return DI_OK;
And initialize data format from them.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 417 ++++++++++++++++++++++++++++++++++++- 1 file changed, 415 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 601de3b00b0..5f709a7c30a 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -48,6 +48,93 @@ WINE_DEFAULT_DEBUG_CHANNEL(dinput); DEFINE_GUID( hid_joystick_guid, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 ); DEFINE_DEVPROPKEY( DEVPROPKEY_HID_HANDLE, 0xbc62e415, 0xf4fe, 0x405c, 0x8e, 0xda, 0x63, 0x6f, 0xb5, 0x9f, 0x08, 0x98, 2 );
+static inline const char *debugstr_hidp_link_collection_node( HIDP_LINK_COLLECTION_NODE *node ) +{ + if (!node) return "(null)"; + return wine_dbg_sprintf( "Usg %02x:%02x Par %u Nxt %u Cnt %u Chld %u Type %u Alias %d User %p", + node->LinkUsagePage, node->LinkUsage, node->Parent, node->NextSibling, + node->NumberOfChildren, node->FirstChild, node->CollectionType, node->IsAlias, + node->UserContext ); +} + +static inline const char *debugstr_hidp_button_caps( HIDP_BUTTON_CAPS *caps ) +{ + const char *str; + if (!caps) return "(null)"; + + str = wine_dbg_sprintf( "RId %d,", caps->ReportID ); + if (!caps->IsRange) + str = wine_dbg_sprintf( "%s Usg %02x:%02x Dat %02x,", str, caps->UsagePage, caps->NotRange.Usage, + caps->NotRange.DataIndex ); + else + str = wine_dbg_sprintf( "%s Usg %02x:%02x-%02x Dat %02x-%02x,", str, caps->UsagePage, caps->Range.UsageMin, + caps->Range.UsageMax, caps->Range.DataIndexMin, caps->Range.DataIndexMax ); + if (!caps->IsStringRange) + str = wine_dbg_sprintf( "%s Str %d,", str, caps->NotRange.StringIndex ); + else + str = wine_dbg_sprintf( "%s Str %d-%d,", str, caps->Range.StringMin, caps->Range.StringMax ); + if (!caps->IsDesignatorRange) + str = wine_dbg_sprintf( "%s Des %d,", str, caps->NotRange.DesignatorIndex ); + else + str = wine_dbg_sprintf( "%s Des %d-%d,", str, caps->Range.DesignatorMin, caps->Range.DesignatorMax ); + return wine_dbg_sprintf( "%s Bits %02x Alias %d Abs %d, LCol %u LUsg %02x-%02x", str, caps->BitField, caps->IsAlias, + caps->IsAbsolute, caps->LinkCollection, caps->LinkUsagePage, caps->LinkUsage ); +} + +static inline const char *debugstr_hidp_value_caps( HIDP_VALUE_CAPS *caps ) +{ + const char *str; + if (!caps) return "(null)"; + + str = wine_dbg_sprintf( "RId %d,", caps->ReportID ); + if (!caps->IsRange) + str = wine_dbg_sprintf( "%s Usg %02x:%02x Dat %02x,", str, caps->UsagePage, caps->NotRange.Usage, + caps->NotRange.DataIndex ); + else + str = wine_dbg_sprintf( "%s Usg %02x:%02x-%02x Dat %02x-%02x,", str, caps->UsagePage, caps->Range.UsageMin, + caps->Range.UsageMax, caps->Range.DataIndexMin, caps->Range.DataIndexMax ); + if (!caps->IsStringRange) + str = wine_dbg_sprintf( "%s Str %d,", str, caps->NotRange.StringIndex ); + else + str = wine_dbg_sprintf( "%s Str %d-%d,", str, caps->Range.StringMin, caps->Range.StringMax ); + if (!caps->IsDesignatorRange) + str = wine_dbg_sprintf( "%s Des %d,", str, caps->NotRange.DesignatorIndex ); + else + str = wine_dbg_sprintf( "%s Des %d-%d,", str, caps->Range.DesignatorMin, caps->Range.DesignatorMax ); + return wine_dbg_sprintf( "%s Bits %02x Alias %d Abs %d Null %d, LCol %u LUsg %02x-%02x, " + "BitSz %d, RCnt %d, Unit %x E%+d, Log %+d-%+d, Phy %+d-%+d", + str, caps->BitField, caps->IsAlias, caps->IsAbsolute, caps->HasNull, + caps->LinkCollection, caps->LinkUsagePage, caps->LinkUsage, + caps->BitSize, caps->ReportCount, caps->Units, caps->UnitsExp, + caps->LogicalMin, caps->LogicalMax, caps->PhysicalMin, caps->PhysicalMax ); +} + +struct hid_caps +{ + enum { LINK_COLLECTION_NODE, BUTTON_CAPS, VALUE_CAPS } type; + union + { + HIDP_LINK_COLLECTION_NODE *node; + HIDP_BUTTON_CAPS *button; + HIDP_VALUE_CAPS *value; + }; +}; + +static inline const char *debugstr_hid_caps( struct hid_caps *caps ) +{ + switch (caps->type) + { + case LINK_COLLECTION_NODE: + return debugstr_hidp_link_collection_node( caps->node ); + case BUTTON_CAPS: + return debugstr_hidp_button_caps( caps->button ); + case VALUE_CAPS: + return debugstr_hidp_value_caps( caps->value ); + } + + return "(unknown type)"; +} + struct hid_joystick { IDirectInputDeviceImpl base; @@ -60,6 +147,11 @@ struct hid_joystick WCHAR device_path[MAX_PATH]; HIDD_ATTRIBUTES attrs; DIDEVCAPS dev_caps; + HIDP_CAPS caps; + + HIDP_LINK_COLLECTION_NODE *collection_nodes; + HIDP_BUTTON_CAPS *input_button_caps; + HIDP_VALUE_CAPS *input_value_caps; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -68,6 +160,242 @@ static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputD struct hid_joystick, base ); }
+static const GUID *object_usage_to_guid( USAGE usage_page, USAGE usage ) +{ + switch (usage_page) + { + case HID_USAGE_PAGE_BUTTON: return &GUID_Button; + case HID_USAGE_PAGE_GENERIC: + switch (usage) + { + case HID_USAGE_GENERIC_X: return &GUID_XAxis; + case HID_USAGE_GENERIC_Y: return &GUID_YAxis; + case HID_USAGE_GENERIC_Z: return &GUID_ZAxis; + case HID_USAGE_GENERIC_RX: return &GUID_RxAxis; + case HID_USAGE_GENERIC_RY: return &GUID_RyAxis; + case HID_USAGE_GENERIC_RZ: return &GUID_RzAxis; + case HID_USAGE_GENERIC_SLIDER: return &GUID_Slider; + case HID_USAGE_GENERIC_HATSWITCH: return &GUID_POV; + break; + } + } + + return &GUID_Unknown; +} + +typedef BOOL (*enum_object_callback)( struct hid_joystick *impl, struct hid_caps *caps, DIDEVICEOBJECTINSTANCEW *instance, void *data ); + +static BOOL enum_object( struct hid_joystick *impl, const DIPROPHEADER *filter, DWORD flags, + enum_object_callback callback, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + if (flags != DIDFT_ALL && !(flags & DIDFT_GETTYPE( instance->dwType ))) return DIENUM_CONTINUE; + + switch (filter->dwHow) + { + case DIPH_DEVICE: + return callback( impl, caps, instance, data ); + case DIPH_BYOFFSET: + if (filter->dwObj != instance->dwOfs) return DIENUM_CONTINUE; + return callback( impl, caps, instance, data ); + case DIPH_BYID: + if ((filter->dwObj & 0x00ffffff) != (instance->dwType & 0x00ffffff)) return DIENUM_CONTINUE; + return callback( impl, caps, instance, data ); + case DIPH_BYUSAGE: + if (LOWORD(filter->dwObj) != instance->wUsagePage || HIWORD(filter->dwObj) != instance->wUsage) return DIENUM_CONTINUE; + return callback( impl, caps, instance, data ); + default: + FIXME( "unimplemented filter dwHow %#x dwObj %#x\n", filter->dwHow, filter->dwObj ); + break; + } + + return DIENUM_CONTINUE; +} + +static BOOL enum_value_objects( struct hid_joystick *impl, const DIPROPHEADER *filter, + DWORD flags, enum_object_callback callback, void *data ) +{ + DIDEVICEOBJECTINSTANCEW instance = {.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW)}; + struct hid_caps caps = {.type = VALUE_CAPS}; + DWORD axis = 0, pov = 0, i; + BOOL ret; + + for (i = 0; i < impl->caps.NumberInputValueCaps; ++i) + { + caps.value = impl->input_value_caps + i; + + if (caps.value->UsagePage >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN) + TRACE( "Ignoring input value %s, vendor specific.\n", debugstr_hid_caps( &caps ) ); + else if (caps.value->IsAlias) + TRACE( "Ignoring input value %s, aliased.\n", debugstr_hid_caps( &caps ) ); + else if (caps.value->IsRange) + FIXME( "Ignoring input value %s, usage range not implemented.\n", debugstr_hid_caps( &caps ) ); + else if (caps.value->ReportCount > 1) + FIXME( "Ignoring input value %s, array not implemented.\n", debugstr_hid_caps( &caps ) ); + else if (caps.value->UsagePage != HID_USAGE_PAGE_GENERIC) + TRACE( "Ignoring input value %s, usage page not implemented.\n", debugstr_hid_caps( &caps ) ); + else + { + instance.wUsagePage = caps.value->UsagePage; + instance.wUsage = caps.value->NotRange.Usage; + instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage ); + instance.wReportId = caps.value->ReportID; + + switch (instance.wUsage) + { + case HID_USAGE_GENERIC_X: + instance.dwOfs = DIJOFS_X; + instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( axis++ ); + instance.dwFlags = DIDOI_ASPECTPOSITION; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + case HID_USAGE_GENERIC_Y: + instance.dwOfs = DIJOFS_Y; + instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( axis++ ); + instance.dwFlags = DIDOI_ASPECTPOSITION; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + case HID_USAGE_GENERIC_Z: + instance.dwOfs = DIJOFS_Z; + instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( axis++ ); + instance.dwFlags = DIDOI_ASPECTPOSITION; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + case HID_USAGE_GENERIC_RX: + instance.dwOfs = DIJOFS_RX; + instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( axis++ ); + instance.dwFlags = DIDOI_ASPECTPOSITION; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + case HID_USAGE_GENERIC_RY: + instance.dwOfs = DIJOFS_RY; + instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( axis++ ); + instance.dwFlags = DIDOI_ASPECTPOSITION; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + case HID_USAGE_GENERIC_RZ: + instance.dwOfs = DIJOFS_RZ; + instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( axis++ ); + instance.dwFlags = DIDOI_ASPECTPOSITION; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + case HID_USAGE_GENERIC_SLIDER: + instance.dwOfs = DIJOFS_SLIDER( 0 ); + instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( axis++ ); + instance.dwFlags = DIDOI_ASPECTPOSITION; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + case HID_USAGE_GENERIC_HATSWITCH: + instance.dwOfs = DIJOFS_POV( 0 ); + instance.dwType = DIDFT_POV | DIDFT_MAKEINSTANCE( pov++ ); + instance.dwFlags = 0; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + break; + default: + FIXME( "Ignoring input value %s, usage not implemented.\n", debugstr_hid_caps( &caps ) ); + break; + } + } + } + + return DIENUM_CONTINUE; +} + +static BOOL enum_button_objects( struct hid_joystick *impl, const DIPROPHEADER *filter, + DWORD flags, enum_object_callback callback, void *data ) +{ + DIDEVICEOBJECTINSTANCEW instance = {.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW)}; + struct hid_caps caps = {.type = BUTTON_CAPS}; + DWORD button = 0, i, j; + BOOL ret; + + for (i = 0; i < impl->caps.NumberInputButtonCaps; ++i) + { + caps.button = impl->input_button_caps + i; + + if (caps.button->UsagePage >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN) + TRACE( "Ignoring input button %s, vendor specific.\n", debugstr_hid_caps( &caps ) ); + else if (caps.button->IsAlias) + TRACE( "Ignoring input button %s, aliased.\n", debugstr_hid_caps( &caps ) ); + else if (caps.button->UsagePage != HID_USAGE_PAGE_BUTTON) + TRACE( "Ignoring input button %s, usage page not implemented.\n", debugstr_hid_caps( &caps ) ); + else if (caps.button->IsRange) + { + if (caps.button->NotRange.Usage >= 128) + FIXME( "Ignoring input button %s, too many buttons.\n", debugstr_hid_caps( &caps ) ); + else for (j = caps.button->Range.UsageMin; j <= caps.button->Range.UsageMax; ++j) + { + instance.dwOfs = DIJOFS_BUTTON( j - 1 ); + instance.dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( button++ ); + instance.dwFlags = 0; + instance.wUsagePage = caps.button->UsagePage; + instance.wUsage = j; + instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage ); + instance.wReportId = caps.button->ReportID; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + } + } + else if (caps.button->NotRange.Usage >= 128) + FIXME( "Ignoring input button %s, too many buttons.\n", debugstr_hid_caps( &caps ) ); + else + { + instance.dwOfs = DIJOFS_BUTTON( caps.button->NotRange.Usage - 1 ); + instance.dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( button++ ); + instance.dwFlags = 0; + instance.wUsagePage = caps.button->UsagePage; + instance.wUsage = caps.button->NotRange.Usage; + instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage ); + instance.wReportId = caps.button->ReportID; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + } + } + + return DIENUM_CONTINUE; +} + +static BOOL enum_collections_objects( struct hid_joystick *impl, const DIPROPHEADER *filter, DWORD flags, + enum_object_callback callback, void *data ) +{ + DIDEVICEOBJECTINSTANCEW instance = {.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW)}; + struct hid_caps caps = {.type = LINK_COLLECTION_NODE}; + BOOL ret; + DWORD i; + + for (i = 0; i < impl->caps.NumberLinkCollectionNodes; ++i) + { + caps.node = impl->collection_nodes + i; + + if (caps.node->LinkUsagePage >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN) + TRACE( "Ignoring collection %s, vendor specific.\n", debugstr_hid_caps( &caps ) ); + else if (caps.node->IsAlias) + TRACE( "Ignoring collection %s, aliased.\n", debugstr_hid_caps( &caps ) ); + else + { + instance.dwOfs = 0; + instance.dwType = DIDFT_COLLECTION | DIDFT_NODATA; + instance.dwFlags = 0; + instance.wUsagePage = caps.node->LinkUsagePage; + instance.wUsage = caps.node->LinkUsage; + instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage ); + instance.wReportId = 0; + ret = enum_object( impl, filter, flags, callback, &caps, &instance, data ); + if (ret != DIENUM_CONTINUE) return ret; + } + } + + return DIENUM_CONTINUE; +} + static ULONG WINAPI hid_joystick_Release( IDirectInputDevice8W *iface ) { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); @@ -76,6 +404,9 @@ static ULONG WINAPI hid_joystick_Release( IDirectInputDevice8W *iface )
if (!(ref = IDirectInputDevice2WImpl_Release( iface ))) { + HeapFree( GetProcessHeap(), 0, tmp.input_value_caps ); + HeapFree( GetProcessHeap(), 0, tmp.input_button_caps ); + HeapFree( GetProcessHeap(), 0, tmp.collection_nodes ); HidD_FreePreparsedData( tmp.preparsed ); CloseHandle( tmp.device ); } @@ -372,8 +703,44 @@ static HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTAN return DI_OK; }
+static BOOL init_objects( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + DIDATAFORMAT *format = impl->base.data_format.wine_df; + + format->dwNumObjs++; + if (instance->dwType & DIDFT_PSHBUTTON) impl->dev_caps.dwButtons++; + if (instance->dwType & DIDFT_AXIS) impl->dev_caps.dwAxes++; + if (instance->dwType & DIDFT_POV) impl->dev_caps.dwPOVs++; + + return DIENUM_CONTINUE; +} + +static BOOL init_data_format( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + DIDATAFORMAT *format = impl->base.data_format.wine_df; + DIOBJECTDATAFORMAT *obj_format; + DWORD *index = data; + + obj_format = format->rgodf + *index; + obj_format->pguid = object_usage_to_guid( instance->wUsagePage, instance->wUsage ); + obj_format->dwOfs = instance->dwOfs; + obj_format->dwType = instance->dwType; + obj_format->dwFlags = instance->dwFlags; + (*index)++; + + return DIENUM_CONTINUE; +} + static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID *guid, IDirectInputDevice8W **out ) { + static const DIPROPHEADER filter = + { + .dwSize = sizeof(filter), + .dwHeaderSize = sizeof(filter), + .dwHow = DIPH_DEVICE, + }; DIDEVICEINSTANCEW instance = { .dwSize = sizeof(instance), @@ -381,8 +748,13 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID .guidInstance = *guid }; HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)}; + HIDP_LINK_COLLECTION_NODE *nodes; struct hid_joystick *impl = NULL; - HIDP_CAPS caps; + DIDATAFORMAT *format = NULL; + HIDP_BUTTON_CAPS *buttons; + HIDP_VALUE_CAPS *values; + DWORD size, index; + NTSTATUS status; HRESULT hr;
TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out ); @@ -404,7 +776,7 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
hr = hid_joystick_device_open( -1, &instance, impl->device_path, &impl->device, &impl->preparsed, - &attrs, &caps, dinput->dwVersion ); + &attrs, &impl->caps, dinput->dwVersion ); if (hr != DI_OK) goto failed;
impl->instance = instance; @@ -413,6 +785,47 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID impl->dev_caps.dwFlags = DIDC_ATTACHED | DIDC_EMULATED; impl->dev_caps.dwDevType = instance.dwDevType;
+ size = impl->caps.NumberLinkCollectionNodes * sizeof(HIDP_LINK_COLLECTION_NODE); + if (!(nodes = HeapAlloc( GetProcessHeap(), 0, size ))) goto failed; + impl->collection_nodes = nodes; + size = impl->caps.NumberInputButtonCaps * sizeof(HIDP_BUTTON_CAPS); + if (!(buttons = HeapAlloc( GetProcessHeap(), 0, size ))) goto failed; + impl->input_button_caps = buttons; + size = impl->caps.NumberInputValueCaps * sizeof(HIDP_VALUE_CAPS); + if (!(values = HeapAlloc( GetProcessHeap(), 0, size ))) goto failed; + impl->input_value_caps = values; + + size = impl->caps.NumberLinkCollectionNodes; + status = HidP_GetLinkCollectionNodes( nodes, &size, impl->preparsed ); + if (status != HIDP_STATUS_SUCCESS) goto failed; + impl->caps.NumberLinkCollectionNodes = size; + status = HidP_GetButtonCaps( HidP_Input, impl->input_button_caps, + &impl->caps.NumberInputButtonCaps, impl->preparsed ); + if (status != HIDP_STATUS_SUCCESS && status != HIDP_STATUS_USAGE_NOT_FOUND) goto failed; + status = HidP_GetValueCaps( HidP_Input, impl->input_value_caps, + &impl->caps.NumberInputValueCaps, impl->preparsed ); + if (status != HIDP_STATUS_SUCCESS && status != HIDP_STATUS_USAGE_NOT_FOUND) goto failed; + + /* enumerate collections first, so we can find report collections */ + enum_collections_objects( impl, &filter, DIDFT_ALL, init_objects, NULL ); + enum_value_objects( impl, &filter, DIDFT_ALL, init_objects, NULL ); + enum_button_objects( impl, &filter, DIDFT_ALL, init_objects, NULL ); + + format = impl->base.data_format.wine_df; + size = format->dwNumObjs * sizeof(*format->rgodf); + if (!(format->rgodf = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto failed; + format->dwSize = sizeof(*format); + format->dwObjSize = sizeof(*format->rgodf); + format->dwFlags = DIDF_ABSAXIS; + format->dwDataSize = sizeof(impl->state); + + index = 0; + enum_value_objects( impl, &filter, DIDFT_ALL, init_data_format, &index ); + enum_button_objects( impl, &filter, DIDFT_ALL, init_data_format, &index ); + enum_collections_objects( impl, &filter, DIDFT_ALL, init_data_format, &index ); + + _dump_DIDATAFORMAT( impl->base.data_format.wine_df ); + *out = &impl->base.IDirectInputDevice8W_iface; return DI_OK;
For DIPROP_RANGE property on POVs and Axes, and initialize it on creation.
This uses the value caps physical range, as it's exactly the purpose and we don't use it for anything else otherwise.
We'll scale values from their logical range directly anyway to save a conversion.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 64 +++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 5f709a7c30a..db44cd6b4db 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -427,6 +427,16 @@ static HRESULT WINAPI hid_joystick_GetCapabilities( IDirectInputDevice8W *iface, return DI_OK; }
+static BOOL get_property_prop_range( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + HIDP_VALUE_CAPS *value_caps = caps->value; + DIPROPRANGE *value = data; + value->lMin = value_caps->PhysicalMin; + value->lMax = value_caps->PhysicalMax; + return DIENUM_CONTINUE; +} + static HRESULT WINAPI hid_joystick_GetProperty( IDirectInputDevice8W *iface, const GUID *guid, DIPROPHEADER *header ) { @@ -439,6 +449,9 @@ static HRESULT WINAPI hid_joystick_GetProperty( IDirectInputDevice8W *iface, con
switch (LOWORD( guid )) { + case (DWORD_PTR)DIPROP_RANGE: + enum_value_objects( impl, header, DIDFT_AXIS, get_property_prop_range, header ); + return DI_OK; case (DWORD_PTR)DIPROP_PRODUCTNAME: { DIPROPSTRING *value = (DIPROPSTRING *)header; @@ -477,6 +490,41 @@ static HRESULT WINAPI hid_joystick_GetProperty( IDirectInputDevice8W *iface, con return DI_OK; }
+static BOOL set_property_prop_range( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + HIDP_VALUE_CAPS *value_caps = caps->value; + DIPROPRANGE *value = data; + LONG range = value_caps->LogicalMax - value_caps->LogicalMin; + value_caps->PhysicalMin = value->lMin; + value_caps->PhysicalMax = value->lMax; + if (instance->dwType & DIDFT_POV && range > 0) + value_caps->PhysicalMax -= value->lMax / (range + 1); + return DIENUM_CONTINUE; +} + +static HRESULT WINAPI hid_joystick_SetProperty( IDirectInputDevice8W *iface, const GUID *guid, + const DIPROPHEADER *header ) +{ + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + + TRACE( "iface %p, guid %s, header %p\n", iface, debugstr_guid( guid ), header ); + + if (!header) return DIERR_INVALIDPARAM; + if (!IS_DIPROP( guid )) return DI_OK; + + switch (LOWORD( guid )) + { + case (DWORD_PTR)DIPROP_RANGE: + enum_value_objects( impl, header, DIDFT_AXIS, set_property_prop_range, (void *)header ); + return DI_OK; + default: + return IDirectInputDevice2WImpl_SetProperty( iface, guid, header ); + } + + return DI_OK; +} + static HRESULT WINAPI hid_joystick_GetDeviceState( IDirectInputDevice8W *iface, DWORD len, void *ptr ) { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); @@ -536,7 +584,7 @@ static const IDirectInputDevice8WVtbl hid_joystick_vtbl = hid_joystick_GetCapabilities, IDirectInputDevice2WImpl_EnumObjects, hid_joystick_GetProperty, - IDirectInputDevice2WImpl_SetProperty, + hid_joystick_SetProperty, IDirectInputDevice2WImpl_Acquire, IDirectInputDevice2WImpl_Unacquire, hid_joystick_GetDeviceState, @@ -747,6 +795,15 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID .guidProduct = *guid, .guidInstance = *guid }; + DIPROPRANGE range = + { + .diph = + { + .dwSize = sizeof(range), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)}; HIDP_LINK_COLLECTION_NODE *nodes; struct hid_joystick *impl = NULL; @@ -826,6 +883,11 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID
_dump_DIDATAFORMAT( impl->base.data_format.wine_df );
+ range.lMax = 65535; + enum_value_objects( impl, &range.diph, DIDFT_AXIS, set_property_prop_range, &range ); + range.lMax = 36000; + enum_value_objects( impl, &range.diph, DIDFT_POV, set_property_prop_range, &range ); + *out = &impl->base.IDirectInputDevice8W_iface; return DI_OK;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 39 +++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index db44cd6b4db..0bbe5f0ef33 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -536,6 +536,43 @@ static HRESULT WINAPI hid_joystick_GetDeviceState( IDirectInputDevice8W *iface, return DI_OK; }
+static BOOL get_object_info( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + DIDEVICEOBJECTINSTANCEW *dest = data; + memcpy( dest, instance, dest->dwSize ); + return DIENUM_STOP; +} + +static HRESULT WINAPI hid_joystick_GetObjectInfo( IDirectInputDevice8W *iface, DIDEVICEOBJECTINSTANCEW *instance, + DWORD obj, DWORD how ) +{ + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + const DIPROPHEADER filter = + { + .dwSize = sizeof(filter), + .dwHeaderSize = sizeof(filter), + .dwHow = how, + .dwObj = obj + }; + BOOL ret; + + TRACE( "iface %p, instance %p, obj %#x, how %#x.\n", iface, instance, obj, how ); + + if (!instance) return E_POINTER; + if (instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3W) && + instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCEW)) + return DIERR_INVALIDPARAM; + + ret = enum_value_objects( impl, &filter, DIDFT_ALL, get_object_info, NULL ); + if (ret != DIENUM_CONTINUE) return S_OK; + ret = enum_button_objects( impl, &filter, DIDFT_ALL, get_object_info, NULL ); + if (ret != DIENUM_CONTINUE) return S_OK; + enum_collections_objects( impl, &filter, DIDFT_ALL, get_object_info, NULL ); + + return S_OK; +} + static HRESULT WINAPI hid_joystick_GetDeviceInfo( IDirectInputDevice8W *iface, DIDEVICEINSTANCEW *instance ) { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); @@ -592,7 +629,7 @@ static const IDirectInputDevice8WVtbl hid_joystick_vtbl = IDirectInputDevice2WImpl_SetDataFormat, IDirectInputDevice2WImpl_SetEventNotification, IDirectInputDevice2WImpl_SetCooperativeLevel, - IDirectInputDevice2WImpl_GetObjectInfo, + hid_joystick_GetObjectInfo, hid_joystick_GetDeviceInfo, IDirectInputDevice2WImpl_RunControlPanel, IDirectInputDevice2WImpl_Initialize,
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 0bbe5f0ef33..ea0ab2b8f0d 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -427,6 +427,49 @@ static HRESULT WINAPI hid_joystick_GetCapabilities( IDirectInputDevice8W *iface, return DI_OK; }
+struct enum_objects_params +{ + LPDIENUMDEVICEOBJECTSCALLBACKW callback; + void *context; +}; + +static BOOL enum_objects_callback( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + struct enum_objects_params *params = data; + return params->callback( instance, params->context ); +} + +static HRESULT WINAPI hid_joystick_EnumObjects( IDirectInputDevice8W *iface, LPDIENUMDEVICEOBJECTSCALLBACKW callback, + void *context, DWORD flags ) +{ + static const DIPROPHEADER filter = + { + .dwSize = sizeof(filter), + .dwHeaderSize = sizeof(filter), + .dwHow = DIPH_DEVICE, + }; + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + struct enum_objects_params params = + { + .callback = callback, + .context = context, + }; + BOOL ret; + + TRACE( "iface %p, callback %p, context %p, flags %#x.\n", iface, callback, context, flags ); + + if (!callback) return DIERR_INVALIDPARAM; + + ret = enum_value_objects( impl, &filter, flags, enum_objects_callback, ¶ms ); + if (ret != DIENUM_CONTINUE) return S_OK; + ret = enum_button_objects( impl, &filter, flags, enum_objects_callback, ¶ms ); + if (ret != DIENUM_CONTINUE) return S_OK; + enum_collections_objects( impl, &filter, flags, enum_objects_callback, ¶ms ); + + return S_OK; +} + static BOOL get_property_prop_range( struct hid_joystick *impl, struct hid_caps *caps, DIDEVICEOBJECTINSTANCEW *instance, void *data ) { @@ -619,7 +662,7 @@ static const IDirectInputDevice8WVtbl hid_joystick_vtbl = hid_joystick_Release, /*** IDirectInputDevice methods ***/ hid_joystick_GetCapabilities, - IDirectInputDevice2WImpl_EnumObjects, + hid_joystick_EnumObjects, hid_joystick_GetProperty, hid_joystick_SetProperty, IDirectInputDevice2WImpl_Acquire,
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
This series makes the new dinput(8) HID joystick usable for input, (at least from the joy.cpl perspective).
It's a bit long but hopefully most patches are trivial, and it may be interesting to have it for today's release.
dlls/dinput/device.c | 2 + dlls/dinput/device_private.h | 6 + dlls/dinput/dinput_main.c | 36 ++++- dlls/dinput/joystick_hid.c | 245 ++++++++++++++++++++++++++++++++++- 4 files changed, 285 insertions(+), 4 deletions(-)
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index 25084f82a9c..6d4c630fb23 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -1725,6 +1725,8 @@ HRESULT direct_input_device_alloc( SIZE_T size, const IDirectInputDevice8WVtbl * InitializeCriticalSection( &This->crit ); This->dinput = dinput; IDirectInput_AddRef( &dinput->IDirectInput7A_iface ); + This->read_event = NULL; + This->read_callback = NULL;
*out = This; return DI_OK; diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h index 1811e0cc5a6..0e72ee77bf1 100644 --- a/dlls/dinput/device_private.h +++ b/dlls/dinput/device_private.h @@ -53,6 +53,8 @@ typedef struct UINT_PTR uAppData; } ActionMap;
+typedef HRESULT dinput_device_read_state( IDirectInputDevice8W *iface ); + /* Device implementation */ typedef struct IDirectInputDeviceImpl IDirectInputDeviceImpl; struct IDirectInputDeviceImpl @@ -84,6 +86,10 @@ struct IDirectInputDeviceImpl /* Action mapping */ int num_actions; /* number of actions mapped */ ActionMap *action_map; /* array of mappings */ + + /* internal device file reading */ + HANDLE read_event; + dinput_device_read_state *read_callback; };
extern HRESULT direct_input_device_alloc( SIZE_T size, const IDirectInputDevice8WVtbl *vtbl, const GUID *guid, diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index 88ee1855675..cbedc9f0880 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -1289,6 +1289,10 @@ static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam static DWORD WINAPI hook_thread_proc(void *param) { static HHOOK kbd_hook, mouse_hook; + IDirectInputDeviceImpl *dev; + SIZE_T events_count = 0; + HANDLE events[128]; + DWORD ret; MSG msg;
di_em_win = CreateWindowW( di_em_win_w, di_em_win_w, 0, 0, 0, 0, 0, @@ -1298,10 +1302,33 @@ static DWORD WINAPI hook_thread_proc(void *param) PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ); SetEvent(param);
- while (GetMessageW( &msg, 0, 0, 0 )) + while ((ret = MsgWaitForMultipleObjectsEx( events_count, events, INFINITE, QS_ALLINPUT, 0 )) <= events_count) { + IDirectInputDevice8W *iface = NULL; UINT kbd_cnt = 0, mice_cnt = 0;
+ if (ret < events_count) + { + EnterCriticalSection( &dinput_hook_crit ); + LIST_FOR_EACH_ENTRY( dev, &acquired_device_list, IDirectInputDeviceImpl, entry ) + { + if (dev->read_event == events[ret]) + { + iface = &dev->IDirectInputDevice8W_iface; + IDirectInputDevice8_AddRef( iface ); + break; + } + } + LeaveCriticalSection( &dinput_hook_crit ); + + if (iface) + { + dev->read_callback( iface ); + IDirectInputDevice8_Release( iface ); + } + } + + if (!PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) continue; if (msg.message == WM_USER+0x10) { HANDLE finished_event = (HANDLE)msg.lParam; @@ -1316,9 +1343,16 @@ static DWORD WINAPI hook_thread_proc(void *param) break; }
+ events_count = 0; EnterCriticalSection( &dinput_hook_crit ); kbd_cnt = list_count( &acquired_keyboard_list ); mice_cnt = list_count( &acquired_mouse_list ); + LIST_FOR_EACH_ENTRY( dev, &acquired_device_list, IDirectInputDeviceImpl, entry ) + { + if (!dev->read_event || !dev->read_callback) continue; + if (events_count >= ARRAY_SIZE(events)) break; + events[events_count++] = dev->read_event; + } LeaveCriticalSection( &dinput_hook_crit );
if (kbd_cnt && !kbd_hook) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index ea0ab2b8f0d..82458570c82 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -141,6 +141,7 @@ struct hid_joystick DIJOYSTATE2 state;
HANDLE device; + OVERLAPPED read_ovl; PHIDP_PREPARSED_DATA preparsed;
DIDEVICEINSTANCEW instance; @@ -152,6 +153,12 @@ struct hid_joystick HIDP_LINK_COLLECTION_NODE *collection_nodes; HIDP_BUTTON_CAPS *input_button_caps; HIDP_VALUE_CAPS *input_value_caps; + + char *input_report_buf; + USAGE_AND_PAGE *usages_buf; + ULONG usages_count; + + BYTE device_state_report_id; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -404,10 +411,14 @@ static ULONG WINAPI hid_joystick_Release( IDirectInputDevice8W *iface )
if (!(ref = IDirectInputDevice2WImpl_Release( iface ))) { + HeapFree( GetProcessHeap(), 0, tmp.usages_buf ); + HeapFree( GetProcessHeap(), 0, tmp.input_report_buf ); HeapFree( GetProcessHeap(), 0, tmp.input_value_caps ); HeapFree( GetProcessHeap(), 0, tmp.input_button_caps ); HeapFree( GetProcessHeap(), 0, tmp.collection_nodes ); HidD_FreePreparsedData( tmp.preparsed ); + CancelIoEx( tmp.device, &tmp.read_ovl ); + CloseHandle( tmp.base.read_event ); CloseHandle( tmp.device ); }
@@ -568,6 +579,40 @@ static HRESULT WINAPI hid_joystick_SetProperty( IDirectInputDevice8W *iface, con return DI_OK; }
+static HRESULT WINAPI hid_joystick_Acquire( IDirectInputDevice8W *iface ) +{ + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + ULONG report_len = impl->caps.InputReportByteLength; + HRESULT hr; + + TRACE( "iface %p.\n", iface ); + + if ((hr = IDirectInputDevice2WImpl_Acquire( iface )) != DI_OK) return hr; + + memset( &impl->read_ovl, 0, sizeof(impl->read_ovl) ); + impl->read_ovl.hEvent = impl->base.read_event; + if (ReadFile( impl->device, impl->input_report_buf, report_len, NULL, &impl->read_ovl )) + impl->base.read_callback( iface ); + + return DI_OK; +} + +static HRESULT WINAPI hid_joystick_Unacquire( IDirectInputDevice8W *iface ) +{ + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + HRESULT hr; + BOOL ret; + + TRACE( "iface %p.\n", iface ); + + if ((hr = IDirectInputDevice2WImpl_Unacquire( iface )) != DI_OK) return hr; + + ret = CancelIoEx( impl->device, &impl->read_ovl ); + if (!ret) WARN( "CancelIoEx failed, last error %u\n", GetLastError() ); + + return DI_OK; +} + static HRESULT WINAPI hid_joystick_GetDeviceState( IDirectInputDevice8W *iface, DWORD len, void *ptr ) { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); @@ -665,8 +710,8 @@ static const IDirectInputDevice8WVtbl hid_joystick_vtbl = hid_joystick_EnumObjects, hid_joystick_GetProperty, hid_joystick_SetProperty, - IDirectInputDevice2WImpl_Acquire, - IDirectInputDevice2WImpl_Unacquire, + hid_joystick_Acquire, + hid_joystick_Unacquire, hid_joystick_GetDeviceState, IDirectInputDevice2WImpl_GetDeviceData, IDirectInputDevice2WImpl_SetDataFormat, @@ -695,6 +740,183 @@ static const IDirectInputDevice8WVtbl hid_joystick_vtbl = IDirectInputDevice8WImpl_GetImageInfo, };
+struct parse_device_state_params +{ + DIJOYSTATE2 old_state; + DWORD time; + DWORD seq; +}; + +static BOOL check_device_state_button( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + IDirectInputDevice8W *iface = &impl->base.IDirectInputDevice8W_iface; + struct parse_device_state_params *params = data; + DWORD i = DIDFT_GETINSTANCE( instance->dwType ); + + if (!(instance->dwType & DIDFT_BUTTON)) + FIXME( "unexpected object type %#x, expected DIDFT_BUTTON\n", instance->dwType ); + else if (params->old_state.rgbButtons[i] != impl->state.rgbButtons[i]) + queue_event( iface, instance->dwType, impl->state.rgbButtons[i], params->time, params->seq ); + + return DIENUM_CONTINUE; +} + +static LONG sign_extend( ULONG value, const HIDP_VALUE_CAPS *caps ) +{ + UINT sign = 1 << (caps->BitSize - 1); + if (sign <= 1 || caps->LogicalMin >= 0) return value; + return value - ((value & sign) << 1); +} + +static LONG scale_value( ULONG value, const HIDP_VALUE_CAPS *caps, LONG min, LONG max ) +{ + ULONG bit_max = (1 << caps->BitSize) - 1; + LONG tmp = sign_extend( value, caps ); + + /* xinput HID gamepad have bogus logical value range, let's use the bit range instead */ + if (caps->LogicalMin == 0 && caps->LogicalMax == -1) return min + MulDiv( tmp, max - min, bit_max ); + if (caps->LogicalMin > tmp || caps->LogicalMax < tmp) return -1; /* invalid / null value */ + return min + MulDiv( tmp - caps->LogicalMin, max - min, caps->LogicalMax - caps->LogicalMin ); +} + +static BOOL read_device_state_value( struct hid_joystick *impl, struct hid_caps *caps, + DIDEVICEOBJECTINSTANCEW *instance, void *data ) +{ + IDirectInputDevice8W *iface = &impl->base.IDirectInputDevice8W_iface; + ULONG logical_value, report_len = impl->caps.InputReportByteLength; + struct parse_device_state_params *params = data; + char *report_buf = impl->input_report_buf; + HIDP_VALUE_CAPS *value_caps = caps->value; + NTSTATUS status; + LONG value; + + if (!(instance->dwType & (DIDFT_POV | DIDFT_AXIS))) + FIXME( "unexpected object type %#x, expected DIDFT_POV | DIDFT_AXIS\n", instance->dwType ); + else + { + status = HidP_GetUsageValue( HidP_Input, instance->wUsagePage, 0, instance->wUsage, + &logical_value, impl->preparsed, report_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsageValue %04x:%04x returned %#x\n", + instance->wUsagePage, instance->wUsage, status ); + value = scale_value( logical_value, value_caps, value_caps->PhysicalMin, value_caps->PhysicalMax ); + + switch (instance->dwOfs) + { + case DIJOFS_X: + if (impl->state.lX == value) break; + impl->state.lX = value; + queue_event( iface, instance->dwType, value, params->time, params->seq ); + break; + case DIJOFS_Y: + if (impl->state.lY == value) break; + impl->state.lY = value; + queue_event( iface, instance->dwType, value, params->time, params->seq ); + break; + case DIJOFS_Z: + if (impl->state.lZ == value) break; + impl->state.lZ = value; + queue_event( iface, instance->dwType, value, params->time, params->seq ); + break; + case DIJOFS_RX: + if (impl->state.lRx == value) break; + impl->state.lRx = value; + queue_event( iface, instance->dwType, value, params->time, params->seq ); + break; + case DIJOFS_RY: + if (impl->state.lRy == value) break; + impl->state.lRy = value; + queue_event( iface, instance->dwType, value, params->time, params->seq ); + break; + case DIJOFS_RZ: + if (impl->state.lRz == value) break; + impl->state.lRz = value; + queue_event( iface, instance->dwType, value, params->time, params->seq ); + break; + case DIJOFS_POV( 0 ): + if (impl->state.rgdwPOV[0] == value) break; + impl->state.rgdwPOV[0] = value; + queue_event( iface, instance->dwType, value, params->time, params->seq ); + break; + default: + FIXME( "unimplemented offset %#x.\n", instance->dwOfs ); + break; + } + } + + return DIENUM_CONTINUE; +} + +static HRESULT hid_joystick_read_state( IDirectInputDevice8W *iface ) +{ + static const DIPROPHEADER filter = + { + .dwSize = sizeof(filter), + .dwHeaderSize = sizeof(filter), + .dwHow = DIPH_DEVICE, + }; + struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); + ULONG i, count, report_len = impl->caps.InputReportByteLength; + struct parse_device_state_params params = {0}; + char *report_buf = impl->input_report_buf; + USAGE_AND_PAGE *usages; + NTSTATUS status; + BOOL ret; + + ret = GetOverlappedResult( impl->device, &impl->read_ovl, &count, FALSE ); + if (!ret) WARN( "ReadFile failed, error %u\n", GetLastError() ); + else if (TRACE_ON(dinput)) + { + TRACE( "read size %u report:\n", count ); + for (i = 0; i < report_len;) + { + char buffer[256], *buf = buffer; + buf += sprintf(buf, "%08x ", i); + do + { + buf += sprintf(buf, " %02x", (BYTE)report_buf[i] ); + } while (++i % 16 && i < report_len); + TRACE("%s\n", buffer); + } + } + + do + { + count = impl->usages_count; + memset( impl->usages_buf, 0, count * sizeof(*impl->usages_buf) ); + status = HidP_GetUsagesEx( HidP_Input, 0, impl->usages_buf, &count, + impl->preparsed, report_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsagesEx returned %#x\n", status ); + + if (report_buf[0] == impl->device_state_report_id) + { + params.old_state = impl->state; + params.time = GetCurrentTime(); + params.seq = impl->base.dinput->evsequence++; + + memset( impl->state.rgbButtons, 0, sizeof(impl->state.rgbButtons) ); + while (count--) + { + usages = impl->usages_buf + count; + if (usages->UsagePage != HID_USAGE_PAGE_BUTTON) + FIXME( "unimplemented usage page %x.\n", usages->UsagePage ); + else if (usages->Usage >= 128) + FIXME( "ignoring extraneous button %d.\n", usages->Usage ); + else + impl->state.rgbButtons[usages->Usage - 1] = 0x80; + } + + enum_value_objects( impl, &filter, DIDFT_ALL, read_device_state_value, ¶ms ); + enum_button_objects( impl, &filter, DIDFT_ALL, check_device_state_button, ¶ms ); + } + + memset( &impl->read_ovl, 0, sizeof(impl->read_ovl) ); + impl->read_ovl.hEvent = impl->base.read_event; + } while (ReadFile( impl->device, report_buf, report_len, &count, &impl->read_ovl )); + + return DI_OK; +} + static BOOL hid_joystick_device_try_open( UINT32 handle, const WCHAR *path, HANDLE *device, PHIDP_PREPARSED_DATA *preparsed, HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DIDEVICEINSTANCEW *instance, DWORD version ) @@ -703,7 +925,7 @@ static BOOL hid_joystick_device_try_open( UINT32 handle, const WCHAR *path, HAND HANDLE device_file;
device_file = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, 0 ); + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 0 ); if (device_file == INVALID_HANDLE_VALUE) return FALSE;
if (!HidD_GetPreparsedData( device_file, &preparsed_data )) goto failed; @@ -841,6 +1063,11 @@ static BOOL init_objects( struct hid_joystick *impl, struct hid_caps *caps, if (instance->dwType & DIDFT_AXIS) impl->dev_caps.dwAxes++; if (instance->dwType & DIDFT_POV) impl->dev_caps.dwPOVs++;
+ if (!impl->device_state_report_id) + impl->device_state_report_id = instance->wReportId; + else if (impl->device_state_report_id != instance->wReportId) + FIXME( "multiple device state reports found!\n" ); + return DIENUM_CONTINUE; }
@@ -890,8 +1117,10 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID DIDATAFORMAT *format = NULL; HIDP_BUTTON_CAPS *buttons; HIDP_VALUE_CAPS *values; + USAGE_AND_PAGE *usages; DWORD size, index; NTSTATUS status; + char *buffer; HRESULT hr;
TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out ); @@ -911,6 +1140,8 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID if (FAILED(hr)) return hr; impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_joystick.base.crit"); impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND; + impl->base.read_event = CreateEventA( NULL, FALSE, FALSE, NULL ); + impl->base.read_callback = hid_joystick_read_state;
hr = hid_joystick_device_open( -1, &instance, impl->device_path, &impl->device, &impl->preparsed, &attrs, &impl->caps, dinput->dwVersion ); @@ -932,6 +1163,14 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID if (!(values = HeapAlloc( GetProcessHeap(), 0, size ))) goto failed; impl->input_value_caps = values;
+ size = impl->caps.InputReportByteLength; + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size ))) goto failed; + impl->input_report_buf = buffer; + impl->usages_count = HidP_MaxUsageListLength( HidP_Input, 0, impl->preparsed ); + size = impl->usages_count * sizeof(USAGE_AND_PAGE); + if (!(usages = HeapAlloc( GetProcessHeap(), 0, size ))) goto failed; + impl->usages_buf = usages; + size = impl->caps.NumberLinkCollectionNodes; status = HidP_GetLinkCollectionNodes( nodes, &size, impl->preparsed ); if (status != HIDP_STATUS_SUCCESS) goto failed;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=96456
Your paranoid android.
=== debiant2 (32 bit report) ===
dinput8: device.c:130: Test failed: GetDeviceData() failed: 00000000 device: Timeout
dinput: device: Timeout
=== debiant2 (32 bit Chinese:China report) ===
dinput8: device: Timeout
dinput: device: Timeout
=== debiant2 (32 bit WoW report) ===
dinput8: device: Timeout
dinput: device: Timeout keyboard: Timeout
=== debiant2 (64 bit WoW report) ===
dinput8: device: Timeout
dinput: keyboard: Timeout