1use anyhow::Result;
4
5use crate::tui::runtime::{LoopControl, RuntimeOptions, TuiSession};
6
7use super::event::{Action, AppEvent};
8use super::App;
9
10impl App {
11 pub async fn run(&mut self) -> Result<()> {
12 let mut session = TuiSession::enter(RuntimeOptions::default())?;
13 let mut quit = false;
14
15 while !quit {
16 let events = self.poll_frame_events()?;
17 for event in events {
18 for action in self.map_event(event) {
19 if self.update(action).await? {
20 quit = true;
21 break;
22 }
23 }
24 if quit {
25 break;
26 }
27 }
28
29 session.terminal_mut().draw(|f| self.render(f))?;
30
31 if self.on_frame_end().await? == LoopControl::Break {
32 quit = true;
33 }
34 }
35
36 session.leave()?;
37 Ok(())
38 }
39
40 fn poll_frame_events(&mut self) -> Result<Vec<AppEvent>> {
41 let mut events = self.drain_background_events();
42 if self
43 .startup_splash
44 .as_ref()
45 .is_some_and(|s| s.should_auto_dismiss())
46 {
47 events.push(AppEvent::AutoDismissSplash);
48 }
49
50 if self
51 .startup_update_prompt
52 .as_ref()
53 .is_some_and(|p| p.updating)
54 {
55 return Ok(events);
56 }
57
58 if crossterm::event::poll(std::time::Duration::from_millis(100))? {
59 match crossterm::event::read()? {
60 crossterm::event::Event::Key(key) => events.push(AppEvent::Key(key)),
61 crossterm::event::Event::Paste(text) => events.push(AppEvent::Paste(text)),
62 _ => {}
63 }
64 }
65 Ok(events)
66 }
67
68 async fn on_frame_end(&mut self) -> Result<LoopControl> {
69 if self
70 .startup_update_prompt
71 .as_ref()
72 .is_some_and(|p| p.updating)
73 {
74 self.update(Action::ApplyStartupUpdate).await?;
75 return Ok(LoopControl::Continue);
76 }
77 self.update(Action::ProcessDeferredRomLoad).await?;
78 Ok(LoopControl::Continue)
79 }
80}