1 /* DirectInput HID Joystick device
3 * Copyright 2021 RĂ©mi Bernon for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "ddk/hidsdi.h"
37 #include "wine/debug.h"
39 #include "dinput_private.h"
40 #include "device_private.h"
41 #include "joystick_private.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(dinput
);
48 DEFINE_GUID( hid_joystick_guid
, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 );
49 DEFINE_DEVPROPKEY( DEVPROPKEY_HID_HANDLE
, 0xbc62e415, 0xf4fe, 0x405c, 0x8e, 0xda, 0x63, 0x6f, 0xb5, 0x9f, 0x08, 0x98, 2 );
51 static inline const char *debugstr_hidp_link_collection_node( HIDP_LINK_COLLECTION_NODE
*node
)
53 if (!node
) return "(null)";
54 return wine_dbg_sprintf( "Usg %02x:%02x Par %u Nxt %u Cnt %u Chld %u Type %u Alias %d User %p",
55 node
->LinkUsagePage
, node
->LinkUsage
, node
->Parent
, node
->NextSibling
,
56 node
->NumberOfChildren
, node
->FirstChild
, node
->CollectionType
, node
->IsAlias
,
60 static inline const char *debugstr_hidp_button_caps( HIDP_BUTTON_CAPS
*caps
)
63 if (!caps
) return "(null)";
65 str
= wine_dbg_sprintf( "RId %d,", caps
->ReportID
);
67 str
= wine_dbg_sprintf( "%s Usg %02x:%02x Dat %02x,", str
, caps
->UsagePage
, caps
->NotRange
.Usage
,
68 caps
->NotRange
.DataIndex
);
70 str
= wine_dbg_sprintf( "%s Usg %02x:%02x-%02x Dat %02x-%02x,", str
, caps
->UsagePage
, caps
->Range
.UsageMin
,
71 caps
->Range
.UsageMax
, caps
->Range
.DataIndexMin
, caps
->Range
.DataIndexMax
);
72 if (!caps
->IsStringRange
)
73 str
= wine_dbg_sprintf( "%s Str %d,", str
, caps
->NotRange
.StringIndex
);
75 str
= wine_dbg_sprintf( "%s Str %d-%d,", str
, caps
->Range
.StringMin
, caps
->Range
.StringMax
);
76 if (!caps
->IsDesignatorRange
)
77 str
= wine_dbg_sprintf( "%s Des %d,", str
, caps
->NotRange
.DesignatorIndex
);
79 str
= wine_dbg_sprintf( "%s Des %d-%d,", str
, caps
->Range
.DesignatorMin
, caps
->Range
.DesignatorMax
);
80 return wine_dbg_sprintf( "%s Bits %02x Alias %d Abs %d, LCol %u LUsg %02x-%02x", str
, caps
->BitField
, caps
->IsAlias
,
81 caps
->IsAbsolute
, caps
->LinkCollection
, caps
->LinkUsagePage
, caps
->LinkUsage
);
84 static inline const char *debugstr_hidp_value_caps( HIDP_VALUE_CAPS
*caps
)
87 if (!caps
) return "(null)";
89 str
= wine_dbg_sprintf( "RId %d,", caps
->ReportID
);
91 str
= wine_dbg_sprintf( "%s Usg %02x:%02x Dat %02x,", str
, caps
->UsagePage
, caps
->NotRange
.Usage
,
92 caps
->NotRange
.DataIndex
);
94 str
= wine_dbg_sprintf( "%s Usg %02x:%02x-%02x Dat %02x-%02x,", str
, caps
->UsagePage
, caps
->Range
.UsageMin
,
95 caps
->Range
.UsageMax
, caps
->Range
.DataIndexMin
, caps
->Range
.DataIndexMax
);
96 if (!caps
->IsStringRange
)
97 str
= wine_dbg_sprintf( "%s Str %d,", str
, caps
->NotRange
.StringIndex
);
99 str
= wine_dbg_sprintf( "%s Str %d-%d,", str
, caps
->Range
.StringMin
, caps
->Range
.StringMax
);
100 if (!caps
->IsDesignatorRange
)
101 str
= wine_dbg_sprintf( "%s Des %d,", str
, caps
->NotRange
.DesignatorIndex
);
103 str
= wine_dbg_sprintf( "%s Des %d-%d,", str
, caps
->Range
.DesignatorMin
, caps
->Range
.DesignatorMax
);
104 return wine_dbg_sprintf( "%s Bits %02x Alias %d Abs %d Null %d, LCol %u LUsg %02x-%02x, "
105 "BitSz %d, RCnt %d, Unit %x E%+d, Log %+d-%+d, Phy %+d-%+d",
106 str
, caps
->BitField
, caps
->IsAlias
, caps
->IsAbsolute
, caps
->HasNull
,
107 caps
->LinkCollection
, caps
->LinkUsagePage
, caps
->LinkUsage
,
108 caps
->BitSize
, caps
->ReportCount
, caps
->Units
, caps
->UnitsExp
,
109 caps
->LogicalMin
, caps
->LogicalMax
, caps
->PhysicalMin
, caps
->PhysicalMax
);
114 enum { LINK_COLLECTION_NODE
, BUTTON_CAPS
, VALUE_CAPS
} type
;
117 HIDP_LINK_COLLECTION_NODE
*node
;
118 HIDP_BUTTON_CAPS
*button
;
119 HIDP_VALUE_CAPS
*value
;
123 static inline const char *debugstr_hid_caps( struct hid_caps
*caps
)
127 case LINK_COLLECTION_NODE
:
128 return debugstr_hidp_link_collection_node( caps
->node
);
130 return debugstr_hidp_button_caps( caps
->button
);
132 return debugstr_hidp_value_caps( caps
->value
);
135 return "(unknown type)";
140 IDirectInputDeviceImpl base
;
145 PHIDP_PREPARSED_DATA preparsed
;
147 DIDEVICEINSTANCEW instance
;
148 WCHAR device_path
[MAX_PATH
];
149 HIDD_ATTRIBUTES attrs
;
153 HIDP_LINK_COLLECTION_NODE
*collection_nodes
;
154 HIDP_BUTTON_CAPS
*input_button_caps
;
155 HIDP_VALUE_CAPS
*input_value_caps
;
157 char *input_report_buf
;
158 USAGE_AND_PAGE
*usages_buf
;
161 BYTE device_state_report_id
;
164 static inline struct hid_joystick
*impl_from_IDirectInputDevice8W( IDirectInputDevice8W
*iface
)
166 return CONTAINING_RECORD( CONTAINING_RECORD( iface
, IDirectInputDeviceImpl
, IDirectInputDevice8W_iface
),
167 struct hid_joystick
, base
);
170 static const GUID
*object_usage_to_guid( USAGE usage_page
, USAGE usage
)
174 case HID_USAGE_PAGE_BUTTON
: return &GUID_Button
;
175 case HID_USAGE_PAGE_GENERIC
:
178 case HID_USAGE_GENERIC_X
: return &GUID_XAxis
;
179 case HID_USAGE_GENERIC_Y
: return &GUID_YAxis
;
180 case HID_USAGE_GENERIC_Z
: return &GUID_ZAxis
;
181 case HID_USAGE_GENERIC_RX
: return &GUID_RxAxis
;
182 case HID_USAGE_GENERIC_RY
: return &GUID_RyAxis
;
183 case HID_USAGE_GENERIC_RZ
: return &GUID_RzAxis
;
184 case HID_USAGE_GENERIC_SLIDER
: return &GUID_Slider
;
185 case HID_USAGE_GENERIC_HATSWITCH
: return &GUID_POV
;
190 return &GUID_Unknown
;
193 typedef BOOL (*enum_object_callback
)( struct hid_joystick
*impl
, struct hid_caps
*caps
, DIDEVICEOBJECTINSTANCEW
*instance
, void *data
);
195 static BOOL
enum_object( struct hid_joystick
*impl
, const DIPROPHEADER
*filter
, DWORD flags
,
196 enum_object_callback callback
, struct hid_caps
*caps
,
197 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
199 if (flags
!= DIDFT_ALL
&& !(flags
& DIDFT_GETTYPE( instance
->dwType
))) return DIENUM_CONTINUE
;
201 switch (filter
->dwHow
)
204 return callback( impl
, caps
, instance
, data
);
206 if (filter
->dwObj
!= instance
->dwOfs
) return DIENUM_CONTINUE
;
207 return callback( impl
, caps
, instance
, data
);
209 if ((filter
->dwObj
& 0x00ffffff) != (instance
->dwType
& 0x00ffffff)) return DIENUM_CONTINUE
;
210 return callback( impl
, caps
, instance
, data
);
212 if (LOWORD(filter
->dwObj
) != instance
->wUsagePage
|| HIWORD(filter
->dwObj
) != instance
->wUsage
) return DIENUM_CONTINUE
;
213 return callback( impl
, caps
, instance
, data
);
215 FIXME( "unimplemented filter dwHow %#x dwObj %#x\n", filter
->dwHow
, filter
->dwObj
);
219 return DIENUM_CONTINUE
;
222 static BOOL
enum_value_objects( struct hid_joystick
*impl
, const DIPROPHEADER
*filter
,
223 DWORD flags
, enum_object_callback callback
, void *data
)
225 DIDEVICEOBJECTINSTANCEW instance
= {.dwSize
= sizeof(DIDEVICEOBJECTINSTANCEW
)};
226 struct hid_caps caps
= {.type
= VALUE_CAPS
};
227 DWORD axis
= 0, pov
= 0, i
;
230 for (i
= 0; i
< impl
->caps
.NumberInputValueCaps
; ++i
)
232 caps
.value
= impl
->input_value_caps
+ i
;
234 if (caps
.value
->UsagePage
>= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN
)
235 TRACE( "Ignoring input value %s, vendor specific.\n", debugstr_hid_caps( &caps
) );
236 else if (caps
.value
->IsAlias
)
237 TRACE( "Ignoring input value %s, aliased.\n", debugstr_hid_caps( &caps
) );
238 else if (caps
.value
->IsRange
)
239 FIXME( "Ignoring input value %s, usage range not implemented.\n", debugstr_hid_caps( &caps
) );
240 else if (caps
.value
->ReportCount
> 1)
241 FIXME( "Ignoring input value %s, array not implemented.\n", debugstr_hid_caps( &caps
) );
242 else if (caps
.value
->UsagePage
!= HID_USAGE_PAGE_GENERIC
)
243 TRACE( "Ignoring input value %s, usage page not implemented.\n", debugstr_hid_caps( &caps
) );
246 instance
.wUsagePage
= caps
.value
->UsagePage
;
247 instance
.wUsage
= caps
.value
->NotRange
.Usage
;
248 instance
.guidType
= *object_usage_to_guid( instance
.wUsagePage
, instance
.wUsage
);
249 instance
.wReportId
= caps
.value
->ReportID
;
251 switch (instance
.wUsage
)
253 case HID_USAGE_GENERIC_X
:
254 instance
.dwOfs
= DIJOFS_X
;
255 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( axis
++ );
256 instance
.dwFlags
= DIDOI_ASPECTPOSITION
;
257 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
258 if (ret
!= DIENUM_CONTINUE
) return ret
;
260 case HID_USAGE_GENERIC_Y
:
261 instance
.dwOfs
= DIJOFS_Y
;
262 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( axis
++ );
263 instance
.dwFlags
= DIDOI_ASPECTPOSITION
;
264 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
265 if (ret
!= DIENUM_CONTINUE
) return ret
;
267 case HID_USAGE_GENERIC_Z
:
268 instance
.dwOfs
= DIJOFS_Z
;
269 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( axis
++ );
270 instance
.dwFlags
= DIDOI_ASPECTPOSITION
;
271 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
272 if (ret
!= DIENUM_CONTINUE
) return ret
;
274 case HID_USAGE_GENERIC_RX
:
275 instance
.dwOfs
= DIJOFS_RX
;
276 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( axis
++ );
277 instance
.dwFlags
= DIDOI_ASPECTPOSITION
;
278 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
279 if (ret
!= DIENUM_CONTINUE
) return ret
;
281 case HID_USAGE_GENERIC_RY
:
282 instance
.dwOfs
= DIJOFS_RY
;
283 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( axis
++ );
284 instance
.dwFlags
= DIDOI_ASPECTPOSITION
;
285 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
286 if (ret
!= DIENUM_CONTINUE
) return ret
;
288 case HID_USAGE_GENERIC_RZ
:
289 instance
.dwOfs
= DIJOFS_RZ
;
290 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( axis
++ );
291 instance
.dwFlags
= DIDOI_ASPECTPOSITION
;
292 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
293 if (ret
!= DIENUM_CONTINUE
) return ret
;
295 case HID_USAGE_GENERIC_SLIDER
:
296 instance
.dwOfs
= DIJOFS_SLIDER( 0 );
297 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( axis
++ );
298 instance
.dwFlags
= DIDOI_ASPECTPOSITION
;
299 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
300 if (ret
!= DIENUM_CONTINUE
) return ret
;
302 case HID_USAGE_GENERIC_HATSWITCH
:
303 instance
.dwOfs
= DIJOFS_POV( 0 );
304 instance
.dwType
= DIDFT_POV
| DIDFT_MAKEINSTANCE( pov
++ );
305 instance
.dwFlags
= 0;
306 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
307 if (ret
!= DIENUM_CONTINUE
) return ret
;
310 FIXME( "Ignoring input value %s, usage not implemented.\n", debugstr_hid_caps( &caps
) );
316 return DIENUM_CONTINUE
;
319 static BOOL
enum_button_objects( struct hid_joystick
*impl
, const DIPROPHEADER
*filter
,
320 DWORD flags
, enum_object_callback callback
, void *data
)
322 DIDEVICEOBJECTINSTANCEW instance
= {.dwSize
= sizeof(DIDEVICEOBJECTINSTANCEW
)};
323 struct hid_caps caps
= {.type
= BUTTON_CAPS
};
324 DWORD button
= 0, i
, j
;
327 for (i
= 0; i
< impl
->caps
.NumberInputButtonCaps
; ++i
)
329 caps
.button
= impl
->input_button_caps
+ i
;
331 if (caps
.button
->UsagePage
>= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN
)
332 TRACE( "Ignoring input button %s, vendor specific.\n", debugstr_hid_caps( &caps
) );
333 else if (caps
.button
->IsAlias
)
334 TRACE( "Ignoring input button %s, aliased.\n", debugstr_hid_caps( &caps
) );
335 else if (caps
.button
->UsagePage
!= HID_USAGE_PAGE_BUTTON
)
336 TRACE( "Ignoring input button %s, usage page not implemented.\n", debugstr_hid_caps( &caps
) );
337 else if (caps
.button
->IsRange
)
339 if (caps
.button
->NotRange
.Usage
>= 128)
340 FIXME( "Ignoring input button %s, too many buttons.\n", debugstr_hid_caps( &caps
) );
341 else for (j
= caps
.button
->Range
.UsageMin
; j
<= caps
.button
->Range
.UsageMax
; ++j
)
343 instance
.dwOfs
= DIJOFS_BUTTON( j
- 1 );
344 instance
.dwType
= DIDFT_PSHBUTTON
| DIDFT_MAKEINSTANCE( button
++ );
345 instance
.dwFlags
= 0;
346 instance
.wUsagePage
= caps
.button
->UsagePage
;
348 instance
.guidType
= *object_usage_to_guid( instance
.wUsagePage
, instance
.wUsage
);
349 instance
.wReportId
= caps
.button
->ReportID
;
350 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
351 if (ret
!= DIENUM_CONTINUE
) return ret
;
354 else if (caps
.button
->NotRange
.Usage
>= 128)
355 FIXME( "Ignoring input button %s, too many buttons.\n", debugstr_hid_caps( &caps
) );
358 instance
.dwOfs
= DIJOFS_BUTTON( caps
.button
->NotRange
.Usage
- 1 );
359 instance
.dwType
= DIDFT_PSHBUTTON
| DIDFT_MAKEINSTANCE( button
++ );
360 instance
.dwFlags
= 0;
361 instance
.wUsagePage
= caps
.button
->UsagePage
;
362 instance
.wUsage
= caps
.button
->NotRange
.Usage
;
363 instance
.guidType
= *object_usage_to_guid( instance
.wUsagePage
, instance
.wUsage
);
364 instance
.wReportId
= caps
.button
->ReportID
;
365 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
366 if (ret
!= DIENUM_CONTINUE
) return ret
;
370 return DIENUM_CONTINUE
;
373 static BOOL
enum_collections_objects( struct hid_joystick
*impl
, const DIPROPHEADER
*filter
, DWORD flags
,
374 enum_object_callback callback
, void *data
)
376 DIDEVICEOBJECTINSTANCEW instance
= {.dwSize
= sizeof(DIDEVICEOBJECTINSTANCEW
)};
377 struct hid_caps caps
= {.type
= LINK_COLLECTION_NODE
};
381 for (i
= 0; i
< impl
->caps
.NumberLinkCollectionNodes
; ++i
)
383 caps
.node
= impl
->collection_nodes
+ i
;
385 if (caps
.node
->LinkUsagePage
>= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN
)
386 TRACE( "Ignoring collection %s, vendor specific.\n", debugstr_hid_caps( &caps
) );
387 else if (caps
.node
->IsAlias
)
388 TRACE( "Ignoring collection %s, aliased.\n", debugstr_hid_caps( &caps
) );
392 instance
.dwType
= DIDFT_COLLECTION
| DIDFT_NODATA
;
393 instance
.dwFlags
= 0;
394 instance
.wUsagePage
= caps
.node
->LinkUsagePage
;
395 instance
.wUsage
= caps
.node
->LinkUsage
;
396 instance
.guidType
= *object_usage_to_guid( instance
.wUsagePage
, instance
.wUsage
);
397 instance
.wReportId
= 0;
398 ret
= enum_object( impl
, filter
, flags
, callback
, &caps
, &instance
, data
);
399 if (ret
!= DIENUM_CONTINUE
) return ret
;
403 return DIENUM_CONTINUE
;
406 static ULONG WINAPI
hid_joystick_Release( IDirectInputDevice8W
*iface
)
408 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
409 struct hid_joystick tmp
= *impl
;
412 if (!(ref
= IDirectInputDevice2WImpl_Release( iface
)))
414 HeapFree( GetProcessHeap(), 0, tmp
.usages_buf
);
415 HeapFree( GetProcessHeap(), 0, tmp
.input_report_buf
);
416 HeapFree( GetProcessHeap(), 0, tmp
.input_value_caps
);
417 HeapFree( GetProcessHeap(), 0, tmp
.input_button_caps
);
418 HeapFree( GetProcessHeap(), 0, tmp
.collection_nodes
);
419 HidD_FreePreparsedData( tmp
.preparsed
);
420 CancelIoEx( tmp
.device
, &tmp
.read_ovl
);
421 CloseHandle( tmp
.base
.read_event
);
422 CloseHandle( tmp
.device
);
428 static HRESULT WINAPI
hid_joystick_GetCapabilities( IDirectInputDevice8W
*iface
, DIDEVCAPS
*caps
)
430 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
432 TRACE( "iface %p, caps %p.\n", iface
, caps
);
434 if (!caps
) return E_POINTER
;
436 *caps
= impl
->dev_caps
;
441 struct enum_objects_params
443 LPDIENUMDEVICEOBJECTSCALLBACKW callback
;
447 static BOOL
enum_objects_callback( struct hid_joystick
*impl
, struct hid_caps
*caps
,
448 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
450 struct enum_objects_params
*params
= data
;
451 return params
->callback( instance
, params
->context
);
454 static HRESULT WINAPI
hid_joystick_EnumObjects( IDirectInputDevice8W
*iface
, LPDIENUMDEVICEOBJECTSCALLBACKW callback
,
455 void *context
, DWORD flags
)
457 static const DIPROPHEADER filter
=
459 .dwSize
= sizeof(filter
),
460 .dwHeaderSize
= sizeof(filter
),
461 .dwHow
= DIPH_DEVICE
,
463 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
464 struct enum_objects_params params
=
466 .callback
= callback
,
471 TRACE( "iface %p, callback %p, context %p, flags %#x.\n", iface
, callback
, context
, flags
);
473 if (!callback
) return DIERR_INVALIDPARAM
;
475 ret
= enum_value_objects( impl
, &filter
, flags
, enum_objects_callback
, ¶ms
);
476 if (ret
!= DIENUM_CONTINUE
) return S_OK
;
477 ret
= enum_button_objects( impl
, &filter
, flags
, enum_objects_callback
, ¶ms
);
478 if (ret
!= DIENUM_CONTINUE
) return S_OK
;
479 enum_collections_objects( impl
, &filter
, flags
, enum_objects_callback
, ¶ms
);
484 static BOOL
get_property_prop_range( struct hid_joystick
*impl
, struct hid_caps
*caps
,
485 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
487 HIDP_VALUE_CAPS
*value_caps
= caps
->value
;
488 DIPROPRANGE
*value
= data
;
489 value
->lMin
= value_caps
->PhysicalMin
;
490 value
->lMax
= value_caps
->PhysicalMax
;
491 return DIENUM_CONTINUE
;
494 static HRESULT WINAPI
hid_joystick_GetProperty( IDirectInputDevice8W
*iface
, const GUID
*guid
,
495 DIPROPHEADER
*header
)
497 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
499 TRACE( "iface %p, guid %s, header %p\n", iface
, debugstr_guid( guid
), header
);
501 if (!header
) return DIERR_INVALIDPARAM
;
502 if (!IS_DIPROP( guid
)) return DI_OK
;
504 switch (LOWORD( guid
))
506 case (DWORD_PTR
)DIPROP_RANGE
:
507 enum_value_objects( impl
, header
, DIDFT_AXIS
, get_property_prop_range
, header
);
509 case (DWORD_PTR
)DIPROP_PRODUCTNAME
:
511 DIPROPSTRING
*value
= (DIPROPSTRING
*)header
;
512 lstrcpynW( value
->wsz
, impl
->instance
.tszProductName
, MAX_PATH
);
515 case (DWORD_PTR
)DIPROP_INSTANCENAME
:
517 DIPROPSTRING
*value
= (DIPROPSTRING
*)header
;
518 lstrcpynW( value
->wsz
, impl
->instance
.tszInstanceName
, MAX_PATH
);
521 case (DWORD_PTR
)DIPROP_VIDPID
:
523 DIPROPDWORD
*value
= (DIPROPDWORD
*)header
;
524 if (!impl
->attrs
.VendorID
|| !impl
->attrs
.ProductID
) return DIERR_UNSUPPORTED
;
525 value
->dwData
= MAKELONG( impl
->attrs
.VendorID
, impl
->attrs
.ProductID
);
528 case (DWORD_PTR
)DIPROP_JOYSTICKID
:
530 DIPROPDWORD
*value
= (DIPROPDWORD
*)header
;
531 value
->dwData
= impl
->instance
.guidInstance
.Data3
;
534 case (DWORD_PTR
)DIPROP_GUIDANDPATH
:
536 DIPROPGUIDANDPATH
*value
= (DIPROPGUIDANDPATH
*)header
;
537 lstrcpynW( value
->wszPath
, impl
->device_path
, MAX_PATH
);
541 return IDirectInputDevice2WImpl_GetProperty( iface
, guid
, header
);
547 static BOOL
set_property_prop_range( struct hid_joystick
*impl
, struct hid_caps
*caps
,
548 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
550 HIDP_VALUE_CAPS
*value_caps
= caps
->value
;
551 DIPROPRANGE
*value
= data
;
552 LONG range
= value_caps
->LogicalMax
- value_caps
->LogicalMin
;
553 value_caps
->PhysicalMin
= value
->lMin
;
554 value_caps
->PhysicalMax
= value
->lMax
;
555 if (instance
->dwType
& DIDFT_POV
&& range
> 0)
556 value_caps
->PhysicalMax
-= value
->lMax
/ (range
+ 1);
557 return DIENUM_CONTINUE
;
560 static HRESULT WINAPI
hid_joystick_SetProperty( IDirectInputDevice8W
*iface
, const GUID
*guid
,
561 const DIPROPHEADER
*header
)
563 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
565 TRACE( "iface %p, guid %s, header %p\n", iface
, debugstr_guid( guid
), header
);
567 if (!header
) return DIERR_INVALIDPARAM
;
568 if (!IS_DIPROP( guid
)) return DI_OK
;
570 switch (LOWORD( guid
))
572 case (DWORD_PTR
)DIPROP_RANGE
:
573 enum_value_objects( impl
, header
, DIDFT_AXIS
, set_property_prop_range
, (void *)header
);
576 return IDirectInputDevice2WImpl_SetProperty( iface
, guid
, header
);
582 static HRESULT WINAPI
hid_joystick_Acquire( IDirectInputDevice8W
*iface
)
584 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
585 ULONG report_len
= impl
->caps
.InputReportByteLength
;
588 TRACE( "iface %p.\n", iface
);
590 if ((hr
= IDirectInputDevice2WImpl_Acquire( iface
)) != DI_OK
) return hr
;
592 memset( &impl
->read_ovl
, 0, sizeof(impl
->read_ovl
) );
593 impl
->read_ovl
.hEvent
= impl
->base
.read_event
;
594 if (ReadFile( impl
->device
, impl
->input_report_buf
, report_len
, NULL
, &impl
->read_ovl
))
595 impl
->base
.read_callback( iface
);
600 static HRESULT WINAPI
hid_joystick_Unacquire( IDirectInputDevice8W
*iface
)
602 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
606 TRACE( "iface %p.\n", iface
);
608 if ((hr
= IDirectInputDevice2WImpl_Unacquire( iface
)) != DI_OK
) return hr
;
610 ret
= CancelIoEx( impl
->device
, &impl
->read_ovl
);
611 if (!ret
) WARN( "CancelIoEx failed, last error %u\n", GetLastError() );
616 static HRESULT WINAPI
hid_joystick_GetDeviceState( IDirectInputDevice8W
*iface
, DWORD len
, void *ptr
)
618 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
620 if (!ptr
) return DIERR_INVALIDPARAM
;
622 fill_DataFormat( ptr
, len
, &impl
->state
, &impl
->base
.data_format
);
627 static BOOL
get_object_info( struct hid_joystick
*impl
, struct hid_caps
*caps
,
628 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
630 DIDEVICEOBJECTINSTANCEW
*dest
= data
;
631 memcpy( dest
, instance
, dest
->dwSize
);
635 static HRESULT WINAPI
hid_joystick_GetObjectInfo( IDirectInputDevice8W
*iface
, DIDEVICEOBJECTINSTANCEW
*instance
,
636 DWORD obj
, DWORD how
)
638 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
639 const DIPROPHEADER filter
=
641 .dwSize
= sizeof(filter
),
642 .dwHeaderSize
= sizeof(filter
),
648 TRACE( "iface %p, instance %p, obj %#x, how %#x.\n", iface
, instance
, obj
, how
);
650 if (!instance
) return E_POINTER
;
651 if (instance
->dwSize
!= sizeof(DIDEVICEOBJECTINSTANCE_DX3W
) &&
652 instance
->dwSize
!= sizeof(DIDEVICEOBJECTINSTANCEW
))
653 return DIERR_INVALIDPARAM
;
655 ret
= enum_value_objects( impl
, &filter
, DIDFT_ALL
, get_object_info
, NULL
);
656 if (ret
!= DIENUM_CONTINUE
) return S_OK
;
657 ret
= enum_button_objects( impl
, &filter
, DIDFT_ALL
, get_object_info
, NULL
);
658 if (ret
!= DIENUM_CONTINUE
) return S_OK
;
659 enum_collections_objects( impl
, &filter
, DIDFT_ALL
, get_object_info
, NULL
);
664 static HRESULT WINAPI
hid_joystick_GetDeviceInfo( IDirectInputDevice8W
*iface
, DIDEVICEINSTANCEW
*instance
)
666 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
668 TRACE( "iface %p, instance %p.\n", iface
, instance
);
670 if (!instance
) return E_POINTER
;
671 if (instance
->dwSize
!= sizeof(DIDEVICEINSTANCE_DX3W
) &&
672 instance
->dwSize
!= sizeof(DIDEVICEINSTANCEW
))
673 return DIERR_INVALIDPARAM
;
675 memcpy( instance
, &impl
->instance
, instance
->dwSize
);
680 static HRESULT WINAPI
hid_joystick_BuildActionMap( IDirectInputDevice8W
*iface
, DIACTIONFORMATW
*format
,
681 const WCHAR
*username
, DWORD flags
)
683 FIXME( "iface %p, format %p, username %s, flags %#x stub!\n", iface
, format
, debugstr_w(username
), flags
);
685 if (!format
) return DIERR_INVALIDPARAM
;
687 return DIERR_UNSUPPORTED
;
690 static HRESULT WINAPI
hid_joystick_SetActionMap( IDirectInputDevice8W
*iface
, DIACTIONFORMATW
*format
,
691 const WCHAR
*username
, DWORD flags
)
693 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
695 TRACE( "iface %p, format %p, username %s, flags %#x.\n", iface
, format
, debugstr_w(username
), flags
);
697 if (!format
) return DIERR_INVALIDPARAM
;
699 return _set_action_map( iface
, format
, username
, flags
, impl
->base
.data_format
.wine_df
);
702 static const IDirectInputDevice8WVtbl hid_joystick_vtbl
=
704 /*** IUnknown methods ***/
705 IDirectInputDevice2WImpl_QueryInterface
,
706 IDirectInputDevice2WImpl_AddRef
,
707 hid_joystick_Release
,
708 /*** IDirectInputDevice methods ***/
709 hid_joystick_GetCapabilities
,
710 hid_joystick_EnumObjects
,
711 hid_joystick_GetProperty
,
712 hid_joystick_SetProperty
,
713 hid_joystick_Acquire
,
714 hid_joystick_Unacquire
,
715 hid_joystick_GetDeviceState
,
716 IDirectInputDevice2WImpl_GetDeviceData
,
717 IDirectInputDevice2WImpl_SetDataFormat
,
718 IDirectInputDevice2WImpl_SetEventNotification
,
719 IDirectInputDevice2WImpl_SetCooperativeLevel
,
720 hid_joystick_GetObjectInfo
,
721 hid_joystick_GetDeviceInfo
,
722 IDirectInputDevice2WImpl_RunControlPanel
,
723 IDirectInputDevice2WImpl_Initialize
,
724 /*** IDirectInputDevice2 methods ***/
725 IDirectInputDevice2WImpl_CreateEffect
,
726 IDirectInputDevice2WImpl_EnumEffects
,
727 IDirectInputDevice2WImpl_GetEffectInfo
,
728 IDirectInputDevice2WImpl_GetForceFeedbackState
,
729 IDirectInputDevice2WImpl_SendForceFeedbackCommand
,
730 IDirectInputDevice2WImpl_EnumCreatedEffectObjects
,
731 IDirectInputDevice2WImpl_Escape
,
732 IDirectInputDevice2WImpl_Poll
,
733 IDirectInputDevice2WImpl_SendDeviceData
,
734 /*** IDirectInputDevice7 methods ***/
735 IDirectInputDevice7WImpl_EnumEffectsInFile
,
736 IDirectInputDevice7WImpl_WriteEffectToFile
,
737 /*** IDirectInputDevice8 methods ***/
738 hid_joystick_BuildActionMap
,
739 hid_joystick_SetActionMap
,
740 IDirectInputDevice8WImpl_GetImageInfo
,
743 struct parse_device_state_params
745 DIJOYSTATE2 old_state
;
750 static BOOL
check_device_state_button( struct hid_joystick
*impl
, struct hid_caps
*caps
,
751 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
753 IDirectInputDevice8W
*iface
= &impl
->base
.IDirectInputDevice8W_iface
;
754 struct parse_device_state_params
*params
= data
;
755 DWORD i
= DIDFT_GETINSTANCE( instance
->dwType
);
757 if (!(instance
->dwType
& DIDFT_BUTTON
))
758 FIXME( "unexpected object type %#x, expected DIDFT_BUTTON\n", instance
->dwType
);
759 else if (params
->old_state
.rgbButtons
[i
] != impl
->state
.rgbButtons
[i
])
760 queue_event( iface
, instance
->dwType
, impl
->state
.rgbButtons
[i
], params
->time
, params
->seq
);
762 return DIENUM_CONTINUE
;
765 static LONG
sign_extend( ULONG value
, const HIDP_VALUE_CAPS
*caps
)
767 UINT sign
= 1 << (caps
->BitSize
- 1);
768 if (sign
<= 1 || caps
->LogicalMin
>= 0) return value
;
769 return value
- ((value
& sign
) << 1);
772 static LONG
scale_value( ULONG value
, const HIDP_VALUE_CAPS
*caps
, LONG min
, LONG max
)
774 ULONG bit_max
= (1 << caps
->BitSize
) - 1;
775 LONG tmp
= sign_extend( value
, caps
);
777 /* xinput HID gamepad have bogus logical value range, let's use the bit range instead */
778 if (caps
->LogicalMin
== 0 && caps
->LogicalMax
== -1) return min
+ MulDiv( tmp
, max
- min
, bit_max
);
779 if (caps
->LogicalMin
> tmp
|| caps
->LogicalMax
< tmp
) return -1; /* invalid / null value */
780 return min
+ MulDiv( tmp
- caps
->LogicalMin
, max
- min
, caps
->LogicalMax
- caps
->LogicalMin
);
783 static BOOL
read_device_state_value( struct hid_joystick
*impl
, struct hid_caps
*caps
,
784 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
786 IDirectInputDevice8W
*iface
= &impl
->base
.IDirectInputDevice8W_iface
;
787 ULONG logical_value
, report_len
= impl
->caps
.InputReportByteLength
;
788 struct parse_device_state_params
*params
= data
;
789 char *report_buf
= impl
->input_report_buf
;
790 HIDP_VALUE_CAPS
*value_caps
= caps
->value
;
794 if (!(instance
->dwType
& (DIDFT_POV
| DIDFT_AXIS
)))
795 FIXME( "unexpected object type %#x, expected DIDFT_POV | DIDFT_AXIS\n", instance
->dwType
);
798 status
= HidP_GetUsageValue( HidP_Input
, instance
->wUsagePage
, 0, instance
->wUsage
,
799 &logical_value
, impl
->preparsed
, report_buf
, report_len
);
800 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_GetUsageValue %04x:%04x returned %#x\n",
801 instance
->wUsagePage
, instance
->wUsage
, status
);
802 value
= scale_value( logical_value
, value_caps
, value_caps
->PhysicalMin
, value_caps
->PhysicalMax
);
804 switch (instance
->dwOfs
)
807 if (impl
->state
.lX
== value
) break;
808 impl
->state
.lX
= value
;
809 queue_event( iface
, instance
->dwType
, value
, params
->time
, params
->seq
);
812 if (impl
->state
.lY
== value
) break;
813 impl
->state
.lY
= value
;
814 queue_event( iface
, instance
->dwType
, value
, params
->time
, params
->seq
);
817 if (impl
->state
.lZ
== value
) break;
818 impl
->state
.lZ
= value
;
819 queue_event( iface
, instance
->dwType
, value
, params
->time
, params
->seq
);
822 if (impl
->state
.lRx
== value
) break;
823 impl
->state
.lRx
= value
;
824 queue_event( iface
, instance
->dwType
, value
, params
->time
, params
->seq
);
827 if (impl
->state
.lRy
== value
) break;
828 impl
->state
.lRy
= value
;
829 queue_event( iface
, instance
->dwType
, value
, params
->time
, params
->seq
);
832 if (impl
->state
.lRz
== value
) break;
833 impl
->state
.lRz
= value
;
834 queue_event( iface
, instance
->dwType
, value
, params
->time
, params
->seq
);
836 case DIJOFS_POV( 0 ):
837 if (impl
->state
.rgdwPOV
[0] == value
) break;
838 impl
->state
.rgdwPOV
[0] = value
;
839 queue_event( iface
, instance
->dwType
, value
, params
->time
, params
->seq
);
842 FIXME( "unimplemented offset %#x.\n", instance
->dwOfs
);
847 return DIENUM_CONTINUE
;
850 static HRESULT
hid_joystick_read_state( IDirectInputDevice8W
*iface
)
852 static const DIPROPHEADER filter
=
854 .dwSize
= sizeof(filter
),
855 .dwHeaderSize
= sizeof(filter
),
856 .dwHow
= DIPH_DEVICE
,
858 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
859 ULONG i
, count
, report_len
= impl
->caps
.InputReportByteLength
;
860 struct parse_device_state_params params
= {0};
861 char *report_buf
= impl
->input_report_buf
;
862 USAGE_AND_PAGE
*usages
;
866 ret
= GetOverlappedResult( impl
->device
, &impl
->read_ovl
, &count
, FALSE
);
867 if (!ret
) WARN( "ReadFile failed, error %u\n", GetLastError() );
868 else if (TRACE_ON(dinput
))
870 TRACE( "read size %u report:\n", count
);
871 for (i
= 0; i
< report_len
;)
873 char buffer
[256], *buf
= buffer
;
874 buf
+= sprintf(buf
, "%08x ", i
);
877 buf
+= sprintf(buf
, " %02x", (BYTE
)report_buf
[i
] );
878 } while (++i
% 16 && i
< report_len
);
879 TRACE("%s\n", buffer
);
885 count
= impl
->usages_count
;
886 memset( impl
->usages_buf
, 0, count
* sizeof(*impl
->usages_buf
) );
887 status
= HidP_GetUsagesEx( HidP_Input
, 0, impl
->usages_buf
, &count
,
888 impl
->preparsed
, report_buf
, report_len
);
889 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_GetUsagesEx returned %#x\n", status
);
891 if (report_buf
[0] == impl
->device_state_report_id
)
893 params
.old_state
= impl
->state
;
894 params
.time
= GetCurrentTime();
895 params
.seq
= impl
->base
.dinput
->evsequence
++;
897 memset( impl
->state
.rgbButtons
, 0, sizeof(impl
->state
.rgbButtons
) );
900 usages
= impl
->usages_buf
+ count
;
901 if (usages
->UsagePage
!= HID_USAGE_PAGE_BUTTON
)
902 FIXME( "unimplemented usage page %x.\n", usages
->UsagePage
);
903 else if (usages
->Usage
>= 128)
904 FIXME( "ignoring extraneous button %d.\n", usages
->Usage
);
906 impl
->state
.rgbButtons
[usages
->Usage
- 1] = 0x80;
909 enum_value_objects( impl
, &filter
, DIDFT_ALL
, read_device_state_value
, ¶ms
);
910 enum_button_objects( impl
, &filter
, DIDFT_ALL
, check_device_state_button
, ¶ms
);
913 memset( &impl
->read_ovl
, 0, sizeof(impl
->read_ovl
) );
914 impl
->read_ovl
.hEvent
= impl
->base
.read_event
;
915 } while (ReadFile( impl
->device
, report_buf
, report_len
, &count
, &impl
->read_ovl
));
920 static BOOL
hid_joystick_device_try_open( UINT32 handle
, const WCHAR
*path
, HANDLE
*device
,
921 PHIDP_PREPARSED_DATA
*preparsed
, HIDD_ATTRIBUTES
*attrs
,
922 HIDP_CAPS
*caps
, DIDEVICEINSTANCEW
*instance
, DWORD version
)
924 PHIDP_PREPARSED_DATA preparsed_data
= NULL
;
927 device_file
= CreateFileW( path
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
928 NULL
, OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
| FILE_FLAG_NO_BUFFERING
, 0 );
929 if (device_file
== INVALID_HANDLE_VALUE
) return FALSE
;
931 if (!HidD_GetPreparsedData( device_file
, &preparsed_data
)) goto failed
;
932 if (!HidD_GetAttributes( device_file
, attrs
)) goto failed
;
933 if (HidP_GetCaps( preparsed_data
, caps
) != HIDP_STATUS_SUCCESS
) goto failed
;
935 if (caps
->UsagePage
== HID_USAGE_PAGE_GAME
) FIXME( "game usage page not implemented!\n" );
936 if (caps
->UsagePage
== HID_USAGE_PAGE_SIMULATION
) FIXME( "simulation usage page not implemented!\n" );
937 if (caps
->UsagePage
!= HID_USAGE_PAGE_GENERIC
) goto failed
;
938 if (caps
->Usage
!= HID_USAGE_GENERIC_GAMEPAD
&& caps
->Usage
!= HID_USAGE_GENERIC_JOYSTICK
) goto failed
;
940 if (!HidD_GetProductString( device_file
, instance
->tszInstanceName
, MAX_PATH
)) goto failed
;
941 if (!HidD_GetProductString( device_file
, instance
->tszProductName
, MAX_PATH
)) goto failed
;
943 instance
->guidInstance
= hid_joystick_guid
;
944 instance
->guidInstance
.Data1
^= handle
;
945 instance
->guidProduct
= DInput_PIDVID_Product_GUID
;
946 instance
->guidProduct
.Data1
= MAKELONG( attrs
->VendorID
, attrs
->ProductID
);
947 instance
->dwDevType
= get_device_type( version
, caps
->Usage
!= HID_USAGE_GENERIC_GAMEPAD
) | DIDEVTYPE_HID
;
948 instance
->guidFFDriver
= GUID_NULL
;
949 instance
->wUsagePage
= caps
->UsagePage
;
950 instance
->wUsage
= caps
->Usage
;
952 *device
= device_file
;
953 *preparsed
= preparsed_data
;
957 CloseHandle( device_file
);
958 HidD_FreePreparsedData( preparsed_data
);
962 static HRESULT
hid_joystick_device_open( int index
, DIDEVICEINSTANCEW
*filter
, WCHAR
*device_path
,
963 HANDLE
*device
, PHIDP_PREPARSED_DATA
*preparsed
,
964 HIDD_ATTRIBUTES
*attrs
, HIDP_CAPS
*caps
, DWORD version
)
966 char buffer
[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W
) + MAX_PATH
* sizeof(WCHAR
)];
967 SP_DEVICE_INTERFACE_DETAIL_DATA_W
*detail
= (void *)buffer
;
968 SP_DEVICE_INTERFACE_DATA iface
= {.cbSize
= sizeof(iface
)};
969 SP_DEVINFO_DATA devinfo
= {.cbSize
= sizeof(devinfo
)};
970 DIDEVICEINSTANCEW instance
= *filter
;
971 UINT32 i
= 0, handle
;
976 TRACE( "index %d, product %s, instance %s\n", index
, debugstr_guid( &filter
->guidProduct
),
977 debugstr_guid( &filter
->guidInstance
) );
979 HidD_GetHidGuid( &hid
);
981 set
= SetupDiGetClassDevsW( &hid
, NULL
, NULL
, DIGCF_DEVICEINTERFACE
| DIGCF_PRESENT
);
982 if (set
== INVALID_HANDLE_VALUE
) return DIERR_DEVICENOTREG
;
986 while (SetupDiEnumDeviceInterfaces( set
, NULL
, &hid
, i
++, &iface
))
988 detail
->cbSize
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W
);
989 if (!SetupDiGetDeviceInterfaceDetailW( set
, &iface
, detail
, sizeof(buffer
), NULL
, &devinfo
))
991 if (!SetupDiGetDevicePropertyW( set
, &devinfo
, &DEVPROPKEY_HID_HANDLE
, &type
,
992 (BYTE
*)&handle
, sizeof(handle
), NULL
, 0 ) ||
993 type
!= DEVPROP_TYPE_UINT32
)
995 if (!hid_joystick_device_try_open( handle
, detail
->DevicePath
, device
, preparsed
,
996 attrs
, caps
, &instance
, version
))
999 /* enumerate device by GUID */
1000 if (index
< 0 && IsEqualGUID( &filter
->guidProduct
, &instance
.guidProduct
)) break;
1001 if (index
< 0 && IsEqualGUID( &filter
->guidInstance
, &instance
.guidInstance
)) break;
1003 /* enumerate all devices */
1004 if (index
>= 0 && !index
--) break;
1006 CloseHandle( *device
);
1007 HidD_FreePreparsedData( *preparsed
);
1012 SetupDiDestroyDeviceInfoList( set
);
1013 if (!*device
|| !*preparsed
) return DIERR_DEVICENOTREG
;
1015 lstrcpynW( device_path
, detail
->DevicePath
, MAX_PATH
);
1020 static HRESULT
hid_joystick_enum_device( DWORD type
, DWORD flags
, DIDEVICEINSTANCEW
*instance
,
1021 DWORD version
, int index
)
1023 HIDD_ATTRIBUTES attrs
= {.Size
= sizeof(attrs
)};
1024 PHIDP_PREPARSED_DATA preparsed
;
1025 WCHAR device_path
[MAX_PATH
];
1030 TRACE( "type %#x, flags %#x, instance %p, version %#04x, index %d\n", type
, flags
, instance
, version
, index
);
1032 hr
= hid_joystick_device_open( index
, instance
, device_path
, &device
, &preparsed
,
1033 &attrs
, &caps
, version
);
1034 if (hr
!= DI_OK
) return hr
;
1036 HidD_FreePreparsedData( preparsed
);
1037 CloseHandle( device
);
1039 if (instance
->dwSize
!= sizeof(DIDEVICEINSTANCEW
))
1041 if (version
< 0x0800 && type
!= DIDEVTYPE_JOYSTICK
)
1043 if (version
>= 0x0800 && type
!= DI8DEVCLASS_ALL
&& type
!= DI8DEVCLASS_GAMECTRL
)
1046 if (device_disabled_registry( "HID", TRUE
))
1047 return DIERR_DEVICENOTREG
;
1049 TRACE( "found device %s, usage %04x:%04x, product %s, instance %s, name %s\n", debugstr_w(device_path
),
1050 instance
->wUsagePage
, instance
->wUsage
, debugstr_guid( &instance
->guidProduct
),
1051 debugstr_guid( &instance
->guidInstance
), debugstr_w(instance
->tszInstanceName
) );
1056 static BOOL
init_objects( struct hid_joystick
*impl
, struct hid_caps
*caps
,
1057 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
1059 DIDATAFORMAT
*format
= impl
->base
.data_format
.wine_df
;
1061 format
->dwNumObjs
++;
1062 if (instance
->dwType
& DIDFT_PSHBUTTON
) impl
->dev_caps
.dwButtons
++;
1063 if (instance
->dwType
& DIDFT_AXIS
) impl
->dev_caps
.dwAxes
++;
1064 if (instance
->dwType
& DIDFT_POV
) impl
->dev_caps
.dwPOVs
++;
1066 if (!impl
->device_state_report_id
)
1067 impl
->device_state_report_id
= instance
->wReportId
;
1068 else if (impl
->device_state_report_id
!= instance
->wReportId
)
1069 FIXME( "multiple device state reports found!\n" );
1071 return DIENUM_CONTINUE
;
1074 static BOOL
init_data_format( struct hid_joystick
*impl
, struct hid_caps
*caps
,
1075 DIDEVICEOBJECTINSTANCEW
*instance
, void *data
)
1077 DIDATAFORMAT
*format
= impl
->base
.data_format
.wine_df
;
1078 DIOBJECTDATAFORMAT
*obj_format
;
1079 DWORD
*index
= data
;
1081 obj_format
= format
->rgodf
+ *index
;
1082 obj_format
->pguid
= object_usage_to_guid( instance
->wUsagePage
, instance
->wUsage
);
1083 obj_format
->dwOfs
= instance
->dwOfs
;
1084 obj_format
->dwType
= instance
->dwType
;
1085 obj_format
->dwFlags
= instance
->dwFlags
;
1088 return DIENUM_CONTINUE
;
1091 static HRESULT
hid_joystick_create_device( IDirectInputImpl
*dinput
, const GUID
*guid
, IDirectInputDevice8W
**out
)
1093 static const DIPROPHEADER filter
=
1095 .dwSize
= sizeof(filter
),
1096 .dwHeaderSize
= sizeof(filter
),
1097 .dwHow
= DIPH_DEVICE
,
1099 DIDEVICEINSTANCEW instance
=
1101 .dwSize
= sizeof(instance
),
1102 .guidProduct
= *guid
,
1103 .guidInstance
= *guid
1109 .dwSize
= sizeof(range
),
1110 .dwHeaderSize
= sizeof(DIPROPHEADER
),
1111 .dwHow
= DIPH_DEVICE
,
1114 HIDD_ATTRIBUTES attrs
= {.Size
= sizeof(attrs
)};
1115 HIDP_LINK_COLLECTION_NODE
*nodes
;
1116 struct hid_joystick
*impl
= NULL
;
1117 DIDATAFORMAT
*format
= NULL
;
1118 HIDP_BUTTON_CAPS
*buttons
;
1119 HIDP_VALUE_CAPS
*values
;
1120 USAGE_AND_PAGE
*usages
;
1126 TRACE( "dinput %p, guid %s, out %p\n", dinput
, debugstr_guid( guid
), out
);
1129 instance
.guidProduct
.Data1
= DInput_PIDVID_Product_GUID
.Data1
;
1130 instance
.guidInstance
.Data1
= hid_joystick_guid
.Data1
;
1131 if (IsEqualGUID( &DInput_PIDVID_Product_GUID
, &instance
.guidProduct
))
1132 instance
.guidProduct
= *guid
;
1133 else if (IsEqualGUID( &hid_joystick_guid
, &instance
.guidInstance
))
1134 instance
.guidInstance
= *guid
;
1136 return DIERR_DEVICENOTREG
;
1138 hr
= direct_input_device_alloc( sizeof(struct hid_joystick
), &hid_joystick_vtbl
, guid
,
1139 dinput
, (void **)&impl
);
1140 if (FAILED(hr
)) return hr
;
1141 impl
->base
.crit
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": hid_joystick.base.crit");
1142 impl
->base
.dwCoopLevel
= DISCL_NONEXCLUSIVE
| DISCL_BACKGROUND
;
1143 impl
->base
.read_event
= CreateEventA( NULL
, FALSE
, FALSE
, NULL
);
1144 impl
->base
.read_callback
= hid_joystick_read_state
;
1146 hr
= hid_joystick_device_open( -1, &instance
, impl
->device_path
, &impl
->device
, &impl
->preparsed
,
1147 &attrs
, &impl
->caps
, dinput
->dwVersion
);
1148 if (hr
!= DI_OK
) goto failed
;
1150 impl
->instance
= instance
;
1151 impl
->attrs
= attrs
;
1152 impl
->dev_caps
.dwSize
= sizeof(impl
->dev_caps
);
1153 impl
->dev_caps
.dwFlags
= DIDC_ATTACHED
| DIDC_EMULATED
;
1154 impl
->dev_caps
.dwDevType
= instance
.dwDevType
;
1156 size
= impl
->caps
.NumberLinkCollectionNodes
* sizeof(HIDP_LINK_COLLECTION_NODE
);
1157 if (!(nodes
= HeapAlloc( GetProcessHeap(), 0, size
))) goto failed
;
1158 impl
->collection_nodes
= nodes
;
1159 size
= impl
->caps
.NumberInputButtonCaps
* sizeof(HIDP_BUTTON_CAPS
);
1160 if (!(buttons
= HeapAlloc( GetProcessHeap(), 0, size
))) goto failed
;
1161 impl
->input_button_caps
= buttons
;
1162 size
= impl
->caps
.NumberInputValueCaps
* sizeof(HIDP_VALUE_CAPS
);
1163 if (!(values
= HeapAlloc( GetProcessHeap(), 0, size
))) goto failed
;
1164 impl
->input_value_caps
= values
;
1166 size
= impl
->caps
.InputReportByteLength
;
1167 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, size
))) goto failed
;
1168 impl
->input_report_buf
= buffer
;
1169 impl
->usages_count
= HidP_MaxUsageListLength( HidP_Input
, 0, impl
->preparsed
);
1170 size
= impl
->usages_count
* sizeof(USAGE_AND_PAGE
);
1171 if (!(usages
= HeapAlloc( GetProcessHeap(), 0, size
))) goto failed
;
1172 impl
->usages_buf
= usages
;
1174 size
= impl
->caps
.NumberLinkCollectionNodes
;
1175 status
= HidP_GetLinkCollectionNodes( nodes
, &size
, impl
->preparsed
);
1176 if (status
!= HIDP_STATUS_SUCCESS
) goto failed
;
1177 impl
->caps
.NumberLinkCollectionNodes
= size
;
1178 status
= HidP_GetButtonCaps( HidP_Input
, impl
->input_button_caps
,
1179 &impl
->caps
.NumberInputButtonCaps
, impl
->preparsed
);
1180 if (status
!= HIDP_STATUS_SUCCESS
&& status
!= HIDP_STATUS_USAGE_NOT_FOUND
) goto failed
;
1181 status
= HidP_GetValueCaps( HidP_Input
, impl
->input_value_caps
,
1182 &impl
->caps
.NumberInputValueCaps
, impl
->preparsed
);
1183 if (status
!= HIDP_STATUS_SUCCESS
&& status
!= HIDP_STATUS_USAGE_NOT_FOUND
) goto failed
;
1185 /* enumerate collections first, so we can find report collections */
1186 enum_collections_objects( impl
, &filter
, DIDFT_ALL
, init_objects
, NULL
);
1187 enum_value_objects( impl
, &filter
, DIDFT_ALL
, init_objects
, NULL
);
1188 enum_button_objects( impl
, &filter
, DIDFT_ALL
, init_objects
, NULL
);
1190 format
= impl
->base
.data_format
.wine_df
;
1191 size
= format
->dwNumObjs
* sizeof(*format
->rgodf
);
1192 if (!(format
->rgodf
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, size
))) goto failed
;
1193 format
->dwSize
= sizeof(*format
);
1194 format
->dwObjSize
= sizeof(*format
->rgodf
);
1195 format
->dwFlags
= DIDF_ABSAXIS
;
1196 format
->dwDataSize
= sizeof(impl
->state
);
1199 enum_value_objects( impl
, &filter
, DIDFT_ALL
, init_data_format
, &index
);
1200 enum_button_objects( impl
, &filter
, DIDFT_ALL
, init_data_format
, &index
);
1201 enum_collections_objects( impl
, &filter
, DIDFT_ALL
, init_data_format
, &index
);
1203 _dump_DIDATAFORMAT( impl
->base
.data_format
.wine_df
);
1206 enum_value_objects( impl
, &range
.diph
, DIDFT_AXIS
, set_property_prop_range
, &range
);
1208 enum_value_objects( impl
, &range
.diph
, DIDFT_POV
, set_property_prop_range
, &range
);
1210 *out
= &impl
->base
.IDirectInputDevice8W_iface
;
1214 IDirectInputDevice_Release( &impl
->base
.IDirectInputDevice8W_iface
);
1218 const struct dinput_device joystick_hid_device
=
1220 "Wine HID joystick driver",
1221 hid_joystick_enum_device
,
1222 hid_joystick_create_device
,