ntdll: Implement RtlIsEcCode().
[wine.git] / dlls / windows.gaming.input / force_feedback.c
blobcff3c184bf9bc74aa8b83d513adf34a1efb091c2
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
20 #include "private.h"
22 #include "math.h"
24 #include "ddk/hidsdi.h"
25 #include "dinput.h"
26 #include "hidusage.h"
27 #include "provider.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(input);
33 struct effect
35 IWineForceFeedbackEffectImpl IWineForceFeedbackEffectImpl_iface;
36 IForceFeedbackEffect IForceFeedbackEffect_iface;
37 IInspectable *IInspectable_outer;
38 LONG ref;
40 CRITICAL_SECTION cs;
41 IDirectInputEffect *effect;
43 GUID type;
44 DWORD axes[3];
45 LONG directions[3];
46 ULONG repeat_count;
47 DICONSTANTFORCE constant_force;
48 DIRAMPFORCE ramp_force;
49 DICONDITION condition;
50 DIPERIODIC periodic;
51 DIENVELOPE envelope;
52 DIEFFECT params;
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) );
72 return S_OK;
75 if (IsEqualGUID( iid, &IID_IForceFeedbackEffect ))
77 IInspectable_AddRef( (*out = &impl->IForceFeedbackEffect_iface) );
78 return S_OK;
81 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
82 *out = NULL;
83 return E_NOINTERFACE;
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 );
91 return 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 );
101 if (!ref)
103 if (impl->effect) IDirectInputEffect_Release( impl->effect );
104 impl->cs.DebugInfo->Spare[0] = 0;
105 DeleteCriticalSection( &impl->cs );
106 free( impl );
109 return ref;
112 static int effect_reorient_direction( const WineForceFeedbackEffectParameters *params, Vector3 *direction )
114 int sign = +1;
116 switch (params->type)
118 case WineForceFeedbackEffectType_Constant:
119 *direction = params->constant.direction;
120 sign = params->constant.direction.X < 0 ? -1 : +1;
121 break;
123 case WineForceFeedbackEffectType_Ramp:
124 *direction = params->ramp.start_vector;
125 sign = params->ramp.start_vector.X < 0 ? -1 : +1;
126 break;
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;
134 break;
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;
141 sign = -1;
142 break;
145 direction->X *= -sign;
146 direction->Y *= -sign;
147 direction->Z *= -sign;
149 return 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;
158 DWORD count = 0;
159 HRESULT hr;
160 int sign;
162 TRACE( "iface %p, params %p, envelope %p.\n", iface, &params, envelope );
164 EnterCriticalSection( &impl->cs );
166 sign = effect_reorient_direction( &params, &direction );
167 /* Y and Z axes seems to be always ignored, is it really the case? */
168 magnitude += direction.X * direction.X;
170 switch (params.type)
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 );
177 break;
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 );
185 break;
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 );
199 break;
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;
214 break;
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;
222 else
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 );
235 return hr;
238 static const struct IWineForceFeedbackEffectImplVtbl effect_impl_vtbl =
240 effect_impl_QueryInterface,
241 effect_impl_AddRef,
242 effect_impl_Release,
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 );
259 return S_OK;
262 static HRESULT WINAPI effect_put_Gain( IForceFeedbackEffect *iface, DOUBLE value )
264 struct effect *impl = impl_from_IForceFeedbackEffect( iface );
265 HRESULT hr;
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 );
275 return hr;
278 static HRESULT WINAPI effect_get_State( IForceFeedbackEffect *iface, ForceFeedbackEffectState *value )
280 struct effect *impl = impl_from_IForceFeedbackEffect( iface );
281 DWORD status;
282 HRESULT hr;
284 TRACE( "iface %p, value %p.\n", iface, value );
286 EnterCriticalSection( &impl->cs );
287 if (!impl->effect)
288 *value = ForceFeedbackEffectState_Stopped;
289 else if (FAILED(hr = IDirectInputEffect_GetEffectStatus( impl->effect, &status )))
290 *value = ForceFeedbackEffectState_Faulted;
291 else
293 if (status == DIEGES_PLAYING) *value = ForceFeedbackEffectState_Running;
294 else *value = ForceFeedbackEffectState_Stopped;
296 LeaveCriticalSection( &impl->cs );
298 return S_OK;
301 static HRESULT WINAPI effect_Start( IForceFeedbackEffect *iface )
303 struct effect *impl = impl_from_IForceFeedbackEffect( iface );
304 HRESULT hr = E_UNEXPECTED;
305 DWORD flags = 0;
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 );
313 return hr;
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 );
327 return hr;
330 static const struct IForceFeedbackEffectVtbl effect_vtbl =
332 effect_QueryInterface,
333 effect_AddRef,
334 effect_Release,
335 /* IInspectable methods */
336 effect_GetIids,
337 effect_GetRuntimeClassName,
338 effect_GetTrustLevel,
339 /* IForceFeedbackEffect methods */
340 effect_get_Gain,
341 effect_put_Gain,
342 effect_get_State,
343 effect_Start,
344 effect_Stop,
347 HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IInspectable *outer, IWineForceFeedbackEffectImpl **out )
349 struct effect *impl;
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;
357 impl->ref = 1;
359 switch (type)
361 case WineForceFeedbackEffectType_Constant:
362 impl->type = GUID_ConstantForce;
363 impl->params.lpvTypeSpecificParams = &impl->constant_force;
364 impl->params.cbTypeSpecificParams = sizeof(impl->constant_force);
365 break;
367 case WineForceFeedbackEffectType_Ramp:
368 impl->type = GUID_RampForce;
369 impl->params.lpvTypeSpecificParams = &impl->ramp_force;
370 impl->params.cbTypeSpecificParams = sizeof(impl->ramp_force);
371 break;
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);
391 break;
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);
408 break;
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 );
428 return S_OK;
431 struct motor
433 IForceFeedbackMotor IForceFeedbackMotor_iface;
434 LONG ref;
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) );
456 return S_OK;
459 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
460 *out = NULL;
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 );
469 return 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 );
479 if (!ref)
481 IDirectInputDevice8_Release( impl->device );
482 free( impl );
485 return ref;
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 );
491 return E_NOTIMPL;
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),
498 class_name );
501 static HRESULT WINAPI motor_GetTrustLevel( IForceFeedbackMotor *iface, TrustLevel *trust_level )
503 FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
504 return E_NOTIMPL;
507 static HRESULT WINAPI motor_get_AreEffectsPaused( IForceFeedbackMotor *iface, BOOLEAN *value )
509 struct motor *impl = impl_from_IForceFeedbackMotor( iface );
510 DWORD state;
511 HRESULT hr;
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);
518 return hr;
521 static HRESULT WINAPI motor_get_MasterGain( IForceFeedbackMotor *iface, double *value )
523 struct motor *impl = impl_from_IForceFeedbackMotor( iface );
524 DIPROPDWORD gain =
526 .diph =
528 .dwSize = sizeof(DIPROPDWORD),
529 .dwHeaderSize = sizeof(DIPROPHEADER),
530 .dwHow = DIPH_DEVICE,
533 HRESULT hr;
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.;
540 return hr;
543 static HRESULT WINAPI motor_put_MasterGain( IForceFeedbackMotor *iface, double value )
545 struct motor *impl = impl_from_IForceFeedbackMotor( iface );
546 DIPROPDWORD gain =
548 .diph =
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 );
565 DWORD state;
566 HRESULT hr;
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);
573 return hr;
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 );
593 HRESULT hr;
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;
601 return hr;
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;
611 HRESULT hr;
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)
622 DWORD count = 0;
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 );
654 result->vt = VT_UI4;
655 if (SUCCEEDED(hr)) result->ulVal = ForceFeedbackLoadEffectResult_Succeeded;
656 else if (hr == DIERR_DEVICEFULL) result->ulVal = ForceFeedbackLoadEffectResult_EffectStorageFull;
657 else result->ulVal = ForceFeedbackLoadEffectResult_EffectNotSupported;
659 return hr;
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 );
699 HRESULT hr;
701 hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_SETACTUATORSOFF );
702 result->vt = VT_BOOL;
703 result->boolVal = SUCCEEDED(hr);
705 return 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 );
717 HRESULT hr;
719 hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_SETACTUATORSON );
720 result->vt = VT_BOOL;
721 result->boolVal = SUCCEEDED(hr);
723 return 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 );
735 HRESULT hr;
737 hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_RESET );
738 result->vt = VT_BOOL;
739 result->boolVal = SUCCEEDED(hr);
741 return 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;
754 HRESULT hr;
756 EnterCriticalSection( &effect->cs );
757 dinput_effect = effect->effect;
758 effect->effect = NULL;
759 LeaveCriticalSection( &effect->cs );
761 if (!dinput_effect) hr = S_OK;
762 else
764 hr = IDirectInputEffect_Unload( dinput_effect );
765 IDirectInputEffect_Release( dinput_effect );
768 result->vt = VT_BOOL;
769 result->boolVal = SUCCEEDED(hr);
770 return 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 );
777 HRESULT hr = S_OK;
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,
792 motor_AddRef,
793 motor_Release,
794 /* IInspectable methods */
795 motor_GetIids,
796 motor_GetRuntimeClassName,
797 motor_GetTrustLevel,
798 /* IForceFeedbackMotor methods */
799 motor_get_AreEffectsPaused,
800 motor_get_MasterGain,
801 motor_put_MasterGain,
802 motor_get_IsEnabled,
803 motor_get_SupportedAxes,
804 motor_LoadEffectAsync,
805 motor_PauseAllEffects,
806 motor_ResumeAllEffects,
807 motor_StopAllEffects,
808 motor_TryDisableAsync,
809 motor_TryEnableAsync,
810 motor_TryResetAsync,
811 motor_TryUnloadEffectAsync,
814 HRESULT force_feedback_motor_create( IDirectInputDevice8W *device, IForceFeedbackMotor **out )
816 struct motor *impl;
817 HRESULT hr;
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;
827 impl->ref = 1;
829 IDirectInputDevice_AddRef( device );
830 impl->device = device;
832 *out = &impl->IForceFeedbackMotor_iface;
833 TRACE( "created ForceFeedbackMotor %p\n", *out );
834 return S_OK;
836 failed:
837 IDirectInputDevice8_SetCooperativeLevel( device, 0, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE );
838 IDirectInputDevice8_Acquire( device );
839 WARN( "Failed to acquire device exclusively, hr %#lx\n", hr );
840 return hr;