Browse Source

Merge pull request #1873 from MikuAuahDark/sensor

love.sensor module and Joystick sensor functions.
Sasha Szpakowski 2 years ago
parent
commit
77f43342f6

+ 25 - 0
CMakeLists.txt

@@ -907,6 +907,30 @@ set(LOVE_SRC_MODULE_PHYSICS
 source_group("modules\\physics" FILES ${LOVE_SRC_MODULE_PHYSICS_ROOT})
 source_group("modules\\physics" FILES ${LOVE_SRC_MODULE_PHYSICS_ROOT})
 source_group("modules\\physics\\box2d" FILES ${LOVE_SRC_MODULE_PHYSICS_BOX2D})
 source_group("modules\\physics\\box2d" FILES ${LOVE_SRC_MODULE_PHYSICS_BOX2D})
 
 
+#
+# love.sensor
+#
+
+set(LOVE_SRC_MODULE_SENSOR_ROOT
+	src/modules/sensor/Sensor.cpp
+	src/modules/sensor/Sensor.h
+	src/modules/sensor/wrap_Sensor.cpp
+	src/modules/sensor/wrap_Sensor.h
+)
+
+set(LOVE_SRC_MODULE_SENSOR_SDL
+	src/modules/sensor/sdl/Sensor.cpp
+	src/modules/sensor/sdl/Sensor.h
+)
+
+set(LOVE_SRC_MODULE_SENSOR
+	${LOVE_SRC_MODULE_SENSOR_ROOT}
+	${LOVE_SRC_MODULE_SENSOR_SDL}
+)
+
+source_group("modules\\sensor" FILES ${LOVE_SRC_MODULE_SENSOR_ROOT})
+source_group("modules\\sensor\\sdl" FILES ${LOVE_SRC_MODULE_SENSOR_SDL})
+
 #
 #
 # love.sound
 # love.sound
 #
 #
@@ -1881,6 +1905,7 @@ set(LOVE_LIB_SRC
 	${LOVE_SRC_MODULE_MATH}
 	${LOVE_SRC_MODULE_MATH}
 	${LOVE_SRC_MODULE_MOUSE}
 	${LOVE_SRC_MODULE_MOUSE}
 	${LOVE_SRC_MODULE_PHYSICS}
 	${LOVE_SRC_MODULE_PHYSICS}
+	${LOVE_SRC_MODULE_SENSOR}
 	${LOVE_SRC_MODULE_SOUND}
 	${LOVE_SRC_MODULE_SOUND}
 	${LOVE_SRC_MODULE_SYSTEM}
 	${LOVE_SRC_MODULE_SYSTEM}
 	${LOVE_SRC_MODULE_THREAD}
 	${LOVE_SRC_MODULE_THREAD}

+ 9 - 0
changes.txt

@@ -25,6 +25,10 @@ Released: N/A
 * Added love.keyboard.isModifierActive.
 * Added love.keyboard.isModifierActive.
 * Added Joystick:setPlayerIndex and Joystick:getPlayerIndex.
 * Added Joystick:setPlayerIndex and Joystick:getPlayerIndex.
 * Added Joystick:getGamepadType.
 * Added Joystick:getGamepadType.
+* Added Joystick:hasSensor.
+* Added Joystick:isSensorEnabled.
+* Added Joystick:setSensorEnabled.
+* Added Joystick:getSensorData.
 * Added new Gamepad API buttons: "misc1", "paddle1", "paddle2", "paddle3", "paddle4". and "touchpad".
 * Added new Gamepad API buttons: "misc1", "paddle1", "paddle2", "paddle3", "paddle4". and "touchpad".
 * Added World:getFixturesInArea().
 * Added World:getFixturesInArea().
 * Added support for saving .exr image files via ImageData:encode.
 * Added support for saving .exr image files via ImageData:encode.
@@ -61,6 +65,9 @@ Released: N/A
 * Added a variant of love.graphics.setColorMask which accepts a single boolean.
 * Added a variant of love.graphics.setColorMask which accepts a single boolean.
 * Added new 'clampone' wrap mode.
 * Added new 'clampone' wrap mode.
 * Added a variant of Font:getWidth which takes a codepoint number argument.
 * Added a variant of Font:getWidth which takes a codepoint number argument.
+* Added love.sensor module.
+* Added love.sensorupdated callback.
+* Added love.joysticksensorupdated callback.
 
 
 * Changed the default font from Vera size 12 to Noto Sans size 13.
 * Changed the default font from Vera size 12 to Noto Sans size 13.
 * Changed the Texture class and implementation to no longer have separate Canvas and Image subclasses.
 * Changed the Texture class and implementation to no longer have separate Canvas and Image subclasses.
@@ -75,6 +82,7 @@ Released: N/A
 * Changed love.filesystem.exists to no longer be deprecated.
 * Changed love.filesystem.exists to no longer be deprecated.
 * Changed RevoluteJoint:getMotorTorque to take 'dt' as a parameter instead of 'inverse_dt'.
 * Changed RevoluteJoint:getMotorTorque to take 'dt' as a parameter instead of 'inverse_dt'.
 * Changed love.math.perlinNoise and simplexNoise to use higher precision numbers for its internal calculations.
 * Changed love.math.perlinNoise and simplexNoise to use higher precision numbers for its internal calculations.
+* Changed t.accelerometerjoystick startup flag in love.conf to unset by default.
 
 
 * Renamed 'display' field to 'displayindex' in love.window.setMode/updateMode/getMode and love.conf.
 * Renamed 'display' field to 'displayindex' in love.window.setMode/updateMode/getMode and love.conf.
 
 
@@ -86,6 +94,7 @@ Released: N/A
 * Deprecated love.math.noise (replaced by perlinNoise and simplexNoise).
 * Deprecated love.math.noise (replaced by perlinNoise and simplexNoise).
 * Deprecated love.graphics.getImageFormats and love.graphics.getCanvasFormats (replaced by getTextureFormats).
 * Deprecated love.graphics.getImageFormats and love.graphics.getCanvasFormats (replaced by getTextureFormats).
 * Deprecated t.window.highdpi in love.conf and the highdpi flag of love.window.setMode (replaced by t.highdpi in love.conf).
 * Deprecated t.window.highdpi in love.conf and the highdpi flag of love.window.setMode (replaced by t.highdpi in love.conf).
+* Deprecated t.accelerometerjoystick in love.conf (replaced by love.sensor module).
 * Deprecated the variants of Mesh:attachAttribute and SpriteBatch:attachAttribute which accept a Mesh (replaced by variants which accept a Buffer).
 * Deprecated the variants of Mesh:attachAttribute and SpriteBatch:attachAttribute which accept a Mesh (replaced by variants which accept a Buffer).
 * Deprecated Texture:newImageData (replaced by love.graphics.readbackTexture).
 * Deprecated Texture:newImageData (replaced by love.graphics.readbackTexture).
 
 

+ 1 - 0
src/common/Module.h

@@ -51,6 +51,7 @@ public:
 		M_MATH,
 		M_MATH,
 		M_MOUSE,
 		M_MOUSE,
 		M_PHYSICS,
 		M_PHYSICS,
+		M_SENSOR,
 		M_SOUND,
 		M_SOUND,
 		M_SYSTEM,
 		M_SYSTEM,
 		M_THREAD,
 		M_THREAD,

+ 1 - 0
src/common/config.h

@@ -159,6 +159,7 @@
 #	define LOVE_ENABLE_MATH
 #	define LOVE_ENABLE_MATH
 #	define LOVE_ENABLE_MOUSE
 #	define LOVE_ENABLE_MOUSE
 #	define LOVE_ENABLE_PHYSICS
 #	define LOVE_ENABLE_PHYSICS
+#	define LOVE_ENABLE_SENSOR
 #	define LOVE_ENABLE_SOUND
 #	define LOVE_ENABLE_SOUND
 #	define LOVE_ENABLE_SYSTEM
 #	define LOVE_ENABLE_SYSTEM
 #	define LOVE_ENABLE_THREAD
 #	define LOVE_ENABLE_THREAD

+ 55 - 0
src/modules/event/sdl/Event.cpp

@@ -32,6 +32,7 @@
 #include "audio/Audio.h"
 #include "audio/Audio.h"
 #include "common/config.h"
 #include "common/config.h"
 #include "timer/Timer.h"
 #include "timer/Timer.h"
+#include "sensor/sdl/Sensor.h"
 
 
 #include <cmath>
 #include <cmath>
 
 
@@ -178,6 +179,7 @@ Message *Event::convert(const SDL_Event &e)
 	vargs.reserve(4);
 	vargs.reserve(4);
 
 
 	love::filesystem::Filesystem *filesystem = nullptr;
 	love::filesystem::Filesystem *filesystem = nullptr;
+	love::sensor::Sensor *sensorInstance = nullptr;
 
 
 	love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN;
 	love::keyboard::Keyboard::Key key = love::keyboard::Keyboard::KEY_UNKNOWN;
 	love::keyboard::Keyboard::Scancode scancode = love::keyboard::Keyboard::SCANCODE_UNKNOWN;
 	love::keyboard::Keyboard::Scancode scancode = love::keyboard::Keyboard::SCANCODE_UNKNOWN;
@@ -366,6 +368,7 @@ Message *Event::convert(const SDL_Event &e)
 	case SDL_CONTROLLERBUTTONDOWN:
 	case SDL_CONTROLLERBUTTONDOWN:
 	case SDL_CONTROLLERBUTTONUP:
 	case SDL_CONTROLLERBUTTONUP:
 	case SDL_CONTROLLERAXISMOTION:
 	case SDL_CONTROLLERAXISMOTION:
+	case SDL_CONTROLLERSENSORUPDATE:
 		msg = convertJoystickEvent(e);
 		msg = convertJoystickEvent(e);
 		break;
 		break;
 	case SDL_WINDOWEVENT:
 	case SDL_WINDOWEVENT:
@@ -446,6 +449,37 @@ Message *Event::convert(const SDL_Event &e)
 		msg = new Message("localechanged");
 		msg = new Message("localechanged");
 		break;
 		break;
 #endif
 #endif
+	case SDL_SENSORUPDATE:
+		sensorInstance = Module::getInstance<sensor::Sensor>(M_SENSOR);
+		if (sensorInstance)
+		{
+			std::vector<void*> sensors = sensorInstance->getHandles();
+
+			for (void *s: sensors)
+			{
+				SDL_Sensor *sensor = (SDL_Sensor *) s;
+				SDL_SensorID id = SDL_SensorGetInstanceID(sensor);
+
+				if (e.sensor.which == id)
+				{
+					// Found sensor
+					const char *sensorType;
+					if (!sensor::Sensor::getConstant(sensor::sdl::Sensor::convert(SDL_SensorGetType(sensor)), sensorType))
+						sensorType = "unknown";
+
+					vargs.emplace_back(sensorType, strlen(sensorType));
+					// Both accelerometer and gyroscope only pass up to 3 values.
+					// https://github.com/libsdl-org/SDL/blob/SDL2/include/SDL_sensor.h#L81-L127
+					vargs.emplace_back(e.sensor.data[0]);
+					vargs.emplace_back(e.sensor.data[1]);
+					vargs.emplace_back(e.sensor.data[2]);
+					msg = new Message("sensorupdated", vargs);
+
+					break;
+				}
+			}
+		}
+		break;
 	default:
 	default:
 		break;
 		break;
 	}
 	}
@@ -564,6 +598,27 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 			msg = new Message("joystickremoved", vargs);
 			msg = new Message("joystickremoved", vargs);
 		}
 		}
 		break;
 		break;
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	case SDL_CONTROLLERSENSORUPDATE:
+		stick = joymodule->getJoystickFromID(e.csensor.which);
+		if (stick)
+		{
+			using Sensor = love::sensor::Sensor;
+
+			const char *sensorName;
+			Sensor::SensorType sensorType = love::sensor::sdl::Sensor::convert((SDL_SensorType) e.csensor.sensor);
+			if (!Sensor::getConstant(sensorType, sensorName))
+				sensorName = "unknown";
+
+			vargs.emplace_back(joysticktype, stick);
+			vargs.emplace_back(sensorName, strlen(sensorName));
+			vargs.emplace_back(e.csensor.data[0]);
+			vargs.emplace_back(e.csensor.data[1]);
+			vargs.emplace_back(e.csensor.data[2]);
+			msg = new Message("joysticksensorupdated", vargs);
+		}
+		break;
+#endif
 	default:
 	default:
 		break;
 		break;
 	}
 	}

+ 8 - 0
src/modules/joystick/Joystick.h

@@ -24,6 +24,7 @@
 // LOVE
 // LOVE
 #include "common/Object.h"
 #include "common/Object.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
+#include "sensor/Sensor.h"
 
 
 // stdlib
 // stdlib
 #include <vector>
 #include <vector>
@@ -38,6 +39,8 @@ class Joystick : public Object
 {
 {
 public:
 public:
 
 
+	using Sensor = love::sensor::Sensor;
+
 	static love::Type type;
 	static love::Type type;
 
 
 	// Joystick hat values.
 	// Joystick hat values.
@@ -198,6 +201,11 @@ public:
 	virtual bool setVibration() = 0;
 	virtual bool setVibration() = 0;
 	virtual void getVibration(float &left, float &right) = 0;
 	virtual void getVibration(float &left, float &right) = 0;
 
 
+	virtual bool hasSensor(Sensor::SensorType type) const = 0;
+	virtual bool isSensorEnabled(Sensor::SensorType type) const = 0;
+	virtual void setSensorEnabled(Sensor::SensorType type, bool enabled) = 0;
+	virtual std::vector<float> getSensorData(Sensor::SensorType type) const = 0;
+
 	STRINGMAP_CLASS_DECLARE(Hat);
 	STRINGMAP_CLASS_DECLARE(Hat);
 	STRINGMAP_CLASS_DECLARE(GamepadType);
 	STRINGMAP_CLASS_DECLARE(GamepadType);
 	STRINGMAP_CLASS_DECLARE(GamepadAxis);
 	STRINGMAP_CLASS_DECLARE(GamepadAxis);

+ 84 - 0
src/modules/joystick/sdl/Joystick.cpp

@@ -22,6 +22,10 @@
 #include "common/config.h"
 #include "common/config.h"
 #include "Joystick.h"
 #include "Joystick.h"
 #include "common/int.h"
 #include "common/int.h"
+#include "sensor/sdl/Sensor.h"
+
+// SDL
+#include <SDL_version.h>
 
 
 // C++
 // C++
 #include <algorithm>
 #include <algorithm>
@@ -643,6 +647,86 @@ void Joystick::getVibration(float &left, float &right)
 	right = vibration.right;
 	right = vibration.right;
 }
 }
 
 
+bool Joystick::hasSensor(Sensor::SensorType type) const
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		return false;
+
+	return SDL_GameControllerHasSensor(controller, SDLSensor::convert(type)) == SDL_TRUE;
+#else
+	return false;
+#endif
+}
+
+bool Joystick::isSensorEnabled(Sensor::SensorType type) const
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		return false;
+
+	return SDL_GameControllerIsSensorEnabled(controller, SDLSensor::convert(type)) == SDL_TRUE;
+#else
+	return false;
+#endif
+}
+
+void Joystick::setSensorEnabled(Sensor::SensorType type, bool enabled)
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		throw love::Exception("Sensor is only supported on gamepad");
+
+	if (SDL_GameControllerSetSensorEnabled(controller, SDLSensor::convert(type), enabled ? SDL_TRUE : SDL_FALSE) != 0)
+	{
+		const char *name = nullptr;
+		SDLSensor::getConstant(type, name);
+
+		throw love::Exception("Could not open \"%s\" SDL gamepad sensor (%s)", name, SDL_GetError());
+	}
+#else
+	throw love::Exception("Compiled version of SDL or LOVE does not support gamepad sensor");
+#endif
+}
+
+std::vector<float> Joystick::getSensorData(Sensor::SensorType type) const
+{
+#if SDL_VERSION_ATLEAST(2, 0, 14) && defined(LOVE_ENABLE_SENSOR)
+	using SDLSensor = love::sensor::sdl::Sensor;
+
+	if (!isGamepad())
+		throw love::Exception("Sensor is only supported on gamepad");
+
+	std::vector<float> data(3);
+
+	if (!isSensorEnabled(type))
+	{
+		const char *name = nullptr;
+		SDLSensor::getConstant(type, name);
+
+		throw love::Exception("\"%s\" gamepad sensor is not enabled", name);
+	}
+
+	if (SDL_GameControllerGetSensorData(controller, SDLSensor::convert(type), data.data(), (int) data.size()) != 0)
+	{
+		const char *name = nullptr;
+		SDLSensor::getConstant(type, name);
+
+		throw love::Exception("Could not get \"%s\" SDL gamepad sensor data (%s)", name, SDL_GetError());
+	}
+
+	return data;
+#else
+	throw love::Exception("Compiled version of SDL or LOVE does not support gamepad sensor");
+#endif
+}
+
 bool Joystick::getConstant(Uint8 in, Joystick::Hat &out)
 bool Joystick::getConstant(Uint8 in, Joystick::Hat &out)
 {
 {
 	return hats.find(in, out);
 	return hats.find(in, out);

+ 5 - 0
src/modules/joystick/sdl/Joystick.h

@@ -88,6 +88,11 @@ public:
 	bool setVibration() override;
 	bool setVibration() override;
 	void getVibration(float &left, float &right) override;
 	void getVibration(float &left, float &right) override;
 
 
+	bool hasSensor(Sensor::SensorType type) const override;
+	bool isSensorEnabled(Sensor::SensorType type) const override;
+	void setSensorEnabled(Sensor::SensorType type, bool enabled) override;
+	std::vector<float> getSensorData(Sensor::SensorType type) const override;
+
 	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);
 
 

+ 80 - 0
src/modules/joystick/wrap_Joystick.cpp

@@ -21,6 +21,7 @@
 // LOVE
 // LOVE
 #include "wrap_Joystick.h"
 #include "wrap_Joystick.h"
 #include "wrap_JoystickModule.h"
 #include "wrap_JoystickModule.h"
+#include "sensor/Sensor.h"
 
 
 #include <vector>
 #include <vector>
 
 
@@ -367,6 +368,78 @@ int w_Joystick_getVibration(lua_State *L)
 	return 2;
 	return 2;
 }
 }
 
 
+#ifdef LOVE_ENABLE_SENSOR
+
+int w_Joystick_hasSensor(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	luax_pushboolean(L, j->hasSensor(type));
+	return 1;
+}
+
+int w_Joystick_isSensorEnabled(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	luax_pushboolean(L, j->isSensorEnabled(type));
+	return 1;
+}
+
+int w_Joystick_setSensorEnabled(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	bool enabled = luax_checkboolean(L, 3);
+
+	luax_catchexcept(L, [&]() { j->setSensorEnabled(type, enabled); });
+	return 0;
+}
+
+int w_Joystick_getSensorData(lua_State *L)
+{
+	using namespace love::sensor;
+
+	Joystick *j = luax_checkjoystick(L, 1);
+	const char *sensorType = luaL_checkstring(L, 2);
+
+	Sensor::SensorType type;
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor type", Sensor::getConstants(type), sensorType);
+
+	std::vector<float> data;
+
+	luax_catchexcept(L, [&]() { data = j->getSensorData(type); });
+
+	for (float f: data)
+		lua_pushnumber(L, f);
+
+	return (int) data.size();
+}
+
+#endif // LOVE_ENABLE_SENSOR
+
 // List of functions to wrap.
 // List of functions to wrap.
 static const luaL_Reg w_Joystick_functions[] =
 static const luaL_Reg w_Joystick_functions[] =
 {
 {
@@ -396,6 +469,13 @@ static const luaL_Reg w_Joystick_functions[] =
 	{ "setVibration", w_Joystick_setVibration },
 	{ "setVibration", w_Joystick_setVibration },
 	{ "getVibration", w_Joystick_getVibration },
 	{ "getVibration", w_Joystick_getVibration },
 
 
+#ifdef LOVE_ENABLE_SENSOR
+	{ "hasSensor", w_Joystick_hasSensor },
+	{ "isSensorEnabled", w_Joystick_isSensorEnabled },
+	{ "setSensorEnabled", w_Joystick_setSensorEnabled },
+	{ "getSensorData", w_Joystick_setSensorEnabled },
+#endif
+
 	// From wrap_JoystickModule.
 	// From wrap_JoystickModule.
 	{ "getConnectedIndex", w_getIndex },
 	{ "getConnectedIndex", w_getIndex },
 
 

+ 3 - 1
src/modules/love/boot.lua

@@ -189,6 +189,7 @@ function love.init()
 			audio = true,
 			audio = true,
 			math = true,
 			math = true,
 			physics = true,
 			physics = true,
+			sensor = true,
 			sound = true,
 			sound = true,
 			system = true,
 			system = true,
 			font = true,
 			font = true,
@@ -204,7 +205,7 @@ function love.init()
 		identity = false,
 		identity = false,
 		appendidentity = false,
 		appendidentity = false,
 		externalstorage = false, -- Only relevant for Android.
 		externalstorage = false, -- Only relevant for Android.
-		accelerometerjoystick = true, -- Only relevant for Android / iOS.
+		accelerometerjoystick = nil, -- Only relevant for Android / iOS, deprecated.
 		gammacorrect = false,
 		gammacorrect = false,
 		highdpi = false,
 		highdpi = false,
 		renderers = nil,
 		renderers = nil,
@@ -311,6 +312,7 @@ function love.init()
 		"touch",
 		"touch",
 		"sound",
 		"sound",
 		"system",
 		"system",
+		"sensor",
 		"audio",
 		"audio",
 		"image",
 		"image",
 		"video",
 		"video",

+ 6 - 0
src/modules/love/callbacks.lua

@@ -88,6 +88,9 @@ function love.createhandlers()
 		joystickremoved = function (j)
 		joystickremoved = function (j)
 			if love.joystickremoved then return love.joystickremoved(j) end
 			if love.joystickremoved then return love.joystickremoved(j) end
 		end,
 		end,
+		joysticksensorupdated = function (j, sensorType, x, y, z)
+			if love.joysticksensorupdated then return love.joysticksensorupdated(j, sensorType, x, y, z) end
+		end,
 		focus = function (f)
 		focus = function (f)
 			if love.focus then return love.focus(f) end
 			if love.focus then return love.focus(f) end
 		end,
 		end,
@@ -134,6 +137,9 @@ function love.createhandlers()
 				love.audio.setPlaybackDevice()
 				love.audio.setPlaybackDevice()
 			end
 			end
 		end,
 		end,
+		sensorupdated = function (sensorType, x, y, z)
+			if love.sensorupdated then return love.sensorupdated(sensorType, x, y, z) end
+		end,
 	}, {
 	}, {
 		__index = function(self, name)
 		__index = function(self, name)
 			error("Unknown event: " .. name)
 			error("Unknown event: " .. name)

+ 10 - 0
src/modules/love/love.cpp

@@ -152,6 +152,9 @@ extern "C"
 #if defined(LOVE_ENABLE_PHYSICS)
 #if defined(LOVE_ENABLE_PHYSICS)
 	extern int luaopen_love_physics(lua_State*);
 	extern int luaopen_love_physics(lua_State*);
 #endif
 #endif
+#if defined(LOVE_ENABLE_SENSOR)
+	extern int luaopen_love_sensor(lua_State*);
+#endif
 #if defined(LOVE_ENABLE_SOUND)
 #if defined(LOVE_ENABLE_SOUND)
 	extern int luaopen_love_sound(lua_State*);
 	extern int luaopen_love_sound(lua_State*);
 #endif
 #endif
@@ -221,6 +224,9 @@ static const luaL_Reg modules[] = {
 #if defined(LOVE_ENABLE_PHYSICS)
 #if defined(LOVE_ENABLE_PHYSICS)
 	{ "love.physics", luaopen_love_physics },
 	{ "love.physics", luaopen_love_physics },
 #endif
 #endif
+#if defined(LOVE_ENABLE_SENSOR)
+	{ "love.sensor", luaopen_love_sensor },
+#endif
 #if defined(LOVE_ENABLE_SOUND)
 #if defined(LOVE_ENABLE_SOUND)
 	{ "love.sound", luaopen_love_sound },
 	{ "love.sound", luaopen_love_sound },
 #endif
 #endif
@@ -778,6 +784,10 @@ int w__setAccelerometerAsJoystick(lua_State *L)
 {
 {
 	bool enable = (bool) lua_toboolean(L, 1);
 	bool enable = (bool) lua_toboolean(L, 1);
 	SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, enable ? "1" : "0");
 	SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, enable ? "1" : "0");
+
+	if (enable)
+		love::luax_markdeprecated(L, 1, "accelerometerjoystick", love::API_FIELD, love::DEPRECATED_REPLACED, "love.sensor module");
+
 	return 0;
 	return 0;
 }
 }
 #endif // LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK
 #endif // LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK

+ 37 - 0
src/modules/sensor/Sensor.cpp

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * 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.
+ **/
+
+// LOVE
+#include "Sensor.h"
+
+namespace love
+{
+namespace sensor
+{
+
+STRINGMAP_CLASS_BEGIN(Sensor, Sensor::SensorType, Sensor::SENSOR_MAX_ENUM, sensorType)
+{
+	{ "accelerometer", Sensor::SENSOR_ACCELEROMETER },
+	{ "gyroscope",     Sensor::SENSOR_GYROSCOPE     },
+}
+STRINGMAP_CLASS_END(Sensor, Sensor::SensorType, Sensor::SENSOR_MAX_ENUM, sensorType)
+
+} // sensor
+} // love

+ 83 - 0
src/modules/sensor/Sensor.h

@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * 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 LOVE_SENSOR_H
+#define LOVE_SENSOR_H
+
+ // LOVE
+#include "common/Module.h"
+#include "common/StringMap.h"
+
+namespace love
+{
+namespace sensor
+{
+
+class Sensor: public Module
+{
+public:
+
+	enum SensorType
+	{
+		SENSOR_ACCELEROMETER,
+		SENSOR_GYROSCOPE,
+		SENSOR_MAX_ENUM
+	};
+
+	virtual ~Sensor() {}
+
+	// Implements Module.
+	ModuleType getModuleType() const override { return M_SENSOR; }
+
+	/**
+	 * Check the availability of the sensor.
+	 **/
+	virtual bool hasSensor(SensorType type) = 0;
+
+	/**
+	 * Check if the sensor is enabled.
+	 **/
+	virtual bool isEnabled(SensorType type) = 0;
+
+	/**
+	 * Enable or disable a sensor.
+	 **/
+	virtual void setEnabled(SensorType type, bool enabled) = 0;
+
+	/**
+	 * Get data from sensor.
+	 **/
+	virtual std::vector<float> getData(SensorType type) = 0;
+
+	/**
+	 * Get backend-dependent handle of sensor.
+	 **/
+	virtual std::vector<void *> getHandles() = 0;
+
+	virtual const char *getSensorName(SensorType type) = 0;
+
+	STRINGMAP_CLASS_DECLARE(SensorType);
+
+}; // Sensor
+
+} // sensor
+} // love
+
+#endif

+ 174 - 0
src/modules/sensor/sdl/Sensor.cpp

@@ -0,0 +1,174 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * 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.
+ **/
+
+// LOVE
+#include "Sensor.h"
+
+// SDL
+#include <SDL.h>
+#include <SDL_sensor.h>
+
+namespace love
+{
+namespace sensor
+{
+namespace sdl
+{
+
+Sensor::Sensor()
+: sensors()
+{
+	if (SDL_InitSubSystem(SDL_INIT_SENSOR) < 0)
+		throw love::Exception("Could not initialize SDL sensor subsystem (%s)", SDL_GetError());
+}
+
+Sensor::~Sensor()
+{
+	SDL_QuitSubSystem(SDL_INIT_SENSOR);
+}
+
+const char *Sensor::getName() const
+{
+	return "love.sensor.sdl";
+}
+
+bool Sensor::hasSensor(SensorType type)
+{
+	for (int i = 0; i < SDL_NumSensors(); i++)
+	{
+		if (convert(SDL_SensorGetDeviceType(i)) == type)
+			return true;
+	}
+
+	return false;
+}
+
+bool Sensor::isEnabled(SensorType type)
+{
+	return sensors[type]; // nullptr is default
+}
+
+void Sensor::setEnabled(SensorType type, bool enable)
+{
+	if (sensors[type] && !enable)
+	{
+		SDL_SensorClose(sensors[type]);
+		sensors[type] = nullptr;
+	}
+	else if (sensors[type] == nullptr && enable)
+	{
+		for (int i = 0; i < SDL_NumSensors(); i++)
+		{
+			if (convert(SDL_SensorGetDeviceType(i)) == type)
+			{
+				SDL_Sensor *sensorHandle = SDL_SensorOpen(i);
+
+				if (sensorHandle == nullptr)
+				{
+					const char *name = nullptr;
+					getConstant(type, name);
+
+					throw love::Exception("Could not open \"%s\" SDL sensor (%s)", name, SDL_GetError());
+				}
+
+				sensors[type] = sensorHandle;
+			}
+		}
+	}
+}
+
+std::vector<float> Sensor::getData(SensorType type)
+{
+	if (sensors[type] == nullptr)
+	{
+		const char *name = nullptr;
+		getConstant(type, name);
+
+		throw love::Exception("\"%s\" sensor is not enabled", name);
+	}
+
+	std::vector<float> values(3);
+
+	if (SDL_SensorGetData(sensors[type], values.data(), (int) values.size()) != 0)
+	{
+		const char *name = nullptr;
+		getConstant(type, name);
+
+		throw love::Exception("Could not get \"%s\" SDL sensor data (%s)", name, SDL_GetError());
+	}
+
+	return values;
+}
+
+std::vector<void*> Sensor::getHandles()
+{
+	std::vector<void*> nativeSensor;
+
+	for (const std::pair<SensorType, SDL_Sensor*> &data: sensors)
+	{
+		if (data.second)
+			nativeSensor.push_back(data.second);
+	}
+
+	return nativeSensor;
+}
+
+const char *Sensor::getSensorName(SensorType type)
+{
+	if (sensors[type] == nullptr)
+	{
+		const char *name = nullptr;
+		getConstant(type, name);
+
+		throw love::Exception("\"%s\" sensor is not enabled", name);
+	}
+
+	return SDL_SensorGetName(sensors[type]);
+}
+
+Sensor::SensorType Sensor::convert(SDL_SensorType type)
+{
+	switch (type)
+	{
+		case SDL_SENSOR_ACCEL:
+			return SENSOR_ACCELEROMETER;
+		case SDL_SENSOR_GYRO:
+			return SENSOR_GYROSCOPE;
+		default:
+			return SENSOR_MAX_ENUM;
+	}
+}
+
+SDL_SensorType Sensor::convert(Sensor::SensorType type)
+{
+	switch (type)
+	{
+		case SENSOR_ACCELEROMETER:
+			return SDL_SENSOR_ACCEL;
+		case SENSOR_GYROSCOPE:
+			return SDL_SENSOR_GYRO;
+		default:
+			return SDL_SENSOR_UNKNOWN;
+	}
+}
+
+}
+}
+}

+ 68 - 0
src/modules/sensor/sdl/Sensor.h

@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * 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 LOVE_SENSOR_SDL_SENSOR_H
+#define LOVE_SENSOR_SDL_SENSOR_H
+
+// LOVE
+#include "sensor/Sensor.h"
+
+// SDL
+#include <SDL_sensor.h>
+
+// std
+#include <map>
+
+namespace love
+{
+namespace sensor
+{
+namespace sdl
+{
+
+class Sensor : public love::sensor::Sensor
+{
+public:
+	Sensor();
+	~Sensor() override;
+
+	// Implements Module.
+	const char *getName() const override;
+
+	bool hasSensor(SensorType type) override;
+	bool isEnabled(SensorType type) override;
+	void setEnabled(SensorType type, bool enable) override;
+	std::vector<float> getData(SensorType type) override;
+	std::vector<void*> getHandles() override;
+	const char *getSensorName(SensorType type) override;
+
+	static SensorType convert(SDL_SensorType type);
+	static SDL_SensorType convert(SensorType type);
+
+private:
+	std::map<SensorType, SDL_Sensor*> sensors;
+
+}; // Sensor
+
+} // sdl
+} // sensor
+} // love
+
+#endif

+ 120 - 0
src/modules/sensor/wrap_Sensor.cpp

@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * 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.
+ **/
+
+ // LOVE
+#include "wrap_Sensor.h"
+#include "sdl/Sensor.h"
+
+namespace love
+{
+namespace sensor
+{
+
+#define instance() (Module::getInstance<Sensor>(Module::M_SENSOR))
+
+inline Sensor::SensorType luax_checksensortype(lua_State *L, int i)
+{
+	const char *sensorType = luaL_checkstring(L, i);
+	Sensor::SensorType type = Sensor::SENSOR_MAX_ENUM;
+
+	if (!Sensor::getConstant(sensorType, type))
+		luax_enumerror(L, "sensor mode", Sensor::getConstants(type), sensorType);
+
+	return type;
+}
+
+static int w_hasSensor(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+
+	lua_pushboolean(L, instance()->hasSensor(type));
+	return 1;
+}
+
+static int w_isEnabled(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+
+	lua_pushboolean(L, instance()->isEnabled(type));
+	return 1;
+}
+
+static int w_setEnabled(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+	bool enabled = luax_checkboolean(L, 2);
+
+	luax_catchexcept(L, [&](){ instance()->setEnabled(type, enabled); });
+	return 0;
+}
+
+static int w_getData(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+
+	std::vector<float> data;
+	luax_catchexcept(L, [&](){ data = instance()->getData(type); });
+
+	for (float f: data)
+		lua_pushnumber(L, f);
+
+	return (int) data.size();
+}
+
+static int w_getName(lua_State *L)
+{
+	Sensor::SensorType type = luax_checksensortype(L, 1);
+	const char *name = nullptr;
+
+	luax_catchexcept(L, [&](){ name = instance()->getSensorName(type); });
+	lua_pushstring(L, name);
+	return 1;
+}
+
+static const luaL_Reg functions[] =
+{
+	{ "hasSensor", w_hasSensor },
+	{ "isEnabled", w_isEnabled },
+	{ "setEnabled", w_setEnabled },
+	{ "getData", w_getData },
+	{ "getName", w_getName },
+	{ nullptr, nullptr }
+};
+
+extern "C" int luaopen_love_sensor(lua_State * L)
+{
+	Sensor *instance = instance();
+	if (instance == nullptr)
+		luax_catchexcept(L, [&]() { instance = new love::sensor::sdl::Sensor(); });
+	else
+		instance->retain();
+
+	WrappedModule w;
+	w.module = instance;
+	w.name = "sensor";
+	w.type = &Module::type;
+	w.functions = functions;
+	w.types = nullptr;
+
+	return luax_register_module(L, w);
+}
+
+} // sensor
+} // love

+ 37 - 0
src/modules/sensor/wrap_Sensor.h

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2006-2022 LOVE Development Team
+ *
+ * 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 LOVE_WRAP_SENSOR_H
+#define LOVE_WRAP_SENSOR_H
+
+ // LOVE
+#include "common/runtime.h"
+
+namespace love
+{
+namespace sensor
+{
+
+extern "C" LOVE_EXPORT int luaopen_love_sensor(lua_State *L);
+
+} // sensor
+} // love
+
+#endif