use std::sync::OnceLock;
use crossterm::event::{self, Event, KeyEventKind};
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::System::Console::{
GetConsoleMode, GetStdHandle, SetConsoleMode, ENABLE_EXTENDED_FLAGS, ENABLE_MOUSE_INPUT,
ENABLE_VIRTUAL_TERMINAL_INPUT, ENABLE_WINDOW_INPUT, STD_INPUT_HANDLE,
};
use crate::InputInstruction;
use zellij_utils::channels::SenderWithContext;
use zellij_utils::input::{cast_crossterm_key, from_crossterm_mouse};
use zellij_utils::vendored::termwiz::input::InputEvent;
static ORIGINAL_CONSOLE_MODE: OnceLock<u32> = OnceLock::new();
pub(crate) fn enable_vt_input() -> bool {
unsafe {
let handle = GetStdHandle(STD_INPUT_HANDLE);
if handle == 0 || handle == INVALID_HANDLE_VALUE {
return false;
}
let mut mode: u32 = 0;
if GetConsoleMode(handle, &mut mode) == 0 {
return false;
}
let _ = ORIGINAL_CONSOLE_MODE.set(mode);
let new_mode = ENABLE_WINDOW_INPUT
| ENABLE_MOUSE_INPUT
| ENABLE_EXTENDED_FLAGS
| ENABLE_VIRTUAL_TERMINAL_INPUT;
if SetConsoleMode(handle, new_mode) == 0 {
return false;
}
true
}
}
pub(crate) fn restore_vt_input() {
if let Some(&original_mode) = ORIGINAL_CONSOLE_MODE.get() {
unsafe {
let handle = GetStdHandle(STD_INPUT_HANDLE);
if handle != 0 && handle != INVALID_HANDLE_VALUE {
SetConsoleMode(handle, original_mode);
}
}
}
}
pub(crate) fn native_console_stdin_loop(
send_input_instructions: SenderWithContext<InputInstruction>,
resize_sender: Option<std::sync::mpsc::Sender<()>>,
) {
loop {
match event::read() {
Ok(Event::Key(key_event)) => {
if key_event.kind != KeyEventKind::Press {
continue;
}
if let Some((key, bytes)) = cast_crossterm_key(key_event) {
if send_input_instructions
.send(InputInstruction::KeyWithModifierEvent(key, bytes, false))
.is_err()
{
break;
}
}
},
Ok(Event::Mouse(mouse_event)) => {
let mouse_event = from_crossterm_mouse(mouse_event);
if send_input_instructions
.send(InputInstruction::MouseEvent(mouse_event))
.is_err()
{
break;
}
},
Ok(Event::Paste(text)) => {
let raw_bytes = text.as_bytes().to_vec();
let paste_event = InputEvent::Paste(text);
if send_input_instructions
.send(InputInstruction::KeyEvent(paste_event, raw_bytes))
.is_err()
{
break;
}
},
Ok(Event::Resize(..)) => {
if let Some(ref tx) = resize_sender {
let _ = tx.send(());
}
},
Ok(_) => {},
Err(e) => {
log::error!("Failed to read crossterm event: {}", e);
let _ = send_input_instructions.send(InputInstruction::Exit);
break;
},
}
}
}