diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp index ffdbb27968..1e179334df 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp @@ -158,13 +158,18 @@ namespace rsx { if (auto iface = std::dynamic_pointer_cast(get(uid))) { - // TODO: Hijack input immediately! + std::lock_guard lock(m_input_stack_guard); + + // Add our interface to the queue m_input_token_stack.push( name, std::move(iface), on_input_loop_enter, on_input_loop_exit, input_loop_override); + + // Signal input thread loop after pushing to avoid a race. + m_input_thread_interrupted = true; } } @@ -184,10 +189,12 @@ namespace rsx { // Avoid tail recursion by reinserting pushed-down items std::vector interrupted_items; - bool in_interrupted_mode = false; while (!m_input_thread_abort) { + // We're about to load the whole list, interruption makes no sense before this point + m_input_thread_interrupted = false; + for (auto&& input_context : m_input_token_stack.pop_all_reversed()) { if (input_context.target->is_detached()) @@ -195,10 +202,18 @@ namespace rsx continue; } - if (in_interrupted_mode) + if (m_input_thread_interrupted) { - interrupted_items.push_back(input_context); - continue; + // Someone just pushed something onto the stack. Check if we already saw it. + if (m_input_token_stack) + { + // We actually have new items to read out. Skip the remaining list. + interrupted_items.push_back(input_context); + continue; + } + + // False alarm, we already saw it. + m_input_thread_interrupted = false; } if (input_context.input_loop_prologue && @@ -209,9 +224,14 @@ namespace rsx } s32 result = 0; + if (!input_context.input_loop_override) [[ likely ]] { - result = input_context.target->run_input_loop(); + result = input_context.target->run_input_loop([this]() + { + // Stop if interrupt status is set or input stack is empty + return !m_input_thread_interrupted || !m_input_token_stack; + }); } else { @@ -220,8 +240,9 @@ namespace rsx if (result == user_interface::selection_code::interrupted) { - // Push back the items onto the stack - in_interrupted_mode = true; + // This dialog was exited prematurely, so we must re-run it's input routine later. + ensure(m_input_thread_interrupted); + ensure(m_input_token_stack); interrupted_items.push_back(input_context); continue; } @@ -236,12 +257,27 @@ namespace rsx } } - if (in_interrupted_mode) + if (!interrupted_items.empty()) { - for (const auto& iface : interrupted_items) + std::lock_guard lock(m_input_stack_guard); + + // We need to rebuild the stack in reverse order here + const auto current_stack = m_input_token_stack.pop_all(); + + // Re-insert interrupted list + for (auto it = interrupted_items.crbegin(); it != interrupted_items.crend(); ++it) + { + m_input_token_stack.push(*it); + } + + // Re-insert the 'new' list oldest-first + for (const auto& iface : current_stack) { m_input_token_stack.push(iface); } + + // Clear + interrupted_items.clear(); } else { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.h b/rpcs3/Emu/RSX/Overlays/overlay_manager.h index d2b0738f54..00a383ed81 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.h @@ -181,6 +181,8 @@ namespace rsx lf_queue m_input_token_stack; atomic_t m_input_thread_abort = false; + atomic_t m_input_thread_interrupted = false; + shared_mutex m_input_stack_guard; std::shared_ptr> m_input_thread; void input_thread_loop(); diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index 798782d2e5..912c1b53d3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -46,7 +46,7 @@ namespace rsx // Singleton instance declaration fontmgr* fontmgr::m_instance = nullptr; - s32 user_interface::run_input_loop() + s32 user_interface::run_input_loop(std::function check_state) { user_interface::thread_bits_allocator thread_bits_alloc(this); @@ -122,8 +122,14 @@ namespace rsx last_button_state[pad_index][button_id] = pressed; }; - while (!m_stop_input_loop && !m_input_loop_interrupted) + while (!m_stop_input_loop) { + if (check_state && !check_state()) + { + // Interrupted externally. + break; + } + if (Emu.IsStopped()) { return selection_code::canceled; @@ -361,9 +367,7 @@ namespace rsx input::SetIntercepted(false); } - m_interactive = false; - - return (m_input_loop_interrupted && !m_stop_input_loop) + return !m_stop_input_loop ? selection_code::interrupted : selection_code::ok; } diff --git a/rpcs3/Emu/RSX/Overlays/overlays.h b/rpcs3/Emu/RSX/Overlays/overlays.h index fca32b5ca4..c248ff2d95 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.h +++ b/rpcs3/Emu/RSX/Overlays/overlays.h @@ -115,7 +115,6 @@ namespace rsx bool m_start_pad_interception = true; atomic_t m_stop_pad_interception = false; atomic_t m_input_thread_detached = false; - atomic_t m_input_loop_interrupted = false; atomic_t thread_bits = 0; bool m_keyboard_input_enabled = false; // Allow keyboard input bool m_keyboard_pad_handler_active = true; // Initialized as true to prevent keyboard input until proven otherwise. @@ -152,8 +151,6 @@ namespace rsx bool is_detached() const { return m_input_thread_detached; } void detach_input() { m_input_thread_detached.store(true); } - void on_input_interrupted() { m_input_loop_interrupted.store(true); } - void on_input_resumed() { m_input_loop_interrupted.store(false); } void update() override {} @@ -164,7 +161,7 @@ namespace rsx virtual void close(bool use_callback, bool stop_pad_interception); - s32 run_input_loop(); + s32 run_input_loop(std::function check_state = nullptr); }; } }