From eee7ae4289e21c388b95307187a5212f69635514 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 5 Dec 2021 02:17:26 -0500 Subject: more refactoring --- src/bin/ttyplay/event.rs | 47 +++++++++++- src/bin/ttyplay/frames.rs | 35 +++++++++ src/bin/ttyplay/main.rs | 187 ++-------------------------------------------- src/bin/ttyplay/timer.rs | 115 ++++++++++++++++++++++++++++ 4 files changed, 200 insertions(+), 184 deletions(-) create mode 100644 src/bin/ttyplay/timer.rs diff --git a/src/bin/ttyplay/event.rs b/src/bin/ttyplay/event.rs index 8b47a5a..260c523 100644 --- a/src/bin/ttyplay/event.rs +++ b/src/bin/ttyplay/event.rs @@ -16,13 +16,13 @@ pub enum TimerAction { Quit, } -pub struct Reader { +struct Reader { pending: async_std::sync::Mutex, cvar: async_std::sync::Condvar, } impl Reader { - pub fn new( + fn new( input: async_std::channel::Receiver, ) -> async_std::sync::Arc { let this = Self { @@ -42,7 +42,7 @@ impl Reader { this } - pub async fn read(&self) -> Option { + async fn read(&self) -> Option { let mut pending = self .cvar .wait_until(self.pending.lock().await, |pending| { @@ -135,3 +135,44 @@ impl Pending { } } } + +pub async fn handle_events( + event_r: async_std::channel::Receiver, + timer_w: async_std::channel::Sender, + mut output: textmode::Output, +) -> anyhow::Result<()> { + let mut display = crate::display::Display::new(); + let mut current_screen = vt100::Parser::default().screen().clone(); + let events = Reader::new(event_r); + while let Some(event) = events.read().await { + match event { + Event::TimerAction(action) => { + timer_w.send(action).await?; + continue; + } + Event::FrameTransition((idx, screen)) => { + current_screen = screen; + display.current_frame(idx); + } + Event::FrameLoaded(n) => { + if let Some(n) = n { + display.total_frames(n); + } else { + display.done_loading(); + } + } + Event::Paused(paused) => { + display.paused(paused); + } + Event::ToggleUi => { + display.toggle_ui(); + } + Event::Quit => { + break; + } + } + display.render(¤t_screen, &mut output).await?; + } + + Ok(()) +} diff --git a/src/bin/ttyplay/frames.rs b/src/bin/ttyplay/frames.rs index 2c70f7d..713e2e9 100644 --- a/src/bin/ttyplay/frames.rs +++ b/src/bin/ttyplay/frames.rs @@ -80,3 +80,38 @@ impl FrameData { }) } } + +pub fn load_from_file( + frames: async_std::sync::Arc>, + fh: async_std::fs::File, + event_w: async_std::channel::Sender, +) { + async_std::task::spawn(async move { + let mut reader = ttyrec::Reader::new(fh); + let size = terminal_size::terminal_size().map_or( + (24, 80), + |(terminal_size::Width(w), terminal_size::Height(h))| (h, w), + ); + let mut parser = vt100::Parser::new(size.0, size.1, 0); + while let Ok(frame) = reader.read_frame().await { + let delay = reader.offset().map_or_else( + || std::time::Duration::from_secs(0), + |time| frame.time - time, + ); + parser.process(&frame.data); + let mut frames = frames.lock_arc().await; + frames + .add_frame(Frame::new(parser.screen().clone(), delay)) + .await; + event_w + .send(crate::event::Event::FrameLoaded(Some(frames.count()))) + .await + .unwrap(); + } + frames.lock_arc().await.done_reading().await; + event_w + .send(crate::event::Event::FrameLoaded(None)) + .await + .unwrap(); + }); +} diff --git a/src/bin/ttyplay/main.rs b/src/bin/ttyplay/main.rs index 6c9fc15..9e14fca 100644 --- a/src/bin/ttyplay/main.rs +++ b/src/bin/ttyplay/main.rs @@ -3,12 +3,11 @@ #![allow(clippy::missing_const_for_fn)] #![allow(clippy::struct_excessive_bools)] -use async_std::prelude::FutureExt as _; - mod display; mod event; mod frames; mod input; +mod timer; #[derive(Debug, structopt::StructOpt)] #[structopt(about = "ttyplay")] @@ -17,181 +16,6 @@ struct Opt { file: std::ffi::OsString, } -fn spawn_frame_reader_task( - event_w: async_std::channel::Sender, - frames: async_std::sync::Arc>, - fh: async_std::fs::File, -) { - async_std::task::spawn(async move { - let mut reader = ttyrec::Reader::new(fh); - let size = terminal_size::terminal_size().map_or( - (24, 80), - |(terminal_size::Width(w), terminal_size::Height(h))| (h, w), - ); - let mut parser = vt100::Parser::new(size.0, size.1, 0); - while let Ok(frame) = reader.read_frame().await { - let delay = reader.offset().map_or_else( - || std::time::Duration::from_secs(0), - |time| frame.time - time, - ); - parser.process(&frame.data); - let mut frames = frames.lock_arc().await; - frames - .add_frame(frames::Frame::new(parser.screen().clone(), delay)) - .await; - event_w - .send(event::Event::FrameLoaded(Some(frames.count()))) - .await - .unwrap(); - } - frames.lock_arc().await.done_reading().await; - event_w.send(event::Event::FrameLoaded(None)).await.unwrap(); - }); -} - -fn spawn_timer_task( - event_w: async_std::channel::Sender, - frames: async_std::sync::Arc>, - timer_r: async_std::channel::Receiver, -) -> async_std::task::JoinHandle<()> { - async_std::task::spawn(async move { - let mut idx = 0; - let mut start_time = std::time::Instant::now(); - let mut paused_time = None; - let mut force_update_time = false; - loop { - enum Res { - Wait(Option), - TimerAction( - Result, - ), - } - let wait = async { - let wait_read = frames.lock_arc().await.wait_for_frame(idx); - if wait_read.await { - let frame = - frames.lock_arc().await.get(idx).unwrap().clone(); - if force_update_time { - let now = std::time::Instant::now(); - start_time = now - frame.delay() - // give a bit of extra time before moving to the - // next frame, otherwise backing up behind two - // frames that are extremely close together - // doesn't work - + std::time::Duration::from_millis(200); - if paused_time.take().is_some() { - paused_time = Some(now); - } - force_update_time = false; - } else if paused_time.is_some() { - std::future::pending::<()>().await; - } else { - async_std::task::sleep( - (start_time + frame.delay()) - .saturating_duration_since( - std::time::Instant::now(), - ), - ) - .await; - } - Res::Wait(Some(frame.into_screen())) - } else { - Res::Wait(None) - } - }; - let action = async { Res::TimerAction(timer_r.recv().await) }; - match wait.race(action).await { - Res::Wait(Some(screen)) => { - event_w - .send(event::Event::FrameTransition((idx, screen))) - .await - .unwrap(); - idx += 1; - } - Res::Wait(None) => { - idx = frames.lock_arc().await.count() - 1; - paused_time = Some(std::time::Instant::now()); - event_w.send(event::Event::Paused(true)).await.unwrap(); - } - Res::TimerAction(Ok(action)) => match action { - event::TimerAction::Pause => { - let now = std::time::Instant::now(); - if let Some(time) = paused_time.take() { - start_time += now - time; - } else { - paused_time = Some(now); - } - event_w - .send(event::Event::Paused(paused_time.is_some())) - .await - .unwrap(); - } - event::TimerAction::FirstFrame => { - idx = 0; - force_update_time = true; - } - event::TimerAction::LastFrame => { - idx = frames.lock_arc().await.count() - 1; - force_update_time = true; - } - // force_update_time will immediately transition to the - // next frame and do idx += 1 on its own - event::TimerAction::NextFrame => { - force_update_time = true; - } - event::TimerAction::PreviousFrame => { - idx = idx.saturating_sub(2); - force_update_time = true; - } - event::TimerAction::Quit => break, - }, - Res::TimerAction(Err(e)) => panic!("{}", e), - } - } - }) -} - -async fn event_loop( - event_r: async_std::channel::Receiver, - timer_w: async_std::channel::Sender, - mut output: textmode::Output, -) -> anyhow::Result<()> { - let mut display = display::Display::new(); - let mut current_screen = vt100::Parser::default().screen().clone(); - let events = event::Reader::new(event_r); - while let Some(event) = events.read().await { - match event { - event::Event::TimerAction(action) => { - timer_w.send(action).await?; - continue; - } - event::Event::FrameTransition((idx, screen)) => { - current_screen = screen; - display.current_frame(idx); - } - event::Event::FrameLoaded(n) => { - if let Some(n) = n { - display.total_frames(n); - } else { - display.done_loading(); - } - } - event::Event::Paused(paused) => { - display.paused(paused); - } - event::Event::ToggleUi => { - display.toggle_ui(); - } - event::Event::Quit => { - break; - } - } - display.render(¤t_screen, &mut output).await?; - } - - Ok(()) -} - async fn async_main(opt: Opt) -> anyhow::Result<()> { let Opt { file } = opt; @@ -207,14 +31,15 @@ async fn async_main(opt: Opt) -> anyhow::Result<()> { input::spawn_task(event_w.clone(), input); - let frames = async_std::sync::Arc::new(async_std::sync::Mutex::new( + let frame_data = async_std::sync::Arc::new(async_std::sync::Mutex::new( frames::FrameData::new(), )); - spawn_frame_reader_task(event_w.clone(), frames.clone(), fh); + frames::load_from_file(frame_data.clone(), fh, event_w.clone()); + let timer_task = - spawn_timer_task(event_w.clone(), frames.clone(), timer_r); + timer::spawn_task(event_w.clone(), frame_data.clone(), timer_r); - event_loop(event_r, timer_w.clone(), output).await?; + event::handle_events(event_r, timer_w.clone(), output).await?; timer_w.send(event::TimerAction::Quit).await?; timer_task.await; diff --git a/src/bin/ttyplay/timer.rs b/src/bin/ttyplay/timer.rs new file mode 100644 index 0000000..16541a6 --- /dev/null +++ b/src/bin/ttyplay/timer.rs @@ -0,0 +1,115 @@ +use async_std::prelude::FutureExt as _; + +pub fn spawn_task( + event_w: async_std::channel::Sender, + frames: async_std::sync::Arc< + async_std::sync::Mutex, + >, + timer_r: async_std::channel::Receiver, +) -> async_std::task::JoinHandle<()> { + async_std::task::spawn(async move { + let mut idx = 0; + let mut start_time = std::time::Instant::now(); + let mut paused_time = None; + let mut force_update_time = false; + loop { + enum Res { + Wait(Option), + TimerAction( + Result< + crate::event::TimerAction, + async_std::channel::RecvError, + >, + ), + } + let wait = async { + let wait_read = frames.lock_arc().await.wait_for_frame(idx); + if wait_read.await { + let frame = + frames.lock_arc().await.get(idx).unwrap().clone(); + if force_update_time { + let now = std::time::Instant::now(); + start_time = now - frame.delay() + // give a bit of extra time before moving to the + // next frame, otherwise backing up behind two + // frames that are extremely close together + // doesn't work + + std::time::Duration::from_millis(200); + if paused_time.take().is_some() { + paused_time = Some(now); + } + force_update_time = false; + } else if paused_time.is_some() { + std::future::pending::<()>().await; + } else { + async_std::task::sleep( + (start_time + frame.delay()) + .saturating_duration_since( + std::time::Instant::now(), + ), + ) + .await; + } + Res::Wait(Some(frame.into_screen())) + } else { + Res::Wait(None) + } + }; + let action = async { Res::TimerAction(timer_r.recv().await) }; + match wait.race(action).await { + Res::Wait(Some(screen)) => { + event_w + .send(crate::event::Event::FrameTransition(( + idx, screen, + ))) + .await + .unwrap(); + idx += 1; + } + Res::Wait(None) => { + idx = frames.lock_arc().await.count() - 1; + paused_time = Some(std::time::Instant::now()); + event_w + .send(crate::event::Event::Paused(true)) + .await + .unwrap(); + } + Res::TimerAction(Ok(action)) => match action { + crate::event::TimerAction::Pause => { + let now = std::time::Instant::now(); + if let Some(time) = paused_time.take() { + start_time += now - time; + } else { + paused_time = Some(now); + } + event_w + .send(crate::event::Event::Paused( + paused_time.is_some(), + )) + .await + .unwrap(); + } + crate::event::TimerAction::FirstFrame => { + idx = 0; + force_update_time = true; + } + crate::event::TimerAction::LastFrame => { + idx = frames.lock_arc().await.count() - 1; + force_update_time = true; + } + // force_update_time will immediately transition to the + // next frame and do idx += 1 on its own + crate::event::TimerAction::NextFrame => { + force_update_time = true; + } + crate::event::TimerAction::PreviousFrame => { + idx = idx.saturating_sub(2); + force_update_time = true; + } + crate::event::TimerAction::Quit => break, + }, + Res::TimerAction(Err(e)) => panic!("{}", e), + } + } + }) +} -- cgit v1.2.3-54-g00ecf