diff --git a/src/modules/joystick/JoystickModule.h b/src/modules/joystick/JoystickModule.h index aca2819f3..b7df698ff 100644 --- a/src/modules/joystick/JoystickModule.h +++ b/src/modules/joystick/JoystickModule.h @@ -88,15 +88,16 @@ public: /** * Loads a newline-separated list of virtual Gamepad mapping strings for * multiple joysticks at a time. The mapping strings must have been - * generated with saveGamepadMapping, via Steam, or some other tool which + * generated with saveGamepadMappings, via Steam, or some other tool which * generates SDL GameController mappings. **/ virtual void loadGamepadMappings(const std::string &mappings) = 0; /** - * Gets the virtual Gamepad mapping string for the specified joystick GUID. + * Gets a newline-separated list of virtual Gamepad mapping strings for + * all used or modified Joysticks which are identified as Gamepads. **/ - virtual std::string saveGamepadMapping(const std::string &pguid) = 0; + virtual std::string saveGamepadMappings() = 0; }; // JoystickModule diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp index a9534c2ae..4ec6deb00 100644 --- a/src/modules/joystick/sdl/JoystickModule.cpp +++ b/src/modules/joystick/sdl/JoystickModule.cpp @@ -161,6 +161,9 @@ love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) } } + if (joystick->isGamepad()) + recentGamepadGUIDs[joystick->getGUID()] = true; + activeSticks.push_back(joystick); return joystick; } @@ -257,6 +260,9 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa // 1 == added, 0 == updated, -1 == error. int status = SDL_GameControllerAddMapping(mapstr.c_str()); + if (status != -1) + recentGamepadGUIDs[guid] = true; + // FIXME: massive hack until missing APIs are added to SDL 2: // https://bugzilla.libsdl.org/show_bug.cgi?id=1975 if (status == 1) @@ -498,10 +504,11 @@ void JoystickModule::loadGamepadMappings(const std::string &mappings) if (SDL_GameControllerAddMapping(mapping.c_str()) != -1) { success = true; + std::string guid = mapping.substr(0, mapping.find_first_of(',')); + recentGamepadGUIDs[guid] = true; // FIXME: massive hack until missing APIs are added to SDL 2: // https://bugzilla.libsdl.org/show_bug.cgi?id=1975 - std::string guid = mapping.substr(0, mapping.find_first_of(',')); checkGamepads(guid); } } @@ -510,26 +517,30 @@ void JoystickModule::loadGamepadMappings(const std::string &mappings) throw love::Exception("Invalid gamepad mappings."); } -std::string JoystickModule::saveGamepadMapping(const std::string &pguid) +std::string JoystickModule::saveGamepadMappings() { - SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(pguid.c_str()); + std::string mappings; - std::string mapping; - char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); + for (const auto &g : recentGamepadGUIDs) + { + SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(g.first.c_str()); - if (sdlmapping == nullptr) - throw love::Exception("The specified Joystick GUID string has no gamepad mapping."); + char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); + if (sdlmapping == nullptr) + continue; - mapping = sdlmapping; - SDL_free(sdlmapping); + std::string mapping = sdlmapping; + SDL_free(sdlmapping); - if (mapping.find_last_of(',') != mapping.size() - 1) - mapping += ","; + if (mapping.find_last_of(',') != mapping.length() - 1) + mapping += ","; - // Matches SDL_GameControllerAddMappingsFromRW. - mapping += "platform:" + std::string(SDL_GetPlatform()) + ","; + // Matches SDL_GameControllerAddMappingsFromRW. + mapping += "platform:" + std::string(SDL_GetPlatform()) + ",\n"; + mappings += mapping; + } - return mapping; + return mappings; } } // sdl diff --git a/src/modules/joystick/sdl/JoystickModule.h b/src/modules/joystick/sdl/JoystickModule.h index 4369c8966..047fedf60 100644 --- a/src/modules/joystick/sdl/JoystickModule.h +++ b/src/modules/joystick/sdl/JoystickModule.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace love { @@ -57,7 +58,7 @@ 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); + std::string saveGamepadMappings(); private: @@ -76,6 +77,10 @@ private: // Persistent list of all Joysticks which have been connected at some point. std::list joysticks; + // Persistent map indicating GUIDs for Gamepads which have been connected or + // modified at some point. + std::map recentGamepadGUIDs; + }; // JoystickModule } // sdl diff --git a/src/modules/joystick/wrap_Joystick.cpp b/src/modules/joystick/wrap_Joystick.cpp index b78819dee..02e6484f5 100644 --- a/src/modules/joystick/wrap_Joystick.cpp +++ b/src/modules/joystick/wrap_Joystick.cpp @@ -119,7 +119,7 @@ int w_Joystick_getHat(lua_State *L) Joystick::Hat h = j->getHat(hatindex); const char *direction = ""; - love::joystick::Joystick::getConstant(h, direction); + Joystick::getConstant(h, direction); lua_pushstring(L, direction); return 1; @@ -249,7 +249,6 @@ 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/wrap_JoystickModule.cpp b/src/modules/joystick/wrap_JoystickModule.cpp index 8f052d2cd..2c0d51298 100644 --- a/src/modules/joystick/wrap_JoystickModule.cpp +++ b/src/modules/joystick/wrap_JoystickModule.cpp @@ -39,7 +39,7 @@ int w_getJoysticks(lua_State *L) for (int i = 0; i < stickcount; i++) { - love::joystick::Joystick *stick = instance->getJoystick(i); + Joystick *stick = instance->getJoystick(i); stick->retain(); luax_pushtype(L, "Joystick", JOYSTICK_JOYSTICK_T, stick); lua_rawseti(L, -2, i + 1); @@ -50,7 +50,7 @@ int w_getJoysticks(lua_State *L) int w_getIndex(lua_State *L) { - love::joystick::Joystick *j = luax_checkjoystick(L, 1); + Joystick *j = luax_checkjoystick(L, 1); int index = instance->getIndex(j); if (index >= 0) lua_pushinteger(L, index + 1); @@ -75,9 +75,9 @@ int w_setGamepadMapping(lua_State *L) const char *gpbindstr = luaL_checkstring(L, 2); Joystick::GamepadInput gpinput; - if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.axis)) + if (Joystick::getConstant(gpbindstr, gpinput.axis)) gpinput.type = Joystick::INPUT_TYPE_AXIS; - else if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.button)) + else if (Joystick::getConstant(gpbindstr, gpinput.button)) gpinput.type = Joystick::INPUT_TYPE_BUTTON; else return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr); @@ -85,7 +85,7 @@ int w_setGamepadMapping(lua_State *L) const char *jinputtypestr = luaL_checkstring(L, 3); Joystick::JoystickInput jinput; - if (!love::joystick::Joystick::getConstant(jinputtypestr, jinput.type)) + if (!Joystick::getConstant(jinputtypestr, jinput.type)) return luaL_error(L, "Invalid joystick input type: %s", jinputtypestr); const char *hatstr; @@ -101,7 +101,7 @@ int w_setGamepadMapping(lua_State *L) // Hats need both a hat index and a hat value. jinput.hat.index = luaL_checkint(L, 4) - 1; hatstr = luaL_checkstring(L, 5); - if (!love::joystick::Joystick::getConstant(hatstr, jinput.hat.value)) + if (!Joystick::getConstant(hatstr, jinput.hat.value)) return luaL_error(L, "Invalid joystick hat: %s", hatstr); break; default: @@ -125,16 +125,16 @@ int w_getGamepadMapping(lua_State *L) guid = luax_checkstring(L, 1); else { - love::joystick::Joystick *stick = luax_checkjoystick(L, 1); + Joystick *stick = luax_checkjoystick(L, 1); guid = stick->getGUID(); } const char *gpbindstr = luaL_checkstring(L, 2); Joystick::GamepadInput gpinput; - if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.axis)) + if (Joystick::getConstant(gpbindstr, gpinput.axis)) gpinput.type = Joystick::INPUT_TYPE_AXIS; - else if (love::joystick::Joystick::getConstant(gpbindstr, gpinput.button)) + else if (Joystick::getConstant(gpbindstr, gpinput.button)) gpinput.type = Joystick::INPUT_TYPE_BUTTON; else return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr); @@ -148,7 +148,7 @@ int w_getGamepadMapping(lua_State *L) return 0; const char *inputtypestr; - if (!love::joystick::Joystick::getConstant(jinput.type, inputtypestr)) + if (!Joystick::getConstant(jinput.type, inputtypestr)) return luaL_error(L, "Unknown joystick input type."); lua_pushstring(L, inputtypestr); @@ -164,7 +164,7 @@ int w_getGamepadMapping(lua_State *L) return 2; case Joystick::INPUT_TYPE_HAT: lua_pushinteger(L, jinput.hat.index + 1); - if (love::joystick::Joystick::getConstant(jinput.hat.value, hatstr)) + if (Joystick::getConstant(jinput.hat.value, hatstr)) { lua_pushstring(L, hatstr); return 3; @@ -201,23 +201,22 @@ int w_loadGamepadMappings(lua_State *L) return 0; } -int w_saveGamepadMapping(lua_State *L) +int w_saveGamepadMappings(lua_State *L) { - std::string guid, mapping; + lua_settop(L, 1); + std::string mappings = instance->saveGamepadMappings(); - // 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 + // Optionally write the mappings string to a file. + if (!lua_isnoneornil(L, 1)) { - love::joystick::Joystick *stick = luax_checkjoystick(L, 1); - guid = stick->getGUID(); + luax_pushstring(L, mappings); + int idxs[] = {1, 2}; + luax_convobj(L, idxs, 2, "filesystem", "write"); + lua_pop(L, 1); // Pop the return value. } - luax_catchexcept(L, [&](){ mapping = instance->saveGamepadMapping(guid); }); - - luax_pushstring(L, mapping); + // Return the actual string even if we also wrote it to a file. + luax_pushstring(L, mappings); return 1; } @@ -229,7 +228,7 @@ static const luaL_Reg functions[] = { "setGamepadMapping", w_setGamepadMapping }, { "getGamepadMapping", w_getGamepadMapping }, { "loadGamepadMappings", w_loadGamepadMappings }, - { "saveGamepadMapping", w_saveGamepadMapping }, + { "saveGamepadMappings", w_saveGamepadMappings }, { 0, 0 } }; diff --git a/src/modules/joystick/wrap_JoystickModule.h b/src/modules/joystick/wrap_JoystickModule.h index f8b1a1141..5d9223e79 100644 --- a/src/modules/joystick/wrap_JoystickModule.h +++ b/src/modules/joystick/wrap_JoystickModule.h @@ -37,7 +37,7 @@ 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); +int w_saveGamepadMappings(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_joystick(lua_State *L); } // joystick