mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user