Commits

Alex Szpakowski committed b7a6774

Added love.joystick.loadGamepadMappings (resolves issue #842), and Joystick:saveGamepadMapping.

The latter returns a mapping string, and the former loads a newline-separated list of mapping strings.

  • Participants
  • Parent commits 6e47058

Comments (0)

Files changed (14)

src/common/runtime.cpp

 
 int luax_convobj(lua_State *L, int idx, const char *mod, const char *fn)
 {
+	// Convert to absolute index if necessary.
+	if (idx < 0 && idx > LUA_REGISTRYINDEX)
+		idx += lua_gettop(L) + 1;
+
 	// Convert string to a file.
 	luax_getfunction(L, mod, fn);
 	lua_pushvalue(L, idx); // The initial argument.

src/modules/filesystem/wrap_Filesystem.cpp

 	return 1;
 }
 
-FileData *luax_getFileData(lua_State *L, int idx)
+FileData *luax_getfiledata(lua_State *L, int idx)
 {
 	FileData *data = nullptr;
 	File *file = nullptr;
 	// Single argument: treat as filepath or File.
 	if (lua_gettop(L) == 1)
 	{
-		// We don't use luax_getFileData because we want to use an ioError.
+		// We don't use luax_getfiledata because we want to use an ioError.
 		if (lua_isstring(L, 1))
 			luax_convobj(L, 1, "filesystem", "newFile");
 

src/modules/filesystem/wrap_Filesystem.h

  * so a matching release() is required!
  * May trigger a Lua error.
  **/
-FileData *luax_getFileData(lua_State *L, int idx);
+FileData *luax_getfiledata(lua_State *L, int idx);
 
 bool hack_setupWriteDirectory();
 int w_init(lua_State *L);

src/modules/font/freetype/wrap_Font.cpp

 	}
 	else if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
 	{
-		love::filesystem::FileData *d = love::filesystem::luax_getFileData(L, 1);
+		love::filesystem::FileData *d = love::filesystem::luax_getfiledata(L, 1);
 		int size = luaL_checkint(L, 2);
 		luax_catchexcept(L,
 			[&]() { t = instance->newRasterizer(d, size); },

src/modules/graphics/opengl/wrap_Graphics.cpp

 		if (image == nullptr)
 			return luaL_error(L, "Cannot load images without the love.image module.");
 
-		love::filesystem::FileData *fdata = love::filesystem::luax_getFileData(L, 1);
+		love::filesystem::FileData *fdata = love::filesystem::luax_getfiledata(L, 1);
 
 		if (image->isCompressed(fdata))
 		{

src/modules/image/wrap_Image.cpp

 	}
 
 	// Case 2: File(Data).
-	love::filesystem::FileData *data = love::filesystem::luax_getFileData(L, 1);
+	love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
 
 	ImageData *t = nullptr;
 	luax_catchexcept(L,
 
 int w_newCompressedData(lua_State *L)
 {
-	love::filesystem::FileData *data = love::filesystem::luax_getFileData(L, 1);
+	love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
 
 	CompressedData *t = nullptr;
 	luax_catchexcept(L,
 
 int w_isCompressed(lua_State *L)
 {
-	love::filesystem::FileData *data = love::filesystem::luax_getFileData(L, 1);
+	love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
 	bool compressed = instance->isCompressed(data);
 	data->release();
 

src/modules/joystick/JoystickModule.h

 	 **/
 	virtual Joystick::JoystickInput getGamepadMapping(const std::string &pguid, Joystick::GamepadInput gpinput) = 0;
 
+	/**
+	 *
+	 **/
+	virtual void loadGamepadMappings(const std::string &mappings) = 0;
+
+	/**
+	 *
+	 **/
+	virtual std::string saveGamepadMapping(const std::string &pguid) = 0;
+
 }; // JoystickModule
 
 } // joystick

src/modules/joystick/sdl/Joystick.cpp

 	if (!isConnected())
 		return false;
 
-	int num = getButtonCount();
+	int numbuttons = getButtonCount();
 
-	for (size_t i = 0; i < buttonlist.size(); i++)
+	for (int button : buttonlist)
 	{
-		int button = buttonlist[i];
-		if (button >= 0 && button < num && SDL_JoystickGetButton(joyhandle, button) == 1)
+		if (button < 0 || button >= numbuttons)
+			continue;
+
+		if (SDL_JoystickGetButton(joyhandle, button) == 1)
 			return true;
 	}
 
 
 	SDL_GameControllerButton sdlbutton;
 
-	for (size_t i = 0; i < blist.size(); i++)
+	for (GamepadButton button : blist)
 	{
-		if (!getConstant(blist[i], sdlbutton))
+		if (!getConstant(button, sdlbutton))
 			continue;
 
 		if (SDL_GameControllerGetButton(controller, sdlbutton) == 1)

src/modules/joystick/sdl/JoystickModule.cpp

 JoystickModule::~JoystickModule()
 {
 	// Close any open Joysticks.
-	for (auto it = joysticks.begin(); it != joysticks.end(); ++it)
+	for (auto stick : joysticks)
 	{
-		(*it)->close();
-		(*it)->release();
+		stick->close();
+		stick->release();
 	}
 
 	if (SDL_WasInit(SDL_INIT_HAPTIC) != 0)
 
 love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid)
 {
-	for (size_t i = 0; i < activeSticks.size(); i++)
+	for (auto stick : activeSticks)
 	{
-		if (instanceid == activeSticks[i]->getInstanceID())
-			return activeSticks[i];
+		if (stick->getInstanceID() == instanceid)
+			return stick;
 	}
 
 	return nullptr;
 	joystick::Joystick *joystick = 0;
 	bool reused = false;
 
-	for (auto it = joysticks.begin(); it != joysticks.end(); ++it)
+	for (auto stick : joysticks)
 	{
 		// Try to re-use a disconnected Joystick with the same GUID.
-		if (!(*it)->isConnected() && (*it)->getGUID() == guidstr)
+		if (!stick->isConnected() && stick->getGUID() == guidstr)
 		{
-			joystick = *it;
+			joystick = stick;
 			reused = true;
 			break;
 		}
 
 	// Make sure multiple instances of the same physical joystick aren't added
 	// to the active list.
-	for (auto it = activeSticks.begin(); it != activeSticks.end(); ++it)
+	for (auto activestick : activeSticks)
 	{
-		if (joystick->getHandle() == (*it)->getHandle())
+		if (joystick->getHandle() == activestick->getHandle())
 		{
 			joystick->close();
 
 				joystick->release();
 			}
 
-			return *it;
+			return activestick;
 		}
 	}
 
 		if (guid.compare(getDeviceGUID(d_index)) != 0)
 			continue;
 
-		for (auto it = activeSticks.begin(); it != activeSticks.end(); ++it)
+		for (auto stick : activeSticks)
 		{
-			if ((*it)->isGamepad() || guid.compare((*it)->getGUID()) != 0)
+			if (stick->isGamepad() || guid.compare(stick->getGUID()) != 0)
 				continue;
 
 			// 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 == nullptr)
+			SDL_GameController *controller = SDL_GameControllerOpen(d_index);
+			if (controller == nullptr)
 				continue;
 
-			SDL_Joystick *stick = SDL_GameControllerGetJoystick(ctrl);
-			if (stick == (SDL_Joystick *) (*it)->getHandle())
-				(*it)->openGamepad(d_index);
+			SDL_Joystick *sdlstick = SDL_GameControllerGetJoystick(controller);
+			if (sdlstick == (SDL_Joystick *) stick->getHandle())
+				stick->openGamepad(d_index);
 
-			SDL_GameControllerClose(ctrl);
+			// GameController objects are reference-counted in SDL.
+			SDL_GameControllerClose(controller);
 		}
 	}
 }
 	return std::string(guidstr);
 }
 
+void JoystickModule::loadGamepadMappings(const std::string &mappings)
+{
+	// TODO: We should use SDL_GameControllerAddMappingsFromRW. We're
+	// duplicating its functionality for now because it was added after
+	// SDL 2.0.0's release, and we want runtime compat with 2.0.0 on Linux...
+
+	std::stringstream ss(mappings);
+	std::string mapping;
+	bool success = false;
+
+	// The mappings string contains newline-separated mappings.
+	while (std::getline(ss, mapping))
+	{
+		if (mapping.empty())
+			continue;
+
+		// Strip out and compare any "platform:XYZ," in the mapping.
+		size_t pstartpos = mapping.find("platform:");
+		if (pstartpos != std::string::npos)
+		{
+			pstartpos += strlen("platform:");
+
+			size_t pendpos = mapping.find_first_of(',', pstartpos);
+			std::string platform = mapping.substr(pstartpos, pendpos - pstartpos);
+
+			if (platform.compare(SDL_GetPlatform()) != 0)
+				continue;
+
+			pstartpos -= strlen("platform:");
+			mapping.erase(pstartpos, pendpos - pstartpos + 1);
+		}
+
+		success = success || (SDL_GameControllerAddMapping(mapping.c_str()) != -1);
+	}
+
+	if (!success)
+		throw love::Exception("Invalid gamepad mappings.");
+}
+
+std::string JoystickModule::saveGamepadMapping(const std::string &pguid)
+{
+	SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(pguid.c_str());
+
+	std::string mapping;
+	char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid);
+
+	if (sdlmapping == nullptr)
+		throw love::Exception("The specified Joystick GUID string has no gamepad mapping.");
+
+	mapping = sdlmapping;
+	SDL_free(sdlmapping);
+
+	if (mapping.find_last_of(',') != mapping.size() - 1)
+		mapping += ",";
+
+	// Matches SDL_GameControllerAddMappingsFromRW.
+	mapping += "platform:" + std::string(SDL_GetPlatform()) + ",";
+
+	return mapping;
+}
+
 } // sdl
 } // joystick
 } // love

src/modules/joystick/sdl/JoystickModule.h

 
 	bool setGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput);
 	Joystick::JoystickInput getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput);
+	void loadGamepadMappings(const std::string &mappings);
+	std::string saveGamepadMapping(const std::string &pguid);
 
 private:
 

src/modules/joystick/sdl/wrap_Joystick.cpp

 	// From wrap_JoystickModule.
 	{ "getConnectedIndex", w_getIndex },
 	{ "getGamepadMapping", w_getGamepadMapping },
+	{ "saveGamepadMapping", w_saveGamepadMapping },
+
 	{ 0, 0 },
 };
 

src/modules/joystick/sdl/wrap_JoystickModule.cpp

 #include "wrap_JoystickModule.h"
 #include "wrap_Joystick.h"
 
+#include "filesystem/wrap_Filesystem.h"
+
 namespace love
 {
 namespace joystick
 	return 1;
 }
 
+int w_loadGamepadMappings(lua_State *L)
+{
+	lua_pushvalue(L, 1);
+	luax_convobj(L, -1, "filesystem", "isFile");
+	bool isfile = luax_toboolean(L, -1);
+	lua_pop(L, 1);
+
+	std::string mappings;
+
+	if (isfile)
+	{
+		love::filesystem::FileData *fd = love::filesystem::luax_getfiledata(L, 1);
+		mappings = std::string((const char *) fd->getData(), fd->getSize());
+		fd->release();
+
+	}
+	else
+		mappings = luax_checkstring(L, 1);
+
+	luax_catchexcept(L, [&](){ instance->loadGamepadMappings(mappings); });
+	return 0;
+}
+
+int w_saveGamepadMapping(lua_State *L)
+{
+	std::string guid, mapping;
+
+	// Accept either a GUID string or a Joystick object. This way we can re-use
+	// the function for Joystick:getGamepadMapping.
+	if (lua_type(L, 1) == LUA_TSTRING)
+		guid = luax_checkstring(L, 1);
+	else
+	{
+		love::joystick::Joystick *stick = luax_checkjoystick(L, 1);
+		guid = stick->getGUID();
+	}
+
+	luax_catchexcept(L, [&](){ mapping = instance->saveGamepadMapping(guid); });
+
+	luax_pushstring(L, mapping);
+	return 1;
+}
+
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 {
 	{ "getJoystickCount", w_getJoystickCount },
 	{ "setGamepadMapping", w_setGamepadMapping },
 	{ "getGamepadMapping", w_getGamepadMapping },
+	{ "loadGamepadMappings", w_loadGamepadMappings },
+	{ "saveGamepadMapping", w_saveGamepadMapping },
 	{ 0, 0 }
 };
 

src/modules/joystick/sdl/wrap_JoystickModule.h

 int w_getJoystickCount(lua_State *L);
 int w_setGamepadMapping(lua_State *L);
 int w_getGamepadMapping(lua_State *L);
+int w_loadGamepadMappings(lua_State *L);
+int w_saveGamepadMapping(lua_State *L);
 extern "C" LOVE_EXPORT int luaopen_love_joystick(lua_State *L);
 
 } // sdl

src/modules/sound/wrap_Sound.cpp

 
 int w_newDecoder(lua_State *L)
 {
-	love::filesystem::FileData *data = love::filesystem::luax_getFileData(L, 1);
+	love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
 	int bufferSize = luaL_optint(L, 2, Decoder::DEFAULT_BUFFER_SIZE);
 
 	Decoder *t = nullptr;