use std::{
env,
io::stdout,
sync::Arc,
time::Duration,
};
use anyhow::anyhow;
use crossterm::{
event::{
DisableMouseCapture,
EnableMouseCapture,
},
execute,
terminal::{
EnterAlternateScreen,
LeaveAlternateScreen,
},
};
use futures::{
channel::mpsc,
lock::Mutex,
prelude::*,
};
use tracing_subscriber::fmt::format::FmtSpan;
use mudrs_milk::{
renderer::Renderer,
script::{
Flags,
NopTelnetEngine,
ScriptEngine,
},
user::User,
widget::{
input::*,
scrollback::*,
},
};
use tokio::time::timeout;
#[allow(unused_imports)]
use tracing::{
debug,
warn,
error,
};
use tui::backend::{
Backend,
CrosstermBackend,
};
fn main() -> anyhow::Result<()> {
let file_appender = tracing_appender::rolling::never(".", "milk.log");
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
let filter = tracing_subscriber::EnvFilter::from_default_env();
tracing_subscriber::fmt()
.with_writer(non_blocking)
.with_span_events(FmtSpan::NONE)
.with_env_filter(filter)
.init();
std::panic::set_hook(Box::new(move |info| {
let trace = backtrace::Backtrace::new();
tracing::error!(?trace, ?info, "something panicked");
}));
let addr = env::args()
.nth(1)
.ok_or_else(|| anyhow!("Need server address"))?;
crossterm::terminal::enable_raw_mode()?;
execute!(stdout(), EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout());
let rt = tokio::runtime::Runtime::new()?;
let res = rt.block_on(async_main(backend, addr));
execute!(stdout(), DisableMouseCapture, LeaveAlternateScreen)?;
crossterm::terminal::disable_raw_mode()?;
res
}
async fn async_main(backend: impl Backend, addr: String) -> anyhow::Result<()> {
let flags = Flags::default();
if !cfg!(windows) {
use tokio::signal::unix;
let mut term = unix::signal(unix::SignalKind::terminate())?;
tokio::spawn({
let flags = flags.clone();
async move {
while let Some(_) = term.recv().await {
flags.exit();
}
}
});
}
let (input_tx, input_rx) = mpsc::channel(32);
let (buffer_tx, buffer_rx) = mpsc::channel(32);
let (render_tx, mut render_rx) = mpsc::channel(32);
let scrollback = Arc::new(Mutex::new(ScrollbackBuffer::new(render_tx.clone())));
let input = Arc::new(Mutex::new(InputBuffer::new(
flags.clone(),
input_tx.clone(),
render_tx.clone(),
)));
let mut renderer = Renderer::new(backend, input.clone(), scrollback.clone(), 60f64)?;
let user_input = User::new();
user_input.dispatch_events(input.clone(), scrollback.clone(), render_tx.clone());
ScrollbackBuffer::append_lines(scrollback, buffer_rx);
NopTelnetEngine(addr).start(input_rx, buffer_tx, flags.clone())?;
loop {
let res = timeout(Duration::from_secs(5), render_rx.next()).await;
if res.is_err() {
debug!("timed out waiting for render tick");
}
if flags.should_exit() {
break;
}
renderer.render().await?;
}
Ok(())
}