diff --git a/src/modules/joystick/Joystick.h b/src/modules/joystick/Joystick.h index dde6bebdc..7f76c3a9c 100644 --- a/src/modules/joystick/Joystick.h +++ b/src/modules/joystick/Joystick.h @@ -157,9 +157,9 @@ public: virtual int getID() const = 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 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(Hat in, const char *&out); diff --git a/src/modules/joystick/sdl/Joystick.cpp b/src/modules/joystick/sdl/Joystick.cpp index ed370a270..a7eccae0c 100644 --- a/src/modules/joystick/sdl/Joystick.cpp +++ b/src/modules/joystick/sdl/Joystick.cpp @@ -25,6 +25,11 @@ // C++ #include +#include + +#ifndef SDL_TICKS_PASSED +#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0) +#endif namespace love { @@ -34,9 +39,9 @@ namespace sdl { Joystick::Joystick(int id) - : joyhandle(0) - , controller(0) - , haptic(0) + : joyhandle(nullptr) + , controller(nullptr) + , haptic(nullptr) , instanceid(-1) , id(id) , vibration() @@ -44,9 +49,9 @@ Joystick::Joystick(int id) } Joystick::Joystick(int id, int joyindex) - : joyhandle(0) - , controller(0) - , haptic(0) + : joyhandle(nullptr) + , controller(nullptr) + , haptic(nullptr) , instanceid(-1) , id(id) , vibration() @@ -95,10 +100,7 @@ bool Joystick::open(int deviceindex) void Joystick::close() { if (haptic) - { - SDL_HapticRumbleStop(haptic); SDL_HapticClose(haptic); - } if (controller) SDL_GameControllerClose(controller); @@ -106,16 +108,16 @@ void Joystick::close() if (joyhandle) SDL_JoystickClose(joyhandle); - joyhandle = 0; - controller = 0; - haptic = 0; + joyhandle = nullptr; + controller = nullptr; + haptic = nullptr; instanceid = -1; vibration = Vibration(); } bool Joystick::isConnected() const { - return joyhandle != 0 && SDL_JoystickGetAttached(joyhandle); + return joyhandle != nullptr && SDL_JoystickGetAttached(joyhandle); } const char *Joystick::getName() const @@ -209,7 +211,7 @@ bool Joystick::openGamepad(int deviceindex) if (isGamepad()) { SDL_GameControllerClose(controller); - controller = 0; + controller = nullptr; } controller = SDL_GameControllerOpen(deviceindex); @@ -218,7 +220,7 @@ bool Joystick::openGamepad(int deviceindex) bool Joystick::isGamepad() const { - return controller != 0; + return controller != nullptr; } float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const @@ -289,13 +291,13 @@ bool Joystick::checkCreateHaptic() if (haptic) { SDL_HapticClose(haptic); - haptic = 0; + haptic = nullptr; } haptic = SDL_HapticOpenFromJoystick(joyhandle); vibration = Vibration(); - return haptic != 0; + return haptic != nullptr; } bool Joystick::isVibrationSupported() @@ -312,8 +314,8 @@ bool Joystick::isVibrationSupported() if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0) return true; - // Check SDL's simple rumble as a last resort. - if (SDL_HapticRumbleSupported(haptic) == 1) + // Test for simple sine wave support as a last resort. + if ((features & SDL_HAPTIC_SINE) != 0) return true; return false; @@ -325,7 +327,7 @@ bool Joystick::runVibrationEffect() { 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; } @@ -336,17 +338,14 @@ bool Joystick::runVibrationEffect() 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 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); right = std::min(std::max(right, 0.0f), 1.0f); @@ -356,24 +355,32 @@ bool Joystick::setVibration(float left, float right) if (!checkCreateHaptic()) return false; + Uint32 length = SDL_HAPTIC_INFINITY; + if (duration >= 0.0f) + { + float maxduration = std::numeric_limits::max() / 1000.0f; + length = Uint32(std::min(duration, maxduration) * 1000); + } + bool success = false; unsigned int features = SDL_HapticQuery(haptic); + int axes = SDL_HapticNumAxes(haptic); if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) { memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); 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.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) + // Some gamepad drivers only give support for controlling individual motors + // through a custom FF effect. + if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2) { // NOTE: this may cause issues with drivers which support custom effects // 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)); 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.period = 10; vibration.effect.custom.samples = 2; @@ -394,19 +401,36 @@ bool Joystick::setVibration(float left, float right) 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) + // Fall back to a simple sine wave if all else fails. This only supports a + // single strength value. + 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); - int played = SDL_HapticRumblePlay(haptic, strength, SDL_HAPTIC_INFINITY); - success = (played == 0); + vibration.effect.periodic.magnitude = Sint16(strength * 0x7FFF); + + success = runVibrationEffect(); } if (success) { vibration.left = left; 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; @@ -417,12 +441,7 @@ 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); - } + success = (SDL_HapticStopEffect(haptic, vibration.id) == 0); if (success) vibration.left = vibration.right = 0.0f; @@ -430,8 +449,25 @@ bool Joystick::setVibration() 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; right = vibration.right; } diff --git a/src/modules/joystick/sdl/Joystick.h b/src/modules/joystick/sdl/Joystick.h index 929cb5454..6d9cdaba8 100644 --- a/src/modules/joystick/sdl/Joystick.h +++ b/src/modules/joystick/sdl/Joystick.h @@ -74,9 +74,9 @@ public: int getID() const; bool isVibrationSupported(); - bool setVibration(float left, float right); + bool setVibration(float left, float right, float duration = -1.0f); 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(Uint8 in, Hat &out); @@ -111,8 +111,12 @@ private: Uint16 data[4]; int id; + Uint32 endtime; + 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; diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp index f037927b1..ab0a28b16 100644 --- a/src/modules/joystick/sdl/JoystickModule.cpp +++ b/src/modules/joystick/sdl/JoystickModule.cpp @@ -77,7 +77,7 @@ const char *JoystickModule::getName() const love::joystick::Joystick *JoystickModule::getJoystick(int joyindex) { if (joyindex < 0 || (size_t) joyindex >= activeSticks.size()) - return 0; + return nullptr; return activeSticks[joyindex]; } @@ -107,13 +107,13 @@ love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid) return activeSticks[i]; } - return 0; + return nullptr; } love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) { if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) - return 0; + return nullptr; std::string guidstr = getDeviceGUID(deviceindex); joystick::Joystick *joystick = 0; @@ -140,7 +140,7 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) removeJoystick(joystick); if (!joystick->open(deviceindex)) - return 0; + return nullptr; // Make sure multiple instances of the same physical joystick aren't added // to the active list. @@ -262,7 +262,7 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa if (status == 1) checkGamepads(guid); - return status >= 0; + return status >= 0; } 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_GameControllerButton sdlbutton; - const char *gpinputname = 0; + const char *gpinputname = nullptr; 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 // the underlying joystick handle to the active stick's. SDL_GameController *ctrl = SDL_GameControllerOpen(d_index); - if (ctrl == NULL) + if (ctrl == nullptr) continue; SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl); diff --git a/src/modules/joystick/sdl/wrap_Joystick.cpp b/src/modules/joystick/sdl/wrap_Joystick.cpp index a0eb45694..90a3f4d3f 100644 --- a/src/modules/joystick/sdl/wrap_Joystick.cpp +++ b/src/modules/joystick/sdl/wrap_Joystick.cpp @@ -207,7 +207,8 @@ int w_Joystick_setVibration(lua_State *L) { float left = (float) luaL_checknumber(L, 2); 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); diff --git a/src/modules/joystick/sdl/wrap_JoystickModule.cpp b/src/modules/joystick/sdl/wrap_JoystickModule.cpp index a069e0156..7a438ff16 100644 --- a/src/modules/joystick/sdl/wrap_JoystickModule.cpp +++ b/src/modules/joystick/sdl/wrap_JoystickModule.cpp @@ -28,7 +28,7 @@ namespace joystick namespace sdl { -static JoystickModule *instance = 0; +static JoystickModule *instance = nullptr; int w_getJoysticks(lua_State *L) { @@ -194,7 +194,7 @@ static const lua_CFunction types[] = extern "C" int luaopen_love_joystick(lua_State *L) { - if (instance == 0) + if (instance == nullptr) { EXCEPT_GUARD(instance = new JoystickModule();) }