mirror of
https://github.com/love2d/love.git
synced 2026-08-19 12:14:20 +02:00
Added joystick vibration support for most common controllers (resolves issue #159).
Added Joystick:setVibration(left, right), Joystick:getVibration, and Joystick:isVibrationSupported. left and right are numbers between 0 and 1 which correspond to the strength of the gamepad's left and right rumble motors, if it has them.
This commit is contained in:
@@ -18,7 +18,13 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "Joystick.h"
|
||||
#include "common/int.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -30,16 +36,20 @@ namespace sdl
|
||||
Joystick::Joystick(int id)
|
||||
: joyhandle(0)
|
||||
, controller(0)
|
||||
, haptic(0)
|
||||
, instanceid(-1)
|
||||
, id(id)
|
||||
, vibration()
|
||||
{
|
||||
}
|
||||
|
||||
Joystick::Joystick(int id, int joyindex)
|
||||
: joyhandle(0)
|
||||
, controller(0)
|
||||
, haptic(0)
|
||||
, instanceid(-1)
|
||||
, id(id)
|
||||
, vibration()
|
||||
{
|
||||
open(joyindex);
|
||||
}
|
||||
@@ -84,6 +94,12 @@ bool Joystick::open(int deviceindex)
|
||||
|
||||
void Joystick::close()
|
||||
{
|
||||
if (haptic)
|
||||
{
|
||||
SDL_HapticRumbleStop(haptic);
|
||||
SDL_HapticClose(haptic);
|
||||
}
|
||||
|
||||
if (controller)
|
||||
SDL_GameControllerClose(controller);
|
||||
|
||||
@@ -92,7 +108,9 @@ void Joystick::close()
|
||||
|
||||
joyhandle = 0;
|
||||
controller = 0;
|
||||
haptic = 0;
|
||||
instanceid = -1;
|
||||
vibration = Vibration();
|
||||
}
|
||||
|
||||
bool Joystick::isConnected() const
|
||||
@@ -255,6 +273,170 @@ int Joystick::getID() const
|
||||
return id;
|
||||
}
|
||||
|
||||
bool Joystick::checkCreateHaptic()
|
||||
{
|
||||
if (!isConnected())
|
||||
return false;
|
||||
|
||||
if (!SDL_WasInit(SDL_INIT_HAPTIC) && SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0)
|
||||
return false;
|
||||
|
||||
if (haptic && SDL_HapticIndex(haptic) != -1)
|
||||
return true;
|
||||
|
||||
if (haptic)
|
||||
{
|
||||
SDL_HapticClose(haptic);
|
||||
haptic = 0;
|
||||
}
|
||||
|
||||
haptic = SDL_HapticOpenFromJoystick(joyhandle);
|
||||
vibration = Vibration();
|
||||
|
||||
return haptic != 0;
|
||||
}
|
||||
|
||||
bool Joystick::isVibrationSupported()
|
||||
{
|
||||
if (!checkCreateHaptic())
|
||||
return false;
|
||||
|
||||
unsigned int features = SDL_HapticQuery(haptic);
|
||||
|
||||
if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
|
||||
return true;
|
||||
|
||||
// Some gamepad drivers only support left/right motors via a custom effect.
|
||||
if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
|
||||
return true;
|
||||
|
||||
// Check SDL's simple rumble as a last resort.
|
||||
if (SDL_HapticRumbleSupported(haptic) == 1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Joystick::runVibrationEffect()
|
||||
{
|
||||
if (vibration.id != -1)
|
||||
{
|
||||
if (SDL_HapticUpdateEffect(haptic, vibration.id, &vibration.effect) == 0)
|
||||
{
|
||||
if (SDL_HapticRunEffect(haptic, vibration.id, 1) != -1)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the effect fails to update, we should destroy and re-create it.
|
||||
SDL_HapticDestroyEffect(haptic, vibration.id);
|
||||
vibration.id = -1;
|
||||
}
|
||||
|
||||
vibration.id = SDL_HapticNewEffect(haptic, &vibration.effect);
|
||||
|
||||
if (vibration.id != -1 && SDL_HapticRunEffect(haptic, vibration.id, 1) != -1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Joystick::setVibration(float left, float right)
|
||||
{
|
||||
// TODO: support non-infinite durations? The working Tattiebogle Xbox
|
||||
// controller driver in OS X seems to ignore durations under 1 second.
|
||||
|
||||
left = std::min(std::max(left, 0.0f), 1.0f);
|
||||
right = std::min(std::max(right, 0.0f), 1.0f);
|
||||
|
||||
if (left == 0.0f && right == 0.0f)
|
||||
return setVibration();
|
||||
|
||||
if (!checkCreateHaptic())
|
||||
return false;
|
||||
|
||||
bool success = false;
|
||||
unsigned int features = SDL_HapticQuery(haptic);
|
||||
|
||||
if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
|
||||
{
|
||||
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
||||
vibration.effect.type = SDL_HAPTIC_LEFTRIGHT;
|
||||
|
||||
// SDL's Xinput code has an int. overflow bug with SDL_HAPTIC_INFINITY.
|
||||
const Uint32 long_time = 1000 * 60 * 60 * 2;
|
||||
|
||||
vibration.effect.leftright.length = long_time;
|
||||
vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX);
|
||||
vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX);
|
||||
|
||||
success = runVibrationEffect();
|
||||
}
|
||||
|
||||
// Some gamepad drivers only give support for controlling the motors via
|
||||
// a custom FF effect.
|
||||
if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
|
||||
{
|
||||
// NOTE: this may cause issues with drivers which support custom effects
|
||||
// but aren't similar to https://github.com/d235j/360Controller .
|
||||
|
||||
// Custom effect data is clamped to 0x7FFF in SDL.
|
||||
vibration.data[0] = vibration.data[2] = Uint16(left * 0x7FFF);
|
||||
vibration.data[1] = vibration.data[3] = Uint16(right * 0x7FFF);
|
||||
|
||||
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
|
||||
vibration.effect.type = SDL_HAPTIC_CUSTOM;
|
||||
|
||||
vibration.effect.custom.length = SDL_HAPTIC_INFINITY;
|
||||
vibration.effect.custom.channels = 2;
|
||||
vibration.effect.custom.period = 10;
|
||||
vibration.effect.custom.samples = 2;
|
||||
vibration.effect.custom.data = vibration.data;
|
||||
|
||||
success = runVibrationEffect();
|
||||
}
|
||||
|
||||
// Fall back to a simple rumble if all else fails. SDL's simple rumble API
|
||||
// only supports a single strength value.
|
||||
if (!success && SDL_HapticRumbleInit(haptic) == 0)
|
||||
{
|
||||
float strength = std::max(left, right);
|
||||
int played = SDL_HapticRumblePlay(haptic, strength, SDL_HAPTIC_INFINITY);
|
||||
success = (played == 0);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
vibration.left = left;
|
||||
vibration.right = right;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Joystick::setVibration()
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
|
||||
{
|
||||
// Stop all playing effects on the haptic device.
|
||||
// FIXME: We should only stop the vibration effect, in case we use the
|
||||
// Haptic API for other things in the future.
|
||||
success = (SDL_HapticStopAll(haptic) == 0);
|
||||
}
|
||||
|
||||
if (success)
|
||||
vibration.left = vibration.right = 0.0f;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Joystick::getVibration(float &left, float &right) const
|
||||
{
|
||||
left = vibration.left;
|
||||
right = vibration.right;
|
||||
}
|
||||
|
||||
bool Joystick::getConstant(Uint8 in, Joystick::Hat &out)
|
||||
{
|
||||
return hats.find(in, out);
|
||||
|
||||
Reference in New Issue
Block a user