summaryrefslogtreecommitdiff
path: root/winpty
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2020-02-17 16:24:27 +0000
committerGitHub <noreply@github.com>2020-02-17 16:24:27 +0000
commiteb1a28ce581fbd42a5e0e1418d7c2c070cfe7545 (patch)
treef61df07b0e2a847999b9f83b4448e55aaf442262 /winpty
parentff09e393090ebe1ac01322823e3b1fe373a253bf (diff)
downloadalacritty-eb1a28ce581fbd42a5e0e1418d7c2c070cfe7545.tar.gz
alacritty-eb1a28ce581fbd42a5e0e1418d7c2c070cfe7545.zip
Extract winpty crate
The winpty crate and its winpty-sys depedency have been moved to https://github.com/alacritty/winpty.
Diffstat (limited to 'winpty')
-rw-r--r--winpty/Cargo.toml20
-rw-r--r--winpty/build.rs74
-rw-r--r--winpty/src/lib.rs7
-rw-r--r--winpty/src/windows.rs438
4 files changed, 0 insertions, 539 deletions
diff --git a/winpty/Cargo.toml b/winpty/Cargo.toml
deleted file mode 100644
index 02cc21c7..00000000
--- a/winpty/Cargo.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[package]
-name = "winpty"
-version = "0.1.0"
-authors = ["Zac Pullar-Strecker <zacmps@gmail.com>"]
-license = "MIT"
-description = "Safe rust bindings for winpty"
-edition = "2018"
-
-[target.'cfg(windows)'.dependencies]
-winpty-sys = "0.4.3"
-bitflags = "1.0"
-
-[target.'cfg(windows)'.dev-dependencies]
-named_pipe = "0.4.1"
-winapi = { version = "0.3", features = ["winnt", "processthreadsapi"] }
-
-[target.'cfg(windows)'.build-dependencies]
-tempfile = "3.0.4"
-http_req = "0.5"
-zip = "0.5"
diff --git a/winpty/build.rs b/winpty/build.rs
deleted file mode 100644
index ae141f7e..00000000
--- a/winpty/build.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-#[cfg(windows)]
-use std::fs::OpenOptions;
-#[cfg(windows)]
-use std::io;
-
-#[cfg(windows)]
-use std::env;
-#[cfg(windows)]
-use std::fs::{copy, File};
-#[cfg(windows)]
-use std::path::Path;
-
-#[cfg(windows)]
-use http_req;
-#[cfg(windows)]
-use tempfile;
-#[cfg(windows)]
-use zip;
-
-#[cfg(windows)]
-const WINPTY_PACKAGE_URL: &str =
- "https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip";
-
-fn main() {
- #[cfg(windows)]
- {
- // Path is relative to target/{profile}/build/alacritty-HASH/out
- let file = Path::new(&env::var("OUT_DIR").unwrap()).join("../../../winpty-agent.exe");
- if !file.exists() {
- aquire_winpty_agent(&file);
- }
-
- // The working directory for `cargo test` is in the deps folder, not the debug/release root
- copy(&file, file.parent().unwrap().join("deps/winpty-agent.exe")).unwrap();
- }
-}
-
-#[cfg(windows)]
-fn aquire_winpty_agent(out_path: &Path) {
- let tmp_dir = tempfile::Builder::new().prefix("alacritty_build").tempdir().unwrap();
-
- let mut file = OpenOptions::new()
- .read(true)
- .write(true)
- .create(true)
- .open(tmp_dir.path().join("winpty_package.zip"))
- .unwrap();
- let mut redirects = 0;
- let mut url = WINPTY_PACKAGE_URL.to_string();
- loop {
- let res = http_req::request::get(url.clone(), &mut file).unwrap();
- if res.status_code().is_redirect() {
- redirects += 1;
- url = res.headers().get("Location").unwrap().to_string();
- if redirects > 5 {
- panic!("Too many redirects");
- }
- } else {
- break;
- }
- }
-
- let mut archive = zip::ZipArchive::new(file).unwrap();
-
- let target = match env::var("TARGET").unwrap().split('-').next().unwrap() {
- "x86_64" => "x64",
- "i386" => "ia32",
- _ => panic!("architecture has no winpty binary"),
- };
-
- let mut winpty_agent = archive.by_name(&format!("{}/bin/winpty-agent.exe", target)).unwrap();
-
- io::copy(&mut winpty_agent, &mut File::create(out_path).unwrap()).unwrap();
-}
diff --git a/winpty/src/lib.rs b/winpty/src/lib.rs
deleted file mode 100644
index f3b7aff9..00000000
--- a/winpty/src/lib.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
-
-#[cfg(windows)]
-pub mod windows;
-
-#[cfg(windows)]
-pub use crate::windows::*;
diff --git a/winpty/src/windows.rs b/winpty/src/windows.rs
deleted file mode 100644
index fb2b1e80..00000000
--- a/winpty/src/windows.rs
+++ /dev/null
@@ -1,438 +0,0 @@
-use std::ffi::{OsStr, OsString};
-use std::fmt::{self, Display, Formatter};
-use std::iter::once;
-use std::os::windows::ffi::{OsStrExt, OsStringExt};
-use std::os::windows::io::RawHandle;
-use std::path::{Path, PathBuf};
-use std::ptr::{null, null_mut};
-use std::result::Result;
-
-use bitflags::bitflags;
-
-use winpty_sys::*;
-
-#[derive(Copy, Clone, Debug)]
-pub enum ErrorCode {
- OutOfMemory,
- SpawnCreateProcessFailed,
- LostConnection,
- AgentExeMissing,
- Unspecified,
- AgentDied,
- AgentTimeout,
- AgentCreationFailed,
- UnknownError(u32),
-}
-
-pub enum MouseMode {
- None,
- Auto,
- Force,
-}
-
-bitflags!(
- pub struct SpawnFlags: u64 {
- const AUTO_SHUTDOWN = 0x1;
- const EXIT_AFTER_SHUTDOWN = 0x2;
- }
-);
-
-bitflags!(
- pub struct ConfigFlags: u64 {
- const CONERR = 0x1;
- const PLAIN_OUTPUT = 0x2;
- const COLOR_ESCAPES = 0x4;
- }
-);
-
-#[derive(Debug)]
-pub struct Error {
- code: ErrorCode,
- message: String,
-}
-
-// Check to see whether winpty gave us an error, and perform the necessary memory freeing
-fn check_err(e: *mut winpty_error_t) -> Result<(), Error> {
- unsafe {
- let code = winpty_error_code(e);
- let raw = winpty_error_msg(e);
- let message = String::from_utf16_lossy(std::slice::from_raw_parts(raw, wcslen(raw)));
- winpty_error_free(e);
-
- let code = match code {
- WINPTY_ERROR_SUCCESS => return Ok(()),
- WINPTY_ERROR_OUT_OF_MEMORY => ErrorCode::OutOfMemory,
- WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED => ErrorCode::SpawnCreateProcessFailed,
- WINPTY_ERROR_LOST_CONNECTION => ErrorCode::LostConnection,
- WINPTY_ERROR_AGENT_EXE_MISSING => ErrorCode::AgentExeMissing,
- WINPTY_ERROR_UNSPECIFIED => ErrorCode::Unspecified,
- WINPTY_ERROR_AGENT_DIED => ErrorCode::AgentDied,
- WINPTY_ERROR_AGENT_TIMEOUT => ErrorCode::AgentTimeout,
- WINPTY_ERROR_AGENT_CREATION_FAILED => ErrorCode::AgentCreationFailed,
- code => ErrorCode::UnknownError(code),
- };
-
- Err(Error { code, message })
- }
-}
-
-impl Display for Error {
- fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
- write!(f, "Code: {:?}, Message: {}", self.code, self.message)
- }
-}
-
-impl std::error::Error for Error {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- None
- }
-}
-
-#[derive(Debug)]
-/// Winpty agent config
-pub struct Config(*mut winpty_config_t);
-
-impl Config {
- pub fn new(flags: ConfigFlags) -> Result<Self, Error> {
- let mut err = null_mut() as *mut winpty_error_t;
- let config = unsafe { winpty_config_new(flags.bits(), &mut err) };
- check_err(err)?;
-
- Ok(Self(config))
- }
-
- /// Set the initial size of the console window
- pub fn set_initial_size(&mut self, cols: i32, rows: i32) {
- unsafe {
- winpty_config_set_initial_size(self.0, cols, rows);
- }
- }
-
- /// Set the mouse mode
- pub fn set_mouse_mode(&mut self, mode: &MouseMode) {
- let m = match mode {
- MouseMode::None => WINPTY_MOUSE_MODE_NONE,
- MouseMode::Auto => WINPTY_MOUSE_MODE_AUTO,
- MouseMode::Force => WINPTY_MOUSE_MODE_FORCE,
- };
- unsafe {
- winpty_config_set_mouse_mode(self.0, m as i32);
- }
- }
-
- /// Amount of time to wait for the agent to startup and to wait for any given
- /// agent RPC request. Must be greater than 0. Can be INFINITE.
- // Might be a better way to represent this while still retaining infinite capability?
- // Enum?
- pub fn set_agent_timeout(&mut self, timeout: u32) {
- unsafe {
- winpty_config_set_agent_timeout(self.0, timeout);
- }
- }
-}
-
-impl Drop for Config {
- fn drop(&mut self) {
- unsafe {
- winpty_config_free(self.0);
- }
- }
-}
-
-#[derive(Debug)]
-/// A struct representing the winpty agent process
-pub struct Winpty(*mut winpty_t);
-
-pub struct ChildHandles {
- pub process: HANDLE,
- pub thread: HANDLE,
-}
-
-impl Winpty {
- /// Starts the agent. This process will connect to the agent
- /// over a control pipe, and the agent will open data pipes
- /// (e.g. CONIN and CONOUT).
- pub fn open(cfg: &Config) -> Result<Self, Error> {
- let mut err = null_mut() as *mut winpty_error_t;
- let winpty = unsafe { winpty_open(cfg.0, &mut err) };
- check_err(err)?;
-
- Ok(Self(winpty))
- }
-
- /// Returns the handle to the winpty agent process
- pub fn raw_handle(&mut self) -> RawHandle {
- unsafe { winpty_agent_process(self.0) }
- }
-
- /// Returns the name of the input pipe.
- /// Pipe is half-duplex.
- pub fn conin_name(&mut self) -> PathBuf {
- unsafe {
- let raw = winpty_conin_name(self.0);
- OsString::from_wide(std::slice::from_raw_parts(raw, wcslen(raw))).into()
- }
- }
-
- /// Returns the name of the output pipe.
- /// Pipe is half-duplex.
- pub fn conout_name(&mut self) -> PathBuf {
- unsafe {
- let raw = winpty_conout_name(self.0);
- OsString::from_wide(std::slice::from_raw_parts(raw, wcslen(raw))).into()
- }
- }
-
- /// Returns the name of the error pipe.
- /// The name will only be valid if ConfigFlags::CONERR was specified.
- /// Pipe is half-duplex.
- pub fn conerr_name(&mut self) -> PathBuf {
- unsafe {
- let raw = winpty_conerr_name(self.0);
- OsString::from_wide(std::slice::from_raw_parts(raw, wcslen(raw))).into()
- }
- }
-
- /// Change the size of the Windows console window.
- ///
- /// cols & rows MUST be greater than 0
- pub fn set_size(&mut self, cols: u16, rows: u16) -> Result<(), Error> {
- assert!(cols > 0 && rows > 0);
- let mut err = null_mut() as *mut winpty_error_t;
-
- unsafe {
- winpty_set_size(self.0, i32::from(cols), i32::from(rows), &mut err);
- }
-
- check_err(err)
- }
-
- /// Get the list of processes running in the winpty agent. Returns <= count processes
- ///
- /// `count` must be greater than 0. Larger values cause a larger allocation.
- // TODO: This should return Vec<Handle> instead of Vec<i32>
- pub fn console_process_list(&mut self, count: usize) -> Result<Vec<i32>, Error> {
- assert!(count > 0);
-
- let mut err = null_mut() as *mut winpty_error_t;
- let mut process_list = Vec::with_capacity(count);
-
- unsafe {
- let len = winpty_get_console_process_list(
- self.0,
- process_list.as_mut_ptr(),
- count as i32,
- &mut err,
- ) as usize;
- process_list.set_len(len);
- }
-
- check_err(err)?;
-
- Ok(process_list)
- }
-
- /// Spawns the new process.
- ///
- /// spawn can only be called once per Winpty object. If it is called
- /// before the output data pipe(s) is/are connected, then collected output is
- /// buffered until the pipes are connected, rather than being discarded.
- /// (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803)
- pub fn spawn(&mut self, cfg: &SpawnConfig) -> Result<ChildHandles, Error> {
- let mut handles =
- ChildHandles { process: std::ptr::null_mut(), thread: std::ptr::null_mut() };
-
- let mut create_process_error: DWORD = 0;
- let mut err = null_mut() as *mut winpty_error_t;
-
- unsafe {
- winpty_spawn(
- self.0,
- cfg.0 as *const winpty_spawn_config_s,
- &mut handles.process as *mut _,
- &mut handles.thread as *mut _,
- &mut create_process_error as *mut _,
- &mut err,
- );
- }
-
- let mut result = check_err(err);
- if let Err(Error { code: ErrorCode::SpawnCreateProcessFailed, message }) = &mut result {
- *message = format!("{} (error code {})", message, create_process_error);
- }
- result.map(|_| handles)
- }
-}
-
-// winpty_t is thread-safe
-unsafe impl Sync for Winpty {}
-unsafe impl Send for Winpty {}
-
-impl Drop for Winpty {
- fn drop(&mut self) {
- unsafe {
- winpty_free(self.0);
- }
- }
-}
-
-#[derive(Debug)]
-/// Information about a process for winpty to spawn
-pub struct SpawnConfig(*mut winpty_spawn_config_t);
-
-impl SpawnConfig {
- /// Creates a new spawnconfig
- pub fn new(
- spawnflags: SpawnFlags,
- appname: Option<&str>,
- cmdline: Option<&str>,
- cwd: Option<&Path>,
- env: Option<&str>,
- ) -> Result<Self, Error> {
- let mut err = null_mut() as *mut winpty_error_t;
-
- fn to_wstring<S: AsRef<OsStr> + ?Sized>(s: &S) -> Vec<u16> {
- OsStr::new(s).encode_wide().chain(once(0)).collect()
- }
-
- let appname = appname.map(to_wstring);
- let cmdline = cmdline.map(to_wstring);
- let cwd = cwd.map(to_wstring);
- let env = env.map(to_wstring);
-
- let wstring_ptr = |opt: &Option<Vec<u16>>| opt.as_ref().map_or(null(), |ws| ws.as_ptr());
- let spawn_config = unsafe {
- winpty_spawn_config_new(
- spawnflags.bits(),
- wstring_ptr(&appname),
- wstring_ptr(&cmdline),
- wstring_ptr(&cwd),
- wstring_ptr(&env),
- &mut err,
- )
- };
-
- check_err(err)?;
-
- Ok(Self(spawn_config))
- }
-}
-
-impl Drop for SpawnConfig {
- fn drop(&mut self) {
- unsafe {
- winpty_spawn_config_free(self.0);
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use named_pipe::PipeClient;
- use winapi::um::processthreadsapi::OpenProcess;
- use winapi::um::winnt::READ_CONTROL;
-
- use crate::{Config, ConfigFlags, SpawnConfig, SpawnFlags, Winpty};
-
- #[test]
- // Test that we can start a process in winpty
- fn spawn_process() {
- let mut winpty =
- Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
- .expect("failed to create winpty instance");
-
- winpty
- .spawn(
- &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
- .expect("failed to create spawn config"),
- )
- .unwrap();
- }
-
- #[test]
- // Test that pipes connected before winpty is spawned can be connected to
- fn valid_pipe_connect_before() {
- let mut winpty =
- Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
- .expect("failed to create winpty instance");
-
- // Check we can connect to both pipes
- PipeClient::connect_ms(winpty.conout_name(), 1000)
- .expect("failed to connect to conout pipe");
- PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
-
- winpty
- .spawn(
- &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
- .expect("failed to create spawn config"),
- )
- .unwrap();
- }
-
- #[test]
- // Test that pipes connected after winpty is spawned can be connected to
- fn valid_pipe_connect_after() {
- let mut winpty =
- Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
- .expect("failed to create winpty instance");
-
- winpty
- .spawn(
- &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
- .expect("failed to create spawn config"),
- )
- .unwrap();
-
- // Check we can connect to both pipes
- PipeClient::connect_ms(winpty.conout_name(), 1000)
- .expect("failed to connect to conout pipe");
- PipeClient::connect_ms(winpty.conin_name(), 1000).expect("failed to connect to conin pipe");
- }
-
- #[test]
- fn resize() {
- let mut winpty =
- Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
- .expect("failed to create winpty instance");
-
- winpty
- .spawn(
- &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
- .expect("failed to create spawn config"),
- )
- .unwrap();
-
- winpty.set_size(1, 1).unwrap();
- }
-
- #[test]
- #[ignore]
- // Test that each id returned by cosole_process_list points to an actual process
- fn console_process_list_valid() {
- let mut winpty =
- Winpty::open(&Config::new(ConfigFlags::empty()).expect("failed to create config"))
- .expect("failed to create winpty instance");
-
- winpty
- .spawn(
- &SpawnConfig::new(SpawnFlags::empty(), None, Some("cmd"), None, None)
- .expect("failed to create spawn config"),
- )
- .unwrap();
-
- let processes =
- winpty.console_process_list(1000).expect("failed to get console process list");
-
- // Check that each id is valid
- processes.iter().for_each(|id| {
- let handle = unsafe {
- OpenProcess(
- READ_CONTROL, // permissions
- false as i32, // inheret
- *id as u32,
- )
- };
- assert!(!handle.is_null());
- });
- }
-}