diff --git a/include/recomp_input.h b/include/recomp_input.h index 12b1439..ce72ef9 100644 --- a/include/recomp_input.h +++ b/include/recomp_input.h @@ -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 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(); diff --git a/src/game/input.cpp b/src/game/input.cpp index 0d2b511..4d4dc22 100644 --- a/src/game/input.cpp +++ b/src/game/input.cpp @@ -50,26 +50,45 @@ static struct { std::list files_dropped; } DropState; -std::atomic scanning_device = recomp::InputDevice::COUNT; -std::atomic 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(recomp::GameInput::TOGGLE_MENU) || - scanned_input_index == static_cast(recomp::GameInput::ACCEPT_MENU) || - scanned_input_index == static_cast(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(recomp::GameInput::TOGGLE_MENU) || - scanned_input_index == static_cast(recomp::GameInput::ACCEPT_MENU) || - scanned_input_index == static_cast(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() { diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index e980b50..ad8530a 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -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(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(); scanned_binding_index = inputs.at(1).Get(); - recomp::start_scanning_input(cur_device); model_handle.DirtyVariable("active_binding_input"); model_handle.DirtyVariable("active_binding_slot"); }); diff --git a/src/ui/ui_config_page_controls.cpp b/src/ui/ui_config_page_controls.cpp index 5c3f968..9b0c239 100644 --- a/src/ui/ui_config_page_controls.cpp +++ b/src/ui/ui_config_page_controls.cpp @@ -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 game_input_contexts, - on_player_bind_callback on_player_bind + std::vector 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) { diff --git a/src/ui/ui_config_page_controls.h b/src/ui/ui_config_page_controls.h index d04012a..30eb2fe 100644 --- a/src/ui/ui_config_page_controls.h +++ b/src/ui/ui_config_page_controls.h @@ -77,6 +77,8 @@ protected: bool single_player_show_keyboard_mappings = false; + bool awaiting_binding = false; + std::vector game_input_contexts; PlayerBindings game_input_bindings; @@ -105,8 +107,7 @@ public: ConfigPageControls( Element *parent, int num_players, - std::vector game_input_contexts, - on_player_bind_callback on_player_bind + std::vector game_input_contexts ); virtual ~ConfigPageControls(); diff --git a/src/ui/ui_config_page_controls_element.cpp b/src/ui/ui_config_page_controls_element.cpp index 7afde85..9e1bfe5 100644 --- a/src/ui/ui_config_page_controls_element.cpp +++ b/src/ui/ui_config_page_controls_element.cpp @@ -20,22 +20,6 @@ static std::vector 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( &this_compat, recompinput::get_num_players(), - temp_game_input_contexts, - temp_on_bind_player + temp_game_input_contexts ); } diff --git a/src/ui/ui_state.cpp b/src/ui/ui_state.cpp index 1aa53bf..3845ad8 100644 --- a/src/ui/ui_state.cpp +++ b/src/ui/ui_state.cpp @@ -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);