[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