use std::io::stdout;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::execute;
use tokio::sync::mpsc;
use tokio::time::{Duration, Instant};
use crate::handler;
use crate::state::App;
use crate::tailer::{TailRequest, UiEvent};
use crate::ui;
const TICK: Duration = Duration::from_millis(16);
const STATUS_TICK: Duration = Duration::from_secs(1);
pub async fn run(
mut app: App,
_tail_tx: mpsc::Sender<TailRequest>,
mut ui_rx: mpsc::Receiver<UiEvent>,
) -> anyhow::Result<()> {
let mut terminal = ratatui::init();
execute!(stdout(), EnableMouseCapture)?;
install_panic_hook();
let (event_tx, mut event_rx) = mpsc::unbounded_channel();
tokio::spawn(async move {
use futures::StreamExt;
let mut reader = crossterm::event::EventStream::new();
while let Some(Ok(event)) = reader.next().await {
if event_tx.send(event).is_err() {
break;
}
}
});
let mut tick = tokio::time::interval(TICK);
let mut last_tick = Instant::now();
let mut last_status_tick = Instant::now();
let demo = crate::autopilot::requested();
let mut pilot: Option<crate::autopilot::Autopilot> = None;
let result = loop {
let now = Instant::now();
let elapsed = now - last_tick;
let _ = app.flow.tick_auto_pan(elapsed);
app.flow.tick_animation(elapsed);
app.tick_camera(elapsed);
app.tick_timeline(elapsed);
last_tick = now;
if let Some(p) = pilot.as_mut() {
for ev in p.tick(elapsed) {
handler::handle_event(&ev, &mut app);
}
}
if now - last_status_tick >= STATUS_TICK {
app.status_tick();
last_status_tick = now;
}
let cursor = pilot.as_ref().map(|p| p.cell());
if let Err(e) = terminal.draw(|frame| {
ui::draw(frame, &mut app);
if let (Some(p), Some(_)) = (pilot.as_ref(), cursor) {
p.draw(frame.buffer_mut());
}
}) {
break Err(e.into());
}
tokio::select! {
_ = tick.tick() => {}
Some(ev) = ui_rx.recv() => app.handle_ui_event(ev),
Some(ev) = event_rx.recv() => {
if route(&ev, &mut app, &mut pilot, demo) {
break Ok(());
}
}
}
let mut quit = false;
while let Ok(ev) = event_rx.try_recv() {
if route(&ev, &mut app, &mut pilot, demo) {
quit = true;
break;
}
}
while let Ok(ev) = ui_rx.try_recv() {
app.handle_ui_event(ev);
}
if quit || app.should_quit {
break Ok(());
}
};
let _ = execute!(stdout(), DisableMouseCapture);
ratatui::restore();
result
}
pub fn install_panic_hook() {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = execute!(stdout(), DisableMouseCapture);
prev(info);
}));
}
fn route(
event: &crossterm::event::Event,
app: &mut App,
pilot: &mut Option<crate::autopilot::Autopilot>,
demo: bool,
) -> bool {
if demo && pilot.is_none() && crate::autopilot::is_trigger(event) {
let start = app
.scrubber_area
.map_or((40, 10), |b| (b.x + b.width / 2, b.y.saturating_sub(6)));
*pilot = Some(crate::autopilot::Autopilot::new(
start,
crate::autopilot::tour(app),
));
return false;
}
if pilot.is_some() && crate::autopilot::is_key_press(event) {
return false;
}
handler::handle_event(event, app)
}