diff options
Diffstat (limited to 'src/tty/windows/conpty.rs')
-rw-r--r-- | src/tty/windows/conpty.rs | 289 |
1 files changed, 0 insertions, 289 deletions
diff --git a/src/tty/windows/conpty.rs b/src/tty/windows/conpty.rs deleted file mode 100644 index f23d78a7..00000000 --- a/src/tty/windows/conpty.rs +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2016 Joe Wilm, The Alacritty Project Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{Pty, HANDLE}; - -use std::i16; -use std::io::Error; -use std::mem; -use std::os::windows::io::IntoRawHandle; -use std::ptr; -use std::sync::Arc; - -use dunce::canonicalize; -use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite}; -use miow; -use widestring::U16CString; -use winapi::shared::basetsd::{PSIZE_T, SIZE_T}; -use winapi::shared::minwindef::{BYTE, DWORD}; -use winapi::shared::ntdef::{HANDLE, HRESULT, LPWSTR}; -use winapi::shared::winerror::S_OK; -use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress}; -use winapi::um::processthreadsapi::{ - CreateProcessW, InitializeProcThreadAttributeList, UpdateProcThreadAttribute, - PROCESS_INFORMATION, STARTUPINFOW, -}; -use winapi::um::winbase::{EXTENDED_STARTUPINFO_PRESENT, STARTF_USESTDHANDLES, STARTUPINFOEXW}; -use winapi::um::wincontypes::{COORD, HPCON}; - -use crate::cli::Options; -use crate::config::{Config, Shell}; -use crate::display::OnResize; -use crate::term::SizeInfo; - -/// Dynamically-loaded Pseudoconsole API from kernel32.dll -/// -/// The field names are deliberately PascalCase as this matches -/// the defined symbols in kernel32 and also is the convention -/// that the `winapi` crate follows. -#[allow(non_snake_case)] -struct ConptyApi { - CreatePseudoConsole: - unsafe extern "system" fn(COORD, HANDLE, HANDLE, DWORD, *mut HPCON) -> HRESULT, - ResizePseudoConsole: unsafe extern "system" fn(HPCON, COORD) -> HRESULT, - ClosePseudoConsole: unsafe extern "system" fn(HPCON), -} - -impl ConptyApi { - /// Load the API or None if it cannot be found. - pub fn new() -> Option<Self> { - // Unsafe because windows API calls - unsafe { - let hmodule = GetModuleHandleA("kernel32\0".as_ptr() as _); - assert!(!hmodule.is_null()); - - let cpc = GetProcAddress(hmodule, "CreatePseudoConsole\0".as_ptr() as _); - let rpc = GetProcAddress(hmodule, "ResizePseudoConsole\0".as_ptr() as _); - let clpc = GetProcAddress(hmodule, "ClosePseudoConsole\0".as_ptr() as _); - - if cpc.is_null() || rpc.is_null() || clpc.is_null() { - None - } else { - Some(Self { - CreatePseudoConsole: mem::transmute(cpc), - ResizePseudoConsole: mem::transmute(rpc), - ClosePseudoConsole: mem::transmute(clpc), - }) - } - } - } -} - -/// RAII Pseudoconsole -pub struct Conpty { - pub handle: HPCON, - api: ConptyApi, -} - -/// Handle can be cloned freely and moved between threads. -pub type ConptyHandle = Arc<Conpty>; - -impl Drop for Conpty { - fn drop(&mut self) { - unsafe { (self.api.ClosePseudoConsole)(self.handle) } - } -} - -// The Conpty API can be accessed from multiple threads. -unsafe impl Send for Conpty {} -unsafe impl Sync for Conpty {} - -pub fn new<'a>( - config: &Config, - options: &Options, - size: &SizeInfo, - _window_id: Option<usize>, -) -> Option<Pty<'a>> { - if !config.enable_experimental_conpty_backend() { - return None; - } - - let api = ConptyApi::new()?; - - let mut pty_handle = 0 as HPCON; - - // Passing 0 as the size parameter allows the "system default" buffer - // size to be used. There may be small performance and memory advantages - // to be gained by tuning this in the future, but it's likely a reasonable - // start point. - let (conout, conout_pty_handle) = miow::pipe::anonymous(0).unwrap(); - let (conin_pty_handle, conin) = miow::pipe::anonymous(0).unwrap(); - - let coord = - coord_from_sizeinfo(size).expect("Overflow when creating initial size on pseudoconsole"); - - // Create the Pseudo Console, using the pipes - let result = unsafe { - (api.CreatePseudoConsole)( - coord, - conin_pty_handle.into_raw_handle(), - conout_pty_handle.into_raw_handle(), - 0, - &mut pty_handle as *mut HPCON, - ) - }; - - assert!(result == S_OK); - - let mut success; - - // Prepare child process startup info - - let mut size: SIZE_T = 0; - - let mut startup_info_ex: STARTUPINFOEXW = Default::default(); - - let title = options.title.as_ref().map(String::as_str).unwrap_or("Alacritty"); - let title = U16CString::from_str(title).unwrap(); - startup_info_ex.StartupInfo.lpTitle = title.as_ptr() as LPWSTR; - - startup_info_ex.StartupInfo.cb = mem::size_of::<STARTUPINFOEXW>() as u32; - - // Setting this flag but leaving all the handles as default (null) ensures the - // pty process does not inherit any handles from this Alacritty process. - startup_info_ex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; - - // Create the appropriately sized thread attribute list. - unsafe { - let failure = - InitializeProcThreadAttributeList(ptr::null_mut(), 1, 0, &mut size as PSIZE_T) > 0; - - // This call was expected to return false. - if failure { - panic_shell_spawn(); - } - } - - let mut attr_list: Box<[BYTE]> = vec![0; size].into_boxed_slice(); - - // Set startup info's attribute list & initialize it - // - // Lint failure is spurious; it's because winapi's definition of PROC_THREAD_ATTRIBUTE_LIST - // implies it is one pointer in size (32 or 64 bits) but really this is just a dummy value. - // Casting a *mut u8 (pointer to 8 bit type) might therefore not be aligned correctly in - // the compiler's eyes. - #[allow(clippy::cast_ptr_alignment)] - { - startup_info_ex.lpAttributeList = attr_list.as_mut_ptr() as _; - } - - unsafe { - success = InitializeProcThreadAttributeList( - startup_info_ex.lpAttributeList, - 1, - 0, - &mut size as PSIZE_T, - ) > 0; - - if !success { - panic_shell_spawn(); - } - } - - // Set thread attribute list's Pseudo Console to the specified ConPTY - unsafe { - success = UpdateProcThreadAttribute( - startup_info_ex.lpAttributeList, - 0, - 22 | 0x0002_0000, // PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE - pty_handle, - mem::size_of::<HPCON>(), - ptr::null_mut(), - ptr::null_mut(), - ) > 0; - - if !success { - panic_shell_spawn(); - } - } - - // Get process commandline - let default_shell = &Shell::new("powershell"); - let shell = config.shell().unwrap_or(default_shell); - let initial_command = options.command().unwrap_or(shell); - let mut cmdline = initial_command.args().to_vec(); - cmdline.insert(0, initial_command.program().into()); - - // Warning, here be borrow hell - let cwd = options.working_dir.as_ref().map(|dir| canonicalize(dir).unwrap()); - let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap()); - - // Create the client application, using startup info containing ConPTY info - let cmdline = U16CString::from_str(&cmdline.join(" ")).unwrap(); - let cwd = cwd.map(|s| U16CString::from_str(&s).unwrap()); - - let mut proc_info: PROCESS_INFORMATION = Default::default(); - unsafe { - success = CreateProcessW( - ptr::null(), - cmdline.as_ptr() as LPWSTR, - ptr::null_mut(), - ptr::null_mut(), - false as i32, - EXTENDED_STARTUPINFO_PRESENT, - ptr::null_mut(), - cwd.as_ref().map_or_else(ptr::null, |s| s.as_ptr()), - &mut startup_info_ex.StartupInfo as *mut STARTUPINFOW, - &mut proc_info as *mut PROCESS_INFORMATION, - ) > 0; - - if !success { - panic_shell_spawn(); - } - } - - // Store handle to console - unsafe { - HANDLE = proc_info.hProcess; - } - - let conin = EventedAnonWrite::new(conin); - let conout = EventedAnonRead::new(conout); - - let agent = Conpty { handle: pty_handle, api }; - - Some(Pty { - handle: super::PtyHandle::Conpty(ConptyHandle::new(agent)), - conout: super::EventedReadablePipe::Anonymous(conout), - conin: super::EventedWritablePipe::Anonymous(conin), - read_token: 0.into(), - write_token: 0.into(), - }) -} - -// Panic with the last os error as message -fn panic_shell_spawn() { - panic!("Unable to spawn shell: {}", Error::last_os_error()); -} - -impl OnResize for ConptyHandle { - fn on_resize(&mut self, sizeinfo: &SizeInfo) { - if let Some(coord) = coord_from_sizeinfo(sizeinfo) { - let result = unsafe { (self.api.ResizePseudoConsole)(self.handle, coord) }; - assert!(result == S_OK); - } - } -} - -/// Helper to build a COORD from a SizeInfo, returing None in overflow cases. -fn coord_from_sizeinfo(sizeinfo: &SizeInfo) -> Option<COORD> { - let cols = sizeinfo.cols().0; - let lines = sizeinfo.lines().0; - - if cols <= i16::MAX as usize && lines <= i16::MAX as usize { - Some(COORD { X: sizeinfo.cols().0 as i16, Y: sizeinfo.lines().0 as i16 }) - } else { - None - } -} |