diff options
author | 2023-01-12 17:59:59 +1100 | |
---|---|---|
committer | 2023-01-12 18:14:13 +1100 | |
commit | fee7047c16a04f816a0de031ec469f5b96be9f74 (patch) | |
tree | f9be050cbdb952d8315cc241648e5ddc8422b247 /src/osd/modules/input/input_sdl.cpp | |
parent | 9e8064c85eb420e61df7ba56f197877680e642c6 (diff) |
-osd: Better XInput and SDL game controller input enhancements:
* Added initial support for XInput controller subtypes, starting with
driving, arcade and flight controllers.
* Check XInput capabilities to ignore buttons and hats that aren't
present.
* Added preliminary SDL Game Controller joystick provider. Reconnection
and mixed Game Controller/Joystick devices are unsupported.
* Show the input token for the highlighted control on input device
menus.
-ui: Allow menus to set required space above and below menu when metrics
change. Fixes the initial bad layout on the system selecton menu, or
bad layout after resizing windows.
Diffstat (limited to 'src/osd/modules/input/input_sdl.cpp')
-rw-r--r-- | src/osd/modules/input/input_sdl.cpp | 999 |
1 files changed, 831 insertions, 168 deletions
diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp index 2c8f604c9a2..94b904d0cf8 100644 --- a/src/osd/modules/input/input_sdl.cpp +++ b/src/osd/modules/input/input_sdl.cpp @@ -18,15 +18,6 @@ // standard sdl header #include <SDL2/SDL.h> -#include <cctype> -// ReSharper disable once CppUnusedIncludeDirective -#include <algorithm> -#include <cstddef> -#include <iterator> -#include <memory> -#include <mutex> -#include <optional> -#include <queue> // MAME headers #include "emu.h" @@ -41,6 +32,14 @@ #include "../../sdl/osdsdl.h" #include "../../sdl/window.h" +#include <algorithm> +#include <cctype> +#include <cstddef> +#include <iterator> +#include <memory> +#include <optional> +#include <tuple> + // winnt.h defines this #ifdef DELETE #undef DELETE @@ -53,6 +52,145 @@ namespace { #define GET_FOCUS_WINDOW(ev) focus_window() //#define GET_FOCUS_WINDOW(ev) window_from_id((ev)->windowID) +char const *const CONTROLLER_AXIS_XBOX[]{ + "LSX", + "LSY", + "RSX", + "RSY", + "LT", + "RT" }; + +[[maybe_unused]] char const *const CONTROLLER_AXIS_PS[]{ + "LSX", + "LSY", + "RSX", + "RSY", + "L2", + "R2" }; + +[[maybe_unused]] char const *const CONTROLLER_AXIS_SWITCH[]{ + "LSX", + "LSY", + "RSX", + "RSY", + "ZL", + "ZR" }; + +char const *const CONTROLLER_BUTTON_XBOX360[]{ + "A", + "B", + "X", + "Y", + "Back", + "Guide", + "Start", + "LSB", + "RSB", + "LB", + "RB", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Share", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +[[maybe_unused]] char const *const CONTROLLER_BUTTON_XBOXONE[]{ + "A", + "B", + "X", + "Y", + "View", + "Guide", + "Menu", + "LSB", + "RSB", + "LB", + "RB", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Share", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +[[maybe_unused]] char const *const CONTROLLER_BUTTON_PS3[]{ + "Cross", + "Circle", + "Square", + "Triangle", + "Select", + "PS", + "Start", + "L3", + "R3", + "L1", + "R1", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Microphone", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +[[maybe_unused]] char const *const CONTROLLER_BUTTON_PS4[]{ + "Cross", + "Circle", + "Square", + "Triangle", + "Share", + "PS", + "Options", + "L3", + "R3", + "L1", + "R1", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Microphone", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +[[maybe_unused]] char const *const CONTROLLER_BUTTON_SWITCH[]{ + "B", + "A", + "Y", + "Z", + "-", + "Home", + "+", + "LSB", + "RSB", + "L", + "R", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Capture", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + struct key_lookup_table { int code; @@ -326,9 +464,7 @@ key_lookup_table sdl_lookup_table[] = KE(APP2) -{ - -1, "" -} + { -1, "" } }; //============================================================ @@ -615,111 +751,305 @@ public: }; //============================================================ -// sdl_joystick_device +// sdl_joystick_device_base //============================================================ -class sdl_joystick_device : public sdl_device +class sdl_joystick_device_base : public sdl_device { public: - // state information for a joystick - struct sdl_joystick_state + std::optional<std::string> const &serial() const { return m_serial; } + SDL_JoystickID instance() const { return m_instance; } + + bool is_instance(SDL_JoystickID instance) const { return m_instance == instance; } + +protected: + sdl_joystick_device_base( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + char const *serial) : + sdl_device(machine, std::move(name), std::move(id), DEVICE_CLASS_JOYSTICK, module), + m_instance(-1) { - int32_t axes[MAX_AXES]; - int32_t buttons[MAX_BUTTONS]; - int32_t hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS]; - int32_t balls[MAX_AXES]; - }; + if (serial) + m_serial = serial; + } - struct sdl_api_state + void set_instance(SDL_JoystickID instance) { - SDL_Joystick *device = nullptr; - SDL_Haptic *hapdevice = nullptr; - SDL_JoystickID joystick_id; - std::optional<std::string> serial; - }; + assert(0 > m_instance); + assert(0 <= instance); - sdl_joystick_state joystick; - sdl_api_state sdl_state; + m_instance = instance; + } - sdl_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - sdl_device(machine, std::move(name), std::move(id), DEVICE_CLASS_JOYSTICK, module), - joystick({{0}}) + void clear_instance() { + m_instance = -1; + } + +private: + std::optional<std::string> m_serial; + SDL_JoystickID m_instance; +}; + +//============================================================ +// sdl_joystick_device +//============================================================ + +class sdl_joystick_device : public sdl_joystick_device_base +{ +public: + sdl_joystick_device( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + SDL_Joystick *joy, + char const *serial) : + sdl_joystick_device_base( + machine, + std::move(name), + std::move(id), + module, + serial), + m_joystick({{0}}), + m_joydevice(joy), + m_hapdevice(SDL_HapticOpenFromJoystick(joy)) + { + set_instance(SDL_JoystickInstanceID(joy)); + } + + void configure() + { + char tempname[32]; + + // loop over all axes + for (int axis = 0; (axis < MAX_AXES) && (axis < SDL_JoystickNumAxes(m_joydevice)); axis++) + { + input_item_id itemid; + + if (axis < INPUT_MAX_AXIS) + itemid = input_item_id(ITEM_ID_XAXIS + axis); + else if (axis < (INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE)) + itemid = input_item_id(ITEM_ID_ADD_ABSOLUTE1 - INPUT_MAX_AXIS + axis); + else + itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE; + + snprintf(tempname, sizeof(tempname), "A%d", axis + 1); + device()->add_item( + tempname, + itemid, + generic_axis_get_state<std::int32_t>, + &m_joystick.axes[axis]); + } + + // loop over all buttons + for (int button = 0; (button < MAX_BUTTONS) && (button < SDL_JoystickNumButtons(m_joydevice)); button++) + { + input_item_id itemid; + + m_joystick.buttons[button] = 0; + + if (button < INPUT_MAX_BUTTONS) + itemid = input_item_id(ITEM_ID_BUTTON1 + button); + else if (button < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) + itemid = input_item_id(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + button); + else + itemid = ITEM_ID_OTHER_SWITCH; + + device()->add_item( + default_button_name(button), + itemid, + generic_button_get_state<std::int32_t>, + &m_joystick.buttons[button]); + } + + // loop over all hats + for (int hat = 0; (hat < MAX_HATS) && (hat < SDL_JoystickNumHats(m_joydevice)); hat++) + { + input_item_id itemid; + + snprintf(tempname, sizeof(tempname), "Hat %d Up", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1UP + 4 * hat : ITEM_ID_OTHER_SWITCH); + device()->add_item( + tempname, + itemid, + generic_button_get_state<std::int32_t>, + &m_joystick.hatsU[hat]); + + snprintf(tempname, sizeof(tempname), "Hat %d Down", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1DOWN + 4 * hat : ITEM_ID_OTHER_SWITCH); + device()->add_item( + tempname, + itemid, + generic_button_get_state<std::int32_t>, + &m_joystick.hatsD[hat]); + + snprintf(tempname, sizeof(tempname), "Hat %d Left", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1LEFT + 4 * hat : ITEM_ID_OTHER_SWITCH); + device()->add_item( + tempname, + itemid, + generic_button_get_state<std::int32_t>, + &m_joystick.hatsL[hat]); + + snprintf(tempname, sizeof(tempname), "Hat %d Right", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1RIGHT + 4 * hat : ITEM_ID_OTHER_SWITCH); + device()->add_item( + tempname, + itemid, + generic_button_get_state<std::int32_t>, + &m_joystick.hatsR[hat]); + } + + // loop over all (track)balls + for (int ball = 0; (ball < (MAX_AXES / 2)) && (ball < SDL_JoystickNumBalls(m_joydevice)); ball++) + { + int itemid; + + if (ball * 2 < INPUT_MAX_ADD_RELATIVE) + itemid = ITEM_ID_ADD_RELATIVE1 + ball * 2; + else + itemid = ITEM_ID_OTHER_AXIS_RELATIVE; + + snprintf(tempname, sizeof(tempname), "R%d X", ball + 1); + device()->add_item( + tempname, + input_item_id(itemid), + generic_axis_get_state<std::int32_t>, + &m_joystick.balls[ball * 2]); + + snprintf(tempname, sizeof(tempname), "R%d Y", ball + 1); + device()->add_item( + tempname, input_item_id(itemid + 1), + generic_axis_get_state<std::int32_t>, + &m_joystick.balls[ball * 2 + 1]); + } } ~sdl_joystick_device() { - if (sdl_state.device) + if (m_joydevice) { - if (sdl_state.hapdevice) + if (m_hapdevice) { - SDL_HapticClose(sdl_state.hapdevice); - sdl_state.hapdevice = nullptr; + SDL_HapticClose(m_hapdevice); + m_hapdevice = nullptr; } - SDL_JoystickClose(sdl_state.device); - sdl_state.device = nullptr; + SDL_JoystickClose(m_joydevice); + m_joydevice = nullptr; } } - void reset() override + virtual void reset() override { - memset(&joystick, 0, sizeof(joystick)); + memset(&m_joystick, 0, sizeof(m_joystick)); } - void process_event(SDL_Event &sdlevent) override + virtual void process_event(SDL_Event &sdlevent) override { switch (sdlevent.type) { case SDL_JOYAXISMOTION: - joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2); + if (sdlevent.jaxis.axis < MAX_AXES) + m_joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2); break; case SDL_JOYBALLMOTION: //printf("Ball %d %d\n", sdlevent.jball.xrel, sdlevent.jball.yrel); - joystick.balls[sdlevent.jball.ball * 2] = sdlevent.jball.xrel * osd::INPUT_RELATIVE_PER_PIXEL; - joystick.balls[sdlevent.jball.ball * 2 + 1] = sdlevent.jball.yrel * osd::INPUT_RELATIVE_PER_PIXEL; + if (sdlevent.jball.ball < (MAX_AXES / 2)) + { + m_joystick.balls[sdlevent.jball.ball * 2] = sdlevent.jball.xrel * osd::INPUT_RELATIVE_PER_PIXEL; + m_joystick.balls[sdlevent.jball.ball * 2 + 1] = sdlevent.jball.yrel * osd::INPUT_RELATIVE_PER_PIXEL; + } break; case SDL_JOYHATMOTION: - joystick.hatsU[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_UP) ? 0x80 : 0; - joystick.hatsD[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_DOWN) ? 0x80 : 0; - joystick.hatsL[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_LEFT) ? 0x80 : 0; - joystick.hatsR[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_RIGHT) ? 0x80 : 0; + if (sdlevent.jhat.hat < MAX_HATS) + { + m_joystick.hatsU[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_UP) ? 0x80 : 0; + m_joystick.hatsD[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_DOWN) ? 0x80 : 0; + m_joystick.hatsL[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_LEFT) ? 0x80 : 0; + m_joystick.hatsR[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_RIGHT) ? 0x80 : 0; + } break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: if (sdlevent.jbutton.button < MAX_BUTTONS) - joystick.buttons[sdlevent.jbutton.button] = (sdlevent.jbutton.state == SDL_PRESSED) ? 0x80 : 0; + m_joystick.buttons[sdlevent.jbutton.button] = (sdlevent.jbutton.state == SDL_PRESSED) ? 0x80 : 0; break; case SDL_JOYDEVICEREMOVED: osd_printf_verbose("Joystick: %s [GUID %s] disconnected\n", name(), id()); + clear_instance(); reset(); - if (sdl_state.device) + if (m_joydevice) { - if (sdl_state.hapdevice) + if (m_hapdevice) { - SDL_HapticClose(sdl_state.hapdevice); - sdl_state.hapdevice = nullptr; + SDL_HapticClose(m_hapdevice); + m_hapdevice = nullptr; } - SDL_JoystickClose(sdl_state.device); - sdl_state.device = nullptr; + SDL_JoystickClose(m_joydevice); + m_joydevice = nullptr; } break; } } + + bool has_haptic() const + { + return m_hapdevice != nullptr; + } + + bool reconnect_match(std::string_view g, char const *s) const + { + return + !m_joydevice && + (id() == g) && + ((s && serial() && (*serial() == s)) || (!s && !serial())); + } + + void attach_device(SDL_Joystick *joy) + { + assert(joy); + assert(!m_joydevice); + + set_instance(SDL_JoystickInstanceID(joy)); + m_joydevice = joy; + m_hapdevice = SDL_HapticOpenFromJoystick(joy); + } + +protected: + // state information for a joystick + struct sdl_joystick_state + { + int32_t axes[MAX_AXES]; + int32_t buttons[MAX_BUTTONS]; + int32_t hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS]; + int32_t balls[MAX_AXES]; + }; + + sdl_joystick_state m_joystick; + +private: + SDL_Joystick *m_joydevice; + SDL_Haptic *m_hapdevice; }; +//============================================================ +// sdl_sixaxis_joystick_device +//============================================================ + class sdl_sixaxis_joystick_device : public sdl_joystick_device { public: - sdl_sixaxis_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - sdl_joystick_device(machine, std::move(name), std::move(id), module) - { - } + using sdl_joystick_device::sdl_joystick_device; - void process_event(SDL_Event &sdlevent) override + virtual void process_event(SDL_Event &sdlevent) override { switch (sdlevent.type) { @@ -728,12 +1058,12 @@ public: int const axis = sdlevent.jaxis.axis; if (axis <= 3) { - joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2); + m_joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2); } else { int const magic = (sdlevent.jaxis.value / 2) + 16384; - joystick.axes[sdlevent.jaxis.axis] = magic; + m_joystick.axes[sdlevent.jaxis.axis] = magic; } } break; @@ -747,13 +1077,315 @@ public: }; //============================================================ +// sdl_game_controller_device +//============================================================ + +class sdl_game_controller_device : public sdl_joystick_device_base +{ +public: + sdl_game_controller_device( + running_machine &machine, + std::string &&name, + std::string &&id, + input_module &module, + SDL_GameController *ctrl, + char const *serial) : + sdl_joystick_device_base( + machine, + std::move(name), + std::move(id), + module, + serial), + m_controller({{0}}), + m_ctrldevice(ctrl) + { + set_instance(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl))); + } + + ~sdl_game_controller_device() + { + if (m_ctrldevice) + { + SDL_GameControllerClose(m_ctrldevice); + m_ctrldevice = nullptr; + } + } + + void configure() + { + char const *const *axisnames = CONTROLLER_AXIS_XBOX; + char const *const *buttonnames = CONTROLLER_BUTTON_XBOX360; +#if SDL_VERSION_ATLEAST(2, 0, 12) + auto const ctrltype = SDL_GameControllerGetType(m_ctrldevice); + switch (ctrltype) + { + case SDL_CONTROLLER_TYPE_UNKNOWN: + osd_printf_verbose("Game Controller: ... unknown type\n", int(ctrltype)); + break; + case SDL_CONTROLLER_TYPE_XBOX360: + osd_printf_verbose("Game Controller: ... Xbox 360 type\n"); + axisnames = CONTROLLER_AXIS_XBOX; + buttonnames = CONTROLLER_BUTTON_XBOX360; + break; + case SDL_CONTROLLER_TYPE_XBOXONE: + osd_printf_verbose("Game Controller: ... Xbox One type\n"); + axisnames = CONTROLLER_AXIS_XBOX; + buttonnames = CONTROLLER_BUTTON_XBOXONE; + break; + case SDL_CONTROLLER_TYPE_PS3: + osd_printf_verbose("Game Controller: ... PlayStation 3 type\n"); + axisnames = CONTROLLER_AXIS_PS; + buttonnames = CONTROLLER_BUTTON_PS3; + break; + case SDL_CONTROLLER_TYPE_PS4: + osd_printf_verbose("Game Controller: ... PlayStation 4 type\n"); + axisnames = CONTROLLER_AXIS_PS; + buttonnames = CONTROLLER_BUTTON_PS4; + break; + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: + osd_printf_verbose("Game Controller: ... Switch Pro Controller type\n"); + axisnames = CONTROLLER_AXIS_SWITCH; + buttonnames = CONTROLLER_BUTTON_SWITCH; + break; +#if SDL_VERSION_ATLEAST(2, 0, 14) // TODO: support more controller types + //case SDL_CONTROLLER_TYPE_VIRTUAL: + //case SDL_CONTROLLER_TYPE_PS5: + //case SDL_CONTROLLER_TYPE_AMAZON_LUNA: + //case SDL_CONTROLLER_TYPE_GOOGLE_STADIA: + //case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_LEFT: + //case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_RIGHT: + //case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_PAIR: +#endif + default: // default to Xbox 360 names + osd_printf_verbose("Game Controller: ... unrecognised type (%d)\n", int(ctrltype)); + break; + } +#endif + + // add axes + std::tuple<SDL_GameControllerAxis, input_item_id, bool> 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 }, + { SDL_CONTROLLER_AXIS_RIGHTY, ITEM_ID_RZAXIS, false }, + { SDL_CONTROLLER_AXIS_TRIGGERLEFT, ITEM_ID_SLIDER1, true }, + { SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ITEM_ID_SLIDER2, true } }; + for (auto [axis, item, buttontest] : axes) + { + bool avail = true; +#if SDL_VERSION_ATLEAST(2, 0, 14) + avail = SDL_GameControllerHasAxis(m_ctrldevice, axis); +#endif + if (avail) + { + auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_NONE: + avail = false; + break; + case SDL_CONTROLLER_BINDTYPE_BUTTON: + if (buttontest) + avail = false; + break; + default: + break; + } + } + if (avail) + { + device()->add_item( + axisnames[axis], + item, + generic_axis_get_state<std::int32_t>, + &m_controller.axes[axis]); + } + } + + // add automatically numbered buttons + std::pair<SDL_GameControllerButton, SDL_GameControllerAxis> 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 }, + { SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERLEFT }, + { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERRIGHT }, + { SDL_CONTROLLER_BUTTON_LEFTSTICK, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_AXIS_INVALID }, +#if SDL_VERSION_ATLEAST(2, 0, 14) + { SDL_CONTROLLER_BUTTON_PADDLE1, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_PADDLE2, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_PADDLE3, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_PADDLE4, SDL_CONTROLLER_AXIS_INVALID }, +#endif + { SDL_CONTROLLER_BUTTON_GUIDE, SDL_CONTROLLER_AXIS_INVALID }, +#if SDL_VERSION_ATLEAST(2, 0, 14) + { SDL_CONTROLLER_BUTTON_MISC1, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_TOUCHPAD, SDL_CONTROLLER_AXIS_INVALID }, +#endif + }; + input_item_id button_item = ITEM_ID_BUTTON1; + for (auto [button, axis] : numberedbuttons) + { + bool avail = true; + if (SDL_CONTROLLER_BUTTON_INVALID != button) + { +#if SDL_VERSION_ATLEAST(2, 0, 14) + avail = SDL_GameControllerHasButton(m_ctrldevice, button); +#endif + if (avail) + { + auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_NONE: + avail = false; + break; + default: + break; + } + } + if (avail) + { + device()->add_item( + buttonnames[button], + button_item++, + generic_button_get_state<std::int32_t>, + &m_controller.buttons[button]); + } + } + else + { +#if SDL_VERSION_ATLEAST(2, 0, 14) + avail = SDL_GameControllerHasAxis(m_ctrldevice, axis); +#endif + if (avail) + { + auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_BUTTON: + break; + default: + avail = false; + } + } + if (avail) + { + device()->add_item( + axisnames[axis], + button_item++, + [] (void *device_internal, void *item_internal) -> int + { + return (*reinterpret_cast<int32_t const *>(item_internal) <= -16'384) ? 1 : 0; + }, + &m_controller.axes[axis]); + } + } + } + + // add buttons with fixed item IDs + std::pair<SDL_GameControllerButton, input_item_id> fixedbuttons[]{ + { SDL_CONTROLLER_BUTTON_BACK, ITEM_ID_SELECT }, + { SDL_CONTROLLER_BUTTON_START, ITEM_ID_START }, + { SDL_CONTROLLER_BUTTON_DPAD_UP, ITEM_ID_HAT1UP }, + { SDL_CONTROLLER_BUTTON_DPAD_DOWN, ITEM_ID_HAT1DOWN }, + { SDL_CONTROLLER_BUTTON_DPAD_LEFT, ITEM_ID_HAT1LEFT }, + { SDL_CONTROLLER_BUTTON_DPAD_RIGHT, ITEM_ID_HAT1RIGHT } }; + for (auto [button, item] : fixedbuttons) + { + bool avail = true; +#if SDL_VERSION_ATLEAST(2, 0, 14) + avail = SDL_GameControllerHasButton(m_ctrldevice, button); +#endif + if (avail) + { + auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_NONE: + avail = false; + break; + default: + break; + } + } + if (avail) + { + device()->add_item( + buttonnames[button], + item, + generic_button_get_state<std::int32_t>, + &m_controller.buttons[button]); + } + } + } + + virtual void reset() override + { + memset(&m_controller, 0, sizeof(m_controller)); + } + + virtual void process_event(SDL_Event &sdlevent) override + { + switch (sdlevent.type) + { + case SDL_CONTROLLERAXISMOTION: + if (sdlevent.caxis.axis < SDL_CONTROLLER_AXIS_MAX) + { + switch (sdlevent.caxis.axis) + { + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: // MAME wants negative values for triggers + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + m_controller.axes[sdlevent.caxis.axis] = -normalize_absolute_axis(sdlevent.caxis.value, -32'767, 32'767); + break; + default: + m_controller.axes[sdlevent.caxis.axis] = normalize_absolute_axis(sdlevent.caxis.value, -32'767, 32'767); + } + } + break; + + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + if (sdlevent.cbutton.button < SDL_CONTROLLER_BUTTON_MAX) + m_controller.buttons[sdlevent.cbutton.button] = (sdlevent.cbutton.state == SDL_PRESSED) ? 0x80 : 0x00; + break; + + case SDL_CONTROLLERDEVICEREMOVED: + osd_printf_verbose("Game Controller: %s [GUID %s] disconnected\n", name(), id()); + clear_instance(); + reset(); + if (m_ctrldevice) + { + SDL_GameControllerClose(m_ctrldevice); + m_ctrldevice = nullptr; + } + break; + } + } + +private: + // state information for a game controller + struct sdl_controller_state + { + int32_t axes[SDL_CONTROLLER_AXIS_MAX]; + int32_t buttons[SDL_CONTROLLER_BUTTON_MAX]; + }; + + sdl_controller_state m_controller; + SDL_GameController *m_ctrldevice; +}; + +//============================================================ // sdl_input_module //============================================================ class sdl_input_module : public input_module_base, public sdl_event_subscriber { public: - sdl_input_module(const char *type) : input_module_base(type, "sdl") + sdl_input_module(char const *type, char const *name) : input_module_base(type, name) { } @@ -804,7 +1436,7 @@ class sdl_keyboard_module : public sdl_input_module keyboard_trans_table * m_key_trans_table; public: sdl_keyboard_module() : - sdl_input_module(OSD_KEYBOARDINPUT_PROVIDER), + sdl_input_module(OSD_KEYBOARDINPUT_PROVIDER, "sdl"), m_key_trans_table(nullptr) { } @@ -936,7 +1568,7 @@ private: class sdl_mouse_module : public sdl_input_module { public: - sdl_mouse_module() : sdl_input_module(OSD_MOUSEINPUT_PROVIDER) + sdl_mouse_module() : sdl_input_module(OSD_MOUSEINPUT_PROVIDER, "sdl") { } @@ -979,16 +1611,11 @@ public: class sdl_joystick_module : public sdl_input_module { -private: - bool m_initialized_joystick; - bool m_initialized_haptic; - bool m_sixaxis_mode; public: sdl_joystick_module() : - sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER), + sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER, "sdljoy"), m_initialized_joystick(false), - m_initialized_haptic(false), - m_sixaxis_mode(false) + m_initialized_haptic(false) { } @@ -997,14 +1624,10 @@ public: sdl_input_module::exit(); if (m_initialized_joystick) - { SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - } if (m_initialized_haptic) - { SDL_QuitSubSystem(SDL_INIT_HAPTIC); - } } virtual void input_init(running_machine &machine) override @@ -1026,9 +1649,7 @@ public: sdl_input_module::input_init(machine); - char tempname[512]; - - m_sixaxis_mode = downcast<const sdl_options *>(options())->sixaxis(); + bool const sixaxis_mode = downcast<const sdl_options *>(options())->sixaxis(); osd_printf_verbose("Joystick: Start initialization\n"); for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) @@ -1047,9 +1668,9 @@ public: if (serial) id.append(1, '-').append(serial); - sdl_joystick_device *const devinfo = m_sixaxis_mode - ? &devicelist().create_device<sdl_sixaxis_joystick_device>(machine, name ? name : guid_str, guid_str, *this) - : &devicelist().create_device<sdl_joystick_device>(machine, name ? name : guid_str, guid_str, *this); + sdl_joystick_device *const devinfo = sixaxis_mode + ? &devicelist().create_device<sdl_sixaxis_joystick_device>(machine, name ? name : guid_str, guid_str, *this, joy, serial) + : &devicelist().create_device<sdl_joystick_device>(machine, name ? name : guid_str, guid_str, *this, joy, serial); if (!devinfo) { @@ -1057,12 +1678,6 @@ public: continue; } - devinfo->sdl_state.device = joy; - devinfo->sdl_state.joystick_id = SDL_JoystickInstanceID(joy); - devinfo->sdl_state.hapdevice = SDL_HapticOpenFromJoystick(joy); - if (serial) - devinfo->sdl_state.serial = serial; - osd_printf_verbose("Joystick: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n", name ? name : "<nullptr>", guid_str, @@ -1075,80 +1690,14 @@ public: SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy), SDL_JoystickNumBalls(joy)); - if (devinfo->sdl_state.hapdevice) + if (devinfo->has_haptic()) osd_printf_verbose("Joystick: ... Has haptic capability\n"); else osd_printf_verbose("Joystick: ... Does not have haptic capability\n"); - - // loop over all axes - for (int axis = 0; axis < SDL_JoystickNumAxes(joy); axis++) - { - input_item_id itemid; - - if (axis < INPUT_MAX_AXIS) - itemid = (input_item_id)(ITEM_ID_XAXIS + axis); - else if (axis < INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE) - itemid = (input_item_id)(ITEM_ID_ADD_ABSOLUTE1 - INPUT_MAX_AXIS + axis); - else - itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE; - - snprintf(tempname, sizeof(tempname), "A%d", axis + 1); - devinfo->device()->add_item(tempname, itemid, generic_axis_get_state<std::int32_t>, &devinfo->joystick.axes[axis]); - } - - // loop over all buttons if (SDL_JoystickNumButtons(joy) > MAX_BUTTONS) osd_printf_verbose("Joystick: ... Has %d buttons which exceeds supported %d buttons\n", SDL_JoystickNumButtons(joy), MAX_BUTTONS); - for (int button = 0; (button < MAX_BUTTONS) && (button < SDL_JoystickNumButtons(joy)); button++) - { - input_item_id itemid; - - devinfo->joystick.buttons[button] = 0; - - if (button < INPUT_MAX_BUTTONS) - itemid = (input_item_id)(ITEM_ID_BUTTON1 + button); - else if (button < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) - itemid = (input_item_id)(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + button); - else - itemid = ITEM_ID_OTHER_SWITCH; - - devinfo->device()->add_item(default_button_name(button), itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.buttons[button]); - } - - // loop over all hats - for (int hat = 0; hat < SDL_JoystickNumHats(joy); hat++) - { - input_item_id itemid; - - snprintf(tempname, sizeof(tempname), "Hat %d Up", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1UP + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsU[hat]); - snprintf(tempname, sizeof(tempname), "Hat %d Down", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1DOWN + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsD[hat]); - snprintf(tempname, sizeof(tempname), "Hat %d Left", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1LEFT + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsL[hat]); - snprintf(tempname, sizeof(tempname), "Hat %d Right", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1RIGHT + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsR[hat]); - } - - // loop over all (track)balls - for (int ball = 0; ball < SDL_JoystickNumBalls(joy); ball++) - { - int itemid; - if (ball * 2 < INPUT_MAX_ADD_RELATIVE) - itemid = ITEM_ID_ADD_RELATIVE1 + ball * 2; - else - itemid = ITEM_ID_OTHER_AXIS_RELATIVE; - - snprintf(tempname, sizeof(tempname), "R%d X", ball + 1); - devinfo->device()->add_item(tempname, (input_item_id)itemid, generic_axis_get_state<std::int32_t>, &devinfo->joystick.balls[ball * 2]); - snprintf(tempname, sizeof(tempname), "R%d Y", ball + 1); - devinfo->device()->add_item(tempname, (input_item_id)(itemid + 1), generic_axis_get_state<std::int32_t>, &devinfo->joystick.balls[ball * 2 + 1]); - } + devinfo->configure(); } static int const event_types[] = { @@ -1186,17 +1735,12 @@ public: [&guid_str, &serial] (auto const &device) { auto &devinfo = downcast<sdl_joystick_device &>(*device); - return - !devinfo.sdl_state.device && - (devinfo.id() == guid_str) && - ((serial && devinfo.sdl_state.serial && (*devinfo.sdl_state.serial == serial)) || (!serial && !devinfo.sdl_state.serial)); + return devinfo.reconnect_match(guid_str, serial); }); if (devicelist().end() != target_device) { auto &devinfo = downcast<sdl_joystick_device &>(**target_device); - devinfo.sdl_state.device = joy; - devinfo.sdl_state.joystick_id = SDL_JoystickInstanceID(joy); - devinfo.sdl_state.hapdevice = SDL_HapticOpenFromJoystick(joy); + devinfo.attach_device(joy); osd_printf_verbose("Joystick: %s [GUID %s] reconnected\n", devinfo.name(), guid_str); } else @@ -1207,10 +1751,10 @@ public: } else { - // Figure out which joystick this event id destined for + // figure out which joystick this event is destined for sdl_joystick_device *const target_device = find_joystick(sdlevent.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32 - // If we find a matching joystick, dispatch the event to the joystick + // if we find a matching joystick, dispatch the event to the joystick if (target_device) target_device->queue_events(&sdlevent, 1); } @@ -1222,11 +1766,128 @@ private: for (auto &ptr : devicelist()) { sdl_joystick_device *const device = downcast<sdl_joystick_device *>(ptr.get()); - if (device->sdl_state.device && (device->sdl_state.joystick_id == instance)) + if (device->is_instance(instance)) return device; } return nullptr; } + + bool m_initialized_joystick; + bool m_initialized_haptic; +}; + + +//============================================================ +// sdl_game_controller_module +//============================================================ + +class sdl_game_controller_module : public sdl_input_module +{ +public: + sdl_game_controller_module() : + sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER, "sdlgame"), + m_initialized_game_controller(false) + { + } + + virtual void exit() override + { + sdl_input_module::exit(); + + if (m_initialized_game_controller) + SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); + } + + virtual void input_init(running_machine &machine) override + { + m_initialized_game_controller = !SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); + if (!m_initialized_game_controller) + { + osd_printf_error("Could not initialize SDL Game Controller: %s.\n", SDL_GetError()); + return; + } + + sdl_input_module::input_init(machine); + + osd_printf_verbose("Game Controller: Start initialization\n"); + for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) + { + if (!SDL_IsGameController(physical_stick)) + continue; + + SDL_GameController *const ctrl = SDL_GameControllerOpen(physical_stick); + char const *const name = SDL_GameControllerName(ctrl); + SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(physical_stick); + char guid_str[256]; + guid_str[0] = '\0'; + SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1); + char const *serial = nullptr; +#if SDL_VERSION_ATLEAST(2, 0, 14) + serial = SDL_GameControllerGetSerial(ctrl); +#endif + std::string id(guid_str); + if (serial) + id.append(1, '-').append(serial); + + sdl_game_controller_device *const devinfo = &devicelist().create_device<sdl_game_controller_device>( + machine, + name ? name : guid_str, + guid_str, + *this, + ctrl, + serial); + + if (!devinfo) + { + SDL_GameControllerClose(ctrl); + continue; + } + + osd_printf_verbose("Game Controller: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n", + name ? name : "<nullptr>", + guid_str, + SDL_GameControllerGetVendor(ctrl), + SDL_GameControllerGetProduct(ctrl), + SDL_GameControllerGetProductVersion(ctrl), + serial ? serial : "<nullptr>"); + + devinfo->configure(); + } + + static int const event_types[] = { + int(SDL_CONTROLLERAXISMOTION), + int(SDL_CONTROLLERBUTTONDOWN), + int(SDL_CONTROLLERBUTTONUP), + int(SDL_CONTROLLERDEVICEREMOVED) }; + + sdl_event_manager::instance().subscribe(event_types, this); + + osd_printf_verbose("Joystick: End initialization\n"); + } + + virtual void handle_event(SDL_Event &sdlevent) override + { + // figure out which game controller this event is destined for + sdl_game_controller_device *const target_device = find_controller(sdlevent.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32 + + // if we find a matching controller, dispatch the event to the game controller + if (target_device) + target_device->queue_events(&sdlevent, 1); + } + +private: + sdl_game_controller_device *find_controller(SDL_JoystickID instance) + { + for (auto &ptr : devicelist()) + { + sdl_game_controller_device *const device = downcast<sdl_game_controller_device *>(ptr.get()); + if (device->is_instance(instance)) + return device; + } + return nullptr; + } + + bool m_initialized_game_controller; }; } // anonymous namespace @@ -1237,10 +1898,12 @@ private: MODULE_NOT_SUPPORTED(sdl_keyboard_module, OSD_KEYBOARDINPUT_PROVIDER, "sdl") MODULE_NOT_SUPPORTED(sdl_mouse_module, OSD_MOUSEINPUT_PROVIDER, "sdl") -MODULE_NOT_SUPPORTED(sdl_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "sdl") +MODULE_NOT_SUPPORTED(sdl_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "sdljoy") +MODULE_NOT_SUPPORTED(sdl_game_controller_module, OSD_JOYSTICKINPUT_PROVIDER, "sdlgame") #endif // defined(SDLMAME_SDL2) MODULE_DEFINITION(KEYBOARDINPUT_SDL, sdl_keyboard_module) MODULE_DEFINITION(MOUSEINPUT_SDL, sdl_mouse_module) -MODULE_DEFINITION(JOYSTICKINPUT_SDL, sdl_joystick_module) +MODULE_DEFINITION(JOYSTICKINPUT_SDLJOY, sdl_joystick_module) +MODULE_DEFINITION(JOYSTICKINPUT_SDLGAME, sdl_game_controller_module) |