diff --git a/bin/commands/inbox/flux/select.rs b/bin/commands/inbox/flux/select.rs index 266099ff..9f8b43f8 100644 --- a/bin/commands/inbox/flux/select.rs +++ b/bin/commands/inbox/flux/select.rs @@ -14,7 +14,7 @@ use radicle_tui as tui; use tui::common::cob::inbox::{self}; use tui::flux::store::{State, Store}; -use tui::flux::termination::{self, Interrupted}; +use tui::flux::task::{self, Interrupted}; use tui::flux::ui::cob::NotificationItem; use tui::flux::ui::Frontend; use tui::Exit; @@ -186,7 +186,7 @@ impl App { } pub async fn run(&self) -> Result> { - let (terminator, mut interrupt_rx) = termination::create_termination(); + let (terminator, mut interrupt_rx) = task::create_termination(); let (store, state_rx) = Store::::new(); let (frontend, action_rx) = Frontend::::new(); let state = InboxState::try_from(&self.context)?; diff --git a/bin/commands/issue/flux/select.rs b/bin/commands/issue/flux/select.rs index 580ec8d7..b50e039f 100644 --- a/bin/commands/issue/flux/select.rs +++ b/bin/commands/issue/flux/select.rs @@ -11,7 +11,7 @@ use radicle_tui as tui; use tui::common::cob::issue::{self, Filter}; use tui::flux::store::{State, Store}; -use tui::flux::termination::{self, Interrupted}; +use tui::flux::task::{self, Interrupted}; use tui::flux::ui::cob::IssueItem; use tui::flux::ui::Frontend; use tui::Exit; @@ -115,7 +115,7 @@ impl App { } pub async fn run(&self) -> Result> { - let (terminator, mut interrupt_rx) = termination::create_termination(); + let (terminator, mut interrupt_rx) = task::create_termination(); let (store, state_rx) = Store::::new(); let (frontend, action_rx) = Frontend::::new(); let state = IssuesState::try_from(&self.context)?; diff --git a/bin/commands/patch/flux/select.rs b/bin/commands/patch/flux/select.rs index d0c4b195..f3dcbc34 100644 --- a/bin/commands/patch/flux/select.rs +++ b/bin/commands/patch/flux/select.rs @@ -11,7 +11,7 @@ use radicle_tui as tui; use tui::common::cob::patch::{self, Filter}; use tui::flux::store::{State, Store}; -use tui::flux::termination::{self, Interrupted}; +use tui::flux::task::{self, Interrupted}; use tui::flux::ui::cob::PatchItem; use tui::flux::ui::Frontend; use tui::Exit; @@ -116,7 +116,7 @@ impl App { } pub async fn run(&self) -> Result> { - let (terminator, mut interrupt_rx) = termination::create_termination(); + let (terminator, mut interrupt_rx) = task::create_termination(); let (store, state_rx) = Store::::new(); let (frontend, action_rx) = Frontend::::new(); let state = PatchesState::try_from(&self.context)?; diff --git a/src/flux.rs b/src/flux.rs index fa856d52..e8170dac 100644 --- a/src/flux.rs +++ b/src/flux.rs @@ -1,4 +1,5 @@ pub mod event; pub mod store; -pub mod termination; +pub mod task; +pub mod terminal; pub mod ui; diff --git a/src/flux/store.rs b/src/flux/store.rs index 08d2737f..95ff9925 100644 --- a/src/flux/store.rs +++ b/src/flux/store.rs @@ -7,7 +7,7 @@ use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use crate::Exit; -use super::termination::{Interrupted, Terminator}; +use super::task::{Interrupted, Terminator}; const STORE_TICK_RATE: Duration = Duration::from_millis(1000); diff --git a/src/flux/termination.rs b/src/flux/task.rs similarity index 100% rename from src/flux/termination.rs rename to src/flux/task.rs diff --git a/src/flux/terminal.rs b/src/flux/terminal.rs new file mode 100644 index 00000000..898e659c --- /dev/null +++ b/src/flux/terminal.rs @@ -0,0 +1,55 @@ +use std::io::{self, Write}; +use std::thread; + +use termion::input::TermRead; +use termion::raw::{IntoRawMode, RawTerminal}; + +use ratatui::prelude::*; + +use tokio::sync::mpsc::{self}; + +use super::event::Event; + +type Backend = TermionBackendExt>; + +pub fn setup(height: usize) -> anyhow::Result> { + let stdout = io::stdout().into_raw_mode()?; + let options = TerminalOptions { + viewport: Viewport::Inline(height as u16), + }; + + Ok(Terminal::with_options( + TermionBackendExt::new(stdout), + options, + )?) +} + +pub fn restore(terminal: &mut Terminal) -> anyhow::Result<()> { + terminal.clear()?; + Ok(()) +} + +pub fn events() -> mpsc::UnboundedReceiver { + let (tx, rx) = mpsc::unbounded_channel(); + let events_tx = tx.clone(); + thread::spawn(move || { + let stdin = io::stdin(); + for key in stdin.keys().flatten() { + if events_tx.send(Event::Key(key)).is_err() { + return; + } + } + }); + + let events_tx = tx.clone(); + if let Ok(mut signals) = signal_hook::iterator::Signals::new([libc::SIGWINCH]) { + thread::spawn(move || { + for _ in signals.forever() { + if events_tx.send(Event::Resize).is_err() { + return; + } + } + }); + } + rx +} diff --git a/src/flux/ui.rs b/src/flux/ui.rs index b38ecc0e..995510bc 100644 --- a/src/flux/ui.rs +++ b/src/flux/ui.rs @@ -8,27 +8,23 @@ pub mod widget; use std::fmt::Debug; use std::io::{self}; -use std::thread; use std::time::Duration; -// use termion::event::Event; -use termion::input::TermRead; -use termion::raw::{IntoRawMode, RawTerminal}; - -use ratatui::prelude::*; +use termion::raw::RawTerminal; use tokio::sync::broadcast; use tokio::sync::mpsc::{self, UnboundedReceiver}; use super::event::Event; use super::store::State; -use super::termination::Interrupted; +use super::task::Interrupted; +use super::terminal; use super::ui::widget::{Render, Widget}; type Backend = TermionBackend>; const RENDERING_TICK_RATE: Duration = Duration::from_millis(250); -const INLINE_HEIGHT: u16 = 20; +const INLINE_HEIGHT: usize = 20; pub struct Frontend { action_tx: mpsc::UnboundedSender, @@ -51,9 +47,10 @@ impl Frontend { W: Widget + Render<()>, P: Clone + Send + Sync + Debug, { - let mut terminal = setup_terminal()?; let mut ticker = tokio::time::interval(RENDERING_TICK_RATE); - let mut events_rx = events(); + + let mut terminal = terminal::setup(INLINE_HEIGHT)?; + let mut events_rx = terminal::events(); let mut root = { let state = state_rx.recv().await.unwrap(); @@ -84,50 +81,8 @@ impl Frontend { terminal.draw(|frame| root.render::(frame, frame.size(), ()))?; }; - restore_terminal(&mut terminal)?; + terminal::restore(&mut terminal)?; result } } - -fn setup_terminal() -> anyhow::Result> { - let stdout = io::stdout().into_raw_mode()?; - let options = TerminalOptions { - viewport: Viewport::Inline(INLINE_HEIGHT), - }; - - Ok(Terminal::with_options( - TermionBackend::new(stdout), - options, - )?) -} - -fn restore_terminal(terminal: &mut Terminal) -> anyhow::Result<()> { - terminal.clear()?; - Ok(()) -} - -fn events() -> mpsc::UnboundedReceiver { - let (tx, rx) = mpsc::unbounded_channel(); - let events_tx = tx.clone(); - thread::spawn(move || { - let stdin = io::stdin(); - for key in stdin.keys().flatten() { - if events_tx.send(Event::Key(key)).is_err() { - return; - } - } - }); - - let events_tx = tx.clone(); - if let Ok(mut signals) = signal_hook::iterator::Signals::new([libc::SIGWINCH]) { - thread::spawn(move || { - for _ in signals.forever() { - if events_tx.send(Event::Resize).is_err() { - return; - } - } - }); - } - rx -}