diff --git a/src/xenia/hid/input_system.cc b/src/xenia/hid/input_system.cc index b6086c72d..883797b3e 100644 --- a/src/xenia/hid/input_system.cc +++ b/src/xenia/hid/input_system.cc @@ -127,7 +127,20 @@ X_RESULT InputSystem::GetState(uint32_t user_index, uint32_t flags, return X_ERROR_SUCCESS; } - return GetStateForUI(user_index, flags, out_state); + X_RESULT result = GetStateForUI(user_index, flags, out_state); + + // Handle consumed buttons - these are buttons that were held when a UI dialog + // closed. We mask them from the game until they are released. + if (result == X_ERROR_SUCCESS && user_index < XUserMaxUserCount && + consumed_buttons_[user_index] != 0) { + uint16_t buttons = out_state->gamepad.buttons; + // Update consumed_buttons_ to only include buttons still being held + consumed_buttons_[user_index] &= buttons; + // Mask consumed buttons from the output + out_state->gamepad.buttons = buttons & ~consumed_buttons_[user_index]; + } + + return result; } X_RESULT InputSystem::GetStateForUI(uint32_t user_index, uint32_t flags, @@ -157,7 +170,19 @@ X_RESULT InputSystem::GetStateForUI(uint32_t user_index, uint32_t flags, void InputSystem::AddUIInputBlocker() { ui_input_blockers_.fetch_add(1); } -void InputSystem::RemoveUIInputBlocker() { ui_input_blockers_.fetch_sub(1); } +void InputSystem::RemoveUIInputBlocker() { + // Before removing the blocker, capture any currently pressed buttons. + // These will be masked from game input until they are released, preventing + // the button press that closed the UI from carrying over into the game. + X_INPUT_STATE state; + for (uint32_t user_index = 0; user_index < XUserMaxUserCount; user_index++) { + if (GetStateForUI(user_index, 1, &state) == X_ERROR_SUCCESS) { + consumed_buttons_[user_index] |= state.gamepad.buttons; + } + } + + ui_input_blockers_.fetch_sub(1); +} X_RESULT InputSystem::SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) { diff --git a/src/xenia/hid/input_system.h b/src/xenia/hid/input_system.h index becd523e7..9183cbba9 100644 --- a/src/xenia/hid/input_system.h +++ b/src/xenia/hid/input_system.h @@ -10,6 +10,7 @@ #ifndef XENIA_HID_INPUT_SYSTEM_H_ #define XENIA_HID_INPUT_SYSTEM_H_ +#include #include #include #include @@ -96,6 +97,11 @@ class InputSystem { // Reference count for UI elements blocking game input std::atomic ui_input_blockers_{0}; + + // Buttons that should be masked from game input until released (per slot). + // This prevents button presses used to close UI dialogs from being + // seen by the game immediately after the dialog closes. + std::array consumed_buttons_{}; }; } // namespace hid diff --git a/src/xenia/ui/context_menu_widget_qt.cc b/src/xenia/ui/context_menu_widget_qt.cc index b3e3b486c..bea55b287 100644 --- a/src/xenia/ui/context_menu_widget_qt.cc +++ b/src/xenia/ui/context_menu_widget_qt.cc @@ -46,6 +46,16 @@ ContextMenuWidgetQt::ContextMenuWidgetQt(QWidget* parent, // Block input to the game while this menu is open input_system_->AddUIInputBlocker(); + // Initialize prev_buttons_ to current state so held buttons aren't + // detected as "just pressed" when the menu opens + hid::X_INPUT_STATE state; + for (uint32_t user_index = 0; user_index < 4; user_index++) { + if (input_system_->GetStateForUI(user_index, 1, &state) == 0) { + prev_buttons_ = state.gamepad.buttons; + break; + } + } + poll_timer_ = new QTimer(this); connect(poll_timer_, &QTimer::timeout, this, &ContextMenuWidgetQt::PollGamepad); @@ -257,8 +267,8 @@ void ContextMenuWidgetQt::PollGamepad() { ActivateFocusedItem(); } - // B button to close - if (pressed & 0x2000) { + // B button, Back button, or Guide button to close + if (pressed & (0x2000 | 0x0020 | 0x0400)) { close(); } diff --git a/src/xenia/ui/gamepad_dialog_qt.cc b/src/xenia/ui/gamepad_dialog_qt.cc index 5b6ae50b2..35e0dbb12 100644 --- a/src/xenia/ui/gamepad_dialog_qt.cc +++ b/src/xenia/ui/gamepad_dialog_qt.cc @@ -115,6 +115,9 @@ void GamepadDialog::PollGamepad() { if (pressed & 0x0020) { // Back button OnGamepadBack(); } + if (pressed & 0x0400) { // Guide button + OnGamepadGuide(); + } // D-pad navigation with repeat // First press (edge detection) diff --git a/src/xenia/ui/gamepad_dialog_qt.h b/src/xenia/ui/gamepad_dialog_qt.h index 647152354..82eb2b2eb 100644 --- a/src/xenia/ui/gamepad_dialog_qt.h +++ b/src/xenia/ui/gamepad_dialog_qt.h @@ -44,6 +44,7 @@ class GamepadDialog : public QDialog { virtual void OnGamepadButtonY() {} virtual void OnGamepadStart() { AcceptFocusedButton(); } virtual void OnGamepadBack() { reject(); } + virtual void OnGamepadGuide() { reject(); } // Override to customize which widgets are focusable virtual bool IsWidgetGamepadFocusable(QWidget* widget) const;