Controller Mapping: System Profiles & Unmap Button (#269)
* add unmap button * separate controller profiles for dc & arcade games * loads current system mapping on menu exit * simplify, use default filename for dc controls * create blank mapping file for sdl controllers if not found
This commit is contained in:
parent
b3ad0a62c1
commit
46e3258629
|
@ -22,9 +22,11 @@
|
|||
#include "oslib/oslib.h"
|
||||
#include "rend/gui.h"
|
||||
#include "emulator.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <fstream>
|
||||
|
||||
#define MAPLE_PORT_CFG_PREFIX "maple_"
|
||||
|
||||
|
@ -320,6 +322,84 @@ std::string GamepadDevice::make_mapping_filename(bool instance)
|
|||
return mapping_file;
|
||||
}
|
||||
|
||||
void GamepadDevice::verify_or_create_system_mappings()
|
||||
{
|
||||
std::string dc_name = make_mapping_filename(false, 0);
|
||||
std::string arcade_name = make_mapping_filename(false, 2);
|
||||
|
||||
std::string dc_path = get_readonly_config_path(std::string("mappings/") + dc_name);
|
||||
std::string arcade_path = get_readonly_config_path(std::string("mappings/") + arcade_name);
|
||||
|
||||
if (!file_exists(arcade_path))
|
||||
{
|
||||
save_mapping(2);
|
||||
input_mapper->ClearMappings();
|
||||
}
|
||||
if (!file_exists(dc_path))
|
||||
{
|
||||
save_mapping(0);
|
||||
input_mapper->ClearMappings();
|
||||
}
|
||||
|
||||
find_mapping(DC_PLATFORM_DREAMCAST);
|
||||
}
|
||||
|
||||
void GamepadDevice::load_system_mappings(int system)
|
||||
{
|
||||
for (int i = 0; i < GetGamepadCount(); i++)
|
||||
{
|
||||
std::shared_ptr<GamepadDevice> gamepad = GetGamepad(i);
|
||||
gamepad->find_mapping(system);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GamepadDevice::make_mapping_filename(bool instance, int system)
|
||||
{
|
||||
std::string mapping_file = api_name() + "_" + name();
|
||||
if (instance)
|
||||
mapping_file += "-" + _unique_id;
|
||||
if (system != 0)
|
||||
mapping_file += "_arcade";
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '/', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '\\', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), ':', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '?', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '*', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '|', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '"', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '<', '-');
|
||||
std::replace(mapping_file.begin(), mapping_file.end(), '>', '-');
|
||||
mapping_file += ".cfg";
|
||||
|
||||
return mapping_file;
|
||||
}
|
||||
|
||||
bool GamepadDevice::find_mapping(int system)
|
||||
{
|
||||
std::string mapping_file;
|
||||
mapping_file = make_mapping_filename(false, system);
|
||||
|
||||
// fall back on default flycast mapping filename if system profile not found
|
||||
std::string system_mapping_path = get_readonly_config_path(std::string("mappings/") + mapping_file);
|
||||
if (!file_exists(system_mapping_path))
|
||||
mapping_file = make_mapping_filename(false);
|
||||
|
||||
input_mapper = InputMapping::LoadMapping(mapping_file.c_str());
|
||||
|
||||
// fallback to default mapping filename for sdl inputs
|
||||
if (!input_mapper && mapping_file.find("SDL") != std::string::npos)
|
||||
{
|
||||
mapping_file = make_mapping_filename(false);
|
||||
std::string mapping_path = get_readonly_config_path(std::string("mappings/") + mapping_file);
|
||||
|
||||
// create default mapping filename if none exists
|
||||
if (!file_exists(mapping_path))
|
||||
std::ofstream file{ mapping_path.c_str() };
|
||||
input_mapper = InputMapping::LoadMapping(mapping_file.c_str());
|
||||
}
|
||||
return !!input_mapper;
|
||||
}
|
||||
|
||||
bool GamepadDevice::find_mapping(const char *custom_mapping /* = nullptr */)
|
||||
{
|
||||
std::string mapping_file;
|
||||
|
@ -365,6 +445,15 @@ void GamepadDevice::save_mapping()
|
|||
InputMapping::SaveMapping(filename.c_str(), input_mapper);
|
||||
}
|
||||
|
||||
void GamepadDevice::save_mapping(int system)
|
||||
{
|
||||
if (!input_mapper)
|
||||
return;
|
||||
std::string filename = make_mapping_filename(false, system);
|
||||
input_mapper->set_dirty();
|
||||
InputMapping::SaveMapping(filename.c_str(), input_mapper);
|
||||
}
|
||||
|
||||
void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms)
|
||||
{
|
||||
int i = GamepadDevice::GetGamepadCount() - 1;
|
||||
|
|
|
@ -48,6 +48,10 @@ public:
|
|||
}
|
||||
std::shared_ptr<InputMapping> get_input_mapping() { return input_mapper; }
|
||||
void save_mapping();
|
||||
void save_mapping(int system);
|
||||
|
||||
void verify_or_create_system_mappings();
|
||||
|
||||
virtual const char *get_button_name(u32 code) { return nullptr; }
|
||||
virtual const char *get_axis_name(u32 code) { return nullptr; }
|
||||
bool remappable() { return _remappable && input_mapper; }
|
||||
|
@ -65,6 +69,8 @@ public:
|
|||
static std::shared_ptr<GamepadDevice> GetGamepad(int index);
|
||||
static void SaveMaplePorts();
|
||||
|
||||
static void load_system_mappings(int system = settings.platform.system);
|
||||
bool find_mapping(int system);
|
||||
protected:
|
||||
GamepadDevice(int maple_port, const char *api_name, bool remappable = true)
|
||||
: _api_name(api_name), _maple_port(maple_port), _input_detected(nullptr), _remappable(remappable)
|
||||
|
@ -86,6 +92,7 @@ private:
|
|||
int get_axis_min_value(u32 axis);
|
||||
unsigned int get_axis_range(u32 axis);
|
||||
std::string make_mapping_filename(bool instance = false);
|
||||
std::string make_mapping_filename(bool instance, int system);
|
||||
|
||||
std::string _api_name;
|
||||
int _maple_port;
|
||||
|
|
|
@ -97,6 +97,21 @@ axis_list[] =
|
|||
|
||||
std::map<std::string, std::shared_ptr<InputMapping>> InputMapping::loaded_mappings;
|
||||
|
||||
void InputMapping::clear_button(u32 port, DreamcastKey id, u32 code)
|
||||
{
|
||||
if (id != EMU_BTN_NONE)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
u32 code = get_button_code(port, id);
|
||||
if (code == (u32)-1)
|
||||
break;
|
||||
buttons[port][code] = EMU_BTN_NONE;
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void InputMapping::set_button(u32 port, DreamcastKey id, u32 code)
|
||||
{
|
||||
if (id != EMU_BTN_NONE)
|
||||
|
@ -113,6 +128,21 @@ void InputMapping::set_button(u32 port, DreamcastKey id, u32 code)
|
|||
}
|
||||
}
|
||||
|
||||
void InputMapping::clear_axis(u32 port, DreamcastKey id, u32 code)
|
||||
{
|
||||
if (id != EMU_AXIS_NONE)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
u32 code = get_axis_code(port, id);
|
||||
if (code == (u32)-1)
|
||||
break;
|
||||
axes[port][code] = EMU_AXIS_NONE;
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void InputMapping::set_axis(u32 port, DreamcastKey id, u32 code, bool is_inverted)
|
||||
{
|
||||
if (id != EMU_AXIS_NONE)
|
||||
|
@ -200,6 +230,11 @@ u32 InputMapping::get_axis_code(u32 port, DreamcastKey key)
|
|||
return -1;
|
||||
}
|
||||
|
||||
void InputMapping::ClearMappings()
|
||||
{
|
||||
loaded_mappings.clear();
|
||||
}
|
||||
|
||||
std::shared_ptr<InputMapping> InputMapping::LoadMapping(const char *name)
|
||||
{
|
||||
auto it = loaded_mappings.find(name);
|
||||
|
@ -218,6 +253,11 @@ std::shared_ptr<InputMapping> InputMapping::LoadMapping(const char *name)
|
|||
return mapping;
|
||||
}
|
||||
|
||||
void InputMapping::set_dirty()
|
||||
{
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
bool InputMapping::save(const char *name)
|
||||
{
|
||||
if (!dirty)
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
else
|
||||
return EMU_BTN_NONE;
|
||||
}
|
||||
void clear_button(u32 port, DreamcastKey id, u32 code);
|
||||
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 key);
|
||||
|
@ -71,17 +72,21 @@ public:
|
|||
return false;
|
||||
}
|
||||
u32 get_axis_code(u32 port, DreamcastKey key);
|
||||
void clear_axis(u32 port, DreamcastKey id, u32 code);
|
||||
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);
|
||||
|
||||
void set_dirty();
|
||||
bool is_dirty() const { return dirty; }
|
||||
|
||||
static std::shared_ptr<InputMapping> LoadMapping(const char *name);
|
||||
static void SaveMapping(const char *name, const std::shared_ptr<InputMapping>& mapping);
|
||||
|
||||
void ClearMappings();
|
||||
|
||||
protected:
|
||||
bool dirty = false;
|
||||
|
||||
|
|
|
@ -493,6 +493,8 @@ static void dc_start_game(const char *path)
|
|||
config::Settings::instance().reset();
|
||||
dc_reset(true);
|
||||
config::Settings::instance().load(false);
|
||||
|
||||
GamepadDevice::load_system_mappings();
|
||||
|
||||
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
|
||||
{
|
||||
|
|
|
@ -63,6 +63,7 @@ static std::string osd_message;
|
|||
static double osd_message_end;
|
||||
static std::mutex osd_message_mutex;
|
||||
|
||||
static int map_system = 0;
|
||||
static void display_vmus();
|
||||
static void reset_vmus();
|
||||
static void term_vmus();
|
||||
|
@ -390,6 +391,7 @@ void gui_open_settings()
|
|||
else if (gui_state == GuiState::Commands)
|
||||
{
|
||||
gui_state = GuiState::Closed;
|
||||
GamepadDevice::load_system_mappings();
|
||||
dc_resume();
|
||||
}
|
||||
}
|
||||
|
@ -459,6 +461,7 @@ static void gui_display_commands()
|
|||
ImGui::NextColumn();
|
||||
if (ImGui::Button("Resume", ImVec2(150 * scaling, 50 * scaling)))
|
||||
{
|
||||
GamepadDevice::load_system_mappings();
|
||||
gui_state = GuiState::Closed;
|
||||
}
|
||||
|
||||
|
@ -682,13 +685,14 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
|
|||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
const float width = (ImGui::GetIO().DisplaySize.x - insetLeft - insetRight - style.ItemSpacing.x) / 2 - style.WindowBorderSize - style.WindowPadding.x;
|
||||
const float col_width = (width - style.GrabMinSize - style.ItemSpacing.x
|
||||
- (ImGui::CalcTextSize("Map").x + style.FramePadding.x * 2.0f + style.ItemSpacing.x)) / 2;
|
||||
- (ImGui::CalcTextSize("Map").x + style.FramePadding.x * 2.0f + style.ItemSpacing.x)
|
||||
- (ImGui::CalcTextSize("Unmap").x + style.FramePadding.x * 2.0f + style.ItemSpacing.x)) / 2;
|
||||
|
||||
std::shared_ptr<InputMapping> input_mapping = gamepad->get_input_mapping();
|
||||
if (input_mapping == NULL || ImGui::Button("Done", ImVec2(100 * scaling, 30 * scaling)))
|
||||
{
|
||||
ImGui::CloseCurrentPopup();
|
||||
gamepad->save_mapping();
|
||||
gamepad->save_mapping(map_system);
|
||||
}
|
||||
ImGui::SetItemDefaultFocus();
|
||||
|
||||
|
@ -711,9 +715,45 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
|
|||
}
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize("Arcade button names").x
|
||||
- style.FramePadding.x * 3.0f - style.ItemSpacing.x);
|
||||
ImGui::Checkbox("Arcade button names", &arcade_button_mode);
|
||||
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize("Dreamcast Controls").x
|
||||
- ImGui::GetStyle().FramePadding.x * 3.0f - ImGui::GetStyle().ItemSpacing.x * 3.0f);
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
static ImGuiComboFlags flags = 0;
|
||||
const char* items[] = { "Dreamcast Controls", "Arcade Controls" };
|
||||
static int item_current_map_idx = 0;
|
||||
static int last_item_current_map_idx = 2;
|
||||
|
||||
// Here our selection data is an index.
|
||||
const char* combo_label = items[item_current_map_idx]; // Label to preview before opening the combo (technically it could be anything)
|
||||
|
||||
ImGui::PushItemWidth(ImGui::CalcTextSize("Dreamcast Controls").x + ImGui::GetStyle().ItemSpacing.x * 2.0f * 3);
|
||||
|
||||
ImGui::Combo("", &item_current_map_idx, items, IM_ARRAYSIZE(items));
|
||||
|
||||
if (item_current_map_idx != last_item_current_map_idx)
|
||||
{
|
||||
gamepad->save_mapping(map_system);
|
||||
}
|
||||
|
||||
if (item_current_map_idx == 0)
|
||||
{
|
||||
arcade_button_mode = false;
|
||||
map_system = DC_PLATFORM_DREAMCAST;
|
||||
}
|
||||
else if (item_current_map_idx == 1)
|
||||
{
|
||||
arcade_button_mode = true;
|
||||
map_system = DC_PLATFORM_NAOMI;
|
||||
}
|
||||
|
||||
if (item_current_map_idx != last_item_current_map_idx)
|
||||
{
|
||||
gamepad->find_mapping(map_system);
|
||||
input_mapping = gamepad->get_input_mapping();
|
||||
|
||||
last_item_current_map_idx = item_current_map_idx;
|
||||
}
|
||||
|
||||
char key_id[32];
|
||||
ImGui::BeginGroup();
|
||||
|
@ -723,6 +763,8 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
|
|||
ImGui::Columns(3, "bindings", false);
|
||||
ImGui::SetColumnWidth(0, col_width);
|
||||
ImGui::SetColumnWidth(1, col_width);
|
||||
|
||||
gamepad->find_mapping(map_system);
|
||||
for (u32 j = 0; j < ARRAY_SIZE(button_keys); j++)
|
||||
{
|
||||
sprintf(key_id, "key_id%d", j);
|
||||
|
@ -758,6 +800,12 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
|
|||
});
|
||||
}
|
||||
detect_input_popup(j, false);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Unmap"))
|
||||
{
|
||||
input_mapping = gamepad->get_input_mapping();
|
||||
input_mapping->clear_button(gamepad_port, button_keys[j], j);
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
@ -812,6 +860,12 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
|
|||
});
|
||||
}
|
||||
detect_input_popup(j, true);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Unmap"))
|
||||
{
|
||||
input_mapping = gamepad->get_input_mapping();
|
||||
input_mapping->clear_axis(gamepad_port, axis_keys[j], j);
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
@ -1130,6 +1184,7 @@ static void gui_display_settings()
|
|||
if (gamepad->remappable() && ImGui::Button("Map"))
|
||||
{
|
||||
gamepad_port = 0;
|
||||
gamepad->verify_or_create_system_mappings();
|
||||
ImGui::OpenPopup("Controller Mapping");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue