use crossterm::event::Event;
use std::{
fs::File,
io,
os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle},
sync::Arc,
thread,
};
use tokio::sync::mpsc;
use windows_sys::Win32::{
Foundation::{WAIT_FAILED, WAIT_OBJECT_0},
System::{
Console::{
GetNumberOfConsoleInputEvents, ReadConsoleInputW, ENABLE_VIRTUAL_TERMINAL_INPUT,
INPUT_RECORD,
},
Threading::{CreateEventW, SetEvent, WaitForMultipleObjects, INFINITE},
},
};
#[path = "windows_input_mode.rs"]
mod mode;
pub(in crate::tui) use mode::PasteMode;
#[path = "windows_input_records.rs"]
mod records;
pub(super) struct Input {
events: mpsc::UnboundedReceiver<io::Result<Event>>,
stop: Arc<OwnedHandle>,
worker: Option<thread::JoinHandle<()>>,
}
impl Input {
pub(super) fn start() -> io::Result<Option<Self>> {
let console = match mode::console() {
Ok(console) => console,
Err(_) => return Ok(None),
};
if mode::mode(&console)? & ENABLE_VIRTUAL_TERMINAL_INPUT == 0 {
return Ok(None);
}
let handle = unsafe {
CreateEventW(
std::ptr::null(),
1,
0,
std::ptr::null(),
)
};
if handle.is_null() {
return Err(io::Error::last_os_error());
}
let stop = Arc::new(unsafe { OwnedHandle::from_raw_handle(handle) });
let worker_stop = Arc::clone(&stop);
let (tx, events) = mpsc::unbounded_channel();
let worker = thread::Builder::new()
.name("rho-windows-input".into())
.spawn(move || {
if let Err(error) = read(console, &worker_stop, &tx) {
let _ = tx.send(Err(error));
}
})?;
Ok(Some(Self {
events,
stop,
worker: Some(worker),
}))
}
pub(super) async fn next(&mut self) -> io::Result<Event> {
super::event_result(self.events.recv().await)
}
}
impl Drop for Input {
fn drop(&mut self) {
unsafe {
SetEvent(self.stop.as_raw_handle());
}
if let Some(worker) = self.worker.take() {
let _ = worker.join();
}
}
}
fn read(
console: File,
stop: &OwnedHandle,
tx: &mpsc::UnboundedSender<io::Result<Event>>,
) -> io::Result<()> {
let handles = [stop.as_raw_handle(), console.as_raw_handle()];
let mut decoder = records::Decoder::default();
loop {
let ready = unsafe {
WaitForMultipleObjects(
handles.len() as u32,
handles.as_ptr(),
0,
INFINITE,
)
};
if ready == WAIT_OBJECT_0 {
return Ok(());
}
if ready == WAIT_FAILED {
return Err(io::Error::last_os_error());
}
if ready != WAIT_OBJECT_0 + 1 {
return Err(io::Error::other("unexpected console wait result"));
}
let mut count = 0;
if unsafe { GetNumberOfConsoleInputEvents(console.as_raw_handle(), &mut count) } == 0 {
return Err(io::Error::last_os_error());
}
if count == 0 {
continue;
}
let mut records = vec![INPUT_RECORD::default(); count as usize];
let mut read = 0;
if unsafe {
ReadConsoleInputW(
console.as_raw_handle(),
records.as_mut_ptr(),
count,
&mut read,
)
} == 0
{
return Err(io::Error::last_os_error());
}
if unsafe { GetNumberOfConsoleInputEvents(console.as_raw_handle(), &mut count) } == 0 {
return Err(io::Error::last_os_error());
}
for event in decoder.decode(&records[..read as usize], count != 0) {
if tx.send(Ok(event)).is_err() {
return Ok(());
}
}
}
}