Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
663d0ee81a
4 changed files with 76 additions and 40 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -698,6 +698,7 @@ checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
|||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"crossterm_winapi",
|
||||
"futures-core",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot 0.12.1",
|
||||
|
|
|
@ -12,7 +12,7 @@ clap_derive = "4.5.4"
|
|||
cliclack = "0.2.5"
|
||||
color-eyre = "0.6.3"
|
||||
config = "0.14.0"
|
||||
crossterm = "0.27.0"
|
||||
crossterm = { version = "0.27.0", features = ["event-stream"] }
|
||||
futures = "0.3.30"
|
||||
keyring = "2.3.2"
|
||||
ratatui = "0.26.2"
|
||||
|
|
|
@ -113,8 +113,7 @@ mod ui {
|
|||
|
||||
impl Args {
|
||||
pub async fn run(&self, tasks: SharedTasks) -> Result<()> {
|
||||
let tui = crate::tui::Tui::new(tasks);
|
||||
tui.run().await
|
||||
crate::tui::Tui::run(tasks).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
110
src/tui.rs
110
src/tui.rs
|
@ -6,6 +6,7 @@ use crossterm::{
|
|||
ExecutableCommand,
|
||||
};
|
||||
use futures::stream::StreamExt;
|
||||
use futures::FutureExt;
|
||||
use ratatui::{
|
||||
prelude::{CrosstermBackend, Stylize, Terminal},
|
||||
widgets::Paragraph,
|
||||
|
@ -14,39 +15,88 @@ use signal_hook::consts::*;
|
|||
use signal_hook_tokio::Signals;
|
||||
use std::io::stdout;
|
||||
|
||||
pub struct Tui {
|
||||
tasks: SharedTasks,
|
||||
pub struct Tui {}
|
||||
|
||||
enum Event {
|
||||
CrosstermEvent(crossterm::event::Event),
|
||||
// CrosstermError(Arc<dyn std::error::Error>),
|
||||
Quit,
|
||||
Tick,
|
||||
}
|
||||
|
||||
impl Tui {
|
||||
pub fn new(tasks: SharedTasks) -> Self {
|
||||
Self { tasks }
|
||||
}
|
||||
|
||||
pub async fn run(&self) -> Result<()> {
|
||||
self.tui().await
|
||||
}
|
||||
|
||||
async fn tui(&self) -> Result<()> {
|
||||
pub async fn run(tasks: SharedTasks) -> Result<()> {
|
||||
stdout().execute(EnterAlternateScreen)?;
|
||||
enable_raw_mode()?;
|
||||
|
||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||
terminal.clear()?;
|
||||
let mut signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
|
||||
let signalled: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
|
||||
let handle = signals.handle();
|
||||
|
||||
let signaler = Arc::clone(&signalled);
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<Event>();
|
||||
|
||||
let mut signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
|
||||
let signal_handle = signals.handle();
|
||||
let signal_sender = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
// await a signal - once we receive one mark the boolean as true to
|
||||
// quit the app
|
||||
// await a signal - once we receive one send the quit event
|
||||
signals.next().await;
|
||||
let mut signalled = signaler.lock().await;
|
||||
*signalled = true;
|
||||
signal_sender.send(Event::Quit).unwrap();
|
||||
});
|
||||
|
||||
let tick_rate = std::time::Duration::from_millis(1000);
|
||||
|
||||
let event_publisher_loop = tokio::spawn(async move {
|
||||
let mut crossterm_event_stream = crossterm::event::EventStream::new();
|
||||
let mut interval = tokio::time::interval(tick_rate);
|
||||
|
||||
loop {
|
||||
let delay = interval.tick();
|
||||
// TODO: read about FusedFutures
|
||||
let crossterm_event = crossterm_event_stream.next().fuse();
|
||||
|
||||
// TODO: fix unwrap?
|
||||
tokio::select! {
|
||||
e = crossterm_event => {
|
||||
match e {
|
||||
Some(Ok(e)) => {
|
||||
tx.send(Event::CrosstermEvent(e)).unwrap();
|
||||
},
|
||||
Some(Err(_e)) => {
|
||||
// tx.send(Event::CrosstermError(Arc::new(e))).unwrap();
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
}
|
||||
_ = delay => {
|
||||
tx.send(Event::Tick).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// TODO: enable isig to re-allow ctrl-z?
|
||||
|
||||
loop {
|
||||
let event = rx.recv().await;
|
||||
|
||||
match event {
|
||||
Some(Event::CrosstermEvent(e)) => match e {
|
||||
event::Event::Key(key) => {
|
||||
if key.kind == KeyEventKind::Press
|
||||
&& key.code == KeyCode::Char('c')
|
||||
&& key.modifiers == KeyModifiers::CONTROL
|
||||
{
|
||||
break;
|
||||
}
|
||||
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Some(Event::Tick) => {}
|
||||
Some(Event::Quit) => break,
|
||||
None => {}
|
||||
}
|
||||
|
||||
terminal.draw(|frame| {
|
||||
let area = frame.size();
|
||||
frame.render_widget(
|
||||
|
@ -54,27 +104,13 @@ impl Tui {
|
|||
area,
|
||||
);
|
||||
})?;
|
||||
if event::poll(std::time::Duration::from_millis(10))? {
|
||||
if let event::Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press
|
||||
&& key.code == KeyCode::Char('c')
|
||||
&& key.modifiers == KeyModifiers::CONTROL
|
||||
{
|
||||
break;
|
||||
}
|
||||
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if *(*signalled).lock().await {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle.close();
|
||||
signal_handle.close();
|
||||
|
||||
stdout().execute(LeaveAlternateScreen)?;
|
||||
disable_raw_mode()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue