From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 529 +++++++++++++++++++++++++++-- 1 file changed, 507 insertions(+), 22 deletions(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index f9a01ff93ba..fd305753173 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -4866,10 +4866,11 @@ static void test_windows_gaming_input(void) USAGE(1, PID_USAGE_ET_SQUARE), USAGE(1, PID_USAGE_ET_SINE), USAGE(1, PID_USAGE_ET_SPRING), + USAGE(1, PID_USAGE_ET_CONSTANT_FORCE), LOGICAL_MINIMUM(1, 1), - LOGICAL_MAXIMUM(1, 3), + LOGICAL_MAXIMUM(1, 4), PHYSICAL_MINIMUM(1, 1), - PHYSICAL_MAXIMUM(1, 3), + PHYSICAL_MAXIMUM(1, 4), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Ary|Abs), @@ -4895,6 +4896,8 @@ static void test_windows_gaming_input(void) OUTPUT(1, Cnst|Var|Abs),
USAGE(1, PID_USAGE_DURATION), + USAGE(1, PID_USAGE_TRIGGER_REPEAT_INTERVAL), + USAGE(1, PID_USAGE_SAMPLE_PERIOD), USAGE(1, PID_USAGE_START_DELAY), UNIT(2, 0x1003), /* Eng Lin:Time */ UNIT_EXPONENT(1, -3), /* 10^-3 */ @@ -4903,7 +4906,7 @@ static void test_windows_gaming_input(void) PHYSICAL_MINIMUM(1, 0), PHYSICAL_MAXIMUM(2, 0x7fff), REPORT_SIZE(1, 16), - REPORT_COUNT(1, 2), + REPORT_COUNT(1, 4), OUTPUT(1, Data|Var|Abs), UNIT(1, 0), UNIT_EXPONENT(1, 0), @@ -4917,6 +4920,15 @@ static void test_windows_gaming_input(void) REPORT_COUNT(1, 1), OUTPUT(1, Data|Var|Abs),
+ USAGE(1, PID_USAGE_GAIN), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + USAGE(1, PID_USAGE_DIRECTION), COLLECTION(1, Logical), USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1), @@ -4924,11 +4936,11 @@ static void test_windows_gaming_input(void) UNIT(1, 0x14), /* Eng Rot:Angular Pos */ UNIT_EXPONENT(1, -2), /* 10^-2 */ LOGICAL_MINIMUM(1, 0), - LOGICAL_MAXIMUM(2, 0x00ff), + LOGICAL_MAXIMUM(4, 360), PHYSICAL_MINIMUM(1, 0), - PHYSICAL_MAXIMUM(4, 0x00008ca0), + PHYSICAL_MAXIMUM(4, 36000), UNIT(1, 0), - REPORT_SIZE(1, 8), + REPORT_SIZE(1, 16), REPORT_COUNT(1, 2), OUTPUT(1, Data|Var|Abs), UNIT_EXPONENT(1, 0), @@ -5038,6 +5050,124 @@ static void test_windows_gaming_input(void) OUTPUT(1, Data|Var|Abs), END_COLLECTION,
+ USAGE(1, PID_USAGE_SET_PERIODIC_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 7), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_MAGNITUDE), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 10000), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 10000), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_OFFSET), + LOGICAL_MINIMUM(2, -10000), + LOGICAL_MAXIMUM(2, +10000), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, +10000), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_PHASE), + UNIT(1, 0x14), /* Eng Rot:Angular Pos */ + UNIT_EXPONENT(1, -2), + LOGICAL_MINIMUM(2, -180), + LOGICAL_MAXIMUM(2, +180), + PHYSICAL_MINIMUM(2, -18000), + PHYSICAL_MAXIMUM(2, +18000), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_PERIOD), + UNIT(2, 0x1003), /* Eng Lin:Time */ + UNIT_EXPONENT(1, -3), /* 10^-3 */ + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x7fff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x7fff), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + UNIT_EXPONENT(1, 0), + UNIT(1, 0), /* None */ + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_ENVELOPE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 8), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_ATTACK_LEVEL), + USAGE(1, PID_USAGE_FADE_LEVEL), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_ATTACK_TIME), + USAGE(1, PID_USAGE_FADE_TIME), + UNIT(2, 0x1003), /* Eng Lin:Time */ + UNIT_EXPONENT(1, -3), /* 10^-3 */ + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x7fff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x7fff), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_CONSTANT_FORCE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 9), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_MAGNITUDE), + LOGICAL_MINIMUM(2, -10000), + LOGICAL_MAXIMUM(2, +10000), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, +10000), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + USAGE(1, PID_USAGE_POOL_REPORT), COLLECTION(1, Logical), REPORT_ID(1, 1), @@ -5080,14 +5210,24 @@ static void test_windows_gaming_input(void) USAGE(1, PID_USAGE_ET_SQUARE), USAGE(1, PID_USAGE_ET_SINE), USAGE(1, PID_USAGE_ET_SPRING), + USAGE(1, PID_USAGE_ET_CONSTANT_FORCE), LOGICAL_MINIMUM(1, 1), - LOGICAL_MAXIMUM(1, 3), + LOGICAL_MAXIMUM(1, 4), PHYSICAL_MINIMUM(1, 1), - PHYSICAL_MAXIMUM(1, 3), + PHYSICAL_MAXIMUM(1, 4), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), FEATURE(1, Data|Ary|Abs), END_COLLECTION, + + USAGE(4, (HID_USAGE_PAGE_GENERIC<<16)|HID_USAGE_GENERIC_BYTE_COUNT), + LOGICAL_MINIMUM(1, 0x7f), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0x7f), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + FEATURE(1, Data|Ary|Abs), END_COLLECTION,
USAGE(1, PID_USAGE_BLOCK_LOAD_REPORT), @@ -5137,7 +5277,7 @@ static void test_windows_gaming_input(void) .caps = { .InputReportByteLength = 6, - .OutputReportByteLength = 11, + .OutputReportByteLength = 18, .FeatureReportByteLength = 5, }, .attributes = default_attributes, @@ -5149,7 +5289,7 @@ static void test_windows_gaming_input(void) .code = IOCTL_HID_GET_FEATURE, .report_id = 1, .report_len = 5, - .report_buf = {1,0x10,0x00,0x04,0x03}, + .report_buf = {1,0xff,0x7f,0x7f,0x03}, .todo = TRUE, }, }; @@ -5160,7 +5300,7 @@ static void test_windows_gaming_input(void) .code = IOCTL_HID_GET_FEATURE, .report_id = 1, .report_len = 5, - .report_buf = {1,0x10,0x00,0x04,0x03}, + .report_buf = {1,0xff,0x7f,0x7f,0x03}, .todo = TRUE, }, /* device control */ @@ -5279,6 +5419,138 @@ static void test_windows_gaming_input(void) .report_buf = {6, 0x7f}, }, }; + struct hid_expect expect_create_periodic[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x02,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set periodic */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 10, + .report_buf = {7,0x01,0xa0,0x0f,0xd0,0x07,0x70,0xff,0x0a,0x00}, + .todo = TRUE, + }, + /* set envelope */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 8, + .report_buf = {8,0x01,0x4c,0x7f,0x28,0x00,0x50,0x00}, + .todo = TRUE, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x02,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00}, + .wine_only = TRUE, + .todo = TRUE, + }, + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x02,0x08,0x78,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0xff,0xff,0x4e,0x01,0x00,0x00}, + .todo = TRUE, + }, + }; + struct hid_expect expect_create_constant[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x04,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set constant */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 9, + .report_len = 4, + .report_buf = {9,0x01,0xc8,0x00}, + .todo = TRUE, + }, + /* set envelope */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 8, + .report_buf = {8,0x01,0x19,0x4c,0x14,0x00,0x3c,0x00}, + .todo = TRUE, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00}, + .wine_only = TRUE, + .todo = TRUE, + }, + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x04,0x08,0x5a,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0xff,0x7f,0x4e,0x01,0x00,0x00}, + .todo = TRUE, + }, + }; + struct hid_expect expect_effect_start = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 2, + .report_len = 4, + .report_buf = {2,0x01,0x01,0x01}, + .todo = TRUE, + }; + struct hid_expect expect_effect_stop = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 2, + .report_len = 4, + .report_buf = {2,0x01,0x03,0x00}, + .todo = TRUE, + }; + struct hid_expect expect_unload[] = + { + /* effect stop */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 2, + .report_len = 4, + .report_buf = {2,0x01,0x03,0x00}, + }, + /* effect free */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 5, + .report_len = 2, + .report_buf = {5,0x01}, + }, + }; + static const WCHAR *periodic_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect; static const WCHAR *constant_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect; static const WCHAR *force_feedback_motor = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor; static const WCHAR *controller_class_name = RuntimeClass_Windows_Gaming_Input_RawGameController; @@ -5292,6 +5564,7 @@ static void test_windows_gaming_input(void) .dwHow = DIPH_DEVICE, }, }; + TimeSpan delay = {100000}, attack_duration = {200000}, release_duration = {300000}, duration = {400000}; DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; IAsyncOperation_ForceFeedbackLoadEffectResult *result_async; IAsyncOperationCompletedHandler_boolean *tmp_handler; @@ -5299,16 +5572,18 @@ static void test_windows_gaming_input(void) IVectorView_RawGameController *controllers_view; IRawGameControllerStatics *controller_statics; EventRegistrationToken controller_added_token; + IPeriodicForceEffectFactory *periodic_factory; struct bool_async_handler bool_async_handler; IVectorView_ForceFeedbackMotor *motors_view; ForceFeedbackEffectAxes supported_axes; IActivationFactory *activation_factory; + IPeriodicForceEffect *periodic_effect; IConstantForceEffect *constant_effect; + PeriodicForceEffectKind periodic_kind; + Vector3 direction = {0.1, 0.2, 0.3}; IAsyncOperation_boolean *bool_async; IRawGameController *raw_controller; ForceFeedbackEffectState state; - Vector3 vector3 = {1., 0., 0.}; - TimeSpan duration = {10000000}; IInspectable *tmp_inspectable; IForceFeedbackEffect *effect; IDirectInputDevice8W *device; @@ -5617,6 +5892,189 @@ static void test_windows_gaming_input(void) pWindowsDeleteString( str );
+ hr = pWindowsCreateString( periodic_effect_class_name, wcslen( periodic_effect_class_name ), &str ); + ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); + hr = pRoGetActivationFactory( str, &IID_IPeriodicForceEffectFactory, (void **)&periodic_factory ); + todo_wine + ok( hr == S_OK, "RoGetActivationFactory returned %#lx\n", hr ); + pWindowsDeleteString( str ); + if (hr != S_OK) goto skip_periodic; + + check_interface( periodic_factory, &IID_IUnknown, TRUE ); + check_interface( periodic_factory, &IID_IInspectable, TRUE ); + check_interface( periodic_factory, &IID_IAgileObject, TRUE ); + check_interface( periodic_factory, &IID_IActivationFactory, TRUE ); + check_interface( periodic_factory, &IID_IPeriodicForceEffectFactory, TRUE ); + check_interface( periodic_factory, &IID_IConditionForceEffectFactory, FALSE ); + + hr = IPeriodicForceEffectFactory_QueryInterface( periodic_factory, &IID_IActivationFactory, (void **)&activation_factory ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + hr = IActivationFactory_ActivateInstance( activation_factory, &tmp_inspectable ); + ok( hr == E_NOTIMPL, "ActivateInstance returned %#lx\n", hr ); + IActivationFactory_Release( activation_factory ); + + hr = IPeriodicForceEffectFactory_CreateInstance( periodic_factory, PeriodicForceEffectKind_SawtoothWaveUp, &effect ); + ok( hr == S_OK, "CreateInstance returned %#lx\n", hr ); + + check_interface( effect, &IID_IUnknown, TRUE ); + check_interface( effect, &IID_IInspectable, TRUE ); + check_interface( effect, &IID_IAgileObject, TRUE ); + check_interface( effect, &IID_IForceFeedbackEffect, TRUE ); + check_interface( effect, &IID_IPeriodicForceEffect, TRUE ); + check_interface( effect, &IID_IConditionForceEffect, FALSE ); + check_runtimeclass( effect, periodic_effect_class_name ); + + hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IPeriodicForceEffect, (void **)&periodic_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + hr = IPeriodicForceEffect_get_Kind( periodic_effect, &periodic_kind ); + ok( hr == S_OK, "get_Kind returned %#lx\n", hr ); + ok( periodic_kind == PeriodicForceEffectKind_SawtoothWaveUp, "got kind %u\n", periodic_kind ); + hr = IPeriodicForceEffect_SetParameters( periodic_effect, direction, 1.0, 0.1, 0.0, duration ); + todo_wine + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + hr = IPeriodicForceEffect_SetParametersWithEnvelope( periodic_effect, direction, 100.0, 0.1, 0.2, 0.3, 0.4, 0.5, + delay, attack_duration, duration, release_duration, 1 ); + todo_wine + ok( hr == S_OK, "SetParametersWithEnvelope returned %#lx\n", hr ); + IPeriodicForceEffect_Release( periodic_effect ); + + gain = 12345.6; + hr = IForceFeedbackEffect_get_Gain( effect, &gain ); + todo_wine + ok( hr == S_OK, "get_Gain returned %#lx\n", hr ); + todo_wine + ok( gain == 1.0, "got gain %f\n", gain ); + hr = IForceFeedbackEffect_put_Gain( effect, 0.5 ); + todo_wine + ok( hr == S_FALSE, "put_Gain returned %#lx\n", hr ); + state = 0xdeadbeef; + hr = IForceFeedbackEffect_get_State( effect, &state ); + todo_wine + ok( hr == S_OK, "get_State returned %#lx\n", hr ); + todo_wine + ok( state == ForceFeedbackEffectState_Stopped, "got state %#x\n", state ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == 0x86854003, "Start returned %#lx\n", hr ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == 0x86854003, "Stop returned %#lx\n", hr ); + + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + result_async_handler = default_result_async_handler; + result_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!result_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + hr = IAsyncOperation_ForceFeedbackLoadEffectResult_put_Completed( result_async, &result_async_handler.IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ret = WaitForSingleObject( result_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( result_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + check_result_async( result_async, 1, Error, 0x86854008, ForceFeedbackLoadEffectResult_EffectNotSupported ); + ref = IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == 0x86854003, "Start returned %#lx\n", hr ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == 0x86854003, "Stop returned %#lx\n", hr ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + todo_wine + ok( hr == 0x86854003, "TryUnloadEffectAsync returned %#lx\n", hr ); + + ref = IForceFeedbackEffect_Release( effect ); + ok( ref == 0, "Release returned %lu\n", ref ); + + + hr = IPeriodicForceEffectFactory_CreateInstance( periodic_factory, PeriodicForceEffectKind_SineWave, &effect ); + ok( hr == S_OK, "CreateInstance returned %#lx\n", hr ); + + check_interface( effect, &IID_IUnknown, TRUE ); + check_interface( effect, &IID_IInspectable, TRUE ); + check_interface( effect, &IID_IAgileObject, TRUE ); + check_interface( effect, &IID_IForceFeedbackEffect, TRUE ); + check_interface( effect, &IID_IPeriodicForceEffect, TRUE ); + check_interface( effect, &IID_IConditionForceEffect, FALSE ); + check_runtimeclass( effect, periodic_effect_class_name ); + + hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IPeriodicForceEffect, (void **)&periodic_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + hr = IPeriodicForceEffect_get_Kind( periodic_effect, &periodic_kind ); + ok( hr == S_OK, "get_Kind returned %#lx\n", hr ); + ok( periodic_kind == PeriodicForceEffectKind_SineWave, "got kind %u\n", periodic_kind ); + hr = IPeriodicForceEffect_SetParameters( periodic_effect, direction, 1.0, 0.1, 0.0, duration ); + todo_wine + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + hr = IPeriodicForceEffect_SetParametersWithEnvelope( periodic_effect, direction, 100.0, 0.1, 0.2, 0.3, 0.4, 0.5, + delay, duration, duration, duration, 1 ); + todo_wine + ok( hr == S_OK, "SetParametersWithEnvelope returned %#lx\n", hr ); + IPeriodicForceEffect_Release( periodic_effect ); + + set_hid_expect( file, expect_create_periodic, sizeof(expect_create_periodic) ); + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + result_async_handler = default_result_async_handler; + result_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!result_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + hr = IAsyncOperation_ForceFeedbackLoadEffectResult_put_Completed( result_async, &result_async_handler.IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ret = WaitForSingleObject( result_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( result_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); + ref = IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == S_OK, "Start returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == S_OK, "Start returned %#lx\n", hr ); + + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + 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 ); + ret = WaitForSingleObject( bool_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( bool_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + 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, NULL, 0 ); + + ref = IForceFeedbackEffect_Release( effect ); + ok( ref == 0, "Release returned %lu\n", ref ); + + IPeriodicForceEffectFactory_Release( periodic_factory ); + + +skip_periodic: hr = pWindowsCreateString( constant_effect_class_name, wcslen( constant_effect_class_name ), &str ); ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); hr = pRoGetActivationFactory( str, &IID_IActivationFactory, (void **)&activation_factory ); @@ -5624,7 +6082,7 @@ static void test_windows_gaming_input(void) pWindowsDeleteString( str );
hr = IActivationFactory_ActivateInstance( activation_factory, &tmp_inspectable ); - ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + ok( hr == S_OK, "ActivateInstance returned %#lx\n", hr ); IActivationFactory_Release( activation_factory );
hr = IInspectable_QueryInterface( tmp_inspectable, &IID_IForceFeedbackEffect, (void **)&effect ); @@ -5634,11 +6092,11 @@ static void test_windows_gaming_input(void) hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IConstantForceEffect, (void **)&constant_effect ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr );
- hr = IConstantForceEffect_SetParameters( constant_effect, vector3, duration ); + hr = IConstantForceEffect_SetParameters( constant_effect, direction, duration ); todo_wine ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); - hr = IConstantForceEffect_SetParametersWithEnvelope( constant_effect, vector3, 0.1, 0.2, 0.3, - duration, duration, duration, duration, 1 ); + hr = IConstantForceEffect_SetParametersWithEnvelope( constant_effect, direction, 0.1, 0.2, 0.3, + delay, attack_duration, duration, release_duration, 1 ); todo_wine ok( hr == S_OK, "SetParametersWithEnvelope returned %#lx\n", hr ); IConstantForceEffect_Release( constant_effect ); @@ -5665,6 +6123,7 @@ static void test_windows_gaming_input(void) todo_wine ok( hr == 0x86854003, "Stop returned %#lx\n", hr );
+ set_hid_expect( file, expect_create_constant, sizeof(expect_create_constant) ); hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); result_async_handler = default_result_async_handler; @@ -5676,19 +6135,45 @@ static void test_windows_gaming_input(void) ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); ret = CloseHandle( result_async_handler.event ); ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); - check_result_async( result_async, 1, Error, 0x86854008, ForceFeedbackLoadEffectResult_EffectNotSupported ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); ref = IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); ok( ref == 0, "Release returned %lu\n", ref ); + set_hid_expect( file, NULL, 0 );
+ set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); hr = IForceFeedbackEffect_Start( effect ); todo_wine - ok( hr == 0x86854003, "Start returned %#lx\n", hr ); + ok( hr == S_OK, "Start returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == S_OK, "Start returned %#lx\n", hr ); + + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); hr = IForceFeedbackEffect_Stop( effect ); todo_wine - ok( hr == 0x86854003, "Stop returned %#lx\n", hr ); - hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); + hr = IForceFeedbackEffect_Stop( effect ); todo_wine - ok( hr == 0x86854003, "TryUnloadEffectAsync returned %#lx\n", hr ); + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + 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 ); + ret = WaitForSingleObject( bool_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( bool_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + 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, NULL, 0 );
ref = IForceFeedbackEffect_Release( effect ); ok( ref == 0, "Release returned %lu\n", ref );
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 172 ++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 5 deletions(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index fd305753173..af1d7b8430e 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -4867,10 +4867,11 @@ static void test_windows_gaming_input(void) USAGE(1, PID_USAGE_ET_SINE), USAGE(1, PID_USAGE_ET_SPRING), USAGE(1, PID_USAGE_ET_CONSTANT_FORCE), + USAGE(1, PID_USAGE_ET_RAMP), LOGICAL_MINIMUM(1, 1), - LOGICAL_MAXIMUM(1, 4), + LOGICAL_MAXIMUM(1, 5), PHYSICAL_MINIMUM(1, 1), - PHYSICAL_MAXIMUM(1, 4), + PHYSICAL_MAXIMUM(1, 5), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Ary|Abs), @@ -5168,6 +5169,30 @@ static void test_windows_gaming_input(void) OUTPUT(1, Data|Var|Abs), END_COLLECTION,
+ USAGE(1, PID_USAGE_SET_RAMP_FORCE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 10), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_RAMP_START), + USAGE(1, PID_USAGE_RAMP_END), + LOGICAL_MINIMUM(2, -10000), + LOGICAL_MAXIMUM(2, +10000), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, +10000), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + USAGE(1, PID_USAGE_POOL_REPORT), COLLECTION(1, Logical), REPORT_ID(1, 1), @@ -5211,10 +5236,11 @@ static void test_windows_gaming_input(void) USAGE(1, PID_USAGE_ET_SINE), USAGE(1, PID_USAGE_ET_SPRING), USAGE(1, PID_USAGE_ET_CONSTANT_FORCE), + USAGE(1, PID_USAGE_ET_RAMP), LOGICAL_MINIMUM(1, 1), - LOGICAL_MAXIMUM(1, 4), + LOGICAL_MAXIMUM(1, 5), PHYSICAL_MINIMUM(1, 1), - PHYSICAL_MAXIMUM(1, 4), + PHYSICAL_MAXIMUM(1, 5), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), FEATURE(1, Data|Ary|Abs), @@ -5517,6 +5543,55 @@ static void test_windows_gaming_input(void) .todo = TRUE, }, }; + struct hid_expect expect_create_ramp[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x05,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set ramp */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 10, + .report_len = 6, + .report_buf = {10,0x01,0xc8,0x00,0x20,0x03}, + .todo = TRUE, + }, + /* set envelope */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 8, + .report_buf = {8,0x01,0x19,0x4c,0x14,0x00,0x3c,0x00}, + .todo = TRUE, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x05,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00}, + .wine_only = TRUE, + .todo = TRUE, + }, + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x05,0x08,0x5a,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0xff,0xff,0x4e,0x01,0x00,0x00}, + .todo = TRUE, + }, + }; struct hid_expect expect_effect_start = { .code = IOCTL_HID_WRITE_REPORT, @@ -5553,6 +5628,7 @@ static void test_windows_gaming_input(void) static const WCHAR *periodic_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect; static const WCHAR *constant_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect; static const WCHAR *force_feedback_motor = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor; + static const WCHAR *ramp_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect; static const WCHAR *controller_class_name = RuntimeClass_Windows_Gaming_Input_RawGameController;
DIPROPGUIDANDPATH guid_path = @@ -5565,6 +5641,7 @@ static void test_windows_gaming_input(void) }, }; TimeSpan delay = {100000}, attack_duration = {200000}, release_duration = {300000}, duration = {400000}; + Vector3 direction = {0.1, 0.2, 0.3}, end_direction = {0.4, 0.5, 0.6}; DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; IAsyncOperation_ForceFeedbackLoadEffectResult *result_async; IAsyncOperationCompletedHandler_boolean *tmp_handler; @@ -5580,10 +5657,10 @@ static void test_windows_gaming_input(void) IPeriodicForceEffect *periodic_effect; IConstantForceEffect *constant_effect; PeriodicForceEffectKind periodic_kind; - Vector3 direction = {0.1, 0.2, 0.3}; IAsyncOperation_boolean *bool_async; IRawGameController *raw_controller; ForceFeedbackEffectState state; + IRampForceEffect *ramp_effect; IInspectable *tmp_inspectable; IForceFeedbackEffect *effect; IDirectInputDevice8W *device; @@ -6179,6 +6256,91 @@ skip_periodic: ok( ref == 0, "Release returned %lu\n", ref );
+ hr = pWindowsCreateString( ramp_effect_class_name, wcslen( ramp_effect_class_name ), &str ); + ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); + hr = pRoGetActivationFactory( str, &IID_IActivationFactory, (void **)&activation_factory ); + todo_wine + ok( hr == S_OK, "RoGetActivationFactory returned %#lx\n", hr ); + pWindowsDeleteString( str ); + if (hr != S_OK) goto skip_ramp; + + hr = IActivationFactory_ActivateInstance( activation_factory, &tmp_inspectable ); + ok( hr == S_OK, "ActivateInstance returned %#lx\n", hr ); + IActivationFactory_Release( activation_factory ); + + hr = IInspectable_QueryInterface( tmp_inspectable, &IID_IForceFeedbackEffect, (void **)&effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + IInspectable_Release( tmp_inspectable ); + + hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IRampForceEffect, (void **)&ramp_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + hr = IRampForceEffect_SetParameters( ramp_effect, direction, end_direction, duration ); + todo_wine + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + hr = IRampForceEffect_SetParametersWithEnvelope( ramp_effect, direction, end_direction, 0.1, 0.2, 0.3, + delay, attack_duration, duration, release_duration, 1 ); + todo_wine + ok( hr == S_OK, "SetParametersWithEnvelope returned %#lx\n", hr ); + IRampForceEffect_Release( ramp_effect ); + + set_hid_expect( file, expect_create_ramp, sizeof(expect_create_ramp) ); + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + result_async_handler = default_result_async_handler; + result_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!result_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + hr = IAsyncOperation_ForceFeedbackLoadEffectResult_put_Completed( result_async, &result_async_handler.IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ret = WaitForSingleObject( result_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( result_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); + ref = IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == S_OK, "Start returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == S_OK, "Start returned %#lx\n", hr ); + + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + 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 ); + ret = WaitForSingleObject( bool_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( bool_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + 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, NULL, 0 ); + + ref = IForceFeedbackEffect_Release( effect ); + ok( ref == 0, "Release returned %lu\n", ref ); + + +skip_ramp: IForceFeedbackMotor_Release( motor );
IRawGameController_Release( raw_controller );
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 172 +++++++++++++++++++++++++++-- 1 file changed, 160 insertions(+), 12 deletions(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index af1d7b8430e..111f250f7f6 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -4985,21 +4985,21 @@ static void test_windows_gaming_input(void) END_COLLECTION,
USAGE(1, PID_USAGE_CP_OFFSET), - LOGICAL_MINIMUM(1, 0x80), - LOGICAL_MAXIMUM(1, 0x7f), - PHYSICAL_MINIMUM(2, 0xd8f0), - PHYSICAL_MAXIMUM(2, 0x2710), - REPORT_SIZE(1, 8), + LOGICAL_MINIMUM(2, -10000), + LOGICAL_MAXIMUM(2, +10000), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, +10000), + REPORT_SIZE(1, 16), REPORT_COUNT(1, 1), OUTPUT(1, Data|Var|Abs),
USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT), USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT), - LOGICAL_MINIMUM(1, 0x80), - LOGICAL_MAXIMUM(1, 0x7f), - PHYSICAL_MINIMUM(2, 0xd8f0), - PHYSICAL_MAXIMUM(2, 0x2710), - REPORT_SIZE(1, 8), + LOGICAL_MINIMUM(2, -10000), + LOGICAL_MAXIMUM(2, +10000), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, +10000), + REPORT_SIZE(1, 16), REPORT_COUNT(1, 2), OUTPUT(1, Data|Var|Abs),
@@ -5008,7 +5008,7 @@ static void test_windows_gaming_input(void) LOGICAL_MINIMUM(1, 0), LOGICAL_MAXIMUM(2, 0x00ff), PHYSICAL_MINIMUM(1, 0), - PHYSICAL_MAXIMUM(2, 0x2710), + PHYSICAL_MAXIMUM(2, 10000), REPORT_SIZE(1, 8), REPORT_COUNT(1, 2), OUTPUT(1, Data|Var|Abs), @@ -5017,7 +5017,7 @@ static void test_windows_gaming_input(void) LOGICAL_MINIMUM(1, 0), LOGICAL_MAXIMUM(2, 0x00ff), PHYSICAL_MINIMUM(1, 0), - PHYSICAL_MAXIMUM(2, 0x2710), + PHYSICAL_MAXIMUM(2, 10000), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Var|Abs), @@ -5494,6 +5494,47 @@ static void test_windows_gaming_input(void) .todo = TRUE, }, }; + struct hid_expect expect_create_condition[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 2, + .report_len = 3, + .report_buf = {2,0x03,0x00}, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 3, + .report_len = 5, + .report_buf = {3,0x01,0x01,0x00,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 4, + .report_len = 12, + .report_buf = {4,0x01,0x00,0x70,0x17,0x7b,0x02,0xe9,0x04,0x4c,0x66,0x7f}, + .todo = TRUE, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x03,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00}, + .wine_only = TRUE, + .todo = TRUE, + }, + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 18, + .report_buf = {3,0x01,0x03,0x08,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x99,0x00,0x00,0x00}, + .todo = TRUE, + }, + }; struct hid_expect expect_create_constant[] = { /* create new effect */ @@ -5625,6 +5666,7 @@ static void test_windows_gaming_input(void) .report_buf = {5,0x01}, }, }; + static const WCHAR *condition_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect; static const WCHAR *periodic_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect; static const WCHAR *constant_effect_class_name = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect; static const WCHAR *force_feedback_motor = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor; @@ -5647,11 +5689,14 @@ static void test_windows_gaming_input(void) IAsyncOperationCompletedHandler_boolean *tmp_handler; struct result_async_handler result_async_handler; IVectorView_RawGameController *controllers_view; + IConditionForceEffectFactory *condition_factory; IRawGameControllerStatics *controller_statics; EventRegistrationToken controller_added_token; IPeriodicForceEffectFactory *periodic_factory; struct bool_async_handler bool_async_handler; IVectorView_ForceFeedbackMotor *motors_view; + IConditionForceEffect *condition_effect; + ConditionForceEffectKind condition_kind; ForceFeedbackEffectAxes supported_axes; IActivationFactory *activation_factory; IPeriodicForceEffect *periodic_effect; @@ -6152,6 +6197,109 @@ static void test_windows_gaming_input(void)
skip_periodic: + hr = pWindowsCreateString( condition_effect_class_name, wcslen( condition_effect_class_name ), &str ); + ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); + hr = pRoGetActivationFactory( str, &IID_IConditionForceEffectFactory, (void **)&condition_factory ); + todo_wine + ok( hr == S_OK, "RoGetActivationFactory returned %#lx\n", hr ); + pWindowsDeleteString( str ); + if (hr != S_OK) goto skip_condition; + + check_interface( condition_factory, &IID_IUnknown, TRUE ); + check_interface( condition_factory, &IID_IInspectable, TRUE ); + check_interface( condition_factory, &IID_IAgileObject, TRUE ); + check_interface( condition_factory, &IID_IActivationFactory, TRUE ); + check_interface( condition_factory, &IID_IConditionForceEffectFactory, TRUE ); + check_interface( condition_factory, &IID_IPeriodicForceEffectFactory, FALSE ); + + hr = IConditionForceEffectFactory_QueryInterface( condition_factory, &IID_IActivationFactory, (void **)&activation_factory ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + hr = IActivationFactory_ActivateInstance( activation_factory, &tmp_inspectable ); + ok( hr == E_NOTIMPL, "ActivateInstance returned %#lx\n", hr ); + IActivationFactory_Release( activation_factory ); + + + hr = IConditionForceEffectFactory_CreateInstance( condition_factory, ConditionForceEffectKind_Spring, &effect ); + ok( hr == S_OK, "CreateInstance returned %#lx\n", hr ); + + check_interface( effect, &IID_IUnknown, TRUE ); + check_interface( effect, &IID_IInspectable, TRUE ); + check_interface( effect, &IID_IAgileObject, TRUE ); + check_interface( effect, &IID_IForceFeedbackEffect, TRUE ); + check_interface( effect, &IID_IConditionForceEffect, TRUE ); + check_interface( effect, &IID_IPeriodicForceEffect, FALSE ); + check_runtimeclass( effect, condition_effect_class_name ); + + hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IConditionForceEffect, (void **)&condition_effect ); + ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); + + hr = IConditionForceEffect_get_Kind( condition_effect, &condition_kind ); + ok( hr == S_OK, "get_Kind returned %#lx\n", hr ); + ok( condition_kind == ConditionForceEffectKind_Spring, "got kind %u\n", condition_kind ); + hr = IConditionForceEffect_SetParameters( condition_effect, direction, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6 ); + todo_wine + ok( hr == S_OK, "SetParameters returned %#lx\n", hr ); + IConditionForceEffect_Release( condition_effect ); + + set_hid_expect( file, expect_create_condition, sizeof(expect_create_condition) ); + hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); + ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); + result_async_handler = default_result_async_handler; + result_async_handler.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!result_async_handler.event, "CreateEventW failed, error %lu\n", GetLastError() ); + hr = IAsyncOperation_ForceFeedbackLoadEffectResult_put_Completed( result_async, &result_async_handler.IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult_iface ); + ok( hr == S_OK, "put_Completed returned %#lx\n", hr ); + ret = WaitForSingleObject( result_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( result_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + check_result_async( result_async, 1, Completed, S_OK, ForceFeedbackLoadEffectResult_Succeeded ); + ref = IAsyncOperation_ForceFeedbackLoadEffectResult_Release( result_async ); + ok( ref == 0, "Release returned %lu\n", ref ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == S_OK, "Start returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_start, sizeof(expect_effect_start) ); + hr = IForceFeedbackEffect_Start( effect ); + todo_wine + ok( hr == S_OK, "Start returned %#lx\n", hr ); + + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + set_hid_expect( file, &expect_effect_stop, sizeof(expect_effect_stop) ); + hr = IForceFeedbackEffect_Stop( effect ); + todo_wine + ok( hr == S_OK, "Stop returned %#lx\n", hr ); + + set_hid_expect( file, expect_unload, sizeof(expect_unload) ); + hr = IForceFeedbackMotor_TryUnloadEffectAsync( motor, effect, &bool_async ); + ok( hr == S_OK, "TryUnloadEffectAsync returned %#lx\n", hr ); + 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 ); + ret = WaitForSingleObject( bool_async_handler.event, 5000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + ret = CloseHandle( bool_async_handler.event ); + ok( ret, "CloseHandle failed, error %lu\n", GetLastError() ); + 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, NULL, 0 ); + + ref = IForceFeedbackEffect_Release( effect ); + ok( ref == 0, "Release returned %lu\n", ref ); + + IConditionForceEffectFactory_Release( condition_factory ); + + +skip_condition: hr = pWindowsCreateString( constant_effect_class_name, wcslen( constant_effect_class_name ), &str ); ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); hr = pRoGetActivationFactory( str, &IID_IActivationFactory, (void **)&activation_factory );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=114902
Your paranoid android.
=== w8 (32 bit report) ===
dinput: driver_bus.c:292: Test failed: id 1: hid.c:4020 got spurious packet driver_bus.c:1100: Test failed: id 1: hid.c:4020 expect[0]: got 0xb000f, expected 0 driver_bus.c:1101: Test failed: id 1: hid.c:4020 expect[0]: got id 6 driver_bus.c:1102: Test failed: id 1: hid.c:4020 expect[0]: got len 2
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 3 - dlls/windows.gaming.input/Makefile.in | 1 + dlls/windows.gaming.input/force_feedback.c | 8 + dlls/windows.gaming.input/main.c | 2 + dlls/windows.gaming.input/private.h | 1 + dlls/windows.gaming.input/provider.idl | 1 + dlls/windows.gaming.input/ramp_effect.c | 240 +++++++++++++++++++++ 7 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 dlls/windows.gaming.input/ramp_effect.c
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 111f250f7f6..f7d49f16d22 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -6407,10 +6407,8 @@ skip_condition: hr = pWindowsCreateString( ramp_effect_class_name, wcslen( ramp_effect_class_name ), &str ); ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); hr = pRoGetActivationFactory( str, &IID_IActivationFactory, (void **)&activation_factory ); - todo_wine ok( hr == S_OK, "RoGetActivationFactory returned %#lx\n", hr ); pWindowsDeleteString( str ); - if (hr != S_OK) goto skip_ramp;
hr = IActivationFactory_ActivateInstance( activation_factory, &tmp_inspectable ); ok( hr == S_OK, "ActivateInstance returned %#lx\n", hr ); @@ -6488,7 +6486,6 @@ skip_condition: ok( ref == 0, "Release returned %lu\n", ref );
-skip_ramp: IForceFeedbackMotor_Release( motor );
IRawGameController_Release( raw_controller ); diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index 8f3af4fec62..1509241ace6 100644 --- a/dlls/windows.gaming.input/Makefile.in +++ b/dlls/windows.gaming.input/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ manager.c \ provider.c \ racing_wheel.c \ + ramp_effect.c \ vector.c
IDL_SRCS = \ diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index 7ac69bcec1b..89df8abcdc9 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -22,6 +22,7 @@ #include "ddk/hidsdi.h" #include "dinput.h" #include "hidusage.h" +#include "provider.h"
#include "wine/debug.h"
@@ -41,6 +42,7 @@ struct effect DWORD axes[3]; LONG directions[3]; DICONSTANTFORCE constant_force; + DIRAMPFORCE ramp_force; DIEFFECT params; };
@@ -177,6 +179,12 @@ HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IIn impl->params.lpvTypeSpecificParams = &impl->constant_force; impl->params.cbTypeSpecificParams = sizeof(impl->constant_force); break; + + case WineForceFeedbackEffectType_Ramp: + impl->type = GUID_RampForce; + impl->params.lpvTypeSpecificParams = &impl->ramp_force; + impl->params.cbTypeSpecificParams = sizeof(impl->ramp_force); + break; }
impl->params.dwSize = sizeof(DIEFFECT); diff --git a/dlls/windows.gaming.input/main.c b/dlls/windows.gaming.input/main.c index aeb870b039b..9d1c856c0df 100644 --- a/dlls/windows.gaming.input/main.c +++ b/dlls/windows.gaming.input/main.c @@ -187,6 +187,8 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory **
if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect )) IInspectable_QueryInterface( constant_effect_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect )) + IInspectable_QueryInterface( ramp_effect_factory, &IID_IActivationFactory, (void **)factory );
if (*factory) return S_OK; return CLASS_E_CLASSNOTAVAILABLE; diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 40e5c7c67ea..8a25e6aff71 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -50,6 +50,7 @@ extern ICustomGameControllerFactory *gamepad_factory; extern ICustomGameControllerFactory *racing_wheel_factory; extern IGameControllerFactoryManagerStatics2 *manager_factory; extern IInspectable *constant_effect_factory; +extern IInspectable *ramp_effect_factory;
struct vector_iids { diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index fbfa15f4100..43a69b7f79a 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -53,6 +53,7 @@ namespace Windows.Gaming.Input.Custom { enum WineForceFeedbackEffectType { Constant = 1, + Ramp = 2, };
struct WineGameControllerState diff --git a/dlls/windows.gaming.input/ramp_effect.c b/dlls/windows.gaming.input/ramp_effect.c new file mode 100644 index 00000000000..0a719b3e2a6 --- /dev/null +++ b/dlls/windows.gaming.input/ramp_effect.c @@ -0,0 +1,240 @@ +/* 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" +#include "provider.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct ramp_effect +{ + IRampForceEffect IRampForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; + LONG ref; +}; + +static inline struct ramp_effect *impl_from_IRampForceEffect( IRampForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct ramp_effect, IRampForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IRampForceEffect *iface, REFIID iid, void **out ) +{ + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IRampForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IRampForceEffect_iface) ); + return S_OK; + } + + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); +} + +static ULONG WINAPI effect_AddRef( IRampForceEffect *iface ) +{ + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IRampForceEffect *iface ) +{ + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IRampForceEffect *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_GetRuntimeClassName( IRampForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IRampForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_SetParameters( IRampForceEffect *iface, Vector3 start_vector, Vector3 end_vector, TimeSpan duration ) +{ + FIXME( "iface %p, start_vector %s, end_vector %s, duration %I64u stub!\n", iface, + debugstr_vector3( &start_vector ), debugstr_vector3( &end_vector ), duration.Duration ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_SetParametersWithEnvelope( IRampForceEffect *iface, Vector3 start_vector, Vector3 end_vector, FLOAT attack_gain, + FLOAT sustain_gain, FLOAT release_gain, TimeSpan start_delay, + TimeSpan attack_duration, TimeSpan sustain_duration, + TimeSpan release_duration, UINT32 repeat_count ) +{ + FIXME( "iface %p, start_vector %s, end_vector %s, attack_gain %f, sustain_gain %f, release_gain %f, start_delay %I64u, attack_duration %I64u, " + "sustain_duration %I64u, release_duration %I64u, repeat_count %u stub!\n", iface, debugstr_vector3( &start_vector ), debugstr_vector3( &end_vector ), + attack_gain, sustain_gain, release_gain, start_delay.Duration, attack_duration.Duration, sustain_duration.Duration, + release_duration.Duration, repeat_count ); + return E_NOTIMPL; +} + +static const struct IRampForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IRampForceEffect methods */ + effect_SetParameters, + effect_SetParametersWithEnvelope, +}; + +struct ramp_factory +{ + IActivationFactory IActivationFactory_iface; + LONG ref; +}; + +static inline struct ramp_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct ramp_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI activation_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct ramp_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI activation_AddRef( IActivationFactory *iface ) +{ + struct ramp_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI activation_Release( IActivationFactory *iface ) +{ + struct ramp_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI activation_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + struct ramp_effect *impl; + HRESULT hr; + + TRACE( "iface %p, instance %p.\n", iface, instance ); + + if (!(impl = calloc( 1, sizeof(struct ramp_effect) ))) return E_OUTOFMEMORY; + impl->IRampForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + + if (FAILED(hr = force_feedback_effect_create( WineForceFeedbackEffectType_Ramp, (IInspectable *)&impl->IRampForceEffect_iface, + &impl->IWineForceFeedbackEffectImpl_inner ))) + { + free( impl ); + return hr; + } + + *instance = (IInspectable *)&impl->IRampForceEffect_iface; + TRACE( "created RampForceEffect %p\n", *instance ); + return S_OK; +} + +static const struct IActivationFactoryVtbl activation_vtbl = +{ + activation_QueryInterface, + activation_AddRef, + activation_Release, + /* IInspectable methods */ + activation_GetIids, + activation_GetRuntimeClassName, + activation_GetTrustLevel, + /* IActivationFactory methods */ + activation_ActivateInstance, +}; + +static struct ramp_factory ramp_statics = +{ + {&activation_vtbl}, + 1, +}; + +IInspectable *ramp_effect_factory = (IInspectable *)&ramp_statics.IActivationFactory_iface;
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 3 - dlls/windows.gaming.input/Makefile.in | 1 + dlls/windows.gaming.input/force_feedback.c | 21 ++ dlls/windows.gaming.input/main.c | 2 + dlls/windows.gaming.input/periodic_effect.c | 284 ++++++++++++++++++++ dlls/windows.gaming.input/private.h | 1 + dlls/windows.gaming.input/provider.idl | 8 + 7 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 dlls/windows.gaming.input/periodic_effect.c
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index f7d49f16d22..0ad85ba0cf4 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -6017,10 +6017,8 @@ static void test_windows_gaming_input(void) hr = pWindowsCreateString( periodic_effect_class_name, wcslen( periodic_effect_class_name ), &str ); ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); hr = pRoGetActivationFactory( str, &IID_IPeriodicForceEffectFactory, (void **)&periodic_factory ); - todo_wine ok( hr == S_OK, "RoGetActivationFactory returned %#lx\n", hr ); pWindowsDeleteString( str ); - if (hr != S_OK) goto skip_periodic;
check_interface( periodic_factory, &IID_IUnknown, TRUE ); check_interface( periodic_factory, &IID_IInspectable, TRUE ); @@ -6196,7 +6194,6 @@ static void test_windows_gaming_input(void) IPeriodicForceEffectFactory_Release( periodic_factory );
-skip_periodic: hr = pWindowsCreateString( condition_effect_class_name, wcslen( condition_effect_class_name ), &str ); ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); hr = pRoGetActivationFactory( str, &IID_IConditionForceEffectFactory, (void **)&condition_factory ); diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index 1509241ace6..3535b8ab365 100644 --- a/dlls/windows.gaming.input/Makefile.in +++ b/dlls/windows.gaming.input/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ gamepad.c \ main.c \ manager.c \ + periodic_effect.c \ provider.c \ racing_wheel.c \ ramp_effect.c \ diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index 89df8abcdc9..3621f245d21 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -43,6 +43,7 @@ struct effect LONG directions[3]; DICONSTANTFORCE constant_force; DIRAMPFORCE ramp_force; + DIPERIODIC periodic; DIEFFECT params; };
@@ -185,6 +186,26 @@ HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IIn impl->params.lpvTypeSpecificParams = &impl->ramp_force; impl->params.cbTypeSpecificParams = sizeof(impl->ramp_force); break; + + case WineForceFeedbackEffectType_Periodic_SineWave: + impl->type = GUID_Sine; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_TriangleWave: + impl->type = GUID_Triangle; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_SquareWave: + impl->type = GUID_Square; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_SawtoothWaveDown: + impl->type = GUID_SawtoothDown; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_SawtoothWaveUp: + impl->type = GUID_SawtoothUp; + goto WineForceFeedbackEffectType_Periodic; + WineForceFeedbackEffectType_Periodic: + impl->params.lpvTypeSpecificParams = &impl->periodic; + impl->params.cbTypeSpecificParams = sizeof(impl->periodic); + break; }
impl->params.dwSize = sizeof(DIEFFECT); diff --git a/dlls/windows.gaming.input/main.c b/dlls/windows.gaming.input/main.c index 9d1c856c0df..e5f2f261eb4 100644 --- a/dlls/windows.gaming.input/main.c +++ b/dlls/windows.gaming.input/main.c @@ -189,6 +189,8 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory ** IInspectable_QueryInterface( constant_effect_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect )) IInspectable_QueryInterface( ramp_effect_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect )) + IInspectable_QueryInterface( periodic_effect_factory, &IID_IActivationFactory, (void **)factory );
if (*factory) return S_OK; return CLASS_E_CLASSNOTAVAILABLE; diff --git a/dlls/windows.gaming.input/periodic_effect.c b/dlls/windows.gaming.input/periodic_effect.c new file mode 100644 index 00000000000..81ca60f28ef --- /dev/null +++ b/dlls/windows.gaming.input/periodic_effect.c @@ -0,0 +1,284 @@ +/* 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" +#include "provider.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct periodic_effect +{ + IPeriodicForceEffect IPeriodicForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; + LONG ref; + + PeriodicForceEffectKind kind; +}; + +static inline struct periodic_effect *impl_from_IPeriodicForceEffect( IPeriodicForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct periodic_effect, IPeriodicForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IPeriodicForceEffect *iface, REFIID iid, void **out ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IPeriodicForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IPeriodicForceEffect_iface) ); + return S_OK; + } + + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); +} + +static ULONG WINAPI effect_AddRef( IPeriodicForceEffect *iface ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IPeriodicForceEffect *iface ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IPeriodicForceEffect *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_GetRuntimeClassName( IPeriodicForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IPeriodicForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_get_Kind( IPeriodicForceEffect *iface, PeriodicForceEffectKind *kind ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + TRACE( "iface %p, kind %p.\n", iface, kind ); + *kind = impl->kind; + return S_OK; +} + +static HRESULT WINAPI effect_SetParameters( IPeriodicForceEffect *iface, Vector3 direction, FLOAT frequency, FLOAT phase, + FLOAT bias, TimeSpan duration ) +{ + FIXME( "iface %p, direction %s, frequency %f, phase %f, bias %f, duration %I64u stub!\n", iface, + debugstr_vector3( &direction ), frequency, phase, bias, duration.Duration ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_SetParametersWithEnvelope( IPeriodicForceEffect *iface, Vector3 direction, FLOAT frequency, FLOAT phase, FLOAT bias, + FLOAT attack_gain, FLOAT sustain_gain, FLOAT release_gain, TimeSpan start_delay, + TimeSpan attack_duration, TimeSpan sustain_duration, + TimeSpan release_duration, UINT32 repeat_count ) +{ + FIXME( "iface %p, direction %s, frequency %f, phase %f, bias %f, attack_gain %f, sustain_gain %f, release_gain %f, start_delay %I64u, " + "attack_duration %I64u, sustain_duration %I64u, release_duration %I64u, repeat_count %u stub!\n", iface, debugstr_vector3( &direction ), + frequency, phase, bias, attack_gain, sustain_gain, release_gain, start_delay.Duration, attack_duration.Duration, sustain_duration.Duration, + release_duration.Duration, repeat_count ); + return E_NOTIMPL; +} + +static const struct IPeriodicForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IPeriodicForceEffect methods */ + effect_get_Kind, + effect_SetParameters, + effect_SetParametersWithEnvelope, +}; + +struct periodic_factory +{ + IActivationFactory IActivationFactory_iface; + IPeriodicForceEffectFactory IPeriodicForceEffectFactory_iface; + LONG ref; +}; + +static inline struct periodic_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct periodic_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI activation_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct periodic_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IPeriodicForceEffectFactory )) + { + IInspectable_AddRef( (*out = &impl->IPeriodicForceEffectFactory_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI activation_AddRef( IActivationFactory *iface ) +{ + struct periodic_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI activation_Release( IActivationFactory *iface ) +{ + struct periodic_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI activation_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl activation_vtbl = +{ + activation_QueryInterface, + activation_AddRef, + activation_Release, + /* IInspectable methods */ + activation_GetIids, + activation_GetRuntimeClassName, + activation_GetTrustLevel, + /* IActivationFactory methods */ + activation_ActivateInstance, +}; + +DEFINE_IINSPECTABLE( factory, IPeriodicForceEffectFactory, struct periodic_factory, IActivationFactory_iface ) + +static HRESULT WINAPI factory_CreateInstance( IPeriodicForceEffectFactory *iface, enum PeriodicForceEffectKind kind, IForceFeedbackEffect **out ) +{ + enum WineForceFeedbackEffectType type = WineForceFeedbackEffectType_Periodic + kind; + struct periodic_effect *impl; + HRESULT hr; + + TRACE( "iface %p, kind %u, out %p.\n", iface, kind, out ); + + if (!(impl = calloc( 1, sizeof(struct periodic_effect) ))) return E_OUTOFMEMORY; + impl->IPeriodicForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + impl->kind = kind; + + if (FAILED(hr = force_feedback_effect_create( type, (IInspectable *)&impl->IPeriodicForceEffect_iface, &impl->IWineForceFeedbackEffectImpl_inner )) || + FAILED(hr = IPeriodicForceEffect_QueryInterface( &impl->IPeriodicForceEffect_iface, &IID_IForceFeedbackEffect, (void **)out ))) + { + if (impl->IWineForceFeedbackEffectImpl_inner) IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + return hr; + } + + IPeriodicForceEffect_Release( &impl->IPeriodicForceEffect_iface ); + TRACE( "created PeriodicForceEffect %p\n", *out ); + return S_OK; +} + +static const struct IPeriodicForceEffectFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IPeriodicForceEffectFactory methods */ + factory_CreateInstance, +}; + +static struct periodic_factory periodic_statics = +{ + {&activation_vtbl}, + {&factory_vtbl}, + 1, +}; + +IInspectable *periodic_effect_factory = (IInspectable *)&periodic_statics.IActivationFactory_iface; diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 8a25e6aff71..915695aa31b 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -51,6 +51,7 @@ extern ICustomGameControllerFactory *racing_wheel_factory; extern IGameControllerFactoryManagerStatics2 *manager_factory; extern IInspectable *constant_effect_factory; extern IInspectable *ramp_effect_factory; +extern IInspectable *periodic_effect_factory;
struct vector_iids { diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index 43a69b7f79a..9b9cda0ddc5 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -54,6 +54,14 @@ namespace Windows.Gaming.Input.Custom { { Constant = 1, Ramp = 2, + + Periodic = 10, + /* same order as PeriodicForceEffectKind */ + Periodic_SquareWave = 10, + Periodic_SineWave = 11, + Periodic_TriangleWave = 12, + Periodic_SawtoothWaveUp = 13, + Periodic_SawtoothWaveDown = 14, };
struct WineGameControllerState
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 3 - dlls/windows.gaming.input/Makefile.in | 1 + dlls/windows.gaming.input/condition_effect.c | 272 +++++++++++++++++++ dlls/windows.gaming.input/force_feedback.c | 18 ++ dlls/windows.gaming.input/main.c | 2 + dlls/windows.gaming.input/private.h | 1 + dlls/windows.gaming.input/provider.idl | 7 + 7 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 dlls/windows.gaming.input/condition_effect.c
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 0ad85ba0cf4..696828df3fc 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -6197,10 +6197,8 @@ static void test_windows_gaming_input(void) hr = pWindowsCreateString( condition_effect_class_name, wcslen( condition_effect_class_name ), &str ); ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); hr = pRoGetActivationFactory( str, &IID_IConditionForceEffectFactory, (void **)&condition_factory ); - todo_wine ok( hr == S_OK, "RoGetActivationFactory returned %#lx\n", hr ); pWindowsDeleteString( str ); - if (hr != S_OK) goto skip_condition;
check_interface( condition_factory, &IID_IUnknown, TRUE ); check_interface( condition_factory, &IID_IInspectable, TRUE ); @@ -6296,7 +6294,6 @@ static void test_windows_gaming_input(void) IConditionForceEffectFactory_Release( condition_factory );
-skip_condition: hr = pWindowsCreateString( constant_effect_class_name, wcslen( constant_effect_class_name ), &str ); ok( hr == S_OK, "WindowsCreateString returned %#lx\n", hr ); hr = pRoGetActivationFactory( str, &IID_IActivationFactory, (void **)&activation_factory ); diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index 3535b8ab365..3ec3dd0d864 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 = \ async.c \ + condition_effect.c \ constant_effect.c \ controller.c \ event_handlers.c \ diff --git a/dlls/windows.gaming.input/condition_effect.c b/dlls/windows.gaming.input/condition_effect.c new file mode 100644 index 00000000000..dccc50bb48f --- /dev/null +++ b/dlls/windows.gaming.input/condition_effect.c @@ -0,0 +1,272 @@ +/* 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" +#include "provider.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct condition_effect +{ + IConditionForceEffect IConditionForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; + LONG ref; + + ConditionForceEffectKind kind; +}; + +static inline struct condition_effect *impl_from_IConditionForceEffect( IConditionForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct condition_effect, IConditionForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IConditionForceEffect *iface, REFIID iid, void **out ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IConditionForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IConditionForceEffect_iface) ); + return S_OK; + } + + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); +} + +static ULONG WINAPI effect_AddRef( IConditionForceEffect *iface ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IConditionForceEffect *iface ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IConditionForceEffect *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_GetRuntimeClassName( IConditionForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IConditionForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_get_Kind( IConditionForceEffect *iface, ConditionForceEffectKind *kind ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + TRACE( "iface %p, kind %p.\n", iface, kind ); + *kind = impl->kind; + return S_OK; +} + +static HRESULT WINAPI effect_SetParameters( IConditionForceEffect *iface, Vector3 direction, FLOAT positive_coeff, FLOAT negative_coeff, + FLOAT max_positive_magnitude, FLOAT max_negative_magnitude, FLOAT deadzone, FLOAT bias ) +{ + FIXME( "iface %p, direction %s, positive_coeff %f, negative_coeff %f, max_positive_magnitude %f, max_negative_magnitude %f, deadzone %f, bias %f stub!\n", + iface, debugstr_vector3( &direction ), positive_coeff, negative_coeff, max_positive_magnitude, max_negative_magnitude, deadzone, bias ); + + return E_NOTIMPL; +} + +static const struct IConditionForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IConditionForceEffect methods */ + effect_get_Kind, + effect_SetParameters, +}; + +struct condition_factory +{ + IActivationFactory IActivationFactory_iface; + IConditionForceEffectFactory IConditionForceEffectFactory_iface; + LONG ref; +}; + +static inline struct condition_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct condition_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI activation_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct condition_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IConditionForceEffectFactory )) + { + IInspectable_AddRef( (*out = &impl->IConditionForceEffectFactory_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI activation_AddRef( IActivationFactory *iface ) +{ + struct condition_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI activation_Release( IActivationFactory *iface ) +{ + struct condition_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI activation_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl activation_vtbl = +{ + activation_QueryInterface, + activation_AddRef, + activation_Release, + /* IInspectable methods */ + activation_GetIids, + activation_GetRuntimeClassName, + activation_GetTrustLevel, + /* IActivationFactory methods */ + activation_ActivateInstance, +}; + +DEFINE_IINSPECTABLE( factory, IConditionForceEffectFactory, struct condition_factory, IActivationFactory_iface ) + +static HRESULT WINAPI factory_CreateInstance( IConditionForceEffectFactory *iface, enum ConditionForceEffectKind kind, IForceFeedbackEffect **out ) +{ + enum WineForceFeedbackEffectType type = WineForceFeedbackEffectType_Condition + kind; + struct condition_effect *impl; + HRESULT hr; + + TRACE( "iface %p, kind %u, out %p.\n", iface, kind, out ); + + if (!(impl = calloc( 1, sizeof(struct condition_effect) ))) return E_OUTOFMEMORY; + impl->IConditionForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + impl->kind = kind; + + if (FAILED(hr = force_feedback_effect_create( type, (IInspectable *)&impl->IConditionForceEffect_iface, &impl->IWineForceFeedbackEffectImpl_inner )) || + FAILED(hr = IConditionForceEffect_QueryInterface( &impl->IConditionForceEffect_iface, &IID_IForceFeedbackEffect, (void **)out ))) + { + if (impl->IWineForceFeedbackEffectImpl_inner) IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + return hr; + } + + IConditionForceEffect_Release( &impl->IConditionForceEffect_iface ); + TRACE( "created ConditionForceEffect %p\n", *out ); + return S_OK; +} + +static const struct IConditionForceEffectFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IConditionForceEffectFactory methods */ + factory_CreateInstance, +}; + +static struct condition_factory condition_statics = +{ + {&activation_vtbl}, + {&factory_vtbl}, + 1, +}; + +IInspectable *condition_effect_factory = (IInspectable *)&condition_statics.IActivationFactory_iface; diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index 3621f245d21..f3b5f16f8c0 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -43,6 +43,7 @@ struct effect LONG directions[3]; DICONSTANTFORCE constant_force; DIRAMPFORCE ramp_force; + DICONDITION condition; DIPERIODIC periodic; DIEFFECT params; }; @@ -206,6 +207,23 @@ HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IIn impl->params.lpvTypeSpecificParams = &impl->periodic; impl->params.cbTypeSpecificParams = sizeof(impl->periodic); break; + + case WineForceFeedbackEffectType_Condition_Spring: + impl->type = GUID_Spring; + goto WineForceFeedbackEffectType_Condition; + case WineForceFeedbackEffectType_Condition_Damper: + impl->type = GUID_Damper; + goto WineForceFeedbackEffectType_Condition; + case WineForceFeedbackEffectType_Condition_Inertia: + impl->type = GUID_Inertia; + goto WineForceFeedbackEffectType_Condition; + case WineForceFeedbackEffectType_Condition_Friction: + impl->type = GUID_Friction; + goto WineForceFeedbackEffectType_Condition; + WineForceFeedbackEffectType_Condition: + impl->params.lpvTypeSpecificParams = &impl->condition; + impl->params.cbTypeSpecificParams = sizeof(impl->condition); + break; }
impl->params.dwSize = sizeof(DIEFFECT); diff --git a/dlls/windows.gaming.input/main.c b/dlls/windows.gaming.input/main.c index e5f2f261eb4..a20630cd20b 100644 --- a/dlls/windows.gaming.input/main.c +++ b/dlls/windows.gaming.input/main.c @@ -191,6 +191,8 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory ** IInspectable_QueryInterface( ramp_effect_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect )) IInspectable_QueryInterface( periodic_effect_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect )) + IInspectable_QueryInterface( condition_effect_factory, &IID_IActivationFactory, (void **)factory );
if (*factory) return S_OK; return CLASS_E_CLASSNOTAVAILABLE; diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 915695aa31b..f53d5b5bc37 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -52,6 +52,7 @@ extern IGameControllerFactoryManagerStatics2 *manager_factory; extern IInspectable *constant_effect_factory; extern IInspectable *ramp_effect_factory; extern IInspectable *periodic_effect_factory; +extern IInspectable *condition_effect_factory;
struct vector_iids { diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index 9b9cda0ddc5..128e68893a9 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -62,6 +62,13 @@ namespace Windows.Gaming.Input.Custom { Periodic_TriangleWave = 12, Periodic_SawtoothWaveUp = 13, Periodic_SawtoothWaveDown = 14, + + Condition = 20, + /* same order as ConditionForceEffectKind */ + Condition_Spring = 20, + Condition_Damper = 21, + Condition_Inertia = 22, + Condition_Friction = 23, };
struct WineGameControllerState