input: allow binding controller buttons/axes to more than 1 player

Issue #68
This commit is contained in:
Flyinghead 2020-11-21 17:57:23 +01:00
parent 30b8c0d4d4
commit 4cb361bf9f
4 changed files with 317 additions and 212 deletions

View File

@ -55,188 +55,229 @@ bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed)
_input_detected = nullptr;
return true;
}
if (!input_mapper || _maple_port < 0 || _maple_port >= (int)ARRAY_SIZE(kcode))
return false;
DreamcastKey key = input_mapper->get_button_id(code);
if (key == EMU_BTN_NONE)
if (!input_mapper || _maple_port < 0 || _maple_port > (int)ARRAY_SIZE(kcode))
return false;
if (key < 0x10000)
auto handle_key = [&](u32 port, DreamcastKey key)
{
if (pressed)
if (key == EMU_BTN_NONE)
return false;
if (key < 0x10000)
{
kcode[_maple_port] &= ~(u16)key;
// Avoid two opposite dpad keys being pressed simultaneously
switch (key)
if (pressed)
{
case DC_DPAD_UP:
kcode[_maple_port] |= (u16)DC_DPAD_DOWN;
break;
case DC_DPAD_DOWN:
kcode[_maple_port] |= (u16)DC_DPAD_UP;
break;
case DC_DPAD_LEFT:
kcode[_maple_port] |= (u16)DC_DPAD_RIGHT;
break;
case DC_DPAD_RIGHT:
kcode[_maple_port] |= (u16)DC_DPAD_LEFT;
break;
case DC_DPAD2_UP:
kcode[_maple_port] |= (u16)DC_DPAD2_DOWN;
break;
case DC_DPAD2_DOWN:
kcode[_maple_port] |= (u16)DC_DPAD2_UP;
break;
case DC_DPAD2_LEFT:
kcode[_maple_port] |= (u16)DC_DPAD2_RIGHT;
break;
case DC_DPAD2_RIGHT:
kcode[_maple_port] |= (u16)DC_DPAD2_LEFT;
break;
default:
break;
kcode[port] &= ~(u16)key;
// Avoid two opposite dpad keys being pressed simultaneously
switch (key)
{
case DC_DPAD_UP:
kcode[port] |= (u16)DC_DPAD_DOWN;
break;
case DC_DPAD_DOWN:
kcode[port] |= (u16)DC_DPAD_UP;
break;
case DC_DPAD_LEFT:
kcode[port] |= (u16)DC_DPAD_RIGHT;
break;
case DC_DPAD_RIGHT:
kcode[port] |= (u16)DC_DPAD_LEFT;
break;
case DC_DPAD2_UP:
kcode[port] |= (u16)DC_DPAD2_DOWN;
break;
case DC_DPAD2_DOWN:
kcode[port] |= (u16)DC_DPAD2_UP;
break;
case DC_DPAD2_LEFT:
kcode[port] |= (u16)DC_DPAD2_RIGHT;
break;
case DC_DPAD2_RIGHT:
kcode[port] |= (u16)DC_DPAD2_LEFT;
break;
default:
break;
}
}
else
kcode[port] |= (u16)key;
#ifdef TEST_AUTOMATION
if (record_input != NULL)
fprintf(record_input, "%ld button %x %04x\n", sh4_sched_now64(), port, kcode[port]);
#endif
}
else
kcode[_maple_port] |= (u16)key;
#ifdef TEST_AUTOMATION
if (record_input != NULL)
fprintf(record_input, "%ld button %x %04x\n", sh4_sched_now64(), _maple_port, kcode[_maple_port]);
#endif
{
switch (key)
{
case EMU_BTN_ESCAPE:
if (pressed)
dc_exit();
break;
case EMU_BTN_MENU:
if (pressed)
gui_open_settings();
break;
case EMU_BTN_FFORWARD:
if (pressed)
fast_forward_mode = !fast_forward_mode;
break;
case EMU_BTN_TRIGGER_LEFT:
lt[port] = pressed ? 255 : 0;
break;
case EMU_BTN_TRIGGER_RIGHT:
rt[port] = pressed ? 255 : 0;
break;
case EMU_BTN_ANA_UP:
joyy[port] = pressed ? -128 : 0;
break;
case EMU_BTN_ANA_DOWN:
joyy[port] = pressed ? 127 : 0;
break;
case EMU_BTN_ANA_LEFT:
joyx[port] = pressed ? -128 : 0;
break;
case EMU_BTN_ANA_RIGHT:
joyx[port] = pressed ? 127 : 0;
break;
default:
return false;
}
}
DEBUG_LOG(INPUT, "%d: BUTTON %s %x -> %d. kcode=%x", port, pressed ? "down" : "up", code, key, kcode[port]);
return true;
};
bool rc = false;
if (_maple_port == 4)
{
for (int port = 0; port < 4; port++)
{
DreamcastKey key = input_mapper->get_button_id(port, code);
rc = handle_key(port, key) || rc;
}
}
else
{
switch (key)
{
case EMU_BTN_ESCAPE:
if (pressed)
dc_exit();
break;
case EMU_BTN_MENU:
if (pressed)
gui_open_settings();
break;
case EMU_BTN_FFORWARD:
if (pressed)
fast_forward_mode = !fast_forward_mode;
break;
case EMU_BTN_TRIGGER_LEFT:
lt[_maple_port] = pressed ? 255 : 0;
break;
case EMU_BTN_TRIGGER_RIGHT:
rt[_maple_port] = pressed ? 255 : 0;
break;
case EMU_BTN_ANA_UP:
joyy[_maple_port] = pressed ? -128 : 0;
break;
case EMU_BTN_ANA_DOWN:
joyy[_maple_port] = pressed ? 127 : 0;
break;
case EMU_BTN_ANA_LEFT:
joyx[_maple_port] = pressed ? -128 : 0;
break;
case EMU_BTN_ANA_RIGHT:
joyx[_maple_port] = pressed ? 127 : 0;
break;
default:
return false;
}
DreamcastKey key = input_mapper->get_button_id(0, code);
rc = handle_key(_maple_port, key);
}
DEBUG_LOG(INPUT, "%d: BUTTON %s %x -> %d. kcode=%x", _maple_port, pressed ? "down" : "up", code, key, kcode[_maple_port]);
return true;
return rc;
}
bool GamepadDevice::gamepad_axis_input(u32 code, int value)
{
s32 v;
if (input_mapper->get_axis_inverted(code))
if (input_mapper->get_axis_inverted(0, code))
v = (get_axis_min_value(code) + get_axis_range(code) - value) * 255 / get_axis_range(code) - 128;
else
v = (value - get_axis_min_value(code)) * 255 / get_axis_range(code) - 128; //-128 ... +127 range
if (_input_detected != NULL && !_detecting_button
if (_input_detected != NULL && !_detecting_button
&& os_GetSeconds() >= _detection_start_time && (v >= 64 || v <= -64))
{
_input_detected(code);
_input_detected = NULL;
return true;
}
if (!input_mapper || _maple_port < 0 || _maple_port >= (int)ARRAY_SIZE(kcode))
if (!input_mapper || _maple_port < 0 || _maple_port > 4)
return false;
DreamcastKey key = input_mapper->get_axis_id(code);
if ((int)key < 0x10000)
auto handle_axis = [&](u32 port, DreamcastKey key)
{
kcode[_maple_port] |= key | (key << 1);
if (v <= -64)
kcode[_maple_port] &= ~key;
else if (v >= 64)
kcode[_maple_port] &= ~(key << 1);
// printf("Mapped to %d %d %d\n",mo,kcode[port]&mo,kcode[port]&(mo*2));
}
else if (((int)key >> 16) == 1) // Triggers
{
//printf("T-AXIS %d Mapped to %d -> %d\n",key, value, v + 128);
if (key == DC_AXIS_LT)
lt[_maple_port] = (u8)(v + 128);
else if (key == DC_AXIS_RT)
rt[_maple_port] = (u8)(v + 128);
else
return false;
}
else if (((int)key >> 16) == 2) // Analog axes
{
//printf("AXIS %d Mapped to %d -> %d\n", key, value, v);
s8 *this_axis;
s8 *other_axis;
switch (key)
if ((int)key < 0x10000)
{
case DC_AXIS_X:
this_axis = &joyx[_maple_port];
other_axis = &joyy[_maple_port];
break;
kcode[port] |= key | (key << 1);
if (v <= -64)
kcode[port] &= ~key;
else if (v >= 64)
kcode[port] &= ~(key << 1);
case DC_AXIS_Y:
this_axis = &joyy[_maple_port];
other_axis = &joyx[_maple_port];
break;
case DC_AXIS_X2:
this_axis = &joyrx[_maple_port];
other_axis = &joyry[_maple_port];
break;
case DC_AXIS_Y2:
this_axis = &joyry[_maple_port];
other_axis = &joyrx[_maple_port];
break;
default:
return false;
// printf("Mapped to %d %d %d\n",mo,kcode[port]&mo,kcode[port]&(mo*2));
}
// Radial dead zone
// FIXME compute both axes at the same time
if ((float)(v * v + *other_axis * *other_axis) < input_mapper->dead_zone * input_mapper->dead_zone * 128.f * 128.f)
else if (((int)key >> 16) == 1) // Triggers
{
*this_axis = 0;
*other_axis = 0;
//printf("T-AXIS %d Mapped to %d -> %d\n",key, value, v + 128);
if (key == DC_AXIS_LT)
lt[port] = (u8)(v + 128);
else if (key == DC_AXIS_RT)
rt[port] = (u8)(v + 128);
else
return false;
}
else if (((int)key >> 16) == 2) // Analog axes
{
//printf("AXIS %d Mapped to %d -> %d\n", key, value, v);
s8 *this_axis;
s8 *other_axis;
switch (key)
{
case DC_AXIS_X:
this_axis = &joyx[port];
other_axis = &joyy[port];
break;
case DC_AXIS_Y:
this_axis = &joyy[port];
other_axis = &joyx[port];
break;
case DC_AXIS_X2:
this_axis = &joyrx[port];
other_axis = &joyry[port];
break;
case DC_AXIS_Y2:
this_axis = &joyry[port];
other_axis = &joyrx[port];
break;
default:
return false;
}
// Radial dead zone
// FIXME compute both axes at the same time
if ((float)(v * v + *other_axis * *other_axis) < input_mapper->dead_zone * input_mapper->dead_zone * 128.f * 128.f)
{
*this_axis = 0;
*other_axis = 0;
}
else
*this_axis = (s8)v;
}
else if (((int)key >> 16) == 4) // Map triggers to digital buttons
{
if (v <= -64)
kcode[port] |= (key & ~0x40000); // button released
else if (v >= 64)
kcode[port] &= ~(key & ~0x40000); // button pressed
}
else
*this_axis = (s8)v;
}
else if (((int)key >> 16) == 4) // Map triggers to digital buttons
return false;
return true;
};
bool rc = false;
if (_maple_port == 4)
{
if (v <= -64)
kcode[_maple_port] |= (key & ~0x40000); // button released
else if (v >= 64)
kcode[_maple_port] &= ~(key & ~0x40000); // button pressed
for (u32 port = 0; port < 4; port++)
{
DreamcastKey key = input_mapper->get_axis_id(port, code);
rc = handle_axis(port, key) || rc;
}
}
else
return false;
{
DreamcastKey key = input_mapper->get_axis_id(0, code);
rc = handle_axis(_maple_port, key);
}
return true;
return rc;
}
int GamepadDevice::get_axis_min_value(u32 axis) {

View File

@ -96,35 +96,35 @@ axis_list[] =
std::map<std::string, std::shared_ptr<InputMapping>> InputMapping::loaded_mappings;
void InputMapping::set_button(DreamcastKey id, u32 code)
void InputMapping::set_button(u32 port, DreamcastKey id, u32 code)
{
if (id != EMU_BTN_NONE)
{
while (true)
{
u32 code = get_button_code(id);
u32 code = get_button_code(port, id);
if (code == (u32)-1)
break;
buttons[code] = EMU_BTN_NONE;
buttons[port][code] = EMU_BTN_NONE;
}
buttons[code] = id;
buttons[port][code] = id;
dirty = true;
}
}
void InputMapping::set_axis(DreamcastKey id, u32 code, bool is_inverted)
void InputMapping::set_axis(u32 port, DreamcastKey id, u32 code, bool is_inverted)
{
if (id != EMU_AXIS_NONE)
{
while (true)
{
u32 code = get_axis_code(id);
u32 code = get_axis_code(port, id);
if (code == (u32)-1)
break;
axes[code] = EMU_AXIS_NONE;
axes[port][code] = EMU_AXIS_NONE;
}
axes[code] = id;
axes_inverted[code] = is_inverted;
axes[port][code] = id;
axes_inverted[port][code] = is_inverted;
dirty = true;
}
}
@ -144,29 +144,44 @@ void InputMapping::load(FILE* fp)
this->dead_zone = (float)dz / 100.f;
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
for (int port = 0; port < 4; port++)
{
int button_code = mf.get_int(button_list[i].section, button_list[i].option, -1);
if (button_code >= 0)
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
{
this->set_button(button_list[i].id, button_code);
std::string option;
if (port == 0)
option = button_list[i].option;
else
option = button_list[i].option + std::to_string(port);
int button_code = mf.get_int(button_list[i].section, option, -1);
if (button_code >= 0)
this->set_button(port, button_list[i].id, button_code);
}
}
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
{
int axis_code = mf.get_int(axis_list[i].section, axis_list[i].option, -1);
if (axis_code >= 0)
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
{
this->set_axis(axis_list[i].id, axis_code, mf.get_bool(axis_list[i].section_inverted, axis_list[i].option_inverted, false));
std::string option;
if (port == 0)
option = axis_list[i].option;
else
option = axis_list[i].option + std::to_string(port);
int axis_code = mf.get_int(axis_list[i].section, option, -1);
if (axis_code >= 0)
{
if (port == 0)
option = axis_list[i].option_inverted;
else
option = axis_list[i].option_inverted + std::to_string(port);
this->set_axis(port, axis_list[i].id, axis_code, mf.get_bool(axis_list[i].section_inverted, option, false));
}
}
}
dirty = false;
}
u32 InputMapping::get_button_code(DreamcastKey key)
u32 InputMapping::get_button_code(u32 port, DreamcastKey key)
{
for (auto& it : buttons)
for (auto& it : buttons[port])
{
if (it.second == key)
return it.first;
@ -174,9 +189,9 @@ u32 InputMapping::get_button_code(DreamcastKey key)
return -1;
}
u32 InputMapping::get_axis_code(DreamcastKey key)
u32 InputMapping::get_axis_code(u32 port, DreamcastKey key)
{
for (auto& it : axes)
for (auto& it : axes[port])
{
if (it.second == key)
return it.first;
@ -221,23 +236,43 @@ bool InputMapping::save(const char *name)
mf.set("emulator", "mapping_name", this->name);
mf.set_int("emulator", "dead_zone", (int)std::round(this->dead_zone * 100.f));
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
for (int port = 0; port < 4; port++)
{
for (auto& it : buttons)
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
{
if (it.second == button_list[i].id)
mf.set_int(button_list[i].section, button_list[i].option, (int)it.first);
}
}
std::string option;
if (port == 0)
option = button_list[i].option;
else
option = button_list[i].option + std::to_string(port);
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
{
for (auto& it : axes)
{
if (it.second == axis_list[i].id)
for (auto& it : buttons[port])
{
mf.set_int(axis_list[i].section, axis_list[i].option, (int)it.first);
mf.set_bool(axis_list[i].section_inverted, axis_list[i].option_inverted, axes_inverted[it.first]);
if (it.second == button_list[i].id)
mf.set_int(button_list[i].section, option, (int)it.first);
}
}
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
{
std::string option;
if (port == 0)
option = axis_list[i].option;
else
option = axis_list[i].option + std::to_string(port);
std::string option_inverted;
if (port == 0)
option_inverted = axis_list[i].option_inverted;
else
option_inverted = axis_list[i].option_inverted + std::to_string(port);
for (auto& it : axes[port])
{
if (it.second == axis_list[i].id)
{
mf.set_int(axis_list[i].section, option, (int)it.first);
mf.set_bool(axis_list[i].section_inverted, option_inverted, axes_inverted[port][it.first]);
}
}
}
}

View File

@ -31,43 +31,48 @@ public:
InputMapping(const InputMapping& other) {
name = other.name;
dead_zone = other.dead_zone;
buttons = other.buttons;
axes = other.axes;
axes_inverted = other.axes_inverted;
for (int port = 0; port < 4; port++)
{
buttons[port] = other.buttons[port];
axes[port] = other.axes[port];
axes_inverted[port] = other.axes_inverted[port];
}
}
std::string name;
float dead_zone;
float dead_zone = 0.1f;
DreamcastKey get_button_id(u32 code)
DreamcastKey get_button_id(u32 port, u32 code)
{
auto it = buttons.find(code);
if (it != buttons.end())
auto it = buttons[port].find(code);
if (it != buttons[port].end())
return it->second;
else
return EMU_BTN_NONE;
}
void set_button(DreamcastKey id, u32 code);
u32 get_button_code(DreamcastKey key);
void set_button(u32 port, DreamcastKey id, u32 code);
void set_button(DreamcastKey id, u32 code) { set_button(0, id, code); }
u32 get_button_code(u32 port, DreamcastKey id);
DreamcastKey get_axis_id(u32 code)
DreamcastKey get_axis_id(u32 port, u32 code)
{
auto it = axes.find(code);
if (it != axes.end())
auto it = axes[port].find(code);
if (it != axes[port].end())
return it->second;
else
return EMU_AXIS_NONE;
}
bool get_axis_inverted(u32 code)
bool get_axis_inverted(u32 port, u32 code)
{
auto it = axes_inverted.find(code);
if (it != axes_inverted.end())
auto it = axes_inverted[port].find(code);
if (it != axes_inverted[port].end())
return it->second;
else
return false;
}
u32 get_axis_code(DreamcastKey key);
void set_axis(DreamcastKey id, u32 code, bool inverted);
u32 get_axis_code(u32 port, DreamcastKey key);
void set_axis(u32 port, DreamcastKey id, u32 code, bool inverted);
void set_axis(DreamcastKey id, u32 code, bool inverted) { set_axis(0, id, code, inverted); }
void load(FILE* fp);
bool save(const char *name);
@ -81,9 +86,9 @@ protected:
bool dirty = false;
private:
std::map<u32, DreamcastKey> buttons;
std::map<u32, DreamcastKey> axes;
std::map<u32, bool> axes_inverted;
std::map<u32, DreamcastKey> buttons[4];
std::map<u32, DreamcastKey> axes[4];
std::map<u32, bool> axes_inverted[4];
static std::map<std::string, std::shared_ptr<InputMapping>> loaded_mappings;
};
@ -96,12 +101,12 @@ public:
dead_zone = 0.1f;
for (int i = 0; i < 32; i++)
set_button((DreamcastKey)(1 << i), 1 << i);
set_axis(DC_AXIS_X, DC_AXIS_X, false);
set_axis(DC_AXIS_Y, DC_AXIS_Y, false);
set_axis(DC_AXIS_LT, DC_AXIS_LT, false);
set_axis(DC_AXIS_RT, DC_AXIS_RT, false);
set_axis(DC_AXIS_X2, DC_AXIS_X2, false);
set_axis(DC_AXIS_Y2, DC_AXIS_Y2, false);
set_button(0, (DreamcastKey)(1 << i), 1 << i);
set_axis(0, DC_AXIS_X, DC_AXIS_X, false);
set_axis(0, DC_AXIS_Y, DC_AXIS_Y, false);
set_axis(0, DC_AXIS_LT, DC_AXIS_LT, false);
set_axis(0, DC_AXIS_RT, DC_AXIS_RT, false);
set_axis(0, DC_AXIS_X2, DC_AXIS_X2, false);
set_axis(0, DC_AXIS_Y2, DC_AXIS_Y2, false);
}
};

View File

@ -442,7 +442,7 @@ static const char *maple_expansion_device_name(MapleDeviceType type)
}
}
const char *maple_ports[] = { "None", "A", "B", "C", "D" };
const char *maple_ports[] = { "None", "A", "B", "C", "D", "All" };
const DreamcastKey button_keys[] = {
DC_BTN_START, DC_BTN_A, DC_BTN_B, DC_BTN_X, DC_BTN_Y, DC_DPAD_UP, DC_DPAD_DOWN, DC_DPAD_LEFT, DC_DPAD_RIGHT,
EMU_BTN_MENU, EMU_BTN_ESCAPE, EMU_BTN_FFORWARD, EMU_BTN_TRIGGER_LEFT, EMU_BTN_TRIGGER_RIGHT,
@ -500,6 +500,7 @@ static std::shared_ptr<GamepadDevice> mapped_device;
static u32 mapped_code;
static double map_start_time;
static bool arcade_button_mode;
static u32 gamepad_port;
static void input_detected(u32 code)
{
@ -525,15 +526,15 @@ static void detect_input_popup(int index, bool analog)
{
if (analog)
{
u32 previous_mapping = input_mapping->get_axis_code(axis_keys[index]);
u32 previous_mapping = input_mapping->get_axis_code(gamepad_port, axis_keys[index]);
bool inverted = false;
if (previous_mapping != (u32)-1)
inverted = input_mapping->get_axis_inverted(previous_mapping);
inverted = input_mapping->get_axis_inverted(gamepad_port, previous_mapping);
// FIXME Allow inverted to be set
input_mapping->set_axis(axis_keys[index], mapped_code, inverted);
input_mapping->set_axis(gamepad_port, axis_keys[index], mapped_code, inverted);
}
else
input_mapping->set_button(button_keys[index], mapped_code);
input_mapping->set_button(gamepad_port, button_keys[index], mapped_code);
}
mapped_device = NULL;
ImGui::CloseCurrentPopup();
@ -569,6 +570,26 @@ static void controller_mapping_popup(std::shared_ptr<GamepadDevice> gamepad)
gamepad->save_mapping();
}
ImGui::SetItemDefaultFocus();
if (gamepad->maple_port() == MAPLE_PORTS)
{
ImGui::SameLine();
float w = ImGui::CalcItemWidth();
ImGui::PushItemWidth(w / 2);
if (ImGui::BeginCombo("Port", maple_ports[gamepad_port + 1]))
{
for (u32 j = 0; j < MAPLE_PORTS; j++)
{
bool is_selected = gamepad_port == j;
if (ImGui::Selectable(maple_ports[j + 1], &is_selected))
gamepad_port = j;
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
}
ImGui::SameLine(ImGui::GetContentRegionAvailWidth() - ImGui::CalcTextSize("Arcade button names").x
- ImGui::GetStyle().FramePadding.x * 3.0f - ImGui::GetStyle().ItemSpacing.x);
ImGui::Checkbox("Arcade button names", &arcade_button_mode);
@ -587,7 +608,7 @@ static void controller_mapping_popup(std::shared_ptr<GamepadDevice> gamepad)
ImGui::PushID(key_id);
ImGui::Text("%s", arcade_button_mode ? arcade_button_names[j] : button_names[j]);
ImGui::NextColumn();
u32 code = input_mapping->get_button_code(button_keys[j]);
u32 code = input_mapping->get_button_code(gamepad_port, button_keys[j]);
if (code != (u32)-1)
{
const char *label = gamepad->get_button_name(code);
@ -627,7 +648,7 @@ static void controller_mapping_popup(std::shared_ptr<GamepadDevice> gamepad)
ImGui::PushID(key_id);
ImGui::Text("%s", arcade_button_mode ? arcade_axis_names[j] : axis_names[j]);
ImGui::NextColumn();
u32 code = input_mapping->get_axis_code(axis_keys[j]);
u32 code = input_mapping->get_axis_code(gamepad_port, axis_keys[j]);
if (code != (u32)-1)
{
const char *label = gamepad->get_axis_name(code);
@ -1015,7 +1036,7 @@ static void gui_display_settings()
ImGui::PushID(port_name);
if (ImGui::BeginCombo(port_name, maple_ports[gamepad->maple_port() + 1]))
{
for (int j = -1; j < MAPLE_PORTS; j++)
for (int j = -1; j < (int)ARRAY_SIZE(maple_ports) - 1; j++)
{
bool is_selected = gamepad->maple_port() == j;
if (ImGui::Selectable(maple_ports[j + 1], &is_selected))
@ -1028,7 +1049,10 @@ static void gui_display_settings()
}
ImGui::NextColumn();
if (gamepad->remappable() && ImGui::Button("Map"))
{
gamepad_port = 0;
ImGui::OpenPopup("Controller Mapping");
}
controller_mapping_popup(gamepad);