mirror of
https://github.com/love2d/love.git
synced 2026-08-15 15:51:12 +02:00
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.
This commit is contained in:
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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); },
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -191,12 +191,14 @@ bool Joystick::isDown(const std::vector<int> &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<GamepadButton> &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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -251,6 +251,8 @@ static const luaL_Reg functions[] =
|
||||
// From wrap_JoystickModule.
|
||||
{ "getConnectedIndex", w_getIndex },
|
||||
{ "getGamepadMapping", w_getGamepadMapping },
|
||||
{ "saveGamepadMapping", w_saveGamepadMapping },
|
||||
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user