windows.gaming.input: Implement Controller(Added|Removed) event support.

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>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Rémi Bernon 2022-03-10 13:05:49 +01:00 committed by Alexandre Julliard
parent e609c640a2
commit 1e9b83a440
9 changed files with 171 additions and 39 deletions

View file

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

View file

@ -3,6 +3,7 @@ IMPORTS = combase uuid user32 dinput8 setupapi hid
C_SRCS = \
controller.c \
event_handlers.c \
gamepad.c \
main.c \
manager.c \

View file

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

View file

@ -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 );
}

View file

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

View file

@ -21,7 +21,6 @@
#include "provider.h"
#include "wine/debug.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(input);

View file

@ -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 ) \
{ \

View file

@ -24,7 +24,6 @@
#include "provider.h"
#include "wine/debug.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(input);

View file

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