use std::{error::Error, io, sync::mpsc, thread, time::Duration}; use ratatui::prelude::*; use termion::{ event::Key, input::{MouseTerminal, TermRead}, raw::IntoRawMode, screen::IntoAlternateScreen, }; use crate::{app::App, ui}; pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box> { // setup terminal let stdout = io::stdout() .into_raw_mode() .unwrap() .into_alternate_screen() .unwrap(); let stdout = MouseTerminal::from(stdout); let backend = TermionBackend::new(stdout); let mut terminal = Terminal::new(backend)?; // create app and run it let app = App::new("Termion demo", enhanced_graphics); run_app(&mut terminal, app, tick_rate)?; Ok(()) } fn run_app( terminal: &mut Terminal, mut app: App, tick_rate: Duration, ) -> Result<(), Box> { let events = events(tick_rate); loop { terminal.draw(|f| ui::draw(f, &mut app))?; match events.recv()? { Event::Input(key) => match key { Key::Char(c) => app.on_key(c), Key::Up => app.on_up(), Key::Down => app.on_down(), Key::Left => app.on_left(), Key::Right => app.on_right(), _ => {} }, Event::Tick => app.on_tick(), } if app.should_quit { return Ok(()); } } } enum Event { Input(Key), Tick, } fn events(tick_rate: Duration) -> mpsc::Receiver { let (tx, rx) = mpsc::channel(); let keys_tx = tx.clone(); thread::spawn(move || { let stdin = io::stdin(); for key in stdin.keys().flatten() { if let Err(err) = keys_tx.send(Event::Input(key)) { eprintln!("{err}"); return; } } }); thread::spawn(move || loop { if let Err(err) = tx.send(Event::Tick) { eprintln!("{err}"); break; } thread::sleep(tick_rate); }); rx }