use std::io;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
use std::time::Duration;
use anyhow::Result;
use crossterm::event::{self, Event as TerminalEvent};
use crate::git::GitInfo;
use crate::tree::scan::Entry;
const POLL: Duration = Duration::from_millis(10);
const HANDOVER: Duration = Duration::from_millis(20);
pub enum Event {
Input(TerminalEvent),
ScanDone {
path: PathBuf,
entries: io::Result<Vec<Entry>>,
},
GitDone(Box<Result<GitInfo>>),
FsChange(Vec<PathBuf>),
}
pub struct Events {
sender: Sender<Event>,
receiver: Receiver<Event>,
input: Arc<Input>,
}
struct Input {
suspended: AtomicBool,
on_stdin: AtomicBool,
}
impl Events {
pub fn new() -> Self {
let (sender, receiver) = mpsc::channel();
let input = Arc::new(Input {
suspended: AtomicBool::new(false),
on_stdin: AtomicBool::new(false),
});
let control = Arc::clone(&input);
let forward = sender.clone();
thread::spawn(move || read_input(&control, &forward));
Self {
sender,
receiver,
input,
}
}
pub fn sender(&self) -> Sender<Event> {
self.sender.clone()
}
pub fn next(&self) -> Result<Event> {
Ok(self.receiver.recv()?)
}
pub fn suspend(&self) {
self.input.suspended.store(true, Ordering::SeqCst);
while self.input.on_stdin.load(Ordering::SeqCst) {
thread::sleep(HANDOVER);
}
}
pub fn resume(&self) {
self.input.suspended.store(false, Ordering::SeqCst);
}
}
fn read_input(control: &Input, events: &Sender<Event>) {
loop {
if control.suspended.load(Ordering::SeqCst) {
control.on_stdin.store(false, Ordering::SeqCst);
thread::sleep(HANDOVER);
continue;
}
control.on_stdin.store(true, Ordering::SeqCst);
match event::poll(POLL) {
Ok(true) => {}
Ok(false) => continue,
Err(_) => return,
}
let Ok(event) = event::read() else {
return;
};
if events.send(Event::Input(event)).is_err() {
return;
}
}
}