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