summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2023-01-18 05:12:18 +1100
committer Vas Crabb <vas@vastheman.com>2023-01-18 05:12:18 +1100
commit701898d84102194d38b263c51145f8a15662984a (patch)
treec62ca2645600f4dc77978efdd5e3134258149b4f
parent0d5d066839bc9afa0e5336a3d744bb4e7af7aa3d (diff)
osd: Improved XInput guitar support and added XInput Rock Band keyboard support.
-rw-r--r--src/osd/modules/input/input_xinput.cpp997
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)