reimplement binding system in new menu

This commit is contained in:
thecozies
2025-07-31 11:49:46 -05:00
parent b4f8db80e2
commit 6f53279cf0
7 changed files with 90 additions and 116 deletions
+6 -9
View File
@@ -103,13 +103,7 @@ namespace recomp {
{ recomp::InputDevice::Keyboard, "Keyboard" },
});
void start_scanning_input(InputDevice device);
void stop_scanning_input();
void finish_scanning_input(InputField scanned_field);
void cancel_scanning_input();
void config_menu_set_cont_or_kb(bool cont_interacted);
InputField get_scanned_input();
int get_scanned_input_index();
struct DefaultN64Mappings {
std::vector<InputField> a;
@@ -257,16 +251,19 @@ namespace recomp {
namespace recompinput {
struct BindingState {
bool is_scanning = false;
bool found_binding = false;
bool active = false;
// Designates when binding has been cancelled or completed and the event queue should be purged/ignored.
bool skip_events = false;
int player_index = -1;
recomp::GameInput game_input = recomp::GameInput::COUNT;
int binding_index = -1;
recomp::InputField new_binding = {};
recomp::InputDevice device = recomp::InputDevice::COUNT;
};
void start_scanning_for_binding(int player_index, recomp::GameInput game_input, int binding_index);
void start_scanning_for_binding(int player_index, recomp::GameInput game_input, int binding_index, recomp::InputDevice device);
void stop_scanning_for_binding();
bool is_binding();
BindingState& get_binding_state();
+59 -46
View File
@@ -50,26 +50,45 @@ static struct {
std::list<std::filesystem::path> files_dropped;
} DropState;
std::atomic<recomp::InputDevice> scanning_device = recomp::InputDevice::COUNT;
std::atomic<recomp::InputField> scanned_input;
static recompinput::BindingState binding_state;
static recompinput::PlayerAssignmentState player_assignment_state{};
void recompinput::start_scanning_for_binding(int player_index, recomp::GameInput game_input, int binding_index) {
binding_state.is_scanning = true;
binding_state.found_binding = false;
void recompinput::start_scanning_for_binding(int player_index, recomp::GameInput game_input, int binding_index, recomp::InputDevice device) {
binding_state.active = true;
binding_state.skip_events = false;
binding_state.player_index = player_index;
binding_state.game_input = game_input;
binding_state.binding_index = binding_index;
binding_state.device = device;
}
void recompinput::stop_scanning_for_binding() {
binding_state.is_scanning = false;
binding_state.found_binding = false;
binding_state.player_index = -1;
binding_state.game_input = recomp::GameInput::COUNT;
binding_state.binding_index = -1;
binding_state = recompinput::BindingState{};
binding_state.skip_events = true;
}
bool recompinput::is_binding() {
return binding_state.active;
}
bool is_controller_being_bound(SDL_JoystickID joystick_id) {
if (binding_state.device != recomp::InputDevice::Controller) {
return false;
}
if (recompinput::get_num_players() == 1) {
return true;
}
const auto& player = InputState.assigned_controllers[binding_state.player_index];
if (player.controller != nullptr) {
SDL_JoystickID assigned_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(player.controller));
if (assigned_id == joystick_id) {
return true;
}
}
return false;
}
recompinput::BindingState& recompinput::get_binding_state() {
@@ -176,7 +195,7 @@ void process_player_assignment(SDL_Event* event) {
return;
}
if (!player_assignment_state.is_assigning) {
if (!recompinput::is_player_assignment_active()) {
return;
}
@@ -238,27 +257,17 @@ void process_player_assignment(SDL_Event* event) {
}
void set_scanned_input(recomp::InputField value) {
scanning_device.store(recomp::InputDevice::COUNT);
scanned_input.store(value);
}
recomp::InputField recomp::get_scanned_input() {
recomp::InputField ret = scanned_input.load();
scanned_input.store({});
return ret;
}
void recomp::start_scanning_input(recomp::InputDevice device) {
scanned_input.store({});
scanning_device.store(device);
}
void recomp::stop_scanning_input() {
scanning_device.store(recomp::InputDevice::COUNT);
recomp::set_input_binding(
recomp::get_input_profile_for_player(binding_state.player_index, binding_state.device),
binding_state.game_input,
binding_state.binding_index,
value
);
recompinput::stop_scanning_for_binding();
}
void queue_if_enabled(SDL_Event* event) {
if (!recomp::all_input_disabled()) {
if (!recomp::all_input_disabled() && !binding_state.skip_events) {
recompui::queue_event(*event);
}
}
@@ -297,11 +306,11 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
) {
recompui::toggle_fullscreen();
}
if (scanning_device != recomp::InputDevice::COUNT) {
if (recompinput::is_binding()) {
if (keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_ESCAPE) {
recomp::cancel_scanning_input();
recompinput::stop_scanning_for_binding();
}
else if (scanning_device == recomp::InputDevice::Keyboard) {
else if (binding_state.device == recomp::InputDevice::Keyboard) {
set_scanned_input({ recomp::InputType::Keyboard, keyevent->keysym.scancode });
}
}
@@ -367,21 +376,21 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
queue_if_enabled(event);
break;
case SDL_EventType::SDL_CONTROLLERBUTTONDOWN:
if (scanning_device != recomp::InputDevice::COUNT) {
if (recompinput::is_binding() && is_controller_being_bound(event->cbutton.which)) {
// 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)) {
recomp::cancel_scanning_input();
recompinput::stop_scanning_for_binding();
}
else if (scanning_device == recomp::InputDevice::Controller) {
else if (binding_state.device == recomp::InputDevice::Controller) {
SDL_ControllerButtonEvent* button_event = &event->cbutton;
auto scanned_input_index = recomp::get_scanned_input_index();
if ((scanned_input_index == static_cast<int>(recomp::GameInput::TOGGLE_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::ACCEPT_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::APPLY_MENU)) && (
recomp::GameInput scanning_game_input = binding_state.game_input;
if ((scanning_game_input == recomp::GameInput::TOGGLE_MENU ||
scanning_game_input == recomp::GameInput::ACCEPT_MENU ||
scanning_game_input == recomp::GameInput::APPLY_MENU) && (
button_event->button == SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP ||
button_event->button == SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN ||
button_event->button == SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
@@ -397,11 +406,11 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
}
break;
case SDL_EventType::SDL_CONTROLLERAXISMOTION:
if (scanning_device == recomp::InputDevice::Controller) {
auto scanned_input_index = recomp::get_scanned_input_index();
if (scanned_input_index == static_cast<int>(recomp::GameInput::TOGGLE_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::ACCEPT_MENU) ||
scanned_input_index == static_cast<int>(recomp::GameInput::APPLY_MENU)) {
if (is_controller_being_bound(event->caxis.which)) {
recomp::GameInput scanning_game_input = binding_state.game_input;
if (scanning_game_input == recomp::GameInput::TOGGLE_MENU ||
scanning_game_input == recomp::GameInput::ACCEPT_MENU ||
scanning_game_input == recomp::GameInput::APPLY_MENU) {
break;
}
@@ -518,6 +527,8 @@ void recomp::handle_events() {
SDL_SetRelativeMouseMode(cursor_locked ? SDL_TRUE : SDL_FALSE);
}
binding_state.skip_events = false;
if (!started && ultramodern::is_game_started()) {
started = true;
recompui::process_game_started();
@@ -952,7 +963,9 @@ bool recomp::game_input_disabled() {
bool recomp::all_input_disabled() {
// Disable all input if an input is being polled.
return scanning_device != recomp::InputDevice::COUNT || recompinput::is_player_assignment_active();
return
recompinput::is_player_assignment_active() ||
recompinput::is_binding();
}
bool recomp::get_single_controller_mode() {
-30
View File
@@ -105,35 +105,6 @@ static bool cont_active = true;
static recomp::InputDevice cur_device = recomp::InputDevice::Controller;
int recomp::get_scanned_input_index() {
return scanned_input_index;
}
void recomp::finish_scanning_input(recomp::InputField 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");
controls_model_handle.DirtyVariable("active_binding_input");
controls_model_handle.DirtyVariable("active_binding_slot");
nav_help_model_handle.DirtyVariable("nav_help__accept");
nav_help_model_handle.DirtyVariable("nav_help__exit");
graphics_model_handle.DirtyVariable("gfx_help__apply");
}
void recomp::cancel_scanning_input() {
recomp::stop_scanning_input();
scanned_input_index = -1;
scanned_binding_index = -1;
controls_model_handle.DirtyVariable("inputs");
controls_model_handle.DirtyVariable("active_binding_input");
controls_model_handle.DirtyVariable("active_binding_slot");
nav_help_model_handle.DirtyVariable("nav_help__accept");
nav_help_model_handle.DirtyVariable("nav_help__exit");
graphics_model_handle.DirtyVariable("gfx_help__apply");
}
void recomp::config_menu_set_cont_or_kb(bool cont_interacted) {
if (cont_active != cont_interacted) {
cont_active = cont_interacted;
@@ -638,7 +609,6 @@ public:
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
scanned_input_index = inputs.at(0).Get<size_t>();
scanned_binding_index = inputs.at(1).Get<size_t>();
recomp::start_scanning_input(cur_device);
model_handle.DirtyVariable("active_binding_input");
model_handle.DirtyVariable("active_binding_slot");
});
+18 -5
View File
@@ -95,6 +95,8 @@ GameInputRow::~GameInputRow() {
void GameInputRow::update_bindings(BindingList &new_bindings) {
for (size_t i = 0; i < new_bindings.size(); i++) {
binding_buttons[i]->set_is_binding(false);
// skip update if no changes
if (
new_bindings[i].input_id == bindings[i].input_id &&
@@ -103,7 +105,6 @@ void GameInputRow::update_bindings(BindingList &new_bindings) {
}
binding_buttons[i]->set_binding(new_bindings[i].to_string());
binding_buttons[i]->set_is_binding(false);
bindings[i] = new_bindings[i];
}
}
@@ -127,10 +128,8 @@ void GameInputRow::process_event(const Event &e) {
ConfigPageControls::ConfigPageControls(
Element *parent,
int num_players,
std::vector<GameInputContext> game_input_contexts,
on_player_bind_callback on_player_bind
std::vector<GameInputContext> game_input_contexts
) : ConfigPage(parent) {
this->on_player_bind = on_player_bind;
this->game_input_contexts = game_input_contexts;
this->num_players = num_players;
this->multiplayer_enabled = num_players > 1;
@@ -147,6 +146,10 @@ ConfigPageControls::ConfigPageControls(
void ConfigPageControls::process_event(const Event &e) {
switch (e.type) {
case EventType::Update:
if (awaiting_binding && !recompinput::is_binding()) {
awaiting_binding = false;
update_control_mappings();
}
if (last_update_index != update_index) {
last_update_index = update_index;
render_all();
@@ -397,7 +400,17 @@ ConfigPageControls::~ConfigPageControls() {
}
void ConfigPageControls::on_bind_click(recompinput::GameInput game_input, int input_index) {
on_player_bind(this->selected_player, game_input, input_index);
recompinput::InputDevice device;
if (multiplayer_enabled) {
device = recompinput::get_assigned_player_input_device(this->selected_player);
} else {
device = single_player_show_keyboard_mappings
? recomp::InputDevice::Keyboard
: recomp::InputDevice::Controller;
}
recompinput::start_scanning_for_binding(this->selected_player, game_input, input_index, device);
awaiting_binding = true;
}
void ConfigPageControls::set_selected_player(int player) {
+3 -2
View File
@@ -77,6 +77,8 @@ protected:
bool single_player_show_keyboard_mappings = false;
bool awaiting_binding = false;
std::vector<GameInputContext> game_input_contexts;
PlayerBindings game_input_bindings;
@@ -105,8 +107,7 @@ public:
ConfigPageControls(
Element *parent,
int num_players,
std::vector<GameInputContext> game_input_contexts,
on_player_bind_callback on_player_bind
std::vector<GameInputContext> game_input_contexts
);
virtual ~ConfigPageControls();
+1 -18
View File
@@ -20,22 +20,6 @@ static std::vector<struct GameInputContext> temp_game_input_contexts = {
};
#undef DEFINE_INPUT
struct BindingInfo {
int player_index;
recompinput::GameInput game_input;
int binding_index;
bool is_scanning = false;
};
static BindingInfo temp_binding_info = { 0, recompinput::GameInput::COUNT, 0 };
static void temp_on_bind_player(int player_index, recompinput::GameInput game_input, int binding_index) {
temp_binding_info.player_index = player_index;
temp_binding_info.game_input = game_input;
temp_binding_info.binding_index = binding_index;
temp_binding_info.is_scanning = true;
}
ElementConfigPageControls::ElementConfigPageControls(const Rml::String& tag) : Rml::Element(tag) {
SetProperty(Rml::PropertyId::Display, Rml::Style::Display::Block);
SetProperty("width", "100%");
@@ -47,8 +31,7 @@ ElementConfigPageControls::ElementConfigPageControls(const Rml::String& tag) : R
controls_page = context.create_element<ConfigPageControls>(
&this_compat,
recompinput::get_num_players(),
temp_game_input_contexts,
temp_on_bind_player
temp_game_input_contexts
);
}
+3 -6
View File
@@ -584,6 +584,8 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
static clock::time_point next_repeat_time = {};
static int latest_controller_key_pressed = SDLK_UNKNOWN;
bool all_input_is_disabled = recomp::all_input_disabled();
while (recompui::try_deque_event(cur_event)) {
bool context_capturing_input = recompui::is_context_capturing_input();
bool context_capturing_mouse = recompui::is_context_capturing_mouse();
@@ -596,7 +598,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
}
}
if (!recomp::all_input_disabled()) {
if (!all_input_is_disabled) {
bool is_mouse_input = false;
// Implement some additional behavior for specific events on top of what RmlUi normally does with them.
switch (cur_event.type) {
@@ -747,11 +749,6 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
}
recomp::config_menu_set_cont_or_kb(ui_state->cont_is_active);
recomp::InputField scanned_field = recomp::get_scanned_input();
if (scanned_field != recomp::InputField{}) {
recomp::finish_scanning_input(scanned_field);
}
ui_state->update_primary_input(mouse_moved, non_mouse_interacted);
ui_state->update_focus(mouse_moved, non_mouse_interacted);