aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-12-05 02:17:26 -0500
committerJesse Luehrs <doy@tozt.net>2021-12-05 02:17:26 -0500
commiteee7ae4289e21c388b95307187a5212f69635514 (patch)
treea6d1c98eee07f6a8bb57f4a397c1f53dcdab3c6f
parent4f21ca4dfdf95945342b7a244d55549eda6251c2 (diff)
downloadttyrec-bin-eee7ae4289e21c388b95307187a5212f69635514.tar.gz
ttyrec-bin-eee7ae4289e21c388b95307187a5212f69635514.zip
more refactoring
-rw-r--r--src/bin/ttyplay/event.rs47
-rw-r--r--src/bin/ttyplay/frames.rs35
-rw-r--r--src/bin/ttyplay/main.rs187
-rw-r--r--src/bin/ttyplay/timer.rs115
4 files changed, 200 insertions, 184 deletions
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<Pending>,
cvar: async_std::sync::Condvar,
}
impl Reader {
- pub fn new(
+ fn new(
input: async_std::channel::Receiver<Event>,
) -> async_std::sync::Arc<Self> {
let this = Self {
@@ -42,7 +42,7 @@ impl Reader {
this
}
- pub async fn read(&self) -> Option<Event> {
+ async fn read(&self) -> Option<Event> {
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<Event>,
+ timer_w: async_std::channel::Sender<TimerAction>,
+ 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(&current_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<async_std::sync::Mutex<FrameData>>,
+ fh: async_std::fs::File,
+ event_w: async_std::channel::Sender<crate::event::Event>,
+) {
+ 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<event::Event>,
- frames: async_std::sync::Arc<async_std::sync::Mutex<frames::FrameData>>,
- 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<event::Event>,
- frames: async_std::sync::Arc<async_std::sync::Mutex<frames::FrameData>>,
- timer_r: async_std::channel::Receiver<event::TimerAction>,
-) -> 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<vt100::Screen>),
- TimerAction(
- Result<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(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<event::Event>,
- timer_w: async_std::channel::Sender<event::TimerAction>,
- 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(&current_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<crate::event::Event>,
+ frames: async_std::sync::Arc<
+ async_std::sync::Mutex<crate::frames::FrameData>,
+ >,
+ timer_r: async_std::channel::Receiver<crate::event::TimerAction>,
+) -> 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<vt100::Screen>),
+ 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),
+ }
+ }
+ })
+}