use anyhow::Result;
use crossterm::{
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
terminal,
};
use std::io;
use std::time::{Duration, Instant};
use tokio_util::sync::CancellationToken;
use crate::state::InputEvent;
pub fn is_tty() -> bool {
use std::io::IsTerminal;
io::stdin().is_terminal()
}
pub struct KeyboardInput {
toggle_key: char,
sender: tokio::sync::mpsc::UnboundedSender<InputEvent>,
cancel: CancellationToken,
}
impl KeyboardInput {
pub fn new(
toggle_key: char,
sender: tokio::sync::mpsc::UnboundedSender<InputEvent>,
cancel: CancellationToken,
) -> Self {
KeyboardInput {
toggle_key,
sender,
cancel,
}
}
pub fn run(&self) -> Result<()> {
terminal::enable_raw_mode()?;
let _guard = RawModeGuard;
let mut last_toggle = Instant::now()
.checked_sub(Duration::from_millis(500))
.unwrap_or(Instant::now());
loop {
if self.cancel.is_cancelled() {
break;
}
if !event::poll(Duration::from_millis(100))? {
continue;
}
let ev = event::read()?;
match ev {
Event::Key(KeyEvent {
code, modifiers, ..
}) => match code {
KeyCode::Char('c') if modifiers.contains(KeyModifiers::CONTROL) => {
let _ = self.sender.send(InputEvent::Quit);
break;
}
KeyCode::Char('q') => {
let _ = self.sender.send(InputEvent::Quit);
break;
}
KeyCode::Char(c) if c == self.toggle_key => {
let now = Instant::now();
if now.duration_since(last_toggle) >= Duration::from_millis(200) {
last_toggle = now;
let _ = self.sender.send(InputEvent::Toggle);
}
}
_ => {}
},
_ => {}
}
}
Ok(())
}
}
struct RawModeGuard;
impl Drop for RawModeGuard {
fn drop(&mut self) {
let _ = terminal::disable_raw_mode();
}
}