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:
parent
e609c640a2
commit
1e9b83a440
|
@ -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 );
|
||||
|
|
|
@ -3,6 +3,7 @@ IMPORTS = combase uuid user32 dinput8 setupapi hid
|
|||
|
||||
C_SRCS = \
|
||||
controller.c \
|
||||
event_handlers.c \
|
||||
gamepad.c \
|
||||
main.c \
|
||||
manager.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;
|
||||
|
|
|
@ -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 );
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "provider.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
#include "wine/list.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(input);
|
||||
|
||||
|
|
|
@ -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 ) \
|
||||
{ \
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "provider.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
#include "wine/list.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(input);
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
|
Loading…
Reference in New Issue