Complete tracker TUI
This commit is contained in:
parent
287f416f0b
commit
2d3e4991eb
2 changed files with 63 additions and 11 deletions
56
src/app.rs
56
src/app.rs
|
|
@ -4,21 +4,36 @@ use ratatui::{
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, Row, Table, TableState},
|
widgets::{Block, Borders, Row, Table, TableState},
|
||||||
};
|
};
|
||||||
|
use std::ffi::OsStr;
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
|
use sysinfo::UpdateKind;
|
||||||
|
use sysinfo::Users;
|
||||||
|
|
||||||
pub struct SystemStats {
|
pub struct SystemStats {
|
||||||
pub sys: System,
|
pub sys: System,
|
||||||
pub cpu_count: f32,
|
pub cpu_count: f32,
|
||||||
|
pub users: Users,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_system() -> SystemStats {
|
pub fn init_system() -> SystemStats {
|
||||||
let mut sys = System::new_all();
|
let mut sys = System::new_all();
|
||||||
sys.refresh_cpu_usage();
|
sys.refresh_all();
|
||||||
|
let users = Users::new_with_refreshed_list();
|
||||||
let cpu_count = sys.cpus().len() as f32;
|
let cpu_count = sys.cpus().len() as f32;
|
||||||
SystemStats { sys, cpu_count }
|
SystemStats {
|
||||||
|
sys,
|
||||||
|
cpu_count,
|
||||||
|
users,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_ui(f: &mut Frame, sys: &mut System, cpu_count: f32, state: &mut TableState) {
|
pub fn draw_ui(
|
||||||
|
f: &mut Frame,
|
||||||
|
sys: &mut System,
|
||||||
|
users: &mut Users,
|
||||||
|
cpu_count: f32,
|
||||||
|
state: &mut TableState,
|
||||||
|
) {
|
||||||
let mut processes: Vec<_> = sys.processes().values().collect();
|
let mut processes: Vec<_> = sys.processes().values().collect();
|
||||||
processes.sort_by(|a, b| {
|
processes.sort_by(|a, b| {
|
||||||
b.cpu_usage()
|
b.cpu_usage()
|
||||||
|
|
@ -30,9 +45,21 @@ pub fn draw_ui(f: &mut Frame, sys: &mut System, cpu_count: f32, state: &mut Tabl
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
let cpu_usage = p.cpu_usage();
|
let cpu_usage = p.cpu_usage();
|
||||||
|
let owner_name = if let Some(user_id) = p.user_id() {
|
||||||
|
if let Some(user) = users.get_user_by_id(user_id) {
|
||||||
|
user.name().to_string()
|
||||||
|
} else {
|
||||||
|
format!("UID: {}", user_id.to_string())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"Unknown".to_string()
|
||||||
|
};
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
p.pid().to_string(),
|
p.pid().to_string(),
|
||||||
p.name().to_string_lossy().to_string(),
|
p.name().to_string_lossy().to_string(),
|
||||||
|
p.cmd().join(OsStr::new(" ")).to_string_lossy().to_string(),
|
||||||
|
owner_name,
|
||||||
|
format!("{:.1} bytes", p.memory()),
|
||||||
format!("{:.1}%", cpu_usage / cpu_count),
|
format!("{:.1}%", cpu_usage / cpu_count),
|
||||||
format!("{:.1}%", cpu_usage),
|
format!("{:.1}%", cpu_usage),
|
||||||
])
|
])
|
||||||
|
|
@ -42,19 +69,31 @@ pub fn draw_ui(f: &mut Frame, sys: &mut System, cpu_count: f32, state: &mut Tabl
|
||||||
let widths = [
|
let widths = [
|
||||||
Constraint::Length(10),
|
Constraint::Length(10),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(10),
|
||||||
|
Constraint::Length(20),
|
||||||
|
Constraint::Length(10),
|
||||||
Constraint::Length(10),
|
Constraint::Length(10),
|
||||||
Constraint::Length(15),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let table = Table::new(rows, widths)
|
let table = Table::new(rows, widths)
|
||||||
.header(
|
.header(
|
||||||
Row::new(vec!["PID", "Name", "CPU %", "CPU Core %"]).style(Style::new().blue().bold()),
|
Row::new(vec![
|
||||||
|
"PID",
|
||||||
|
"Name",
|
||||||
|
"Command",
|
||||||
|
"Owner",
|
||||||
|
"Memory",
|
||||||
|
"CPU %",
|
||||||
|
"CPU Core %",
|
||||||
|
])
|
||||||
|
.style(Style::new().blue().bold()),
|
||||||
)
|
)
|
||||||
.block(
|
.block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.title(" Process Manager ")
|
.title(" Process Manager ")
|
||||||
.title_bottom(" Use ↑/↓ to Scroll, 'q' to Quit "),
|
.title_bottom(" Use ↑/↓ to Scroll, 'q' to Quit, 'p' to Pause Refresh "),
|
||||||
)
|
)
|
||||||
.row_highlight_style(Style::new().bg(Color::Cyan).fg(Color::Black).bold())
|
.row_highlight_style(Style::new().bg(Color::Cyan).fg(Color::Black).bold())
|
||||||
.highlight_symbol(">> ");
|
.highlight_symbol(">> ");
|
||||||
|
|
@ -66,6 +105,9 @@ pub fn refresh_system_data(sys: &mut System) {
|
||||||
sys.refresh_processes_specifics(
|
sys.refresh_processes_specifics(
|
||||||
sysinfo::ProcessesToUpdate::All,
|
sysinfo::ProcessesToUpdate::All,
|
||||||
true,
|
true,
|
||||||
sysinfo::ProcessRefreshKind::nothing().with_cpu(),
|
sysinfo::ProcessRefreshKind::nothing()
|
||||||
|
.with_cpu()
|
||||||
|
.with_user(UpdateKind::Always)
|
||||||
|
.with_memory(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/main.rs
12
src/main.rs
|
|
@ -12,6 +12,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
crossterm::terminal::enable_raw_mode()?;
|
crossterm::terminal::enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
crossterm::execute!(stdout, crossterm::terminal::EnterAlternateScreen)?;
|
crossterm::execute!(stdout, crossterm::terminal::EnterAlternateScreen)?;
|
||||||
|
let mut paused: bool = false;
|
||||||
|
|
||||||
let backend = CrosstermBackend::new(stdout);
|
let backend = CrosstermBackend::new(stdout);
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
@ -24,18 +25,27 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| {
|
terminal.draw(|f| {
|
||||||
draw_ui(f, &mut stats.sys, stats.cpu_count, &mut table_state);
|
draw_ui(
|
||||||
|
f,
|
||||||
|
&mut stats.sys,
|
||||||
|
&mut stats.users,
|
||||||
|
stats.cpu_count,
|
||||||
|
&mut table_state,
|
||||||
|
);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if !paused {
|
||||||
if last_tick.elapsed() >= tick_rate {
|
if last_tick.elapsed() >= tick_rate {
|
||||||
refresh_system_data(&mut stats.sys);
|
refresh_system_data(&mut stats.sys);
|
||||||
last_tick = Instant::now();
|
last_tick = Instant::now();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if event::poll(Duration::from_millis(50))? {
|
if event::poll(Duration::from_millis(50))? {
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('q') => break,
|
KeyCode::Char('q') => break,
|
||||||
|
KeyCode::Char('p') => paused = !paused,
|
||||||
KeyCode::Down => {
|
KeyCode::Down => {
|
||||||
let i = match table_state.selected() {
|
let i = match table_state.selected() {
|
||||||
Some(i) => (i + 1).min(stats.sys.processes().len() - 1),
|
Some(i) => (i + 1).min(stats.sys.processes().len() - 1),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue