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 let mut event_loop = EventLoop::new();
56
57 event_loop.run(Duration::from_millis(self.config.tick_interval_ms));
58 self.tui
59 .title
60 .set_tick_interval(self.config.tick_interval_ms);
61
62 while self.running {
63 self.tui.render(terminal, &self.info)?;
64
65 match event_loop.next().await? {
66 Event::Tick => {
67 self.info = self.framework.get_info();
68 }
69 Event::Input(event) => {
70 if let Some(app_event) = self.tui.handle_input(event)? {
71 self.handle_event(app_event, &event_loop)?;
72 }
73 }
74 }
75 }
76
77 Ok(())
78 }
79
80 fn handle_event(&mut self, event: AppEvent, event_loop: &EventLoop) -> color_eyre::Result<()> {
81 match event {
82 AppEvent::Quit => self.quit(),
83 AppEvent::SetMaxChargeLimit(value) => {
84 self.framework.set_max_charge_limit(value)?;
85 self.info.max_charge_limit = Some(value);
86 }
87 AppEvent::SetFingerprintBrightness(percentage) => {
88 match self.framework.set_fp_brightness(percentage) {
89 Err(report) => match report.downcast::<EcErrorWrapper>() {
90 Ok(EcErrorWrapper(EcError::Response(EcResponseStatus::InvalidVersion))) => {
91 self.tui.set_error(
92 "Couldn't set fingerprint brightness. Please, update your BIOS."
93 .to_string(),
94 );
95 }
96 Ok(error) => {
97 return Err(Report::from(error));
98 }
99 Err(report) => {
100 return Err(report);
101 }
102 },
103 Ok(_) => {
104 self.info.fp_brightness_percentage = Some(percentage);
105 }
106 }
107 }
108 AppEvent::SetKeyboardBrightness(percentage) => {
109 self.framework.set_kb_brightness(percentage);
110 self.info.kb_brightness_percentage = Some(percentage);
111 }
112 AppEvent::SetTickInterval(interval_ms) => {
113 self.config.set_tick_interval(interval_ms)?;
114 event_loop.set_tick_interval(Duration::from_millis(interval_ms));
115 self.tui.title.set_tick_interval(interval_ms);
116 }
117 }
118
119 Ok(())
120 }
121
122 fn quit(&mut self) {
123 self.running = false;
124 }
125}