2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 #include "SDL_config.h"
23 #ifdef SDL_HAPTIC_DINPUT
25 #include "SDL_haptic.h"
26 #include "../SDL_syshaptic.h"
27 #include "SDL_joystick.h"
28 #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
29 #include "../../joystick/windows/SDL_dxjoystick_c.h" /* For joystick hwdata */
32 #define MAX_HAPTICS 32
36 * List of available haptic devices.
40 DIDEVICEINSTANCE instance;
43 DIDEVCAPS capabilities;
44 } SDL_hapticlist[MAX_HAPTICS];
48 * Haptic system hardware data.
52 LPDIRECTINPUTDEVICE2 device;
53 DWORD axes[3]; /* Axes to use. */
54 int is_joystick; /* Device is loaded as joystick. */
59 * Haptic system effect data.
61 struct haptic_hweffect
64 LPDIRECTINPUTEFFECT ref;
71 static SDL_bool coinitialized = SDL_FALSE;
72 static LPDIRECTINPUT dinput = NULL;
78 extern HWND SDL_HelperWindow;
84 static void DI_SetError(const char *str, HRESULT err);
85 static int DI_GUIDIsSame(const GUID * a, const GUID * b);
86 static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic,
87 DIDEVICEINSTANCE instance);
88 static int SDL_SYS_HapticOpenFromDevice2(SDL_Haptic * haptic,
89 LPDIRECTINPUTDEVICE2 device2);
90 static DWORD DIGetTriggerButton(Uint16 button);
91 static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
93 static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
94 SDL_HapticEffect * src);
95 static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type);
96 static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect);
98 static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE *
99 pdidInstance, VOID * pContext);
100 static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv);
104 * Like SDL_SetError but for DX error codes.
107 DI_SetError(const char *str, HRESULT err)
110 SDL_SetError("Haptic: %s - %s: %s", str,
111 DXGetErrorString8A(err), DXGetErrorDescription8A(err));
113 SDL_SetError("Haptic error %s", str);
118 * Checks to see if two GUID are the same.
121 DI_GUIDIsSame(const GUID * a, const GUID * b)
123 if (((a)->Data1 == (b)->Data1) &&
124 ((a)->Data2 == (b)->Data2) &&
125 ((a)->Data3 == (b)->Data3) &&
126 (SDL_strcmp((a)->Data4, (b)->Data4) == 0))
133 * Initializes the haptic subsystem.
136 SDL_SYS_HapticInit(void)
141 if (dinput != NULL) { /* Already open. */
142 SDL_SetError("Haptic: SubSystem already open.");
146 /* Clear all the memory. */
147 SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
151 ret = WIN_CoInitialize();
153 DI_SetError("Coinitialize", ret);
157 coinitialized = SDL_TRUE;
159 ret = CoCreateInstance(&CLSID_DirectInput, NULL, CLSCTX_INPROC_SERVER,
160 &IID_IDirectInput, (LPVOID) & dinput);
162 SDL_SYS_HapticQuit();
163 DI_SetError("CoCreateInstance", ret);
167 /* Because we used CoCreateInstance, we need to Initialize it, first. */
168 instance = GetModuleHandle(NULL);
169 if (instance == NULL) {
170 SDL_SYS_HapticQuit();
171 SDL_SetError("GetModuleHandle() failed with error code %d.",
175 ret = IDirectInput_Initialize(dinput, instance, DIRECTINPUT_VERSION);
177 SDL_SYS_HapticQuit();
178 DI_SetError("Initializing DirectInput device", ret);
182 /* Look for haptic devices. */
183 ret = IDirectInput_EnumDevices(dinput,
187 DIEDFL_FORCEFEEDBACK |
188 DIEDFL_ATTACHEDONLY);
190 SDL_SYS_HapticQuit();
191 DI_SetError("Enumerating DirectInput devices", ret);
195 return SDL_numhaptics;
199 * Callback to find the haptic devices.
202 EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
205 LPDIRECTINPUTDEVICE device;
207 /* Copy the instance over, useful for creating devices. */
208 SDL_memcpy(&SDL_hapticlist[SDL_numhaptics].instance, pdidInstance,
209 sizeof(DIDEVICEINSTANCE));
211 /* Open the device */
212 ret = IDirectInput_CreateDevice(dinput, &pdidInstance->guidInstance,
215 /* DI_SetError("Creating DirectInput device",ret); */
216 return DIENUM_CONTINUE;
219 /* Get capabilities. */
220 SDL_hapticlist[SDL_numhaptics].capabilities.dwSize = sizeof(DIDEVCAPS);
221 ret = IDirectInputDevice_GetCapabilities(device,
222 &SDL_hapticlist[SDL_numhaptics].
225 /* DI_SetError("Getting device capabilities",ret); */
226 IDirectInputDevice_Release(device);
227 return DIENUM_CONTINUE;
231 SDL_hapticlist[SDL_numhaptics].name = WIN_StringToUTF8(SDL_hapticlist[SDL_numhaptics].instance.tszProductName);
233 /* Close up device and count it. */
234 IDirectInputDevice_Release(device);
237 /* Watch out for hard limit. */
238 if (SDL_numhaptics >= MAX_HAPTICS)
241 return DIENUM_CONTINUE;
246 * Return the name of a haptic device, does not need to be opened.
249 SDL_SYS_HapticName(int index)
251 return SDL_hapticlist[index].name;
256 * Callback to get all supported effects.
258 #define EFFECT_TEST(e,s) \
259 if (DI_GUIDIsSame(&pei->guid, &(e))) \
260 haptic->supported |= (s)
262 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
264 /* Prepare the haptic device. */
265 SDL_Haptic *haptic = (SDL_Haptic *) pv;
268 EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING);
269 EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER);
270 EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA);
271 EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION);
272 EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT);
273 EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM);
274 EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE);
275 EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE);
276 EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE);
277 EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP);
278 EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN);
279 EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP);
281 /* Check for more. */
282 return DIENUM_CONTINUE;
287 * Callback to get supported axes.
290 DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
292 SDL_Haptic *haptic = (SDL_Haptic *) pvRef;
294 if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
296 haptic->hwdata->axes[haptic->naxes] = dev->dwOfs;
299 /* Currently using the artificial limit of 3 axes. */
300 if (haptic->naxes >= 3) {
305 return DIENUM_CONTINUE;
310 * Opens the haptic device from the file descriptor.
313 * - Open temporary DirectInputDevice interface.
314 * - Create DirectInputDevice2 interface.
315 * - Release DirectInputDevice interface.
316 * - Call SDL_SYS_HapticOpenFromDevice2
319 SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
323 LPDIRECTINPUTDEVICE device;
325 /* Allocate the hwdata */
326 haptic->hwdata = (struct haptic_hwdata *)
327 SDL_malloc(sizeof(*haptic->hwdata));
328 if (haptic->hwdata == NULL) {
332 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
334 /* Open the device */
335 ret = IDirectInput_CreateDevice(dinput, &instance.guidInstance,
338 DI_SetError("Creating DirectInput device", ret);
342 /* Now get the IDirectInputDevice2 interface, instead. */
343 ret = IDirectInputDevice_QueryInterface(device,
344 &IID_IDirectInputDevice2,
345 (LPVOID *) & haptic->hwdata->
347 /* Done with the temporary one now. */
348 IDirectInputDevice_Release(device);
350 DI_SetError("Querying DirectInput interface", ret);
354 ret2 = SDL_SYS_HapticOpenFromDevice2(haptic, haptic->hwdata->device);
362 IDirectInputDevice2_Release(haptic->hwdata->device);
364 if (haptic->hwdata != NULL) {
365 SDL_free(haptic->hwdata);
366 haptic->hwdata = NULL;
373 * Opens the haptic device from the file descriptor.
376 * - Set cooperative level.
378 * - Acquire exclusiveness.
380 * - Get supported featuers.
383 SDL_SYS_HapticOpenFromDevice2(SDL_Haptic * haptic,
384 LPDIRECTINPUTDEVICE2 device2)
389 /* We'll use the device2 from now on. */
390 haptic->hwdata->device = device2;
392 /* Grab it exclusively to use force feedback stuff. */
393 ret = IDirectInputDevice2_SetCooperativeLevel(haptic->hwdata->device,
398 DI_SetError("Setting cooperative level to exclusive", ret);
402 /* Set data format. */
403 ret = IDirectInputDevice2_SetDataFormat(haptic->hwdata->device,
406 DI_SetError("Setting data format", ret);
410 /* Get number of axes. */
411 ret = IDirectInputDevice2_EnumObjects(haptic->hwdata->device,
412 DI_DeviceObjectCallback,
415 DI_SetError("Getting device axes", ret);
419 /* Acquire the device. */
420 ret = IDirectInputDevice2_Acquire(haptic->hwdata->device);
422 DI_SetError("Acquiring DirectInput device", ret);
426 /* Reset all actuators - just in case. */
427 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
430 DI_SetError("Resetting device", ret);
434 /* Enabling actuators. */
435 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
436 DISFFC_SETACTUATORSON);
438 DI_SetError("Enabling actuators", ret);
442 /* Get supported effects. */
443 ret = IDirectInputDevice2_EnumEffects(haptic->hwdata->device,
444 DI_EffectCallback, haptic,
447 DI_SetError("Enumerating supported effects", ret);
450 if (haptic->supported == 0) { /* Error since device supports nothing. */
451 SDL_SetError("Haptic: Internal error on finding supported effects.");
455 /* Check autogain and autocenter. */
456 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
457 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
458 dipdw.diph.dwObj = 0;
459 dipdw.diph.dwHow = DIPH_DEVICE;
460 dipdw.dwData = 10000;
461 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
462 DIPROP_FFGAIN, &dipdw.diph);
463 if (!FAILED(ret)) { /* Gain is supported. */
464 haptic->supported |= SDL_HAPTIC_GAIN;
466 dipdw.diph.dwObj = 0;
467 dipdw.diph.dwHow = DIPH_DEVICE;
468 dipdw.dwData = DIPROPAUTOCENTER_OFF;
469 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
470 DIPROP_AUTOCENTER, &dipdw.diph);
471 if (!FAILED(ret)) { /* Autocenter is supported. */
472 haptic->supported |= SDL_HAPTIC_AUTOCENTER;
475 /* Status is always supported. */
476 haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
478 /* Check maximum effects. */
479 haptic->neffects = 128; /* This is not actually supported as thus under windows,
480 there is no way to tell the number of EFFECTS that a
481 device can hold, so we'll just use a "random" number
482 instead and put warnings in SDL_haptic.h */
483 haptic->nplaying = 128; /* Even more impossible to get this then neffects. */
485 /* Prepare effects memory. */
486 haptic->effects = (struct haptic_effect *)
487 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
488 if (haptic->effects == NULL) {
492 /* Clear the memory */
493 SDL_memset(haptic->effects, 0,
494 sizeof(struct haptic_effect) * haptic->neffects);
500 IDirectInputDevice2_Unacquire(haptic->hwdata->device);
507 * Opens a haptic device for usage.
510 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
512 return SDL_SYS_HapticOpenFromInstance(haptic,
513 SDL_hapticlist[haptic->index].
519 * Opens a haptic device from first mouse it finds for usage.
522 SDL_SYS_HapticMouse(void)
526 /* Grab the first mouse haptic device we find. */
527 for (i = 0; i < SDL_numhaptics; i++) {
528 if (SDL_hapticlist[i].capabilities.dwDevType == DIDEVTYPE_MOUSE) {
538 * Checks to see if a joystick has haptic features.
541 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
543 if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
552 * Checks to see if the haptic device and joystick and in reality the same.
555 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
558 DIDEVICEINSTANCE hap_instance, joy_instance;
559 hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
560 joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
562 /* Get the device instances. */
563 ret = IDirectInputDevice2_GetDeviceInfo(haptic->hwdata->device,
568 ret = IDirectInputDevice2_GetDeviceInfo(joystick->hwdata->InputDevice,
574 if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
582 * Opens a SDL_Haptic from a SDL_Joystick.
585 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
589 DIDEVICEINSTANCE joy_instance;
590 joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
592 /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
593 for (i=0; i<SDL_numhaptics; i++) {
594 idret = IDirectInputDevice2_GetDeviceInfo(joystick->hwdata->InputDevice,
599 if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
600 &joy_instance.guidInstance)) {
605 if (i >= SDL_numhaptics) {
609 /* Allocate the hwdata */
610 haptic->hwdata = (struct haptic_hwdata *)
611 SDL_malloc(sizeof(*haptic->hwdata));
612 if (haptic->hwdata == NULL) {
616 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
618 /* Now open the device. */
620 SDL_SYS_HapticOpenFromDevice2(haptic, joystick->hwdata->InputDevice);
625 /* It's using the joystick device. */
626 haptic->hwdata->is_joystick = 1;
633 * Closes the haptic device.
636 SDL_SYS_HapticClose(SDL_Haptic * haptic)
638 if (haptic->hwdata) {
641 SDL_free(haptic->effects);
642 haptic->effects = NULL;
643 haptic->neffects = 0;
646 IDirectInputDevice2_Unacquire(haptic->hwdata->device);
647 /* Only release if isn't grabbed by a joystick. */
648 if (haptic->hwdata->is_joystick == 0) {
649 IDirectInputDevice2_Release(haptic->hwdata->device);
653 SDL_free(haptic->hwdata);
654 haptic->hwdata = NULL;
660 * Clean up after system specific haptic stuff
663 SDL_SYS_HapticQuit(void)
667 for (i = 0; i < SDL_arraysize(SDL_hapticlist); ++i) {
668 if (SDL_hapticlist[i].name) {
669 SDL_free(SDL_hapticlist[i].name);
670 SDL_hapticlist[i].name = NULL;
674 if (dinput != NULL) {
675 IDirectInput_Release(dinput);
680 WIN_CoUninitialize();
681 coinitialized = SDL_FALSE;
687 * Converts an SDL trigger button to an DIEFFECT trigger button.
690 DIGetTriggerButton(Uint16 button)
692 DWORD dwTriggerButton;
694 dwTriggerButton = DIEB_NOTRIGGER;
697 dwTriggerButton = DIJOFS_BUTTON(button - 1);
700 return dwTriggerButton;
705 * Sets the direction.
708 SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes)
712 /* Handle no axes a part. */
714 effect->dwFlags |= DIEFF_SPHERICAL; /* Set as default. */
715 effect->rglDirection = NULL;
720 rglDir = SDL_malloc(sizeof(LONG) * naxes);
721 if (rglDir == NULL) {
725 SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
726 effect->rglDirection = rglDir;
729 case SDL_HAPTIC_POLAR:
730 effect->dwFlags |= DIEFF_POLAR;
731 rglDir[0] = dir->dir[0];
733 case SDL_HAPTIC_CARTESIAN:
734 effect->dwFlags |= DIEFF_CARTESIAN;
735 rglDir[0] = dir->dir[0];
737 rglDir[1] = dir->dir[1];
739 rglDir[2] = dir->dir[2];
741 case SDL_HAPTIC_SPHERICAL:
742 effect->dwFlags |= DIEFF_SPHERICAL;
743 rglDir[0] = dir->dir[0];
745 rglDir[1] = dir->dir[1];
747 rglDir[2] = dir->dir[2];
751 SDL_SetError("Haptic: Unknown direction type.");
756 #define CONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
758 * Creates the DIEFFECT from a SDL_HapticEffect.
761 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
762 SDL_HapticEffect * src)
765 DICONSTANTFORCE *constant;
766 DIPERIODIC *periodic;
767 DICONDITION *condition; /* Actually an array of conditions - one per axis. */
769 DICUSTOMFORCE *custom;
770 DIENVELOPE *envelope;
771 SDL_HapticConstant *hap_constant;
772 SDL_HapticPeriodic *hap_periodic;
773 SDL_HapticCondition *hap_condition;
774 SDL_HapticRamp *hap_ramp;
775 SDL_HapticCustom *hap_custom;
778 /* Set global stuff. */
779 SDL_memset(dest, 0, sizeof(DIEFFECT));
780 dest->dwSize = sizeof(DIEFFECT); /* Set the structure size. */
781 dest->dwSamplePeriod = 0; /* Not used by us. */
782 dest->dwGain = 10000; /* Gain is set globally, not locally. */
783 dest->dwFlags = DIEFF_OBJECTOFFSETS; /* Seems obligatory. */
786 envelope = SDL_malloc(sizeof(DIENVELOPE));
787 if (envelope == NULL) {
791 SDL_memset(envelope, 0, sizeof(DIENVELOPE));
792 dest->lpEnvelope = envelope;
793 envelope->dwSize = sizeof(DIENVELOPE); /* Always should be this. */
796 dest->cAxes = haptic->naxes;
797 if (dest->cAxes > 0) {
798 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
803 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
804 if (dest->cAxes > 1) {
805 axes[1] = haptic->hwdata->axes[1];
807 if (dest->cAxes > 2) {
808 axes[2] = haptic->hwdata->axes[2];
810 dest->rgdwAxes = axes;
814 /* The big type handling switch, even bigger then linux's version. */
816 case SDL_HAPTIC_CONSTANT:
817 hap_constant = &src->constant;
818 constant = SDL_malloc(sizeof(DICONSTANTFORCE));
819 if (constant == NULL) {
823 SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
826 constant->lMagnitude = CONVERT(hap_constant->level);
827 dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
828 dest->lpvTypeSpecificParams = constant;
831 dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
832 dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
833 dest->dwTriggerRepeatInterval = hap_constant->interval;
834 dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
837 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
843 if ((hap_constant->attack_length == 0)
844 && (hap_constant->fade_length == 0)) {
845 SDL_free(dest->lpEnvelope);
846 dest->lpEnvelope = NULL;
848 envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
849 envelope->dwAttackTime = hap_constant->attack_length * 1000;
850 envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
851 envelope->dwFadeTime = hap_constant->fade_length * 1000;
856 case SDL_HAPTIC_SINE:
857 case SDL_HAPTIC_SQUARE:
858 case SDL_HAPTIC_TRIANGLE:
859 case SDL_HAPTIC_SAWTOOTHUP:
860 case SDL_HAPTIC_SAWTOOTHDOWN:
861 hap_periodic = &src->periodic;
862 periodic = SDL_malloc(sizeof(DIPERIODIC));
863 if (periodic == NULL) {
867 SDL_memset(periodic, 0, sizeof(DIPERIODIC));
870 periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
871 periodic->lOffset = CONVERT(hap_periodic->offset);
872 periodic->dwPhase = hap_periodic->phase;
873 periodic->dwPeriod = hap_periodic->period * 1000;
874 dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
875 dest->lpvTypeSpecificParams = periodic;
878 dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
879 dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
880 dest->dwTriggerRepeatInterval = hap_periodic->interval;
881 dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
884 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
890 if ((hap_periodic->attack_length == 0)
891 && (hap_periodic->fade_length == 0)) {
892 SDL_free(dest->lpEnvelope);
893 dest->lpEnvelope = NULL;
895 envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
896 envelope->dwAttackTime = hap_periodic->attack_length * 1000;
897 envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
898 envelope->dwFadeTime = hap_periodic->fade_length * 1000;
903 case SDL_HAPTIC_SPRING:
904 case SDL_HAPTIC_DAMPER:
905 case SDL_HAPTIC_INERTIA:
906 case SDL_HAPTIC_FRICTION:
907 hap_condition = &src->condition;
908 condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
909 if (condition == NULL) {
913 SDL_memset(condition, 0, sizeof(DICONDITION));
916 for (i = 0; i < (int) dest->cAxes; i++) {
917 condition[i].lOffset = CONVERT(hap_condition->center[i]);
918 condition[i].lPositiveCoefficient =
919 CONVERT(hap_condition->right_coeff[i]);
920 condition[i].lNegativeCoefficient =
921 CONVERT(hap_condition->left_coeff[i]);
922 condition[i].dwPositiveSaturation =
923 CONVERT(hap_condition->right_sat[i]);
924 condition[i].dwNegativeSaturation =
925 CONVERT(hap_condition->left_sat[i]);
926 condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
928 dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
929 dest->lpvTypeSpecificParams = condition;
932 dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
933 dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
934 dest->dwTriggerRepeatInterval = hap_condition->interval;
935 dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
938 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
943 /* Envelope - Not actually supported by most CONDITION implementations. */
944 SDL_free(dest->lpEnvelope);
945 dest->lpEnvelope = NULL;
949 case SDL_HAPTIC_RAMP:
950 hap_ramp = &src->ramp;
951 ramp = SDL_malloc(sizeof(DIRAMPFORCE));
956 SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
959 ramp->lStart = CONVERT(hap_ramp->start);
960 ramp->lEnd = CONVERT(hap_ramp->end);
961 dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
962 dest->lpvTypeSpecificParams = ramp;
965 dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
966 dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
967 dest->dwTriggerRepeatInterval = hap_ramp->interval;
968 dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
971 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
976 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
977 SDL_free(dest->lpEnvelope);
978 dest->lpEnvelope = NULL;
980 envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
981 envelope->dwAttackTime = hap_ramp->attack_length * 1000;
982 envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
983 envelope->dwFadeTime = hap_ramp->fade_length * 1000;
988 case SDL_HAPTIC_CUSTOM:
989 hap_custom = &src->custom;
990 custom = SDL_malloc(sizeof(DICUSTOMFORCE));
991 if (custom == NULL) {
995 SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
998 custom->cChannels = hap_custom->channels;
999 custom->dwSamplePeriod = hap_custom->period * 1000;
1000 custom->cSamples = hap_custom->samples;
1001 custom->rglForceData =
1002 SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
1003 for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */
1004 custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
1006 dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
1007 dest->lpvTypeSpecificParams = custom;
1010 dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
1011 dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
1012 dest->dwTriggerRepeatInterval = hap_custom->interval;
1013 dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
1016 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
1022 if ((hap_custom->attack_length == 0)
1023 && (hap_custom->fade_length == 0)) {
1024 SDL_free(dest->lpEnvelope);
1025 dest->lpEnvelope = NULL;
1027 envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
1028 envelope->dwAttackTime = hap_custom->attack_length * 1000;
1029 envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
1030 envelope->dwFadeTime = hap_custom->fade_length * 1000;
1037 SDL_SetError("Haptic: Unknown effect type.");
1046 * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
1049 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
1051 DICUSTOMFORCE *custom;
1053 if (effect->lpEnvelope != NULL) {
1054 SDL_free(effect->lpEnvelope);
1055 effect->lpEnvelope = NULL;
1057 if (effect->rgdwAxes != NULL) {
1058 SDL_free(effect->rgdwAxes);
1059 effect->rgdwAxes = NULL;
1061 if (effect->lpvTypeSpecificParams != NULL) {
1062 if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
1063 custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
1064 SDL_free(custom->rglForceData);
1065 custom->rglForceData = NULL;
1067 SDL_free(effect->lpvTypeSpecificParams);
1068 effect->lpvTypeSpecificParams = NULL;
1070 if (effect->rglDirection != NULL) {
1071 SDL_free(effect->rglDirection);
1072 effect->rglDirection = NULL;
1078 * Gets the effect type from the generic SDL haptic effect wrapper.
1081 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
1083 switch (effect->type) {
1084 case SDL_HAPTIC_CONSTANT:
1085 return &GUID_ConstantForce;
1087 case SDL_HAPTIC_RAMP:
1088 return &GUID_RampForce;
1090 case SDL_HAPTIC_SQUARE:
1091 return &GUID_Square;
1093 case SDL_HAPTIC_SINE:
1096 case SDL_HAPTIC_TRIANGLE:
1097 return &GUID_Triangle;
1099 case SDL_HAPTIC_SAWTOOTHUP:
1100 return &GUID_SawtoothUp;
1102 case SDL_HAPTIC_SAWTOOTHDOWN:
1103 return &GUID_SawtoothDown;
1105 case SDL_HAPTIC_SPRING:
1106 return &GUID_Spring;
1108 case SDL_HAPTIC_DAMPER:
1109 return &GUID_Damper;
1111 case SDL_HAPTIC_INERTIA:
1112 return &GUID_Inertia;
1114 case SDL_HAPTIC_FRICTION:
1115 return &GUID_Friction;
1117 case SDL_HAPTIC_CUSTOM:
1118 return &GUID_CustomForce;
1121 SDL_SetError("Haptic: Unknown effect type.");
1128 * Creates a new haptic effect.
1131 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1132 SDL_HapticEffect * base)
1137 REFGUID type = SDL_SYS_HapticEffectType(base);
1142 /* Alloc the effect. */
1143 effect->hweffect = (struct haptic_hweffect *)
1144 SDL_malloc(sizeof(struct haptic_hweffect));
1145 if (effect->hweffect == NULL) {
1150 /* Get the effect. */
1151 if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1152 goto err_effectdone;
1155 /* Create the actual effect. */
1156 ret = IDirectInputDevice2_CreateEffect(haptic->hwdata->device, type,
1157 &effect->hweffect->effect,
1158 &effect->hweffect->ref, NULL);
1160 DI_SetError("Unable to create effect", ret);
1161 goto err_effectdone;
1167 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
1169 if (effect->hweffect != NULL) {
1170 SDL_free(effect->hweffect);
1171 effect->hweffect = NULL;
1178 * Updates an effect.
1181 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
1182 struct haptic_effect *effect,
1183 SDL_HapticEffect * data)
1189 /* Get the effect. */
1190 SDL_memset(&temp, 0, sizeof(DIEFFECT));
1191 if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
1195 /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1196 * only change those parameters. */
1197 flags = DIEP_DIRECTION |
1201 DIEP_TRIGGERBUTTON |
1202 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
1204 /* Create the actual effect. */
1206 IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
1208 DI_SetError("Unable to update effect", ret);
1213 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
1214 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
1219 SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
1228 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1234 /* Check if it's infinite. */
1235 if (iterations == SDL_HAPTIC_INFINITY) {
1240 /* Run the effect. */
1241 ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
1243 DI_SetError("Running the effect", ret);
1255 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1259 ret = IDirectInputEffect_Stop(effect->hweffect->ref);
1261 DI_SetError("Unable to stop effect", ret);
1273 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1277 ret = IDirectInputEffect_Unload(effect->hweffect->ref);
1279 DI_SetError("Removing effect from the device", ret);
1281 SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
1282 effect->effect.type);
1283 SDL_free(effect->hweffect);
1284 effect->hweffect = NULL;
1289 * Gets the status of a haptic effect.
1292 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1293 struct haptic_effect *effect)
1298 ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
1300 DI_SetError("Getting effect status", ret);
1314 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1319 /* Create the weird structure thingy. */
1320 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
1321 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1322 dipdw.diph.dwObj = 0;
1323 dipdw.diph.dwHow = DIPH_DEVICE;
1324 dipdw.dwData = gain * 100; /* 0 to 10,000 */
1326 /* Try to set the autocenter. */
1327 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
1328 DIPROP_FFGAIN, &dipdw.diph);
1330 DI_SetError("Setting gain", ret);
1339 * Sets the autocentering.
1342 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1347 /* Create the weird structure thingy. */
1348 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
1349 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1350 dipdw.diph.dwObj = 0;
1351 dipdw.diph.dwHow = DIPH_DEVICE;
1352 dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
1353 DIPROPAUTOCENTER_ON;
1355 /* Try to set the autocenter. */
1356 ret = IDirectInputDevice2_SetProperty(haptic->hwdata->device,
1357 DIPROP_AUTOCENTER, &dipdw.diph);
1359 DI_SetError("Setting autocenter", ret);
1368 * Pauses the device.
1371 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1375 /* Pause the device. */
1376 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
1379 DI_SetError("Pausing the device", ret);
1388 * Pauses the device.
1391 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1395 /* Unpause the device. */
1396 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
1399 DI_SetError("Pausing the device", ret);
1408 * Stops all the playing effects on the device.
1411 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1415 /* Try to stop the effects. */
1416 ret = IDirectInputDevice2_SendForceFeedbackCommand(haptic->hwdata->device,
1419 DI_SetError("Stopping the device", ret);
1427 #endif /* SDL_HAPTIC_DINPUT */