mirror of
https://github.com/BanjoRecomp/BanjoRecomp
synced 2026-06-11 04:37:59 -04:00
Add Input Profiles. (#4)
* Add Input Profiles. * Add capability of setting custom profiles. * Remove leftover early return.
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
+27
-15
@@ -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<int> 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<ControllerOption> &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();
|
||||
|
||||
|
||||
+115
-83
@@ -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<recomp::InputField>& value) {
|
||||
void assign_mapping(int profile_index, recomp::GameInput input, const std::vector<recomp::InputField>& 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<recomp::InputField>& value) {
|
||||
void assign_mapping_complete(int profile_index, recomp::GameInput input, const std::vector<recomp::InputField>& 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<nlohmann::json>(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<nlohmann::json>(profile_count);
|
||||
config_json["controllers"] = std::vector<nlohmann::json>(controller_count);
|
||||
|
||||
for (size_t j = 0; j < recomp::get_num_inputs(); j++) {
|
||||
recomp::GameInput cur_input = static_cast<recomp::GameInput>(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);
|
||||
|
||||
+224
-32
@@ -1,5 +1,7 @@
|
||||
#include <array>
|
||||
|
||||
#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<recomp::InputField, recomp::bindings_per_input>;
|
||||
using input_mapping_array = std::array<input_mapping, static_cast<size_t>(recomp::GameInput::COUNT)>;
|
||||
static std::array<input_mapping_array, 4> keyboard_input_mappings{};
|
||||
static std::array<input_mapping_array, 4> controller_input_mappings{};
|
||||
static std::array<recomp::ControllerGUID, 4> controller_guids{};
|
||||
|
||||
struct InputProfile {
|
||||
std::string key;
|
||||
std::string name;
|
||||
recomp::InputDevice device;
|
||||
input_mapping_array mappings;
|
||||
bool custom = false;
|
||||
};
|
||||
|
||||
static std::vector<InputProfile> input_profiles{};
|
||||
static std::array<std::pair<int, int>, 4> players_input_profile_indices{};
|
||||
static std::unordered_map<std::string, int> input_profile_key_index_map{};
|
||||
static std::vector<int> input_profile_custom_indices[2]{};
|
||||
|
||||
struct Controller {
|
||||
recomp::ControllerGUID guid;
|
||||
int profile_index;
|
||||
};
|
||||
|
||||
static std::vector<Controller> controllers;
|
||||
static std::unordered_map<uint64_t, int> 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<size_t>(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<size_t>(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<size_t>(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<size_t>(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<int> recomp::get_indices_for_custom_profiles(InputDevice device) {
|
||||
static std::vector<int> 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;
|
||||
|
||||
+16
-135
@@ -31,7 +31,6 @@ static struct {
|
||||
std::atomic_int32_t mouse_wheel_pos = 0;
|
||||
std::mutex controllers_mutex;
|
||||
std::vector<SDL_GameController*> detected_controllers{};
|
||||
std::vector<recomp::ControllerOption> detected_controller_options{};
|
||||
std::array<recompinput::AssignedPlayer, recompinput::temp_max_players> assigned_controllers{}; // Only used when Multiplayer is enabled.
|
||||
std::unordered_map<SDL_JoystickID, ControllerState> 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::ControllerOption> &recomp::get_controller_options() {
|
||||
return InputState.detected_controller_options;
|
||||
}
|
||||
}
|
||||
+20
-20
@@ -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<recomp::GameInput>(scanned_input_index), scanned_binding_index, cur_device, scanned_field);
|
||||
// TODO: Needs the profile index.
|
||||
recomp::set_input_binding(0, static_cast<recomp::GameInput>(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<recomp::GameInput>(inputs.at(0).Get<size_t>());
|
||||
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<recomp::GameInput>(inputs.at(0).Get<size_t>());
|
||||
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<recomp::GameInput>((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;
|
||||
}
|
||||
|
||||
+10
-10
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user