#![allow(dead_code)]
use anyhow::Result;
use crossterm::{
cursor,
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
terminal,
};
use std::io::stdout;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
struct TerminalGuard;
impl TerminalGuard {
fn new() -> Result<Self> {
terminal::enable_raw_mode()?;
Ok(Self)
}
}
impl Drop for TerminalGuard {
fn drop(&mut self) {
use crossterm::execute;
let _ = terminal::disable_raw_mode();
let mut stdout = stdout();
let _ = execute!(stdout, cursor::Show);
}
}
pub struct DevModeUi {
running: Arc<AtomicBool>,
}
impl DevModeUi {
pub fn new(running: Arc<AtomicBool>) -> Self {
Self { running }
}
pub fn display_instructions() {
println!("Development mode active");
println!();
println!("Commands:");
println!(" 's' - Switch to simulation mode and start simulator");
println!(" 't' - Switch to teleop mode");
println!(" Ctrl+C or Ctrl+X - Stop all nodes and exit");
println!();
println!("Note: If terminal gets messed up, run: reset");
println!();
}
pub async fn run<SF, TF, SFut, TFut>(&mut self, mut on_simulator: SF, mut on_teleop: TF) -> Result<()>
where
SF: FnMut() -> SFut,
TF: FnMut() -> TFut,
SFut: std::future::Future<Output = Result<()>>,
TFut: std::future::Future<Output = Result<()>>,
{
Self::display_instructions();
let _guard = TerminalGuard::new()?;
while self.running.load(Ordering::SeqCst) {
if event::poll(Duration::from_millis(100))? {
if let Event::Key(key_event) = event::read()? {
match self
.handle_key_event(key_event, &mut on_simulator, &mut on_teleop)
.await
{
Ok(true) => continue, Ok(false) => break, Err(e) => return Err(e), }
}
}
}
Ok(())
}
async fn handle_key_event<SF, TF, SFut, TFut>(
&mut self,
key_event: KeyEvent,
on_simulator: &mut SF,
on_teleop: &mut TF,
) -> Result<bool>
where
SF: FnMut() -> SFut,
TF: FnMut() -> TFut,
SFut: std::future::Future<Output = Result<()>>,
TFut: std::future::Future<Output = Result<()>>,
{
match key_event.code {
KeyCode::Char('c') | KeyCode::Char('x') if key_event.modifiers.contains(KeyModifiers::CONTROL) => {
self.running.store(false, Ordering::SeqCst);
Ok(false)
}
KeyCode::Char('s') => {
terminal::disable_raw_mode()?;
on_simulator().await?;
Self::display_instructions();
terminal::enable_raw_mode()?;
Ok(true)
}
KeyCode::Char('t') => {
terminal::disable_raw_mode()?;
on_teleop().await?;
Self::display_instructions();
terminal::enable_raw_mode()?;
Ok(true)
}
_ => {
Ok(true)
}
}
}
pub fn disable_raw_mode() -> Result<()> {
terminal::disable_raw_mode()?;
Ok(())
}
pub fn enable_raw_mode() -> Result<()> {
terminal::enable_raw_mode()?;
Ok(())
}
}