dbghelp: Remove superfluous casts to self.
[wine.git] / dlls / dinput / joystick_hid.c
blob3c69c74b4fca7f6bb55c2542aba891a6fca247ef
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
20 #include <assert.h>
21 #include <stdarg.h>
22 #include <string.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winternl.h"
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "winreg.h"
31 #include "ddk/hidsdi.h"
32 #include "setupapi.h"
33 #include "devguid.h"
34 #include "dinput.h"
35 #include "setupapi.h"
37 #include "wine/debug.h"
39 #include "dinput_private.h"
40 #include "device_private.h"
41 #include "joystick_private.h"
43 #include "initguid.h"
44 #include "devpkey.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,
57 node->UserContext );
60 static inline const char *debugstr_hidp_button_caps( HIDP_BUTTON_CAPS *caps )
62 const char *str;
63 if (!caps) return "(null)";
65 str = wine_dbg_sprintf( "RId %d,", caps->ReportID );
66 if (!caps->IsRange)
67 str = wine_dbg_sprintf( "%s Usg %02x:%02x Dat %02x,", str, caps->UsagePage, caps->NotRange.Usage,
68 caps->NotRange.DataIndex );
69 else
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 );
74 else
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 );
78 else
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 )
86 const char *str;
87 if (!caps) return "(null)";
89 str = wine_dbg_sprintf( "RId %d,", caps->ReportID );
90 if (!caps->IsRange)
91 str = wine_dbg_sprintf( "%s Usg %02x:%02x Dat %02x,", str, caps->UsagePage, caps->NotRange.Usage,
92 caps->NotRange.DataIndex );
93 else
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 );
98 else
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 );
102 else
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 );
112 struct hid_caps
114 enum { LINK_COLLECTION_NODE, BUTTON_CAPS, VALUE_CAPS } type;
115 union
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 )
125 switch (caps->type)
127 case LINK_COLLECTION_NODE:
128 return debugstr_hidp_link_collection_node( caps->node );
129 case BUTTON_CAPS:
130 return debugstr_hidp_button_caps( caps->button );
131 case VALUE_CAPS:
132 return debugstr_hidp_value_caps( caps->value );
135 return "(unknown type)";
138 struct hid_joystick
140 IDirectInputDeviceImpl base;
141 DIJOYSTATE2 state;
143 HANDLE device;
144 OVERLAPPED read_ovl;
145 PHIDP_PREPARSED_DATA preparsed;
147 DIDEVICEINSTANCEW instance;
148 WCHAR device_path[MAX_PATH];
149 HIDD_ATTRIBUTES attrs;
150 DIDEVCAPS dev_caps;
151 HIDP_CAPS caps;
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;
159 ULONG usages_count;
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 )
172 switch (usage_page)
174 case HID_USAGE_PAGE_BUTTON: return &GUID_Button;
175 case HID_USAGE_PAGE_GENERIC:
176 switch (usage)
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;
187 break;
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)
203 case DIPH_DEVICE:
204 return callback( impl, caps, instance, data );
205 case DIPH_BYOFFSET:
206 if (filter->dwObj != instance->dwOfs) return DIENUM_CONTINUE;
207 return callback( impl, caps, instance, data );
208 case DIPH_BYID:
209 if ((filter->dwObj & 0x00ffffff) != (instance->dwType & 0x00ffffff)) return DIENUM_CONTINUE;
210 return callback( impl, caps, instance, data );
211 case DIPH_BYUSAGE:
212 if (LOWORD(filter->dwObj) != instance->wUsagePage || HIWORD(filter->dwObj) != instance->wUsage) return DIENUM_CONTINUE;
213 return callback( impl, caps, instance, data );
214 default:
215 FIXME( "unimplemented filter dwHow %#x dwObj %#x\n", filter->dwHow, filter->dwObj );
216 break;
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;
228 BOOL ret;
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 ) );
244 else
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;
259 break;
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;
266 break;
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;
273 break;
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;
280 break;
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;
287 break;
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;
294 break;
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;
301 break;
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;
308 break;
309 default:
310 FIXME( "Ignoring input value %s, usage not implemented.\n", debugstr_hid_caps( &caps ) );
311 break;
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;
325 BOOL ret;
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;
347 instance.wUsage = j;
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 ) );
356 else
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};
378 BOOL ret;
379 DWORD i;
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 ) );
389 else
391 instance.dwOfs = 0;
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;
410 ULONG ref;
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 );
425 return ref;
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;
438 return DI_OK;
441 struct enum_objects_params
443 LPDIENUMDEVICEOBJECTSCALLBACKW callback;
444 void *context;
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,
467 .context = context,
469 BOOL ret;
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, &params );
476 if (ret != DIENUM_CONTINUE) return S_OK;
477 ret = enum_button_objects( impl, &filter, flags, enum_objects_callback, &params );
478 if (ret != DIENUM_CONTINUE) return S_OK;
479 enum_collections_objects( impl, &filter, flags, enum_objects_callback, &params );
481 return S_OK;
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 );
508 return DI_OK;
509 case (DWORD_PTR)DIPROP_PRODUCTNAME:
511 DIPROPSTRING *value = (DIPROPSTRING *)header;
512 lstrcpynW( value->wsz, impl->instance.tszProductName, MAX_PATH );
513 return DI_OK;
515 case (DWORD_PTR)DIPROP_INSTANCENAME:
517 DIPROPSTRING *value = (DIPROPSTRING *)header;
518 lstrcpynW( value->wsz, impl->instance.tszInstanceName, MAX_PATH );
519 return DI_OK;
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 );
526 return DI_OK;
528 case (DWORD_PTR)DIPROP_JOYSTICKID:
530 DIPROPDWORD *value = (DIPROPDWORD *)header;
531 value->dwData = impl->instance.guidInstance.Data3;
532 return DI_OK;
534 case (DWORD_PTR)DIPROP_GUIDANDPATH:
536 DIPROPGUIDANDPATH *value = (DIPROPGUIDANDPATH *)header;
537 lstrcpynW( value->wszPath, impl->device_path, MAX_PATH );
538 return DI_OK;
540 default:
541 return IDirectInputDevice2WImpl_GetProperty( iface, guid, header );
544 return DI_OK;
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 );
574 return DI_OK;
575 default:
576 return IDirectInputDevice2WImpl_SetProperty( iface, guid, header );
579 return DI_OK;
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;
586 HRESULT hr;
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 );
597 return DI_OK;
600 static HRESULT WINAPI hid_joystick_Unacquire( IDirectInputDevice8W *iface )
602 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
603 HRESULT hr;
604 BOOL ret;
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() );
613 return DI_OK;
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 );
624 return DI_OK;
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 );
632 return DIENUM_STOP;
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),
643 .dwHow = how,
644 .dwObj = obj
646 BOOL ret;
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 );
661 return S_OK;
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 );
677 return S_OK;
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;
746 DWORD time;
747 DWORD seq;
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;
791 NTSTATUS status;
792 LONG value;
794 if (!(instance->dwType & (DIDFT_POV | DIDFT_AXIS)))
795 FIXME( "unexpected object type %#x, expected DIDFT_POV | DIDFT_AXIS\n", instance->dwType );
796 else
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)
806 case DIJOFS_X:
807 if (impl->state.lX == value) break;
808 impl->state.lX = value;
809 queue_event( iface, instance->dwType, value, params->time, params->seq );
810 break;
811 case DIJOFS_Y:
812 if (impl->state.lY == value) break;
813 impl->state.lY = value;
814 queue_event( iface, instance->dwType, value, params->time, params->seq );
815 break;
816 case DIJOFS_Z:
817 if (impl->state.lZ == value) break;
818 impl->state.lZ = value;
819 queue_event( iface, instance->dwType, value, params->time, params->seq );
820 break;
821 case DIJOFS_RX:
822 if (impl->state.lRx == value) break;
823 impl->state.lRx = value;
824 queue_event( iface, instance->dwType, value, params->time, params->seq );
825 break;
826 case DIJOFS_RY:
827 if (impl->state.lRy == value) break;
828 impl->state.lRy = value;
829 queue_event( iface, instance->dwType, value, params->time, params->seq );
830 break;
831 case DIJOFS_RZ:
832 if (impl->state.lRz == value) break;
833 impl->state.lRz = value;
834 queue_event( iface, instance->dwType, value, params->time, params->seq );
835 break;
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 );
840 break;
841 default:
842 FIXME( "unimplemented offset %#x.\n", instance->dwOfs );
843 break;
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;
863 NTSTATUS status;
864 BOOL ret;
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) );
898 while (count--)
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 );
905 else
906 impl->state.rgbButtons[usages->Usage - 1] = 0x80;
909 enum_value_objects( impl, &filter, DIDFT_ALL, read_device_state_value, &params );
910 enum_button_objects( impl, &filter, DIDFT_ALL, check_device_state_button, &params );
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 ));
917 return DI_OK;
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;
925 HANDLE device_file;
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;
954 return TRUE;
956 failed:
957 CloseHandle( device_file );
958 HidD_FreePreparsedData( preparsed_data );
959 return FALSE;
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;
972 HDEVINFO set;
973 DWORD type;
974 GUID hid;
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;
984 *device = NULL;
985 *preparsed = NULL;
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 ))
990 continue;
991 if (!SetupDiGetDevicePropertyW( set, &devinfo, &DEVPROPKEY_HID_HANDLE, &type,
992 (BYTE *)&handle, sizeof(handle), NULL, 0 ) ||
993 type != DEVPROP_TYPE_UINT32)
994 continue;
995 if (!hid_joystick_device_try_open( handle, detail->DevicePath, device, preparsed,
996 attrs, caps, &instance, version ))
997 continue;
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 );
1008 *device = NULL;
1009 *preparsed = NULL;
1012 SetupDiDestroyDeviceInfoList( set );
1013 if (!*device || !*preparsed) return DIERR_DEVICENOTREG;
1015 lstrcpynW( device_path, detail->DevicePath, MAX_PATH );
1016 *filter = instance;
1017 return DI_OK;
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];
1026 HIDP_CAPS caps;
1027 HANDLE device;
1028 HRESULT hr;
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))
1040 return S_FALSE;
1041 if (version < 0x0800 && type != DIDEVTYPE_JOYSTICK)
1042 return S_FALSE;
1043 if (version >= 0x0800 && type != DI8DEVCLASS_ALL && type != DI8DEVCLASS_GAMECTRL)
1044 return S_FALSE;
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) );
1053 return DI_OK;
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;
1086 (*index)++;
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
1105 DIPROPRANGE range =
1107 .diph =
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;
1121 DWORD size, index;
1122 NTSTATUS status;
1123 char *buffer;
1124 HRESULT hr;
1126 TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out );
1128 *out = NULL;
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;
1135 else
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);
1198 index = 0;
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 );
1205 range.lMax = 65535;
1206 enum_value_objects( impl, &range.diph, DIDFT_AXIS, set_property_prop_range, &range );
1207 range.lMax = 36000;
1208 enum_value_objects( impl, &range.diph, DIDFT_POV, set_property_prop_range, &range );
1210 *out = &impl->base.IDirectInputDevice8W_iface;
1211 return DI_OK;
1213 failed:
1214 IDirectInputDevice_Release( &impl->base.IDirectInputDevice8W_iface );
1215 return hr;
1218 const struct dinput_device joystick_hid_device =
1220 "Wine HID joystick driver",
1221 hid_joystick_enum_device,
1222 hid_joystick_create_device,