Skip to content

Commit a7beb89

Browse files
authored
tui: add live-adjustable refresh interval (#20)
Signed-off-by: chang-ning <spiderpower02@gmail.com>
1 parent 8c05e50 commit a7beb89

4 files changed

Lines changed: 48 additions & 26 deletions

File tree

src/main.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod stat;
55
mod tui;
66

77
use std::io;
8-
use std::time::{Duration, Instant};
8+
use std::time::Instant;
99

1010
fn run_tui() -> io::Result<()> {
1111
crossterm::terminal::enable_raw_mode()?;
@@ -16,11 +16,10 @@ fn run_tui() -> io::Result<()> {
1616
let mut terminal = ratatui::Terminal::new(backend)?;
1717
let mut app = tui::app::App::new();
1818

19-
let interval = Duration::from_secs(1);
20-
let mut last_refresh = Instant::now() - interval;
19+
let mut last_refresh = Instant::now() - app.refresh_interval;
2120

2221
loop {
23-
if last_refresh.elapsed() >= interval {
22+
if last_refresh.elapsed() >= app.refresh_interval {
2423
app.refresh_stats();
2524
last_refresh = Instant::now();
2625
}

src/tui/app.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::theme::Theme;
22
use crate::net::{self, IfStats, NetRate};
33
use crate::stat::{self, PortStat};
44
use std::collections::VecDeque;
5-
use std::time::Instant;
5+
use std::time::{Duration, Instant};
66

77
use std::collections::HashMap;
88

@@ -77,6 +77,13 @@ impl TableColumn {
7777

7878
pub const BAR_WIDTH: usize = 12;
7979

80+
/// Stats refresh interval bounds (seconds). Floor matches the 200ms event-poll
81+
/// so input stays responsive even at the fastest setting.
82+
pub const REFRESH_DEFAULT_SECS: f64 = 1.0;
83+
const REFRESH_MIN_SECS: f64 = 0.2;
84+
const REFRESH_MAX_SECS: f64 = 10.0;
85+
const REFRESH_STEP_SECS: f64 = 0.5;
86+
8087
/// All counter names that can be added as extra columns.
8188
pub const EXTRA_COUNTERS: &[&str] = &[
8289
"send_bytes",
@@ -274,7 +281,6 @@ pub struct App {
274281
prev_stats: Vec<PortStat>,
275282
prev_ifstats: Vec<IfStats>,
276283
prev_time: Instant,
277-
pub elapsed: f64,
278284
pub rolling_avg: RollingAvgState,
279285
pub show_rolling_avg: bool,
280286
pub show_window_input: bool,
@@ -285,6 +291,7 @@ pub struct App {
285291
pub h_scroll: usize,
286292
pub h_scroll_max: usize,
287293
pub table_offset: usize,
294+
pub refresh_interval: Duration,
288295
cached_display: Vec<PortThroughput>,
289296
}
290297

@@ -346,7 +353,6 @@ impl App {
346353
prev_stats: stats,
347354
prev_ifstats: ifstats,
348355
prev_time: Instant::now(),
349-
elapsed: 1.0,
350356
rolling_avg: RollingAvgState::new(ROLLING_AVG_DEFAULT_WINDOW),
351357
show_rolling_avg: false,
352358
show_window_input: false,
@@ -357,6 +363,7 @@ impl App {
357363
h_scroll: 0,
358364
h_scroll_max: 0,
359365
table_offset: 0,
366+
refresh_interval: Duration::from_secs_f64(REFRESH_DEFAULT_SECS),
360367
cached_display: Vec::new(),
361368
}
362369
}
@@ -370,7 +377,6 @@ impl App {
370377
if elapsed < 0.1 {
371378
return;
372379
}
373-
self.elapsed = elapsed;
374380
self.throughputs = compute_throughputs(&self.prev_stats, &curr, elapsed);
375381
self.prev_stats = curr;
376382

@@ -474,6 +480,18 @@ impl App {
474480
}
475481
}
476482

483+
/// Slow the refresh by one step (longer interval), clamped to the max.
484+
pub fn increase_refresh_interval(&mut self) {
485+
let secs = (self.refresh_interval.as_secs_f64() + REFRESH_STEP_SECS).min(REFRESH_MAX_SECS);
486+
self.refresh_interval = Duration::from_secs_f64(secs);
487+
}
488+
489+
/// Speed the refresh up by one step (shorter interval), clamped to the min.
490+
pub fn decrease_refresh_interval(&mut self) {
491+
let secs = (self.refresh_interval.as_secs_f64() - REFRESH_STEP_SECS).max(REFRESH_MIN_SECS);
492+
self.refresh_interval = Duration::from_secs_f64(secs);
493+
}
494+
477495
pub fn open_window_input(&mut self) {
478496
self.window_input_buf = self.rolling_avg.window_secs.to_string();
479497
self.show_window_input = true;

src/tui/events.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ fn handle_normal_mode(app: &mut App, key: KeyCode) {
5555
KeyCode::Char('a') => app.toggle_rolling_avg(),
5656
KeyCode::Char('+') | KeyCode::Char('=') => app.increase_avg_window(),
5757
KeyCode::Char('-') => app.decrease_avg_window(),
58+
KeyCode::Char('>') => app.increase_refresh_interval(),
59+
KeyCode::Char('<') => app.decrease_refresh_interval(),
5860
KeyCode::Char('w') => app.open_window_input(),
5961
KeyCode::Char('c') => app.open_column_picker(),
6062
_ => {}
@@ -71,6 +73,8 @@ fn handle_detail_mode(app: &mut App, key: KeyCode) {
7173
KeyCode::Char('a') => app.toggle_rolling_avg(),
7274
KeyCode::Char('+') | KeyCode::Char('=') => app.increase_avg_window(),
7375
KeyCode::Char('-') => app.decrease_avg_window(),
76+
KeyCode::Char('>') => app.increase_refresh_interval(),
77+
KeyCode::Char('<') => app.decrease_refresh_interval(),
7478
KeyCode::Char('w') => app.open_window_input(),
7579
KeyCode::Char('c') => app.open_column_picker(),
7680
_ => {}

src/tui/ui.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const HELP_KEYS: &[(&str, &str)] = &[
2525
("+ / =", "Increase avg window (+1s)"),
2626
("-", "Decrease avg window (-1s)"),
2727
("w", "Set avg window (custom)"),
28+
("< / >", "Refresh faster / slower (±0.5s)"),
2829
("c", "Configure columns"),
2930
("h", "Toggle this help"),
3031
("q", "Quit"),
@@ -122,6 +123,12 @@ fn header_line2(app: &App, tc: &ThemeColors) -> Line<'static> {
122123
String::new()
123124
};
124125

126+
let status = format!(
127+
" │ refresh: {:.1}s │ theme: {}",
128+
app.refresh_interval.as_secs_f64(),
129+
app.theme.label()
130+
);
131+
125132
Line::from(vec![
126133
styled(
127134
&format!(" RDMA: {} device{}", n, if n == 1 { "" } else { "s" }),
@@ -134,11 +141,7 @@ fn header_line2(app: &App, tc: &ThemeColors) -> Line<'static> {
134141
styled(&format!("{:.2} Gbps", total_rx), tc.good, false),
135142
styled(" │ Drops: ", tc.muted, false),
136143
styled(&format!("{:.0}/s", total_drops), drop_color, false),
137-
styled(
138-
&format!(" │ {:.1}s │ theme: {}", app.elapsed, app.theme.label()),
139-
tc.muted,
140-
false,
141-
),
144+
styled(&status, tc.muted, false),
142145
styled(&avg_label, tc.accent, false),
143146
])
144147
}
@@ -655,19 +658,17 @@ fn draw_status_bar(frame: &mut Frame, app: &App, area: Rect, tc: &ThemeColors) {
655658
} else {
656659
" a:avg w:set".to_string()
657660
};
658-
let line = Line::from(vec![
659-
Span::styled(
660-
" NORMAL ",
661-
Style::default()
662-
.fg(tc.status_fg)
663-
.bg(tc.status_bg)
664-
.add_modifier(Modifier::BOLD),
665-
),
666-
Span::styled(
667-
format!(" ↑↓/jk:nav {} t:theme h:help q:quit{}", hint, avg_hint),
668-
Style::default().fg(tc.muted),
669-
),
670-
]);
661+
let keys = format!(
662+
" ↑↓/jk:nav {} t:theme <>:refresh h:help q:quit{}",
663+
hint, avg_hint
664+
);
665+
let mode_style = Style::default()
666+
.fg(tc.status_fg)
667+
.bg(tc.status_bg)
668+
.add_modifier(Modifier::BOLD);
669+
let mode = Span::styled(" NORMAL ", mode_style);
670+
let keys = Span::styled(keys, Style::default().fg(tc.muted));
671+
let line = Line::from(vec![mode, keys]);
671672
frame.render_widget(Paragraph::new(line), area);
672673
}
673674

0 commit comments

Comments
 (0)