21#include "../../SDL_internal.h"
25#include "../SDL_syshaptic.h"
33#include "../../joystick/windows/SDL_windowsjoystick_c.h"
38extern HWND SDL_HelperWindow;
45static LPDIRECTINPUT8 dinput =
NULL;
52DI_SetError(
const char *str, HRESULT err)
69 return DIENUM_CONTINUE;
84 return DI_SetError(
"Coinitialize", ret);
89 ret = CoCreateInstance(&CLSID_DirectInput8,
NULL, CLSCTX_INPROC_SERVER,
90 &IID_IDirectInput8, (LPVOID)& dinput);
93 return DI_SetError(
"CoCreateInstance", ret);
97 instance = GetModuleHandle(
NULL);
98 if (instance ==
NULL) {
100 return SDL_SetError(
"GetModuleHandle() failed with error code %lu.",
106 return DI_SetError(
"Initializing DirectInput device", ret);
110 ret = IDirectInput8_EnumDevices(dinput,
114 DIEDFL_FORCEFEEDBACK |
115 DIEDFL_ATTACHEDONLY);
118 return DI_SetError(
"Enumerating DirectInput devices", ret);
127 LPDIRECTINPUTDEVICE8
device;
128 const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK;
129 DIDEVCAPS capabilities;
132 if (dinput ==
NULL) {
144 ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &
device,
NULL);
152 capabilities.dwSize =
sizeof(DIDEVCAPS);
153 ret = IDirectInputDevice8_GetCapabilities(
device, &capabilities);
154 IDirectInputDevice8_Release(
device);
160 if ((capabilities.dwFlags & needflags) != needflags) {
177 SDL_memcpy(&item->capabilities, &capabilities,
sizeof(capabilities));
188 if (dinput ==
NULL) {
206DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
208 SDL_Haptic *
haptic = (SDL_Haptic *) pvRef;
210 if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
211 const GUID *guid = &dev->guidType;
226 return DIENUM_CONTINUE;
238 return DIENUM_CONTINUE;
244#define EFFECT_TEST(e,s) \
245if (WIN_IsEqualGUID(&pei->guid, &(e))) \
246 haptic->supported |= (s)
248DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
251 SDL_Haptic *
haptic = (SDL_Haptic *) pv;
269 return DIENUM_CONTINUE;
283SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic *
haptic, LPDIRECTINPUTDEVICE8 device8,
SDL_bool is_joystick)
296 haptic->hwdata->device = device8;
308 ret = IDirectInputDevice8_SetCooperativeLevel(
haptic->hwdata->device,
313 DI_SetError(
"Setting cooperative level to exclusive", ret);
318 ret = IDirectInputDevice8_SetDataFormat(
haptic->hwdata->device,
319 &SDL_c_dfDIJoystick2);
321 DI_SetError(
"Setting data format", ret);
327 ret = IDirectInputDevice8_Acquire(
haptic->hwdata->device);
329 DI_SetError(
"Acquiring DirectInput device", ret);
335 ret = IDirectInputDevice8_EnumObjects(
haptic->hwdata->device,
336 DI_DeviceObjectCallback,
339 DI_SetError(
"Getting device axes", ret);
344 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
347 DI_SetError(
"Resetting device", ret);
352 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
353 DISFFC_SETACTUATORSON);
355 DI_SetError(
"Enabling actuators", ret);
360 ret = IDirectInputDevice8_EnumEffects(
haptic->hwdata->device,
361 DI_EffectCallback,
haptic,
364 DI_SetError(
"Enumerating supported effects", ret);
367 if (
haptic->supported == 0) {
368 SDL_SetError(
"Haptic: Internal error on finding supported effects.");
373 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
374 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
375 dipdw.diph.dwObj = 0;
376 dipdw.diph.dwHow = DIPH_DEVICE;
377 dipdw.dwData = 10000;
378 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
379 DIPROP_FFGAIN, &dipdw.diph);
383 dipdw.diph.dwObj = 0;
384 dipdw.diph.dwHow = DIPH_DEVICE;
385 dipdw.dwData = DIPROPAUTOCENTER_OFF;
386 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
387 DIPROP_AUTOCENTER, &dipdw.diph);
417 IDirectInputDevice8_Unacquire(
haptic->hwdata->device);
425 LPDIRECTINPUTDEVICE8
device;
426 LPDIRECTINPUTDEVICE8 device8;
429 ret = IDirectInput8_CreateDevice(dinput, &item->instance.guidInstance,
432 DI_SetError(
"Creating DirectInput device", ret);
437 ret = IDirectInputDevice8_QueryInterface(
device,
438 &IID_IDirectInputDevice8,
441 IDirectInputDevice8_Release(
device);
443 DI_SetError(
"Querying DirectInput interface", ret);
448 IDirectInputDevice8_Release(device8);
464 ret = IDirectInputDevice8_GetDeviceInfo(
haptic->hwdata->device,
469 ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
475 return WIN_IsEqualGUID(&hap_instance.guidInstance, &joy_instance.guidInstance);
487 ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance);
496 return SDL_DINPUT_HapticOpenFromDevice(
haptic, joystick->hwdata->InputDevice,
SDL_TRUE);
501 SDL_SetError(
"Couldn't find joystick in haptic device list");
508 IDirectInputDevice8_Unacquire(
haptic->hwdata->device);
511 if (
haptic->hwdata->is_joystick == 0) {
512 IDirectInputDevice8_Release(
haptic->hwdata->device);
519 if (dinput !=
NULL) {
520 IDirectInput8_Release(dinput);
536 DWORD dwTriggerButton;
538 dwTriggerButton = DIEB_NOTRIGGER;
541 dwTriggerButton = DIJOFS_BUTTON(
button - 1);
544 return dwTriggerButton;
558 effect->dwFlags |= DIEFF_SPHERICAL;
565 if (rglDir ==
NULL) {
569 effect->rglDirection = rglDir;
573 effect->dwFlags |= DIEFF_POLAR;
574 rglDir[0] = dir->
dir[0];
577 effect->dwFlags |= DIEFF_CARTESIAN;
578 rglDir[0] = dir->
dir[0];
580 rglDir[1] = dir->
dir[1];
582 rglDir[2] = dir->
dir[2];
585 effect->dwFlags |= DIEFF_SPHERICAL;
586 rglDir[0] = dir->
dir[0];
588 rglDir[1] = dir->
dir[1];
590 rglDir[2] = dir->
dir[2];
599#define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
601#define CONVERT(x) (((x)*10000) / 0x7FFF)
606SDL_SYS_ToDIEFFECT(SDL_Haptic *
haptic, DIEFFECT * dest,
610 DICONSTANTFORCE *constant;
611 DIPERIODIC *periodic;
614 DICUSTOMFORCE *custom;
615 DIENVELOPE *envelope;
625 dest->dwSize =
sizeof(DIEFFECT);
626 dest->dwSamplePeriod = 0;
627 dest->dwGain = 10000;
628 dest->dwFlags = DIEFF_OBJECTOFFSETS;
632 if (envelope ==
NULL) {
636 dest->lpEnvelope = envelope;
637 envelope->dwSize =
sizeof(DIENVELOPE);
640 dest->cAxes =
haptic->naxes;
641 if (dest->cAxes > 0) {
642 axes =
SDL_malloc(
sizeof(DWORD) * dest->cAxes);
646 axes[0] =
haptic->hwdata->axes[0];
647 if (dest->cAxes > 1) {
648 axes[1] =
haptic->hwdata->axes[1];
650 if (dest->cAxes > 2) {
651 axes[2] =
haptic->hwdata->axes[2];
653 dest->rgdwAxes = axes;
659 hap_constant = &
src->constant;
660 constant =
SDL_malloc(
sizeof(DICONSTANTFORCE));
661 if (constant ==
NULL) {
664 SDL_memset(constant, 0,
sizeof(DICONSTANTFORCE));
667 constant->lMagnitude = CONVERT(hap_constant->
level);
668 dest->cbTypeSpecificParams =
sizeof(DICONSTANTFORCE);
669 dest->lpvTypeSpecificParams = constant;
672 dest->dwDuration = hap_constant->
length * 1000;
673 dest->dwTriggerButton = DIGetTriggerButton(hap_constant->
button);
674 dest->dwTriggerRepeatInterval = hap_constant->
interval;
675 dest->dwStartDelay = hap_constant->
delay * 1000;
678 if (SDL_SYS_SetDirection(dest, &hap_constant->
direction, dest->cAxes) < 0) {
686 dest->lpEnvelope =
NULL;
688 envelope->dwAttackLevel = CCONVERT(hap_constant->
attack_level);
690 envelope->dwFadeLevel = CCONVERT(hap_constant->
fade_level);
691 envelope->dwFadeTime = hap_constant->
fade_length * 1000;
702 hap_periodic = &
src->periodic;
704 if (periodic ==
NULL) {
711 periodic->lOffset = CONVERT(hap_periodic->
offset);
713 (hap_periodic->
phase + (hap_periodic->
magnitude < 0 ? 18000 : 0)) % 36000;
714 periodic->dwPeriod = hap_periodic->
period * 1000;
715 dest->cbTypeSpecificParams =
sizeof(DIPERIODIC);
716 dest->lpvTypeSpecificParams = periodic;
719 dest->dwDuration = hap_periodic->
length * 1000;
720 dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->
button);
721 dest->dwTriggerRepeatInterval = hap_periodic->
interval;
722 dest->dwStartDelay = hap_periodic->
delay * 1000;
725 if (SDL_SYS_SetDirection(dest, &hap_periodic->
direction, dest->cAxes)
734 dest->lpEnvelope =
NULL;
736 envelope->dwAttackLevel = CCONVERT(hap_periodic->
attack_level);
738 envelope->dwFadeLevel = CCONVERT(hap_periodic->
fade_level);
739 envelope->dwFadeTime = hap_periodic->
fade_length * 1000;
748 hap_condition = &
src->condition;
756 for (
i = 0;
i < (int) dest->cAxes;
i++) {
765 CCONVERT(hap_condition->
left_sat[
i] / 2);
768 dest->cbTypeSpecificParams =
sizeof(DICONDITION) * dest->cAxes;
772 dest->dwDuration = hap_condition->
length * 1000;
773 dest->dwTriggerButton = DIGetTriggerButton(hap_condition->
button);
774 dest->dwTriggerRepeatInterval = hap_condition->
interval;
775 dest->dwStartDelay = hap_condition->
delay * 1000;
778 if (SDL_SYS_SetDirection(dest, &hap_condition->
direction, dest->cAxes)
785 dest->lpEnvelope =
NULL;
790 hap_ramp = &
src->ramp;
798 ramp->lStart = CONVERT(hap_ramp->
start);
799 ramp->lEnd = CONVERT(hap_ramp->
end);
800 dest->cbTypeSpecificParams =
sizeof(DIRAMPFORCE);
801 dest->lpvTypeSpecificParams = ramp;
804 dest->dwDuration = hap_ramp->
length * 1000;
805 dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->
button);
806 dest->dwTriggerRepeatInterval = hap_ramp->
interval;
807 dest->dwStartDelay = hap_ramp->
delay * 1000;
810 if (SDL_SYS_SetDirection(dest, &hap_ramp->
direction, dest->cAxes) < 0) {
817 dest->lpEnvelope =
NULL;
819 envelope->dwAttackLevel = CCONVERT(hap_ramp->
attack_level);
821 envelope->dwFadeLevel = CCONVERT(hap_ramp->
fade_level);
822 envelope->dwFadeTime = hap_ramp->
fade_length * 1000;
828 hap_custom = &
src->custom;
830 if (custom ==
NULL) {
836 custom->cChannels = hap_custom->
channels;
837 custom->dwSamplePeriod = hap_custom->
period * 1000;
838 custom->cSamples = hap_custom->
samples;
839 custom->rglForceData =
840 SDL_malloc(
sizeof(LONG) * custom->cSamples * custom->cChannels);
842 custom->rglForceData[
i] = CCONVERT(hap_custom->
data[
i]);
844 dest->cbTypeSpecificParams =
sizeof(DICUSTOMFORCE);
845 dest->lpvTypeSpecificParams = custom;
848 dest->dwDuration = hap_custom->
length * 1000;
849 dest->dwTriggerButton = DIGetTriggerButton(hap_custom->
button);
850 dest->dwTriggerRepeatInterval = hap_custom->
interval;
851 dest->dwStartDelay = hap_custom->
delay * 1000;
854 if (SDL_SYS_SetDirection(dest, &hap_custom->
direction, dest->cAxes) < 0) {
862 dest->lpEnvelope =
NULL;
864 envelope->dwAttackLevel = CCONVERT(hap_custom->
attack_level);
866 envelope->dwFadeLevel = CCONVERT(hap_custom->
fade_level);
867 envelope->dwFadeTime = hap_custom->
fade_length * 1000;
884SDL_SYS_HapticFreeDIEFFECT(DIEFFECT *
effect,
int type)
886 DICUSTOMFORCE *custom;
894 custom = (DICUSTOMFORCE *)
effect->lpvTypeSpecificParams;
896 custom->rglForceData =
NULL;
913 return &GUID_ConstantForce;
916 return &GUID_RampForce;
926 return &GUID_Triangle;
929 return &GUID_SawtoothUp;
932 return &GUID_SawtoothDown;
941 return &GUID_Inertia;
944 return &GUID_Friction;
947 return &GUID_CustomForce;
957 REFGUID
type = SDL_SYS_HapticEffectType(base);
965 if (SDL_SYS_ToDIEFFECT(
haptic, &
effect->hweffect->effect, base) < 0) {
970 ret = IDirectInputDevice8_CreateEffect(
haptic->hwdata->device,
type,
971 &
effect->hweffect->effect,
974 DI_SetError(
"Unable to create effect", ret);
981 SDL_SYS_HapticFreeDIEFFECT(&
effect->hweffect->effect, base->
type);
994 if (SDL_SYS_ToDIEFFECT(
haptic, &temp,
data) < 0) {
1000 flags = DIEP_DIRECTION |
1004 DIEP_TRIGGERBUTTON |
1005 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
1009 IDirectInputEffect_SetParameters(
effect->hweffect->ref, &temp,
flags);
1010 if (ret == DIERR_NOTEXCLUSIVEACQUIRED) {
1011 IDirectInputDevice8_Unacquire(
haptic->hwdata->device);
1012 ret = IDirectInputDevice8_SetCooperativeLevel(
haptic->hwdata->device, SDL_HelperWindow, DISCL_EXCLUSIVE | DISCL_BACKGROUND);
1014 ret = DIERR_NOTACQUIRED;
1017 if (ret == DIERR_INPUTLOST || ret == DIERR_NOTACQUIRED) {
1018 ret = IDirectInputDevice8_Acquire(
haptic->hwdata->device);
1020 ret = IDirectInputEffect_SetParameters(
effect->hweffect->ref, &temp,
flags);
1024 DI_SetError(
"Unable to update effect", ret);
1029 SDL_SYS_HapticFreeDIEFFECT(&
effect->hweffect->effect,
data->
type);
1035 SDL_SYS_HapticFreeDIEFFECT(&temp,
data->type);
1053 ret = IDirectInputEffect_Start(
effect->hweffect->ref, iter, 0);
1055 return DI_SetError(
"Running the effect", ret);
1065 ret = IDirectInputEffect_Stop(
effect->hweffect->ref);
1067 return DI_SetError(
"Unable to stop effect", ret);
1077 ret = IDirectInputEffect_Unload(
effect->hweffect->ref);
1079 DI_SetError(
"Removing effect from the device", ret);
1081 SDL_SYS_HapticFreeDIEFFECT(&
effect->hweffect->effect,
effect->effect.
type);
1090 ret = IDirectInputEffect_GetEffectStatus(
effect->hweffect->ref, &status);
1092 return DI_SetError(
"Getting effect status", ret);
1107 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
1108 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
1109 dipdw.diph.dwObj = 0;
1110 dipdw.diph.dwHow = DIPH_DEVICE;
1111 dipdw.dwData = gain * 100;
1114 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
1115 DIPROP_FFGAIN, &dipdw.diph);
1117 return DI_SetError(
"Setting gain", ret);
1129 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
1130 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
1131 dipdw.diph.dwObj = 0;
1132 dipdw.diph.dwHow = DIPH_DEVICE;
1133 dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
1134 DIPROPAUTOCENTER_ON;
1137 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
1138 DIPROP_AUTOCENTER, &dipdw.diph);
1140 return DI_SetError(
"Setting autocenter", ret);
1151 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
1154 return DI_SetError(
"Pausing the device", ret);
1165 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
1168 return DI_SetError(
"Pausing the device", ret);
1179 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
1182 return DI_SetError(
"Stopping the device", ret);
#define DIRECTINPUT_VERSION
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
#define SDL_OutOfMemory()
#define SDL_Unsupported()
The SDL haptic subsystem allows you to control haptic (force feedback) devices.
#define SDL_HAPTIC_INERTIA
Inertia effect supported - uses axes acceleration.
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
#define SDL_HAPTIC_GAIN
Device can set global gain.
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
#define SDL_HAPTIC_DAMPER
Damper effect supported - uses axes velocity.
#define SDL_HAPTIC_PAUSE
Device can be paused.
#define SDL_HAPTIC_CUSTOM
Custom effect is supported.
#define SDL_HAPTIC_CONSTANT
Constant effect supported.
#define SDL_HAPTIC_FRICTION
Friction effect supported - uses axes movement.
#define SDL_HAPTIC_SINE
Sine wave effect supported.
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
#define SDL_HAPTIC_STATUS
Device can be queried for effect status.
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
#define SDL_HAPTIC_TRIANGLE
Triangle wave effect supported.
#define SDL_HAPTIC_RAMP
Ramp effect supported.
#define SDL_HAPTIC_CARTESIAN
Uses cartesian coordinates for the direction.
#define SDL_HAPTIC_SAWTOOTHDOWN
Sawtoothdown wave effect supported.
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
GLuint GLuint GLsizei GLenum type
void SDL_SYS_HapticQuit(void)
HRESULT WIN_CoInitialize(void)
void WIN_CoUninitialize(void)
#define WIN_StringToUTF8(S)
BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b)
int SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item)
SDL_hapticlist_item * SDL_hapticlist
int SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
static SDL_AudioDeviceID device
A structure containing a template for a Condition effect.
SDL_HapticDirection direction
A structure containing a template for a Constant effect.
SDL_HapticDirection direction
A structure containing a template for the SDL_HAPTIC_CUSTOM effect.
SDL_HapticDirection direction
Structure that represents a haptic direction.
A structure containing a template for a Periodic effect.
SDL_HapticDirection direction
A structure containing a template for a Ramp effect.
SDL_HapticDirection direction
struct SDL_hapticlist_item * next
static SDL_Haptic * haptic
The generic template for any haptic effect.