1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-10-17 11:59:28 +00:00

Major input handling improvements (#28):

- Support joystick hotplugging
- Support axes as buttons (means xbox360 shoulder triggers can be used as buttons)
- Show pretty joystick axis & button names if possible
- Tabify input actions UI
- Add 'mouse' column to input actions UI
- Allow to configure form hotkeys
- Allow ALL keys, get rid of internal key remapping
- SDL2: Use scancodes instead of keycodes since they are layout independent
- Allow extra mouse buttons (if present)
- Remove "lmbd" & "lmbu" actions in favor of "PrimaryAction" & "SecondaryAction"
  Makes the configuration less redundant and doesn't send each action twice,
  which happened if both were set to the same key.
- Fix Regressions from prev commits (menu not opening on Esc)

Still has a few minor bugs/issues that need to be fixed,
but pushing this now before the commit gets too large again.
This commit is contained in:
fgenesis 2016-07-13 05:00:19 +02:00
commit a043dd852f
19 changed files with 585 additions and 223 deletions

View file

@ -21,14 +21,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "ActionInput.h"
#include "ActionMapper.h"
#include "Core.h"
#include "SDL.h"
#ifndef BBGE_BUILD_SDL2
#error Needs fixes for SDL 1.2, doesnt support scancodes
#endif
std::string getInputCodeToString(int k)
static std::string inputcode2string(int k)
{
if(k <= 0)
return std::string();
@ -42,7 +42,7 @@ std::string getInputCodeToString(int k)
os << "K:" << k;
return os.str();
}
if(k >= JOY_BUTTON_0 && k < JOY_BUTTON_END)
{
std::stringstream os;
@ -50,6 +50,20 @@ std::string getInputCodeToString(int k)
return os.str();
}
if(k >= JOY_AXIS_0_POS && k < JOY_AXIS_END_POS)
{
std::stringstream os;
os << "AX:+" << (k - JOY_AXIS_0_POS);
return os.str();
}
if(k >= JOY_AXIS_0_NEG && k < JOY_AXIS_END_NEG)
{
std::stringstream os;
os << "AX:-" << (k - JOY_AXIS_0_NEG);
return os.str();
}
switch(k)
{
case MOUSE_BUTTON_LEFT:
@ -58,11 +72,66 @@ std::string getInputCodeToString(int k)
return "RMB";
case MOUSE_BUTTON_MIDDLE:
return "MMB";
default:
if(k >= MOUSE_BUTTON_EXTRA_START && k < MOUSE_BUTTON_EXTRA_START+mouseExtraButtons)
{
std::ostringstream os;
os << "MB:" << (k - MOUSE_BUTTON_LEFT);
return os.str();
}
}
return std::string();
}
static const char *jaxisname(int joysickID, int axis)
{
Joystick *j = joysickID < core->joysticks.size() ? core->joysticks[joysickID] : NULL;
return j ? j->getAxisName(axis) : NULL;
}
static const char *jbtnname(int joysickID, int btn)
{
Joystick *j = joysickID < core->joysticks.size() ? core->joysticks[joysickID] : NULL;
return j ? j->getButtonName(btn) : NULL;
}
std::string getInputCodeToString(int k)
{
std::string s = inputcode2string(k);
if(s.empty())
return "NONE";
return spacesToUnderscores(s);
}
std::string getInputCodeToUserString(int k, int joystickID)
{
const char *pretty = NULL;
// Special case keyboard input:
// Return key name for current keyboard layout!
// It's just confusing to see Y instead of Z with a german keyboard layout...
if(k < SDL_NUM_SCANCODES)
{
const SDL_Keycode kcode = SDL_GetKeyFromScancode((SDL_Scancode)k);
if(kcode != SDLK_UNKNOWN)
pretty = SDL_GetKeyName(kcode);
}
if(k >= JOY_AXIS_0_POS && k < JOY_AXIS_END_POS)
pretty = jaxisname(joystickID, k - JOY_AXIS_0_POS);
else if(k >= JOY_AXIS_0_NEG && k < JOY_AXIS_END_NEG)
pretty = jaxisname(joystickID, k - JOY_AXIS_0_NEG);
else if(k >= JOY_BUTTON_0 && k < JOY_BUTTON_END)
pretty = jbtnname(joystickID, k - JOY_BUTTON_0);
if(pretty && *pretty)
return pretty;
return inputcode2string(k);
}
int getStringToInputCode(const std::string& s)
{
if(s == "LMB")
@ -75,8 +144,22 @@ int getStringToInputCode(const std::string& s)
return atoi(s.c_str() + 2);
if(!strncmp(s.c_str(), "JB:", 3))
return JOY_BUTTON_0 + atoi(s.c_str() + 3);
if(!strncmp(s.c_str(), "MB:", 3))
return MOUSE_BUTTON_LEFT + atoi(s.c_str() + 3);
if(s.length() > 4 && !strncmp(s.c_str(), "AX:", 3))
{
int n = atoi(s.c_str() + 4);
switch(s[3])
{
case '+': return JOY_AXIS_0_POS + n;
case '-': return JOY_AXIS_0_NEG + n;
default: return 0;
}
}
if(s == "NONE")
return 0;
return SDL_GetScancodeFromName(s.c_str());
return SDL_GetScancodeFromName(underscoresToSpaces(s).c_str());
}

View file

@ -29,6 +29,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define INP_JOYSIZE 1
std::string getInputCodeToString(int k);
std::string getInputCodeToUserString(int k, int joystickID);
int getStringToInputCode(const std::string& s);
class ActionInput

View file

@ -195,6 +195,10 @@ bool ActionMapper::getKeyState(int k)
{
keyState = (core->mouse.buttons.middle == DOWN);
}
else if (k >= MOUSE_BUTTON_EXTRA_START && k < MOUSE_BUTTON_EXTRA_START+mouseExtraButtons)
{
keyState = core->mouse.buttons.extra[k - MOUSE_BUTTON_EXTRA_START];
}
else if (k >= JOY_BUTTON_0 && k < JOY_BUTTON_END)
{
int v = k - JOY_BUTTON_0;
@ -205,6 +209,34 @@ bool ActionMapper::getKeyState(int k)
if( ((keyState = j->getButton(v))) )
break;
}
else if (k >= JOY_AXIS_0_POS && k < JOY_AXIS_END_POS)
{
int v = k - JOY_AXIS_0_POS;
for(size_t i = 0; i < core->joysticks.size(); ++i)
if(Joystick *j = core->joysticks[i])
if(j->isEnabled())
{
float ax = j->getAxisUncalibrated(v);
keyState = ax > JOY_AXIS_THRESHOLD;
if(keyState)
break;
}
}
else if (k >= JOY_AXIS_0_NEG && k < JOY_AXIS_END_NEG)
{
int v = k - JOY_AXIS_END_NEG;
for(size_t i = 0; i < core->joysticks.size(); ++i)
if(Joystick *j = core->joysticks[i])
if(j->isEnabled())
{
float ax = j->getAxisUncalibrated(v);
keyState = ax < -JOY_AXIS_THRESHOLD;
if(keyState)
break;
}
}
return keyState;
}

View file

@ -45,15 +45,18 @@ struct ActionData
enum ActionButtonType
{
// must start at > 512 (SDL scancodes end there)
MOUSE_BUTTON_LEFT = 999,
MOUSE_BUTTON_RIGHT = 1000,
MOUSE_BUTTON_MIDDLE = 1001,
MOUSE_BUTTON_LEFT = 1000,
MOUSE_BUTTON_RIGHT = 1001,
MOUSE_BUTTON_MIDDLE = 1002,
MOUSE_BUTTON_EXTRA_START = 1003,
JOY_BUTTON_0 = 2000,
JOY_BUTTON_END = JOY_BUTTON_0 + MAX_JOYSTICK_BTN, // one past end
JOY_AXIS_0 = 3000,
JOY_AXIS_END = JOY_AXIS_0 + MAX_JOYSTICK_AXIS,
JOY_AXIS_0_POS = 3000,
JOY_AXIS_0_NEG = 3100,
JOY_AXIS_END_POS = JOY_AXIS_0_POS + MAX_JOYSTICK_AXIS,
JOY_AXIS_END_NEG = JOY_AXIS_0_NEG + MAX_JOYSTICK_AXIS,
};
class ActionMapper

View file

@ -627,9 +627,10 @@ void Core::initJoystickLibrary()
SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER);
#else
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
detectJoysticks();
#endif
detectJoysticks();
joystickEnabled = true;
}
void Core::clearJoysticks()
@ -639,6 +640,9 @@ void Core::clearJoysticks()
joysticks.clear();
}
// Only used for SDL 1.2 code path.
// SDL2 automatically fires joystick added events upon startup
void Core::detectJoysticks()
{
clearJoysticks();
@ -648,6 +652,7 @@ void Core::detectJoysticks()
os << "Found [" << n << "] joysticks";
debugLog(os.str());
joysticks.reserve(n);
for(unsigned i = 0; i < n; ++i)
{
Joystick *j = new Joystick;
@ -656,8 +661,6 @@ void Core::detectJoysticks()
else
delete j;
}
joystickEnabled = !joysticks.empty();
}
bool Core::initInputLibrary()
@ -685,7 +688,8 @@ void Core::onUpdate(float dt)
pollEvents();
for(size_t i = 0; i < joysticks.size(); ++i)
joysticks[i]->update(dt);
if(joysticks[i])
joysticks[i]->update(dt);
onMouseInput();
@ -1587,7 +1591,7 @@ void Core::pollEvents()
if (updateMouse)
{
int x, y;
Uint8 mousestate = SDL_GetMouseState(&x,&y);
unsigned mousestate = SDL_GetMouseState(&x,&y);
if (mouse.buttonsEnabled)
{
@ -1595,7 +1599,11 @@ void Core::pollEvents()
mouse.buttons.right = mousestate & SDL_BUTTON(3)?DOWN:UP;
mouse.buttons.middle = mousestate & SDL_BUTTON(2)?DOWN:UP;
for(unsigned i = 0; i < mouseExtraButtons; ++i)
mouse.buttons.extra[i] = mousestate & SDL_BUTTON(3+i)?DOWN:UP;
mouse.pure_buttons = mouse.buttons;
mouse.rawButtonMask = mousestate;
if (flipMouseButtons)
{

View file

@ -76,6 +76,8 @@ const int baseVirtualHeight = 600;
enum ButtonState { UP = 0, DOWN };
const unsigned mouseExtraButtons = 8;
struct MouseButtons
{
MouseButtons ()
@ -86,6 +88,7 @@ struct MouseButtons
}
ButtonState left, right, middle;
ButtonState extra[mouseExtraButtons];
};
struct Mouse
@ -94,16 +97,13 @@ struct Mouse
{
scrollWheel = scrollWheelChange = lastScrollWheel = 0;
buttonsEnabled = true;
movementEnabled = true;
}
Vector position, lastPosition;
MouseButtons buttons;
MouseButtons pure_buttons;
unsigned rawButtonMask;
Vector change;
// movementEnabled is not implemented yet
bool buttonsEnabled, movementEnabled;
bool buttonsEnabled;
int scrollWheel, scrollWheelChange, lastScrollWheel;
};

View file

@ -165,6 +165,7 @@ void DebugFont::setAlign(Align align)
DebugButton::DebugButton(int buttonID, DebugButtonReceiver *receiver, int bgWidth, int fsize, bool quitMain)
: RenderObject(), label(0), highlight(0), quitMain(quitMain), receiver(receiver), buttonID(buttonID)
, activeAlpha(0.5f), activeColor(1,1,1), inactiveAlpha(0.5f), inactiveColor(0,0,0)
{
if (bgWidth == 0)
bgWidth = 150;
@ -175,7 +176,8 @@ DebugButton::DebugButton(int buttonID, DebugButtonReceiver *receiver, int bgWidt
highlight = new Quad();
highlight->setWidthHeight(szw, fsize);
highlight->position = Vector(szw*0.5f, 0);
highlight->alpha = 0.5;
highlight->alpha = inactiveAlpha;
highlight->color = inactiveColor;
addChild(highlight, PM_POINTER);
label = new DebugFont(float(fsize)/3.0f, "DebugButton");
@ -199,7 +201,8 @@ void DebugButton::onUpdate(float dt)
if (highlight->isCoordinateInsideWorld(core->mouse.position) && ((!md) || (md && !core->mouse.buttons.left)))
{
highlight->color.interpolateTo(Vector(1, 1, 1), 0.1);
highlight->color.interpolateTo(activeColor, 0.1);
highlight->alpha.interpolateTo(activeAlpha, 0.1);
if (core->mouse.buttons.left && !md)
md = true;
@ -224,7 +227,8 @@ void DebugButton::onUpdate(float dt)
{
md = false;
}
highlight->color.interpolateTo(Vector(0,0,0), 0.1);
highlight->color.interpolateTo(inactiveColor, 0.1);
highlight->alpha.interpolateTo(inactiveAlpha, 0.1);
}

View file

@ -58,6 +58,10 @@ public:
EventPtr event;
bool quitMain;
int buttonID;
float activeAlpha;
Vector activeColor;
float inactiveAlpha;
Vector inactiveColor;
protected:
void onUpdate(float dt);

View file

@ -44,6 +44,8 @@ Joystick::Joystick()
deadZone2 = 0.3;
clearRumbleTime= 0;
numJoyAxes = 0;
clearAxes();
s1ax = 0;
s1ay = 1;
@ -56,6 +58,7 @@ Joystick::Joystick()
bool Joystick::init(int stick)
{
stickIndex = stick;
numJoyAxes = 0;
#ifdef BBGE_BUILD_SDL2
if (SDL_IsGameController(stick))
@ -88,7 +91,10 @@ bool Joystick::init(int stick)
#ifdef BBGE_BUILD_SDL2
debugLog(std::string("Initialized Joystick [") + SDL_JoystickName(sdl_joy) + "]");
if (sdl_controller)
{
debugLog("Joystick is a Game Controller");
numJoyAxes = SDL_CONTROLLER_AXIS_MAX;
}
if (sdl_haptic)
debugLog("Joystick has force feedback support");
instanceID = SDL_JoystickInstanceID(sdl_joy);
@ -97,6 +103,11 @@ bool Joystick::init(int stick)
instanceID = SDL_JoystickIndex(sdl_joy);
#endif
if(!numJoyAxes)
numJoyAxes = SDL_JoystickNumAxes(sdl_joy);
if(numJoyAxes > MAX_JOYSTICK_AXIS)
numJoyAxes = MAX_JOYSTICK_AXIS;
return true;
}
@ -126,6 +137,14 @@ void Joystick::shutdown()
SDL_JoystickClose(sdl_joy);
sdl_joy = 0;
}
numJoyAxes = 0;
clearAxes();
}
void Joystick::clearAxes()
{
memset(axisRaw, 0, sizeof(axisRaw));
}
void Joystick::rumble(float leftMotor, float rightMotor, float time)
@ -195,28 +214,18 @@ void Joystick::update(float dt)
if (sdl_controller)
{
Sint16 xaxis = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_LEFTX);
Sint16 yaxis = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_LEFTY);
position.x = float(xaxis)/32768.0f;
position.y = float(yaxis)/32768.0f;
for(int i = 0; i < numJoyAxes; ++i)
{
Sint16 ax = SDL_GameControllerGetAxis(sdl_controller, (SDL_GameControllerAxis)i);
axisRaw[i] = float(ax)/32768.0f;
}
Sint16 xaxis2 = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_RIGHTX);
Sint16 yaxis2 = SDL_GameControllerGetAxis(sdl_controller, SDL_CONTROLLER_AXIS_RIGHTY);
rightStick.x = float(xaxis2)/32768.0f;
rightStick.y = float(yaxis2)/32768.0f;
position.x = axisRaw[SDL_CONTROLLER_AXIS_LEFTX];
position.y = axisRaw[SDL_CONTROLLER_AXIS_LEFTY];
rightStick.x = axisRaw[SDL_CONTROLLER_AXIS_RIGHTX];
rightStick.y = axisRaw[SDL_CONTROLLER_AXIS_RIGHTY];
}
else
{
Sint16 xaxis = SDL_JoystickGetAxis(sdl_joy, s1ax);
Sint16 yaxis = SDL_JoystickGetAxis(sdl_joy, s1ay);
position.x = float(xaxis)/32768.0f;
position.y = float(yaxis)/32768.0f;
Sint16 xaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ax);
Sint16 yaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ay);
rightStick.x = float(xaxis2)/32768.0f;
rightStick.y = float(yaxis2)/32768.0f;
}
#else
if (!SDL_JoystickOpened(stickIndex))
{
@ -224,17 +233,19 @@ void Joystick::update(float dt)
sdl_joy = NULL;
return;
}
Sint16 xaxis = SDL_JoystickGetAxis(sdl_joy, s1ax);
Sint16 yaxis = SDL_JoystickGetAxis(sdl_joy, s1ay);
position.x = xaxis/32768.0f;
position.y = yaxis/32768.0f;
Sint16 xaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ax);
Sint16 yaxis2 = SDL_JoystickGetAxis(sdl_joy, s2ay);
rightStick.x = xaxis2/32768.0f;
rightStick.y = yaxis2/32768.0f;
#endif
// Note: this connects with the else above when the SDL2 path is compiled!
{
for(int i = 0; i < numJoyAxes; ++i)
{
Sint16 ax = SDL_JoystickGetAxis(sdl_joy, i);
axisRaw[i] = float(ax)/32768.0f;
}
position.x = axisRaw[s1ax];
position.y = axisRaw[s1ay];
rightStick.x = axisRaw[s2ax];
rightStick.y = axisRaw[s2ay];
}
calibrate(position, deadZone1);
calibrate(rightStick, deadZone2);
@ -269,3 +280,37 @@ bool Joystick::anyButton() const
{
return !!buttonBitmask;;
}
int Joystick::getNumAxes() const
{
#ifdef BBGE_BUILD_SDL2
return sdl_controller ? SDL_CONTROLLER_AXIS_MAX : numJoyAxes;
#else
return numJoyAxes;
#endif
}
float Joystick::getAxisUncalibrated(int id) const
{
return id < MAX_JOYSTICK_AXIS ? axisRaw[id] : 0.0f;
}
const char *Joystick::getAxisName(int axis) const
{
if(axis >= numJoyAxes)
return NULL;
#ifdef BBGE_BUILD_SDL2
if(sdl_controller)
return SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)axis);
#endif
return NULL;
}
const char *Joystick::getButtonName(int btn) const
{
#ifdef BBGE_BUILD_SDL2
if(sdl_controller)
return SDL_GameControllerGetStringForButton((SDL_GameControllerButton)btn);
#endif
return NULL;
}

View file

@ -13,6 +13,8 @@
#define MAX_JOYSTICK_BTN 32
#define MAX_JOYSTICK_AXIS 32
const static float JOY_AXIS_THRESHOLD = 0.6f;
class Joystick
{
public:
@ -32,20 +34,28 @@ public:
void calibrate(Vector &vec, float dead);
bool anyButton() const;
bool getButton(unsigned id) const { return !!(buttonBitmask & (1u << id)); }
float getAxisUncalibrated(int id) const;
int getNumAxes() const;
int getIndex() const { return stickIndex; }
int getInstanceID() const { return instanceID; }
inline bool isEnabled() const { return enabled; }
const char *getAxisName(int axis) const;
const char *getButtonName(int btn) const;
Vector rightStick;
int s1ax, s1ay, s2ax, s2ay;
private:
void clearAxes();
bool enabled;
int stickIndex;
int instanceID;
unsigned buttonBitmask; // FIXME: this should go
unsigned buttonBitmask;
int numJoyAxes;
SDL_Joystick *sdl_joy;
float axisRaw[MAX_JOYSTICK_AXIS];
# ifdef BBGE_BUILD_SDL2
SDL_GameController *sdl_controller;