mod hardware;
mod input;
mod metrics;
mod panels;
mod render;
pub use hardware::{DiskMetrics, HardwareInfo, LoadMetrics, MemoryBreakdown, NetworkMetrics};
pub use panels::ActivePanel;
use std::time::{Duration, Instant};
use presentar_terminal::direct::{CellBuffer, DiffRenderer};
use presentar_terminal::{ColorMode, Theme};
use crate::bricks::generators::SimdLoadBrick;
use crate::bricks::panels::{
ConfigPanelBrick, CpuPanelBrick, GpuPanelBrick, HelpPanelBrick, LoadControlPanelBrick,
MemoryPanelBrick, OverviewPanelBrick, PciePanelBrick, ThermalPanelBrick,
};
use crate::config::{ComputeBackend, Config, WorkloadType};
use crate::error::CbtopError;
use crate::ring_buffer::RingBuffer;
pub struct CbtopApp {
pub(crate) config: Config,
pub(crate) active_panel: ActivePanel,
pub(crate) is_running: bool,
pub(crate) intensity: f64,
pub(crate) backend: ComputeBackend,
pub(crate) workload: WorkloadType,
pub(crate) problem_size: usize,
pub(crate) hardware: HardwareInfo,
pub(crate) simd_generator: SimdLoadBrick,
pub(crate) load_metrics: LoadMetrics,
pub(crate) cpu_history: RingBuffer<f64>,
pub(crate) bricks_history: RingBuffer<f64>,
pub(crate) frame_times: RingBuffer<f64>,
pub(crate) last_cpu_stat: Option<(u64, u64)>,
pub(crate) last_network_stat: Option<(u64, u64, Instant)>,
pub(crate) overview_panel: OverviewPanelBrick,
pub(crate) cpu_panel: CpuPanelBrick,
pub(crate) gpu_panel: GpuPanelBrick,
pub(crate) help_panel: HelpPanelBrick,
pub(crate) memory_panel: MemoryPanelBrick,
pub(crate) thermal_panel: ThermalPanelBrick,
pub(crate) pcie_panel: PciePanelBrick,
pub(crate) load_panel: LoadControlPanelBrick,
pub(crate) config_panel: ConfigPanelBrick,
pub(crate) buffer: CellBuffer,
pub(crate) renderer: DiffRenderer,
pub(crate) theme: Theme,
pub(crate) last_frame: Instant,
pub(crate) frame_count: u64,
pub(crate) should_quit: bool,
}
impl CbtopApp {
pub fn new(config: Config) -> Result<Self, CbtopError> {
let (width, height) = crossterm::terminal::size()?;
let hardware = HardwareInfo::detect();
let mut simd_generator = SimdLoadBrick::new(config.problem_size);
simd_generator.set_intensity(config.load_profile.intensity());
Ok(Self {
config: config.clone(),
active_panel: ActivePanel::Overview,
is_running: false,
intensity: config.load_profile.intensity(),
backend: config.backend,
workload: config.workload,
problem_size: config.problem_size,
hardware,
simd_generator,
load_metrics: LoadMetrics::default(),
cpu_history: RingBuffer::new(120),
bricks_history: RingBuffer::new(120),
frame_times: RingBuffer::new(60),
last_cpu_stat: None,
last_network_stat: None,
overview_panel: OverviewPanelBrick::new(),
cpu_panel: CpuPanelBrick::new(),
gpu_panel: GpuPanelBrick::new(),
help_panel: HelpPanelBrick::new(),
memory_panel: MemoryPanelBrick::new(),
thermal_panel: ThermalPanelBrick::new(),
pcie_panel: PciePanelBrick::new(),
load_panel: LoadControlPanelBrick::new(),
config_panel: ConfigPanelBrick::new(),
buffer: CellBuffer::new(width, height),
renderer: DiffRenderer::with_color_mode(ColorMode::TrueColor),
theme: Theme::tokyo_night(),
last_frame: Instant::now(),
frame_count: 0,
should_quit: false,
})
}
pub fn run(&mut self) -> Result<(), CbtopError> {
use crossterm::{
event::{self, Event, KeyEventKind},
terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
},
ExecutableCommand,
};
use std::io::stdout;
enable_raw_mode()?;
stdout().execute(EnterAlternateScreen)?;
stdout().execute(crossterm::cursor::Hide)?;
let refresh = Duration::from_millis(self.config.refresh_ms);
while !self.should_quit {
if event::poll(refresh)? {
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press {
self.handle_key(key.code);
}
}
}
if self.is_running {
self.run_load_iteration();
}
self.collect_real_metrics();
self.render()?;
}
stdout().execute(crossterm::cursor::Show)?;
stdout().execute(LeaveAlternateScreen)?;
disable_raw_mode()?;
Ok(())
}
}