21 #include "../../SDL_internal.h" 25 #include "../SDL_syshaptic.h" 33 #include "../../joystick/windows/SDL_windowsjoystick_c.h" 38 extern HWND SDL_HelperWindow;
45 static LPDIRECTINPUT8 dinput =
NULL;
52 DI_SetError(
const char *str, HRESULT err)
65 DI_GUIDIsSame(
const GUID *
a,
const GUID *
b)
78 return DIENUM_CONTINUE;
93 return DI_SetError(
"Coinitialize", ret);
98 ret = CoCreateInstance(&CLSID_DirectInput8,
NULL, CLSCTX_INPROC_SERVER,
99 &IID_IDirectInput8, (LPVOID)& dinput);
102 return DI_SetError(
"CoCreateInstance", ret);
106 instance = GetModuleHandle(
NULL);
107 if (instance ==
NULL) {
109 return SDL_SetError(
"GetModuleHandle() failed with error code %lu.",
115 return DI_SetError(
"Initializing DirectInput device", ret);
119 ret = IDirectInput8_EnumDevices(dinput,
123 DIEDFL_FORCEFEEDBACK |
124 DIEDFL_ATTACHEDONLY);
127 return DI_SetError(
"Enumerating DirectInput devices", ret);
136 LPDIRECTINPUTDEVICE8 device;
137 const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK;
138 DIDEVCAPS capabilities;
141 if (dinput ==
NULL) {
153 ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &device,
NULL);
161 capabilities.dwSize =
sizeof(DIDEVCAPS);
162 ret = IDirectInputDevice8_GetCapabilities(device, &capabilities);
163 IDirectInputDevice8_Release(device);
169 if ((capabilities.dwFlags & needflags) != needflags) {
186 SDL_memcpy(&item->capabilities, &capabilities,
sizeof(capabilities));
197 if (dinput ==
NULL) {
215 DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
217 SDL_Haptic *
haptic = (SDL_Haptic *) pvRef;
219 if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
220 const GUID *guid = &dev->guidType;
222 if (DI_GUIDIsSame(guid, &GUID_XAxis)) {
224 }
else if (DI_GUIDIsSame(guid, &GUID_YAxis)) {
226 }
else if (DI_GUIDIsSame(guid, &GUID_ZAxis)) {
228 }
else if (DI_GUIDIsSame(guid, &GUID_RxAxis)) {
230 }
else if (DI_GUIDIsSame(guid, &GUID_RyAxis)) {
232 }
else if (DI_GUIDIsSame(guid, &GUID_RzAxis)) {
235 return DIENUM_CONTINUE;
238 haptic->hwdata->axes[haptic->naxes] =
offset;
242 if (haptic->naxes >= 3) {
247 return DIENUM_CONTINUE;
253 #define EFFECT_TEST(e,s) \ 254 if (DI_GUIDIsSame(&pei->guid, &(e))) \ 255 haptic->supported |= (s) 257 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
260 SDL_Haptic *haptic = (SDL_Haptic *) pv;
278 return DIENUM_CONTINUE;
292 SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic * haptic, LPDIRECTINPUTDEVICE8 device8,
SDL_bool is_joystick)
299 if (haptic->hwdata ==
NULL) {
302 SDL_memset(haptic->hwdata, 0,
sizeof(*haptic->hwdata));
305 haptic->hwdata->device = device8;
317 ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device,
322 DI_SetError(
"Setting cooperative level to exclusive", ret);
327 ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device,
330 DI_SetError(
"Setting data format", ret);
335 ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device,
336 DI_DeviceObjectCallback,
339 DI_SetError(
"Getting device axes", ret);
344 ret = IDirectInputDevice8_Acquire(haptic->hwdata->device);
346 DI_SetError(
"Acquiring DirectInput device", ret);
352 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
355 DI_SetError(
"Resetting device", ret);
360 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
361 DISFFC_SETACTUATORSON);
363 DI_SetError(
"Enabling actuators", ret);
368 ret = IDirectInputDevice8_EnumEffects(haptic->hwdata->device,
369 DI_EffectCallback, haptic,
372 DI_SetError(
"Enumerating supported effects", ret);
375 if (haptic->supported == 0) {
376 SDL_SetError(
"Haptic: Internal error on finding supported effects.");
381 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
382 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
383 dipdw.diph.dwObj = 0;
384 dipdw.diph.dwHow = DIPH_DEVICE;
385 dipdw.dwData = 10000;
386 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
387 DIPROP_FFGAIN, &dipdw.diph);
391 dipdw.diph.dwObj = 0;
392 dipdw.diph.dwHow = DIPH_DEVICE;
393 dipdw.dwData = DIPROPAUTOCENTER_OFF;
394 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
395 DIPROP_AUTOCENTER, &dipdw.diph);
404 haptic->neffects = 128;
408 haptic->nplaying = 128;
413 if (haptic->effects ==
NULL) {
425 IDirectInputDevice8_Unacquire(haptic->hwdata->device);
433 LPDIRECTINPUTDEVICE8 device;
434 LPDIRECTINPUTDEVICE8 device8;
437 ret = IDirectInput8_CreateDevice(dinput, &item->instance.guidInstance,
440 DI_SetError(
"Creating DirectInput device", ret);
445 ret = IDirectInputDevice8_QueryInterface(device,
446 &IID_IDirectInputDevice8,
449 IDirectInputDevice8_Release(device);
451 DI_SetError(
"Querying DirectInput interface", ret);
455 if (SDL_DINPUT_HapticOpenFromDevice(haptic, device8,
SDL_FALSE) < 0) {
456 IDirectInputDevice8_Release(device8);
472 ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
477 ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
483 return DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance);
495 ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance);
502 if (!item->
bXInputHaptic && DI_GUIDIsSame(&item->instance.guidInstance, &joy_instance.guidInstance)) {
503 haptic->index =
index;
504 return SDL_DINPUT_HapticOpenFromDevice(haptic, joystick->hwdata->InputDevice,
SDL_TRUE);
509 SDL_SetError(
"Couldn't find joystick in haptic device list");
516 IDirectInputDevice8_Unacquire(haptic->hwdata->device);
519 if (haptic->hwdata->is_joystick == 0) {
520 IDirectInputDevice8_Release(haptic->hwdata->device);
527 if (dinput !=
NULL) {
528 IDirectInput8_Release(dinput);
544 DWORD dwTriggerButton;
546 dwTriggerButton = DIEB_NOTRIGGER;
549 dwTriggerButton = DIJOFS_BUTTON(button - 1);
552 return dwTriggerButton;
566 effect->dwFlags |= DIEFF_SPHERICAL;
567 effect->rglDirection =
NULL;
573 if (rglDir ==
NULL) {
577 effect->rglDirection = rglDir;
581 effect->dwFlags |= DIEFF_POLAR;
582 rglDir[0] = dir->
dir[0];
585 effect->dwFlags |= DIEFF_CARTESIAN;
586 rglDir[0] = dir->
dir[0];
588 rglDir[1] = dir->
dir[1];
590 rglDir[2] = dir->
dir[2];
593 effect->dwFlags |= DIEFF_SPHERICAL;
594 rglDir[0] = dir->
dir[0];
596 rglDir[1] = dir->
dir[1];
598 rglDir[2] = dir->
dir[2];
607 #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) 609 #define CONVERT(x) (((x)*10000) / 0x7FFF) 614 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
618 DICONSTANTFORCE *constant;
619 DIPERIODIC *periodic;
622 DICUSTOMFORCE *custom;
623 DIENVELOPE *envelope;
633 dest->dwSize =
sizeof(DIEFFECT);
634 dest->dwSamplePeriod = 0;
635 dest->dwGain = 10000;
636 dest->dwFlags = DIEFF_OBJECTOFFSETS;
640 if (envelope ==
NULL) {
644 dest->lpEnvelope = envelope;
645 envelope->dwSize =
sizeof(DIENVELOPE);
648 dest->cAxes = haptic->naxes;
649 if (dest->cAxes > 0) {
650 axes =
SDL_malloc(
sizeof(DWORD) * dest->cAxes);
654 axes[0] = haptic->hwdata->axes[0];
655 if (dest->cAxes > 1) {
656 axes[1] = haptic->hwdata->axes[1];
658 if (dest->cAxes > 2) {
659 axes[2] = haptic->hwdata->axes[2];
661 dest->rgdwAxes = axes;
668 constant =
SDL_malloc(
sizeof(DICONSTANTFORCE));
669 if (constant ==
NULL) {
672 SDL_memset(constant, 0,
sizeof(DICONSTANTFORCE));
675 constant->lMagnitude = CONVERT(hap_constant->
level);
676 dest->cbTypeSpecificParams =
sizeof(DICONSTANTFORCE);
677 dest->lpvTypeSpecificParams = constant;
680 dest->dwDuration = hap_constant->
length * 1000;
681 dest->dwTriggerButton = DIGetTriggerButton(hap_constant->
button);
682 dest->dwTriggerRepeatInterval = hap_constant->
interval;
683 dest->dwStartDelay = hap_constant->
delay * 1000;
686 if (SDL_SYS_SetDirection(dest, &hap_constant->
direction, dest->cAxes) < 0) {
694 dest->lpEnvelope =
NULL;
696 envelope->dwAttackLevel = CCONVERT(hap_constant->
attack_level);
698 envelope->dwFadeLevel = CCONVERT(hap_constant->
fade_level);
699 envelope->dwFadeTime = hap_constant->
fade_length * 1000;
712 if (periodic ==
NULL) {
719 periodic->lOffset = CONVERT(hap_periodic->
offset);
721 (hap_periodic->
phase + (hap_periodic->
magnitude < 0 ? 18000 : 0)) % 36000;
722 periodic->dwPeriod = hap_periodic->
period * 1000;
723 dest->cbTypeSpecificParams =
sizeof(DIPERIODIC);
724 dest->lpvTypeSpecificParams = periodic;
727 dest->dwDuration = hap_periodic->
length * 1000;
728 dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->
button);
729 dest->dwTriggerRepeatInterval = hap_periodic->
interval;
730 dest->dwStartDelay = hap_periodic->
delay * 1000;
733 if (SDL_SYS_SetDirection(dest, &hap_periodic->
direction, dest->cAxes)
742 dest->lpEnvelope =
NULL;
744 envelope->dwAttackLevel = CCONVERT(hap_periodic->
attack_level);
746 envelope->dwFadeLevel = CCONVERT(hap_periodic->
fade_level);
747 envelope->dwFadeTime = hap_periodic->
fade_length * 1000;
757 condition =
SDL_malloc(
sizeof(DICONDITION) * dest->cAxes);
758 if (condition ==
NULL) {
761 SDL_memset(condition, 0,
sizeof(DICONDITION));
764 for (i = 0; i < (int) dest->cAxes; i++) {
765 condition[
i].lOffset = CONVERT(hap_condition->
center[i]);
766 condition[
i].lPositiveCoefficient =
768 condition[
i].lNegativeCoefficient =
770 condition[
i].dwPositiveSaturation =
771 CCONVERT(hap_condition->
right_sat[i] / 2);
772 condition[
i].dwNegativeSaturation =
773 CCONVERT(hap_condition->
left_sat[i] / 2);
774 condition[
i].lDeadBand = CCONVERT(hap_condition->
deadband[i] / 2);
776 dest->cbTypeSpecificParams =
sizeof(DICONDITION) * dest->cAxes;
777 dest->lpvTypeSpecificParams = condition;
780 dest->dwDuration = hap_condition->
length * 1000;
781 dest->dwTriggerButton = DIGetTriggerButton(hap_condition->
button);
782 dest->dwTriggerRepeatInterval = hap_condition->
interval;
783 dest->dwStartDelay = hap_condition->
delay * 1000;
786 if (SDL_SYS_SetDirection(dest, &hap_condition->
direction, dest->cAxes)
793 dest->lpEnvelope =
NULL;
798 hap_ramp = &src->
ramp;
806 ramp->lStart = CONVERT(hap_ramp->
start);
807 ramp->lEnd = CONVERT(hap_ramp->
end);
808 dest->cbTypeSpecificParams =
sizeof(DIRAMPFORCE);
809 dest->lpvTypeSpecificParams = ramp;
812 dest->dwDuration = hap_ramp->
length * 1000;
813 dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->
button);
814 dest->dwTriggerRepeatInterval = hap_ramp->
interval;
815 dest->dwStartDelay = hap_ramp->
delay * 1000;
818 if (SDL_SYS_SetDirection(dest, &hap_ramp->
direction, dest->cAxes) < 0) {
825 dest->lpEnvelope =
NULL;
827 envelope->dwAttackLevel = CCONVERT(hap_ramp->
attack_level);
829 envelope->dwFadeLevel = CCONVERT(hap_ramp->
fade_level);
830 envelope->dwFadeTime = hap_ramp->
fade_length * 1000;
836 hap_custom = &src->
custom;
838 if (custom ==
NULL) {
844 custom->cChannels = hap_custom->
channels;
845 custom->dwSamplePeriod = hap_custom->
period * 1000;
846 custom->cSamples = hap_custom->
samples;
847 custom->rglForceData =
848 SDL_malloc(
sizeof(LONG) * custom->cSamples * custom->cChannels);
850 custom->rglForceData[
i] = CCONVERT(hap_custom->
data[i]);
852 dest->cbTypeSpecificParams =
sizeof(DICUSTOMFORCE);
853 dest->lpvTypeSpecificParams = custom;
856 dest->dwDuration = hap_custom->
length * 1000;
857 dest->dwTriggerButton = DIGetTriggerButton(hap_custom->
button);
858 dest->dwTriggerRepeatInterval = hap_custom->
interval;
859 dest->dwStartDelay = hap_custom->
delay * 1000;
862 if (SDL_SYS_SetDirection(dest, &hap_custom->
direction, dest->cAxes) < 0) {
870 dest->lpEnvelope =
NULL;
872 envelope->dwAttackLevel = CCONVERT(hap_custom->
attack_level);
874 envelope->dwFadeLevel = CCONVERT(hap_custom->
fade_level);
875 envelope->dwFadeTime = hap_custom->
fade_length * 1000;
892 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect,
int type)
894 DICUSTOMFORCE *custom;
897 effect->lpEnvelope =
NULL;
899 effect->rgdwAxes =
NULL;
900 if (effect->lpvTypeSpecificParams !=
NULL) {
902 custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
904 custom->rglForceData =
NULL;
906 SDL_free(effect->lpvTypeSpecificParams);
907 effect->lpvTypeSpecificParams =
NULL;
910 effect->rglDirection =
NULL;
919 switch (effect->
type) {
921 return &GUID_ConstantForce;
924 return &GUID_RampForce;
934 return &GUID_Triangle;
937 return &GUID_SawtoothUp;
940 return &GUID_SawtoothDown;
949 return &GUID_Inertia;
952 return &GUID_Friction;
955 return &GUID_CustomForce;
965 REFGUID type = SDL_SYS_HapticEffectType(base);
973 if (SDL_SYS_ToDIEFFECT(haptic, &effect->
hweffect->effect, base) < 0) {
978 ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
982 DI_SetError(
"Unable to create effect", ret);
989 SDL_SYS_HapticFreeDIEFFECT(&effect->
hweffect->effect, base->
type);
1002 if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
1008 flags = DIEP_DIRECTION |
1012 DIEP_TRIGGERBUTTON |
1013 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
1017 IDirectInputEffect_SetParameters(effect->
hweffect->ref, &temp, flags);
1019 DI_SetError(
"Unable to update effect", ret);
1024 SDL_SYS_HapticFreeDIEFFECT(&effect->
hweffect->effect, data->
type);
1030 SDL_SYS_HapticFreeDIEFFECT(&temp, data->
type);
1048 ret = IDirectInputEffect_Start(effect->
hweffect->ref, iter, 0);
1050 return DI_SetError(
"Running the effect", ret);
1060 ret = IDirectInputEffect_Stop(effect->
hweffect->ref);
1062 return DI_SetError(
"Unable to stop effect", ret);
1072 ret = IDirectInputEffect_Unload(effect->
hweffect->ref);
1074 DI_SetError(
"Removing effect from the device", ret);
1085 ret = IDirectInputEffect_GetEffectStatus(effect->
hweffect->ref, &status);
1087 return DI_SetError(
"Getting effect status", ret);
1102 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
1103 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
1104 dipdw.diph.dwObj = 0;
1105 dipdw.diph.dwHow = DIPH_DEVICE;
1106 dipdw.dwData = gain * 100;
1109 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
1110 DIPROP_FFGAIN, &dipdw.diph);
1112 return DI_SetError(
"Setting gain", ret);
1124 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
1125 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
1126 dipdw.diph.dwObj = 0;
1127 dipdw.diph.dwHow = DIPH_DEVICE;
1128 dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
1129 DIPROPAUTOCENTER_ON;
1132 ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
1133 DIPROP_AUTOCENTER, &dipdw.diph);
1135 return DI_SetError(
"Setting autocenter", ret);
1146 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
1149 return DI_SetError(
"Pausing the device", ret);
1160 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
1163 return DI_SetError(
"Pausing the device", ret);
1174 ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
1177 return DI_SetError(
"Stopping the device", ret);
Structure that represents a haptic direction.
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
uint32_t Uint32
An unsigned 32-bit integer type.
A structure containing a template for a Periodic effect.
#define SDL_HAPTIC_GAIN
Device can set global gain.
#define DIRECTINPUT_VERSION
#define SDL_HAPTIC_CUSTOM
Custom effect is supported.
#define SDL_HAPTIC_TRIANGLE
Triangle wave effect supported.
SDL_HapticDirection direction
#define SDL_HAPTIC_INERTIA
Inertia effect supported - uses axes acceleration.
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
A structure containing a template for a Condition effect.
The SDL Haptic subsystem allows you to control haptic (force feedback) devices.
#define SDL_HAPTIC_SINE
Sine wave effect supported.
A structure containing a template for a Constant effect.
int SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item)
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
#define SDL_HAPTIC_CARTESIAN
Uses cartesian coordinates for the direction.
SDL_hapticlist_item * SDL_hapticlist
struct SDL_hapticlist_item * next
SDL_HapticCondition condition
GLuint GLuint GLsizei GLenum type
void * SDL_calloc(size_t nmemb, size_t size)
The generic template for any haptic effect.
#define SDL_HAPTIC_CONSTANT
Constant effect supported.
HRESULT WIN_CoInitialize(void)
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
#define WIN_StringToUTF8(S)
SDL_HapticConstant constant
#define SDL_HAPTIC_PAUSE
Device can be paused.
SDL_HapticDirection direction
A structure containing a template for a Ramp effect.
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)
#define SDL_OutOfMemory()
void WIN_CoUninitialize(void)
static SDL_Haptic * haptic
void SDL_SYS_HapticQuit(void)
struct haptic_hweffect * hweffect
#define SDL_HAPTIC_STATUS
Device can be queried for effect status.
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
SDL_HapticDirection direction
#define SDL_HAPTIC_RAMP
Ramp effect supported.
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
uint16_t Uint16
An unsigned 16-bit integer type.
A structure containing a template for the SDL_HAPTIC_CUSTOM effect.
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
GLboolean GLboolean GLboolean GLboolean a
SDL_HapticPeriodic periodic
GLboolean GLboolean GLboolean b
#define SDL_HAPTIC_FRICTION
Friction effect supported - uses axes movement.
#define SDL_Unsupported()
#define SDL_HAPTIC_SAWTOOTHDOWN
Sawtoothdown wave effect supported.
int SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item)
SDL_HapticDirection direction
SDL_HapticDirection direction
#define SDL_HAPTIC_DAMPER
Damper effect supported - uses axes velocity.