From c650cbbf082cdd2561aa7999ecfbd859d0a4f28a Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 16 Feb 2019 23:36:50 -0800 Subject: [PATCH] flesh out some of the windows pty impl --- Cargo.toml | 9 ++ src/gliumwindows.rs | 2 +- src/main.rs | 5 +- src/pty.rs | 4 +- src/winpty.rs | 199 +++++++++++++++++++++++++++++++++++++++++-- src/xwindows/xwin.rs | 3 +- 6 files changed, 206 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0aa5172a6..8d5f85a79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,15 @@ path = "term" [dependencies.termwiz] path = "termwiz" +[target."cfg(windows)".dependencies.winapi] +features = [ + "winuser", + "consoleapi", + "handleapi", + "fileapi", + "namedpipeapi", +] +version = "~0.3" [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies] freetype = "~0.4" diff --git a/src/gliumwindows.rs b/src/gliumwindows.rs index 7686771fa..49f9aaba6 100644 --- a/src/gliumwindows.rs +++ b/src/gliumwindows.rs @@ -1,6 +1,7 @@ //! Generic system dependent windows via glium+glutin use super::MasterPty; +use super::{Child, Command}; use clipboard::{Clipboard, ClipboardImpl, Paste}; use config::Config; use failure::Error; @@ -11,7 +12,6 @@ use guiloop::{GuiEventLoop, SessionTerminated}; use opengl::render::Renderer; use opengl::textureatlas::OutOfTextureSpace; use std::io::Write; -use std::process::{Child, Command}; use std::rc::Rc; use term::KeyCode; use term::KeyModifiers; diff --git a/src/main.rs b/src/main.rs index a5514f60d..2696e1991 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,6 @@ extern crate xcb_util; #[cfg(all(unix, not(feature = "force-glutin"), not(target_os = "macos")))] mod xwindows; -use std::process::Command; use std::rc::Rc; mod config; @@ -71,11 +70,11 @@ use font::FontConfiguration; #[cfg(unix)] mod pty; #[cfg(unix)] -pub use pty::{openpty, MasterPty, SlavePty}; +pub use pty::{openpty, Child, Command, MasterPty, SlavePty}; #[cfg(windows)] mod winpty; #[cfg(windows)] -pub use winpty::{openpty, MasterPty, SlavePty}; +pub use winpty::{openpty, Child, Command, MasterPty, SlavePty}; #[cfg(unix)] mod sigchld; diff --git a/src/pty.rs b/src/pty.rs index 54356f9b2..1cdfac3da 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -9,9 +9,11 @@ use std::io; use std::mem; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::process::CommandExt; -use std::process::{Child, Command, Stdio}; +use std::process::Stdio; use std::ptr; +pub use std::process::{Child, Command}; + /// Represents the master end of a pty. /// The file descriptor will be closed when the Pty is dropped. pub struct MasterPty { diff --git a/src/winpty.rs b/src/winpty.rs index 57c0a1acf..c889f609f 100644 --- a/src/winpty.rs +++ b/src/winpty.rs @@ -1,9 +1,117 @@ use failure::Error; use std::io; -use std::process::{Child, Command}; +use std::io::Error as IoError; +extern crate winapi; +use std::os::windows::raw::HANDLE; +use std::ptr; +use std::sync::{Arc, Mutex}; +use winpty::winapi::shared::minwindef::DWORD; +use winpty::winapi::shared::winerror::{HRESULT, S_OK}; +use winpty::winapi::um::fileapi::{ReadFile, WriteFile}; +use winpty::winapi::um::handleapi::*; +use winpty::winapi::um::namedpipeapi::CreatePipe; +use winpty::winapi::um::wincon::COORD; -pub struct MasterPty {} -pub struct SlavePty {} +pub use std::process::{Child, Command}; + +type HPCON = HANDLE; + +extern "system" { + fn CreatePseudoConsole( + size: COORD, + hInput: HANDLE, + hOutput: HANDLE, + flags: DWORD, + hpc: *mut HPCON, + ) -> HRESULT; + fn ResizePseudoConsole(hpc: HPCON, size: COORD) -> HRESULT; + fn ClosePseudoConsole(hpc: HPCON); +} + +struct PsuedoCon { + con: HPCON, +} +unsafe impl Send for PsuedoCon {} +unsafe impl Sync for PsuedoCon {} +impl Drop for PsuedoCon { + fn drop(&mut self) { + unsafe { ClosePseudoConsole(self.con) }; + } +} +impl PsuedoCon { + fn new(size: COORD, input: &OwnedHandle, output: &OwnedHandle) -> Result { + let mut con: HPCON = INVALID_HANDLE_VALUE; + let result = unsafe { CreatePseudoConsole(size, input.handle, output.handle, 0, &mut con) }; + ensure!( + result == S_OK, + "failed to create psuedo console: HRESULT {}", + result + ); + Ok(Self { con }) + } + fn resize(&self, size: COORD) -> Result<(), Error> { + let result = unsafe { ResizePseudoConsole(self.con, size) }; + ensure!( + result == S_OK, + "failed to resize console to {}x{}: HRESULT: {}", + size.X, + size.Y, + result + ); + Ok(()) + } +} + +struct OwnedHandle { + handle: HANDLE, +} +unsafe impl Send for OwnedHandle {} +impl Drop for OwnedHandle { + fn drop(&mut self) { + unsafe { CloseHandle(self.handle) }; + } +} + +struct Inner { + con: PsuedoCon, + readable: OwnedHandle, + writable: OwnedHandle, + size: winsize, +} + +impl Inner { + pub fn resize( + &mut self, + num_rows: u16, + num_cols: u16, + pixel_width: u16, + pixel_height: u16, + ) -> Result<(), Error> { + self.con.resize(COORD { + X: num_cols as i16, + Y: num_rows as i16, + })?; + self.size = winsize { + ws_row: num_rows, + ws_col: num_cols, + ws_xpixel: pixel_width, + ws_ypixel: pixel_height, + }; + Ok(()) + } +} + +#[derive(Clone)] +pub struct MasterPty { + inner: Arc>, +} + +pub struct SlavePty { + inner: Arc>, +} + +#[derive(Debug, Clone, Copy)] +#[allow(non_camel_case_types)] pub struct winsize { pub ws_row: u16, pub ws_col: u16, @@ -19,15 +127,19 @@ impl MasterPty { pixel_width: u16, pixel_height: u16, ) -> Result<(), Error> { - bail!("MasterPty::resize not implemented") + let mut inner = self.inner.lock().unwrap(); + inner.resize(num_rows, num_cols, pixel_width, pixel_height) } pub fn get_size(&self) -> Result { - bail!("MasterPty::get_size not implemented") + let inner = self.inner.lock().unwrap(); + Ok(inner.size.clone()) } pub fn try_clone(&self) -> Result { - bail!("MasterPty::try_clone not implemented") + Ok(Self { + inner: self.inner.clone(), + }) } pub fn clear_nonblocking(&self) -> Result<(), Error> { @@ -37,7 +149,21 @@ impl MasterPty { impl io::Write for MasterPty { fn write(&mut self, buf: &[u8]) -> Result { - unimplemented!(); + let mut num_wrote = 0; + let ok = unsafe { + WriteFile( + self.inner.lock().unwrap().writable.handle as *mut _, + buf.as_ptr() as *const _, + buf.len() as u32, + &mut num_wrote, + ptr::null_mut(), + ) + }; + if ok == 0 { + Err(IoError::last_os_error()) + } else { + Ok(num_wrote as usize) + } } fn flush(&mut self) -> Result<(), io::Error> { Ok(()) @@ -46,7 +172,21 @@ impl io::Write for MasterPty { impl io::Read for MasterPty { fn read(&mut self, buf: &mut [u8]) -> Result { - unimplemented!(); + let mut num_read = 0; + let ok = unsafe { + ReadFile( + self.inner.lock().unwrap().readable.handle as *mut _, + buf.as_mut_ptr() as *mut _, + buf.len() as u32, + &mut num_read, + ptr::null_mut(), + ) + }; + if ok == 0 { + Err(IoError::last_os_error()) + } else { + Ok(num_read as usize) + } } } @@ -56,11 +196,52 @@ impl SlavePty { } } +fn pipe() -> Result<(OwnedHandle, OwnedHandle), Error> { + let mut read: HANDLE = INVALID_HANDLE_VALUE; + let mut write: HANDLE = INVALID_HANDLE_VALUE; + if unsafe { CreatePipe(&mut read, &mut write, ptr::null_mut(), 0) } == 0 { + bail!("CreatePipe failed: {}", IoError::last_os_error()); + } + Ok((OwnedHandle { handle: read }, OwnedHandle { handle: write })) +} + pub fn openpty( num_rows: u16, num_cols: u16, pixel_width: u16, pixel_height: u16, ) -> Result<(MasterPty, SlavePty), Error> { - bail!("openpty not implemented") + let (stdin_read, stdin_write) = pipe()?; + let (stdout_read, stdout_write) = pipe()?; + + let con = PsuedoCon::new( + COORD { + X: num_cols as i16, + Y: num_rows as i16, + }, + &stdin_read, + &stdout_write, + )?; + + let size = winsize { + ws_row: num_rows, + ws_col: num_cols, + ws_xpixel: pixel_width, + ws_ypixel: pixel_height, + }; + + let master = MasterPty { + inner: Arc::new(Mutex::new(Inner { + con, + readable: stdout_read, + writable: stdin_write, + size, + })), + }; + + let slave = SlavePty { + inner: master.inner.clone(), + }; + + Ok((master, slave)) } diff --git a/src/xwindows/xwin.rs b/src/xwindows/xwin.rs index b54dc0dde..d4860232c 100644 --- a/src/xwindows/xwin.rs +++ b/src/xwindows/xwin.rs @@ -1,6 +1,7 @@ use super::super::opengl::render::Renderer; use super::super::{get_shell, spawn_window}; use super::xkeysyms; +use super::{Child, Command}; use super::{Connection, Window}; use clipboard::{Clipboard, ClipboardImpl, Paste}; use config::Config; @@ -14,8 +15,6 @@ use pty::MasterPty; use std::cell::RefCell; use std::io::{self, Read, Write}; use std::os::unix::io::{AsRawFd, RawFd}; -use std::process::Child; -use std::process::Command; use std::rc::Rc; use term::{self, KeyCode, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}; use termwiz::hyperlink::Hyperlink;