Add cargo feature for WinPTY

This commit is contained in:
David Hewitt 2020-06-02 22:31:06 +01:00 committed by GitHub
parent 1dacc99183
commit c102e845cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 236 additions and 167 deletions

View File

@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Option `cursor.thickness` to set terminal cursor thickness
- Font fallback on Windows
- Support for Fontconfig embolden and matrix options
- Opt-out compilation flag `winpty` to disable WinPTY support
### Changed

View File

@ -11,7 +11,7 @@ edition = "2018"
[dependencies]
alacritty_terminal = { path = "../alacritty_terminal", default-features = false }
clap = "2"
log = "0.4"
log = { version = "0.4", features = ["std"] }
time = "0.1.40"
fnv = "1"
serde = { version = "1", features = ["derive"] }
@ -47,9 +47,10 @@ winapi = { version = "0.3.7", features = ["impl-default", "wincon"]}
embed-resource = "1.3"
[features]
default = ["wayland", "x11"]
default = ["wayland", "x11", "winpty"]
x11 = ["alacritty_terminal/x11"]
wayland = ["alacritty_terminal/wayland"]
winpty = ["alacritty_terminal/winpty"]
# Enabling this feature makes shaders automatically reload when changed
live-shader-reload = []
nightly = []

View File

@ -29,7 +29,7 @@ nix = "0.17.0"
signal-hook = { version = "0.1", features = ["mio-support"] }
[target.'cfg(windows)'.dependencies]
winpty = "0.2.0"
winpty = { version = "0.2.0", optional = true }
mio-named-pipes = "0.1"
miow = "0.3"
winapi = { version = "0.3.7", features = [
@ -42,7 +42,7 @@ mio-anonymous-pipes = "0.1"
objc = "0.2.2"
[features]
default = ["x11", "wayland"]
default = ["x11", "wayland", "winpty"]
x11 = ["copypasta/x11"]
wayland = ["copypasta/wayland"]
nightly = []

View File

@ -0,0 +1,189 @@
/// Types to determine the appropriate PTY backend at runtime.
///
/// Unless the winpty feature is disabled, the PTY backend will automatically fall back to
/// WinPTY when the newer ConPTY API is not supported, as long as the user hasn't explicitly
/// opted into the WinPTY config option.
use std::io::{self, Read, Write};
use log::info;
use mio::{Evented, Poll, PollOpt, Ready, Token};
use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite};
use mio_named_pipes::NamedPipe;
use crate::config::Config;
use crate::event::OnResize;
use crate::term::SizeInfo;
use super::{conpty, winpty, Pty};
pub fn new<C>(config: &Config<C>, size: &SizeInfo, window_id: Option<usize>) -> Pty {
if let Some(pty) = conpty::new(config, size, window_id) {
info!("Using ConPTY backend");
pty
} else {
info!("Using WinPTY backend");
winpty::new(config, size, window_id)
}
}
pub enum PtyBackend {
Winpty(winpty::Agent),
Conpty(conpty::Conpty),
}
impl OnResize for PtyBackend {
fn on_resize(&mut self, size: &SizeInfo) {
match self {
PtyBackend::Winpty(w) => w.on_resize(size),
PtyBackend::Conpty(c) => c.on_resize(size),
}
}
}
// TODO: The ConPTY API currently must use synchronous pipes as the input
// and output handles. This has led to the need to support two different
// types of pipe.
//
// When https://github.com/Microsoft/console/issues/262 lands then the
// Anonymous variant of this enum can be removed from the codebase and
// everything can just use NamedPipe.
pub enum EventedReadablePipe {
Anonymous(EventedAnonRead),
Named(NamedPipe),
}
pub enum EventedWritablePipe {
Anonymous(EventedAnonWrite),
Named(NamedPipe),
}
impl Evented for EventedReadablePipe {
fn register(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedReadablePipe::Anonymous(p) => p.register(poll, token, interest, opts),
EventedReadablePipe::Named(p) => p.register(poll, token, interest, opts),
}
}
fn reregister(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedReadablePipe::Anonymous(p) => p.reregister(poll, token, interest, opts),
EventedReadablePipe::Named(p) => p.reregister(poll, token, interest, opts),
}
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
match self {
EventedReadablePipe::Anonymous(p) => p.deregister(poll),
EventedReadablePipe::Named(p) => p.deregister(poll),
}
}
}
impl Read for EventedReadablePipe {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
EventedReadablePipe::Anonymous(p) => p.read(buf),
EventedReadablePipe::Named(p) => p.read(buf),
}
}
}
impl Evented for EventedWritablePipe {
fn register(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.register(poll, token, interest, opts),
EventedWritablePipe::Named(p) => p.register(poll, token, interest, opts),
}
}
fn reregister(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.reregister(poll, token, interest, opts),
EventedWritablePipe::Named(p) => p.reregister(poll, token, interest, opts),
}
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.deregister(poll),
EventedWritablePipe::Named(p) => p.deregister(poll),
}
}
}
impl Write for EventedWritablePipe {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
EventedWritablePipe::Anonymous(p) => p.write(buf),
EventedWritablePipe::Named(p) => p.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.flush(),
EventedWritablePipe::Named(p) => p.flush(),
}
}
}
impl From<winpty::Agent> for PtyBackend {
fn from(inner: winpty::Agent) -> Self {
PtyBackend::Winpty(inner)
}
}
impl From<conpty::Conpty> for PtyBackend {
fn from(inner: conpty::Conpty) -> Self {
PtyBackend::Conpty(inner)
}
}
impl From<EventedAnonRead> for EventedReadablePipe {
fn from(inner: EventedAnonRead) -> Self {
EventedReadablePipe::Anonymous(inner)
}
}
impl From<NamedPipe> for EventedReadablePipe {
fn from(inner: NamedPipe) -> Self {
EventedReadablePipe::Named(inner)
}
}
impl From<EventedAnonWrite> for EventedWritablePipe {
fn from(inner: EventedAnonWrite) -> Self {
EventedWritablePipe::Anonymous(inner)
}
}
impl From<NamedPipe> for EventedWritablePipe {
fn from(inner: NamedPipe) -> Self {
EventedWritablePipe::Named(inner)
}
}

View File

@ -231,15 +231,7 @@ pub fn new<C>(config: &Config<C>, size: &SizeInfo, _window_id: Option<usize>) ->
let child_watcher = ChildExitWatcher::new(proc_info.hProcess).unwrap();
let conpty = Conpty { handle: pty_handle, api };
Some(Pty {
backend: super::PtyBackend::Conpty(conpty),
conout: super::EventedReadablePipe::Anonymous(conout),
conin: super::EventedWritablePipe::Anonymous(conin),
read_token: 0.into(),
write_token: 0.into(),
child_event_token: 0.into(),
child_watcher,
})
Some(Pty::new(conpty, conout, conin, child_watcher))
}
// Panic with the last os error as message.

View File

@ -13,181 +13,78 @@
// limitations under the License.
use std::ffi::OsStr;
use std::io::{self, Read, Write};
use std::io;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::TryRecvError;
use mio::{self, Evented, Poll, PollOpt, Ready, Token};
use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite};
use mio_named_pipes::NamedPipe;
use log::info;
use crate::config::{Config, Shell};
use crate::event::OnResize;
use crate::term::SizeInfo;
use crate::tty::windows::child::ChildExitWatcher;
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
#[cfg(feature = "winpty")]
mod automatic_backend;
mod child;
mod conpty;
#[cfg(feature = "winpty")]
mod winpty;
static IS_CONPTY: AtomicBool = AtomicBool::new(false);
#[cfg(not(feature = "winpty"))]
use conpty::Conpty as Backend;
#[cfg(not(feature = "winpty"))]
use mio_anonymous_pipes::{EventedAnonRead as ReadPipe, EventedAnonWrite as WritePipe};
pub fn is_conpty() -> bool {
IS_CONPTY.load(Ordering::Relaxed)
}
enum PtyBackend {
Winpty(winpty::Agent),
Conpty(conpty::Conpty),
}
#[cfg(feature = "winpty")]
use automatic_backend::{
EventedReadablePipe as ReadPipe, EventedWritablePipe as WritePipe, PtyBackend as Backend,
};
pub struct Pty {
// XXX: Backend is required to be the first field, to ensure correct drop order. Dropping
// `conout` before `backend` will cause a deadlock.
backend: PtyBackend,
// TODO: It's on the roadmap for the Conpty API to support Overlapped I/O.
// See https://github.com/Microsoft/console/issues/262.
// When support for that lands then it should be possible to use
// NamedPipe for the conout and conin handles.
conout: EventedReadablePipe,
conin: EventedWritablePipe,
// `conout` before `backend` will cause a deadlock (with Conpty).
backend: Backend,
conout: ReadPipe,
conin: WritePipe,
read_token: mio::Token,
write_token: mio::Token,
child_event_token: mio::Token,
child_watcher: ChildExitWatcher,
}
#[cfg(not(feature = "winpty"))]
pub fn new<C>(config: &Config<C>, size: &SizeInfo, window_id: Option<usize>) -> Pty {
if let Some(pty) = conpty::new(config, size, window_id) {
info!("Using ConPTY backend");
IS_CONPTY.store(true, Ordering::Relaxed);
pty
} else {
info!("Using WinPTY backend");
winpty::new(config, size, window_id)
}
conpty::new(config, size, window_id).expect("Failed to create ConPTY backend")
}
// TODO: The ConPTY API currently must use synchronous pipes as the input
// and output handles. This has led to the need to support two different
// types of pipe.
//
// When https://github.com/Microsoft/console/issues/262 lands then the
// Anonymous variant of this enum can be removed from the codebase and
// everything can just use NamedPipe.
pub enum EventedReadablePipe {
Anonymous(EventedAnonRead),
Named(NamedPipe),
#[cfg(feature = "winpty")]
pub fn new<C>(config: &Config<C>, size: &SizeInfo, window_id: Option<usize>) -> Pty {
automatic_backend::new(config, size, window_id)
}
pub enum EventedWritablePipe {
Anonymous(EventedAnonWrite),
Named(NamedPipe),
}
impl Evented for EventedReadablePipe {
fn register(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedReadablePipe::Anonymous(p) => p.register(poll, token, interest, opts),
EventedReadablePipe::Named(p) => p.register(poll, token, interest, opts),
}
}
fn reregister(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedReadablePipe::Anonymous(p) => p.reregister(poll, token, interest, opts),
EventedReadablePipe::Named(p) => p.reregister(poll, token, interest, opts),
}
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
match self {
EventedReadablePipe::Anonymous(p) => p.deregister(poll),
EventedReadablePipe::Named(p) => p.deregister(poll),
}
}
}
impl Read for EventedReadablePipe {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
EventedReadablePipe::Anonymous(p) => p.read(buf),
EventedReadablePipe::Named(p) => p.read(buf),
}
}
}
impl Evented for EventedWritablePipe {
fn register(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.register(poll, token, interest, opts),
EventedWritablePipe::Named(p) => p.register(poll, token, interest, opts),
}
}
fn reregister(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.reregister(poll, token, interest, opts),
EventedWritablePipe::Named(p) => p.reregister(poll, token, interest, opts),
}
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.deregister(poll),
EventedWritablePipe::Named(p) => p.deregister(poll),
}
}
}
impl Write for EventedWritablePipe {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
EventedWritablePipe::Anonymous(p) => p.write(buf),
EventedWritablePipe::Named(p) => p.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
EventedWritablePipe::Anonymous(p) => p.flush(),
EventedWritablePipe::Named(p) => p.flush(),
impl Pty {
fn new(
backend: impl Into<Backend>,
conout: impl Into<ReadPipe>,
conin: impl Into<WritePipe>,
child_watcher: ChildExitWatcher,
) -> Self {
Self {
backend: backend.into(),
conout: conout.into(),
conin: conin.into(),
read_token: 0.into(),
write_token: 0.into(),
child_event_token: 0.into(),
child_watcher,
}
}
}
impl EventedReadWrite for Pty {
type Reader = EventedReadablePipe;
type Writer = EventedWritablePipe;
type Reader = ReadPipe;
type Writer = WritePipe;
#[inline]
fn register(
@ -295,10 +192,7 @@ impl EventedPty for Pty {
impl OnResize for Pty {
fn on_resize(&mut self, size: &SizeInfo) {
match &mut self.backend {
PtyBackend::Winpty(w) => w.on_resize(size),
PtyBackend::Conpty(c) => c.on_resize(size),
}
self.backend.on_resize(size)
}
}

View File

@ -70,15 +70,7 @@ pub fn new<C>(config: &Config<C>, size: &SizeInfo, _window_id: Option<usize>) ->
let child_watcher = ChildExitWatcher::new(agent.raw_handle()).unwrap();
Pty {
backend: super::PtyBackend::Winpty(agent),
conout: super::EventedReadablePipe::Named(conout_pipe),
conin: super::EventedWritablePipe::Named(conin_pipe),
read_token: 0.into(),
write_token: 0.into(),
child_event_token: 0.into(),
child_watcher,
}
Pty::new(agent, conout_pipe, conin_pipe, child_watcher)
}
impl OnResize for Agent {