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 return DIENUM_CONTINUE;
80 return DI_SetError(
"Coinitialize", ret);
85 ret = CoCreateInstance(&CLSID_DirectInput8,
NULL, CLSCTX_INPROC_SERVER,
86 &IID_IDirectInput8, (LPVOID *) &dinput);
89 return DI_SetError(
"CoCreateInstance", ret);
93 instance = GetModuleHandle(
NULL);
94 if (instance ==
NULL) {
96 return SDL_SetError(
"GetModuleHandle() failed with error code %lu.",
102 return DI_SetError(
"Initializing DirectInput device", ret);
106 ret = IDirectInput8_EnumDevices(dinput,
110 DIEDFL_FORCEFEEDBACK |
111 DIEDFL_ATTACHEDONLY);
114 return DI_SetError(
"Enumerating DirectInput devices", ret);
123 LPDIRECTINPUTDEVICE8
device;
124 const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK;
125 DIDEVCAPS capabilities;
128 if (dinput ==
NULL) {
140 ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &
device,
NULL);
148 capabilities.dwSize =
sizeof(DIDEVCAPS);
149 ret = IDirectInputDevice8_GetCapabilities(
device, &capabilities);
150 IDirectInputDevice8_Release(
device);
156 if ((capabilities.dwFlags & needflags) != needflags) {
173 SDL_memcpy(&item->capabilities, &capabilities,
sizeof(capabilities));
184 if (dinput ==
NULL) {
202 DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
204 SDL_Haptic *
haptic = (SDL_Haptic *) pvRef;
206 if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
207 const GUID *guid = &dev->guidType;
222 return DIENUM_CONTINUE;
234 return DIENUM_CONTINUE;
240 #define EFFECT_TEST(e,s) \
241 if (WIN_IsEqualGUID(&pei->guid, &(e))) \
242 haptic->supported |= (s)
244 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
247 SDL_Haptic *
haptic = (SDL_Haptic *) pv;
265 return DIENUM_CONTINUE;
279 SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic *
haptic, LPDIRECTINPUTDEVICE8 device8,
SDL_bool is_joystick)
292 haptic->hwdata->device = device8;
304 ret = IDirectInputDevice8_SetCooperativeLevel(
haptic->hwdata->device,
309 DI_SetError(
"Setting cooperative level to exclusive", ret);
314 ret = IDirectInputDevice8_SetDataFormat(
haptic->hwdata->device,
315 &SDL_c_dfDIJoystick2);
317 DI_SetError(
"Setting data format", ret);
323 ret = IDirectInputDevice8_Acquire(
haptic->hwdata->device);
325 DI_SetError(
"Acquiring DirectInput device", ret);
331 ret = IDirectInputDevice8_EnumObjects(
haptic->hwdata->device,
332 DI_DeviceObjectCallback,
335 DI_SetError(
"Getting device axes", ret);
340 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
343 DI_SetError(
"Resetting device", ret);
348 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
349 DISFFC_SETACTUATORSON);
351 DI_SetError(
"Enabling actuators", ret);
356 ret = IDirectInputDevice8_EnumEffects(
haptic->hwdata->device,
357 DI_EffectCallback,
haptic,
360 DI_SetError(
"Enumerating supported effects", ret);
363 if (
haptic->supported == 0) {
364 SDL_SetError(
"Haptic: Internal error on finding supported effects.");
369 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
370 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
371 dipdw.diph.dwObj = 0;
372 dipdw.diph.dwHow = DIPH_DEVICE;
373 dipdw.dwData = 10000;
374 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
375 DIPROP_FFGAIN, &dipdw.diph);
379 dipdw.diph.dwObj = 0;
380 dipdw.diph.dwHow = DIPH_DEVICE;
381 dipdw.dwData = DIPROPAUTOCENTER_OFF;
382 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
383 DIPROP_AUTOCENTER, &dipdw.diph);
413 IDirectInputDevice8_Unacquire(
haptic->hwdata->device);
421 LPDIRECTINPUTDEVICE8
device;
422 LPDIRECTINPUTDEVICE8 device8;
425 ret = IDirectInput8_CreateDevice(dinput, &item->instance.guidInstance,
428 DI_SetError(
"Creating DirectInput device", ret);
433 ret = IDirectInputDevice8_QueryInterface(
device,
434 &IID_IDirectInputDevice8,
437 IDirectInputDevice8_Release(
device);
439 DI_SetError(
"Querying DirectInput interface", ret);
444 IDirectInputDevice8_Release(device8);
460 ret = IDirectInputDevice8_GetDeviceInfo(
haptic->hwdata->device,
465 ret = IDirectInputDevice8_GetDeviceInfo(
joystick->hwdata->InputDevice,
471 return WIN_IsEqualGUID(&hap_instance.guidInstance, &joy_instance.guidInstance);
483 ret = IDirectInputDevice8_GetDeviceInfo(
joystick->hwdata->InputDevice, &joy_instance);
497 SDL_SetError(
"Couldn't find joystick in haptic device list");
504 IDirectInputDevice8_Unacquire(
haptic->hwdata->device);
507 if (
haptic->hwdata->is_joystick == 0) {
508 IDirectInputDevice8_Release(
haptic->hwdata->device);
515 if (dinput !=
NULL) {
516 IDirectInput8_Release(dinput);
532 DWORD dwTriggerButton;
534 dwTriggerButton = DIEB_NOTRIGGER;
537 dwTriggerButton = DIJOFS_BUTTON(
button - 1);
540 return dwTriggerButton;
554 effect->dwFlags |= DIEFF_SPHERICAL;
561 if (rglDir ==
NULL) {
565 effect->rglDirection = rglDir;
569 effect->dwFlags |= DIEFF_POLAR;
570 rglDir[0] = dir->
dir[0];
573 effect->dwFlags |= DIEFF_CARTESIAN;
574 rglDir[0] = dir->
dir[0];
576 rglDir[1] = dir->
dir[1];
578 rglDir[2] = dir->
dir[2];
581 effect->dwFlags |= DIEFF_SPHERICAL;
582 rglDir[0] = dir->
dir[0];
584 rglDir[1] = dir->
dir[1];
586 rglDir[2] = dir->
dir[2];
589 effect->dwFlags |= DIEFF_CARTESIAN;
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);
643 dest->cAxes =
haptic->naxes;
645 if (dest->cAxes > 0) {
646 axes =
SDL_malloc(
sizeof(DWORD) * dest->cAxes);
650 axes[0] =
haptic->hwdata->axes[0];
651 if (dest->cAxes > 1) {
652 axes[1] =
haptic->hwdata->axes[1];
654 if (dest->cAxes > 2) {
655 axes[2] =
haptic->hwdata->axes[2];
657 dest->rgdwAxes = axes;
663 hap_constant = &
src->constant;
664 constant =
SDL_malloc(
sizeof(DICONSTANTFORCE));
665 if (constant ==
NULL) {
668 SDL_memset(constant, 0,
sizeof(DICONSTANTFORCE));
671 constant->lMagnitude = CONVERT(hap_constant->
level);
672 dest->cbTypeSpecificParams =
sizeof(DICONSTANTFORCE);
673 dest->lpvTypeSpecificParams = constant;
676 dest->dwDuration = hap_constant->
length * 1000;
677 dest->dwTriggerButton = DIGetTriggerButton(hap_constant->
button);
678 dest->dwTriggerRepeatInterval = hap_constant->
interval;
679 dest->dwStartDelay = hap_constant->
delay * 1000;
682 if (SDL_SYS_SetDirection(dest, &hap_constant->
direction, dest->cAxes) < 0) {
690 dest->lpEnvelope =
NULL;
692 envelope->dwAttackLevel = CCONVERT(hap_constant->
attack_level);
694 envelope->dwFadeLevel = CCONVERT(hap_constant->
fade_level);
695 envelope->dwFadeTime = hap_constant->
fade_length * 1000;
706 hap_periodic = &
src->periodic;
708 if (periodic ==
NULL) {
715 periodic->lOffset = CONVERT(hap_periodic->
offset);
717 (hap_periodic->
phase + (hap_periodic->
magnitude < 0 ? 18000 : 0)) % 36000;
718 periodic->dwPeriod = hap_periodic->
period * 1000;
719 dest->cbTypeSpecificParams =
sizeof(DIPERIODIC);
720 dest->lpvTypeSpecificParams = periodic;
723 dest->dwDuration = hap_periodic->
length * 1000;
724 dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->
button);
725 dest->dwTriggerRepeatInterval = hap_periodic->
interval;
726 dest->dwStartDelay = hap_periodic->
delay * 1000;
729 if (SDL_SYS_SetDirection(dest, &hap_periodic->
direction, dest->cAxes)
738 dest->lpEnvelope =
NULL;
740 envelope->dwAttackLevel = CCONVERT(hap_periodic->
attack_level);
742 envelope->dwFadeLevel = CCONVERT(hap_periodic->
fade_level);
743 envelope->dwFadeTime = hap_periodic->
fade_length * 1000;
752 hap_condition = &
src->condition;
760 for (
i = 0;
i < (
int) dest->cAxes;
i++) {
769 CCONVERT(hap_condition->
left_sat[
i] / 2);
772 dest->cbTypeSpecificParams =
sizeof(DICONDITION) * dest->cAxes;
776 dest->dwDuration = hap_condition->
length * 1000;
777 dest->dwTriggerButton = DIGetTriggerButton(hap_condition->
button);
778 dest->dwTriggerRepeatInterval = hap_condition->
interval;
779 dest->dwStartDelay = hap_condition->
delay * 1000;
782 if (SDL_SYS_SetDirection(dest, &hap_condition->
direction, dest->cAxes)
789 dest->lpEnvelope =
NULL;
794 hap_ramp = &
src->ramp;
802 ramp->lStart = CONVERT(hap_ramp->
start);
803 ramp->lEnd = CONVERT(hap_ramp->
end);
804 dest->cbTypeSpecificParams =
sizeof(DIRAMPFORCE);
805 dest->lpvTypeSpecificParams = ramp;
808 dest->dwDuration = hap_ramp->
length * 1000;
809 dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->
button);
810 dest->dwTriggerRepeatInterval = hap_ramp->
interval;
811 dest->dwStartDelay = hap_ramp->
delay * 1000;
814 if (SDL_SYS_SetDirection(dest, &hap_ramp->
direction, dest->cAxes) < 0) {
821 dest->lpEnvelope =
NULL;
823 envelope->dwAttackLevel = CCONVERT(hap_ramp->
attack_level);
825 envelope->dwFadeLevel = CCONVERT(hap_ramp->
fade_level);
826 envelope->dwFadeTime = hap_ramp->
fade_length * 1000;
832 hap_custom = &
src->custom;
834 if (custom ==
NULL) {
840 custom->cChannels = hap_custom->
channels;
841 custom->dwSamplePeriod = hap_custom->
period * 1000;
842 custom->cSamples = hap_custom->
samples;
843 custom->rglForceData =
844 SDL_malloc(
sizeof(LONG) * custom->cSamples * custom->cChannels);
846 custom->rglForceData[
i] = CCONVERT(hap_custom->
data[
i]);
848 dest->cbTypeSpecificParams =
sizeof(DICUSTOMFORCE);
849 dest->lpvTypeSpecificParams = custom;
852 dest->dwDuration = hap_custom->
length * 1000;
853 dest->dwTriggerButton = DIGetTriggerButton(hap_custom->
button);
854 dest->dwTriggerRepeatInterval = hap_custom->
interval;
855 dest->dwStartDelay = hap_custom->
delay * 1000;
858 if (SDL_SYS_SetDirection(dest, &hap_custom->
direction, dest->cAxes) < 0) {
866 dest->lpEnvelope =
NULL;
868 envelope->dwAttackLevel = CCONVERT(hap_custom->
attack_level);
870 envelope->dwFadeLevel = CCONVERT(hap_custom->
fade_level);
871 envelope->dwFadeTime = hap_custom->
fade_length * 1000;
888 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT *
effect,
int type)
890 DICUSTOMFORCE *custom;
898 custom = (DICUSTOMFORCE *)
effect->lpvTypeSpecificParams;
900 custom->rglForceData =
NULL;
917 return &GUID_ConstantForce;
920 return &GUID_RampForce;
930 return &GUID_Triangle;
933 return &GUID_SawtoothUp;
936 return &GUID_SawtoothDown;
945 return &GUID_Inertia;
948 return &GUID_Friction;
951 return &GUID_CustomForce;
961 REFGUID
type = SDL_SYS_HapticEffectType(
base);
974 ret = IDirectInputDevice8_CreateEffect(
haptic->hwdata->device,
type,
975 &
effect->hweffect->effect,
978 DI_SetError(
"Unable to create effect", ret);
985 SDL_SYS_HapticFreeDIEFFECT(&
effect->hweffect->effect,
base->type);
998 if (SDL_SYS_ToDIEFFECT(
haptic, &temp,
data) < 0) {
1004 flags = DIEP_DIRECTION |
1008 DIEP_TRIGGERBUTTON |
1009 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
1013 IDirectInputEffect_SetParameters(
effect->hweffect->ref, &temp,
flags);
1014 if (ret == DIERR_NOTEXCLUSIVEACQUIRED) {
1015 IDirectInputDevice8_Unacquire(
haptic->hwdata->device);
1016 ret = IDirectInputDevice8_SetCooperativeLevel(
haptic->hwdata->device, SDL_HelperWindow, DISCL_EXCLUSIVE | DISCL_BACKGROUND);
1018 ret = DIERR_NOTACQUIRED;
1021 if (ret == DIERR_INPUTLOST || ret == DIERR_NOTACQUIRED) {
1022 ret = IDirectInputDevice8_Acquire(
haptic->hwdata->device);
1024 ret = IDirectInputEffect_SetParameters(
effect->hweffect->ref, &temp,
flags);
1028 DI_SetError(
"Unable to update effect", ret);
1033 SDL_SYS_HapticFreeDIEFFECT(&
effect->hweffect->effect,
data->
type);
1039 SDL_SYS_HapticFreeDIEFFECT(&temp,
data->type);
1057 ret = IDirectInputEffect_Start(
effect->hweffect->ref, iter, 0);
1059 return DI_SetError(
"Running the effect", ret);
1069 ret = IDirectInputEffect_Stop(
effect->hweffect->ref);
1071 return DI_SetError(
"Unable to stop effect", ret);
1081 ret = IDirectInputEffect_Unload(
effect->hweffect->ref);
1083 DI_SetError(
"Removing effect from the device", ret);
1085 SDL_SYS_HapticFreeDIEFFECT(&
effect->hweffect->effect,
effect->effect.
type);
1094 ret = IDirectInputEffect_GetEffectStatus(
effect->hweffect->ref, &status);
1096 return DI_SetError(
"Getting effect status", ret);
1111 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
1112 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
1113 dipdw.diph.dwObj = 0;
1114 dipdw.diph.dwHow = DIPH_DEVICE;
1115 dipdw.dwData = gain * 100;
1118 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
1119 DIPROP_FFGAIN, &dipdw.diph);
1121 return DI_SetError(
"Setting gain", ret);
1133 dipdw.diph.dwSize =
sizeof(DIPROPDWORD);
1134 dipdw.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
1135 dipdw.diph.dwObj = 0;
1136 dipdw.diph.dwHow = DIPH_DEVICE;
1137 dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
1138 DIPROPAUTOCENTER_ON;
1141 ret = IDirectInputDevice8_SetProperty(
haptic->hwdata->device,
1142 DIPROP_AUTOCENTER, &dipdw.diph);
1144 return DI_SetError(
"Setting autocenter", ret);
1155 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
1158 return DI_SetError(
"Pausing the device", ret);
1169 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
1172 return DI_SetError(
"Pausing the device", ret);
1183 ret = IDirectInputDevice8_SendForceFeedbackCommand(
haptic->hwdata->device,
1186 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_STEERING_AXIS
Use this value to play an effect on the steering wheel axis. This provides better compatibility acros...
#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
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst base
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
static SDL_Joystick * joystick
The generic template for any haptic effect.
struct HINSTANCE__ * HINSTANCE
typedef int(__stdcall *FARPROC)()