From 79b453653a3dea2d273ef082c510575f0d37cd10 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Wed, 4 Jun 2014 15:50:06 -0300 Subject: [PATCH] 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. --- src/common/runtime.cpp | 4 + src/modules/filesystem/wrap_Filesystem.cpp | 4 +- src/modules/filesystem/wrap_Filesystem.h | 2 +- src/modules/font/freetype/wrap_Font.cpp | 2 +- src/modules/graphics/opengl/wrap_Graphics.cpp | 2 +- src/modules/image/wrap_Image.cpp | 6 +- src/modules/joystick/JoystickModule.h | 10 ++ src/modules/joystick/sdl/Joystick.cpp | 14 +-- src/modules/joystick/sdl/JoystickModule.cpp | 102 ++++++++++++++---- src/modules/joystick/sdl/JoystickModule.h | 2 + src/modules/joystick/sdl/wrap_Joystick.cpp | 2 + .../joystick/sdl/wrap_JoystickModule.cpp | 47 ++++++++ .../joystick/sdl/wrap_JoystickModule.h | 2 + src/modules/sound/wrap_Sound.cpp | 2 +- 14 files changed, 166 insertions(+), 35 deletions(-) diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 720090176..eed6295c0 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -486,6 +486,10 @@ int luax_getfunction(lua_State *L, const char *mod, const char *fn) 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. diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index 780c28d3e..fa184006c 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -148,7 +148,7 @@ int w_newFile(lua_State *L) return 1; } -FileData *luax_getFileData(lua_State *L, int idx) +FileData *luax_getfiledata(lua_State *L, int idx) { FileData *data = nullptr; File *file = nullptr; @@ -191,7 +191,7 @@ int w_newFileData(lua_State *L) // 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"); diff --git a/src/modules/filesystem/wrap_Filesystem.h b/src/modules/filesystem/wrap_Filesystem.h index 86fdf00dd..e11a88c40 100644 --- a/src/modules/filesystem/wrap_Filesystem.h +++ b/src/modules/filesystem/wrap_Filesystem.h @@ -37,7 +37,7 @@ namespace filesystem * 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); diff --git a/src/modules/font/freetype/wrap_Font.cpp b/src/modules/font/freetype/wrap_Font.cpp index ac0996a36..310d09a1f 100644 --- a/src/modules/font/freetype/wrap_Font.cpp +++ b/src/modules/font/freetype/wrap_Font.cpp @@ -51,7 +51,7 @@ int w_newRasterizer(lua_State *L) } 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); }, diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index fa629f377..78fd08e63 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -172,7 +172,7 @@ int w_newImage(lua_State *L) 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)) { diff --git a/src/modules/image/wrap_Image.cpp b/src/modules/image/wrap_Image.cpp index bd37950cc..33ce40cce 100644 --- a/src/modules/image/wrap_Image.cpp +++ b/src/modules/image/wrap_Image.cpp @@ -52,7 +52,7 @@ int w_newImageData(lua_State *L) } // 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, @@ -66,7 +66,7 @@ int w_newImageData(lua_State *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, @@ -80,7 +80,7 @@ int w_newCompressedData(lua_State *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(); diff --git a/src/modules/joystick/JoystickModule.h b/src/modules/joystick/JoystickModule.h index d1a488c3d..6478e4c48 100644 --- a/src/modules/joystick/JoystickModule.h +++ b/src/modules/joystick/JoystickModule.h @@ -85,6 +85,16 @@ public: **/ 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 diff --git a/src/modules/joystick/sdl/Joystick.cpp b/src/modules/joystick/sdl/Joystick.cpp index a7eccae0c..3ae2a03dd 100644 --- a/src/modules/joystick/sdl/Joystick.cpp +++ b/src/modules/joystick/sdl/Joystick.cpp @@ -191,12 +191,14 @@ bool Joystick::isDown(const std::vector &buttonlist) const 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; } @@ -244,9 +246,9 @@ bool Joystick::isGamepadDown(const std::vector &blist) const 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) diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp index ab0a28b16..9c156fced 100644 --- a/src/modules/joystick/sdl/JoystickModule.cpp +++ b/src/modules/joystick/sdl/JoystickModule.cpp @@ -57,10 +57,10 @@ JoystickModule::JoystickModule() 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) @@ -101,10 +101,10 @@ int JoystickModule::getJoystickCount() const 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; @@ -119,12 +119,12 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) 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; } @@ -144,9 +144,9 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) // 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(); @@ -157,7 +157,7 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) joystick->release(); } - return *it; + return activestick; } } @@ -427,22 +427,23 @@ void JoystickModule::checkGamepads(const std::string &guid) const 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); } } } @@ -462,6 +463,67 @@ std::string JoystickModule::getDeviceGUID(int deviceindex) const 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 diff --git a/src/modules/joystick/sdl/JoystickModule.h b/src/modules/joystick/sdl/JoystickModule.h index ba7c8feb1..4369c8966 100644 --- a/src/modules/joystick/sdl/JoystickModule.h +++ b/src/modules/joystick/sdl/JoystickModule.h @@ -56,6 +56,8 @@ public: 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: diff --git a/src/modules/joystick/sdl/wrap_Joystick.cpp b/src/modules/joystick/sdl/wrap_Joystick.cpp index 90a3f4d3f..fe96a1c91 100644 --- a/src/modules/joystick/sdl/wrap_Joystick.cpp +++ b/src/modules/joystick/sdl/wrap_Joystick.cpp @@ -251,6 +251,8 @@ static const luaL_Reg functions[] = // From wrap_JoystickModule. { "getConnectedIndex", w_getIndex }, { "getGamepadMapping", w_getGamepadMapping }, + { "saveGamepadMapping", w_saveGamepadMapping }, + { 0, 0 }, }; diff --git a/src/modules/joystick/sdl/wrap_JoystickModule.cpp b/src/modules/joystick/sdl/wrap_JoystickModule.cpp index 305a5a9d9..b40c30905 100644 --- a/src/modules/joystick/sdl/wrap_JoystickModule.cpp +++ b/src/modules/joystick/sdl/wrap_JoystickModule.cpp @@ -21,6 +21,8 @@ #include "wrap_JoystickModule.h" #include "wrap_Joystick.h" +#include "filesystem/wrap_Filesystem.h" + namespace love { namespace joystick @@ -176,6 +178,49 @@ int w_getGamepadMapping(lua_State *L) 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[] = { @@ -183,6 +228,8 @@ static const luaL_Reg functions[] = { "getJoystickCount", w_getJoystickCount }, { "setGamepadMapping", w_setGamepadMapping }, { "getGamepadMapping", w_getGamepadMapping }, + { "loadGamepadMappings", w_loadGamepadMappings }, + { "saveGamepadMapping", w_saveGamepadMapping }, { 0, 0 } }; diff --git a/src/modules/joystick/sdl/wrap_JoystickModule.h b/src/modules/joystick/sdl/wrap_JoystickModule.h index 24de3264c..fc1adca03 100644 --- a/src/modules/joystick/sdl/wrap_JoystickModule.h +++ b/src/modules/joystick/sdl/wrap_JoystickModule.h @@ -38,6 +38,8 @@ int w_getIndex(lua_State *L); 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 diff --git a/src/modules/sound/wrap_Sound.cpp b/src/modules/sound/wrap_Sound.cpp index eaedb9aa0..563e1ae94 100644 --- a/src/modules/sound/wrap_Sound.cpp +++ b/src/modules/sound/wrap_Sound.cpp @@ -64,7 +64,7 @@ int w_newSoundData(lua_State *L) 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;