mirror of
https://github.com/wez/wezterm.git
synced 2024-12-11 07:03:35 +03:00
201 lines
6.4 KiB
Rust
201 lines
6.4 KiB
Rust
//! This crate provides a cross platform API for working with the
|
|
//! psuedo terminal (pty) interfaces provided by the system.
|
|
//! Unlike other crates in this space, this crate provides a set
|
|
//! of traits that allow selecting from different implementations
|
|
//! at runtime.
|
|
//! This crate is part of [wezterm](https://github.com/wez/wezterm).
|
|
//!
|
|
//! ```no_run
|
|
//! use portable_pty::{CommandBuilder, PtySize, native_pty_system, PtySystem};
|
|
//! use anyhow::Error;
|
|
//!
|
|
//! // Use the native pty implementation for the system
|
|
//! let pty_system = native_pty_system();
|
|
//!
|
|
//! // Create a new pty
|
|
//! let mut pair = pty_system.openpty(PtySize {
|
|
//! rows: 24,
|
|
//! cols: 80,
|
|
//! // Not all systems support pixel_width, pixel_height,
|
|
//! // but it is good practice to set it to something
|
|
//! // that matches the size of the selected font. That
|
|
//! // is more complex than can be shown here in this
|
|
//! // brief example though!
|
|
//! pixel_width: 0,
|
|
//! pixel_height: 0,
|
|
//! })?;
|
|
//!
|
|
//! // Spawn a shell into the pty
|
|
//! let cmd = CommandBuilder::new("bash");
|
|
//! let child = pair.slave.spawn_command(cmd)?;
|
|
//!
|
|
//! // Read and parse output from the pty with reader
|
|
//! let mut reader = pair.master.try_clone_reader()?;
|
|
//!
|
|
//! // Send data to the pty by writing to the master
|
|
//! writeln!(pair.master, "ls -l\r\n")?;
|
|
//! # Ok::<(), Error>(())
|
|
//! ```
|
|
//!
|
|
//! ## ssh2
|
|
//!
|
|
//! If the `ssh` feature is enabled, this crate exposes an
|
|
//! `ssh::SshSession` type that can wrap an established ssh
|
|
//! session with an implementation of `PtySystem`, allowing
|
|
//! you to use the same pty interface with remote ptys.
|
|
use anyhow::Error;
|
|
#[cfg(feature = "serde_support")]
|
|
use serde_derive::*;
|
|
use std::io::Result as IoResult;
|
|
|
|
pub mod cmdbuilder;
|
|
pub use cmdbuilder::CommandBuilder;
|
|
|
|
#[cfg(unix)]
|
|
pub mod unix;
|
|
#[cfg(windows)]
|
|
pub mod win;
|
|
|
|
#[cfg(feature = "ssh")]
|
|
pub mod ssh;
|
|
|
|
pub mod serial;
|
|
|
|
/// Represents the size of the visible display area in the pty
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
|
pub struct PtySize {
|
|
/// The number of lines of text
|
|
pub rows: u16,
|
|
/// The number of columns of text
|
|
pub cols: u16,
|
|
/// The width of a cell in pixels. Note that some systems never
|
|
/// fill this value and ignore it.
|
|
pub pixel_width: u16,
|
|
/// The height of a cell in pixels. Note that some systems never
|
|
/// fill this value and ignore it.
|
|
pub pixel_height: u16,
|
|
}
|
|
|
|
impl Default for PtySize {
|
|
fn default() -> Self {
|
|
PtySize {
|
|
rows: 24,
|
|
cols: 80,
|
|
pixel_width: 0,
|
|
pixel_height: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Represents the master/control end of the pty
|
|
pub trait MasterPty: std::io::Write {
|
|
/// Inform the kernel and thus the child process that the window resized.
|
|
/// It will update the winsize information maintained by the kernel,
|
|
/// and generate a signal for the child to notice and update its state.
|
|
fn resize(&self, size: PtySize) -> Result<(), Error>;
|
|
/// Retrieves the size of the pty as known by the kernel
|
|
fn get_size(&self) -> Result<PtySize, Error>;
|
|
/// Obtain a readable handle; output from the slave(s) is readable
|
|
/// via this stream.
|
|
fn try_clone_reader(&self) -> Result<Box<dyn std::io::Read + Send>, Error>;
|
|
/// Obtain a writable handle; writing to it will send data to the
|
|
/// slave end. This is equivalent to the Write impl on MasterPty
|
|
/// itself, but allows splitting it off into a separate object.
|
|
fn try_clone_writer(&self) -> Result<Box<dyn std::io::Write + Send>, Error>;
|
|
}
|
|
|
|
/// Represents a child process spawned into the pty.
|
|
/// This handle can be used to wait for or terminate that child process.
|
|
pub trait Child: std::fmt::Debug {
|
|
/// Poll the child to see if it has completed.
|
|
/// Does not block.
|
|
/// Returns None if the has not yet terminated,
|
|
/// else returns its exit status.
|
|
fn try_wait(&mut self) -> IoResult<Option<ExitStatus>>;
|
|
/// Terminate the child process
|
|
fn kill(&mut self) -> IoResult<()>;
|
|
/// Blocks execution until the child process has completed,
|
|
/// yielding its exit status.
|
|
fn wait(&mut self) -> IoResult<ExitStatus>;
|
|
}
|
|
|
|
/// Represents the slave side of a pty.
|
|
/// Can be used to spawn processes into the pty.
|
|
pub trait SlavePty {
|
|
/// Spawns the command specified by the provided CommandBuilder
|
|
fn spawn_command(&self, cmd: CommandBuilder) -> Result<Box<dyn Child + Send>, Error>;
|
|
}
|
|
|
|
/// Represents the exit status of a child process.
|
|
/// This is rather anemic in the current version of this crate,
|
|
/// holding only an indicator of success or failure.
|
|
#[derive(Debug, Clone)]
|
|
pub struct ExitStatus {
|
|
successful: bool,
|
|
}
|
|
|
|
impl ExitStatus {
|
|
/// Construct an ExitStatus from a process return code
|
|
pub fn with_exit_code(code: u32) -> Self {
|
|
Self {
|
|
successful: code == 0,
|
|
}
|
|
}
|
|
|
|
pub fn success(&self) -> bool {
|
|
self.successful
|
|
}
|
|
}
|
|
|
|
impl From<std::process::ExitStatus> for ExitStatus {
|
|
fn from(status: std::process::ExitStatus) -> ExitStatus {
|
|
ExitStatus {
|
|
successful: status.success(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct PtyPair {
|
|
// slave is listed first so that it is dropped first.
|
|
// The drop order is stable and specified by rust rfc 1857
|
|
pub slave: Box<dyn SlavePty + Send>,
|
|
pub master: Box<dyn MasterPty + Send>,
|
|
}
|
|
|
|
/// The `PtySystem` trait allows an application to work with multiple
|
|
/// possible Pty implementations at runtime. This is important on
|
|
/// Windows systems which have a variety of implementations.
|
|
pub trait PtySystem {
|
|
/// Create a new Pty instance with the window size set to the specified
|
|
/// dimensions. Returns a (master, slave) Pty pair. The master side
|
|
/// is used to drive the slave side.
|
|
fn openpty(&self, size: PtySize) -> anyhow::Result<PtyPair>;
|
|
}
|
|
|
|
impl Child for std::process::Child {
|
|
fn try_wait(&mut self) -> IoResult<Option<ExitStatus>> {
|
|
std::process::Child::try_wait(self).map(|s| match s {
|
|
Some(s) => Some(s.into()),
|
|
None => None,
|
|
})
|
|
}
|
|
|
|
fn kill(&mut self) -> IoResult<()> {
|
|
std::process::Child::kill(self)
|
|
}
|
|
|
|
fn wait(&mut self) -> IoResult<ExitStatus> {
|
|
std::process::Child::wait(self).map(Into::into)
|
|
}
|
|
}
|
|
|
|
pub fn native_pty_system() -> Box<dyn PtySystem> {
|
|
Box::new(NativePtySystem::default())
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
pub type NativePtySystem = unix::UnixPtySystem;
|
|
#[cfg(windows)]
|
|
pub type NativePtySystem = win::conpty::ConPtySystem;
|