Skip to content

Commit df47b26

Browse files
allow for gamepad triggers to be used as buttons
Allots special buttons for use by gamepad triggers. This allows triggers to be usable as an axis or a button depending on the control type. NOTE: This is implemented in a way that shouldn't break compatiblity with other builds using the same cc profile. It does not use the control config code that appears to allow for axis buttons due to it not being fully implemented and needing too much work for sort out. Should that ever happen this feature should be revisited so that it can be implemented properly.
1 parent 97f8e91 commit df47b26

File tree

3 files changed

+88
-12
lines changed

3 files changed

+88
-12
lines changed

code/controlconfig/controlsconfigcommon.cpp

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2589,25 +2589,51 @@ void CC_bind::invert_toggle() {
25892589
flags ^= CCF_INVERTED;
25902590
}
25912591

2592+
2593+
static bool compare_btn(short cid, const CC_bind& A, char& A_flags, const CC_bind& B, char& B_flags)
2594+
{
2595+
auto A_btn = A.get_btn();
2596+
auto B_btn = B.get_btn();
2597+
2598+
auto current = io::joystick::getPlayerJoystick(cid);
2599+
2600+
if (current && current->isGamepad()) {
2601+
if (A_btn >= SDL_GAMEPAD_BUTTON_COUNT) {
2602+
A_btn = (A_btn - SDL_GAMEPAD_BUTTON_COUNT) + SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
2603+
A_flags |= CCF_AXIS_BTN;
2604+
}
2605+
2606+
if (B_btn >= SDL_GAMEPAD_BUTTON_COUNT) {
2607+
B_btn = (B_btn - SDL_GAMEPAD_BUTTON_COUNT) + SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
2608+
B_flags |= CCF_AXIS_BTN;
2609+
}
2610+
}
2611+
2612+
return (A_btn == B_btn);
2613+
}
2614+
25922615
bool CC_bind::conflicts_with(const CC_bind& B) const {
2616+
char A_flags = flags;
2617+
char B_flags = B.flags;
2618+
25932619
// Bail early if CID or btn are not the same
2594-
if ((cid != B.cid) || (btn != B.btn)) {
2620+
if ((cid != B.cid) || !compare_btn(cid, *this, A_flags, B, B_flags)) {
25952621
return false;
25962622
}
25972623

25982624
// Check if A is an Axis or Axis Button, and if B is an Axis or Axis Button
25992625
char mask = (CCF_AXIS_BTN | CCF_AXIS);
2600-
if ((flags & mask) && (B.flags & mask)) {
2626+
if ((A_flags & mask) && (B_flags & mask)) {
26012627
return true;
26022628
}
26032629

26042630
// Check if Hat
2605-
if (flags & B.flags & CCF_HAT) {
2631+
if (A_flags & B_flags & CCF_HAT) {
26062632
return true;
26072633
}
26082634

26092635
// Check if Ball
2610-
if (flags & B.flags & CCF_BALL) {
2636+
if (A_flags & B_flags & CCF_BALL) {
26112637
return true;
26122638
}
26132639

@@ -2619,7 +2645,7 @@ bool CC_bind::conflicts_with(const CC_bind& B) const {
26192645
// First off, check if A or B is NOT a button, as according to the mask. Buttons do not have a flag, so we check
26202646
// if they are any of the other input types
26212647
// Next, we return the inverse of the result. Negative of a Negative = Positive. Not Not a button = Is a button
2622-
return !((flags | B.flags) & mask);
2648+
return !((A_flags | B_flags) & mask);
26232649
}
26242650

26252651
bool CC_bind::is_inverted() const {
@@ -2710,10 +2736,19 @@ SCP_string CC_bind::textify() const {
27102736
case CID_JOY0:
27112737
case CID_JOY1:
27122738
case CID_JOY2:
2713-
case CID_JOY3:
2714-
Assert((btn >= 0) && (btn < JOY_TOTAL_BUTTONS));
2715-
retval = SCP_string(textify_button(btn));
2739+
case CID_JOY3: {
2740+
auto axis = joy_get_button_axis(cid, btn);
2741+
2742+
if (axis < 0) {
2743+
Assert((btn >= 0) && (btn < JOY_TOTAL_BUTTONS));
2744+
retval = SCP_string(textify_button(btn));
2745+
} else {
2746+
Assert((axis >= 0) && (axis < NUM_AXIS_TEXT));
2747+
retval = SCP_string(Axis_text[axis]);
2748+
}
2749+
27162750
break;
2751+
}
27172752

27182753
case CID_NONE:
27192754
default:

code/io/joy-sdl.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,8 @@ namespace joystick
844844
if (_isGamepad) {
845845
// gamepads may not support all buttons, but they don't necessarily match
846846
// the number or index of what's reported by the joystick api either
847-
_button.resize(static_cast<size_t>(SDL_GAMEPAD_BUTTON_COUNT));
847+
// NOTE: +2 added so we can map left & right triggers to a button
848+
_button.resize(static_cast<size_t>(SDL_GAMEPAD_BUTTON_COUNT + 2));
848849
for (size_t i = 0; i < _button.size(); ++i) {
849850
if (SDL_GetGamepadButton(_gamepad, static_cast<SDL_GamepadButton>(i))) {
850851
_button[i].DownTimestamp = ui_timestamp();
@@ -977,15 +978,30 @@ namespace joystick
977978

978979
Assertion(axis < numAxes(), "SDL event contained invalid axis index!");
979980

980-
// Triggers range from 0..32767 so we need to scale the value for FSO
981-
// since it expects a full -32768..32767 range. Note that precision is
982-
// lost in the scaling so only scale if it's not a min/max trigger value
983981
if ((axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) {
982+
// Triggers range from 0..32767 so we need to scale the value for FSO
983+
// since it expects a full -32768..32767 range. Note that precision is
984+
// lost in the scaling so only scale if it's not a min/max trigger value
984985
if (value == 0) {
985986
value = -32768;
986987
} else if (value < 32767) {
987988
value = static_cast<decltype(value)>((value * 2) - 32768);
988989
}
990+
991+
// We can also take this opportunity to map triggers to a button
992+
int button = SDL_GAMEPAD_BUTTON_COUNT + (axis - SDL_GAMEPAD_AXIS_LEFT_TRIGGER);
993+
bool down = (value >= 0);
994+
995+
Assertion(button < numButtons(), "Gamepad trigger button is out of bounds!");
996+
997+
// We also need to avoid bumping DownCount for every tiny axis movement.
998+
if ( !down || (_button[button].DownCount == 0) ) {
999+
_button[button].DownTimestamp = down ? ui_timestamp() : UI_TIMESTAMP::invalid();
1000+
1001+
if (down) {
1002+
++_button[button].DownCount;
1003+
}
1004+
}
9891005
}
9901006

9911007
_axisValues[axis] = value;
@@ -1434,3 +1450,26 @@ bool joy_present(short cid) {
14341450

14351451
return pJoystick[cid] != nullptr;
14361452
}
1453+
1454+
// Check for special trigger buttons and return axis for button
1455+
// This is primarily for easier UI/UX in controlconfig (like conflict detection)
1456+
// returns axis or -1 if not an axis button
1457+
short joy_get_button_axis(short cid, short btn)
1458+
{
1459+
auto current = io::joystick::getPlayerJoystick(cid);
1460+
1461+
// gamepads only (for now?)
1462+
if ( !current || !current->isGamepad() ) {
1463+
return -1;
1464+
}
1465+
1466+
// the special trigger buttons are extended on normal buttons so we need to
1467+
// remove those and sort out whether this is the left (0) or right (1) trigger
1468+
auto axis_button = btn - SDL_GAMEPAD_BUTTON_COUNT;
1469+
1470+
if (axis_button < 0 || axis_button > 1) {
1471+
return -1;
1472+
}
1473+
1474+
return static_cast<short>(SDL_GAMEPAD_AXIS_LEFT_TRIGGER + axis_button);
1475+
}

code/io/joy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ int joy_down_count(const CC_bind &bind, int reset_count);
383383

384384
int joy_down(const CC_bind &bind);
385385

386+
short joy_get_button_axis(const short cid, short btn);
387+
386388
/**
387389
* Checks if the given joystick is present or not
388390
*/

0 commit comments

Comments
 (0)