Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
This introduces a new background thread, although I still don't have a good way to shut it down. For now the thread holds a module reference and stays alive until process shutdown.
dlls/windows.gaming.input/Makefile.in | 3 +- dlls/windows.gaming.input/provider.idl | 55 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 dlls/windows.gaming.input/provider.idl
diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index d7f93283bec..46470ac045b 100644 --- a/dlls/windows.gaming.input/Makefile.in +++ b/dlls/windows.gaming.input/Makefile.in @@ -8,4 +8,5 @@ C_SRCS = \ manager.c \ vector.c
-IDL_SRCS = classes.idl +IDL_SRCS = classes.idl \ + provider.idl diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl new file mode 100644 index 00000000000..dc67a94acaa --- /dev/null +++ b/dlls/windows.gaming.input/provider.idl @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep header + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +import "inspectable.idl"; +import "asyncinfo.idl"; +import "eventtoken.idl"; +import "windowscontracts.idl"; +import "windows.foundation.idl"; +import "windows.gaming.input.idl"; +import "windows.gaming.input.custom.idl"; + +namespace Windows.Gaming.Input.Custom { + interface IWineGameControllerProvider; + runtimeclass WineGameControllerProvider; + + [ + exclusiveto(Windows.Gaming.Input.Custom.WineGameControllerProvider), + uuid(8967b6ef-a4de-4b9a-984a-9f920b4d1b26) + ] + interface IWineGameControllerProvider : IInspectable + requires Windows.Gaming.Input.Custom.IGameControllerProvider + { + } + + [ + marshaling_behavior(agile), + threading(both) + ] + runtimeclass WineGameControllerProvider + { + [default] interface Windows.Gaming.Input.Custom.IWineGameControllerProvider; + interface Windows.Gaming.Input.Custom.IGameControllerProvider; + } +}
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/windows.gaming.input/Makefile.in | 3 +- dlls/windows.gaming.input/main.c | 145 ++++++++++++++++++++++++++ dlls/windows.gaming.input/private.h | 3 + dlls/windows.gaming.input/provider.c | 37 +++++++ 4 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 dlls/windows.gaming.input/provider.c
diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index 46470ac045b..1a3cdb364f2 100644 --- a/dlls/windows.gaming.input/Makefile.in +++ b/dlls/windows.gaming.input/Makefile.in @@ -1,11 +1,12 @@ MODULE = windows.gaming.input.dll -IMPORTS = combase uuid +IMPORTS = combase uuid user32 setupapi hid
C_SRCS = \ controller.c \ gamepad.c \ main.c \ manager.c \ + provider.c \ vector.c
IDL_SRCS = classes.idl \ diff --git a/dlls/windows.gaming.input/main.c b/dlls/windows.gaming.input/main.c index c050d1d5113..0fc23cea5bf 100644 --- a/dlls/windows.gaming.input/main.c +++ b/dlls/windows.gaming.input/main.c @@ -23,6 +23,13 @@ #define COBJMACROS #include "windef.h" #include "winbase.h" +#include "winreg.h" +#include "winuser.h" + +#include "dbt.h" +#include "ddk/hidclass.h" +#include "ddk/hidsdi.h" +#include "setupapi.h"
#include "initguid.h" #include "private.h" @@ -31,6 +38,127 @@
WINE_DEFAULT_DEBUG_CHANNEL(input);
+static HINSTANCE windows_gaming_input; + +DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 ); + +static LRESULT CALLBACK devnotify_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + DEV_BROADCAST_DEVICEINTERFACE_W *iface; + + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (msg == WM_DEVICECHANGE) + { + switch (wparam) + { + case DBT_DEVICEARRIVAL: + iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)lparam; + provider_create( iface->dbcc_name ); + break; + case DBT_DEVICEREMOVECOMPLETE: + iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)lparam; + provider_remove( iface->dbcc_name ); + break; + default: break; + } + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static void initialize_providers( void ) +{ + char buffer[offsetof( SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath[MAX_PATH] )]; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer; + SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; + GUID guid = GUID_DEVINTERFACE_WINEXINPUT; + HDEVINFO set; + DWORD i = 0; + + set = SetupDiGetClassDevsW( NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE | DIGCF_PRESENT ); + + while (SetupDiEnumDeviceInterfaces( set, NULL, &guid, i++, &iface )) + { + detail->cbSize = sizeof(*detail); + if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, NULL )) continue; + provider_create( detail->DevicePath ); + } + + HidD_GetHidGuid( &guid ); + + while (SetupDiEnumDeviceInterfaces( set, NULL, &guid, i++, &iface )) + { + detail->cbSize = sizeof(*detail); + if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, NULL )) continue; + provider_create( detail->DevicePath ); + } + + SetupDiDestroyDeviceInfoList( set ); +} + +static DWORD WINAPI monitor_thread_proc( void *param ) +{ + DEV_BROADCAST_DEVICEINTERFACE_W filter = + { + .dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W), + .dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE, + }; + WNDCLASSEXW wndclass = + { + .cbSize = sizeof(WNDCLASSEXW), + .lpszClassName = L"__wine_gaming_input_devnotify", + .lpfnWndProc = devnotify_wndproc, + }; + HANDLE start_event = param; + HDEVNOTIFY devnotify; + HMODULE module; + HWND hwnd; + MSG msg; + + GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (void *)windows_gaming_input, &module ); + RegisterClassExW( &wndclass ); + hwnd = CreateWindowExW( 0, wndclass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL ); + devnotify = RegisterDeviceNotificationW( hwnd, &filter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES ); + initialize_providers(); + SetEvent( start_event ); + + do + { + while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + } while (!MsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE )); + + UnregisterDeviceNotification( devnotify ); + DestroyWindow( hwnd ); + UnregisterClassW( wndclass.lpszClassName, NULL ); + + FreeLibraryAndExitThread( module, 0 ); + return 0; +} + +static BOOL WINAPI start_monitor_thread( INIT_ONCE *once, void *param, void **context ) +{ + HANDLE thread, start_event; + + start_event = CreateEventA( NULL, FALSE, FALSE, NULL ); + if (!start_event) ERR( "Failed to create start event, error %lu\n", GetLastError() ); + + thread = CreateThread( NULL, 0, monitor_thread_proc, start_event, 0, NULL ); + if (!thread) ERR( "Failed to create monitor thread, error %lu\n", GetLastError() ); + else + { + WaitForSingleObject( start_event, INFINITE ); + CloseHandle( thread ); + } + + CloseHandle( start_event ); + return !!thread; +} + HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) { FIXME("clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out); @@ -39,10 +167,13 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out)
HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory **factory ) { + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; const WCHAR *buffer = WindowsGetStringRawBuffer( class_str, NULL );
TRACE( "class %s, factory %p.\n", debugstr_w(buffer), factory );
+ InitOnceExecuteOnce( &init_once, start_monitor_thread, NULL, NULL ); + *factory = NULL;
if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_RawGameController )) @@ -55,3 +186,17 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory ** if (*factory) return S_OK; return REGDB_E_CLASSNOTREG; } + +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) +{ + TRACE( "instance %p, reason %lu, reserved %p.\n", instance, reason, reserved ); + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls( instance ); + windows_gaming_input = instance; + break; + } + return TRUE; +} diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index c6dd1105896..87a3f677d8c 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -41,6 +41,9 @@ extern IActivationFactory *manager_factory;
extern HRESULT vector_create( REFIID iid, REFIID view_iid, void **out );
+extern void provider_create( const WCHAR *device_path ); +extern void provider_remove( const WCHAR *device_path ); + #define DEFINE_IINSPECTABLE( pfx, iface_type, impl_type, base_iface ) \ static inline impl_type *impl_from_##iface_type( iface_type *iface ) \ { \ diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c new file mode 100644 index 00000000000..710349eb75f --- /dev/null +++ b/dlls/windows.gaming.input/provider.c @@ -0,0 +1,37 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" + +#include "initguid.h" +#include "provider.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +void provider_create( const WCHAR *device_path ) +{ + FIXME( "device_path %s stub!\n", debugstr_w( device_path ) ); +} + +void provider_remove( const WCHAR *device_path ) +{ + FIXME( "device_path %s stub!\n", debugstr_w( device_path ) ); +}
So that we can access all the controller devices conveniently through DInput without having to reimplement the whole HID client, or having to enumerate the devices every time a new device needs to be open, and being able to workaround the limitations of the device the XInput controllers are usually exposing.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 4f1c8444e43..c97588d8a07 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -50,6 +50,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dinput);
DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 ); DEFINE_GUID( hid_joystick_guid, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 ); +DEFINE_GUID( device_path_guid, 0x00000000, 0x0000, 0x0000, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf8 ); DEFINE_DEVPROPKEY( DEVPROPKEY_HID_HANDLE, 0xbc62e415, 0xf4fe, 0x405c, 0x8e, 0xda, 0x63, 0x6f, 0xb5, 0x9f, 0x08, 0x98, 2 );
struct pid_control_report @@ -2041,7 +2042,12 @@ HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDi else if (IsEqualGUID( &hid_joystick_guid, &instance.guidInstance )) instance.guidInstance = *guid; else - return DIERR_DEVICENOTREG; + { + instance.guidInstance.Data1 = device_path_guid.Data1; + instance.guidInstance.Data2 = device_path_guid.Data2; + instance.guidInstance.Data3 = device_path_guid.Data3; + if (!IsEqualGUID( &device_path_guid, &instance.guidInstance )) return DIERR_DEVICENOTREG; + }
hr = dinput_device_alloc( sizeof(struct hid_joystick), &hid_joystick_vtbl, guid, dinput, (void **)&impl ); if (FAILED(hr)) return hr; @@ -2050,8 +2056,16 @@ HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDi impl->base.read_event = CreateEventW( NULL, TRUE, FALSE, NULL ); impl->internal_ref = 1;
- hr = hid_joystick_device_open( -1, &instance, impl->device_path, &impl->device, &impl->preparsed, - &attrs, &impl->caps, dinput->dwVersion ); + if (!IsEqualGUID( &device_path_guid, &instance.guidInstance )) + hr = hid_joystick_device_open( -1, &instance, impl->device_path, &impl->device, &impl->preparsed, + &attrs, &impl->caps, dinput->dwVersion ); + else + { + wcscpy( impl->device_path, *(const WCHAR **)guid ); + if (!hid_joystick_device_try_open( 0, impl->device_path, &impl->device, &impl->preparsed, &attrs, + &impl->caps, &instance, dinput->dwVersion )) + hr = DIERR_DEVICENOTREG; + } if (hr != DI_OK) goto failed;
impl->base.instance = instance;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/windows.gaming.input/Makefile.in | 2 +- dlls/windows.gaming.input/main.c | 2 +- dlls/windows.gaming.input/manager.c | 10 ++ dlls/windows.gaming.input/private.h | 4 + dlls/windows.gaming.input/provider.c | 222 +++++++++++++++++++++++++- 5 files changed, 236 insertions(+), 4 deletions(-)
diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index 1a3cdb364f2..93d167f61e9 100644 --- a/dlls/windows.gaming.input/Makefile.in +++ b/dlls/windows.gaming.input/Makefile.in @@ -1,5 +1,5 @@ MODULE = windows.gaming.input.dll -IMPORTS = combase uuid user32 setupapi hid +IMPORTS = combase uuid user32 dinput8 setupapi hid
C_SRCS = \ controller.c \ diff --git a/dlls/windows.gaming.input/main.c b/dlls/windows.gaming.input/main.c index 0fc23cea5bf..03d47e1ce2f 100644 --- a/dlls/windows.gaming.input/main.c +++ b/dlls/windows.gaming.input/main.c @@ -38,7 +38,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(input);
-static HINSTANCE windows_gaming_input; +HINSTANCE windows_gaming_input;
DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 );
diff --git a/dlls/windows.gaming.input/manager.c b/dlls/windows.gaming.input/manager.c index 61f639b8e7e..e6366256fe7 100644 --- a/dlls/windows.gaming.input/manager.c +++ b/dlls/windows.gaming.input/manager.c @@ -199,3 +199,13 @@ static struct manager_statics manager_statics = };
IActivationFactory *manager_factory = &manager_statics.IActivationFactory_iface; + +void manager_on_provider_created( IGameControllerProvider *provider ) +{ + FIXME( "provider %p stub!\n", provider ); +} + +void manager_on_provider_removed( IGameControllerProvider *provider ) +{ + FIXME( "provider %p stub!\n", provider ); +} diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 87a3f677d8c..a0bd45b3f20 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -35,6 +35,7 @@ #define WIDL_using_Windows_Gaming_Input_Custom #include "windows.gaming.input.custom.h"
+extern HINSTANCE windows_gaming_input; extern IActivationFactory *controller_factory; extern IActivationFactory *gamepad_factory; extern IActivationFactory *manager_factory; @@ -44,6 +45,9 @@ extern HRESULT vector_create( REFIID iid, REFIID view_iid, void **out ); extern void provider_create( const WCHAR *device_path ); extern void provider_remove( const WCHAR *device_path );
+extern void manager_on_provider_created( IGameControllerProvider *provider ); +extern void manager_on_provider_removed( IGameControllerProvider *provider ); + #define DEFINE_IINSPECTABLE( pfx, iface_type, impl_type, base_iface ) \ static inline impl_type *impl_from_##iface_type( iface_type *iface ) \ { \ diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index 710349eb75f..9ab9682f221 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -20,18 +20,236 @@ #include "private.h"
#include "initguid.h" +#include "dinput.h" #include "provider.h"
#include "wine/debug.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(input);
+DEFINE_GUID( device_path_guid, 0x00000000, 0x0000, 0x0000, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf8 ); + +static CRITICAL_SECTION provider_cs; +static CRITICAL_SECTION_DEBUG provider_cs_debug = +{ + 0, 0, &provider_cs, + { &provider_cs_debug.ProcessLocksList, &provider_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": provider_cs") } +}; +static CRITICAL_SECTION provider_cs = { &provider_cs_debug, -1, 0, 0, 0, 0 }; + +static struct list provider_list = LIST_INIT( provider_list ); + +struct provider +{ + IWineGameControllerProvider IWineGameControllerProvider_iface; + IGameControllerProvider IGameControllerProvider_iface; + LONG ref; + + IDirectInputDevice8W *dinput_device; + WCHAR device_path[MAX_PATH]; + struct list entry; +}; + +static inline struct provider *impl_from_IWineGameControllerProvider( IWineGameControllerProvider *iface ) +{ + return CONTAINING_RECORD( iface, struct provider, IWineGameControllerProvider_iface ); +} + +static HRESULT WINAPI wine_provider_QueryInterface( IWineGameControllerProvider *iface, REFIID iid, void **out ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IWineGameControllerProvider )) + { + IInspectable_AddRef( (*out = &impl->IWineGameControllerProvider_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IGameControllerProvider )) + { + IInspectable_AddRef( (*out = &impl->IGameControllerProvider_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI wine_provider_AddRef( IWineGameControllerProvider *iface ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI wine_provider_Release( IWineGameControllerProvider *iface ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + IDirectInputDevice8_Release( impl->dinput_device ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI wine_provider_GetIids( IWineGameControllerProvider *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI wine_provider_GetRuntimeClassName( IWineGameControllerProvider *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI wine_provider_GetTrustLevel( IWineGameControllerProvider *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = +{ + wine_provider_QueryInterface, + wine_provider_AddRef, + wine_provider_Release, + /* IInspectable methods */ + wine_provider_GetIids, + wine_provider_GetRuntimeClassName, + wine_provider_GetTrustLevel, +}; + +DEFINE_IINSPECTABLE( game_provider, IGameControllerProvider, struct provider, IWineGameControllerProvider_iface ) + +static HRESULT WINAPI game_provider_get_FirmwareVersionInfo( IGameControllerProvider *iface, GameControllerVersionInfo *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI game_provider_get_HardwareProductId( IGameControllerProvider *iface, UINT16 *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI game_provider_get_HardwareVendorId( IGameControllerProvider *iface, UINT16 *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI game_provider_get_HardwareVersionInfo( IGameControllerProvider *iface, GameControllerVersionInfo *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI game_provider_get_IsConnected( IGameControllerProvider *iface, boolean *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static const struct IGameControllerProviderVtbl game_provider_vtbl = +{ + game_provider_QueryInterface, + game_provider_AddRef, + game_provider_Release, + /* IInspectable methods */ + game_provider_GetIids, + game_provider_GetRuntimeClassName, + game_provider_GetTrustLevel, + /* IGameControllerProvider methods */ + game_provider_get_FirmwareVersionInfo, + game_provider_get_HardwareProductId, + game_provider_get_HardwareVendorId, + game_provider_get_HardwareVersionInfo, + game_provider_get_IsConnected, +}; + void provider_create( const WCHAR *device_path ) { - FIXME( "device_path %s stub!\n", debugstr_w( device_path ) ); + IDirectInputDevice8W *dinput_device; + IGameControllerProvider *provider; + struct provider *impl, *entry; + GUID guid = device_path_guid; + IDirectInput8W *dinput; + BOOL found = FALSE; + const WCHAR *tmp; + HRESULT hr; + + if (wcsnicmp( device_path, L"\\?\HID#", 8 )) return; + if ((tmp = wcschr( device_path + 8, '#' )) && !wcsnicmp( tmp - 6, L"&IG_", 4 )) return; + + TRACE( "device_path %s\n", debugstr_w( device_path ) ); + + *(const WCHAR **)&guid = device_path; + if (FAILED(DirectInput8Create( windows_gaming_input, DIRECTINPUT_VERSION, &IID_IDirectInput8W, + (void **)&dinput, NULL ))) return; + hr = IDirectInput8_CreateDevice( dinput, &guid, &dinput_device, NULL ); + IDirectInput8_Release( dinput ); + if (FAILED(hr)) return; + + if (!(impl = calloc( 1, sizeof(*impl) ))) goto done; + impl->IWineGameControllerProvider_iface.lpVtbl = &wine_provider_vtbl; + impl->IGameControllerProvider_iface.lpVtbl = &game_provider_vtbl; + IDirectInputDevice_AddRef( dinput_device ); + impl->dinput_device = dinput_device; + impl->ref = 1; + + wcscpy( impl->device_path, device_path ); + list_init( &impl->entry ); + provider = &impl->IGameControllerProvider_iface; + TRACE( "created WineGameControllerProvider %p\n", provider ); + + EnterCriticalSection( &provider_cs ); + LIST_FOR_EACH_ENTRY( entry, &provider_list, struct provider, entry ) + if ((found = !wcscmp( entry->device_path, device_path ))) break; + if (!found) list_add_tail( &provider_list, &impl->entry ); + LeaveCriticalSection( &provider_cs ); + + if (found) IGameControllerProvider_Release( provider ); + else manager_on_provider_created( provider ); +done: + IDirectInputDevice_Release( dinput_device ); }
void provider_remove( const WCHAR *device_path ) { - FIXME( "device_path %s stub!\n", debugstr_w( device_path ) ); + IGameControllerProvider *provider; + struct provider *entry; + BOOL found = FALSE; + + TRACE( "device_path %s\n", debugstr_w( device_path ) ); + + EnterCriticalSection( &provider_cs ); + LIST_FOR_EACH_ENTRY( entry, &provider_list, struct provider, entry ) + if ((found = !wcscmp( entry->device_path, device_path ))) break; + if (found) list_remove( &entry->entry ); + LeaveCriticalSection( &provider_cs ); + + if (found) + { + provider = &entry->IGameControllerProvider_iface; + manager_on_provider_removed( provider ); + IGameControllerProvider_Release( provider ); + } }