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>;