From 01638373c010419995da067de9a4502f9d3d382d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo?= Date: Thu, 24 Jul 2025 11:24:22 -0300 Subject: [PATCH] Add Input Profiles. (#4) * Add Input Profiles. * Add capability of setting custom profiles. * Remove leftover early return. --- include/banjo_config.h | 7 +- include/recomp_input.h | 42 ++++--- src/game/config.cpp | 198 ++++++++++++++++++------------- src/game/controls.cpp | 256 +++++++++++++++++++++++++++++++++++------ src/game/input.cpp | 151 +++--------------------- src/ui/ui_config.cpp | 40 +++---- src/ui/ui_state.cpp | 20 ++-- 7 files changed, 416 insertions(+), 298 deletions(-) diff --git a/include/banjo_config.h b/include/banjo_config.h index aace8e4..e77e5dd 100644 --- a/include/banjo_config.h +++ b/include/banjo_config.h @@ -14,10 +14,11 @@ namespace banjo { void load_config(); void save_config(); + void initialize_input_bindings(); void reset_input_bindings(); - void reset_cont_input_bindings(int controller_num); - void reset_kb_input_bindings(int controller_num); - void reset_single_input_binding(int controller_num, recomp::InputDevice device, recomp::GameInput input); + void reset_cont_input_bindings(int profile_index); + void reset_kb_input_bindings(int profile_index); + void reset_single_input_binding(int profile_index, recomp::InputDevice device, recomp::GameInput input); std::filesystem::path get_app_folder_path(); diff --git a/include/recomp_input.h b/include/recomp_input.h index a0f654f..9dd1bd9 100644 --- a/include/recomp_input.h +++ b/include/recomp_input.h @@ -98,6 +98,11 @@ namespace recomp { COUNT }; + NLOHMANN_JSON_SERIALIZE_ENUM(recomp::InputDevice, { + { recomp::InputDevice::Controller, "Controller" }, + { recomp::InputDevice::Keyboard, "Keyboard" }, + }); + void start_scanning_input(InputDevice device); void stop_scanning_input(); void finish_scanning_input(InputField scanned_field); @@ -171,31 +176,38 @@ namespace recomp { const std::string& get_input_name(GameInput input); const std::string& get_input_enum_name(GameInput input); GameInput get_input_from_enum_name(const std::string_view name); - InputField& get_input_binding(int controller_num, GameInput input, size_t binding_index, InputDevice device); - void set_input_binding(int controller_num, GameInput input, size_t binding_index, InputDevice device, InputField value); + InputField& get_input_binding(int profile_index, GameInput input, size_t binding_index); + void set_input_binding(int profile_index, GameInput input, size_t binding_index, InputField value); + int add_input_profile(const std::string &key, const std::string &name, InputDevice device, bool custom); + int get_input_profile_by_key(const std::string &key); + const std::string &get_input_profile_key(int profile_index); + const std::string &get_input_profile_name(int profile_index); + InputDevice get_input_profile_device(int profile_index); + bool is_input_profile_custom(int profile_index); + int get_input_profile_count(); + const std::vector get_indices_for_custom_profiles(InputDevice device); + void set_input_profile_for_player(int player_index, int profile_index, InputDevice device); + int get_input_profile_for_player(int player_index, InputDevice device); struct ControllerGUID { + uint64_t hash; std::string serial; int vendor{}; int product{}; int version{}; int crc16{}; - int player_index{}; }; - void set_input_controller_guid(int controller_num, const ControllerGUID &guid); - ControllerGUID get_input_controller_guid(int controller_num); + int add_controller(ControllerGUID guid, int profile_index); + const ControllerGUID &get_controller_guid(int controller_index); + int get_controller_profile_index(int controller_index); + int get_controller_by_guid(ControllerGUID guid); + int get_controller_count(); + ControllerGUID get_guid_from_sdl_controller(SDL_GameController* game_controller); + std::string get_string_from_controller_guid(ControllerGUID guid); - struct ControllerOption { - std::string name; - ControllerGUID guid; - }; - - void refresh_controller_options(); - const std::vector &get_controller_options(); - - bool get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out); - void set_rumble(int controller_num, bool); + bool get_n64_input(int player_index, uint16_t* buttons_out, float* x_out, float* y_out); + void set_rumble(int player_index, bool); void update_rumble(); void handle_events(); diff --git a/src/game/config.cpp b/src/game/config.cpp index 2b8a06d..fceafc5 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -34,6 +34,13 @@ constexpr int ds_default = 1; constexpr int rr_manual_default = 60; constexpr bool developer_mode_default = false; +static std::string keyboard_sp_profile_key = "keyboard_sp"; +static std::string controller_sp_profile_key = "controller_sp"; +static std::string keyboard_sp_profile_name = "Keyboard (SP)"; +static std::string controller_sp_profile_name = "Controller (SP)"; +static int keyboard_sp_profile_index = -1; +static int controller_sp_profile_index = -1; + static bool is_steam_deck = false; ultramodern::renderer::WindowMode wm_default() { @@ -131,7 +138,7 @@ namespace recomp { } void to_json(json& j, const ControllerGUID& guid) { - j = json{ {"serial", guid.serial}, {"vendor", guid.vendor}, {"product", guid.product}, {"version", guid.version}, {"crc16", guid.crc16}, {"player_index", guid.player_index} }; + j = json{ {"serial", guid.serial}, {"vendor", guid.vendor}, {"product", guid.product}, {"version", guid.version}, {"crc16", guid.crc16} }; } void from_json(const json& j, ControllerGUID& guid) { @@ -140,7 +147,6 @@ namespace recomp { j.at("product").get_to(guid.product); j.at("version").get_to(guid.version); j.at("crc16").get_to(guid.crc16); - j.at("player_index").get_to(guid.player_index); } } @@ -279,69 +285,74 @@ bool load_general_config(const std::filesystem::path& path) { return true; } -void assign_mapping(int controller_num, recomp::InputDevice device, recomp::GameInput input, const std::vector& value) { +void assign_mapping(int profile_index, recomp::GameInput input, const std::vector& value) { for (size_t binding_index = 0; binding_index < std::min(value.size(), recomp::bindings_per_input); binding_index++) { - recomp::set_input_binding(controller_num, input, binding_index, device, value[binding_index]); + recomp::set_input_binding(profile_index, input, binding_index, value[binding_index]); } }; // same as assign_mapping, except will clear unassigned bindings if not in value -void assign_mapping_complete(int controller_num, recomp::InputDevice device, recomp::GameInput input, const std::vector& value) { +void assign_mapping_complete(int profile_index, recomp::GameInput input, const std::vector& value) { for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { if (binding_index >= value.size()) { - recomp::set_input_binding(controller_num, input, binding_index, device, recomp::InputField{}); + recomp::set_input_binding(profile_index, input, binding_index, recomp::InputField{}); } else { - recomp::set_input_binding(controller_num, input, binding_index, device, value[binding_index]); + recomp::set_input_binding(profile_index, input, binding_index, value[binding_index]); } } }; -void assign_all_mappings(int controller_num, recomp::InputDevice device, const recomp::DefaultN64Mappings& values) { - assign_mapping_complete(controller_num, device, recomp::GameInput::A, values.a); - assign_mapping_complete(controller_num, device, recomp::GameInput::B, values.b); - assign_mapping_complete(controller_num, device, recomp::GameInput::Z, values.z); - assign_mapping_complete(controller_num, device, recomp::GameInput::START, values.start); - assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_UP, values.dpad_up); - assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_DOWN, values.dpad_down); - assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_LEFT, values.dpad_left); - assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_RIGHT, values.dpad_right); - assign_mapping_complete(controller_num, device, recomp::GameInput::L, values.l); - assign_mapping_complete(controller_num, device, recomp::GameInput::R, values.r); - assign_mapping_complete(controller_num, device, recomp::GameInput::C_UP, values.c_up); - assign_mapping_complete(controller_num, device, recomp::GameInput::C_DOWN, values.c_down); - assign_mapping_complete(controller_num, device, recomp::GameInput::C_LEFT, values.c_left); - assign_mapping_complete(controller_num, device, recomp::GameInput::C_RIGHT, values.c_right); +void assign_all_mappings(int profile_index, const recomp::DefaultN64Mappings& values) { + assign_mapping_complete(profile_index, recomp::GameInput::A, values.a); + assign_mapping_complete(profile_index, recomp::GameInput::B, values.b); + assign_mapping_complete(profile_index, recomp::GameInput::Z, values.z); + assign_mapping_complete(profile_index, recomp::GameInput::START, values.start); + assign_mapping_complete(profile_index, recomp::GameInput::DPAD_UP, values.dpad_up); + assign_mapping_complete(profile_index, recomp::GameInput::DPAD_DOWN, values.dpad_down); + assign_mapping_complete(profile_index, recomp::GameInput::DPAD_LEFT, values.dpad_left); + assign_mapping_complete(profile_index, recomp::GameInput::DPAD_RIGHT, values.dpad_right); + assign_mapping_complete(profile_index, recomp::GameInput::L, values.l); + assign_mapping_complete(profile_index, recomp::GameInput::R, values.r); + assign_mapping_complete(profile_index, recomp::GameInput::C_UP, values.c_up); + assign_mapping_complete(profile_index, recomp::GameInput::C_DOWN, values.c_down); + assign_mapping_complete(profile_index, recomp::GameInput::C_LEFT, values.c_left); + assign_mapping_complete(profile_index, recomp::GameInput::C_RIGHT, values.c_right); - assign_mapping_complete(controller_num, device, recomp::GameInput::X_AXIS_NEG, values.analog_left); - assign_mapping_complete(controller_num, device, recomp::GameInput::X_AXIS_POS, values.analog_right); - assign_mapping_complete(controller_num, device, recomp::GameInput::Y_AXIS_NEG, values.analog_down); - assign_mapping_complete(controller_num, device, recomp::GameInput::Y_AXIS_POS, values.analog_up); + assign_mapping_complete(profile_index, recomp::GameInput::X_AXIS_NEG, values.analog_left); + assign_mapping_complete(profile_index, recomp::GameInput::X_AXIS_POS, values.analog_right); + assign_mapping_complete(profile_index, recomp::GameInput::Y_AXIS_NEG, values.analog_down); + assign_mapping_complete(profile_index, recomp::GameInput::Y_AXIS_POS, values.analog_up); - assign_mapping_complete(controller_num, device, recomp::GameInput::TOGGLE_MENU, values.toggle_menu); - assign_mapping_complete(controller_num, device, recomp::GameInput::ACCEPT_MENU, values.accept_menu); - assign_mapping_complete(controller_num, device, recomp::GameInput::APPLY_MENU, values.apply_menu); + assign_mapping_complete(profile_index, recomp::GameInput::TOGGLE_MENU, values.toggle_menu); + assign_mapping_complete(profile_index, recomp::GameInput::ACCEPT_MENU, values.accept_menu); + assign_mapping_complete(profile_index, recomp::GameInput::APPLY_MENU, values.apply_menu); }; +void banjo::initialize_input_bindings() { + keyboard_sp_profile_index = recomp::add_input_profile(keyboard_sp_profile_key, keyboard_sp_profile_name, recomp::InputDevice::Keyboard, false); + controller_sp_profile_index = recomp::add_input_profile(controller_sp_profile_key, controller_sp_profile_name, recomp::InputDevice::Controller, false); + + // Set Player 1 to the SP profiles by default. + recomp::set_input_profile_for_player(0, keyboard_sp_profile_index, recomp::InputDevice::Keyboard); + recomp::set_input_profile_for_player(0, controller_sp_profile_index, recomp::InputDevice::Controller); +} + void banjo::reset_input_bindings() { - assign_all_mappings(0, recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); - - for (int i = 0; i < 4; i++) { - assign_all_mappings(i, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); - } + assign_all_mappings(keyboard_sp_profile_index, recomp::default_n64_keyboard_mappings); + assign_all_mappings(controller_sp_profile_index, recomp::default_n64_controller_mappings); } -void banjo::reset_cont_input_bindings(int controller_num) { - assign_all_mappings(controller_num, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); +void banjo::reset_cont_input_bindings(int profile_index) { + assign_all_mappings(profile_index, recomp::default_n64_controller_mappings); } -void banjo::reset_kb_input_bindings(int controller_num) { - assign_all_mappings(controller_num, recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); +void banjo::reset_kb_input_bindings(int profile_index) { + assign_all_mappings(profile_index, recomp::default_n64_keyboard_mappings); } -void banjo::reset_single_input_binding(int controller_num, recomp::InputDevice device, recomp::GameInput input) { +void banjo::reset_single_input_binding(int profile_index, recomp::InputDevice device, recomp::GameInput input) { assign_mapping_complete( - controller_num, - device, + profile_index, input, recomp::get_default_mapping_for_input( device == recomp::InputDevice::Keyboard ? @@ -385,39 +396,50 @@ bool load_graphics_config(const std::filesystem::path& path) { return true; } -void add_input_bindings(nlohmann::json& out, int controller_num, recomp::GameInput input, recomp::InputDevice device) { +void add_input_bindings(nlohmann::json& out, int profile_index, recomp::GameInput input) { const std::string& input_name = recomp::get_input_enum_name(input); nlohmann::json& out_array = out[input_name]; out_array = nlohmann::json::array(); for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { - out_array[binding_index] = recomp::get_input_binding(controller_num, input, binding_index, device); + out_array[binding_index] = recomp::get_input_binding(profile_index, input, binding_index); } }; -constexpr int controls_version = 2; +constexpr int controls_version = 3; bool save_controls_config(const std::filesystem::path& path) { + int profile_count = recomp::get_input_profile_count(); + int controller_count = recomp::get_controller_count(); nlohmann::json config_json{}; config_json["version"] = controls_version; - config_json["players"] = std::vector(4); - for (size_t i = 0; i < config_json["players"].size(); i++) { - nlohmann::json &player = config_json["players"][i]; - player["keyboard"] = {}; - player["controller"] = {}; + config_json["profiles"] = std::vector(profile_count); + config_json["controllers"] = std::vector(controller_count); - for (size_t j = 0; j < recomp::get_num_inputs(); j++) { - recomp::GameInput cur_input = static_cast(j); - add_input_bindings(player["keyboard"], i, cur_input, recomp::InputDevice::Keyboard); - add_input_bindings(player["controller"], i, cur_input, recomp::InputDevice::Controller); + nlohmann::json &profiles = config_json["profiles"]; + for (int i = 0; i < profile_count; i++) { + nlohmann::json &profile = profiles[i]; + profile["key"] = recomp::get_input_profile_key(i); + profile["name"] = recomp::get_input_profile_name(i); + profile["device"] = recomp::get_input_profile_device(i); + profile["custom"] = recomp::is_input_profile_custom(i); + profile["mappings"] = nlohmann::json(); + + for (int j = 0; j < (int)(recomp::GameInput::COUNT); j++) { + add_input_bindings(profile["mappings"], i, (recomp::GameInput)(j)); } + } - player["controller"]["guid"] = recomp::get_input_controller_guid(i); + nlohmann::json &controllers = config_json["controllers"]; + for (int i = 0; i < controller_count; i++) { + nlohmann::json &controller = controllers[i]; + controller["guid"] = recomp::get_controller_guid(i); + controller["profile"] = recomp::get_input_profile_key(recomp::get_controller_profile_index(i)); } return save_json_with_backups(path, config_json); } -bool load_input_device_from_json(const nlohmann::json& config_json, int controller_num, recomp::InputDevice device, const std::string& key) { +bool load_input_device_from_json(const nlohmann::json& config_json, int profile_index, recomp::InputDevice device, const std::string& key) { // Check if the json object for the given key exists. auto find_it = config_json.find(key); if (find_it == config_json.end()) { @@ -434,8 +456,7 @@ bool load_input_device_from_json(const nlohmann::json& config_json, int controll auto find_input_it = mappings_json.find(input_name); if (find_input_it == mappings_json.end() || !find_input_it->is_array()) { assign_mapping( - controller_num, - device, + profile_index, cur_input, recomp::get_default_mapping_for_input( device == recomp::InputDevice::Keyboard ? @@ -452,23 +473,13 @@ bool load_input_device_from_json(const nlohmann::json& config_json, int controll for (size_t binding_index = 0; binding_index < std::min(recomp::bindings_per_input, input_json.size()); binding_index++) { recomp::InputField cur_field{}; recomp::from_json(input_json[binding_index], cur_field); - recomp::set_input_binding(controller_num, cur_input, binding_index, device, cur_field); + recomp::set_input_binding(profile_index, cur_input, binding_index, cur_field); } } return true; } -void load_bindings_config(const nlohmann::json &config_json, int controller_num) { - if (!load_input_device_from_json(config_json, controller_num, recomp::InputDevice::Keyboard, "keyboard")) { - assign_all_mappings(controller_num, recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); - } - - if (!load_input_device_from_json(config_json, controller_num, recomp::InputDevice::Controller, "controller")) { - assign_all_mappings(controller_num, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); - } -} - bool load_controls_config(const std::filesystem::path& path) { nlohmann::json config_json{}; if (!read_json_with_backups(path, config_json)) { @@ -477,29 +488,48 @@ bool load_controls_config(const std::filesystem::path& path) { auto version_it = config_json.find("version"); if (version_it != config_json.end()) { - auto players_it = config_json.find("players"); - for (size_t i = 0; i < 4; i++) { - const bool player_exists = players_it != config_json.end() && players_it->is_array() && (players_it->size() > i); - const nlohmann::json &player = player_exists ? (*players_it)[i] : nlohmann::json(); - load_bindings_config(player, (int)(i)); + auto profiles = config_json.find("profiles"); + if (profiles == config_json.end() || !profiles->is_array()) { + return false; + } - auto controller_it = player.find("controller"); - if (controller_it != player.end()) { - const nlohmann::json &controller = *controller_it; - auto guid_it = controller.find("guid"); - if (guid_it != controller.end()) { - recomp::set_input_controller_guid((int)(i), *guid_it); + for (const nlohmann::json &profile : *profiles) { + std::string key = profile.value("key", std::string()); + std::string name = profile.value("name", std::string()); + recomp::InputDevice device = profile.value("device", recomp::InputDevice::COUNT); + bool custom = profile.value("custom", false); + if (!key.empty() && !name.empty() && device != recomp::InputDevice::COUNT) { + int profile_index = recomp::add_input_profile(key, name, device, custom); + if (!load_input_device_from_json(profile, profile_index, device, "mappings")) { + assign_all_mappings(profile_index, device == recomp::InputDevice::Keyboard ? recomp::default_n64_keyboard_mappings : recomp::default_n64_controller_mappings); + } + } + } + + auto controllers = config_json.find("controllers"); + if (controllers == config_json.end() || !controllers->is_array()) { + return false; + } + + for (const nlohmann::json &controller : *controllers) { + auto guid = controller.find("guid"); + auto profile = controller.find("profile"); + if (guid != controller.end() && guid->is_object() && profile != controller.end() && profile->is_string()) { + int profile_index = recomp::get_input_profile_by_key(*profile); + if (profile_index >= 0) { + recomp::add_controller(*guid, profile_index); } } } } else { // Version 1 of the format only had bindings for Player 1 on the root element. - load_bindings_config(config_json, 0); + if (!load_input_device_from_json(config_json, keyboard_sp_profile_index, recomp::InputDevice::Keyboard, "keyboard")) { + assign_all_mappings(keyboard_sp_profile_index, recomp::default_n64_keyboard_mappings); + } - for (int i = 1; i < 4; i++) { - // Assign defaults for every other controller. - assign_all_mappings(i, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); + if (!load_input_device_from_json(config_json, controller_sp_profile_index, recomp::InputDevice::Controller, "controller")) { + assign_all_mappings(controller_sp_profile_index, recomp::default_n64_controller_mappings); } } @@ -553,6 +583,8 @@ void banjo::load_config() { save_graphics_config(graphics_path); } + banjo::initialize_input_bindings(); + if (!load_controls_config(controls_path)) { banjo::reset_input_bindings(); save_controls_config(controls_path); diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 2c51266..549f2f6 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -1,5 +1,7 @@ #include +#include "xxHash/xxh3.h" + #include "librecomp/helpers.hpp" #include "recomp_input.h" #include "ultramodern/ultramodern.hpp" @@ -7,9 +9,27 @@ // Arrays that hold the mappings for every input for keyboard and controller respectively. using input_mapping = std::array; using input_mapping_array = std::array(recomp::GameInput::COUNT)>; -static std::array keyboard_input_mappings{}; -static std::array controller_input_mappings{}; -static std::array controller_guids{}; + +struct InputProfile { + std::string key; + std::string name; + recomp::InputDevice device; + input_mapping_array mappings; + bool custom = false; +}; + +static std::vector input_profiles{}; +static std::array, 4> players_input_profile_indices{}; +static std::unordered_map input_profile_key_index_map{}; +static std::vector input_profile_custom_indices[2]{}; + +struct Controller { + recomp::ControllerGUID guid; + int profile_index; +}; + +static std::vector controllers; +static std::unordered_map controller_hash_index_map{}; // Make the button value array, which maps a button index to its bit field. #define DEFINE_INPUT(name, value, readable) uint16_t(value##u), @@ -54,10 +74,8 @@ recomp::GameInput recomp::get_input_from_enum_name(const std::string_view enum_n } // Due to an RmlUi limitation this can't be const. Ideally it would return a const reference or even just a straight up copy. -recomp::InputField& recomp::get_input_binding(int controller_num, GameInput input, size_t binding_index, recomp::InputDevice device) { - input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings[controller_num] : keyboard_input_mappings[controller_num]; - input_mapping& cur_input_mapping = device_mappings.at(static_cast(input)); - +recomp::InputField& recomp::get_input_binding(int profile_index, GameInput input, size_t binding_index) { + input_mapping& cur_input_mapping = input_profiles[profile_index].mappings.at(static_cast(input)); if (binding_index < cur_input_mapping.size()) { return cur_input_mapping[binding_index]; } @@ -67,49 +85,223 @@ recomp::InputField& recomp::get_input_binding(int controller_num, GameInput inpu } } -void recomp::set_input_binding(int controller_num, recomp::GameInput input, size_t binding_index, recomp::InputDevice device, recomp::InputField value) { - input_mapping_array &device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings[controller_num] : keyboard_input_mappings[controller_num]; - input_mapping& cur_input_mapping = device_mappings.at(static_cast(input)); - +void recomp::set_input_binding(int profile_index, recomp::GameInput input, size_t binding_index, recomp::InputField value) { + input_mapping& cur_input_mapping = input_profiles[profile_index].mappings.at(static_cast(input)); if (binding_index < cur_input_mapping.size()) { cur_input_mapping[binding_index] = value; } } -void recomp::set_input_controller_guid(int controller_num, const ControllerGUID &guid) { - controller_guids[controller_num] = guid; +int recomp::add_input_profile(const std::string &key, const std::string &name, InputDevice device, bool custom) { + auto it = input_profile_key_index_map.find(key); + if (it != input_profile_key_index_map.end()) { + return it->second; + } + + int index = (int)(input_profiles.size()); + InputProfile profile; + profile.key = key; + profile.name = name; + profile.device = device; + profile.custom = custom; + input_profiles.emplace_back(profile); + input_profile_key_index_map.emplace(key, index); + + if (custom) { + switch (device) { + case InputDevice::Controller: + input_profile_custom_indices[0].emplace_back(index); + break; + case InputDevice::Keyboard: + input_profile_custom_indices[1].emplace_back(index); + break; + default: + assert(false && "Unknown input device."); + break; + } + } + + return index; } -recomp::ControllerGUID recomp::get_input_controller_guid(int controller_num) { - return controller_guids[controller_num]; +int recomp::get_input_profile_by_key(const std::string &key) { + auto it = input_profile_key_index_map.find(key); + if (it != input_profile_key_index_map.end()) { + return it->second; + } + else { + return -1; + } } -bool recomp::get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out) { +const std::string &recomp::get_input_profile_key(int profile_index) { + return input_profiles[profile_index].key; +} + +const std::string &recomp::get_input_profile_name(int profile_index) { + return input_profiles[profile_index].name; +} + +recomp::InputDevice recomp::get_input_profile_device(int profile_index) { + return input_profiles[profile_index].device; +} + +bool recomp::is_input_profile_custom(int profile_index) { + return input_profiles[profile_index].custom; +} + +int recomp::get_input_profile_count() { + return (int)(input_profiles.size()); +} + +const std::vector recomp::get_indices_for_custom_profiles(InputDevice device) { + static std::vector empty; + + switch (device) { + case InputDevice::Controller: + return input_profile_custom_indices[0]; + case InputDevice::Keyboard: + return input_profile_custom_indices[1]; + default: + assert(false && "Unknown input device."); + return empty; + } +} + +void recomp::set_input_profile_for_player(int player_index, int profile_index, InputDevice device) { + switch (device) { + case InputDevice::Controller: + players_input_profile_indices[player_index].first = profile_index; + break; + case InputDevice::Keyboard: + players_input_profile_indices[player_index].second = profile_index; + break; + default: + assert(false && "Unknown input device."); + break; + } +} + +int recomp::get_input_profile_for_player(int player_index, InputDevice device) { + switch (device) { + case InputDevice::Controller: + return players_input_profile_indices[player_index].first; + case InputDevice::Keyboard: + return players_input_profile_indices[player_index].second; + default: + assert(false && "Unknown input device."); + return -1; + } +} + +int recomp::add_controller(ControllerGUID guid, int profile_index) { + auto it = controller_hash_index_map.find(guid.hash); + if (it != controller_hash_index_map.end()) { + controllers[it->second].profile_index = profile_index; + return it->second; + } + + int index = (int)(controllers.size()); + Controller controller; + controller.guid = guid; + controller.profile_index = profile_index; + controllers.emplace_back(controller); + controller_hash_index_map.emplace(guid.hash, index); + return index; +} + +const recomp::ControllerGUID &recomp::get_controller_guid(int controller_index) { + return controllers[controller_index].guid; +} + +int recomp::get_controller_profile_index(int controller_index) { + return controllers[controller_index].profile_index; +} + +int recomp::get_controller_by_guid(ControllerGUID guid) { + auto it = controller_hash_index_map.find(guid.hash); + if (it != controller_hash_index_map.end()) { + return it->second; + } + else { + return -1; + } +} + +int recomp::get_controller_count() { + return (int)(controllers.size()); +} + +recomp::ControllerGUID recomp::get_guid_from_sdl_controller(SDL_GameController *game_controller) { + if (game_controller == nullptr) { + return {}; + } + + SDL_Joystick *joystick = SDL_GameControllerGetJoystick(game_controller); + if (joystick == nullptr) { + return {}; + } + + Uint16 vendor, product, version, crc16; + const char *joystick_serial = SDL_JoystickGetSerial(joystick); + SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(joystick); + SDL_GetJoystickGUIDInfo(joystick_guid, &vendor, &product, &version, &crc16); + + recomp::ControllerGUID guid; + guid.serial = joystick_serial != nullptr ? joystick_serial : ""; + guid.vendor = vendor; + guid.product = product; + guid.version = version; + guid.crc16 = crc16; + + // Compute the hash from the GUID. + XXH3_state_t state; + XXH3_64bits_reset(&state); + XXH3_64bits_update(&state, &guid.vendor, sizeof(guid.vendor)); + XXH3_64bits_update(&state, &guid.product, sizeof(guid.product)); + XXH3_64bits_update(&state, &guid.version, sizeof(guid.version)); + XXH3_64bits_update(&state, &guid.crc16, sizeof(guid.crc16)); + guid.hash = XXH3_64bits_digest(&state); + + return guid; +} + +std::string recomp::get_string_from_controller_guid(ControllerGUID guid) { + return "SERIAL_" + guid.serial + "_VID_" + std::to_string(guid.vendor) + "_PID_" + std::to_string(guid.product) +"_VERSION_" + std::to_string(guid.version) +"_CRC16_" + std::to_string(guid.crc16); +} + +bool recomp::get_n64_input(int player_index, uint16_t* buttons_out, float* x_out, float* y_out) { uint16_t cur_buttons = 0; float cur_x = 0.0f; float cur_y = 0.0f; if (!recomp::game_input_disabled()) { - for (size_t i = 0; i < n64_button_values.size(); i++) { - size_t input_index = (size_t)GameInput::N64_BUTTON_START + i; - cur_buttons |= recomp::get_input_digital(controller_num, keyboard_input_mappings[controller_num][input_index]) ? n64_button_values[i] : 0; - cur_buttons |= recomp::get_input_digital(controller_num, controller_input_mappings[controller_num][input_index]) ? n64_button_values[i] : 0; - } + auto check_buttons = [&](int profile_index) { + if (profile_index < 0) { + return; + } - float joystick_deadzone = recomp::get_joystick_deadzone() / 100.0f; + const input_mapping_array &mappings = input_profiles[profile_index].mappings; + for (size_t i = 0; i < n64_button_values.size(); i++) { + cur_buttons |= recomp::get_input_digital(player_index, mappings[(size_t)(GameInput::N64_BUTTON_START) + i]) ? n64_button_values[i] : 0; + } + }; - float joystick_x = recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::X_AXIS_POS]) - - recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::X_AXIS_NEG]); + auto check_joystick = [&](int profile_index) { + if (profile_index < 0) { + return; + } - float joystick_y = recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_POS]) - - recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_NEG]); + const input_mapping_array &mappings = input_profiles[profile_index].mappings; + cur_x += recomp::get_input_analog(player_index, mappings[(size_t)GameInput::X_AXIS_POS]) - recomp::get_input_analog(player_index, mappings[(size_t)GameInput::X_AXIS_NEG]); + cur_y += recomp::get_input_analog(player_index, mappings[(size_t)GameInput::Y_AXIS_POS]) - recomp::get_input_analog(player_index, mappings[(size_t)GameInput::Y_AXIS_NEG]); + }; - recomp::apply_joystick_deadzone(joystick_x, joystick_y, &joystick_x, &joystick_y); + check_buttons(players_input_profile_indices[player_index].first); + check_buttons(players_input_profile_indices[player_index].second); - cur_x = recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::X_AXIS_POS]) - - recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::X_AXIS_NEG]) + joystick_x; - - cur_y = recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_POS]) - - recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_NEG]) + joystick_y; + check_joystick(players_input_profile_indices[player_index].first); + recomp::apply_joystick_deadzone(cur_x, cur_y, &cur_x, &cur_y); + check_joystick(players_input_profile_indices[player_index].second); } *buttons_out = cur_buttons; diff --git a/src/game/input.cpp b/src/game/input.cpp index 29ac8aa..69c6b73 100644 --- a/src/game/input.cpp +++ b/src/game/input.cpp @@ -31,7 +31,6 @@ static struct { std::atomic_int32_t mouse_wheel_pos = 0; std::mutex controllers_mutex; std::vector detected_controllers{}; - std::vector detected_controller_options{}; std::array assigned_controllers{}; // Only used when Multiplayer is enabled. std::unordered_map controller_states; bool single_controller = false; @@ -153,8 +152,6 @@ void process_player_assignment(SDL_Event* event) { return; } - recomp::refresh_controller_options(); - switch (event->type) { case SDL_EventType::SDL_KEYDOWN: { SDL_KeyboardEvent* keyevent = &event->key; @@ -303,6 +300,18 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) { SDL_GameControllerSetSensorEnabled(controller, SDL_SensorType::SDL_SENSOR_ACCEL, SDL_TRUE); } } + + recomp::ControllerGUID guid = recomp::get_guid_from_sdl_controller(controller); + if (recomp::get_controller_by_guid(guid) < 0) { + std::string default_profile_key = recomp::get_string_from_controller_guid(guid); + int profile_index = recomp::get_input_profile_by_key(default_profile_key); + if (profile_index < 0) { + profile_index = recomp::add_input_profile(default_profile_key, "Controller", recomp::InputDevice::Controller, false); + banjo::reset_cont_input_bindings(profile_index); + } + + recomp::add_controller(guid, profile_index); + } } break; case SDL_EventType::SDL_CONTROLLERDEVICEREMOVED: @@ -331,9 +340,9 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) { break; case SDL_EventType::SDL_CONTROLLERBUTTONDOWN: if (scanning_device != recomp::InputDevice::COUNT) { - // TODO: Needs the active controller tab index. - auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller); - auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller); + // TODO: Needs the controller profile index. + auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0); + auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1); // note - magic number: 0 is InputType::None if ((menuToggleBinding0.input_type != recomp::InputType::None && event->cbutton.button == menuToggleBinding0.input_id) || (menuToggleBinding1.input_type != recomp::InputType::None && event->cbutton.button == menuToggleBinding1.input_id)) { @@ -629,33 +638,6 @@ const recomp::DefaultN64Mappings recomp::default_n64_controller_mappings = { } }; -// Returns true if the guid can be fetched. -static bool get_controller_guid_from_sdl_controller(SDL_GameController* controller, recomp::ControllerGUID *guid) { - if (controller == nullptr) { - return false; - } - - SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller); - if (joystick == nullptr) { - return false; - } - - Uint16 vendor, product, version, crc16; - const char *joystick_name = SDL_JoystickName(joystick); - const char *joystick_serial = SDL_JoystickGetSerial(joystick); - std::string joystick_name_string = joystick_name != nullptr ? std::string(joystick_name) : "Unknown controller"; - SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(joystick); - int joystick_player_index = SDL_JoystickGetPlayerIndex(joystick); - SDL_GetJoystickGUIDInfo(joystick_guid, &vendor, &product, &version, &crc16); - - *guid = { joystick_serial != nullptr ? std::string(joystick_serial) : std::string(), vendor, product, version, crc16, joystick_player_index }; - return true; -} - -static bool compare_controller_guid(const recomp::ControllerGUID& a, const recomp::ControllerGUID& b) { - return a.vendor == b.vendor && a.product == b.product && a.version == b.version && a.crc16 == b.crc16 && a.serial == b.serial; -} - void recomp::poll_inputs() { InputState.keys = SDL_GetKeyboardState(&InputState.numkeys); InputState.keymod = SDL_GetModState(); @@ -676,71 +658,6 @@ void recomp::poll_inputs() { InputState.detected_controllers.push_back(controller); } } - - if (first_poll) { - first_poll = false; - - // Assign controllers based on configuration. - for (auto& player : InputState.assigned_controllers) { - player.controller = nullptr; - player.is_assigned = false; - player.keyboard_enabled = false; - } - - // FIXME: Use active player count instead of iterating on all possible players. - Uint16 vendor, product, version, crc16; - for (size_t i = 0; i < 4; i++) { - recomp::ControllerGUID controller_guid = recomp::get_input_controller_guid(i); - int min_index_difference = INT_MAX; - size_t j = 0; - while (j < free_controllers.size()) { - SDL_GameController *controller = InputState.detected_controllers[free_controllers[j]]; - recomp::ControllerGUID controller_guid_from_sdl; - if (!get_controller_guid_from_sdl_controller(controller, &controller_guid_from_sdl)) { - // If we can't get the controller guid, skip this controller. - j++; - continue; - } - if (!compare_controller_guid(controller_guid, controller_guid_from_sdl)) { - // If the controller guid doesn't match, skip this controller. - j++; - continue; - } - - // The controller seems to be a match, but we use the controller with the least difference in player index to sort out potential duplicates. - int index_difference = abs(controller_guid.player_index - controller_guid_from_sdl.player_index); - if (min_index_difference > index_difference) { - InputState.assigned_controllers[i].controller = controller; - min_index_difference = index_difference; - free_controllers.erase(free_controllers.begin() + j); - continue; - } - - j++; - } - } - } - - // Do a second pass to assign controllers that are currently unused to the remaining players that failed to be assigned a controller. - for (int i = 0; i < 4; i++) { - if (i == 0) { - InputState.assigned_controllers[i].keyboard_enabled = true; - } - - if (InputState.assigned_controllers[i].controller != nullptr) { - continue; - } - - if (!free_controllers.empty()) { - // Assign a free controller only. - InputState.assigned_controllers[i].controller = InputState.detected_controllers[free_controllers.front()]; - free_controllers.erase(free_controllers.begin()); - } - else { - // Prefer not to assign a controller if none of them are free. - break; - } - } } // Read the deltas while resetting them to zero. @@ -1182,40 +1099,4 @@ std::string recomp::InputField::to_string() const { default: return std::to_string((uint32_t)input_type) + "," + std::to_string(input_id); } -} - -void recomp::refresh_controller_options() { - std::lock_guard lock{ InputState.controllers_mutex }; - InputState.detected_controllers.clear(); - - for (const auto& [id, state] : InputState.controller_states) { - (void)id; // Avoid unused variable warning. - SDL_GameController* controller = state.controller; - if (controller != nullptr) { - InputState.detected_controllers.push_back(controller); - } - } - - InputState.detected_controller_options.clear(); - for (SDL_GameController* controller : InputState.detected_controllers) { - SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller); - if (joystick == nullptr) { - continue; - } - - Uint16 vendor, product, version, crc16; - const char *joystick_name = SDL_JoystickName(joystick); - const char *joystick_serial = SDL_JoystickGetSerial(joystick); - std::string joystick_name_string = joystick_name != nullptr ? std::string(joystick_name) : "Unknown controller"; - SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(joystick); - int joystick_player_index = SDL_JoystickGetPlayerIndex(joystick); - SDL_GetJoystickGUIDInfo(joystick_guid, &vendor, &product, &version, &crc16); - - ControllerGUID guid = { joystick_serial != nullptr ? std::string(joystick_serial) : std::string(), vendor, product, version, crc16, joystick_player_index}; - InputState.detected_controller_options.emplace_back(ControllerOption{ joystick_name_string, guid }); - } -} - -const std::vector &recomp::get_controller_options() { - return InputState.detected_controller_options; -} +} \ No newline at end of file diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index f57f934..e980b50 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -110,8 +110,8 @@ int recomp::get_scanned_input_index() { } void recomp::finish_scanning_input(recomp::InputField scanned_field) { - // TODO: Needs the active controller tab. - recomp::set_input_binding(0, static_cast(scanned_input_index), scanned_binding_index, cur_device, scanned_field); + // TODO: Needs the profile index. + recomp::set_input_binding(0, static_cast(scanned_input_index), scanned_binding_index, scanned_field); scanned_input_index = -1; scanned_binding_index = -1; controls_model_handle.DirtyVariable("inputs"); @@ -594,14 +594,14 @@ public: constructor.BindFunc("gfx_help__apply", [](Rml::Variant& out) { if (cont_active) { - // TODO: Needs the active controller tab. + // TODO: Needs the profile index. out = \ - (recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() != "" ? - " " + recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() : + (recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0).to_string() != "" ? + " " + recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0).to_string() : "" ) + \ - (recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() != "" ? - " " + recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() : + (recomp::get_input_binding(1, recomp::GameInput::APPLY_MENU, 1).to_string() != "" ? + " " + recomp::get_input_binding(1, recomp::GameInput::APPLY_MENU, 1).to_string() : "" ); } else { @@ -646,10 +646,10 @@ public: constructor.BindEventCallback("reset_input_bindings_to_defaults", [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { if (cur_device == recomp::InputDevice::Controller) { - // TODO: Needs the active controller tab. + // TODO: Needs the profile index. banjo::reset_cont_input_bindings(0); } else { - // TODO: Needs the active controller tab. + // TODO: Needs the profile index. banjo::reset_kb_input_bindings(0); } model_handle.DirtyAllVariables(); @@ -662,8 +662,8 @@ public: [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { recomp::GameInput input = static_cast(inputs.at(0).Get()); for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { - // TODO: Needs the active controller tab. - recomp::set_input_binding(0, input, binding_index, cur_device, recomp::InputField{}); + // TODO: Needs the profile index. + recomp::set_input_binding(0, input, binding_index, recomp::InputField{}); } model_handle.DirtyVariable("inputs"); graphics_model_handle.DirtyVariable("gfx_help__apply"); @@ -671,7 +671,7 @@ public: constructor.BindEventCallback("reset_single_input_binding_to_default", [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { - // TODO: Needs the active controller tab. + // TODO: Needs the profile index. recomp::GameInput input = static_cast(inputs.at(0).Get()); banjo::reset_single_input_binding(0, cur_device, input); model_handle.DirtyVariable("inputs"); @@ -710,8 +710,8 @@ public: virtual int Size(void* ptr) override { return recomp::bindings_per_input; } virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { recomp::GameInput input = static_cast((uintptr_t)ptr); - // TODO: Needs the active controller tab. - return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(0, input, address.index, cur_device)}; + // TODO: Needs the profile index. + return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(0, input, address.index)}; } }; // Static instance of the InputField array variable definition to have a fixed pointer to return to RmlUi. @@ -801,22 +801,22 @@ public: }); constructor.BindFunc("nav_help__accept", [](Rml::Variant& out) { - // TODO: Needs the active controller tab. + // TODO: Needs the profile index. if (cont_active) { out = \ - recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller).to_string() + \ - recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller).to_string(); + recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 0).to_string() + \ + recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 1).to_string(); } else { out = PF_KEYBOARD_ENTER; } }); constructor.BindFunc("nav_help__exit", [](Rml::Variant& out) { - // TODO: Needs the active controller tab. + // TODO: Needs the profile index. if (cont_active) { out = \ - recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller).to_string() + \ - recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller).to_string(); + recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0).to_string() + \ + recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1).to_string(); } else { out = PF_KEYBOARD_ESCAPE; } diff --git a/src/ui/ui_state.cpp b/src/ui/ui_state.cpp index 8f7ec65..1aa53bf 100644 --- a/src/ui/ui_state.cpp +++ b/src/ui/ui_state.cpp @@ -466,10 +466,10 @@ bool recompui::try_deque_event(SDL_Event& out) { } int cont_button_to_key(SDL_ControllerButtonEvent& button) { - // TODO: Needs the active controller tab. + // TODO: Needs the profile index. // Configurable accept button in menu - auto menuAcceptBinding0 = recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller); - auto menuAcceptBinding1 = recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller); + auto menuAcceptBinding0 = recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 0); + auto menuAcceptBinding1 = recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 1); // note - magic number: 0 is InputType::None if ((menuAcceptBinding0.input_type != recomp::InputType::None && button.button == menuAcceptBinding0.input_id) || (menuAcceptBinding1.input_type != recomp::InputType::None && button.button == menuAcceptBinding1.input_id)) { @@ -477,8 +477,8 @@ int cont_button_to_key(SDL_ControllerButtonEvent& button) { } // Configurable apply button in menu - auto menuApplyBinding0 = recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller); - auto menuApplyBinding1 = recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller); + auto menuApplyBinding0 = recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0); + auto menuApplyBinding1 = recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 1); // note - magic number: 0 is InputType::None if ((menuApplyBinding0.input_type != recomp::InputType::None && button.button == menuApplyBinding0.input_id) || (menuApplyBinding1.input_type != recomp::InputType::None && button.button == menuApplyBinding1.input_id)) { @@ -486,8 +486,8 @@ int cont_button_to_key(SDL_ControllerButtonEvent& button) { } // Allows closing the menu - auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller); - auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller); + auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0); + auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1); // note - magic number: 0 is InputType::None if ((menuToggleBinding0.input_type != recomp::InputType::None && button.button == menuToggleBinding0.input_id) || (menuToggleBinding1.input_type != recomp::InputType::None && button.button == menuToggleBinding1.input_id)) { @@ -716,9 +716,9 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s } break; case SDL_EventType::SDL_CONTROLLERBUTTONDOWN: - // TODO: Needs the active controller tab. - auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller); - auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller); + // TODO: Needs the profile index. + auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0); + auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1); // note - magic number: 0 is InputType::None if ((menuToggleBinding0.input_type != recomp::InputType::None && cur_event.cbutton.button == menuToggleBinding0.input_id) || (menuToggleBinding1.input_type != recomp::InputType::None && cur_event.cbutton.button == menuToggleBinding1.input_id)) {