use std::os::windows::io::{AsRawHandle, OwnedHandle};
use windows_sys::Win32::Foundation::{HANDLE, S_OK};
use windows_sys::Win32::System::Console::{
COORD, ClosePseudoConsole, CreatePseudoConsole, HPCON, ResizePseudoConsole,
};
use crate::config::WindowSize;
use crate::error::{PtyError, Result};
#[derive(Debug)]
pub struct ConPty {
handle: HPCON,
input_write: OwnedHandle,
output_read: OwnedHandle,
pty_pipes: Option<(OwnedHandle, OwnedHandle)>,
}
unsafe impl Send for ConPty {}
unsafe impl Sync for ConPty {}
impl ConPty {
pub fn new(
size: WindowSize,
input_read: OwnedHandle,
output_write: OwnedHandle,
input_write: OwnedHandle,
output_read: OwnedHandle,
) -> Result<Self> {
let coord = COORD {
X: size.cols as i16,
Y: size.rows as i16,
};
let mut hpc: HPCON = 0;
let result = unsafe {
CreatePseudoConsole(
coord,
input_read.as_raw_handle() as HANDLE,
output_write.as_raw_handle() as HANDLE,
0, &mut hpc,
)
};
if result != S_OK {
return Err(PtyError::Windows {
message: "failed to create pseudo console".into(),
code: result as u32,
});
}
if hpc == 0 {
return Err(PtyError::ConPtyNotAvailable);
}
Ok(Self {
handle: hpc,
input_write,
output_read,
pty_pipes: Some((input_read, output_write)),
})
}
pub fn close_pty_pipes(&mut self) {
self.pty_pipes = None;
}
#[must_use]
pub fn handle(&self) -> HPCON {
self.handle
}
#[must_use]
pub fn input(&self) -> &OwnedHandle {
&self.input_write
}
#[must_use]
pub fn output(&self) -> &OwnedHandle {
&self.output_read
}
pub fn resize(&self, size: WindowSize) -> Result<()> {
let coord = COORD {
X: size.cols as i16,
Y: size.rows as i16,
};
let result = unsafe { ResizePseudoConsole(self.handle, coord) };
if result != S_OK {
return Err(PtyError::Windows {
message: "failed to resize pseudo console".into(),
code: result as u32,
});
}
Ok(())
}
}
impl Drop for ConPty {
fn drop(&mut self) {
unsafe {
ClosePseudoConsole(self.handle);
}
}
}
#[must_use]
pub fn is_conpty_available() -> bool {
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress};
let kernel32 = unsafe { GetModuleHandleW(windows_sys::w!("kernel32.dll")) };
if kernel32.is_null() {
return false;
}
let proc = unsafe { GetProcAddress(kernel32, windows_sys::s!("CreatePseudoConsole")) };
proc.is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_conpty_availability() {
let _ = is_conpty_available();
}
}