mirror of
https://github.com/alacritty/alacritty.git
synced 2025-04-21 18:02:37 -04:00
Add cargo feature for WinPTY
This commit is contained in:
parent
1dacc99183
commit
c102e845cd
7 changed files with 236 additions and 167 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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 = []
|
||||
|
|
189
alacritty_terminal/src/tty/windows/automatic_backend.rs
Normal file
189
alacritty_terminal/src/tty/windows/automatic_backend.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue