1 /* WinRT Windows.Gaming.Input implementation
3 * Copyright 2022 RĂ©mi Bernon for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "ddk/hidsdi.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(input
);
35 IWineForceFeedbackEffectImpl IWineForceFeedbackEffectImpl_iface
;
36 IForceFeedbackEffect IForceFeedbackEffect_iface
;
37 IInspectable
*IInspectable_outer
;
41 IDirectInputEffect
*effect
;
47 DICONSTANTFORCE constant_force
;
48 DIRAMPFORCE ramp_force
;
49 DICONDITION condition
;
55 static inline struct effect
*impl_from_IWineForceFeedbackEffectImpl( IWineForceFeedbackEffectImpl
*iface
)
57 return CONTAINING_RECORD( iface
, struct effect
, IWineForceFeedbackEffectImpl_iface
);
60 static HRESULT WINAPI
effect_impl_QueryInterface( IWineForceFeedbackEffectImpl
*iface
, REFIID iid
, void **out
)
62 struct effect
*impl
= impl_from_IWineForceFeedbackEffectImpl( iface
);
64 TRACE( "iface %p, iid %s, out %p.\n", iface
, debugstr_guid( iid
), out
);
66 if (IsEqualGUID( iid
, &IID_IUnknown
) ||
67 IsEqualGUID( iid
, &IID_IInspectable
) ||
68 IsEqualGUID( iid
, &IID_IAgileObject
) ||
69 IsEqualGUID( iid
, &IID_IWineForceFeedbackEffectImpl
))
71 IWineForceFeedbackEffectImpl_AddRef( (*out
= &impl
->IWineForceFeedbackEffectImpl_iface
) );
75 if (IsEqualGUID( iid
, &IID_IForceFeedbackEffect
))
77 IInspectable_AddRef( (*out
= &impl
->IForceFeedbackEffect_iface
) );
81 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid
) );
86 static ULONG WINAPI
effect_impl_AddRef( IWineForceFeedbackEffectImpl
*iface
)
88 struct effect
*impl
= impl_from_IWineForceFeedbackEffectImpl( iface
);
89 ULONG ref
= InterlockedIncrement( &impl
->ref
);
90 TRACE( "iface %p increasing refcount to %lu.\n", iface
, ref
);
94 static ULONG WINAPI
effect_impl_Release( IWineForceFeedbackEffectImpl
*iface
)
96 struct effect
*impl
= impl_from_IWineForceFeedbackEffectImpl( iface
);
97 ULONG ref
= InterlockedDecrement( &impl
->ref
);
99 TRACE( "iface %p decreasing refcount to %lu.\n", iface
, ref
);
103 if (impl
->effect
) IDirectInputEffect_Release( impl
->effect
);
104 impl
->cs
.DebugInfo
->Spare
[0] = 0;
105 DeleteCriticalSection( &impl
->cs
);
112 static int effect_reorient_direction( const WineForceFeedbackEffectParameters
*params
, Vector3
*direction
)
116 switch (params
->type
)
118 case WineForceFeedbackEffectType_Constant
:
119 *direction
= params
->constant
.direction
;
120 sign
= params
->constant
.direction
.X
< 0 ? -1 : +1;
123 case WineForceFeedbackEffectType_Ramp
:
124 *direction
= params
->ramp
.start_vector
;
125 sign
= params
->ramp
.start_vector
.X
< 0 ? -1 : +1;
128 case WineForceFeedbackEffectType_Periodic_SineWave
:
129 case WineForceFeedbackEffectType_Periodic_TriangleWave
:
130 case WineForceFeedbackEffectType_Periodic_SquareWave
:
131 case WineForceFeedbackEffectType_Periodic_SawtoothWaveDown
:
132 case WineForceFeedbackEffectType_Periodic_SawtoothWaveUp
:
133 *direction
= params
->periodic
.direction
;
136 case WineForceFeedbackEffectType_Condition_Spring
:
137 case WineForceFeedbackEffectType_Condition_Damper
:
138 case WineForceFeedbackEffectType_Condition_Inertia
:
139 case WineForceFeedbackEffectType_Condition_Friction
:
140 *direction
= params
->condition
.direction
;
145 direction
->X
*= -sign
;
146 direction
->Y
*= -sign
;
147 direction
->Z
*= -sign
;
152 static HRESULT WINAPI
effect_impl_put_Parameters( IWineForceFeedbackEffectImpl
*iface
, WineForceFeedbackEffectParameters params
,
153 WineForceFeedbackEffectEnvelope
*envelope
)
155 struct effect
*impl
= impl_from_IWineForceFeedbackEffectImpl( iface
);
156 Vector3 direction
= {0};
157 double magnitude
= 0;
162 TRACE( "iface %p, params %p, envelope %p.\n", iface
, ¶ms
, envelope
);
164 EnterCriticalSection( &impl
->cs
);
166 sign
= effect_reorient_direction( ¶ms
, &direction
);
167 /* Y and Z axes seems to be always ignored, is it really the case? */
168 magnitude
+= direction
.X
* direction
.X
;
172 case WineForceFeedbackEffectType_Constant
:
173 impl
->repeat_count
= params
.constant
.repeat_count
;
174 impl
->constant_force
.lMagnitude
= sign
* round( params
.constant
.gain
* sqrt( magnitude
) * 10000 );
175 impl
->params
.dwDuration
= min( max( params
.constant
.duration
.Duration
/ 10, 0 ), INFINITE
);
176 impl
->params
.dwStartDelay
= min( max( params
.constant
.start_delay
.Duration
/ 10, 0 ), INFINITE
);
179 case WineForceFeedbackEffectType_Ramp
:
180 impl
->repeat_count
= params
.ramp
.repeat_count
;
181 impl
->ramp_force
.lStart
= sign
* round( params
.ramp
.gain
* sqrt( magnitude
) * 10000 );
182 impl
->ramp_force
.lEnd
= round( params
.ramp
.gain
* params
.ramp
.end_vector
.X
* 10000 );
183 impl
->params
.dwDuration
= min( max( params
.ramp
.duration
.Duration
/ 10, 0 ), INFINITE
);
184 impl
->params
.dwStartDelay
= min( max( params
.ramp
.start_delay
.Duration
/ 10, 0 ), INFINITE
);
187 case WineForceFeedbackEffectType_Periodic_SineWave
:
188 case WineForceFeedbackEffectType_Periodic_TriangleWave
:
189 case WineForceFeedbackEffectType_Periodic_SquareWave
:
190 case WineForceFeedbackEffectType_Periodic_SawtoothWaveDown
:
191 case WineForceFeedbackEffectType_Periodic_SawtoothWaveUp
:
192 impl
->repeat_count
= params
.periodic
.repeat_count
;
193 impl
->periodic
.dwMagnitude
= round( params
.periodic
.gain
* 10000 );
194 impl
->periodic
.dwPeriod
= 1000000 / params
.periodic
.frequency
;
195 impl
->periodic
.dwPhase
= round( params
.periodic
.phase
* 36000 );
196 impl
->periodic
.lOffset
= round( params
.periodic
.bias
* 10000 );
197 impl
->params
.dwDuration
= min( max( params
.periodic
.duration
.Duration
/ 10, 0 ), INFINITE
);
198 impl
->params
.dwStartDelay
= min( max( params
.periodic
.start_delay
.Duration
/ 10, 0 ), INFINITE
);
201 case WineForceFeedbackEffectType_Condition_Spring
:
202 case WineForceFeedbackEffectType_Condition_Damper
:
203 case WineForceFeedbackEffectType_Condition_Inertia
:
204 case WineForceFeedbackEffectType_Condition_Friction
:
205 impl
->repeat_count
= 1;
206 impl
->condition
.lPositiveCoefficient
= round( atan( params
.condition
.positive_coeff
) / M_PI_2
* 10000 );
207 impl
->condition
.lNegativeCoefficient
= round( atan( params
.condition
.negative_coeff
) / M_PI_2
* 10000 );
208 impl
->condition
.dwPositiveSaturation
= round( params
.condition
.max_positive_magnitude
* 10000 );
209 impl
->condition
.dwNegativeSaturation
= round( params
.condition
.max_negative_magnitude
* 10000 );
210 impl
->condition
.lDeadBand
= round( params
.condition
.deadzone
* 10000 );
211 impl
->condition
.lOffset
= round( params
.condition
.bias
* 10000 );
212 impl
->params
.dwDuration
= -1;
213 impl
->params
.dwStartDelay
= 0;
217 if (impl
->axes
[count
] == DIJOFS_X
) impl
->directions
[count
++] = round( direction
.X
* 10000 );
218 if (impl
->axes
[count
] == DIJOFS_Y
) impl
->directions
[count
++] = round( direction
.Y
* 10000 );
219 if (impl
->axes
[count
] == DIJOFS_Z
) impl
->directions
[count
++] = round( direction
.Z
* 10000 );
221 if (!envelope
) impl
->params
.lpEnvelope
= NULL
;
224 impl
->envelope
.dwAttackTime
= min( max( envelope
->attack_duration
.Duration
/ 10, 0 ), INFINITE
);
225 impl
->envelope
.dwAttackLevel
= round( envelope
->attack_gain
* 10000 );
226 impl
->envelope
.dwFadeTime
= impl
->params
.dwDuration
- min( max( envelope
->release_duration
.Duration
/ 10, 0 ), INFINITE
);
227 impl
->envelope
.dwFadeLevel
= round( envelope
->release_gain
* 10000 );
228 impl
->params
.lpEnvelope
= &impl
->envelope
;
231 if (!impl
->effect
) hr
= S_OK
;
232 else hr
= IDirectInputEffect_SetParameters( impl
->effect
, &impl
->params
, DIEP_ALLPARAMS
& ~DIEP_AXES
);
233 LeaveCriticalSection( &impl
->cs
);
238 static const struct IWineForceFeedbackEffectImplVtbl effect_impl_vtbl
=
240 effect_impl_QueryInterface
,
243 /* IWineForceFeedbackEffectImpl methods */
244 effect_impl_put_Parameters
,
247 DEFINE_IINSPECTABLE_OUTER( effect
, IForceFeedbackEffect
, struct effect
, IInspectable_outer
)
249 static HRESULT WINAPI
effect_get_Gain( IForceFeedbackEffect
*iface
, DOUBLE
*value
)
251 struct effect
*impl
= impl_from_IForceFeedbackEffect( iface
);
253 TRACE( "iface %p, value %p.\n", iface
, value
);
255 EnterCriticalSection( &impl
->cs
);
256 *value
= impl
->params
.dwGain
/ 10000.;
257 LeaveCriticalSection( &impl
->cs
);
262 static HRESULT WINAPI
effect_put_Gain( IForceFeedbackEffect
*iface
, DOUBLE value
)
264 struct effect
*impl
= impl_from_IForceFeedbackEffect( iface
);
267 TRACE( "iface %p, value %f.\n", iface
, value
);
269 EnterCriticalSection( &impl
->cs
);
270 impl
->params
.dwGain
= round( value
* 10000 );
271 if (!impl
->effect
) hr
= S_FALSE
;
272 else hr
= IDirectInputEffect_SetParameters( impl
->effect
, &impl
->params
, DIEP_GAIN
);
273 LeaveCriticalSection( &impl
->cs
);
278 static HRESULT WINAPI
effect_get_State( IForceFeedbackEffect
*iface
, ForceFeedbackEffectState
*value
)
280 struct effect
*impl
= impl_from_IForceFeedbackEffect( iface
);
284 TRACE( "iface %p, value %p.\n", iface
, value
);
286 EnterCriticalSection( &impl
->cs
);
288 *value
= ForceFeedbackEffectState_Stopped
;
289 else if (FAILED(hr
= IDirectInputEffect_GetEffectStatus( impl
->effect
, &status
)))
290 *value
= ForceFeedbackEffectState_Faulted
;
293 if (status
== DIEGES_PLAYING
) *value
= ForceFeedbackEffectState_Running
;
294 else *value
= ForceFeedbackEffectState_Stopped
;
296 LeaveCriticalSection( &impl
->cs
);
301 static HRESULT WINAPI
effect_Start( IForceFeedbackEffect
*iface
)
303 struct effect
*impl
= impl_from_IForceFeedbackEffect( iface
);
304 HRESULT hr
= E_UNEXPECTED
;
307 TRACE( "iface %p.\n", iface
);
309 EnterCriticalSection( &impl
->cs
);
310 if (impl
->effect
) hr
= IDirectInputEffect_Start( impl
->effect
, impl
->repeat_count
, flags
);
311 LeaveCriticalSection( &impl
->cs
);
316 static HRESULT WINAPI
effect_Stop( IForceFeedbackEffect
*iface
)
318 struct effect
*impl
= impl_from_IForceFeedbackEffect( iface
);
319 HRESULT hr
= E_UNEXPECTED
;
321 TRACE( "iface %p.\n", iface
);
323 EnterCriticalSection( &impl
->cs
);
324 if (impl
->effect
) hr
= IDirectInputEffect_Stop( impl
->effect
);
325 LeaveCriticalSection( &impl
->cs
);
330 static const struct IForceFeedbackEffectVtbl effect_vtbl
=
332 effect_QueryInterface
,
335 /* IInspectable methods */
337 effect_GetRuntimeClassName
,
338 effect_GetTrustLevel
,
339 /* IForceFeedbackEffect methods */
347 HRESULT
force_feedback_effect_create( enum WineForceFeedbackEffectType type
, IInspectable
*outer
, IWineForceFeedbackEffectImpl
**out
)
351 TRACE( "outer %p, out %p\n", outer
, out
);
353 if (!(impl
= calloc( 1, sizeof(*impl
) ))) return E_OUTOFMEMORY
;
354 impl
->IWineForceFeedbackEffectImpl_iface
.lpVtbl
= &effect_impl_vtbl
;
355 impl
->IForceFeedbackEffect_iface
.lpVtbl
= &effect_vtbl
;
356 impl
->IInspectable_outer
= outer
;
361 case WineForceFeedbackEffectType_Constant
:
362 impl
->type
= GUID_ConstantForce
;
363 impl
->params
.lpvTypeSpecificParams
= &impl
->constant_force
;
364 impl
->params
.cbTypeSpecificParams
= sizeof(impl
->constant_force
);
367 case WineForceFeedbackEffectType_Ramp
:
368 impl
->type
= GUID_RampForce
;
369 impl
->params
.lpvTypeSpecificParams
= &impl
->ramp_force
;
370 impl
->params
.cbTypeSpecificParams
= sizeof(impl
->ramp_force
);
373 case WineForceFeedbackEffectType_Periodic_SineWave
:
374 impl
->type
= GUID_Sine
;
375 goto WineForceFeedbackEffectType_Periodic
;
376 case WineForceFeedbackEffectType_Periodic_TriangleWave
:
377 impl
->type
= GUID_Triangle
;
378 goto WineForceFeedbackEffectType_Periodic
;
379 case WineForceFeedbackEffectType_Periodic_SquareWave
:
380 impl
->type
= GUID_Square
;
381 goto WineForceFeedbackEffectType_Periodic
;
382 case WineForceFeedbackEffectType_Periodic_SawtoothWaveDown
:
383 impl
->type
= GUID_SawtoothDown
;
384 goto WineForceFeedbackEffectType_Periodic
;
385 case WineForceFeedbackEffectType_Periodic_SawtoothWaveUp
:
386 impl
->type
= GUID_SawtoothUp
;
387 goto WineForceFeedbackEffectType_Periodic
;
388 WineForceFeedbackEffectType_Periodic
:
389 impl
->params
.lpvTypeSpecificParams
= &impl
->periodic
;
390 impl
->params
.cbTypeSpecificParams
= sizeof(impl
->periodic
);
393 case WineForceFeedbackEffectType_Condition_Spring
:
394 impl
->type
= GUID_Spring
;
395 goto WineForceFeedbackEffectType_Condition
;
396 case WineForceFeedbackEffectType_Condition_Damper
:
397 impl
->type
= GUID_Damper
;
398 goto WineForceFeedbackEffectType_Condition
;
399 case WineForceFeedbackEffectType_Condition_Inertia
:
400 impl
->type
= GUID_Inertia
;
401 goto WineForceFeedbackEffectType_Condition
;
402 case WineForceFeedbackEffectType_Condition_Friction
:
403 impl
->type
= GUID_Friction
;
404 goto WineForceFeedbackEffectType_Condition
;
405 WineForceFeedbackEffectType_Condition
:
406 impl
->params
.lpvTypeSpecificParams
= &impl
->condition
;
407 impl
->params
.cbTypeSpecificParams
= sizeof(impl
->condition
);
411 impl
->envelope
.dwSize
= sizeof(DIENVELOPE
);
412 impl
->params
.dwSize
= sizeof(DIEFFECT
);
413 impl
->params
.rgdwAxes
= impl
->axes
;
414 impl
->params
.rglDirection
= impl
->directions
;
415 impl
->params
.dwTriggerButton
= -1;
416 impl
->params
.dwGain
= 10000;
417 impl
->params
.dwFlags
= DIEFF_CARTESIAN
|DIEFF_OBJECTOFFSETS
;
418 impl
->params
.cAxes
= -1;
419 impl
->axes
[0] = DIJOFS_X
;
420 impl
->axes
[1] = DIJOFS_Y
;
421 impl
->axes
[2] = DIJOFS_Z
;
423 InitializeCriticalSection( &impl
->cs
);
424 impl
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)( __FILE__
": effect.cs" );
426 *out
= &impl
->IWineForceFeedbackEffectImpl_iface
;
427 TRACE( "created ForceFeedbackEffect %p\n", *out
);
433 IForceFeedbackMotor IForceFeedbackMotor_iface
;
436 IDirectInputDevice8W
*device
;
439 static inline struct motor
*impl_from_IForceFeedbackMotor( IForceFeedbackMotor
*iface
)
441 return CONTAINING_RECORD( iface
, struct motor
, IForceFeedbackMotor_iface
);
444 static HRESULT WINAPI
motor_QueryInterface( IForceFeedbackMotor
*iface
, REFIID iid
, void **out
)
446 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
448 TRACE( "iface %p, iid %s, out %p.\n", iface
, debugstr_guid( iid
), out
);
450 if (IsEqualGUID( iid
, &IID_IUnknown
) ||
451 IsEqualGUID( iid
, &IID_IInspectable
) ||
452 IsEqualGUID( iid
, &IID_IAgileObject
) ||
453 IsEqualGUID( iid
, &IID_IForceFeedbackMotor
))
455 IInspectable_AddRef( (*out
= &impl
->IForceFeedbackMotor_iface
) );
459 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid
) );
461 return E_NOINTERFACE
;
464 static ULONG WINAPI
motor_AddRef( IForceFeedbackMotor
*iface
)
466 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
467 ULONG ref
= InterlockedIncrement( &impl
->ref
);
468 TRACE( "iface %p increasing refcount to %lu.\n", iface
, ref
);
472 static ULONG WINAPI
motor_Release( IForceFeedbackMotor
*iface
)
474 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
475 ULONG ref
= InterlockedDecrement( &impl
->ref
);
477 TRACE( "iface %p decreasing refcount to %lu.\n", iface
, ref
);
481 IDirectInputDevice8_Release( impl
->device
);
488 static HRESULT WINAPI
motor_GetIids( IForceFeedbackMotor
*iface
, ULONG
*iid_count
, IID
**iids
)
490 FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface
, iid_count
, iids
);
494 static HRESULT WINAPI
motor_GetRuntimeClassName( IForceFeedbackMotor
*iface
, HSTRING
*class_name
)
496 return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor
,
497 ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor
),
501 static HRESULT WINAPI
motor_GetTrustLevel( IForceFeedbackMotor
*iface
, TrustLevel
*trust_level
)
503 FIXME( "iface %p, trust_level %p stub!\n", iface
, trust_level
);
507 static HRESULT WINAPI
motor_get_AreEffectsPaused( IForceFeedbackMotor
*iface
, BOOLEAN
*value
)
509 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
513 TRACE( "iface %p, value %p.\n", iface
, value
);
515 if (FAILED(hr
= IDirectInputDevice8_GetForceFeedbackState( impl
->device
, &state
))) *value
= FALSE
;
516 else *value
= (state
& DIGFFS_PAUSED
);
521 static HRESULT WINAPI
motor_get_MasterGain( IForceFeedbackMotor
*iface
, double *value
)
523 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
528 .dwSize
= sizeof(DIPROPDWORD
),
529 .dwHeaderSize
= sizeof(DIPROPHEADER
),
530 .dwHow
= DIPH_DEVICE
,
535 TRACE( "iface %p, value %p.\n", iface
, value
);
537 if (FAILED(hr
= IDirectInputDevice8_GetProperty( impl
->device
, DIPROP_FFGAIN
, &gain
.diph
))) *value
= 1.;
538 else *value
= gain
.dwData
/ 10000.;
543 static HRESULT WINAPI
motor_put_MasterGain( IForceFeedbackMotor
*iface
, double value
)
545 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
550 .dwSize
= sizeof(DIPROPDWORD
),
551 .dwHeaderSize
= sizeof(DIPROPHEADER
),
552 .dwHow
= DIPH_DEVICE
,
556 TRACE( "iface %p, value %f.\n", iface
, value
);
558 gain
.dwData
= 10000 * value
;
559 return IDirectInputDevice8_SetProperty( impl
->device
, DIPROP_FFGAIN
, &gain
.diph
);
562 static HRESULT WINAPI
motor_get_IsEnabled( IForceFeedbackMotor
*iface
, BOOLEAN
*value
)
564 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
568 TRACE( "iface %p, value %p.\n", iface
, value
);
570 if (FAILED(hr
= IDirectInputDevice8_GetForceFeedbackState( impl
->device
, &state
))) *value
= FALSE
;
571 else *value
= !(state
& DIGFFS_ACTUATORSOFF
);
576 static BOOL CALLBACK
check_ffb_axes( const DIDEVICEOBJECTINSTANCEW
*obj
, void *args
)
578 ForceFeedbackEffectAxes
*value
= args
;
580 if (obj
->dwType
& DIDFT_FFACTUATOR
)
582 if (IsEqualIID( &obj
->guidType
, &GUID_XAxis
)) *value
|= ForceFeedbackEffectAxes_X
;
583 else if (IsEqualIID( &obj
->guidType
, &GUID_YAxis
)) *value
|= ForceFeedbackEffectAxes_Y
;
584 else if (IsEqualIID( &obj
->guidType
, &GUID_ZAxis
)) *value
|= ForceFeedbackEffectAxes_Z
;
587 return DIENUM_CONTINUE
;
590 static HRESULT WINAPI
motor_get_SupportedAxes( IForceFeedbackMotor
*iface
, enum ForceFeedbackEffectAxes
*value
)
592 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
595 TRACE( "iface %p, value %p.\n", iface
, value
);
597 *value
= ForceFeedbackEffectAxes_None
;
598 if (FAILED(hr
= IDirectInputDevice8_EnumObjects( impl
->device
, check_ffb_axes
, value
, DIDFT_AXIS
)))
599 *value
= ForceFeedbackEffectAxes_None
;
604 static HRESULT WINAPI
motor_load_effect_async( IUnknown
*invoker
, IUnknown
*param
, PROPVARIANT
*result
)
606 struct effect
*effect
= impl_from_IForceFeedbackEffect( (IForceFeedbackEffect
*)param
);
607 IForceFeedbackMotor
*motor
= (IForceFeedbackMotor
*)invoker
;
608 struct motor
*impl
= impl_from_IForceFeedbackMotor( motor
);
609 ForceFeedbackEffectAxes supported_axes
= 0;
610 IDirectInputEffect
*dinput_effect
;
613 EnterCriticalSection( &effect
->cs
);
615 if (FAILED(hr
= IForceFeedbackMotor_get_SupportedAxes( motor
, &supported_axes
)))
617 WARN( "get_SupportedAxes for motor %p returned %#lx\n", motor
, hr
);
618 effect
->params
.cAxes
= 0;
620 else if (effect
->params
.cAxes
== -1)
624 /* initialize axis mapping and re-map directions that were set with the initial mapping */
625 if (supported_axes
& ForceFeedbackEffectAxes_X
)
627 effect
->directions
[count
] = effect
->directions
[0];
628 effect
->axes
[count
++] = DIJOFS_X
;
630 if (supported_axes
& ForceFeedbackEffectAxes_Y
)
632 effect
->directions
[count
] = effect
->directions
[1];
633 effect
->axes
[count
++] = DIJOFS_Y
;
635 if (supported_axes
& ForceFeedbackEffectAxes_Z
)
637 effect
->directions
[count
] = effect
->directions
[2];
638 effect
->axes
[count
++] = DIJOFS_Z
;
641 effect
->params
.cAxes
= count
;
644 if (SUCCEEDED(hr
= IDirectInputDevice8_CreateEffect( impl
->device
, &effect
->type
, &effect
->params
,
645 &dinput_effect
, NULL
)))
647 if (effect
->effect
) IDirectInputEffect_Release( effect
->effect
);
648 effect
->effect
= dinput_effect
;
649 IDirectInputEffect_AddRef( effect
->effect
);
652 LeaveCriticalSection( &effect
->cs
);
655 if (SUCCEEDED(hr
)) result
->ulVal
= ForceFeedbackLoadEffectResult_Succeeded
;
656 else if (hr
== DIERR_DEVICEFULL
) result
->ulVal
= ForceFeedbackLoadEffectResult_EffectStorageFull
;
657 else result
->ulVal
= ForceFeedbackLoadEffectResult_EffectNotSupported
;
662 static HRESULT WINAPI
motor_LoadEffectAsync( IForceFeedbackMotor
*iface
, IForceFeedbackEffect
*effect
,
663 IAsyncOperation_ForceFeedbackLoadEffectResult
**async_op
)
665 TRACE( "iface %p, effect %p, async_op %p.\n", iface
, effect
, async_op
);
666 return async_operation_effect_result_create( (IUnknown
*)iface
, (IUnknown
*)effect
, motor_load_effect_async
, async_op
);
669 static HRESULT WINAPI
motor_PauseAllEffects( IForceFeedbackMotor
*iface
)
671 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
673 TRACE( "iface %p.\n", iface
);
675 return IDirectInputDevice8_SendForceFeedbackCommand( impl
->device
, DISFFC_PAUSE
);
678 static HRESULT WINAPI
motor_ResumeAllEffects( IForceFeedbackMotor
*iface
)
680 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
682 TRACE( "iface %p.\n", iface
);
684 return IDirectInputDevice8_SendForceFeedbackCommand( impl
->device
, DISFFC_CONTINUE
);
687 static HRESULT WINAPI
motor_StopAllEffects( IForceFeedbackMotor
*iface
)
689 struct motor
*impl
= impl_from_IForceFeedbackMotor( iface
);
691 TRACE( "iface %p.\n", iface
);
693 return IDirectInputDevice8_SendForceFeedbackCommand( impl
->device
, DISFFC_STOPALL
);
696 static HRESULT WINAPI
motor_try_disable_async( IUnknown
*invoker
, IUnknown
*param
, PROPVARIANT
*result
)
698 struct motor
*impl
= impl_from_IForceFeedbackMotor( (IForceFeedbackMotor
*)invoker
);
701 hr
= IDirectInputDevice8_SendForceFeedbackCommand( impl
->device
, DISFFC_SETACTUATORSOFF
);
702 result
->vt
= VT_BOOL
;
703 result
->boolVal
= SUCCEEDED(hr
);
708 static HRESULT WINAPI
motor_TryDisableAsync( IForceFeedbackMotor
*iface
, IAsyncOperation_boolean
**async_op
)
710 TRACE( "iface %p, async_op %p.\n", iface
, async_op
);
711 return async_operation_boolean_create( (IUnknown
*)iface
, NULL
, motor_try_disable_async
, async_op
);
714 static HRESULT WINAPI
motor_try_enable_async( IUnknown
*invoker
, IUnknown
*param
, PROPVARIANT
*result
)
716 struct motor
*impl
= impl_from_IForceFeedbackMotor( (IForceFeedbackMotor
*)invoker
);
719 hr
= IDirectInputDevice8_SendForceFeedbackCommand( impl
->device
, DISFFC_SETACTUATORSON
);
720 result
->vt
= VT_BOOL
;
721 result
->boolVal
= SUCCEEDED(hr
);
726 static HRESULT WINAPI
motor_TryEnableAsync( IForceFeedbackMotor
*iface
, IAsyncOperation_boolean
**async_op
)
728 TRACE( "iface %p, async_op %p.\n", iface
, async_op
);
729 return async_operation_boolean_create( (IUnknown
*)iface
, NULL
, motor_try_enable_async
, async_op
);
732 static HRESULT WINAPI
motor_try_reset_async( IUnknown
*invoker
, IUnknown
*param
, PROPVARIANT
*result
)
734 struct motor
*impl
= impl_from_IForceFeedbackMotor( (IForceFeedbackMotor
*)invoker
);
737 hr
= IDirectInputDevice8_SendForceFeedbackCommand( impl
->device
, DISFFC_RESET
);
738 result
->vt
= VT_BOOL
;
739 result
->boolVal
= SUCCEEDED(hr
);
744 static HRESULT WINAPI
motor_TryResetAsync( IForceFeedbackMotor
*iface
, IAsyncOperation_boolean
**async_op
)
746 TRACE( "iface %p, async_op %p.\n", iface
, async_op
);
747 return async_operation_boolean_create( (IUnknown
*)iface
, NULL
, motor_try_reset_async
, async_op
);
750 static HRESULT WINAPI
motor_unload_effect_async( IUnknown
*iface
, IUnknown
*param
, PROPVARIANT
*result
)
752 struct effect
*effect
= impl_from_IForceFeedbackEffect( (IForceFeedbackEffect
*)param
);
753 IDirectInputEffect
*dinput_effect
;
756 EnterCriticalSection( &effect
->cs
);
757 dinput_effect
= effect
->effect
;
758 effect
->effect
= NULL
;
759 LeaveCriticalSection( &effect
->cs
);
761 if (!dinput_effect
) hr
= S_OK
;
764 hr
= IDirectInputEffect_Unload( dinput_effect
);
765 IDirectInputEffect_Release( dinput_effect
);
768 result
->vt
= VT_BOOL
;
769 result
->boolVal
= SUCCEEDED(hr
);
773 static HRESULT WINAPI
motor_TryUnloadEffectAsync( IForceFeedbackMotor
*iface
, IForceFeedbackEffect
*effect
,
774 IAsyncOperation_boolean
**async_op
)
776 struct effect
*impl
= impl_from_IForceFeedbackEffect( (IForceFeedbackEffect
*)effect
);
779 TRACE( "iface %p, effect %p, async_op %p.\n", iface
, effect
, async_op
);
781 EnterCriticalSection( &impl
->cs
);
782 if (!impl
->effect
) hr
= E_FAIL
;
783 LeaveCriticalSection( &impl
->cs
);
784 if (FAILED(hr
)) return hr
;
786 return async_operation_boolean_create( (IUnknown
*)iface
, (IUnknown
*)effect
, motor_unload_effect_async
, async_op
);
789 static const struct IForceFeedbackMotorVtbl motor_vtbl
=
791 motor_QueryInterface
,
794 /* IInspectable methods */
796 motor_GetRuntimeClassName
,
798 /* IForceFeedbackMotor methods */
799 motor_get_AreEffectsPaused
,
800 motor_get_MasterGain
,
801 motor_put_MasterGain
,
803 motor_get_SupportedAxes
,
804 motor_LoadEffectAsync
,
805 motor_PauseAllEffects
,
806 motor_ResumeAllEffects
,
807 motor_StopAllEffects
,
808 motor_TryDisableAsync
,
809 motor_TryEnableAsync
,
811 motor_TryUnloadEffectAsync
,
814 HRESULT
force_feedback_motor_create( IDirectInputDevice8W
*device
, IForceFeedbackMotor
**out
)
819 TRACE( "device %p, out %p\n", device
, out
);
821 if (FAILED(hr
= IDirectInputDevice8_Unacquire( device
))) goto failed
;
822 if (FAILED(hr
= IDirectInputDevice8_SetCooperativeLevel( device
, GetDesktopWindow(), DISCL_BACKGROUND
| DISCL_EXCLUSIVE
))) goto failed
;
823 if (FAILED(hr
= IDirectInputDevice8_Acquire( device
))) goto failed
;
825 if (!(impl
= calloc( 1, sizeof(*impl
) ))) return E_OUTOFMEMORY
;
826 impl
->IForceFeedbackMotor_iface
.lpVtbl
= &motor_vtbl
;
829 IDirectInputDevice_AddRef( device
);
830 impl
->device
= device
;
832 *out
= &impl
->IForceFeedbackMotor_iface
;
833 TRACE( "created ForceFeedbackMotor %p\n", *out
);
837 IDirectInputDevice8_SetCooperativeLevel( device
, 0, DISCL_BACKGROUND
| DISCL_NONEXCLUSIVE
);
838 IDirectInputDevice8_Acquire( device
);
839 WARN( "Failed to acquire device exclusively, hr %#lx\n", hr
);