ddr: add support for analog axis-only pads and handle left+right / up+down input (#659)

## Link to GitHub Issue or related Pull Request, if one exists
n/a

## Description of change
Some crappy DDR soft mats (either USB-based or console pads with crappy
adapters that lack a dedicated "DDR pad mode") use X and Y analog axis
for arrows, which means they cannot cleanly report Up+Down or Left+Right
input.

According to some ancient Stepmania code, a lot of these report a middle
value (value between center and right, for example, for left+right).
Borrow this hack from Stepmania and implement it in spice as analog
input for DDR.

Also, for rawinput/xinput automatic bind scenario, update the logic so
that face buttons are always detected before analog axis / dpads for
consistency. This would help with binding any DDR pads that
simultaneously input face buttons / dpad / analog axis, so that we can
prefer face buttons when detected.

## Testing
I don't have any soft mats that have this, but it was tested with an
xbox controller.
This commit is contained in:
bicarus
2026-04-27 20:17:54 -07:00
committed by GitHub
parent 37218e7fe0
commit 6bc1357d6a
10 changed files with 351 additions and 128 deletions
+46
View File
@@ -291,4 +291,50 @@ namespace games::ddr {
// dispose device hook
devicehook_dispose();
}
static void get_analog_xy_axis(Analog &analog, bool &less, bool &more) {
if (!analog.isSet()) {
return;
}
const auto value = GameAPI::Analogs::getState(RI_MGR, analog);
// stepmania linux source:
// https://github.com/stepmania/stepmania/blob/d55acb1ba26f1c5b5e3048d6d6c0bd116625216f/src/arch/InputHandler/InputHandler_Linux_Event.cpp#L410
// for example, value cap with range [0, 255]:
// 127 = 0.498, neutral
// 128 = 0.502, both
// apparently some adapters report values like above; this obviously makes a lot of
// assumptions about bit width of the HID value cap and how the adapter reports
// neutral/both values, but this is good enough for parity with stepmania and its many forks
if (0.5001f < value && value < 0.75f) {
less |= true;
more |= true;
} else if (value <= 0.25f) {
less |= true;
} else if (value >= 0.75f) {
more |= true;
}
}
void get_analog_x_axis(int player, bool &left, bool &right) {
if (player != 1 && player != 2) {
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
return;
}
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_LEFT_RIGHT : Analogs::P2_LEFT_RIGHT);
get_analog_xy_axis(analog, left, right);
}
void get_analog_y_axis(int player, bool &up, bool &down) {
if (player != 1 && player != 2) {
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
return;
}
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_UP_DOWN : Analogs::P2_UP_DOWN);
get_analog_xy_axis(analog, up, down);
}
}