[PATCH 3/6] windows.gaming.input: Implement Controller(Added|Removed) event support.

Rémi Bernon rbernon at codeweavers.com
Thu Mar 10 03:38:41 CST 2022


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




More information about the wine-devel mailing list