[PATCH v2 7/7] dinput/tests: Add tests for ICustomGameControllerFactory_CreateGameController.

Rémi Bernon rbernon at codeweavers.com
Wed Mar 2 05:12:58 CST 2022


This shows that the runtime uses COM aggregation to provide the
IGameController and IGameControllerBatteryInfo implementation, while
expecting the custom controller to implement several other interfaces
depending on the type of provider used as a backend.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/dinput/tests/hotplug.c | 389 +++++++++++++++++++++++++++++++++++-
 1 file changed, 384 insertions(+), 5 deletions(-)

diff --git a/dlls/dinput/tests/hotplug.c b/dlls/dinput/tests/hotplug.c
index e8f5157d73f..308ae283300 100644
--- a/dlls/dinput/tests/hotplug.c
+++ b/dlls/dinput/tests/hotplug.c
@@ -41,6 +41,7 @@
 
 #include "initguid.h"
 #include "roapi.h"
+#include "weakreference.h"
 #define WIDL_using_Windows_Foundation
 #define WIDL_using_Windows_Foundation_Collections
 #include "windows.foundation.h"
@@ -95,6 +96,19 @@ static DWORD wait_for_events( DWORD count, HANDLE *events, DWORD timeout )
     return ret;
 }
 
+#define check_interface( a, b, c ) check_interface_( __LINE__, a, b, c )
+static void check_interface_( unsigned int line, void *iface_ptr, REFIID iid, BOOL supported )
+{
+    IUnknown *iface = iface_ptr;
+    HRESULT hr, expected;
+    IUnknown *unk;
+
+    expected = supported ? S_OK : E_NOINTERFACE;
+    hr = IUnknown_QueryInterface( iface, iid, (void **)&unk );
+    ok_ (__FILE__, line)( hr == expected, "got hr %#lx, expected %#lx.\n", hr, expected );
+    if (SUCCEEDED(hr)) IUnknown_Release( unk );
+}
+
 static BOOL test_input_lost( DWORD version )
 {
 #include "psh_hid_macros.h"
@@ -528,10 +542,266 @@ static const IEventHandler_RawGameControllerVtbl controller_handler_vtbl =
 static struct controller_handler controller_removed = {{&controller_handler_vtbl}};
 static struct controller_handler controller_added = {{&controller_handler_vtbl}};
 
+DEFINE_GUID( IID_IGameControllerImpl, 0x06e58977, 0x7684, 0x4dc5, 0xba, 0xd1, 0xcd, 0xa5, 0x2a, 0x4a, 0xa0, 0x6d );
+typedef IInspectable IGameControllerImpl;
+
+#define DEFINE_IINSPECTABLE_OUTER( pfx, iface_type, impl_type, outer_iface )                       \
+    static inline impl_type *impl_from_##iface_type( iface_type *iface )                           \
+    {                                                                                              \
+        return CONTAINING_RECORD( iface, impl_type, iface_type##_iface );                          \
+    }                                                                                              \
+    static HRESULT WINAPI pfx##_QueryInterface( iface_type *iface, REFIID iid, void **out )        \
+    {                                                                                              \
+        impl_type *impl = impl_from_##iface_type( iface );                                         \
+        return IInspectable_QueryInterface( (IInspectable *)impl->outer_iface, iid, out );         \
+    }                                                                                              \
+    static ULONG WINAPI pfx##_AddRef( iface_type *iface )                                          \
+    {                                                                                              \
+        impl_type *impl = impl_from_##iface_type( iface );                                         \
+        return IInspectable_AddRef( (IInspectable *)impl->outer_iface );                           \
+    }                                                                                              \
+    static ULONG WINAPI pfx##_Release( iface_type *iface )                                         \
+    {                                                                                              \
+        impl_type *impl = impl_from_##iface_type( iface );                                         \
+        return IInspectable_Release( (IInspectable *)impl->outer_iface );                          \
+    }                                                                                              \
+    static HRESULT WINAPI pfx##_GetIids( iface_type *iface, ULONG *iid_count, IID **iids )         \
+    {                                                                                              \
+        impl_type *impl = impl_from_##iface_type( iface );                                         \
+        return IInspectable_GetIids( (IInspectable *)impl->outer_iface, iid_count, iids );         \
+    }                                                                                              \
+    static HRESULT WINAPI pfx##_GetRuntimeClassName( iface_type *iface, HSTRING *class_name )      \
+    {                                                                                              \
+        impl_type *impl = impl_from_##iface_type( iface );                                         \
+        return IInspectable_GetRuntimeClassName( (IInspectable *)impl->outer_iface, class_name );  \
+    }                                                                                              \
+    static HRESULT WINAPI pfx##_GetTrustLevel( iface_type *iface, TrustLevel *trust_level )        \
+    {                                                                                              \
+        impl_type *impl = impl_from_##iface_type( iface );                                         \
+        return IInspectable_GetTrustLevel( (IInspectable *)impl->outer_iface, trust_level );       \
+    }
+
+struct custom_controller
+{
+    IInspectable IInspectable_inner;
+    IGameControllerImpl IGameControllerImpl_iface;
+    IGameControllerInputSink IGameControllerInputSink_iface;
+    IHidGameControllerInputSink IHidGameControllerInputSink_iface;
+    IGameController *IGameController_outer;
+    LONG ref;
+
+    BOOL initialize_called;
+    BOOL on_input_resumed_called;
+    BOOL on_input_suspended_called;
+    BOOL raw_game_controller_queried;
+};
+
+static inline struct custom_controller *impl_from_IInspectable( IInspectable *iface )
+{
+    return CONTAINING_RECORD( iface, struct custom_controller, IInspectable_inner );
+}
+
+static HRESULT WINAPI inspectable_QueryInterface( IInspectable *iface, REFIID iid, void **out )
+{
+    struct custom_controller *impl = impl_from_IInspectable( iface );
+
+    if (IsEqualGUID( iid, &IID_IUnknown ) ||
+        IsEqualGUID( iid, &IID_IInspectable ))
+    {
+        IInspectable_AddRef( (*out = &impl->IInspectable_inner) );
+        return S_OK;
+    }
+
+    if (IsEqualGUID( iid, &IID_IGameControllerImpl ))
+    {
+        IInspectable_AddRef( (*out = &impl->IGameControllerImpl_iface) );
+        return S_OK;
+    }
+
+    if (IsEqualGUID( iid, &IID_IGameControllerInputSink ))
+    {
+        IInspectable_AddRef( (*out = &impl->IGameControllerInputSink_iface) );
+        return S_OK;
+    }
+
+    if (IsEqualGUID( iid, &IID_IHidGameControllerInputSink ))
+    {
+        IInspectable_AddRef( (*out = &impl->IHidGameControllerInputSink_iface) );
+        return S_OK;
+    }
+
+    if (IsEqualGUID( iid, &IID_IRawGameController ))
+    {
+        impl->raw_game_controller_queried = TRUE;
+        *out = NULL;
+        return E_NOINTERFACE;
+    }
+
+    ok( 0, "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI inspectable_AddRef( IInspectable *iface )
+{
+    struct custom_controller *impl = impl_from_IInspectable( iface );
+    return InterlockedIncrement( &impl->ref );
+}
+
+static ULONG WINAPI inspectable_Release( IInspectable *iface )
+{
+    struct custom_controller *impl = impl_from_IInspectable( iface );
+    return InterlockedDecrement( &impl->ref );
+}
+
+static HRESULT WINAPI inspectable_GetIids( IInspectable *iface, ULONG *iid_count, IID **iids )
+{
+    ok( 0, "unexpected call\n" );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI inspectable_GetRuntimeClassName( IInspectable *iface, HSTRING *class_name )
+{
+    ok( 0, "unexpected call\n" );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI inspectable_GetTrustLevel( IInspectable *iface, TrustLevel *trust_level )
+{
+    ok( 0, "unexpected call\n" );
+    return E_NOTIMPL;
+}
+
+static const IInspectableVtbl inspectable_vtbl =
+{
+    inspectable_QueryInterface,
+    inspectable_AddRef,
+    inspectable_Release,
+    /* IInspectable methods */
+    inspectable_GetIids,
+    inspectable_GetRuntimeClassName,
+    inspectable_GetTrustLevel,
+};
+
+DEFINE_IINSPECTABLE_OUTER( controller, IGameControllerImpl, struct custom_controller, IGameController_outer )
+
+static HRESULT WINAPI controller_Initialize( IInspectable *iface, IGameController *outer, IGameControllerProvider *provider )
+{
+    struct custom_controller *impl = impl_from_IInspectable( iface );
+
+    ok( !impl->initialize_called, "Initialize already called\n" );
+    impl->initialize_called = TRUE;
+
+    check_interface( outer, &IID_IUnknown, TRUE );
+    check_interface( outer, &IID_IInspectable, TRUE );
+    check_interface( outer, &IID_IAgileObject, TRUE );
+    check_interface( outer, &IID_IWeakReferenceSource, TRUE );
+    check_interface( outer, &IID_IGameController, TRUE );
+    impl->IGameController_outer = outer;
+
+    check_interface( provider, &IID_IUnknown, TRUE );
+    check_interface( provider, &IID_IInspectable, TRUE );
+    check_interface( provider, &IID_IAgileObject, TRUE );
+    check_interface( provider, &IID_IWeakReferenceSource, TRUE );
+    check_interface( provider, &IID_IGameControllerProvider, TRUE );
+    check_interface( provider, &IID_IHidGameControllerProvider, TRUE );
+
+    return S_OK;
+}
+
+static const void *controller_vtbl[] =
+{
+    controller_QueryInterface,
+    controller_AddRef,
+    controller_Release,
+    /* IInspectable methods */
+    controller_GetIids,
+    controller_GetRuntimeClassName,
+    controller_GetTrustLevel,
+    /* IGameControllerImpl methods */
+    controller_Initialize,
+};
+
+DEFINE_IINSPECTABLE_OUTER( input_sink, IGameControllerInputSink, struct custom_controller, IGameController_outer )
+
+static HRESULT WINAPI input_sink_OnInputResumed( IGameControllerInputSink *iface, UINT64 timestamp )
+{
+    struct custom_controller *impl = impl_from_IGameControllerInputSink( iface );
+
+    trace( "iface %p, timestamp %I64u\n", iface, timestamp );
+
+    ok( !controller_added.invoked, "controller added handler invoked\n" );
+    ok( !impl->on_input_resumed_called, "OnInputResumed already called\n" );
+    impl->on_input_resumed_called = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI input_sink_OnInputSuspended( IGameControllerInputSink *iface, UINT64 timestamp )
+{
+    struct custom_controller *impl = impl_from_IGameControllerInputSink( iface );
+
+    trace( "iface %p, timestamp %I64u\n", iface, timestamp );
+
+    ok( !controller_removed.invoked, "controller removed handler invoked\n" );
+    ok( !impl->on_input_suspended_called, "OnInputSuspended already called\n" );
+    impl->on_input_suspended_called = TRUE;
+
+    return S_OK;
+}
+
+static const struct IGameControllerInputSinkVtbl input_sink_vtbl =
+{
+    input_sink_QueryInterface,
+    input_sink_AddRef,
+    input_sink_Release,
+    /* IInspectable methods */
+    input_sink_GetIids,
+    input_sink_GetRuntimeClassName,
+    input_sink_GetTrustLevel,
+    /* IGameControllerInputSink methods */
+    input_sink_OnInputResumed,
+    input_sink_OnInputSuspended,
+};
+
+DEFINE_IINSPECTABLE_OUTER( hid_sink, IHidGameControllerInputSink, struct custom_controller, IGameController_outer )
+
+static HRESULT WINAPI hid_sink_OnInputReportReceived( IHidGameControllerInputSink *iface, UINT64 timestamp, BYTE id, UINT32 report_len, BYTE *report_buf )
+{
+    ok( 0, "unexpected call\n" );
+    return S_OK;
+}
+
+static const struct IHidGameControllerInputSinkVtbl hid_sink_vtbl =
+{
+    hid_sink_QueryInterface,
+    hid_sink_AddRef,
+    hid_sink_Release,
+    /* IInspectable methods */
+    hid_sink_GetIids,
+    hid_sink_GetRuntimeClassName,
+    hid_sink_GetTrustLevel,
+    /* IGameControllerInputSink methods */
+    hid_sink_OnInputReportReceived,
+};
+
+static struct custom_controller custom_controller =
+{
+    {&inspectable_vtbl},
+    {(IInspectableVtbl *)controller_vtbl},
+    {&input_sink_vtbl},
+    {&hid_sink_vtbl},
+};
+
 struct custom_factory
 {
     ICustomGameControllerFactory ICustomGameControllerFactory_iface;
     BOOL create_controller_called;
+    BOOL create_controller;
+    BOOL on_game_controller_added_called;
+    HANDLE added_event;
+    BOOL on_game_controller_removed_called;
+    HANDLE removed_event;
 };
 
 static inline struct custom_factory *impl_from_ICustomGameControllerFactory( ICustomGameControllerFactory *iface )
@@ -590,23 +860,60 @@ static HRESULT WINAPI custom_factory_CreateGameController( ICustomGameController
 {
     struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
 
+    trace( "iface %p, provider %p, value %p\n", iface, provider, value );
+
     ok( !controller_added.invoked, "controller added handler invoked\n" );
     ok( !impl->create_controller_called, "unexpected call\n" );
     impl->create_controller_called = TRUE;
+    if (!impl->create_controller) return E_NOTIMPL;
 
-    return E_NOTIMPL;
+    check_interface( provider, &IID_IUnknown, TRUE );
+    check_interface( provider, &IID_IInspectable, TRUE );
+    check_interface( provider, &IID_IAgileObject, TRUE );
+    check_interface( provider, &IID_IGameControllerProvider, TRUE );
+    check_interface( provider, &IID_IHidGameControllerProvider, TRUE );
+    check_interface( provider, &IID_IXusbGameControllerProvider, FALSE );
+    check_interface( provider, &IID_IGameControllerInputSink, FALSE );
+    custom_controller.ref = 1;
+
+    *value = &custom_controller.IInspectable_inner;
+    return S_OK;
 }
 
 static HRESULT WINAPI custom_factory_OnGameControllerAdded( ICustomGameControllerFactory *iface, IGameController *value )
 {
-    ok( 0, "unexpected call\n" );
-    return E_NOTIMPL;
+    struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
+
+    trace( "iface %p, value %p\n", iface, value );
+
+    ok( controller_added.invoked, "controller added handler not invoked\n" );
+    ok( impl->create_controller_called, "CreateGameController not called\n" );
+    ok( impl->create_controller, "unexpected call\n" );
+    ok( custom_controller.initialize_called, "Initialize not called\n" );
+    ok( custom_controller.on_input_resumed_called, "OnInputResumed not called\n" );
+    ok( !custom_controller.on_input_suspended_called, "OnInputSuspended not called\n" );
+    ok( !impl->on_game_controller_added_called, "OnGameControllerAdded already called\n" );
+    impl->on_game_controller_added_called = TRUE;
+    SetEvent( impl->added_event );
+
+    return S_OK;
 }
 
 static HRESULT WINAPI custom_factory_OnGameControllerRemoved( ICustomGameControllerFactory *iface, IGameController *value )
 {
-    ok( 0, "unexpected call\n" );
-    return E_NOTIMPL;
+    struct custom_factory *impl = impl_from_ICustomGameControllerFactory( iface );
+
+    trace( "iface %p, value %p\n", iface, value );
+
+    ok( !controller_removed.invoked, "controller removed handler invoked\n" );
+    ok( custom_controller.on_input_suspended_called, "OnInputSuspended not called\n" );
+    ok( impl->create_controller, "unexpected call\n" );
+    ok( impl->on_game_controller_added_called, "OnGameControllerAdded already called\n" );
+    ok( !impl->on_game_controller_removed_called, "OnGameControllerRemoved already called\n" );
+    impl->on_game_controller_removed_called = TRUE;
+    SetEvent( impl->removed_event );
+
+    return S_OK;
 }
 
 static const struct ICustomGameControllerFactoryVtbl custom_factory_vtbl =
@@ -675,6 +982,7 @@ static void test_windows_gaming_input(void)
     EventRegistrationToken controller_added_token;
     IRawGameControllerStatics *statics;
     HANDLE hwnd, thread, stop_event;
+    IInspectable *tmp_inspectable;
     HDEVNOTIFY devnotify;
     HSTRING str;
     UINT32 size;
@@ -799,6 +1107,75 @@ static void test_windows_gaming_input(void)
     ok( hr == S_OK, "get_Size returned %#lx\n", hr );
     ok( size == 0, "got size %u\n", size );
 
+    IVectorView_RawGameController_Release( controller_view );
+
+    WaitForSingleObject( thread, INFINITE );
+    CloseHandle( thread );
+    while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) DispatchMessageW( &msg );
+
+    device_change_count = 0;
+    device_change_expect = 2;
+    custom_factory.create_controller = TRUE;
+    custom_factory.create_controller_called = FALSE;
+    ResetEvent( controller_added.event );
+    controller_added.invoked = FALSE;
+    ResetEvent( controller_removed.event );
+    controller_removed.invoked = FALSE;
+    ResetEvent( stop_event );
+
+    custom_factory.added_event = CreateEventW( NULL, FALSE, FALSE, NULL );
+    ok( !!custom_factory.added_event, "CreateEventW failed, error %lu\n", GetLastError() );
+    custom_factory.removed_event = CreateEventW( NULL, FALSE, FALSE, NULL );
+    ok( !!custom_factory.removed_event, "CreateEventW failed, error %lu\n", GetLastError() );
+
+    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 );
+    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 );
+    ok( hr == S_OK, "GetAt returned %#lx\n", hr );
+    hr = IRawGameController_QueryInterface( raw_controller, &IID_IGameController, (void **)&game_controller );
+    ok( hr == S_OK, "QueryInterface returned %#lx\n", hr );
+    ok( game_controller != custom_controller.IGameController_outer, "got controller %p\n", game_controller );
+
+    hr = IGameControllerFactoryManagerStatics2_TryGetFactoryControllerFromGameController( manager_statics2,
+            &custom_factory.ICustomGameControllerFactory_iface, game_controller, &tmp_game_controller );
+    ok( hr == S_OK, "TryGetFactoryControllerFromGameController returned %#lx\n", hr );
+    ok( tmp_game_controller == custom_controller.IGameController_outer, "got controller %p\n", tmp_game_controller );
+    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 );
+
+    check_interface( tmp_inspectable, &IID_IUnknown, TRUE );
+    check_interface( tmp_inspectable, &IID_IInspectable, TRUE );
+    check_interface( tmp_inspectable, &IID_IAgileObject, TRUE );
+    check_interface( tmp_inspectable, &IID_IWeakReferenceSource, TRUE );
+    check_interface( tmp_inspectable, &IID_IGameController, TRUE );
+    check_interface( tmp_inspectable, &IID_IGameControllerBatteryInfo, TRUE );
+    check_interface( tmp_inspectable, &IID_IGameControllerInputSink, TRUE );
+    check_interface( tmp_inspectable, &IID_IHidGameControllerInputSink, TRUE );
+    check_interface( tmp_inspectable, &IID_IGameControllerImpl, TRUE );
+
+    check_interface( tmp_inspectable, &IID_IRawGameController, FALSE );
+    check_interface( tmp_inspectable, &IID_IGameControllerProvider, FALSE );
+    IInspectable_Release( tmp_inspectable );
+    ok( custom_controller.raw_game_controller_queried, "IRawGameController not queried\n" );
+
+    IGameController_Release( tmp_game_controller );
+
+    hr = IRawGameControllerStatics_FromGameController( statics, custom_controller.IGameController_outer, &tmp_raw_controller );
+    ok( hr == S_OK, "FromGameController returned %#lx\n", hr );
+    ok( tmp_raw_controller == raw_controller, "got controller %p\n", tmp_raw_controller );
+    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 );
+    wait_for_events( 1, &controller_removed.event, INFINITE );
+
     hr = IRawGameControllerStatics_remove_RawGameControllerAdded( statics, controller_added_token );
     ok( hr == S_OK, "remove_RawGameControllerAdded returned %#lx\n", hr );
     hr = IRawGameControllerStatics_remove_RawGameControllerRemoved( statics, controller_removed_token );
@@ -820,6 +1197,8 @@ static void test_windows_gaming_input(void)
     DestroyWindow( hwnd );
     UnregisterClassW( class.lpszClassName, class.hInstance );
 
+    CloseHandle( custom_factory.added_event );
+    CloseHandle( custom_factory.removed_event );
     CloseHandle( controller_added.event );
     CloseHandle( controller_removed.event );
 }
-- 
2.34.1




More information about the wine-devel mailing list