diff options
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) |