1use crossterm::event::{Event, EventStream, KeyCode};
2use futures::StreamExt;
3
4use systemstat::Duration;
5
6use std::error::Error;
7use std::sync::{Arc, Mutex};
8
9use crate::display::state::UIState;
10use crate::display::ui::{draw, init_ui, shutdown_ui};
11
12use crate::monitoring::polling::{SystemPollResult, SystemPoller, SystemPollerTarget};
13use crate::monitoring::system::SystemData;
14use crate::ringbuffer::RingBuffer;
15
16enum MFAMessage {
17 Exit,
18}
19
20pub struct MainFrameApp {
21 refresh_rate: f32,
22 poll_rate: f32,
23}
24
25impl Default for MainFrameApp {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl MainFrameApp {
32 pub fn with_refresh_rate(mut self, hz: f32) -> Self {
37 self.refresh_rate = hz;
38
39 self
40 }
41
42 pub fn with_poll_rate(mut self, hz: f32) -> Self {
47 self.poll_rate = hz;
48
49 self
50 }
51
52 pub fn new() -> Self {
59 MainFrameApp {
60 refresh_rate: 20.0,
61 poll_rate: 1.0,
62 }
63 }
64
65 pub async fn run(self) -> Result<(), Box<dyn Error>> {
66 let mut terminal = init_ui()?;
68
69 defer! {
70 shutdown_ui().unwrap();
74 }
75
76 let app_state = UIState::new_shared();
79 let app_data = Arc::new(Mutex::new(SystemData::new_from_poll()));
80
81 let poll_results = Arc::new(Mutex::new(RingBuffer::<SystemPollResult>::new(1)));
82
83 let mut system_poller = SystemPoller::new().with_poll_targets(vec![
84 SystemPollerTarget::CpuUsage,
85 SystemPollerTarget::CpuTemperature,
86 SystemPollerTarget::Gpu,
87 SystemPollerTarget::Memory,
88 ]);
89
90 poll_results.lock().unwrap().add(system_poller.poll());
91
92 let _app_state_handle = app_state.clone();
93 let _app_data_handle = app_data.clone();
94 let _poll_result_handle_poll_thread = poll_results.clone();
95 let _poll_result_handle_draw_thread = poll_results.clone();
96
97 let (ui_tx, mut ui_rx) = tokio::sync::mpsc::unbounded_channel::<MFAMessage>();
99
100 let mut redraw_interval =
101 tokio::time::interval(Duration::from_secs_f32(1.0 / self.refresh_rate));
102
103 let mut polling_interval =
104 tokio::time::interval(Duration::from_secs_f32(1.0 / self.poll_rate));
105
106 let ui_thread = tokio::spawn(async move {
108 loop {
109 redraw_interval.tick().await;
111
112 let msg = match ui_rx.try_recv() {
114 Ok(x) => Some(x),
115 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => None,
116 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
117 return;
118 }
119 };
120
121 match msg {
122 Some(MFAMessage::Exit) => {
123 return;
124 }
125 _ => (),
126 };
127
128 {
129 let mut s = _app_state_handle.lock().unwrap();
130 let d = _app_data_handle.lock().unwrap();
131 let r = _poll_result_handle_draw_thread.lock().unwrap();
132
133 terminal.draw(|f| draw(&mut s, &d, &r, f)).unwrap();
135 }
136 }
137 });
138
139 let polling_thread = tokio::spawn(async move {
141 loop {
142 polling_interval.tick().await;
143
144 let poll_result = system_poller.poll();
145
146 {
147 let mut p = _poll_result_handle_poll_thread.lock().unwrap();
148
149 p.add(poll_result);
150 }
151 }
152 });
153
154 let mut events = EventStream::new();
155
156 'mainloop: loop {
158 match events.next().await {
160 Some(Ok(Event::Key(evnt))) => match evnt.code {
161 KeyCode::Char('q') => {
163 ui_tx.send(MFAMessage::Exit).unwrap();
164 break 'mainloop;
165 }
166 _ => (),
177 },
178 Some(Err(e)) => return Err(Box::new(e)),
179 None => break 'mainloop,
180 _ => (),
181 };
182 }
183
184 ui_thread.abort();
185 polling_thread.abort();
186
187 Ok(())
188 }
189}