10 changed files with 294 additions and 8 deletions
@ -1,4 +1,5 @@ |
|||
/target |
|||
Cargo.lock |
|||
db.sqlite* |
|||
/games |
|||
/games |
|||
output.log |
|||
@ -0,0 +1,257 @@ |
|||
use std::sync::mpsc; |
|||
|
|||
use std::io::Write; |
|||
|
|||
use crossterm::{ |
|||
cursor::{MoveTo, MoveToNextLine}, |
|||
event::{Event, KeyCode, KeyModifiers, MouseEvent}, |
|||
queue, |
|||
style::Print, |
|||
terminal::{Clear, ClearType}, |
|||
}; |
|||
|
|||
pub fn setup() -> Result<(Close, Stdin<String>), fern::InitError> { |
|||
let (stdout, stdin, close, join_handle) = TerminalHandler::new(|x| { |
|||
if x == "sv_cheats" { |
|||
log::info!("CHEATS ENABLED") |
|||
} |
|||
x |
|||
}); |
|||
fern::Dispatch::new() |
|||
.format(|out, message, record| { |
|||
out.finish(format_args!( |
|||
"{}[{}][{}] {}", |
|||
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), |
|||
record.target(), |
|||
record.level(), |
|||
message |
|||
)) |
|||
}) |
|||
.level(log::LevelFilter::Debug) |
|||
.chain(stdout) |
|||
.chain( |
|||
std::fs::OpenOptions::new() |
|||
.write(true) |
|||
.create(true) |
|||
.truncate(true) |
|||
.open("output.log")?, |
|||
) |
|||
.apply()?; |
|||
log::info!("TEST1"); |
|||
log::info!("TEST2"); |
|||
// std::process::exit(1);
|
|||
Ok((Close{close, handle: join_handle}, stdin)) |
|||
} |
|||
|
|||
pub type Stdout = mpsc::Sender<String>; |
|||
pub type Stdin<T> = mpsc::Receiver<T>; |
|||
|
|||
struct TerminalHandler<T, F> |
|||
where |
|||
T: Send + 'static, |
|||
F: Fn(String) -> T + Send + 'static, |
|||
{ |
|||
stdout: mpsc::Receiver<String>, |
|||
stdin: mpsc::Sender<T>, |
|||
close: mpsc::Receiver<()>, |
|||
handler: F, |
|||
lines: LimitedVec<String>, |
|||
command: String, |
|||
} |
|||
|
|||
impl<T, F> TerminalHandler<T, F> |
|||
where |
|||
T: Send + 'static, |
|||
F: Fn(String) -> T + Send + 'static, |
|||
{ |
|||
fn new( |
|||
handler: F, |
|||
) -> ( |
|||
Stdout, |
|||
Stdin<T>, |
|||
mpsc::Sender<()>, |
|||
std::thread::JoinHandle<()>, |
|||
) { |
|||
crossterm::terminal::enable_raw_mode().unwrap(); |
|||
let mut stdout = std::io::stdout(); |
|||
queue!(stdout, crossterm::event::EnableMouseCapture).unwrap(); |
|||
let (stdout_send, stdout_recv) = mpsc::channel(); |
|||
let (stdin_send, stdin_recv) = mpsc::channel(); |
|||
let (close_send, close_recv) = mpsc::channel(); |
|||
let join_handle = std::thread::spawn(move || { |
|||
let mut s = Self { |
|||
stdout: stdout_recv, |
|||
stdin: stdin_send, |
|||
close: close_recv, |
|||
handler, |
|||
lines: LimitedVec::new(crossterm::terminal::size().unwrap().1 as usize - 1), |
|||
command: String::new(), |
|||
}; |
|||
loop { |
|||
if s.iterate() { |
|||
break; |
|||
} |
|||
} |
|||
queue!(stdout, crossterm::event::DisableMouseCapture).unwrap(); |
|||
crossterm::terminal::disable_raw_mode().ok(); |
|||
println!("Closing printing thread"); |
|||
}); |
|||
(stdout_send, stdin_recv, close_send, join_handle) |
|||
} |
|||
|
|||
fn iterate(&mut self) -> bool { |
|||
let mut updated = false; |
|||
match self.close.try_recv() { |
|||
Err(mpsc::TryRecvError::Empty) => (), |
|||
_ => return true, |
|||
} |
|||
while is_event_available() { |
|||
match crossterm::event::read().unwrap() { |
|||
Event::Resize(_cols, rows) => self.lines.size = rows as usize - 1, |
|||
Event::Key(k) => match (k.code, k.modifiers) { |
|||
(KeyCode::Char('c'), KeyModifiers::CONTROL) => { |
|||
let mut stdout = std::io::stdout(); |
|||
queue!(stdout, crossterm::event::DisableMouseCapture).unwrap(); |
|||
crossterm::terminal::disable_raw_mode().unwrap(); |
|||
stdout.flush().unwrap(); |
|||
println!("Exiting server"); |
|||
std::process::exit(0) |
|||
} |
|||
(k, _m) => { |
|||
match k { |
|||
KeyCode::Char(c) => self.command.push(c), |
|||
KeyCode::Backspace => {self.command.pop();}, |
|||
KeyCode::Enter => { |
|||
match self.stdin.send((self.handler)(self.command.clone())) { |
|||
Ok(_) => (), |
|||
Err(e) => log::error!("{}", e), |
|||
}; |
|||
self.lines.push(format!(" >> {}", self.command)); |
|||
self.command = String::new(); |
|||
} |
|||
_ => () |
|||
} |
|||
}, |
|||
}, |
|||
Event::Mouse(m) => match m { |
|||
MouseEvent::ScrollUp(_, _, _) => self.lines.scroll_up(), |
|||
MouseEvent::ScrollDown(_, _, _) => self.lines.scroll_down(), |
|||
_ => (), |
|||
}, |
|||
} |
|||
|
|||
updated = true; |
|||
} |
|||
while let Some(x) = match self.stdout.try_recv() { |
|||
Err(mpsc::TryRecvError::Empty) => None, |
|||
Err(mpsc::TryRecvError::Disconnected) => return true, |
|||
Ok(v) => Some(v), |
|||
} { |
|||
for line in x.split('\n') { |
|||
if !line.is_empty() { |
|||
self.lines.push(line.trim_end().to_string()); |
|||
} |
|||
} |
|||
|
|||
updated = true; |
|||
} |
|||
let mut stdout = std::io::stdout(); |
|||
// println!("Got stdout");
|
|||
if updated { |
|||
queue!(stdout, Clear(ClearType::All), MoveTo(0, 0)).unwrap(); |
|||
for v in self.lines.iter() { |
|||
queue!(stdout, Print(v), MoveToNextLine(1)).unwrap(); |
|||
// println!("{}", v);
|
|||
} |
|||
let size = crossterm::terminal::size().unwrap(); |
|||
queue!(stdout, MoveTo(0, size.1), Clear(ClearType::CurrentLine), Print(&self.command)).unwrap(); |
|||
stdout.flush().unwrap(); |
|||
} |
|||
false |
|||
} |
|||
} |
|||
|
|||
impl<T, F> Drop for TerminalHandler<T, F> |
|||
where |
|||
T: Send + 'static, |
|||
F: Fn(String) -> T + Send + 'static, |
|||
{ |
|||
fn drop(&mut self) { |
|||
queue!(std::io::stdout(), crossterm::event::DisableMouseCapture).unwrap(); |
|||
} |
|||
} |
|||
|
|||
fn is_event_available() -> bool { |
|||
crossterm::event::poll(std::time::Duration::from_millis(10)) |
|||
.expect("Error polling for terminal events") |
|||
} |
|||
|
|||
pub struct Close { |
|||
close: mpsc::Sender<()>, |
|||
handle: std::thread::JoinHandle<()>, |
|||
} |
|||
|
|||
impl Close { |
|||
pub fn close(self) { |
|||
self.close.send(()).unwrap(); |
|||
self.handle.join().unwrap(); |
|||
println!("CLOSING"); |
|||
queue!(std::io::stdout(), crossterm::event::DisableMouseCapture).unwrap(); |
|||
} |
|||
} |
|||
|
|||
struct LimitedVec<T> { |
|||
inner: Vec<T>, |
|||
size: usize, |
|||
pos: usize, |
|||
} |
|||
|
|||
impl<T> LimitedVec<T> { |
|||
fn new(size: usize) -> Self { |
|||
Self { |
|||
inner: Vec::with_capacity(size), |
|||
size, |
|||
pos: 0, |
|||
} |
|||
} |
|||
|
|||
fn push(&mut self, v: T) { |
|||
// while self.inner.len() >= self.size {
|
|||
// self.inner.remove(0);
|
|||
// }
|
|||
if self.pos != 0 { |
|||
self.pos += 1; |
|||
} |
|||
self.inner.push(v); |
|||
// assert!(self.inner.len() <= self.size)
|
|||
} |
|||
|
|||
fn iter(&self) -> std::slice::Iter<T> { |
|||
let start = if self.inner.len() < self.size { |
|||
0 |
|||
} else { |
|||
self.inner |
|||
.len() |
|||
.checked_sub(self.size) |
|||
.and_then(|x| x.checked_sub(self.pos)) |
|||
.unwrap_or(0) |
|||
}; |
|||
let mut end = self.inner.len().checked_sub(self.pos).unwrap_or(0); |
|||
if end < self.inner.len() { |
|||
end = self.inner.len() |
|||
} |
|||
self.inner[start..end].iter() |
|||
} |
|||
|
|||
fn scroll_up(&mut self) { |
|||
if self.pos + 1 < self.inner.len() { |
|||
self.pos += 1; |
|||
} |
|||
} |
|||
|
|||
fn scroll_down(&mut self) { |
|||
if self.pos > 0 { |
|||
self.pos -= 1; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue