From b149a8ecf2c2edb51d5de0c2b3de241464a6610b Mon Sep 17 00:00:00 2001 From: michel Date: Sat, 18 Jul 2026 14:37:35 +0200 Subject: [PATCH] fix: always restore the terminal when the TUI loop errors MainTui::run only disabled mouse capture and called ratatui::restore() after the event loop exited normally; any error propagated by ? from draw/poll/read returned early and left the terminal in raw mode, on the alternate screen, with mouse capture still enabled. The loop body moved into a helper whose result is returned only after the terminal is unconditionally restored. --- soulseek-rs/src/ui/main_tui.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/soulseek-rs/src/ui/main_tui.rs b/soulseek-rs/src/ui/main_tui.rs index b3f80ae..a40326d 100644 --- a/soulseek-rs/src/ui/main_tui.rs +++ b/soulseek-rs/src/ui/main_tui.rs @@ -61,6 +61,24 @@ impl MainTui { } pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { + // Run the event loop, then restore the terminal unconditionally: if the + // loop returns early with an error the terminal must still be taken out + // of raw mode / the alternate screen and mouse capture disabled, or the + // user is left with a corrupted terminal. + let result = self.run_event_loop(&mut terminal); + + use ratatui::crossterm::{event::DisableMouseCapture, execute}; + let _ = execute!(std::io::stdout(), DisableMouseCapture); + ratatui::restore(); + soulseek_rs::utils::logger::disable_buffering(); + + result + } + + fn run_event_loop( + &mut self, + terminal: &mut DefaultTerminal, + ) -> Result<()> { while !self.state.should_exit { terminal.draw(|frame| self.render(frame))?; @@ -87,14 +105,6 @@ impl MainTui { } } - // Disable mouse capture before exiting - use ratatui::crossterm::{event::DisableMouseCapture, execute}; - let _ = execute!(std::io::stdout(), DisableMouseCapture); - - // Restore terminal state - ratatui::restore(); - - soulseek_rs::utils::logger::disable_buffering(); Ok(()) }