1mod hardware;
11mod input;
12mod metrics;
13mod panels;
14mod render;
15
16pub use hardware::{DiskMetrics, HardwareInfo, LoadMetrics, MemoryBreakdown, NetworkMetrics};
17pub use panels::ActivePanel;
18
19use std::time::{Duration, Instant};
20
21use presentar_terminal::direct::{CellBuffer, DiffRenderer};
22use presentar_terminal::{ColorMode, Theme};
23
24use crate::bricks::generators::SimdLoadBrick;
25use crate::bricks::panels::{
26 ConfigPanelBrick, CpuPanelBrick, GpuPanelBrick, HelpPanelBrick, LoadControlPanelBrick,
27 MemoryPanelBrick, OverviewPanelBrick, PciePanelBrick, ThermalPanelBrick,
28};
29use crate::config::{ComputeBackend, Config, WorkloadType};
30use crate::error::CbtopError;
31use crate::ring_buffer::RingBuffer;
32
33pub struct CbtopApp {
35 pub(crate) config: Config,
37 pub(crate) active_panel: ActivePanel,
39 pub(crate) is_running: bool,
41 pub(crate) intensity: f64,
43 pub(crate) backend: ComputeBackend,
45 pub(crate) workload: WorkloadType,
47 pub(crate) problem_size: usize,
49
50 pub(crate) hardware: HardwareInfo,
52 pub(crate) simd_generator: SimdLoadBrick,
54 pub(crate) load_metrics: LoadMetrics,
56
57 pub(crate) cpu_history: RingBuffer<f64>,
59 pub(crate) bricks_history: RingBuffer<f64>,
61 pub(crate) frame_times: RingBuffer<f64>,
63 pub(crate) last_cpu_stat: Option<(u64, u64)>,
65 pub(crate) last_network_stat: Option<(u64, u64, Instant)>,
67
68 pub(crate) overview_panel: OverviewPanelBrick,
70 pub(crate) cpu_panel: CpuPanelBrick,
71 pub(crate) gpu_panel: GpuPanelBrick,
72 pub(crate) help_panel: HelpPanelBrick,
73 pub(crate) memory_panel: MemoryPanelBrick,
74 pub(crate) thermal_panel: ThermalPanelBrick,
75 pub(crate) pcie_panel: PciePanelBrick,
76 pub(crate) load_panel: LoadControlPanelBrick,
77 pub(crate) config_panel: ConfigPanelBrick,
78
79 pub(crate) buffer: CellBuffer,
81 pub(crate) renderer: DiffRenderer,
83 pub(crate) theme: Theme,
85 pub(crate) last_frame: Instant,
87 pub(crate) frame_count: u64,
89
90 pub(crate) should_quit: bool,
92}
93
94impl CbtopApp {
95 pub fn new(config: Config) -> Result<Self, CbtopError> {
97 let (width, height) = crossterm::terminal::size()?;
98
99 let hardware = HardwareInfo::detect();
101
102 let mut simd_generator = SimdLoadBrick::new(config.problem_size);
104 simd_generator.set_intensity(config.load_profile.intensity());
105
106 Ok(Self {
107 config: config.clone(),
108 active_panel: ActivePanel::Overview,
109 is_running: false,
110 intensity: config.load_profile.intensity(),
111 backend: config.backend,
112 workload: config.workload,
113 problem_size: config.problem_size,
114 hardware,
115 simd_generator,
116 load_metrics: LoadMetrics::default(),
117 cpu_history: RingBuffer::new(120),
118 bricks_history: RingBuffer::new(120),
119 frame_times: RingBuffer::new(60),
120 last_cpu_stat: None,
121 last_network_stat: None,
122 overview_panel: OverviewPanelBrick::new(),
123 cpu_panel: CpuPanelBrick::new(),
124 gpu_panel: GpuPanelBrick::new(),
125 help_panel: HelpPanelBrick::new(),
126 memory_panel: MemoryPanelBrick::new(),
127 thermal_panel: ThermalPanelBrick::new(),
128 pcie_panel: PciePanelBrick::new(),
129 load_panel: LoadControlPanelBrick::new(),
130 config_panel: ConfigPanelBrick::new(),
131 buffer: CellBuffer::new(width, height),
132 renderer: DiffRenderer::with_color_mode(ColorMode::TrueColor),
133 theme: Theme::tokyo_night(),
134 last_frame: Instant::now(),
135 frame_count: 0,
136 should_quit: false,
137 })
138 }
139
140 pub fn run(&mut self) -> Result<(), CbtopError> {
142 use crossterm::{
143 event::{self, Event, KeyEventKind},
144 terminal::{
145 disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
146 },
147 ExecutableCommand,
148 };
149 use std::io::stdout;
150
151 enable_raw_mode()?;
153 stdout().execute(EnterAlternateScreen)?;
154 stdout().execute(crossterm::cursor::Hide)?;
155
156 let refresh = Duration::from_millis(self.config.refresh_ms);
157
158 while !self.should_quit {
159 if event::poll(refresh)? {
161 if let Event::Key(key) = event::read()? {
162 if key.kind == KeyEventKind::Press {
163 self.handle_key(key.code);
164 }
165 }
166 }
167
168 if self.is_running {
170 self.run_load_iteration();
171 }
172
173 self.collect_real_metrics();
175
176 self.render()?;
178 }
179
180 stdout().execute(crossterm::cursor::Show)?;
182 stdout().execute(LeaveAlternateScreen)?;
183 disable_raw_mode()?;
184
185 Ok(())
186 }
187}