Added an optional duration argument to Joystick:setVibration.

This commit is contained in:
Alex Szpakowski
2014-05-10 16:52:40 -03:00
parent 6a491ed0f0
commit 036f6eb8cb
6 changed files with 98 additions and 57 deletions
+2 -2
View File
@@ -157,9 +157,9 @@ public:
virtual int getID() const = 0; virtual int getID() const = 0;
virtual bool isVibrationSupported() = 0; virtual bool isVibrationSupported() = 0;
virtual bool setVibration(float left, float right) = 0; virtual bool setVibration(float left, float right, float duration = -1.0f) = 0;
virtual bool setVibration() = 0; virtual bool setVibration() = 0;
virtual void getVibration(float &left, float &right) const = 0; virtual void getVibration(float &left, float &right) = 0;
static bool getConstant(const char *in, Hat &out); static bool getConstant(const char *in, Hat &out);
static bool getConstant(Hat in, const char *&out); static bool getConstant(Hat in, const char *&out);
+78 -42
View File
@@ -25,6 +25,11 @@
// C++ // C++
#include <algorithm> #include <algorithm>
#include <limits>
#ifndef SDL_TICKS_PASSED
#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0)
#endif
namespace love namespace love
{ {
@@ -34,9 +39,9 @@ namespace sdl
{ {
Joystick::Joystick(int id) Joystick::Joystick(int id)
: joyhandle(0) : joyhandle(nullptr)
, controller(0) , controller(nullptr)
, haptic(0) , haptic(nullptr)
, instanceid(-1) , instanceid(-1)
, id(id) , id(id)
, vibration() , vibration()
@@ -44,9 +49,9 @@ Joystick::Joystick(int id)
} }
Joystick::Joystick(int id, int joyindex) Joystick::Joystick(int id, int joyindex)
: joyhandle(0) : joyhandle(nullptr)
, controller(0) , controller(nullptr)
, haptic(0) , haptic(nullptr)
, instanceid(-1) , instanceid(-1)
, id(id) , id(id)
, vibration() , vibration()
@@ -95,10 +100,7 @@ bool Joystick::open(int deviceindex)
void Joystick::close() void Joystick::close()
{ {
if (haptic) if (haptic)
{
SDL_HapticRumbleStop(haptic);
SDL_HapticClose(haptic); SDL_HapticClose(haptic);
}
if (controller) if (controller)
SDL_GameControllerClose(controller); SDL_GameControllerClose(controller);
@@ -106,16 +108,16 @@ void Joystick::close()
if (joyhandle) if (joyhandle)
SDL_JoystickClose(joyhandle); SDL_JoystickClose(joyhandle);
joyhandle = 0; joyhandle = nullptr;
controller = 0; controller = nullptr;
haptic = 0; haptic = nullptr;
instanceid = -1; instanceid = -1;
vibration = Vibration(); vibration = Vibration();
} }
bool Joystick::isConnected() const bool Joystick::isConnected() const
{ {
return joyhandle != 0 && SDL_JoystickGetAttached(joyhandle); return joyhandle != nullptr && SDL_JoystickGetAttached(joyhandle);
} }
const char *Joystick::getName() const const char *Joystick::getName() const
@@ -209,7 +211,7 @@ bool Joystick::openGamepad(int deviceindex)
if (isGamepad()) if (isGamepad())
{ {
SDL_GameControllerClose(controller); SDL_GameControllerClose(controller);
controller = 0; controller = nullptr;
} }
controller = SDL_GameControllerOpen(deviceindex); controller = SDL_GameControllerOpen(deviceindex);
@@ -218,7 +220,7 @@ bool Joystick::openGamepad(int deviceindex)
bool Joystick::isGamepad() const bool Joystick::isGamepad() const
{ {
return controller != 0; return controller != nullptr;
} }
float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const
@@ -289,13 +291,13 @@ bool Joystick::checkCreateHaptic()
if (haptic) if (haptic)
{ {
SDL_HapticClose(haptic); SDL_HapticClose(haptic);
haptic = 0; haptic = nullptr;
} }
haptic = SDL_HapticOpenFromJoystick(joyhandle); haptic = SDL_HapticOpenFromJoystick(joyhandle);
vibration = Vibration(); vibration = Vibration();
return haptic != 0; return haptic != nullptr;
} }
bool Joystick::isVibrationSupported() bool Joystick::isVibrationSupported()
@@ -312,8 +314,8 @@ bool Joystick::isVibrationSupported()
if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0) if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0)
return true; return true;
// Check SDL's simple rumble as a last resort. // Test for simple sine wave support as a last resort.
if (SDL_HapticRumbleSupported(haptic) == 1) if ((features & SDL_HAPTIC_SINE) != 0)
return true; return true;
return false; return false;
@@ -325,7 +327,7 @@ bool Joystick::runVibrationEffect()
{ {
if (SDL_HapticUpdateEffect(haptic, vibration.id, &vibration.effect) == 0) if (SDL_HapticUpdateEffect(haptic, vibration.id, &vibration.effect) == 0)
{ {
if (SDL_HapticRunEffect(haptic, vibration.id, 1) != -1) if (SDL_HapticRunEffect(haptic, vibration.id, 1) == 0)
return true; return true;
} }
@@ -336,17 +338,14 @@ bool Joystick::runVibrationEffect()
vibration.id = SDL_HapticNewEffect(haptic, &vibration.effect); vibration.id = SDL_HapticNewEffect(haptic, &vibration.effect);
if (vibration.id != -1 && SDL_HapticRunEffect(haptic, vibration.id, 1) != -1) if (vibration.id != -1 && SDL_HapticRunEffect(haptic, vibration.id, 1) == 0)
return true; return true;
return false; return false;
} }
bool Joystick::setVibration(float left, float right) bool Joystick::setVibration(float left, float right, float duration)
{ {
// 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); left = std::min(std::max(left, 0.0f), 1.0f);
right = std::min(std::max(right, 0.0f), 1.0f); right = std::min(std::max(right, 0.0f), 1.0f);
@@ -356,24 +355,32 @@ bool Joystick::setVibration(float left, float right)
if (!checkCreateHaptic()) if (!checkCreateHaptic())
return false; return false;
Uint32 length = SDL_HAPTIC_INFINITY;
if (duration >= 0.0f)
{
float maxduration = std::numeric_limits<Uint32>::max() / 1000.0f;
length = Uint32(std::min(duration, maxduration) * 1000);
}
bool success = false; bool success = false;
unsigned int features = SDL_HapticQuery(haptic); unsigned int features = SDL_HapticQuery(haptic);
int axes = SDL_HapticNumAxes(haptic);
if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
{ {
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
vibration.effect.type = SDL_HAPTIC_LEFTRIGHT; vibration.effect.type = SDL_HAPTIC_LEFTRIGHT;
vibration.effect.leftright.length = SDL_HAPTIC_INFINITY; vibration.effect.leftright.length = length;
vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX); vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX);
vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX); vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX);
success = runVibrationEffect(); success = runVibrationEffect();
} }
// Some gamepad drivers only give support for controlling the motors via // Some gamepad drivers only give support for controlling individual motors
// a custom FF effect. // through a custom FF effect.
if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0) if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2)
{ {
// NOTE: this may cause issues with drivers which support custom effects // NOTE: this may cause issues with drivers which support custom effects
// but aren't similar to https://github.com/d235j/360Controller . // but aren't similar to https://github.com/d235j/360Controller .
@@ -385,7 +392,7 @@ bool Joystick::setVibration(float left, float right)
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
vibration.effect.type = SDL_HAPTIC_CUSTOM; vibration.effect.type = SDL_HAPTIC_CUSTOM;
vibration.effect.custom.length = SDL_HAPTIC_INFINITY; vibration.effect.custom.length = length;
vibration.effect.custom.channels = 2; vibration.effect.custom.channels = 2;
vibration.effect.custom.period = 10; vibration.effect.custom.period = 10;
vibration.effect.custom.samples = 2; vibration.effect.custom.samples = 2;
@@ -394,19 +401,36 @@ bool Joystick::setVibration(float left, float right)
success = runVibrationEffect(); success = runVibrationEffect();
} }
// Fall back to a simple rumble if all else fails. SDL's simple rumble API // Fall back to a simple sine wave if all else fails. This only supports a
// only supports a single strength value. // single strength value.
if (!success && SDL_HapticRumbleInit(haptic) == 0) if (!success && (features & SDL_HAPTIC_SINE) != 0)
{ {
memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
vibration.effect.type = SDL_HAPTIC_SINE;
vibration.effect.periodic.length = length;
vibration.effect.periodic.period = 10;
float strength = std::max(left, right); float strength = std::max(left, right);
int played = SDL_HapticRumblePlay(haptic, strength, SDL_HAPTIC_INFINITY); vibration.effect.periodic.magnitude = Sint16(strength * 0x7FFF);
success = (played == 0);
success = runVibrationEffect();
} }
if (success) if (success)
{ {
vibration.left = left; vibration.left = left;
vibration.right = right; vibration.right = right;
if (length == SDL_HAPTIC_INFINITY)
vibration.endtime = SDL_HAPTIC_INFINITY;
else
vibration.endtime = SDL_GetTicks() + length;
}
else
{
vibration.left = vibration.right = 0.0f;
vibration.endtime = SDL_HAPTIC_INFINITY;
} }
return success; return success;
@@ -417,12 +441,7 @@ bool Joystick::setVibration()
bool success = true; bool success = true;
if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1) if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
{ success = (SDL_HapticStopEffect(haptic, vibration.id) == 0);
// 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) if (success)
vibration.left = vibration.right = 0.0f; vibration.left = vibration.right = 0.0f;
@@ -430,8 +449,25 @@ bool Joystick::setVibration()
return success; return success;
} }
void Joystick::getVibration(float &left, float &right) const void Joystick::getVibration(float &left, float &right)
{ {
if (vibration.endtime != SDL_HAPTIC_INFINITY)
{
// With some drivers, the effect physically stops at the right time, but
// SDL_HapticGetEffectStatus still thinks it's playing. So we explicitly
// stop it once it's done, just to be sure.
if (SDL_TICKS_PASSED(SDL_GetTicks(), vibration.endtime))
{
setVibration();
vibration.endtime = SDL_HAPTIC_INFINITY;
}
}
// Check if the haptic effect has stopped playing.
int id = vibration.id;
if (!haptic || id == -1 || SDL_HapticGetEffectStatus(haptic, id) != 1)
vibration.left = vibration.right = 0.0f;
left = vibration.left; left = vibration.left;
right = vibration.right; right = vibration.right;
} }
+7 -3
View File
@@ -74,9 +74,9 @@ public:
int getID() const; int getID() const;
bool isVibrationSupported(); bool isVibrationSupported();
bool setVibration(float left, float right); bool setVibration(float left, float right, float duration = -1.0f);
bool setVibration(); bool setVibration();
void getVibration(float &left, float &right) const; void getVibration(float &left, float &right);
static bool getConstant(Hat in, Uint8 &out); static bool getConstant(Hat in, Uint8 &out);
static bool getConstant(Uint8 in, Hat &out); static bool getConstant(Uint8 in, Hat &out);
@@ -111,8 +111,12 @@ private:
Uint16 data[4]; Uint16 data[4];
int id; int id;
Uint32 endtime;
Vibration() Vibration()
: left(0.0f), right(0.0f), effect(), data(), id(-1) : left(0.0f), right(0.0f)
, effect(), data(), id(-1)
, endtime(SDL_HAPTIC_INFINITY)
{} {}
} vibration; } vibration;
+7 -7
View File
@@ -77,7 +77,7 @@ const char *JoystickModule::getName() const
love::joystick::Joystick *JoystickModule::getJoystick(int joyindex) love::joystick::Joystick *JoystickModule::getJoystick(int joyindex)
{ {
if (joyindex < 0 || (size_t) joyindex >= activeSticks.size()) if (joyindex < 0 || (size_t) joyindex >= activeSticks.size())
return 0; return nullptr;
return activeSticks[joyindex]; return activeSticks[joyindex];
} }
@@ -107,13 +107,13 @@ love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid)
return activeSticks[i]; return activeSticks[i];
} }
return 0; return nullptr;
} }
love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
{ {
if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks())
return 0; return nullptr;
std::string guidstr = getDeviceGUID(deviceindex); std::string guidstr = getDeviceGUID(deviceindex);
joystick::Joystick *joystick = 0; joystick::Joystick *joystick = 0;
@@ -140,7 +140,7 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex)
removeJoystick(joystick); removeJoystick(joystick);
if (!joystick->open(deviceindex)) if (!joystick->open(deviceindex))
return 0; return nullptr;
// Make sure multiple instances of the same physical joystick aren't added // Make sure multiple instances of the same physical joystick aren't added
// to the active list. // to the active list.
@@ -262,7 +262,7 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa
if (status == 1) if (status == 1)
checkGamepads(guid); checkGamepads(guid);
return status >= 0; return status >= 0;
} }
Joystick::JoystickInput JoystickModule::getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput) Joystick::JoystickInput JoystickModule::getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput)
@@ -318,7 +318,7 @@ std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinpu
SDL_GameControllerAxis sdlaxis; SDL_GameControllerAxis sdlaxis;
SDL_GameControllerButton sdlbutton; SDL_GameControllerButton sdlbutton;
const char *gpinputname = 0; const char *gpinputname = nullptr;
switch (gpinput.type) switch (gpinput.type)
{ {
@@ -435,7 +435,7 @@ void JoystickModule::checkGamepads(const std::string &guid) const
// Big hack time: open the index as a game controller and compare // Big hack time: open the index as a game controller and compare
// the underlying joystick handle to the active stick's. // the underlying joystick handle to the active stick's.
SDL_GameController *ctrl = SDL_GameControllerOpen(d_index); SDL_GameController *ctrl = SDL_GameControllerOpen(d_index);
if (ctrl == NULL) if (ctrl == nullptr)
continue; continue;
SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl); SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl);
+2 -1
View File
@@ -207,7 +207,8 @@ int w_Joystick_setVibration(lua_State *L)
{ {
float left = (float) luaL_checknumber(L, 2); float left = (float) luaL_checknumber(L, 2);
float right = (float) luaL_optnumber(L, 3, left); float right = (float) luaL_optnumber(L, 3, left);
success = j->setVibration(left, right); float duration = (float) luaL_optnumber(L, 4, -1.0); // -1 is infinite.
success = j->setVibration(left, right, duration);
} }
luax_pushboolean(L, success); luax_pushboolean(L, success);
@@ -28,7 +28,7 @@ namespace joystick
namespace sdl namespace sdl
{ {
static JoystickModule *instance = 0; static JoystickModule *instance = nullptr;
int w_getJoysticks(lua_State *L) int w_getJoysticks(lua_State *L)
{ {
@@ -194,7 +194,7 @@ static const lua_CFunction types[] =
extern "C" int luaopen_love_joystick(lua_State *L) extern "C" int luaopen_love_joystick(lua_State *L)
{ {
if (instance == 0) if (instance == nullptr)
{ {
EXCEPT_GUARD(instance = new JoystickModule();) EXCEPT_GUARD(instance = new JoystickModule();)
} }