diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index bbd9ed2310b..f9a01ff93ba 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5666,9 +5666,7 @@ static void test_windows_gaming_input(void) ok( hr == 0x86854003, "Stop returned %#lx\n", hr ); hr = IForceFeedbackMotor_LoadEffectAsync( motor, effect, &result_async ); - todo_wine ok( hr == S_OK, "LoadEffectAsync returned %#lx\n", hr ); - if (hr != S_OK) goto skip_tests; 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() ); @@ -5696,7 +5694,6 @@ static void test_windows_gaming_input(void) ok( ref == 0, "Release returned %lu\n", ref ); -skip_tests: IForceFeedbackMotor_Release( motor ); IRawGameController_Release( raw_controller ); diff --git a/dlls/windows.gaming.input/async.c b/dlls/windows.gaming.input/async.c index fc593bc6121..2cb6e51c9c5 100644 --- a/dlls/windows.gaming.input/async.c +++ b/dlls/windows.gaming.input/async.c @@ -502,3 +502,146 @@ HRESULT async_operation_boolean_create( IUnknown *invoker, IUnknown *param, asyn TRACE( "created IAsyncOperation_boolean %p\n", *out ); return S_OK; } + +struct async_result +{ + IAsyncOperation_ForceFeedbackLoadEffectResult IAsyncOperation_ForceFeedbackLoadEffectResult_iface; + IWineAsyncInfoImpl *IWineAsyncInfoImpl_inner; + LONG ref; +}; + +static inline struct async_result *impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( IAsyncOperation_ForceFeedbackLoadEffectResult *iface ) +{ + return CONTAINING_RECORD( iface, struct async_result, IAsyncOperation_ForceFeedbackLoadEffectResult_iface ); +} + +static HRESULT WINAPI async_result_QueryInterface( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, REFIID iid, void **out ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( 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_IAsyncOperation_ForceFeedbackLoadEffectResult )) + { + IInspectable_AddRef( (*out = &impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface) ); + return S_OK; + } + + return IWineAsyncInfoImpl_QueryInterface( impl->IWineAsyncInfoImpl_inner, iid, out ); +} + +static ULONG WINAPI async_result_AddRef( IAsyncOperation_ForceFeedbackLoadEffectResult *iface ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_result_Release( IAsyncOperation_ForceFeedbackLoadEffectResult *iface ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_result_GetIids( IAsyncOperation_ForceFeedbackLoadEffectResult *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 async_result_GetRuntimeClassName( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, HSTRING *class_name ) +{ + return WindowsCreateString( L"Windows.Foundation.IAsyncOperation`1", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1"), + class_name ); +} + +static HRESULT WINAPI async_result_GetTrustLevel( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_result_put_Completed( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult *handler ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_put_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler *)handler ); +} + +static HRESULT WINAPI async_result_get_Completed( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult **handler ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_get_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler **)handler ); +} + +static HRESULT WINAPI async_result_GetResults( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, ForceFeedbackLoadEffectResult *results ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + PROPVARIANT result = {.vt = VT_UI4}; + HRESULT hr; + + TRACE( "iface %p, results %p.\n", iface, results ); + + hr = IWineAsyncInfoImpl_get_Result( impl->IWineAsyncInfoImpl_inner, &result ); + + *results = result.ulVal; + PropVariantClear( &result ); + return hr; +} + +static const struct IAsyncOperation_ForceFeedbackLoadEffectResultVtbl async_result_vtbl = +{ + /* IUnknown methods */ + async_result_QueryInterface, + async_result_AddRef, + async_result_Release, + /* IInspectable methods */ + async_result_GetIids, + async_result_GetRuntimeClassName, + async_result_GetTrustLevel, + /* IAsyncOperation */ + async_result_put_Completed, + async_result_get_Completed, + async_result_GetResults, +}; + +HRESULT async_operation_effect_result_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_ForceFeedbackLoadEffectResult **out ) +{ + struct async_result *impl; + HRESULT hr; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface.lpVtbl = &async_result_vtbl; + impl->ref = 1; + + if (FAILED(hr = async_info_create( invoker, param, callback, (IInspectable *)&impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface, &impl->IWineAsyncInfoImpl_inner )) || + FAILED(hr = IWineAsyncInfoImpl_Start( impl->IWineAsyncInfoImpl_inner ))) + { + if (impl->IWineAsyncInfoImpl_inner) IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + return hr; + } + + *out = &impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface; + TRACE( "created IAsyncOperation_ForceFeedbackLoadEffectResult %p\n", *out ); + return S_OK; +} diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index 9d3ead5fc2b..027ef459a1a 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -34,6 +34,9 @@ struct effect IInspectable *IInspectable_outer; LONG ref; + CRITICAL_SECTION cs; + IDirectInputEffect *effect; + GUID type; DWORD axes[3]; LONG directions[3]; @@ -87,7 +90,13 @@ static ULONG WINAPI effect_impl_Release( IWineForceFeedbackEffectImpl *iface ) TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); - if (!ref) free( impl ); + if (!ref) + { + if (impl->effect) IDirectInputEffect_Release( impl->effect ); + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &impl->cs ); + free( impl ); + } return ref; } @@ -181,6 +190,9 @@ HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IIn impl->axes[1] = DIJOFS_Y; impl->axes[2] = DIJOFS_Z; + InitializeCriticalSection( &impl->cs ); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": effect.cs" ); + *out = &impl->IWineForceFeedbackEffectImpl_iface; TRACE( "created ForceFeedbackEffect %p\n", *out ); return S_OK; @@ -337,11 +349,38 @@ static HRESULT WINAPI motor_get_SupportedAxes( IForceFeedbackMotor *iface, enum return E_NOTIMPL; } +static HRESULT WINAPI motor_load_effect_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + struct effect *effect = impl_from_IForceFeedbackEffect( (IForceFeedbackEffect *)param ); + struct motor *impl = impl_from_IForceFeedbackMotor( (IForceFeedbackMotor *)invoker ); + IDirectInputEffect *dinput_effect; + HRESULT hr; + + if (SUCCEEDED(hr = IDirectInputDevice8_CreateEffect( impl->device, &effect->type, &effect->params, &dinput_effect, NULL ))) + { + EnterCriticalSection( &effect->cs ); + if (!effect->effect) + { + effect->effect = dinput_effect; + IDirectInputEffect_AddRef( effect->effect ); + } + LeaveCriticalSection( &effect->cs ); + IDirectInputEffect_Release( dinput_effect ); + } + + result->vt = VT_UI4; + if (SUCCEEDED(hr)) result->ulVal = ForceFeedbackLoadEffectResult_Succeeded; + else if (hr == DIERR_DEVICEFULL) result->ulVal = ForceFeedbackLoadEffectResult_EffectStorageFull; + else result->ulVal = ForceFeedbackLoadEffectResult_EffectNotSupported; + + return hr; +} + static HRESULT WINAPI motor_LoadEffectAsync( IForceFeedbackMotor *iface, IForceFeedbackEffect *effect, IAsyncOperation_ForceFeedbackLoadEffectResult **async_op ) { - FIXME( "iface %p, effect %p, async_op %p stub!\n", iface, effect, async_op ); - return E_NOTIMPL; + TRACE( "iface %p, effect %p, async_op %p.\n", iface, effect, async_op ); + return async_operation_effect_result_create( (IUnknown *)iface, (IUnknown *)effect, motor_load_effect_async, async_op ); } static HRESULT WINAPI motor_PauseAllEffects( IForceFeedbackMotor *iface ) diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index f8edff61455..40e5c7c67ea 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -76,6 +76,8 @@ extern HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType ty typedef HRESULT (WINAPI *async_operation_callback)( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ); extern HRESULT async_operation_boolean_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, IAsyncOperation_boolean **out ); +extern HRESULT async_operation_effect_result_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_ForceFeedbackLoadEffectResult **out ); #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \