From b7f2ebd477dde6c094bff1a04be366668a069745 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Mon, 8 Aug 2016 02:36:10 -0700 Subject: [PATCH] Better gamepad support. --- code/client/cl_input.c | 24 +++-- code/client/cl_keys.c | 27 +++++ code/client/keycodes.h | 30 ++++++ code/sdl/sdl_input.c | 222 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 293 insertions(+), 10 deletions(-) diff --git a/code/client/cl_input.c b/code/client/cl_input.c index 296a7cc0..f4089d7e 100644 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -392,6 +392,12 @@ CL_JoystickMove void CL_JoystickMove( usercmd_t *cmd ) { float anglespeed; + float yaw = j_yaw->value * cl.joystickAxis[j_yaw_axis->integer]; + float right = j_side->value * cl.joystickAxis[j_side_axis->integer]; + float forward = j_forward->value * cl.joystickAxis[j_forward_axis->integer]; + float pitch = j_pitch->value * cl.joystickAxis[j_pitch_axis->integer]; + float up = j_up->value * cl.joystickAxis[j_up_axis->integer]; + if ( !(in_speed.active ^ cl_run->integer) ) { cmd->buttons |= BUTTON_WALKING; } @@ -403,22 +409,22 @@ void CL_JoystickMove( usercmd_t *cmd ) { } if ( !in_strafe.active ) { - cl.viewangles[YAW] += anglespeed * j_yaw->value * cl.joystickAxis[j_yaw_axis->integer]; - cmd->rightmove = ClampChar( cmd->rightmove + (int) (j_side->value * cl.joystickAxis[j_side_axis->integer]) ); + cl.viewangles[YAW] += anglespeed * yaw; + cmd->rightmove = ClampChar( cmd->rightmove + (int)right ); } else { - cl.viewangles[YAW] += anglespeed * j_side->value * cl.joystickAxis[j_side_axis->integer]; - cmd->rightmove = ClampChar( cmd->rightmove + (int) (j_yaw->value * cl.joystickAxis[j_yaw_axis->integer]) ); + cl.viewangles[YAW] += anglespeed * right; + cmd->rightmove = ClampChar( cmd->rightmove + (int)yaw ); } if ( in_mlooking ) { - cl.viewangles[PITCH] += anglespeed * j_forward->value * cl.joystickAxis[j_forward_axis->integer]; - cmd->forwardmove = ClampChar( cmd->forwardmove + (int) (j_pitch->value * cl.joystickAxis[j_pitch_axis->integer]) ); + cl.viewangles[PITCH] += anglespeed * forward; + cmd->forwardmove = ClampChar( cmd->forwardmove + (int)pitch ); } else { - cl.viewangles[PITCH] += anglespeed * j_pitch->value * cl.joystickAxis[j_pitch_axis->integer]; - cmd->forwardmove = ClampChar( cmd->forwardmove + (int) (j_forward->value * cl.joystickAxis[j_forward_axis->integer]) ); + cl.viewangles[PITCH] += anglespeed * pitch; + cmd->forwardmove = ClampChar( cmd->forwardmove + (int)forward ); } - cmd->upmove = ClampChar( cmd->upmove + (int) (j_up->value * cl.joystickAxis[j_up_axis->integer]) ); + cmd->upmove = ClampChar( cmd->upmove + (int)up ); } /* diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c index a6173ddf..f73083a5 100644 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -289,6 +289,33 @@ keyname_t keynames[] = {"EURO", K_EURO}, {"UNDO", K_UNDO}, + {"PAD0_A", K_PAD0_A }, + {"PAD0_B", K_PAD0_B }, + {"PAD0_X", K_PAD0_X }, + {"PAD0_Y", K_PAD0_Y }, + {"PAD0_BACK", K_PAD0_BACK }, + {"PAD0_GUIDE", K_PAD0_GUIDE }, + {"PAD0_START", K_PAD0_START }, + {"PAD0_LEFTSTICK_CLICK", K_PAD0_LEFTSTICK_CLICK }, + {"PAD0_RIGHTSTICK_CLICK", K_PAD0_RIGHTSTICK_CLICK }, + {"PAD0_LEFTSHOULDER", K_PAD0_LEFTSHOULDER }, + {"PAD0_RIGHTSHOULDER", K_PAD0_RIGHTSHOULDER }, + {"PAD0_DPAD_UP", K_PAD0_DPAD_UP }, + {"PAD0_DPAD_DOWN", K_PAD0_DPAD_DOWN }, + {"PAD0_DPAD_LEFT", K_PAD0_DPAD_LEFT }, + {"PAD0_DPAD_RIGHT", K_PAD0_DPAD_RIGHT }, + + {"PAD0_LEFTSTICK_LEFT", K_PAD0_LEFTSTICK_LEFT }, + {"PAD0_LEFTSTICK_RIGHT", K_PAD0_LEFTSTICK_RIGHT }, + {"PAD0_LEFTSTICK_UP", K_PAD0_LEFTSTICK_UP }, + {"PAD0_LEFTSTICK_DOWN", K_PAD0_LEFTSTICK_DOWN }, + {"PAD0_RIGHTSTICK_LEFT", K_PAD0_RIGHTSTICK_LEFT }, + {"PAD0_RIGHTSTICK_RIGHT", K_PAD0_RIGHTSTICK_RIGHT }, + {"PAD0_RIGHTSTICK_UP", K_PAD0_RIGHTSTICK_UP }, + {"PAD0_RIGHTSTICK_DOWN", K_PAD0_RIGHTSTICK_DOWN }, + {"PAD0_LEFTTRIGGER", K_PAD0_LEFTTRIGGER }, + {"PAD0_RIGHTTRIGGER", K_PAD0_RIGHTTRIGGER }, + {NULL,0} }; diff --git a/code/client/keycodes.h b/code/client/keycodes.h index 706ca1b0..e813368c 100644 --- a/code/client/keycodes.h +++ b/code/client/keycodes.h @@ -260,6 +260,36 @@ typedef enum { K_EURO, K_UNDO, + // Gamepad controls + // Ordered to match SDL2 game controller buttons and axes + // Do not change this order without also changing IN_GamepadMove() in SDL_input.c + K_PAD0_A, + K_PAD0_B, + K_PAD0_X, + K_PAD0_Y, + K_PAD0_BACK, + K_PAD0_GUIDE, + K_PAD0_START, + K_PAD0_LEFTSTICK_CLICK, + K_PAD0_RIGHTSTICK_CLICK, + K_PAD0_LEFTSHOULDER, + K_PAD0_RIGHTSHOULDER, + K_PAD0_DPAD_UP, + K_PAD0_DPAD_DOWN, + K_PAD0_DPAD_LEFT, + K_PAD0_DPAD_RIGHT, + + K_PAD0_LEFTSTICK_LEFT, + K_PAD0_LEFTSTICK_RIGHT, + K_PAD0_LEFTSTICK_UP, + K_PAD0_LEFTSTICK_DOWN, + K_PAD0_RIGHTSTICK_LEFT, + K_PAD0_RIGHTSTICK_RIGHT, + K_PAD0_RIGHTSTICK_UP, + K_PAD0_RIGHTSTICK_DOWN, + K_PAD0_LEFTTRIGGER, + K_PAD0_RIGHTTRIGGER, + // Pseudo-key that brings the console down K_CONSOLE, diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 1af20ec2..4744e0e9 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -35,6 +35,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA static cvar_t *in_keyboardDebug = NULL; +static SDL_GameController *gamepad = NULL; static SDL_Joystick *stick = NULL; static qboolean mouseAvailable = qfalse; @@ -410,7 +411,7 @@ static int hat_keys[16] = { struct { - qboolean buttons[16]; // !!! FIXME: these might be too many. + qboolean buttons[SDL_CONTROLLER_BUTTON_MAX + 1]; // +1 because old max was 16, current SDL_CONTROLLER_BUTTON_MAX is 15 unsigned int oldaxes; int oldaaxes[MAX_JOYSTICK_AXIS]; unsigned int oldhats; @@ -428,10 +429,14 @@ static void IN_InitJoystick( void ) int total = 0; char buf[16384] = ""; + if (gamepad) + SDL_GameControllerClose(gamepad); + if (stick != NULL) SDL_JoystickClose(stick); stick = NULL; + gamepad = NULL; memset(&stick_state, '\0', sizeof (stick_state)); if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER)) @@ -476,6 +481,9 @@ static void IN_InitJoystick( void ) return; } + if (SDL_IsGameController(in_joystickNo->integer)) + gamepad = SDL_GameControllerOpen(in_joystickNo->integer); + Com_DPrintf( "Joystick %d opened\n", in_joystickNo->integer ); Com_DPrintf( "Name: %s\n", SDL_JoystickNameForIndex(in_joystickNo->integer) ); Com_DPrintf( "Axes: %d\n", SDL_JoystickNumAxes(stick) ); @@ -483,8 +491,10 @@ static void IN_InitJoystick( void ) Com_DPrintf( "Buttons: %d\n", SDL_JoystickNumButtons(stick) ); Com_DPrintf( "Balls: %d\n", SDL_JoystickNumBalls(stick) ); Com_DPrintf( "Use Analog: %s\n", in_joystickUseAnalog->integer ? "Yes" : "No" ); + Com_DPrintf( "Is gamepad: %s\n", gamepad ? "Yes" : "No" ); SDL_JoystickEventState(SDL_QUERY); + SDL_GameControllerEventState(SDL_QUERY); } /* @@ -497,6 +507,12 @@ static void IN_ShutdownJoystick( void ) if ( !SDL_WasInit( SDL_INIT_GAMECONTROLLER ) ) return; + if (gamepad) + { + SDL_GameControllerClose(gamepad); + gamepad = NULL; + } + if (stick) { SDL_JoystickClose(stick); @@ -506,6 +522,204 @@ static void IN_ShutdownJoystick( void ) SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); } + +static qboolean KeyToAxisAndSign(int keynum, int *outAxis, int *outSign) +{ + char *bind; + + if (!keynum) + return qfalse; + + bind = Key_GetBinding(keynum); + + if (!bind || *bind != '+') + return qfalse; + + *outSign = 0; + + if (stricmp(bind, "+forward") == 0) + { + *outAxis = j_forward_axis->integer; + *outSign = j_forward->value > 0.0f ? 1 : -1; + } + else if (stricmp(bind, "+back") == 0) + { + *outAxis = j_forward_axis->integer; + *outSign = j_forward->value > 0.0f ? -1 : 1; + } + else if (stricmp(bind, "+moveleft") == 0) + { + *outAxis = j_side_axis->integer; + *outSign = j_side->value > 0.0f ? -1 : 1; + } + else if (stricmp(bind, "+moveright") == 0) + { + *outAxis = j_side_axis->integer; + *outSign = j_side->value > 0.0f ? 1 : -1; + } + else if (stricmp(bind, "+lookup") == 0) + { + *outAxis = j_pitch_axis->integer; + *outSign = j_pitch->value > 0.0f ? -1 : 1; + } + else if (stricmp(bind, "+lookdown") == 0) + { + *outAxis = j_pitch_axis->integer; + *outSign = j_pitch->value > 0.0f ? 1 : -1; + } + else if (stricmp(bind, "+left") == 0) + { + *outAxis = j_yaw_axis->integer; + *outSign = j_yaw->value > 0.0f ? 1 : -1; + } + else if (stricmp(bind, "+right") == 0) + { + *outAxis = j_yaw_axis->integer; + *outSign = j_yaw->value > 0.0f ? -1 : 1; + } + else if (stricmp(bind, "+moveup") == 0) + { + *outAxis = j_up_axis->integer; + *outSign = j_up->value > 0.0f ? 1 : -1; + } + else if (stricmp(bind, "+movedown") == 0) + { + *outAxis = j_up_axis->integer; + *outSign = j_up->value > 0.0f ? -1 : 1; + } + + return *outSign != 0; +} + +/* +=============== +IN_GamepadMove +=============== +*/ +static void IN_GamepadMove( void ) +{ + int i; + int translatedAxes[MAX_JOYSTICK_AXIS]; + qboolean translatedAxesSet[MAX_JOYSTICK_AXIS]; + + SDL_GameControllerUpdate(); + + // check buttons + for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) + { + qboolean pressed = SDL_GameControllerGetButton(gamepad, SDL_CONTROLLER_BUTTON_A + i); + if (pressed != stick_state.buttons[i]) + { + Com_QueueEvent(0, SE_KEY, K_PAD0_A + i, pressed, 0, NULL); + stick_state.buttons[i] = pressed; + } + } + + // must defer translated axes until all real axes are processed + // must be done this way to prevent a later mapped axis from zeroing out a previous one + if (in_joystickUseAnalog->integer) + { + for (i = 0; i < MAX_JOYSTICK_AXIS; i++) + { + translatedAxes[i] = 0; + translatedAxesSet[i] = qfalse; + } + } + + // check axes + for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++) + { + int axis = SDL_GameControllerGetAxis(gamepad, SDL_CONTROLLER_AXIS_LEFTX + i); + int oldAxis = stick_state.oldaaxes[i]; + + // Smoothly ramp from dead zone to maximum value + float f = ((float)abs(axis) / 32767.0f - in_joystickThreshold->value) / (1.0f - in_joystickThreshold->value); + + if (f < 0.0f) + f = 0.0f; + + axis = (int)(32767 * ((axis < 0) ? -f : f)); + + if (axis != oldAxis) + { + const int negMap[SDL_CONTROLLER_AXIS_MAX] = { K_PAD0_LEFTSTICK_LEFT, K_PAD0_LEFTSTICK_UP, K_PAD0_RIGHTSTICK_LEFT, K_PAD0_RIGHTSTICK_UP, 0, 0 }; + const int posMap[SDL_CONTROLLER_AXIS_MAX] = { K_PAD0_LEFTSTICK_RIGHT, K_PAD0_LEFTSTICK_DOWN, K_PAD0_RIGHTSTICK_RIGHT, K_PAD0_RIGHTSTICK_DOWN, K_PAD0_LEFTTRIGGER, K_PAD0_RIGHTTRIGGER }; + + qboolean posAnalog = qfalse, negAnalog = qfalse; + int negKey = negMap[i]; + int posKey = posMap[i]; + + if (in_joystickUseAnalog->integer) + { + int posAxis = 0, posSign = 0, negAxis = 0, negSign = 0; + + // get axes and axes signs for keys if available + posAnalog = KeyToAxisAndSign(posKey, &posAxis, &posSign); + negAnalog = KeyToAxisAndSign(negKey, &negAxis, &negSign); + + // positive to negative/neutral -> keyup if axis hasn't yet been set + if (posAnalog && !translatedAxesSet[posAxis] && oldAxis > 0 && axis <= 0) + { + translatedAxes[posAxis] = 0; + translatedAxesSet[posAxis] = qtrue; + } + + // negative to positive/neutral -> keyup if axis hasn't yet been set + if (negAnalog && !translatedAxesSet[negAxis] && oldAxis < 0 && axis >= 0) + { + translatedAxes[negAxis] = 0; + translatedAxesSet[negAxis] = qtrue; + } + + // negative/neutral to positive -> keydown + if (posAnalog && axis > 0) + { + translatedAxes[posAxis] = axis * posSign; + translatedAxesSet[posAxis] = qtrue; + } + + // positive/neutral to negative -> keydown + if (negAnalog && axis < 0) + { + translatedAxes[negAxis] = -axis * negSign; + translatedAxesSet[negAxis] = qtrue; + } + } + + // keyups first so they get overridden by keydowns later + + // positive to negative/neutral -> keyup + if (!posAnalog && posKey && oldAxis > 0 && axis <= 0) + Com_QueueEvent(0, SE_KEY, posKey, qfalse, 0, NULL); + + // negative to positive/neutral -> keyup + if (!negAnalog && negKey && oldAxis < 0 && axis >= 0) + Com_QueueEvent(0, SE_KEY, negKey, qfalse, 0, NULL); + + // negative/neutral to positive -> keydown + if (!posAnalog && posKey && oldAxis <= 0 && axis > 0) + Com_QueueEvent(0, SE_KEY, posKey, qtrue, 0, NULL); + + // positive/neutral to negative -> keydown + if (!negAnalog && negKey && oldAxis >= 0 && axis < 0) + Com_QueueEvent(0, SE_KEY, negKey, qtrue, 0, NULL); + + stick_state.oldaaxes[i] = axis; + } + } + + // set translated axes + if (in_joystickUseAnalog->integer) + { + for (i = 0; i < MAX_JOYSTICK_AXIS; i++) + { + if (translatedAxesSet[i]) + Com_QueueEvent(0, SE_JOYSTICK_AXIS, i, translatedAxes[i], 0, NULL); + } + } +} + + /* =============== IN_JoyMove @@ -518,6 +732,12 @@ static void IN_JoyMove( void ) int total = 0; int i = 0; + if (gamepad) + { + IN_GamepadMove(); + return; + } + if (!stick) return;