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)
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) {
206 DI_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;
229 haptic->hwdata->axes[haptic->naxes] =
offset;
233 if (haptic->naxes >= 3) {
238 return DIENUM_CONTINUE;
244 #define EFFECT_TEST(e,s) \ 245 if (WIN_IsEqualGUID(&pei->guid, &(e))) \ 246 haptic->supported |= (s) 248 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
251 SDL_Haptic *haptic = (SDL_Haptic *) pv;
269 return DIENUM_CONTINUE;
283 SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic * haptic, LPDIRECTINPUTDEVICE8 device8,
SDL_bool is_joystick)
290 if (haptic->hwdata ==
NULL) {
293 SDL_memset(haptic->hwdata, 0,
sizeof(*haptic->hwdata));
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);
396 haptic->neffects = 128;
400 haptic->nplaying = 128;
405 if (haptic->effects ==
NULL) {
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);
447 if (SDL_DINPUT_HapticOpenFromDevice(haptic, device8,
SDL_FALSE) < 0) {
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);
495 haptic->index =
index;
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;
559 effect->rglDirection =
NULL;
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) 606 SDL_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;
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;
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;
749 condition =
SDL_malloc(
sizeof(DICONDITION) * dest->cAxes);
750 if (condition ==
NULL) {
753 SDL_memset(condition, 0,
sizeof(DICONDITION));
756 for (i = 0; i < (int) dest->cAxes; i++) {
757 condition[
i].lOffset = CONVERT(hap_condition->
center[i]);
758 condition[
i].lPositiveCoefficient =
760 condition[
i].lNegativeCoefficient =
762 condition[
i].dwPositiveSaturation =
763 CCONVERT(hap_condition->
right_sat[i] / 2);
764 condition[
i].dwNegativeSaturation =
765 CCONVERT(hap_condition->
left_sat[i] / 2);
766 condition[
i].lDeadBand = CCONVERT(hap_condition->
deadband[i] / 2);
768 dest->cbTypeSpecificParams =
sizeof(DICONDITION) * dest->cAxes;
769 dest->lpvTypeSpecificParams = condition;
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;
884 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect,
int type)
886 DICUSTOMFORCE *custom;
889 effect->lpEnvelope =
NULL;
891 effect->rgdwAxes =
NULL;
892 if (effect->lpvTypeSpecificParams !=
NULL) {
894 custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
896 custom->rglForceData =
NULL;
898 SDL_free(effect->lpvTypeSpecificParams);
899 effect->lpvTypeSpecificParams =
NULL;
902 effect->rglDirection =
NULL;
911 switch (effect->
type) {
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,
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);
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);
Structure that represents a haptic direction.
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
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.
A structure containing a template for a Condition effect.
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
The SDL haptic subsystem allows you to control haptic (force feedback) devices.
BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b)
#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
static SDL_AudioDeviceID device
SDL_HapticCondition condition
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_HapticDirection direction
#define SDL_HAPTIC_RAMP
Ramp effect supported.
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
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
GLuint GLuint GLsizei GLenum type
A structure containing a template for the SDL_HAPTIC_CUSTOM effect.
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
SDL_HapticPeriodic periodic
#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.