wineps: Handle EMR_INVERTRGN record in spool files.
[wine.git] / dlls / windows.gaming.input / provider.c
blob6908d733a53b20f25632aac4fe58e4500c81eb0d
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
20 #include "private.h"
22 #include "initguid.h"
23 #include "ddk/hidsdi.h"
24 #include "dinput.h"
25 #include "provider.h"
26 #include "hidusage.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 =
37 0, 0, &provider_cs,
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 );
45 struct provider
47 IWineGameControllerProvider IWineGameControllerProvider_iface;
48 IGameControllerProvider IGameControllerProvider_iface;
49 LONG ref;
51 IDirectInputDevice8W *dinput_device;
52 WCHAR device_path[MAX_PATH];
53 struct list entry;
55 struct WineGameControllerVibration vibration;
57 char *report_buf;
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;
63 BYTE haptics_report;
64 HIDP_CAPS caps;
65 HANDLE device;
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) );
85 return S_OK;
88 if (IsEqualGUID( iid, &IID_IGameControllerProvider ))
90 IInspectable_AddRef( (*out = &impl->IGameControllerProvider_iface) );
91 return S_OK;
94 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
95 *out = NULL;
96 return E_NOINTERFACE;
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 );
104 return 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 );
114 if (!ref)
116 IDirectInputDevice8_Release( impl->dinput_device );
117 HidD_FreePreparsedData( impl->preparsed );
118 CloseHandle( impl->device );
119 free( impl->report_buf );
120 free( impl );
123 return ref;
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 );
129 return E_NOTIMPL;
132 static HRESULT WINAPI wine_provider_GetRuntimeClassName( IWineGameControllerProvider *iface, HSTRING *class_name )
134 FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
135 return E_NOTIMPL;
138 static HRESULT WINAPI wine_provider_GetTrustLevel( IWineGameControllerProvider *iface, TrustLevel *trust_level )
140 FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
141 return E_NOTIMPL;
144 static BOOL CALLBACK count_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *args )
146 DWORD *count = 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)};
155 HRESULT hr;
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;
165 default:
167 DWORD count = 0;
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;
171 break;
175 return S_OK;
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)};
182 HRESULT hr;
184 TRACE( "iface %p, value %p.\n", iface, value );
186 if (SUCCEEDED(hr = IDirectInputDevice8_GetCapabilities( impl->dinput_device, &caps )))
187 *value = caps.dwAxes;
188 return hr;
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)};
195 HRESULT hr;
197 TRACE( "iface %p, value %p.\n", iface, value );
199 if (SUCCEEDED(hr = IDirectInputDevice8_GetCapabilities( impl->dinput_device, &caps )))
200 *value = caps.dwButtons;
201 return hr;
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)};
208 HRESULT hr;
210 TRACE( "iface %p, value %p.\n", iface, value );
212 if (SUCCEEDED(hr = IDirectInputDevice8_GetCapabilities( impl->dinput_device, &caps )))
213 *value = caps.dwPOVs;
214 return hr;
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};
221 UINT32 i = 0;
222 HRESULT hr;
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 );
229 return S_OK;
232 i = ARRAY_SIZE(state.rgbButtons);
233 while (i--) out->buttons[i] = (state.rgbButtons[i] != 0);
235 i = ARRAY_SIZE(state.rgdwPOV);
236 while (i--)
238 if (state.rgdwPOV[i] == ~0) out->switches[i] = GameControllerSwitchPosition_Center;
239 else out->switches[i] = state.rgdwPOV[i] * 8 / 36000 + 1;
242 i = 0;
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();
277 return S_OK;
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;
285 return S_OK;
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;
294 USHORT collection;
295 NTSTATUS status;
296 BOOL ret;
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() );
329 return S_OK;
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)};
336 HRESULT hr;
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 );
343 *value = NULL;
344 return S_OK;
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 );
372 return E_NOTIMPL;
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 );
379 HRESULT hr;
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);
385 return hr;
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 );
392 HRESULT hr;
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);
398 return hr;
401 static HRESULT WINAPI game_provider_get_HardwareVersionInfo( IGameControllerProvider *iface, GameControllerVersionInfo *value )
403 FIXME( "iface %p, value %p stub!\n", iface, value );
404 return E_NOTIMPL;
407 static HRESULT WINAPI game_provider_get_IsConnected( IGameControllerProvider *iface, boolean *value )
409 FIXME( "iface %p, value %p stub!\n", iface, value );
410 return E_NOTIMPL;
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;
438 NTSTATUS status;
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" );
448 return;
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() );
458 return;
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 );
465 count = 1;
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];
503 NTSTATUS status;
504 HANDLE device;
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 );
533 free( collections );
535 provider->preparsed = preparsed;
536 provider->device = device;
537 return;
539 failed:
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;
553 BOOL found = FALSE;
554 const WCHAR *tmp;
555 HRESULT hr;
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;
578 impl->ref = 1;
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 );
595 done:
596 IDirectInputDevice_Release( dinput_device );
599 void provider_remove( const WCHAR *device_path )
601 IGameControllerProvider *provider;
602 struct provider *entry;
603 BOOL found = FALSE;
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 ) );
614 else
616 provider = &entry->IGameControllerProvider_iface;
617 manager_on_provider_removed( provider );
618 IGameControllerProvider_Release( provider );