select fixes and player profile assignment

This commit is contained in:
thecozies
2025-07-29 14:41:37 -05:00
parent 1ac45d11a6
commit ce12002c28
13 changed files with 236 additions and 40 deletions
-1
View File
@@ -14,7 +14,6 @@ namespace banjo {
void load_config();
void save_config();
void initialize_input_bindings();
void reset_input_bindings();
void reset_cont_input_bindings(int profile_index);
void reset_kb_input_bindings(int profile_index);
+7
View File
@@ -204,8 +204,14 @@ namespace recomp {
int get_controller_by_guid(ControllerGUID guid);
int get_controller_count();
ControllerGUID get_guid_from_sdl_controller(SDL_GameController* game_controller);
int get_controller_profile_index_from_sdl_controller(SDL_GameController* game_controller);
std::string get_string_from_controller_guid(ControllerGUID guid);
void initialize_input_bindings();
int get_sp_controller_profile_index();
int get_sp_keyboard_profile_index();
int get_mp_keyboard_profile_index(int player_index);
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();
@@ -288,6 +294,7 @@ namespace recompinput {
AssignedPlayer& get_assigned_player(int player_index, bool temp_player = false);
bool get_player_is_assigned(int player_index);
recomp::InputDevice get_assigned_player_input_device(int player_index);
void start_player_assignment(void);
void stop_player_assignment(void);
void stop_player_assignment_and_close_modal(void);
+7 -23
View File
@@ -34,13 +34,6 @@ 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() {
@@ -328,18 +321,9 @@ void assign_all_mappings(int profile_index, const recomp::DefaultN64Mappings& va
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(keyboard_sp_profile_index, recomp::default_n64_keyboard_mappings);
assign_all_mappings(controller_sp_profile_index, recomp::default_n64_controller_mappings);
assign_all_mappings(recomp::get_sp_keyboard_profile_index(), recomp::default_n64_keyboard_mappings);
assign_all_mappings(recomp::get_sp_controller_profile_index(), recomp::default_n64_controller_mappings);
}
void banjo::reset_cont_input_bindings(int profile_index) {
@@ -524,12 +508,12 @@ bool load_controls_config(const std::filesystem::path& path) {
}
else {
// Version 1 of the format only had bindings for Player 1 on the root element.
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);
if (!load_input_device_from_json(config_json, recomp::get_sp_keyboard_profile_index(), recomp::InputDevice::Keyboard, "keyboard")) {
assign_all_mappings(recomp::get_sp_keyboard_profile_index(), recomp::default_n64_keyboard_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);
if (!load_input_device_from_json(config_json, recomp::get_sp_controller_profile_index(), recomp::InputDevice::Controller, "controller")) {
assign_all_mappings(recomp::get_sp_controller_profile_index(), recomp::default_n64_controller_mappings);
}
}
@@ -583,7 +567,7 @@ void banjo::load_config() {
save_graphics_config(graphics_path);
}
banjo::initialize_input_bindings();
recomp::initialize_input_bindings();
if (!load_controls_config(controls_path)) {
banjo::reset_input_bindings();
+67
View File
@@ -31,6 +31,17 @@ struct Controller {
static std::vector<Controller> controllers;
static std::unordered_map<uint64_t, int> controller_hash_index_map{};
static int keyboard_sp_profile_index = -1;
static int controller_sp_profile_index = -1;
static const std::string keyboard_sp_profile_key = "keyboard_sp";
static const std::string controller_sp_profile_key = "controller_sp";
static const std::string keyboard_sp_profile_name = "Keyboard (SP)";
static const std::string controller_sp_profile_name = "Controller (SP)";
static const std::string keyboard_mp_profile_key = "keyboard_mp_player_"; // + player index
static const std::string keyboard_mp_profile_name = "Keyboard "; // + "(player number)"
// Make the button value array, which maps a button index to its bit field.
#define DEFINE_INPUT(name, value, readable) uint16_t(value##u),
static const std::array n64_button_values = {
@@ -266,6 +277,62 @@ recomp::ControllerGUID recomp::get_guid_from_sdl_controller(SDL_GameController *
return guid;
}
int recomp::get_controller_profile_index_from_sdl_controller(SDL_GameController *game_controller) {
if (game_controller == nullptr) {
return -1;
}
ControllerGUID guid = recomp::get_guid_from_sdl_controller(game_controller);
if (guid.hash == 0) {
return -1;
}
int controller_index = recomp::get_controller_by_guid(guid);
if (controller_index < 0) {
return -1;
}
return recomp::get_controller_profile_index(controller_index);
}
std::string get_mp_keyboard_profile_key(int player_index) {
return keyboard_mp_profile_key + std::to_string(player_index);
}
std::string get_mp_keyboard_profile_name(int player_index) {
return keyboard_mp_profile_name + "(Player " + std::to_string(player_index + 1) + ")";
}
void recomp::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);
for (int i = 0; i < recompinput::get_num_players(); i++) {
recomp::add_input_profile(
get_mp_keyboard_profile_key(i),
get_mp_keyboard_profile_name(i),
recomp::InputDevice::Keyboard,
false
);
}
}
int recomp::get_sp_controller_profile_index() {
return controller_sp_profile_index;
}
int recomp::get_sp_keyboard_profile_index() {
return keyboard_sp_profile_index;
}
int recomp::get_mp_keyboard_profile_index(int player_index) {
return recomp::get_input_profile_by_key(get_mp_keyboard_profile_key(player_index));
}
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);
}
+23
View File
@@ -88,6 +88,21 @@ recompinput::AssignedPlayer& recompinput::get_assigned_player(int player_index,
}
}
recomp::InputDevice recompinput::get_assigned_player_input_device(int player_index) {
if (player_index < 0 || player_index >= recompinput::get_num_players()) {
return recomp::InputDevice::COUNT;
}
const auto& assigned_player = InputState.assigned_controllers[player_index];
if (assigned_player.controller != nullptr) {
return recomp::InputDevice::Controller;
} else if (assigned_player.keyboard_enabled) {
return recomp::InputDevice::Keyboard;
} else {
return recomp::InputDevice::COUNT;
}
}
bool recompinput::get_player_is_assigned(int player_index) {
if (player_index < 0 || player_index >= recompinput::get_num_players()) {
return false;
@@ -122,6 +137,14 @@ void recompinput::commit_player_assignment() {
for (int i = 0; i < recompinput::get_num_players(); i++) {
InputState.assigned_controllers[i] = player_assignment_state.temp_assigned_players[i];
if (InputState.assigned_controllers[i].controller != nullptr) {
int cont_profile_index = recomp::get_controller_profile_index_from_sdl_controller(InputState.assigned_controllers[i].controller);
if (cont_profile_index >= 0) {
recomp::set_input_profile_for_player(i, cont_profile_index, InputDevice::Controller);
}
} else {
recomp::set_input_profile_for_player(i, recomp::get_mp_keyboard_profile_index(i), InputDevice::Keyboard);
}
}
}
+12
View File
@@ -547,6 +547,18 @@ Element *Element::select_add_option(std::string_view text, std::string_view valu
return option_element;
}
void Element::select_set_selection(std::string_view option_value) {
if (base->GetTagName() != "select") {
return;
}
Rml::ElementFormControlSelect* select = (Rml::ElementFormControlSelect *)(base);
if (!select) {
return;
}
select->SetValue(std::string(option_value));
}
Element Element::get_element_with_tag_name(std::string_view tag_name) {
for (int i = 0; i < base->GetNumChildren(true); i++) {
Rml::Element* child = base->GetChild(i);
+1
View File
@@ -105,6 +105,7 @@ public:
bool is_pseudo_class_set(Rml::String pseudo_class);
Element *select_add_option(std::string_view text, std::string_view value);
void select_set_selection(std::string_view option_value);
};
void queue_ui_callback(recompui::ResourceId resource, const Event& e, const UICallback& callback);
+7 -3
View File
@@ -85,15 +85,14 @@ namespace recompui {
constexpr float select_element_caret_size = 24.0f;
Select::Select(
Element *parent, std::vector<SelectOption> options, const std::string &label, const std::string &default_text
Element *parent, std::vector<SelectOption> options, std::string selected_option_value
) :
Element(
parent,
Events(EventType::Text, EventType::Click, EventType::Hover, EventType::Enable, EventType::Focus),
"select",
false),
label(label),
default_text(default_text)
selected_option_value(selected_option_value)
{
this->options = options;
@@ -285,6 +284,11 @@ namespace recompui {
auto &option = options[i];
Option *option_element = context.create_element<Option>(this, option);
option_elements.push_back(option_element);
if (!selected_option_value.empty() && option.value == selected_option_value) {
set_selection(selected_option_value);
selected_option_index = i;
}
}
}
};
+9 -3
View File
@@ -41,8 +41,7 @@ namespace recompui {
class Select : public Element {
protected:
int selected_option_index = -1;
std::string label;
std::string default_text;
std::string selected_option_value;
Element *wrapper = nullptr;
Svg *arrow = nullptr;
std::vector<SelectOption> options;
@@ -59,12 +58,19 @@ namespace recompui {
virtual void process_event(const Event &e) override;
std::string_view get_type_name() override { return "Select"; }
public:
Select(Element *parent, std::vector<SelectOption> options = {}, const std::string &label = "", const std::string &default_text = "");
Select(
Element *parent,
std::vector<SelectOption> options = {},
std::string selected_option_value = ""
);
void add_change_callback(std::function<void(SelectOption& option, int option_index)> callback);
Style* get_hover_style() { return &hover_style; }
Style* get_focus_style() { return &focus_style; }
Style* get_disabled_style() { return &disabled_style; }
Style* get_hover_disabled_style() { return &hover_disabled_style; }
void set_selection(std::string_view option_value) {
select_set_selection(option_value);
}
private:
void add_option_elements();
};
+30 -2
View File
@@ -181,7 +181,11 @@ void ConfigPageControls::render_header() {
auto header_left = header->get_left();
header_left->clear_children();
if (multiplayer_view_mappings) {
auto profile_name = context.create_element<Label>(header_left, "Editing: Name of da profile", LabelStyle::Normal);
auto profile_name = context.create_element<Label>(
header_left,
"Editing: " + recomp::get_input_profile_name(selected_profile_index),
LabelStyle::Normal
);
} else {
// Nothing rendered here as of now.. maybe single player toggle
}
@@ -196,7 +200,7 @@ void ConfigPageControls::render_header() {
Button* go_back_button = context.create_element<Button>(header_right, "Go back", ButtonStyle::Tertiary);
go_back_button->add_pressed_callback([this]() {
this->multiplayer_view_mappings = false;
this->render_all();
this->force_update();
});
} else {
Button* assign_players_button = context.create_element<Button>(header_right, "Assign players", ButtonStyle::Primary);
@@ -266,10 +270,34 @@ void ConfigPageControls::render_body_players() {
i,
false
);
player_card->set_on_select_profile_callback([this](int player_index, int profile_index) {
this->on_select_player_profile(player_index, profile_index);
});
player_card->set_on_edit_profile_callback([this](int player_index) {
this->on_edit_player_profile(player_index);
});
player_cards.push_back(player_card);
}
}
void ConfigPageControls::on_select_player_profile(int player_index, int profile_index) {
auto& assigned_player = recompinput::get_assigned_player(player_index);
recomp::InputDevice device = recompinput::get_assigned_player_input_device(player_index);
if (device != recomp::InputDevice::COUNT) {
recomp::set_input_profile_for_player(player_index, profile_index, device);
}
}
void ConfigPageControls::on_edit_player_profile(int player_index) {
selected_player = player_index;
recomp::InputDevice device = recompinput::get_assigned_player_input_device(player_index);
if (device != recomp::InputDevice::COUNT) {
selected_profile_index = recomp::get_input_profile_for_player(player_index, device);
multiplayer_view_mappings = true;
force_update();
}
}
void ConfigPageControls::render_footer() {
if (multiplayer_enabled && !multiplayer_view_mappings) {
hide_footer();
+4
View File
@@ -69,6 +69,7 @@ protected:
int update_index = 0;
int selected_player = 0;
int selected_profile_index = -1;
int num_players;
bool multiplayer_enabled;
@@ -91,6 +92,9 @@ private:
void on_option_hover(uint8_t index);
void on_bind_click(recompinput::GameInput game_input, int input_index);
void on_select_player_profile(int player_index, int profile_index);
void on_edit_player_profile(int player_index);
void render_all();
void render_header();
void render_body();
+52 -8
View File
@@ -60,25 +60,57 @@ PlayerCard::PlayerCard(
profile_label->set_margin_top(8.0f);
bool has_controller = recompinput::does_player_have_controller(player_index);
recomp::InputDevice device = has_controller ? recomp::InputDevice::Controller : recomp::InputDevice::Keyboard;
const std::vector<int> profile_indices = recomp::get_indices_for_custom_profiles(device);
int cur_profile = recomp::get_input_profile_for_player(player_index, device);
std::vector<SelectOption> options;
options.emplace_back("Controller", "cont");
options.emplace_back("customprofile", "unassigned");
int device_profile_index = has_controller
? recomp::get_controller_profile_index_from_sdl_controller(assigned_player.controller)
: recomp::get_mp_keyboard_profile_index(player_index);
if (device_profile_index >= 0) {
options.emplace_back(
recomp::get_input_profile_name(device_profile_index),
std::to_string(device_profile_index)
);
}
if (has_controller) {
options.emplace_back(
recomp::get_input_profile_name(recomp::get_sp_controller_profile_index()),
std::to_string(recomp::get_sp_controller_profile_index())
);
} else {
options.emplace_back(
recomp::get_input_profile_name(recomp::get_sp_keyboard_profile_index()),
std::to_string(recomp::get_sp_keyboard_profile_index())
);
}
for (size_t i = 0; i < profile_indices.size(); ++i) {
int profile_index = profile_indices[i];
std::string profile_name = recomp::get_input_profile_name(profile_index);
options.emplace_back(profile_name, std::to_string(profile_index));
}
auto select = context.create_element<Select>(
this,
options,
"Player",
"Select Player"
std::to_string(cur_profile)
);
select->add_change_callback([this, player_index](SelectOption& option, int option_index) {
printf("Selected option: %s at index %d for player %d\n", option.text.c_str(), option_index, player_index);
select->add_change_callback([this](SelectOption& option, int option_index) {
this->on_select_player_profile(std::stoi(option.value));
});
select->set_width(100.0f, Unit::Percent);
select->set_enabled(assigned_player.is_assigned);
auto edit_profile_button = context.create_element<Button>(this, "Edit Profile", ButtonStyle::Secondary, ButtonSize::Medium);
edit_profile_button->add_pressed_callback([this, player_index]() {
printf("Edit Profile button pressed for player %d\n", player_index);
edit_profile_button->add_pressed_callback([this]() {
this->on_edit_profile();
});
edit_profile_button->set_width(100.0f, Unit::Percent);
edit_profile_button->set_enabled(assigned_player.is_assigned);
@@ -88,6 +120,18 @@ PlayerCard::PlayerCard(
PlayerCard::~PlayerCard() {
}
void PlayerCard::on_select_player_profile(int profile_index) {
if (this->on_select_profile_callback) {
this->on_select_profile_callback(this->player_index, profile_index);
}
}
void PlayerCard::on_edit_profile() {
if (this->on_edit_profile_callback) {
this->on_edit_profile_callback(this->player_index);
}
}
void PlayerCard::update_player_card_icon() {
recompinput::AssignedPlayer& assigned_player = recompinput::get_assigned_player(player_index, is_assignment_card);
if (assigned_player.is_assigned) {
+17
View File
@@ -8,6 +8,11 @@
namespace recompui {
// player index, profile index
using on_select_player_profile_callback = std::function<void(int, int)>;
// player index
using on_edit_player_profile_callback = std::function<void(int)>;
class PlayerCard : public Element {
protected:
bool is_open = false;
@@ -17,12 +22,24 @@ protected:
int player_index = -1;
bool is_assignment_card = false;
on_select_player_profile_callback on_select_profile_callback;
on_edit_player_profile_callback on_edit_profile_callback;
std::string_view get_type_name() override { return "PlayerCard"; }
private:
void on_select_player_profile(int profile_index);
void on_edit_profile();
public:
PlayerCard(Element *parent, int player_index, bool is_assignment_card = false);
virtual ~PlayerCard();
void update_assignment_player_card();
void update_player_card_icon();
void set_on_select_profile_callback(on_select_player_profile_callback callback) {
on_select_profile_callback = std::move(callback);
}
void set_on_edit_profile_callback(on_edit_player_profile_callback callback) {
on_edit_profile_callback = std::move(callback);
}
};
} // namespace recompui