diff options
Diffstat (limited to 'src/osd/modules/input/input_sdl.cpp')
-rw-r--r-- | src/osd/modules/input/input_sdl.cpp | 553 |
1 files changed, 520 insertions, 33 deletions
diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp index 6def55c5cc0..68779a151e4 100644 --- a/src/osd/modules/input/input_sdl.cpp +++ b/src/osd/modules/input/input_sdl.cpp @@ -17,6 +17,7 @@ #if defined(OSD_SDL) +#include "assignmenthelper.h" #include "input_common.h" #include "interface/inputseq.h" @@ -24,7 +25,7 @@ #include "sdl/osdsdl.h" // emu -#include "emu.h" +#include "inpttype.h" // standard SDL header #include <SDL2/SDL.h> @@ -759,7 +760,7 @@ private: // sdl_joystick_device_base //============================================================ -class sdl_joystick_device_base : public sdl_device +class sdl_joystick_device_base : public sdl_device, protected joystick_assignment_helper { public: std::optional<std::string> const &serial() const { return m_serial; } @@ -834,6 +835,7 @@ public: virtual void configure(input_device &device) override { + input_device::assignment_vector assignments; char tempname[32]; int const axiscount = SDL_JoystickNumAxes(m_joydevice); @@ -842,6 +844,7 @@ public: int const ballcount = SDL_JoystickNumBalls(m_joydevice); // loop over all axes + input_item_id axisactual[MAX_AXES]; for (int axis = 0; (axis < MAX_AXES) && (axis < axiscount); axis++) { input_item_id itemid; @@ -854,7 +857,7 @@ public: itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE; snprintf(tempname, sizeof(tempname), "A%d", axis + 1); - device.add_item( + axisactual[axis] = device.add_item( tempname, std::string_view(), itemid, @@ -876,22 +879,50 @@ public: else itemid = ITEM_ID_OTHER_SWITCH; - device.add_item( + input_item_id const actual = device.add_item( default_button_name(button), std::string_view(), itemid, generic_button_get_state<s32>, &m_joystick.buttons[button]); + + // there are sixteen action button types + if (button < 16) + { + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, actual)); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + button), SEQ_TYPE_STANDARD, seq); + + // assign the first few buttons to UI actions and pedals + switch (button) + { + case 0: + assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back(IPT_UI_SELECT, SEQ_TYPE_STANDARD, seq); + break; + case 1: + assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back((3 > buttoncount) ? IPT_UI_CLEAR : IPT_UI_BACK, SEQ_TYPE_STANDARD, seq); + break; + case 2: + assignments.emplace_back(IPT_PEDAL3, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back(IPT_UI_CLEAR, SEQ_TYPE_STANDARD, seq); + break; + case 3: + assignments.emplace_back(IPT_UI_HELP, SEQ_TYPE_STANDARD, seq); + break; + } + } } // loop over all hats + input_item_id hatactual[MAX_HATS][4]; for (int hat = 0; (hat < MAX_HATS) && (hat < hatcount); 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( + hatactual[hat][0] = device.add_item( tempname, std::string_view(), itemid, @@ -900,7 +931,7 @@ public: 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( + hatactual[hat][1] = device.add_item( tempname, std::string_view(), itemid, @@ -909,7 +940,7 @@ public: 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( + hatactual[hat][2] = device.add_item( tempname, std::string_view(), itemid, @@ -918,7 +949,7 @@ public: 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( + hatactual[hat][3] = device.add_item( tempname, std::string_view(), itemid, @@ -937,7 +968,7 @@ public: itemid = ITEM_ID_OTHER_AXIS_RELATIVE; snprintf(tempname, sizeof(tempname), "R%d X", ball + 1); - device.add_item( + input_item_id const xactual = device.add_item( tempname, std::string_view(), input_item_id(itemid), @@ -945,13 +976,130 @@ public: &m_joystick.balls[ball * 2]); snprintf(tempname, sizeof(tempname), "R%d Y", ball + 1); - device.add_item( + input_item_id const yactual = device.add_item( tempname, std::string_view(), input_item_id(itemid + 1), generic_axis_get_state<s32>, &m_joystick.balls[ball * 2 + 1]); + + if (0 == ball) + { + // assign the first trackball to dial, trackball, mouse and lightgun inputs + input_seq const xseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, xactual)); + input_seq const yseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, yactual)); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_MOUSE_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_MOUSE_Y, SEQ_TYPE_STANDARD, yseq); + if (2 > axiscount) + { + // use it for joystick inputs if axes are limited + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_STANDARD, yseq); + } + else + { + // use for non-centring throttle control + assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, yseq); + } + } + else if ((1 == ball) && (2 > axiscount)) + { + // provide a non-centring throttle control + input_seq const yseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, yactual)); + assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, yseq); + } + } + + // set up default assignments for axes and hats + add_directional_assignments( + assignments, + (1 <= axiscount) ? axisactual[0] : ITEM_ID_INVALID, // assume first axis is X + (2 <= axiscount) ? axisactual[1] : ITEM_ID_INVALID, // assume second axis is Y + (1 <= hatcount) ? hatactual[0][2] : ITEM_ID_INVALID, + (1 <= hatcount) ? hatactual[0][3] : ITEM_ID_INVALID, + (1 <= hatcount) ? hatactual[0][0] : ITEM_ID_INVALID, + (1 <= hatcount) ? hatactual[0][1] : ITEM_ID_INVALID); + if (2 <= axiscount) + { + // put pedals on the last of the second, third or fourth axis + input_item_id const pedalitem = axisactual[(std::min)(axiscount, 4) - 1]; + assignments.emplace_back( + IPT_PEDAL, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitem))); + assignments.emplace_back( + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, pedalitem))); + } + if (3 <= axiscount) + { + // assign X/Y to one of the twin sticks + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_LEFT : IPT_JOYSTICKRIGHT_LEFT, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_LEFT, axisactual[0]))); + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_RIGHT : IPT_JOYSTICKRIGHT_RIGHT, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_RIGHT, axisactual[0]))); + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_UP : IPT_JOYSTICKRIGHT_UP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_UP, axisactual[1]))); + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_DOWN : IPT_JOYSTICKRIGHT_DOWN, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_DOWN, axisactual[1]))); + + // use third or fourth axis for Z + input_seq const seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axisactual[(std::min)(axiscount, 4) - 1])); + assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, seq); + + // use this for focus next/previous to make system selection menu practical to navigate + input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisactual[2])); + input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisactual[2])); + assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, downseq); + if (4 <= axiscount) + { + // use for zoom as well if there's another axis to use for previous/next group + assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, downseq); + assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, upseq); + } + + // use this for twin sticks, too + assignments.emplace_back((4 <= axiscount) ? IPT_JOYSTICKRIGHT_LEFT : IPT_JOYSTICKLEFT_UP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back((4 <= axiscount) ? IPT_JOYSTICKRIGHT_RIGHT : IPT_JOYSTICKLEFT_DOWN, SEQ_TYPE_STANDARD, downseq); + + // put previous/next group on the last of the third or fourth axis + input_item_id const groupitem = axisactual[(std::min)(axiscount, 4) - 1]; + assignments.emplace_back( + IPT_UI_PREV_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, groupitem))); + assignments.emplace_back( + IPT_UI_NEXT_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, groupitem))); + } + if (4 <= axiscount) + { + // use this for twin sticks + input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisactual[3])); + input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisactual[3])); + assignments.emplace_back(IPT_JOYSTICKRIGHT_UP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_JOYSTICKRIGHT_DOWN, SEQ_TYPE_STANDARD, downseq); } + + // set default assignments + device.set_default_assignments(std::move(assignments)); } ~sdl_joystick_device() @@ -1132,9 +1280,13 @@ public: virtual void configure(input_device &device) override { + input_device::assignment_vector assignments; char const *const *axisnames = CONTROLLER_AXIS_XBOX; char const *const *buttonnames = CONTROLLER_BUTTON_XBOX360; bool digitaltriggers = false; +#if SDL_VERSION_ATLEAST(2, 0, 14) + bool avoidpaddles = false; +#endif #if SDL_VERSION_ATLEAST(2, 0, 12) auto const ctrltype = SDL_GameControllerGetType(m_ctrldevice); switch (ctrltype) @@ -1197,6 +1349,7 @@ public: axisnames = CONTROLLER_AXIS_SWITCH; buttonnames = CONTROLLER_BUTTON_SWITCH; digitaltriggers = true; + avoidpaddles = true; break; #endif default: // default to Xbox 360 names @@ -1205,6 +1358,23 @@ public: } #endif + // keep track of item numbers as we add controls + std::pair<input_item_id, input_item_id> axisitems[SDL_CONTROLLER_AXIS_MAX]; + input_item_id buttonitems[SDL_CONTROLLER_BUTTON_MAX]; + std::tuple<input_item_id, SDL_GameControllerButton, SDL_GameControllerAxis> numberedbuttons[16]; + std::fill( + std::begin(axisitems), + std::end(axisitems), + std::make_pair(ITEM_ID_INVALID, ITEM_ID_INVALID)); + std::fill( + std::begin(buttonitems), + std::end(buttonitems), + ITEM_ID_INVALID); + std::fill( + std::begin(numberedbuttons), + std::end(numberedbuttons), + std::make_tuple(ITEM_ID_INVALID, SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_INVALID)); + // add axes std::tuple<SDL_GameControllerAxis, input_item_id, bool> const axes[]{ { SDL_CONTROLLER_AXIS_LEFTX, ITEM_ID_XAXIS, false }, @@ -1237,7 +1407,7 @@ public: } if (avail) { - device.add_item( + axisitems[axis].first = device.add_item( axisnames[axis], std::string_view(), item, @@ -1247,33 +1417,35 @@ public: } // add automatically numbered buttons - std::tuple<SDL_GameControllerButton, SDL_GameControllerAxis> const generalbuttons[]{ - { 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 }, + std::tuple<SDL_GameControllerButton, SDL_GameControllerAxis, bool> const generalbuttons[]{ + { SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERLEFT, true }, + { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, true }, + { SDL_CONTROLLER_BUTTON_LEFTSTICK, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_AXIS_INVALID, true }, #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 }, + { SDL_CONTROLLER_BUTTON_PADDLE1, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_PADDLE2, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_PADDLE3, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_PADDLE4, SDL_CONTROLLER_AXIS_INVALID, true }, #endif - { SDL_CONTROLLER_BUTTON_GUIDE, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_GUIDE, SDL_CONTROLLER_AXIS_INVALID, false }, #if SDL_VERSION_ATLEAST(2, 0, 14) - { SDL_CONTROLLER_BUTTON_MISC1, SDL_CONTROLLER_AXIS_INVALID }, - { SDL_CONTROLLER_BUTTON_TOUCHPAD, SDL_CONTROLLER_AXIS_INVALID }, + { SDL_CONTROLLER_BUTTON_MISC1, SDL_CONTROLLER_AXIS_INVALID, false }, + { SDL_CONTROLLER_BUTTON_TOUCHPAD, SDL_CONTROLLER_AXIS_INVALID, false }, #endif }; input_item_id button_item = ITEM_ID_BUTTON1; - for (auto [button, axis] : generalbuttons) + unsigned buttoncount = 0; + for (auto [button, axis, field] : generalbuttons) { bool avail = true; + input_item_id actual = ITEM_ID_INVALID; if (SDL_CONTROLLER_BUTTON_INVALID != button) { #if SDL_VERSION_ATLEAST(2, 0, 14) @@ -1293,12 +1465,14 @@ public: } if (avail) { - device.add_item( + actual = buttonitems[button] = device.add_item( buttonnames[button], std::string_view(), button_item++, generic_button_get_state<s32>, &m_controller.buttons[button]); + if (field && (std::size(numberedbuttons) > buttoncount)) + std::get<1>(numberedbuttons[buttoncount]) = button; } } else @@ -1322,7 +1496,7 @@ public: } if (avail) { - device.add_item( + actual = axisitems[axis].second = device.add_item( axisnames[axis], std::string_view(), button_item++, @@ -1332,8 +1506,17 @@ public: return (*reinterpret_cast<s32 const *>(item_internal) <= -16'384) ? 1 : 0; }, &m_controller.axes[axis]); + if (field && (std::size(numberedbuttons) > buttoncount)) + std::get<2>(numberedbuttons[buttoncount]) = axis; } } + + // add default button assignments + if (field && avail && (std::size(numberedbuttons) > buttoncount)) + { + std::get<0>(numberedbuttons[buttoncount]) = actual; + add_button_assignment(assignments, ioport_type(IPT_BUTTON1 + buttoncount++), { actual }); + } } // add buttons with fixed item IDs @@ -1364,7 +1547,7 @@ public: } if (avail) { - device.add_item( + buttonitems[button] = device.add_item( buttonnames[button], std::string_view(), item, @@ -1372,6 +1555,310 @@ public: &m_controller.buttons[button]); } } + + // try to get a "complete" joystick for primary movement controls + input_item_id diraxis[2][2]; + choose_primary_stick( + diraxis, + axisitems[SDL_CONTROLLER_AXIS_LEFTX].first, + axisitems[SDL_CONTROLLER_AXIS_LEFTY].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first); + + // now set up controls using the primary joystick + add_directional_assignments( + assignments, + diraxis[0][0], + diraxis[0][1], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_LEFT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_RIGHT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_UP], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_DOWN]); + + // assign a secondary stick axis to joystick Z if available + bool const zaxis = add_assignment( + assignments, + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NONE, + { diraxis[1][1], diraxis[1][0] }); + if (!zaxis) + { + // if both triggers are present, combine them, or failing that, fall back to a pair of buttons + if ((ITEM_ID_INVALID != axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first) && (ITEM_ID_INVALID != axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first)) + { + assignments.emplace_back( + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + input_seq( + make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first), + make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_REVERSE, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first))); + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER])) + { + // took shoulder buttons + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second)) + { + // took trigger buttons + } +#if SDL_VERSION_ATLEAST(2, 0, 14) + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2])) + { + // took P1/P2 + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4])) + { + // took P3/P4 + } +#endif + } + + // prefer trigger axes for pedals, otherwise take half axes and buttons + unsigned pedalbutton = 0; + if (!add_assignment(assignments, IPT_PEDAL, SEQ_TYPE_STANDARD, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, { axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first })) + { + add_assignment( + assignments, + IPT_PEDAL, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NEG, + { diraxis[1][1], diraxis[0][1] }); + bool const incbutton = add_assignment( + assignments, + IPT_PEDAL, + SEQ_TYPE_INCREMENT, + ITEM_CLASS_SWITCH, + ITEM_MODIFIER_NONE, + { axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second, buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] }); + if (!incbutton) + { + if (add_assignment(assignments, IPT_PEDAL, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) })) + ++pedalbutton; + } + } + if (!add_assignment(assignments, IPT_PEDAL2, SEQ_TYPE_STANDARD, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, { axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first })) + { + add_assignment( + assignments, + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_POS, + { diraxis[1][1], diraxis[0][1] }); + bool const incbutton = add_assignment( + assignments, + IPT_PEDAL2, + SEQ_TYPE_INCREMENT, + ITEM_CLASS_SWITCH, + ITEM_MODIFIER_NONE, + { axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] }); + if (!incbutton) + { + if (add_assignment(assignments, IPT_PEDAL2, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) })) + ++pedalbutton; + } + } + add_assignment(assignments, IPT_PEDAL3, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) }); + + // potentially use thumb sticks and/or D-pad and A/B/X/Y diamond for twin sticks + add_twin_stick_assignments( + assignments, + axisitems[SDL_CONTROLLER_AXIS_LEFTX].first, + axisitems[SDL_CONTROLLER_AXIS_LEFTY].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first, + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_LEFT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_RIGHT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_UP], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_DOWN], + buttonitems[SDL_CONTROLLER_BUTTON_X], + buttonitems[SDL_CONTROLLER_BUTTON_B], + buttonitems[SDL_CONTROLLER_BUTTON_Y], + buttonitems[SDL_CONTROLLER_BUTTON_A]); + + // add assignments for buttons with fixed functions + add_button_assignment(assignments, IPT_SELECT, { buttonitems[SDL_CONTROLLER_BUTTON_BACK] }); + add_button_assignment(assignments, IPT_START, { buttonitems[SDL_CONTROLLER_BUTTON_START] }); + add_button_assignment(assignments, IPT_UI_MENU, { buttonitems[SDL_CONTROLLER_BUTTON_GUIDE] }); + + // the first button is always UI select + if (add_button_assignment(assignments, IPT_UI_SELECT, { std::get<0>(numberedbuttons[0]) })) + { + if (SDL_CONTROLLER_BUTTON_INVALID != std::get<1>(numberedbuttons[0])) + buttonitems[std::get<1>(numberedbuttons[0])] = ITEM_ID_INVALID; + if (SDL_CONTROLLER_AXIS_INVALID != std::get<2>(numberedbuttons[0])) + axisitems[std::get<2>(numberedbuttons[0])].second = ITEM_ID_INVALID; + } + + // try to get a matching pair of buttons for previous/next group + if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second)) + { + // took digital triggers + } +#if SDL_VERSION_ATLEAST(2, 0, 14) + else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2])) + { + // took upper paddles + } +#endif + else if (consume_trigger_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first)) + { + // took analog triggers + } +#if SDL_VERSION_ATLEAST(2, 0, 14) + else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4])) + { + // took lower paddles + } +#endif + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][1])) + { + // took secondary Y + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][0])) + { + // took secondary X + } + + // try to get a matching pair of buttons for page up/down +#if SDL_VERSION_ATLEAST(2, 0, 14) + if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2])) + { + // took upper paddles + } + else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4])) + { + // took lower paddles + } + else +#endif + if (consume_trigger_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first)) + { + // took analog triggers + } + else if (consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, diraxis[1][1])) + { + // took secondary Y + } + + // try to assign X button to UI clear + if (add_button_assignment(assignments, IPT_UI_CLEAR, { buttonitems[SDL_CONTROLLER_BUTTON_X] })) + { + buttonitems[SDL_CONTROLLER_BUTTON_X] = ITEM_ID_INVALID; + } + else + { + // otherwise try to find an unassigned button + for (auto [item, button, axis] : numberedbuttons) + { + if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button])) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + buttonitems[button] = ITEM_ID_INVALID; + break; + } + else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second)) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + axisitems[axis].second = ITEM_ID_INVALID; + break; + } + } + } + + // try to assign B button to UI back + if (add_button_assignment(assignments, IPT_UI_BACK, { buttonitems[SDL_CONTROLLER_BUTTON_B] })) + { + buttonitems[SDL_CONTROLLER_BUTTON_X] = ITEM_ID_INVALID; + } + else + { + // otherwise try to find an unassigned button + for (auto [item, button, axis] : numberedbuttons) + { + if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button])) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + buttonitems[button] = ITEM_ID_INVALID; + break; + } + else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second)) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + axisitems[axis].second = ITEM_ID_INVALID; + break; + } + } + } + + // try to assign Y button to UI help + if (add_button_assignment(assignments, IPT_UI_HELP, { buttonitems[SDL_CONTROLLER_BUTTON_Y] })) + { + buttonitems[SDL_CONTROLLER_BUTTON_Y] = ITEM_ID_INVALID; + } + else + { + // otherwise try to find an unassigned button + for (auto [item, button, axis] : numberedbuttons) + { + if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button])) + { + add_button_assignment(assignments, IPT_UI_HELP, { item }); + buttonitems[button] = ITEM_ID_INVALID; + break; + } + else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second)) + { + add_button_assignment(assignments, IPT_UI_HELP, { item }); + axisitems[axis].second = ITEM_ID_INVALID; + break; + } + } + } + + // put focus previous/next on the shoulder buttons if available - this can be overloaded with zoom + if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER])) + { + // took shoulder buttons + } + else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][0])) + { + // took secondary X + } + else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][1])) + { + // took secondary Y + } + + // put zoom on the secondary stick if available, or fall back to shoulder buttons + if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, diraxis[1][0])) + { + // took secondary X + if (axisitems[SDL_CONTROLLER_AXIS_LEFTX].first == diraxis[1][0]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_LEFTSTICK] }); + else if (axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first == diraxis[1][0]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSTICK] }); + diraxis[1][0] = ITEM_ID_INVALID; + } + else if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_IN, IPT_UI_ZOOM_OUT, diraxis[1][1])) + { + // took secondary Y + if (axisitems[SDL_CONTROLLER_AXIS_LEFTY].first == diraxis[1][1]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_LEFTSTICK] }); + else if (axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first == diraxis[1][1]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSTICK] }); + diraxis[1][1] = ITEM_ID_INVALID; + } + else if (consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER])) + { + // took shoulder buttons + } + + // set default assignments + device.set_default_assignments(std::move(assignments)); } virtual void reset() override |