framework_tool_tui/
app.rs1use color_eyre::eyre::Report;
2use framework_lib::chromium_ec::{CrosEc, EcError, EcResponseStatus};
3use ratatui::{prelude::Backend, Terminal};
4use std::{sync::Arc, time::Duration};
5
6use crate::{
7 config::Config,
8 event::{Event, EventLoop},
9 framework::{fingerprint::Fingerprint, info::FrameworkInfo, EcErrorWrapper, Framework},
10 tui::Tui,
11};
12
13pub const APP_TITLE: &str = " Framework System ";
14pub const FOOTER_HELP: &str = "[Tab] Switch panels [Up/Down] Scroll [Enter] Edit/Apply [Left/Right] Adjust value [Esc] Cancel [q] Quit";
15pub const VERSION: &str = env!("CARGO_PKG_VERSION");
16
17pub struct App {
18 framework: Framework,
19 info: FrameworkInfo,
20 running: bool,
21 tui: Tui,
22 config: Config,
23}
24
25pub enum AppEvent {
26 Quit,
27 SetMaxChargeLimit(u8),
28 SetFingerprintBrightness(u8),
29 SetKeyboardBrightness(u8),
30 SetTickInterval(u64),
31}
32
33impl App {
34 pub fn new() -> color_eyre::Result<Self> {
35 let ec = CrosEc::new();
36 let fingerprint = Arc::new(Fingerprint::new(&ec)?);
37 let mut framework = Framework::new(ec, fingerprint.clone());
39 let info = framework.get_info();
40
41 let config = Config::load_or_create()?;
43 let tui = Tui::new(fingerprint, &info, config.clone())?;
44
45 Ok(Self {
46 framework,
47 info,
48 running: true,
49 tui,
50 config,
51 })
52 }
53
54 pub async fn run<B: Backend>(&mut self, terminal: &mut Terminal<B>) -> color_eyre::Result<()>
55 where
56 B::Error: Send + Sync + 'static,
57 {
58 let mut event_loop = EventLoop::new();
59
60 event_loop.run(Duration::from_millis(self.config.tick_interval_ms));
61 self.tui
62 .title
63 .set_tick_interval(self.config.tick_interval_ms);
64
65 while self.running {
66 self.tui.render(terminal, &self.info)?;
67
68 match event_loop.next().await? {
69 Event::Tick => {
70 self.info = self.framework.get_info();
71 }
72 Event::Input(event) => {
73 if let Some(app_event) = self.tui.handle_input(event)? {
74 self.handle_event(app_event, &event_loop)?;
75 }
76 }
77 }
78 }
79
80 Ok(())
81 }
82
83 fn handle_event(&mut self, event: AppEvent, event_loop: &EventLoop) -> color_eyre::Result<()> {
84 match event {
85 AppEvent::Quit => self.quit(),
86 AppEvent::SetMaxChargeLimit(value) => {
87 self.framework.set_max_charge_limit(value)?;
88 self.info.max_charge_limit = Some(value);
89 }
90 AppEvent::SetFingerprintBrightness(percentage) => {
91 match self.framework.set_fp_brightness(percentage) {
92 Err(report) => match report.downcast::<EcErrorWrapper>() {
93 Ok(EcErrorWrapper(EcError::Response(EcResponseStatus::InvalidVersion))) => {
94 self.tui.set_error(
95 "Couldn't set fingerprint brightness. Please, update your BIOS."
96 .to_string(),
97 );
98 }
99 Ok(error) => {
100 return Err(Report::from(error));
101 }
102 Err(report) => {
103 return Err(report);
104 }
105 },
106 Ok(_) => {
107 self.info.fp_brightness_percentage = Some(percentage);
108 }
109 }
110 }
111 AppEvent::SetKeyboardBrightness(percentage) => {
112 self.framework.set_kb_brightness(percentage);
113 self.info.kb_brightness_percentage = Some(percentage);
114 }
115 AppEvent::SetTickInterval(interval_ms) => {
116 self.config.set_tick_interval(interval_ms)?;
117 event_loop.set_tick_interval(Duration::from_millis(interval_ms));
118 self.tui.title.set_tick_interval(interval_ms);
119 }
120 }
121
122 Ok(())
123 }
124
125 fn quit(&mut self) {
126 self.running = false;
127 }
128}