diff options
author | 2023-02-18 06:18:45 +1100 | |
---|---|---|
committer | 2023-02-18 06:18:45 +1100 | |
commit | d4589e0b29d6085072ff635d87c3d95c21813a58 (patch) | |
tree | c0d3283ecdd99bd380fc155f4e09b7f4bd573116 /src/osd/modules/input/input_sdl.cpp | |
parent | 20d7135179ffe9959f4bfb759a0e5735b7aaaf15 (diff) |
Input refactoring:
osd/modules/input, emu/inpttype.cpp: Made most default joystick
assignments supplied by input modules. Input modules take available
controls into consideration when generating default assignments.
emu/inpttype.ipp: Added a separate "Back" UI input separate from Cancel.
You may want an easier to hit combination for moving to the previous
menu than for exiting or cancelling input. They both default to Escape.
emu/inpttype.ipp: Added a UI Help control. Currently only used by
analog inputs menu
emu/inpttype.h: Moved I/O port field type enum to its own header and
sorted UI controls so they appear in a more logical order.
ui: Don't use UI Select to restore defaults - people should be getting
used to the UI Clear input by now. UI Select cycles multi-value items
instead.
ui/inputmap.cpp: Don't use immediate cancel to cycle between clearing
and restoring default assignment (use UI Clear instead).
osd: Reduced the number of files needing to include the dreaded emu.h.
Got some implementation out of headers.
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 |