Work around SDL issues in Joystick:getGamepadMapping with XInput controllers (issue #1239).

This doesn’t fix the underlying issues in SDL so there may be other aspects of love.joystick which are still affected.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2016-12-10 16:14:14 -04:00
parent 8e2d9c5138
commit b620f32200
9 changed files with 134 additions and 190 deletions
+2
View File
@@ -152,6 +152,8 @@ public:
virtual float getGamepadAxis(GamepadAxis axis) const = 0;
virtual bool isGamepadDown(const std::vector<GamepadButton> &blist) const = 0;
virtual JoystickInput getGamepadMapping(const GamepadInput &input) const = 0;
virtual void *getHandle() const = 0;
virtual std::string getGUID() const = 0;
-6
View File
@@ -82,12 +82,6 @@ public:
**/
virtual bool setGamepadMapping(const std::string &pguid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput) = 0;
/**
* Gets the joystick input value the gamepad input value is bound to for the
* specified joystick product GUID.
**/
virtual Joystick::JoystickInput getGamepadMapping(const std::string &pguid, Joystick::GamepadInput gpinput) = 0;
/**
* Loads a newline-separated list of virtual Gamepad mapping strings for
* multiple joysticks at a time. The mapping strings must have been
+53
View File
@@ -248,6 +248,59 @@ bool Joystick::isGamepadDown(const std::vector<GamepadButton> &blist) const
return false;
}
Joystick::JoystickInput Joystick::getGamepadMapping(const GamepadInput &input) const
{
Joystick::JoystickInput jinput;
jinput.type = INPUT_TYPE_MAX_ENUM;
if (!isGamepad())
return jinput;
SDL_GameControllerButtonBind sdlbind = {};
sdlbind.bindType = SDL_CONTROLLER_BINDTYPE_NONE;
SDL_GameControllerButton sdlbutton;
SDL_GameControllerAxis sdlaxis;
switch (input.type)
{
case INPUT_TYPE_BUTTON:
if (getConstant(input.button, sdlbutton))
sdlbind = SDL_GameControllerGetBindForButton(controller, sdlbutton);
break;
case INPUT_TYPE_AXIS:
if (getConstant(input.axis, sdlaxis))
sdlbind = SDL_GameControllerGetBindForAxis(controller, sdlaxis);
break;
default:
break;
}
switch (sdlbind.bindType)
{
case SDL_CONTROLLER_BINDTYPE_BUTTON:
jinput.type = INPUT_TYPE_BUTTON;
jinput.button = sdlbind.value.button;
break;
case SDL_CONTROLLER_BINDTYPE_AXIS:
jinput.type = INPUT_TYPE_AXIS;
jinput.axis = sdlbind.value.axis;
break;
case SDL_CONTROLLER_BINDTYPE_HAT:
if (getConstant(sdlbind.value.hat.hat_mask, jinput.hat.value))
{
jinput.type = INPUT_TYPE_HAT;
jinput.hat.index = sdlbind.value.hat.hat;
}
break;
case SDL_CONTROLLER_BINDTYPE_NONE:
default:
break;
}
return jinput;
}
void *Joystick::getHandle() const
{
return joyhandle;
+25 -23
View File
@@ -44,39 +44,41 @@ public:
virtual ~Joystick();
bool open(int deviceindex);
void close();
bool open(int deviceindex) override;
void close() override;
bool isConnected() const;
bool isConnected() const override;
const char *getName() const;
const char *getName() const override;
int getAxisCount() const;
int getButtonCount() const;
int getHatCount() const;
int getAxisCount() const override;
int getButtonCount() const override;
int getHatCount() const override;
float getAxis(int axisindex) const;
std::vector<float> getAxes() const;
Hat getHat(int hatindex) const;
float getAxis(int axisindex) const override;
std::vector<float> getAxes() const override;
Hat getHat(int hatindex) const override;
bool isDown(const std::vector<int> &buttonlist) const;
bool isDown(const std::vector<int> &buttonlist) const override;
bool openGamepad(int deviceindex);
bool isGamepad() const;
bool openGamepad(int deviceindex) override;
bool isGamepad() const override;
float getGamepadAxis(GamepadAxis axis) const;
bool isGamepadDown(const std::vector<GamepadButton> &blist) const;
float getGamepadAxis(GamepadAxis axis) const override;
bool isGamepadDown(const std::vector<GamepadButton> &blist) const override;
void *getHandle() const;
JoystickInput getGamepadMapping(const GamepadInput &input) const override;
std::string getGUID() const;
int getInstanceID() const;
int getID() const;
void *getHandle() const override;
bool isVibrationSupported();
bool setVibration(float left, float right, float duration = -1.0f);
bool setVibration();
void getVibration(float &left, float &right);
std::string getGUID() const override;
int getInstanceID() const override;
int getID() const override;
bool isVibrationSupported() override;
bool setVibration(float left, float right, float duration = -1.0f) override;
bool setVibration() override;
void getVibration(float &left, float &right) override;
static bool getConstant(Hat in, Uint8 &out);
static bool getConstant(Uint8 in, Hat &out);
@@ -271,54 +271,6 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa
return status >= 0;
}
Joystick::JoystickInput JoystickModule::getGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput)
{
// All SDL joystick GUID strings are 32 characters.
if (guid.length() != 32)
throw love::Exception("Invalid joystick GUID: %s", guid.c_str());
Joystick::JoystickInput jinput;
jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str());
std::string mapstr;
char *sdlmapstr = SDL_GameControllerMappingForGUID(sdlguid);
if (!sdlmapstr)
return jinput;
mapstr = sdlmapstr;
SDL_free(sdlmapstr);
std::string gpbindname = stringFromGamepadInput(gpinput);
size_t findpos = mapstr.find(std::string(",") + gpbindname + ":");
if (findpos == std::string::npos)
return jinput;
size_t endpos = mapstr.find_first_of(',', findpos + 1);
if (endpos == std::string::npos)
{
// Assume end-of-string if we can't find the next comma.
endpos = mapstr.length() - 1;
}
if (endpos >= mapstr.length())
return jinput; // Something went wrong.
// Strip out the trailing comma from our search position, if it exists.
if (mapstr[endpos] == ',')
endpos--;
// New start position: comma + gamepadinputlength + ":".
findpos += 1 + gpbindname.length() + 1;
std::string jbindstr = mapstr.substr(findpos, endpos - findpos + 1);
jinput = JoystickInputFromString(jbindstr);
return jinput;
}
std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinput) const
{
SDL_GameControllerAxis sdlaxis;
@@ -346,51 +298,6 @@ std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinpu
return std::string(gpinputname);
}
Joystick::JoystickInput JoystickModule::JoystickInputFromString(const std::string &str) const
{
Joystick::JoystickInput jinput;
jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
// Return an invalid value rather than throwing an exception.
if (str.length() < 2)
return jinput;
// The input type will always be the first character in the string.
char inputtype = str[0];
std::string bindvalues = str.substr(1);
Uint8 sdlhat;
switch (inputtype)
{
case 'a':
jinput.type = Joystick::INPUT_TYPE_AXIS;
jinput.axis = (int) strtol(bindvalues.c_str(), nullptr, 10);
break;
case 'b':
jinput.type = Joystick::INPUT_TYPE_BUTTON;
jinput.button = (int) strtol(bindvalues.c_str(), nullptr, 10);
break;
case 'h':
// Hat string syntax is "index.value".
if (bindvalues.length() < 3)
break;
jinput.type = Joystick::INPUT_TYPE_HAT;
jinput.hat.index = (int) strtol(bindvalues.substr(0, 1).c_str(), nullptr, 10);
sdlhat = (Uint8) strtol(bindvalues.substr(2).c_str(), nullptr, 10);
if (!Joystick::getConstant(sdlhat, jinput.hat.value))
{
// Return an invalid value if we can't find the hat constant.
jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
return jinput;
}
break;
default:
break;
}
return jinput;
}
void JoystickModule::removeBindFromMapString(std::string &mapstr, const std::string &joybindstr) const
{
// Find the joystick part of the bind in the string.
@@ -56,14 +56,12 @@ public:
int getJoystickCount() const;
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 saveGamepadMappings();
private:
std::string stringFromGamepadInput(Joystick::GamepadInput gpinput) const;
Joystick::JoystickInput JoystickInputFromString(const std::string &str) const;
void removeBindFromMapString(std::string &mapstr, const std::string &joybindstr) const;
void checkGamepads(const std::string &guid) const;
+54 -1
View File
@@ -225,6 +225,59 @@ int w_Joystick_isGamepadDown(lua_State *L)
return 1;
}
int w_Joystick_getGamepadMapping(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
const char *gpbindstr = luaL_checkstring(L, 2);
Joystick::GamepadInput gpinput;
if (Joystick::getConstant(gpbindstr, gpinput.axis))
gpinput.type = Joystick::INPUT_TYPE_AXIS;
else if (Joystick::getConstant(gpbindstr, gpinput.button))
gpinput.type = Joystick::INPUT_TYPE_BUTTON;
else
return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
Joystick::JoystickInput jinput;
jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
luax_catchexcept(L, [&](){ jinput = j->getGamepadMapping(gpinput); });
if (jinput.type == Joystick::INPUT_TYPE_MAX_ENUM)
return 0;
const char *inputtypestr;
if (!Joystick::getConstant(jinput.type, inputtypestr))
return luaL_error(L, "Unknown joystick input type.");
lua_pushstring(L, inputtypestr);
const char *hatstr;
switch (jinput.type)
{
case Joystick::INPUT_TYPE_AXIS:
lua_pushinteger(L, jinput.axis + 1);
return 2;
case Joystick::INPUT_TYPE_BUTTON:
lua_pushinteger(L, jinput.button + 1);
return 2;
case Joystick::INPUT_TYPE_HAT:
lua_pushinteger(L, jinput.hat.index + 1);
if (Joystick::getConstant(jinput.hat.value, hatstr))
{
lua_pushstring(L, hatstr);
return 3;
}
else
return luaL_error(L, "Unknown joystick hat.");
default:
return luaL_error(L, "Unknown joystick input type.");
}
return 1;
}
int w_Joystick_isVibrationSupported(lua_State *L)
{
Joystick *j = luax_checkjoystick(L, 1);
@@ -282,6 +335,7 @@ static const luaL_Reg w_Joystick_functions[] =
{ "isGamepad", w_Joystick_isGamepad },
{ "getGamepadAxis", w_Joystick_getGamepadAxis },
{ "isGamepadDown", w_Joystick_isGamepadDown },
{ "getGamepadMapping", w_Joystick_getGamepadMapping },
{ "isVibrationSupported", w_Joystick_isVibrationSupported },
{ "setVibration", w_Joystick_setVibration },
@@ -289,7 +343,6 @@ static const luaL_Reg w_Joystick_functions[] =
// From wrap_JoystickModule.
{ "getConnectedIndex", w_getIndex },
{ "getGamepadMapping", w_getGamepadMapping },
{ 0, 0 },
};
@@ -114,69 +114,6 @@ int w_setGamepadMapping(lua_State *L)
return 1;
}
int w_getGamepadMapping(lua_State *L)
{
std::string guid;
// 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
{
Joystick *stick = luax_checkjoystick(L, 1);
guid = stick->getGUID();
}
const char *gpbindstr = luaL_checkstring(L, 2);
Joystick::GamepadInput gpinput;
if (Joystick::getConstant(gpbindstr, gpinput.axis))
gpinput.type = Joystick::INPUT_TYPE_AXIS;
else if (Joystick::getConstant(gpbindstr, gpinput.button))
gpinput.type = Joystick::INPUT_TYPE_BUTTON;
else
return luaL_error(L, "Invalid gamepad axis/button: %s", gpbindstr);
Joystick::JoystickInput jinput;
jinput.type = Joystick::INPUT_TYPE_MAX_ENUM;
luax_catchexcept(L, [&](){ jinput = instance()->getGamepadMapping(guid, gpinput); });
if (jinput.type == Joystick::INPUT_TYPE_MAX_ENUM)
return 0;
const char *inputtypestr;
if (!Joystick::getConstant(jinput.type, inputtypestr))
return luaL_error(L, "Unknown joystick input type.");
lua_pushstring(L, inputtypestr);
const char *hatstr;
switch (jinput.type)
{
case Joystick::INPUT_TYPE_AXIS:
lua_pushinteger(L, jinput.axis + 1);
return 2;
case Joystick::INPUT_TYPE_BUTTON:
lua_pushinteger(L, jinput.button + 1);
return 2;
case Joystick::INPUT_TYPE_HAT:
lua_pushinteger(L, jinput.hat.index + 1);
if (Joystick::getConstant(jinput.hat.value, hatstr))
{
lua_pushstring(L, hatstr);
return 3;
}
else
return luaL_error(L, "Unknown joystick hat.");
default:
break; // ?
}
return 1;
}
int w_loadGamepadMappings(lua_State *L)
{
bool isfile = true;
@@ -229,7 +166,6 @@ static const luaL_Reg functions[] =
{ "getJoysticks", w_getJoysticks },
{ "getJoystickCount", w_getJoystickCount },
{ "setGamepadMapping", w_setGamepadMapping },
{ "getGamepadMapping", w_getGamepadMapping },
{ "loadGamepadMappings", w_loadGamepadMappings },
{ "saveGamepadMappings", w_saveGamepadMappings },
{ 0, 0 }
@@ -32,7 +32,6 @@ namespace joystick
{
int w_getIndex(lua_State *L);
int w_getGamepadMapping(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_joystick(lua_State *L);
} // joystick