Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 458 +++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index e414bd700af..094612d7140 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -4453,6 +4453,125 @@ static const IEventHandler_RawGameControllerVtbl controller_handler_vtbl =
static struct controller_handler controller_added = {{&controller_handler_vtbl}};
+#define check_bool_async( a, b, c, d, e ) check_bool_async_( __LINE__, a, b, c, d, e ) +static void check_bool_async_( int line, IAsyncOperation_boolean *async, UINT32 expect_id, AsyncStatus expect_status, + HRESULT expect_hr, BOOLEAN expect_result ) +{ + AsyncStatus async_status; + IAsyncInfo *async_info; + HRESULT hr, async_hr; + UINT32 async_id; + BOOLEAN result; + + hr = IAsyncOperation_boolean_QueryInterface( async, &IID_IAsyncInfo, (void **)&async_info ); + ok_(__FILE__, line)( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + async_id = 0xdeadbeef; + hr = IAsyncInfo_get_Id( async_info, &async_id ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Id returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Id returned %#lx\n", hr ); + ok_(__FILE__, line)( async_id == expect_id, "got id %u\n", async_id ); + + async_status = 0xdeadbeef; + hr = IAsyncInfo_get_Status( async_info, &async_status ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Status returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Status returned %#lx\n", hr ); + ok_(__FILE__, line)( async_status == expect_status, "got status %u\n", async_status ); + + async_hr = 0xdeadbeef; + hr = IAsyncInfo_get_ErrorCode( async_info, &async_hr ); + if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_ErrorCode returned %#lx\n", hr ); + else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_ErrorCode returned %#lx\n", hr ); + if (expect_status < 4) ok_(__FILE__, line)( async_hr == expect_hr, "got error %#lx\n", async_hr ); + else ok_(__FILE__, line)( async_hr == E_ILLEGAL_METHOD_CALL, "got error %#lx\n", async_hr ); + + IAsyncInfo_Release( async_info ); + + result = !expect_result; + hr = IAsyncOperation_boolean_GetResults( async, &result ); + switch (expect_status) + { + case Completed: + case Error: + ok_(__FILE__, line)( hr == expect_hr, "GetResults returned %#lx\n", hr ); + ok_(__FILE__, line)( result == expect_result, "got result %u\n", result ); + break; + case Canceled: + case Started: + default: + ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "GetResults returned %#lx\n", hr ); + break; + } +} + +struct bool_async_handler +{ + IAsyncOperationCompletedHandler_boolean IAsyncOperationCompletedHandler_boolean_iface; + IAsyncOperation_boolean *async; + AsyncStatus status; + BOOL invoked; + HANDLE event; +}; + +static inline struct bool_async_handler *impl_from_IAsyncOperationCompletedHandler_boolean( IAsyncOperationCompletedHandler_boolean *iface ) +{ + return CONTAINING_RECORD( iface, struct bool_async_handler, IAsyncOperationCompletedHandler_boolean_iface ); +} + +static HRESULT WINAPI bool_async_handler_QueryInterface( IAsyncOperationCompletedHandler_boolean *iface, REFIID iid, void **out ) +{ + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IAsyncOperationCompletedHandler_boolean )) + { + IUnknown_AddRef( iface ); + *out = iface; + return S_OK; + } + + trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI bool_async_handler_AddRef( IAsyncOperationCompletedHandler_boolean *iface ) +{ + return 2; +} + +static ULONG WINAPI bool_async_handler_Release( IAsyncOperationCompletedHandler_boolean *iface ) +{ + return 1; +} + +static HRESULT WINAPI bool_async_handler_Invoke( IAsyncOperationCompletedHandler_boolean *iface, + IAsyncOperation_boolean *async, AsyncStatus status ) +{ + struct bool_async_handler *impl = impl_from_IAsyncOperationCompletedHandler_boolean( iface ); + + trace( "iface %p, async %p, status %u\n", iface, async, status ); + + ok( !impl->invoked, "invoked twice\n" ); + impl->invoked = TRUE; + impl->async = async; + impl->status = status; + if (impl->event) SetEvent( impl->event ); + + return S_OK; +} + +static IAsyncOperationCompletedHandler_booleanVtbl bool_async_handler_vtbl = +{ + /*** IUnknown methods ***/ + bool_async_handler_QueryInterface, + bool_async_handler_AddRef, + bool_async_handler_Release, + /*** IAsyncOperationCompletedHandler<boolean> methods ***/ + bool_async_handler_Invoke, +}; + +struct bool_async_handler default_bool_async_handler = {{&bool_async_handler_vtbl}}; + static void test_windows_gaming_input(void) { #include "psh_hid_macros.h" @@ -4940,6 +5059,102 @@ static void test_windows_gaming_input(void) .todo = TRUE, }, }; + static struct hid_expect expect_set_gain = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 6, + .report_len = 2, + .report_buf = {6, 0x7f}, + .todo = TRUE, + }; + static struct hid_expect expect_pause = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x02}, + .todo = TRUE, + }; + static struct hid_expect expect_resume = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x03}, + .todo = TRUE, + }; + static struct hid_expect expect_stop = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x06}, + .todo = TRUE, + }; + static struct hid_expect expect_disable = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x05}, + .todo = TRUE, + }; + static struct hid_expect expect_enable = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x04}, + .todo = TRUE, + }; + static struct hid_expect expect_enable_fail = + { + .code = IOCTL_HID_WRITE_REPORT, + .ret_status = STATUS_NOT_SUPPORTED, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x04}, + .todo = TRUE, + }; + static struct hid_expect expect_reset_delay[] = + { + /* device control */ + { + .code = IOCTL_HID_WRITE_REPORT, + .ret_status = STATUS_PENDING, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x01}, + .todo = TRUE, + }, + /* device gain */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 6, + .report_len = 2, + .report_buf = {6, 0x7f}, + .todo = TRUE, + }, + }; + struct hid_expect expect_reset[] = + { + /* device control */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x01}, + .todo = TRUE, + }, + /* device gain */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 6, + .report_len = 2, + .report_buf = {6, 0x7f}, + .todo = TRUE, + }, + }; static const WCHAR *force_feedback_motor = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor; static const WCHAR *controller_class_name = RuntimeClass_Windows_Gaming_Input_RawGameController;
@@ -4953,17 +5168,26 @@ static void test_windows_gaming_input(void) }, }; DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + IAsyncOperationCompletedHandler_boolean *tmp_handler; IVectorView_RawGameController *controllers_view; IRawGameControllerStatics *controller_statics; EventRegistrationToken controller_added_token; + struct bool_async_handler bool_async_handler; IVectorView_ForceFeedbackMotor *motors_view; + ForceFeedbackEffectAxes supported_axes; + IAsyncOperation_boolean *bool_async; IRawGameController *raw_controller; IDirectInputDevice8W *device; IForceFeedbackMotor *motor; + BOOLEAN paused, enabled; + IAsyncInfo *async_info; HSTRING str; HANDLE file; UINT32 size; + DOUBLE gain; HRESULT hr; + DWORD ret; + ULONG ref;
if (!load_combase_functions()) return;
@@ -5046,6 +5270,240 @@ static void test_windows_gaming_input(void) check_interface( motor, &IID_IForceFeedbackMotor, TRUE ); check_runtimeclass( motor, force_feedback_motor );
+ paused = TRUE; + hr = IForceFeedbackMotor_get_AreEffectsPaused( motor, &paused ); + todo_wine + ok( hr == S_OK, "get_AreEffectsPaused returned %#lx\n", hr ); + todo_wine + ok( paused == FALSE, "got paused %u\n", paused ); + + gain = 12345.6; + hr = IForceFeedbackMotor_get_MasterGain( motor, &gain ); + todo_wine + ok( hr == S_OK, "get_MasterGain returned %#lx\n", hr ); + todo_wine + ok( gain == 1.0, "got gain %f\n", gain ); + set_hid_expect( file, &expect_set_gain, sizeof(expect_set_gain) ); + hr = IForceFeedbackMotor_put_MasterGain( motor, 0.5 ); + todo_wine + ok( hr == S_OK, "put_MasterGain returned %#lx\n", hr ); + wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); /* device gain reports are written asynchronously */ + + enabled = FALSE; + hr = IForceFeedbackMotor_get_IsEnabled( motor, &enabled ); + todo_wine + ok( hr == S_OK, "get_IsEnabled returned %#lx\n", hr ); + todo_wine + ok( enabled == TRUE, "got enabled %u\n", enabled ); + + supported_axes = 0xdeadbeef; + hr = IForceFeedbackMotor_get_SupportedAxes( motor, &supported_axes ); + todo_wine + ok( hr == S_OK, "get_SupportedAxes returned %#lx\n", hr ); + todo_wine + ok( supported_axes == ForceFeedbackEffectAxes_X, "got axes %#x\n", supported_axes ); + + set_hid_expect( file, &expect_pause, sizeof(expect_pause) ); + hr = IForceFeedbackMotor_PauseAllEffects( motor ); + todo_wine + ok( hr == S_OK, "PauseAllEffects returned %#lx\n", hr ); + set_hid_expect( file, &expect_resume, sizeof(expect_resume) ); + hr = IForceFeedbackMotor_ResumeAllEffects( motor ); + todo_wine + ok( hr == S_OK, "ResumeAllEffects returned %#lx\n", hr ); + set_hid_expect( file, &expect_stop, sizeof(expect_stop) ); + hr = IForceFeedbackMotor_StopAllEffects( motor ); + todo_wine + ok( hr == S_OK, "StopAllEffects returned %#lx\n", hr ); + set_hid_expect( file, NULL, 0 ); + + + set_hid_expect( file, &expect_disable, sizeof(expect_disable) ); + hr = IForceFeedbackMotor_TryDisableAsync( motor, &bool_async ); + todo_wine + ok( hr == S_OK, "TryDisableAsync returned %#lx\n", hr ); + wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + + check_interface( bool_async, &IID_IUnknown, TRUE ); + check_interface( bool_async, &IID_IInspectable, TRUE ); + check_interface( bool_async, &IID_IAgileObject, TRUE ); + check_interface( bool_async, &IID_IAsyncInfo, TRUE ); + check_interface( bool_async, &IID_IAsyncOperation_boolean, TRUE ); + check_runtimeclass( bool_async, L"Windows.Foundation.IAsyncOperation`1<Boolean>" ); + + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + bool_async_handler = default_bool_async_handler; + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ok( bool_async_handler.invoked, "handler not invoked\n" ); + ok( bool_async_handler.async == bool_async, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Completed, "got status %u\n", bool_async_handler.status ); + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + bool_async_handler = default_bool_async_handler; + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "put_Completed returned %#lx\n", hr ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + ok( bool_async_handler.async == NULL, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Started, "got status %u\n", bool_async_handler.status ); + + hr = IAsyncOperation_boolean_QueryInterface( bool_async, &IID_IAsyncInfo, (void **)&async_info ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + hr = IAsyncInfo_Cancel( async_info ); + ok( hr == S_OK, "Cancel returned %#lx\n", hr ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + hr = IAsyncInfo_Close( async_info ); + ok( hr == S_OK, "Close returned %#lx\n", hr ); + check_bool_async( bool_async, 1, 4, S_OK, FALSE ); + IAsyncInfo_Release( async_info ); + + ref = IAsyncOperation_boolean_Release( bool_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + + + set_hid_expect( file, &expect_enable_fail, sizeof(expect_enable_fail) ); + hr = IForceFeedbackMotor_TryEnableAsync( motor, &bool_async ); + todo_wine + ok( hr == S_OK, "TryEnableAsync returned %#lx\n", hr ); + wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + check_bool_async( bool_async, 1, Error, 0x8685400d, FALSE ); + + bool_async_handler = default_bool_async_handler; + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ok( bool_async_handler.invoked, "handler not invoked\n" ); + ok( bool_async_handler.async == bool_async, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Error, "got status %u\n", bool_async_handler.status ); + + hr = IAsyncOperation_boolean_QueryInterface( bool_async, &IID_IAsyncInfo, (void **)&async_info ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + hr = IAsyncInfo_Cancel( async_info ); + ok( hr == S_OK, "Cancel returned %#lx\n", hr ); + check_bool_async( bool_async, 1, Error, 0x8685400d, FALSE ); + hr = IAsyncInfo_Close( async_info ); + ok( hr == S_OK, "Close returned %#lx\n", hr ); + check_bool_async( bool_async, 1, 4, 0x8685400d, FALSE ); + IAsyncInfo_Release( async_info ); + + ref = IAsyncOperation_boolean_Release( bool_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + + + /* canceling the async op is just ignored */ + + set_hid_expect( file, expect_reset_delay, sizeof(expect_reset_delay) ); + hr = IForceFeedbackMotor_TryResetAsync( motor, &bool_async ); + todo_wine + ok( hr == S_OK, "TryResetAsync returned %#lx\n", hr ); + check_bool_async( bool_async, 1, Started, S_OK, FALSE ); + + bool_async_handler = default_bool_async_handler; + bool_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!bool_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface, + "got handler %p\n", tmp_handler ); + + hr = IAsyncOperation_boolean_QueryInterface( bool_async, &IID_IAsyncInfo, (void **)&async_info ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + hr = IAsyncInfo_Cancel( async_info ); + ok( hr == S_OK, "Cancel returned %#lx\n", hr ); + check_bool_async( bool_async, 1, Canceled, S_OK, FALSE ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + IAsyncInfo_Release( async_info ); + + wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + ret = WaitForSingleObject( bool_async_handler.event, 100 ); + ok( ret == 0, "WaitForSingleObject returned %#lx\n", ret ); + CloseHandle( bool_async_handler.event ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + + ok( bool_async_handler.invoked, "handler not invoked\n" ); + ok( bool_async_handler.async == bool_async, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == Completed, "got status %u\n", bool_async_handler.status ); + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == S_OK, "get_Completed returned %#lx\n", hr ); + ok( tmp_handler == NULL, "got handler %p\n", tmp_handler ); + + ref = IAsyncOperation_boolean_Release( bool_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + + + /* canceling then closing it calls the handler with closed state */ + + set_hid_expect( file, expect_reset_delay, sizeof(expect_reset_delay) ); + hr = IForceFeedbackMotor_TryResetAsync( motor, &bool_async ); + todo_wine + ok( hr == S_OK, "TryResetAsync returned %#lx\n", hr ); + check_bool_async( bool_async, 1, Started, S_OK, FALSE ); + + bool_async_handler = default_bool_async_handler; + bool_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!bool_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + + hr = IAsyncOperation_boolean_put_Completed( bool_async, &bool_async_handler.IAsyncOperationCompletedHandler_boolean_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + + hr = IAsyncOperation_boolean_QueryInterface( bool_async, &IID_IAsyncInfo, (void **)&async_info ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + hr = IAsyncInfo_Close( async_info ); + ok( hr == E_ILLEGAL_STATE_CHANGE, "Close returned %#lx\n", hr ); + hr = IAsyncInfo_Cancel( async_info ); + ok( hr == S_OK, "Cancel returned %#lx\n", hr ); + check_bool_async( bool_async, 1, Canceled, S_OK, FALSE ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + hr = IAsyncInfo_Close( async_info ); + ok( hr == S_OK, "Close returned %#lx\n", hr ); + check_bool_async( bool_async, 1, 4, S_OK, FALSE ); + ok( !bool_async_handler.invoked, "handler invoked\n" ); + IAsyncInfo_Release( async_info ); + + wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + ret = WaitForSingleObject( bool_async_handler.event, 100 ); + ok( ret == 0, "WaitForSingleObject returned %#lx\n", ret ); + CloseHandle( bool_async_handler.event ); + check_bool_async( bool_async, 1, 4, S_OK, FALSE ); + + ok( bool_async_handler.invoked, "handler not invoked\n" ); + ok( bool_async_handler.async == bool_async, "got async %p\n", bool_async_handler.async ); + ok( bool_async_handler.status == 4, "got status %u\n", bool_async_handler.status ); + hr = IAsyncOperation_boolean_get_Completed( bool_async, &tmp_handler ); + ok( hr == E_ILLEGAL_METHOD_CALL, "get_Completed returned %#lx\n", hr ); + + ref = IAsyncOperation_boolean_Release( bool_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + + + set_hid_expect( file, &expect_enable, sizeof(expect_enable) ); + hr = IForceFeedbackMotor_TryEnableAsync( motor, &bool_async ); + todo_wine + ok( hr == S_OK, "TryEnableAsync returned %#lx\n", hr ); + wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + ref = IAsyncOperation_boolean_Release( bool_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + + + set_hid_expect( file, expect_reset, sizeof(expect_reset) ); + hr = IForceFeedbackMotor_TryResetAsync( motor, &bool_async ); + todo_wine + ok( hr == S_OK, "TryResetAsync returned %#lx\n", hr ); + wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); + ref = IAsyncOperation_boolean_Release( bool_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + + IForceFeedbackMotor_Release( motor );
skip_tests: