-- v3: windows.gaming.input: Implement IForceFeedbackMotor_TryUnloadEffectAsync. windows.gaming.input: Implement IForceFeedbackMotor_LoadEffectAsync. windows.gaming.input: Add a type parameter to force_feedback_effect_create. windows.gaming.input: Implement IForceFeedbackEffect interface for ConstantForceEffect. windows.gaming.input: Implement and instantiate ConstantForceEffect runtimeclass stubs. dinput/tests: Increase async bool handler wait timeout.
From: Rémi Bernon rbernon@codeweavers.com
We waited on the driver-side buffers to be sent, but sometimes the async completion needs a bit more time to complete itself.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index a1e41dedf44..806212d966d 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5537,7 +5537,7 @@ static void test_windows_gaming_input(void) IAsyncInfo_Release( async_info );
wait_hid_pending( file, 100 ); - ret = WaitForSingleObject( bool_async_handler.event, 100 ); + ret = WaitForSingleObject( bool_async_handler.event, 500 ); ok( ret == 0, "WaitForSingleObject returned %#lx\n", ret ); CloseHandle( bool_async_handler.event ); check_bool_async( bool_async, 1, Completed, S_OK, TRUE ); @@ -5582,7 +5582,7 @@ static void test_windows_gaming_input(void) IAsyncInfo_Release( async_info );
wait_hid_pending( file, 100 ); - ret = WaitForSingleObject( bool_async_handler.event, 100 ); + ret = WaitForSingleObject( bool_async_handler.event, 500 ); ok( ret == 0, "WaitForSingleObject returned %#lx\n", ret ); CloseHandle( bool_async_handler.event ); check_bool_async( bool_async, 1, 4, S_OK, FALSE );
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/constant_effect.c | 117 +++++++++++++++++++- dlls/windows.gaming.input/private.h | 8 ++ 3 files changed, 122 insertions(+), 6 deletions(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 806212d966d..e688f575704 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5624,15 +5624,14 @@ static void test_windows_gaming_input(void) pWindowsDeleteString( str );
hr = IActivationFactory_ActivateInstance( activation_factory, &tmp_inspectable ); - todo_wine ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); IActivationFactory_Release( activation_factory ); - if (hr != S_OK) goto skip_tests;
hr = IInspectable_QueryInterface( tmp_inspectable, &IID_IForceFeedbackEffect, (void **)&effect ); todo_wine ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); IInspectable_Release( tmp_inspectable ); + if (hr != S_OK) goto skip_tests;
hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IConstantForceEffect, (void **)&constant_effect ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); diff --git a/dlls/windows.gaming.input/constant_effect.c b/dlls/windows.gaming.input/constant_effect.c index a27258dd9bf..a491d49c18c 100644 --- a/dlls/windows.gaming.input/constant_effect.c +++ b/dlls/windows.gaming.input/constant_effect.c @@ -20,10 +20,110 @@ #include "private.h" #include "provider.h"
-#include "wine/debug.h" - WINE_DEFAULT_DEBUG_CHANNEL(input);
+struct constant_effect +{ + IConstantForceEffect IConstantForceEffect_iface; + LONG ref; +}; + +static inline struct constant_effect *impl_from_IConstantForceEffect( IConstantForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct constant_effect, IConstantForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IConstantForceEffect *iface, REFIID iid, void **out ) +{ + struct constant_effect *impl = impl_from_IConstantForceEffect( 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_IConstantForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IConstantForceEffect_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI effect_AddRef( IConstantForceEffect *iface ) +{ + struct constant_effect *impl = impl_from_IConstantForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IConstantForceEffect *iface ) +{ + struct constant_effect *impl = impl_from_IConstantForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) free( impl ); + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IConstantForceEffect *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( IConstantForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IConstantForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_SetParameters( IConstantForceEffect *iface, Vector3 direction, TimeSpan duration ) +{ + FIXME( "iface %p, direction %s, duration %I64u stub!\n", iface, debugstr_vector3( &direction ), duration.Duration ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_SetParametersWithEnvelope( IConstantForceEffect *iface, Vector3 direction, 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, 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 ), + 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 IConstantForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IConstantForceEffect methods */ + effect_SetParameters, + effect_SetParametersWithEnvelope, +}; + struct constant_factory { IActivationFactory IActivationFactory_iface; @@ -91,8 +191,17 @@ static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, Trust
static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) { - FIXME( "iface %p, instance %p stub!\n", iface, instance ); - return E_NOTIMPL; + struct constant_effect *impl; + + TRACE( "iface %p, instance %p.\n", iface, instance ); + + if (!(impl = calloc( 1, sizeof(struct constant_effect) ))) return E_OUTOFMEMORY; + impl->IConstantForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + + *instance = (IInspectable *)&impl->IConstantForceEffect_iface; + TRACE( "created ConstantForceEffect %p\n", *instance ); + return S_OK; }
static const struct IActivationFactoryVtbl activation_vtbl = diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 2e97f182e78..9bbaae5554b 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -31,6 +31,7 @@
#define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections +#define WIDL_using_Windows_Foundation_Numerics #include "windows.foundation.h" #define WIDL_using_Windows_Devices_Power #define WIDL_using_Windows_Gaming_Input @@ -38,6 +39,7 @@ #define WIDL_using_Windows_Gaming_Input_ForceFeedback #include "windows.gaming.input.custom.h"
+#include "wine/debug.h" #include "wine/list.h"
extern HINSTANCE windows_gaming_input; @@ -111,3 +113,9 @@ extern HRESULT async_operation_boolean_create( IUnknown *invoker, IUnknown *para DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface ) #define DEFINE_IINSPECTABLE_OUTER( pfx, iface_type, impl_type, outer_iface ) \ DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, impl->outer_iface ) + +static inline const char *debugstr_vector3( const Vector3 *vector ) +{ + if (!vector) return "(null)"; + return wine_dbg_sprintf( "[%f, %f, %f]", vector->X, vector->Y, vector->Z ); +}
From: Rémi Bernon rbernon@codeweavers.com
Using COM aggregation and a new IWineForceFeedbackEffectImpl interface, deriving from IUnknown as it is only used internally.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 3 +- dlls/windows.gaming.input/constant_effect.c | 21 +++- dlls/windows.gaming.input/force_feedback.c | 133 ++++++++++++++++++++ dlls/windows.gaming.input/private.h | 3 + dlls/windows.gaming.input/provider.idl | 14 +++ 5 files changed, 168 insertions(+), 6 deletions(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index e688f575704..bbd9ed2310b 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -5628,10 +5628,8 @@ static void test_windows_gaming_input(void) IActivationFactory_Release( activation_factory );
hr = IInspectable_QueryInterface( tmp_inspectable, &IID_IForceFeedbackEffect, (void **)&effect ); - todo_wine ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); IInspectable_Release( tmp_inspectable ); - if (hr != S_OK) goto skip_tests;
hr = IForceFeedbackEffect_QueryInterface( effect, &IID_IConstantForceEffect, (void **)&constant_effect ); ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); @@ -5670,6 +5668,7 @@ static void test_windows_gaming_input(void) 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() ); diff --git a/dlls/windows.gaming.input/constant_effect.c b/dlls/windows.gaming.input/constant_effect.c index a491d49c18c..ae0c0f9904d 100644 --- a/dlls/windows.gaming.input/constant_effect.c +++ b/dlls/windows.gaming.input/constant_effect.c @@ -25,6 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(input); struct constant_effect { IConstantForceEffect IConstantForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; LONG ref; };
@@ -48,9 +49,7 @@ static HRESULT WINAPI effect_QueryInterface( IConstantForceEffect *iface, REFIID return S_OK; }
- FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); - *out = NULL; - return E_NOINTERFACE; + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); }
static ULONG WINAPI effect_AddRef( IConstantForceEffect *iface ) @@ -68,7 +67,13 @@ static ULONG WINAPI effect_Release( IConstantForceEffect *iface )
TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref );
- if (!ref) free( impl ); + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + }
return ref; } @@ -192,6 +197,7 @@ static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, Trust static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) { struct constant_effect *impl; + HRESULT hr;
TRACE( "iface %p, instance %p.\n", iface, instance );
@@ -199,6 +205,13 @@ static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, II impl->IConstantForceEffect_iface.lpVtbl = &effect_vtbl; impl->ref = 1;
+ if (FAILED(hr = force_feedback_effect_create( (IInspectable *)&impl->IConstantForceEffect_iface, + &impl->IWineForceFeedbackEffectImpl_inner ))) + { + free( impl ); + return hr; + } + *instance = (IInspectable *)&impl->IConstantForceEffect_iface; TRACE( "created ConstantForceEffect %p\n", *instance ); return S_OK; diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index 3fdcc636bbd..ab29a179c6a 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -27,6 +27,139 @@
WINE_DEFAULT_DEBUG_CHANNEL(input);
+struct effect +{ + IWineForceFeedbackEffectImpl IWineForceFeedbackEffectImpl_iface; + IForceFeedbackEffect IForceFeedbackEffect_iface; + IInspectable *IInspectable_outer; + LONG ref; +}; + +static inline struct effect *impl_from_IWineForceFeedbackEffectImpl( IWineForceFeedbackEffectImpl *iface ) +{ + return CONTAINING_RECORD( iface, struct effect, IWineForceFeedbackEffectImpl_iface ); +} + +static HRESULT WINAPI effect_impl_QueryInterface( IWineForceFeedbackEffectImpl *iface, REFIID iid, void **out ) +{ + struct effect *impl = impl_from_IWineForceFeedbackEffectImpl( 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_IWineForceFeedbackEffectImpl )) + { + IWineForceFeedbackEffectImpl_AddRef( (*out = &impl->IWineForceFeedbackEffectImpl_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IForceFeedbackEffect )) + { + IInspectable_AddRef( (*out = &impl->IForceFeedbackEffect_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI effect_impl_AddRef( IWineForceFeedbackEffectImpl *iface ) +{ + struct effect *impl = impl_from_IWineForceFeedbackEffectImpl( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_impl_Release( IWineForceFeedbackEffectImpl *iface ) +{ + struct effect *impl = impl_from_IWineForceFeedbackEffectImpl( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) free( impl ); + + return ref; +} + +static const struct IWineForceFeedbackEffectImplVtbl effect_impl_vtbl = +{ + effect_impl_QueryInterface, + effect_impl_AddRef, + effect_impl_Release, + /* IWineForceFeedbackEffectImpl methods */ +}; + +DEFINE_IINSPECTABLE_OUTER( effect, IForceFeedbackEffect, struct effect, IInspectable_outer ) + +static HRESULT WINAPI effect_get_Gain( IForceFeedbackEffect *iface, DOUBLE *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_put_Gain( IForceFeedbackEffect *iface, DOUBLE value ) +{ + FIXME( "iface %p, value %f stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_get_State( IForceFeedbackEffect *iface, ForceFeedbackEffectState *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_Start( IForceFeedbackEffect *iface ) +{ + FIXME( "iface %p stub!\n", iface ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_Stop( IForceFeedbackEffect *iface ) +{ + FIXME( "iface %p stub!\n", iface ); + return E_NOTIMPL; +} + +static const struct IForceFeedbackEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IForceFeedbackEffect methods */ + effect_get_Gain, + effect_put_Gain, + effect_get_State, + effect_Start, + effect_Stop, +}; + +HRESULT force_feedback_effect_create( IInspectable *outer, IWineForceFeedbackEffectImpl **out ) +{ + struct effect *impl; + + TRACE( "outer %p, out %p\n", outer, out ); + + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IWineForceFeedbackEffectImpl_iface.lpVtbl = &effect_impl_vtbl; + impl->IForceFeedbackEffect_iface.lpVtbl = &effect_vtbl; + impl->IInspectable_outer = outer; + impl->ref = 1; + + *out = &impl->IWineForceFeedbackEffectImpl_iface; + TRACE( "created ForceFeedbackEffect %p\n", *out ); + return S_OK; +} + struct motor { IForceFeedbackMotor IForceFeedbackMotor_iface; diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 9bbaae5554b..58378304a26 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -42,6 +42,8 @@ #include "wine/debug.h" #include "wine/list.h"
+#include "provider.h" + extern HINSTANCE windows_gaming_input; extern ICustomGameControllerFactory *controller_factory; extern ICustomGameControllerFactory *gamepad_factory; @@ -69,6 +71,7 @@ extern HRESULT event_handlers_remove( struct list *list, EventRegistrationToken extern void event_handlers_notify( struct list *list, IInspectable *element );
extern HRESULT force_feedback_motor_create( IDirectInputDevice8W *device, IForceFeedbackMotor **out ); +extern HRESULT force_feedback_effect_create( IInspectable *outer, IWineForceFeedbackEffectImpl **out );
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, diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index af5fbff05df..fbfa15f4100 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -34,6 +34,7 @@ import "windows.gaming.input.forcefeedback.idl";
namespace Windows.Gaming.Input.Custom { typedef enum WineGameControllerType WineGameControllerType; + typedef enum WineForceFeedbackEffectType WineForceFeedbackEffectType; typedef struct WineGameControllerState WineGameControllerState; typedef struct WineGameControllerVibration WineGameControllerVibration; interface IWineGameControllerProvider; @@ -49,6 +50,11 @@ namespace Windows.Gaming.Input.Custom { RacingWheel = 2, };
+ enum WineForceFeedbackEffectType + { + Constant = 1, + }; + struct WineGameControllerState { UINT64 timestamp; @@ -94,6 +100,14 @@ namespace Windows.Gaming.Input.Custom { [propget] HRESULT ForceFeedbackMotor([out, retval] Windows.Gaming.Input.ForceFeedback.ForceFeedbackMotor **motor); }
+ [ + uuid(27833469-7760-417e-adbe-e011a66e16ee) + ] + interface IWineForceFeedbackEffectImpl : IUnknown + requires Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect + { + } + [ uuid(83f377ee-c799-11ec-9d64-0242ac120002) ]
From: Rémi Bernon rbernon@codeweavers.com
And convert it to IDirectInputEffect type GUID.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/windows.gaming.input/constant_effect.c | 2 +- dlls/windows.gaming.input/force_feedback.c | 28 ++++++++++++++++++++- dlls/windows.gaming.input/private.h | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/dlls/windows.gaming.input/constant_effect.c b/dlls/windows.gaming.input/constant_effect.c index ae0c0f9904d..928927144b9 100644 --- a/dlls/windows.gaming.input/constant_effect.c +++ b/dlls/windows.gaming.input/constant_effect.c @@ -205,7 +205,7 @@ static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, II impl->IConstantForceEffect_iface.lpVtbl = &effect_vtbl; impl->ref = 1;
- if (FAILED(hr = force_feedback_effect_create( (IInspectable *)&impl->IConstantForceEffect_iface, + if (FAILED(hr = force_feedback_effect_create( WineForceFeedbackEffectType_Constant, (IInspectable *)&impl->IConstantForceEffect_iface, &impl->IWineForceFeedbackEffectImpl_inner ))) { free( impl ); diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index ab29a179c6a..9d3ead5fc2b 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -33,6 +33,12 @@ struct effect IForceFeedbackEffect IForceFeedbackEffect_iface; IInspectable *IInspectable_outer; LONG ref; + + GUID type; + DWORD axes[3]; + LONG directions[3]; + DICONSTANTFORCE constant_force; + DIEFFECT params; };
static inline struct effect *impl_from_IWineForceFeedbackEffectImpl( IWineForceFeedbackEffectImpl *iface ) @@ -143,7 +149,7 @@ static const struct IForceFeedbackEffectVtbl effect_vtbl = effect_Stop, };
-HRESULT force_feedback_effect_create( IInspectable *outer, IWineForceFeedbackEffectImpl **out ) +HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IInspectable *outer, IWineForceFeedbackEffectImpl **out ) { struct effect *impl;
@@ -155,6 +161,26 @@ HRESULT force_feedback_effect_create( IInspectable *outer, IWineForceFeedbackEff impl->IInspectable_outer = outer; impl->ref = 1;
+ switch (type) + { + case WineForceFeedbackEffectType_Constant: + impl->type = GUID_ConstantForce; + impl->params.lpvTypeSpecificParams = &impl->constant_force; + impl->params.cbTypeSpecificParams = sizeof(impl->constant_force); + break; + } + + impl->params.dwSize = sizeof(DIEFFECT); + impl->params.rgdwAxes = impl->axes; + impl->params.rglDirection = impl->directions; + impl->params.dwTriggerButton = -1; + impl->params.dwGain = 10000; + impl->params.dwFlags = DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS; + impl->params.cAxes = 0; + impl->axes[0] = DIJOFS_X; + impl->axes[1] = DIJOFS_Y; + impl->axes[2] = DIJOFS_Z; + *out = &impl->IWineForceFeedbackEffectImpl_iface; TRACE( "created ForceFeedbackEffect %p\n", *out ); return S_OK; diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 58378304a26..f8edff61455 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -71,7 +71,7 @@ extern HRESULT event_handlers_remove( struct list *list, EventRegistrationToken extern void event_handlers_notify( struct list *list, IInspectable *element );
extern HRESULT force_feedback_motor_create( IDirectInputDevice8W *device, IForceFeedbackMotor **out ); -extern HRESULT force_feedback_effect_create( IInspectable *outer, IWineForceFeedbackEffectImpl **out ); +extern HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IInspectable *outer, IWineForceFeedbackEffectImpl **out );
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,
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/async.c | 143 +++++++++++++++++++++ dlls/windows.gaming.input/force_feedback.c | 45 ++++++- dlls/windows.gaming.input/private.h | 2 + 4 files changed, 187 insertions(+), 6 deletions(-)
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<Windows.Gaming.Input.ForceFeedback.ForceFeedbackLoadEffectResult>", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1<Windows.Gaming.Input.ForceFeedback.ForceFeedbackLoadEffectResult>"), + 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<ForceFeedbackLoadEffectResult> */ + 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 ) \
From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/windows.gaming.input/force_feedback.c | 36 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index 027ef459a1a..7ac69bcec1b 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -464,11 +464,43 @@ static HRESULT WINAPI motor_TryResetAsync( IForceFeedbackMotor *iface, IAsyncOpe return async_operation_boolean_create( (IUnknown *)iface, NULL, motor_try_reset_async, async_op ); }
+static HRESULT WINAPI motor_unload_effect_async( IUnknown *iface, IUnknown *param, PROPVARIANT *result ) +{ + struct effect *effect = impl_from_IForceFeedbackEffect( (IForceFeedbackEffect *)param ); + IDirectInputEffect *dinput_effect; + HRESULT hr; + + EnterCriticalSection( &effect->cs ); + dinput_effect = effect->effect; + effect->effect = NULL; + LeaveCriticalSection( &effect->cs ); + + if (!dinput_effect) hr = S_OK; + else + { + hr = IDirectInputEffect_Unload( dinput_effect ); + IDirectInputEffect_Release( dinput_effect ); + } + + result->vt = VT_BOOL; + result->boolVal = SUCCEEDED(hr); + return hr; +} + static HRESULT WINAPI motor_TryUnloadEffectAsync( IForceFeedbackMotor *iface, IForceFeedbackEffect *effect, IAsyncOperation_boolean **async_op ) { - FIXME( "iface %p, effect %p, async_op %p stub!\n", iface, effect, async_op ); - return E_NOTIMPL; + struct effect *impl = impl_from_IForceFeedbackEffect( (IForceFeedbackEffect *)effect ); + HRESULT hr = S_OK; + + TRACE( "iface %p, effect %p, async_op %p.\n", iface, effect, async_op ); + + EnterCriticalSection( &impl->cs ); + if (!impl->effect) hr = E_FAIL; + LeaveCriticalSection( &impl->cs ); + if (FAILED(hr)) return hr; + + return async_operation_boolean_create( (IUnknown *)iface, (IUnknown *)effect, motor_unload_effect_async, async_op ); }
static const struct IForceFeedbackMotorVtbl motor_vtbl =