diff --git a/CHANGELOG.md b/CHANGELOG.md index 205efa541..7ccf76547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added - Highlight lost probes in sample history ([#1247](https://github.com/fujiapple852/trippy/issues/1247)) +- Added `quit-preserve-screen` (default: `shift+q`) key binding to quit Tui without clearing the + screen ([#1382](https://github.com/fujiapple852/trippy/issues/1382)) ### Changed @@ -26,6 +28,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Reverse dns enqueued multiple times when dns-ttl expires ([#1290](https://github.com/fujiapple852/trippy/issues/1290)) - Fixed panic for icmp extensions with malformed length ([#1287](https://github.com/fujiapple852/trippy/issues/1287)) +- Cursor not moved to the bottom on exit when using + `--tui-preserve-screen` ([#1375](https://github.com/fujiapple852/trippy/issues/1375)) ## [0.11.0] - 2024-08-11 diff --git a/README.md b/README.md index e7f601ffb..3b3f837d2 100644 --- a/README.md +++ b/README.md @@ -886,6 +886,7 @@ command line option or in the `bindings` section of the configuration file. | `toggle-as-info` | Toggle AS info display | `z` | | `toggle-hop-details` | Toggle hop details | `d` | | `quit` | Quit the application | `q` | +| `quit-preserve-screen` | Quit the application and preserve the screen | `shift+q`| The supported modifiers are: `shift`, `ctrl`, `alt`, `super`, `hyper` & `meta`. Multiple modifiers may be specified, for example `ctrl+shift+b`. diff --git a/crates/trippy-tui/src/config/binding.rs b/crates/trippy-tui/src/config/binding.rs index dde1da652..371975fc9 100644 --- a/crates/trippy-tui/src/config/binding.rs +++ b/crates/trippy-tui/src/config/binding.rs @@ -47,6 +47,7 @@ pub struct TuiBindings { pub toggle_as_info: TuiKeyBinding, pub toggle_hop_details: TuiKeyBinding, pub quit: TuiKeyBinding, + pub quit_preserve_screen: TuiKeyBinding, } impl Default for TuiBindings { @@ -98,6 +99,10 @@ impl Default for TuiBindings { toggle_as_info: TuiKeyBinding::new(KeyCode::Char('z')), toggle_hop_details: TuiKeyBinding::new(KeyCode::Char('d')), quit: TuiKeyBinding::new(KeyCode::Char('q')), + quit_preserve_screen: TuiKeyBinding::new_with_modifier( + KeyCode::Char('q'), + KeyModifiers::SHIFT, + ), } } } @@ -151,6 +156,10 @@ impl TuiBindings { (self.toggle_as_info, TuiCommandItem::ToggleASInfo), (self.toggle_hop_details, TuiCommandItem::ToggleHopDetails), (self.quit, TuiCommandItem::Quit), + ( + self.quit_preserve_screen, + TuiCommandItem::QuitPreserveScreen, + ), ] .iter() .fold( @@ -326,6 +335,10 @@ impl From<(HashMap, ConfigBindings)> for TuiBindi .get(&TuiCommandItem::Quit) .or(cfg.quit.as_ref()) .unwrap_or(&Self::default().quit), + quit_preserve_screen: *cmd_items + .get(&TuiCommandItem::QuitPreserveScreen) + .or(cfg.quit_preserve_screen.as_ref()) + .unwrap_or(&Self::default().quit_preserve_screen), } } } @@ -628,4 +641,6 @@ pub enum TuiCommandItem { ToggleHopDetails, /// Quit the application. Quit, + /// Quit the application and preserve the screen. + QuitPreserveScreen, } diff --git a/crates/trippy-tui/src/config/file.rs b/crates/trippy-tui/src/config/file.rs index 25b367a73..acada47e5 100644 --- a/crates/trippy-tui/src/config/file.rs +++ b/crates/trippy-tui/src/config/file.rs @@ -398,6 +398,7 @@ pub struct ConfigBindings { pub toggle_as_info: Option, pub toggle_hop_details: Option, pub quit: Option, + pub quit_preserve_screen: Option, } impl Default for ConfigBindings { @@ -442,6 +443,7 @@ impl Default for ConfigBindings { toggle_as_info: Some(bindings.toggle_as_info), toggle_hop_details: Some(bindings.toggle_hop_details), quit: Some(bindings.quit), + quit_preserve_screen: Some(bindings.quit_preserve_screen), } } } diff --git a/crates/trippy-tui/src/frontend.rs b/crates/trippy-tui/src/frontend.rs index 3f1011611..4fd0fda68 100644 --- a/crates/trippy-tui/src/frontend.rs +++ b/crates/trippy-tui/src/frontend.rs @@ -9,6 +9,7 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; +use ratatui::layout::Position; use ratatui::{ backend::{Backend, CrosstermBackend}, Terminal, @@ -43,9 +44,13 @@ pub fn run_frontend( let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; let preserve_screen = tui_config.preserve_screen; - let res = run_app(&mut terminal, traces, tui_config, resolver, geoip_lookup); + let mut app = TuiApp::new(tui_config, resolver, geoip_lookup, traces); + let res = run_app(&mut terminal, &mut app); disable_raw_mode()?; - if !preserve_screen { + if preserve_screen || matches!(res, Ok(ExitAction::PreserveScreen)) { + terminal.set_cursor_position(Position::new(0, terminal.size()?.height))?; + terminal.backend_mut().append_lines(1)?; + } else { execute!(terminal.backend_mut(), LeaveAlternateScreen)?; } terminal.show_cursor()?; @@ -55,22 +60,23 @@ pub fn run_frontend( Ok(()) } +/// The exit action to take when the frontend exits. +enum ExitAction { + /// Exit the frontend normally. + Normal, + /// Preserve the screen on exit. + PreserveScreen, +} + #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] -fn run_app( - terminal: &mut Terminal, - trace_info: Vec, - tui_config: TuiConfig, - resolver: DnsResolver, - geoip_lookup: GeoIpLookup, -) -> io::Result<()> { - let mut app = TuiApp::new(tui_config, resolver, geoip_lookup, trace_info); +fn run_app(terminal: &mut Terminal, app: &mut TuiApp) -> io::Result { loop { if app.frozen_start.is_none() { app.snapshot_trace_data(); app.clamp_selected_hop(); app.update_order_flow_counts(); }; - terminal.draw(|f| render::app::render(f, &mut app))?; + terminal.draw(|f| render::app::render(f, app))?; if event::poll(app.tui_config.refresh_rate)? { if let Event::Key(key) = event::read()? { if key.kind == KeyEventKind::Press { @@ -223,7 +229,9 @@ fn run_app( } else if bindings.toggle_hop_details.check(key) { app.toggle_hop_details(); } else if bindings.quit.check(key) || CTRL_C.check(key) { - return Ok(()); + return Ok(ExitAction::Normal); + } else if bindings.quit_preserve_screen.check(key) { + return Ok(ExitAction::PreserveScreen); } } } diff --git a/crates/trippy-tui/src/frontend/binding.rs b/crates/trippy-tui/src/frontend/binding.rs index 8e0189b98..f74aea421 100644 --- a/crates/trippy-tui/src/frontend/binding.rs +++ b/crates/trippy-tui/src/frontend/binding.rs @@ -43,6 +43,7 @@ pub struct Bindings { pub toggle_as_info: KeyBinding, pub toggle_hop_details: KeyBinding, pub quit: KeyBinding, + pub quit_preserve_screen: KeyBinding, } impl From for Bindings { @@ -85,6 +86,7 @@ impl From for Bindings { toggle_as_info: KeyBinding::from(value.toggle_as_info), toggle_hop_details: KeyBinding::from(value.toggle_hop_details), quit: KeyBinding::from(value.quit), + quit_preserve_screen: KeyBinding::from(value.quit_preserve_screen), } } } diff --git a/crates/trippy-tui/src/frontend/render/settings.rs b/crates/trippy-tui/src/frontend/render/settings.rs index d62a4dce2..49fa5e7ca 100644 --- a/crates/trippy-tui/src/frontend/render/settings.rs +++ b/crates/trippy-tui/src/frontend/render/settings.rs @@ -393,6 +393,10 @@ fn format_binding_settings(app: &TuiApp) -> Vec { format!("{}", binds.toggle_hop_details), ), SettingsItem::new("quit", format!("{}", binds.quit)), + SettingsItem::new( + "quit-preserve-screen", + format!("{}", binds.quit_preserve_screen), + ), ] } diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap index 21d0a648a..01e183af1 100644 --- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap +++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@tui_binding_commands_match.snap @@ -1,4 +1,4 @@ --- source: crates/trippy-tui/src/print.rs --- -TUIbindingcommands:toggle-help,toggle-help-alt,toggle-settings,toggle-settings-tui,toggle-settings-trace,toggle-settings-dns,toggle-settings-geoip,toggle-settings-bindings,toggle-settings-theme,toggle-settings-columns,next-hop,previous-hop,next-trace,previous-trace,next-hop-address,previous-hop-address,address-mode-ip,address-mode-host,address-mode-both,toggle-freeze,toggle-chart,toggle-map,toggle-flows,toggle-privacy,expand-privacy,contract-privacy,expand-hosts,expand-hosts-max,contract-hosts,contract-hosts-min,chart-zoom-in,chart-zoom-out,clear-trace-data,clear-dns-cache,clear-selection,toggle-as-info,toggle-hop-details,quit +TUIbindingcommands:toggle-help,toggle-help-alt,toggle-settings,toggle-settings-tui,toggle-settings-trace,toggle-settings-dns,toggle-settings-geoip,toggle-settings-bindings,toggle-settings-theme,toggle-settings-columns,next-hop,previous-hop,next-trace,previous-trace,next-hop-address,previous-hop-address,address-mode-ip,address-mode-host,address-mode-both,toggle-freeze,toggle-chart,toggle-map,toggle-flows,toggle-privacy,expand-privacy,contract-privacy,expand-hosts,expand-hosts-max,contract-hosts,contract-hosts-min,chart-zoom-in,chart-zoom-out,clear-trace-data,clear-dns-cache,clear-selection,toggle-as-info,toggle-hop-details,quit,quit-preserve-screen diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml index 010d88ff7..64305c9d5 100644 --- a/trippy-config-sample.toml +++ b/trippy-config-sample.toml @@ -424,3 +424,4 @@ clear-selection = "esc" toggle-as-info = "z" toggle-hop-details = "d" quit = "q" +quit-preserve-screen = "shift+q"