diff options
Diffstat (limited to 'source/3rd-party/SDL2/src/haptic')
15 files changed, 6695 insertions, 0 deletions
diff --git a/source/3rd-party/SDL2/src/haptic/SDL_haptic.c b/source/3rd-party/SDL2/src/haptic/SDL_haptic.c new file mode 100644 index 0000000..f23ba18 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/SDL_haptic.c @@ -0,0 +1,852 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../SDL_internal.h" + +#include "SDL_syshaptic.h" +#include "SDL_haptic_c.h" +#include "../joystick/SDL_joystick_c.h" /* For SDL_PrivateJoystickValid */ +#include "SDL_assert.h" + +/* Global for SDL_windowshaptic.c */ +SDL_Haptic *SDL_haptics = NULL; + +/* + * Initializes the Haptic devices. + */ +int +SDL_HapticInit(void) +{ + int status; + + status = SDL_SYS_HapticInit(); + if (status >= 0) { + status = 0; + } + + return status; +} + + +/* + * Checks to see if the haptic device is valid + */ +static int +ValidHaptic(SDL_Haptic * haptic) +{ + int valid; + SDL_Haptic *hapticlist; + + valid = 0; + if (haptic != NULL) { + hapticlist = SDL_haptics; + while ( hapticlist ) + { + if (hapticlist == haptic) { + valid = 1; + break; + } + hapticlist = hapticlist->next; + } + } + + /* Create the error here. */ + if (valid == 0) { + SDL_SetError("Haptic: Invalid haptic device identifier"); + } + + return valid; +} + + +/* + * Returns the number of available devices. + */ +int +SDL_NumHaptics(void) +{ + return SDL_SYS_NumHaptics(); +} + + +/* + * Gets the name of a Haptic device by index. + */ +const char * +SDL_HapticName(int device_index) +{ + if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { + SDL_SetError("Haptic: There are %d haptic devices available", + SDL_NumHaptics()); + return NULL; + } + return SDL_SYS_HapticName(device_index); +} + + +/* + * Opens a Haptic device. + */ +SDL_Haptic * +SDL_HapticOpen(int device_index) +{ + SDL_Haptic *haptic; + SDL_Haptic *hapticlist; + + if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { + SDL_SetError("Haptic: There are %d haptic devices available", + SDL_NumHaptics()); + return NULL; + } + + hapticlist = SDL_haptics; + /* If the haptic is already open, return it + * TODO: Should we create haptic instance IDs like the Joystick API? + */ + while ( hapticlist ) + { + if (device_index == hapticlist->index) { + haptic = hapticlist; + ++haptic->ref_count; + return haptic; + } + hapticlist = hapticlist->next; + } + + /* Create the haptic device */ + haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic)); + if (haptic == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + /* Initialize the haptic device */ + SDL_memset(haptic, 0, (sizeof *haptic)); + haptic->rumble_id = -1; + haptic->index = device_index; + if (SDL_SYS_HapticOpen(haptic) < 0) { + SDL_free(haptic); + return NULL; + } + + /* Add haptic to list */ + ++haptic->ref_count; + /* Link the haptic in the list */ + haptic->next = SDL_haptics; + SDL_haptics = haptic; + + /* Disable autocenter and set gain to max. */ + if (haptic->supported & SDL_HAPTIC_GAIN) + SDL_HapticSetGain(haptic, 100); + if (haptic->supported & SDL_HAPTIC_AUTOCENTER) + SDL_HapticSetAutocenter(haptic, 0); + + return haptic; +} + + +/* + * Returns 1 if the device has been opened. + */ +int +SDL_HapticOpened(int device_index) +{ + int opened; + SDL_Haptic *hapticlist; + + /* Make sure it's valid. */ + if ((device_index < 0) || (device_index >= SDL_NumHaptics())) { + SDL_SetError("Haptic: There are %d haptic devices available", + SDL_NumHaptics()); + return 0; + } + + opened = 0; + hapticlist = SDL_haptics; + /* TODO Should this use an instance ID? */ + while ( hapticlist ) + { + if (hapticlist->index == (Uint8) device_index) { + opened = 1; + break; + } + hapticlist = hapticlist->next; + } + return opened; +} + + +/* + * Returns the index to a haptic device. + */ +int +SDL_HapticIndex(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->index; +} + + +/* + * Returns SDL_TRUE if mouse is haptic, SDL_FALSE if it isn't. + */ +int +SDL_MouseIsHaptic(void) +{ + if (SDL_SYS_HapticMouse() < 0) + return SDL_FALSE; + return SDL_TRUE; +} + + +/* + * Returns the haptic device if mouse is haptic or NULL elsewise. + */ +SDL_Haptic * +SDL_HapticOpenFromMouse(void) +{ + int device_index; + + device_index = SDL_SYS_HapticMouse(); + + if (device_index < 0) { + SDL_SetError("Haptic: Mouse isn't a haptic device."); + return NULL; + } + + return SDL_HapticOpen(device_index); +} + + +/* + * Returns SDL_TRUE if joystick has haptic features. + */ +int +SDL_JoystickIsHaptic(SDL_Joystick * joystick) +{ + int ret; + + /* Must be a valid joystick */ + if (!SDL_PrivateJoystickValid(joystick)) { + return -1; + } + + ret = SDL_SYS_JoystickIsHaptic(joystick); + + if (ret > 0) + return SDL_TRUE; + else if (ret == 0) + return SDL_FALSE; + else + return -1; +} + + +/* + * Opens a haptic device from a joystick. + */ +SDL_Haptic * +SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) +{ + SDL_Haptic *haptic; + SDL_Haptic *hapticlist; + + /* Make sure there is room. */ + if (SDL_NumHaptics() <= 0) { + SDL_SetError("Haptic: There are %d haptic devices available", + SDL_NumHaptics()); + return NULL; + } + + /* Must be a valid joystick */ + if (!SDL_PrivateJoystickValid(joystick)) { + SDL_SetError("Haptic: Joystick isn't valid."); + return NULL; + } + + /* Joystick must be haptic */ + if (SDL_SYS_JoystickIsHaptic(joystick) <= 0) { + SDL_SetError("Haptic: Joystick isn't a haptic device."); + return NULL; + } + + hapticlist = SDL_haptics; + /* Check to see if joystick's haptic is already open */ + while ( hapticlist ) + { + if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) { + haptic = hapticlist; + ++haptic->ref_count; + return haptic; + } + hapticlist = hapticlist->next; + } + + /* Create the haptic device */ + haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic)); + if (haptic == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + /* Initialize the haptic device */ + SDL_memset(haptic, 0, sizeof(SDL_Haptic)); + haptic->rumble_id = -1; + if (SDL_SYS_HapticOpenFromJoystick(haptic, joystick) < 0) { + SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed."); + SDL_free(haptic); + return NULL; + } + + /* Add haptic to list */ + ++haptic->ref_count; + /* Link the haptic in the list */ + haptic->next = SDL_haptics; + SDL_haptics = haptic; + + return haptic; +} + + +/* + * Closes a SDL_Haptic device. + */ +void +SDL_HapticClose(SDL_Haptic * haptic) +{ + int i; + SDL_Haptic *hapticlist; + SDL_Haptic *hapticlistprev; + + /* Must be valid */ + if (!ValidHaptic(haptic)) { + return; + } + + /* Check if it's still in use */ + if (--haptic->ref_count > 0) { + return; + } + + /* Close it, properly removing effects if needed */ + for (i = 0; i < haptic->neffects; i++) { + if (haptic->effects[i].hweffect != NULL) { + SDL_HapticDestroyEffect(haptic, i); + } + } + SDL_SYS_HapticClose(haptic); + + /* Remove from the list */ + hapticlist = SDL_haptics; + hapticlistprev = NULL; + while ( hapticlist ) + { + if (haptic == hapticlist) + { + if ( hapticlistprev ) + { + /* unlink this entry */ + hapticlistprev->next = hapticlist->next; + } + else + { + SDL_haptics = haptic->next; + } + + break; + } + hapticlistprev = hapticlist; + hapticlist = hapticlist->next; + } + + /* Free */ + SDL_free(haptic); +} + +/* + * Cleans up after the subsystem. + */ +void +SDL_HapticQuit(void) +{ + while (SDL_haptics) { + SDL_HapticClose(SDL_haptics); + } + + SDL_SYS_HapticQuit(); +} + +/* + * Returns the number of effects a haptic device has. + */ +int +SDL_HapticNumEffects(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->neffects; +} + + +/* + * Returns the number of effects a haptic device can play. + */ +int +SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->nplaying; +} + + +/* + * Returns supported effects by the device. + */ +unsigned int +SDL_HapticQuery(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return 0; /* same as if no effects were supported */ + } + + return haptic->supported; +} + + +/* + * Returns the number of axis on the device. + */ +int +SDL_HapticNumAxes(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return haptic->naxes; +} + +/* + * Checks to see if the device can support the effect. + */ +int +SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & effect->type) != 0) + return SDL_TRUE; + return SDL_FALSE; +} + +/* + * Creates a new haptic effect. + */ +int +SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect) +{ + int i; + + /* Check for device validity. */ + if (!ValidHaptic(haptic)) { + return -1; + } + + /* Check to see if effect is supported */ + if (SDL_HapticEffectSupported(haptic, effect) == SDL_FALSE) { + return SDL_SetError("Haptic: Effect not supported by haptic device."); + } + + /* See if there's a free slot */ + for (i = 0; i < haptic->neffects; i++) { + if (haptic->effects[i].hweffect == NULL) { + + /* Now let the backend create the real effect */ + if (SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect) + != 0) { + return -1; /* Backend failed to create effect */ + } + + SDL_memcpy(&haptic->effects[i].effect, effect, + sizeof(SDL_HapticEffect)); + return i; + } + } + + return SDL_SetError("Haptic: Device has no free space left."); +} + +/* + * Checks to see if an effect is valid. + */ +static int +ValidEffect(SDL_Haptic * haptic, int effect) +{ + if ((effect < 0) || (effect >= haptic->neffects)) { + SDL_SetError("Haptic: Invalid effect identifier."); + return 0; + } + return 1; +} + +/* + * Updates an effect. + */ +int +SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect, + SDL_HapticEffect * data) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + /* Can't change type dynamically. */ + if (data->type != haptic->effects[effect].effect.type) { + return SDL_SetError("Haptic: Updating effect type is illegal."); + } + + /* Updates the effect */ + if (SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data) < + 0) { + return -1; + } + + SDL_memcpy(&haptic->effects[effect].effect, data, + sizeof(SDL_HapticEffect)); + return 0; +} + + +/* + * Runs the haptic effect on the device. + */ +int +SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + /* Run the effect */ + if (SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations) + < 0) { + return -1; + } + + return 0; +} + +/* + * Stops the haptic effect on the device. + */ +int +SDL_HapticStopEffect(SDL_Haptic * haptic, int effect) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + /* Stop the effect */ + if (SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect]) < 0) { + return -1; + } + + return 0; +} + +/* + * Gets rid of a haptic effect. + */ +void +SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return; + } + + /* Not allocated */ + if (haptic->effects[effect].hweffect == NULL) { + return; + } + + SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]); +} + +/* + * Gets the status of a haptic effect. + */ +int +SDL_HapticGetEffectStatus(SDL_Haptic * haptic, int effect) +{ + if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_STATUS) == 0) { + return SDL_SetError("Haptic: Device does not support status queries."); + } + + return SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]); +} + +/* + * Sets the global gain of the device. + */ +int +SDL_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + const char *env; + int real_gain, max_gain; + + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_GAIN) == 0) { + return SDL_SetError("Haptic: Device does not support setting gain."); + } + + if ((gain < 0) || (gain > 100)) { + return SDL_SetError("Haptic: Gain must be between 0 and 100."); + } + + /* We use the envvar to get the maximum gain. */ + env = SDL_getenv("SDL_HAPTIC_GAIN_MAX"); + if (env != NULL) { + max_gain = SDL_atoi(env); + + /* Check for sanity. */ + if (max_gain < 0) + max_gain = 0; + else if (max_gain > 100) + max_gain = 100; + + /* We'll scale it linearly with SDL_HAPTIC_GAIN_MAX */ + real_gain = (gain * max_gain) / 100; + } else { + real_gain = gain; + } + + if (SDL_SYS_HapticSetGain(haptic, real_gain) < 0) { + return -1; + } + + return 0; +} + +/* + * Makes the device autocenter, 0 disables. + */ +int +SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_AUTOCENTER) == 0) { + return SDL_SetError("Haptic: Device does not support setting autocenter."); + } + + if ((autocenter < 0) || (autocenter > 100)) { + return SDL_SetError("Haptic: Autocenter must be between 0 and 100."); + } + + if (SDL_SYS_HapticSetAutocenter(haptic, autocenter) < 0) { + return -1; + } + + return 0; +} + +/* + * Pauses the haptic device. + */ +int +SDL_HapticPause(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { + return SDL_SetError("Haptic: Device does not support setting pausing."); + } + + return SDL_SYS_HapticPause(haptic); +} + +/* + * Unpauses the haptic device. + */ +int +SDL_HapticUnpause(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) { + return 0; /* Not going to be paused, so we pretend it's unpaused. */ + } + + return SDL_SYS_HapticUnpause(haptic); +} + +/* + * Stops all the currently playing effects. + */ +int +SDL_HapticStopAll(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + return SDL_SYS_HapticStopAll(haptic); +} + +/* + * Checks to see if rumble is supported. + */ +int +SDL_HapticRumbleSupported(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + /* Most things can use SINE, but XInput only has LEFTRIGHT. */ + return ((haptic->supported & (SDL_HAPTIC_SINE|SDL_HAPTIC_LEFTRIGHT)) != 0); +} + +/* + * Initializes the haptic device for simple rumble playback. + */ +int +SDL_HapticRumbleInit(SDL_Haptic * haptic) +{ + SDL_HapticEffect *efx = &haptic->rumble_effect; + + if (!ValidHaptic(haptic)) { + return -1; + } + + /* Already allocated. */ + if (haptic->rumble_id >= 0) { + return 0; + } + + SDL_zerop(efx); + if (haptic->supported & SDL_HAPTIC_SINE) { + efx->type = SDL_HAPTIC_SINE; + efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN; + efx->periodic.period = 1000; + efx->periodic.magnitude = 0x4000; + efx->periodic.length = 5000; + efx->periodic.attack_length = 0; + efx->periodic.fade_length = 0; + } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { /* XInput? */ + efx->type = SDL_HAPTIC_LEFTRIGHT; + efx->leftright.length = 5000; + efx->leftright.large_magnitude = 0x4000; + efx->leftright.small_magnitude = 0x4000; + } else { + return SDL_SetError("Device doesn't support rumble"); + } + + haptic->rumble_id = SDL_HapticNewEffect(haptic, &haptic->rumble_effect); + if (haptic->rumble_id >= 0) { + return 0; + } + return -1; +} + +/* + * Runs simple rumble on a haptic device + */ +int +SDL_HapticRumblePlay(SDL_Haptic * haptic, float strength, Uint32 length) +{ + SDL_HapticEffect *efx; + Sint16 magnitude; + + if (!ValidHaptic(haptic)) { + return -1; + } + + if (haptic->rumble_id < 0) { + return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); + } + + /* Clamp strength. */ + if (strength > 1.0f) { + strength = 1.0f; + } else if (strength < 0.0f) { + strength = 0.0f; + } + magnitude = (Sint16)(32767.0f*strength); + + efx = &haptic->rumble_effect; + if (efx->type == SDL_HAPTIC_SINE) { + efx->periodic.magnitude = magnitude; + efx->periodic.length = length; + } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) { + efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude; + efx->leftright.length = length; + } else { + SDL_assert(0 && "This should have been caught elsewhere"); + } + + if (SDL_HapticUpdateEffect(haptic, haptic->rumble_id, &haptic->rumble_effect) < 0) { + return -1; + } + + return SDL_HapticRunEffect(haptic, haptic->rumble_id, 1); +} + +/* + * Stops the simple rumble on a haptic device. + */ +int +SDL_HapticRumbleStop(SDL_Haptic * haptic) +{ + if (!ValidHaptic(haptic)) { + return -1; + } + + if (haptic->rumble_id < 0) { + return SDL_SetError("Haptic: Rumble effect not initialized on haptic device"); + } + + return SDL_HapticStopEffect(haptic, haptic->rumble_id); +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/SDL_haptic_c.h b/source/3rd-party/SDL2/src/haptic/SDL_haptic_c.h new file mode 100644 index 0000000..390dc78 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/SDL_haptic_c.h @@ -0,0 +1,30 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_haptic_c_h_ +#define SDL_haptic_c_h_ + +extern int SDL_HapticInit(void); +extern void SDL_HapticQuit(void); + +#endif /* SDL_haptic_c_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/SDL_syshaptic.h b/source/3rd-party/SDL2/src/haptic/SDL_syshaptic.h new file mode 100644 index 0000000..4f4cd9f --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/SDL_syshaptic.h @@ -0,0 +1,208 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "../SDL_internal.h" + +#ifndef SDL_syshaptic_h_ +#define SDL_syshaptic_h_ + +#include "SDL_haptic.h" + + +struct haptic_effect +{ + SDL_HapticEffect effect; /* The current event */ + struct haptic_hweffect *hweffect; /* The hardware behind the event */ +}; + +/* + * The real SDL_Haptic struct. + */ +struct _SDL_Haptic +{ + Uint8 index; /* Stores index it is attached to */ + + struct haptic_effect *effects; /* Allocated effects */ + int neffects; /* Maximum amount of effects */ + int nplaying; /* Maximum amount of effects to play at the same time */ + unsigned int supported; /* Supported effects */ + int naxes; /* Number of axes on the device. */ + + struct haptic_hwdata *hwdata; /* Driver dependent */ + int ref_count; /* Count for multiple opens */ + + int rumble_id; /* ID of rumble effect for simple rumble API. */ + SDL_HapticEffect rumble_effect; /* Rumble effect. */ + struct _SDL_Haptic *next; /* pointer to next haptic we have allocated */ +}; + +/* + * Scans the system for haptic devices. + * + * Returns number of devices on success, -1 on error. + */ +extern int SDL_SYS_HapticInit(void); + +/* Function to return the number of haptic devices plugged in right now */ +extern int SDL_SYS_NumHaptics(void); + +/* + * Gets the device dependent name of the haptic device + */ +extern const char *SDL_SYS_HapticName(int index); + +/* + * Opens the haptic device for usage. The haptic device should have + * the index value set previously. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticOpen(SDL_Haptic * haptic); + +/* + * Returns the index of the haptic core pointer or -1 if none is found. + */ +int SDL_SYS_HapticMouse(void); + +/* + * Checks to see if the joystick has haptic capabilities. + * + * Returns >0 if haptic capabilities are detected, 0 if haptic + * capabilities aren't detected and -1 on error. + */ +extern int SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick); + +/* + * Opens the haptic device for usage using the same device as + * the joystick. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, + SDL_Joystick * joystick); +/* + * Checks to see if haptic device and joystick device are the same. + * + * Returns 1 if they are the same, 0 if they aren't. + */ +extern int SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, + SDL_Joystick * joystick); + +/* + * Closes a haptic device after usage. + */ +extern void SDL_SYS_HapticClose(SDL_Haptic * haptic); + +/* + * Performs a cleanup on the haptic subsystem. + */ +extern void SDL_SYS_HapticQuit(void); + +/* + * Creates a new haptic effect on the haptic device using base + * as a template for the effect. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * base); + +/* + * Updates the haptic effect on the haptic device using data + * as a template. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data); + +/* + * Runs the effect on the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + Uint32 iterations); + +/* + * Stops the effect on the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, + struct haptic_effect *effect); + +/* + * Cleanups up the effect on the haptic device. + */ +extern void SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, + struct haptic_effect *effect); + +/* + * Queries the device for the status of effect. + * + * Returns 0 if device is stopped, >0 if device is playing and + * -1 on error. + */ +extern int SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect); + +/* + * Sets the global gain of the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain); + +/* + * Sets the autocenter feature of the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter); + +/* + * Pauses the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticPause(SDL_Haptic * haptic); + +/* + * Unpauses the haptic device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticUnpause(SDL_Haptic * haptic); + +/* + * Stops all the currently playing haptic effects on the device. + * + * Returns 0 on success, -1 on error. + */ +extern int SDL_SYS_HapticStopAll(SDL_Haptic * haptic); + +#endif /* SDL_syshaptic_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/android/SDL_syshaptic.c b/source/3rd-party/SDL2/src/haptic/android/SDL_syshaptic.c new file mode 100644 index 0000000..7cb289b --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/android/SDL_syshaptic.c @@ -0,0 +1,368 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_HAPTIC_ANDROID + +#include "SDL_assert.h" +#include "SDL_timer.h" +#include "SDL_syshaptic_c.h" +#include "../SDL_syshaptic.h" +#include "SDL_haptic.h" +#include "../../core/android/SDL_android.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/android/SDL_sysjoystick_c.h" /* For joystick hwdata */ + + +typedef struct SDL_hapticlist_item +{ + int device_id; + char *name; + SDL_Haptic *haptic; + struct SDL_hapticlist_item *next; +} SDL_hapticlist_item; + +static SDL_hapticlist_item *SDL_hapticlist = NULL; +static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; +static int numhaptics = 0; + + +int +SDL_SYS_HapticInit(void) +{ + /* Support for device connect/disconnect is API >= 16 only, + * so we poll every three seconds + * Ref: http://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html + */ + static Uint32 timeout = 0; + if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout)) { + timeout = SDL_GetTicks() + 3000; + Android_JNI_PollHapticDevices(); + } + return (numhaptics); +} + +int +SDL_SYS_NumHaptics(void) +{ + return (numhaptics); +} + +static SDL_hapticlist_item * +HapticByOrder(int index) +{ + SDL_hapticlist_item *item = SDL_hapticlist; + if ((index < 0) || (index >= numhaptics)) { + return NULL; + } + while (index > 0) { + SDL_assert(item != NULL); + --index; + item = item->next; + } + return item; +} + +static SDL_hapticlist_item * +HapticByDevId (int device_id) +{ + SDL_hapticlist_item *item; + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (device_id == item->device_id) { + /*SDL_Log("=+=+=+=+=+= HapticByDevId id [%d]", device_id);*/ + return item; + } + } + return NULL; +} + +const char * +SDL_SYS_HapticName(int index) +{ + SDL_hapticlist_item *item = HapticByOrder(index); + if (item == NULL ) { + SDL_SetError("No such device"); + return NULL; + } + return item->name; +} + + +static SDL_hapticlist_item * +OpenHaptic(SDL_Haptic *haptic, SDL_hapticlist_item *item) +{ + if (item == NULL ) { + SDL_SetError("No such device"); + return NULL; + } + if (item->haptic != NULL) { + SDL_SetError("Haptic already opened"); + return NULL; + } + + haptic->hwdata = (struct haptic_hwdata *)item; + item->haptic = haptic; + + haptic->supported = SDL_HAPTIC_LEFTRIGHT; + haptic->neffects = 1; + haptic->nplaying = haptic->neffects; + haptic->effects = (struct haptic_effect *)SDL_malloc (sizeof (struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + SDL_OutOfMemory(); + return NULL; + } + SDL_memset(haptic->effects, 0, sizeof (struct haptic_effect) * haptic->neffects); + return item; +} + +static SDL_hapticlist_item * +OpenHapticByOrder(SDL_Haptic *haptic, int index) +{ + return OpenHaptic (haptic, HapticByOrder(index)); +} + +static SDL_hapticlist_item * +OpenHapticByDevId(SDL_Haptic *haptic, int device_id) +{ + return OpenHaptic (haptic, HapticByDevId(device_id)); +} + +int +SDL_SYS_HapticOpen(SDL_Haptic *haptic) +{ + return (OpenHapticByOrder(haptic, haptic->index) == NULL ? -1 : 0); +} + + +int +SDL_SYS_HapticMouse(void) +{ + return 0; +} + + +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick) +{ + SDL_hapticlist_item *item; + item = HapticByDevId(((joystick_hwdata *)joystick->hwdata)->device_id); + return (item != NULL) ? 1 : 0; +} + + +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick) +{ + return (OpenHapticByDevId(haptic, ((joystick_hwdata *)joystick->hwdata)->device_id) == NULL ? -1 : 0); +} + + +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return (((SDL_hapticlist_item *)haptic->hwdata)->device_id == ((joystick_hwdata *)joystick->hwdata)->device_id ? 1 : 0); +} + + +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + ((SDL_hapticlist_item *)haptic->hwdata)->haptic = NULL; + haptic->hwdata = NULL; + return; +} + + +void +SDL_SYS_HapticQuit(void) +{ +/* We don't have any way to scan for joysticks (and their vibrators) at init, so don't wipe the list + * of joysticks here in case this is a reinit. + */ +#if 0 + SDL_hapticlist_item *item = NULL; + SDL_hapticlist_item *next = NULL; + + for (item = SDL_hapticlist; item; item = next) { + next = item->next; + SDL_free(item); + } + + SDL_hapticlist = SDL_hapticlist_tail = NULL; + numhaptics = 0; + return; +#endif +} + + +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, SDL_HapticEffect * base) +{ + return 0; +} + + +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + return 0; +} + + +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + float large = effect->effect.leftright.large_magnitude / 32767.0f; + float small = effect->effect.leftright.small_magnitude / 32767.0f; + + float total = (large * 0.6f) + (small * 0.4f); + + Android_JNI_HapticRun (((SDL_hapticlist_item *)haptic->hwdata)->device_id, total, effect->effect.leftright.length); + return 0; +} + + +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + Android_JNI_HapticStop (((SDL_hapticlist_item *)haptic->hwdata)->device_id); + return 0; +} + + +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return; +} + + +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + return 0; +} + + +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return 0; +} + + +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return 0; +} + +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + return 0; +} + +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + return 0; +} + +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + return 0; +} + + + +int +Android_AddHaptic(int device_id, const char *name) +{ + SDL_hapticlist_item *item; + item = (SDL_hapticlist_item *) SDL_calloc(1, sizeof (SDL_hapticlist_item)); + if (item == NULL) { + return -1; + } + + item->device_id = device_id; + item->name = SDL_strdup (name); + if (item->name == NULL) { + SDL_free (item); + return -1; + } + + if (SDL_hapticlist_tail == NULL) { + SDL_hapticlist = SDL_hapticlist_tail = item; + } else { + SDL_hapticlist_tail->next = item; + SDL_hapticlist_tail = item; + } + + ++numhaptics; + return numhaptics; +} + +int +Android_RemoveHaptic(int device_id) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *prev = NULL; + + for (item = SDL_hapticlist; item != NULL; item = item->next) { + /* found it, remove it. */ + if (device_id == item->device_id) { + const int retval = item->haptic ? item->haptic->index : -1; + + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_hapticlist == item); + SDL_hapticlist = item->next; + } + if (item == SDL_hapticlist_tail) { + SDL_hapticlist_tail = prev; + } + + /* Need to decrement the haptic count */ + --numhaptics; + /* !!! TODO: Send a haptic remove event? */ + + SDL_free(item->name); + SDL_free(item); + return retval; + } + prev = item; + } + return -1; +} + + +#endif /* SDL_HAPTIC_ANDROID */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/android/SDL_syshaptic_c.h b/source/3rd-party/SDL2/src/haptic/android/SDL_syshaptic_c.h new file mode 100644 index 0000000..08634d2 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/android/SDL_syshaptic_c.h @@ -0,0 +1,12 @@ +#include "SDL_config.h" + +#ifdef SDL_HAPTIC_ANDROID + + +extern int Android_AddHaptic(int device_id, const char *name); +extern int Android_RemoveHaptic(int device_id); + + +#endif /* SDL_HAPTIC_ANDROID */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/darwin/SDL_syshaptic.c b/source/3rd-party/SDL2/src/haptic/darwin/SDL_syshaptic.c new file mode 100644 index 0000000..67cb9f5 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/darwin/SDL_syshaptic.c @@ -0,0 +1,1417 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_HAPTIC_IOKIT + +#include "SDL_assert.h" +#include "SDL_stdinc.h" +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/darwin/SDL_sysjoystick_c.h" /* For joystick hwdata */ +#include "SDL_syshaptic_c.h" + +#include <IOKit/IOKitLib.h> +#include <IOKit/hid/IOHIDKeys.h> +#include <IOKit/hid/IOHIDUsageTables.h> +#include <ForceFeedback/ForceFeedback.h> +#include <ForceFeedback/ForceFeedbackConstants.h> + +#ifndef IO_OBJECT_NULL +#define IO_OBJECT_NULL ((io_service_t)0) +#endif + +/* + * List of available haptic devices. + */ +typedef struct SDL_hapticlist_item +{ + char name[256]; /* Name of the device. */ + + io_service_t dev; /* Node we use to create the device. */ + SDL_Haptic *haptic; /* Haptic currently associated with it. */ + + /* Usage pages for determining if it's a mouse or not. */ + long usage; + long usagePage; + + struct SDL_hapticlist_item *next; +} SDL_hapticlist_item; + + +/* + * Haptic system hardware data. + */ +struct haptic_hwdata +{ + FFDeviceObjectReference device; /* Hardware device. */ + UInt8 axes[3]; +}; + + +/* + * Haptic system effect data. + */ +struct haptic_hweffect +{ + FFEffectObjectReference ref; /* Reference. */ + struct FFEFFECT effect; /* Hardware effect. */ +}; + +/* + * Prototypes. + */ +static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type); +static int HIDGetDeviceProduct(io_service_t dev, char *name); + +static SDL_hapticlist_item *SDL_hapticlist = NULL; +static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; +static int numhaptics = -1; + +/* + * Like strerror but for force feedback errors. + */ +static const char * +FFStrError(unsigned int err) +{ + switch (err) { + case FFERR_DEVICEFULL: + return "device full"; + /* This should be valid, but for some reason isn't defined... */ + /* case FFERR_DEVICENOTREG: + return "device not registered"; */ + case FFERR_DEVICEPAUSED: + return "device paused"; + case FFERR_DEVICERELEASED: + return "device released"; + case FFERR_EFFECTPLAYING: + return "effect playing"; + case FFERR_EFFECTTYPEMISMATCH: + return "effect type mismatch"; + case FFERR_EFFECTTYPENOTSUPPORTED: + return "effect type not supported"; + case FFERR_GENERIC: + return "undetermined error"; + case FFERR_HASEFFECTS: + return "device has effects"; + case FFERR_INCOMPLETEEFFECT: + return "incomplete effect"; + case FFERR_INTERNAL: + return "internal fault"; + case FFERR_INVALIDDOWNLOADID: + return "invalid download id"; + case FFERR_INVALIDPARAM: + return "invalid parameter"; + case FFERR_MOREDATA: + return "more data"; + case FFERR_NOINTERFACE: + return "interface not supported"; + case FFERR_NOTDOWNLOADED: + return "effect is not downloaded"; + case FFERR_NOTINITIALIZED: + return "object has not been initialized"; + case FFERR_OUTOFMEMORY: + return "out of memory"; + case FFERR_UNPLUGGED: + return "device is unplugged"; + case FFERR_UNSUPPORTED: + return "function call unsupported"; + case FFERR_UNSUPPORTEDAXIS: + return "axis unsupported"; + + default: + return "unknown error"; + } +} + + +/* + * Initializes the haptic subsystem. + */ +int +SDL_SYS_HapticInit(void) +{ + IOReturn result; + io_iterator_t iter; + CFDictionaryRef match; + io_service_t device; + + if (numhaptics != -1) { + return SDL_SetError("Haptic subsystem already initialized!"); + } + numhaptics = 0; + + /* Get HID devices. */ + match = IOServiceMatching(kIOHIDDeviceKey); + if (match == NULL) { + return SDL_SetError("Haptic: Failed to get IOServiceMatching."); + } + + /* Now search I/O Registry for matching devices. */ + result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter); + if (result != kIOReturnSuccess) { + return SDL_SetError("Haptic: Couldn't create a HID object iterator."); + } + /* IOServiceGetMatchingServices consumes dictionary. */ + + if (!IOIteratorIsValid(iter)) { /* No iterator. */ + return 0; + } + + while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) { + MacHaptic_MaybeAddDevice(device); + /* always release as the AddDevice will retain IF it's a forcefeedback device */ + IOObjectRelease(device); + } + IOObjectRelease(iter); + + return numhaptics; +} + +int +SDL_SYS_NumHaptics(void) +{ + return numhaptics; +} + +static SDL_hapticlist_item * +HapticByDevIndex(int device_index) +{ + SDL_hapticlist_item *item = SDL_hapticlist; + + if ((device_index < 0) || (device_index >= numhaptics)) { + return NULL; + } + + while (device_index > 0) { + SDL_assert(item != NULL); + --device_index; + item = item->next; + } + + return item; +} + +int +MacHaptic_MaybeAddDevice( io_object_t device ) +{ + IOReturn result; + CFMutableDictionaryRef hidProperties; + CFTypeRef refCF; + SDL_hapticlist_item *item; + + if (numhaptics == -1) { + return -1; /* not initialized. We'll pick these up on enumeration if we init later. */ + } + + /* Check for force feedback. */ + if (FFIsForceFeedback(device) != FF_OK) { + return -1; + } + + /* Make sure we don't already have it */ + for (item = SDL_hapticlist; item ; item = item->next) + { + if (IOObjectIsEqualTo((io_object_t) item->dev, device)) { + /* Already added */ + return -1; + } + } + + item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item)); + if (item == NULL) { + return SDL_SetError("Could not allocate haptic storage"); + } + + /* retain it as we are going to keep it around a while */ + IOObjectRetain(device); + + /* Set basic device data. */ + HIDGetDeviceProduct(device, item->name); + item->dev = device; + + /* Set usage pages. */ + hidProperties = 0; + refCF = 0; + result = IORegistryEntryCreateCFProperties(device, + &hidProperties, + kCFAllocatorDefault, + kNilOptions); + if ((result == KERN_SUCCESS) && hidProperties) { + refCF = CFDictionaryGetValue(hidProperties, + CFSTR(kIOHIDPrimaryUsagePageKey)); + if (refCF) { + if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usagePage)) { + SDL_SetError("Haptic: Receiving device's usage page."); + } + refCF = CFDictionaryGetValue(hidProperties, + CFSTR(kIOHIDPrimaryUsageKey)); + if (refCF) { + if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usage)) { + SDL_SetError("Haptic: Receiving device's usage."); + } + } + } + CFRelease(hidProperties); + } + + if (SDL_hapticlist_tail == NULL) { + SDL_hapticlist = SDL_hapticlist_tail = item; + } else { + SDL_hapticlist_tail->next = item; + SDL_hapticlist_tail = item; + } + + /* Device has been added. */ + ++numhaptics; + + return numhaptics; +} + +int +MacHaptic_MaybeRemoveDevice( io_object_t device ) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *prev = NULL; + + if (numhaptics == -1) { + return -1; /* not initialized. ignore this. */ + } + + for (item = SDL_hapticlist; item != NULL; item = item->next) { + /* found it, remove it. */ + if (IOObjectIsEqualTo((io_object_t) item->dev, device)) { + const int retval = item->haptic ? item->haptic->index : -1; + + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_hapticlist == item); + SDL_hapticlist = item->next; + } + if (item == SDL_hapticlist_tail) { + SDL_hapticlist_tail = prev; + } + + /* Need to decrement the haptic count */ + --numhaptics; + /* !!! TODO: Send a haptic remove event? */ + + IOObjectRelease(item->dev); + SDL_free(item); + return retval; + } + prev = item; + } + + return -1; +} + +/* + * Return the name of a haptic device, does not need to be opened. + */ +const char * +SDL_SYS_HapticName(int index) +{ + SDL_hapticlist_item *item; + item = HapticByDevIndex(index); + return item->name; +} + +/* + * Gets the device's product name. + */ +static int +HIDGetDeviceProduct(io_service_t dev, char *name) +{ + CFMutableDictionaryRef hidProperties, usbProperties; + io_registry_entry_t parent1, parent2; + kern_return_t ret; + + hidProperties = usbProperties = 0; + + ret = IORegistryEntryCreateCFProperties(dev, &hidProperties, + kCFAllocatorDefault, kNilOptions); + if ((ret != KERN_SUCCESS) || !hidProperties) { + return SDL_SetError("Haptic: Unable to create CFProperties."); + } + + /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also + * get dictionary for USB properties: step up two levels and get CF dictionary for USB properties + */ + if ((KERN_SUCCESS == + IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1)) + && (KERN_SUCCESS == + IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2)) + && (KERN_SUCCESS == + IORegistryEntryCreateCFProperties(parent2, &usbProperties, + kCFAllocatorDefault, + kNilOptions))) { + if (usbProperties) { + CFTypeRef refCF = 0; + /* get device info + * try hid dictionary first, if fail then go to USB dictionary + */ + + + /* Get product name */ + refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey)); + if (!refCF) { + refCF = CFDictionaryGetValue(usbProperties, + CFSTR("USB Product Name")); + } + if (refCF) { + if (!CFStringGetCString(refCF, name, 256, + CFStringGetSystemEncoding())) { + return SDL_SetError("Haptic: CFStringGetCString error retrieving pDevice->product."); + } + } + + CFRelease(usbProperties); + } else { + return SDL_SetError("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties."); + } + + /* Release stuff. */ + if (kIOReturnSuccess != IOObjectRelease(parent2)) { + SDL_SetError("Haptic: IOObjectRelease error with parent2."); + } + if (kIOReturnSuccess != IOObjectRelease(parent1)) { + SDL_SetError("Haptic: IOObjectRelease error with parent1."); + } + } else { + return SDL_SetError("Haptic: Error getting registry entries."); + } + + return 0; +} + + +#define FF_TEST(ff, s) \ +if (features.supportedEffects & (ff)) supported |= (s) +/* + * Gets supported features. + */ +static unsigned int +GetSupportedFeatures(SDL_Haptic * haptic) +{ + HRESULT ret; + FFDeviceObjectReference device; + FFCAPABILITIES features; + unsigned int supported; + Uint32 val; + + device = haptic->hwdata->device; + + ret = FFDeviceGetForceFeedbackCapabilities(device, &features); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Unable to get device's supported features."); + } + + supported = 0; + + /* Get maximum effects. */ + haptic->neffects = features.storageCapacity; + haptic->nplaying = features.playbackCapacity; + + /* Test for effects. */ + FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT); + FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP); + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE); */ + FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE); + FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE); + FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP); + FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN); + FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING); + FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER); + FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA); + FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION); + FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM); + + /* Check if supports gain. */ + ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN, + &val, sizeof(val)); + if (ret == FF_OK) { + supported |= SDL_HAPTIC_GAIN; + } else if (ret != FFERR_UNSUPPORTED) { + return SDL_SetError("Haptic: Unable to get if device supports gain: %s.", + FFStrError(ret)); + } + + /* Checks if supports autocenter. */ + ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER, + &val, sizeof(val)); + if (ret == FF_OK) { + supported |= SDL_HAPTIC_AUTOCENTER; + } else if (ret != FFERR_UNSUPPORTED) { + return SDL_SetError + ("Haptic: Unable to get if device supports autocenter: %s.", + FFStrError(ret)); + } + + /* Check for axes, we have an artificial limit on axes */ + haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes; + /* Actually store the axes we want to use */ + SDL_memcpy(haptic->hwdata->axes, features.ffAxes, + haptic->naxes * sizeof(Uint8)); + + /* Always supported features. */ + supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE; + + haptic->supported = supported; + return 0; +} + + +/* + * Opens the haptic device from the file descriptor. + */ +static int +SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service) +{ + HRESULT ret; + int ret2; + + /* Allocate the hwdata */ + haptic->hwdata = (struct haptic_hwdata *) + SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_OutOfMemory(); + goto creat_err; + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + /* Open the device */ + ret = FFCreateDevice(service, &haptic->hwdata->device); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to create device from service: %s.", + FFStrError(ret)); + goto creat_err; + } + + /* Get supported features. */ + ret2 = GetSupportedFeatures(haptic); + if (ret2 < 0) { + goto open_err; + } + + + /* Reset and then enable actuators. */ + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_RESET); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret)); + goto open_err; + } + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_SETACTUATORSON); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to enable actuators: %s.", + FFStrError(ret)); + goto open_err; + } + + + /* Allocate effects memory. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + SDL_OutOfMemory(); + goto open_err; + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + return 0; + + /* Error handling */ + open_err: + FFReleaseDevice(haptic->hwdata->device); + creat_err: + if (haptic->hwdata != NULL) { + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } + return -1; + +} + + +/* + * Opens a haptic device for usage. + */ +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + SDL_hapticlist_item *item; + item = HapticByDevIndex(haptic->index); + + return SDL_SYS_HapticOpenFromService(haptic, item->dev); +} + + +/* + * Opens a haptic device from first mouse it finds for usage. + */ +int +SDL_SYS_HapticMouse(void) +{ + int device_index = 0; + SDL_hapticlist_item *item; + + for (item = SDL_hapticlist; item; item = item->next) { + if ((item->usagePage == kHIDPage_GenericDesktop) && + (item->usage == kHIDUsage_GD_Mouse)) { + return device_index; + } + ++device_index; + } + + return -1; +} + + +/* + * Checks to see if a joystick has haptic features. + */ +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + if (joystick->hwdata->ffservice != 0) { + return SDL_TRUE; + } + return SDL_FALSE; +} + + +/* + * Checks to see if the haptic device and joystick are in reality the same. + */ +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + if (IOObjectIsEqualTo((io_object_t) ((size_t)haptic->hwdata->device), + joystick->hwdata->ffservice)) { + return 1; + } + return 0; +} + + +/* + * Opens a SDL_Haptic from a SDL_Joystick. + */ +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + int device_index = 0; + SDL_hapticlist_item *item; + + for (item = SDL_hapticlist; item; item = item->next) { + if (IOObjectIsEqualTo((io_object_t) item->dev, + joystick->hwdata->ffservice)) { + haptic->index = device_index; + break; + } + ++device_index; + } + + return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice); +} + + +/* + * Closes the haptic device. + */ +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + if (haptic->hwdata) { + + /* Free Effects. */ + SDL_free(haptic->effects); + haptic->effects = NULL; + haptic->neffects = 0; + + /* Clean up */ + FFReleaseDevice(haptic->hwdata->device); + + /* Free */ + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } +} + + +/* + * Clean up after system specific haptic stuff + */ +void +SDL_SYS_HapticQuit(void) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *next = NULL; + + for (item = SDL_hapticlist; item; item = next) { + next = item->next; + /* Opened and not closed haptics are leaked, this is on purpose. + * Close your haptic devices after usage. */ + + /* Free the io_service_t */ + IOObjectRelease(item->dev); + SDL_free(item); + } + + numhaptics = -1; + SDL_hapticlist = NULL; + SDL_hapticlist_tail = NULL; +} + + +/* + * Converts an SDL trigger button to an FFEFFECT trigger button. + */ +static DWORD +FFGetTriggerButton(Uint16 button) +{ + DWORD dwTriggerButton; + + dwTriggerButton = FFEB_NOTRIGGER; + + if (button != 0) { + dwTriggerButton = FFJOFS_BUTTON(button - 1); + } + + return dwTriggerButton; +} + + +/* + * Sets the direction. + */ +static int +SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes) +{ + LONG *rglDir; + + /* Handle no axes a part. */ + if (naxes == 0) { + effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */ + effect->rglDirection = NULL; + return 0; + } + + /* Has axes. */ + rglDir = SDL_malloc(sizeof(LONG) * naxes); + if (rglDir == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(rglDir, 0, sizeof(LONG) * naxes); + effect->rglDirection = rglDir; + + switch (dir->type) { + case SDL_HAPTIC_POLAR: + effect->dwFlags |= FFEFF_POLAR; + rglDir[0] = dir->dir[0]; + return 0; + case SDL_HAPTIC_CARTESIAN: + effect->dwFlags |= FFEFF_CARTESIAN; + rglDir[0] = dir->dir[0]; + if (naxes > 1) { + rglDir[1] = dir->dir[1]; + } + if (naxes > 2) { + rglDir[2] = dir->dir[2]; + } + return 0; + case SDL_HAPTIC_SPHERICAL: + effect->dwFlags |= FFEFF_SPHERICAL; + rglDir[0] = dir->dir[0]; + if (naxes > 1) { + rglDir[1] = dir->dir[1]; + } + if (naxes > 2) { + rglDir[2] = dir->dir[2]; + } + return 0; + + default: + return SDL_SetError("Haptic: Unknown direction type."); + } +} + + +/* Clamps and converts. */ +#define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) +/* Just converts. */ +#define CONVERT(x) (((x)*10000) / 0x7FFF) +/* + * Creates the FFEFFECT from a SDL_HapticEffect. + */ +static int +SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src) +{ + int i; + FFCONSTANTFORCE *constant = NULL; + FFPERIODIC *periodic = NULL; + FFCONDITION *condition = NULL; /* Actually an array of conditions - one per axis. */ + FFRAMPFORCE *ramp = NULL; + FFCUSTOMFORCE *custom = NULL; + FFENVELOPE *envelope = NULL; + SDL_HapticConstant *hap_constant = NULL; + SDL_HapticPeriodic *hap_periodic = NULL; + SDL_HapticCondition *hap_condition = NULL; + SDL_HapticRamp *hap_ramp = NULL; + SDL_HapticCustom *hap_custom = NULL; + DWORD *axes = NULL; + + /* Set global stuff. */ + SDL_memset(dest, 0, sizeof(FFEFFECT)); + dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */ + dest->dwSamplePeriod = 0; /* Not used by us. */ + dest->dwGain = 10000; /* Gain is set globally, not locally. */ + dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */ + + /* Envelope. */ + envelope = SDL_malloc(sizeof(FFENVELOPE)); + if (envelope == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(envelope, 0, sizeof(FFENVELOPE)); + dest->lpEnvelope = envelope; + envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */ + + /* Axes. */ + dest->cAxes = haptic->naxes; + if (dest->cAxes > 0) { + axes = SDL_malloc(sizeof(DWORD) * dest->cAxes); + if (axes == NULL) { + return SDL_OutOfMemory(); + } + axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */ + if (dest->cAxes > 1) { + axes[1] = haptic->hwdata->axes[1]; + } + if (dest->cAxes > 2) { + axes[2] = haptic->hwdata->axes[2]; + } + dest->rgdwAxes = axes; + } + + + /* The big type handling switch, even bigger then Linux's version. */ + switch (src->type) { + case SDL_HAPTIC_CONSTANT: + hap_constant = &src->constant; + constant = SDL_malloc(sizeof(FFCONSTANTFORCE)); + if (constant == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE)); + + /* Specifics */ + constant->lMagnitude = CONVERT(hap_constant->level); + dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE); + dest->lpvTypeSpecificParams = constant; + + /* Generics */ + dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button); + dest->dwTriggerRepeatInterval = hap_constant->interval; + dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope */ + if ((hap_constant->attack_length == 0) + && (hap_constant->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level); + envelope->dwAttackTime = hap_constant->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level); + envelope->dwFadeTime = hap_constant->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SINE: + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* case SDL_HAPTIC_SQUARE: */ + case SDL_HAPTIC_TRIANGLE: + case SDL_HAPTIC_SAWTOOTHUP: + case SDL_HAPTIC_SAWTOOTHDOWN: + hap_periodic = &src->periodic; + periodic = SDL_malloc(sizeof(FFPERIODIC)); + if (periodic == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(periodic, 0, sizeof(FFPERIODIC)); + + /* Specifics */ + periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude)); + periodic->lOffset = CONVERT(hap_periodic->offset); + periodic->dwPhase = + (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000; + periodic->dwPeriod = hap_periodic->period * 1000; + dest->cbTypeSpecificParams = sizeof(FFPERIODIC); + dest->lpvTypeSpecificParams = periodic; + + /* Generics */ + dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button); + dest->dwTriggerRepeatInterval = hap_periodic->interval; + dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope */ + if ((hap_periodic->attack_length == 0) + && (hap_periodic->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level); + envelope->dwAttackTime = hap_periodic->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level); + envelope->dwFadeTime = hap_periodic->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SPRING: + case SDL_HAPTIC_DAMPER: + case SDL_HAPTIC_INERTIA: + case SDL_HAPTIC_FRICTION: + hap_condition = &src->condition; + if (dest->cAxes > 0) { + condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes); + if (condition == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(condition, 0, sizeof(FFCONDITION)); + + /* Specifics */ + for (i = 0; i < dest->cAxes; i++) { + condition[i].lOffset = CONVERT(hap_condition->center[i]); + condition[i].lPositiveCoefficient = + CONVERT(hap_condition->right_coeff[i]); + condition[i].lNegativeCoefficient = + CONVERT(hap_condition->left_coeff[i]); + condition[i].dwPositiveSaturation = + CCONVERT(hap_condition->right_sat[i] / 2); + condition[i].dwNegativeSaturation = + CCONVERT(hap_condition->left_sat[i] / 2); + condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2); + } + } + + dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes; + dest->lpvTypeSpecificParams = condition; + + /* Generics */ + dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button); + dest->dwTriggerRepeatInterval = hap_condition->interval; + dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope - Not actually supported by most CONDITION implementations. */ + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + + break; + + case SDL_HAPTIC_RAMP: + hap_ramp = &src->ramp; + ramp = SDL_malloc(sizeof(FFRAMPFORCE)); + if (ramp == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(ramp, 0, sizeof(FFRAMPFORCE)); + + /* Specifics */ + ramp->lStart = CONVERT(hap_ramp->start); + ramp->lEnd = CONVERT(hap_ramp->end); + dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE); + dest->lpvTypeSpecificParams = ramp; + + /* Generics */ + dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button); + dest->dwTriggerRepeatInterval = hap_ramp->interval; + dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) { + return -1; + } + + /* Envelope */ + if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level); + envelope->dwAttackTime = hap_ramp->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level); + envelope->dwFadeTime = hap_ramp->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_CUSTOM: + hap_custom = &src->custom; + custom = SDL_malloc(sizeof(FFCUSTOMFORCE)); + if (custom == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE)); + + /* Specifics */ + custom->cChannels = hap_custom->channels; + custom->dwSamplePeriod = hap_custom->period * 1000; + custom->cSamples = hap_custom->samples; + custom->rglForceData = + SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels); + for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */ + custom->rglForceData[i] = CCONVERT(hap_custom->data[i]); + } + dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE); + dest->lpvTypeSpecificParams = custom; + + /* Generics */ + dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button); + dest->dwTriggerRepeatInterval = hap_custom->interval; + dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < + 0) { + return -1; + } + + /* Envelope */ + if ((hap_custom->attack_length == 0) + && (hap_custom->fade_length == 0)) { + SDL_free(envelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level); + envelope->dwAttackTime = hap_custom->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level); + envelope->dwFadeTime = hap_custom->fade_length * 1000; + } + + break; + + + default: + return SDL_SetError("Haptic: Unknown effect type."); + } + + return 0; +} + + +/* + * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT. + */ +static void +SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type) +{ + FFCUSTOMFORCE *custom; + + SDL_free(effect->lpEnvelope); + effect->lpEnvelope = NULL; + SDL_free(effect->rgdwAxes); + effect->rgdwAxes = NULL; + if (effect->lpvTypeSpecificParams != NULL) { + if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */ + custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams; + SDL_free(custom->rglForceData); + custom->rglForceData = NULL; + } + SDL_free(effect->lpvTypeSpecificParams); + effect->lpvTypeSpecificParams = NULL; + } + SDL_free(effect->rglDirection); + effect->rglDirection = NULL; +} + + +/* + * Gets the effect type from the generic SDL haptic effect wrapper. + */ +CFUUIDRef +SDL_SYS_HapticEffectType(Uint16 type) +{ + switch (type) { + case SDL_HAPTIC_CONSTANT: + return kFFEffectType_ConstantForce_ID; + + case SDL_HAPTIC_RAMP: + return kFFEffectType_RampForce_ID; + + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* case SDL_HAPTIC_SQUARE: + return kFFEffectType_Square_ID; */ + + case SDL_HAPTIC_SINE: + return kFFEffectType_Sine_ID; + + case SDL_HAPTIC_TRIANGLE: + return kFFEffectType_Triangle_ID; + + case SDL_HAPTIC_SAWTOOTHUP: + return kFFEffectType_SawtoothUp_ID; + + case SDL_HAPTIC_SAWTOOTHDOWN: + return kFFEffectType_SawtoothDown_ID; + + case SDL_HAPTIC_SPRING: + return kFFEffectType_Spring_ID; + + case SDL_HAPTIC_DAMPER: + return kFFEffectType_Damper_ID; + + case SDL_HAPTIC_INERTIA: + return kFFEffectType_Inertia_ID; + + case SDL_HAPTIC_FRICTION: + return kFFEffectType_Friction_ID; + + case SDL_HAPTIC_CUSTOM: + return kFFEffectType_CustomForce_ID; + + default: + SDL_SetError("Haptic: Unknown effect type."); + return NULL; + } +} + + +/* + * Creates a new haptic effect. + */ +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + SDL_HapticEffect * base) +{ + HRESULT ret; + CFUUIDRef type; + + /* Alloc the effect. */ + effect->hweffect = (struct haptic_hweffect *) + SDL_malloc(sizeof(struct haptic_hweffect)); + if (effect->hweffect == NULL) { + SDL_OutOfMemory(); + goto err_hweffect; + } + + /* Get the type. */ + type = SDL_SYS_HapticEffectType(base->type); + if (type == NULL) { + goto err_hweffect; + } + + /* Get the effect. */ + if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) { + goto err_effectdone; + } + + /* Create the actual effect. */ + ret = FFDeviceCreateEffect(haptic->hwdata->device, type, + &effect->hweffect->effect, + &effect->hweffect->ref); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret)); + goto err_effectdone; + } + + return 0; + + err_effectdone: + SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type); + err_hweffect: + SDL_free(effect->hweffect); + effect->hweffect = NULL; + return -1; +} + + +/* + * Updates an effect. + */ +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + HRESULT ret; + FFEffectParameterFlag flags; + FFEFFECT temp; + + /* Get the effect. */ + SDL_memset(&temp, 0, sizeof(FFEFFECT)); + if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) { + goto err_update; + } + + /* Set the flags. Might be worthwhile to diff temp with loaded effect and + * only change those parameters. */ + flags = FFEP_DIRECTION | + FFEP_DURATION | + FFEP_ENVELOPE | + FFEP_STARTDELAY | + FFEP_TRIGGERBUTTON | + FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS; + + /* Create the actual effect. */ + ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret)); + goto err_update; + } + + /* Copy it over. */ + SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type); + SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT)); + + return 0; + + err_update: + SDL_SYS_HapticFreeFFEFFECT(&temp, data->type); + return -1; +} + + +/* + * Runs an effect. + */ +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + HRESULT ret; + Uint32 iter; + + /* Check if it's infinite. */ + if (iterations == SDL_HAPTIC_INFINITY) { + iter = FF_INFINITE; + } else + iter = iterations; + + /* Run the effect. */ + ret = FFEffectStart(effect->hweffect->ref, iter, 0); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Unable to run the effect: %s.", + FFStrError(ret)); + } + + return 0; +} + + +/* + * Stops an effect. + */ +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = FFEffectStop(effect->hweffect->ref); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Unable to stop the effect: %s.", + FFStrError(ret)); + } + + return 0; +} + + +/* + * Frees the effect. + */ +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref); + if (ret != FF_OK) { + SDL_SetError("Haptic: Error removing the effect from the device: %s.", + FFStrError(ret)); + } + SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, + effect->effect.type); + SDL_free(effect->hweffect); + effect->hweffect = NULL; +} + + +/* + * Gets the status of a haptic effect. + */ +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + HRESULT ret; + FFEffectStatusFlag status; + + ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status); + if (ret != FF_OK) { + SDL_SetError("Haptic: Unable to get effect status: %s.", + FFStrError(ret)); + return -1; + } + + if (status == 0) { + return SDL_FALSE; + } + return SDL_TRUE; /* Assume it's playing or emulated. */ +} + + +/* + * Sets the gain. + */ +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + HRESULT ret; + Uint32 val; + + val = gain * 100; /* Mac OS X uses 0 to 10,000 */ + ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, + FFPROP_FFGAIN, &val); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret)); + } + + return 0; +} + + +/* + * Sets the autocentering. + */ +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + HRESULT ret; + Uint32 val; + + /* Mac OS X only has 0 (off) and 1 (on) */ + if (autocenter == 0) { + val = 0; + } else { + val = 1; + } + + ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, + FFPROP_AUTOCENTER, &val); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Error setting autocenter: %s.", + FFStrError(ret)); + } + + return 0; +} + + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + HRESULT ret; + + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_PAUSE); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); + } + + return 0; +} + + +/* + * Unpauses the device. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + HRESULT ret; + + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_CONTINUE); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); + } + + return 0; +} + + +/* + * Stops all currently playing effects. + */ +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + HRESULT ret; + + ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, + FFSFFC_STOPALL); + if (ret != FF_OK) { + return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret)); + } + + return 0; +} + +#endif /* SDL_HAPTIC_IOKIT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/darwin/SDL_syshaptic_c.h b/source/3rd-party/SDL2/src/haptic/darwin/SDL_syshaptic_c.h new file mode 100644 index 0000000..073db53 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/darwin/SDL_syshaptic_c.h @@ -0,0 +1,26 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +extern int MacHaptic_MaybeAddDevice( io_object_t device ); +extern int MacHaptic_MaybeRemoveDevice( io_object_t device ); + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/source/3rd-party/SDL2/src/haptic/dummy/SDL_syshaptic.c b/source/3rd-party/SDL2/src/haptic/dummy/SDL_syshaptic.c new file mode 100644 index 0000000..283fe67 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/dummy/SDL_syshaptic.c @@ -0,0 +1,186 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if defined(SDL_HAPTIC_DUMMY) || defined(SDL_HAPTIC_DISABLED) + +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" + + +static int +SDL_SYS_LogicError(void) +{ + return SDL_SetError("Logic error: No haptic devices available."); +} + + +int +SDL_SYS_HapticInit(void) +{ + return 0; +} + +int +SDL_SYS_NumHaptics(void) +{ + return 0; +} + +const char * +SDL_SYS_HapticName(int index) +{ + SDL_SYS_LogicError(); + return NULL; +} + + +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + return SDL_SYS_LogicError(); +} + + +int +SDL_SYS_HapticMouse(void) +{ + return -1; +} + + +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + return 0; +} + + +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_SYS_LogicError(); +} + + +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return 0; +} + + +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + return; +} + + +void +SDL_SYS_HapticQuit(void) +{ + return; +} + + +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, SDL_HapticEffect * base) +{ + return SDL_SYS_LogicError(); +} + + +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + return SDL_SYS_LogicError(); +} + + +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + return SDL_SYS_LogicError(); +} + + +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_SYS_LogicError(); +} + + +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + SDL_SYS_LogicError(); + return; +} + + +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + return SDL_SYS_LogicError(); +} + + +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return SDL_SYS_LogicError(); +} + + +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return SDL_SYS_LogicError(); +} + +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + return SDL_SYS_LogicError(); +} + +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + return SDL_SYS_LogicError(); +} + +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + return SDL_SYS_LogicError(); +} + +#endif /* SDL_HAPTIC_DUMMY || SDL_HAPTIC_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/linux/SDL_syshaptic.c b/source/3rd-party/SDL2/src/haptic/linux/SDL_syshaptic.c new file mode 100644 index 0000000..4e4f8a5 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/linux/SDL_syshaptic.c @@ -0,0 +1,1166 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_HAPTIC_LINUX + +#include "SDL_assert.h" +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/linux/SDL_sysjoystick_c.h" /* For joystick hwdata */ +#include "../../core/linux/SDL_udev.h" + +#include <unistd.h> /* close */ +#include <linux/input.h> /* Force feedback linux stuff. */ +#include <fcntl.h> /* O_RDWR */ +#include <limits.h> /* INT_MAX */ +#include <errno.h> /* errno, strerror */ +#include <math.h> /* atan2 */ +#include <sys/stat.h> /* stat */ + +/* Just in case. */ +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + + +#define MAX_HAPTICS 32 /* It's doubtful someone has more then 32 evdev */ + +static int MaybeAddDevice(const char *path); +#if SDL_USE_LIBUDEV +static int MaybeRemoveDevice(const char *path); +static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); +#endif /* SDL_USE_LIBUDEV */ + +/* + * List of available haptic devices. + */ +typedef struct SDL_hapticlist_item +{ + char *fname; /* Dev path name (like /dev/input/event1) */ + SDL_Haptic *haptic; /* Associated haptic. */ + dev_t dev_num; + struct SDL_hapticlist_item *next; +} SDL_hapticlist_item; + + +/* + * Haptic system hardware data. + */ +struct haptic_hwdata +{ + int fd; /* File descriptor of the device. */ + char *fname; /* Points to the name in SDL_hapticlist. */ +}; + + +/* + * Haptic system effect data. + */ +struct haptic_hweffect +{ + struct ff_effect effect; /* The linux kernel effect structure. */ +}; + +static SDL_hapticlist_item *SDL_hapticlist = NULL; +static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; +static int numhaptics = 0; + +#define test_bit(nr, addr) \ + (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0) +#define EV_TEST(ev,f) \ + if (test_bit((ev), features)) ret |= (f); +/* + * Test whether a device has haptic properties. + * Returns available properties or 0 if there are none. + */ +static int +EV_IsHaptic(int fd) +{ + unsigned int ret; + unsigned long features[1 + FF_MAX / sizeof(unsigned long)]; + + /* Ask device for what it has. */ + ret = 0; + if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) { + return SDL_SetError("Haptic: Unable to get device's features: %s", + strerror(errno)); + } + + /* Convert supported features to SDL_HAPTIC platform-neutral features. */ + EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT); + EV_TEST(FF_SINE, SDL_HAPTIC_SINE); + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE); */ + EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE); + EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP); + EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN); + EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP); + EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING); + EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION); + EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER); + EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA); + EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM); + EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN); + EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER); + EV_TEST(FF_RUMBLE, SDL_HAPTIC_LEFTRIGHT); + + /* Return what it supports. */ + return ret; +} + + +/* + * Tests whether a device is a mouse or not. + */ +static int +EV_IsMouse(int fd) +{ + unsigned long argp[40]; + + /* Ask for supported features. */ + if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) { + return -1; + } + + /* Currently we only test for BTN_MOUSE which can give fake positives. */ + if (test_bit(BTN_MOUSE, argp) != 0) { + return 1; + } + + return 0; +} + +/* + * Initializes the haptic subsystem by finding available devices. + */ +int +SDL_SYS_HapticInit(void) +{ + const char joydev_pattern[] = "/dev/input/event%d"; + char path[PATH_MAX]; + int i, j; + + /* + * Limit amount of checks to MAX_HAPTICS since we may or may not have + * permission to some or all devices. + */ + i = 0; + for (j = 0; j < MAX_HAPTICS; ++j) { + + snprintf(path, PATH_MAX, joydev_pattern, i++); + MaybeAddDevice(path); + } + +#if SDL_USE_LIBUDEV + if (SDL_UDEV_Init() < 0) { + return SDL_SetError("Could not initialize UDEV"); + } + + if ( SDL_UDEV_AddCallback(haptic_udev_callback) < 0) { + SDL_UDEV_Quit(); + return SDL_SetError("Could not setup haptic <-> udev callback"); + } + + /* Force a scan to build the initial device list */ + SDL_UDEV_Scan(); +#endif /* SDL_USE_LIBUDEV */ + + return numhaptics; +} + +int +SDL_SYS_NumHaptics(void) +{ + return numhaptics; +} + +static SDL_hapticlist_item * +HapticByDevIndex(int device_index) +{ + SDL_hapticlist_item *item = SDL_hapticlist; + + if ((device_index < 0) || (device_index >= numhaptics)) { + return NULL; + } + + while (device_index > 0) { + SDL_assert(item != NULL); + --device_index; + item = item->next; + } + + return item; +} + +#if SDL_USE_LIBUDEV +static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) +{ + if (devpath == NULL || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) { + return; + } + + switch( udev_type ) + { + case SDL_UDEV_DEVICEADDED: + MaybeAddDevice(devpath); + break; + + case SDL_UDEV_DEVICEREMOVED: + MaybeRemoveDevice(devpath); + break; + + default: + break; + } + +} +#endif /* SDL_USE_LIBUDEV */ + +static int +MaybeAddDevice(const char *path) +{ + struct stat sb; + int fd; + int success; + SDL_hapticlist_item *item; + + if (path == NULL) { + return -1; + } + + /* check to see if file exists */ + if (stat(path, &sb) != 0) { + return -1; + } + + /* check for duplicates */ + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (item->dev_num == sb.st_rdev) { + return -1; /* duplicate. */ + } + } + + /* try to open */ + fd = open(path, O_RDWR, 0); + if (fd < 0) { + return -1; + } + +#ifdef DEBUG_INPUT_EVENTS + printf("Checking %s\n", path); +#endif + + /* see if it works */ + success = EV_IsHaptic(fd); + close(fd); + if (success <= 0) { + return -1; + } + + item = (SDL_hapticlist_item *) SDL_calloc(1, sizeof (SDL_hapticlist_item)); + if (item == NULL) { + return -1; + } + + item->fname = SDL_strdup(path); + if (item->fname == NULL) { + SDL_free(item); + return -1; + } + + item->dev_num = sb.st_rdev; + + /* TODO: should we add instance IDs? */ + if (SDL_hapticlist_tail == NULL) { + SDL_hapticlist = SDL_hapticlist_tail = item; + } else { + SDL_hapticlist_tail->next = item; + SDL_hapticlist_tail = item; + } + + ++numhaptics; + + /* !!! TODO: Send a haptic add event? */ + + return numhaptics; +} + +#if SDL_USE_LIBUDEV +static int +MaybeRemoveDevice(const char* path) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *prev = NULL; + + if (path == NULL) { + return -1; + } + + for (item = SDL_hapticlist; item != NULL; item = item->next) { + /* found it, remove it. */ + if (SDL_strcmp(path, item->fname) == 0) { + const int retval = item->haptic ? item->haptic->index : -1; + + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_hapticlist == item); + SDL_hapticlist = item->next; + } + if (item == SDL_hapticlist_tail) { + SDL_hapticlist_tail = prev; + } + + /* Need to decrement the haptic count */ + --numhaptics; + /* !!! TODO: Send a haptic remove event? */ + + SDL_free(item->fname); + SDL_free(item); + return retval; + } + prev = item; + } + + return -1; +} +#endif /* SDL_USE_LIBUDEV */ + +/* + * Gets the name from a file descriptor. + */ +static const char * +SDL_SYS_HapticNameFromFD(int fd) +{ + static char namebuf[128]; + + /* We use the evdev name ioctl. */ + if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) { + return NULL; + } + + return namebuf; +} + + +/* + * Return the name of a haptic device, does not need to be opened. + */ +const char * +SDL_SYS_HapticName(int index) +{ + SDL_hapticlist_item *item; + int fd; + const char *name; + + item = HapticByDevIndex(index); + /* Open the haptic device. */ + name = NULL; + fd = open(item->fname, O_RDONLY, 0); + + if (fd >= 0) { + + name = SDL_SYS_HapticNameFromFD(fd); + if (name == NULL) { + /* No name found, return device character device */ + name = item->fname; + } + close(fd); + } + + return name; +} + + +/* + * Opens the haptic device from the file descriptor. + */ +static int +SDL_SYS_HapticOpenFromFD(SDL_Haptic * haptic, int fd) +{ + /* Allocate the hwdata */ + haptic->hwdata = (struct haptic_hwdata *) + SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_OutOfMemory(); + goto open_err; + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + /* Set the data. */ + haptic->hwdata->fd = fd; + haptic->supported = EV_IsHaptic(fd); + haptic->naxes = 2; /* Hardcoded for now, not sure if it's possible to find out. */ + + /* Set the effects */ + if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) { + SDL_SetError("Haptic: Unable to query device memory: %s", + strerror(errno)); + goto open_err; + } + haptic->nplaying = haptic->neffects; /* Linux makes no distinction. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + SDL_OutOfMemory(); + goto open_err; + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + return 0; + + /* Error handling */ + open_err: + close(fd); + if (haptic->hwdata != NULL) { + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } + return -1; +} + + +/* + * Opens a haptic device for usage. + */ +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + int fd; + int ret; + SDL_hapticlist_item *item; + + item = HapticByDevIndex(haptic->index); + /* Open the character device */ + fd = open(item->fname, O_RDWR, 0); + if (fd < 0) { + return SDL_SetError("Haptic: Unable to open %s: %s", + item->fname, strerror(errno)); + } + + /* Try to create the haptic. */ + ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */ + if (ret < 0) { + return -1; + } + + /* Set the fname. */ + haptic->hwdata->fname = SDL_strdup( item->fname ); + return 0; +} + + +/* + * Opens a haptic device from first mouse it finds for usage. + */ +int +SDL_SYS_HapticMouse(void) +{ + int fd; + int device_index = 0; + SDL_hapticlist_item *item; + + for (item = SDL_hapticlist; item; item = item->next) { + /* Open the device. */ + fd = open(item->fname, O_RDWR, 0); + if (fd < 0) { + return SDL_SetError("Haptic: Unable to open %s: %s", + item->fname, strerror(errno)); + } + + /* Is it a mouse? */ + if (EV_IsMouse(fd)) { + close(fd); + return device_index; + } + + close(fd); + + ++device_index; + } + + return -1; +} + + +/* + * Checks to see if a joystick has haptic features. + */ +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + return EV_IsHaptic(joystick->hwdata->fd); +} + + +/* + * Checks to see if the haptic device and joystick are in reality the same. + */ +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + /* We are assuming Linux is using evdev which should trump the old + * joystick methods. */ + if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) { + return 1; + } + return 0; +} + + +/* + * Opens a SDL_Haptic from a SDL_Joystick. + */ +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + int device_index = 0; + int fd; + int ret; + SDL_hapticlist_item *item; + + /* Find the joystick in the haptic list. */ + for (item = SDL_hapticlist; item; item = item->next) { + if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) { + break; + } + ++device_index; + } + haptic->index = device_index; + + if (device_index >= MAX_HAPTICS) { + return SDL_SetError("Haptic: Joystick doesn't have Haptic capabilities"); + } + + fd = open(joystick->hwdata->fname, O_RDWR, 0); + if (fd < 0) { + return SDL_SetError("Haptic: Unable to open %s: %s", + joystick->hwdata->fname, strerror(errno)); + } + ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */ + if (ret < 0) { + return -1; + } + + haptic->hwdata->fname = SDL_strdup( joystick->hwdata->fname ); + + return 0; +} + + +/* + * Closes the haptic device. + */ +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + if (haptic->hwdata) { + + /* Free effects. */ + SDL_free(haptic->effects); + haptic->effects = NULL; + haptic->neffects = 0; + + /* Clean up */ + close(haptic->hwdata->fd); + + /* Free */ + SDL_free(haptic->hwdata->fname); + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } + + /* Clear the rest. */ + SDL_memset(haptic, 0, sizeof(SDL_Haptic)); +} + + +/* + * Clean up after system specific haptic stuff + */ +void +SDL_SYS_HapticQuit(void) +{ + SDL_hapticlist_item *item = NULL; + SDL_hapticlist_item *next = NULL; + + for (item = SDL_hapticlist; item; item = next) { + next = item->next; + /* Opened and not closed haptics are leaked, this is on purpose. + * Close your haptic devices after usage. */ + SDL_free(item->fname); + SDL_free(item); + } + +#if SDL_USE_LIBUDEV + SDL_UDEV_DelCallback(haptic_udev_callback); + SDL_UDEV_Quit(); +#endif /* SDL_USE_LIBUDEV */ + + numhaptics = 0; + SDL_hapticlist = NULL; + SDL_hapticlist_tail = NULL; +} + + +/* + * Converts an SDL button to a ff_trigger button. + */ +static Uint16 +SDL_SYS_ToButton(Uint16 button) +{ + Uint16 ff_button; + + ff_button = 0; + + /* + * Not sure what the proper syntax is because this actually isn't implemented + * in the current kernel from what I've seen (2.6.26). + */ + if (button != 0) { + ff_button = BTN_GAMEPAD + button - 1; + } + + return ff_button; +} + + +/* + * Initializes the ff_effect usable direction from a SDL_HapticDirection. + */ +static int +SDL_SYS_ToDirection(Uint16 *dest, SDL_HapticDirection * src) +{ + Uint32 tmp; + + switch (src->type) { + case SDL_HAPTIC_POLAR: + /* Linux directions start from south. + (and range from 0 to 0xFFFF) + Quoting include/linux/input.h, line 926: + Direction of the effect is encoded as follows: + 0 deg -> 0x0000 (down) + 90 deg -> 0x4000 (left) + 180 deg -> 0x8000 (up) + 270 deg -> 0xC000 (right) + The force pulls into the direction specified by Linux directions, + i.e. the opposite convention of SDL directions. + */ + tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; /* convert to range [0,0xFFFF] */ + *dest = (Uint16) tmp; + break; + + case SDL_HAPTIC_SPHERICAL: + /* + We convert to polar, because that's the only supported direction on Linux. + The first value of a spherical direction is practically the same as a + Polar direction, except that we have to add 90 degrees. It is the angle + from EAST {1,0} towards SOUTH {0,1}. + --> add 9000 + --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR. + */ + tmp = ((src->dir[0]) + 9000) % 36000; /* Convert to polars */ + tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */ + *dest = (Uint16) tmp; + break; + + case SDL_HAPTIC_CARTESIAN: + if (!src->dir[1]) + *dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000); + else if (!src->dir[0]) + *dest = (src->dir[1] >= 0 ? 0x8000 : 0); + else { + float f = SDL_atan2(src->dir[1], src->dir[0]); /* Ideally we'd use fixed point math instead of floats... */ + /* + atan2 takes the parameters: Y-axis-value and X-axis-value (in that order) + - Y-axis-value is the second coordinate (from center to SOUTH) + - X-axis-value is the first coordinate (from center to EAST) + We add 36000, because atan2 also returns negative values. Then we practically + have the first spherical value. Therefore we proceed as in case + SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value. + --> add 45000 in total + --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR. + */ + tmp = (((Sint32) (f * 18000. / M_PI)) + 45000) % 36000; + tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */ + *dest = (Uint16) tmp; + } + break; + + default: + return SDL_SetError("Haptic: Unsupported direction type."); + } + + return 0; +} + + +#define CLAMP(x) (((x) > 32767) ? 32767 : x) +/* + * Initializes the Linux effect struct from a haptic_effect. + * Values above 32767 (for unsigned) are unspecified so we must clamp. + */ +static int +SDL_SYS_ToFFEffect(struct ff_effect *dest, SDL_HapticEffect * src) +{ + SDL_HapticConstant *constant; + SDL_HapticPeriodic *periodic; + SDL_HapticCondition *condition; + SDL_HapticRamp *ramp; + SDL_HapticLeftRight *leftright; + + /* Clear up */ + SDL_memset(dest, 0, sizeof(struct ff_effect)); + + switch (src->type) { + case SDL_HAPTIC_CONSTANT: + constant = &src->constant; + + /* Header */ + dest->type = FF_CONSTANT; + if (SDL_SYS_ToDirection(&dest->direction, &constant->direction) == -1) + return -1; + + /* Replay */ + dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(constant->length); + dest->replay.delay = CLAMP(constant->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(constant->button); + dest->trigger.interval = CLAMP(constant->interval); + + /* Constant */ + dest->u.constant.level = constant->level; + + /* Envelope */ + dest->u.constant.envelope.attack_length = + CLAMP(constant->attack_length); + dest->u.constant.envelope.attack_level = + CLAMP(constant->attack_level); + dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length); + dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level); + + break; + + case SDL_HAPTIC_SINE: + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* case SDL_HAPTIC_SQUARE: */ + case SDL_HAPTIC_TRIANGLE: + case SDL_HAPTIC_SAWTOOTHUP: + case SDL_HAPTIC_SAWTOOTHDOWN: + periodic = &src->periodic; + + /* Header */ + dest->type = FF_PERIODIC; + if (SDL_SYS_ToDirection(&dest->direction, &periodic->direction) == -1) + return -1; + + /* Replay */ + dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(periodic->length); + dest->replay.delay = CLAMP(periodic->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(periodic->button); + dest->trigger.interval = CLAMP(periodic->interval); + + /* Periodic */ + if (periodic->type == SDL_HAPTIC_SINE) + dest->u.periodic.waveform = FF_SINE; + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* else if (periodic->type == SDL_HAPTIC_SQUARE) + dest->u.periodic.waveform = FF_SQUARE; */ + else if (periodic->type == SDL_HAPTIC_TRIANGLE) + dest->u.periodic.waveform = FF_TRIANGLE; + else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP) + dest->u.periodic.waveform = FF_SAW_UP; + else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN) + dest->u.periodic.waveform = FF_SAW_DOWN; + dest->u.periodic.period = CLAMP(periodic->period); + /* Linux expects 0-65535, so multiply by 2 */ + dest->u.periodic.magnitude = CLAMP(periodic->magnitude) * 2; + dest->u.periodic.offset = periodic->offset; + /* Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift. */ + dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000; + + /* Envelope */ + dest->u.periodic.envelope.attack_length = + CLAMP(periodic->attack_length); + dest->u.periodic.envelope.attack_level = + CLAMP(periodic->attack_level); + dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length); + dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level); + + break; + + case SDL_HAPTIC_SPRING: + case SDL_HAPTIC_DAMPER: + case SDL_HAPTIC_INERTIA: + case SDL_HAPTIC_FRICTION: + condition = &src->condition; + + /* Header */ + if (condition->type == SDL_HAPTIC_SPRING) + dest->type = FF_SPRING; + else if (condition->type == SDL_HAPTIC_DAMPER) + dest->type = FF_DAMPER; + else if (condition->type == SDL_HAPTIC_INERTIA) + dest->type = FF_INERTIA; + else if (condition->type == SDL_HAPTIC_FRICTION) + dest->type = FF_FRICTION; + dest->direction = 0; /* Handled by the condition-specifics. */ + + /* Replay */ + dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(condition->length); + dest->replay.delay = CLAMP(condition->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(condition->button); + dest->trigger.interval = CLAMP(condition->interval); + + /* Condition */ + /* X axis */ + dest->u.condition[0].right_saturation = condition->right_sat[0]; + dest->u.condition[0].left_saturation = condition->left_sat[0]; + dest->u.condition[0].right_coeff = condition->right_coeff[0]; + dest->u.condition[0].left_coeff = condition->left_coeff[0]; + dest->u.condition[0].deadband = condition->deadband[0]; + dest->u.condition[0].center = condition->center[0]; + /* Y axis */ + dest->u.condition[1].right_saturation = condition->right_sat[1]; + dest->u.condition[1].left_saturation = condition->left_sat[1]; + dest->u.condition[1].right_coeff = condition->right_coeff[1]; + dest->u.condition[1].left_coeff = condition->left_coeff[1]; + dest->u.condition[1].deadband = condition->deadband[1]; + dest->u.condition[1].center = condition->center[1]; + + /* + * There is no envelope in the linux force feedback api for conditions. + */ + + break; + + case SDL_HAPTIC_RAMP: + ramp = &src->ramp; + + /* Header */ + dest->type = FF_RAMP; + if (SDL_SYS_ToDirection(&dest->direction, &ramp->direction) == -1) + return -1; + + /* Replay */ + dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(ramp->length); + dest->replay.delay = CLAMP(ramp->delay); + + /* Trigger */ + dest->trigger.button = SDL_SYS_ToButton(ramp->button); + dest->trigger.interval = CLAMP(ramp->interval); + + /* Ramp */ + dest->u.ramp.start_level = ramp->start; + dest->u.ramp.end_level = ramp->end; + + /* Envelope */ + dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length); + dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level); + dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length); + dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level); + + break; + + case SDL_HAPTIC_LEFTRIGHT: + leftright = &src->leftright; + + /* Header */ + dest->type = FF_RUMBLE; + dest->direction = 0; + + /* Replay */ + dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ? + 0 : CLAMP(leftright->length); + + /* Trigger */ + dest->trigger.button = 0; + dest->trigger.interval = 0; + + /* Rumble (Linux expects 0-65535, so multiply by 2) */ + dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2; + dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2; + + break; + + + default: + return SDL_SetError("Haptic: Unknown effect type."); + } + + return 0; +} + + +/* + * Creates a new haptic effect. + */ +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + SDL_HapticEffect * base) +{ + struct ff_effect *linux_effect; + + /* Allocate the hardware effect */ + effect->hweffect = (struct haptic_hweffect *) + SDL_malloc(sizeof(struct haptic_hweffect)); + if (effect->hweffect == NULL) { + return SDL_OutOfMemory(); + } + + /* Prepare the ff_effect */ + linux_effect = &effect->hweffect->effect; + if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) { + goto new_effect_err; + } + linux_effect->id = -1; /* Have the kernel give it an id */ + + /* Upload the effect */ + if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) { + SDL_SetError("Haptic: Error uploading effect to the device: %s", + strerror(errno)); + goto new_effect_err; + } + + return 0; + + new_effect_err: + SDL_free(effect->hweffect); + effect->hweffect = NULL; + return -1; +} + + +/* + * Updates an effect. + * + * Note: Dynamically updating the direction can in some cases force + * the effect to restart and run once. + */ +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + struct ff_effect linux_effect; + + /* Create the new effect */ + if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) { + return -1; + } + linux_effect.id = effect->hweffect->effect.id; + + /* See if it can be uploaded. */ + if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) { + return SDL_SetError("Haptic: Error updating the effect: %s", + strerror(errno)); + } + + /* Copy the new effect into memory. */ + SDL_memcpy(&effect->hweffect->effect, &linux_effect, + sizeof(struct ff_effect)); + + return effect->hweffect->effect.id; +} + + +/* + * Runs an effect. + */ +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + struct input_event run; + + /* Prepare to run the effect */ + run.type = EV_FF; + run.code = effect->hweffect->effect.id; + /* We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. */ + run.value = (iterations > INT_MAX) ? INT_MAX : iterations; + + if (write(haptic->hwdata->fd, (const void *) &run, sizeof(run)) < 0) { + return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno)); + } + + return 0; +} + + +/* + * Stops an effect. + */ +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + struct input_event stop; + + stop.type = EV_FF; + stop.code = effect->hweffect->effect.id; + stop.value = 0; + + if (write(haptic->hwdata->fd, (const void *) &stop, sizeof(stop)) < 0) { + return SDL_SetError("Haptic: Unable to stop the effect: %s", + strerror(errno)); + } + + return 0; +} + + +/* + * Frees the effect. + */ +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) { + SDL_SetError("Haptic: Error removing the effect from the device: %s", + strerror(errno)); + } + SDL_free(effect->hweffect); + effect->hweffect = NULL; +} + + +/* + * Gets the status of a haptic effect. + */ +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ +#if 0 /* Not supported atm. */ + struct input_event ie; + + ie.type = EV_FF; + ie.type = EV_FF_STATUS; + ie.code = effect->hweffect->effect.id; + + if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { + return SDL_SetError("Haptic: Error getting device status."); + } + + return 0; +#endif + + return -1; +} + + +/* + * Sets the gain. + */ +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + struct input_event ie; + + ie.type = EV_FF; + ie.code = FF_GAIN; + ie.value = (0xFFFFUL * gain) / 100; + + if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { + return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno)); + } + + return 0; +} + + +/* + * Sets the autocentering. + */ +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + struct input_event ie; + + ie.type = EV_FF; + ie.code = FF_AUTOCENTER; + ie.value = (0xFFFFUL * autocenter) / 100; + + if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) { + return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno)); + } + + return 0; +} + + +/* + * Pausing is not supported atm by linux. + */ +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + return -1; +} + + +/* + * Unpausing is not supported atm by linux. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + return -1; +} + + +/* + * Stops all the currently playing effects. + */ +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + int i, ret; + + /* Linux does not support this natively so we have to loop. */ + for (i = 0; i < haptic->neffects; i++) { + if (haptic->effects[i].hweffect != NULL) { + ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]); + if (ret < 0) { + return SDL_SetError + ("Haptic: Error while trying to stop all playing effects."); + } + } + } + return 0; +} + +#endif /* SDL_HAPTIC_LINUX */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/windows/SDL_dinputhaptic.c b/source/3rd-party/SDL2/src/haptic/windows/SDL_dinputhaptic.c new file mode 100644 index 0000000..897d128 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/windows/SDL_dinputhaptic.c @@ -0,0 +1,1305 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_error.h" +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" + +#if SDL_HAPTIC_DINPUT + +#include "SDL_stdinc.h" +#include "SDL_timer.h" +#include "SDL_windowshaptic_c.h" +#include "SDL_dinputhaptic_c.h" +#include "../../joystick/windows/SDL_windowsjoystick_c.h" + +/* + * External stuff. + */ +extern HWND SDL_HelperWindow; + + +/* + * Internal stuff. + */ +static SDL_bool coinitialized = SDL_FALSE; +static LPDIRECTINPUT8 dinput = NULL; + + +/* + * Like SDL_SetError but for DX error codes. + */ +static int +DI_SetError(const char *str, HRESULT err) +{ + /* + SDL_SetError("Haptic: %s - %s: %s", str, + DXGetErrorString8A(err), DXGetErrorDescription8A(err)); + */ + return SDL_SetError("Haptic error %s", str); +} + +/* + * Callback to find the haptic devices. + */ +static BOOL CALLBACK +EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) +{ + (void) pContext; + SDL_DINPUT_MaybeAddDevice(pdidInstance); + return DIENUM_CONTINUE; /* continue enumerating */ +} + +int +SDL_DINPUT_HapticInit(void) +{ + HRESULT ret; + HINSTANCE instance; + + if (dinput != NULL) { /* Already open. */ + return SDL_SetError("Haptic: SubSystem already open."); + } + + ret = WIN_CoInitialize(); + if (FAILED(ret)) { + return DI_SetError("Coinitialize", ret); + } + + coinitialized = SDL_TRUE; + + ret = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectInput8, (LPVOID)& dinput); + if (FAILED(ret)) { + SDL_SYS_HapticQuit(); + return DI_SetError("CoCreateInstance", ret); + } + + /* Because we used CoCreateInstance, we need to Initialize it, first. */ + instance = GetModuleHandle(NULL); + if (instance == NULL) { + SDL_SYS_HapticQuit(); + return SDL_SetError("GetModuleHandle() failed with error code %lu.", + GetLastError()); + } + ret = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION); + if (FAILED(ret)) { + SDL_SYS_HapticQuit(); + return DI_SetError("Initializing DirectInput device", ret); + } + + /* Look for haptic devices. */ + ret = IDirectInput8_EnumDevices(dinput, + 0, + EnumHapticsCallback, + NULL, + DIEDFL_FORCEFEEDBACK | + DIEDFL_ATTACHEDONLY); + if (FAILED(ret)) { + SDL_SYS_HapticQuit(); + return DI_SetError("Enumerating DirectInput devices", ret); + } + return 0; +} + +int +SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) +{ + HRESULT ret; + LPDIRECTINPUTDEVICE8 device; + const DWORD needflags = DIDC_ATTACHED | DIDC_FORCEFEEDBACK; + DIDEVCAPS capabilities; + SDL_hapticlist_item *item = NULL; + + if (dinput == NULL) { + return -1; /* not initialized. We'll pick these up on enumeration if we init later. */ + } + + /* Make sure we don't already have it */ + for (item = SDL_hapticlist; item; item = item->next) { + if ((!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof(*pdidInstance)) == 0)) { + return -1; /* Already added */ + } + } + + /* Open the device */ + ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance, &device, NULL); + if (FAILED(ret)) { + /* DI_SetError("Creating DirectInput device",ret); */ + return -1; + } + + /* Get capabilities. */ + SDL_zero(capabilities); + capabilities.dwSize = sizeof(DIDEVCAPS); + ret = IDirectInputDevice8_GetCapabilities(device, &capabilities); + IDirectInputDevice8_Release(device); + if (FAILED(ret)) { + /* DI_SetError("Getting device capabilities",ret); */ + return -1; + } + + if ((capabilities.dwFlags & needflags) != needflags) { + return -1; /* not a device we can use. */ + } + + item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item)); + if (item == NULL) { + return SDL_OutOfMemory(); + } + + item->name = WIN_StringToUTF8(pdidInstance->tszProductName); + if (!item->name) { + SDL_free(item); + return -1; + } + + /* Copy the instance over, useful for creating devices. */ + SDL_memcpy(&item->instance, pdidInstance, sizeof(DIDEVICEINSTANCE)); + SDL_memcpy(&item->capabilities, &capabilities, sizeof(capabilities)); + + return SDL_SYS_AddHapticDevice(item); +} + +int +SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *prev = NULL; + + if (dinput == NULL) { + return -1; /* not initialized, ignore this. */ + } + + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (!item->bXInputHaptic && SDL_memcmp(&item->instance, pdidInstance, sizeof(*pdidInstance)) == 0) { + /* found it, remove it. */ + return SDL_SYS_RemoveHapticDevice(prev, item); + } + prev = item; + } + return -1; +} + +/* + * Callback to get supported axes. + */ +static BOOL CALLBACK +DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) +{ + SDL_Haptic *haptic = (SDL_Haptic *) pvRef; + + if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) { + const GUID *guid = &dev->guidType; + DWORD offset = 0; + if (WIN_IsEqualGUID(guid, &GUID_XAxis)) { + offset = DIJOFS_X; + } else if (WIN_IsEqualGUID(guid, &GUID_YAxis)) { + offset = DIJOFS_Y; + } else if (WIN_IsEqualGUID(guid, &GUID_ZAxis)) { + offset = DIJOFS_Z; + } else if (WIN_IsEqualGUID(guid, &GUID_RxAxis)) { + offset = DIJOFS_RX; + } else if (WIN_IsEqualGUID(guid, &GUID_RyAxis)) { + offset = DIJOFS_RY; + } else if (WIN_IsEqualGUID(guid, &GUID_RzAxis)) { + offset = DIJOFS_RZ; + } else { + return DIENUM_CONTINUE; /* can't use this, go on. */ + } + + haptic->hwdata->axes[haptic->naxes] = offset; + haptic->naxes++; + + /* Currently using the artificial limit of 3 axes. */ + if (haptic->naxes >= 3) { + return DIENUM_STOP; + } + } + + return DIENUM_CONTINUE; +} + +/* + * Callback to get all supported effects. + */ +#define EFFECT_TEST(e,s) \ +if (WIN_IsEqualGUID(&pei->guid, &(e))) \ + haptic->supported |= (s) +static BOOL CALLBACK +DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv) +{ + /* Prepare the haptic device. */ + SDL_Haptic *haptic = (SDL_Haptic *) pv; + + /* Get supported. */ + EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING); + EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER); + EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA); + EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION); + EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT); + EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM); + EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE); + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */ + EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE); + EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP); + EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN); + EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP); + + /* Check for more. */ + return DIENUM_CONTINUE; +} + +/* + * Opens the haptic device. + * + * Steps: + * - Set cooperative level. + * - Set data format. + * - Acquire exclusiveness. + * - Reset actuators. + * - Get supported features. + */ +static int +SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic * haptic, LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick) +{ + HRESULT ret; + DIPROPDWORD dipdw; + + /* Allocate the hwdata */ + haptic->hwdata = (struct haptic_hwdata *)SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + /* We'll use the device8 from now on. */ + haptic->hwdata->device = device8; + haptic->hwdata->is_joystick = is_joystick; + + /* !!! FIXME: opening a haptic device here first will make an attempt to + !!! FIXME: SDL_JoystickOpen() that same device fail later, since we + !!! FIXME: have it open in exclusive mode. But this will allow + !!! FIXME: SDL_JoystickOpen() followed by SDL_HapticOpenFromJoystick() + !!! FIXME: to work, and that's probably the common case. Still, + !!! FIXME: ideally, We need to unify the opening code. */ + + if (!is_joystick) { /* if is_joystick, we already set this up elsewhere. */ + /* Grab it exclusively to use force feedback stuff. */ + ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device, + SDL_HelperWindow, + DISCL_EXCLUSIVE | + DISCL_BACKGROUND); + if (FAILED(ret)) { + DI_SetError("Setting cooperative level to exclusive", ret); + goto acquire_err; + } + + /* Set data format. */ + ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device, + &SDL_c_dfDIJoystick2); + if (FAILED(ret)) { + DI_SetError("Setting data format", ret); + goto acquire_err; + } + + + /* Acquire the device. */ + ret = IDirectInputDevice8_Acquire(haptic->hwdata->device); + if (FAILED(ret)) { + DI_SetError("Acquiring DirectInput device", ret); + goto acquire_err; + } + } + + /* Get number of axes. */ + ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device, + DI_DeviceObjectCallback, + haptic, DIDFT_AXIS); + if (FAILED(ret)) { + DI_SetError("Getting device axes", ret); + goto acquire_err; + } + + /* Reset all actuators - just in case. */ + ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_RESET); + if (FAILED(ret)) { + DI_SetError("Resetting device", ret); + goto acquire_err; + } + + /* Enabling actuators. */ + ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_SETACTUATORSON); + if (FAILED(ret)) { + DI_SetError("Enabling actuators", ret); + goto acquire_err; + } + + /* Get supported effects. */ + ret = IDirectInputDevice8_EnumEffects(haptic->hwdata->device, + DI_EffectCallback, haptic, + DIEFT_ALL); + if (FAILED(ret)) { + DI_SetError("Enumerating supported effects", ret); + goto acquire_err; + } + if (haptic->supported == 0) { /* Error since device supports nothing. */ + SDL_SetError("Haptic: Internal error on finding supported effects."); + goto acquire_err; + } + + /* Check autogain and autocenter. */ + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = 10000; + ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, + DIPROP_FFGAIN, &dipdw.diph); + if (!FAILED(ret)) { /* Gain is supported. */ + haptic->supported |= SDL_HAPTIC_GAIN; + } + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DIPROPAUTOCENTER_OFF; + ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, + DIPROP_AUTOCENTER, &dipdw.diph); + if (!FAILED(ret)) { /* Autocenter is supported. */ + haptic->supported |= SDL_HAPTIC_AUTOCENTER; + } + + /* Status is always supported. */ + haptic->supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE; + + /* Check maximum effects. */ + haptic->neffects = 128; /* This is not actually supported as thus under windows, + there is no way to tell the number of EFFECTS that a + device can hold, so we'll just use a "random" number + instead and put warnings in SDL_haptic.h */ + haptic->nplaying = 128; /* Even more impossible to get this then neffects. */ + + /* Prepare effects memory. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + SDL_OutOfMemory(); + goto acquire_err; + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + return 0; + + /* Error handling */ + acquire_err: + IDirectInputDevice8_Unacquire(haptic->hwdata->device); + return -1; +} + +int +SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) +{ + HRESULT ret; + LPDIRECTINPUTDEVICE8 device; + LPDIRECTINPUTDEVICE8 device8; + + /* Open the device */ + ret = IDirectInput8_CreateDevice(dinput, &item->instance.guidInstance, + &device, NULL); + if (FAILED(ret)) { + DI_SetError("Creating DirectInput device", ret); + return -1; + } + + /* Now get the IDirectInputDevice8 interface, instead. */ + ret = IDirectInputDevice8_QueryInterface(device, + &IID_IDirectInputDevice8, + (LPVOID *)&device8); + /* Done with the temporary one now. */ + IDirectInputDevice8_Release(device); + if (FAILED(ret)) { + DI_SetError("Querying DirectInput interface", ret); + return -1; + } + + if (SDL_DINPUT_HapticOpenFromDevice(haptic, device8, SDL_FALSE) < 0) { + IDirectInputDevice8_Release(device8); + return -1; + } + return 0; +} + +int +SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + HRESULT ret; + DIDEVICEINSTANCE hap_instance, joy_instance; + + hap_instance.dwSize = sizeof(DIDEVICEINSTANCE); + joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); + + /* Get the device instances. */ + ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device, + &hap_instance); + if (FAILED(ret)) { + return 0; + } + ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, + &joy_instance); + if (FAILED(ret)) { + return 0; + } + + return WIN_IsEqualGUID(&hap_instance.guidInstance, &joy_instance.guidInstance); +} + +int +SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + SDL_hapticlist_item *item; + int index = 0; + HRESULT ret; + DIDEVICEINSTANCE joy_instance; + + joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); + ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance); + if (FAILED(ret)) { + return -1; + } + + /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */ + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (!item->bXInputHaptic && WIN_IsEqualGUID(&item->instance.guidInstance, &joy_instance.guidInstance)) { + haptic->index = index; + return SDL_DINPUT_HapticOpenFromDevice(haptic, joystick->hwdata->InputDevice, SDL_TRUE); + } + ++index; + } + + SDL_SetError("Couldn't find joystick in haptic device list"); + return -1; +} + +void +SDL_DINPUT_HapticClose(SDL_Haptic * haptic) +{ + IDirectInputDevice8_Unacquire(haptic->hwdata->device); + + /* Only release if isn't grabbed by a joystick. */ + if (haptic->hwdata->is_joystick == 0) { + IDirectInputDevice8_Release(haptic->hwdata->device); + } +} + +void +SDL_DINPUT_HapticQuit(void) +{ + if (dinput != NULL) { + IDirectInput8_Release(dinput); + dinput = NULL; + } + + if (coinitialized) { + WIN_CoUninitialize(); + coinitialized = SDL_FALSE; + } +} + +/* + * Converts an SDL trigger button to an DIEFFECT trigger button. + */ +static DWORD +DIGetTriggerButton(Uint16 button) +{ + DWORD dwTriggerButton; + + dwTriggerButton = DIEB_NOTRIGGER; + + if (button != 0) { + dwTriggerButton = DIJOFS_BUTTON(button - 1); + } + + return dwTriggerButton; +} + + +/* + * Sets the direction. + */ +static int +SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes) +{ + LONG *rglDir; + + /* Handle no axes a part. */ + if (naxes == 0) { + effect->dwFlags |= DIEFF_SPHERICAL; /* Set as default. */ + effect->rglDirection = NULL; + return 0; + } + + /* Has axes. */ + rglDir = SDL_malloc(sizeof(LONG) * naxes); + if (rglDir == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(rglDir, 0, sizeof(LONG) * naxes); + effect->rglDirection = rglDir; + + switch (dir->type) { + case SDL_HAPTIC_POLAR: + effect->dwFlags |= DIEFF_POLAR; + rglDir[0] = dir->dir[0]; + return 0; + case SDL_HAPTIC_CARTESIAN: + effect->dwFlags |= DIEFF_CARTESIAN; + rglDir[0] = dir->dir[0]; + if (naxes > 1) + rglDir[1] = dir->dir[1]; + if (naxes > 2) + rglDir[2] = dir->dir[2]; + return 0; + case SDL_HAPTIC_SPHERICAL: + effect->dwFlags |= DIEFF_SPHERICAL; + rglDir[0] = dir->dir[0]; + if (naxes > 1) + rglDir[1] = dir->dir[1]; + if (naxes > 2) + rglDir[2] = dir->dir[2]; + return 0; + + default: + return SDL_SetError("Haptic: Unknown direction type."); + } +} + +/* Clamps and converts. */ +#define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) +/* Just converts. */ +#define CONVERT(x) (((x)*10000) / 0x7FFF) +/* + * Creates the DIEFFECT from a SDL_HapticEffect. + */ +static int +SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, + SDL_HapticEffect * src) +{ + int i; + DICONSTANTFORCE *constant; + DIPERIODIC *periodic; + DICONDITION *condition; /* Actually an array of conditions - one per axis. */ + DIRAMPFORCE *ramp; + DICUSTOMFORCE *custom; + DIENVELOPE *envelope; + SDL_HapticConstant *hap_constant; + SDL_HapticPeriodic *hap_periodic; + SDL_HapticCondition *hap_condition; + SDL_HapticRamp *hap_ramp; + SDL_HapticCustom *hap_custom; + DWORD *axes; + + /* Set global stuff. */ + SDL_memset(dest, 0, sizeof(DIEFFECT)); + dest->dwSize = sizeof(DIEFFECT); /* Set the structure size. */ + dest->dwSamplePeriod = 0; /* Not used by us. */ + dest->dwGain = 10000; /* Gain is set globally, not locally. */ + dest->dwFlags = DIEFF_OBJECTOFFSETS; /* Seems obligatory. */ + + /* Envelope. */ + envelope = SDL_malloc(sizeof(DIENVELOPE)); + if (envelope == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(envelope, 0, sizeof(DIENVELOPE)); + dest->lpEnvelope = envelope; + envelope->dwSize = sizeof(DIENVELOPE); /* Always should be this. */ + + /* Axes. */ + dest->cAxes = haptic->naxes; + if (dest->cAxes > 0) { + axes = SDL_malloc(sizeof(DWORD) * dest->cAxes); + if (axes == NULL) { + return SDL_OutOfMemory(); + } + axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */ + if (dest->cAxes > 1) { + axes[1] = haptic->hwdata->axes[1]; + } + if (dest->cAxes > 2) { + axes[2] = haptic->hwdata->axes[2]; + } + dest->rgdwAxes = axes; + } + + /* The big type handling switch, even bigger than Linux's version. */ + switch (src->type) { + case SDL_HAPTIC_CONSTANT: + hap_constant = &src->constant; + constant = SDL_malloc(sizeof(DICONSTANTFORCE)); + if (constant == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(constant, 0, sizeof(DICONSTANTFORCE)); + + /* Specifics */ + constant->lMagnitude = CONVERT(hap_constant->level); + dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + dest->lpvTypeSpecificParams = constant; + + /* Generics */ + dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button); + dest->dwTriggerRepeatInterval = hap_constant->interval; + dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) < 0) { + return -1; + } + + /* Envelope */ + if ((hap_constant->attack_length == 0) + && (hap_constant->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level); + envelope->dwAttackTime = hap_constant->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level); + envelope->dwFadeTime = hap_constant->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SINE: + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* case SDL_HAPTIC_SQUARE: */ + case SDL_HAPTIC_TRIANGLE: + case SDL_HAPTIC_SAWTOOTHUP: + case SDL_HAPTIC_SAWTOOTHDOWN: + hap_periodic = &src->periodic; + periodic = SDL_malloc(sizeof(DIPERIODIC)); + if (periodic == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(periodic, 0, sizeof(DIPERIODIC)); + + /* Specifics */ + periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude)); + periodic->lOffset = CONVERT(hap_periodic->offset); + periodic->dwPhase = + (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000; + periodic->dwPeriod = hap_periodic->period * 1000; + dest->cbTypeSpecificParams = sizeof(DIPERIODIC); + dest->lpvTypeSpecificParams = periodic; + + /* Generics */ + dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button); + dest->dwTriggerRepeatInterval = hap_periodic->interval; + dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope */ + if ((hap_periodic->attack_length == 0) + && (hap_periodic->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level); + envelope->dwAttackTime = hap_periodic->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level); + envelope->dwFadeTime = hap_periodic->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_SPRING: + case SDL_HAPTIC_DAMPER: + case SDL_HAPTIC_INERTIA: + case SDL_HAPTIC_FRICTION: + hap_condition = &src->condition; + condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes); + if (condition == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(condition, 0, sizeof(DICONDITION)); + + /* Specifics */ + for (i = 0; i < (int) dest->cAxes; i++) { + condition[i].lOffset = CONVERT(hap_condition->center[i]); + condition[i].lPositiveCoefficient = + CONVERT(hap_condition->right_coeff[i]); + condition[i].lNegativeCoefficient = + CONVERT(hap_condition->left_coeff[i]); + condition[i].dwPositiveSaturation = + CCONVERT(hap_condition->right_sat[i] / 2); + condition[i].dwNegativeSaturation = + CCONVERT(hap_condition->left_sat[i] / 2); + condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2); + } + dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes; + dest->lpvTypeSpecificParams = condition; + + /* Generics */ + dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button); + dest->dwTriggerRepeatInterval = hap_condition->interval; + dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) + < 0) { + return -1; + } + + /* Envelope - Not actually supported by most CONDITION implementations. */ + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + + break; + + case SDL_HAPTIC_RAMP: + hap_ramp = &src->ramp; + ramp = SDL_malloc(sizeof(DIRAMPFORCE)); + if (ramp == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(ramp, 0, sizeof(DIRAMPFORCE)); + + /* Specifics */ + ramp->lStart = CONVERT(hap_ramp->start); + ramp->lEnd = CONVERT(hap_ramp->end); + dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE); + dest->lpvTypeSpecificParams = ramp; + + /* Generics */ + dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button); + dest->dwTriggerRepeatInterval = hap_ramp->interval; + dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) { + return -1; + } + + /* Envelope */ + if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level); + envelope->dwAttackTime = hap_ramp->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level); + envelope->dwFadeTime = hap_ramp->fade_length * 1000; + } + + break; + + case SDL_HAPTIC_CUSTOM: + hap_custom = &src->custom; + custom = SDL_malloc(sizeof(DICUSTOMFORCE)); + if (custom == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(custom, 0, sizeof(DICUSTOMFORCE)); + + /* Specifics */ + custom->cChannels = hap_custom->channels; + custom->dwSamplePeriod = hap_custom->period * 1000; + custom->cSamples = hap_custom->samples; + custom->rglForceData = + SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels); + for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */ + custom->rglForceData[i] = CCONVERT(hap_custom->data[i]); + } + dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE); + dest->lpvTypeSpecificParams = custom; + + /* Generics */ + dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */ + dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button); + dest->dwTriggerRepeatInterval = hap_custom->interval; + dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ + + /* Direction. */ + if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 0) { + return -1; + } + + /* Envelope */ + if ((hap_custom->attack_length == 0) + && (hap_custom->fade_length == 0)) { + SDL_free(dest->lpEnvelope); + dest->lpEnvelope = NULL; + } else { + envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level); + envelope->dwAttackTime = hap_custom->attack_length * 1000; + envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level); + envelope->dwFadeTime = hap_custom->fade_length * 1000; + } + + break; + + default: + return SDL_SetError("Haptic: Unknown effect type."); + } + + return 0; +} + + +/* + * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT. + */ +static void +SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type) +{ + DICUSTOMFORCE *custom; + + SDL_free(effect->lpEnvelope); + effect->lpEnvelope = NULL; + SDL_free(effect->rgdwAxes); + effect->rgdwAxes = NULL; + if (effect->lpvTypeSpecificParams != NULL) { + if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */ + custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams; + SDL_free(custom->rglForceData); + custom->rglForceData = NULL; + } + SDL_free(effect->lpvTypeSpecificParams); + effect->lpvTypeSpecificParams = NULL; + } + SDL_free(effect->rglDirection); + effect->rglDirection = NULL; +} + +/* + * Gets the effect type from the generic SDL haptic effect wrapper. + */ +static REFGUID +SDL_SYS_HapticEffectType(SDL_HapticEffect * effect) +{ + switch (effect->type) { + case SDL_HAPTIC_CONSTANT: + return &GUID_ConstantForce; + + case SDL_HAPTIC_RAMP: + return &GUID_RampForce; + + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* case SDL_HAPTIC_SQUARE: + return &GUID_Square; */ + + case SDL_HAPTIC_SINE: + return &GUID_Sine; + + case SDL_HAPTIC_TRIANGLE: + return &GUID_Triangle; + + case SDL_HAPTIC_SAWTOOTHUP: + return &GUID_SawtoothUp; + + case SDL_HAPTIC_SAWTOOTHDOWN: + return &GUID_SawtoothDown; + + case SDL_HAPTIC_SPRING: + return &GUID_Spring; + + case SDL_HAPTIC_DAMPER: + return &GUID_Damper; + + case SDL_HAPTIC_INERTIA: + return &GUID_Inertia; + + case SDL_HAPTIC_FRICTION: + return &GUID_Friction; + + case SDL_HAPTIC_CUSTOM: + return &GUID_CustomForce; + + default: + return NULL; + } +} +int +SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) +{ + HRESULT ret; + REFGUID type = SDL_SYS_HapticEffectType(base); + + if (type == NULL) { + SDL_SetError("Haptic: Unknown effect type."); + return -1; + } + + /* Get the effect. */ + if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) { + goto err_effectdone; + } + + /* Create the actual effect. */ + ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type, + &effect->hweffect->effect, + &effect->hweffect->ref, NULL); + if (FAILED(ret)) { + DI_SetError("Unable to create effect", ret); + goto err_effectdone; + } + + return 0; + +err_effectdone: + SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type); + return -1; +} + +int +SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) +{ + HRESULT ret; + DWORD flags; + DIEFFECT temp; + + /* Get the effect. */ + SDL_memset(&temp, 0, sizeof(DIEFFECT)); + if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) { + goto err_update; + } + + /* Set the flags. Might be worthwhile to diff temp with loaded effect and + * only change those parameters. */ + flags = DIEP_DIRECTION | + DIEP_DURATION | + DIEP_ENVELOPE | + DIEP_STARTDELAY | + DIEP_TRIGGERBUTTON | + DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS; + + /* Create the actual effect. */ + ret = + IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags); + if (ret == DIERR_NOTEXCLUSIVEACQUIRED) { + IDirectInputDevice8_Unacquire(haptic->hwdata->device); + ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device, SDL_HelperWindow, DISCL_EXCLUSIVE | DISCL_BACKGROUND); + if (SUCCEEDED(ret)) { + ret = DIERR_NOTACQUIRED; + } + } + if (ret == DIERR_INPUTLOST || ret == DIERR_NOTACQUIRED) { + ret = IDirectInputDevice8_Acquire(haptic->hwdata->device); + if (SUCCEEDED(ret)) { + ret = IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags); + } + } + if (FAILED(ret)) { + DI_SetError("Unable to update effect", ret); + goto err_update; + } + + /* Copy it over. */ + SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type); + SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT)); + + return 0; + +err_update: + SDL_SYS_HapticFreeDIEFFECT(&temp, data->type); + return -1; +} + +int +SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) +{ + HRESULT ret; + DWORD iter; + + /* Check if it's infinite. */ + if (iterations == SDL_HAPTIC_INFINITY) { + iter = INFINITE; + } else { + iter = iterations; + } + + /* Run the effect. */ + ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0); + if (FAILED(ret)) { + return DI_SetError("Running the effect", ret); + } + return 0; +} + +int +SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = IDirectInputEffect_Stop(effect->hweffect->ref); + if (FAILED(ret)) { + return DI_SetError("Unable to stop effect", ret); + } + return 0; +} + +void +SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + + ret = IDirectInputEffect_Unload(effect->hweffect->ref); + if (FAILED(ret)) { + DI_SetError("Removing effect from the device", ret); + } + SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, effect->effect.type); +} + +int +SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + HRESULT ret; + DWORD status; + + ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status); + if (FAILED(ret)) { + return DI_SetError("Getting effect status", ret); + } + + if (status == 0) + return SDL_FALSE; + return SDL_TRUE; +} + +int +SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + HRESULT ret; + DIPROPDWORD dipdw; + + /* Create the weird structure thingy. */ + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = gain * 100; /* 0 to 10,000 */ + + /* Try to set the autocenter. */ + ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, + DIPROP_FFGAIN, &dipdw.diph); + if (FAILED(ret)) { + return DI_SetError("Setting gain", ret); + } + return 0; +} + +int +SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + HRESULT ret; + DIPROPDWORD dipdw; + + /* Create the weird structure thingy. */ + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF : + DIPROPAUTOCENTER_ON; + + /* Try to set the autocenter. */ + ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, + DIPROP_AUTOCENTER, &dipdw.diph); + if (FAILED(ret)) { + return DI_SetError("Setting autocenter", ret); + } + return 0; +} + +int +SDL_DINPUT_HapticPause(SDL_Haptic * haptic) +{ + HRESULT ret; + + /* Pause the device. */ + ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_PAUSE); + if (FAILED(ret)) { + return DI_SetError("Pausing the device", ret); + } + return 0; +} + +int +SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic) +{ + HRESULT ret; + + /* Unpause the device. */ + ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_CONTINUE); + if (FAILED(ret)) { + return DI_SetError("Pausing the device", ret); + } + return 0; +} + +int +SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic) +{ + HRESULT ret; + + /* Try to stop the effects. */ + ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, + DISFFC_STOPALL); + if (FAILED(ret)) { + return DI_SetError("Stopping the device", ret); + } + return 0; +} + +#else /* !SDL_HAPTIC_DINPUT */ + +typedef struct DIDEVICEINSTANCE DIDEVICEINSTANCE; +typedef struct SDL_hapticlist_item SDL_hapticlist_item; + +int +SDL_DINPUT_HapticInit(void) +{ + return 0; +} + +int +SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +void +SDL_DINPUT_HapticClose(SDL_Haptic * haptic) +{ +} + +void +SDL_DINPUT_HapticQuit(void) +{ +} + +int +SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +void +SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ +} + +int +SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticPause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +#endif /* SDL_HAPTIC_DINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/windows/SDL_dinputhaptic_c.h b/source/3rd-party/SDL2/src/haptic/windows/SDL_dinputhaptic_c.h new file mode 100644 index 0000000..81c0ad1 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/windows/SDL_dinputhaptic_c.h @@ -0,0 +1,47 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_haptic.h" +#include "SDL_windowshaptic_c.h" + + +extern int SDL_DINPUT_HapticInit(void); +extern int SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance); +extern int SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance); +extern int SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item); +extern int SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern int SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern void SDL_DINPUT_HapticClose(SDL_Haptic * haptic); +extern void SDL_DINPUT_HapticQuit(void); +extern int SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base); +extern int SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data); +extern int SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations); +extern int SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern void SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain); +extern int SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter); +extern int SDL_DINPUT_HapticPause(SDL_Haptic * haptic); +extern int SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic); +extern int SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/windows/SDL_windowshaptic.c b/source/3rd-party/SDL2/src/haptic/windows/SDL_windowshaptic.c new file mode 100644 index 0000000..2e806c9 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/windows/SDL_windowshaptic.c @@ -0,0 +1,456 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_HAPTIC_DINPUT || SDL_HAPTIC_XINPUT + +#include "SDL_assert.h" +#include "SDL_thread.h" +#include "SDL_mutex.h" +#include "SDL_timer.h" +#include "SDL_hints.h" +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/windows/SDL_windowsjoystick_c.h" /* For joystick hwdata */ +#include "../../joystick/windows/SDL_xinputjoystick_c.h" /* For xinput rumble */ + +#include "SDL_windowshaptic_c.h" +#include "SDL_dinputhaptic_c.h" +#include "SDL_xinputhaptic_c.h" + + +/* + * Internal stuff. + */ +SDL_hapticlist_item *SDL_hapticlist = NULL; +static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; +static int numhaptics = 0; + + +/* + * Initializes the haptic subsystem. + */ +int +SDL_SYS_HapticInit(void) +{ + if (SDL_DINPUT_HapticInit() < 0) { + return -1; + } + if (SDL_XINPUT_HapticInit() < 0) { + return -1; + } + return numhaptics; +} + +int +SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item) +{ + if (SDL_hapticlist_tail == NULL) { + SDL_hapticlist = SDL_hapticlist_tail = item; + } else { + SDL_hapticlist_tail->next = item; + SDL_hapticlist_tail = item; + } + + /* Device has been added. */ + ++numhaptics; + + return numhaptics; +} + +int +SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item) +{ + const int retval = item->haptic ? item->haptic->index : -1; + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_hapticlist == item); + SDL_hapticlist = item->next; + } + if (item == SDL_hapticlist_tail) { + SDL_hapticlist_tail = prev; + } + --numhaptics; + /* !!! TODO: Send a haptic remove event? */ + SDL_free(item); + return retval; +} + +int +SDL_SYS_NumHaptics(void) +{ + return numhaptics; +} + +static SDL_hapticlist_item * +HapticByDevIndex(int device_index) +{ + SDL_hapticlist_item *item = SDL_hapticlist; + + if ((device_index < 0) || (device_index >= numhaptics)) { + return NULL; + } + + while (device_index > 0) { + SDL_assert(item != NULL); + --device_index; + item = item->next; + } + return item; +} + +/* + * Return the name of a haptic device, does not need to be opened. + */ +const char * +SDL_SYS_HapticName(int index) +{ + SDL_hapticlist_item *item = HapticByDevIndex(index); + return item->name; +} + +/* + * Opens a haptic device for usage. + */ +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + SDL_hapticlist_item *item = HapticByDevIndex(haptic->index); + if (item->bXInputHaptic) { + return SDL_XINPUT_HapticOpen(haptic, item); + } else { + return SDL_DINPUT_HapticOpen(haptic, item); + } +} + + +/* + * Opens a haptic device from first mouse it finds for usage. + */ +int +SDL_SYS_HapticMouse(void) +{ +#if SDL_HAPTIC_DINPUT + SDL_hapticlist_item *item; + int index = 0; + + /* Grab the first mouse haptic device we find. */ + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER) { + return index; + } + ++index; + } +#endif /* SDL_HAPTIC_DINPUT */ + return -1; +} + + +/* + * Checks to see if a joystick has haptic features. + */ +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + if (joystick->driver != &SDL_WINDOWS_JoystickDriver) { + return 0; + } +#if SDL_HAPTIC_XINPUT + if (joystick->hwdata->bXInputHaptic) { + return 1; + } +#endif +#if SDL_HAPTIC_DINPUT + if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) { + return 1; + } +#endif + return 0; +} + +/* + * Checks to see if the haptic device and joystick are in reality the same. + */ +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + if (joystick->driver != &SDL_WINDOWS_JoystickDriver) { + return 0; + } + if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) { + return 0; /* one is XInput, one is not; not the same device. */ + } else if (joystick->hwdata->bXInputHaptic) { + return SDL_XINPUT_JoystickSameHaptic(haptic, joystick); + } else { + return SDL_DINPUT_JoystickSameHaptic(haptic, joystick); + } +} + +/* + * Opens a SDL_Haptic from a SDL_Joystick. + */ +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + SDL_assert(joystick->driver == &SDL_WINDOWS_JoystickDriver); + + if (joystick->hwdata->bXInputDevice) { + return SDL_XINPUT_HapticOpenFromJoystick(haptic, joystick); + } else { + return SDL_DINPUT_HapticOpenFromJoystick(haptic, joystick); + } +} + +/* + * Closes the haptic device. + */ +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + if (haptic->hwdata) { + + /* Free effects. */ + SDL_free(haptic->effects); + haptic->effects = NULL; + haptic->neffects = 0; + + /* Clean up */ + if (haptic->hwdata->bXInputHaptic) { + SDL_XINPUT_HapticClose(haptic); + } else { + SDL_DINPUT_HapticClose(haptic); + } + + /* Free */ + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } +} + +/* + * Clean up after system specific haptic stuff + */ +void +SDL_SYS_HapticQuit(void) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *next = NULL; + SDL_Haptic *hapticitem = NULL; + + extern SDL_Haptic *SDL_haptics; + for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) { + if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) { + /* we _have_ to stop the thread before we free the XInput DLL! */ + SDL_AtomicSet(&hapticitem->hwdata->stopThread, 1); + SDL_WaitThread(hapticitem->hwdata->thread, NULL); + hapticitem->hwdata->thread = NULL; + } + } + + for (item = SDL_hapticlist; item; item = next) { + /* Opened and not closed haptics are leaked, this is on purpose. + * Close your haptic devices after usage. */ + /* !!! FIXME: (...is leaking on purpose a good idea?) - No, of course not. */ + next = item->next; + SDL_free(item->name); + SDL_free(item); + } + + SDL_XINPUT_HapticQuit(); + SDL_DINPUT_HapticQuit(); + + numhaptics = 0; + SDL_hapticlist = NULL; + SDL_hapticlist_tail = NULL; +} + +/* + * Creates a new haptic effect. + */ +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + SDL_HapticEffect * base) +{ + int result; + + /* Alloc the effect. */ + effect->hweffect = (struct haptic_hweffect *) + SDL_malloc(sizeof(struct haptic_hweffect)); + if (effect->hweffect == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_zerop(effect->hweffect); + + if (haptic->hwdata->bXInputHaptic) { + result = SDL_XINPUT_HapticNewEffect(haptic, effect, base); + } else { + result = SDL_DINPUT_HapticNewEffect(haptic, effect, base); + } + if (result < 0) { + SDL_free(effect->hweffect); + effect->hweffect = NULL; + } + return result; +} + +/* + * Updates an effect. + */ +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticUpdateEffect(haptic, effect, data); + } else { + return SDL_DINPUT_HapticUpdateEffect(haptic, effect, data); + } +} + +/* + * Runs an effect. + */ +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticRunEffect(haptic, effect, iterations); + } else { + return SDL_DINPUT_HapticRunEffect(haptic, effect, iterations); + } +} + +/* + * Stops an effect. + */ +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticStopEffect(haptic, effect); + } else { + return SDL_DINPUT_HapticStopEffect(haptic, effect); + } +} + +/* + * Frees the effect. + */ +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + if (haptic->hwdata->bXInputHaptic) { + SDL_XINPUT_HapticDestroyEffect(haptic, effect); + } else { + SDL_DINPUT_HapticDestroyEffect(haptic, effect); + } + SDL_free(effect->hweffect); + effect->hweffect = NULL; +} + +/* + * Gets the status of a haptic effect. + */ +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticGetEffectStatus(haptic, effect); + } else { + return SDL_DINPUT_HapticGetEffectStatus(haptic, effect); + } +} + +/* + * Sets the gain. + */ +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticSetGain(haptic, gain); + } else { + return SDL_DINPUT_HapticSetGain(haptic, gain); + } +} + +/* + * Sets the autocentering. + */ +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticSetAutocenter(haptic, autocenter); + } else { + return SDL_DINPUT_HapticSetAutocenter(haptic, autocenter); + } +} + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticPause(haptic); + } else { + return SDL_DINPUT_HapticPause(haptic); + } +} + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticUnpause(haptic); + } else { + return SDL_DINPUT_HapticUnpause(haptic); + } +} + +/* + * Stops all the playing effects on the device. + */ +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticStopAll(haptic); + } else { + return SDL_DINPUT_HapticStopAll(haptic); + } +} + +#endif /* SDL_HAPTIC_DINPUT || SDL_HAPTIC_XINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/windows/SDL_windowshaptic_c.h b/source/3rd-party/SDL2/src/haptic/windows/SDL_windowshaptic_c.h new file mode 100644 index 0000000..256ffbf --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/windows/SDL_windowshaptic_c.h @@ -0,0 +1,88 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_windowshaptic_c_h_ +#define SDL_windowshaptic_c_h_ + +#include "SDL_thread.h" +#include "../SDL_syshaptic.h" +#include "../../core/windows/SDL_directx.h" +#include "../../core/windows/SDL_xinput.h" + +/* + * Haptic system hardware data. + */ +struct haptic_hwdata +{ +#if SDL_HAPTIC_DINPUT + LPDIRECTINPUTDEVICE8 device; +#endif + DWORD axes[3]; /* Axes to use. */ + SDL_bool is_joystick; /* Device is loaded as joystick. */ + Uint8 bXInputHaptic; /* Supports force feedback via XInput. */ + Uint8 userid; /* XInput userid index for this joystick */ + SDL_Thread *thread; + SDL_mutex *mutex; + Uint32 stopTicks; + SDL_atomic_t stopThread; +}; + + +/* + * Haptic system effect data. + */ +struct haptic_hweffect +{ +#if SDL_HAPTIC_DINPUT + DIEFFECT effect; + LPDIRECTINPUTEFFECT ref; +#endif +#if SDL_HAPTIC_XINPUT + XINPUT_VIBRATION vibration; +#endif +}; + +/* +* List of available haptic devices. +*/ +typedef struct SDL_hapticlist_item +{ + char *name; + SDL_Haptic *haptic; +#if SDL_HAPTIC_DINPUT + DIDEVICEINSTANCE instance; + DIDEVCAPS capabilities; +#endif + SDL_bool bXInputHaptic; /* Supports force feedback via XInput. */ + Uint8 userid; /* XInput userid index for this joystick */ + struct SDL_hapticlist_item *next; +} SDL_hapticlist_item; + +extern SDL_hapticlist_item *SDL_hapticlist; + +extern int SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item); +extern int SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item); + +#endif /* SDL_windowshaptic_c_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/source/3rd-party/SDL2/src/haptic/windows/SDL_xinputhaptic.c b/source/3rd-party/SDL2/src/haptic/windows/SDL_xinputhaptic.c new file mode 100644 index 0000000..53e7ad3 --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/windows/SDL_xinputhaptic.c @@ -0,0 +1,487 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_error.h" +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" + +#if SDL_HAPTIC_XINPUT + +#include "SDL_assert.h" +#include "SDL_hints.h" +#include "SDL_timer.h" +#include "SDL_windowshaptic_c.h" +#include "SDL_xinputhaptic_c.h" +#include "../../core/windows/SDL_xinput.h" +#include "../../joystick/windows/SDL_windowsjoystick_c.h" +#include "../../thread/SDL_systhread.h" + +/* + * Internal stuff. + */ +static SDL_bool loaded_xinput = SDL_FALSE; + + +int +SDL_XINPUT_HapticInit(void) +{ + if (SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE)) { + loaded_xinput = (WIN_LoadXInputDLL() == 0); + } + + if (loaded_xinput) { + DWORD i; + for (i = 0; i < XUSER_MAX_COUNT; i++) { + SDL_XINPUT_MaybeAddDevice(i); + } + } + return 0; +} + +int +SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid) +{ + const Uint8 userid = (Uint8)dwUserid; + SDL_hapticlist_item *item; + XINPUT_VIBRATION state; + + if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) { + return -1; + } + + /* Make sure we don't already have it */ + for (item = SDL_hapticlist; item; item = item->next) { + if (item->bXInputHaptic && item->userid == userid) { + return -1; /* Already added */ + } + } + + SDL_zero(state); + if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) { + return -1; /* no force feedback on this device. */ + } + + item = (SDL_hapticlist_item *)SDL_malloc(sizeof(SDL_hapticlist_item)); + if (item == NULL) { + return SDL_OutOfMemory(); + } + + SDL_zerop(item); + + /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */ + { + char buf[64]; + SDL_snprintf(buf, sizeof(buf), "XInput Controller #%u", (unsigned int)(userid + 1)); + item->name = SDL_strdup(buf); + } + + if (!item->name) { + SDL_free(item); + return -1; + } + + /* Copy the instance over, useful for creating devices. */ + item->bXInputHaptic = SDL_TRUE; + item->userid = userid; + + return SDL_SYS_AddHapticDevice(item); +} + +int +SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid) +{ + const Uint8 userid = (Uint8)dwUserid; + SDL_hapticlist_item *item; + SDL_hapticlist_item *prev = NULL; + + if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) { + return -1; + } + + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (item->bXInputHaptic && item->userid == userid) { + /* found it, remove it. */ + return SDL_SYS_RemoveHapticDevice(prev, item); + } + prev = item; + } + return -1; +} + +/* !!! FIXME: this is a hack, remove this later. */ +/* Since XInput doesn't offer a way to vibrate for X time, we hook into + * SDL_PumpEvents() to check if it's time to stop vibrating with some + * frequency. + * In practice, this works for 99% of use cases. But in an ideal world, + * we do this in a separate thread so that: + * - we aren't bound to when the app chooses to pump the event queue. + * - we aren't adding more polling to the event queue + * - we can emulate all the haptic effects correctly (start on a delay, + * mix multiple effects, etc). + * + * Mostly, this is here to get rumbling to work, and all the other features + * are absent in the XInput path for now. :( + */ +static int SDLCALL +SDL_RunXInputHaptic(void *arg) +{ + struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg; + + while (!SDL_AtomicGet(&hwdata->stopThread)) { + SDL_Delay(50); + SDL_LockMutex(hwdata->mutex); + /* If we're currently running and need to stop... */ + if (hwdata->stopTicks) { + if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) { + XINPUT_VIBRATION vibration = { 0, 0 }; + hwdata->stopTicks = 0; + XINPUTSETSTATE(hwdata->userid, &vibration); + } + } + SDL_UnlockMutex(hwdata->mutex); + } + + return 0; +} + +static int +SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid) +{ + char threadName[32]; + XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */ + XINPUTSETSTATE(userid, &vibration); + + haptic->supported = SDL_HAPTIC_LEFTRIGHT; + + haptic->neffects = 1; + haptic->nplaying = 1; + + /* Prepare effects memory. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + return SDL_OutOfMemory(); + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_free(haptic->effects); + haptic->effects = NULL; + return SDL_OutOfMemory(); + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + haptic->hwdata->bXInputHaptic = 1; + haptic->hwdata->userid = userid; + + haptic->hwdata->mutex = SDL_CreateMutex(); + if (haptic->hwdata->mutex == NULL) { + SDL_free(haptic->effects); + SDL_free(haptic->hwdata); + haptic->effects = NULL; + return SDL_SetError("Couldn't create XInput haptic mutex"); + } + + SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid); + haptic->hwdata->thread = SDL_CreateThreadInternal(SDL_RunXInputHaptic, threadName, 64 * 1024, haptic->hwdata); + + if (haptic->hwdata->thread == NULL) { + SDL_DestroyMutex(haptic->hwdata->mutex); + SDL_free(haptic->effects); + SDL_free(haptic->hwdata); + haptic->effects = NULL; + return SDL_SetError("Couldn't create XInput haptic thread"); + } + + return 0; +} + +int +SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) +{ + return SDL_XINPUT_HapticOpenFromUserIndex(haptic, item->userid); +} + +int +SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return (haptic->hwdata->userid == joystick->hwdata->userid); +} + +int +SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + SDL_hapticlist_item *item; + int index = 0; + + /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */ + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (item->bXInputHaptic && item->userid == joystick->hwdata->userid) { + haptic->index = index; + return SDL_XINPUT_HapticOpenFromUserIndex(haptic, joystick->hwdata->userid); + } + ++index; + } + + SDL_SetError("Couldn't find joystick in haptic device list"); + return -1; +} + +void +SDL_XINPUT_HapticClose(SDL_Haptic * haptic) +{ + SDL_AtomicSet(&haptic->hwdata->stopThread, 1); + SDL_WaitThread(haptic->hwdata->thread, NULL); + SDL_DestroyMutex(haptic->hwdata->mutex); +} + +void +SDL_XINPUT_HapticQuit(void) +{ + if (loaded_xinput) { + WIN_UnloadXInputDLL(); + loaded_xinput = SDL_FALSE; + } +} + +int +SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) +{ + SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ + return SDL_XINPUT_HapticUpdateEffect(haptic, effect, base); +} + +int +SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) +{ + XINPUT_VIBRATION *vib = &effect->hweffect->vibration; + SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT); + /* SDL_HapticEffect has max magnitude of 32767, XInput expects 65535 max, so multiply */ + vib->wLeftMotorSpeed = data->leftright.large_magnitude * 2; + vib->wRightMotorSpeed = data->leftright.small_magnitude * 2; + SDL_LockMutex(haptic->hwdata->mutex); + if (haptic->hwdata->stopTicks) { /* running right now? Update it. */ + XINPUTSETSTATE(haptic->hwdata->userid, vib); + } + SDL_UnlockMutex(haptic->hwdata->mutex); + return 0; +} + +int +SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) +{ + XINPUT_VIBRATION *vib = &effect->hweffect->vibration; + SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ + SDL_LockMutex(haptic->hwdata->mutex); + if (effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) { + haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY; + } else if ((!effect->effect.leftright.length) || (!iterations)) { + /* do nothing. Effect runs for zero milliseconds. */ + } else { + haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations); + if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) { + haptic->hwdata->stopTicks = 1; /* fix edge cases. */ + } + } + SDL_UnlockMutex(haptic->hwdata->mutex); + return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1; +} + +int +SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + XINPUT_VIBRATION vibration = { 0, 0 }; + SDL_LockMutex(haptic->hwdata->mutex); + haptic->hwdata->stopTicks = 0; + SDL_UnlockMutex(haptic->hwdata->mutex); + return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; +} + +void +SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + SDL_XINPUT_HapticStopEffect(haptic, effect); +} + +int +SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticPause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic) +{ + XINPUT_VIBRATION vibration = { 0, 0 }; + SDL_LockMutex(haptic->hwdata->mutex); + haptic->hwdata->stopTicks = 0; + SDL_UnlockMutex(haptic->hwdata->mutex); + return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; +} + +#else /* !SDL_HAPTIC_XINPUT */ + +#include "../../core/windows/SDL_windows.h" + +typedef struct SDL_hapticlist_item SDL_hapticlist_item; + +int +SDL_XINPUT_HapticInit(void) +{ + return 0; +} + +int +SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +void +SDL_XINPUT_HapticClose(SDL_Haptic * haptic) +{ +} + +void +SDL_XINPUT_HapticQuit(void) +{ +} + +int +SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +void +SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ +} + +int +SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticPause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +#endif /* SDL_HAPTIC_XINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/haptic/windows/SDL_xinputhaptic_c.h b/source/3rd-party/SDL2/src/haptic/windows/SDL_xinputhaptic_c.h new file mode 100644 index 0000000..eed029e --- /dev/null +++ b/source/3rd-party/SDL2/src/haptic/windows/SDL_xinputhaptic_c.h @@ -0,0 +1,47 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_haptic.h" +#include "SDL_windowshaptic_c.h" + + +extern int SDL_XINPUT_HapticInit(void); +extern int SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid); +extern int SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid); +extern int SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item); +extern int SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern int SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern void SDL_XINPUT_HapticClose(SDL_Haptic * haptic); +extern void SDL_XINPUT_HapticQuit(void); +extern int SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base); +extern int SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data); +extern int SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations); +extern int SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern void SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain); +extern int SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter); +extern int SDL_XINPUT_HapticPause(SDL_Haptic * haptic); +extern int SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic); +extern int SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic); + +/* vi: set ts=4 sw=4 expandtab: */ |