It is important to add RawGameController instances to the vector in the initialization callback, because some games check the vector, as well as the Gamepad class vector in the OnGameControllerAdded callback.
This also removes the OnGameControllerAdded failure case, to avoid leaking controllers that were already added to the controller vector, and instead rely on the OnGameControllerRemoved call to do the cleanup.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
v2: * Initialize controller state in GetReading calls,
* Fix IWineGameControllerProvider query failure support in the class destruction.
dlls/dinput/tests/joystick8.c | 9 +-- dlls/windows.gaming.input/controller.c | 82 +++++++++++++++++++++----- dlls/windows.gaming.input/manager.c | 6 +- 3 files changed, 70 insertions(+), 27 deletions(-)
diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index 2d801aacda2..e74e76482f9 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -3340,17 +3340,10 @@ static void test_windows_gaming_input(void) ok( hr == S_OK, "get_RawGameControllers returned %#lx\n", hr ); hr = IVectorView_RawGameController_get_Size( controllers_view, &size ); ok( hr == S_OK, "get_Size returned %#lx\n", hr ); - todo_wine ok( size == 1, "got size %u\n", size ); hr = IVectorView_RawGameController_GetAt( controllers_view, 0, &raw_controller ); - todo_wine ok( hr == S_OK, "GetAt returned %#lx\n", hr ); IVectorView_RawGameController_Release( controllers_view ); - if (hr != S_OK) - { - IRawGameControllerStatics_Release( controller_statics ); - goto done; - }
/* HID gamepads aren't exposed as WGI gamepads on Windows */
@@ -3372,6 +3365,7 @@ static void test_windows_gaming_input(void) check_interface( raw_controller, &IID_IInspectable, TRUE ); check_interface( raw_controller, &IID_IAgileObject, TRUE ); check_interface( raw_controller, &IID_IRawGameController, TRUE ); + todo_wine check_interface( raw_controller, &IID_IRawGameController2, TRUE ); check_interface( raw_controller, &IID_IGameController, TRUE ); check_interface( raw_controller, &IID_IGamepad, FALSE ); @@ -3384,6 +3378,7 @@ static void test_windows_gaming_input(void) check_interface( game_controller, &IID_IInspectable, TRUE ); check_interface( game_controller, &IID_IAgileObject, TRUE ); check_interface( game_controller, &IID_IRawGameController, TRUE ); + todo_wine check_interface( game_controller, &IID_IRawGameController2, TRUE ); check_interface( game_controller, &IID_IGameController, TRUE ); check_interface( game_controller, &IID_IGamepad, FALSE ); diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index 9fb1b1a69a2..51ede5a0e47 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -24,6 +24,36 @@
WINE_DEFAULT_DEBUG_CHANNEL(input);
+static CRITICAL_SECTION controller_cs; +static CRITICAL_SECTION_DEBUG controller_cs_debug = +{ + 0, 0, &controller_cs, + { &controller_cs_debug.ProcessLocksList, &controller_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": controller_cs") } +}; +static CRITICAL_SECTION controller_cs = { &controller_cs_debug, -1, 0, 0, 0, 0 }; + +static IVector_RawGameController *controllers; + +static HRESULT init_controllers(void) +{ + static const struct vector_iids iids = + { + .vector = &IID_IVector_RawGameController, + .view = &IID_IVectorView_RawGameController, + .iterable = &IID_IIterable_RawGameController, + .iterator = &IID_IIterator_RawGameController, + }; + HRESULT hr; + + EnterCriticalSection( &controller_cs ); + if (controllers) hr = S_OK; + else hr = vector_create( &iids, (void **)&controllers ); + LeaveCriticalSection( &controller_cs ); + + return hr; +} + struct controller { IGameControllerImpl IGameControllerImpl_iface; @@ -118,13 +148,19 @@ static HRESULT WINAPI controller_Initialize( IGameControllerImpl *iface, IGameCo IGameControllerProvider *provider ) { struct controller *impl = impl_from_IGameControllerImpl( iface ); + HRESULT hr;
TRACE( "iface %p, outer %p, provider %p.\n", iface, outer, provider );
impl->IGameController_outer = outer; IGameControllerProvider_AddRef( (impl->provider = provider) );
- return S_OK; + EnterCriticalSection( &controller_cs ); + if (SUCCEEDED(hr = init_controllers())) + hr = IVector_RawGameController_Append( controllers, &impl->IRawGameController_iface ); + LeaveCriticalSection( &controller_cs ); + + return hr; }
static const struct IGameControllerImplVtbl controller_vtbl = @@ -381,23 +417,14 @@ static HRESULT WINAPI statics_remove_RawGameControllerRemoved( IRawGameControlle
static HRESULT WINAPI statics_get_RawGameControllers( IRawGameControllerStatics *iface, IVectorView_RawGameController **value ) { - static const struct vector_iids iids = - { - .vector = &IID_IVector_RawGameController, - .view = &IID_IVectorView_RawGameController, - .iterable = &IID_IIterable_RawGameController, - .iterator = &IID_IIterator_RawGameController, - }; - IVector_RawGameController *controllers; HRESULT hr;
TRACE( "iface %p, value %p.\n", iface, value );
- if (SUCCEEDED(hr = vector_create( &iids, (void **)&controllers ))) - { + EnterCriticalSection( &controller_cs ); + if (SUCCEEDED(hr = init_controllers())) hr = IVector_RawGameController_GetView( controllers, value ); - IVector_RawGameController_Release( controllers ); - } + LeaveCriticalSection( &controller_cs );
return hr; } @@ -468,8 +495,33 @@ static HRESULT WINAPI controller_factory_OnGameControllerAdded( ICustomGameContr
static HRESULT WINAPI controller_factory_OnGameControllerRemoved( ICustomGameControllerFactory *iface, IGameController *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + IRawGameController *controller; + BOOLEAN found; + UINT32 index; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IGameController_QueryInterface( value, &IID_IRawGameController, (void **)&controller ))) + return hr; + + EnterCriticalSection( &controller_cs ); + if (SUCCEEDED(hr = init_controllers())) + { + if (FAILED(hr = IVector_RawGameController_IndexOf( controllers, controller, &index, &found )) || !found) + WARN( "Could not find controller %p, hr %#lx!\n", controller, hr ); + else + hr = IVector_RawGameController_RemoveAt( controllers, index ); + } + LeaveCriticalSection( &controller_cs ); + + if (FAILED(hr)) + WARN( "Failed to remove controller %p, hr %#lx!\n", controller, hr ); + else if (found) + TRACE( "Removed controller %p.\n", controller ); + IRawGameController_Release( controller ); + + return S_OK; }
static const struct ICustomGameControllerFactoryVtbl controller_factory_vtbl = diff --git a/dlls/windows.gaming.input/manager.c b/dlls/windows.gaming.input/manager.c index 489588a68ab..6a8963a1fc4 100644 --- a/dlls/windows.gaming.input/manager.c +++ b/dlls/windows.gaming.input/manager.c @@ -502,11 +502,7 @@ void manager_on_provider_created( IGameControllerProvider *provider ) controller = LIST_ENTRY( entry, struct controller, entry ); hr = ICustomGameControllerFactory_OnGameControllerAdded( controller->factory, &controller->IGameController_iface ); - if (FAILED(hr)) - { - list_remove( &controller->entry ); - IGameController_Release( &controller->IGameController_iface ); - } + if (FAILED(hr)) WARN( "OnGameControllerAdded failed, hr %#lx\n", hr ); if (next == &controller_list) break; }
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/joystick8.c | 1 + dlls/windows.gaming.input/gamepad.c | 82 +++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 15 deletions(-)
diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index e74e76482f9..c4cfceeee3e 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -3356,6 +3356,7 @@ static void test_windows_gaming_input(void) ok( hr == S_OK, "get_Gamepads returned %#lx\n", hr ); hr = IVectorView_Gamepad_get_Size( gamepads_view, &size ); ok( hr == S_OK, "get_Size returned %#lx\n", hr ); + todo_wine /* but Wine currently intentionally does */ ok( size == 0, "got size %u\n", size ); IVectorView_Gamepad_Release( gamepads_view ); IGamepadStatics_Release( gamepad_statics ); diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index 172ca74e3f6..70c9f4cd792 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -24,6 +24,36 @@
WINE_DEFAULT_DEBUG_CHANNEL(input);
+static CRITICAL_SECTION gamepad_cs; +static CRITICAL_SECTION_DEBUG gamepad_cs_debug = +{ + 0, 0, &gamepad_cs, + { &gamepad_cs_debug.ProcessLocksList, &gamepad_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": gamepad_cs") } +}; +static CRITICAL_SECTION gamepad_cs = { &gamepad_cs_debug, -1, 0, 0, 0, 0 }; + +static IVector_Gamepad *gamepads; + +static HRESULT init_gamepads(void) +{ + static const struct vector_iids iids = + { + .vector = &IID_IVector_Gamepad, + .view = &IID_IVectorView_Gamepad, + .iterable = &IID_IIterable_Gamepad, + .iterator = &IID_IIterator_Gamepad, + }; + HRESULT hr; + + EnterCriticalSection( &gamepad_cs ); + if (gamepads) hr = S_OK; + else hr = vector_create( &iids, (void **)&gamepads ); + LeaveCriticalSection( &gamepad_cs ); + + return hr; +} + struct gamepad { IGameControllerImpl IGameControllerImpl_iface; @@ -118,13 +148,19 @@ static HRESULT WINAPI controller_Initialize( IGameControllerImpl *iface, IGameCo IGameControllerProvider *provider ) { struct gamepad *impl = impl_from_IGameControllerImpl( iface ); + HRESULT hr;
TRACE( "iface %p, outer %p, provider %p.\n", iface, outer, provider );
impl->IGameController_outer = outer; IGameControllerProvider_AddRef( (impl->provider = provider) );
- return S_OK; + EnterCriticalSection( &gamepad_cs ); + if (SUCCEEDED(hr = init_gamepads())) + hr = IVector_Gamepad_Append( gamepads, &impl->IGamepad_iface ); + LeaveCriticalSection( &gamepad_cs ); + + return hr; }
static const struct IGameControllerImplVtbl controller_vtbl = @@ -341,23 +377,14 @@ static HRESULT WINAPI statics_remove_GamepadRemoved( IGamepadStatics *iface, Eve
static HRESULT WINAPI statics_get_Gamepads( IGamepadStatics *iface, IVectorView_Gamepad **value ) { - static const struct vector_iids iids = - { - .vector = &IID_IVector_Gamepad, - .view = &IID_IVectorView_Gamepad, - .iterable = &IID_IIterable_Gamepad, - .iterator = &IID_IIterator_Gamepad, - }; - IVector_Gamepad *gamepads; HRESULT hr;
TRACE( "iface %p, value %p.\n", iface, value );
- if (SUCCEEDED(hr = vector_create( &iids, (void **)&gamepads ))) - { + EnterCriticalSection( &gamepad_cs ); + if (SUCCEEDED(hr = init_gamepads())) hr = IVector_Gamepad_GetView( gamepads, value ); - IVector_Gamepad_Release( gamepads ); - } + LeaveCriticalSection( &gamepad_cs );
return hr; } @@ -440,8 +467,33 @@ static HRESULT WINAPI controller_factory_OnGameControllerAdded( ICustomGameContr
static HRESULT WINAPI controller_factory_OnGameControllerRemoved( ICustomGameControllerFactory *iface, IGameController *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + IGamepad *gamepad; + BOOLEAN found; + UINT32 index; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IGameController_QueryInterface( value, &IID_IGamepad, (void **)&gamepad ))) + return hr; + + EnterCriticalSection( &gamepad_cs ); + if (SUCCEEDED(hr = init_gamepads())) + { + if (FAILED(hr = IVector_Gamepad_IndexOf( gamepads, gamepad, &index, &found )) || !found) + WARN( "Could not find gamepad %p, hr %#lx!\n", gamepad, hr ); + else + hr = IVector_Gamepad_RemoveAt( gamepads, index ); + } + LeaveCriticalSection( &gamepad_cs ); + + if (FAILED(hr)) + WARN( "Failed to remove gamepad %p, hr %#lx!\n", gamepad, hr ); + else if (found) + TRACE( "Removed gamepad %p.\n", gamepad ); + IGamepad_Release( gamepad ); + + return S_OK; }
static const struct ICustomGameControllerFactoryVtbl controller_factory_vtbl =
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=110246
Your paranoid android.
=== w8 (32 bit report) ===
dinput: hid.c:581: Test failed: 0x800: desc[5]: unexpected error 1058
Using a generic EventHandler<IInspectable *> implementation.
This adds several todo_wine because we currently do not implement custom game controller factories.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/hotplug.c | 21 +++-- dlls/windows.gaming.input/Makefile.in | 1 + dlls/windows.gaming.input/controller.c | 46 +++++++---- dlls/windows.gaming.input/event_handlers.c | 89 ++++++++++++++++++++++ dlls/windows.gaming.input/gamepad.c | 44 +++++++---- dlls/windows.gaming.input/manager.c | 1 - dlls/windows.gaming.input/private.h | 6 ++ dlls/windows.gaming.input/provider.c | 1 - include/windows.foundation.idl | 1 + 9 files changed, 171 insertions(+), 39 deletions(-) create mode 100644 dlls/windows.gaming.input/event_handlers.c
diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c index 46e297d5b91..24f913dc140 100644 --- a/dlls/dinput/tests/hotplug.c +++ b/dlls/dinput/tests/hotplug.c @@ -927,6 +927,7 @@ static LRESULT CALLBACK windows_gaming_input_wndproc( HWND hwnd, UINT msg, WPARA else { ok( wparam == DBT_DEVICEARRIVAL, "got wparam %#Ix\n", wparam ); + todo_wine /* Wine currently listens to WINEXINPUT device arrival, which is received earlier than HID */ ok( !controller_added.invoked, "controller added handler not invoked\n" ); ok( !controller_removed.invoked, "controller removed handler invoked\n" ); } @@ -967,6 +968,7 @@ static void test_windows_gaming_input(void) HSTRING str; UINT32 size; HRESULT hr; + DWORD ret; MSG msg;
if (!load_combase_functions()) return; @@ -1007,9 +1009,7 @@ static void test_windows_gaming_input(void) hr = IRawGameControllerStatics_add_RawGameControllerAdded( statics, &controller_added.IEventHandler_RawGameController_iface, &controller_added_token ); ok( hr == S_OK, "add_RawGameControllerAdded returned %#lx\n", hr ); - todo_wine ok( controller_added_token.value, "got token %I64u\n", controller_added_token.value ); - if (!controller_added_token.value) return;
hr = IRawGameControllerStatics_add_RawGameControllerRemoved( statics, &controller_removed.IEventHandler_RawGameController_iface, &controller_removed_token ); @@ -1038,6 +1038,7 @@ static void test_windows_gaming_input(void)
ok( controller_added.invoked, "controller added handler not invoked\n" ); ok( !controller_removed.invoked, "controller removed handler invoked\n" ); + todo_wine ok( custom_factory.create_controller_called, "CreateGameController not called\n" );
hr = IVectorView_RawGameController_get_Size( controller_view, &size ); @@ -1058,6 +1059,7 @@ static void test_windows_gaming_input(void)
hr = IGameControllerFactoryManagerStatics2_TryGetFactoryControllerFromGameController( manager_statics2, &custom_factory.ICustomGameControllerFactory_iface, game_controller, &tmp_game_controller ); + todo_wine ok( hr == S_OK, "TryGetFactoryControllerFromGameController returned %#lx\n", hr ); ok( !tmp_game_controller, "got controller %p\n", tmp_game_controller );
@@ -1111,7 +1113,9 @@ static void test_windows_gaming_input(void) thread = CreateThread( NULL, 0, dinput_test_device_thread, stop_event, 0, NULL ); ok( !!thread, "CreateThread failed, error %lu\n", GetLastError() ); wait_for_events( 1, &controller_added.event, INFINITE ); - wait_for_events( 1, &custom_factory.added_event, INFINITE ); + ret = wait_for_events( 1, &custom_factory.added_event, 500 ); + todo_wine + ok( !ret, "wait_for_events returned %#lx\n", ret ); hr = IRawGameControllerStatics_get_RawGameControllers( statics, &controller_view ); ok( hr == S_OK, "get_RawGameControllers returned %#lx\n", hr ); hr = IVectorView_RawGameController_GetAt( controller_view, 0, &raw_controller ); @@ -1122,8 +1126,10 @@ static void test_windows_gaming_input(void)
hr = IGameControllerFactoryManagerStatics2_TryGetFactoryControllerFromGameController( manager_statics2, &custom_factory.ICustomGameControllerFactory_iface, game_controller, &tmp_game_controller ); + todo_wine ok( hr == S_OK, "TryGetFactoryControllerFromGameController returned %#lx\n", hr ); ok( tmp_game_controller == custom_controller.IGameController_outer, "got controller %p\n", tmp_game_controller ); + if (hr != S_OK) goto next; hr = IGameController_QueryInterface( tmp_game_controller, &IID_IInspectable, (void **)&tmp_inspectable ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); ok( tmp_inspectable == (void *)tmp_game_controller, "got inspectable %p\n", tmp_inspectable ); @@ -1145,15 +1151,20 @@ static void test_windows_gaming_input(void)
IGameController_Release( tmp_game_controller );
+next: hr = IRawGameControllerStatics_FromGameController( statics, custom_controller.IGameController_outer, &tmp_raw_controller ); + todo_wine ok( hr == S_OK, "FromGameController returned %#lx\n", hr ); + todo_wine ok( tmp_raw_controller == raw_controller, "got controller %p\n", tmp_raw_controller ); - IRawGameController_Release( tmp_raw_controller ); + if (hr == S_OK) IRawGameController_Release( tmp_raw_controller );
IGameController_Release( game_controller ); IRawGameController_Release( raw_controller ); SetEvent( stop_event ); - wait_for_events( 1, &custom_factory.removed_event, INFINITE ); + ret = wait_for_events( 1, &custom_factory.removed_event, 500 ); + todo_wine + ok( !ret, "wait_for_events returned %#lx\n", ret ); wait_for_events( 1, &controller_removed.event, INFINITE );
hr = IRawGameControllerStatics_remove_RawGameControllerAdded( statics, controller_added_token ); diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index 8689e381c87..6c075c3d67c 100644 --- a/dlls/windows.gaming.input/Makefile.in +++ b/dlls/windows.gaming.input/Makefile.in @@ -3,6 +3,7 @@ IMPORTS = combase uuid user32 dinput8 setupapi hid
C_SRCS = \ controller.c \ + event_handlers.c \ gamepad.c \ main.c \ manager.c \ diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index 51ede5a0e47..ab0183c7d4f 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -34,6 +34,8 @@ static CRITICAL_SECTION_DEBUG controller_cs_debug = static CRITICAL_SECTION controller_cs = { &controller_cs_debug, -1, 0, 0, 0, 0 };
static IVector_RawGameController *controllers; +static struct list controller_added_handlers = LIST_INIT( controller_added_handlers ); +static struct list controller_removed_handlers = LIST_INIT( controller_removed_handlers );
static HRESULT init_controllers(void) { @@ -385,34 +387,34 @@ static const struct IActivationFactoryVtbl factory_vtbl =
DEFINE_IINSPECTABLE( statics, IRawGameControllerStatics, struct controller_statics, IActivationFactory_iface )
-static HRESULT WINAPI statics_add_RawGameControllerAdded( IRawGameControllerStatics *iface, IEventHandler_RawGameController *value, +static HRESULT WINAPI statics_add_RawGameControllerAdded( IRawGameControllerStatics *iface, + IEventHandler_RawGameController *handler, EventRegistrationToken *token ) { - FIXME( "iface %p, value %p, token %p stub!\n", iface, value, token ); - if (!value) return E_INVALIDARG; - token->value = 0; - return S_OK; + TRACE( "iface %p, handler %p, token %p.\n", iface, handler, token ); + if (!handler) return E_INVALIDARG; + return event_handlers_append( &controller_added_handlers, (IEventHandler_IInspectable *)handler, token ); }
static HRESULT WINAPI statics_remove_RawGameControllerAdded( IRawGameControllerStatics *iface, EventRegistrationToken token ) { - FIXME( "iface %p, token %#I64x stub!\n", iface, token.value ); - return S_OK; + TRACE( "iface %p, token %#I64x.\n", iface, token.value ); + return event_handlers_remove( &controller_added_handlers, &token ); }
-static HRESULT WINAPI statics_add_RawGameControllerRemoved( IRawGameControllerStatics *iface, IEventHandler_RawGameController *value, +static HRESULT WINAPI statics_add_RawGameControllerRemoved( IRawGameControllerStatics *iface, + IEventHandler_RawGameController *handler, EventRegistrationToken *token ) { - FIXME( "iface %p, value %p, token %p stub!\n", iface, value, token ); - if (!value) return E_INVALIDARG; - token->value = 0; - return S_OK; + TRACE( "iface %p, handler %p, token %p.\n", iface, handler, token ); + if (!handler) return E_INVALIDARG; + return event_handlers_append( &controller_removed_handlers, (IEventHandler_IInspectable *)handler, token ); }
static HRESULT WINAPI statics_remove_RawGameControllerRemoved( IRawGameControllerStatics *iface, EventRegistrationToken token ) { - FIXME( "iface %p, token %#I64x stub!\n", iface, token.value ); - return S_OK; + TRACE( "iface %p, token %#I64x.\n", iface, token.value ); + return event_handlers_remove( &controller_removed_handlers, &token ); }
static HRESULT WINAPI statics_get_RawGameControllers( IRawGameControllerStatics *iface, IVectorView_RawGameController **value ) @@ -489,8 +491,17 @@ static HRESULT WINAPI controller_factory_CreateGameController( ICustomGameContro
static HRESULT WINAPI controller_factory_OnGameControllerAdded( ICustomGameControllerFactory *iface, IGameController *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + IRawGameController *controller; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IGameController_QueryInterface( value, &IID_IRawGameController, (void **)&controller ))) + return hr; + event_handlers_notify( &controller_added_handlers, (IInspectable *)controller ); + IRawGameController_Release( controller ); + + return S_OK; }
static HRESULT WINAPI controller_factory_OnGameControllerRemoved( ICustomGameControllerFactory *iface, IGameController *value ) @@ -518,7 +529,10 @@ static HRESULT WINAPI controller_factory_OnGameControllerRemoved( ICustomGameCon if (FAILED(hr)) WARN( "Failed to remove controller %p, hr %#lx!\n", controller, hr ); else if (found) + { TRACE( "Removed controller %p.\n", controller ); + event_handlers_notify( &controller_removed_handlers, (IInspectable *)controller ); + } IRawGameController_Release( controller );
return S_OK; diff --git a/dlls/windows.gaming.input/event_handlers.c b/dlls/windows.gaming.input/event_handlers.c new file mode 100644 index 00000000000..b9b061e3362 --- /dev/null +++ b/dlls/windows.gaming.input/event_handlers.c @@ -0,0 +1,89 @@ +/* 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" + +static CRITICAL_SECTION handlers_cs; +static CRITICAL_SECTION_DEBUG handlers_cs_debug = +{ + 0, 0, &handlers_cs, + { &handlers_cs_debug.ProcessLocksList, &handlers_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": handlers_cs") } +}; +static CRITICAL_SECTION handlers_cs = { &handlers_cs_debug, -1, 0, 0, 0, 0 }; +static EventRegistrationToken next_token = {.value = 1}; + +struct handler_entry +{ + struct list entry; + EventRegistrationToken token; + IEventHandler_IInspectable *handler; +}; + +HRESULT event_handlers_append( struct list *list, IEventHandler_IInspectable *handler, EventRegistrationToken *token ) +{ + struct handler_entry *entry; + + if (!(entry = calloc( 1, sizeof(*entry) ))) return E_OUTOFMEMORY; + IEventHandler_IInspectable_AddRef( (entry->handler = handler) ); + + EnterCriticalSection( &handlers_cs ); + + *token = entry->token = next_token; + next_token.value++; + list_add_tail( list, &entry->entry ); + + LeaveCriticalSection( &handlers_cs ); + + return S_OK; +} + +HRESULT event_handlers_remove( struct list *list, EventRegistrationToken *token ) +{ + struct handler_entry *entry; + BOOL found = FALSE; + + EnterCriticalSection( &handlers_cs ); + + LIST_FOR_EACH_ENTRY( entry, list, struct handler_entry, entry ) + if ((found = !memcmp( &entry->token, token, sizeof(*token) ))) break; + if (found) list_remove( &entry->entry ); + + LeaveCriticalSection( &handlers_cs ); + + if (found) + { + IEventHandler_IInspectable_Release( entry->handler ); + free( entry ); + } + + return S_OK; +} + +void event_handlers_notify( struct list *list, IInspectable *element ) +{ + struct handler_entry *entry; + + EnterCriticalSection( &handlers_cs ); + + LIST_FOR_EACH_ENTRY( entry, list, struct handler_entry, entry ) + IEventHandler_IInspectable_Invoke( entry->handler, NULL, element ); + + LeaveCriticalSection( &handlers_cs ); +} diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index 70c9f4cd792..fbb439d3fec 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -34,6 +34,8 @@ static CRITICAL_SECTION_DEBUG gamepad_cs_debug = static CRITICAL_SECTION gamepad_cs = { &gamepad_cs_debug, -1, 0, 0, 0, 0 };
static IVector_Gamepad *gamepads; +static struct list gamepad_added_handlers = LIST_INIT( gamepad_added_handlers ); +static struct list gamepad_removed_handlers = LIST_INIT( gamepad_removed_handlers );
static HRESULT init_gamepads(void) { @@ -345,34 +347,32 @@ static const struct IActivationFactoryVtbl factory_vtbl =
DEFINE_IINSPECTABLE( statics, IGamepadStatics, struct gamepad_statics, IActivationFactory_iface )
-static HRESULT WINAPI statics_add_GamepadAdded( IGamepadStatics *iface, IEventHandler_Gamepad *value, +static HRESULT WINAPI statics_add_GamepadAdded( IGamepadStatics *iface, IEventHandler_Gamepad *handler, EventRegistrationToken *token ) { - FIXME( "iface %p, value %p, token %p stub!\n", iface, value, token ); - if (!value) return E_INVALIDARG; - token->value = 0; - return S_OK; + TRACE( "iface %p, handler %p, token %p.\n", iface, handler, token ); + if (!handler) return E_INVALIDARG; + return event_handlers_append( &gamepad_added_handlers, (IEventHandler_IInspectable *)handler, token ); }
static HRESULT WINAPI statics_remove_GamepadAdded( IGamepadStatics *iface, EventRegistrationToken token ) { - FIXME( "iface %p, token %#I64x stub!\n", iface, token.value ); - return S_OK; + TRACE( "iface %p, token %#I64x.\n", iface, token.value ); + return event_handlers_remove( &gamepad_added_handlers, &token ); }
-static HRESULT WINAPI statics_add_GamepadRemoved( IGamepadStatics *iface, IEventHandler_Gamepad *value, +static HRESULT WINAPI statics_add_GamepadRemoved( IGamepadStatics *iface, IEventHandler_Gamepad *handler, EventRegistrationToken *token ) { - FIXME( "iface %p, value %p, token %p stub!\n", iface, value, token ); - if (!value) return E_INVALIDARG; - token->value = 0; - return S_OK; + TRACE( "iface %p, handler %p, token %p.\n", iface, handler, token ); + if (!handler) return E_INVALIDARG; + return event_handlers_append( &gamepad_removed_handlers, (IEventHandler_IInspectable *)handler, token ); }
static HRESULT WINAPI statics_remove_GamepadRemoved( IGamepadStatics *iface, EventRegistrationToken token ) { - FIXME( "iface %p, token %#I64x stub!\n", iface, token.value ); - return S_OK; + TRACE( "iface %p, token %#I64x.\n", iface, token.value ); + return event_handlers_remove( &gamepad_removed_handlers, &token ); }
static HRESULT WINAPI statics_get_Gamepads( IGamepadStatics *iface, IVectorView_Gamepad **value ) @@ -461,8 +461,17 @@ static HRESULT WINAPI controller_factory_CreateGameController( ICustomGameContro
static HRESULT WINAPI controller_factory_OnGameControllerAdded( ICustomGameControllerFactory *iface, IGameController *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + IGamepad *gamepad; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IGameController_QueryInterface( value, &IID_IGamepad, (void **)&gamepad ))) + return hr; + event_handlers_notify( &gamepad_added_handlers, (IInspectable *)gamepad ); + IGamepad_Release( gamepad ); + + return S_OK; }
static HRESULT WINAPI controller_factory_OnGameControllerRemoved( ICustomGameControllerFactory *iface, IGameController *value ) @@ -490,7 +499,10 @@ static HRESULT WINAPI controller_factory_OnGameControllerRemoved( ICustomGameCon if (FAILED(hr)) WARN( "Failed to remove gamepad %p, hr %#lx!\n", gamepad, hr ); else if (found) + { TRACE( "Removed gamepad %p.\n", gamepad ); + event_handlers_notify( &gamepad_removed_handlers, (IInspectable *)gamepad ); + } IGamepad_Release( gamepad );
return S_OK; diff --git a/dlls/windows.gaming.input/manager.c b/dlls/windows.gaming.input/manager.c index 6a8963a1fc4..393b5a850c8 100644 --- a/dlls/windows.gaming.input/manager.c +++ b/dlls/windows.gaming.input/manager.c @@ -21,7 +21,6 @@ #include "provider.h"
#include "wine/debug.h" -#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(input);
diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 5d5f48d12e7..bb2fb68b8a8 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -36,6 +36,8 @@ #define WIDL_using_Windows_Gaming_Input_Custom #include "windows.gaming.input.custom.h"
+#include "wine/list.h" + extern HINSTANCE windows_gaming_input; extern ICustomGameControllerFactory *controller_factory; extern ICustomGameControllerFactory *gamepad_factory; @@ -56,6 +58,10 @@ extern void provider_remove( const WCHAR *device_path ); extern void manager_on_provider_created( IGameControllerProvider *provider ); extern void manager_on_provider_removed( IGameControllerProvider *provider );
+extern HRESULT event_handlers_append( struct list *list, IEventHandler_IInspectable *handler, EventRegistrationToken *token ); +extern HRESULT event_handlers_remove( struct list *list, EventRegistrationToken *token ); +extern void event_handlers_notify( struct list *list, IInspectable *element ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index b4817b42c46..703516ccfe2 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -24,7 +24,6 @@ #include "provider.h"
#include "wine/debug.h" -#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(input);
diff --git a/include/windows.foundation.idl b/include/windows.foundation.idl index e0fd35e45eb..27bec63b0ef 100644 --- a/include/windows.foundation.idl +++ b/include/windows.foundation.idl @@ -167,6 +167,7 @@ namespace Windows { interface Windows.Foundation.Collections.IVector<HSTRING>; interface Windows.Foundation.Collections.IVector<IInspectable *>; interface Windows.Foundation.Collections.IMapView<HSTRING, Windows.Foundation.Collections.IVectorView<HSTRING>*>; + interface Windows.Foundation.EventHandler<IInspectable *>; interface Windows.Foundation.AsyncOperationCompletedHandler<boolean>; interface Windows.Foundation.IAsyncOperation<boolean>; interface Windows.Foundation.IReference<INT32>;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/windows.gaming.input/controller.c | 27 +++++++---- dlls/windows.gaming.input/provider.c | 64 ++++++++++++++++++++++++-- dlls/windows.gaming.input/provider.idl | 3 ++ 3 files changed, 80 insertions(+), 14 deletions(-)
diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index ab0183c7d4f..83e4e167ec9 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -65,6 +65,7 @@ struct controller LONG ref;
IGameControllerProvider *provider; + IWineGameControllerProvider *wine_provider; };
static inline struct controller *impl_from_IGameControllerImpl( IGameControllerImpl *iface ) @@ -120,6 +121,8 @@ static ULONG WINAPI controller_Release( IGameControllerImpl *iface )
if (!ref) { + if (impl->wine_provider) + IWineGameControllerProvider_Release( impl->wine_provider ); IGameControllerProvider_Release( impl->provider ); free( impl ); } @@ -157,6 +160,10 @@ static HRESULT WINAPI controller_Initialize( IGameControllerImpl *iface, IGameCo impl->IGameController_outer = outer; IGameControllerProvider_AddRef( (impl->provider = provider) );
+ hr = IGameControllerProvider_QueryInterface( provider, &IID_IWineGameControllerProvider, + (void **)&impl->wine_provider ); + if (FAILED(hr)) return hr; + EnterCriticalSection( &controller_cs ); if (SUCCEEDED(hr = init_controllers())) hr = IVector_RawGameController_Append( controllers, &impl->IRawGameController_iface ); @@ -210,14 +217,14 @@ DEFINE_IINSPECTABLE_OUTER( raw_controller, IRawGameController, struct controller
static HRESULT WINAPI raw_controller_get_AxisCount( IRawGameController *iface, INT32 *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct controller *impl = impl_from_IRawGameController( iface ); + return IWineGameControllerProvider_get_AxisCount( impl->wine_provider, value ); }
static HRESULT WINAPI raw_controller_get_ButtonCount( IRawGameController *iface, INT32 *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct controller *impl = impl_from_IRawGameController( iface ); + return IWineGameControllerProvider_get_ButtonCount( impl->wine_provider, value ); }
static HRESULT WINAPI raw_controller_get_ForceFeedbackMotors( IRawGameController *iface, IVectorView_ForceFeedbackMotor **value ) @@ -228,20 +235,20 @@ static HRESULT WINAPI raw_controller_get_ForceFeedbackMotors( IRawGameController
static HRESULT WINAPI raw_controller_get_HardwareProductId( IRawGameController *iface, UINT16 *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct controller *impl = impl_from_IRawGameController( iface ); + return IGameControllerProvider_get_HardwareProductId( impl->provider, value ); }
static HRESULT WINAPI raw_controller_get_HardwareVendorId( IRawGameController *iface, UINT16 *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct controller *impl = impl_from_IRawGameController( iface ); + return IGameControllerProvider_get_HardwareVendorId( impl->provider, value ); }
static HRESULT WINAPI raw_controller_get_SwitchCount( IRawGameController *iface, INT32 *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct controller *impl = impl_from_IRawGameController( iface ); + return IWineGameControllerProvider_get_SwitchCount( impl->wine_provider, value ); }
static HRESULT WINAPI raw_controller_GetButtonLabel( IRawGameController *iface, INT32 index, diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index 703516ccfe2..7883ec6af68 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -143,6 +143,45 @@ static HRESULT WINAPI wine_provider_get_Type( IWineGameControllerProvider *iface return S_OK; }
+static HRESULT WINAPI wine_provider_get_AxisCount( IWineGameControllerProvider *iface, INT32 *value ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (SUCCEEDED(hr = IDirectInputDevice8_GetCapabilities( impl->dinput_device, &caps ))) + *value = caps.dwAxes; + return hr; +} + +static HRESULT WINAPI wine_provider_get_ButtonCount( IWineGameControllerProvider *iface, INT32 *value ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (SUCCEEDED(hr = IDirectInputDevice8_GetCapabilities( impl->dinput_device, &caps ))) + *value = caps.dwButtons; + return hr; +} + +static HRESULT WINAPI wine_provider_get_SwitchCount( IWineGameControllerProvider *iface, INT32 *value ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (SUCCEEDED(hr = IDirectInputDevice8_GetCapabilities( impl->dinput_device, &caps ))) + *value = caps.dwPOVs; + return hr; +} + static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = { wine_provider_QueryInterface, @@ -154,6 +193,9 @@ static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = wine_provider_GetTrustLevel, /* IWineGameControllerProvider methods */ wine_provider_get_Type, + wine_provider_get_AxisCount, + wine_provider_get_ButtonCount, + wine_provider_get_SwitchCount, };
DEFINE_IINSPECTABLE( game_provider, IGameControllerProvider, struct provider, IWineGameControllerProvider_iface ) @@ -166,14 +208,28 @@ static HRESULT WINAPI game_provider_get_FirmwareVersionInfo( IGameControllerProv
static HRESULT WINAPI game_provider_get_HardwareProductId( IGameControllerProvider *iface, UINT16 *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + DIPROPDWORD vid_pid = {.diph = {.dwHeaderSize = sizeof(DIPROPHEADER), .dwSize = sizeof(DIPROPDWORD)}}; + struct provider *impl = impl_from_IGameControllerProvider( iface ); + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (SUCCEEDED(hr = IDirectInputDevice8_GetProperty( impl->dinput_device, DIPROP_VIDPID, &vid_pid.diph ))) + *value = HIWORD(vid_pid.dwData); + return hr; }
static HRESULT WINAPI game_provider_get_HardwareVendorId( IGameControllerProvider *iface, UINT16 *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + DIPROPDWORD vid_pid = {.diph = {.dwHeaderSize = sizeof(DIPROPHEADER), .dwSize = sizeof(DIPROPDWORD)}}; + struct provider *impl = impl_from_IGameControllerProvider( iface ); + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (SUCCEEDED(hr = IDirectInputDevice8_GetProperty( impl->dinput_device, DIPROP_VIDPID, &vid_pid.diph ))) + *value = LOWORD(vid_pid.dwData); + return hr; }
static HRESULT WINAPI game_provider_get_HardwareVersionInfo( IGameControllerProvider *iface, GameControllerVersionInfo *value ) diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index 6af700bb12d..19d5643f767 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -59,6 +59,9 @@ namespace Windows.Gaming.Input.Custom { requires Windows.Gaming.Input.Custom.IGameControllerProvider { [propget] HRESULT Type([out, retval] WineGameControllerType *value); + [propget] HRESULT AxisCount([out, retval] INT32 *value); + [propget] HRESULT ButtonCount([out, retval] INT32 *value); + [propget] HRESULT SwitchCount([out, retval] INT32 *value); }
[
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/windows.gaming.input/controller.c | 16 +++++- dlls/windows.gaming.input/provider.c | 68 ++++++++++++++++++++++++++ dlls/windows.gaming.input/provider.idl | 11 +++++ 3 files changed, 93 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index 83e4e167ec9..3e00da917e7 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -262,9 +262,21 @@ static HRESULT WINAPI raw_controller_GetCurrentReading( IRawGameController *ifac UINT32 switches_size, enum GameControllerSwitchPosition *switches, UINT32 axes_size, DOUBLE *axes, UINT64 *timestamp ) { - FIXME( "iface %p, buttons_size %u, buttons %p, switches_size %u, switches %p, axes_size %u, axes %p, timestamp %p stub!\n", + struct controller *impl = impl_from_IRawGameController( iface ); + WineGameControllerState state; + HRESULT hr; + + TRACE( "iface %p, buttons_size %u, buttons %p, switches_size %u, switches %p, axes_size %u, axes %p, timestamp %p.\n", iface, buttons_size, buttons, switches_size, switches, axes_size, axes, timestamp ); - return E_NOTIMPL; + + if (FAILED(hr = IWineGameControllerProvider_get_State( impl->wine_provider, &state ))) return hr; + + memcpy( axes, state.axes, axes_size * sizeof(*axes) ); + memcpy( buttons, state.buttons, buttons_size * sizeof(*buttons) ); + memcpy( switches, state.switches, switches_size * sizeof(*switches) ); + *timestamp = state.timestamp; + + return hr; }
static HRESULT WINAPI raw_controller_GetSwitchKind( IRawGameController *iface, INT32 index, enum GameControllerSwitchKind *value ) diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index 7883ec6af68..0877938f774 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -182,6 +182,69 @@ static HRESULT WINAPI wine_provider_get_SwitchCount( IWineGameControllerProvider return hr; }
+static HRESULT WINAPI wine_provider_get_State( IWineGameControllerProvider *iface, struct WineGameControllerState *out ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + DIJOYSTATE2 state = {0}; + UINT32 i = 0; + HRESULT hr; + + TRACE( "iface %p, out %p.\n", iface, out ); + + if (FAILED(hr = IDirectInputDevice8_GetDeviceState( impl->dinput_device, sizeof(state), &state ))) + { + WARN( "Failed to read device state, hr %#lx\n", hr ); + return hr; + } + + i = ARRAY_SIZE(state.rgbButtons); + while (i--) out->buttons[i] = (state.rgbButtons[i] != 0); + + i = ARRAY_SIZE(state.rgdwPOV); + while (i--) + { + if (state.rgdwPOV[i] == ~0) out->switches[i] = GameControllerSwitchPosition_Center; + else out->switches[i] = state.rgdwPOV[i] * 8 / 36000 + 1; + } + + i = 0; + out->axes[i++] = state.lX / 65535.; + out->axes[i++] = state.lY / 65535.; + out->axes[i++] = state.lZ / 65535.; + out->axes[i++] = state.lRx / 65535.; + out->axes[i++] = state.lRy / 65535.; + out->axes[i++] = state.lRz / 65535.; + out->axes[i++] = state.rglSlider[0] / 65535.; + out->axes[i++] = state.rglSlider[1] / 65535.; + out->axes[i++] = state.lVX / 65535.; + out->axes[i++] = state.lVY / 65535.; + out->axes[i++] = state.lVZ / 65535.; + out->axes[i++] = state.lVRx / 65535.; + out->axes[i++] = state.lVRy / 65535.; + out->axes[i++] = state.lVRz / 65535.; + out->axes[i++] = state.rglVSlider[0] / 65535.; + out->axes[i++] = state.rglVSlider[1] / 65535.; + out->axes[i++] = state.lAX / 65535.; + out->axes[i++] = state.lAY / 65535.; + out->axes[i++] = state.lAZ / 65535.; + out->axes[i++] = state.lARx / 65535.; + out->axes[i++] = state.lARy / 65535.; + out->axes[i++] = state.lARz / 65535.; + out->axes[i++] = state.rglASlider[0] / 65535.; + out->axes[i++] = state.rglASlider[1] / 65535.; + out->axes[i++] = state.lFX / 65535.; + out->axes[i++] = state.lFY / 65535.; + out->axes[i++] = state.lFZ / 65535.; + out->axes[i++] = state.lFRx / 65535.; + out->axes[i++] = state.lFRy / 65535.; + out->axes[i++] = state.lFRz / 65535.; + out->axes[i++] = state.rglFSlider[0] / 65535.; + out->axes[i++] = state.rglFSlider[1] / 65535.; + out->timestamp = GetTickCount64(); + + return S_OK; +} + static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = { wine_provider_QueryInterface, @@ -196,6 +259,7 @@ static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = wine_provider_get_AxisCount, wine_provider_get_ButtonCount, wine_provider_get_SwitchCount, + wine_provider_get_State, };
DEFINE_IINSPECTABLE( game_provider, IGameControllerProvider, struct provider, IWineGameControllerProvider_iface ) @@ -284,6 +348,10 @@ void provider_create( const WCHAR *device_path ) IDirectInput8_Release( dinput ); if (FAILED(hr)) return;
+ if (FAILED(hr = IDirectInputDevice8_SetCooperativeLevel( dinput_device, 0, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE ))) goto done; + if (FAILED(hr = IDirectInputDevice8_SetDataFormat( dinput_device, &c_dfDIJoystick2 ))) goto done; + if (FAILED(hr = IDirectInputDevice8_Acquire( dinput_device ))) goto done; + if (!(impl = calloc( 1, sizeof(*impl) ))) goto done; impl->IWineGameControllerProvider_iface.lpVtbl = &wine_provider_vtbl; impl->IGameControllerProvider_iface.lpVtbl = &game_provider_vtbl; diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index 19d5643f767..25191078721 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -32,6 +32,7 @@ import "windows.gaming.input.custom.idl";
namespace Windows.Gaming.Input.Custom { typedef enum WineGameControllerType WineGameControllerType; + typedef struct WineGameControllerState WineGameControllerState; interface IWineGameControllerProvider; runtimeclass WineGameControllerProvider;
@@ -41,6 +42,14 @@ namespace Windows.Gaming.Input.Custom { Gamepad = 1, };
+ struct WineGameControllerState + { + UINT64 timestamp; + DOUBLE axes[32]; + BOOLEAN buttons[128]; + Windows.Gaming.Input.GameControllerSwitchPosition switches[4]; + }; + [ uuid(06e58977-7684-4dc5-bad1-cda52a4aa06d) ] @@ -62,6 +71,8 @@ namespace Windows.Gaming.Input.Custom { [propget] HRESULT AxisCount([out, retval] INT32 *value); [propget] HRESULT ButtonCount([out, retval] INT32 *value); [propget] HRESULT SwitchCount([out, retval] INT32 *value); + + [propget] HRESULT State([out, retval] WineGameControllerState *state); }
[
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/windows.gaming.input/gamepad.c | 72 ++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index fbb439d3fec..b815d5c0a13 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -65,6 +65,7 @@ struct gamepad LONG ref;
IGameControllerProvider *provider; + IWineGameControllerProvider *wine_provider; };
static inline struct gamepad *impl_from_IGameControllerImpl( IGameControllerImpl *iface ) @@ -120,6 +121,8 @@ static ULONG WINAPI controller_Release( IGameControllerImpl *iface )
if (!ref) { + if (impl->wine_provider) + IWineGameControllerProvider_Release( impl->wine_provider ); IGameControllerProvider_Release( impl->provider ); free( impl ); } @@ -157,6 +160,10 @@ static HRESULT WINAPI controller_Initialize( IGameControllerImpl *iface, IGameCo impl->IGameController_outer = outer; IGameControllerProvider_AddRef( (impl->provider = provider) );
+ hr = IGameControllerProvider_QueryInterface( provider, &IID_IWineGameControllerProvider, + (void **)&impl->wine_provider ); + if (FAILED(hr)) return hr; + EnterCriticalSection( &gamepad_cs ); if (SUCCEEDED(hr = init_gamepads())) hr = IVector_Gamepad_Append( gamepads, &impl->IGamepad_iface ); @@ -222,8 +229,69 @@ static HRESULT WINAPI gamepad_put_Vibration( IGamepad *iface, struct GamepadVibr
static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct GamepadReading *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL;} + struct gamepad *impl = impl_from_IGamepad( iface ); + struct WineGameControllerState state; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IWineGameControllerProvider_get_State( impl->wine_provider, &state ))) return hr; + + value->Buttons = 0; + if (state.buttons[0]) value->Buttons |= GamepadButtons_A; + if (state.buttons[1]) value->Buttons |= GamepadButtons_B; + if (state.buttons[2]) value->Buttons |= GamepadButtons_X; + if (state.buttons[3]) value->Buttons |= GamepadButtons_Y; + if (state.buttons[4]) value->Buttons |= GamepadButtons_LeftShoulder; + if (state.buttons[5]) value->Buttons |= GamepadButtons_RightShoulder; + if (state.buttons[6]) value->Buttons |= GamepadButtons_Menu; + if (state.buttons[7]) value->Buttons |= GamepadButtons_View; + if (state.buttons[8]) value->Buttons |= GamepadButtons_LeftThumbstick; + if (state.buttons[9]) value->Buttons |= GamepadButtons_RightThumbstick; + + switch (state.switches[0]) + { + case GameControllerSwitchPosition_Up: + case GameControllerSwitchPosition_UpRight: + case GameControllerSwitchPosition_UpLeft: + value->Buttons |= GamepadButtons_DPadUp; + break; + case GameControllerSwitchPosition_Down: + case GameControllerSwitchPosition_DownRight: + case GameControllerSwitchPosition_DownLeft: + value->Buttons |= GamepadButtons_DPadDown; + break; + default: + break; + } + + switch (state.switches[0]) + { + case GameControllerSwitchPosition_Right: + case GameControllerSwitchPosition_UpRight: + case GameControllerSwitchPosition_DownRight: + value->Buttons |= GamepadButtons_DPadRight; + break; + case GameControllerSwitchPosition_Left: + case GameControllerSwitchPosition_UpLeft: + case GameControllerSwitchPosition_DownLeft: + value->Buttons |= GamepadButtons_DPadLeft; + break; + default: + break; + } + + value->LeftThumbstickX = 2. * state.axes[0] - 1.; + value->LeftThumbstickY = 1. - 2. * state.axes[1]; + value->LeftTrigger = state.axes[2]; + value->RightThumbstickX = 2. * state.axes[3] - 1.; + value->RightThumbstickY = 1. - 2. * state.axes[4]; + value->RightTrigger = state.axes[5]; + + value->Timestamp = state.timestamp; + + return hr; +}
static const struct IGamepadVtbl gamepad_vtbl = {