1 /* WinRT Windows.Gaming.Input implementation
3 * Copyright 2022 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
23 #include "ddk/hidsdi.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(input
);
32 DEFINE_GUID( device_path_guid
, 0x00000000, 0x0000, 0x0000, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf8 );
34 static CRITICAL_SECTION provider_cs
;
35 static CRITICAL_SECTION_DEBUG provider_cs_debug
=
38 { &provider_cs_debug
.ProcessLocksList
, &provider_cs_debug
.ProcessLocksList
},
39 0, 0, { (DWORD_PTR
)(__FILE__
": provider_cs") }
41 static CRITICAL_SECTION provider_cs
= { &provider_cs_debug
, -1, 0, 0, 0, 0 };
43 static struct list provider_list
= LIST_INIT( provider_list
);
47 IWineGameControllerProvider IWineGameControllerProvider_iface
;
48 IGameControllerProvider IGameControllerProvider_iface
;
51 IDirectInputDevice8W
*dinput_device
;
52 WCHAR device_path
[MAX_PATH
];
55 struct WineGameControllerVibration vibration
;
58 PHIDP_PREPARSED_DATA preparsed
;
59 HIDP_VALUE_CAPS haptics_rumble_caps
;
60 HIDP_VALUE_CAPS haptics_buzz_caps
;
61 HIDP_VALUE_CAPS haptics_left_caps
;
62 HIDP_VALUE_CAPS haptics_right_caps
;
68 static inline struct provider
*impl_from_IWineGameControllerProvider( IWineGameControllerProvider
*iface
)
70 return CONTAINING_RECORD( iface
, struct provider
, IWineGameControllerProvider_iface
);
73 static HRESULT WINAPI
wine_provider_QueryInterface( IWineGameControllerProvider
*iface
, REFIID iid
, void **out
)
75 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
77 TRACE( "iface %p, iid %s, out %p.\n", iface
, debugstr_guid( iid
), out
);
79 if (IsEqualGUID( iid
, &IID_IUnknown
) ||
80 IsEqualGUID( iid
, &IID_IInspectable
) ||
81 IsEqualGUID( iid
, &IID_IAgileObject
) ||
82 IsEqualGUID( iid
, &IID_IWineGameControllerProvider
))
84 IInspectable_AddRef( (*out
= &impl
->IWineGameControllerProvider_iface
) );
88 if (IsEqualGUID( iid
, &IID_IGameControllerProvider
))
90 IInspectable_AddRef( (*out
= &impl
->IGameControllerProvider_iface
) );
94 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid
) );
99 static ULONG WINAPI
wine_provider_AddRef( IWineGameControllerProvider
*iface
)
101 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
102 ULONG ref
= InterlockedIncrement( &impl
->ref
);
103 TRACE( "iface %p increasing refcount to %lu.\n", iface
, ref
);
107 static ULONG WINAPI
wine_provider_Release( IWineGameControllerProvider
*iface
)
109 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
110 ULONG ref
= InterlockedDecrement( &impl
->ref
);
112 TRACE( "iface %p decreasing refcount to %lu.\n", iface
, ref
);
116 IDirectInputDevice8_Release( impl
->dinput_device
);
117 HidD_FreePreparsedData( impl
->preparsed
);
118 CloseHandle( impl
->device
);
119 free( impl
->report_buf
);
126 static HRESULT WINAPI
wine_provider_GetIids( IWineGameControllerProvider
*iface
, ULONG
*iid_count
, IID
**iids
)
128 FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface
, iid_count
, iids
);
132 static HRESULT WINAPI
wine_provider_GetRuntimeClassName( IWineGameControllerProvider
*iface
, HSTRING
*class_name
)
134 FIXME( "iface %p, class_name %p stub!\n", iface
, class_name
);
138 static HRESULT WINAPI
wine_provider_GetTrustLevel( IWineGameControllerProvider
*iface
, TrustLevel
*trust_level
)
140 FIXME( "iface %p, trust_level %p stub!\n", iface
, trust_level
);
144 static BOOL CALLBACK
count_ffb_axes( const DIDEVICEOBJECTINSTANCEW
*obj
, void *args
)
147 if (obj
->dwType
& DIDFT_FFACTUATOR
) (*count
)++;
148 return DIENUM_CONTINUE
;
151 static HRESULT WINAPI
wine_provider_get_Type( IWineGameControllerProvider
*iface
, WineGameControllerType
*value
)
153 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
154 DIDEVICEINSTANCEW instance
= {.dwSize
= sizeof(DIDEVICEINSTANCEW
)};
157 TRACE( "iface %p, value %p.\n", iface
, value
);
159 if (FAILED(hr
= IDirectInputDevice8_GetDeviceInfo( impl
->dinput_device
, &instance
))) return hr
;
161 switch (GET_DIDEVICE_TYPE( instance
.dwDevType
))
163 case DI8DEVTYPE_DRIVING
: *value
= WineGameControllerType_RacingWheel
; break;
164 case DI8DEVTYPE_GAMEPAD
: *value
= WineGameControllerType_Gamepad
; break;
168 hr
= IDirectInputDevice8_EnumObjects( impl
->dinput_device
, count_ffb_axes
, &count
, DIDFT_AXIS
);
169 if (SUCCEEDED(hr
) && count
== 1) *value
= WineGameControllerType_RacingWheel
;
170 else *value
= WineGameControllerType_Joystick
;
178 static HRESULT WINAPI
wine_provider_get_AxisCount( IWineGameControllerProvider
*iface
, INT32
*value
)
180 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
181 DIDEVCAPS caps
= {.dwSize
= sizeof(DIDEVCAPS
)};
184 TRACE( "iface %p, value %p.\n", iface
, value
);
186 if (SUCCEEDED(hr
= IDirectInputDevice8_GetCapabilities( impl
->dinput_device
, &caps
)))
187 *value
= caps
.dwAxes
;
191 static HRESULT WINAPI
wine_provider_get_ButtonCount( IWineGameControllerProvider
*iface
, INT32
*value
)
193 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
194 DIDEVCAPS caps
= {.dwSize
= sizeof(DIDEVCAPS
)};
197 TRACE( "iface %p, value %p.\n", iface
, value
);
199 if (SUCCEEDED(hr
= IDirectInputDevice8_GetCapabilities( impl
->dinput_device
, &caps
)))
200 *value
= caps
.dwButtons
;
204 static HRESULT WINAPI
wine_provider_get_SwitchCount( IWineGameControllerProvider
*iface
, INT32
*value
)
206 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
207 DIDEVCAPS caps
= {.dwSize
= sizeof(DIDEVCAPS
)};
210 TRACE( "iface %p, value %p.\n", iface
, value
);
212 if (SUCCEEDED(hr
= IDirectInputDevice8_GetCapabilities( impl
->dinput_device
, &caps
)))
213 *value
= caps
.dwPOVs
;
217 static HRESULT WINAPI
wine_provider_get_State( IWineGameControllerProvider
*iface
, struct WineGameControllerState
*out
)
219 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
220 DIJOYSTATE2 state
= {0};
224 TRACE( "iface %p, out %p.\n", iface
, out
);
226 if (FAILED(hr
= IDirectInputDevice8_GetDeviceState( impl
->dinput_device
, sizeof(state
), &state
)))
228 WARN( "Failed to read device state, hr %#lx\n", hr
);
232 i
= ARRAY_SIZE(state
.rgbButtons
);
233 while (i
--) out
->buttons
[i
] = (state
.rgbButtons
[i
] != 0);
235 i
= ARRAY_SIZE(state
.rgdwPOV
);
238 if (state
.rgdwPOV
[i
] == ~0) out
->switches
[i
] = GameControllerSwitchPosition_Center
;
239 else out
->switches
[i
] = state
.rgdwPOV
[i
] * 8 / 36000 + 1;
243 out
->axes
[i
++] = state
.lX
/ 65535.;
244 out
->axes
[i
++] = state
.lY
/ 65535.;
245 out
->axes
[i
++] = state
.lZ
/ 65535.;
246 out
->axes
[i
++] = state
.lRx
/ 65535.;
247 out
->axes
[i
++] = state
.lRy
/ 65535.;
248 out
->axes
[i
++] = state
.lRz
/ 65535.;
249 out
->axes
[i
++] = state
.rglSlider
[0] / 65535.;
250 out
->axes
[i
++] = state
.rglSlider
[1] / 65535.;
251 out
->axes
[i
++] = state
.lVX
/ 65535.;
252 out
->axes
[i
++] = state
.lVY
/ 65535.;
253 out
->axes
[i
++] = state
.lVZ
/ 65535.;
254 out
->axes
[i
++] = state
.lVRx
/ 65535.;
255 out
->axes
[i
++] = state
.lVRy
/ 65535.;
256 out
->axes
[i
++] = state
.lVRz
/ 65535.;
257 out
->axes
[i
++] = state
.rglVSlider
[0] / 65535.;
258 out
->axes
[i
++] = state
.rglVSlider
[1] / 65535.;
259 out
->axes
[i
++] = state
.lAX
/ 65535.;
260 out
->axes
[i
++] = state
.lAY
/ 65535.;
261 out
->axes
[i
++] = state
.lAZ
/ 65535.;
262 out
->axes
[i
++] = state
.lARx
/ 65535.;
263 out
->axes
[i
++] = state
.lARy
/ 65535.;
264 out
->axes
[i
++] = state
.lARz
/ 65535.;
265 out
->axes
[i
++] = state
.rglASlider
[0] / 65535.;
266 out
->axes
[i
++] = state
.rglASlider
[1] / 65535.;
267 out
->axes
[i
++] = state
.lFX
/ 65535.;
268 out
->axes
[i
++] = state
.lFY
/ 65535.;
269 out
->axes
[i
++] = state
.lFZ
/ 65535.;
270 out
->axes
[i
++] = state
.lFRx
/ 65535.;
271 out
->axes
[i
++] = state
.lFRy
/ 65535.;
272 out
->axes
[i
++] = state
.lFRz
/ 65535.;
273 out
->axes
[i
++] = state
.rglFSlider
[0] / 65535.;
274 out
->axes
[i
++] = state
.rglFSlider
[1] / 65535.;
275 out
->timestamp
= GetTickCount64();
280 static HRESULT WINAPI
wine_provider_get_Vibration( IWineGameControllerProvider
*iface
, struct WineGameControllerVibration
*out
)
282 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
283 TRACE( "iface %p, out %p.\n", iface
, out
);
284 *out
= impl
->vibration
;
288 static HRESULT WINAPI
wine_provider_put_Vibration( IWineGameControllerProvider
*iface
, struct WineGameControllerVibration value
)
290 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
291 ULONG report_len
= impl
->caps
.OutputReportByteLength
;
292 PHIDP_PREPARSED_DATA preparsed
= impl
->preparsed
;
293 char *report_buf
= impl
->report_buf
;
298 TRACE( "iface %p, value %p.\n", iface
, &value
);
300 if (!memcmp( &impl
->vibration
, &value
, sizeof(value
) )) return S_OK
;
301 impl
->vibration
= value
;
303 status
= HidP_InitializeReportForID( HidP_Output
, impl
->haptics_report
, preparsed
, report_buf
, report_len
);
304 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_InitializeReportForID returned %#lx\n", status
);
306 collection
= impl
->haptics_rumble_caps
.LinkCollection
;
307 status
= HidP_SetUsageValue( HidP_Output
, HID_USAGE_PAGE_HAPTICS
, collection
, HID_USAGE_HAPTICS_INTENSITY
,
308 impl
->vibration
.rumble
, preparsed
, report_buf
, report_len
);
309 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_SetUsageValue INTENSITY returned %#lx\n", status
);
311 collection
= impl
->haptics_buzz_caps
.LinkCollection
;
312 status
= HidP_SetUsageValue( HidP_Output
, HID_USAGE_PAGE_HAPTICS
, collection
, HID_USAGE_HAPTICS_INTENSITY
,
313 impl
->vibration
.buzz
, preparsed
, report_buf
, report_len
);
314 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_SetUsageValue INTENSITY returned %#lx\n", status
);
316 collection
= impl
->haptics_left_caps
.LinkCollection
;
317 status
= HidP_SetUsageValue( HidP_Output
, HID_USAGE_PAGE_HAPTICS
, collection
, HID_USAGE_HAPTICS_INTENSITY
,
318 impl
->vibration
.left
, preparsed
, report_buf
, report_len
);
319 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_SetUsageValue INTENSITY returned %#lx\n", status
);
321 collection
= impl
->haptics_right_caps
.LinkCollection
;
322 status
= HidP_SetUsageValue( HidP_Output
, HID_USAGE_PAGE_HAPTICS
, collection
, HID_USAGE_HAPTICS_INTENSITY
,
323 impl
->vibration
.right
, preparsed
, report_buf
, report_len
);
324 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_SetUsageValue INTENSITY returned %#lx\n", status
);
326 ret
= HidD_SetOutputReport( impl
->device
, report_buf
, report_len
);
327 if (!ret
) WARN( "HidD_SetOutputReport failed with error %lu\n", GetLastError() );
332 static HRESULT WINAPI
wine_provider_get_ForceFeedbackMotor( IWineGameControllerProvider
*iface
, IForceFeedbackMotor
**value
)
334 struct provider
*impl
= impl_from_IWineGameControllerProvider( iface
);
335 DIDEVCAPS caps
= {.dwSize
= sizeof(DIDEVCAPS
)};
338 TRACE( "iface %p, value %p.\n", iface
, value
);
340 if (SUCCEEDED(hr
= IDirectInputDevice8_GetCapabilities( impl
->dinput_device
, &caps
)) && (caps
.dwFlags
& DIDC_FORCEFEEDBACK
))
341 return force_feedback_motor_create( impl
->dinput_device
, value
);
347 static const struct IWineGameControllerProviderVtbl wine_provider_vtbl
=
349 wine_provider_QueryInterface
,
350 wine_provider_AddRef
,
351 wine_provider_Release
,
352 /* IInspectable methods */
353 wine_provider_GetIids
,
354 wine_provider_GetRuntimeClassName
,
355 wine_provider_GetTrustLevel
,
356 /* IWineGameControllerProvider methods */
357 wine_provider_get_Type
,
358 wine_provider_get_AxisCount
,
359 wine_provider_get_ButtonCount
,
360 wine_provider_get_SwitchCount
,
361 wine_provider_get_State
,
362 wine_provider_get_Vibration
,
363 wine_provider_put_Vibration
,
364 wine_provider_get_ForceFeedbackMotor
,
367 DEFINE_IINSPECTABLE( game_provider
, IGameControllerProvider
, struct provider
, IWineGameControllerProvider_iface
)
369 static HRESULT WINAPI
game_provider_get_FirmwareVersionInfo( IGameControllerProvider
*iface
, GameControllerVersionInfo
*value
)
371 FIXME( "iface %p, value %p stub!\n", iface
, value
);
375 static HRESULT WINAPI
game_provider_get_HardwareProductId( IGameControllerProvider
*iface
, UINT16
*value
)
377 DIPROPDWORD vid_pid
= {.diph
= {.dwHeaderSize
= sizeof(DIPROPHEADER
), .dwSize
= sizeof(DIPROPDWORD
)}};
378 struct provider
*impl
= impl_from_IGameControllerProvider( iface
);
381 TRACE( "iface %p, value %p.\n", iface
, value
);
383 if (SUCCEEDED(hr
= IDirectInputDevice8_GetProperty( impl
->dinput_device
, DIPROP_VIDPID
, &vid_pid
.diph
)))
384 *value
= HIWORD(vid_pid
.dwData
);
388 static HRESULT WINAPI
game_provider_get_HardwareVendorId( IGameControllerProvider
*iface
, UINT16
*value
)
390 DIPROPDWORD vid_pid
= {.diph
= {.dwHeaderSize
= sizeof(DIPROPHEADER
), .dwSize
= sizeof(DIPROPDWORD
)}};
391 struct provider
*impl
= impl_from_IGameControllerProvider( iface
);
394 TRACE( "iface %p, value %p.\n", iface
, value
);
396 if (SUCCEEDED(hr
= IDirectInputDevice8_GetProperty( impl
->dinput_device
, DIPROP_VIDPID
, &vid_pid
.diph
)))
397 *value
= LOWORD(vid_pid
.dwData
);
401 static HRESULT WINAPI
game_provider_get_HardwareVersionInfo( IGameControllerProvider
*iface
, GameControllerVersionInfo
*value
)
403 FIXME( "iface %p, value %p stub!\n", iface
, value
);
407 static HRESULT WINAPI
game_provider_get_IsConnected( IGameControllerProvider
*iface
, boolean
*value
)
409 FIXME( "iface %p, value %p stub!\n", iface
, value
);
413 static const struct IGameControllerProviderVtbl game_provider_vtbl
=
415 game_provider_QueryInterface
,
416 game_provider_AddRef
,
417 game_provider_Release
,
418 /* IInspectable methods */
419 game_provider_GetIids
,
420 game_provider_GetRuntimeClassName
,
421 game_provider_GetTrustLevel
,
422 /* IGameControllerProvider methods */
423 game_provider_get_FirmwareVersionInfo
,
424 game_provider_get_HardwareProductId
,
425 game_provider_get_HardwareVendorId
,
426 game_provider_get_HardwareVersionInfo
,
427 game_provider_get_IsConnected
,
430 static void check_haptics_caps( struct provider
*provider
, HANDLE device
, PHIDP_PREPARSED_DATA preparsed
,
431 HIDP_LINK_COLLECTION_NODE
*collections
, HIDP_VALUE_CAPS
*caps
)
433 USHORT count
, report_len
= provider
->caps
.FeatureReportByteLength
;
434 ULONG parent
= caps
->LinkCollection
, waveform
= 0;
435 char *report_buf
= provider
->report_buf
;
436 HIDP_VALUE_CAPS value_caps
;
437 USAGE_AND_PAGE phy_usages
;
440 while (collections
[parent
].LinkUsagePage
!= HID_USAGE_PAGE_HAPTICS
||
441 collections
[parent
].LinkUsage
!= HID_USAGE_HAPTICS_SIMPLE_CONTROLLER
)
442 if (!(parent
= collections
[parent
].Parent
)) break;
444 if (collections
[parent
].LinkUsagePage
!= HID_USAGE_PAGE_HAPTICS
||
445 collections
[parent
].LinkUsage
!= HID_USAGE_HAPTICS_SIMPLE_CONTROLLER
)
447 WARN( "Failed to find haptics simple controller collection\n" );
450 phy_usages
.UsagePage
= collections
[collections
[parent
].Parent
].LinkUsagePage
;
451 phy_usages
.Usage
= collections
[collections
[parent
].Parent
].LinkUsage
;
453 status
= HidP_InitializeReportForID( HidP_Feature
, caps
->ReportID
, preparsed
, report_buf
, report_len
);
454 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_InitializeReportForID returned %#lx\n", status
);
455 if (!HidD_GetFeature( device
, report_buf
, report_len
))
457 WARN( "Failed to get waveform list report, error %lu\n", GetLastError() );
461 status
= HidP_GetUsageValue( HidP_Feature
, caps
->UsagePage
, caps
->LinkCollection
,
462 caps
->NotRange
.Usage
, &waveform
, preparsed
, report_buf
, report_len
);
463 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_GetUsageValue returned %#lx\n", status
);
466 status
= HidP_GetSpecificValueCaps( HidP_Output
, HID_USAGE_PAGE_HAPTICS
, parent
,
467 HID_USAGE_HAPTICS_INTENSITY
, &value_caps
, &count
, preparsed
);
468 if (status
!= HIDP_STATUS_SUCCESS
|| !count
) WARN( "Failed to get waveform intensity caps, status %#lx\n", status
);
469 else if (phy_usages
.UsagePage
== HID_USAGE_PAGE_GENERIC
&& phy_usages
.Usage
== HID_USAGE_GENERIC_Z
)
471 TRACE( "Found left rumble caps, report %u collection %u\n", value_caps
.ReportID
, value_caps
.LinkCollection
);
472 provider
->haptics_report
= value_caps
.ReportID
;
473 provider
->haptics_left_caps
= value_caps
;
475 else if (phy_usages
.UsagePage
== HID_USAGE_PAGE_GENERIC
&& phy_usages
.Usage
== HID_USAGE_GENERIC_RZ
)
477 TRACE( "Found right rumble caps, report %u collection %u\n", value_caps
.ReportID
, value_caps
.LinkCollection
);
478 provider
->haptics_report
= value_caps
.ReportID
;
479 provider
->haptics_right_caps
= value_caps
;
481 else if (waveform
== HID_USAGE_HAPTICS_WAVEFORM_RUMBLE
)
483 TRACE( "Found rumble caps, report %u collection %u\n", value_caps
.ReportID
, value_caps
.LinkCollection
);
484 provider
->haptics_report
= value_caps
.ReportID
;
485 provider
->haptics_rumble_caps
= value_caps
;
487 else if (waveform
== HID_USAGE_HAPTICS_WAVEFORM_BUZZ
)
489 TRACE( "Found buzz caps, report %u collection %u\n", value_caps
.ReportID
, value_caps
.LinkCollection
);
490 provider
->haptics_report
= value_caps
.ReportID
;
491 provider
->haptics_buzz_caps
= value_caps
;
493 else FIXME( "Unsupported waveform type %#lx\n", waveform
);
496 static void open_haptics_device( struct provider
*provider
)
498 HIDP_LINK_COLLECTION_NODE
*collections
;
499 PHIDP_PREPARSED_DATA preparsed
= NULL
;
500 ULONG i
, size
, coll_count
= 0;
501 USHORT count
, caps_count
= 0;
502 HIDP_VALUE_CAPS caps
[8];
506 device
= CreateFileW( provider
->device_path
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
507 NULL
, OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
| FILE_FLAG_NO_BUFFERING
, 0 );
508 if (device
== INVALID_HANDLE_VALUE
) return;
510 if (!HidD_GetPreparsedData( device
, &preparsed
)) goto failed
;
511 if (HidP_GetCaps( preparsed
, &provider
->caps
) != HIDP_STATUS_SUCCESS
) goto failed
;
513 size
= max( provider
->caps
.OutputReportByteLength
, provider
->caps
.FeatureReportByteLength
);
514 if (!(provider
->report_buf
= malloc( size
))) goto failed
;
516 coll_count
= provider
->caps
.NumberLinkCollectionNodes
;
517 if (!(collections
= malloc( sizeof(*collections
) * coll_count
))) goto failed
;
519 status
= HidP_GetLinkCollectionNodes( collections
, &coll_count
, preparsed
);
520 if (status
!= HIDP_STATUS_SUCCESS
) WARN( "HidP_GetLinkCollectionNodes returned %#lx\n", status
);
521 else for (i
= 0; i
< coll_count
; ++i
)
523 if (collections
[i
].LinkUsagePage
!= HID_USAGE_PAGE_HAPTICS
) continue;
524 if (collections
[i
].LinkUsage
== HID_USAGE_HAPTICS_WAVEFORM_LIST
)
526 count
= ARRAY_SIZE(caps
) - caps_count
;
527 status
= HidP_GetSpecificValueCaps( HidP_Feature
, HID_USAGE_PAGE_ORDINAL
, i
, 0,
528 caps
+ caps_count
, &count
, preparsed
);
529 if (status
== HIDP_STATUS_SUCCESS
) caps_count
+= count
;
532 for (i
= 0; i
< caps_count
; ++i
) check_haptics_caps( provider
, device
, preparsed
, collections
, caps
+ i
);
535 provider
->preparsed
= preparsed
;
536 provider
->device
= device
;
540 free( provider
->report_buf
);
541 provider
->report_buf
= NULL
;
542 HidD_FreePreparsedData( preparsed
);
543 CloseHandle( device
);
546 void provider_create( const WCHAR
*device_path
)
548 IDirectInputDevice8W
*dinput_device
;
549 IGameControllerProvider
*provider
;
550 struct provider
*impl
, *entry
;
551 GUID guid
= device_path_guid
;
552 IDirectInput8W
*dinput
;
557 if (wcsnicmp( device_path
, L
"\\\\?\\HID#", 8 )) return;
558 if ((tmp
= wcschr( device_path
+ 8, '#' )) && !wcsnicmp( tmp
- 6, L
"&IG_", 4 )) return;
560 TRACE( "device_path %s\n", debugstr_w( device_path
) );
562 *(const WCHAR
**)&guid
= device_path
;
563 if (FAILED(DirectInput8Create( windows_gaming_input
, DIRECTINPUT_VERSION
, &IID_IDirectInput8W
,
564 (void **)&dinput
, NULL
))) return;
565 hr
= IDirectInput8_CreateDevice( dinput
, &guid
, &dinput_device
, NULL
);
566 IDirectInput8_Release( dinput
);
567 if (FAILED(hr
)) return;
569 if (FAILED(hr
= IDirectInputDevice8_SetCooperativeLevel( dinput_device
, 0, DISCL_BACKGROUND
| DISCL_NONEXCLUSIVE
))) goto done
;
570 if (FAILED(hr
= IDirectInputDevice8_SetDataFormat( dinput_device
, &c_dfDIJoystick2
))) goto done
;
571 if (FAILED(hr
= IDirectInputDevice8_Acquire( dinput_device
))) goto done
;
573 if (!(impl
= calloc( 1, sizeof(*impl
) ))) goto done
;
574 impl
->IWineGameControllerProvider_iface
.lpVtbl
= &wine_provider_vtbl
;
575 impl
->IGameControllerProvider_iface
.lpVtbl
= &game_provider_vtbl
;
576 IDirectInputDevice_AddRef( dinput_device
);
577 impl
->dinput_device
= dinput_device
;
580 wcscpy( impl
->device_path
, device_path
);
581 list_init( &impl
->entry
);
582 open_haptics_device( impl
);
584 provider
= &impl
->IGameControllerProvider_iface
;
585 TRACE( "created WineGameControllerProvider %p\n", provider
);
587 EnterCriticalSection( &provider_cs
);
588 LIST_FOR_EACH_ENTRY( entry
, &provider_list
, struct provider
, entry
)
589 if ((found
= !wcsicmp( entry
->device_path
, device_path
))) break;
590 if (!found
) list_add_tail( &provider_list
, &impl
->entry
);
591 LeaveCriticalSection( &provider_cs
);
593 if (found
) IGameControllerProvider_Release( provider
);
594 else manager_on_provider_created( provider
);
596 IDirectInputDevice_Release( dinput_device
);
599 void provider_remove( const WCHAR
*device_path
)
601 IGameControllerProvider
*provider
;
602 struct provider
*entry
;
605 TRACE( "device_path %s\n", debugstr_w( device_path
) );
607 EnterCriticalSection( &provider_cs
);
608 LIST_FOR_EACH_ENTRY( entry
, &provider_list
, struct provider
, entry
)
609 if ((found
= !wcsicmp( entry
->device_path
, device_path
))) break;
610 if (found
) list_remove( &entry
->entry
);
611 LeaveCriticalSection( &provider_cs
);
613 if (!found
) WARN( "provider not found for device %s\n", debugstr_w( device_path
) );
616 provider
= &entry
->IGameControllerProvider_iface
;
617 manager_on_provider_removed( provider
);
618 IGameControllerProvider_Release( provider
);