diff options
author | 2023-01-18 05:12:18 +1100 | |
---|---|---|
committer | 2023-01-18 05:12:18 +1100 | |
commit | 701898d84102194d38b263c51145f8a15662984a (patch) | |
tree | c62ca2645600f4dc77978efdd5e3134258149b4f | |
parent | 0d5d066839bc9afa0e5336a3d744bb4e7af7aa3d (diff) |
osd: Improved XInput guitar support and added XInput Rock Band keyboard support.
-rw-r--r-- | src/osd/modules/input/input_xinput.cpp | 997 |
1 files changed, 780 insertions, 217 deletions
diff --git a/src/osd/modules/input/input_xinput.cpp b/src/osd/modules/input/input_xinput.cpp index d5b7614478b..82180e4b862 100644 --- a/src/osd/modules/input/input_xinput.cpp +++ b/src/osd/modules/input/input_xinput.cpp @@ -38,7 +38,7 @@ X Button Button Button Button Y Button Button Button Button Button Foot switch Fret 3 High tom LB Button Button Button^ Button Button^ Fret 5 Bass drum RB Button Button Button^ Button Button^ Button^ Button^ -LSB Button Button^ Button^ Button^ Button^ Fret mod^ Button^ +LSB Button Button^ Button^ Button^ Button^ Fret modifier^ Button^ RSB Button Button^ Button^ Button^ Button^ Button^ Button^ ^ optional @@ -133,6 +133,35 @@ Guitar Hero: A X B LB + +Rock band keyboards use axes as bit fields: + +LT 7 C +LT 6 C# +LT 5 D +LT 4 D# +LT 3 E +LT 2 F +LT 1 F# +LT 0 G +RT 7 G# +RT 6 A +RT 5 A# +RT 4 B +RT 3 C +RT 2 C# +RT 1 D +RT 0 D# +LSX 7 E +LSX 6 F +LSX 5 F# +LSX 4 G +LSX 3 G# +LSX 2 A +LSX 1 A# +LSX 0 B +LSX 15 C + */ #include "modules/osdmodule.h" @@ -149,6 +178,7 @@ A X B #include <algorithm> #include <cstdint> #include <string> +#include <tuple> #include <utility> @@ -227,6 +257,12 @@ char const *const HAT_NAMES_ARCADE_STICK[]{ nullptr, nullptr }; +char const *const HAT_NAMES_GUITAR[]{ + "Strum/D-pad Up", + "Strum/D-pad Down", + "D-pad Left", + "D-pad Right" }; + char const *const BUTTON_NAMES_GAMEPAD[]{ "A", "B", @@ -239,18 +275,6 @@ char const *const BUTTON_NAMES_GAMEPAD[]{ "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 @@ -263,12 +287,231 @@ char const *const BUTTON_NAMES_DRUMKIT[]{ "LSB", "RSB" }; +char const *const BUTTON_NAMES_GUITAR[]{ + "Fret 1", + "Fret 2", + "Fret 3", + "Fret 4", + "Fret 5", + "Fret Modifier", + "RB", + "RSB" }; + + //============================================================ -// xinput_joystick_device +// base class for XInput controller handlers //============================================================ -class xinput_joystick_device : public device_info +class xinput_device_base : public device_info +{ +public: + virtual void configure() = 0; + +protected: + xinput_device_base( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + uint32_t player, + XINPUT_CAPABILITIES const &caps, + std::shared_ptr<xinput_api_helper> const &helper); + + // capabilities + BYTE device_type() const { return m_capabilities.Type; } + BYTE device_subtype() const { return m_capabilities.SubType; } + bool has_button(WORD mask) const { return (m_capabilities.Gamepad.wButtons & mask) != 0; } + bool has_trigger_left() const { return m_capabilities.Gamepad.bLeftTrigger != 0; } + bool has_trigger_right() const { return m_capabilities.Gamepad.bRightTrigger != 0; } + bool has_thumb_left_x() const { return m_capabilities.Gamepad.sThumbLX != 0; } + bool has_thumb_left_y() const { return m_capabilities.Gamepad.sThumbLY != 0; } + bool has_thumb_right_x() const { return m_capabilities.Gamepad.sThumbRX != 0; } + bool has_thumb_right_y() const { return m_capabilities.Gamepad.sThumbRY != 0; } + + // device state + WORD buttons() const { return m_xinput_state.Gamepad.wButtons; } + BYTE trigger_left() const { return m_xinput_state.Gamepad.bLeftTrigger; } + BYTE trigger_right() const { return m_xinput_state.Gamepad.bRightTrigger; } + SHORT thumb_left_x() const { return m_xinput_state.Gamepad.sThumbLX; } + SHORT thumb_left_y() const { return m_xinput_state.Gamepad.sThumbLY; } + SHORT thumb_right_x() const { return m_xinput_state.Gamepad.sThumbRX; } + SHORT thumb_right_y() const { return m_xinput_state.Gamepad.sThumbRY; } + + XINPUT_CAPABILITIES const &check_capabilities(); + bool read_state(); + +private: + uint32_t const m_player_index; + XINPUT_CAPABILITIES m_capabilities; + XINPUT_STATE m_xinput_state; + + std::shared_ptr<xinput_api_helper> m_xinput_helper; +}; + + +xinput_device_base::xinput_device_base( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + uint32_t player, + XINPUT_CAPABILITIES const &caps, + std::shared_ptr<xinput_api_helper> const &helper) : + device_info(machine, std::move(name), std::move(id), DEVICE_CLASS_JOYSTICK, module), + m_player_index(player), + m_capabilities(caps), + m_xinput_state{ 0 }, + m_xinput_helper(helper) +{ + // TODO: some controller types may have different capabilities format (e.g. DJ Hero turntable) + + // get friendly names for controller type and subtype + char const *type_name = "unsupported"; + char const *subtype_name = "unsupported"; + switch (m_capabilities.Type) + { + case XINPUT_DEVTYPE_GAMEPAD: + type_name = "game controller"; + switch (m_capabilities.SubType) + { + case 0x00: // XINPUT_DEVSUBTYPE_UNKNOWN: work around MinGW header issues + subtype_name = "unknown"; + break; + case XINPUT_DEVSUBTYPE_GAMEPAD: + subtype_name = "gamepad"; + break; + case XINPUT_DEVSUBTYPE_WHEEL: + subtype_name = "wheel"; + break; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + subtype_name = "arcade stick"; + break; + case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK: work around MinGW header issues + subtype_name = "flight stick"; + break; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + subtype_name = "dance pad"; + break; + case XINPUT_DEVSUBTYPE_GUITAR: + subtype_name = "guitar"; + break; + case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: + subtype_name = "alternate guitar"; + break; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + subtype_name = "drum kit"; + break; + case XINPUT_DEVSUBTYPE_GUITAR_BASS: + subtype_name = "bass guitar"; + break; + case XINPUT_DEVSUBTYPE_ARCADE_PAD: + subtype_name = "arcade pad"; + break; + } + break; + } + + // detect invalid axis resolutions + bool const ltcap_bad = m_capabilities.Gamepad.bLeftTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bLeftTrigger << 24); + bool const rtcap_bad = m_capabilities.Gamepad.bRightTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bRightTrigger << 24); + bool const lsxcap_bad = m_capabilities.Gamepad.sThumbLX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLX << 16); + bool const lsycap_bad = m_capabilities.Gamepad.sThumbLY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLY << 16); + bool const rsxcap_bad = m_capabilities.Gamepad.sThumbRX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRX << 16); + bool const rsycap_bad = m_capabilities.Gamepad.sThumbRY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRY << 16); + + // log some diagnostic information + osd_printf_verbose( + "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", + (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, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1 : 0, + (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", + m_capabilities.Gamepad.bLeftTrigger, + count_leading_ones_32(uint32_t(m_capabilities.Gamepad.bLeftTrigger) << 24), + ltcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.bRightTrigger, + count_leading_ones_32(uint32_t(m_capabilities.Gamepad.bRightTrigger) << 24), + rtcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbLX, + count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbLX)) << 16), + lsxcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbLY, + count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbLY)) << 16), + lsycap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbRX, + count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbRX)) << 16), + rsxcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbRY, + count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbRY)) << 16), + rsycap_bad ? ", invalid" : ""); + + // ignore capabilities if invalid + bool ignore_caps = false; + 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("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("XInput: Ignoring invalid capabilities (no controls reported).\n"); + ignore_caps = true; + } + if (ignore_caps) + { + m_capabilities.Gamepad.wButtons = 0xf3ff; + m_capabilities.Gamepad.bLeftTrigger = 0xff; + m_capabilities.Gamepad.bRightTrigger = 0xff; + m_capabilities.Gamepad.sThumbLX = s16(u16(0xffc0)); + m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0)); + m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); + m_capabilities.Gamepad.sThumbRY = s16(u16(0xffc0)); + } +} + + +bool xinput_device_base::read_state() +{ + // when losing focus, the packet number changes and everything is zeroed + // when regaining focus, the packet number doesn't change when control states are returned + + // save previous packet number and try to read peripheral state + //DWORD const prevpacket = m_xinput_state.dwPacketNumber; + HRESULT const result = m_xinput_helper->xinput_get_state(m_player_index, &m_xinput_state); + + // only update if it succeeded and the packed number changed + return !FAILED(result) /*&& (prevpacket != m_xinput_state.dwPacketNumber)*/; +} + + + +//============================================================ +// general XInput controller handler +//============================================================ + +class xinput_joystick_device : public xinput_device_base { public: xinput_joystick_device( @@ -278,11 +521,12 @@ public: input_module &module, uint32_t player, XINPUT_CAPABILITIES const &caps, - std::shared_ptr<xinput_api_helper> helper); + std::shared_ptr<xinput_api_helper> const &helper); - void poll() override; - void reset() override; - void configure(); + virtual void poll() override; + virtual void reset() override; + + virtual void configure() override; private: static inline constexpr USHORT SWITCH_BITS[] = @@ -349,15 +593,11 @@ private: AXIS_TOTAL }; - uint32_t const m_player_index; - XINPUT_CAPABILITIES m_capabilities; - XINPUT_STATE m_xinput_state; - uint8_t m_switches[SWITCH_TOTAL]; - int32_t m_axes[AXIS_TOTAL]; - - std::shared_ptr<xinput_api_helper> m_xinput_helper; + uint8_t m_switches[SWITCH_TOTAL]; + int32_t m_axes[AXIS_TOTAL]; }; + xinput_joystick_device::xinput_joystick_device( running_machine &machine, std::string &&name, @@ -365,74 +605,66 @@ xinput_joystick_device::xinput_joystick_device( input_module &module, uint32_t player, XINPUT_CAPABILITIES const &caps, - std::shared_ptr<xinput_api_helper> helper) : - device_info(machine, std::move(name), std::move(id), DEVICE_CLASS_JOYSTICK, module), - m_player_index(player), - m_capabilities(caps), - m_xinput_state{ 0 }, - m_xinput_helper(helper) + std::shared_ptr<xinput_api_helper> const &helper) : + xinput_device_base(machine, std::move(name), std::move(id), module, player, caps, helper) { std::fill(std::begin(m_switches), std::end(m_switches), 0); std::fill(std::begin(m_axes), std::end(m_axes), 0); } + void xinput_joystick_device::poll() -{ - // poll the device first - DWORD const prevpacket = m_xinput_state.dwPacketNumber; - HRESULT result = m_xinput_helper->xinput_get_state(m_player_index, &m_xinput_state); - // if we can't poll the device or nothing changed, skip - if (FAILED(result) || (prevpacket == m_xinput_state.dwPacketNumber)) +{ + // poll the device first, and skip if nothing changed + if (!read_state()) return; // translate button bits for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) - m_switches[SWITCH_A + i] = (m_xinput_state.Gamepad.wButtons & SWITCH_BITS[i]) ? 0xff : 0x00; + m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; // translate the triggers onto the negative side of the axes - m_axes[AXIS_LT] = -normalize_absolute_axis(m_xinput_state.Gamepad.bLeftTrigger, -255, 255); - m_axes[AXIS_RT] = -normalize_absolute_axis(m_xinput_state.Gamepad.bRightTrigger, -255, 255); + m_axes[AXIS_LT] = -normalize_absolute_axis(trigger_left(), -255, 255); + m_axes[AXIS_RT] = -normalize_absolute_axis(trigger_right(), -255, 255); // translate full-precision axes - Y direction is opposite to what MAME uses - m_axes[AXIS_LSX] = normalize_absolute_axis(m_xinput_state.Gamepad.sThumbLX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - m_axes[AXIS_LSY] = normalize_absolute_axis(-m_xinput_state.Gamepad.sThumbLY, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - m_axes[AXIS_RSX] = normalize_absolute_axis(m_xinput_state.Gamepad.sThumbRX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - m_axes[AXIS_RSY] = normalize_absolute_axis(-m_xinput_state.Gamepad.sThumbRY, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_LSX] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_LSY] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_RSX] = normalize_absolute_axis(thumb_right_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_RSY] = normalize_absolute_axis(-thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); // translate LT/RT switches for arcade sticks/pads - m_switches[SWITCH_LT] = (0x80 <= m_xinput_state.Gamepad.bLeftTrigger) ? 0xff : 0x00; - m_switches[SWITCH_RT] = (0x80 <= m_xinput_state.Gamepad.bRightTrigger) ? 0xff : 0x00; + m_switches[SWITCH_LT] = (0x80 <= trigger_left()) ? 0xff : 0x00; + m_switches[SWITCH_RT] = (0x80 <= trigger_right()) ? 0xff : 0x00; // translate POV hat for flight sticks - m_switches[SWITCH_HAT_UP] = (16'384 <= m_xinput_state.Gamepad.sThumbRY) ? 0xff : 0x00; - m_switches[SWITCH_HAT_DOWN] = (-16'384 >= m_xinput_state.Gamepad.sThumbRY) ? 0xff : 0x00; - m_switches[SWITCH_HAT_LEFT] = (-16'384 >= m_xinput_state.Gamepad.sThumbRX) ? 0xff : 0x00; - m_switches[SWITCH_HAT_RIGHT] = (16'384 <= m_xinput_state.Gamepad.sThumbRX) ? 0xff : 0x00; + m_switches[SWITCH_HAT_UP] = (16'384 <= thumb_right_y()) ? 0xff : 0x00; + m_switches[SWITCH_HAT_DOWN] = (-16'384 >= thumb_right_y()) ? 0xff : 0x00; + m_switches[SWITCH_HAT_LEFT] = (-16'384 >= thumb_right_x()) ? 0xff : 0x00; + m_switches[SWITCH_HAT_RIGHT] = (16'384 <= thumb_right_x()) ? 0xff : 0x00; // translate rudder and throttle for flight sticks - m_axes[AXIS_RUDDER] = normalize_absolute_axis(m_xinput_state.Gamepad.bLeftTrigger, 0, 255); - m_axes[AXIS_THROTTLE] = normalize_absolute_axis(m_xinput_state.Gamepad.bRightTrigger, 0, 255); + m_axes[AXIS_RUDDER] = normalize_absolute_axis(trigger_left(), 0, 255); + m_axes[AXIS_THROTTLE] = normalize_absolute_axis(trigger_right(), 0, 255); } + void xinput_joystick_device::reset() { std::fill(std::begin(m_switches), std::end(m_switches), 0); std::fill(std::begin(m_axes), std::end(m_axes), 0); } + void xinput_joystick_device::configure() { - // TODO: don't add axes not present in capabilities - // TODO: proper support for dance mat controllers + // TODO: proper support for dance mat and drum kit controllers // default characteristics for a gamepad - char const *type_name = "unsupported"; - char const *subtype_name = "unsupported"; 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; @@ -440,169 +672,68 @@ void xinput_joystick_device::configure() char const *const *button_names = BUTTON_NAMES_GAMEPAD; // consider the device type to decide how to map controls - switch (m_capabilities.Type) + switch (device_type()) { case XINPUT_DEVTYPE_GAMEPAD: - type_name = "game controller"; - switch (m_capabilities.SubType) + switch (device_subtype()) { - case 0x00: // XINPUT_DEVSUBTYPE_UNKNOWN: work around MinGW header issues - subtype_name = "unknown"; - break; - case XINPUT_DEVSUBTYPE_GAMEPAD: - subtype_name = "gamepad"; - break; case XINPUT_DEVSUBTYPE_WHEEL: - subtype_name = "wheel"; axis_names = AXIS_NAMES_WHEEL; break; case XINPUT_DEVSUBTYPE_ARCADE_STICK: - subtype_name = "arcade stick"; lt_rt_button = true; hat_names = HAT_NAMES_ARCADE_STICK; break; case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK: work around MinGW header issues - subtype_name = "flight stick"; lt_rt_fullaxis = true; rstick_hat = true; axis_names = AXIS_NAMES_FLIGHT_STICK; axis_ids = AXIS_IDS_FLIGHT_STICK; break; case XINPUT_DEVSUBTYPE_DANCE_PAD: - subtype_name = "dance pad"; - break; - case XINPUT_DEVSUBTYPE_GUITAR: - 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; + // TODO: proper support 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"; lt_rt_button = true; break; } break; } - // detect invalid axis resolutions - bool const ltcap_bad = m_capabilities.Gamepad.bLeftTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bLeftTrigger << 24); - bool const rtcap_bad = m_capabilities.Gamepad.bRightTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bRightTrigger << 24); - bool const lsxcap_bad = m_capabilities.Gamepad.sThumbLX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLX << 16); - bool const lsycap_bad = m_capabilities.Gamepad.sThumbLY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLY << 16); - bool const rsxcap_bad = m_capabilities.Gamepad.sThumbRX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRX << 16); - bool const rsycap_bad = m_capabilities.Gamepad.sThumbRY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRY << 16); - - // log some diagnostic information - osd_printf_verbose( - "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", - (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, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1 : 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1 : 0, - (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", - m_capabilities.Gamepad.bLeftTrigger, - count_leading_ones_32(uint32_t(m_capabilities.Gamepad.bLeftTrigger) << 24), - ltcap_bad ? ", invalid" : "", - m_capabilities.Gamepad.bRightTrigger, - count_leading_ones_32(uint32_t(m_capabilities.Gamepad.bRightTrigger) << 24), - rtcap_bad ? ", invalid" : "", - m_capabilities.Gamepad.sThumbLX, - count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbLX)) << 16), - lsxcap_bad ? ", invalid" : "", - m_capabilities.Gamepad.sThumbLY, - count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbLY)) << 16), - lsycap_bad ? ", invalid" : "", - m_capabilities.Gamepad.sThumbRX, - count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbRX)) << 16), - rsxcap_bad ? ", invalid" : "", - m_capabilities.Gamepad.sThumbRY, - count_leading_ones_32(uint32_t(uint16_t(m_capabilities.Gamepad.sThumbRY)) << 16), - rsycap_bad ? ", invalid" : ""); - - // ignore capabilities if invalid - bool ignore_caps = false; - 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("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) + // add bidirectional axes + bool const axis_caps[]{ + has_thumb_left_x(), + has_thumb_left_y(), + !rstick_hat && has_thumb_right_x(), + !rstick_hat && has_thumb_right_y(), + lt_rt_fullaxis && has_trigger_left(), + lt_rt_fullaxis && has_trigger_right() }; + for (unsigned i = 0; std::size(axis_caps) > i; ++i) { - // 8BitDo SN30 Pro V1 reports no controls at all, which would be completely useless - osd_printf_verbose("XInput: Ignoring invalid capabilities (no controls reported).\n"); - ignore_caps = true; - } - if (ignore_caps) - { - m_capabilities.Gamepad.wButtons = 0xf3ff; - m_capabilities.Gamepad.bLeftTrigger = 0xff; - m_capabilities.Gamepad.bRightTrigger = 0xff; - m_capabilities.Gamepad.sThumbLX = s16(u16(0xffc0)); - m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0)); - m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); - m_capabilities.Gamepad.sThumbRY = s16(u16(0xffc0)); - } - - // add full-precision axes - for (unsigned i = 0; ((rstick_hat ? AXIS_LSY : AXIS_RSY) - AXIS_LSX) >= i; ++i) - { - device()->add_item( - axis_names[i], - axis_ids[i], - generic_axis_get_state<int32_t>, - &m_axes[AXIS_LSX + i]); - } - - // add extra axes for flight sticks - if (lt_rt_fullaxis) - { - for (unsigned i = 0; (AXIS_THROTTLE - AXIS_RUDDER) >= i; ++i) + if (axis_caps[i]) { device()->add_item( - axis_names[4 + i], - axis_ids[4 + i], + axis_names[i], + axis_ids[i], generic_axis_get_state<int32_t>, - &m_axes[AXIS_RUDDER + i]); + &m_axes[AXIS_LSX + i]); } } // add hats bool const hat_caps[]{ - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0, - (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0, - rstick_hat && m_capabilities.Gamepad.sThumbRY, - rstick_hat && m_capabilities.Gamepad.sThumbRY, - rstick_hat && m_capabilities.Gamepad.sThumbRX, - rstick_hat && m_capabilities.Gamepad.sThumbRX }; + has_button(XINPUT_GAMEPAD_DPAD_UP), + has_button(XINPUT_GAMEPAD_DPAD_DOWN), + has_button(XINPUT_GAMEPAD_DPAD_LEFT), + has_button(XINPUT_GAMEPAD_DPAD_RIGHT), + rstick_hat && has_thumb_right_x(), + rstick_hat && has_thumb_right_x(), + rstick_hat && has_thumb_right_y(), + rstick_hat && has_thumb_right_y() }; for (unsigned i = 0; (SWITCH_HAT_RIGHT - SWITCH_DPAD_UP) >= i; ++i) { if (hat_caps[i]) @@ -617,21 +748,16 @@ void xinput_joystick_device::configure() // add buttons std::pair<unsigned, bool> 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]); - } + { SWITCH_A, has_button(XINPUT_GAMEPAD_A) }, + { SWITCH_B, has_button(XINPUT_GAMEPAD_B) }, + { SWITCH_X, has_button(XINPUT_GAMEPAD_X) }, + { SWITCH_Y, has_button(XINPUT_GAMEPAD_Y) }, + { SWITCH_LT, lt_rt_button && has_trigger_left() }, + { SWITCH_RT, lt_rt_button && has_trigger_right() }, + { SWITCH_LB, has_button(XINPUT_GAMEPAD_LEFT_SHOULDER) }, + { SWITCH_RB, has_button(XINPUT_GAMEPAD_RIGHT_SHOULDER) }, + { SWITCH_LSB, has_button(XINPUT_GAMEPAD_LEFT_THUMB) }, + { SWITCH_RSB, has_button(XINPUT_GAMEPAD_RIGHT_THUMB) } }; if (button_order_drumkit) { using std::swap; @@ -652,7 +778,7 @@ void xinput_joystick_device::configure() } // add start/back - if (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_START) + if (has_button(XINPUT_GAMEPAD_START)) { device()->add_item( "Start", @@ -660,7 +786,7 @@ void xinput_joystick_device::configure() generic_button_get_state<uint8_t>, &m_switches[SWITCH_START]); } - if (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) + if (has_button(XINPUT_GAMEPAD_BACK)) { device()->add_item( "Back", @@ -674,18 +800,413 @@ void xinput_joystick_device::configure() { for (unsigned i = 0; (AXIS_RT - AXIS_LT) >= i; ++i) { + if (i ? has_trigger_right() : has_trigger_left()) + { + device()->add_item( + axis_names[4 + i], + axis_ids[4 + i], + generic_axis_get_state<int32_t>, + &m_axes[AXIS_LT + i]); + } + } + } +} + + + +//============================================================ +// XInput guitar handler +//============================================================ + +class xinput_guitar_device : public xinput_device_base +{ +public: + xinput_guitar_device( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + uint32_t player, + XINPUT_CAPABILITIES const &caps, + std::shared_ptr<xinput_api_helper> const &helper); + + virtual void poll() override; + virtual void reset() override; + + virtual void configure() override; + +private: + static inline constexpr USHORT SWITCH_BITS[] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_FRET1, + SWITCH_FRET2, + SWITCH_FRET3, + SWITCH_FRET4, + SWITCH_FRET5, + SWITCH_FRET_MOD, // indicates a "solo fret" is pressed, not a separate button + SWITCH_RB, // often reported present but no physical button + SWITCH_RSB, // usually reported absent + SWITCH_START, + SWITCH_BACK, // also used for "Star Power" in some games + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_TOTAL + }; + + enum + { + AXIS_SLIDER, // LSX, positive toward bridge + AXIS_LSY, // not used on guitar controllers? + AXIS_WHAMMY, // RSX, single-ended, neutral at negative extreme + + AXIS_ORIENT_NECK, // RSY + AXIS_ORIENT_BRIDGE, // LT, toward zero with frets up + AXIS_ORIENT_BODY, // RT, toward zero in right-handed orientation + + AXIS_PICKUP, // LT, positive extreme at one end + + AXIS_TOTAL + }; + + uint8_t m_switches[SWITCH_TOTAL]; + int32_t m_axes[AXIS_TOTAL]; +}; + + +xinput_guitar_device::xinput_guitar_device( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + uint32_t player, + XINPUT_CAPABILITIES const &caps, + std::shared_ptr<xinput_api_helper> const &helper) : + xinput_device_base(machine, std::move(name), std::move(id), module, player, caps, helper) +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_guitar_device::poll() + +{ + // poll the device first, and skip if nothing changed + if (!read_state()) + return; + + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_FRET1 + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; + + // translate miscellaneous axes + m_axes[AXIS_SLIDER] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_LSY] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_WHAMMY] = -normalize_absolute_axis(int32_t(thumb_right_x()) + 32'768, -65'535, 65'535); + + // translate orientation sensors + m_axes[AXIS_ORIENT_NECK] = normalize_absolute_axis(-thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_ORIENT_BRIDGE] = normalize_absolute_axis(trigger_left(), 0, 255); + m_axes[AXIS_ORIENT_BODY] = normalize_absolute_axis(trigger_right(), 0, 255); + + // translate pickup selector + m_axes[AXIS_PICKUP] = -normalize_absolute_axis(255 - trigger_right(), -255, 255); +} + + +void xinput_guitar_device::reset() +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_guitar_device::configure() +{ + // add axes + std::tuple<input_item_id, char const *, bool> const axis_caps[]{ + { ITEM_ID_RXAXIS, "Neck Slider", has_thumb_left_x() }, + { ITEM_ID_RYAXIS, "LSY", has_thumb_left_y() }, + { ITEM_ID_SLIDER1, "Whammy Bar", has_thumb_right_x() }, + { ITEM_ID_YAXIS, "Neck Orientation", has_thumb_right_y() }, + { ITEM_ID_XAXIS, "Bridge Orientation", has_trigger_left() && has_trigger_right() }, + { ITEM_ID_ZAXIS, "Body Orientation", has_trigger_right() }, + { ITEM_ID_SLIDER2, "Pickup Selector", has_trigger_left() && !has_trigger_right() } }; + for (unsigned i = 0; (AXIS_PICKUP - AXIS_SLIDER) >= i; ++i) + { + auto const [item, name, supported] = axis_caps[i]; + if (supported) + { device()->add_item( - axis_names[4 + i], - axis_ids[4 + i], - generic_axis_get_state<int32_t>, - &m_axes[AXIS_LT + i]); + name, + item, + generic_axis_get_state<uint32_t>, + &m_axes[AXIS_SLIDER + i]); + } + } + + // add hats + for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i) + { + if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i])) + { + device()->add_item( + HAT_NAMES_GUITAR[i], + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_DPAD_UP + i]); + } + } + + // add buttons + input_item_id button_id = ITEM_ID_BUTTON1; + for (unsigned i = 0; (SWITCH_RSB - SWITCH_FRET1) >= i; ++i) + { + if (has_button(SWITCH_BITS[i])) + { + device()->add_item( + BUTTON_NAMES_GUITAR[i], + button_id++, + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_FRET1 + i]); + } + } + + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + device()->add_item( + "Start", + ITEM_ID_START, + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_START]); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + device()->add_item( + "Back", + ITEM_ID_SELECT, + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_BACK]); + } +} + + + +//============================================================ +// XInput keyboard handler +//============================================================ + +class xinput_keyboard_device : public xinput_device_base +{ +public: + xinput_keyboard_device( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + uint32_t player, + XINPUT_CAPABILITIES const &caps, + std::shared_ptr<xinput_api_helper> const &helper); + + virtual void poll() override; + virtual void reset() override; + + virtual void configure() override; + +private: + static inline constexpr USHORT SWITCH_BITS[] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_A, // button bits + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_START, + SWITCH_BACK, + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_C1, + SWITCH_C3 = SWITCH_C1 + 24, + + SWITCH_TOTAL + }; + + enum + { + AXIS_VELOCITY, + + AXIS_TOTAL + }; + + uint8_t m_switches[SWITCH_TOTAL]; + int32_t m_axes[AXIS_TOTAL]; +}; + + +xinput_keyboard_device::xinput_keyboard_device( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + uint32_t player, + XINPUT_CAPABILITIES const &caps, + std::shared_ptr<xinput_api_helper> const &helper) : + xinput_device_base(machine, std::move(name), std::move(id), module, player, caps, helper) +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_keyboard_device::poll() +{ + // TODO: how many bits are really velocity? + // TODO: how are touch strip and overdrive read? + + // poll the device first, and skip if nothing changed + if (!read_state()) + return; + + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; + + // translate keys + for (unsigned i = 0; 8 > i; ++i) + { + m_switches[SWITCH_C1 + i] = BIT(trigger_left(), 7 - i) ? 0xff : 0x00; + m_switches[SWITCH_C1 + 8 + i] = BIT(trigger_right(), 7 - i) ? 0xff : 0x00; + m_switches[SWITCH_C1 + 16 + i] = BIT(thumb_left_x(), 7 - i) ? 0xff : 0x00; + } + m_switches[SWITCH_C3] = BIT(thumb_left_x(), 15) ? 0xff : 0x00; + + // translate velocity + m_axes[AXIS_VELOCITY] = -normalize_absolute_axis(BIT(thumb_left_x(), 8, 7), -127, 127); +} + + +void xinput_keyboard_device::reset() +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_keyboard_device::configure() +{ + // TODO: button capabilities actually seem sane on this controller but we nuke them due to bad axis capabilities + + // add velocity axis + device()->add_item( + "Velocity", + ITEM_ID_SLIDER1, + generic_axis_get_state<int32_t>, + &m_axes[AXIS_VELOCITY]); + + // add hats + for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i) + { + if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i])) + { + device()->add_item( + HAT_NAMES_GAMEPAD[i], + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_DPAD_UP + i]); + } + } + + // add buttons + input_item_id button_id = ITEM_ID_BUTTON1; + for (unsigned i = 0; (SWITCH_Y - SWITCH_A) >= i; ++i) + { + if (has_button(SWITCH_BITS[i])) + { + device()->add_item( + BUTTON_NAMES_GAMEPAD[i], + button_id++, + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_A + i]); } } + + // add keys + char const *const key_formats[]{ + "C %d", "C# %d", "D %d", "D# %d", "E %d", "F %d", "F# %d", "G %d", "G# %d", "A %d", "A# %d", "B %d" }; + for (unsigned i = 0; (SWITCH_C3 - SWITCH_C1) >= i; ++i) + { + device()->add_item( + util::string_format(key_formats[i % 12], (i / 12) + 1), + button_id++, + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_C1 + i]); + } + + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + device()->add_item( + "Start", + ITEM_ID_START, + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_START]); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + device()->add_item( + "Back", + ITEM_ID_SELECT, + generic_button_get_state<uint8_t>, + &m_switches[SWITCH_BACK]); + } } + //============================================================ -// xinput_joystick_module +// XInput joystick module //============================================================ class xinput_joystick_module : public wininput_module @@ -720,7 +1241,7 @@ protected: // Loop through each gamepad to determine if they are connected for (UINT i = 0; i < XUSER_MAX_COUNT; i++) { - XINPUT_STATE state = {0}; + XINPUT_STATE state{ 0 }; // allocate and link in a new device if (m_xinput_helper->xinput_get_state(i, &state) == ERROR_SUCCESS) @@ -751,6 +1272,7 @@ int xinput_api_helper::initialize() return 0; } + //============================================================ // create_xinput_device //============================================================ @@ -758,7 +1280,7 @@ int xinput_api_helper::initialize() device_info *xinput_api_helper::create_xinput_device(running_machine &machine, UINT index, wininput_module &module) { // If we can't get the capabilities skip this device - XINPUT_CAPABILITIES caps = { 0 }; + XINPUT_CAPABILITIES caps{ 0 }; if (FAILED(xinput_get_capabilities(index, 0, &caps))) return nullptr; @@ -766,19 +1288,60 @@ device_info *xinput_api_helper::create_xinput_device(running_machine &machine, U snprintf(device_name, sizeof(device_name), "XInput Player %u", index + 1); // allocate the device object - auto &devinfo = module.devicelist().create_device<xinput_joystick_device>( - machine, - device_name, - device_name, - module, - index, - caps, - shared_from_this()); + xinput_device_base *devinfo; + switch (caps.Type) + { + case XINPUT_DEVTYPE_GAMEPAD: + switch (caps.SubType) + { + case XINPUT_DEVSUBTYPE_GUITAR: + case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: + case XINPUT_DEVSUBTYPE_GUITAR_BASS: + devinfo = &module.devicelist().create_device<xinput_guitar_device>( + machine, + device_name, + device_name, + module, + index, + caps, + shared_from_this()); + break; + case 0x0f: + devinfo = &module.devicelist().create_device<xinput_keyboard_device>( + machine, + device_name, + device_name, + module, + index, + caps, + shared_from_this()); + break; + default: + devinfo = &module.devicelist().create_device<xinput_joystick_device>( + machine, + device_name, + device_name, + module, + index, + caps, + shared_from_this()); + } + break; + default: + devinfo = &module.devicelist().create_device<xinput_joystick_device>( + machine, + device_name, + device_name, + module, + index, + caps, + shared_from_this()); + } // configure each controller to add buttons, axes, etc. - devinfo.configure(); + devinfo->configure(); - return &devinfo; + return devinfo; } #else // defined(OSD_WINDOWS) |