From 3632f1f3aceaccd6901e93836874272a8cf6950b Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Tue, 17 Jan 2023 01:32:14 +1100 Subject: osd: Added support for guitar and drum kit controllers via XInput. --- src/osd/modules/input/input_sdl.cpp | 6 +- src/osd/modules/input/input_xinput.cpp | 124 ++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 28 deletions(-) diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp index 0f7ece4bdf1..9a93177d521 100644 --- a/src/osd/modules/input/input_sdl.cpp +++ b/src/osd/modules/input/input_sdl.cpp @@ -1259,7 +1259,7 @@ public: #endif // add axes - std::tuple axes[]{ + std::tuple const axes[]{ { SDL_CONTROLLER_AXIS_LEFTX, ITEM_ID_XAXIS, false }, { SDL_CONTROLLER_AXIS_LEFTY, ITEM_ID_YAXIS, false }, { SDL_CONTROLLER_AXIS_RIGHTX, ITEM_ID_ZAXIS, false }, @@ -1299,7 +1299,7 @@ public: } // add automatically numbered buttons - std::pair numberedbuttons[]{ + std::pair const numberedbuttons[]{ { SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_AXIS_INVALID }, { SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_AXIS_INVALID }, { SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_AXIS_INVALID }, @@ -1386,7 +1386,7 @@ public: } // add buttons with fixed item IDs - std::pair fixedbuttons[]{ + std::pair const fixedbuttons[]{ { SDL_CONTROLLER_BUTTON_BACK, ITEM_ID_SELECT }, { SDL_CONTROLLER_BUTTON_START, ITEM_ID_START }, { SDL_CONTROLLER_BUTTON_DPAD_UP, ITEM_ID_HAT1UP }, diff --git a/src/osd/modules/input/input_xinput.cpp b/src/osd/modules/input/input_xinput.cpp index 27aefa29d02..d5b7614478b 100644 --- a/src/osd/modules/input/input_xinput.cpp +++ b/src/osd/modules/input/input_xinput.cpp @@ -94,7 +94,7 @@ layouts corresponding to the latter two arcade stick layouts, with the rightmost column on the shoulder buttons. Examples of face button layouts: -Mad Catz Street Fighter IV FightPad, PowerA FUSION Wired FightPad +Hori Fighting Commander OCTA, Mad Catz Street Fighter IV FightPad, PowerA FUSION Wired FightPad X Y RB A B RT @@ -121,6 +121,18 @@ A U B L R Y D X + +Drum kits also have multiple layouts. + +Rock Band: +B Y X A + LB + +Guitar Hero: + Y RB +A X B + LB + */ #include "modules/osdmodule.h" @@ -137,7 +149,6 @@ Y D X #include #include #include -#include #include @@ -172,6 +183,14 @@ char const *const AXIS_NAMES_FLIGHT_STICK[]{ "Rudder", "Throttle" }; +char const *const AXIS_NAMES_GUITAR[]{ + "LSX", + "LSY", + "Whammy Bar", + "Orientation", + "Pickup Selector", + "RT" }; + input_item_id const AXIS_IDS_GAMEPAD[]{ ITEM_ID_XAXIS, ITEM_ID_YAXIS, @@ -188,7 +207,7 @@ input_item_id const AXIS_IDS_FLIGHT_STICK[]{ ITEM_ID_RZAXIS, ITEM_ID_ZAXIS }; -const char *const HAT_NAMES_GAMEPAD[]{ +char const *const HAT_NAMES_GAMEPAD[]{ "D-pad Up", "D-pad Down", "D-pad Left", @@ -198,7 +217,7 @@ const char *const HAT_NAMES_GAMEPAD[]{ "POV Hat Left", "POV Hat Right" }; -const char *const HAT_NAMES_ARCADE_STICK[]{ +char const *const HAT_NAMES_ARCADE_STICK[]{ "Joystick Up", "Joystick Down", "Joystick Left", @@ -208,6 +227,42 @@ const char *const HAT_NAMES_ARCADE_STICK[]{ nullptr, nullptr }; +char const *const BUTTON_NAMES_GAMEPAD[]{ + "A", + "B", + "X", + "Y", + "LT", + "RT", + "LB", + "RB", + "LSB", + "RSB" }; + +char const *const BUTTON_NAMES_GUITAR[]{ + "Fret 1", + "Fret 2", + "Fret 3", + "Fret 4", + nullptr, + nullptr, + "Fret 5", + "RB", + "LSB", // supposedly "fret modifier" - how does this work? + "RSB" }; + +char const *const BUTTON_NAMES_DRUMKIT[]{ + "Green", // floor tom + "Red", // snare + "Blue", // low tom + "Yellow", // Rock Band high tom, Guitar Hero hi-hat + nullptr, + nullptr, + "Orange", // Guitar Hero crash cymbal + "Bass Drum", + "LSB", + "RSB" }; + //============================================================ // xinput_joystick_device @@ -369,7 +424,7 @@ void xinput_joystick_device::reset() void xinput_joystick_device::configure() { // TODO: don't add axes not present in capabilities - // TODO: proper support for dance mat, guitar and drum kit controllers + // TODO: proper support for dance mat controllers // default characteristics for a gamepad char const *type_name = "unsupported"; @@ -377,9 +432,12 @@ void xinput_joystick_device::configure() bool lt_rt_button = false; bool lt_rt_fullaxis = false; bool rstick_hat = false; + bool button_order_guitar = false; + bool button_order_drumkit = false; char const *const *axis_names = AXIS_NAMES_GAMEPAD; input_item_id const *axis_ids = AXIS_IDS_GAMEPAD; char const *const *hat_names = HAT_NAMES_GAMEPAD; + char const *const *button_names = BUTTON_NAMES_GAMEPAD; // consider the device type to decide how to map controls switch (m_capabilities.Type) @@ -417,9 +475,14 @@ void xinput_joystick_device::configure() case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: case XINPUT_DEVSUBTYPE_GUITAR_BASS: subtype_name = "guitar"; + button_order_guitar = true; + axis_names = AXIS_NAMES_GUITAR; + button_names = BUTTON_NAMES_GUITAR; break; case XINPUT_DEVSUBTYPE_DRUM_KIT: subtype_name = "drum kit"; + button_order_drumkit = true; + button_names = BUTTON_NAMES_DRUMKIT; break; case XINPUT_DEVSUBTYPE_ARCADE_PAD: subtype_name = "arcade pad"; @@ -439,14 +502,14 @@ void xinput_joystick_device::configure() // log some diagnostic information osd_printf_verbose( - "Configuring XInput player %d type 0x%02X (%s) sub type 0x%02X (%s)\n", + "XInput: Configuring player %d type 0x%02X (%s) sub type 0x%02X (%s).\n", m_player_index + 1, m_capabilities.Type, type_name, m_capabilities.SubType, subtype_name); osd_printf_verbose( - "XInput switch capabilities A=%d B=%d X=%d Y=%d LB=%d RB=%d LSB=%d RSB=%d Start=%d Back=%d Up=%d Down=%d Left=%d Right=%d\n", + "XInput: Switch capabilities A=%d B=%d X=%d Y=%d LB=%d RB=%d LSB=%d RSB=%d Start=%d Back=%d Up=%d Down=%d Left=%d Right=%d.\n", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 1 : 0, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 1 : 0, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 1 : 0, @@ -462,7 +525,7 @@ void xinput_joystick_device::configure() (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 1 : 0, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 1 : 0); osd_printf_verbose( - "XInput axis capabilities LT=0x%02X (%d-bit%s) RT=0x%02X (%d-bit%s) LSX=0x%04X (%d-bit%s) LSY=0x%04X (%d-bit%s) RSX=0x%04X (%d-bit%s) RSY=0x%04X (%d-bit%s)\n", + "XInput: Axis capabilities LT=0x%02X (%d-bit%s) RT=0x%02X (%d-bit%s) LSX=0x%04X (%d-bit%s) LSY=0x%04X (%d-bit%s) RSX=0x%04X (%d-bit%s) RSY=0x%04X (%d-bit%s).\n", m_capabilities.Gamepad.bLeftTrigger, count_leading_ones_32(uint32_t(m_capabilities.Gamepad.bLeftTrigger) << 24), ltcap_bad ? ", invalid" : "", @@ -487,13 +550,13 @@ void xinput_joystick_device::configure() if (ltcap_bad || rtcap_bad || lsxcap_bad || lsycap_bad || rsxcap_bad || rsycap_bad) { // Retro-Bit Sega Saturn Control Pad reports garbage for axis resolutions and absence of several buttons - osd_printf_verbose("Ignoring invalid XInput capabilities (invalid axis resolution)\n"); + osd_printf_verbose("XInput: Ignoring invalid capabilities (invalid axis resolution).\n"); ignore_caps = true; } else if (!m_capabilities.Gamepad.wButtons && !m_capabilities.Gamepad.bLeftTrigger && !m_capabilities.Gamepad.bRightTrigger && !m_capabilities.Gamepad.sThumbLX && !m_capabilities.Gamepad.sThumbLY && !m_capabilities.Gamepad.sThumbRX && !m_capabilities.Gamepad.sThumbRY) { // 8BitDo SN30 Pro V1 reports no controls at all, which would be completely useless - osd_printf_verbose("Ignoring invalid XInput capabilities (no controls reported)\n"); + osd_printf_verbose("XInput: Ignoring invalid capabilities (no controls reported).\n"); ignore_caps = true; } if (ignore_caps) @@ -553,27 +616,38 @@ void xinput_joystick_device::configure() } // add buttons - std::tuple const button_caps[]{ - { SWITCH_A, "A", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0 }, - { SWITCH_B, "B", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0 }, - { SWITCH_X, "X", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0 }, - { SWITCH_Y, "Y", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0 }, - { SWITCH_LT, "LT", lt_rt_button && m_capabilities.Gamepad.bLeftTrigger }, - { SWITCH_RT, "RT", lt_rt_button && m_capabilities.Gamepad.bRightTrigger }, - { SWITCH_LB, "LB", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0 }, - { SWITCH_RB, "RB", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0 }, - { SWITCH_LSB, "LSB", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0 }, - { SWITCH_RSB, "RSB", (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0 } }; + std::pair button_caps[]{ + { SWITCH_A, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0 }, + { SWITCH_B, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0 }, + { SWITCH_X, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0 }, + { SWITCH_Y, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0 }, + { SWITCH_LT, lt_rt_button && m_capabilities.Gamepad.bLeftTrigger }, + { SWITCH_RT, lt_rt_button && m_capabilities.Gamepad.bRightTrigger }, + { SWITCH_LB, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0 }, + { SWITCH_RB, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0 }, + { SWITCH_LSB, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0 }, + { SWITCH_RSB, (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0 } }; + if (button_order_guitar) + { + using std::swap; + swap(button_caps[2], button_caps[3]); + } + if (button_order_drumkit) + { + using std::swap; + swap(button_caps[6], button_caps[7]); + } input_item_id button_id = ITEM_ID_BUTTON1; - for (auto [i, name, supported] : button_caps) + for (unsigned i = 0; std::size(button_caps) > i; ++i) { + auto const [offset, supported] = button_caps[i]; if (supported) { device()->add_item( - name, + button_names[i], button_id++, generic_button_get_state, - &m_switches[i]); + &m_switches[offset]); } } @@ -670,7 +744,7 @@ int xinput_api_helper::initialize() if (!XInputGetState || !XInputGetCapabilities) { - osd_printf_verbose("Could not find XInput. Please try to reinstall DirectX runtime package.\n"); + osd_printf_error("XInput: Could not find API functions.\n"); return -1; } -- cgit v1.2.3