tui-rs
tui-rs copied to clipboard
`panic!` does not show
I tried this both with the Termion
and the RustBox
backend: when my application panics, it will simply quit without printing any messages to the output. This is presumably because the terminal is in raw mode and exits it after the message was printed, thus clearing the screen.
Is there any way to write stuff to console (especially errors), so that I know why my application crashed?
I managed to get it somewhat working with Termion with a custom panic handler. Since I don't know how to explicitly disable raw mode, I simply switch back to the main Screen and add \r
to each println!
command. Here's my current handler, with some copy-paste from Rust's default handler:
fn panic_hook(info: &PanicInfo<'_>) {
let location = info.location().unwrap(); // The current implementation always returns Some
let msg = match info.payload().downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload().downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
}
};
println!("{}thread '<unnamed>' panicked at '{}', {}\r", termion::screen::ToMainScreen, msg, location);
}
It works, but has quite a few drawbacks:
- I couldn't get the Thread's name since the default implementation uses a private API
- Similar for the back trace
I had a similar issue with crossterm and I also used a panic_hook along with better_panic:
fn setup_panic_hook() {
panic::set_hook(Box::new(|panic_info| {
// Exits raw mode.
cleanup_terminal();
better_panic::Settings::auto().create_panic_handler()(panic_info);
}));
}
https://github.com/cjbassi/ytop/blob/master/src/main.rs#L114
With termion backend, the easy workaround is to set a raw handle on startup and to pass it to the hook. Here's a simple case:
use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen;
use tui::backend::TermionBackend;
fn main() {
setup_panic();
let mut tui = tui::Terminal::new(TermionBackend::new(AlternateScreen::from(
std::io::stdout().into_raw_mode().unwrap(),
)))
.unwrap();
tui.clear().unwrap();
panic!("noes");
}
fn setup_panic() {
let raw_handle = std::io::stdout().into_raw_mode().unwrap();
let default_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
raw_handle
.suspend_raw_mode()
.unwrap_or_else(|e| log::error!("Could not suspend raw mode: {}", e));
default_hook(info);
// or better_panic::Settings::new().create_panic_handler()(info);
}));
}
I had a similar issue with crossterm and I also used a panic_hook along with better_panic:
fn setup_panic_hook() { panic::set_hook(Box::new(|panic_info| { // Exits raw mode. cleanup_terminal(); better_panic::Settings::auto().create_panic_handler()(panic_info); })); }
https://github.com/cjbassi/ytop/blob/master/src/main.rs#L114
Thanks for the example, this convinced me to move from termion to crossterm. Crossterm's handling of terminal state (raw mode etc.) seems much more sensible!
How do you accomplish this when using a AlternateScreen
?
Nevermind:
std::panic::set_hook(Box::new(move |x| {
stdout()
.into_raw_mode()
.unwrap()
.suspend_raw_mode()
.unwrap();
write!(stdout().into_raw_mode().unwrap(), "{}", ToMainScreen).unwrap();
write!(stdout(), "{:?}", x).unwrap();
}));