Browse Source

Added an optional duration argument to Joystick:setVibration.

Alex Szpakowski 11 years ago
parent
commit
036f6eb8cb

+ 2 - 2
src/modules/joystick/Joystick.h

@@ -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
src/modules/joystick/sdl/Joystick.cpp

@@ -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)
-	, controller(0)
-	, haptic(0)
+	: joyhandle(nullptr)
+	, controller(nullptr)
+	, 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)
-	, controller(0)
-	, haptic(0)
+	: joyhandle(nullptr)
+	, controller(nullptr)
+	, 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;
-	controller = 0;
-	haptic = 0;
+	joyhandle = nullptr;
+	controller = nullptr;
+	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.
-	if (SDL_HapticRumbleSupported(haptic) == 1)
+	// Test for simple sine wave support as a last resort.
+	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
-	// 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
 		// 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
-	// 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);
 		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)
 	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)
-	{
-		// 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)
 	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
src/modules/joystick/sdl/Joystick.h

@@ -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
src/modules/joystick/sdl/JoystickModule.cpp

@@ -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
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 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);

+ 2 - 2
src/modules/joystick/sdl/wrap_JoystickModule.cpp

@@ -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();)
 	}
 	}